From 74813ff61d97c8dd34bbc45f457c9c060667fde4 Mon Sep 17 00:00:00 2001 From: MT-Mint <798521692@qq.com> Date: Fri, 24 Apr 2026 17:04:51 +0800 Subject: [PATCH] =?UTF-8?q?fix(frontend-workspace):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E5=BC=95=E7=94=A8=E6=BB=9A=E5=8A=A8=E4=B8=8E=E4=BA=A7=E7=89=A9?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/ui/scroll-area.tsx | 2 +- .../artifacts/artifact-file-detail.tsx | 74 ++++++++++++++----- .../src/components/workspace/input-box.tsx | 4 +- 3 files changed, 57 insertions(+), 23 deletions(-) diff --git a/frontend/src/components/ui/scroll-area.tsx b/frontend/src/components/ui/scroll-area.tsx index e38d9e57..904e0471 100644 --- a/frontend/src/components/ui/scroll-area.tsx +++ b/frontend/src/components/ui/scroll-area.tsx @@ -16,7 +16,7 @@ function ScrollArea({ return ( { if (value) { @@ -734,7 +734,7 @@ export function ArtifactFileDetail({ /> )} {isCodeFile && viewMode === "code" && ( -
+
{isLoading && ( -
+
)} @@ -1089,8 +1089,13 @@ function ArtifactPdfPreview({ if (error) { return ( -
-
+
+

{fileName}

{error}

-
+
+
{pageCount > 0 ? t.artifactPreview.pageCountLabel(fileName, pageCount) : fileName}
{isLoading && ( -
+
)} @@ -1313,7 +1323,12 @@ function ArtifactOfficePreview({ }, [canRenderPptx, t.artifactPreview.pptxDownloadHint]); return ( -
+
{canRenderXlsx && sheetNames.length > 0 && (
{sheetNames.map((sheetName) => ( @@ -1357,7 +1372,7 @@ function ArtifactOfficePreview({ /> )} {isLoading && ( -
+
)} @@ -1376,7 +1391,7 @@ function ArtifactPreviewFallback({ }) { const { t } = useI18n(); return ( -
+

{fileName}

{message}

+ path + .split("/") + .map((segment) => { + try { + return encodeURIComponent(decodeURIComponent(segment)); + } catch { + return encodeURIComponent(segment); + } + }) + .join("/"); const toArtifactUrl = (rawPath: string) => { - const normalizedPath = rawPath.startsWith("/") ? rawPath : `/${rawPath}`; - return resolveArtifactURL(normalizedPath, threadId); + const trimmedPath = rawPath.trim(); + const normalizedPath = trimmedPath.startsWith("/") + ? trimmedPath + : `/${trimmedPath}`; + return resolveArtifactURL(encodeVirtualPath(normalizedPath), threadId); }; const toArtifactUrlFromRelative = (rawPath: string) => { const trimmed = rawPath.trim(); @@ -1416,17 +1445,17 @@ function rewriteArtifactImagePaths( const absolutePath = new URL(trimmed, `file://${baseDir}`).pathname; if (!absolutePath.startsWith("/mnt/user-data/")) return null; - return resolveArtifactURL(absolutePath, threadId); + return resolveArtifactURL(encodeVirtualPath(absolutePath), threadId); }; const markdownRewritten = content.replace( - /!\[([^\]]*)\]\(\s*(\/?mnt\/user-data\/outputs\/[^)\s]+)\s*\)/g, + /!\[([^\]]*)\]\(\s*(\/?mnt\/user-data\/(?:outputs|uploads)\/[^)]+?)\s*\)/g, (_full, alt, rawPath) => { return `![${alt}](${toArtifactUrl(rawPath)})`; }, ); const markdownRelativeRewritten = markdownRewritten.replace( - /!\[([^\]]*)\]\(\s*([^) \t]+)\s*\)/g, + /!\[([^\]]*)\]\(\s*([^)]+?)\s*\)/g, (_full, alt, rawPath) => { const absoluteUrl = toArtifactUrlFromRelative(rawPath); if (!absoluteUrl) { @@ -1437,7 +1466,7 @@ function rewriteArtifactImagePaths( ); const shorthandMarkdownRewritten = markdownRelativeRewritten.replace( - /!(?!\[)([^\n()()]+?)\s*[((]\s*(\/?mnt\/user-data\/outputs\/[^)\s)]+)\s*[))]/g, + /!(?!\[)([^\n()()]+?)\s*[((]\s*(\/?mnt\/user-data\/(?:outputs|uploads)\/[^))]+?)\s*[))]/g, (_full, alt, rawPath) => { return `![${String(alt).trim()}](${toArtifactUrl(rawPath)})`; }, @@ -1446,7 +1475,7 @@ function rewriteArtifactImagePaths( return shorthandMarkdownRewritten.replace( /(]*\bsrc\s*=\s*)(["'])([^"']+)\2/gi, (_full, prefix, quote, rawPath) => { - if (/^\/?mnt\/user-data\/outputs\//.test(rawPath)) { + if (/^\/?mnt\/user-data\/(?:outputs|uploads)\//.test(rawPath)) { return `${prefix}${quote}${toArtifactUrl(rawPath)}${quote}`; } const absoluteUrl = toArtifactUrlFromRelative(rawPath); @@ -1736,7 +1765,12 @@ export const ArtifactZoomSelector = ({ viewBox="0 0 16 16" fill="none" > - + - - + + {filteredMentionCandidates.map((candidate, index) => { const detail = [candidate.typeLabel, candidate.pathTail] .filter(Boolean)