提交当前顶层 mnote Git 工作区,范围集中在 04-tree-domain 的 resource/trash/filetree 生命周期、mnote-web resource_trash 路由、Convex/Next 兼容接口、sidebar/file-tree 客户端适配、smoke 脚本与对应设计/bug 记录。 不包含被 ignore 的 design/05-editor-mainline/reference-code/leptos-tiptap 嵌套仓库改动。新增 smoke 的测试密码改为运行时读取 MNOTE_E2E_PASSWORD,避免提交明文 credential assignment。
236 lines
12 KiB
Markdown
236 lines
12 KiB
Markdown
# 4-35 [process] Convex File Tree 标题即 Markdown 文件名体系切换 v1
|
||
|
||
> 更新时间:2026-05-15
|
||
>
|
||
> 背景:
|
||
> - 用户反馈本地文件夹中新建页面会先显示 `/mnt/Data1T/mnote/tmp/image copy 98.png`,再转回 `新页面.md`。
|
||
> - 用户判断这是在线文件夹与本地文件夹的冲突,后续希望“统一而不是继续局部修改”。
|
||
> - 目标倾向:不再采用 Convex `page -> index.md` 体系,而是采用直接 `新页面.md` 体系,文件名与标题相同;在线文件夹尽量向本地文件夹靠拢。
|
||
|
||
## 1. 决策问题
|
||
|
||
当前 `convex_workspace` 的 File Tree 长期以复合资源树表达页面:
|
||
|
||
```text
|
||
page/document
|
||
index.md
|
||
mindmap asset
|
||
attachment asset
|
||
```
|
||
|
||
`local_folder` 则天然以真实文件系统表达:
|
||
|
||
```text
|
||
新页面.md
|
||
图片.png
|
||
子目录/
|
||
```
|
||
|
||
这两种形态同时存在时,UI、active row、rename、新建、外部 watcher、projection refresh 容易出现不一致。用户观察到的“先显示 tmp 图片路径,再转回新页面.md”就是这类冲突的症状之一:同一套 File Tree UI 里混入了本地路径资源、Convex 页面容器、`index.md` 伪文件和异步投影校准。
|
||
|
||
本设计稿用于冻结新的方向:后续 Convex File Tree 应逐步向本地文件夹模型靠拢,把页面在文件树中的默认表现改为直接的 Markdown 文件行,例如 `新页面.md`,而不是页面容器下的 `index.md`。
|
||
|
||
## 2. 建议结论
|
||
|
||
建议新建一个设计文档,而不是只建 bug 修补。
|
||
|
||
原因:
|
||
|
||
1. 这会改变 `file_tree` projection 合同,不只是修一个显示名称。
|
||
2. 这会影响 `ObjectIdentity`、active row、rename/title sync、resource parent、trash、restore、search、open intent 和 smoke 期望。
|
||
3. 现有 `4-24` / `5-12` 已把 `index.md` 作为 Page Aggregate body 的 object identity 收口到 done;如果要废弃或弱化该 UI 体系,必须有新的迁移设计覆盖旧口径,不能在代码里零散改。
|
||
4. Convex 存储层仍然是 document record,不等于必须在 File Tree UI 中展示 `index.md` 伪文件。可以先改 projection / UI 语义,再决定是否迁移底层存储。
|
||
|
||
## 3. 范围
|
||
|
||
本设计只讨论 `convex_workspace` 在 File Tree 中的页面呈现与命令语义,不改变 `local_folder` 已有真实文件系统规则。
|
||
|
||
纳入范围:
|
||
|
||
- Convex 页面在 File Tree 中显示为 `{title}.md`。
|
||
- 新建页面时直接出现 `新页面.md`,不先出现 `page/index.md` 或临时资源路径。
|
||
- 重命名页面时,标题与文件名同源:`新标题` 对应 `新标题.md`。
|
||
- 点击 `{title}.md` 打开 Page Aggregate body。
|
||
- mindmap、附件、OnlyOffice、代码附件继续作为同一页面下的资源对象,但不能再依赖可见的 `index.md` 子行作为锚点。
|
||
- Active / reveal 以 `{title}.md` row 或明确 asset row 为准。
|
||
|
||
暂不纳入范围:
|
||
|
||
- 立即迁移 Convex 底层 documents 表结构。
|
||
- 自动双向同步 local folder 与 Convex。
|
||
- Markdown 文件名与标题的所有非法字符、大小写、重名冲突最终规则;本稿先冻结方向,具体规则后续 checklist 补齐。
|
||
|
||
## 4. 新旧模型对比
|
||
|
||
旧模型:
|
||
|
||
```text
|
||
新页面
|
||
index.md
|
||
mindmap-mindmap_xxx.json
|
||
image.png
|
||
```
|
||
|
||
新目标模型:
|
||
|
||
```text
|
||
新页面.md
|
||
mindmap-mindmap_xxx.json
|
||
image.png
|
||
```
|
||
|
||
解释:
|
||
|
||
- `新页面.md` 是页面正文对象的 File Tree row。
|
||
- 该 row 的 object identity 可继续内部映射到 Page Aggregate body,但 UI 不再展示 `index.md`。
|
||
- 资源行仍然挂在页面正文对象下面,表达“属于该页面/正文 block 关联资源”。
|
||
- Page Tree 仍可显示 `新页面`,不带 `.md`,作为页面导航 projection。
|
||
|
||
## 5. 关键合同变更
|
||
|
||
### 5.1 File Tree row
|
||
|
||
Convex 页面正文 row:
|
||
|
||
- `rowKind=markdown_page` 或沿用 `index` 但对 UI 隐藏 `index.md` 语义,需要后续冻结。
|
||
- `title="{pageTitle}.md"`。
|
||
- `objectIdentity="page:markdown:{documentId}"` 或兼容映射到既有 `page:index:{documentId}`,但对外不再暴露为 `index.md`。
|
||
- `documentId` 仍是 Convex document id。
|
||
- `resourceMeta.sourceKind="convex_workspace"`。
|
||
|
||
本地 `.md` row:
|
||
|
||
- 保持真实文件名。
|
||
- `objectIdentity` 与本地 page identity 绑定,不以裸路径作为长期唯一 id。
|
||
|
||
### 5.2 Rename / title sync
|
||
|
||
Convex workspace 中:
|
||
|
||
- 文件树重命名 `新标题.md` 应写入页面标题 `新标题`。
|
||
- 页面标题编辑应能同步更新 File Tree 行显示为 `新标题.md`。
|
||
- `.md` 扩展名是 projection/UI 层文件语义,不应直接写进页面标题。
|
||
- 重名冲突必须返回结构化 preflight,不允许静默覆盖或临时跳成其它资源路径。
|
||
|
||
Local folder 中:
|
||
|
||
- 继续以真实文件名为主,标题优先级仍按现有本地 Markdown 规则处理。
|
||
|
||
### 5.3 Create
|
||
|
||
Convex workspace 新建页面:
|
||
|
||
- 命令结果中应直接携带最终 File Tree row identity 与 `title="新页面.md"`。
|
||
- UI 乐观插入时不得先显示本地 tmp 路径、上传文件名或 `index.md` fallback。
|
||
- 如果后端 title 去重为 `新页面 2`,前端最终只校准成 `新页面 2.md`,中间不出现无关资源名。
|
||
|
||
### 5.4 Resource parent
|
||
|
||
mindmap / attachment 等资源仍挂在页面正文对象下,但 parent row 从旧 `index:{documentId}` 或 page container 迁移到新的 markdown page row。
|
||
|
||
需要特别验证:
|
||
|
||
- 点击 mindmap asset 后 active row 不跳回 `{title}.md`,除非确实打开的是页面正文。
|
||
- 删除 `{title}.md` 进入页面垃圾箱;删除资源进入资源垃圾箱。
|
||
- purge 页面时 Convex document 与其资源关系按正式命令同步清理或标记孤儿处理。
|
||
|
||
## 6. 与既有设计的覆盖关系
|
||
|
||
本稿不否定 `4-24` / `5-12` 的单一真源目标,只调整 File Tree 的可见模型:
|
||
|
||
- 仍然保留 Page Aggregate body 是页面正文真源。
|
||
- 仍然保留 mindmap / attachment / OnlyOffice 是独立 resource object。
|
||
- 仍然要求 object identity 隔离,避免 mindmap 污染页面正文。
|
||
- 变化是:Convex File Tree 不再必须通过可见 `index.md` 行表达页面正文对象。
|
||
|
||
后续如执行本设计,必须同步更新以下旧口径:
|
||
|
||
- `design/04-tree-domain/done/4-24-resource-tree-filetree-pagetree-source-contract-checklist-v1.md` 中“页面节点下固定派生 index.md row”的可见 UI 表达。
|
||
- `design/05-editor-mainline/done/5-12-main-editor-object-tab-resource-alignment-checklist-v1.md` 中 `index.md tab` 的命名口径,可迁移为 `markdown page tab` 或 `{title}.md tab`。
|
||
- `design/10-review/done/05-tree.md` 作为历史记录不直接改写,但 README 或新设计需说明其 `index.md` 口径已被本稿覆盖。
|
||
|
||
## 7. 建议实施阶段
|
||
|
||
### P0:只改 Convex File Tree projection 的页面正文显示
|
||
|
||
- 新建页面后 File Tree 直接出现 `{title}.md`。
|
||
- 点击 `{title}.md` 打开 Page Aggregate body。
|
||
- Page Tree 仍显示无扩展名标题。
|
||
- 保持底层 object identity 兼容,减少一次性改动。
|
||
|
||
### P1:Active / reveal / object identity 迁移
|
||
|
||
- active page body 映射到 `{title}.md` row。
|
||
- mindmap asset 打开后保持 asset active,不降级为页面正文 row。
|
||
- 长列表 reveal 稳定。
|
||
|
||
### P2:Rename / create / conflict preflight
|
||
|
||
- 文件树 rename `{title}.md` 与页面标题同步。
|
||
- 新建同名页面明确生成 `新页面 2.md` 或返回冲突选择。
|
||
- 不再出现临时 tmp 路径或资源文件名作为页面 row title。
|
||
|
||
### P3:旧 index.md UI 退场
|
||
|
||
- 默认 Convex File Tree 不显示 `index.md`。
|
||
- 如仍需 debug,可通过显式 debug/projection inspector 展示旧 identity。
|
||
- 更新 smoke 和设计口径,避免后续继续围绕 `index.md` 修补 UI。
|
||
|
||
## 8. 验收标准
|
||
|
||
- 在 Convex workspace 新建页面后,File Tree 首次可见 row 就是 `新页面.md` 或去重后的 `{title}.md`,不得短暂显示 `/tmp/...`、图片文件名、`index.md` 或其它 fallback。
|
||
- 点击 `{title}.md` 打开页面正文;点击 mindmap asset 打开 mindmap object editor,二者 active row 不互相覆盖。
|
||
- 页面标题编辑与 File Tree rename 最终保持一致:标题 `ABC` 对应 File Tree `ABC.md`。
|
||
- 同名冲突、非法字符、扩展名输入等情况有结构化 preflight 和可预测结果。
|
||
- Local folder 与 Convex workspace 共用同一 File Tree UI 组件、selection/focus/reveal/context menu/keyboard 行为;差异只来自 source capability 与 executor。
|
||
- 真实浏览器 smoke 覆盖:新建页面、重命名、点击页面正文、点击 mindmap、滚动下半部分点击、删除进垃圾箱、restore 后 reveal。
|
||
|
||
## 9. 当前状态
|
||
|
||
当前状态:`process`
|
||
|
||
2026-05-15 已完成 P0/P1/P2 的最小切片:
|
||
|
||
- `bridge-runtime` 的 Convex `file_tree` projection 不再为页面正文派生可见 `index.md` row。
|
||
- Convex 页面正文 row 继续使用 `rowKind=document` / `rowId=doc:<documentId>`,但标题改为 `{pageTitle}.md`。
|
||
- mindmap / attachment / table 等资源继续挂在 `doc:<documentId>` 下。
|
||
- 3000 主文档壳本地 create apply 同步改为只插入 `doc:<documentId>`,标题显示为 `{title}.md`,不再插入 `index:<documentId>`。
|
||
- File Tree 当前页面 selected / focused 默认改为 `doc:<documentId>`。
|
||
- `task169` 的“回到页面正文”验证从点击 `index:<documentId>` 改为点击 `doc:<documentId>`,object identity 仍为 `page`。
|
||
- File Tree inline rename 对页面正文 row 输入 `{title}.md` 时,提交给 `tree.node.rename` 前会剥离 `.md`,页面标题保持 `{title}`,File Tree 显示保持 `{title}.md`。
|
||
- File Tree inline rename 已加入主壳内校验:空名、非法文件名字符、同级同名页面会停留在输入框内并显示结构化提示,不提交 rename command。
|
||
|
||
已通过验证:
|
||
|
||
```bash
|
||
cargo test --manifest-path rust/Cargo.toml -p bridge-runtime file_tree_projection -- --nocapture
|
||
cargo test --manifest-path rust/Cargo.toml -p mnote-web file_tree_projection -- --nocapture
|
||
cargo test --manifest-path rust/Cargo.toml -p mnote-web sidebar_filetree_runtime_uses_markdown_page_row_without_local_index_child -- --nocapture
|
||
cargo test --manifest-path rust/Cargo.toml -p mnote-web tree_shell_filetree_renderer_outputs_initial_nested_html_contract -- --nocapture
|
||
cargo test --manifest-path rust/Cargo.toml -p mnote-web sidebar_tree_runtime -- --nocapture
|
||
cargo test --manifest-path rust/Cargo.toml -p mnote-web tree_shell -- --nocapture
|
||
MNOTE_UI_BASE_URL=http://127.0.0.1:3428 node scripts/task426-mnote-web-main-no-reload-smoke.js
|
||
MNOTE_UI_BASE_URL=http://127.0.0.1:3428 node scripts/task438-filetree-title-md-active-reveal-smoke.js
|
||
MNOTE_UI_BASE_URL=http://127.0.0.1:3428 node scripts/task439-filetree-title-md-rename-smoke.js
|
||
MNOTE_UI_BASE_URL=http://127.0.0.1:3428 node scripts/task440-page-title-filetree-md-sync-smoke.js
|
||
MNOTE_UI_BASE_URL=http://127.0.0.1:3428 node scripts/task169-mindmap-realtime-smoke.js
|
||
```
|
||
|
||
真实浏览器结果文件:
|
||
|
||
- `tmp/task426-mnote-web-main-no-reload-smoke/result.json`
|
||
- `tmp/task438-filetree-title-md-active-reveal-smoke/result.json`
|
||
- `tmp/task439-filetree-title-md-rename-smoke/result.json`
|
||
- `tmp/task440-page-title-filetree-md-sync-smoke/result.json`
|
||
- `tmp/task169-mindmap-realtime-smoke/result.json`
|
||
|
||
本轮 P3 收口:
|
||
|
||
- 页面标题编辑反向同步到 File Tree `{title}.md` 已由真实浏览器 smoke `scripts/task440-page-title-filetree-md-sync-smoke.js` 覆盖:标题栏输入 `{title}` 后,File Tree 显示 `{title}.md`,Page Tree 与标题输入框保持 `{title}`,且不出现 `index:<documentId>` 行。
|
||
- `tree_shell/filetree_renderer.rs` 的默认 nested fixture 已从可见 `index.md` 子行改为页面 markdown row 下挂 mindmap asset;`rowKind=index` 仍只作为历史 projection / 命令兼容解析保留,不再作为默认 Convex File Tree UI 口径。
|
||
- 旧设计中的“可见 `index.md`”表述由本稿覆盖:后续 UI / smoke / review 应以 `doc:<documentId>` + `{title}.md` 作为 Convex 页面正文 row;如果文档讨论历史 `index.md`,必须明确它是兼容身份或历史记录,不是默认可见行。
|
||
|
||
仍保留的后续增强:
|
||
|
||
- 删除进垃圾箱、restore 后 reveal、双浏览器 resync、搜索过滤下 reveal 等更宽场景继续由 `4-28`、`4-34` 与后续 tree domain checklist 覆盖。
|