feat(tui): use canonical prompt attachments

This commit is contained in:
Dax Raad 2026-07-06 14:34:57 -04:00
commit c13f06c30c
24 changed files with 611 additions and 466 deletions

View file

@ -51,7 +51,7 @@ export function Home() {
return
}
if (!args.prompt) return
r.set({ input: args.prompt, parts: [] })
r.set({ text: args.prompt, files: [], agents: [], pasted: [] })
once = true
}
@ -62,7 +62,7 @@ export function Home() {
if (!r) return
if (!sync.ready || !local.model.ready) return
if (!args.prompt) return
if (r.current.input !== args.prompt) return
if (r.current.text !== args.prompt) return
sent = true
r.submit()
})

View file

@ -6,8 +6,7 @@ import { Locale } from "../../util/locale"
import { useSDK } from "../../context/sdk"
import { useRoute } from "../../context/route"
import { useDialog, type DialogContext } from "../../ui/dialog"
import type { PromptInfo } from "../../component/prompt/history"
import { stripPromptPartIDs as strip } from "../../prompt/part"
import { emptyPrompt, type PromptInfo } from "../../component/prompt/history"
export function DialogForkFromTimeline(props: { sessionID: string; onMove: (messageID?: string) => void }) {
const sync = useSync()
@ -53,12 +52,25 @@ export function DialogForkFromTimeline(props: { sessionID: string; onMove: (mess
const prompt = parts.reduce(
(agg, part) => {
if (part.type === "text") {
if (!part.synthetic) agg.input += part.text
if (!part.synthetic) agg.text += part.text
}
if (part.type === "file") {
const files = (agg.files ??= [])
files.push({
uri: part.url,
name: part.filename,
source: part.source?.text
? {
start: part.source.text.start,
end: part.source.text.end,
text: part.source.text.value,
}
: undefined,
})
}
if (part.type === "file") agg.parts.push(strip(part))
return agg
},
{ input: "", parts: [] as PromptInfo["parts"] },
emptyPrompt() as PromptInfo,
)
route.navigate({
sessionID: forked.data!.id,

View file

@ -1420,11 +1420,11 @@ function UserMessage(props: { message: SessionMessageUser }) {
<box flexDirection="row" paddingBottom={metadataVisible() ? 1 : 0} paddingTop={1} gap={1} flexWrap="wrap">
<For each={files()}>
{(file) => {
const directory = file.mime === "application/x-directory"
const label = file.mime === "application/x-directory" ? "Directory" : file.mime
return (
<text fg={theme.text}>
<span style={{ bg: theme.secondary, fg: theme.background }}>
{directory ? " Directory " : " File "}
{` ${label} `}
</span>
<span style={{ bg: theme.backgroundElement, fg: theme.textMuted }}>
{" "}