From 3cb142ff2e719605851b64ff4aeb0ba8c157bad8 Mon Sep 17 00:00:00 2001 From: Alec Hrdina Date: Sat, 18 Apr 2026 00:39:10 -0500 Subject: [PATCH] fix(ui): fall back for generic transcript image MIME --- ui/src/ui/chat/grouped-render.ts | 8 +++++++- ui/src/ui/views/chat.test.ts | 28 ++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/ui/src/ui/chat/grouped-render.ts b/ui/src/ui/chat/grouped-render.ts index a03467ec5f1..d4f2be30e46 100644 --- a/ui/src/ui/chat/grouped-render.ts +++ b/ui/src/ui/chat/grouped-render.ts @@ -83,7 +83,13 @@ function getFileExtension(url: string): string | undefined { function isImageTranscriptMediaPath(path: string, mediaType: unknown): boolean { if (typeof mediaType === "string" && mediaType.trim()) { - return mediaType.trim().toLowerCase().startsWith("image/"); + const normalized = mediaType.trim().toLowerCase(); + if (normalized.startsWith("image/")) { + return true; + } + if (normalized !== "application/octet-stream") { + return false; + } } const ext = getFileExtension(path); return ( diff --git a/ui/src/ui/views/chat.test.ts b/ui/src/ui/views/chat.test.ts index 0fa2b25c505..aedc309faf9 100644 --- a/ui/src/ui/views/chat.test.ts +++ b/ui/src/ui/views/chat.test.ts @@ -946,6 +946,34 @@ describe("chat view", () => { ); }); + it("keeps transcript images visible when MIME falls back to application/octet-stream", () => { + const container = document.createElement("div"); + + renderGroupedMessage( + container, + { + id: "user-history-image-octet-stream", + role: "user", + content: "", + MediaPath: "/tmp/openclaw/user-upload.png", + MediaType: "application/octet-stream", + timestamp: Date.now(), + }, + "user", + { + showToolCalls: false, + basePath: "/openclaw", + assistantAttachmentAuthToken: "session-token", + localMediaPreviewRoots: ["/tmp/openclaw"], + }, + ); + + const image = container.querySelector(".chat-message-image"); + expect(image?.getAttribute("src")).toBe( + "/openclaw/__openclaw__/assistant-media?source=%2Ftmp%2Fopenclaw%2Fuser-upload.png&token=session-token", + ); + }); + it("skips non-image transcript media paths after history reload", () => { const container = document.createElement("div");