fix(desktop): render prompt images and patch actions

This commit is contained in:
Brendonovich
2026-08-21 02:49:02 +00:00
parent 960b1ca284
commit 7e5df47700
5 changed files with 45 additions and 5 deletions
@@ -2,6 +2,36 @@ import { expect, test } from "@playwright/test"
import { assistantMessage, setupTimeline, toolPart, userMessage } from "../performance/timeline-stability/fixture"
import { createTwoFilesPatch } from "diff"
test("labels single-file patches by operation", async ({ page }) => {
const cases = [
{ id: "prt_created_patch", file: "src/new.ts", status: "added" as const, title: "Created" },
{ id: "prt_removed_patch", file: "src/old.ts", status: "deleted" as const, title: "Removed" },
{ id: "prt_modified_patch", file: "src/current.ts", status: "modified" as const, title: "Patch" },
]
await setupTimeline(page, {
messages: [
userMessage(),
assistantMessage(
cases.map((item) =>
toolPart(
item.id,
"patch",
"completed",
{ patchText: `Update ${item.file}` },
{ metadata: { files: [patchFile(item.file, item.status)] } },
),
),
),
],
})
for (const item of cases) {
await expect(page.locator(`[data-timeline-part-id="${item.id}"] [data-slot="message-part-title-text"]`)).toHaveText(
item.title,
)
}
})
test("preserves nested patch file state through outer collapse and reopen", async ({ page }) => {
const patchID = "prt_nested_patch"
const files = [patchFile("src/a.ts", "modified"), patchFile("src/b.ts", "added"), patchFile("src/old.ts", "deleted")]
@@ -133,7 +133,7 @@ test.describe("session timeline projection", () => {
})
test("renders user image, file attachment, file reference, and agent reference", async ({ page }) => {
const text = "Use @explore with @src/a.ts and inspect the attachments"
const text = "[Image 1] Use @explore with @src/a.ts and inspect the attachments"
const parts: PartSeed<"user">[] = [
userText(text, { id: "prt_user_rich" }),
{
@@ -142,6 +142,7 @@ test.describe("session timeline projection", () => {
mime: "image/png",
filename: "pixel.png",
url: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
source: { type: "file", path: "pixel.png", text: { value: "[Image 1]", start: 0, end: 9 } },
},
{
id: "prt_user_attachment",
@@ -156,18 +157,19 @@ test.describe("session timeline projection", () => {
mime: "text/plain",
filename: "a.ts",
url: "src/a.ts",
source: { type: "file", path: "src/a.ts", text: { value: "@src/a.ts", start: 18, end: 27 } },
source: { type: "file", path: "src/a.ts", text: { value: "@src/a.ts", start: 28, end: 37 } },
},
{
id: "prt_user_agent",
type: "agent",
name: "explore",
source: { value: "@explore", start: 4, end: 12 },
source: { value: "@explore", start: 14, end: 22 },
},
]
await setupTimeline(page, { messages: [userMessage(parts), assistantMessage()] })
await expect(page.getByAltText("pixel.png")).toBeVisible()
await expect(page.getByText("[Image 1]", { exact: true })).toBeVisible()
await expect(page.getByText("tsconfig.json")).toBeVisible()
await expect(page.getByText("@src/a.ts", { exact: true })).toBeVisible()
await expect(page.getByText("@explore", { exact: true })).toBeVisible()
@@ -206,7 +206,9 @@ export function CurrentUserMessageDisplay(props: {
const dialog = useDialog()
const i18n = useI18n()
const [state, setState] = createStore({ copied: false, reverting: false })
const attachments = createMemo(() => (props.message.files ?? []).filter((file) => !file.mention))
const attachments = createMemo(() =>
(props.message.files ?? []).filter((file) => !file.mention || file.mime.startsWith("image/")),
)
const inlineFiles = createMemo(() => (props.message.files ?? []).filter((file) => !!file.mention))
const agents = createMemo(() => props.message.agents ?? [])
const comments = createMemo(() => props.comments ?? [])
@@ -1397,6 +1397,11 @@ ToolRegistry.register({
if (list.length !== 1) return undefined
return list[0]
})
const title = createMemo(() => {
if (pending() || !single() || single()!.type === "update") return i18n.t("ui.tool.patch")
if (single()?.type === "add") return i18n.t("ui.patch.action.created")
return i18n.t("ui.patch.action.removed")
})
const [expanded, setExpanded] = createSignal<string[]>([])
let seeded = false
@@ -1525,7 +1530,7 @@ ToolRegistry.register({
<div data-slot="message-part-title-area">
<div data-slot="message-part-title">
<span data-slot="message-part-title-text">
<TextShimmer text={i18n.t("ui.tool.patch")} active={pending()} />
<TextShimmer text={title()} active={pending()} />
</span>
<Show when={!pending()}>
<span data-slot="message-part-title-filename">{getFilename(single()!.path)}</span>
+1
View File
@@ -213,6 +213,7 @@ const source = {
"ui.patch.action.deleted": "Deleted",
"ui.patch.action.created": "Created",
"ui.patch.action.removed": "Removed",
"ui.patch.action.moved": "Moved",
"ui.patch.action.patched": "Patched",