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 23ae8933d..fd4d395a8 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -17,6 +17,13 @@ import { type AutocompleteRef, Autocomplete } from "./autocomplete" export type PromptProps = { sessionID?: string onSubmit?: () => void + ref?: (ref: PromptRef) => void +} + +export type PromptRef = { + focused: boolean + set(prompt: PromptInfo): void + reset(): void } export function Prompt(props: PromptProps) { @@ -43,6 +50,22 @@ export function Prompt(props: PromptProps) { if (dialog.stack.length > 0) input.blur() }) + props.ref?.({ + get focused() { + return input.focused + }, + set(prompt) { + setStore(prompt) + input.cursorPosition = prompt.input.length + }, + reset() { + setStore({ + input: "", + parts: [], + }) + }, + }) + return ( <> { for await (const event of events.stream) { + console.log(event.type) switch (event.type) { case "todo.updated": setStore("todo", event.properties.sessionID, event.properties.todos) @@ -76,6 +77,20 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ ) break } + case "message.removed": { + const messages = store.message[event.properties.sessionID] + const result = Binary.search(messages, event.properties.messageID, (m) => m.id) + if (result.found) { + setStore( + "message", + event.properties.sessionID, + produce((draft) => { + draft.splice(result.index, 1) + }), + ) + } + break + } case "message.part.updated": { const parts = store.part[event.properties.part.messageID] if (!parts) { @@ -96,6 +111,20 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ ) break } + + case "message.part.removed": { + const parts = store.part[event.properties.messageID] + const result = Binary.search(parts, event.properties.partID, (p) => p.id) + if (result.found) + setStore( + "part", + event.properties.messageID, + produce((draft) => { + draft.splice(result.index, 1) + }), + ) + break + } } } }) diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index 5afbe4340..f9c179abe 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -6,7 +6,7 @@ import { useSync } from "@tui/context/sync" import { SplitBorder } from "@tui/component/border" import { Theme } from "@tui/context/theme" import { BoxRenderable, ScrollBoxRenderable } from "@opentui/core" -import { Prompt } from "@tui/component/prompt" +import { Prompt, type PromptRef } from "@tui/component/prompt" import type { AssistantMessage, Part, ToolPart, UserMessage, TextPart, ReasoningPart } from "@opencode-ai/sdk" import { useLocal } from "@tui/context/local" import { Locale } from "@/util/locale" @@ -31,6 +31,7 @@ import { Header } from "./header" import { parsePatch } from "diff" import { useDialog } from "../../ui/dialog" import { DialogMessage } from "./dialog-message" +import type { PromptInfo } from "../../component/prompt/history" export function Session() { const route = useRouteData("session") @@ -44,9 +45,11 @@ export function Session() { const sdk = useSDK() let scroll: ScrollBoxRenderable + let prompt: PromptRef const keybind = useKeybind() useKeyboard((evt) => { + if (!prompt.focused) return if (keybind.match("messages_page_up", evt)) scroll.scrollBy(-scroll.height / 2) if (keybind.match("messages_page_down", evt)) scroll.scrollBy(scroll.height / 2) }) @@ -132,6 +135,17 @@ export function Session() { messageID: message.id, }, }) + const parts = sync.data.part[message.id] + prompt.set( + parts.reduce( + (agg, part) => { + if (part.type === "text") agg.input += part.text + if (part.type === "file") agg.parts.push(part) + return agg + }, + { input: "", parts: [] as PromptInfo["parts"] }, + ), + ) dialog.clear() }, }, @@ -150,6 +164,7 @@ export function Session() { id: route.sessionID, }, }) + prompt.set({ input: "", parts: [] }) return } sdk.session.revert({ @@ -288,6 +303,7 @@ export function Session() { (prompt = r)} onSubmit={() => { toBottom() }}