Files
mnote/design/04-tree-domain/done/4-28-trash-restore-location-reveal-focus-v1.md
T
lix-2026 47e224d419 chore: align sqlite control plane architecture
- replace default Convex control-plane wording with Rust SQLite control-plane across architecture, AGENTS, Reasonix, and design docs

- retire root Convex functions source and deploy script into recycle while keeping explicit cloud/compat/sync-replica boundaries

- add control-plane migration guard/docs and keep CodeGraph refreshed after the SQLite control-plane cutover
2026-05-22 17:45:22 +08:00

7.3 KiB
Raw Blame History

4-28 Trash Restore Location Reveal Focus v1

状态:done 创建时间:2026-05-15 所属主线:04-tree-domain / VSCode Explorer 文件树与垃圾箱闭环 来源:design/10-review/07-vscode-explorer-filetree-trash-gap-review.md 阶段 6

1. 目标

页面进入垃圾箱前必须记录可恢复的位置快照;恢复时优先回到删除前父节点与排序位置,并在 UI 中 reveal / focus 恢复对象。若原父节点已不存在或仍在垃圾箱,应显式 fallback 到根目录并提示用户。

2. 已落地范围

  • documents schema 新增 restore_parent_idrestore_sort_order
  • documents.softDelete 对页面子树写入删除前 parent_id / sort_order 快照。
  • documents.restore
    • 读取同 workspace 当前文档集合。
    • 父节点仍存在或属于本次级联恢复子树时,恢复到原父节点。
    • 父节点已不存在或仍在垃圾箱时,恢复到根目录。
    • 恢复时回写 parent_id / sort_order,并对目标父级兄弟节点重新编号,避免恢复到原排序位时产生重复 sort_order
    • 清空恢复位置快照。
    • 返回 restore_location.parent_id / sort_order / fallback_reason
  • /api/tree/commands restore 读取页面 meta 时允许包含已删除页面,避免垃圾箱页面在进入 restore mutation 前被 getMeta 过滤成 404。
  • restoreDocumentCommand 透传恢复位置结果。
  • Sidebar 垃圾箱恢复后刷新数据、展开恢复父链、滚动并 focus 恢复行;发生 fallback 时提示“原父页面已不存在,已恢复到根目录”。
  • local folder 非 md 资源 restore 通过 mnote.pendingLocalFolderRestoreFiletreeRowId 记录恢复目标,filetree 在加载与轮询刷新时消费该 pending row-id 并聚焦恢复行。

3. 设计口径

  • 当前记录的是稳定数据位置:parent_id / sort_orderview path / expanded path 不持久化到 Convex,恢复后由 Sidebar 根据当前父链展开得到。
  • 子树恢复只恢复与父节点同一 deleted_at 批次的子节点,避免把更早独立删除的子页面误恢复。
  • fallback 策略先固定为“恢复到根目录 + 明示提示”,不在本阶段引入位置选择弹窗。
  • sort_order 会按当前兄弟数量 clamp;如果原位置越界,则放到可解释的最近位置。

4. 已验证

cd /mnt/Data1T/mnote/wolai-frontend
pnpm test src/lib/documents/tree-command-client.test.ts src/lib/documents/restore-location.test.ts src/components/sidebar/tree-shell-host.test.tsx src/components/sidebar/sidebar-sync.test.ts

结果:4 个测试文件通过,20 项测试通过。

补充 route 与排序回归:

cd /mnt/Data1T/mnote/wolai-frontend
pnpm test src/lib/documents/tree-command-client.test.ts src/lib/documents/restore-location.test.ts src/components/sidebar/tree-shell-host.test.tsx src/components/sidebar/sidebar-sync.test.ts src/app/api/tree/commands/route.test.ts

结果:5 个测试文件通过,32 项测试通过。

真实浏览器 smoke

cd /mnt/Data1T/mnote
node scripts/task429-trash-restore-location-reveal-smoke.js

结果:通过。脚本使用一次性用户 / workspace,在 3000 主入口验证;历史记录中曾需手工注册 Convex functions,当前默认 auth/session/control-plane 已切到 SQLite,不再要求先启动 Convex

  • 删除子页面后通过 tree.node.restore 恢复,返回 restore_location.parent_id 为原父页面、sort_order 为原排序位。
  • 原排序位已被兄弟页面占用时,兄弟页面被后移,Convex 中不产生重复 sort_order
  • 当前浏览器页面不刷新,恢复后的页面行重新出现在侧边栏树中。
  • 构造 restore_parent_id 指向缺失父节点的历史数据,恢复后返回 fallback_reason: parent_missing_or_deleted,并落到根目录。

局部 TypeScript 筛查:

cd /mnt/Data1T/mnote/wolai-frontend
pnpm exec tsc --noEmit --pretty false 2>&1 | rg "convex/documents.ts\\((160|161|162|163|164|165|166|167|168|169|170|171|172|173|174|175)"

结果:阶段 6 修改附近无新增 TypeScript 报错。全量 tsc 仍受仓库既有错误影响,未作为本阶段完成条件。

5. 2026-05-21 local folder restore focus 收口

5.1 Convex 页面(已有部分证据)

  • React Sidebar Drawer 的恢复后 DOM focus 仍需在 React 入口可稳定渲染页面树时补浏览器证据;当前 3000 主壳 smoke(task429)已覆盖"恢复后无需刷新可见 / reveal"fallback 提示仍由 React Drawer 代码路径负责。
  • 双浏览器 no-refresh:A 恢复页面后,B 不刷新即可看到位置变化;该项归入阶段 8 的实时刷新矩阵。
  • 已知源码问题:selectSidebarFileTreeDocument 存在且工作(create / navigate 路径调用),restore 路径仍未直接调用,但 local folder 已通过 pending row-id 消费实现恢复行 focus。

5.2 Local folder 非 md 资源 restore focus(已完成,Worker C 2026-05-21/22 复核)

当前状态:已完成。实现通过 pending row-id 传递与 filetree 轮询消费完成,审计证据及源码对照如下:

检查项 状态 证据来源
selectSidebarFileTreeDocument 是否从 restore 路径直接调用 采用 pendingLocalFolderRestoreFiletreeRowId + filetree 轮询消费,而不是 restore handler 直调
restore 后 filetree 行是否有 data-active / data-selected task473 通过结果:data-active="true"
restore 后是否 scrollIntoViewfocus() applyPendingLocalFolderRestoreFocusOnce 选中并滚动恢复行
trash workbench 的 restore 后 filetree 同步机制 轮询 1200ms layout.rs:1991-2004 (startLocalFolderSidebarWatch)
trash workbench 的 restore 后是否调用 refreshLocalFolderSidebarSnapshot 否,只调 refresh()(自身 workbench gateway.rs:1064-1065,但 filetree 轮询可消费 pending focus
selectSidebarFileTreeDocument 是否支持 local-file: row-id 目前 focus 由 selectSidebarFileTreeRowByIdlocal:asset: row-id 完成

5.3 已采用方案(供后续复核)

  1. 短路径trash workbench inline script 的 restore .then() 中写入 mnote.pendingLocalFolderRestoreFiletreeRowId
  2. filetree 消费refreshLocalFolderSidebarSnapshot() 与初始 render 共享 schedulePendingLocalFolderRestoreFocus(),在 filetree 加载/轮询后聚焦恢复行。
  3. smoke 回归task473 已从已知缺口回归改为可检测正向 smoke,最终应观察到 data-active="true" 或其他 focus 证据出现。

5.4 2026-05-21/22 浏览器复核

  • Codex 独立复跑 node --check scripts/task473-local-folder-trash-restore-no-refresh-focus-gap-smoke.jsMNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3012 node scripts/task473-local-folder-trash-restore-no-refresh-focus-gap-smoke.jsok:truerestoreNavFree.delta=0fileReappearsInFiletree=truefocusAfterRestore.attrs.data-active="true"
  • Reasonix Browser Worker 输出目录:tmp/reasonix-batch-a-browser-task473-2026-05-21/,初始结论为 PASS_WITH_KNOWN_GAP;本轮修复后,Codex 本地 smoke 已验证 gap 关闭。
  • 复核结论:local folder 非 md 资源 restore 后 no-refresh、行恢复与 focus/reveal 均已完成,可归档到 done/