From 49101ef0e828ebb92d0150537e7ea31769139107 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=91=A3=E5=AE=8F=E5=87=AF?= Date: Mon, 15 Jun 2026 12:57:18 +0800 Subject: [PATCH] =?UTF-8?q?feat(roundtable):=20=E7=AC=AC=E4=BA=8C/?= =?UTF-8?q?=E4=B8=89=E6=AD=A5=E6=B2=99=E7=AE=B1=20md=20=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E7=94=A8=E6=88=B7=E7=BC=96=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 复用主聊天页同款 ArtifactFileDetail 编辑能力:新增 allowVirtualMarkdownEdit 开关,圆桌沙箱里 write-file 虚拟 md 在流式结束后切回真实 artifacts API (可加载 + PATCH 保存),由 Step2SandboxLayout 与 HighFidelityReport 启用。 主聊天页等其它调用方默认关闭、行为不变。 Co-Authored-By: Claude Opus 4.8 (1M context) --- .../artifacts/artifact-file-detail.tsx | 34 ++++++++++++++++--- .../components/HighFidelityReport.tsx | 1 + .../components/Step2SandboxLayout.tsx | 1 + 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/frontend-web/src/components/workspace/artifacts/artifact-file-detail.tsx b/frontend-web/src/components/workspace/artifacts/artifact-file-detail.tsx index d8e4cfdd..5188d8d5 100644 --- a/frontend-web/src/components/workspace/artifacts/artifact-file-detail.tsx +++ b/frontend-web/src/components/workspace/artifacts/artifact-file-detail.tsx @@ -121,12 +121,19 @@ export function ArtifactFileDetail({ filepath: filepathFromProps, threadId, onClose, + allowVirtualMarkdownEdit = false, }: { className?: string; filepath: string; threadId: string; /** 覆盖默认关闭行为(仅 setOpen(false));圆桌 Step3 文件预览用于退出沙箱模式。 */ onClose?: () => void; + /** + * 圆桌沙箱(Step2/Step3)用:流式结束后,把 `write-file:` 虚拟 md 预览切回真实 + * artifacts API(可加载 + PATCH 保存),从而支持用户编辑(对齐主聊天页 md 可编辑)。 + * 默认 false —— 主聊天页等其它调用方行为不变。 + */ + allowVirtualMarkdownEdit?: boolean; }) { const { t } = useI18n(); const { artifacts, setOpen, select } = useArtifacts(); @@ -141,10 +148,6 @@ export function ArtifactFileDetail({ } return filepathFromProps; }, [filepathFromProps, isWriteFileVirtual]); - /** write_file 流式预览走 tool args 文本;图片等二进制产物始终走 artifacts API。 */ - const useWriteFileStreaming = useMemo(() => { - return isWriteFileVirtual && !isImageFile(filepath); - }, [filepath, isWriteFileVirtual]); const isSkillFile = useMemo(() => { return filepath.endsWith(".skill"); }, [filepath]); @@ -153,6 +156,24 @@ export function ArtifactFileDetail({ const isSpreadsheetFile = useMemo(() => { return /\.xlsx$/i.test(filepath); }, [filepath]); + // 圆桌沙箱:write_file 虚拟预览的 md 在流式结束(thread 非 loading)后,切回真实 + // artifacts API 加载并允许编辑/保存。流式进行中仍走内存 stub 只读预览;仅 + // allowVirtualMarkdownEdit 开启的调用方(Step2/Step3 沙箱)生效,主聊天页不受影响。 + const editVirtualMarkdown = useMemo(() => { + return ( + allowVirtualMarkdownEdit && + isWriteFileVirtual && + !isImageFile(filepath) && + !isMock && + !thread.isLoading && + checkCodeFile(filepath).language === "markdown" + ); + }, [allowVirtualMarkdownEdit, isWriteFileVirtual, filepath, isMock, thread.isLoading]); + /** write_file 流式预览走 tool args 文本;图片等二进制产物始终走 artifacts API; + * 圆桌结束后可编辑的 md(editVirtualMarkdown)亦回到真实 API(可加载 + PATCH 保存)。 */ + const useWriteFileStreaming = useMemo(() => { + return isWriteFileVirtual && !isImageFile(filepath) && !editVirtualMarkdown; + }, [filepath, isWriteFileVirtual, editVirtualMarkdown]); const { isCodeFile, language } = useMemo(() => { if (useWriteFileStreaming) { let nextLanguage = checkCodeFile(filepath).language; @@ -173,7 +194,10 @@ export function ArtifactFileDetail({ }, [language]); const { content, isApplyingEdit } = useArtifactContent({ threadId, - filepath: filepathFromProps, + // editVirtualMarkdown:用解码后的真实路径走 artifacts API(而非 write-file 虚拟 + // URL),这样保存后 useUpdateArtifactContent 写入的查询缓存才能命中,编辑内容 + // 不会被旧的内存 stub 值覆盖回退。 + filepath: editVirtualMarkdown ? filepath : filepathFromProps, enabled: isCodeFile && !useWriteFileStreaming, }); diff --git a/frontend-web/src/roundtable-planning/components/HighFidelityReport.tsx b/frontend-web/src/roundtable-planning/components/HighFidelityReport.tsx index 533f8ba9..f12a0bb5 100644 --- a/frontend-web/src/roundtable-planning/components/HighFidelityReport.tsx +++ b/frontend-web/src/roundtable-planning/components/HighFidelityReport.tsx @@ -1009,6 +1009,7 @@ export const HighFidelityReport: React.FC = ({ filepath={fileSandboxVirtualUrl} threadId={fileSandboxTarget.threadId} onClose={handleCloseFileSandbox} + allowVirtualMarkdownEdit /> )} diff --git a/frontend-web/src/roundtable-planning/components/Step2SandboxLayout.tsx b/frontend-web/src/roundtable-planning/components/Step2SandboxLayout.tsx index 1d69dea8..59585fa9 100644 --- a/frontend-web/src/roundtable-planning/components/Step2SandboxLayout.tsx +++ b/frontend-web/src/roundtable-planning/components/Step2SandboxLayout.tsx @@ -115,6 +115,7 @@ export function Step2SandboxLayout({ filepath={selectedArtifact} threadId={previewThreadId} onClose={artifactsFill && onArtifactsClose ? handleArtifactsClose : undefined} + allowVirtualMarkdownEdit /> ) : (