# 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:<documentId>` + `{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:<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 覆盖。
