fix(audit): 审计问题全量修复(docs/12)

高危逻辑 A1-A10:saved 写盘快照、重载卸载回写闸门与期望值校验、
?file 无条件打开、偏好读后才写+防抖、depth=0 守卫、查找命中随编辑
作废、自动保存避让冲突标签、搜索防链接环、重 IO 命令异步化+上限。
样式:--panel 更正为 --bg-panel、新增 .input 基础样式、文档主题
纸面/字色成对、打印隐藏清单补全、paperTone 驱动源码明暗。
性能:状态栏/大纲按正文 memo、大纲延后提取、Sidebar memo、专注
装饰映射复用、查找防抖、图片 base64 过 IPC。
中危一批:关窗落盘后放行、回收站去向明示、GBK 搜索、create_new
落盘、Mermaid 渲染代次守卫、多行粘贴分段等。
回归:新增 tabkey/audit-fix 集成测试(E1 Tab 抢先定性排除),
vitest 71 通过 + 2 跳过。
This commit is contained in:
Muse
2026-11-13 23:34:19 +08:00
parent 78fbf6a91b
commit db81e60d71
26 changed files with 1064 additions and 228 deletions

View File

@@ -0,0 +1,64 @@
# 审计修复报告(对应 docs/11,2026-11-13 晚)
按用户拍板顺序执行:高危逻辑 → 样式 → 性能,并顺带处理中危中的低成本项。
验证:`npx tsc -b` 0 错误;`npx vitest run` **71 通过 + 2 跳过**(修复前 61+2,新增回归 10 例:tabs 1、tabkey 2、audit-fix 7);`npm run build` 通过;dev 冒烟 200;Rust 侧 search/base64 以独立 crate 实跑断言(符号链接环终止、GBK 中文可搜、base64 往返);commands.rs 过 rustfmt 语法解析。本地未推送。
## 一、高危逻辑(A1–A10)——全部已修
| # | 修复 |
|---|---|
| A1 保存竞态 | `saved` action 改为携带写盘快照 `savedContent`;saveTab 用调用时刻快照落盘并回填。写盘期间的新输入保持脏。回归测试已加(tabs.test)。 |
| A2 重载被卸载回写覆盖 | EditorPane 新增 `onFinalMarkdown` 通道专门承接卸载回写;重载活动标签前 App 先把该标签加入抑制集合并 1.5 秒兜底清除,回写被丢弃。 |
| A3 重载检查—执行竞态 | reloadTabFromDisk 增加 `expectedContent` 校验与 isDirty 复检,读取期间状态已变则放弃重载、交下一轮按冲突处理。 |
| A4 ?file= 被挡死 | 启动 effect 重构:恢复计划改为条件块,`?file=` 打开移到无条件路径;多窗口主路径恢复。 |
| A5 偏好启动竞态 | 偏好改为最先读回,读完置 `uiLoadedRef` 后 `[ui]` 保存 effect 才允许写库;落库再加 400ms 防抖(顺带解决自定义 CSS 逐字写库)。 |
| A6 depth=0 RangeError | cursorFromSelection 对 depth=0 单独处理(行=起点前文本块数+1、列=1),并顺手给常规路径加提前终止。回归测试覆盖全选与公式块 NodeSelection。 |
| A7 查找命中过期 | 编辑器/源码两侧:文档一变立即作废命中表,下个查找操作先按新内容重算;同时统一了替换契约——pane 内不再自查一遍,刷新与定位由 App 层单次完成(修掉 B 的双重计算与序号错位)。 |
| A8 自动保存覆盖外部修改 | 自动保存跳过正处于冲突弹窗中的标签。 |
| A9 搜索无限递归 | search.rs 按规范化路径去重(链接环直接跳过)+ 深度上限 24 + 已扫描文件上限 20000。独立 crate 验证链接环可终止。 |
| A10 主线程同步命令 | read_text_file / write_text_file / read_file_base64 / workspace_search 改 async + spawn_blocking;文本读 64MB、base64 读 32MB 上限。 |
## 二、中危顺带修复
- 关窗恢复写入改为先 preventDefault、等全部 saveRecovery 落盘再决定放行/弹窗(原 fire-and-forget 丢未命名草稿)。
- 另存为后新旧两个恢复键都清理(原只清新键,启动误报草稿)。
- 全局快捷键在任一弹窗打开时整体让行(anyModalRef)。
- 打印在源码模式下改为等渲染编辑器就绪再打印(原固定 60ms 赛跑)。
- 原子写临时名加纳秒后缀(并发保存不共用临时文件);create_text_file 改 create_new 原子创建;资源落盘改 create_new 循环(消除 dedupe 竞态与兜底覆盖)。
- 删除到回收站返回实际去向,前端对永久删除明确提示。
- 编码:BOM+坏字节不再 lossy 吞字(退 GBK);搜索改为 GBK 感知解码(GBK 文档中文可搜,独立 crate 验证)。
- DB 锁中毒自愈(into_inner 取回连接);文件名清洗补控制字符/结尾点空格/Windows 保留名。
- 图片粘贴改 base64 过 IPC(原 number[] 数组);落盘失败有错误提示(uploader try/catch + onImageError)。
- 快捷键:录入无修饰键组合直接拦截并提示;`Ctrl+Shift+=` 归一为 `ctrl+=`(与缩放默认键对齐);源码模式切主题改 Compartment 热切换(不丢光标/撤销史);源码字号跟随缩放变量。
- 导出:坏公式不再整篇失败(throwOnError:false);Mermaid 导出 id 加序号防重复;导出图片解析去查询串 + URL 解码。
- 工作区搜索:结果记住搜索时的词(点击不再用输入框现值)、换根目录清旧结果。
- 粘贴多行纯文本按段落插入(原塞单文本节点、往返不保真,回归测试已加)。
- Mermaid 视图渲染代次守卫(旧渲染不覆盖新预览);锚点兜底跳过空段落;list_item 视图闭包节点更新 + label 刷新;代码块语言类名同步;setAlignColumn 恢复完整选区;代码块 Tab 多行选区整体缩进;表格 Tab 命令失败不吞键;Stage5 弹窗补背景点击/Esc 关闭;docOverrides/conflictSeen 随标签关闭回收。
## 三、样式(D)——高危三项全修
- `--panel` → `--bg-panel`(3 处,查找条不再透明)。
- 新增 `.input` 基础样式(边框/背景/字色/聚焦环,全主题变量),查找框/搜索框/弹窗输入框告别原生外观。
- notion 补 `--doc-ink:#37352f`,github 补 `--doc-paper:#ffffff` + `--doc-ink:#1f2328`,深色 UI 下也可读。
- 打印隐藏清单补 `.findbar`、`.mermaid-head`;`.modal-note` 重复定义合并;文件末重复规则删除;新增全应用细滚动条与选区色;FindBar min-width 340 + max-width 兜底。
- themes.ts 的死字段 `darkPaper` 升级为真正被消费的 `paperTone`:源码模式明暗按文档主题纸面决定(修 D5 源码/文档主题不联动)。
## 四、性能(C)
- 状态栏计数与大纲改为只随正文变化重算(useMemo 按 content),大纲再经 useDeferredValue 延后提取;Sidebar memo 化(App 传稳定回调);tabs reducer 对同内容 updateContent 直接返回原 state。
- 专注装饰改「映射复用」:活动块不变时复用映射后的旧装饰集,只在跨块/强制时重建;顺带修 depth=0 选中顶层原子时整篇变淡。
- 查找输入防抖 150ms(Enter/翻页立即冲刷挂起查找)。
- 选区行列计算提前终止(不再每次选区变化数完整篇)。
- 图片 IPC 改 base64(见中危)。
## 五、待核实项结论
- **E1 Tab 抢先:排除。** 新增 tests/tabkey.test.ts 向组装好的编辑器派发真实 Tab:表格内跳格、文档未变;代码块行首缩进、非光标处插入。两项行为正确,indent 插件未抢先。此测试常驻回归。
- E3/E4(KaTeX 抛错、Mermaid 导出 id)已按预防性修复处理(见中危)。
- E2 CSP 维持现状(需结合真机 WebView 行为专项评估,不在本批)。
- E5 双进程同开一个 SQLite 的 SQLITE_BUSY 未处理:同进程多窗口共用 State 连接无此问题;双开进程属边缘场景,列入阶段 7 真机清单观察。
## 六、本批未动的(架构性/需真机)
- C2:Milkdown listener 每 200ms 全量序列化 + 深比较是大文档输入的固有成本,根治需把「状态持有 Markdown 字符串」改为按需序列化,属架构级改动,建议单独立项评估,不在本批冒险。
- 整套 Tauri 构建与真机行为(多窗口、关窗拦截、回收站去向提示、打印)仍待 Windows 验证——本批所有 Rust 改动只过了语法与纯逻辑验证,未过完整 cargo 构建(沙箱限制,同前)。