doc(PROGRESS): 05-19 dev SPA 文件预览弹框
加 05-19 条目 + 文件清单更新 dev.html 行数 + 加 web/static/vendor/ 一行。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
15bbadf6d6
commit
3ca37f7041
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
## 已完成关键能力
|
||||
|
||||
- **05-19 / dev SPA 文件预览弹框**:用户提:"web 右侧点击文件可以弹框加载预览,带下载按钮"。原行为是 click → 直接 `downloadFile`(走 `/v1/files/download`)落盘,不能在线看。**方案**:复用现有 `/v1/files/download`(blob URL 绕过 auth header 限制,不动后端),前端按扩展名分派渲染器。新加 `#file-preview-modal`(90vw × 90vh,max 1200px),头部 filename + 下载 + × 关,body 按 cat 切不同布局。**分派**:① image(jpg/png/gif/webp/bmp/svg/ico)→ `<img>` blob URL;② pdf → `<iframe>` blob URL 强制 `application/pdf` mime,浏览器内置 PDF viewer;③ text 类(txt/log/json/yaml/csv/py/js/ts/go 等近 30 种)→ `<pre>` textContent,2MB 上限超限 fallback;④ md / markdown → 复用现有 `renderMd`(marked + DOMPurify + hljs);⑤ docx → 懒加载 `/static/vendor/jszip.min.js` + `docx-preview.min.js` → `window.docx.renderAsync(blob, host)` 渲染到 DOM,带表格 / 图片 / 样式还原;⑥ xlsx / xls → 懒加载 `xlsx.full.min.js`(SheetJS 社区版),多 sheet 出 tab 切换,`sheet_to_html` 直接出表格;⑦ 其它(pptx / doc / ppt / 未识别)→ fallback "暂不支持在线预览,请下载查看" + 大号下载按钮。**机制**:`loadScript()` 懒加载只在首次访问 office 文件才拉 1MB vendor;`_trackBlobUrl` + `_flushBlobUrls` 弹框关时统一 revoke 防漏;Esc / 点 backdrop 关弹框;auth 401 → logout;binary 50MB 上限兜底防 OOM。**库选型**:① docx 用 docx-preview(Apache-2.0,2k star,2025-07 还在发版,UMD/CDN OK,只依赖 JSZip,DOM 渲染,fidelity 显著优于 mammoth.js)② xlsx 用 SheetJS 社区版(Apache-2.0,长期维护,单文件 UMD,`sheet_to_html` 直出)③ pptx 整个社区 JS 库都不成熟(pptx-preview / PptxViewJS 都对动画 / 复杂版式失真),先 fallback,真有需求再上服务端 LibreOffice 转 PDF 统一处理。**新增 `web/static/vendor/`**(入 git,项目无 npm 工具链就是直 vendor;锁版本好处:复现部署一致 + 安全审计直观;~1MB 可接受):jszip 3.10.1 / docx-preview 0.3.6 / xlsx 0.18.5。**改 `web/static/dev.html`**(+~240 行 JS + ~60 行 CSS):file row .name onclick 从 downloadFile 切到 openFilePreview;现有 downloadFile 保留供 fallback / 头部下载按钮直接复用。**没动**:后端 app.py(blob URL 路径足够;弹框关闭统一 revoke 避免 URL 泄漏);DESIGN(纯 UI 增强非架构变化);RUN(无 CLI / env / 文件布局变化)。**文档**:**只动 PROGRESS + 文件清单加 vendor/ 目录**(按 CLAUDE.md 三文档边界)。
|
||||
- **05-18 / proposal skill 流程图/结构图管线**:用户反馈"申报 skill 关于流程图、结构图等的生成有些问题,包括渲染到 docx 里"。诊断结果:① `render_docx.py` 整个脚本没有 `add_picture` / 没引 `Inches`,所谓"画流程图"只能走 `add_code_block` 的 ASCII 字符画(`新宋体` + Consolas + box drawing),Word 里 CJK 与 `─ │ ┌ ┐` 不真等宽,中文标签一长就错位,评审看到字符画扣印象分;② 模板里写满 `[图 2-2 关键技术关系架构]` 裸占位,但 SKILL.md 零提及 mermaid / graphviz / matplotlib,模型只能瞎编 ASCII;③ 评审红线"图编号连续无遗漏"(`references/review_redlines.md:96`)无机制保证。**方案**:Mermaid 管线 + matplotlib 兜底 + 图编号自增。**新增 `scripts/render_diagrams.py`**(143 行):扫 sections/*.md 的 ```mermaid``` 块 → 算 sha1 前 10 位作稳定 id → 落到 `<task_dir>/figures/fig_<hash>.png`;两阶 backend:① 本地 `mmdc`(npm i -g @mermaid-js/mermaid-cli;最高质量、离线)② `mermaid.ink` 公网 API(`https://mermaid.ink/img/<url-safe-b64>`,urlsafe_b64encode rstrip '=';不装东西、要联网);两个都失败留 WARN 退出 0(不阻塞流水线);`%% caption: <图题>` 行注释抽题文,mermaid 本身当注释跳过、render_docx 当题用;不改动 .md 文件(源是真相);幂等(png 存在跳过)。**改 `render_docx.py`**(+~70 行):① 加 `` 单行识别 → `add_picture(width=Cm(15))` 居中 + 五号宋体居中图题段落"图 N <caption>",N 通过 `ctx` 字典(`{sections_dir, figures_dir, fig_no}`)在 `render_md_block` 调用链里递增,relative 路径以 .md 所在目录为锚;图片源缺失 → 留 `[图片缺失: <src>]` 占位段防 silent miss、文档不崩;② 围栏 lang == "mermaid" 特判:算同源 sha1 查 `<sections_dir>/../figures/fig_<hash>.png`,命中走插图 + 题(同样自增编号、复用 `extract_mermaid_caption`),未命中**继续走原 `add_code_block` ASCII fallback 路径**(mmdc 没装也能交差,只是不漂亮);③ A4 减页边距得正文宽 16cm,图宽 cap `Cm(15)` 留 1cm 安全垫;④ `add_picture` 失败 try/except 不让整 doc 崩,改占位文字。**改 SKILL.md**:`资源` 段加 `render_diagrams.py` 行;阶段三命令链插入 `render_diagrams.py` 前置(可选,无 mermaid 块直接跳过);新增"插图"段(类型选择表 / mermaid `%% caption` 约定 + 完整 flowchart 例子 / matplotlib `figsize=(10,4)` `dpi=150` 中文字体 SimHei 配色规范 / 不要手写"图 2-2"章节-序号);反模式加 3 条(ASCII 字符画当真图 / 手写图编号 / 裸 `[图 N-N ...]` 占位)。**改 `templates/key_rd.md`**:① §04_content (一) 主要研究内容里 `[图 2-2 关键技术关系架构]` 占位换成完整 ```mermaid flowchart LR``` 块(关键问题 Q1/Q2 → 技术 T1/T2/T3 → 平台,带 `%% caption:`);② §04 (三) 技术路线加"项目总体技术路线" mermaid `flowchart TB` 例子(需求→设计→突破→集成→示范 5 阶段 + 双向反馈虚线);③ §09_schedule 甘特图改"两种画法 A. mermaid `gantt` B. matplotlib `barh`"并给完整 mermaid gantt 示例。**没动**:`major_project.md` / `nsfc_joint_fund.md` 只是"配图"提示,不是裸占位,通过 SKILL.md 横向覆盖;`scripts/word_count.py` / `quality_check.py`(图不计字数,质量检查暂不涉及图占位)。**Smoke 4 case 全绿**(`scripts/_smoke_proposal_diagrams.py`,留作回归):① cached mermaid + direct image + ASCII fallback 混排(`figures: 2` 报告对、`inline_shapes == 2`、缓存命中走"图 1/图 2"、缺缓存 mermaid 走 ASCII 源保留 + 不申请图号"图 3");② 无插图回归(`figures: 0` + table 完好);③ `render_diagrams.py` API 调用(`find_mermaid_blocks` 抽 2 块 / `extract_caption` 命中/未命中 / 预填 cache png 全走 `cache` backend 不走网络);④ 图片源缺失走占位文字,后续段落不丢。**Windows GBK 子进程坑**:smoke 跑 subprocess 拿不到 UTF-8 stdout(`UnicodeDecodeError 0xd6`),给子进程 env 加 `PYTHONIOENCODING=utf-8` 修;同 memory 里 emoji 编码教训同源。**文档**:**只动 PROGRESS**(skill 内部能力增强 ≠ 架构变化,不动 DESIGN;skill CLI 不是 zcbot 对外行为,不动 RUN —— 按 CLAUDE.md 三文档边界)。**净增量**:~213 行代码新增,5 行文档示例改写,sections/*.md 不动(源永远是 mermaid 真相)。**留给真用户的体验**:模型不需要再瞎编 ASCII,直接写 mermaid 块就行;mmdc/网络都没的极端环境下 docx 仍能产(ASCII 退化,文字不丢);图编号永远连续不重不漏(自动),手工占位的旧坑彻底关上。
|
||||
- **05-18 / `POST /v1/files/rename` + 顶层目录 delete 加 task 引用闸**:用户反复抠"文件夹改名 / 删除时怎么不破 DB 一致性"。架构最终落点:**`/v1/files/*` 是唯一的目录树 mutation 命名空间,DB-FS 一致性作为服务端不变量内化**(放弃曾经的"files API 永不进 DB"惯例 —— 那是当初没考虑顶层目录时形成的偶然,把它升格成铁律反而导出双命名空间代价);`GET /v1/folders` 保留,但定位为"项目聚合视图"(只读,带 n_tasks/last_used,新建任务 datalist 用),不做 mutation。**判定**:`target.parent.resolve() == root.resolve() and target.is_dir()` ⇒ 顶层目录(就是 task 的 working_dir)。**新 `POST /v1/files/rename`**:校验 `validate_task_name(new_name)` / target 存在 / 不能等于 user_root / sibling 不能已存在;**顶层目录**走 DB-aware 分支:`session_scope()` 事务内 `SELECT task_id, run_status WHERE working_dir=old_db FOR UPDATE` 锁所有关联 task,任一 `run_status in ('running','cancelling')` → 409;`check_no_subtask(new_db, exclude_task_ids=tids)` 防改名后与其它 task 形成嵌套(exclude 平移过去的自己);`UPDATE tasks SET working_dir=new_db` → `os.rename(old_fs, new_fs)` —— FS 失败 raise → session_scope 回滚 DB UPDATE。**非顶层**(子目录 / 文件)纯 FS rename,不动 DB。**事务顺序考量**:DB UPDATE 在 FS rename 之前(都在事务未提交期间),FS 失败可回滚 UPDATE;唯一不一致窗口是"FS 改完 + commit 失败"(PG 单事务 commit 极少失败,接受)。**`POST /v1/files/delete` 收紧**:同样的顶层目录判定,若顶层目录有任意 task 引用 → 409 "请先 DELETE 关联 task 再删目录",避免悬空指针。**`check_no_subtask` 扩 `exclude_task_ids` 参数**:`core/storage/utils.py` 加可选 Iterable[UUID],循环里跳过这些 task_id;rename 场景刚需(否则被改名 task 与自己未来的 new_db 误判为嵌套);其它 caller 默认 None 行为不变。**dev SPA 同步**(`web/static/dev.html`):file row 加 `改名` 按钮,prompt 拿新名 → POST `/v1/files/rename`;rename 后:① 当前 `state.filesPath` 若在被改名子树内做前缀替换继续停留(`rel === filesPath` 或 `filesPath.startsWith(rel + "/")` → 替换前缀为 res.new);② `loadFolderSuggestions()` 刷 datalist;③ `res.tasks_updated > 0` 则 `loadTaskList()` + `selectTask(state.taskId)`(task 卡片 / chat 头里展示的 working_dir 末段也跟着变)。delete confirm 文案补一句"顶层目录且仍被 task 引用需先删 task";删除完成也 `loadFolderSuggestions()`。**Smoke 5 case 全绿**(in-process TestClient + PG):① 子目录 rename 纯 FS / tasks_updated=0;② 顶层目录 rename 同步 UPDATE / tasks_updated=N / FS 改完 + DB working_dir 跟着变;③ 顶层目录 rename 时有 running task → 409;④ 删顶层有 task 引用 → 409;⑤ rename 目标已存在 → 409。**Smoke 文件**(`scripts/smoke_files_rename.py`)跑完未删(留作回归用)。**没动**:`GET /v1/folders` 接口、`DELETE /v1/tasks/{id}` 行为(仍删 DB 行不动 FS,与新 delete 配对刚好覆盖"销毁项目"全链路);`/v1/files/{list,upload,download}` 路由签名;skill / chat / cancel 等其它路由。**架构反思**:此前一版我先提的双命名空间 `/v1/folders/rename` vs `/v1/files/rename`,内部 if path is top-level 切分支被自己视为"代码异味" —— 实际是反了,这种分支**从数据状态派生**(path 恰好是 working_dir),不是从客户端意图派生,放服务端是更安全的位置(client 没法绕过去导致悬空引用);双命名空间反而把同一个分支搬到 client 去做,失去强制力且端点表面翻倍。这条工程教训记 §7.9。
|
||||
- **05-18 / system prompt skill 机制改"可选辅助"**:接 `GET /v1/skills` + 下拉选择落地后,task 创建时 skill 字段允许留空成为常态。原 `prompts/system/general_v1.md` 第 14 行 `"永远 load 一下。skill 数有限,加载成本很低"` 在新形态下变得过激 —— 简单问答 / 通用编码 / 文件操作不该被强行匹配到 coding 等 skill。改为"Skill 是**可选辅助**"+ 明确列出"简单问答、读代码、改 bug、文件操作这类通用任务,直接用通用工具就够,不必为每个任务硬套 skill"。一旦决定要用仍要求 load 完整指引(原则不变)。**未动**:skill discovery block 内容(name + description 注入仍按 registry 顺序)、`load_skill` 工具协议、SKILL.md 内容。**tradeoff**:边缘场景(用户提"整理大纲"可能落 proposal 也可能不用)agent 现在会偏向不 load,可能漏掉好的模板;但比原来"什么都套 coding"的噪音更可接受。
|
||||
|
|
@ -117,9 +118,10 @@ web/app.py 889 ← /v1/ JSON API + user_id 隔离 + run lock +
|
|||
web/auth.py 115 ← D' 过渡:PLATFORM_KEY → JWT 兑换
|
||||
web/broker.py 121 ← in-process pub/sub + cancel signal,全 task_id 索引(0004)
|
||||
web/sinks.py 21 ← WebEventSink 绑 task_id(0004)
|
||||
web/static/dev.html 1133 ← D' dev SPA + stop 按钮 + cancelled badge
|
||||
web/static/dev.html 1516 ← D' dev SPA + 文件预览弹框(image/pdf/text/md/docx/xlsx)
|
||||
web/static/vendor/ ~1 MB ← jszip 3.10.1 / docx-preview 0.3.6 / xlsx 0.18.5(office 预览 vendor)
|
||||
─────────────────────────────────
|
||||
Python 合计 ~3400 行(+ dev.html 1133 静态)— 05-18 入口归位净减 ~400 行 REPL/CLI
|
||||
Python 合计 ~3400 行(+ dev.html 1516 静态 + vendor 1MB)— 05-18 入口归位净减 ~400 行 REPL/CLI
|
||||
```
|
||||
|
||||
加 skills/ppt 脚本 ~600 行 + SKILL.md / references / config / prompts + alembic.ini,总仓库约 3500 行。
|
||||
|
|
|
|||
Loading…
Reference in New Issue