35 KiB
4-10 [recycle] 树域 Rust 家族化执行清单 v1
更新时间:2026-04-26
回收说明(2026-04-28):本文是早期总执行清单,未完成项已被
4-11、4-16、4-18拆分并覆盖。本文保留为历史执行口径,不再作为当前process入口;后续以design/04-tree-domain/process/4-16-tree-rust-family-remaining-final-runtime-checklist-v1.md及done/4-11、done/4-18为准。关联文档:
/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/04-tree-domain/done/4-sidebar-pagetree-filetree-rust-web-rebuild-v1.md/mnt/Data1T/mnote/design/04-tree-domain/done/4-2-sidebar-pagetree-filetree-product-interaction-contract-v1.md/mnt/Data1T/mnote/design/04-tree-domain/done/4-6-tree-command-protocol-cutover-stage2-v1.md/mnt/Data1T/mnote/design/03-rust-web/process/3-3-rust-web-tree-realtime-event-stream-v1.md/mnt/Data1T/mnote/design/04-tree-domain/done/4-11-tree-rust-family-final-renderer-and-host-thinning-checklist-v1.md/mnt/Data1T/mnote/ARCHITECTURE.md
1. 文档目的
这份清单只服务一件事:
把树域从“Rust 持有语义、React 持有主执行面”的过渡态,推进到“Rust 家族同时持有语义与主执行面”的下一阶段。
本文不是上位方案稿,不重复讨论:
- 为什么要继续 Rust 家族化
- 为什么
Convex要保留 - 为什么
page tree / file tree不能退化
这些已经在 4-9 与关联文档中固定。
本文只给:
- 分阶段执行顺序
- 每阶段 checklist
- 每阶段阻塞项
- 每阶段验收口径
2. 当前基线
当前默认真实状态(2026-04-25):
- 树域 truth 已收口到
tree-first graph kernel Convex继续提供存储、同步、内容与实时底座mnote-web已具备 projection / command / transport 基础出口page tree / file tree / picker已开始消费 projection family- 主站默认
treeRendererFamily已切到rust_family page tree / file tree / picker默认主路径已进入 same-origin iframe compat host- 但当前切主的是
compat host,不是 final Rust renderer;宿主与 fallback 仍保留
因此本清单的目标不是“再做一轮 projection 收口”,而是:
- 补齐 Rust 侧剩余契约
- 迁掉主路径 React 树 renderer
- 保住
page tree / file tree现有能力
3. 总体执行顺序
建议固定为下面五段:
Phase A:补齐file_treeRust 直接输出Phase B:完成tree.*命令主路径切换Phase C:补齐树域正式 realtime contractPhase D:迁移page treeRust 家族 rendererPhase E:迁移file tree / pickerRust 家族 renderer,并收薄宿主
原因:
- 先补
file_tree输出,避免 renderer 迁移时继续背前端 adapter。 - 先收命令与 realtime,避免 UI 壳迁完后还卡在旧协议。
page tree先迁,能先验证主行 renderer 与交互骨架。file tree后迁,避免最复杂对象面成为第一块落地风险点。
4. Phase A:补齐 file_tree Rust 直接输出
当前进度(2026-04-25)
- 前端
file tree主路径已优先消费 RustkernelFileTreeProjection.items /api/sidebar、SSR loader、/api/mnote-web/stream已补kernelFileTreeProjection- 非过滤主路径不再默认依赖
pageRows + assetsByDoc拼装 file tree同源 host 的 inline override 已收口到kernel file_tree itemsinlineFileTreeRows旧注入边界已移除TreeShellIframeHost不再接受FileTreeRow[]作为正式 inline renderer 输入
- 搜索过滤态不再回退
pageRows + assets二次重建buildVisibleRows缺少kernel file_tree items时稳定返回空列表- 旧
filterKernelFileTreeProjectionItems宿主裁剪 helper 已移除 - 正式 Rust 搜索 projection 已在
4-11 Phase H接入 3000 同源主链
目标
让 file_tree 不再依赖当前前端在 pageRows + assets 上继续拼主路径可见行语义。
checklist
- 盘点当前
file_tree仍由前端 adapter 补齐的对象语义:index.md- 附件
mindmap文件夹- 导图子附件
- 表格 / 书籍 / PDF 等扩展对象
- 在 Rust 侧明确
file_treeprojection 的正式输出字段:resource_kindasset_kindicon_hintexpandableexpanded_by_default
- 为
index / asset / asset_folder / mindmap / table / book / pdf补 fixturetree-delta.test.ts已覆盖 delta 重建后的mindmap / pdf / book / tableroute.test.ts已覆盖 snapshot route 中的index / asset / asset-folder / mindmap / table / book / pdf
- 为
file_treeroute 增加契约测试/api/mnote-web/streamroute snapshot 已断言file_treeitem contract 与iconHint
- 把前端
file tree的主路径输入改成“消费 Rust 直接输出的 item 列表”,而不是继续拼对象真相sidebar主链默认输入已收口到kernelFileTreeProjection.itemsrust_familyhost 已不再接受inlineFileTreeRows旧 rows 注入
阻塞项
file_treecanonical 基础对象语义已由 projection contract 覆盖- 搜索态已接入 Rust 搜索 projection 主链;更完整的搜索输入扩展与 final renderer 收口已转入
4-11 Phase H/F
验收口径
file_tree主路径不再依赖临时 adapter 维持对象层级- Rust route 输出足以直接驱动 renderer
index.md / asset-folder / asset行语义在 fixture 与 route test 中稳定存在
5. Phase B:完成 tree.* 命令主路径切换
当前进度(2026-04-25)
- 已确认正式命令名:
create -> tree.node.createrename -> tree.node.renamemove -> tree.subtree.move
- 已新增前端
/api/tree/commandsroute,主路径可直接发正式 tree action createDocumentCommand已切到/api/tree/commandsrenameDocumentCommand已切到/api/tree/commandsmoveDocumentCommand已切到/api/tree/commandsdelete / restore / purge / embed / copy-tree已切到/api/tree/commandscreate保留本地ensureDocumentScaffold副作用,不因切流丢失/api/documents/create已收薄为 compat alias,转发/api/tree/commands/api/documents/title已按命令语义分流:- 树重命名兼容请求 ->
/api/tree/commands page.head.updateTitle->page write
- 树重命名兼容请求 ->
/api/documents/move已收薄为 compat alias,转发/api/tree/commands/api/documents/delete / restore / purge / embed / copy-tree已收薄为 compat alias,转发/api/tree/commandsbridge-runtime已补tree.node.archive / restore / purge / embed与tree.subtree.copy别名执行计划tree.*主路径已补路由 / shared client / compat alias / runtime 回归测试move的 target-parent legality、self / descendant 拦截、canonicalparent_id / sort_order / workspace_id / updated_at已下沉到 Convexdocuments.movemove legality已继续离开 Next route 结果拼装,开始转到 Rust bridge-runtime 计算/api/tree/commands不再自己构造movePreflight- Next route 现在只把
documents快照作为preflightData交给 Rust - Rust bridge-runtime 已可从快照推导 target parent / ancestor chain 并拒绝 descendant / missing-parent
move legality与更完整排序规则仍未完全进入最终 Rust kernel 主链;当前实际写入仍停留在 Convex compat mutation- Rust
bridge-runtime已新增 canonical move order plan,并在 command plan 中输出normalizedMove - Convex
documents.move已增加normalizedMove可选校验,执行前确认当前 patch plan 与 Rust plan 一致 - 最终 Rust 写路径执行下沉仍转入
4-11 Phase I
- Rust
目标
让树域主路径默认走正式 tree.* 命令面,documents.* 退到 compat。
checklist
- 盘点当前主路径仍在调用的
documents.*入口:- create
- rename
- move
- archive
- restore
- purge
- embed
- copy-tree
- 明确每条命令对应的
tree.*正式名 - 前端 shared command client 默认发
tree.* documents.*仅保留 alias / compat- 把 move legality、target parent、position normalize 继续从 Convex compat mutation 回收给 Rust kernel
- legality 与 normalized order plan 已在 Rust 可测试;TS transport 已透传
normalizedMove;Convex 已可选校验 Rust plan;最终写入仍待迁移
- legality 与 normalized order plan 已在 Rust 可测试;TS transport 已透传
- 为
tree.*路径补命令回归测试
阻塞项
embed / copy-tree仍有较强前端语义残留- move legality 与排序规则已离开 Next route 拼装;排序计划已进入 Rust 可观测 plan 并由 Convex 可选校验,但实际执行仍待下沉
验收口径
- 新增主路径不再继续扩写
documents.* - 主调用路径默认发
tree.* - 组件层不再承担长期树命令语义解释
6. Phase C:补齐树域正式 realtime contract
当前进度(2026-04-24)
tree stream前端主链已从“freshness 仲裁”收口到“stream live 优先,query/fallback 兜底”usePreferredSidebarSnapshot已按treeStream.status做主链选择:live / connecting / idle有 stream 数据时优先 streamfallback时回退 query
useSidebarData手动refetch已收窄为临时 snapshot:- 仅在当前 live 基线未变化时暂时覆盖
- live 基线变化后回到新的 live snapshot
/api/mnote-web/stream已升级为同源长连接 SSE:- 首帧固定发
snapshot - 后续按最新
command_logcursor 轮询检测变化 - 当前已支持最小
delta/noop/resync分流,不伪造不稳定的复杂树增量
- 首帧固定发
- stream cursor 已从“复用 overview 分页 cursor”改成“stream 自己的 latest command-log cursor”
- stream cursor 已补
domain_events感知- 当
command_logs未推进、但domain_events新增时,不再静默停在旧 cursor - 当前最小口径先触发
resync,避免 workspace realtime 漏感知事件侧推进
- 当
workspace / subtree双 scope 已在 route + test 形成正式 envelope 边界:workspace -> sidebar_treesubtree(rootNodeId) -> page_tree
/api/tree/commands主路径已开始写入bridgeLogs:- create / rename / archive / purge / embed / copy / move / restore 都会推进 stream 可见 cursor
- failed mutation 也会写 failure artifact,但 stream 主查询已收窄为只看
succeeded
- stream 当前已能消费显式
streamDelta:create -> upsert_documentrename -> upsert_document(局部 patch)page.head.updateTitle -> upsert_document(局部 patch)archive / purge -> remove_documentembed -> noopmove -> move_document(细粒度 delta)restore -> upsert_document(细粒度 delta)copy -> upsert_documents(细粒度 delta)documents列表不可用时,仍保留replace_sidebar / replace_documents作为兼容兜底
move / restore / copy已从“只能纯resync”推进到显式 deltamove已推进到move_documentrestore已推进到upsert_documentcopy已推进到upsert_documents- Rust
bridge-runtime已开始输出streamDeltaHint,Next route 仅按 Rust plan hint 与 mutation result 物化 delta
- 已新增树域 renderer 统一 delta 应用边界:
tree-delta.ts已补applyTreeStreamDeltaToProjectionStatesidebar_tree / page_tree / file_tree已可从同一 delta 边界派生各自输入
replace_documentsdelta 重建时已保留mindmapAssetChildrenfile_tree不再把asset-folder在 delta 重建时退化成普通asset
- Rust / 3000 同源 stream 已能从单条新
domain_event.payload.streamDelta发出delta - Rust / 3000 同源 stream 已能对同一
command_id的 command log + domain event 双写做 delta 去重streamDelta一致时发一次delta- 不一致或多条混杂时继续保守
resync
- Rust command 侧已开始生成正式 tree delta / event hint:
streamDeltaHint覆盖 create / rename / archive / restore / purge / embed / move / copydomainEventHint覆盖tree.node.created / renamed / archived / restored / purged / embedded / duplicated与tree.subtree.moved / copied- Next route 不再按 action 手写主 delta,而是按 Rust plan hint + mutation result 物化
streamDelta
- resource command 侧已开始进入正式 delta / event hint:
tree.resource.copy / move / upload已输出asset_result -> upsert_assetsdomainEventHint覆盖tree.resource.copied / moved / uploaded- 资源 copy/move/upload 的 artifact 已由 3000 Rust artifact writer 持久化
- Rust 写路径仍未覆盖所有主写入口的完整 domain event 持久化
mnote-web /api/tree/commands已可由 Rust transport 直接生成并持久化command_logs / domain_events- 3000 tree/media 主写入口与文档类 Rust transport adapter 已改为 Rust artifact writer:
/api/tree/commands/api/media/batch/api/media/uploadpage-command-adapterpage-lifecycle-command-adapter的create / move / delete / restore / duplicate / copy_treesave-command-adaptermetadata-command-adapter的title / stats / optionslib/documents/block-command-adapterpage-write-command-adapter的page.head.updateTitle / page.layout.updateOptions / page.body.save
- 当前显式 adapter 层已不再直接调用 TS
recordBridgeCommandArtifacts(...)
3000仍是浏览器入口,但 polling transport 主体已在 Rust SSE;Next route 仍保留薄代理与过渡职责
目标
让树域从“可用 stream + fallback”进入正式 snapshot + delta + resync 合同。
checklist
- 明确 workspace 与 subtree 两类 stream scope
- 统一
snapshot / delta / resync / cursor协议snapshot / cursor已在同源 stream route 固定delta已支持最小显式 contract,但复杂树变更仍待 Rust 正式事件面补齐resync仍是复杂树变更与漂移场景的保守回退- 当前勾选口径以
3000同源 Next route contract 为准,server.ts/server.test.ts/route.test.ts已覆盖;Rust 原生 transport 仍待继续收口
- 补 cursor 漂移与 resync 策略
- 明确大漂移时的回退口径
- 当前保守口径:无法稳定解释的新 cursor 统一退
resync snapshot
- 当前保守口径:无法稳定解释的新 cursor 统一退
- 为
page tree / file tree / sidebar_tree补事件 fixtureserver.test.ts已覆盖sidebar_treesnapshot / delta / resyncroute.test.ts已覆盖page_treesubtree snapshot fixtureroute.test.ts与tree-delta.test.ts已覆盖file_tree的document / index / asset-folder / asset行语义
- 为树域 renderer 定义统一 delta 应用边界
- 当前口径是
documents/sidebar -> projection state的统一边界,不等于 final Rust domain-event 细粒度 reducer
- 当前口径是
- 让主路径不再主要依赖 freshness 选择与补偿式 refetch
- sidebar stream 主链已变成“长连接 snapshot/delta/resync”
- query/fallback 继续保留为护栏
阻塞项
- Next route 级过渡 transport 仍在承担一部分正式职责
deltapayload 的对象覆盖面还不够完整- Rust command 已给
move / restore / copy输出 delta/event hint;3000 tree/media 主写入口 artifact 持久化与显式 adapter 层已切到 Rust artifact writer;剩余未完成项转入更正式的 block/save-snapshot domain-event contract 收口 - final domain event delta contract 已转入
4-11 Phase J
验收口径
- 树域实时主链存在正式协议说明与测试
- 主路径能稳定处理 snapshot、delta、resync
- fallback 成为护栏,而不是主同步策略本身
7. Phase D:迁移 page tree Rust 家族 renderer
当前进度(2026-04-24)
- 前端
page tree / file treesurface 已显式暴露rendererFamily边界 SidebarTreeSurface已成为更正式的 renderer host 边界,而不只是隐式 React 直连- 已新增
TreeShellHost,page tree / file tree / picker在rust_family下会进入显式宿主分支,而不再只是透传data-renderer-family - 主站已存在极薄挂载位;当前
rust_family分支先受控包住 React fallback,后续再替换成真正的 Rust family renderer - 已新增
3000同源/api/tree/shellproxy route;浏览器不再直接持有mnote-webbase url - 未过滤
page tree在rust_family下已可切到 same-origin iframe compat host,并 bridge:tree.navigatetree.page.context-menutree.node.created / renamed / moved
page tree已新增tree.page.focus.changedbridge- 宿主已显式持有
focusedDocumentId本地状态 - page focus 不再只停留在 iframe 内部私有状态
- iframe host 与 Rust shell contract 已补定向测试
- 宿主已显式持有
- 过滤态
page tree在rust_family下已可通过 inline override 进入 same-origin iframe compat host- 浏览器端直接注入过滤后的 projection rows
- 不再因为 filter 非空而强制回退 React fallback
- 过滤态
page tree即使筛到 0 条,也会继续通过 inline override 留在 same-origin iframe compat host - 默认主路径已切到
rust_familysame-origin compat hosttask112主站 smoke 已验证 page tree hostKind 为iframe- 当前切主的是 compat host,不等于 final Rust renderer 已完成
page tree主路径 React renderer 已退出TreeShellSurface缺少 iframe 宿主时只显示page-tree-renderer-removed- 主路径运行时已不再挂载旧 React
PrivateTree
task112已补 page tree 的 create / rename / 大树滚动真实 smoke- compat shell hover 动作区已用于实跑新建子页面与重命名
- 大树场景下,键盘导航可把目标行稳定带入可视区
- 当前
page tree仍是mnote-web tree shell compat host,不是最终 Rust renderer - 过滤态
page tree当前仍是 compat host + inline override,不是最终 Rust family renderer
目标
让 page tree 的主渲染与主交互骨架退出当前 React 主壳。
checklist
- 明确
page treerenderer 最小输入:- projection items
- local UI state
- command dispatcher
- 抽离正式模块边界:
- row model
- focus model
- keyboard model
- DnD state machine
- context action registry
- 实现 Rust 家族 page tree renderer
- 迁移展开/折叠
- 迁移当前页高亮与祖先自动展开
- 迁移 hover 动作区
- 迁移右键菜单入口
- 迁移键盘导航
- 迁移基础拖拽排序
- 在主站预留极薄挂载位
- 用 feature flag 控制新旧 page tree 切换
page tree 能力保留硬门槛
- 稳定层级展开/折叠
- 当前页高亮
- 祖先自动展开
- 新建子页面
- 重命名
- 页面移动
- 上下文菜单
- 键盘导航
- 基础拖拽排序
- 大树场景稳定滚动
说明:
- 上面已勾选的
右键菜单入口 / 上下文菜单 / feature flag,当前口径是“same-origin compat host + 宿主 bridge 已闭环”,不等于 final Rust renderer 已切主。 - 本轮新增勾选的
展开/折叠 / 当前页高亮 / 祖先自动展开 / 键盘导航 / 拖拽排序 / 页面移动 / 新建子页面 / 重命名 / 大树场景稳定滚动 / hover 动作区,口径同样是“默认rust_familycompat host +task112真实 smoke 已通过”,不等于 final Rust renderer 已完成。
阻塞项
- 宿主边界仍过厚
- 旧 React 组件仍承担主交互状态机
验收口径
- 至少一条默认真实流量使用 Rust 家族 page tree renderer
- 不再依赖当前 React
PrivateTree作为主路径 renderer page tree功能不低于当前产品合同
8. Phase E:迁移 file tree / picker Rust 家族 renderer,并收薄宿主
目标
在 page tree 主路径稳定后,迁移 file tree 与 picker,并继续收薄主站宿主职责。
当前进度(2026-04-24)
picker空查询态与搜索态已统一复用TreePickerSurfacepicker已开始复用与 tree surface 同一 renderer family 边界file tree / picker / page treesurface 已显式输出data-renderer-familyfile tree / picker / page tree已统一经由TreeShellHost进入显式 host 选择边界,并输出data-tree-host-kindpicker已接入rust_familyhost 边界;当前 host 仍先包住 React fallback,不代表已切到 Rust renderer- 未过滤
picker空查询态在rust_family下已切到 same-origin iframe compat host picker搜索结果态在rust_family下已切到 same-origin iframe compat host:- 浏览器端通过 inline override 注入搜索结果
- 不再因为搜索态而强制回退 React fallback
picker在rust_family下,空查询且无可选页面时也会继续留在 same-origin iframe compat host- 不再因为空结果而直接退回宿主内纯文本 div
picker根节点与排除项逻辑已补组件回归:- 空查询时保留根节点
excludeIds不再把当前页误放回候选列表- 组件测试与真实浏览器核对已覆盖该口径
picker空查询态也已支持键盘高亮与 Enter 选中- 不再只在搜索结果态依赖输入框 keydown hack
- React fallback 与 same-origin compat host 都会同步当前高亮项
pickersame-origin compat host 已透传activePickerItemKey- 根节点高亮不再只能停留在 React fallback
- Rust tree shell picker 页面测试已覆盖该 query/state
picker在rust_family下已把输入框键盘命令继续下沉到 same-origin compat shell- 宿主改为发送
tree.picker.command - iframe 会回传
tree.picker.focus.changed MoveEmbedPickerDialog不再直接决定 rust_family 主路径的高亮切换与 Enter 选中
- 宿主改为发送
pickerruntime 状态更新已对齐 Rust renderer input reducer contractmnote-web /tree与 3000 inline host 均暴露rust_picker_state_reducer_v1tree.picker.command的 next/previous/home/end/pick/focus 已先统一经applyPickerStateAction- 当前仍是 compat JS 镜像执行合同,不代表 final Rust runtime 已接管
- 未过滤
file tree在rust_family下已可切到 same-origin iframe compat host,并 bridge:tree.navigatetree.filetree.context-menutree.asset.opentree.node.created / renamed / moved
file tree已新增tree.filetree.selection.changedbridge:- 宿主可同步
selectedRowIds / anchorRowId / focusedRowId - 现有复制 / 删除 / 粘贴等依赖宿主选择态的链路不再只能停留在 React
FileTree
- 宿主可同步
file treeselection truth 已从宿主 legacy reducer 收薄为 renderer event snapshot:rust_family下Sidebar只订阅tree.filetree.selection.changed- 删除 / 复制 / 粘贴 / 上传 / 内部 drop 只读消费 renderer snapshot
SidebarTreeSurface不再向 Rust file tree host 传selectedRowIds控制 prop- 该项不代表 compat runtime 内部选择算法已经 Rust runtime 化;后续仍转入
4-11 Phase F/G
file treeruntime 选择入口已对齐 Rust renderer input reducer contractmnote-web /tree与 3000 inline host 均暴露rust_filetree_selection_reducer_v1- 单选 / 多选 / Shift 范围选 / 右键选中 / 可见行归一化 / drag rows 解析已先统一经
applyFileTreeSelectionAction - 当前仍是 compat JS 镜像执行合同,不代表 final Rust runtime 已接管
file tree删除目标归一化已进入 Rust preflight 主链:tree.filetree.delete.preflight已由 Rustbridge-runtime输出fileTreeDeletePlan- 3000 同源
/api/tree/filetree/delete-preflightroute 与 client 已覆盖 Sidebar删除链已不再自己推导 rust_family 主路径的 doc/asset 删除列表
file tree粘贴目标与复制分类已进入 Rust preflight 主链:tree.filetree.paste.preflight已由 Rustbridge-runtime输出fileTreePastePlan- 3000 同源
/api/tree/filetree/paste-preflightroute 与 client 已覆盖 Sidebar粘贴链在rust_family下已不再自己推导目标页面、doc/index递归语义和可复制 asset 列表
file tree外部上传目标推导已进入 Rust preflight 主链:tree.filetree.upload-target.preflight已由 Rustbridge-runtime输出fileTreeUploadTargetPlan- 3000 同源
/api/tree/filetree/upload-target-preflightroute 与 client 已覆盖 Sidebar外部文件 drop 链已不再自己推导 target row、focused row、workspace fallback 与 mindmap target
- same-origin compat shell 已补最小
file tree交互骨架:- 单选 / 多选 / Shift 范围选
- 右键聚焦选中
- 空白区清空选择
doc / index / asset双击打开
- same-origin compat shell 已补
file tree内部拖放 bridge:- iframe 可发
tree.filetree.internal-drop - 宿主已回接现有
onInternalDrop Altcopy / move 区分继续沿用宿主已有语义
- iframe 可发
- same-origin compat shell 已补
file tree外部文件拖入 bridge:- 行级 drop 与空白区 drop 都会向宿主发
tree.filetree.external-drop - 宿主已回接现有
onDropFiles
- 行级 drop 与空白区 drop 都会向宿主发
- same-origin compat shell 已补最小
asset-folder行语义:mindmap不再只表现为普通asset- 已可渲染
asset-folder -> asset层级 asset-folder已接入双击打开、右键菜单与 drop target
file tree资源类型图标语义已补最小闭环- same-origin compat shell 已区分
pdf / image / video / audio / book / table / mindmap - React fallback
FileTree也已补同口径图标分支,避免切流前后视觉合同分裂
- same-origin compat shell 已区分
- 过滤态
file tree在rust_family下已可通过 inline override 进入 same-origin iframe compat host- 浏览器端已改为直接注入过滤后的
kernel file_tree items - 不再因为 filter 非空而强制回退 React fallback
- 浏览器端已改为直接注入过滤后的
- 过滤态
file tree已先从kernelFileTreeProjection.items收敛正式 item contract,再统一生成 rows 与 same-origin host inline 输入- 不再回退
pageRows + assets二次重建主可见行 sidebar主路径过滤态已优先消费 kernel file tree items
- 不再回退
file tree搜索态已切到 Rust projection query 主链core-protocol的KernelProjectionFilter已包含querybridge-runtime的kernel.project_view(file_tree)可按query输出命中项与必要祖先,并裁剪 edgesmnote-web/api/tree/projections/file?...&query=已透传 query 到 Rust projectionwolai-frontend新增 3000 同源/api/tree/projections/fileroute,Sidebar 搜索态只消费该 route 返回的 Rustfile_treeprojection items- 旧
filterKernelFileTreeProjectionItems宿主裁剪主链已移除
file treesame-origin compat host 已能直接消费 kernel file tree items- 过滤态宿主不再必须先把
FileTreeRow[]重新翻译回 inline item
- 过滤态宿主不再必须先把
file treeinline override 已移除FileTreeRow[]正式输入TreeShellIframeHost现在只接受kernel file_tree items作为 inline projection contract
file tree默认主路径已切到rust_familysame-origin compat hosttask112主站 smoke 已验证 file tree hostKind 为iframe- 当前切主的是 compat host;复杂交互执行面仍依赖宿主 bridge
file tree主路径 React renderer 已退出TreeShellSurface缺少 iframe 宿主时只显示file-tree-renderer-removed- 主路径运行时已不再挂载旧 React
FileTree
file tree首屏 Rust initial DOM 的 hydrate 后状态 patch 已继续收薄TreeShellIframeHost在data-rust-filetree-renderer=initial_v1hydrate 成功后,对宿主 active / selection patch 只更新data-active / data-selected- 该项只是减少 compat JS 整树重绘,不等于 final Rust renderer 已完成
- 3000 inline host 已减少第二份 projection 注入真相
TreeShellIframeHost主路径现在把 inline projection items 放入tree-shell-state.itemsrendererInput与items同时进入 appState JSON,compat runtime 优先从该合同读取 expanded / selection / picker active / exclude__MNOTE_TREE_SHELL_OVERRIDE__不再作为 3000 主路径默认注入第二份 items,只保留旧兼容入口
- 3000 inline host 已对齐 Rust state reducer 合同
- page focus/keyboard 暴露
rust_page_focus_keyboard_reducer_v1 - filetree selection 暴露
rust_filetree_selection_reducer_v1 - picker state 暴露
rust_picker_state_reducer_v1 - 当前只是把默认主路径 compat runtime 的状态更新入口统一到 Rust contract,不等于 final renderer 完成
- page focus/keyboard 暴露
file tree / picker的真正 Rust family renderer 仍未接入;当前只是接入 same-origin compat host,而不是最终 renderer- 宿主仍承担实际命令调度、上传执行与部分可见行 / DOM runtime 过渡职责;还不是最终 Rust family 正式模块边界
- 内部 drop / 删除 / 粘贴 / 外部上传目标的目标与对象分类已下沉 Rust preflight,但文件字节读取、菜单状态、实际 transport 与通知仍在宿主
checklist
- 明确
file treerenderer 的最小输入与可见行模型- 当前主链最小输入已先统一为
kernel file_tree items + expanded document ids + expanded asset-folder ids
- 当前主链最小输入已先统一为
- 迁移
doc -> index -> asset-folder -> asset可见行语义(compat shell 最小版) - 迁移资源图标与资源菜单分支
- 资源图标 contract 已在
route.test.ts/tree-delta.test.ts覆盖 tree.filetree.context-menubridge 已在tree-shell-iframe-host.test.tsx覆盖
- 资源图标 contract 已在
- 迁移单选 / 多选 / Shift 范围选 / 右键选中
- 迁移双击打开资源
- 迁移内部拖放(bridge 到宿主既有 drop 处理)
- 迁移外部文件拖入上传(bridge 到宿主既有上传处理)
- 迁移 copy / move 区分与非法投放校验(Rust preflight 主链 + 宿主 transport 过渡)
- 迁移粘贴目标推导与复制分类(Rust preflight 主链 + 宿主 transport 过渡)
- 让
picker复用同一 renderer / state family 的轻量模式(先完成统一TreePickerSurface) - 让
picker搜索结果态进入rust_familysame-origin compat host - 收掉主路径 React
FileTreerenderer - 把
picker键盘高亮 / Enter 主链继续从宿主 input keydown 收到 iframe shell - 收掉宿主内剩余树结构重建与主交互状态骨架(本批可验证范围)
- 3000 主路径已固定为
state.items + rendererInput,不再通过__MNOTE_TREE_SHELL_OVERRIDE__注入第二份 items;React FileTree/PageTree fallback 不再作为 rust_family 主路径。完整 Rust runtime 替换仍归入后续 final renderer。
- 3000 主路径已固定为
file tree 能力保留硬门槛
- 页面
index.md- 附件
mindmap文件夹- 导图子附件
- 多选
- 范围选
- 资源级右键菜单
- 双击打开
- 内部拖放
- 外部文件拖入
- 资源类型图标语义
picker 能力保留硬门槛
- 空态稳定显示
- 搜索结果稳定显示
- 高亮与键盘选中稳定
- 组件回归与 Rust picker shell 定向测试已补
task113已在3000主站默认rust_family配置下实跑通过
- 根节点与排除项逻辑不回退
阻塞项
file_tree搜索过滤已不再由宿主裁剪 items,但 compat shell 内仍有一层把 items 规范化为 DOM 行的过渡逻辑- 选择 truth 对宿主已变成 renderer event snapshot,但 compat runtime 内部仍有 TS 选择/拖放/菜单 DOM 状态机,仍未形成完全独立的 Rust family 正式模块
file tree复杂拖放 / 粘贴 / 上传态的执行仍依赖宿主 bridge;内部 drop、删除、粘贴目标、外部上传目标 preflight 已下沉 Rust,过滤态已不再强制回退 React fallbackpicker搜索态虽已进入 same-origin compat host,但当前仍是 compat shell + inline override,不是最终 Rust renderer / 正式搜索合同picker键盘高亮链已补组件回归、shell query/state 测试与3000主站真实 smoke- 当前剩余差距不在“键盘能否工作”,而在“仍是 compat shell + 宿主状态机,而非 final Rust renderer”
- final renderer 与宿主收薄已转入
4-11 Phase F/G
验收口径
file tree不再依赖当前 ReactFileTree作为主路径 rendererpicker复用统一 renderer 家族- 宿主不再承担主要树域交互状态机
- 当前实现距离该验收口径仍有明确差距:
- same-origin compat host 已接入
picker搜索态已进入 compat host,但仍不是最终 Rust rendererfile tree的内部拖放 / 外部上传 /asset-folder最小语义与搜索 projection 已接入,但还停留在 compat shell + 宿主 bridge- 还不能宣称
Phase E已完成
9. 跨阶段通用回归要求
每一阶段切流前都必须至少补齐以下回归:
- 页面树切页 smoke
- 页面树展开/折叠 smoke
- 页面树拖拽 smoke
- 页面树右键菜单 smoke
- 文件树多选与范围选 smoke
- 文件树双击打开 smoke
- 文件树内部拖放 smoke
- 文件树外部文件拖入 smoke
- picker 搜索 / 空态 / 选中 smoke
- stream 中断 fallback smoke
要求:
- 不只验证“组件能渲染”
- 要验证“关键交互能力未退化”
补充口径(2026-04-24):
- 当前以上 10 项由
scripts/task112-tree-rust-family-regression-smoke.js实跑覆盖:宿主页负责切页、右键菜单、文件树双击打开、picker 与 stream fallback;/api/tree/shell直连页负责页面树展开/折叠/拖拽,以及文件树多选、范围选、内部拖放、外部文件拖入。 - 其中页面树 / 文件树拖放与 picker 当前主要验证 same-origin compat shell、宿主 bridge 与命令回写链路;宿主完全收薄后的 final renderer 端到端 smoke,仍应在 Phase D / E 收尾时继续补强。
10. 当前最近一步
按当前优先级,最近一步建议固定为:
- 先补
Phase A,把file_treeRust 直接输出缺口补齐。 - 同时推进
Phase B,把主路径命令面收口到tree.*。 - 在这两件事没有完成前,不建议宣称“树域主执行面已经可以正式切到 Rust 家族 renderer”。
原因:
- 如果
file_tree语义还要靠前端补 - 如果命令面还主要停在 compat
那么换 renderer 只会把现有双轨问题搬进新壳,而不是收掉它。
11. 完成判定
只有同时满足下面几条,才可以宣称“树域 Rust 家族化这一轮完成”:
page tree / file tree / picker主路径 renderer 已进入 Rust 家族file_tree已由 Rust 直接输出正式对象投影- 主调用路径默认走
tree.* - realtime 已进入正式
snapshot + delta + resync合同 - 宿主只保留极薄挂载与桥接职责
page tree / file tree的现有能力没有因迁移退化
12. 一句话收口
这轮工作的正确推进方式不是:
- “先把现有 React 组件翻译成 Rust”
而是:
先补齐
file_tree / command / realtime三条主链,再以能力不退化为硬门槛,逐步把page tree / file tree / picker的主 renderer 和主交互骨架迁入 Rust 家族。