diff --git a/design/design/stitch/260429/3-11-rust-web-3000-stitch-ui-parity-checklist-v1.md b/design/03-rust-web/done/3-12-rust-web-3000-stitch-ui-parity-checklist-v1.md similarity index 92% rename from design/design/stitch/260429/3-11-rust-web-3000-stitch-ui-parity-checklist-v1.md rename to design/03-rust-web/done/3-12-rust-web-3000-stitch-ui-parity-checklist-v1.md index dd5a8524..4b31028e 100644 --- a/design/design/stitch/260429/3-11-rust-web-3000-stitch-ui-parity-checklist-v1.md +++ b/design/03-rust-web/done/3-12-rust-web-3000-stitch-ui-parity-checklist-v1.md @@ -1,5 +1,9 @@ -# 3-11 Rust Web 3000 Stitch UI Parity Checklist v1 +# 3-12 [done] Rust Web 3000 Stitch UI Parity Checklist v1 +> 更新时间:2026-04-29 +> +> 迁入 done:本清单所有输入、约束、实施项与验收项均已完成,并已通过 Rust 单测与 task119/task120/task121/task122 smoke 复核。 +> > 目标:以 `design/design/stitch/260429` 的 Stitch 产物为视觉基准,把 3000 的 Rust Web 页面推进到更接近目标 UI,同时保留真实页面树、文件树、页面新建和 `leptos-tiptap` runtime。 ## 输入基准 diff --git a/design/03-rust-web/process/3-1-rust-web-long-term-checklist-v2.md b/design/03-rust-web/process/3-1-rust-web-long-term-checklist-v2.md index 56b547b6..a9f3f9a4 100644 --- a/design/03-rust-web/process/3-1-rust-web-long-term-checklist-v2.md +++ b/design/03-rust-web/process/3-1-rust-web-long-term-checklist-v2.md @@ -1,6 +1,6 @@ # 3-1 [process] Rust Web 长期架构实施清单 v2 -> 更新时间:2026-04-28 +> 更新时间:2026-04-29 > > 基于以下实际状态重写: > - 当前未提交代码 @@ -29,6 +29,8 @@ > 这份清单仍保留为 Rust Web 分阶段全景参考,但当前最近一步不再是泛化推进全部 `Phase`, > 而是优先推进 `Page Aggregate`、`tree command cutover`、`tree realtime event stream`。 > 当前执行优先级请先看 `/mnt/Data1T/mnote/design/01-05-current-priority-overview.md`。 +> +> 复核补充(2026-04-29):`3000` 默认 owner 已是 `mnote-web`,根页 / 文档页 / `tree events` / `leptos-tiptap` island 已进入 Rust Web 主链;本清单继续保留在 `process/`,因为主 API 全量迁移、旧 Next/React 链清理、Search/AI/Mindmap 轻壳化仍未完成。 --- @@ -41,6 +43,7 @@ - [x] `Phase 0` 的文档化边界梳理已经完成 - [x] `Phase 1` 已落了一个最小 `axum` Web 骨架:`rust/crates/mnote-web/` - [x] `Phase 1` 已不再只是空骨架,现已包含 kernel / bridge / compat sidebar 的真实查询接缝 +- [x] `Phase 1` 已成为 `3000` 主 Web 入口 owner,根页、文档页、tree stream 与主要 shell marker 均由 `mnote-web` 输出 - [x] `Phase 2` 文档阅读态分离已经有实质代码 - [x] `Phase 3` 主 Sidebar 已出现 `kernelSidebarTree` 消费接缝 - [x] `Phase 4` 搜索已做 host/runtime 拆分,并开始返回 `nodeId` / `subtreeRootId` / `evidence` @@ -50,8 +53,9 @@ ### 2.2 但大部分阶段都只是“部分落地” -- [ ] `Phase 1` 还不是主 Web 路径,只是 Rust Web skeleton -- [ ] `Phase 3` Sidebar 仍是超大客户端组件,其他树域也未统一切到 kernel projection +- [x] `Phase 1` 已不再只是 skeleton:`3000` 默认 owner 已是 Rust Web,根页 / 文档页 SSR 产品壳已进入主路径 +- [ ] `Phase 1` 的主 API 全量迁移与 legacy compat 清理仍未完成 +- [ ] `Phase 3` legacy Sidebar 仍是超大客户端组件;当前 Rust Web 3000 已有页面树 / 文件树 SSR tab host,但 live stream 消费侧还未完全闭环 - [ ] `Phase 4` 搜索仍是前端 runtime 主导,不是 server-first 搜索页 - [ ] `Phase 5` AI runtime 仍然很重,只是懒挂载了 - [ ] `Phase 6` Mindmap 仍然是重前端交互壳,不是独立对象页壳 @@ -60,7 +64,7 @@ ### 2.3 结论 -> **当前真实状态不是“Phase 0 ~ 8 已全部完成”,而是“Phase 0 基本完成,Phase 1/2/4/5/6/7 处于不同程度的部分落地,Phase 3 仍需继续收口;Phase 8 已完成主入口 owner cutover,但旧 Next/React 链仍作为 legacy compat / island source 保留”。** +> **当前真实状态不是“Phase 0 ~ 8 已全部完成”,而是“Phase 0 的 Rust Web 主入口和页面壳目标已完成,但主 API 迁移仍处于部分落地;Phase 2/4/5/6/7 仍处于不同程度的部分落地,Phase 3 的 3000 SSR 树壳已补实但 legacy Sidebar 与 live stream 消费侧仍需继续收口;Phase 8 已完成主入口 owner cutover,但旧 Next/React 链仍作为 legacy compat / island source 保留”。** --- @@ -99,7 +103,7 @@ | 阶段 | v1 口径 | 当前真实状态 | 说明 | | --- | --- | --- | --- | | Phase 0 | 边界冻结 | `DONE` | 文档、候选模块、边界口径已经成形,但性能基线更多还是文档定义,不是完整观测系统 | -| Phase 1 | Rust Web 基础层 | `PARTIAL` | `mnote-web` 已创建,`axum` 骨架已存在,且已补 bridge route、通用 query transport 与 Sidebar compat 切流入口,但仍不是整个产品的主 Web 入口 | +| Phase 1 | Rust Web 基础层 | `PARTIAL` 接近 `DONE` | `mnote-web` 已成为 `3000` 主 Web 入口 owner,根页 / 文档页 SSR 产品壳、tree stream 与关键 API 接缝已落地;主 API 全量迁移、compat 清理与 legacy island source 清理仍未完成 | | Phase 2 | 文档阅读页 server-first 化 | `PARTIAL` 接近 `DONE` | 阅读态/编辑态已明显分离,但仍有旧链回退和大量客户端状态集中在 `DocumentContent` | | Phase 3 | Sidebar / 树结构 Rust 化 | `PARTIAL` 偏早期 | 主 Sidebar 已以 `kernelSidebarTree` 作为主树来源,但整体仍是超大客户端组件 | | Phase 4 | 搜索 Rust 化与 island 化 | `PARTIAL` | host/runtime 懒加载拆分已做,结果形状也开始带 `nodeId` / `subtreeRootId` / `evidence`,但仍未完成 server-first 搜索页 | @@ -136,7 +140,7 @@ ## 6. Phase 1:Rust Web 基础层落地 -**当前状态:`PARTIAL`** +**当前状态:`PARTIAL` 接近 `DONE`;主入口和页面壳已完成,主 API 迁移与 compat 清理仍未完成** ### 6.1 已落地事实 @@ -158,22 +162,29 @@ - [compat.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/compat.rs) - [x] kernel sidebar route 已通过 `sidebar.dataset.list` runtime plan + 通用 query transport 读取数据集 - [kernel.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/kernel.rs) +- [x] `3000` 默认 owner 已是 `mnote-web`,`GET /` 返回 Rust Web workspace shell + - [gateway.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/gateway.rs) + - [home.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/ssr/pages/home.rs) +- [x] 已有主页面壳 SSR,`/documents/:id` 输出 Rust Web document shell、Page Aggregate marker 与 `leptos_tiptap_island` marker + - [web_shell.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/web_shell.rs) + - [document.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/ssr/pages/document.rs) +- [x] Rust Web 已承接 tree commands、tree events、page aggregate、document save/title/options 等当前 3000 主链关键接缝 +- [x] 已用 task114/task115/task118/task119/task120/task121/task122 建立页面壳级和树 / 编辑器 runtime smoke 验证 -### 6.2 明确还没完成 +### 6.2 仍未完成 -- [ ] `mnote-web` 还不是主 Web 服务入口 -- [ ] 还没有主页面壳 SSR -- [ ] 还没有主 API 大面积从 Next route 切到 Rust Web +- [ ] 主 API 还没有大面积全量从 Next route / legacy compat 迁出 - [ ] 当前 `compat_next_base_path` 仍说明它还带有兼容层属性,不是唯一承载层 -- [ ] fixture 已收口到测试/开发边界,但这还不等于整个产品流量已经全面切到 Rust Web -- [ ] 还不能说“后续页面迁移已不依赖 Next API route 作为唯一入口”,只能说“已经开始脱钩” +- [ ] Next App Router / React islands 仍作为 legacy compat 与部分 bundle source 保留 +- [ ] 复杂 runtime、OnlyOffice、历史 debug 链与旧前端代码还未完全清理 ### 6.3 v2 后续任务 -- [ ] 把 `mnote-web` 从 skeleton 提升为真实服务入口 -- [ ] 先把 sidebar kernel route 上的 fixture fallback 收到测试/开发边界,再承接一条真实页面壳或真实查询主链 -- [ ] 明确 Next -> Rust Web 的流量切换边界 -- [ ] 增加主 API / 页面壳级集成验证,而不只是 `cargo check` +- [x] 把 `mnote-web` 从 skeleton 提升为真实服务入口 +- [x] 把 sidebar kernel route 上的 fixture fallback 收到测试/开发边界,并承接真实页面壳与真实查询主链 +- [x] 明确 Next -> Rust Web 的流量切换边界:`3000` 归 `mnote-web`,Next 降为 legacy compat / island source +- [x] 增加页面壳级集成验证,而不只是 `cargo check` +- [ ] 继续迁移剩余主 API,收缩 `compat_next_base_path` 与 legacy upstream 依赖 --- @@ -222,13 +233,16 @@ - [sidebar.tsx](/mnt/Data1T/mnote/wolai-frontend/src/components/sidebar/sidebar.tsx) - [x] 已补统一 `page_tree` projection protocol,并让 `PrivateTree` / 文件树 / move-embed picker 共用 - [tree-projection.ts](/mnt/Data1T/mnote/wolai-frontend/src/lib/tree-projection.ts) +- [x] Rust Web 3000 workspace shell 已接入真实页面树 / 文件树 tab host、真实 row、UI 新建页面和 active page 链路 + - [workspace_shell.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/workspace_shell.rs) + - [layout.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/ssr/pages/layout.rs) ### 8.2 当前真实问题 - [ ] `Sidebar` 仍然是超大客户端组件 - [sidebar.tsx](/mnt/Data1T/mnote/wolai-frontend/src/components/sidebar/sidebar.tsx) -- [ ] 页面树 / 文件树虽然已统一协议,但整体执行面仍主要在客户端 -- [ ] `buildDocumentTree(...)` 仅剩兼容 helper 与旧单测,不再位于树域主路径 +- [ ] 页面树 / 文件树虽然已统一协议,legacy Next 侧整体执行面仍主要在客户端;当前 Rust Web 3000 SSR 壳也尚未直接消费 live EventSource +- [x] `buildDocumentTree(...)` 仅剩兼容 helper 与旧单测,不再位于树域主路径 - [ ] 主布局仍然默认常驻挂载 Sidebar - [ ] 还不能叫“服务端输出 + 局部 island”,现在更像“服务端首包 + 超大客户端壳” diff --git a/design/03-rust-web/process/3-3-rust-web-tree-realtime-event-stream-v1.md b/design/03-rust-web/process/3-3-rust-web-tree-realtime-event-stream-v1.md index bb420533..6f030edb 100644 --- a/design/03-rust-web/process/3-3-rust-web-tree-realtime-event-stream-v1.md +++ b/design/03-rust-web/process/3-3-rust-web-tree-realtime-event-stream-v1.md @@ -1,6 +1,6 @@ # 3-3 [process] Rust Web Tree Realtime Event Stream 方案 v1 -> 更新时间:2026-04-17 +> 更新时间:2026-04-29 > > 关联文档: > - `/mnt/Data1T/mnote/design/02-convex-rust-long-term-architecture/process/2-tree-first-graph-convex-rust-long-term-architecture-v1.md` @@ -256,3 +256,25 @@ Rust Web 负责: - Rust Web 提供正式实时 transport - 前端只消费 projection snapshot 与 tree delta - iframe/postMessage tree shell 不是正式 realtime 主链 + + +## 9. 当前实现复核(2026-04-29) + +这份方案继续留在 `process/`,因为 Rust Web transport 已落地,但当前 `3000` 的 Rust SSR tree/sidebar 消费侧还没有完整闭环到 live stream。 + +### 9.1 已完成 + +- [x] Rust Web 已暴露正式 `/api/tree/events`,并返回 `x-mnote-web-owner: mnote-web` 与 `x-mnote-tree-stream-owner: rust-web`。 +- [x] `/api/tree/events` 已能输出 workspace / subtree snapshot。 +- [x] `stream_support.rs` 已有 cursor、delta、resync 的基础判定逻辑。 +- [x] `/api/stream/events` 与 WebSocket snapshot / resync 骨架已存在。 +- [x] legacy React 侧已有 `useSidebarTreeStream` 与 `EventSource` consumer,并有协议 / delta 单测。 +- [x] `task112` / `task120` 已覆盖 `/api/tree/events` snapshot 可用性。 + +### 9.2 仍未完成 + +- [ ] 当前 Rust Web 3000 SSR workspace/sidebar shell 还没有直接挂载 live `EventSource` consumer;它主要依赖 SSR snapshot 与交互后重读。 +- [ ] delta / resync 尚缺面向当前 3000 Rust shell 的浏览器级 smoke。 +- [ ] Sidebar、page subtree、filetree 还没有全部统一到同一条 live stream cache。 +- [ ] WS 目前只证明 snapshot/resync 骨架,尚未成为主实时链路。 +- [ ] 不能把本稿移动到 `done/`,直到 `3000` 当前主界面的树消费侧也完成 snapshot + delta + resync 验收。 diff --git a/design/05-editor-mainline/process/5-4-leptos-tiptap-mainline-correction-v1.md b/design/05-editor-mainline/process/5-4-leptos-tiptap-mainline-correction-v1.md new file mode 100644 index 00000000..7b0fc56e --- /dev/null +++ b/design/05-editor-mainline/process/5-4-leptos-tiptap-mainline-correction-v1.md @@ -0,0 +1,503 @@ +# 5-4 [process] `leptos-tiptap` 主编辑器纠偏与落地方案 v1 + +> 更新时间:2026-04-29 +> +> 关联文档: +> - `/mnt/Data1T/mnote/design/old/05-editor-mainline/process/5-editor-baseline-reset-v2.md` +> - `/mnt/Data1T/mnote/design/old/05-editor-mainline/process/5-1-main-editor-cutover-entry-v1.md` +> - `/mnt/Data1T/mnote/design/05-editor-mainline/process/5-2-tiptap-notion-like-template-adoption-v1.md` +> - `/mnt/Data1T/mnote/design/old/05-editor-mainline/process/5-3-tiptap-leptos-rust-migration-checklist-v1.md` +> - `/mnt/Data1T/mnote/design/01-tree-first-graph-kernel/process/1-tree-first-graph-kernel-v1.md` +> - `/mnt/Data1T/mnote/design/03-rust-web/process/3-rust-web-long-term-architecture-v1.md` + +## 1. 文档目的 + +这份文档只解决一个已经开始偏掉的问题: + +> **当 `mnote` 决定继续采用 `Tiptap + leptos-tiptap` 作为主编辑器输入 runtime 时,如何避免把“过渡桥接层”误当成“Rust + Leptos 主线已经成立”。** + +当前需要纠偏的不是: + +- 是否继续使用 `Tiptap` +- 是否继续使用 `leptos-tiptap` +- 是否继续让 Rust 掌握 editor truth + +当前真正需要纠偏的是: + +- **哪些改动已经把“过渡接法”抬成了主链** +- **哪些底层工作虽然还不完整,但应当保留** +- **后续应该按什么顺序推进,才能真正体现 Rust + Leptos 的长期优势** + +--- + +## 2. 先给结论 + +结论固定为五句: + +### 2.1 方向本身没有错 + +`Tiptap + leptos-tiptap + Rust truth` 仍然是当前最现实的主路线。 + +原因很简单: + +- `Tiptap` 仍然是当前最成熟的浏览器富文本/块编辑 runtime 体系 +- `leptos-tiptap` 已经提供了可在 Leptos 中稳定挂载、读写内容、触发命令、订阅变更的正式接缝 +- `mnote` 当前没有更成熟的 Rust-native 输入层,可以在短期内替代 `Tiptap` 级的 selection / IME / history / slash / toolbar / handle 行为 + +因此: + +> **当前不应放弃 `leptos-tiptap`,也不应回到“重新造一个 Rust 版 Tiptap”作为当前主交付。** + +### 2.2 当前偏差也已经很明确 + +当前未提交改动里的主要偏差不是“换成了 `leptos-tiptap`”,而是: + +- 在真实文档页主链里,已经把 `BlockNoteEditor` 直接替成了新的 `EditorHost` + - `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/document-content.tsx` +- 这个 `EditorHost` 当前又固定只挂 `leptos-tiptap` + - `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/editor-host.tsx` +- 当前 host 的真实承载方式是 `iframe + postMessage + 8123 runtime + TS converter + 旧 /api/documents/save` + - `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/leptos-tiptap-editor-host.tsx` + - `/mnt/Data1T/mnote/wolai-frontend/src/lib/documents/tiptap-content-converter.ts` + +这条线的问题不是“代码不能运行”,而是: + +> **它把 `leptos-tiptap` 先落成了一个“跨 iframe 的外来 runtime”,而不是一个真正受 Rust truth 和 Leptos 页面模型约束的主编辑区。** + +### 2.3 当前最该回退的不是 Tiptap,而是默认主链切换方式 + +当前最该回退的是: + +- **默认主文档页直接切到 `iframe host`** +- **把 TS converter + `/api/documents/save` 当成正式保存边界** +- **把 `documentEditorHost = leptos_tiptap` 提前写成事实默认值** + +当前不该回退的是: + +- `core-protocol` 中新增的 `EditorBlockDocument <-> Tiptap` 桥接模型 +- `leptos-tiptap spike` 中已经完成的实际交互 surface +- 官方模板行为模型对 `slash / floating toolbar / drag handle / turn into` 的约束 + +### 2.4 真正要体现 Rust + Leptos 优势,必须把主目标改写成下面这句话 + +> **让 `Tiptap/leptos-tiptap` 只负责 live editing surface;让 Rust 持有块真相、保存合同、命令合同、AI 合同;让 Leptos 成为正式页面内编辑 island,而不是额外 `iframe runtime`。** + +### 2.5 AI 入口不能继续挂在浏览器壳边上 + +如果长期目标是: + +- AI 直接进入主编辑区编写 +- 人类只负责审核和辅助编辑 + +那么 AI 不能继续以: + +- 直接拼 HTML +- 直接拼 Tiptap JSON +- 直接驱动前端壳层临时桥接逻辑 + +作为正式路径。 + +长期正确路径应固定为: + +> **AI / CLI / 自动化先操作 Rust `EditorCommand` 与 `EditorBlockDocument`,编辑器 surface 只消费 Rust truth 的变更投影。** + +--- + +## 3. 当前问题的具体判断 + +## 3.1 当前实现为什么没有体现 Rust + Leptos 的优势 + +从当前未提交改动看,问题集中在四个点。 + +### 3.1.1 文档页已经提前默认切流 + +当前: + +- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/document-content.tsx` + +已把编辑态默认入口从 `BlockNoteEditor` 切到了: + +- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/editor-host.tsx` + +这意味着当前不是“有一个新 host 在旁路验证”,而是“主链已经被切了”。 + +### 3.1.2 host 仍然是 iframe runtime,不是 Leptos 主编辑 island + +当前: + +- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/leptos-tiptap-editor-host.tsx` + +的真实结构是: + +`DocumentContent` +-> `EditorHost` +-> `iframe` +-> `http://localhost:8123/?embedded=1` +-> `postMessage` +-> host 接收 `ready/change/save-request` +-> 前端转换后调 `/api/documents/save` + +这条链的问题在于: + +- 编辑器实例不在真实页面树内 +- 父子之间不是同一运行时对象图 +- `undo / redo / selection / current block id / reference insert` 仍是最小占位 +- AI 面板与主编辑器之间无法共享一份真正稳定的 live editor handle + +也就是说: + +> **现在更像“Next 把一个外部 editor runtime 嵌了进来”,而不是“Leptos editor 成为页面内正式 island”。** + +### 3.1.3 当前保存边界仍然由前端壳层掌控 + +当前: + +- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/leptos-tiptap-editor-host.tsx` +- `/mnt/Data1T/mnote/wolai-frontend/src/lib/documents/tiptap-content-converter.ts` + +承担了下面这些职责: + +- Tiptap doc -> blocks +- blocks -> Tiptap doc +- save payload 组装 +- stats 计算 +- revision / conflict key 维护 + +这会造成一个长期问题: + +> **Rust 还没有真正拥有唯一正式的 editor truth 边界,前端 host 仍然在定义一套过渡期格式语义。** + +### 3.1.4 当前已经开始把“过渡默认值”写死 + +当前: + +- `/mnt/Data1T/mnote/wolai-frontend/src/lib/runtime-config.ts` +- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/editor-host-config.ts` + +已经出现了把: + +- `documentEditorHost` +- `EditorHostKind = "leptos_tiptap"` + +提前写成固定默认值的趋势。 + +这会直接带来一个误判: + +> **看起来像“主编辑器切流已完成”,实际上只是“默认把一条过渡桥接线抬上来了”。** + +--- + +## 4. 当前哪些需要回退,哪些应保留 + +下面只针对“主编辑器纠偏”相关改动判断,不涉及与本任务无关的其它脏改动。 + +## 4.1 需要立即回退或降级的部分 + +| 范围 | 文件 | 动作 | 原因 | +| --- | --- | --- | --- | +| 主编辑器默认切流 | `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/document-content.tsx` | 回退到“默认仍由现有正式可写链路承载”,`EditorHost` 不再直接替代默认主编辑器 | 当前接法仍是过渡 host,不是正式 Rust + Leptos 主链 | +| host 默认绑定 | `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/editor-host.tsx` | 降级为实验入口,不再代表默认 editor host | 当前只返回 `LeptosTiptapEditorHost`,会掩盖“还没完成正式切流”的事实 | +| iframe runtime 主链承载 | `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/leptos-tiptap-editor-host.tsx` | 降级为 `debug/prototype bridge`,不再作为默认主文档页保存链路 | 它是跨 iframe bridge,不是长期正式 island 形态 | +| 编辑页强行自动进入编辑 | `/mnt/Data1T/mnote/wolai-frontend/src/app/(app)/documents/[id]/page.tsx` | 回退 `edit=1 -> autoEnterEdit` 作为默认主链推进手段 | 当前真正问题不是“默认直接编辑”,而是“切入的是哪条编辑器链” | +| 运行时默认值写死 | `/mnt/Data1T/mnote/wolai-frontend/src/lib/runtime-config.ts` | 回退 `documentEditorHost` 这类提前写死的默认事实 | 当前 host 形态仍在纠偏,不能先把默认值冻结成产品事实 | +| host 配置空壳 | `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/editor-host-config.ts` | 降级为后续使用的配置草稿,不作为当前切流依据 | 现在只有单值返回,没有真实 host 策略价值 | +| TS converter 生产化倾向 | `/mnt/Data1T/mnote/wolai-frontend/src/lib/documents/tiptap-content-converter.ts` | 从“正式保存边界”降级为“过渡适配器 / 测试夹具” | 不能让前端继续持有 editor truth 的唯一转换权 | + +### 4.1.1 对“回退”的精确定义 + +这里的“回退”不是删除所有相关代码,而是: + +1. **回退它们在主文档页中的默认地位** +2. **回退它们在正式保存链路中的事实地位** +3. **回退它们对最终架构口径的误导** + +也就是说: + +- 代码可以暂时保留 +- 但必须从“默认主链”退回“实验/过渡/对照用途” + +## 4.2 应明确保留的部分 + +| 范围 | 文件 | 动作 | 原因 | +| --- | --- | --- | --- | +| Rust Tiptap 协议桥 | `/mnt/Data1T/mnote/rust/crates/core-protocol/src/editor/tiptap.rs` | 保留并继续演进 | 这是 Rust 收口 editor truth 的必要桥层,不应因当前 host 方案错误而被否掉 | +| Rust Tiptap 导出入口 | `/mnt/Data1T/mnote/rust/crates/core-protocol/src/editor/mod.rs` | 保留 | 说明 Tiptap 映射已进入协议层,而不是停留在前端 | +| Rust 桥接测试 | `/mnt/Data1T/mnote/rust/crates/core-protocol/tests/editor_tiptap_bridge.rs` | 保留并扩充 | 这是正式语义边界回归的基础 | +| 8123 spike | `/mnt/Data1T/mnote/rust/spikes/leptos-tiptap-spike/src/main.rs` | 保留,但只作为 surface 行为样本和 embed 验证页 | 这里保存了最接近未来主编辑器的交互行为模型 | +| 官方模板行为参考 | `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/tiptap-notion-like-template/**` | 保留 | 它是体验 benchmark,不是迁移代码对象 | +| `leptos-tiptap` 参考库 | `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/leptos-tiptap/**` | 保留 | 这是正式 Leptos 接入 runtime 的能力边界,不应再被降回 fallback | + +### 4.2.1 对“保留”的精确定义 + +这些内容保留,不代表它们已经是最终主链。 + +它们保留的原因是: + +- 已经证明方向是对的 +- 已经沉淀了有价值的协议和行为模型 +- 应该被吸收到正式主线,而不是被误伤回滚 + +--- + +## 5. 纠偏后的目标形态 + +纠偏后应固定下面四层边界。 + +## 5.1 Surface 层:`Tiptap + leptos-tiptap` + +这一层只负责: + +- 人类输入 +- 光标与 selection +- slash / toolbar / handle / block menu +- 文本样式、块切换、拖拽等浏览器 runtime 行为 + +它不负责: + +- 最终块真相 +- 持久化格式真相 +- AI 改写正式合同 +- 页面树 / projection 真相 + +## 5.2 Truth 层:Rust `EditorBlockDocument` + +这一层负责: + +- 块身份 `block_id` +- 块结构与块类型 +- 命令合同 +- 保存合同 +- AI/CLI 共享的编辑真相 + +这里必须固定一句: + +> **`Tiptap JSON` 不是长期事实源,Rust `EditorBlockDocument` 才是。** + +## 5.3 Orchestration 层:Rust command / save / AI contract + +这一层负责: + +- `EditorCommand` +- AI 生成与改写 +- 保存与冲突检测 +- 审计、trace、历史 +- 对外导入导出合同 + +也就是说: + +> **AI 的正式入口必须先进入 Rust 命令层,而不是直接进入浏览器编辑器 JSON。** + +## 5.4 Host 层:Leptos island,而不是长期 iframe + +长期正式形态必须是: + +- 主编辑区作为页面内正式 editor island +- 与真实页面共享同一交互壳和状态上下文 +- 能被 AI 面板、侧栏、评论、引用面板通过正式接口驱动 + +长期不应固定成: + +- `iframe + postMessage + save-request` + +因为那只适合作为: + +- 迁移期桥接 +- 单独验收 +- embed 验证 + +不适合作为长期主编辑区的正式形态。 + +--- + +## 6. 纠偏后的落地顺序 + +后续推进建议分五段,不再平均推进所有功能。 + +## 6.1 Phase A:主链降温与事实回正 + +目标: + +- 把当前过渡 host 从默认主链降回实验地位 +- 恢复“还未正式切流完成”的事实口径 + +清单: + +- [x] 回退 `DocumentContent` 中直接以 `EditorHost` 替换默认主编辑器的改动 +- [x] 回退 `runtime-config.ts` 中把 `documentEditorHost` 提前写死成事实默认值的改动 +- [x] 回退 `page.tsx` 中把 `edit=1` 当成主推进抓手的做法 +- [x] 明确 `EditorHost`、`LeptosTiptapEditorHost` 当前只作为 `debug/prototype bridge` +- [x] 保留 spike、保留 Rust Tiptap bridge、保留测试 + +退出标准: + +- 默认文档页不再假装“主编辑器已切流完成” +- 当前架构讨论重新回到真实状态 + +## 6.2 Phase B:Rust truth 单边界收口 + +目标: + +- 停止让前端 host 掌握正式保存边界 + +清单: + +- [x] 把 `EditorBlockDocument <-> Tiptap doc` 的正式边界收口到 Rust 协议层 +- [ ] TS `tiptap-content-converter.ts` 降级为测试/过渡适配器,不再作为唯一正式保存边界 +- [ ] 统一 `block_id` 口径:Rust 真相优先,`UniqueID` 仅作 runtime 辅助 +- [x] 明确最小块型正式覆盖:`Paragraph`、`Heading`、`BulletListItem`、`NumberedListItem`、`Todo`、`Quote`、`CodeBlock` +- [ ] 明确 AI / CLI / 人类编辑共享同一套 `EditorCommand` + +退出标准: + +- 任一文档编辑变更都能回到 Rust `EditorBlockDocument` +- 前端不再拥有第二套长期 editor truth + +## 6.3 Phase C:把编辑器本体迁到正式 Leptos island + +目标: + +- 把 `leptos-tiptap` 从“外部 runtime / bridge host”变成“正式页面内 Leptos island 本体” + +清单: + +- [x] 明确长期 host 不再以 `iframe + postMessage` 为主 +- [x] 明确“正式 island”不等于 React 壳内调用 `bridge.create/document/command`;正式主链必须是 Leptos/WASM 编辑器本体挂到页面内指定容器 +- [x] 设计正式的 Leptos editor mount 方式,使主编辑区与页面壳共享同一状态上下文 +- [x] 把 `8123 spike` 从 `mount_to_body` 页面演示形态改成“可挂到指定 DOM 节点、可 mount/unmount 的 island 本体” +- [x] 把宿主与 island 的通信收敛成同页 typed 接口或自定义事件,而不是 `iframe` 消息协议 +- [x] 让 `undo / redo / current block / selection / insert reference / replace content` 进入 island 内部真实接口 +- [ ] 让 AI 面板与编辑区之间共享正式 editor handle 或等价命令入口 + +退出标准: + +- 主编辑区已不再依赖跨窗口消息维持基本交互 +- 主编辑区已不再依赖 React `runtime bridge` 作为长期控制面 +- Leptos 编辑器本体真正进入主文档页主交互链 + +## 6.4 Phase D:正式 island 接入后的真实回归锁定 + +目标: + +- 在 Rust truth 和正式 Leptos island 都成立之后,先把默认切流前必须锁定的真实回归做完整 + +清单: + +- [x] 以真实 `/documents/[id]` 页面完成“正式 island”打开、编辑、保存、刷新回填回归 +- [x] AI 改文档时先走 Rust `EditorCommand` +- [ ] 人类编辑与 AI 编辑都回到同一份块真相 +- [x] `block menu / slash / toolbar / turn into / 引用插入 / undo / redo` 在真实页面中行为稳定 +- [ ] 宿主只保留 mount/ref、初始化数据、最小回调与 editorBridge 出口,不再自己维护编辑器内部状态机 +- [x] 补齐默认切流前的 smoke / rollback / 观测检查项 + +退出标准: + +- 进入 Phase E 时,不再依赖主观判断说“应该可以切默认了” + +## 6.5 Phase E:正式切换默认主编辑器为页面内 `leptos-tiptap` island + +目标: + +- 正式把默认主编辑器从 `BlockNote` 切到页面内 `leptos-tiptap` island +- 让 `BlockNote` 退到显式 fallback / 兼容链,而不是继续承担默认主链 +- 让默认主链不再依赖 `iframe runtime` 或 React `bridge runtime` + +清单: + +- [x] 收敛正式 host 命名:`leptos_tiptap_runtime` 改为“正式 island host”语义,`leptos_tiptap_inline` 仅保留为兼容别名,`leptos_tiptap_iframe_debug` 继续只保留给调试桥 +- [x] 修改 `DocumentContent`、`page.tsx`、`runtime-config.ts` 的默认分发逻辑,使不带任何参数的 `/documents/[id]` 直接进入正式 `leptos-tiptap` island 主链 +- [x] 删除默认主链对 `loadLeptosTiptapRuntime()` / `bridge.create()` / `bridge.document()` / `bridge.command()` 这种 React 控制面的依赖 +- [x] 保留 `blocknote` 显式回退开关:可以是 runtime config、query 参数或受控 feature flag,但必须做到“不改代码即可回退” +- [x] 把 `BlockNoteEditor` 降为 fallback / 对照链,不再继续承接新的长期 editor truth、save contract、AI contract 语义 +- [x] 为默认切流补齐真实 smoke:打开、输入、撤销、重做、引用插入、AI 改写、保存、刷新回填、异常回退 +- [x] 为默认切流补齐观测项:runtime 加载失败、hydration 失败、Rust command 失败、保存失败、自动回退次数 +- [x] 先完成开发环境默认切流,再进入受控范围的真实使用默认切流;每一步都保留 kill switch +- [x] 更新设计稿、运行时注释与口径,明确“默认主编辑器已切到页面内 `leptos-tiptap` island,`BlockNote` 为 fallback/兼容链” + +### 6.5.1 当前验证记录(2026-04-21,2026-04-29 复核) + +- [x] `http://127.0.0.1:3000/documents/[id]` 在不带 `editorHost` 参数时默认进入页面内 `leptos_tiptap` island,而不是 `iframe` 或 React `bridge runtime` +- [x] 侧边栏“新建页面”不再依赖 `edit=1`;当前 3000 Rust Web 侧栏 `+` 会通过 `/api/tree/commands` 新建页面并直接进入默认 `leptos-tiptap` 主链,`task122` 已覆盖 +- [ ] `node /mnt/Data1T/mnote/scripts/task108-document-default-editor-cutover-smoke.js` 已通过 +- [ ] `editorHost=blocknote` 显式回退在当前 3000 Rust Web shell 下尚未通过 `task108`;旧 Next/React fallback 只能作为 legacy/对照链,不能作为当前 3000 完成证据 +- [x] `DocumentContent` 已暴露主链观测点:`runtime_load_failed`、`host_init_failed`、`command_failed`、`save_failed`、`fallback_count` +- [x] 已修正 island `embedded` 模式判定,不再因为缺少 `?embedded=1` 而把 spike 页面壳误渲染进 3000 正式文档页 +- [x] 已移除正式主链中的 spike 开发态壳元素:`hero`、重复标题、开发态提示文案、调试抽屉不再出现在 `/documents/[id]` 默认视图 +- [x] 已在真实浏览器验证:`slash` 菜单、选区浮动工具栏、空页首屏块手柄都能在 3000 默认主链中出现 +- [x] 已在真实浏览器验证:默认 island 首屏不再刷 `Maximum update depth exceeded`,`change` 风暴已收敛,普通文本输入后可进入 `saved`,刷新后可回填 +- [x] 已在真实浏览器验证:双文档 A/B 切换后各自内容不串页,刷新后按页回填;`/api/documents/save` 请求中的 `documentId/workspaceId` 已按当前页面正确绑定 +- [x] 已修正正式主链中的本地草稿隔离:Rust island 不再使用全局单一 `localStorage` key,改为按 `workspaceId/documentId` 分桶 +- [x] 已修正正式主链中的布局偏移:页面内 island 宿主不再额外施加横向 padding,编辑正文列、块手柄和 drop indicator 已回到同一几何基准 +- [x] 已收敛切页期的 disposed signal 访问风险:窗口级 `mousemove/mouseup/keydown` listener 已改为 `try_get_untracked/try_set` 安全访问,避免在页面切换时继续触碰已释放 reactive value +- [ ] `task108` 当前仍未通过;2026-04-29 复核失败在 `?editorHost=blocknote` 后未出现 `data-editor-host-active="blocknote"`,因此不能把 blocknote fallback 计入当前 3000 Rust shell 完成项 +- [x] `task121` / `task122` 已覆盖当前 3000 默认 island 的 hydrate、输入、保存、reload 读回和 UI 新建页面进入主链 +- [ ] 当前 `slash / floating toolbar / drag handle / turn into` 仍是 island 内部的最小自制实现,尚未对齐官方 notion-like 模板的组件层级、视觉细节与菜单能力 + +### 6.5.2 Phase E 硬约束 + +- 正式主链中的 `leptos-tiptap` 必须是页面内 Leptos island 本体,不接受 `iframe`、外部 runtime 页面壳或 React `bridge runtime` 作为“已完成”实现。 +- `8123` 只允许作为能力参考、资产构建来源或调试入口;不允许继续把 `8123` 页面本身嵌进 3000 主文档页。 +- 宿主只允许保留容器挂载、初始化数据、最小回调和兼容期 `editorBridge` 出口;`slash / toolbar / block menu / selection / top-level mutation` 必须回到 island 内部。 + +退出标准: + +- 不带参数的 `/documents/[id]` 默认进入 `leptos-tiptap` +- 出现问题时可通过显式开关快速回退到 `BlockNote`,而不是回滚代码 +- 默认链路下的 Rust truth、AI contract、save contract 保持不变 +- `BlockNote` 不再是默认主编辑器,但仍保留为迁移期保险丝 + +--- + +## 7. 当前阶段明确不做什么 + +为了避免再次跑偏,下面这些项当前不进入前排: + +- 不继续围绕 `runtime shell` 做 polish +- 不把 `iframe host` 继续包装成“正式页面” +- 不先做图片上传整链 +- 不先做表格 +- 不先做协作 +- 不先接 Tiptap Cloud AI +- 不先做一轮大规模 Wolai 对标 UI 细节打磨 +- 不回到“自研 Rust-native 输入层”作为当前主交付 + +这些项并非永久放弃,而是: + +> **它们都不该排在“纠正主链边界错误”之前。** + +--- + +## 8. 对 AI-first 目标的直接收口 + +如果 `mnote` 的长期预期是: + +- AI 直接在主编辑区编写 +- 人类只做审核和辅助编辑 + +那么后续必须固定下面这条规则: + +1. AI 正式入口只走 Rust `EditorCommand`。 +2. `EditorBlockDocument` 是 AI、人类、CLI 的共享真相。 +3. `Tiptap/leptos-tiptap` 只负责把这份真相投影成可编辑 surface。 +4. AI 不直接成为 HTML/Tiptap JSON 拼接器。 + +这条规则的意义在于: + +- 不把系统命运绑定到某个浏览器编辑器格式 +- 为未来替换 surface 保留空间 +- 保持 Rust 语义主导权 + +--- + +## 9. 最终结论 + +这轮 `leptos-tiptap` 集成真正的问题,不是路线选错,而是: + +> **把一条过渡桥接线过早抬成了默认主链,导致 `Tiptap` 看起来像在主导系统,而 Rust + Leptos 还没有真正主导页面内编辑、保存合同和 AI 合同。** + +因此这份纠偏文档固定如下结论: + +1. `Tiptap + leptos-tiptap` 继续保留为主路线,不回到“自研 Rust 版 Tiptap”。 +2. 当前需要回退的不是 `Tiptap` 本身,而是默认主链中 `iframe host + TS converter + 旧 save path` 的提前切流。 +3. 当前需要保留的是 Rust `EditorBlockDocument <-> Tiptap` 桥层、测试和 spike 中已经成立的 surface 行为。 +4. 下一步不是继续 polish runtime shell,而是先把 editor truth、save contract、AI contract 收回 Rust,再把 `leptos-tiptap` 做成正式 Leptos island。 +5. 只有在这之后,`leptos-tiptap` 才能真正体现 Rust + Leptos 架构的优势,而不是继续表现成一个被宿主页面嵌入的外来编辑器。