diff --git a/ui/src/pages/chat/chat-view.test.ts b/ui/src/pages/chat/chat-view.test.ts index 4b998361073f..ec575c2cb352 100644 --- a/ui/src/pages/chat/chat-view.test.ts +++ b/ui/src/pages/chat/chat-view.test.ts @@ -5872,11 +5872,11 @@ describe("chat attachment picker", () => { expect(preview.querySelector(".chat-attachment-file__name")?.textContent).toBe("brief.pdf"); }); - it("accepts video file attachments with the generic file preview", async () => { + it("infers video preview glyphs from filenames when MIME is absent", async () => { const onAttachmentsChange = vi.fn(); const container = renderChatView({ onAttachmentsChange }); const input = container.querySelector(".agent-chat__file-input"); - const file = new File(["video"], "clip.mp4", { type: "video/mp4" }); + const file = new File(["video"], "clip.mp4"); expect(input).toBeInstanceOf(HTMLInputElement); expect(input?.accept).toContain("video/*"); @@ -5886,7 +5886,7 @@ describe("chat attachment picker", () => { const attachments = requireFirstAttachmentsChange(onAttachmentsChange); expect(attachments).toHaveLength(1); expect(attachments[0]?.fileName).toBe("clip.mp4"); - expect(attachments[0]?.mimeType).toBe("video/mp4"); + expect(attachments[0]?.mimeType).toBe("application/octet-stream"); expect(attachments[0]?.sizeBytes).toBe(file.size); }); @@ -5894,6 +5894,10 @@ describe("chat attachment picker", () => { const preview = renderChatView({ attachments: nextAttachments }); expect(preview.querySelectorAll(".chat-attachment-thumb--file")).toHaveLength(1); expect(preview.querySelector(".chat-attachment-file__name")?.textContent).toBe("clip.mp4"); + expect(preview.querySelector(".chat-attachment-file__icon")?.getAttribute("data-family")).toBe( + "video", + ); + expect(preview.querySelector(".chat-attachment-file__type")?.textContent).toBe("MP4"); }); }); diff --git a/ui/src/pages/chat/components/chat-attachments.ts b/ui/src/pages/chat/components/chat-attachments.ts index a9e47c0bd1f4..768d35a5d878 100644 --- a/ui/src/pages/chat/components/chat-attachments.ts +++ b/ui/src/pages/chat/components/chat-attachments.ts @@ -17,7 +17,7 @@ import { releaseChatAttachmentPayload, } from "../attachment-payload-store.ts"; import { admitAttachmentFiles } from "./chat-attachment-admission.ts"; -import { renderAttachmentFileIcon } from "./chat-attachment-file-icon.ts"; +import { resolveAttachmentFileIcon } from "./chat-attachment-file-icon.ts"; import { syncChatAttachmentRailScroll } from "./chat-attachment-viewport.ts"; const CHAT_ATTACHMENT_ACCEPT = @@ -195,6 +195,32 @@ function pastedTextPreview(attachment: ChatAttachment): string { ); } +function renderCompactAttachmentFile(attachment: ChatAttachment) { + const resolved = resolveAttachmentFileIcon( + attachment.fileName ?? "attachment", + attachment.mimeType, + ); + const glyph = + resolved.family === "video" + ? icons.play + : resolved.family === "audio" + ? icons.music + : icons.fileText; + return html` + +
+ ${glyph} + + ${attachment.fileName ?? t("chat.attachments.attachedFile")} + ${resolved.extensionLabel} + +
+
+ `; +} + function appendPastedTextToDraft(draft: string, text: string): string { if (!draft.trim()) { return text; @@ -685,13 +711,7 @@ export function renderAttachmentPreview(props: ChatAttachmentControlsProps) { : isLargePastedTextAttachment(att) ? html`
- ${renderAttachmentFileIcon({ - filename: att.fileName ?? "pasted-text.txt", - mimeType: att.mimeType, - mode: "preview-with-favicon", - })} + ${icons.fileText} ${pastedTextPreview(att)}
` - : html` - -
- ${renderAttachmentFileIcon({ - filename: att.fileName ?? "attachment", - mimeType: att.mimeType, - mode: "large-placeholder", - })} - ${att.fileName ?? t("chat.attachments.attachedFile")} -
-
- `} + : renderCompactAttachmentFile(att)}