Files
new-markdown/docs/12-audit-fix-report.md
Muse db81e60d71 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 跳过。
2026-11-13 23:34:19 +08:00

65 lines
7.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 审计修复报告(对应 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 构建(沙箱限制,同前)。