From ff276eb9b3d11b4529f70c398bbf1dd0dc927367 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 30 Jun 2026 08:28:11 +0800 Subject: [PATCH] =?UTF-8?q?fix(web):=20SVG=20=E9=A2=84=E8=A7=88=E5=BC=BA?= =?UTF-8?q?=E5=88=B6=20image/svg+xml(=E5=89=8D=E7=AB=AF=20blob=20mime=20+?= =?UTF-8?q?=20=E5=90=8E=E7=AB=AF=20download)(bump=200.33.1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SVG 在 里必须 Content-Type=image/svg+xml 才渲染。前端 preview.js 的 _showImage / mini 图片分支据扩展名强制 blob mime;后端 download 接口对 .svg 显式回 image/svg+xml(部分部署环境 mimetypes 未注册 svg → FileResponse 会猜成 octet-stream → 不显示)。双保险。 Co-Authored-By: Claude Opus 4.8 (1M context) --- PROGRESS.md | 5 ++++- core/__init__.py | 2 +- web/app.py | 6 ++++++ web/static/js/preview.js | 9 +++++++-- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/PROGRESS.md b/PROGRESS.md index 2160f9b..3ada729 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -2,7 +2,7 @@ > 配合 `DESIGN.md`。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 1-2 句:做了啥 + 关键判断;细节查 `git log` / `git diff` / `DESIGN §7.9`。 -最后更新:2026-06-29(ppt skill 重构为 SVG-first,移植 ppt-master + bump 0.33.0) +最后更新:2026-06-30(web 端 SVG 预览修复:强制 image/svg+xml + bump 0.33.1) --- @@ -21,6 +21,9 @@ ## 已完成关键能力 +### 2026-06-30 / 修复 web 端 SVG 无法预览(bump 0.33.1) +SVG 在 `` 里必须 Content-Type=`image/svg+xml` 才渲染。前端 `preview.js` 的 `_showImage` / mini 图片分支据扩展名强制 blob mime(与服务端响应头无关);后端 `download` 接口对 `.svg` 显式回 `image/svg+xml`(部分部署环境 mimetypes 未注册 svg → 会被 FileResponse 猜成 octet-stream)。双保险。 + ### 2026-06-29 / ppt skill 清空重构为 SVG-first(移植 ppt-master,bump 0.33.0) - 背景:旧 ppt skill 用 python-pptx + 固定组合版式件(`add_card_grid` 等),版面被 helper 框死 → 单调、AI 味重,是架构天花板,调参救不了。用户要求"清空重做,参考 github ppt-master"。 diff --git a/core/__init__.py b/core/__init__.py index 23f8987..ca29ba6 100644 --- a/core/__init__.py +++ b/core/__init__.py @@ -1,3 +1,3 @@ # zcbot 版本号单一事实源:web/app.py 的 FastAPI version、/healthz 返回、前端展示都引这里。 # 改版本只动这一行。 -__version__ = "0.33.0" +__version__ = "0.33.1" diff --git a/web/app.py b/web/app.py index ad58e7d..55d4e86 100644 --- a/web/app.py +++ b/web/app.py @@ -2755,9 +2755,15 @@ def create_app() -> FastAPI: # workspace 文件可变, 禁浏览器启发式缓存 (RFC 7234 默认能缓数小时) # 否则文件改了 SPA 预览还是旧内容 # (Starlette FileResponse 不实现 304, 总是 200 全量; workspace 文件小, 可接受) + # .svg 显式给 image/svg+xml: 部分部署环境 mimetypes 未注册 svg, FileResponse + # 会猜成 octet-stream, 前端 就渲染不出 SVG 预览 + media_type = None + if target.suffix.lower() == ".svg": + media_type = "image/svg+xml" return FileResponse( path=str(target), filename=target.name, + media_type=media_type, headers={"Cache-Control": "no-cache"}, ) diff --git a/web/static/js/preview.js b/web/static/js/preview.js index fe5bef9..5ba9455 100644 --- a/web/static/js/preview.js +++ b/web/static/js/preview.js @@ -228,7 +228,11 @@ export async function openFilePreview(rel) { } function _showImage(blob) { - const url = _trackBlobUrl(blob); + // SVG 在 里必须 Content-Type=image/svg+xml 才渲染;下载接口靠服务端 + // mimetypes 猜类型,部分部署环境(Linux)未注册 .svg → octet-stream → 不显示。 + // 这里据扩展名强制纠正,与服务端响应头无关。 + const mime = /\.svg$/i.test(_fpCurrentRel || "") ? "image/svg+xml" : ""; + const url = _trackBlobUrl(blob, mime); const body = $("fp-body"); body.className = "body center"; body.innerHTML = ""; @@ -470,7 +474,8 @@ async function openMiniFilePreview(rel) { body.className = "body center"; const img = document.createElement("img"); img.className = "preview-img"; - img.src = _trackMiniBlobUrl(blob); + const _mime = /\.svg$/i.test(_mpCurrentRel || "") ? "image/svg+xml" : ""; + img.src = _trackMiniBlobUrl(blob, _mime); body.appendChild(img); _makeImageZoomable(body, img); } else if (cat === "video") {