From 2f9e6824454d0a6f93b4afda8fb87de999c4e9fb Mon Sep 17 00:00:00 2001 From: MT-Mint <798521692@qq.com> Date: Fri, 3 Apr 2026 13:36:27 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8Dmd=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E4=B8=AD=E5=9B=BE=E7=89=87=E4=B8=8D=E6=98=BE=E7=A4=BA=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../artifacts/artifact-file-detail.tsx | 36 +++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/workspace/artifacts/artifact-file-detail.tsx b/frontend/src/components/workspace/artifacts/artifact-file-detail.tsx index cfd4e5db..2638acbe 100644 --- a/frontend/src/components/workspace/artifacts/artifact-file-detail.tsx +++ b/frontend/src/components/workspace/artifacts/artifact-file-detail.tsx @@ -33,7 +33,7 @@ import { DropdownSelector } from "@/components/ui/dropdown-selector"; import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; import { CodeEditor } from "@/components/workspace/code-editor"; import { useArtifactContent } from "@/core/artifacts/hooks"; -import { urlOfArtifact } from "@/core/artifacts/utils"; +import { resolveArtifactURL, urlOfArtifact } from "@/core/artifacts/utils"; import { useI18n } from "@/core/i18n/hooks"; import { streamdownPlugins } from "@/core/streamdown"; import { checkCodeFile, getFileName } from "@/core/utils/files"; @@ -474,6 +474,7 @@ export function ArtifactFileDetail({ content={displayContent} language={language ?? "text"} zoom={zoom} + threadId={threadId} /> )} @@ -505,12 +506,17 @@ export function ArtifactFilePreview({ content, language, zoom = 100, + threadId, }: { content: string; language: string; zoom?: number; + threadId?: string; }) { const zoomScale = zoom / 100; + const normalizedContent = useMemo(() => { + return rewriteArtifactImagePaths(content ?? "", threadId); + }, [content, threadId]); if (language === "markdown") { return ( @@ -523,7 +529,7 @@ export function ArtifactFilePreview({ {...streamdownPlugins} components={{ a: CitationLink }} > - {content ?? ""} + {normalizedContent} @@ -535,7 +541,7 @@ export function ArtifactFilePreview({ className="size-full" containerClassName="h-full mb-[207px]" title="Artifact preview" - srcDoc={content} + srcDoc={normalizedContent} sandbox="allow-scripts allow-forms" style={{ zoom: zoomScale }} /> @@ -582,6 +588,30 @@ function PreviewIframe({ ); } +function rewriteArtifactImagePaths(content: string, threadId?: string) { + if (!threadId || !/\/?mnt\/user-data\//.test(content)) { + return content; + } + + const markdownRewritten = content.replace( + /!\[([^\]]*)\]\(\s*(\/?mnt\/user-data\/outputs\/[^)\s]+)\s*\)/g, + (_full, alt, rawPath) => { + const normalizedPath = rawPath.startsWith("/") ? rawPath : `/${rawPath}`; + const artifactUrl = resolveArtifactURL(normalizedPath, threadId); + return `![${alt}](${artifactUrl})`; + }, + ); + + return markdownRewritten.replace( + /(]*\bsrc\s*=\s*)(["'])(\/?mnt\/user-data\/outputs\/[^"']+)\2/gi, + (_full, prefix, quote, rawPath) => { + const normalizedPath = rawPath.startsWith("/") ? rawPath : `/${rawPath}`; + const artifactUrl = resolveArtifactURL(normalizedPath, threadId); + return `${prefix}${quote}${artifactUrl}${quote}`; + }, + ); +} + type ArtifactPreviewKind = | "html" | "image"