feat(editor): 未保存草稿保护、保存冲突检测与内置查找替换 - #600
Open
nyantused-cpun wants to merge 1 commit into
Open
Conversation
三项编辑器/磁盘边界上的改动,均不改变 ctx.betterSidebar 的服务方法签名:
1. 未保存草稿保护(新增 src/client/editor-dirty.ts)
- EditorHost 把工具栏的 dirty 登记进模块级登记表,卸载即清除
- 关标签页(X / 中键 / 右键菜单)、删文件(closePathTabs)、
页面刷新/关闭(beforeunload)三条路径共用一次确认
2. 保存的乐观并发门
- fs.read 返回 mtimeMs;fs.write 接受 expectedMtimeMs,基线不符
返回 409 fs-conflict 而不是覆盖对方字节;成功回报新基线
- 编辑器显示冲突横幅 +「重新载入」,草稿不丢
- 顺带修复:临时文件改为随机命名兄弟文件,同一路径的并发保存
不再互删(原固定名 .dsh-sidebar-tmp-<pid>)
3. 内置查找/替换
- 接入 @codemirror/search(依赖此前白装)+ searchKeymap,
面板文案经 EditorState.phrases 接入本插件词典
测试:新增 21 例(guard 8 / fs.write 路由 5 / 冲突 4 / 查找 4);
19 个语言词典同步 12 个新 key。设计见
docs/plans/2026-09-09-editor-draft-protection-design.md。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
背景
三项都在「编辑器 ↔ 磁盘」边界上,不改
ctx.betterSidebar的服务方法签名,不碰 DSH 源码。设计文档:docs/plans/2026-09-09-editor-draft-protection-design.md。1. 未保存草稿保护(新增
src/client/editor-dirty.ts)实施前:脏稿确认只挂在手动刷新按钮上;关标签页(
closeTab)、删文件(closePathTabs)都不问,也没有beforeunload——草稿直接消失。EditorHost把工具栏报告里已有的dirty登记进模块级登记表(不新增 viewer 契约),卸载即清除,所以条目不会比草稿活得更久。confirmDiscardDraft:标签页 X / 中键 / 右键菜单「关闭(其他/左/右)」→Sidebar.actions.closeTab;文件树删除 →closePathTabs(文案由调用方传入,tree-mutations.ts保持无 i18n 依赖);浏览器刷新/关页 →beforeunload(按dirtyCount()全量判断,因为整个页面要走了)。2. 保存的乐观并发门 + 并发临时文件互删修复
实施前:模型/另一个标签页/外部编辑器改了文件后,旧草稿
Ctrl/Cmd+S会静默覆盖;且临时文件名固定为${path}.dsh-sidebar-tmp-${process.pid},同一路径两次保存并发时先完成者的rm(tmp)会删掉后完成者的临时文件(ENOENT)。fs.read返回mtimeMs;fs.write接受expectedMtimeMs,基线不符 →409 fs-conflict(写临时文件之前判定,不留垃圾);成功返回新基线mtimeMs。fs-conflict时保留草稿、显示警告横幅 +「重新载入」(FileViewerProps.onReload→ 已有的、会先确认脏稿的刷新路径)。.${basename}.dsh-write-${randomUUID()}.tmp(与fs-operations.ts上传路由同款),每次保存有自己的临时兄弟文件。3. 内置查找/替换
@codemirror/search此前在dependencies里但src/零引用(白装)。接入search({ top: true })+searchKeymap(Ctrl/Cmd+F、Ctrl/Cmd+H、Ctrl/Cmd+G、Ctrl/Cmd+D),面板文案经EditorState.phrases.of(...)接入本插件词典(CodeMirror 默认只有英文),工具栏新增放大镜按钮(预览态下先切编辑态)。验证
tsc --noEmit✅、eslint src tests --max-warnings=0✅editor-unsaved-guard.spec.tsx(8) /fs-write-route.spec.ts(5) /text-editor-conflict.spec.tsx(4) /text-editor-search.spec.tsx(4)tests/locales.spec.ts的键集相等守卫通过)EPERM(需开发者模式/管理员;CI 在 ubuntu 恒绿),1 个caps status entries在全量并发下 5s 超时(单独跑 2.1s 通过)。设置GIT_AUTHOR_*/GIT_COMMITTER_*后 smoke 的 revert/cherry-pick 两项由失败转为通过。EPERM(平台 rename 行为)但拒绝ENOENT(互删的特征),并断言无临时文件残留。已知取舍
mtime 基线不是内容哈希(同毫秒并发写仍可能双双通过,此时以最后一次 rename 为准);外部 viewer 不参与脏稿保护(只有内置文本编辑器报告
dirty);跨会话切换不弹窗;不做跨文件搜索(按仓库理念留给生态插件)。