This commit is contained in:
Dax Raad 2025-09-28 06:17:44 -04:00
parent 256dead643
commit 7894c3834e
3 changed files with 69 additions and 1 deletions

View file

@ -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 (
<>
<Autocomplete

View file

@ -39,6 +39,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
sdk.event.subscribe().then(async (events) => {
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
}
}
}
})

View file

@ -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() {
</Show>
<box flexShrink={0}>
<Prompt
ref={(r) => (prompt = r)}
onSubmit={() => {
toBottom()
}}