524dabcf7f
Co-authored-by: Cursor <cursoragent@cursor.com>
2.6 KiB
2.6 KiB
Changelog — 编辑器重构 P0
日期: 2026-06-02
模块: 文件编辑器(FileEditorWorkbench + 后端 read/write-file)
变更摘要
修复编辑器三个 P0 正确性问题:
-
撤销历史串台(M1):改为每个标签页独立持有 Monaco
ITextModel,切换时通过editor.setModel()+saveViewState/restoreViewState保留光标、折叠、选区,撤销历史互不影响。 -
EOL 强制 LF(M2):
read-file响应新增eol字段(LF/CRLF);编辑器内部用 LF-only 做对比,保存时按原始 EOL 还原。CRLF 文件不再被破坏。 -
并发写覆盖(M3):
read-file新增mtime、etag(sha256hex);write-file接受可选etag,写前校验,不一致返回 HTTP 409;前端弹出冲突弹窗(覆盖/放弃)。
动机
- M1:多标签编辑时,A 文件的撤销会回到 B 文件内容(共享同一 Monaco model 的
setValue导致) - M2:CRLF 脚本/配置保存后被改成 LF,破坏 Windows 批处理等文件
- M3:并发场景(两个管理员、外部工具)下静默覆盖,数据丢失无提示
涉及文件
| 文件 | 变更 |
|---|---|
server/api/schemas.py |
FileWrite 加 `etag: str |
server/api/sync_v2.py |
read_file 返回 mtime/etag/eol;write_file 加 etag 校验(409)和写后更新 etag |
frontend/src/utils/filePreload.ts |
新增 RemoteFileReadResult 接口,缓存改存完整结果 |
frontend/src/composables/files/useFilesEditor.ts |
新增 OpenFilePayload 接口,editFile/previewFile 适配新返回类型 |
frontend/src/components/FileEditorWorkbench.vue |
per-tab model + viewState;EOL 保留;409 冲突弹窗 |
tests/test_editor_write_conflict.py |
11 个 pytest 用例,覆盖 etag 校验逻辑 |
docs/design/specs/2026-06-02-editor-refactor-design.md |
设计文档 |
docs/design/plans/2026-06-02-editor-refactor.md |
技术文档 |
是否需要迁移/重启
- 后端:需
supervisorctl restart nexus(API 响应字段变更) - 前端:需重新构建并上传(bundle 变更)
- 数据库:无 migration
验证方式
python -m ruff check server/api/schemas.py server/api/sync_v2.py→ 0 errorspython -m pytest tests/test_editor_write_conflict.py -v→ 11 passednpx vite build→ build succeeded, 0 TS errors- 浏览器:打开两个文件 A、B,编辑 B 后切回 A,Ctrl+Z 只撤销 A 的历史
- 浏览器:上传 CRLF 文件,编辑后保存,下载验证 CRLF 保留
- 浏览器:同一文件两标签保存,后者收到冲突弹窗