Files
mnote/design/05-editor-mainline/done/5-5-1-page-aggregate-contract-v1.md
T

233 lines
6.6 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.
# 5-5-1 [done] Page Aggregate Contract v1
> 更新时间:2026-04-22
>
> 关联文档:
> - `/mnt/Data1T/mnote/design/05-editor-mainline/process/5-5-page-aggregate-single-truth-alignment-v1.md`
> - `/mnt/Data1T/mnote/design/05-editor-mainline/process/5-6-page-aggregate-alignment-checklist-v1.md`
## 1. 文档目的
这份文档只定义一件事:
> **文档页在前端与 Rust 主链之间,当前最小可落地的 `Page Aggregate` 契约。**
它的作用不是取代长期 Rust kernel contract,而是先冻结当前主文档页的聚合边界,阻止:
- 标题、正文、页面设置、page subtree 继续各自散长字段
- 页面壳继续在 props 链上拼第二份页面真相
- `leptos-tiptap` island 继续只接正文而不接页面布局语义
## 2. 当前最小契约
当前最小 `Page Aggregate` 固定为五层:
- `page_identity`
- `page_head`
- `page_layout`
- `page_body`
- `page_tree`
以及一组附属统计:
- `page_stats`
### 2.1 `page_identity`
作用:
> **标识这份页面聚合属于哪一个 page aggregate。**
当前最小字段:
- `documentId`
- `workspaceId`
说明:
- 这层是聚合身份,不是 UI 展示字段。
- 后续如果 Rust kernel 侧补 `nodeId / aggregateId / projectionVersion`,应继续落在这一层,而不是散回页面 props。
### 2.2 `page_head`
作用:
> **承载页面头部的正式真相。**
当前最小字段:
- `title`
- `updatedAt`
- `permissions.readOnly`
- `permissions.disableDownload`
- `permissions.disableCopy`
说明:
- 页头标题属于 `page_head truth`,不是组件局部输入框自己的真相。
- 前端仍可保留一个“输入中”的临时编辑态,但提交后必须回到 `page_head.title`
### 2.3 `page_layout`
作用:
> **承载页面布局与编辑器 runtime 相关设置。**
当前最小字段:
- `pageOptions.wideLayout`
- `pageOptions.smallText`
- `pageOptions.layoutDensity`
- `pageOptions.showHeadingNumbers`
- `pageOptions.showToc`
- `pageOptions.showStructure`
- `pageOptions.protectEditing`
- `pageOptions.showWordCount`
- `pageOptions.collapseBacklinks`
- `pageOptions.pageFont`
- `pageOptions.hideChildPages`
- `pageOptions.showBlockRefCount`
- `pageOptions.embedDefaultBlockId`
说明:
- 这层不是“页面设置面板 UI state”,而是页面设置的持久化真相。
- 其中只有一部分已经进入 `leptos-tiptap` island 运行时:
- `wideLayout`
- `smallText`
- `layoutDensity`
- 下面两项当前只完成字段贯通,不可描述成“正式支持”:
- `showHeadingNumbers`
- `embedDefaultBlockId`
### 2.4 `page_body`
作用:
> **承载正文内容与保存元数据。**
当前最小字段:
- `content`
- `revision`
- `conflictDetectionKey`
说明:
- 正文相关命令都应收口到 `page_body`,而不是继续发明新的“页面内临时写回”路径。
- `/api/documents/save -> documents.save -> documents:updateContent` 是当前正式 page body 保存入口。
- 2026-04-28 更新:`page.body.saved``document.snapshot.saved` 已拆成两条正式 domain event。`page.body.saved` 只表达正文块集合保存,`document.snapshot.saved` 承载 snapshot version / content hash / updatedAt,避免正文事件 payload 继续夹带快照语义。
- 2026-04-28 更新:`tree.node.embed``pageReference` block 结构与插入位置由 Rust `pageAggregateEmbedPlan` 产出;3000 route 只提供源页面标题、目标内容、anchor block 等 substrate preflight,不再自行拼装页面聚合正文结构。
### 2.5 `page_tree`
作用:
> **承载当前页面对应的子树投影。**
当前最小字段:
- `pageSubtree`
说明:
- 当前 `pageSubtree` 仍是兼容过渡态 projection,不应夸大为最终 Rust page aggregate tree contract。
- 但在文档页入口和阅读态/TOC 消费层,它已经归属于 `page_tree`,不再继续散成独立 props。
### 2.6 `page_stats`
作用:
> **承载页面统计的附属投影。**
当前最小字段:
- `wordCount`
- `characterCount`
- `blockCount`
- `todoTotal`
- `todoDone`
说明:
- 统计不是页面身份,也不是布局或正文真相,但它是 page aggregate 的附属部分。
## 3. 哪些字段是 aggregate truth,哪些只是 UI state
### 3.1 Aggregate Truth
下面这些字段属于 page aggregate truth
- `page_identity.*`
- `page_head.*`
- `page_layout.pageOptions.*`
- `page_body.*`
- `page_tree.pageSubtree`
- `page_stats.*`
### 3.2 UI State
下面这些只能算前端临时态,不是正式真相:
- 标题输入框当前未提交文本
- 当前是否处于编辑态/阅读态
- host fallback banner / runtime observability
- inspector 当前 tab
- history drawer / comments drawer / AI panel 的打开状态
- slash 菜单、浮动工具条、块手柄 hover 等 island 交互态
约束:
> **UI state 允许存在,但不能再反客为主冒充 page aggregate truth。**
## 4. 哪些字段必须由 Rust 主导,哪些当前允许前端临时持有
### 4.1 必须由 Rust 主导
- `page_head.title` 的持久化结果
- `page_body.content/revision/conflictDetectionKey`
- `page_layout.pageOptions` 的持久化结果
- `page_tree.pageSubtree` 的正式投影来源
### 4.2 当前允许前端临时持有
- 标题输入中的 debounce 缓冲态
- 正文 host 内的未保存 dirty 态
- 只影响单次交互的面板/菜单开关
- `showHeadingNumbers/embedDefaultBlockId` 的“字段已贯通,但深语义未完成”阶段性桥接逻辑
约束:
> **允许前端临时持有,不等于允许前端成为第二真相。**
## 5. 当前命令收口口径
当前最小命令口径:
- `page.head.updateTitle`
- 当前前端别名映射到 `documents.title.update`
- `page.layout.updateOptions`
- 当前前端别名映射到 `documents.options.update`
- `page.body.save`
- 当前正式入口映射到 `documents.save`
说明:
- 这轮只是把命名与调用口径收口到 page aggregate 子域。
- 底层 bridge / Convex / Rust mutation 名称暂不要求一次性全部重命名。
## 6. 当前未完成边界
以下事项当前仍未完成,不应在 checklist 中超前打钩:
- 树标题与页头标题已经证明消费同一份更新后的 projection
- AI 写入口已经完全脱离 editor bridge,正式执行 page body command
- `showHeadingNumbers` 真正进入 heading 渲染语义
- `embedDefaultBlockId` 真正进入嵌入默认落点逻辑
## 7. 当前可对外口径
当前可以准确描述为:
> **文档页已开始消费统一 `Page Aggregate`,标题/页面设置/正文保存也开始按 `page_head / page_layout / page_body` 收口;但树域投影统一与 AI 正式 page body 写入口仍未完全完成。**