Persist PageTree expand state via control-plane view-state and align chevron/DOM with restored expansion; keep Sidex-style shallow page-tree scan and drop the unused recursive scanner that only added cargo noise. Add password vault workbench routes/runtime/skill/CLI, split page_ai_pi into a module package, and retire Hermes/ACP/OpenHub recycle + root harness evidence from the index while gitignoring recycle and local diag dumps. Archive superseded design/bugs docs under old/, point architecture at ARCHITECTURE.md, and refresh smokes for Pi S1–S7, vault, and editor regressions so the working tree can stay clean.
331 lines
27 KiB
Markdown
331 lines
27 KiB
Markdown
# [recycle] 4-11 [done] 树域 Rust 家族 final renderer 与宿主收薄清单 v1
|
||
|
||
> 更新时间:2026-04-26
|
||
>
|
||
> 前置文档:
|
||
> - `/mnt/Data1T/mnote/design/old/04-tree-domain/process/4-10-tree-rust-family-cutover-checklist-v1.md`
|
||
> - `/mnt/Data1T/mnote/design/old/04-tree-domain/process/4-9-tree-rust-family-cutover-remaining-architecture-and-capability-preservation-v1.md`
|
||
> - `/mnt/Data1T/mnote/design/03-rust-web/process/3-3-rust-web-tree-realtime-event-stream-v1.md`
|
||
>
|
||
> 口径覆盖(2026-04-28):此前 `rust_runtime_artifact_host`、Rust/WASM reducer runtime、Rust initial DOM 与 `task112/task113` smoke 通过,只能证明 reducer/runtime 阶段完成,不能证明 final Rust DOM renderer 完成。真正 final renderer 的硬门禁以 `/mnt/Data1T/mnote/design/04-tree-domain/done/4-18-tree-final-dom-shell-cutover-hard-gate-v1.md` 为准:默认真实流量必须不再依赖 `TreeShellIframeHost` 的 `iframe_srcdoc` DOM shell。
|
||
>
|
||
> 收口更新(2026-04-28):`task-010` 至 `task-013` 已完成上述 final DOM shell 硬门禁。`rust_family` 默认真实流量已切到 `rust_wasm_dom_shell_host` + `dom_wasm` bridge。
|
||
>
|
||
> 最终清理更新(2026-05-16):`TreeShellIframeHost` 与对应测试已移动到 `recycle/`,`NEXT_PUBLIC_TREE_SHELL_LEGACY_IFRAME_HOST` 入口已删除;active source 不再保留 iframe/srcDoc legacy/debug host。
|
||
|
||
## 1. 当前已收口基线
|
||
|
||
- `3000` 仍是唯一浏览器公开入口。
|
||
- `page tree / file tree / picker` 默认主路径已进入 `rust_family` 3000 `rust_wasm_dom_shell_host`。
|
||
- `3104` tree shell proxy 已退到显式 debug/internal 边界;默认主站 iframe 不再请求 `/api/tree/shell -> mnote-web:3104`,避免 `desktop:hot` 下页面树/文件树显示 `{"error":"fetch failed"}`。
|
||
- `file_tree` 主路径输入已收口到 `kernel file_tree items`。
|
||
- `buildVisibleRows` 不再从 `pageRows + assets` fallback 重建文件树对象语义。
|
||
- `streamDelta` 已可写入 `command_logs` 与 `domain_events`。
|
||
- Rust SSE 与 3000 同源 SSE 都能从单条新 `domain_event.payload.streamDelta` 直接产出 `delta`。
|
||
- Rust `bridge-runtime` 已具备 `tree.subtree.move` 的可测试 canonical move order plan。
|
||
- TS transport 已把 Rust `normalizedMove` 透传到 Convex `documents.move` 的可选校验边界。
|
||
- Convex `documents.move` 在收到 `normalizedMove` 时会先确认当前排序 patch plan 与 Rust plan 一致,再执行既有兼容写入。
|
||
- Rust SSE 与 3000 同源 SSE 对同一 `command_id` 的 command log + domain event 双写做 delta 去重:delta 一致时发一次 `delta`,不一致继续 `resync`。
|
||
- `tree.subtree.move` 已从 `replace_documents` 过渡 delta 推进到 `move_document` 细粒度 delta:
|
||
- Rust `bridge-runtime` 已输出 `streamDeltaHint(kind=move_document)` 与 `domainEventHint(tree.subtree.moved)`
|
||
- 3000 `/api/tree/commands` 按 Rust plan hint + mutation result 物化并写入 `move_document`
|
||
- 3000 SSE 与 Rust SSE 均保留 `documentId / parentId / sortOrder / updatedAt`
|
||
- 前端 `tree-delta` reducer 可用该 delta 重建 `sidebar_tree / page_tree / file_tree`
|
||
- `tree.node.restore / tree.subtree.copy` 已从 `replace_documents` 过渡 delta 推进到细粒度 upsert:
|
||
- `restore -> upsert_document`
|
||
- `copy -> upsert_documents`
|
||
- 前端 `tree-delta` reducer 可用 upsert delta 重建 `sidebar_tree / page_tree / file_tree`
|
||
- Rust `bridge-runtime` 已输出正式 tree domain event plan:
|
||
- `tree.node.created / renamed / archived / restored / purged / embedded`
|
||
- `tree.subtree.moved / copied`
|
||
- 每条正式 tree command plan 都带 `domainEventPlan(schema=mnote.tree.domain_event, schemaVersion=1, eventType, streamDeltaHint)`
|
||
- `recordBridgeCommandArtifacts` 会优先使用 Rust plan 透传的 event type / materialized streamDelta,而不是 `${command}.requested`
|
||
- `file_tree` 搜索过滤已接入 Rust projection query 主链:
|
||
- `KernelProjectionFilter.query` 已进入协议
|
||
- `KernelProjectionFilter.maxResults` 已进入协议
|
||
- Rust `kernel.project_view(file_tree)` 已能输出命中项与必要祖先
|
||
- Rust 搜索 projection 已按“直接命中项限流,必要祖先不计入上限”执行 `maxResults`
|
||
- `mnote-web` `/api/tree/projections/file?...&query=` 与 3000 同源 `/api/tree/projections/file` route 已覆盖
|
||
- `mnote-web` 与 3000 同源 route/client 已透传 `maxResults`
|
||
- Sidebar 搜索态只消费 Rust 搜索 projection items,旧宿主裁剪 helper 已移除
|
||
- tree realtime 主链已补强:
|
||
- command log 与 domain event 双写时,即使上一帧 cursor 来自另一条流,也会按时间排除旧 artifact 后再做同 `command_id` delta 去重
|
||
- 未识别 domain event payload 继续保守 `resync`
|
||
- TS artifact 写入边界已固定 `mnote.tree.domain_event` payload schema v1,并保留历史 snake_case 字段兼容 SSE
|
||
- resource command 主链已补强:
|
||
- `tree.resource.copy / tree.resource.move` 已由 Rust `bridge-runtime` 生成 `resourceTransferPlan`
|
||
- `tree.resource.upload` 已由 Rust `bridge-runtime` 生成 `resourceUploadPlan`
|
||
- Convex `mediaAssets.batchCopy / batchMove / createWithStorage` 已按 Rust plan 做可选一致性校验
|
||
- `tree.resource.copied / moved / uploaded` 已可生成 `asset_result -> upsert_assets` delta / domain event plan
|
||
- 3000 media batch/upload route 已改为调用 Rust artifact writer,按 Rust artifact plan 持久化 `command_logs / domain_events`
|
||
- Sidebar 对 copy / move / rename / delete / restore / upload 的资源请求已抽成 `file-tree/resource-command-client`
|
||
- file tree 内部 drop 已新增 Rust command preflight:
|
||
- `tree.filetree.drop.preflight` 由 Rust `bridge-runtime` 归一化 target、row 去重、doc/asset 分类、mindmap subPath、copy/move action
|
||
- Rust preflight 会拒绝页面移动到自身/后代
|
||
- preflight plan 已输出 `documentTransferPlan` 与 `resourceTransferPlan`,Sidebar 内部 drop 主路径改为先消费 Rust plan 再触发既有 transport
|
||
- 宿主仍负责实际命令触发、乐观 UI、副作用通知和错误提示
|
||
- file tree 删除目标已新增 Rust command preflight:
|
||
- `tree.filetree.delete.preflight` 由 Rust `bridge-runtime` 归一化 row 去重、父页面覆盖过滤、doc/asset 删除目标
|
||
- Rust delete plan 会在“选中父页面”时过滤其子页面附件,避免宿主重复构造 asset 删除列表
|
||
- Sidebar 删除确认与执行主路径改为先消费 Rust delete plan,再触发既有 document/media transport
|
||
- file tree 粘贴目标与复制分类已新增 Rust command preflight:
|
||
- `tree.filetree.paste.preflight` 由 Rust `bridge-runtime` 归一化剪贴板 row、目标页面、focused row、`doc/index` 复制递归语义、真实 asset 过滤和 mindmap `targetSubPath`
|
||
- 3000 同源 `/api/tree/filetree/paste-preflight` route 与 client 已覆盖
|
||
- `Sidebar` 在 `rust_family` 粘贴主路径先消费 Rust paste plan,再触发既有 `copyTreeCommand` / `copyFileTreeResourceAssets`
|
||
- file tree 外部上传目标已新增 Rust command preflight:
|
||
- `tree.filetree.upload-target.preflight` 由 Rust `bridge-runtime` 归一化 drop target、focused row、active doc、目标 workspace 与 mindmap `targetSubPath`
|
||
- 3000 同源 `/api/tree/filetree/upload-target-preflight` route 与 client 已覆盖
|
||
- `Sidebar` 外部文件 drop 主路径先消费 Rust upload target plan,再执行既有文件字节读取与 upload transport
|
||
- Rust tree shell 已新增可测试 renderer/state family 基础合同:
|
||
- `renderer_input` 覆盖 `page / filetree / picker` 的 projection items、expanded ids、focused id、selected row ids、active picker item、excluded ids、command dispatcher
|
||
- `mnote-web` tree shell HTML 已嵌入 Rust 构建的 `rendererInput` JSON contract,供 compat host 与后续 final renderer 共用;compat shell 初始 expanded / selection / picker active / exclude 已优先读取该合同
|
||
- 3000 默认 `TreeShellRustDomShellHost` 已对齐同一 projection model:`page / filetree / picker` 的 projection items 进入 DOM host model;`TreeShellIframeHost` 的 appState JSON 只保留在 legacy/debug host。
|
||
- `rendererInput` 已继续暴露三类 reducer contract:`rust_page_focus_keyboard_reducer_v1`、`rust_filetree_selection_reducer_v1`、`rust_picker_state_reducer_v1`
|
||
- `mnote-web /tree` debug shell 与 3000 legacy inline compat host 均已消费同名 reducer contract;当前默认 DOM host 已切到 `TreeShellRustDomShellHost`,并通过 WASM artifact 或同源 Rust reduce seam 执行 runtime。
|
||
- 前端 `TreeShellHost` 已暴露 `data-tree-renderer-contract=rust_renderer_input_v1`,主路径 `data-tree-host-implementation=rust_wasm_dom_shell_host`;`mnote_web_iframe_proxy`、`rust_inline_compat_host` 与 `rust_runtime_artifact_host` 不再代表默认主执行面
|
||
- `filetree_selection` 覆盖单选、多选、Shift 范围选、右键选中、清空、visible rows 归一化、drag row ids
|
||
- `picker_state` 覆盖高亮归一化、上下/Home/End、排除项、Enter pick
|
||
- `focus_state` 覆盖 focused id 归一化与 next/previous/home/end 行走
|
||
- `expansion_state` 覆盖默认展开、toggle、活动节点祖先展开
|
||
- `keyboard_state` 覆盖导航、打开、展开/折叠、上下文菜单 intent
|
||
- `drag_drop_state` 覆盖拖拽 payload 去重归一化与 copy/move effect
|
||
- `action_registry` 覆盖 page/filetree/picker 的可用动作集合
|
||
- `page_renderer` 已输出 `data-rust-page-renderer=initial_v1` 的 page tree 首屏嵌套 HTML,并由 compat runtime 优先 hydrate 该 Rust DOM;后续状态变化仍保留 JS 重绘路径
|
||
- `filetree_renderer` 已输出 `data-rust-filetree-renderer=initial_v1` 的 file tree 首屏嵌套 HTML;历史 3000 inline `srcDoc` 曾优先 hydrate 该 DOM,当前默认主路径已由 `TreeShellRustDomShellHost` 承载,legacy iframe 与 `mnote-web /tree` 仅保留 debug/internal 验证边界。
|
||
- `picker_renderer` 已输出 `data-rust-picker-renderer=initial_v1` 的 picker 首屏 HTML;历史 3000 inline `srcDoc` 曾优先 hydrate root/item DOM,当前默认主路径已由 `TreeShellRustDomShellHost` 承载,legacy iframe 与 `mnote-web /tree` 仅保留 debug/internal 验证边界。
|
||
- Rust artifact 写路径已开始落地:
|
||
- `bridge-runtime` 已新增 `RuntimeCommandArtifactPlan`,可从 Rust command plan + mutation result 物化 `commandLog` 与 `mnote.tree.domain_event` payload
|
||
- `mnote-web /api/tree/commands` 已在 Rust transport 内持久化 `bridgeLogs.recordCommandLog / recordDomainEvent`
|
||
- artifact 写入失败不会回滚已成功 mutation,响应会暴露 `artifactError` 供观测
|
||
- 3000 `/api/tree/commands`、`/api/media/batch`、`/api/media/upload`、文档类 `page/save/lifecycle/metadata/page-write/documents-block` Rust transport adapter 已改为调用 Rust artifact writer
|
||
- `documents.duplicate` 已补正式 `tree.node.duplicated` domain event plan,3000 duplicate 成功链不再回退 TS artifact helper
|
||
- 当前显式 adapter 层已不再直接调用 TS `recordBridgeCommandArtifacts(...)`
|
||
- file tree selection 的宿主职责已继续收薄:
|
||
- `rust_family` 下 `Sidebar` 不再把 legacy React file tree selection reducer 作为选择真相
|
||
- `Sidebar` 只从 `tree.filetree.selection.changed` 事件物化 renderer selection snapshot,删除 / 复制 / 粘贴 / 上传 / 内部 drop 只读消费该 snapshot
|
||
- `SidebarTreeSurface` 不再向 Rust file tree host 传 `selectedRowIds` 控制 prop,避免宿主反向控制 renderer selection
|
||
- compat runtime 在 file tree 重绘与可见行归一化后会继续回发 selection snapshot,避免宿主业务消费旧镜像
|
||
- compat runtime 的 Shift 范围选择与右键选中语义已对齐 Rust `filetree_selection` 合同:Shift 默认覆盖旧选择,Ctrl/Cmd+Shift 才叠加;右键已选中行保留多选集合
|
||
|
||
## 2. 已完成的 final DOM shell 硬边界与仍未完成项
|
||
|
||
- 2026-04-28 final DOM shell 硬门禁已完成:`TreeShellIframeHost` 的 `iframe_srcdoc` browser bridge 不再是默认 page tree / file tree / picker DOM shell。
|
||
- `page tree / file tree / picker` 3000 默认主路径已标识为 `rust_wasm_dom_shell_host`,默认浏览器 bridge 为 `data-tree-browser-bridge="dom_wasm"`。
|
||
- `TreeShellIframeHost` 已从 active source 移到 `recycle/`;旧 `LOCAL_TREE_SHELL_TEMPLATE` 与 `buildInline*Html` 不再参与 active 路径。
|
||
- 默认 DOM host 已通过 `TreeShellRuntimeRequest/Result` 消费 WASM artifact 或同源 Rust HTTP seam 返回的 state、hostEvents、commandEvents;本地 JS fallback reducer 不再参与默认真实流量。
|
||
- 仍未完成的是非 DOM shell 范围:`tree.subtree.move` 的 `normalizedMove` compat fallback 删除、`document.snapshot.saved` 独立事件、`tree.node.embed` Page Aggregate/Rust artifact 深层收口等历史条目的后续核验;legacy iframe host 清理已完成。
|
||
- 宿主仍承担文件字节读取、外部上传 transport、菜单状态、实际命令调度、乐观 UI 和副作用通知;资源 upload/copy/move、内部 drop preflight、粘贴 preflight、外部上传目标 preflight 的目标与 metadata plan 已进入 Rust command contract。
|
||
- `tree.subtree.move` 的 canonical sort plan 已在 Rust 产出并进入 Convex 可选校验,但实际排序写入仍由 Convex `documents.move` 执行。
|
||
- `file_tree` 搜索过滤主链已改为 Rust projection query;`maxResults / index.md / 附件 / 导图子附件 / book/pdf` 扩展 fixture 已补齐,后续仅保留更丰富搜索语义 hardening。
|
||
- `replace_documents` 已退为 restore/copy 的 fallback;主路径 restore/copy 已是 `upsert_document(s)`。
|
||
- Rust command 已生成 tree/resource delta / domain event plan;`mnote-web /api/tree/commands`、3000 tree/media 主写入口与文档类 Rust transport adapter 已可由 Rust artifact writer 落 `command_logs / domain_events`;block/save 复合命令已形成 `page.body.saved`、`block.patched`、`block.moved`、`block.embedded` formal domain-event contract。
|
||
- block/save 复合命令已并入正式 Rust artifact 主链;OnlyOffice/media writeback 已切到 Rust artifact writer。`document.snapshot.saved` 是否作为独立事件继续留在 `4-16`,不阻塞本文件 final renderer / host thinning 收口。
|
||
|
||
## 3. Phase F:final Rust renderer 接入
|
||
|
||
### 目标
|
||
|
||
把 `page tree / file tree / picker` 从 `TreeShellIframeHost` 内联 DOM compat shell,迁到正式 Rust family renderer。
|
||
|
||
状态:默认主路径已完成迁移,legacy iframe host 已移入 `recycle/`,不再作为显式排障开关保留。
|
||
|
||
### checklist
|
||
|
||
- [x] 固定 Rust renderer 输入合同:
|
||
- `projection items`
|
||
- `expanded ids`
|
||
- `focused id`
|
||
- `selected row ids`
|
||
- `active picker item`
|
||
- `command dispatcher`
|
||
- 当前已作为 `rendererInput` 嵌入 `mnote-web` tree shell HTML;runtime DOM shell 已优先读该合同初始化局部状态,但尚未完全改为只消费该合同。
|
||
- 历史 3000 inline host 已把 `rendererInput` 与 `items` 注入同一个 appState;当前默认 DOM host 直接消费 projection model,不再通过 `__MNOTE_TREE_SHELL_OVERRIDE__` 主路径注入第二份 items。
|
||
- 默认 DOM shell 已固定 `family=rust_family / version=1 / executionStrategy=dom_wasm`,并优先使用 3000 同源 `tree-shell-runtime` artifact;active source 不再保留 `browserBridge=iframe_srcdoc` host。
|
||
- runtime artifact manifest 已新增 `runtimeApi`,显式暴露 `TreeShellRuntimeRequest / TreeShellRuntimeResult`、state snapshot、DOM patch、host event、command event kind。
|
||
- runtime artifact manifest 已继续暴露 `reduceEndpoint=/api/tree/runtime/reduce`;3000 同源 thin proxy 与 mnote-web endpoint 保留为 fallback/debug。
|
||
- 正式 Rust/WASM reducer runtime 已落地:`tree-shell-runtime-wasm` 导出 `reduceTreeShellRuntime`,3000 主路径默认优先加载 `mnote-tree-shell-runtime.js` 与 `mnote-tree-shell-runtime_bg.wasm` 执行 reduce。
|
||
- [x] 拆出 renderer 内部模块:
|
||
- [x] row model 雏形(page/filetree/picker renderer test id contract)
|
||
- [x] page initial DOM renderer(首屏 page tree Rust HTML + hydrate contract)
|
||
- [x] filetree initial DOM renderer(首屏 file tree Rust HTML contract)
|
||
- [x] picker initial DOM renderer(首屏 picker Rust HTML contract)
|
||
- [x] expansion model(默认展开 / toggle / 祖先展开 state family)
|
||
- [x] focus model(focused id normalize / next / previous / home / end)
|
||
- [x] selection model(filetree selection state family)
|
||
- [x] picker state model(高亮 / Enter / exclude)
|
||
- [x] keyboard model(导航 / 打开 / expand-collapse / context menu intent)
|
||
- [x] drag/drop model(payload normalize / copy-move effect)
|
||
- [x] action registry(page / filetree / picker action set)
|
||
- [x] runtime facade(`TreeShellRuntimeRequest / TreeShellRuntimeResult` 可序列化 API,输出统一 DOM patch / host event / command event)
|
||
- [x] `page tree` 先切 final renderer。
|
||
- 当前状态:默认 page tree 由 `TreeShellRustDomShellHost` 渲染 DOM,focus / keyboard / expand / collapse / toggle / open / context menu / move command dispatch 均通过 `TreeShellRuntimeRequest/Result` 返回结果驱动。
|
||
- [x] `picker` 以轻量模式复用同一 renderer state family。
|
||
- 当前状态:默认 picker 由 `TreeShellRustDomShellHost` 渲染 DOM,keyboard command、hover focus、click pick 均通过 runtime state 与 hostEvents 驱动,测试已禁止默认 iframe postMessage 成功路径。
|
||
- [x] `file tree` 后切 final renderer,并保留 `doc / index / asset-folder / asset` 能力。
|
||
- 当前状态:默认 file tree 由 `TreeShellRustDomShellHost` 渲染 DOM,selection、context menu、open、internal/external drop 与 drop target 通过 runtime result/hostEvents 驱动,并保留 doc/index/asset-folder/asset row 口径。
|
||
- [x] compat host 退为 debug/internal fallback(本批可验证范围)。
|
||
- 3000 默认主路径不再请求 `/api/tree/shell -> mnote-web:3104`,也不再回退旧 React renderer;host implementation 已切到 `rust_wasm_dom_shell_host`;`mnote-web /tree` 仅保留显式 debug/internal 验证边界,`TreeShellIframeHost` 已进入 `recycle/`。
|
||
|
||
### 验收
|
||
|
||
- 默认真实流量不再以 `rust_inline_compat_host`、`rust_runtime_artifact_host` 或 `TreeShellIframeHost` 作为主路径标识;page / picker / filetree 默认路径已不再依赖 `iframe_srcdoc` 内联 DOM renderer。
|
||
- `data-tree-host-implementation` 不再以 `mnote_web_iframe_proxy` 代表主执行面。
|
||
- `page tree / file tree / picker` smoke 仍覆盖打开、键盘、选择、拖放、菜单。
|
||
|
||
## 4. Phase G:宿主状态机收薄
|
||
|
||
### 目标
|
||
|
||
宿主只保留挂载、认证、文件读取、transport bridge,不再解释树域交互合法性。
|
||
|
||
### checklist
|
||
|
||
- [x] 上传执行从 sidebar 宿主抽成正式 resource command。
|
||
- `tree.resource.upload` 已进入 Rust plan / domain event hint / `asset_result` delta
|
||
- 浏览器文件字节读取与 Convex upload URL 仍由 3000 upload route 执行
|
||
- [x] 内部 file tree drop payload 归一化进入 Rust command preflight。
|
||
- `tree.filetree.drop.preflight` 已接 3000 同源 route `/api/tree/filetree/drop-preflight`
|
||
- 前端 `file-tree/shell` 只负责从 projection row / parent snapshot 构造 preflight payload
|
||
- Rust plan 输出 target、mindmap subPath、doc/asset 分类、top-level doc、source asset documents、`documentTransferPlan`、`resourceTransferPlan`
|
||
- [x] copy/move 区分与非法投放校验进入 Rust command/projection contract。
|
||
- `tree.filetree.drop.preflight` 已按 `copy` 输出文档/资源 transfer plan
|
||
- 非 copy 的页面移动已在 Rust preflight 拒绝自身/后代投放
|
||
- 更完整的跨工作空间/混合对象权限校验仍随正式 Rust 写路径继续下沉
|
||
- [x] file tree 删除目标归一化进入 Rust command contract。
|
||
- `tree.filetree.delete.preflight` 已接 3000 同源 route `/api/tree/filetree/delete-preflight`
|
||
- `Sidebar` 删除链已先消费 Rust delete plan,再执行既有 `deleteDocumentCommand` / `deleteFileTreeResourceAssets`
|
||
- legacy `computeFileTreeShellDeleteTargets` 已退出生产代码,只保留 Rust preflight payload builder
|
||
- [x] file tree 粘贴目标推导与复制对象分类进入 Rust command contract。
|
||
- `tree.filetree.paste.preflight` 已接 3000 同源 route `/api/tree/filetree/paste-preflight`
|
||
- Rust paste plan 已输出 `docItems` 与 `resourceTransferPlan`,覆盖 `doc -> recursive true`、`index -> recursive false`、真实 asset 过滤和 mindmap `targetSubPath`
|
||
- `Sidebar` 的 `rust_family` 粘贴链已不再本地推导 `docItemsMap / copyableAssetIds`,只消费 Rust paste plan 后触发既有 transport
|
||
- [x] file tree 外部上传目标推导进入 Rust command contract。
|
||
- `tree.filetree.upload-target.preflight` 已接 3000 同源 route `/api/tree/filetree/upload-target-preflight`
|
||
- Rust upload target plan 已输出 `workspaceId / targetDocumentId / targetMindmapId / targetSubPath`
|
||
- `Sidebar` 的外部文件 drop 链已不再本地推导 target row / focused row / workspace fallback,只消费 Rust upload target plan 后执行既有 upload transport
|
||
- [x] 多选、范围选、右键选中状态进入 renderer state family。
|
||
- Rust `filetree_selection` 已有纯状态测试;`mnote-web /tree` 与 3000 legacy inline compat runtime 均已通过 `rust_filetree_selection_reducer_v1` 合同入口执行选择、右键、可见行归一化与 drag rows 解析;当前默认 DOM host 已通过 `TreeShellRuntimeRequest/Result` 消费 Rust/WASM runtime。
|
||
- [x] picker 高亮、Enter 选中、排除项规则进入 renderer state family。
|
||
- Rust `picker_state` 已有纯状态测试;`mnote-web /tree` 与 3000 legacy inline compat runtime 已通过 `rust_picker_state_reducer_v1` 合同入口执行 next/previous/home/end/pick/focus;当前默认 DOM host 已通过 `TreeShellRuntimeRequest/Result` 消费 Rust/WASM runtime。
|
||
- [x] 宿主不再持有 file tree 选择真相,只订阅 renderer state event。
|
||
- `Sidebar` 已拆分 legacy selection 与 renderer selection snapshot;`rust_family` 下只有 `handleFileTreeShellSelectionChange` 写入 renderer snapshot。
|
||
- `SidebarTreeSurface` 已移除 `selectedRowIds` 控制输入,file tree 选择只能由 renderer event 回报给宿主业务。
|
||
- 仍未完成:compat runtime 内部选择算法虽已统一为 `rust_filetree_selection_reducer_v1` 合同入口,但尚未替换为 Rust runtime/wasm 直接执行,继续归入 Phase F final renderer / runtime 收口。
|
||
|
||
### 验收
|
||
|
||
- sidebar 宿主不再包含主要 file tree selection truth 分支;drop legality 仍通过 Rust preflight + 宿主 transport 过渡执行。
|
||
- 上传和资源移动失败能返回稳定 command error,不依赖前端临时判断。
|
||
- renderer state 可独立测试,不需要挂载整个 sidebar。
|
||
|
||
## 5. Phase H:Rust 搜索 projection
|
||
|
||
### 目标
|
||
|
||
`file_tree` 搜索过滤不再由宿主裁剪 `kernel file_tree items`,而是由 Rust 输出搜索 projection。
|
||
|
||
### checklist
|
||
|
||
- [x] 定义 `file_tree.search` 基础输入:
|
||
- query
|
||
- root node
|
||
- include ancestors
|
||
- include assets
|
||
- [x] 补齐 `file_tree.search` 扩展输入:
|
||
- `index.md` 显式作为可搜索资源;仅自身命中时进入结果,祖先仅用于维持路径
|
||
- `maxResults` 限制直接命中项,必要祖先不计入上限
|
||
- [x] 定义搜索结果展开策略:
|
||
- 命中页祖先展开
|
||
- 命中 asset-folder 展开
|
||
- 空结果稳定空态
|
||
- [x] Rust projection fixture 覆盖:
|
||
- mindmap folder
|
||
- table
|
||
- 命中项必要祖先
|
||
- [x] 补齐搜索 fixture 扩展覆盖:
|
||
- `index.md`
|
||
- 附件
|
||
- 导图子附件
|
||
- book/pdf
|
||
- [x] 前端只消费搜索 projection items,不再计算 visible document ids。
|
||
|
||
### 验收
|
||
|
||
- 搜索态、空态、正常态都由同一 Rust projection contract 驱动。
|
||
- 前端不再持有搜索过滤的对象语义。
|
||
- Phase H 当前验收已满足;后续 richer search semantics 作为 hardening,不再阻塞主链。
|
||
|
||
## 6. Phase I:move 排序执行下沉
|
||
|
||
### 目标
|
||
|
||
把 `tree.subtree.move` 的排序执行从 Convex compat mutation 迁到 Rust kernel command 主链。
|
||
|
||
### checklist
|
||
|
||
- [x] Rust 已有 canonical move order plan 纯函数。
|
||
- [x] Rust command plan 已可输出 `normalizedMove`。
|
||
- [x] Convex `documents.move` 增加可选校验:若传入 Rust `normalizedMove`,执行前确认 patch plan 一致。
|
||
- [x] TS transport 对 `documents:move` 透传 `normalizedMove`,避免 Rust plan 停在不可观测状态。
|
||
- [x] Rust command plan 已产出正式 `tree.subtree.moved` domain event hint。
|
||
- [x] `mnote-web /api/tree/commands` Rust 写路径已可直接持久化正式 `tree.subtree.moved` domain event。
|
||
- [x] 前端/stream 不再由 Next route 写 `replace_documents` 作为 move 主 delta。
|
||
- [x] 细粒度 move delta reducer 同时更新 `sidebar_tree / page_tree / file_tree`。
|
||
- [x] `move_document` 不再由 Next route 按 action 手写;当前由 Rust `streamDeltaHint` + mutation result 物化。
|
||
- [x] `mnote-web /api/tree/commands` 的 `move_document` artifact 已由 Rust transport 直接生成/落库。
|
||
- [x] 3000 Next 主写入口已切到 Rust artifact writer。
|
||
|
||
### 验收
|
||
|
||
- sort order clamp、同父移动、跨父移动、重复 sort_order、null sort_order、created_at tie-break 均由 Rust 测试锁定。
|
||
- Convex 只作为存储执行层,不再持有唯一排序规则。
|
||
|
||
## 7. Phase J:正式 delta 主链
|
||
|
||
### 目标
|
||
|
||
树域 realtime 从 `replace_documents` 过渡 delta,推进到 Rust domain event 驱动的细粒度 delta。
|
||
|
||
### checklist
|
||
|
||
- [x] domain event 可携带并被 SSE 解释 `streamDelta`。
|
||
- [x] command log 与 domain event 同一 `command_id` 且 `streamDelta` 一致时,SSE 发一次 `delta` 而不是退回 `resync`。
|
||
- [x] 定义正式 domain event type hint:
|
||
- `tree.node.created`
|
||
- `tree.node.renamed`
|
||
- `tree.node.archived`
|
||
- `tree.node.restored`
|
||
- `tree.node.purged`
|
||
- `tree.node.duplicated`
|
||
- `tree.subtree.moved`
|
||
- `tree.subtree.copied`
|
||
- `tree.resource.copied`
|
||
- `tree.resource.moved`
|
||
- `tree.resource.uploaded`
|
||
- [x] 定义正式 domain event payload schema(TS artifact 写入边界与 Rust artifact writer 均固定 `mnote.tree.domain_event` v1)
|
||
- [x] Rust 侧由 command plan 生成 delta / event hint,Next route 不再按 action 拼主 delta / event type。
|
||
- [x] `mnote-web /api/tree/commands` Rust 写路径已由 command result 直接生成并持久化 domain event。
|
||
- [x] 显式 compat adapter 写入口已迁出 TS artifact transport。
|
||
- [x] block/save-snapshot 复合命令已补齐正式 Rust artifact / domain-event contract。
|
||
- 当前完成口径:`page.body.saved` payload 已携带 snapshot 摘要,`block.patched / block.moved / block.embedded` 已有 formal domain-event contract。
|
||
- `document.snapshot.saved` 是否拆成独立事件继续留在 `4-16`,不阻塞本阶段收口。
|
||
- [x] 前端 reducer 支持细粒度 move。
|
||
- [x] 前端 reducer 支持细粒度 restore/copy:
|
||
- `upsert_document`
|
||
- `upsert_documents`
|
||
- [x] 前端 reducer 支持资源 upsert delta:
|
||
- `upsert_assets`
|
||
- [x] 未识别事件继续保守 `resync`。
|
||
|
||
### 验收
|
||
|
||
- 新树命令默认有 domain event delta。
|
||
- `replace_documents` 仅作为 fallback,不再是 move / restore / copy 主路径。
|
||
|
||
## 8. 不做事项
|
||
|
||
- 不为了“看起来 Rust 化”把 React/DOM compat shell 原样翻译成另一层大壳。
|
||
- 不在 sidebar 继续新增树对象真相。
|
||
- 不把 `3104` 恢复成默认浏览器入口。
|
||
- 不在 Next route 继续扩写长期树命令语义。
|