fix(web-shell): preview document-classified artifacts - #9760
Conversation
E2E test reportTested the final branch after rebasing onto the latest upstream main.
The pre-fix implementation was independently replayed in a temporary tracked-tree copy: the new Markdown/HTML behavior tests failed because the files were routed to download-only, while an image-classified SVG fell through to source preview. No user workspace sample files were modified or included. |
|
✅ Qwen Triage finished — CI landed green on ✅ Qwen Triage 已完成 —— |
|
Thanks for the PR! Template looks good ✓ Problem: This is an observed routing bug, not a theoretical one. On Direction: Aligned — this restores the format-specific previews Web Shell already has (Markdown renderer, CSP-sandboxed HTML iframe, image viewer) instead of replacing them with a download card, and the fail-closed treatment of SVG, Office, PDF, and media formats matches the existing download-only policy. CHANGELOG (claude-code): no direct reference, but the area is Web Shell's own artifact-preview surface. Size: Not applicable — no core paths. All 4 files are in Approach: The scope feels right. The new precedence — download-only extensions and SVG first, then recognized previewable path/MIME, then the broad kind — is the minimal change that fixes the misrouting without adding any new renderer, and reusing Risk: No elevated risk signals — no high-risk paths matched. The widened HTML preview surface is the pre-existing Moving on to code review. 🔍 中文说明感谢贡献! 模板完整 ✓ 问题:这是一个已观测到的路由 bug,不是理论问题。在 方向:对齐——本 PR 恢复 Web Shell 已有的格式专用预览(Markdown 渲染器、CSP 沙箱 HTML iframe、图片查看器),而不是用下载卡片替换它们;SVG、Office、PDF、媒体格式的安全失败(仅下载)处理与现有仅下载策略一致。CHANGELOG(claude-code):无直接引用,但该领域属于 Web Shell 自身的 artifact 预览面。 规模:不适用——不涉及核心路径。4 个文件均在 方案:范围合理。新的优先级——仅下载扩展名与 SVG 优先,其次可识别的可预览路径/MIME,最后才是宽泛 kind——是修复误路由的最小改动,且复用 风险:无升级风险信号——未命中高风险路径。被拓宽的 HTML 预览面是既有的 进入代码审查 🔍 — Qwen Code · qwen3.8-max Reviewed at |
chiga0
left a comment
There was a problem hiding this comment.
No blocking findings. Posting as COMMENT because Test (ubuntu-latest, Node 22.x) is still pending — will approve once it lands green.
What this changes:
The rewrite of isDownloadOnlyWorkspaceArtifact establishes a clear priority order:
- SVG (by extension or MIME) + known download-only extensions → always download — extension-based, cannot be overridden by MIME metadata.
- Recognized image formats, Markdown, and HTML → always preview — overrides the broad
documentkind that was previously blocking these. - Broad kind classifiers (
image/document/pdf/video/audio) → download — fallback for unrecognized formats. - Everything else → not download-only (extensionless, unknown MIME).
Checked:
- DOWNLOAD_ONLY_EXTENSIONS supersedes
isOfficeDocumentPath:DOWNLOAD_ONLY_EXTENSIONS = [...OFFICE_DOCUMENT_EXTENSIONS, '.pdf', '.mp4', …]is a strict superset, so the removal ofisOfficeDocumentPathfrom the kind-fallback block loses no coverage. - SVG safeguard is bidirectional:
{path:'graphic.svg', mimeType:'text/html'}→ extension fires at Priority 1.{path:'safe.png', mimeType:'image/svg+xml'}→ MIME fires at Priority 1. Neither can be overridden by the other side. - Conflict tests match code:
report.docx + text/html MIME→.docxinDOWNLOAD_ONLY_EXTENSIONS→ download-only before the previewable MIME check. Same for.xlsx,.pdf,.mp4. ✓ - MIME parameter stripping:
normalizeArtifactMimeType('text/html; charset=utf-8')→'text/html'. BothisHtmlArtifactandisMarkdownArtifactnow handle charset params — the oldartifact.mimeType?.toLowerCase() === 'text/markdown'silently failed for parameterized MIME. ✓ kind='image'change: previously all image artifacts bypassed download-only. Now only recognized image formats (avif/bmp/gif/ico/jpeg/png/webp — both by MIME and by path extension) reach Priority 2'sfalse; unrecognized formats fall to Priority 3 → download-only. Conservative and intentional.- ArtifactPanel.tsx caller (line 2432): passes a full
DaemonSessionArtifactwhich includesmimeType— the new parameter is wired through. ✓ - Test non-vacuity: old code returned
trueforkind==='document'unconditionally, so the newpreviews document-classified Markdown/HTMLtests would fail at thereadWorkspaceFileassertion with the old implementation. ✓
Not covered: macOS and Windows CI SKIPPING (fork-PR pattern). The changed logic is pure in-memory classification with no platform-dependent paths.
Reviewed with AI assistance.
Code reviewReviewed against
No critical issues. Two non-blocking notes:
Test evidence (PR's own CI via API — this unattended run never executes PR code)Final CI results for
One row per check name (latest run); skipped checks omitted; failures sort first. / 每个检查名一行(取最新一次运行),省略 skipped,失败项排在最前。 No failures so far. The two checks that matter most for this PR — the unit suite and the web-shell visual capture — are still running; the Sandboxed verification would settle what CI cannot: 中文说明代码审查对照
无严重问题。两条非阻塞备注:
测试证据(PR 自身 CI,通过 API 获取——无人值守运行绝不执行 PR 代码)目前无失败(表格见上方英文部分)。对本 PR 最关键的两项检查——单元测试套件与 web-shell 视觉捕获——仍在运行; 沙箱验证可以补足 CI 无法覆盖的部分: — Qwen Code · qwen3.8-max Reviewed at |
|
Confidence: 4/5 — a small, correct, fail-closed routing fix with test coverage on both directions of the new precedence; only nits are the dead Stepping back: I would have written this almost exactly the same way. The precedence order — download-only formats first, then recognized previewable formats, then the broad kind — is the minimal change that fixes the misrouting, and reusing Approval is deferred until CI lands green on 中文说明置信度:4/5 —— 一个小而正确、安全失败(fail-closed)的路由修复,对新优先级的两个方向都有测试覆盖;仅有的小瑕疵是成为死代码的 退一步看:我自己大概也会写成几乎一样的样子。优先级顺序——仅下载格式优先,其次是可识别的可预览格式,最后才是宽泛 kind——是修复误路由的最小改动;复用 审批推迟到 CI 在 — Qwen Code · qwen3.8-max Reviewed at |
ytahdn
left a comment
There was a problem hiding this comment.
静态/diff 审查,head bc52018(未本地运行测试)。
中文:
总体结论:未发现阻塞性问题。isDownloadOnlyWorkspaceArtifact 重写后的三层判定顺序清晰、fail-closed 在前,charset 归一化在路由与渲染器两侧一致落地。以下两条为确认性/清理性备注。
💡 isOfficeDocumentPath 在生产代码中已无调用点
新的第一层判定用 DOWNLOAD_ONLY_EXTENSIONS(本身即 OFFICE_DOCUMENT_EXTENSIONS 的严格超集,另含 .pdf 与媒体扩展)取代了原先 kind 兜底分支里的 isOfficeDocumentPath(...) 调用,覆盖面没有损失——这一点与测试矩阵中 report.docx + text/html、report.xlsx + image/png 等"扩展名压制可预览 MIME"的用例一致。但该函数目前只剩 artifactUtils.test.ts 引用它;若不打算保留为公共工具,可在后续顺手清理或注明保留原因。非阻塞。
💡 下载路径保留原始 MIME(确认,无需修改)
DownloadableWorkspaceArtifact 的下载流程把未归一化的 artifact.mimeType 原样传给 downloadWorkspaceFile,后者仅将其用作 new Blob(chunks, { type: mimeType }) 的类型。这是合理的:Blob type 允许携带 charset 等参数,而所有路由/渲染判定(isDownloadOnlyWorkspaceArtifact、isHtmlArtifact、isMarkdownArtifact、getArtifactImageMimeType)均已统一走 normalizeArtifactMimeType。PR 描述中"MIME 参数在下载路由与渲染器选择中被一致归一化"的说法经核实成立。
🎉 值得肯定的点
- 冲突优先级设计:
.svg扩展名 /image/svg+xmlMIME / 下载类扩展名置于第一层,任何可预览 MIME 都无法反向覆盖(graphic.svg + text/html、image kind + graphic.svg + image/png等六行冲突用例两个方向都钉住了)。 - 行为收敛是有意的:
kind: 'image'但 MIME/扩展名均不在白名单时,由旧实现的降级预览收敛为仅下载,与 SVG 策略一致,保守且合理。 getArtifactImageMimeType参数收窄为Pick<DaemonSessionArtifact, 'mimeType' | 'workspacePath'>,纯派生、无副作用。- 渲染器一致性:ArtifactPanel 的 download-only 闸门先于
previewKind选择执行,冲突 MIME 到不了文本/图片渲染器;新增的 charset MIME 预览测试(text/markdown; charset=utf-8等)与既有 mock 结构一致,且在旧实现下会于readWorkspaceFile断言处失败,非空转测试。
CI 状态:Test (ubuntu-latest, Node 22.x) 与 web-shell 视觉捕获仍在运行,Desktop Shell 双平台已通过,macOS/Windows 测试按 fork 惯例跳过(改动为纯内存分类逻辑,无平台相关路径)。待 CI 全绿后可按惯例批准。此前一轮机器辅助审查亦未发现阻塞项。
English:
Static/diff-based review at head bc52018 (tests not run locally).
Overall: no blocking findings. The three-tier rewrite of isDownloadOnlyWorkspaceArtifact has a clear, fail-closed-first ordering, and charset normalization lands consistently on both the routing and renderer sides. Two confirmatory/cleanup notes follow.
💡 isOfficeDocumentPath has no remaining production callers
The new first tier uses DOWNLOAD_ONLY_EXTENSIONS (a strict superset of OFFICE_DOCUMENT_EXTENSIONS, plus .pdf and the media extensions) in place of the old isOfficeDocumentPath(...) call in the kind-fallback branch, so no coverage is lost — consistent with the "extension overrides previewable MIME" test rows such as report.docx + text/html and report.xlsx + image/png. The function itself is now referenced only by artifactUtils.test.ts; if it isn't being kept as a shared utility, it could be cleaned up or annotated in a follow-up. Non-blocking.
💡 Download path keeps the raw MIME (confirmed, no change needed)
The DownloadableWorkspaceArtifact download flow passes the unnormalized artifact.mimeType straight to downloadWorkspaceFile, which uses it only as the new Blob(chunks, { type: mimeType }) type. That is fine: Blob types may carry parameters such as charset, while every routing/renderer decision (isDownloadOnlyWorkspaceArtifact, isHtmlArtifact, isMarkdownArtifact, getArtifactImageMimeType) now goes through normalizeArtifactMimeType. The PR description's claim that MIME parameters are normalized consistently through download routing and renderer selection holds up on inspection.
🎉 Highlights
- Conflict precedence:
.svgextension /image/svg+xmlMIME / download-only extensions sit in tier one, so no previewable MIME can override them — the six SVG conflict rows and four extension-over-MIME rows lock down both directions. - Intentional narrowing:
kind: 'image'artifacts whose MIME/extension are both unrecognized now fall to download-only instead of the old degraded preview — conservative and consistent with the SVG posture. getArtifactImageMimeTypenarrowed toPick<DaemonSessionArtifact, 'mimeType' | 'workspacePath'>— pure derivation, no side effects.- Renderer consistency: the ArtifactPanel download-only gate runs before
previewKindselection, so conflicting MIME can never reach the text/image renderers; the new charset-MIME preview tests match the existing mock structure and would fail at thereadWorkspaceFileassertion under the old implementation, so they are not vacuous.
CI: Test (ubuntu-latest, Node 22.x) and the web-shell visual capture are still running; Desktop Shell passed on both platforms, and macOS/Windows tests are skipped per the fork convention (the change is pure in-memory classification with no platform-dependent paths). Once CI is green this can be approved as usual; a prior AI-assisted round also found no blockers.
🖼️ web-shell visual previewRendered against a mock daemon (no real backend): the PR base vs this PR head Screenshots · before / afterFull-resolution recordings (.webm) are attached to the workflow run. — Qwen Code · web-shell visuals |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship — CI landed green after the review. ✅
✅ Maintainer local verification: merge-ready147/147 executed assertions passed at 中文摘要结论:建议合并。 使用精确 base 未发现阻塞问题。Web Shell 生产构建、TypeScript typecheck、ESLint 和 Central claimWeb Shell must recover existing Markdown, sandboxed HTML, and safe raster-image previews when an artifact is broadly classified as A/B load-bearing proofThe identical PR test specifications were run against the exact base production source and the PR head production source. On the base arm only the two changed test files were overlaid; the implementation remained at the base OID.
The 17 base-only failures are load-bearing: 10 Markdown/HTML routing assertions, 2 raster-image routing assertions, and 5 SVG fail-closed assertions. No unrelated test in the two focused files regressed on head. Independent routing matrixA separate harness imported the real
The head safety boundary includes 44/44 conflict assertions: 22 Office/PDF/audio/video extensions × FindingsNo blocking or non-blocking correctness findings. Targeted gates
The first full Not covered
MethodologyGitHub metadata resolved base |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed — no blockers. Suggestions are inline.
中文说明
已审查——无阻断问题。 建议见行内评论。
— qwen3.8-max via Qwen Code /review (v0.22.0)
| mimeType === 'text/markdown' || | ||
| mimeType === 'text/html' |
There was a problem hiding this comment.
[Suggestion] The MIME normalization this PR introduces stops at the panel: isRenderedArtifact in TurnOutputs.tsx (~673) still compares the raw artifact.mimeType?.toLowerCase() against 'text/markdown'/'text/html' without stripping parameters, so it now disagrees with the normalized predicates here and in isHtmlArtifact/isMarkdownArtifact for exactly the artifacts this PR makes previewable. A document artifact with mimeType: 'text/markdown; charset=utf-8' and an extensionless path (reports/preview — the exact shape of this PR's own new test) renders as Markdown in the panel, but getArtifactPreviewContent returns undefined for it, so the turn's captured-content fast path is dropped: it first paints the "Loading preview..." placeholder instead of the instantly-rendered captured content, and if the workspace file was removed after the turn, the plain-MIME twin still shows the captured content while the charset variant shows only a load error (verified by probe at this commit: CHARSET getArtifactPreviewContent => undefined vs PLAIN => "# Captured"; with the fix below the charset variant returns the captured content, and TurnOutputs tests stay 19/19 green). Format classification now lives in three unsynchronized copies across two files; at minimum route the TurnOutputs comparison through the new helper (the file already imports from ./artifactUtils):
// packages/web-shell/client/components/artifacts/TurnOutputs.tsx — isRenderedArtifact
const mimeType = normalizeArtifactMimeType(artifact.mimeType);Ideally extract one shared preview-format classifier in artifactUtils.ts consumed by the gate, the panel's previewKind computation, and TurnOutputs.
中文说明
本 PR 引入的 MIME 规范化止步于面板:TurnOutputs.tsx(约 673 行)中的 isRenderedArtifact 仍然用原始的 artifact.mimeType?.toLowerCase() 与 'text/markdown'/'text/html' 比较,没有去掉参数,因此对于本 PR 恰好变为可预览的那些 artifact,它与此处以及 isHtmlArtifact/isMarkdownArtifact 中已规范化的判断出现了分歧。一个 mimeType: 'text/markdown; charset=utf-8'、无扩展名路径(reports/preview —— 正是本 PR 新增测试所用的形态)的 document artifact 在面板中能渲染为 Markdown,但 getArtifactPreviewContent 对它返回 undefined,导致 turn 快照内容的快速路径被丢弃:首屏只能显示 "Loading preview..." 占位而不是立即渲染的快照内容;如果 workspace 文件在 turn 之后被删除,纯 MIME 的同类 artifact 仍能显示快照内容,而带 charset 的变体只显示加载错误(已在本提交上用探针验证:CHARSET getArtifactPreviewContent => undefined,而 PLAIN => "# Captured";应用下面的修复后,charset 变体也能返回快照内容,TurnOutputs 测试保持 19/19 全绿)。格式分类现在有两份文件中的三处不同步副本;至少应让 TurnOutputs 的比较走新的辅助函数(该文件已从 ./artifactUtils 导入)。更理想的做法是在 artifactUtils.ts 中提取一个共享的可预览格式分类器,由 gate、面板的 previewKind 计算和 TurnOutputs 共同使用。
— qwen3.8-max via Qwen Code /review (v0.22.0)
| extension === '.svg' || | ||
| mimeType === 'image/svg+xml' || | ||
| DOWNLOAD_ONLY_EXTENSIONS.has(extension) |
There was a problem hiding this comment.
[Suggestion] This diff removed the only production caller of the exported isOfficeDocumentPath — the gate now reads DOWNLOAD_ONLY_EXTENSIONS directly (a superset of the office set), and a repo-wide grep finds the helper referenced only by its definition and its own test. It is dead production code that still looks authoritative (the adjacent "Keep in sync" comment), so a future office-policy edit to isOfficeDocumentPath would be a silent no-op against the gate, with the test giving false comfort that the API is in use. Consider deleting isOfficeDocumentPath and its test assertion in this same change (OFFICE_DOCUMENT_EXTENSIONS stays — DOWNLOAD_ONLY_EXTENSIONS still uses it), or routing the gate through the helper if you want to keep it.
中文说明
本 diff 移除了导出函数 isOfficeDocumentPath 唯一的生产调用方——现在 gate 直接读取 DOWNLOAD_ONLY_EXTENSIONS(它是 office 集合的超集),全仓 grep 显示该辅助函数只被其定义和自身测试引用。它是看起来仍像权威接口(旁边还有 "Keep in sync" 注释)的死代码:未来对 isOfficeDocumentPath 的 office 策略修改对 gate 将是静默无效的操作,而其测试会给人一种该 API 仍在使用的虚假安慰。建议在本次改动中一并删除 isOfficeDocumentPath 及其测试断言(OFFICE_DOCUMENT_EXTENSIONS 保留——DOWNLOAD_ONLY_EXTENSIONS 仍在使用它),或者如果想保留它,就让 gate 经由该辅助函数判断。
— qwen3.8-max via Qwen Code /review (v0.22.0)
| artifact.kind === 'image' || | ||
| artifact.kind === 'document' || | ||
| artifact.kind === 'pdf' || | ||
| artifact.kind === 'video' || | ||
| artifact.kind === 'audio' || | ||
| isOfficeDocumentPath(artifact.workspacePath) | ||
| artifact.kind === 'audio' |
There was a problem hiding this comment.
[Suggestion] The kind-fallback branch has no effective test: every kind-image row in the new SVG cases also carries a .svg extension (decided earlier by the extension check), every kind-document row resolves via the previewable MIME/extension check above, and the one component-level download-only test uses reports/q3.xlsx, caught by DOWNLOAD_ONLY_EXTENSIONS first. Deleting artifact.kind === 'image' || or artifact.kind === 'document' || leaves the whole artifact suite green — verified by mutation at this commit (91/91 pass with each mutant, and probes confirm both are live: {kind: 'image', workspacePath: 'photo.heic'} and {kind: 'document', workspacePath: 'notes.txt'} flip true → false). If one of those lines ever drops, such artifacts silently flip from download-only to a raw source preview. Rows decided by kind alone would pin the branch:
['image', 'photo.heic', undefined],
['image', 'photo', undefined],
['document', 'notes.txt', undefined],中文说明
kind 兜底分支没有有效的测试:新增 SVG 用例中所有 kind 为 image 的行都带有 .svg 扩展名(已被前面的扩展名检查判定),所有 kind 为 document 的行都经由上面的可预览 MIME/扩展名检查得出结果,而唯一的组件级仅下载测试使用 reports/q3.xlsx,会先被 DOWNLOAD_ONLY_EXTENSIONS 命中。删除 artifact.kind === 'image' || 或 artifact.kind === 'document' || 后,整个 artifact 测试套件仍然全绿——已在本提交上做变异验证(每个变异体下 91/91 全部通过,且探针确认两者都是活变异体:{kind: 'image', workspacePath: 'photo.heic'} 和 {kind: 'document', workspacePath: 'notes.txt'} 会由 true 翻转为 false)。一旦其中某行被误删,这类 artifact 会静默地从仅下载变成原始 source 预览。增加仅由 kind 判定的用例行即可钉住该分支。
— qwen3.8-max via Qwen Code /review (v0.22.0)
| getImageMimeTypeFromPath, | ||
| getReviewDownloadMimeType, | ||
| isDownloadOnlyWorkspaceArtifact, | ||
| normalizeArtifactMimeType, |
There was a problem hiding this comment.
[Suggestion] AttachmentBlobPreview in this same file (~2756) still computes its MIME type with an inline copy of the strip-parameters/trim/lowercase pipeline this PR consolidates into normalizeArtifactMimeType — after this change a repo-wide grep finds exactly two normalization sites: the new helper and that inline copy. This PR exists because two divergent normalizations routed the same artifact differently; leaving the attachment copy inline means the next normalization change can miss it and the same drift class reappears for attachment previews. The existing fallback composes cleanly with the helper:
const resolvedMimeType =
normalizeArtifactMimeType(mimeType || data.type) || 'application/octet-stream';中文说明
同文件中的 AttachmentBlobPreview(约 2756 行)仍在用一份内联的"去参数/trim/小写"流水线计算 MIME 类型,而本 PR 已将该流水线收敛为 normalizeArtifactMimeType——改动后全仓 grep 只有两处规范化:新的辅助函数和那份内联副本。本 PR 的起因正是两种不一致的规范化把同一个 artifact 路由到了不同结果;保留内联副本意味着下次修改规范化时可能漏掉它,同样的漂移问题会在附件预览上重演。现有的兜底值可以与辅助函数自然组合。
— qwen3.8-max via Qwen Code /review (v0.22.0)
|
Released in v0.22.2. |


What this PR does
Web Shell now recognizes Markdown, HTML, and safe raster images from their workspace path or normalized MIME type before applying a generic
documentclassification. MIME parameters such ascharset=utf-8are normalized consistently through both download routing and the final renderer selection. SVG remains download-only, and known Office, PDF, video, and audio extensions take precedence over conflicting previewable MIME metadata.Why it's needed
Generated artifacts can be recorded with the broad
documentkind even when their actual file is Markdown, HTML, or a raster image. The previous kind-first routing replaced those supported previews with a download-only card. This restores the existing format-specific previews while keeping active SVG content and binary document or media formats on the safer download path.Reviewer Test Plan
How to verify
Open workspace artifacts reported as
documentfor.md,.markdown,.html,.htm, and PNG files and confirm they render as Markdown, sandboxed HTML, and an image respectively. Repeat with extensionless Markdown, HTML, and PNG artifacts whose MIME types include parameters such ascharset=utf-8, and confirm they use the same previews. Open SVG, DOCX, XLSX, PDF, and MP4 artifacts, including cases where their MIME metadata claims a previewable text or image type, and confirm they remain download-only without reading the workspace file into a text or image renderer.Evidence (Before & After)
Before: supported Markdown, HTML, and raster files reported as
documentdisplayed only the download detail. SVG reported asimagecould fall through to a source preview, and a previewable conflicting MIME type could override a download-only extension.After: supported text and raster formats use their existing previews; SVG and known binary document/media extensions fail closed to the download detail. The new behavior tests fail against the previous
mainimplementation and pass with this change.Tested on
Environment (optional)
Node.js 22; Web Shell Vitest tests, production build, and TypeScript typecheck.
Risk & Scope
documentkind to force download-only behavior should instead use a download-only file extension; supported text and raster formats now follow their actual path or normalized MIME type.Linked Issues
N/A
中文说明
本 PR 做了什么
Web Shell 现在会先根据 workspace 路径或规范化后的 MIME 类型识别 Markdown、HTML 和安全栅格图片,再处理宽泛的
document分类。下载路由与最终渲染器选择都会统一规范化charset=utf-8等 MIME 参数。SVG 仍然仅下载,已知的 Office、PDF、视频和音频扩展名优先于冲突的可预览 MIME 元数据。为什么需要
生成的 artifact 即使实际是 Markdown、HTML 或栅格图片,也可能被记录为宽泛的
documentkind。此前按 kind 优先的路由会用仅下载卡片替换这些已支持的预览。本次修改恢复已有的格式专用预览,同时让包含主动内容的 SVG 以及二进制文档和媒体格式继续走更安全的下载路径。Reviewer Test Plan
如何验证
打开被报告为
document的.md、.markdown、.html、.htm和 PNG workspace artifact,确认它们分别渲染为 Markdown、沙箱 HTML 和图片。再使用无扩展名且 MIME 带有charset=utf-8等参数的 Markdown、HTML 和 PNG artifact,确认使用相同预览。打开 SVG、DOCX、XLSX、PDF 和 MP4 artifact,包括 MIME 元数据伪装成可预览文本或图片的情况,确认它们保持仅下载,并且不会把 workspace 文件读入文本或图片渲染器。证据(修改前后)
修改前:被报告为
document的 Markdown、HTML 和栅格文件只显示下载详情。被报告为image的 SVG 可能落入源码预览,可预览的冲突 MIME 还可能覆盖仅下载扩展名。修改后:支持的文本和栅格格式使用已有预览;SVG 与已知二进制文档和媒体扩展名会安全地进入下载详情。新增行为测试在旧
main实现上失败,在本次修改后通过。测试平台
环境(可选)
Node.js 22;Web Shell Vitest 测试、生产构建和 TypeScript 类型检查。
风险与范围
documentkind 强制仅下载,应改用下载专用扩展名;受支持的文本和栅格格式现在会按照实际路径或规范化 MIME 类型处理。关联 Issue
无