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 99a90ab46ac..5399b3441f0 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -20,6 +20,7 @@ import { useExit } from "../../context/exit" import { Clipboard } from "../../util/clipboard" import type { FilePart } from "@opencode-ai/sdk/v2" import { TuiEvent } from "../../event" +import { Ide } from "@/ide" import { iife } from "@/util/iife" import { Locale } from "@/util/locale" import { createColors, createFrames } from "../../ui/spinner.ts" @@ -44,7 +45,6 @@ export type PromptRef = { reset(): void blur(): void focus(): void - submit(): void } const PLACEHOLDERS = ["Fix a TODO in the codebase", "What is the tech stack of this project?", "Fix broken tests"] @@ -116,7 +116,7 @@ export function Prompt(props: PromptProps) { const sync = useSync() const dialog = useDialog() const toast = useToast() - const status = createMemo(() => sync.data.session_status?.[props.sessionID ?? ""] ?? { type: "idle" }) + const status = createMemo(() => sync.data.session_status[props.sessionID ?? ""] ?? { type: "idle" }) const history = usePromptHistory() const command = useCommandDialog() const renderer = useRenderer() @@ -312,6 +312,10 @@ export function Prompt(props: PromptProps) { input.insertText(evt.properties.text) }) + sdk.event.on(Ide.Event.SelectionChanged.type, (evt) => { + updateIdeSelection(evt.properties.selection) + }) + createEffect(() => { if (props.disabled) input.cursorColor = theme.backgroundElement if (!props.disabled) input.cursorColor = theme.text @@ -342,6 +346,12 @@ export function Prompt(props: PromptProps) { promptPartTypeId = input.extmarks.registerType("prompt-part") }) + function updateIdeSelection(_selection: Ide.Selection | null) { + // Selection is now displayed in footer via local.selection + // No visual insertion in the input needed + // Content will be included at submit time from local.selection + } + function restoreExtmarksFromParts(parts: PromptInfo["parts"]) { input.extmarks.clear() setStore("extmarkToPartIndex", new Map()) @@ -448,14 +458,11 @@ export function Prompt(props: PromptProps) { }) setStore("extmarkToPartIndex", new Map()) }, - submit() { - submit() - }, }) async function submit() { if (props.disabled) return - if (autocomplete?.visible) return + if (autocomplete.visible) return if (!store.prompt.input) return const trimmed = store.prompt.input.trim() if (trimmed === "exit" || trimmed === "quit" || trimmed === ":q") { @@ -476,6 +483,8 @@ export function Prompt(props: PromptProps) { const messageID = Identifier.ascending("message") let inputText = store.prompt.input + // IDE selection is displayed in footer only - not injected into message + // Expand pasted text inline before submitting const allExtmarks = input.extmarks.getAllForTypeId(promptPartTypeId) const sortedExtmarks = allExtmarks.sort((a: { start: number }, b: { start: number }) => b.start - a.start) @@ -495,9 +504,6 @@ export function Prompt(props: PromptProps) { // Filter out text parts (pasted content) since they're now expanded inline const nonTextParts = store.prompt.parts.filter((part) => part.type !== "text") - // Capture mode before it gets reset - const currentMode = store.mode - if (store.mode === "shell") { sdk.client.session.shell({ sessionID, @@ -539,6 +545,12 @@ export function Prompt(props: PromptProps) { type: "text", text: inputText, }, + ...(local.selection.current()?.text ? [{ + id: Identifier.ascending("part"), + type: "text" as const, + text: `\n\n[IDE Selection: ${local.selection.current()!.filePath.split(/[\/\\]/).pop() || local.selection.current()!.filePath}:${local.selection.current()!.selection.start.line + 1}-${local.selection.current()!.selection.end.line + 1}]\n\`\`\`\n${local.selection.current()!.text}\n\`\`\``, + synthetic: true, + }] : []), ...nonTextParts.map((x) => ({ id: Identifier.ascending("part"), ...x, @@ -546,10 +558,7 @@ export function Prompt(props: PromptProps) { ], }) } - history.append({ - ...store.prompt, - mode: currentMode, - }) + history.append(store.prompt) input.extmarks.clear() setStore("prompt", { input: "", @@ -715,8 +724,8 @@ export function Prompt(props: PromptProps) { >