4.6 KiB
4.6 KiB
阶段 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. 已知问题与边界
- 外部检测是轮询不是文件系统监听:4 秒间隔、靠 mtime+大小判变;同一秒内同大小改写理论上可漏检。换 notify 监听是阶段 6 性能/体验项(需处理防抖与自身保存回声,轮询版已用基线机制规避了自保存误报)。
- 自动保存间隔当前固定 30 秒(偏好已落库,设置界面留到阶段 5 主题/设置页统一做);自动保存不处理未命名文档(靠恢复记录兜底,不悄悄替用户决定文件名)。
- 恢复弹窗「稍后再说」后,本次会话内不再重复弹(记录仍在,下次启动再问);同一文件在两台机器/被外部改名后恢复键失效属预期(按路径键)。
- 删除文件夹到回收站依赖
trash在 Windows 的实现,真机验;删除后若文件树未即时刷新可用树头 ⟳ 手动刷新(已做 refreshSignal 自动刷新,真机复核)。
4. 下一阶段(阶段 5)预告
主题与输出:内置主题(默认/GitHub/Notion/简约蓝/深色护眼/专注写作/经典纸张)+ 自定义 CSS 作用域注入、文档级临时样式、导出 HTML/PDF、另存为 HTML、复制为 HTML、打印与打印预览、导出时资源处理(图片转相对路径/内嵌选项)。