Files
mnote/bugs/04-tree-domain/done/4-27-filetree-open-reveal-active-row-jump-v1.md
T
lix-2026 274779c0d8 fix(tree): stabilize filetree mindmap switching
Open filetree mindmap assets inside the primary document pane so the sidebar root is not rebuilt during rapid mindmap/page switching.

Keep filetree active rows on doc:<documentId> and asset:<mindmapId>, shorten generated mindmap filenames, and preserve legacy index rows only as compatibility input.

Add task438-task445 browser smokes and close the 4-27/4-38/4-39/4-40 tree-domain bug records.
2026-05-16 07:11:06 +08:00

148 lines
9.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 4-27 [done][bug] File Tree 打开对象后 active / reveal 跳动 v1
> 更新时间:2026-05-16
>
> 分类归属:
> - `04-tree-domain/done`
> - 涉及边界:`05-editor-mainline/object editor active identity`、`file_tree row selection/focus/reveal`
>
> 用户反馈:
> - “目前文件树中思维导图点击后文件树会闪一下,然后跳到对应的 index。”
> - “文件树中,点击页面下方(滚动条到下部分)的页面,有时候会跳一下,然后就看不到点的那个文件里。”
## 1. 问题定义
File Tree 中点击某个对象后,树 UI 会发生短暂闪烁或滚动跳动,最终 active / reveal 位置可能落到同页面的 `index.md`,或者滚动到别处导致刚点击的文件不可见。
这类问题不是简单的路由跳转慢,而是 `file_tree row click -> object open intent -> active object identity -> selection/focus/reveal -> virtual/DOM tree render` 链路不一致。尤其是 mindmap asset 点击后如果页面 active identity 被降级成 document/index identity,就会把用户刚点击的 asset 行“纠正”为同页面 `index.md` 行,表现为文件树闪一下并跳到 index。
## 2. 初步判断
当前不应直接把它当成 `5-10 mindmap 污染 index.md` 的重复问题。`5-10` 已经关闭的是“mindmap 打开/保存污染正文真源”;本问题关注的是打开动作之后 File Tree 的 active row 和 scroll reveal 是否稳定。
更可能存在三类根因:
1. `tree.asset.open` 打开 mindmap 后,主编辑区 active state 只回传 `documentId`,没有保留 `objectIdentity=resource:mindmap:{documentId}:{assetId}`,导致 File Tree active row 映射回 `index:{documentId}`
2. 点击文件树下半部分页面时,导航成功、sidebar snapshot 校准、tree live delta/resync 或本地 projection rebuild 触发了 reveal 当前文档逻辑,未区分“用户刚点击的 row”与“路由推导出的默认 index row”。
3. File Tree 在大列表滚动区域内重新渲染时,expanded/selected/focused/active/reveal 状态没有按 rowId 稳定合并,导致 scroll anchor 丢失,用户看到跳动后目标行被滚出视口。
## 3. 与既有 done bug 的关系
关联但不重复:
- `bugs/05-editor-mainline/done/5-10-mindmap-filetree-index-single-truth-split-v1.md`:关闭 mindmap object 与 `index.md` 正文真源混淆。
- `bugs/04-tree-domain/done/4-26-sidebar-filetree-projection-refresh-stalled-after-tree-asset-mutations-v1.md`:关闭新建/删除/新建 mindmap 后 File Tree 不刷新的问题。
本 bug 的新增点是:对象已经可见、也能打开,但打开后的 active/reveal/scroll 行为仍不稳定。
## 4. 建议复现路径
1. 登录真实测试账号,进入 `http://localhost:3000/` 或主文档页。
2. 新建测试页面 `TEST-FILETREE-REVEAL-<timestamp>`,在页面内新建一个 mindmap。
3. 在 File Tree 中滚动到该页面及其 mindmap asset 行。
4. 点击 mindmap asset 行。
5. 记录点击前 rowId、点击后 active rowId、scrollTop、URL、主编辑区 object identity。
6. 期望:active row 应保持在 mindmap asset 或明确的 object tab row,不应自动跳到 `index:{documentId}`
7. 在长列表中滚动到下半部分,点击一个页面或其 `index.md`
8. 期望:导航后目标 row 仍在视口内,不能被 reveal 到其它位置后消失。
## 5. 验收标准
- 点击 File Tree 的 mindmap asset 后,不刷新浏览器,不发生无解释的整树闪烁。
- 点击 mindmap asset 后,File Tree active / selected / focused row 不得被降级成同页面 `index.md`,除非产品明确决定普通点击只打开 index;如果这样决定,UI 必须不展示为 asset 被打开。
- 点击长列表下半部分页面或 `index.md` 后,目标 row 在导航完成和 projection 校准后仍保持可见。
- `reveal` 逻辑必须区分用户点击产生的 explicit focus 与路由/文档 active 推导的 implicit reveal,不能让 implicit reveal 覆盖刚发生的 explicit focus。
- 增加真实浏览器 smoke,至少记录:点击前后 rowId、active object identity、scrollTop 变化、是否发生 navigation/reload、最终目标 row 是否在 viewport 内。
## 6. 建议修复方向
先补 smoke 复现,不要直接改实现。修复时优先沿以下边界排查:
1. File Tree row click 发出的 `tree.asset.open` 是否携带完整 `objectIdentity`
2. 主编辑区打开 mindmap object editor 后是否把 active identity 回传给 Sidebar / File Tree。
3. `index:{documentId}` 的 active 高亮是否只在打开 page/index 时生效,而不是覆盖 asset object。
4. 本地 projection rebuild 后是否按 rowId 恢复 expanded/selected/focused/reveal,不按 documentId 粗粒度重置。
5. scroll reveal 是否只在目标 row 不在 viewport 内时执行,并避免重复 reveal。
## 7. 当前状态
当前状态:`done`
2026-05-15 已随 `4-35` P0/P1 完成第一层止血:
- Convex File Tree 页面正文 row 不再显示为页面容器下的 `index.md`,而是直接显示为 `新页面.md` 这类 markdown row。
- 主文档壳本地 create apply 不再手工插入 `index:<documentId>` 行。
- 当前文档的 File Tree selected / focused 默认从 `index:<documentId>` 改为 `doc:<documentId>`
- 新增真实浏览器 smoke`scripts/task438-filetree-title-md-active-reveal-smoke.js`,覆盖新建页面后没有 `index.md` 行、页面正文 row 显示 `.md`、点击长列表中的页面 row 后 selected 留在 `doc:<id>`,且目标 row 仍在视口内。
- P2 继续补齐 File Tree inline rename:输入 `{title}.md` 时只把 `{title}` 作为页面标题提交,File Tree 仍显示 `{title}.md`;非法字符和同级重名会在输入框内提示,不触发 rename command。
已通过:
```bash
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
```
结果文件:`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`
关键结果:
- `ok=true`
- `beforeClick.indexRowExists=false`
- `beforeClick.rowTitle="新页面.md"`
- `afterClick.selectedRows=[{"rowId":"doc:<documentId>", ...}]`
- `afterClick.visible=true`
- `task439.afterRename.fileTreeTitle="<title>.md"`
- `task439.afterRename.pageTreeTitle="<title>"`
- `task439.invalidState.validationText="文件名不能包含 / \\ : * ? \" < > |"`
- `task439.duplicateState.validationText="同级已存在同名页面"`
- `task440.after.fileTreeTitle="<title>.md"`
- `task440.after.pageTreeTitle="<title>"`
- `task440.after.indexRowExists=false`
2026-05-15 P3 口径补充:
- 后续 File Tree active / reveal 修复不再以可见 `index.md` 行为目标;Convex 页面正文默认 row 是 `doc:<documentId>`,显示标题为 `{title}.md`
- `rowKind=index` 只作为历史 projection / command 兼容输入保留,不能重新作为默认 UI 可见行引入。
2026-05-15 P4 子问题关闭:
- mindmap asset 点击后 active / selected 从 `asset:{mindmapId}` 闪回父页面 `doc:{documentId}` 的子问题已单独关闭,记录见 `bugs/04-tree-domain/done/4-38-filetree-mindmap-click-active-row-flicker-v1.md`
- 修复点:`/mindmap/{documentId}/{mindmapId}` 对象页的 SSR filetree active row 改为 `asset:{mindmapId}`;客户端 tree live snapshot renderer 也按 `/mindmap` 路由恢复 `asset:{mindmapId}`,不再只按 `doc:{documentId}` 选中。
- 真实浏览器 smoke`MNOTE_UI_BASE_URL=http://127.0.0.1:3428 node scripts/task443-filetree-mindmap-click-active-row-smoke.js`,结果文件 `tmp/task443-filetree-mindmap-click-active-row-smoke/result.json`
2026-05-16 当前口径关闭:
- mindmap asset 连续点击和其它页面切换时,不再通过整页 `/mindmap/...` 导航重建 sidebar;主文档壳新增 `openPrimaryMindmap()`,在当前 primary pane 内挂载 mindmap 对象,并保持 `#sidebar-file-tree-root` DOM 根节点稳定。
- mindmap asset 选中语义保持为 `asset:{mindmapId}`,不会回落到父页面 `doc:{documentId}` 或历史 `index:{documentId}`
- Convex 页面正文默认 row 继续保持 `doc:{documentId}`,显示为 `{title}.md`;历史 `index` 只作为兼容输入,不再作为默认 UI active / reveal 目标。
- mindmap 文件名从 `mindmap-mindmap_...json` 这类技术名收敛为 `思维导图-xxxx.json`,避免连续切换时标题重绘造成观感跳动。
- 关闭记录见:
- `bugs/04-tree-domain/done/4-38-filetree-mindmap-click-active-row-flicker-v1.md`
- `bugs/04-tree-domain/done/4-39-filetree-create-title-cache-md-conflict-v1.md`
- `bugs/04-tree-domain/done/4-40-filetree-mindmap-switch-flicker-and-short-title-v1.md`
新增 / 复用真实浏览器 smoke:
```bash
MNOTE_UI_BASE_URL=http://127.0.0.1:3428 node scripts/task443-filetree-mindmap-click-active-row-smoke.js
MNOTE_UI_BASE_URL=http://127.0.0.1:3428 node scripts/task444-filetree-create-mindmap-title-cache-smoke.js
MNOTE_UI_BASE_URL=http://127.0.0.1:3428 node scripts/task445-filetree-mindmap-switch-no-flicker-smoke.js
```
`task445` 关键结果:
- `before.mindmapTitle = "思维导图-7712.json"`
- `afterMindmap.fileRootStable = true`
- `afterPage.fileRootStable = true`
- `afterMindmapAgain.fileRootStable = true`
- `failures = []`
后续如再次出现,应拆成独立新缺陷跟踪:
- 更长列表、搜索过滤、恢复后 reveal、双浏览器 resync 场景下是否仍稳定。
- 明确复现的“点击文件树下半部分页面后跳一下导致看不到刚点击文件”的滚动锚点问题。