mirror of
https://github.com/anomalyco/opencode.git
synced 2026-05-28 18:45:31 +00:00
tui: optimistically render submitted prompts
This commit is contained in:
parent
0ba1081cf1
commit
f5c770d65e
2 changed files with 101 additions and 10 deletions
|
|
@ -1178,6 +1178,23 @@ export function Prompt(props: PromptProps) {
|
|||
})),
|
||||
})
|
||||
} else {
|
||||
const parts = [
|
||||
...editorParts,
|
||||
{
|
||||
id: PartID.ascending(),
|
||||
type: "text" as const,
|
||||
text: inputText,
|
||||
},
|
||||
...nonTextParts.map(assign),
|
||||
]
|
||||
sync.session.addOptimisticPrompt({
|
||||
sessionID,
|
||||
messageID,
|
||||
agent: agent.name,
|
||||
model: selectedModel,
|
||||
variant,
|
||||
parts,
|
||||
})
|
||||
sdk.client.session
|
||||
.prompt({
|
||||
sessionID,
|
||||
|
|
@ -1186,17 +1203,9 @@ export function Prompt(props: PromptProps) {
|
|||
agent: agent.name,
|
||||
model: selectedModel,
|
||||
variant,
|
||||
parts: [
|
||||
...editorParts,
|
||||
{
|
||||
id: PartID.ascending(),
|
||||
type: "text",
|
||||
text: inputText,
|
||||
},
|
||||
...nonTextParts.map(assign),
|
||||
],
|
||||
parts,
|
||||
})
|
||||
.catch(() => {})
|
||||
.catch(() => sync.session.removeOptimisticPrompt(sessionID, messageID))
|
||||
if (editorParts.length > 0) editor.markSelectionSent()
|
||||
}
|
||||
history.append({
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@ import type {
|
|||
Provider,
|
||||
Session,
|
||||
Part,
|
||||
TextPartInput,
|
||||
FilePartInput,
|
||||
AgentPartInput,
|
||||
SubtaskPartInput,
|
||||
Config,
|
||||
Todo,
|
||||
Command,
|
||||
|
|
@ -34,6 +38,8 @@ import path from "path"
|
|||
import { useKV } from "./kv"
|
||||
import { aggregateFailures } from "./aggregate-failures"
|
||||
|
||||
type OptimisticPromptPart = (TextPartInput | FilePartInput | AgentPartInput | SubtaskPartInput) & { id: string }
|
||||
|
||||
export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
||||
name: "Sync",
|
||||
init: () => {
|
||||
|
|
@ -518,6 +524,82 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
|||
if (last.role === "user") return "working"
|
||||
return last.time.completed ? "idle" : "working"
|
||||
},
|
||||
addOptimisticPrompt(input: {
|
||||
sessionID: string
|
||||
messageID: string
|
||||
agent: string
|
||||
model: { providerID: string; modelID: string }
|
||||
variant?: string
|
||||
parts: OptimisticPromptPart[]
|
||||
}) {
|
||||
const messages = store.message[input.sessionID]
|
||||
const match = messages ? Binary.search(messages, input.messageID, (m) => m.id) : undefined
|
||||
const info: Message = {
|
||||
id: input.messageID,
|
||||
sessionID: input.sessionID,
|
||||
role: "user",
|
||||
time: { created: Date.now() },
|
||||
agent: input.agent,
|
||||
model: {
|
||||
providerID: input.model.providerID,
|
||||
modelID: input.model.modelID,
|
||||
...(input.variant ? { variant: input.variant } : {}),
|
||||
},
|
||||
}
|
||||
const parts = input.parts.map((part): Part => {
|
||||
const withIDs = {
|
||||
...part,
|
||||
sessionID: input.sessionID,
|
||||
messageID: input.messageID,
|
||||
}
|
||||
if (withIDs.type !== "text") return withIDs
|
||||
return {
|
||||
...withIDs,
|
||||
metadata: {
|
||||
...withIDs.metadata,
|
||||
optimistic: true,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
batch(() => {
|
||||
if (!messages) {
|
||||
setStore("message", input.sessionID, [info])
|
||||
} else if (!match?.found) {
|
||||
setStore(
|
||||
"message",
|
||||
input.sessionID,
|
||||
produce((draft) => {
|
||||
draft.splice(match?.index ?? draft.length, 0, info)
|
||||
}),
|
||||
)
|
||||
}
|
||||
setStore("part", input.messageID, reconcile(parts))
|
||||
})
|
||||
},
|
||||
removeOptimisticPrompt(sessionID: string, messageID: string) {
|
||||
if (!store.part[messageID]?.some((part) => part.type === "text" && part.metadata?.optimistic === true)) return
|
||||
const messages = store.message[sessionID]
|
||||
if (!messages) return
|
||||
const match = Binary.search(messages, messageID, (m) => m.id)
|
||||
batch(() => {
|
||||
if (match.found) {
|
||||
setStore(
|
||||
"message",
|
||||
sessionID,
|
||||
produce((draft) => {
|
||||
draft.splice(match.index, 1)
|
||||
}),
|
||||
)
|
||||
}
|
||||
setStore(
|
||||
"part",
|
||||
produce((draft) => {
|
||||
delete draft[messageID]
|
||||
}),
|
||||
)
|
||||
})
|
||||
},
|
||||
async sync(sessionID: string) {
|
||||
if (fullSyncedSessions.has(sessionID)) return
|
||||
const [session, messages, todo, diff] = await Promise.all([
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue