diff --git a/bun.lock b/bun.lock index 04da112cf79..8cd5449e72c 100644 --- a/bun.lock +++ b/bun.lock @@ -302,6 +302,7 @@ "@opencode-ai/plugin": "workspace:*", "@opencode-ai/script": "workspace:*", "@opencode-ai/sdk": "workspace:*", + "@opencode-ai/ui": "workspace:*", "@opencode-ai/util": "workspace:*", "@openrouter/ai-sdk-provider": "1.5.4", "@opentui/core": "0.1.79", diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index adfd592f8d0..80c75af06db 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -37,6 +37,7 @@ import { Persist, persisted } from "@/utils/persist" import { usePermission } from "@/context/permission" import { useLanguage } from "@/context/language" import { usePlatform } from "@/context/platform" +import { SINISTER_PLACEHOLDERS as PLACEHOLDERS } from "@opencode-ai/ui/constants/placeholders" import { createTextFragment, getCursorPosition, setCursorPosition, setRangeEdge } from "./prompt-input/editor-dom" import { createPromptAttachments, ACCEPTED_FILE_TYPES } from "./prompt-input/attachments" import { @@ -53,6 +54,15 @@ import { PromptDragOverlay } from "./prompt-input/drag-overlay" import { promptPlaceholder } from "./prompt-input/placeholder" import { ImagePreview } from "@opencode-ai/ui/image-preview" +const ACCEPTED_IMAGE_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"] + +type PendingPrompt = { + abort: AbortController + cleanup: VoidFunction +} + +const pending = new Map() + interface PromptInputProps { class?: string ref?: (el: HTMLDivElement) => void @@ -61,33 +71,7 @@ interface PromptInputProps { onSubmit?: () => void } -const EXAMPLES = [ - "prompt.example.1", - "prompt.example.2", - "prompt.example.3", - "prompt.example.4", - "prompt.example.5", - "prompt.example.6", - "prompt.example.7", - "prompt.example.8", - "prompt.example.9", - "prompt.example.10", - "prompt.example.11", - "prompt.example.12", - "prompt.example.13", - "prompt.example.14", - "prompt.example.15", - "prompt.example.16", - "prompt.example.17", - "prompt.example.18", - "prompt.example.19", - "prompt.example.20", - "prompt.example.21", - "prompt.example.22", - "prompt.example.23", - "prompt.example.24", - "prompt.example.25", -] as const + const NON_EMPTY_TEXT = /[^\s\u200B]/ @@ -228,7 +212,7 @@ export const PromptInput: Component = (props) => { popover: null, historyIndex: -1, savedPrompt: null, - placeholder: Math.floor(Math.random() * EXAMPLES.length), + placeholder: Math.floor(Math.random() * PLACEHOLDERS.length), draggingType: null, mode: "normal", applyingHistory: false, @@ -276,7 +260,7 @@ export const PromptInput: Component = (props) => { promptPlaceholder({ mode: store.mode, commentCount: commentCount(), - example: suggest() ? language.t(EXAMPLES[store.placeholder]) : "", + example: suggest() ? PLACEHOLDERS[store.placeholder] : "", suggest: suggest(), t: (key, params) => language.t(key as Parameters[0], params as never), }), @@ -398,7 +382,7 @@ export const PromptInput: Component = (props) => { if (params.id) return if (!suggest()) return const interval = setInterval(() => { - setStore("placeholder", (prev) => (prev + 1) % EXAMPLES.length) + setStore("placeholder", (prev) => (prev + 1) % PLACEHOLDERS.length) }, 6500) onCleanup(() => clearInterval(interval)) }) diff --git a/packages/opencode/package.json b/packages/opencode/package.json index f8912737375..e96c88b3b12 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -52,6 +52,7 @@ "zod-to-json-schema": "3.24.5" }, "dependencies": { + "@opencode-ai/ui": "workspace:*", "@actions/core": "1.11.1", "@actions/github": "6.0.1", "@agentclientprotocol/sdk": "0.14.1", diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx index d63c248fb83..fea67972fd0 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -55,7 +55,7 @@ export type PromptRef = { submit(): void } -const PLACEHOLDERS = ["Fix a TODO in the codebase", "What is the tech stack of this project?", "Fix broken tests"] +import { SINISTER_PLACEHOLDERS as PLACEHOLDERS } from "@opencode-ai/ui/constants/placeholders" const SHELL_PLACEHOLDERS = ["ls -la", "git status", "pwd"] export function Prompt(props: PromptProps) { @@ -113,6 +113,18 @@ export function Prompt(props: PromptProps) { if (!props.disabled) input.cursorColor = theme.text }) + // Resize textarea when placeholder changes (e.g., when switching sessions or when placeholder index changes) + createEffect(() => { + const placeholderText = props.sessionID ? undefined : PLACEHOLDERS[store.placeholder] + // Track both the placeholder text and sessionID changes + if (input) { + setTimeout(() => { + input.getLayoutNode().markDirty() + renderer.requestRender() + }, 0) + } + }) + const lastUserMessage = createMemo(() => { if (!props.sessionID) return undefined const messages = sync.data.message[props.sessionID] @@ -819,7 +831,11 @@ export function Prompt(props: PromptProps) { flexGrow={1} >