# [recycle] 4-35 [done] 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. 当前状态 当前状态:`done` 本文已从 `process/` 迁入 `done/`。Convex File Tree 默认可见页面正文行当前以 `doc:` + `{title}.md` 为准;旧设计稿中的可见 `index.md` 仅作为历史 object identity / 兼容语义理解,不再作为默认 UI 模型继续派生新任务。 2026-05-15 已完成 P0/P1/P2 的最小切片: - `bridge-runtime` 的 Convex `file_tree` projection 不再为页面正文派生可见 `index.md` row。 - Convex 页面正文 row 继续使用 `rowKind=document` / `rowId=doc:`,但标题改为 `{pageTitle}.md`。 - mindmap / attachment / table 等资源继续挂在 `doc:` 下。 - 3000 主文档壳本地 create apply 同步改为只插入 `doc:`,标题显示为 `{title}.md`,不再插入 `index:`。 - File Tree 当前页面 selected / focused 默认改为 `doc:`。 - `task169` 的“回到页面正文”验证从点击 `index:` 改为点击 `doc:`,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:` 行。 - `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:` + `{title}.md` 作为 Convex 页面正文 row;如果文档讨论历史 `index.md`,必须明确它是兼容身份或历史记录,不是默认可见行。 仍保留的后续增强: - 删除进垃圾箱、restore 后 reveal、双浏览器 resync、搜索过滤下 reveal 等更宽场景继续由 `4-28`、`4-34` 与后续 tree domain checklist 覆盖。