From 17461ee52e86071b2ccbcfebc2848737390e594a Mon Sep 17 00:00:00 2001 From: Baldwinzc <56501736+Baldwinzc@users.noreply.github.com> Date: Fri, 31 Jul 2026 18:08:34 +0800 Subject: [PATCH] fix(frontend): parse uploaded filenames containing parentheses (#4608) parseUploadedFiles stopped the filename capture at the first "(" ([^\n(]+), so an entry like "- photo (1).png (12.3 KB)" failed to match and the file silently disappeared from the message's file chips. Browsers produce such names for duplicate downloads, making this a common real-world shape. Anchor the size group on the "( )" pair the backend emits (uploads_middleware formats sizes as "%.1f KB"/"%.1f MB") and let the filename match greedily up to it, so parenthesized filenames parse correctly. --- frontend/src/core/messages/utils.ts | 6 ++++- .../tests/unit/core/messages/utils.test.ts | 25 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/frontend/src/core/messages/utils.ts b/frontend/src/core/messages/utils.ts index 0b0a09517..85ebed41c 100644 --- a/frontend/src/core/messages/utils.ts +++ b/frontend/src/core/messages/utils.ts @@ -845,7 +845,11 @@ export function parseUploadedFiles(content: string): FileInMessage[] { // Parse file list // Format: - filename (size)\n Path: /path/to/file - const fileRegex = /- ([^\n(]+)\s*\(([^)]+)\)\s*\n\s*Path:\s*([^\n]+)/g; + // The filename itself may contain parentheses (e.g. "photo (1).png"), so + // the size group is anchored on the trailing "( )" pair the + // backend emits instead of stopping the filename at the first "(". + const fileRegex = + /- (.+)\s*\(([\d.]+\s*(?:B|KB|MB|GB|TB))\)\s*\n\s*Path:\s*([^\n]+)/gi; const files: FileInMessage[] = []; let fileMatch; diff --git a/frontend/tests/unit/core/messages/utils.test.ts b/frontend/tests/unit/core/messages/utils.test.ts index 5b201794a..027352b42 100644 --- a/frontend/tests/unit/core/messages/utils.test.ts +++ b/frontend/tests/unit/core/messages/utils.test.ts @@ -603,6 +603,31 @@ describe("human message internal context stripping", () => { ]); }); + test("parses uploaded filenames that contain parentheses", () => { + // Browsers name duplicate downloads "photo (1).png"; the backend emits the + // filename verbatim, so the parser must not stop the name at the first "(". + const content = + "\nThe following files were uploaded in this message:\n\n- photo (1).png (12.3 KB)\n Path: /mnt/user-data/uploads/photo (1).png\n- report (final) (2).docx (1.5 MB)\n Path: /mnt/user-data/uploads/report (final) (2).docx\n- normal.pdf (3.0 KB)\n Path: /mnt/user-data/uploads/normal.pdf\n\n\nSummarize"; + + expect(parseUploadedFiles(content)).toEqual([ + { + filename: "photo (1).png", + size: Math.round(12.3 * 1024), + path: "/mnt/user-data/uploads/photo (1).png", + }, + { + filename: "report (final) (2).docx", + size: Math.round(1.5 * 1024 * 1024), + path: "/mnt/user-data/uploads/report (final) (2).docx", + }, + { + filename: "normal.pdf", + size: 3 * 1024, + path: "/mnt/user-data/uploads/normal.pdf", + }, + ]); + }); + test("stripInternalMarkers removes current_uploads blocks on export", () => { const content = "\n- paper.docx (177.6 KB)\n Path: /mnt/user-data/uploads/paper.docx\n\n\nExport me";