2026-07-21 05:13:05 +08:00
|
|
|
|
# [recycle] 前端编辑器 / 树体验实现偏差审查
|
2026-05-13 22:43:16 +08:00
|
|
|
|
|
2026-05-14 16:30:13 +08:00
|
|
|
|
> 执行状态:已归档到 `design/10-review/done/`。本文保留为历史审查快照,当前活跃口径以 `./06-execution-checklist-and-acceptance.md` 为准。
|
|
|
|
|
|
|
2026-05-13 22:43:16 +08:00
|
|
|
|
## 范围
|
|
|
|
|
|
|
|
|
|
|
|
本报告只审查 `wolai-frontend` 当前文档页、`leptos-tiptap` island host、`Page Aggregate` 消费链、Sidebar / tree shell / tree stream 与设计文档的一致性。
|
|
|
|
|
|
|
|
|
|
|
|
重点对照设计:
|
|
|
|
|
|
|
|
|
|
|
|
- `ARCHITECTURE.md`
|
|
|
|
|
|
- `design/01-05-current-priority-overview.md`
|
|
|
|
|
|
- `design/04-tree-domain/done/4-sidebar-pagetree-filetree-rust-web-rebuild-v1.md`
|
|
|
|
|
|
- `design/04-tree-domain/done/4-2-sidebar-pagetree-filetree-product-interaction-contract-v1.md`
|
|
|
|
|
|
- `design/04-tree-domain/done/4-6-tree-command-protocol-cutover-stage2-v1.md`
|
|
|
|
|
|
- `design/04-tree-domain/done/4-18-tree-final-dom-shell-cutover-hard-gate-v1.md`
|
|
|
|
|
|
- `design/04-tree-domain/process/4-23-local-cloud-explicit-bridge-p3-candidate-v1.md`
|
2026-05-16 22:03:14 +08:00
|
|
|
|
- `design/05-editor-mainline/done/5-4-leptos-tiptap-mainline-correction-v1.md`
|
2026-05-13 22:43:16 +08:00
|
|
|
|
- `design/05-editor-mainline/process/5-5-page-aggregate-single-truth-alignment-v1.md`
|
2026-05-23 23:38:42 +08:00
|
|
|
|
- `design/05-editor-mainline/done/5-6-page-aggregate-alignment-checklist-v1.md`
|
2026-05-13 22:43:16 +08:00
|
|
|
|
- `design/05-editor-mainline/done/5-5-1-page-aggregate-contract-v1.md`
|
|
|
|
|
|
|
|
|
|
|
|
重点读取代码:
|
|
|
|
|
|
|
|
|
|
|
|
- `wolai-frontend/src/app/(app)/documents/`
|
|
|
|
|
|
- `wolai-frontend/src/components/editor/`
|
|
|
|
|
|
- `wolai-frontend/src/lib/documents/`
|
|
|
|
|
|
- `wolai-frontend/src/components/sidebar/`
|
|
|
|
|
|
- `wolai-frontend/src/lib/tree-stream/`
|
|
|
|
|
|
- `wolai-frontend/src/lib/documents/tree-command-client.ts`
|
|
|
|
|
|
|
|
|
|
|
|
## 结论
|
|
|
|
|
|
|
2026-05-16 08:57:40 +08:00
|
|
|
|
当前实现总体方向与最新设计主线一致:文档页 SSR 入口已消费 Rust `mnote.page_aggregate.v1`,默认主编辑器已折返到页面内 `leptos_tiptap_island`,tree command 前端主调用已进入 `/api/tree/commands` + `tree.*` 元数据口径,Sidebar 的默认 tree shell 已切到 `rust_wasm_dom_shell_host`。2026-05-16 后,历史 `iframe_srcdoc` host 与显式 legacy flag 已从 active source 移除并进入 `recycle/`。
|
2026-05-13 22:43:16 +08:00
|
|
|
|
|
|
|
|
|
|
但不能把当前状态描述为“前端编辑器 / 树体验已经完全收口到 Rust 单一真源”。主要未完成点集中在三处:页面本地 aggregate reducer 仍承担临时真相与补偿选择,`pageSubtree` 在本地正文变更后会被置空而不是形成同一份可持续 projection,Sidebar 仍通过 initial / query / tree stream 三源 freshness 选择维持一致性。此外,部分页面设置仍明确是 `planned` / `downgrade`,属于设计清单中尚未完成的范围。
|
|
|
|
|
|
|
|
|
|
|
|
## 关键发现表格
|
|
|
|
|
|
|
|
|
|
|
|
| 编号 | 分类 | 严重度 | 发现 | 证据 |
|
|
|
|
|
|
| --- | --- | --- | --- | --- |
|
|
|
|
|
|
| F-01 | 未完成 | 中 | Page Aggregate 读链已 Rust-first,但客户端仍有本地 aggregate reducer 持有标题、正文、设置、子树快照等临时真相;设计中的“页面域单一真源”尚未闭环。 | `wolai-frontend/src/components/editor/page-aggregate-client-state.ts:7` 定义本地 `PageAggregateClientState`,包含 `serverPageTitle / persistedPageTitle / draftPageTitle / options / content / serverPageSubtreeSnapshot / contentRevision / conflictDetectionKey`;`wolai-frontend/src/components/editor/document-content.tsx:144` 使用 reducer 作为文档页核心状态。 |
|
|
|
|
|
|
| F-02 | 实现偏差 | 中 | `pageSubtree` 不是跟随本地正文和标题持续更新的 page projection;一旦本地正文对象与服务器快照不同,就直接返回 `null`,AI 面板和阅读结构面会失去这份 projection。 | `wolai-frontend/src/components/editor/page-aggregate-client-state.ts:163` 通过 `content === serverContentSnapshot` 判断;`wolai-frontend/src/components/editor/page-aggregate-client-state.ts:166` 只有未改动时返回 `serverPageSubtreeSnapshot`,否则 `:169` 返回 `null`;`wolai-frontend/src/components/editor/document-content.tsx:867` 将该选择结果作为 AI snapshot 的 `pageSubtree` 来源,`:1162` 也传给阅读视图。 |
|
|
|
|
|
|
| F-03 | 未完成 | 中 | Sidebar / Breadcrumb / 文档页头已共享 preferred snapshot,但 live cache 仍不是唯一来源;当前仍在 initial、query refetch、tree stream 之间做 freshness 选择。 | `wolai-frontend/src/components/app-layout-shell.tsx:20` 同时创建 `useSidebarData` 与 `useSidebarTreeStream`;`:22` 用 `usePreferredSidebarSnapshot` 选择;`wolai-frontend/src/components/sidebar/use-preferred-sidebar-snapshot.ts:29` 在 `query / tree_stream / initial` 间选择;`wolai-frontend/src/components/sidebar/sidebar.tsx:201` Sidebar 内部也消费同样选择结果。 |
|
|
|
|
|
|
| F-04 | 未完成 | 低 | 页面设置运行时语义已有代码级分类,但仍有正式 UI 中可见的 planned / downgrade 项;这与 `5-6` 清单中“页面设置未整体收口”的口径一致。 | `wolai-frontend/src/lib/documents/page-option-semantics.ts:60` 将 `protectEditing` 标记为 `planned`,`:84` 将 `showBlockRefCount` 标记为 `planned`;`wolai-frontend/src/components/editor/page-options-sidebar.tsx:416` 对 downgrade 项显示 `待接线`,`:71` 让 `showBlockRefCount` 成为不可交互占位。 |
|
|
|
|
|
|
| F-05 | 方向变化/文档滞后 | 低 | `5-5-1` 中仍写着 `showHeadingNumbers / embedDefaultBlockId` 只完成字段贯通,但当前代码已把它们纳入 island runtime payload;这里更像文档滞后,而不是实现偏差。 | `design/05-editor-mainline/done/5-5-1-page-aggregate-contract-v1.md` 第 2.3 节仍说明两项“不可描述成正式支持”;`wolai-frontend/src/lib/documents/page-option-semantics.ts:48` 与 `:88` 均标记为 `wired`;`:121` 的 `pickLeptosTiptapRuntimePageOptions` 会把 `showHeadingNumbers / embedDefaultBlockId` 传给 island,`wolai-frontend/src/components/editor/leptos-tiptap-island-editor-host.tsx:717` 运行时更新这些选项。 |
|
2026-05-16 08:57:40 +08:00
|
|
|
|
| F-06 | 已收口 | 低 | Tree final DOM shell 默认路径已符合 `4-18`,legacy iframe host 与环境变量回退入口已删除;后续只需保持负向 smoke,防止 active source 重新引入 `iframe_srcdoc`。 | `wolai-frontend/src/components/sidebar/tree-shell-host.tsx` 默认 `rust_family` 且有 workspace 时只使用 `TreeShellRustDomShellHost`;`wolai-frontend/src/components/sidebar/tree-shell-host.test.tsx` 覆盖默认不能进入 `iframe_srcdoc`。 |
|
2026-05-13 22:43:16 +08:00
|
|
|
|
|
|
|
|
|
|
## 证据
|
|
|
|
|
|
|
|
|
|
|
|
### 已对齐部分
|
|
|
|
|
|
|
|
|
|
|
|
1. 文档页读取主链已进入 Rust Page Aggregate。
|
|
|
|
|
|
- `wolai-frontend/src/app/(app)/documents/[id]/page.tsx:51` 调用 `loadPageAggregateFromNextHeaders`。
|
|
|
|
|
|
- `wolai-frontend/src/lib/documents/page-aggregate-loader.ts:170` 请求 `/api/page-aggregate/:id`。
|
|
|
|
|
|
- `wolai-frontend/src/lib/documents/page-aggregate-loader.ts:130` 校验 schema 必须是 `mnote.page_aggregate.v1`。
|
|
|
|
|
|
- `wolai-frontend/src/lib/documents/page-aggregate-loader.ts:225` 的 `loadPageAggregate` 只返回 Rust snapshot,不再在运行时 fallback 到 TS builder。
|
|
|
|
|
|
|
|
|
|
|
|
2. 默认编辑器 host 已是页面内 `leptos_tiptap_island`。
|
|
|
|
|
|
- `wolai-frontend/src/components/editor/editor-host-config.ts:1` 只保留 `EditorHostKind = "leptos_tiptap_island"`。
|
|
|
|
|
|
- `wolai-frontend/src/components/editor/editor-host.tsx:7` 动态加载 `leptos-tiptap-island-editor-host`。
|
|
|
|
|
|
- `wolai-frontend/src/components/editor/leptos-tiptap-island-editor-host.tsx:619` 直接调用 runtime module 的 `mount(container, ...)`,不是 iframe。
|
|
|
|
|
|
|
|
|
|
|
|
3. 页面写命令已经开始按 page command family 收口。
|
|
|
|
|
|
- `wolai-frontend/src/lib/documents/page-command-contract.ts:15` 固定 `page.head.updateTitle / page.layout.updateOptions / page.body.save`。
|
|
|
|
|
|
- `wolai-frontend/src/lib/documents/page-command-client.ts:72` 标题写入发送 `commandName: page.head.updateTitle`。
|
|
|
|
|
|
- `wolai-frontend/src/lib/documents/page-command-client.ts:89` 页面设置写入发送 `commandName: page.layout.updateOptions`。
|
|
|
|
|
|
- `wolai-frontend/src/app/api/documents/save/route.ts:35` 服务端 envelope 使用 `PAGE_COMMAND_NAMES.saveBody`。
|
|
|
|
|
|
|
|
|
|
|
|
4. tree command 前端调用主面已以 `tree.*` 为结果元数据口径。
|
|
|
|
|
|
- `wolai-frontend/src/lib/documents/tree-command-client.ts:26` 定义 `tree.node.create / tree.node.rename / tree.subtree.move / tree.node.archive` 等 preferred command。
|
|
|
|
|
|
- `wolai-frontend/src/lib/documents/tree-command-client.ts:185` 统一 POST 到 `/api/tree/commands`。
|
|
|
|
|
|
- `wolai-frontend/src/app/api/tree/commands/route.ts:205` 由 `action` 分发 tree command,`:278` 创建命令使用 `tree.node.create`。
|
|
|
|
|
|
|
|
|
|
|
|
5. tree final DOM shell 默认路径已符合硬门禁。
|
|
|
|
|
|
- `wolai-frontend/src/components/sidebar/tree-shell-host.tsx:139` 默认 implementation 为 `rust_wasm_dom_shell_host`。
|
|
|
|
|
|
- `wolai-frontend/src/components/sidebar/tree-shell-surface.tsx:113` 旧 React page tree renderer 已显示为 removed fallback,不再作为正常 renderer。
|
|
|
|
|
|
- `wolai-frontend/src/components/sidebar/tree-shell-host.test.tsx:189` 测试断言默认 implementation 是 `rust_wasm_dom_shell_host`,`:194` 断言没有 iframe host。
|
|
|
|
|
|
|
|
|
|
|
|
### 仍需收口部分
|
|
|
|
|
|
|
|
|
|
|
|
1. 客户端 `PageAggregateClientState` 仍是混合态。
|
|
|
|
|
|
- 它把 server snapshot、persisted title、draft title、本地 content、server subtree snapshot、revision/conflict key 放在同一个前端 reducer 中。
|
|
|
|
|
|
- 这符合当前过渡态,但不等于 Rust page aggregate 已成为页面域唯一运行时真相。
|
|
|
|
|
|
|
|
|
|
|
|
2. `pageSubtree` 与正文编辑没有同源更新。
|
|
|
|
|
|
- 本地正文变更只会触发 `apply_local_content_snapshot`,不会同步生成新的 `pageSubtree`。
|
|
|
|
|
|
- selector 在内容对象不同于 server snapshot 时返回 `null`,因此 `AI / read view / TOC` 只能等待后续持久化与重新拉取。
|
|
|
|
|
|
|
|
|
|
|
|
3. Sidebar live cache 仍有 freshness 选择层。
|
|
|
|
|
|
- `AppLayoutShell` 和 `Sidebar` 都依赖 `usePreferredSidebarSnapshot`。
|
|
|
|
|
|
- 当前算法没有基于 Rust stream cursor / projection version 做统一仲裁,而是通过 sync key 与 `treeStreamStatus` 在 `query` 与 `tree_stream` 之间选择。
|
|
|
|
|
|
|
|
|
|
|
|
4. 页面设置中仍有显式未完成项。
|
|
|
|
|
|
- `protectEditing` 与 `showBlockRefCount` 的代码状态已经诚实标为 `planned / downgrade`。
|
|
|
|
|
|
- 这避免了误导用户,但也说明 `Page Aggregate -> island runtime` 的设置语义还没有完全完成。
|
|
|
|
|
|
|
|
|
|
|
|
## 建议优先级
|
|
|
|
|
|
|
|
|
|
|
|
### P0
|
|
|
|
|
|
|
|
|
|
|
|
暂无需要立即阻断主链的 P0。默认 Page Aggregate 读链、默认 island host、默认 DOM tree shell 均未发现与最新主线相反的实现。
|
|
|
|
|
|
|
|
|
|
|
|
### P1
|
|
|
|
|
|
|
|
|
|
|
|
1. 收口 `pageSubtree` 与本地正文编辑的关系。
|
|
|
|
|
|
- 至少明确它是“server projection only”还是“本地编辑也应生成临时 projection”。
|
|
|
|
|
|
- 如果 AI 面板需要稳定结构上下文,不应在用户正常编辑后直接拿到 `null`。
|
|
|
|
|
|
|
|
|
|
|
|
2. 将 Sidebar 的三源 preferred snapshot 推进为统一 live cache。
|
|
|
|
|
|
- 建议以 Rust stream cursor / projection version / query snapshot version 为仲裁字段,减少仅靠 sync key 的 freshness 判断。
|
|
|
|
|
|
|
|
|
|
|
|
### P2
|
|
|
|
|
|
|
|
|
|
|
|
1. 继续移除或强门禁 legacy tree iframe host。
|
|
|
|
|
|
- 当前显式 env flag 符合设计,但应保留负向 smoke,避免后续默认路径误回退。
|
|
|
|
|
|
|
|
|
|
|
|
2. 同步修正文档滞后。
|
|
|
|
|
|
- `5-5-1` 对 `showHeadingNumbers / embedDefaultBlockId` 的描述已经落后于当前代码,应在后续文档整理时更新。
|
|
|
|
|
|
|
|
|
|
|
|
3. 页面设置 planned 项保持降级展示,直到真正进入 island runtime 或被产品侧移除。
|
|
|
|
|
|
|
|
|
|
|
|
## 修改的文件
|
|
|
|
|
|
|
2026-05-14 16:30:13 +08:00
|
|
|
|
- `/mnt/Data1T/mnote/design/10-review/done/02-frontend-editor-tree-review.md`
|