-
0 ? `${thumbnailsHeight + 16}px` : 0,
- zIndex: 1,
- }}
- />
- {
- if (typeof ref === "function") {
- ref(el)
- } else if (ref) {
- ref.current = el
- }
- textAreaRef.current = el
- }}
- value={inputValue}
- disabled={textAreaDisabled}
- onChange={(e) => {
- handleInputChange(e)
- updateHighlights()
- }}
- onFocus={() => setIsFocused(true)}
- onKeyDown={handleKeyDown}
- onKeyUp={handleKeyUp}
- onBlur={handleBlur}
- onPaste={handlePaste}
- onSelect={updateCursorPosition}
- onMouseUp={updateCursorPosition}
- onHeightChange={(height) => {
- if (textAreaBaseHeight === undefined || height < textAreaBaseHeight) {
- setTextAreaBaseHeight(height)
+ flexDirection: "column",
+ gap: "8px",
+ backgroundColor: vscEditorBackground,
+ // margin: "10px 15px",
+ padding: "8px",
+ outline: "none",
+ // border: "1px solid",
+ borderColor: "transparent",
+ borderRadius: "12px",
+ }}
+ onDrop={async (e) => {
+ e.preventDefault()
+ const files = Array.from(e.dataTransfer.files)
+ const text = e.dataTransfer.getData("text")
+ if (text) {
+ const newValue =
+ inputValue.slice(0, cursorPosition) + text + inputValue.slice(cursorPosition)
+ setInputValue(newValue)
+ const newCursorPosition = cursorPosition + text.length
+ setCursorPosition(newCursorPosition)
+ setIntendedCursorPosition(newCursorPosition)
+ return
+ }
+ const acceptedTypes = ["png", "jpeg", "webp"]
+ const imageFiles = files.filter((file) => {
+ const [type, subtype] = file.type.split("/")
+ return type === "image" && acceptedTypes.includes(subtype)
+ })
+ if (!shouldDisableImages && imageFiles.length > 0) {
+ const imagePromises = imageFiles.map((file) => {
+ return new Promise((resolve) => {
+ const reader = new FileReader()
+ reader.onloadend = () => {
+ if (reader.error) {
+ console.error("Error reading file:", reader.error)
+ resolve(null)
+ } else {
+ const result = reader.result
+ resolve(typeof result === "string" ? result : null)
+ }
+ }
+ reader.readAsDataURL(file)
+ })
+ })
+ const imageDataArray = await Promise.all(imagePromises)
+ const dataUrls = imageDataArray.filter((dataUrl): dataUrl is string => dataUrl !== null)
+ if (dataUrls.length > 0) {
+ setSelectedImages((prevImages) =>
+ [...prevImages, ...dataUrls].slice(0, MAX_IMAGES_PER_MESSAGE),
+ )
+ if (typeof vscode !== "undefined") {
+ vscode.postMessage({
+ type: "draggedImages",
+ dataUrls: dataUrls,
+ })
+ }
+ } else {
+ console.warn("No valid images were processed")
}
- onHeightChange?.(height)
- }}
- placeholder={placeholderText}
- minRows={3}
- maxRows={15}
- autoFocus={true}
- style={{
- width: "100%",
- outline: "none",
- boxSizing: "border-box",
- backgroundColor: "transparent",
- color: "var(--vscode-input-foreground)",
- borderRadius: 2,
- fontFamily: "var(--vscode-font-family)",
- fontSize: "var(--vscode-editor-font-size)",
- lineHeight: "var(--vscode-editor-line-height)",
- resize: "none",
- overflowX: "hidden",
- overflowY: "auto",
- border: "none",
- padding: "2px",
- paddingRight: "8px",
- marginBottom: thumbnailsHeight > 0 ? `${thumbnailsHeight + 16}px` : 0,
- cursor: textAreaDisabled ? "not-allowed" : undefined,
- flex: "0 1 auto",
- zIndex: 2,
- scrollbarWidth: "none",
- }}
- onScroll={() => updateHighlights()}
- />
-
-
- {selectedImages.length > 0 && (
-
- )}
-
-
{
+ e.preventDefault()
}}>
+ {showContextMenu && (
+
+
+
+ )}
+
-
-
+ {
- const value = e.target.value
- if (value === "prompts-action") {
- window.postMessage({ type: "action", action: "promptsButtonClicked" })
- return
- }
- setMode(value as Mode)
- vscode.postMessage({
- type: "mode",
- text: value,
- })
+ onClick={() => {
+ // if (!textAreaDisabled && textAreaRef.current) {
+ // const newValue =
+ // inputValue.slice(0, cursorPosition) +
+ // "@" +
+ // inputValue.slice(cursorPosition);
+ // setInputValue(newValue);
+ // const newCursorPosition = cursorPosition + 1;
+ // setCursorPosition(newCursorPosition);
+ // setIntendedCursorPosition(newCursorPosition);
+ // }
+ setShowContextMenu(true)
+ setSearchQuery("")
+ // textAreaRef.current.focus();
}}
style={{
- ...selectStyle,
- minWidth: "70px",
- flex: "0 0 auto",
+ color: vscForeground,
+ backgroundColor: vscInputBackground,
+ border: `1px solid ${vscInputBorder}`,
}}>
- {getAllModes(customModes).map((mode) => (
-
- {mode.name}
-
- ))}
-
- ────
-
-
- Edit...
-
-
-
-
-
+ @ Context
+
+
!shouldDisableImages && onSelectImages()}
+ />
+ {/*
+
+
!textAreaDisabled && onSend()}>
+
+ Send
+
+
*/}
+
+
-
{
- const value = e.target.value
- if (value === "settings-action") {
- window.postMessage({ type: "action", action: "settingsButtonClicked" })
- return
- }
- vscode.postMessage({
- type: "loadApiConfiguration",
- text: value,
- })
- }}
- style={{
- ...selectStyle,
- width: "100%",
- textOverflow: "ellipsis",
- }}>
- {(listApiConfigMeta || []).map((config) => (
-
- {config.name}
-
- ))}
-
- ────
-
-
- Edit...
-
-
-
-
-
-
+ fontFamily: "var(--vscode-font-family)",
+ fontSize: "var(--vscode-editor-font-size)",
+ lineHeight: "var(--vscode-editor-line-height)",
+ padding: "2px",
+ paddingRight: "8px",
+ marginBottom: thumbnailsHeight > 0 ? `${thumbnailsHeight + 16}px` : 0,
+ zIndex: 1,
+ }}
+ />
+
{
+ if (typeof ref === "function") {
+ ref(el)
+ } else if (ref) {
+ ref.current = el
+ }
+ textAreaRef.current = el
+ }}
+ value={inputValue}
+ disabled={textAreaDisabled}
+ onChange={(e) => {
+ handleInputChange(e)
+ updateHighlights()
+ }}
+ onFocus={() => setIsFocused(true)}
+ onKeyDown={handleKeyDown}
+ onKeyUp={handleKeyUp}
+ onBlur={handleBlur}
+ onPaste={handlePaste}
+ onSelect={updateCursorPosition}
+ onMouseUp={updateCursorPosition}
+ onHeightChange={(height) => {
+ if (textAreaBaseHeight === undefined || height < textAreaBaseHeight) {
+ setTextAreaBaseHeight(height)
+ }
+ onHeightChange?.(height)
+ }}
+ placeholder={placeholderText}
+ minRows={isNewTask ? 3 : 1}
+ maxRows={15}
+ autoFocus={true}
+ style={{
+ width: "100%",
+ outline: "none",
+ boxSizing: "border-box",
+ backgroundColor: "transparent",
+ color: "var(--vscode-input-foreground)",
+ borderRadius: 2,
+ fontFamily: "var(--vscode-font-family)",
+ fontSize: "var(--vscode-editor-font-size)",
+ lineHeight: "var(--vscode-editor-line-height)",
+ resize: "none",
+ overflowX: "hidden",
+ overflowY: "auto",
+ border: "none",
+ padding: "2px",
+ paddingTop: "8px",
+ paddingBottom: "8px",
+ paddingRight: "8px",
+ marginBottom: thumbnailsHeight > 0 ? `${thumbnailsHeight + 16}px` : 0,
+ cursor: textAreaDisabled ? "not-allowed" : undefined,
+ flex: "0 1 auto",
+ zIndex: 2,
+ scrollbarWidth: "none",
+ }}
+ onScroll={() => updateHighlights()}
+ />
+ {selectedImages.length > 0 && (
+
+ )}
+
-
- {isEnhancingPrompt ? (
-
- ) : (
-
!textAreaDisabled && handleEnhancePrompt()}
- style={{ fontSize: 16.5 }}
- />
- )}
+
+
+
+
+ {isEnhancingPrompt ? (
+
+ ) : (
+ !textAreaDisabled && handleEnhancePrompt()}
+ style={{ fontSize: 16.5 }}
+ />
+ )}
+
+
+
!textAreaDisabled && onSend()}>
+
+ Send
+
- !shouldDisableImages && onSelectImages()}
- style={{ fontSize: 16.5 }}
- />
- !textAreaDisabled && onSend()}
- style={{ fontSize: 15 }}
- />
-
+
+
+ {
+ if (value === "prompts-action") {
+ window.postMessage({ type: "action", action: "promptsButtonClicked" })
+ return
+ }
+ setMode(value as Mode)
+ vscode.postMessage({
+ type: "mode",
+ text: value,
+ })
+ }}
+ disabled={textAreaDisabled}>
+
+ {getAllModes(customModes).find((m) => m.slug === mode)?.name}
+
+
+
+ {getAllModes(customModes).map((mode) => (
+
+ {mode.name}
+
+ ))}
+
+
+ Edit...
+
+
+
+
+
+
+ {
+ if (value === "settings-action") {
+ window.postMessage({ type: "action", action: "settingsButtonClicked" })
+ return
+ }
+ vscode.postMessage({
+ type: "loadApiConfiguration",
+ text: value,
+ })
+ }}
+ disabled={textAreaDisabled}>
+
+ {currentApiConfigName}
+
+
+
+ {(listApiConfigMeta || []).map((config) => (
+
+ {config.name}
+
+ ))}
+
+
+ Edit...
+
+
+
+
+
+ >
)
},
)
diff --git a/webview-ui/src/components/chat/ChatView.tsx b/webview-ui/src/components/chat/ChatView.tsx
index 519b7e5efb6..ef857e06e26 100644
--- a/webview-ui/src/components/chat/ChatView.tsx
+++ b/webview-ui/src/components/chat/ChatView.tsx
@@ -28,6 +28,18 @@ import TaskHeader from "./TaskHeader"
import AutoApproveMenu from "./AutoApproveMenu"
import { AudioType } from "../../../../src/shared/WebviewMessage"
import { validateCommand } from "../../utils/command-validation"
+import { Button } from "../ui/button-pear-scn"
+import { DownloadIcon } from "@radix-ui/react-icons"
+import {
+ vscBackground,
+ vscBadgeBackground,
+ vscButtonBackground,
+ vscEditorBackground,
+ vscForeground,
+ vscInputBorder,
+ vscSidebarBorder,
+} from "../ui"
+import splashIcon from "../../../../assets/icons/pearai-agent-splash.svg"
interface ChatViewProps {
isHidden: boolean
@@ -872,11 +884,11 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
useEvent("wheel", handleWheel, window, { passive: true }) // passive improves scrolling performance
const placeholderText = useMemo(() => {
- const baseText = task ? "Type a message..." : "Type your task here..."
- const contextText = "(@ to add context"
- const imageText = shouldDisableImages ? "" : ", hold shift to drag in images"
- const helpText = imageText ? `\n${contextText}${imageText})` : `\n${contextText})`
- return baseText + helpText
+ const baseText = task ? "Ask a follow up." : "Give PearAI Agent a task here."
+ const contextText = " Use @ to add context."
+ const imageText = shouldDisableImages ? "" : "\nhold shift to drag in images"
+ const helpText = imageText ? `\n${contextText}${imageText}` : `\n${contextText}`
+ return baseText + contextText
}, [task, shouldDisableImages])
const itemContent = useCallback(
@@ -966,6 +978,7 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
left: 0,
right: 0,
bottom: 0,
+ padding: "12px 12px",
display: isHidden ? "none" : "flex",
flexDirection: "column",
overflow: "hidden",
@@ -989,20 +1002,31 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
minHeight: 0,
overflowY: "auto",
display: "flex",
- flexDirection: "column",
+ flexDirection: "column-reverse",
paddingBottom: "10px",
}}>
{showAnnouncement &&
}
-
-
What can I do for you?
-
- Thanks to the latest breakthroughs in agentic coding capabilities, I can handle complex
- software development tasks step-by-step. With tools that let me create & edit files, explore
- complex projects, use the browser, and execute terminal commands (after you grant
- permission), I can assist you in ways that go beyond code completion or tech support. I can
- even use MCP to create new tools and extend my own capabilities.
-
-
+ {messages.length === 0 && (
+ <>
+
+
+
+
+
+
PearAI Coding Agent
+
+ Powered by Roo Code / Cline
+
+
+
+
+ Autonomous coding agent that has access to your development environment (with
+ your permission) for a feedback loop to add features, fix bugs, and more.
+
+
+
+ >
+ )}
{taskHistory.length > 0 &&
}
)}
@@ -1061,7 +1085,6 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
initialTopMostItemIndex={groupedMessages.length - 1}
/>