feat(stage4): 自动保存/崩溃恢复/外部修改检测/工作区管理(含合并验收清单)

This commit is contained in:
Muse
2026-11-13 22:43:29 +08:00
parent 0774b70d29
commit e5ad58eb93
12 changed files with 1013 additions and 21 deletions

37
docs/08-stage4-report.md Normal file
View File

@@ -0,0 +1,37 @@
# 阶段 4 · 文件管理与工作区报告(实测记录)
> 日期:2026-11-13 · 范围(需求 §23 阶段 4):Rust/文件系统、最近文件(SQLite 迁移)、自动保存、文件恢复记录、外部修改检测、工作区文件管理、未保存提醒。
## 1. 本阶段实现内容
- **SQLite 存储层**(Rust `src-tauri/src/storage.rs`,rusqlite bundled):四张表 `recent_files / workspaces / prefs / recovery`,数据库位于应用数据目录 `new-markdown.db`,启动时建表并由 Tauri State 托管连接。红线不变:正文只在 .md 文件里,recovery 只是未保存草稿的副本。前端经 `services/store.ts` 访问:桌面走 SQLite、Web 降级走 localStorage(同构接口)。
- **数据迁移**:阶段 1 的 localStorage 数据(最近文件是路径字符串数组、UI 偏好是 JSON)在桌面模式首次启动时一次性迁入 SQLite 并清掉本地副本;兼容两种历史格式。
- **自动保存**:视图菜单可开关(默认开,30 秒);只自动保存「已落盘的脏文档」,保存后同步刷新外部检测基线并清掉对应恢复记录。
- **崩溃恢复**:脏标签每 6 秒把最新草稿写入 recovery(文件按路径键、未命名按标签键);启动时先读磁盘现状做划分——与磁盘一致的记录静默清理,不一致/文件已删/未命名的逐项弹窗(恢复/丢弃/稍后再说);恢复文件文档时以磁盘内容为已保存基线、草稿为当前内容,dirty 状态准确。
- **外部修改检测**:打开/保存时记录基线(mtime+大小),每 4 秒轮询 `stat_file`:
- 干净文档被外部改动 → 自动重新加载并右下角通知;
- 脏文档被外部改动 → 冲突弹窗三选:重新加载磁盘版 / 保留我的内容(更新基线,下次保存覆盖)/ 另存为;
- 文件被外部删除 → 通知,脏文档保留编辑内容、保存时重建。
- **工作区文件管理**:文件树右键菜单(打开/重命名/删除到回收站,Rust `trash` 实现,失败对文件退回永久删除);树头「+」在工作区新建文档(自动补 .md、重名报错不覆盖);重命名已打开文档时标签路径、最近记录、恢复键同步迁移;删除已打开文档会连带关标签。另有 `stat_file / create_text_file / create_dir / rename_path` 命令(重名保护)。
- **关窗拦截**:关闭窗口时先把全部草稿刷入恢复记录;若有已落盘文档未保存,弹「保存并退出 / 不保存退出 / 取消」;只有未命名草稿时直接放行(下次启动靠恢复弹窗找回);在退出弹窗里选「不保存」会清掉对应恢复记录,不让已明确放弃的内容下次再冒出来。
## 2. 构建与测试结果(实跑)
| 项 | 结果 |
|---|---|
| 类型检查 `tsc -b` | ✅ 0 错误 |
| 自动化测试 | ✅ **48/48**(10 文件;本阶段新增 3:恢复键/划分计划、reducer 重载与改路径) |
| Rust 存储层 | ✅ 独立 crate 用真 rusqlite(bundled SQLite 编译运行)断言全过:最近文件上限 30 与置顶去重、偏好 upsert、工作区去重、恢复记录更新/排序/删除 |
| Rust 语法 | ✅ 三个源文件 rustfmt 解析通过(命令包装层的类型检查仍随 Windows 完整 Tauri 构建验,同前几阶段口径) |
| 生产构建 / dev 冒烟 | ✅ 主包 943KB / gzip 292KB;HTTP 200 |
## 3. 已知问题与边界
1. **外部检测是轮询不是文件系统监听**:4 秒间隔、靠 mtime+大小判变;同一秒内同大小改写理论上可漏检。换 notify 监听是阶段 6 性能/体验项(需处理防抖与自身保存回声,轮询版已用基线机制规避了自保存误报)。
2. 自动保存间隔当前固定 30 秒(偏好已落库,设置界面留到阶段 5 主题/设置页统一做);自动保存不处理未命名文档(靠恢复记录兜底,不悄悄替用户决定文件名)。
3. 恢复弹窗「稍后再说」后,本次会话内不再重复弹(记录仍在,下次启动再问);同一文件在两台机器/被外部改名后恢复键失效属预期(按路径键)。
4. 删除文件夹到回收站依赖 `trash` 在 Windows 的实现,真机验;删除后若文件树未即时刷新可用树头 ⟳ 手动刷新(已做 refreshSignal 自动刷新,真机复核)。
## 4. 下一阶段(阶段 5)预告
主题与输出:内置主题(默认/GitHub/Notion/简约蓝/深色护眼/专注写作/经典纸张)+ 自定义 CSS 作用域注入、文档级临时样式、导出 HTML/PDF、另存为 HTML、复制为 HTML、打印与打印预览、导出时资源处理(图片转相对路径/内嵌选项)。

View File

@@ -0,0 +1,62 @@
# Windows 真机验收清单(阶段 0–3 合并版)
> 整理时间:2026-11-13 晚 · 对应已推送版本:Gitea `xieyinlu/new-markdown` main = `ac2c29d`(阶段 0–3)
> 来源:docs/03 §4(阶段 0)、docs/05 §4(阶段 1)、docs/06 §3(阶段 2)、docs/07 §3(阶段 3)。
> 本文件为合并整理,不含新功能;逐项打勾即可。发现的问题直接记在对应项后,阶段 4 前统一清零。
> 注意:本文件写于推送之后,**未随 ac2c29d 推送**,随下一次推送一并提交即可。
## 0. 前置与首次构建
前置:Rust stable(rustup)、Visual Studio Build Tools(C++ 桌面开发)、WebView2(Win11 自带)。
```powershell
cd new-markdown
npm install
npm run tauri dev
```
- [ ] 首次真机构建通过,窗口正常启动(沙箱因 libzstd 定制版装不上 GTK/WebKit 开发包,未能完整构建,见 docs/05 §2——属系统层问题,非工程问题)
## 1. 阶段 1:窗口 / 文件全链路
- [ ] 自定义标题栏可拖动、双击最大化、最小化/最大化/关闭三个按钮可用
- [ ] 打开一个真实 .md,再打开一个 GBK 编码的老文件:内容正确,状态栏编码 / 换行符显示正确
- [ ] 改两笔 → 标题栏与标签出现未保存圆点 → Ctrl+S 保存 → 圆点消失,用外部编辑器确认落盘内容
- [ ] 打开文件夹 → 文件树展开、点开第二个文件成新标签;拖拽标签排序;右键「关闭其他」
- [ ] 关一个未保存标签 → 弹保存确认,「保存 / 不保存 / 取消」三选都试一遍
- [ ] 视图:深色主题、源码模式切回不丢内容、Ctrl+= 放大(Ctrl+0 为「段落」,恢复缩放走视图菜单,见 docs/05 §3)
## 2. 阶段 0:IME 与即时渲染手感
- [ ] **中文 IME**:微软拼音连续输入中文长句,组合区不提前上屏、不触发 `#` / 列表等自动转换;搜狗拼音同验一轮
- [ ] **即时渲染手感**:键入 `# `、`- [ ] `、`> `、表格语法,转换即时、无闪烁、光标不跳位
- [ ] **大段粘贴**:从网页 / Word 粘贴富文本,格式映射合理、无整篇重排卡顿
## 3. 阶段 2:标记显隐 / 任务列表 / 源码模式
- [ ] **标记显隐**:光标进标题显 `#`、进引用显 `> `、进粗体/斜体/行内代码/删除线/链接显 `**` / `*` / `` ` `` / `~~` / `[…](href)`,移开即藏;保存后序列化与原文逐字一致
- [ ] **任务列表勾选**:`- [ ]` 渲染出真实复选框,点击勾选 → 文字变灰,保存后变 `- [x]`;再点可取消(可撤销)
- [ ] **代码块 Tab**:代码块内 Tab 插两空格、Shift+Tab 反缩进;普通段落 Tab 行为不变
- [ ] **源码模式(CodeMirror 6)**:行号、Markdown 高亮、深色跟随主题;渲染 ⇄ 源码切换光标大致回到原处、内容不丢
- 已知边界:锚点同步是近似定位——同一段文字在全文重复出现时取首个匹配,可能落到同文的另一段(内容零丢失)
- [ ] **菜单粘贴**:菜单「粘贴」可用(已改 Clipboard API;阶段 1 的 execCommand 版在部分 WebView2 策略下可能被拒)
## 4. 阶段 3:表格 / 图片 / 公式 / Mermaid
- [ ] **表格**:Ctrl+T 插入 3×3;光标进表格出现浮动工具栏(加行 / 加列 / 删行 / 删列 / 整列对齐 / 删表,滚动跟随);在**数据格**点整列对齐也能生效;Tab 跳格、Shift+Tab 回退、末格 Tab 自动新增一行
- [ ] **图片落盘**:粘贴截图 / 拖入图片 → 文件复制进 `<文档主名>.assets/`、Markdown 写相对路径;重名自动 `-1` 递增;文件名含 Windows 非法字符时被清洗
- [ ] **附件**:插入任意附件文件 → 以 `[文件名](相对路径)` 插入,链接标记不丢
- [ ] **数学公式**:行内 `$…$` 与块级 `$$…$$` 渲染成公式,保存往返无损
- [ ] **Mermaid**:mermaid 代码块渲染出真 SVG 图表(沙箱缺布局引擎未验到这一步);块头可切「编辑源码 / 查看预览」;故意写错语法 → 显示错误原因、不白屏,仍能回源码
- [ ] **脚注 / HTML**:脚注定义与引用、行内 `<u>`、块级 `<div>` 往返稳定
## 5. 已知不做 / 留待阶段 6 的项(验到也不算问题)
- 图片暂无缩放 / 对齐操作条;表格无行列拖动排序、无多格拖拽选区
- 列表标记 `-` / `1.` 由浏览器样式天然呈现,不做显隐
- 表格对齐冒号首轮会被规范化(` :--- ` → ` :- `),语义无损、次轮稳定
## 6. 验完之后
- 清单全绿 → 可拍板进**阶段 4**(SQLite 落库、自动保存与崩溃恢复、外部修改检测、工作区新建/重命名/删除)
- 有问题 → 把未通过项贴回对话,按项修,不急着进阶段 4