docs: separate design reference queue
- 将不直接执行的 process-reference 文档迁入各域 reference 目录 - 更新 design/README、AGENTS 和总序文档,固定 process/draft/reference/done 目录语义 - 修正活跃文档中指向旧 process 位置的参考链接 验证:git diff --check;codegraph sync .
This commit is contained in:
-549
@@ -1,549 +0,0 @@
|
||||
# 5-14 [process] Zed / Lapce / VS Code 参考采用矩阵 v1
|
||||
|
||||
> 更新时间:2026-05-19
|
||||
>
|
||||
> 当前状态:`process-reference`。本文是参考实现采用矩阵,不是当前执行 checklist;具体实现任务应拆到对应 domain 的 process/done 文档。
|
||||
>
|
||||
> 关联背景:
|
||||
> - `/mnt/Data1T/mnote/CURRENT_ARCHITECTURE.md`
|
||||
> - `/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`
|
||||
> - `/mnt/Data1T/mnote/design/07-ai/process/7-14-online-local-ai-markdown-editing-convergence-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/04-tree-domain/done/4-24-resource-tree-filetree-pagetree-source-contract-checklist-v1.md`
|
||||
>
|
||||
> 本地参考源码:
|
||||
> - VS Code:`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/vscode`
|
||||
> - Zed:`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/zed`
|
||||
> - Lapce:`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/lapce`
|
||||
> - SideX:`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/sidex-main`
|
||||
|
||||
## 1. 文档目的
|
||||
|
||||
这份文档回答一个工程路线问题:
|
||||
|
||||
> **MNote 当前 Rust 主线应继续从 VS Code 源码里参考什么,又应该从哪些 Rust 编辑器项目里借鉴现成结构,避免把 VS Code TypeScript / Electron 代码大量转译到 Rust。**
|
||||
|
||||
当前结论是:
|
||||
|
||||
> **Zed 作为主架构参考,Lapce 作为辅助实现参考,SideX 作为 VS Code 行为迁移到 Rust 后端的桥接参考,VS Code 官方源码作为行为规格参考。**
|
||||
|
||||
这不是说 MNote 要改造成通用 IDE,也不是要复制 Zed / Lapce 的桌面产品形态。MNote 的主线仍然是:
|
||||
|
||||
- `tree-first graph kernel`
|
||||
- local-first Markdown workspace
|
||||
- Rust `mnote-web` Web shell
|
||||
- `Page Aggregate` / `File Tree` / `Resource Tree` 投影
|
||||
- Hermes / Reasonix agent 在授权目录内进行文件编辑
|
||||
|
||||
Zed、Lapce、SideX、VS Code 只用于补齐“编辑器工作区基础设施”的成熟参考,尤其是文件树、打开态 buffer、dirty/save、搜索、外部文件变更、agent 文件编辑权限这些层。
|
||||
|
||||
---
|
||||
|
||||
## 2. 总体判断
|
||||
|
||||
### 2.1 不建议把 VS Code 源码作为转译目标
|
||||
|
||||
VS Code 官方源码仍然是 TypeScript / Electron 架构。它对 MNote 有很强的产品行为参考价值,但不适合作为 Rust 代码转译目标。
|
||||
|
||||
主要风险:
|
||||
|
||||
- VS Code 的 workbench、service、contribution、lifecycle 与 Electron / DOM / Node 绑定很深。
|
||||
- 大量接口依赖 TypeScript DI、事件模型、extension host 和 workbench contribution 体系,转到 Rust 后会变成重建一套框架。
|
||||
- 文件服务、保存冲突、watcher、context key 等模块很有价值,但它们的价值在行为合同和边界划分,不在逐行代码。
|
||||
- MNote 当前不是通用 IDE,而是 local-first Markdown workspace + agent 编辑器,直接转译会把产品边界拉宽。
|
||||
|
||||
因此,VS Code 的定位应固定为:
|
||||
|
||||
> **行为规格参考,不作为 Rust 实现蓝本。**
|
||||
|
||||
### 2.2 Zed 是当前最接近 MNote 长期主线的 Rust 参考
|
||||
|
||||
Zed 的价值不在 UI 壳,而在这条 Rust 原生编辑器链路:
|
||||
|
||||
> `Worktree / Snapshot -> WorktreeStore -> Project / ProjectPath -> BufferStore / MultiBuffer / Editor -> Search / FileFinder -> AgentTool / ToolPermission`
|
||||
|
||||
这与 MNote 的长期方向高度重合:
|
||||
|
||||
- 多 workspace root / local folder。
|
||||
- 稳定的 `{root + relative path}` 文件身份。
|
||||
- 文件树扫描、ignore、watch 增量更新。
|
||||
- 打开态 buffer、dirty/save、路径到 buffer 的索引。
|
||||
- 全文搜索、快速打开。
|
||||
- agent 文件读写、patch、权限与 allowed roots。
|
||||
|
||||
Zed 应作为:
|
||||
|
||||
> **workspace / project / buffer / search / agent 文件编辑权限的主参考。**
|
||||
|
||||
### 2.3 Lapce 是轻量实现与边界拆分的辅助参考
|
||||
|
||||
Lapce 的价值不在“产品完整度超过 Zed”,而在它更轻、更容易抽取工程模式:
|
||||
|
||||
> `UI -> typed RPC -> proxy -> watcher / search / plugin / terminal -> doc / buffer delta / save`
|
||||
|
||||
这对 MNote 有两类价值:
|
||||
|
||||
- typed command / request / response 如何拆到 proxy / backend 执行层。
|
||||
- 文档 `rev / delta / dirty / save` 生命周期如何保持简单。
|
||||
|
||||
Lapce 应作为:
|
||||
|
||||
> **typed RPC、proxy 边界、doc delta/save、file explorer state machine、配置和 keymap 的辅助参考。**
|
||||
|
||||
### 2.4 SideX 不替代 Zed,但应加入参考矩阵
|
||||
|
||||
SideX 的 README 明确写的是:
|
||||
|
||||
> **VSCode's workbench, without Electron.**
|
||||
|
||||
也就是说,SideX 不是 Rust 原生重写 VS Code。它更接近:
|
||||
|
||||
> **保留 VS Code TypeScript workbench / Monaco / service 模型,把 Electron main process、Node fs、node-pty、sqlite、watcher、search 等系统能力替换为 Tauri + Rust command / crate。**
|
||||
|
||||
因此 SideX 不能替代 Zed 成为 MNote 的主架构参考,原因是:
|
||||
|
||||
- 它仍保留 VS Code 的 TypeScript workbench、DI、service、contrib 体系。
|
||||
- 它的目标是“移植 VS Code”,不是建立 projection-first 的 local-first Markdown workspace。
|
||||
- 它会把 MNote 拉回通用 IDE / extension host / desktop shell 的复杂度。
|
||||
|
||||
但 SideX 很适合作为一类新参考:
|
||||
|
||||
> **VS Code 行为如何落到 Rust 后端的迁移样本。**
|
||||
|
||||
它比官方 VS Code 更接近 MNote 的地方在于:
|
||||
|
||||
- Rust `sidex-workspace` 已经抽出 file tree、watcher、search、path util、dirty diff、multi-root 等 workspace crate。
|
||||
- Rust `sidex-keymap` 已经实现接近 VS Code `when` clause 的 context key evaluator。
|
||||
- Rust `sidex-settings` 已经实现 default / user / workspace 的分层 settings。
|
||||
- Tauri command 层把 `fs`、terminal、git、search、storage 等 Node/Electron 能力转换成 Rust command 边界。
|
||||
|
||||
所以本矩阵应把 SideX 放在:
|
||||
|
||||
> **“VS Code 行为规格”和“MNote Rust 实现”之间的桥接参考。**
|
||||
|
||||
### 2.5 Helix 不作为主参考
|
||||
|
||||
Helix 是优秀的 Rust 终端编辑器,适合参考 LSP、Tree-sitter、文本核心和配置理念,但它不是 workbench / Web shell / local-first workspace 主参考。
|
||||
|
||||
---
|
||||
|
||||
## 3. 参考源职责划分
|
||||
|
||||
### 3.1 Zed:主架构参考
|
||||
|
||||
重点参考:
|
||||
|
||||
- `crates/worktree/src/worktree.rs`
|
||||
- `crates/project/src/worktree_store.rs`
|
||||
- `crates/project/src/project.rs`
|
||||
- `crates/project/src/buffer_store.rs`
|
||||
- `crates/project/src/project_search.rs`
|
||||
- `crates/file_finder/src/file_finder.rs`
|
||||
- `crates/agent/src/tools/edit_file_tool.rs`
|
||||
- `crates/agent/src/tools/read_file_tool.rs`
|
||||
- `crates/agent/src/tools/write_file_tool.rs`
|
||||
- `crates/agent/src/tools/edit_session.rs`
|
||||
- `crates/agent/src/tool_permissions.rs`
|
||||
|
||||
不重点采用:
|
||||
|
||||
- GPUI / Entity / App / Window 渲染壳。
|
||||
- pane / dock / desktop lifecycle。
|
||||
- collab / remote / diagnostics 的完整产品线。
|
||||
- 与 MNote local-first Markdown 主线无关的通用 IDE 复杂度。
|
||||
|
||||
### 3.2 Lapce:辅助实现参考
|
||||
|
||||
重点参考:
|
||||
|
||||
- `lapce-app/src/doc.rs`
|
||||
- `lapce-rpc/src/proxy.rs`
|
||||
- `lapce-proxy/src/dispatch.rs`
|
||||
- `lapce-proxy/src/watcher.rs`
|
||||
- `lapce-app/src/file_explorer/data.rs`
|
||||
- `lapce-app/src/global_search.rs`
|
||||
- `lapce-app/src/command.rs`
|
||||
- `lapce-app/src/main_split.rs`
|
||||
- `lapce-core/src/syntax/mod.rs`
|
||||
- `lapce-core/src/language.rs`
|
||||
- `lapce-core/src/encoding.rs`
|
||||
- `lapce-app/src/config.rs`
|
||||
- `defaults/settings.toml`
|
||||
- `lapce-app/src/keypress/loader.rs`
|
||||
- `defaults/keymaps-common.toml`
|
||||
- `lapce-proxy/src/plugin/mod.rs`
|
||||
|
||||
不重点采用:
|
||||
|
||||
- Lapce 自身 UI 框架选择。
|
||||
- 完整插件生态重建。
|
||||
- 终端、调试器等 MNote 当前阶段不需要的 IDE 面。
|
||||
|
||||
### 3.3 VS Code:行为规格参考
|
||||
|
||||
继续参考:
|
||||
|
||||
- `src/vs/platform/files`
|
||||
- `src/vs/platform/commands`
|
||||
- `src/vs/platform/contextkey`
|
||||
- `src/vs/workbench/contrib/files`
|
||||
- `src/vs/workbench/services/filesConfiguration`
|
||||
- `src/vs/base/browser/ui/list`
|
||||
- `src/vs/base/browser/ui/tree`
|
||||
|
||||
重点看行为:
|
||||
|
||||
- file service 的能力边界。
|
||||
- save / save as / revert / conflict 的用户体验。
|
||||
- workspace watcher 与外部文件变更处理。
|
||||
- context key / command enablement。
|
||||
- file import / export / open editors。
|
||||
- tree/list 的选择、焦点、键盘导航和 DnD 行为。
|
||||
|
||||
不采用:
|
||||
|
||||
- TypeScript service 体系逐行转译。
|
||||
- Electron workbench lifecycle。
|
||||
- VS Code extension host 作为 MNote 当前插件模型。
|
||||
|
||||
### 3.4 SideX:VS Code 行为到 Rust 后端的桥接参考
|
||||
|
||||
重点参考:
|
||||
|
||||
- `ARCHITECTURE.md`
|
||||
- `src-tauri/src/lib.rs`
|
||||
- `src-tauri/src/commands/fs.rs`
|
||||
- `src-tauri/src/commands/validation.rs`
|
||||
- `crates/sidex-workspace/src/lib.rs`
|
||||
- `crates/sidex-workspace/src/file_tree.rs`
|
||||
- `crates/sidex-workspace/src/watcher.rs`
|
||||
- `crates/sidex-workspace/src/search.rs`
|
||||
- `crates/sidex-workspace/src/path_util.rs`
|
||||
- `crates/sidex-keymap/src/context.rs`
|
||||
- `crates/sidex-keymap/src/resolver.rs`
|
||||
- `crates/sidex-settings/src/settings.rs`
|
||||
- `crates/sidex-settings/src/jsonc.rs`
|
||||
- `crates/sidex-terminal/src/pty.rs`
|
||||
- `crates/sidex-terminal/src/manager.rs`
|
||||
|
||||
重点看边界:
|
||||
|
||||
- Electron / Node 能力如何映射为 Rust command。
|
||||
- 文件系统 command 如何先做 path validation,再进入 workspace crate。
|
||||
- Rust watcher / search / file tree 如何对外输出可序列化事件和结果。
|
||||
- VS Code `when` clause / context key 如何在 Rust 侧建模。
|
||||
- default / user / workspace settings 如何分层合并。
|
||||
- PTY / terminal manager 如何从 Node native 模块迁移到 Rust。
|
||||
|
||||
不采用:
|
||||
|
||||
- VS Code TypeScript workbench 的整体移植路线。
|
||||
- Tauri desktop shell 作为 MNote 当前 Web 主壳。
|
||||
- extension host / debugger / terminal / git 的完整 IDE 产品面。
|
||||
- SideX 当前对绝对路径 command 的简单安全模型;MNote 必须继续使用 allowed roots、workspace root、symlink escape 和文件版本仲裁。
|
||||
|
||||
---
|
||||
|
||||
## 4. 采用矩阵
|
||||
|
||||
| 主题 | 主参考 | MNote 当前缺口 | 采用方式 | 优先级 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `ProjectPath` / 文件身份 | Zed `ProjectPath` | local folder、page `.md`、resource asset 需要稳定 `{workspace/root + rel path}` 身份 | 借鉴模式,映射到 `KernelObjectIdentity` / File Tree row | P0 |
|
||||
| Worktree / root 管理 | Zed `worktree` + `worktree_store`,SideX `sidex-workspace` | local-first workspace 需要扫描、ignore、watch、root id、路径归一 | Zed 做主模型,SideX 补充 Rust workspace crate 拆分参考 | P0 |
|
||||
| BufferStore / 打开态文件 | Zed `buffer_store`,Lapce `doc.rs` | tiptap、AI、外部编辑器会同时触碰同一 `.md`,需要统一 dirty/save/version | Zed 做架构,Lapce 做简化实现参考 | P0 |
|
||||
| dirty / save / external change | VS Code file save 行为,Zed buffer,Lapce doc,SideX dirty diff / watcher | `/api/documents/save` 兼容面仍未完全退出,文件版本冲突模型待收口 | VS Code 校准行为,Zed/Lapce 定模型,SideX 看 Rust watcher / dirty diff 落点 | P0 |
|
||||
| agent 文件编辑权限 | Zed `edit_file_tool` / `tool_permissions` | Hermes / Reasonix 需要 allowed roots、symlink escape、防脏 buffer 覆盖 | 直接作为权限模型主参考 | P0 |
|
||||
| workspace search | Zed `project_search`,Lapce `global_search`,SideX `sidex-workspace/search.rs` | MNote 需要本地全文搜索与 Page Aggregate / File Tree 对齐 | Zed 做主搜索模型,Lapce 做结果投影,SideX 补充 Rust 并行搜索实现参考 | P1 |
|
||||
| file finder / quick open | Zed `file_finder` | 工作区文件快速打开与 object tab host 尚未成体系 | 借鉴候选生成与排序,不复制 UI | P1 |
|
||||
| file explorer 状态机 | Lapce `file_explorer/data.rs`,SideX `file_tree.rs`,VS Code tree 行为 | File Tree / Page Tree / Resource Tree 需要选择、展开、刷新、右键命令一致性 | Lapce 状态机 + SideX Rust file tree 能力 + VS Code 行为规格 | P1 |
|
||||
| command / context key | VS Code `commands` / `contextkey`,SideX `sidex-keymap/context.rs`,Lapce `command.rs` | tree/page/object tab 命令 enablement 仍容易散落到 UI | VS Code 给语义,SideX 给 Rust when-clause evaluator 参考 | P1 |
|
||||
| typed RPC / backend proxy | Lapce `lapce-rpc` + `lapce-proxy`,SideX Tauri commands | Rust Web、worker、local FS executor、agent runtime 需要 typed request 边界 | Lapce 参考协议拆分,SideX 参考 Node/Electron 能力迁移到 Rust command | P2 |
|
||||
| config / settings | SideX `sidex-settings`,Lapce `config.rs` + `settings.toml`,VS Code settings 行为 | workspace / user / page options / AI policy 需要层级合并规则 | SideX 分层 settings 更接近 Rust 落地,Lapce/VS Code 校准行为 | P2 |
|
||||
| keymap | SideX `sidex-keymap`,Lapce keypress loader,VS Code keybinding 行为 | editor shell、file tree、command palette 后续需要统一快捷键 | SideX 参考 Rust context/when 解析,Lapce 参考 loader,VS Code 校准行为 | P2 |
|
||||
| terminal / PTY 边界 | SideX `sidex-terminal`,VS Code terminal 行为 | MNote 当前不是 IDE 终端优先,但 agent/local workspace 后续可能需要受控命令执行面 | 只参考 Rust PTY manager 和安全边界,不进入当前主线 | P3 |
|
||||
| MultiBuffer / excerpts | Zed `MultiBuffer` | AI review、搜索结果、跨文件上下文需要多片段视图 | Phase C 之后采用,当前不前置 | P3 |
|
||||
| review diff / streaming apply | Zed agent edit session,VS Code diff 行为 | AI suggest/review 仍设计冻结 | 只保留为后续参考,不当前实施 | P3 |
|
||||
| 插件运行时 | Lapce plugin,SideX WASM extension,VS Code extension host | MNote 当前插件是 simplemindmap / office / local tool,不需要通用扩展平台 | 暂不采用,只保留观察 | P3 |
|
||||
|
||||
---
|
||||
|
||||
## 5. P0 实施建议
|
||||
|
||||
P0 不应从 UI 开始,而应先把文件身份、打开态 buffer 和 agent 文件权限三个底座定住。
|
||||
|
||||
### 5.1 固定 MNote 的 `WorkspacePath`
|
||||
|
||||
目标:
|
||||
|
||||
> **用一个 Rust 侧稳定结构表达“哪个 workspace root 下的哪个相对路径”。**
|
||||
|
||||
参考:
|
||||
|
||||
- Zed `ProjectPath`
|
||||
- Zed `WorktreeStore`
|
||||
|
||||
建议语义:
|
||||
|
||||
- `workspace_id`
|
||||
- `root_id`
|
||||
- `source_kind`
|
||||
- `relative_path`
|
||||
- `object_identity`
|
||||
- `resource_kind`
|
||||
|
||||
约束:
|
||||
|
||||
- 不允许 UI 只拿绝对路径当对象身份。
|
||||
- 不允许 Page Tree / File Tree / AI 工具各自生成不同 path key。
|
||||
- symlink、`..`、case sensitivity、ignore 规则必须由 Rust workspace 层处理。
|
||||
|
||||
### 5.2 建立 `BufferStore` / `DocumentBuffer` 最小模型
|
||||
|
||||
目标:
|
||||
|
||||
> **让 tiptap autosave、AI 文件写入、外部编辑器修改都围绕同一份文件版本和 dirty 状态仲裁。**
|
||||
|
||||
参考:
|
||||
|
||||
- Zed `buffer_store.rs`
|
||||
- Lapce `doc.rs`
|
||||
- VS Code save conflict 行为
|
||||
|
||||
建议最小字段:
|
||||
|
||||
- `workspace_path`
|
||||
- `file_version`
|
||||
- `base_content_hash`
|
||||
- `current_content_hash`
|
||||
- `dirty_state`
|
||||
- `last_loaded_at`
|
||||
- `last_saved_at`
|
||||
- `external_change_state`
|
||||
|
||||
当前必须避免:
|
||||
|
||||
- 继续把 `/api/documents/save` 当长期正文写入主入口。
|
||||
- tiptap 和 AI 分别维护互不知情的 revision。
|
||||
- 外部文件变更直接覆盖前台 dirty buffer。
|
||||
|
||||
### 5.3 收口 agent 文件编辑权限
|
||||
|
||||
目标:
|
||||
|
||||
> **MNote 只负责计算授权范围和同步投影,Hermes / Reasonix 在 allowed roots 内用自身 patch/diff 能力编辑文件。**
|
||||
|
||||
参考:
|
||||
|
||||
- Zed `edit_file_tool.rs`
|
||||
- Zed `edit_session.rs`
|
||||
- Zed `tool_permissions.rs`
|
||||
|
||||
必须具备:
|
||||
|
||||
- allowed roots。
|
||||
- 当前文件引用。
|
||||
- selection / range。
|
||||
- symlink escape 防护。
|
||||
- dirty buffer 写入前检查。
|
||||
- 写后 watcher / projection refresh。
|
||||
|
||||
这条线与当前 `7-14 online/local AI Markdown editing convergence` 一致:普通 local-first Markdown 编辑优先走 agent 原生文件编辑能力,`mnote.doc.markdown_edit` 只作为 cloud / remote agent / compat fallback。
|
||||
|
||||
---
|
||||
|
||||
## 6. P1 / P2 / P3 实施建议
|
||||
|
||||
### 6.1 P1:搜索、文件树、命令上下文
|
||||
|
||||
P1 解决的是 workspace 产品体验闭环:
|
||||
|
||||
- 本地全文搜索。
|
||||
- 快速打开。
|
||||
- File Tree / Resource Tree / Page Tree 展开、选择、刷新、右键命令。
|
||||
- command enablement / context key。
|
||||
|
||||
参考组合:
|
||||
|
||||
- Zed `project_search` + `file_finder`
|
||||
- Lapce `global_search` + `file_explorer/data.rs`
|
||||
- VS Code tree/list/contextkey 行为
|
||||
|
||||
验收口径:
|
||||
|
||||
- 搜索结果能回到稳定 `WorkspacePath` / `ObjectIdentity`。
|
||||
- 文件树操作只发 command,不直接改真实对象。
|
||||
- 右键菜单和快捷键基于统一 command context,不在 UI 分支里临时判断。
|
||||
|
||||
### 6.2 P2:typed RPC、配置、keymap
|
||||
|
||||
P2 解决的是边界清晰和长期可维护:
|
||||
|
||||
- UI 到 Rust executor 的 typed request / response。
|
||||
- user / workspace / page / runtime policy 的配置合并。
|
||||
- 编辑器、树、command palette 的 keymap 加载和冲突处理。
|
||||
|
||||
参考组合:
|
||||
|
||||
- Lapce `lapce-rpc/src/proxy.rs`
|
||||
- Lapce `lapce-proxy/src/dispatch.rs`
|
||||
- Lapce `config.rs`
|
||||
- Lapce `keypress/loader.rs`
|
||||
- VS Code settings / keybinding 行为
|
||||
|
||||
验收口径:
|
||||
|
||||
- route / compat / worker 不再各自定义一套命令 payload。
|
||||
- 配置合并顺序可解释、可测试。
|
||||
- 快捷键归属到 command,不直接绑定到某个 DOM 事件分支。
|
||||
|
||||
### 6.3 P3:MultiBuffer、review diff、插件运行时
|
||||
|
||||
P3 只作为后续能力,不进入当前最前排。
|
||||
|
||||
可参考:
|
||||
|
||||
- Zed `MultiBuffer`:搜索结果、AI 上下文、多文件 excerpt。
|
||||
- Zed agent edit session:review / apply / reject。
|
||||
- VS Code diff:冲突、review、保存体验。
|
||||
- Lapce plugin:轻量插件隔离。
|
||||
|
||||
当前边界:
|
||||
|
||||
- 不实施流式 apply + suggest/review。
|
||||
- 不重建 VS Code extension host。
|
||||
- 不把 MNote 拉成通用 IDE。
|
||||
|
||||
### 6.4 SideX 专项采用边界
|
||||
|
||||
SideX 可以补强 P1 / P2,但不改变 P0 主线。
|
||||
|
||||
可以参考的部分:
|
||||
|
||||
- `sidex-workspace` 的 crate 拆分:`file_tree / watcher / search / path_util / dirty_diff / multi_root`。
|
||||
- `src-tauri/src/commands/fs.rs` 的 command facade:前端请求先进入窄 command,再进入 Rust workspace 能力。
|
||||
- `src-tauri/src/commands/validation.rs` 的输入校验意识,但 MNote 需要更强的 workspace root / allowed roots / symlink escape 校验。
|
||||
- `sidex-keymap/context.rs` 的 `when` clause AST 和 evaluator,可作为 MNote command context 的 Rust 参考。
|
||||
- `sidex-settings/settings.rs` 的 default / user / workspace settings 分层,可作为 MNote user / workspace / page / AI policy 合并规则的早期参考。
|
||||
- `sidex-workspace/search.rs` 的 ignore-aware、rayon 并行全文搜索,可作为本地 Markdown workspace 搜索实现参考。
|
||||
|
||||
不应采用的部分:
|
||||
|
||||
- 不采用“把 VS Code TypeScript workbench 原样移植到 Tauri”的产品路线。
|
||||
- 不把 MNote `3000` Rust Web 主壳替换成 Tauri desktop shell。
|
||||
- 不把 extension host / debugger / terminal / git 做成当前默认产品能力。
|
||||
- 不直接沿用 SideX 的绝对路径 command 安全模型;MNote 必须以 workspace identity、allowed roots、file version 和 agent permission 为准。
|
||||
|
||||
对 MNote 的实际帮助是:
|
||||
|
||||
> **当我们从 VS Code 学到一个行为时,可以先看 SideX 是否已有 Rust crate / Tauri command 级别的落地方式;如果有,就用 SideX 校准“这个行为如何从 Node/Electron 能力迁移到 Rust 后端”,再按 MNote 的 kernel / projection / command contract 重写。**
|
||||
|
||||
---
|
||||
|
||||
## 7. VS Code 仍应继续看的源码
|
||||
|
||||
虽然不建议转译 VS Code,但下面这些仍值得继续深读,因为它们定义了成熟编辑器的行为边界。
|
||||
|
||||
### 7.1 文件服务与保存冲突
|
||||
|
||||
重点:
|
||||
|
||||
- `src/vs/platform/files`
|
||||
- `src/vs/workbench/services/filesConfiguration`
|
||||
- `textFileSaveErrorHandler`
|
||||
|
||||
关注问题:
|
||||
|
||||
- 文件不存在、权限失败、外部修改、编码变化、只读文件如何呈现。
|
||||
- dirty buffer 与磁盘版本冲突时如何让用户选择。
|
||||
- save / save as / revert 的命令边界。
|
||||
|
||||
### 7.2 Watcher 与外部变更
|
||||
|
||||
重点:
|
||||
|
||||
- `workspaceWatcher`
|
||||
- file service event。
|
||||
|
||||
关注问题:
|
||||
|
||||
- 外部编辑器修改 `.md` 后如何刷新投影。
|
||||
- 当前 buffer dirty 时如何避免静默覆盖。
|
||||
- rename / delete / move 对打开 tab 和 file tree 的影响。
|
||||
|
||||
### 7.3 Context Key 与 Command Enablement
|
||||
|
||||
重点:
|
||||
|
||||
- `src/vs/platform/contextkey`
|
||||
- `src/vs/platform/commands`
|
||||
|
||||
关注问题:
|
||||
|
||||
- 命令是否可用不应散落在 UI 条件判断里。
|
||||
- File Tree / Page Tree / Object Tab / Editor selection 应贡献统一 context。
|
||||
- 右键菜单、快捷键、command palette 应消费同一套 command context。
|
||||
|
||||
### 7.4 Tree / List 行为
|
||||
|
||||
重点:
|
||||
|
||||
- `src/vs/base/browser/ui/list`
|
||||
- `src/vs/base/browser/ui/tree`
|
||||
- `src/vs/workbench/contrib/files`
|
||||
|
||||
关注问题:
|
||||
|
||||
- 展开、收起、选择、焦点、键盘导航。
|
||||
- DnD 和 rename 的边界。
|
||||
- open editors / file explorer 如何处理打开态与磁盘态。
|
||||
|
||||
---
|
||||
|
||||
## 8. 风险与边界
|
||||
|
||||
### 8.1 许可与代码来源边界
|
||||
|
||||
Zed、Lapce、VS Code 都只能作为参考来源。后续实现应遵循:
|
||||
|
||||
- 不直接复制大段源码。
|
||||
- 不把第三方项目的 UI 壳、生命周期和产品概念搬进 MNote。
|
||||
- 引用具体设计时在设计稿或实现注释中标明“参考模式”,避免伪装成原创推导。
|
||||
|
||||
### 8.2 产品边界风险
|
||||
|
||||
MNote 不应因为参考 Zed / Lapce / VS Code 而变成通用 IDE。
|
||||
|
||||
必须保持:
|
||||
|
||||
- 页面正文真相仍是 local-first `.md`。
|
||||
- Rust kernel 持有树、资源、投影、命令语义。
|
||||
- Page Aggregate 是页面主投影。
|
||||
- File Tree 是 local workspace 的组织投影。
|
||||
- Hermes / Reasonix agent 文件编辑必须受 allowed roots 和文件版本模型约束。
|
||||
|
||||
### 8.3 技术路线风险
|
||||
|
||||
最大风险不是“参考不够”,而是参考源混用后边界变模糊。
|
||||
|
||||
固定规则:
|
||||
|
||||
- Zed 负责主架构参考。
|
||||
- Lapce 负责轻量实现参考。
|
||||
- SideX 负责 VS Code 行为到 Rust 后端的迁移参考。
|
||||
- VS Code 负责行为规格参考。
|
||||
- MNote 自己的 Rust kernel / projection / command contract 负责最终真相。
|
||||
|
||||
---
|
||||
|
||||
## 9. 当前建议结论
|
||||
|
||||
后续推进顺序建议固定为:
|
||||
|
||||
1. **P0:`WorkspacePath / ProjectPath`、`WorktreeStore`、`BufferStore`、agent file edit permission。**
|
||||
2. **P1:workspace search、file finder、file explorer state machine、command context。**
|
||||
3. **P2:typed RPC / proxy、SideX-style Rust command facade、config、keymap。**
|
||||
4. **P3:terminal / PTY 边界、MultiBuffer excerpt、review diff、插件运行时。**
|
||||
|
||||
一句话结论:
|
||||
|
||||
> **不要把 VS Code TypeScript 源码转译成 Rust;应以 Zed 的 Rust workspace / buffer / agent 架构为主参考,以 Lapce 的 typed RPC / proxy / doc delta 为辅助参考,以 SideX 校准 VS Code 行为迁移到 Rust 后端的落地方式,再用官方 VS Code 校准成熟编辑器行为。**
|
||||
@@ -1,238 +0,0 @@
|
||||
# 5-2 [process] Tiptap Notion-Like 模板裁剪与 `leptos-tiptap` 映射分析 v1
|
||||
|
||||
> 更新时间:2026-04-19
|
||||
>
|
||||
> 当前状态:`process-reference`。本文只作为 tiptap / Notion-like 行为模型参考,不再作为当前执行 checklist;默认主编辑器已是页面内 `leptos-tiptap` island。
|
||||
>
|
||||
> 这份文档已经按当前进展修正:
|
||||
>
|
||||
> 现在的问题不再是“这套模板能不能迁进来”,而是:
|
||||
>
|
||||
> **默认主编辑器已经切到页面内 `leptos-tiptap` island;当前问题转为:应该从官方模板保留什么行为模型,并优先把哪些能力继续收口到当前主编辑器主链。**
|
||||
|
||||
## 1. 修正后的总判断
|
||||
|
||||
当前对官方模板的使用方式应当明确为:
|
||||
|
||||
1. 它是体验 benchmark,不是直接照搬对象。
|
||||
2. 它是行为模型参考,不是 React 代码迁移目标。
|
||||
3. 现阶段最值钱的不是再扩更多模板功能,而是把已实现能力接入主编辑器。
|
||||
|
||||
因此,这份模板的正确用途是:
|
||||
|
||||
> **借它定义“官方行为应当是什么”,再用 `leptos-tiptap` + Rust 把这些行为落到 `mnote` 的主链。**
|
||||
|
||||
## 2. 官方模板里当前最该保留的参考
|
||||
|
||||
### 2.1 主编辑器扩展组合
|
||||
|
||||
核心参考文件:
|
||||
|
||||
- `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/tiptap-notion-like-registry/materialized/tiptap-templates/notion-like/components/notion-like-editor.tsx`
|
||||
|
||||
当前值得保留的不是整份文件,而是这些决策:
|
||||
|
||||
- 以 Tiptap 正式 schema 组织常用块
|
||||
- 常用块优先于低频复杂块
|
||||
- `task list / task item` 使用正式扩展,不做 HTML 占位
|
||||
- `UniqueID` 作为块身份辅助机制
|
||||
- `Indent` 作为普通块缩进增强的参考扩展
|
||||
|
||||
### 2.2 slash 菜单行为模型
|
||||
|
||||
核心参考文件:
|
||||
|
||||
- `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/tiptap-notion-like-registry/materialized/tiptap-ui/slash-dropdown-menu/slash-dropdown-menu.tsx`
|
||||
- `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/tiptap-notion-like-registry/materialized/tiptap-ui/slash-dropdown-menu/use-slash-dropdown-menu.ts`
|
||||
|
||||
当前最该保留的是:
|
||||
|
||||
- slash 入口就是块编辑器的主入口之一
|
||||
- 菜单项的分组与命令组织方式
|
||||
- 菜单锚点应跟随 caret,而不是随便找一个固定位置
|
||||
|
||||
### 2.3 浮动工具条行为模型
|
||||
|
||||
核心参考文件:
|
||||
|
||||
- `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/tiptap-notion-like-registry/materialized/tiptap-templates/notion-like/components/notion-like-editor-toolbar-floating.tsx`
|
||||
|
||||
当前最该保留的是:
|
||||
|
||||
- 工具条只在文本 selection 语境下出现
|
||||
- 文本格式命令和 `turn into` 要分清语境
|
||||
- 不要把固定显示工具条误当成“功能已完成”
|
||||
|
||||
### 2.4 左侧手柄与块菜单行为模型
|
||||
|
||||
核心参考文件:
|
||||
|
||||
- `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/tiptap-notion-like-registry/materialized/tiptap-ui/drag-context-menu/drag-context-menu.tsx`
|
||||
|
||||
当前最该保留的是:
|
||||
|
||||
- hover 先只显露手柄,不自动弹块菜单
|
||||
- 块菜单应由左侧手柄点击触发
|
||||
- `turn into / duplicate / delete / drag` 是块菜单的核心动作
|
||||
|
||||
这点非常关键,因为它直接影响我们后续对标 Wolai/Notion 的交互正确性。
|
||||
|
||||
## 3. 结合当前代码后的现实判断
|
||||
|
||||
### 3.1 已经有的东西
|
||||
|
||||
从当前实现看,下面这些已经不是“要不要做”,而是“如何迁进主链”:
|
||||
|
||||
- `paragraph / heading / list / todo / quote / code block / divider`
|
||||
- slash 菜单
|
||||
- 浮动工具条
|
||||
- 左侧手柄
|
||||
- 块菜单
|
||||
- `turn into`
|
||||
- 顶层拖拽
|
||||
- refresh 后结构保留
|
||||
|
||||
对应当前实现入口:
|
||||
|
||||
- `/mnt/Data1T/mnote/rust/spikes/leptos-tiptap-spike/src/main.rs`
|
||||
|
||||
### 3.2 当前真正的缺口
|
||||
|
||||
当前真正的缺口不是模板 feature 数量,而是下面这些:
|
||||
|
||||
- 它还在 spike 里,不在主编辑器里
|
||||
- 保存还没接上正式 Rust truth
|
||||
- `block_id` 还没成为正式合同
|
||||
- 页面引用 / 块引用还没打通
|
||||
- 图片上传与附件桥还没接
|
||||
- 表格还没做
|
||||
|
||||
所以现阶段不该继续把注意力平均分配到:
|
||||
|
||||
- Markdown 导入导出回归
|
||||
- `kode` 组件分层整理
|
||||
- 表格
|
||||
- AI Cloud
|
||||
- 协作链
|
||||
|
||||
## 4. `P0.5` 该如何采用官方模板
|
||||
|
||||
### 4.1 `P0.5` 要保留的部分
|
||||
|
||||
`P0.5` 应保留的不是一堆零散 feature,而是以下几类“官方行为”:
|
||||
|
||||
- 主编辑器使用正式 Tiptap 扩展,而不是临时 HTML 拼接
|
||||
- slash 菜单锚定 caret
|
||||
- 选中文字才出现浮动工具条
|
||||
- hover 只显手柄,点击手柄才弹块菜单
|
||||
- `turn into` 作为块菜单和工具条共享的一组块类型切换动作
|
||||
- `UniqueID` 作为 runtime 辅助,但最终块 id 仍归 Rust
|
||||
|
||||
### 4.2 `P0.5` 不要再继续扩的部分
|
||||
|
||||
`P0.5` 不应该继续向下扩这些模板能力:
|
||||
|
||||
- 协作
|
||||
- AI
|
||||
- TOC
|
||||
- 完整表格
|
||||
- 图片上传整链
|
||||
- 移动端全量工具条
|
||||
|
||||
原因很简单:
|
||||
|
||||
这些都不会帮助我们更快完成“接入主编辑器”。
|
||||
|
||||
## 5. 官方模板到 `mnote` 的映射口径
|
||||
|
||||
### 5.1 直接借行为,不借代码
|
||||
|
||||
以下部分应当“借行为模型”,不应当尝试直接照搬 React 代码:
|
||||
|
||||
- `slash-dropdown-menu`
|
||||
- `drag-context-menu`
|
||||
- `notion-like-editor-toolbar-floating`
|
||||
|
||||
原因:
|
||||
|
||||
- React hooks / context / portal 不能直接进入 Leptos
|
||||
- 真正有价值的是交互时机、状态边界、动作分组
|
||||
- 不是 `tsx` 组件本身
|
||||
|
||||
### 5.2 `block id` 需要现在就纳入主线
|
||||
|
||||
官方模板里的 `UniqueID.configure(...)` 很重要,但口径要修正成:
|
||||
|
||||
- 官方参考:
|
||||
`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/tiptap-notion-like-registry/materialized/tiptap-templates/notion-like/components/notion-like-editor.tsx`
|
||||
- Rust 真相字段:
|
||||
`/mnt/Data1T/mnote/rust/crates/core-protocol/src/editor/model.rs`
|
||||
|
||||
最终原则:
|
||||
|
||||
1. Rust `EditorBlock.block_id` 是持久化真相。
|
||||
2. Tiptap `UniqueID` 只负责浏览器 runtime 内的节点身份辅助。
|
||||
3. 不允许把前端临时 id 倒灌成正式文档真相。
|
||||
|
||||
### 5.3 `Indent` 值得做,但不在这一轮前排
|
||||
|
||||
官方缩进扩展很值得参考:
|
||||
|
||||
- `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/tiptap-notion-like-registry/materialized/tiptap-extension/indent-extension.ts`
|
||||
|
||||
但当前它不应排在主编辑器接入之前。
|
||||
|
||||
更合理的顺序是:
|
||||
|
||||
1. 先完成主编辑器接入
|
||||
2. 再完成 Rust 保存边界
|
||||
3. 再考虑普通块缩进增强
|
||||
|
||||
### 5.4 图片上传与表格都先后置
|
||||
|
||||
官方模板确实有图片与表格能力,但现在不应让它们进入主 checklist 前排。
|
||||
|
||||
原因:
|
||||
|
||||
- 图片上传不是当前切流主编辑器的阻塞项
|
||||
- 表格更不是当前高频刚需
|
||||
- 两者都容易把工作重新带到复杂 UI / 服务桥接细节
|
||||
|
||||
## 6. 哪些旧 checklist 应从主线降级
|
||||
|
||||
下面这些项不删,但不应继续放在当前主线前排:
|
||||
|
||||
- Markdown 导入导出回归
|
||||
参考 `blocks` 仍然有价值,但它更像切流后的回归与兼容工作。
|
||||
|
||||
- 借 `kode` 做 Leptos 组件分层
|
||||
这个参考可以保留,但它属于实现注记,不是当前交付里程碑。
|
||||
|
||||
- 完整表格
|
||||
继续后置。
|
||||
|
||||
- 完整图片上传
|
||||
继续后置,只保留后续最小桥接预留。
|
||||
|
||||
## 7. 对接下来工作的直接建议
|
||||
|
||||
基于当前代码和官方模板,接下来最合理的顺序是:
|
||||
|
||||
1. 先把当前 `leptos-tiptap` 编辑器接入主编辑器。
|
||||
2. 同时建立正式的 Rust load/save 边界。
|
||||
3. 在这个过程中引入稳定 block id 策略。
|
||||
4. 等它真正成为主编辑器后,再规划 `P1 / P1.5` 的 Wolai 对标体验增强。
|
||||
|
||||
这也意味着:
|
||||
|
||||
当前官方模板对我们的最大价值,不是再提供更多可抄的 feature,而是帮助我们定义:
|
||||
|
||||
- 哪些交互已经够了
|
||||
- 哪些交互时机必须修正
|
||||
- 哪些功能现在根本不该做
|
||||
|
||||
## 8. 最终结论
|
||||
|
||||
当前对这份模板的正确使用方式是:
|
||||
|
||||
> **以后续主编辑器开发继续以 Tiptap 官方能力模型为体验上限,以 `leptos-tiptap` 为运行时接入层;先完成主编辑器接入和 Rust truth 落地,再单独规划 Wolai 对标增强,不再把模板 feature 数量当作当前阶段目标。**
|
||||
@@ -1,640 +0,0 @@
|
||||
# 5-5 [process] 主编辑区与树域单一真源对齐方案 v1
|
||||
|
||||
> 更新时间:2026-05-18
|
||||
>
|
||||
> 当前状态:`process-reference`。本文作为 Page Aggregate 单一真源长期方案保留;当前执行进度与剩余项以 `5-6` 及 MVP 后阶段 checklist 为准。
|
||||
>
|
||||
> 2026-05-18 口径补充:
|
||||
> - 页面聚合的默认 source 已开始向 local-first workspace 收口;页面正文、标题、设置和上传资源不应再默认把 Convex 视作主数据层。
|
||||
> - 本文中涉及 Convex 的表述只应理解为 local-first 与 Convex-backed 两种 `WorkspaceSource` 的过渡兼容背景,不应再作为默认产品形态解释。
|
||||
>
|
||||
> 2026-05-19 口径补充:
|
||||
> - local-first 下,本地 `.md` 文件是正文真相;Page Aggregate / EditorBlockDocument / tiptap state 都是投影或工作副本。
|
||||
> - `documents/save` 只能作为 compat adapter,不能继续承担长期写侧仲裁。AI 后台写入、tiptap 保存、外部编辑器修改必须统一到本地文件版本冲突模型。
|
||||
>
|
||||
> 关联文档:
|
||||
> - `/mnt/Data1T/mnote/design/05-editor-mainline/done/5-4-leptos-tiptap-mainline-correction-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/old/05-editor-mainline/process/5-3-tiptap-leptos-rust-migration-checklist-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-4-tree-projection-protocol-contract-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/01-tree-first-graph-kernel/process/1-tree-first-graph-kernel-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/05-editor-mainline/done/5-5-1-page-aggregate-contract-v1.md`
|
||||
|
||||
## 1. 文档目的
|
||||
|
||||
这份文档只回答一个当前已经暴露出来的主线问题:
|
||||
|
||||
> **当 `leptos-tiptap` 已经成为页面内正式主编辑区后,页面树 / 文件树 / 页面标题 / 页面设置 / 页面正文,是否已经统一成 Rust 主导的单一真源架构。**
|
||||
|
||||
当前结论是:
|
||||
|
||||
> **还没有。**
|
||||
|
||||
当前系统已经不再是:
|
||||
|
||||
- `iframe runtime`
|
||||
- 纯前端假接入
|
||||
- 纯 `BlockNote` 主链
|
||||
|
||||
但也还不是:
|
||||
|
||||
- Rust 持有页面聚合真相
|
||||
- Leptos island 只消费 Rust projection
|
||||
- 树域与主编辑区共享同一份 page aggregate
|
||||
|
||||
它现在更接近于:
|
||||
|
||||
> **`Rust snapshot 主读链 + local-first / Convex-backed 双 source 过渡 + 前端本地 state 仍存在` 的混合态。**
|
||||
|
||||
因此,当前看到的这些“小问题”:
|
||||
|
||||
- 标题单一真源问题已经开始收口,但页面聚合仍未完全统一
|
||||
- 页面宽度和主编辑区宽度看起来不是一套规则
|
||||
- 页面设置大部分只改了壳层,没有真正进入主编辑器
|
||||
|
||||
并不是孤立 UI bug,而是同一个底层断层的表现:
|
||||
|
||||
> **树域 projection 与主编辑区 page runtime 之间,还缺一个统一的 page aggregate 真相层。**
|
||||
|
||||
---
|
||||
|
||||
## 2. 先给结论
|
||||
|
||||
### 2.1 当前不能说已经实现了 Rust 单一真源
|
||||
|
||||
虽然当前文档页已经:
|
||||
|
||||
- 默认使用页面内正式 `leptos-tiptap` island
|
||||
- 不再依赖外部 iframe bridge 作为主编辑器
|
||||
- 正文保存已经能按 `workspaceId/documentId` 绑定到正确页面
|
||||
|
||||
但以下事实仍然成立:
|
||||
|
||||
- 读取主链已固定为 Rust `mnote.page_aggregate.v1` snapshot,不再通过 TS builder 兜底
|
||||
- 标题、正文、页面设置虽已开始收口到同一组 page aggregate command family,但 projection 回流与运行时语义还没完全闭环
|
||||
- 页面树 / 文件树与页头标题的一致性已明显改善,但页头标题、页面设置与编辑器 runtime 还没有完全共享同一份聚合真相
|
||||
- `pageOptions` 已部分进入 `leptos-tiptap` 运行时语义层,但还没有整体收口完成
|
||||
|
||||
所以当前不能把这条线描述成:
|
||||
|
||||
> **“主编辑区、页面树、文件树已经统一为 Rust 下唯一真源”。**
|
||||
|
||||
更准确的表述应该是:
|
||||
|
||||
> **树域已经在向 Rust canonical contract 靠拢,但文档页主编辑区仍处于页面聚合尚未完成的混合切流阶段。**
|
||||
|
||||
### 2.2 当前最该收口的不是换编辑器,而是统一页面聚合边界
|
||||
|
||||
当前不该重新争论:
|
||||
|
||||
- 要不要 `Tiptap`
|
||||
- 要不要 `leptos-tiptap`
|
||||
- 要不要继续保留 Convex 作为控制面和可选同步协作 source
|
||||
|
||||
这些结论都已经足够明确:
|
||||
|
||||
- `Tiptap` 继续作为浏览器输入 runtime
|
||||
- `leptos-tiptap` 继续作为 Leptos 内正式 editor island 接缝
|
||||
- `local_folder` 作为默认页面数据真相,Convex / 服务端退居控制面和可选同步协作 source
|
||||
|
||||
当前真正要收口的是:
|
||||
|
||||
> **页面这一层,到底由谁持有聚合语义,前端到底应该消费什么,编辑器到底应该回发什么。**
|
||||
|
||||
local-first 后,答案进一步收紧为:
|
||||
|
||||
> **正文真相由本地 Markdown 文件持有;Page Aggregate 负责把文件投影成 UI 和 editor runtime 所需结构;写侧必须围绕文件版本做仲裁,而不是继续让 `documents/save` 兼容面决定谁的 revision 有效。**
|
||||
|
||||
### 2.3 后续主线应固定为 Page Aggregate,而不是继续零散补洞
|
||||
|
||||
从长期架构看,当前主线不应再描述成:
|
||||
|
||||
- “继续补几个 `leptos-tiptap` 细节”
|
||||
- “再把标题同步修一下”
|
||||
- “再把页面设置接一点进去”
|
||||
|
||||
后续主线应改写成:
|
||||
|
||||
> **建立 Rust 主导的 `page aggregate`:让页面树 / 文件树 / 页面头部 / 页面设置 / 主编辑区 / AI 写入,都围绕同一组 page projection 与 page command family 运转。**
|
||||
|
||||
---
|
||||
|
||||
## 3. 当前问题的精确判断
|
||||
|
||||
## 3.1 当前页面数据并不是一个统一聚合
|
||||
|
||||
当前文档页读取主链已经固定走 Rust `mnote.page_aggregate.v1` 快照:
|
||||
|
||||
- Rust `/api/page-aggregate/:id` snapshot
|
||||
- 页面本地 aggregate state reducer
|
||||
- preferred sidebar snapshot / 页头标题补偿链
|
||||
|
||||
需要特别区分“Rust route 主读链”和“完整 kernel-native projection”:当前 `/api/page-aggregate/:id` 已经由 Rust route / Rust runtime adapter 对外提供稳定 `mnote.page_aggregate.v1` 契约,前端不再恢复 TS runtime builder 主链;但底层 substrate 仍主要来自 `documents:getMeta + documents:getContent` 的兼容聚合。因此当前 projection provenance 应标记为 `CompatMetaContentJoin`,不能写成已经完全由 Rust kernel 原生投影闭环的 `KernelProjection`。
|
||||
|
||||
这意味着当前已经不再是:
|
||||
|
||||
- `page.tsx` 手工拉 `meta + content` 再现场拼 props
|
||||
|
||||
但也还不是:
|
||||
|
||||
- 页面所有读写、标题回流、页面设置运行时语义都只围绕一份 Rust page aggregate 自动闭环
|
||||
|
||||
这条链本身已经说明:
|
||||
|
||||
> **当前页面已经进入 Rust-first 聚合读取阶段,但 canonical page projection 仍未成为页面域唯一真相。**
|
||||
|
||||
### 3.1.1 标题链已开始收口,但尚未完成页面聚合统一
|
||||
|
||||
当前页头标题已经不再单纯由 `DocumentContent` 内部的长期本地 `pageTitle` 真相驱动,而是开始依赖与 sidebar / breadcrumb 共享的 preferred sidebar snapshot。
|
||||
|
||||
这带来两个直接后果:
|
||||
|
||||
1. 标题不同步这一类“前端本地状态长期漂移”问题已经明显减少
|
||||
2. 但标题仍然没有和正文、页面设置一起统一到单一 page aggregate command / projection 边界中
|
||||
|
||||
于是:
|
||||
|
||||
- 页头、breadcrumb、sidebar、page tree、file tree 的标题一致性已有一轮真实收口
|
||||
- 但从聚合语义上看,标题仍是一条相对独立的更新链,不等于 page aggregate 已经完成
|
||||
|
||||
这类问题不是“防抖没配好”,而是:
|
||||
|
||||
> **标题更新仍然是页面聚合之外的一条独立 side effect 链。**
|
||||
|
||||
### 3.1.2 正文和页面树仍然不是同一份 projection family
|
||||
|
||||
当前正文编辑器使用的是:
|
||||
|
||||
- `leptos-tiptap` runtime
|
||||
- `/api/documents/save`
|
||||
- `EditorBlockDocument / Tiptap / blocks` 的转换边界
|
||||
|
||||
而页面子树 / outline / evidence 使用的是:
|
||||
|
||||
- `pageSubtree`
|
||||
- 前端本地定义的 TS 结构
|
||||
- “内容与标题未变化时复用旧快照”的策略
|
||||
|
||||
这说明当前主编辑区与树域虽然已经能同时工作,但它们仍然不是:
|
||||
|
||||
- 同一个 Rust projection family 的不同视图
|
||||
|
||||
而是:
|
||||
|
||||
- 一边是编辑器保存链
|
||||
- 一边是页面子树附带快照
|
||||
|
||||
因此当前 `pageSubtree` 更像:
|
||||
|
||||
- 页面阅读态和 TOC 的辅助投影
|
||||
|
||||
而不是:
|
||||
|
||||
- 主编辑区与树域共享的 canonical page aggregate
|
||||
|
||||
### 3.1.3 页面设置已部分进入 island,但还不是完整 editor runtime 语义
|
||||
|
||||
当前页面设置里至少有三类配置:
|
||||
|
||||
#### A. 页面壳层布局类
|
||||
|
||||
- `wideLayout`
|
||||
- `smallText`
|
||||
- `layoutDensity`
|
||||
|
||||
#### B. 页面视图类
|
||||
|
||||
- `showToc`
|
||||
- `collapseBacklinks`
|
||||
- `hideChildPages`
|
||||
|
||||
#### C. 编辑器语义类
|
||||
|
||||
- `showHeadingNumbers`
|
||||
- `showBlockRefCount`
|
||||
- `embedDefaultBlockId`
|
||||
|
||||
现在的主要问题不是“这些字段没有存下来”,而是:
|
||||
|
||||
> **它们虽然能持久化到 documents 表,也已有一部分进入 `leptos-tiptap` island,但还没有统一进入正式 page aggregate,更没有完整地进入 editor runtime 语义层。**
|
||||
|
||||
结果就是:
|
||||
|
||||
- 一部分设置已经作用于 island 布局或排版
|
||||
- 一部分设置只影响阅读态
|
||||
- 一部分设置被展示出来,但主编辑器内部几乎不消费
|
||||
|
||||
这也是为什么当前会出现:
|
||||
|
||||
> **页面设置看起来像是真的,但很多只是 UI 层局部生效。**
|
||||
|
||||
### 3.1.4 所谓“Rust 路径”里仍有明显 compat 痕迹
|
||||
|
||||
当前 rename / move / archive 等页面操作,虽然已经开始声明:
|
||||
|
||||
- `preferredCommandName`
|
||||
|
||||
但真实落地仍大量停留在:
|
||||
|
||||
- `documents.title.update`
|
||||
- `documents.move`
|
||||
- `documents.delete`
|
||||
|
||||
这说明当前并不能说:
|
||||
|
||||
- 树域命令已经完整切到 Rust tree command family
|
||||
|
||||
更不能说:
|
||||
|
||||
- 页面编辑域已经和树域命令收敛到同一个 page aggregate contract
|
||||
|
||||
因此现状应判定为:
|
||||
|
||||
> **Rust bridge 已经进入主链,但 page aggregate command cutover 仍未完成。**
|
||||
|
||||
### 3.1.5 写侧冲突不能继续藏在 `documents/save` 后面
|
||||
|
||||
当前读侧已优先消费 Rust `mnote.page_aggregate.v1` snapshot,但写侧仍有明显兼容痕迹:
|
||||
|
||||
- tiptap 保存仍通过 `/api/documents/save` 兼容入口进入 `page.body.save`
|
||||
- AI 兼容工具写正文时也可能最终进入同一条保存链
|
||||
- local-first 下 agent 还会直接修改 `.md` 文件
|
||||
|
||||
如果继续让这些写入都挤在 `documents/save` 兼容面后面,会再次出现:
|
||||
|
||||
- 谁拥有最新 revision
|
||||
- AI 写入是否覆盖了用户未保存编辑
|
||||
- tiptap autosave 是否覆盖了 agent 刚写回的文件
|
||||
- Page Aggregate 读到的是旧 content、EditorBlockDocument 还是最新 `.md`
|
||||
|
||||
因此写侧必须改成 VSCode-like 文件版本模型:
|
||||
|
||||
```text
|
||||
current .md file
|
||||
-> fileVersion = hash + mtime + size
|
||||
-> Page Aggregate snapshot 带 baseFileVersion
|
||||
-> tiptap 工作副本记录 baseFileVersion 与 dirty 状态
|
||||
-> AI / 外部编辑器写入触发 watcher
|
||||
-> clean editor 自动 reload;dirty editor 进入 conflict state
|
||||
```
|
||||
|
||||
这意味着 `EditorBlockDocument` 原生落库闭环不能理解成“再建一份新的正文真相”。local-first 下更合理的定位是:
|
||||
|
||||
- `.md` 是 canonical body
|
||||
- `EditorBlockDocument` 是 runtime-native projection / cache
|
||||
- `.mnote/cache/<page-id>.editor.json` 或内存缓存可用于加速和保留编辑器特有信息,但必须带 `sourceFileVersion`
|
||||
- 当 source file version 不匹配时,cache 失效并重新从 `.md` 投影
|
||||
|
||||
长期命令面应从 `/api/documents/save` compat route 收口到 `page.body.write` / LocalFS executor,并显式接收 `expectedFileVersion`。任何不带 expected version 的正文写入都只能进入 compat / import 路径,不能作为自动保存主链。
|
||||
|
||||
---
|
||||
|
||||
## 4. 为什么这些问题会直接影响长期方向
|
||||
|
||||
## 4.1 它会削弱 Rust + Leptos 的真正优势
|
||||
|
||||
你们要的不是:
|
||||
|
||||
- “一个能跑的 Tiptap 页面”
|
||||
|
||||
而是:
|
||||
|
||||
- Rust 掌握页面与树的真相
|
||||
- Leptos 成为页面内正式 editor island
|
||||
- AI 最终直接接入主编辑区写作
|
||||
|
||||
如果标题、正文、设置、树节点标题分别走不同链路,那么 Rust + Leptos 的长期优势就会被削弱成:
|
||||
|
||||
- 只是“比纯前端多了一层 bridge”
|
||||
|
||||
而不是:
|
||||
|
||||
- “由 Rust 持有统一对象语义,前端只消费真相投影”
|
||||
|
||||
### 4.1.1 AI 直接写主编辑区会缺乏稳定入口
|
||||
|
||||
如果后续 AI 要直接进入主编辑区编写,而当前系统没有统一的 page aggregate command family,那么 AI 最终只能选下面几条坏路:
|
||||
|
||||
- 直接操作前端 DOM
|
||||
- 直接操作 Tiptap JSON
|
||||
- 直接调用不同的标题 / 正文 / 设置接口拼写入
|
||||
|
||||
这三条路都不符合长期目标。
|
||||
|
||||
长期正确路线必须是:
|
||||
|
||||
> **AI 先操作 Rust page aggregate 的命令与块语义,再由 editor island 把结果投影到 live editing surface。**
|
||||
|
||||
### 4.1.2 树域与页面域会继续出现“双真相漂移”
|
||||
|
||||
只要页面树 / 文件树消费的还是一套资源 meta,而主编辑区头部和页面设置是另一套前端状态,那么后续还会不断出现:
|
||||
|
||||
- 标题单一真源已开始收口,但当前尚未和正文、页面设置统一成同一 page aggregate 命令边界
|
||||
- 页面设置变更只影响一部分视图
|
||||
- 阅读态与编辑态宽度规则不一致
|
||||
- 新增一个页面能力时,要改三四条链路
|
||||
|
||||
这类问题越到后面越难收。
|
||||
|
||||
---
|
||||
|
||||
## 5. 正确的目标重写
|
||||
|
||||
当前阶段的目标不应再写成:
|
||||
|
||||
- “把 `leptos-tiptap` 再打磨得更像官方模板”
|
||||
|
||||
而应重写为:
|
||||
|
||||
> **建立 Rust 主导的 `page aggregate`,让页面树 / 文件树 / 页面头部 / 页面设置 / 主编辑区 / AI 写入共享同一份页面真相。**
|
||||
|
||||
这句话拆开后,意味着下面五点。
|
||||
|
||||
### 5.1 Page Aggregate Projection
|
||||
|
||||
Rust 侧需要提供一个统一的页面聚合投影,至少包含:
|
||||
|
||||
- `page_identity`
|
||||
- `workspace_id`
|
||||
- `document_id`
|
||||
- `node_id`
|
||||
- `page_head`
|
||||
- `title`
|
||||
- `icon`
|
||||
- `cover`
|
||||
- `updated_at`
|
||||
- `permissions`
|
||||
- `page_layout`
|
||||
- `wide_layout`
|
||||
- `small_text`
|
||||
- `layout_density`
|
||||
- `show_toc`
|
||||
- `show_structure`
|
||||
- `page_body`
|
||||
- `editor_document`
|
||||
- `revision`
|
||||
- `conflict_detection_key`
|
||||
- `stats`
|
||||
- `page_tree`
|
||||
- `outline`
|
||||
- `child_pages`
|
||||
- `resource_meta`
|
||||
|
||||
当前不要求一次把所有字段做满,但必须先把口径冻结。
|
||||
|
||||
### 5.2 Page Command Family
|
||||
|
||||
后续页面域的操作不应继续被拆成互不统属的零散 mutation,而应在语义上统一到 page aggregate 之下。
|
||||
|
||||
最小命令族至少包括:
|
||||
|
||||
- `page.head.rename`
|
||||
- `page.layout.update`
|
||||
- `page.body.save`
|
||||
- `page.body.insert_reference`
|
||||
- `page.body.set_embed_default_block`
|
||||
- `page.tree.refresh_projection`
|
||||
|
||||
这不意味着现在立刻把所有接口名都改掉,而是:
|
||||
|
||||
> **从现在开始,所有页面能力都应先判断它属于哪个 page aggregate 子域,而不是继续新增离散 mutation。**
|
||||
|
||||
### 5.3 Leptos Island 的正式边界
|
||||
|
||||
`leptos-tiptap` island 的职责应收敛为:
|
||||
|
||||
- 消费 `page_body`
|
||||
- 消费必要的 `page_layout` 编辑器相关配置
|
||||
- 回发正文与块级命令
|
||||
- 暴露可供 AI / 页面壳使用的稳定 editor handle
|
||||
|
||||
它不应再负责:
|
||||
|
||||
- 定义页面总真相
|
||||
- 自己发明页面设置语义
|
||||
- 在页面壳之外另持有一套页面元信息状态
|
||||
|
||||
### 5.4 Tree Domain 的接缝
|
||||
|
||||
页面树 / 文件树不需要知道编辑器内部细节,但必须与页面聚合共享:
|
||||
|
||||
- 同一份 `document_id`
|
||||
- 同一份页面标题
|
||||
- 同一份 `resource_meta`
|
||||
- 同一份页面可见状态与能力语义
|
||||
|
||||
也就是说:
|
||||
|
||||
- 树域保持 tree-first
|
||||
- 页面域保持 page aggregate
|
||||
- 两者通过稳定 projection contract 对齐
|
||||
|
||||
而不是:
|
||||
|
||||
- 树域一套标题来源
|
||||
- 页面域另一套标题来源
|
||||
|
||||
### 5.5 Convex 的正确定位
|
||||
|
||||
当前不应该把 Convex 当作默认页面主数据层继续扩写,也不应该无计划硬拆已有 Convex 路径。
|
||||
|
||||
正确口径是:
|
||||
|
||||
- local folder 是早期产品默认页面数据真相
|
||||
- Convex / 服务端继续作为账号、分享、同步、协作和远端副本控制面
|
||||
- Rust 持有 canonical contract 与语义编排
|
||||
- Leptos / Next 负责消费 projection 与呈现 island
|
||||
|
||||
因此,“Rust 单一真源”在当前阶段的正确含义不是:
|
||||
|
||||
- 只有 Rust 数据库
|
||||
|
||||
而是:
|
||||
|
||||
- **Rust 持有语义单一真源,local-first workspace 持有默认数据真相,Convex 只作为控制面和可选同步协作 source。**
|
||||
|
||||
---
|
||||
|
||||
## 6. 当前哪些地方需要纠偏
|
||||
|
||||
## 6.1 需要纠偏的不是“页面里还有前端状态”
|
||||
|
||||
页面壳中存在一些本地临时状态本身没有问题。
|
||||
|
||||
问题在于:
|
||||
|
||||
- 哪些状态是临时 UI 状态
|
||||
- 哪些状态却在冒充对象真相
|
||||
|
||||
后续必须把两者分清。
|
||||
|
||||
### 6.1.1 可以继续留在前端壳的状态
|
||||
|
||||
- inspector 开关
|
||||
- comments drawer 开关
|
||||
- history drawer 开关
|
||||
- 当前是否在编辑态
|
||||
- 当前 host observability
|
||||
|
||||
这些都是 UI state,不必进入 Rust 真相层。
|
||||
|
||||
### 6.1.2 必须退出前端壳真相地位的状态
|
||||
|
||||
- 页面标题
|
||||
- 页面宽度 / 页面布局正式选项
|
||||
- 编辑器默认嵌入位置
|
||||
- 页面 outline 的正式来源
|
||||
- 页面 stats 的正式来源
|
||||
|
||||
这些都不应继续被前端 `useState` 长期定义为页面事实。
|
||||
|
||||
## 6.2 需要纠偏的不是继续换保存底座
|
||||
|
||||
当前正文保存链已经基本可用,真正的问题不是“保存不到页面”,而是:
|
||||
|
||||
- 保存成功不等于 page aggregate 已建立
|
||||
|
||||
因此:
|
||||
|
||||
- 现在不该推翻 `documents.save`
|
||||
- 也不该另造一套临时保存格式
|
||||
|
||||
而应做的是:
|
||||
|
||||
> **把标题 / 正文 / 页面设置的语义边界对齐到同一 page aggregate 之下。**
|
||||
|
||||
## 6.3 需要纠偏的是页面设置的产品口径
|
||||
|
||||
当前页面设置里已经混入了三类字段:
|
||||
|
||||
- 真正属于页面布局的
|
||||
- 真正属于阅读壳的
|
||||
- 真正属于编辑器 runtime 的
|
||||
|
||||
后续必须先分类,再决定哪些继续保留在页面设置面板里,哪些推迟支持,哪些进入 editor island。
|
||||
|
||||
否则只会继续出现:
|
||||
|
||||
- UI 有按钮
|
||||
- 数据能存
|
||||
- 但主编辑器不真正消费
|
||||
|
||||
---
|
||||
|
||||
## 7. 分阶段落地
|
||||
|
||||
下面的阶段不是完整开发清单,而是主线纠偏顺序。
|
||||
|
||||
## 7.1 Phase F: 冻结 Page Aggregate Contract
|
||||
|
||||
目标:
|
||||
|
||||
- 在设计层明确页面聚合的 canonical contract
|
||||
- 不再让页面页头 / 正文 / 设置 / 子树各自长字段
|
||||
|
||||
完成标准:
|
||||
|
||||
- 有单独的 page aggregate contract 文档
|
||||
- 明确 `page_head / page_layout / page_body / page_tree` 的最小字段
|
||||
- 明确哪些字段是 UI state,哪些字段是 aggregate truth
|
||||
|
||||
## 7.2 Phase G: 主文档页改为消费统一聚合
|
||||
|
||||
目标:
|
||||
|
||||
- 文档页不再手工拼 `title + options + content + pageSubtree`
|
||||
- 改为消费统一页面聚合返回值
|
||||
|
||||
完成标准:
|
||||
|
||||
- 页面头部使用聚合中的 `page_head`
|
||||
- 页面设置初始值使用聚合中的 `page_layout`
|
||||
- 主编辑区 bootstrap 使用聚合中的 `page_body`
|
||||
- 树与 TOC 使用聚合中的 `page_tree`
|
||||
|
||||
## 7.3 Phase H: `leptos-tiptap` 正式消费 editor-related page options
|
||||
|
||||
目标:
|
||||
|
||||
- 把真正属于编辑器 runtime 的设置接入 `leptos-tiptap`
|
||||
|
||||
最小优先级建议:
|
||||
|
||||
1. `wideLayout`
|
||||
2. `smallText`
|
||||
3. `layoutDensity`
|
||||
4. `showHeadingNumbers`
|
||||
5. `embedDefaultBlockId`
|
||||
|
||||
完成标准:
|
||||
|
||||
- inspector 改动后,主编辑区表现真实变化
|
||||
- 不再出现“设置能存,但编辑器内部几乎没变化”
|
||||
|
||||
## 7.4 Phase I: 树域与页面域标题统一
|
||||
|
||||
目标:
|
||||
|
||||
- rename 后页头、sidebar row、page tree、file tree 一致刷新
|
||||
|
||||
完成标准:
|
||||
|
||||
- 标题变更只经过一套 page aggregate command 语义
|
||||
- 所有标题消费者都来自同一份更新后的 projection
|
||||
|
||||
## 7.5 Phase J: AI 写入入口对齐 page aggregate
|
||||
|
||||
> 2026-05-16 口径补充:`mnote.doc.fetch/find/plan_update` 与 `mnote.block.fetch/replace/insert_after/move_after` 已进入 Rust Hermes tool manifest 与 dispatch,最小块级读写闭环已启动。本文继续保留本节,是因为 `scope=selection`、`format=page_xml/text`、多块插入、复杂块移动矩阵和持久审阅 UI 仍由 `7-10` 继续验收。
|
||||
|
||||
目标:
|
||||
|
||||
- AI 不再绕过 page aggregate 直接拼前端对象
|
||||
|
||||
完成标准:
|
||||
|
||||
- AI 能通过统一页面命令把内容写入主编辑区
|
||||
- 人工编辑与 AI 编辑共享同一块语义边界
|
||||
|
||||
---
|
||||
|
||||
## 8. 当前阶段的完成判定
|
||||
|
||||
只有当下面这些条件同时成立时,才能把这条线描述成:
|
||||
|
||||
> **“主编辑区与树域已经基本统一到 Rust 主导的单一真源架构”。**
|
||||
|
||||
- 文档页消费统一 `page aggregate projection`
|
||||
- 标题 / 正文 / 页面设置不再是三条分裂真相链
|
||||
- `leptos-tiptap` island 真正消费 editor-related `page_layout`
|
||||
- 树标题与页头标题来自同一份 projection
|
||||
- AI 写入入口开始围绕 page aggregate command family 设计
|
||||
|
||||
在此之前,正确口径都应保持为:
|
||||
|
||||
> **`leptos-tiptap` 主编辑区已基本可用,但页面聚合仍未收口,当前仍处于从混合态向 Rust 单一真源过渡的过程中。**
|
||||
|
||||
---
|
||||
|
||||
## 9. 本文结论
|
||||
|
||||
这次暴露出的页面宽度不统一、页面设置只部分生效,以及标题链虽已收口但聚合仍未统一,并不是坏消息。
|
||||
|
||||
它们的价值在于:
|
||||
|
||||
> **它们准确暴露了当前真正缺的不是“再修几个细节”,而是“把页面域提升成一个与树域对齐的 Rust page aggregate”。**
|
||||
|
||||
因此,后续主线不应继续散落成:
|
||||
|
||||
- 修标题
|
||||
- 修宽度
|
||||
- 修页面设置
|
||||
|
||||
而应统一收口为:
|
||||
|
||||
> **Page Aggregate Contract**
|
||||
>
|
||||
> **Page Aggregate Projection**
|
||||
>
|
||||
> **Page Aggregate Command Family**
|
||||
|
||||
这才是让 `tree-first graph kernel`、`leptos-tiptap` 主编辑区、以及后续 AI 直接写入主编辑区三者真正对齐的正确底层方向。
|
||||
@@ -12,7 +12,7 @@
|
||||
> - AI 直改文件、tiptap autosave、外部编辑器修改必须共用 VSCode-like 文件版本冲突模型。
|
||||
>
|
||||
> 关联文档:
|
||||
> - `/mnt/Data1T/mnote/design/05-editor-mainline/process/5-5-page-aggregate-single-truth-alignment-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/05-editor-mainline/reference/5-5-page-aggregate-single-truth-alignment-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/05-editor-mainline/done/5-4-leptos-tiptap-mainline-correction-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/05-editor-mainline/done/5-5-1-page-aggregate-contract-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/04-tree-domain/done/4-4-tree-projection-protocol-contract-v1.md`
|
||||
|
||||
-937
@@ -1,937 +0,0 @@
|
||||
# 5-7 [process] Wolai 页面树与主编辑器体验复刻方案 v1
|
||||
|
||||
> 更新时间:2026-04-30
|
||||
>
|
||||
> 当前状态:`process-reference`。本文只保留 Wolai 体验复刻目标和取证基线;持续执行与安全边界以 `08-wolai-aline-test-flow` 和 `wolai-aline` skill 为准。
|
||||
|
||||
> **Wolai-aline 执行口径更新(2026-04-30):** 本文只保留体验复刻任务拆解和产品目标。所有 Wolai 对标测试、浏览器取证、编辑权限、安全边界、subagent 使用、截图复核和 smoke 补齐,统一以 `/home/lix/.codex/skills/wolai-aline` 与 `/mnt/Data1T/mnote/design/08-wolai-aline-test-flow/process/wolai-aline-test-flow-v1.md` 为准。若本文旧段落与该 skill 冲突,以 skill 为准。
|
||||
>
|
||||
> 关联文档:
|
||||
> - `/mnt/Data1T/mnote/ARCHITECTURE.md`
|
||||
> - `/mnt/Data1T/mnote/design/01-05-current-priority-overview.md`
|
||||
> - `/mnt/Data1T/mnote/design/01-tree-first-graph-kernel/process/1-tree-first-graph-kernel-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/02-convex-rust-long-term-architecture/done/2-2-local-first-workspace-convex-control-plane-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/old/02-convex-rust-long-term-architecture/process/2-tree-first-graph-convex-rust-long-term-architecture-v1.md`(历史过渡背景)
|
||||
> - `/mnt/Data1T/mnote/design/03-rust-web/process/3-rust-web-long-term-architecture-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-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/05-editor-mainline/process/5-2-tiptap-notion-like-template-adoption-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/05-editor-mainline/done/5-4-leptos-tiptap-mainline-correction-v1.md`
|
||||
> - `/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`
|
||||
> - `/mnt/Data1T/mnote/design/05-editor-mainline/process/5-9-wolai-aline-continuous-checklist-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/05-editor-mainline/done/5-5-1-page-aggregate-contract-v1.md`
|
||||
|
||||
## 1. 文档目的
|
||||
|
||||
这份方案只处理一件事:
|
||||
|
||||
> **以真实 Wolai 页面为视觉和交互 benchmark,继续还原 mnote 的页面树与主编辑器体验。**
|
||||
|
||||
本轮目标不是重新定义系统架构,也不是把 Wolai 的全部协作能力完整复制过来,而是把当前 `3000` 页面在视觉密度、布局节奏、页面树反馈、主编辑器输入体验上继续向 Wolai 靠近。
|
||||
|
||||
约束如下:
|
||||
|
||||
- UI 以像素级复刻为目标。
|
||||
- 功能以“高频路径可用、复杂能力可降级”为原则。
|
||||
- 当前项目保留“文件树 / Explorer”创新,不要求删除。
|
||||
- 默认主编辑器继续是页面内 `leptos-tiptap` island。
|
||||
- `BlockNote` 只作为 `recycle/` 历史参考 / 对照材料,不重新变成默认方向。
|
||||
- 树、页面结构、标题、正文、页面设置不能在 UI 层再拼第二份真相。
|
||||
|
||||
这份文档应放在 `05-editor-mainline/process`,因为它的核心不是 Rust Web 壳复刻,也不是单独的树命令合同,而是:
|
||||
|
||||
> **围绕 Page Aggregate,把页面树、页头、页面设置、正文编辑器统一成接近 Wolai 的产品体验。**
|
||||
|
||||
---
|
||||
|
||||
## 2. 本轮取证基线
|
||||
|
||||
### 2.1 真实 Wolai 参考
|
||||
|
||||
本轮参考页面:
|
||||
|
||||
- `https://www.wolai.com/wolai/xhqeop8UHpVTMUSVmgz8nq`
|
||||
- `https://www.wolai.com/wolai/qN1Bh9YjLAXs8bxCJoAJ6C`
|
||||
- `https://www.wolai.com/liaibo/ikFSSM1a4GgvmHBYFCNfVd`
|
||||
|
||||
本轮已保存的截图与快照:
|
||||
|
||||
- `/mnt/Data1T/mnote/tmp/wolai-page1.png`
|
||||
- `/mnt/Data1T/mnote/tmp/wolai-page1-snapshot.md`
|
||||
- `/mnt/Data1T/mnote/tmp/wolai-page2.png`
|
||||
- `/mnt/Data1T/mnote/tmp/wolai-page2-snapshot.md`
|
||||
- `/mnt/Data1T/mnote/tmp/wolai-compare/wolai-target-1392x1213.png`
|
||||
- `/mnt/Data1T/mnote/tmp/wolai-compare/wolai-target-snapshot.md`
|
||||
- `/mnt/Data1T/mnote/tmp/wolai-compare/wolai-public-search-overlay.png`
|
||||
- `/mnt/Data1T/mnote/tmp/wolai-compare/wolai-public-search-results-skill.png`
|
||||
- `/mnt/Data1T/mnote/tmp/wolai-compare/wolai-public-presentation-mode.png`
|
||||
- `/mnt/Data1T/mnote/tmp/wolai-compare/wolai-public-start-edit-overlay.png`
|
||||
- `/mnt/Data1T/mnote/tmp/wolai-compare/wolai-public-comment-panel.png`
|
||||
- `/mnt/Data1T/mnote/tmp/wolai-compare/wolai-public-good-night-mode.png`
|
||||
- `/mnt/Data1T/mnote/tmp/wolai-compare/wolai-help-first-viewport-current.png`
|
||||
- `/mnt/Data1T/mnote/tmp/wolai-compare/wolai-help-sidebar-filter-page-reference.png`
|
||||
- `/mnt/Data1T/mnote/tmp/wolai-compare/wolai-help-search-overlay.png`
|
||||
- `/mnt/Data1T/mnote/tmp/wolai-compare/wolai-help-search-results-page-reference.png`
|
||||
- `/mnt/Data1T/mnote/tmp/wolai-compare/wolai-help-search-result-navigated-block-reference.png`
|
||||
- `/mnt/Data1T/mnote/tmp/wolai-compare/wolai-help-embedded-page-reference-hover.png`
|
||||
- `/mnt/Data1T/mnote/tmp/wolai-compare/wolai-help-block-reference-page-snapshot.md`
|
||||
- `/mnt/Data1T/mnote/wolai-basic-editing-reference.png`
|
||||
- `/mnt/Data1T/mnote/wolai-basic-editing-reference-snapshot.md`
|
||||
- `/mnt/Data1T/mnote/wolai-hermes-public-hover-block.png`
|
||||
- `/mnt/Data1T/mnote/wolai-hermes-start-edit-login-dialog.png`
|
||||
- `/mnt/Data1T/mnote/wolai-hermes-comment-panel.png`
|
||||
- `/mnt/Data1T/mnote/wolai-hermes-presentation-mode.png`
|
||||
- `/mnt/Data1T/mnote/wolai-hermes-good-night-mode.png`
|
||||
|
||||
用户在内置浏览器中提供的登录态截图也作为本轮重要视觉证据:该截图展示了真实个人空间的长页面树、当前页 `Hermes` 选中态、完整顶部图标区、底部垃圾桶 / 模板中心入口、右下角 AI / 帮助浮动入口。
|
||||
|
||||
补充说明:历史截图和用户提供的登录态截图只作为现有证据池,不再作为后续任务的唯一验收依据。该 owner 态菜单不是单纯样式:它包含“在右侧边栏打开 / 移动到 / 嵌入到 / 复制访问链接 / 复制页面引用链接 / 复制页面 ID / 拷贝副本 / 重命名 / 删除”等页面树动作,应进入后续交互合同,而不是只当成截图复刻。
|
||||
|
||||
2026-04-30 口径更新:后续取证统一从 `wolai-aline` skill 启动。已知技术路径是 `Playwright Node + /opt/google/chrome/chrome + /mnt/Data1T/mnote/tmp/wolai-playwright-profile`,但本文不再维护独立自动化方案。当前 Hermes 测试页已授权用于最小编辑验证;owner 首屏、搜索浮层、顶部更多菜单、正文块 hover、`.hover-block-menu` 块菜单等行为,都必须通过 subagent 浏览器取证、主线程截图复核和差异矩阵重新确认。`dokobot doko read --local --reuse-tab` 可作为登录态 read evidence,但不能替代键鼠操作验收。
|
||||
|
||||
### 2.2 当前 mnote 参考
|
||||
|
||||
当前本地 `3000` 截图:
|
||||
|
||||
- `/mnt/Data1T/mnote/tmp/wolai-compare/local-3000-playwright-1392x1213.png`
|
||||
- `/mnt/Data1T/mnote/tmp/wolai-compare/local-3000-first-viewport.png`
|
||||
- `/mnt/Data1T/mnote/tmp/wolai-compare/local-3000-dom-snapshot.txt`
|
||||
|
||||
当前本地页面已经具备接近 Wolai 的基本壳:
|
||||
|
||||
- 左侧 workspace / 快捷操作 / 页面树 / 底部入口。
|
||||
- 右侧顶部 breadcrumb / 页面操作。
|
||||
- 中央文档页标题和正文区。
|
||||
- 右下角帮助与 AI 入口。
|
||||
|
||||
但与真实 Wolai 仍有显著差距:
|
||||
|
||||
- 左侧树的视觉密度、选中态、图标系统、滚动条和层级缩进仍不够像 Wolai。
|
||||
- 文档主内容的起始位置、标题尺寸、正文列宽、页头留白和默认元信息展示不一致。
|
||||
- 顶栏按钮体系与 Wolai 的 owner / published 两种状态还没有明确分型。
|
||||
- 主编辑器的块级 hover 手柄、slash、浮动工具条和块菜单需要按 Wolai / Notion 风格继续收口。
|
||||
- 当前页面树、页头、正文、页面设置仍必须继续服从 Page Aggregate 单一真源主线,不能为了 UI 快速复刻重新制造局部状态。
|
||||
|
||||
### 2.3 2026-04-30 subagent 基线复核
|
||||
|
||||
本轮已按 `wolai-aline` skill 派 subagent 同时操作 Wolai Hermes 与本地 `3000`。工具链结论:`Node.js Playwright + /opt/google/chrome/chrome` 可操作两个目标;Wolai 通过 `/mnt/Data1T/mnote/tmp/wolai-playwright-profile` 进入 owner 登录态,本地 `3000` 返回可用页面。无源码改动,证据写入:
|
||||
|
||||
- `/mnt/Data1T/mnote/tmp/wolai-editor-parity/baseline-20260430/`
|
||||
- `/mnt/Data1T/mnote/tmp/wolai-editor-parity/doc-baseline-mini/`
|
||||
|
||||
主线程已复核关键截图:
|
||||
|
||||
- Wolai 首屏:`/mnt/Data1T/mnote/tmp/wolai-editor-parity/doc-baseline-mini/wolai-initial.png`
|
||||
- 本地首屏:`/mnt/Data1T/mnote/tmp/wolai-editor-parity/doc-baseline-mini/local-initial.png`
|
||||
- Wolai 搜索:`/mnt/Data1T/mnote/tmp/wolai-editor-parity/baseline-20260430/wolai-03-search-modal-controls-filled.png`
|
||||
- 本地搜索:`/mnt/Data1T/mnote/tmp/wolai-editor-parity/baseline-20260430/local-03-search-modal-controls-filled.png`
|
||||
- Wolai 块 hover:`/mnt/Data1T/mnote/tmp/wolai-editor-parity/baseline-20260430/wolai-08-block-hover-insert-entry.png`
|
||||
- 本地块 hover:`/mnt/Data1T/mnote/tmp/wolai-editor-parity/baseline-20260430/local-14-block-hover-insert-entry.png`
|
||||
- Wolai 编辑清理后:`/mnt/Data1T/mnote/tmp/wolai-editor-parity/baseline-20260430/wolai-14-title-suffix-after.png`
|
||||
|
||||
当前差异矩阵:
|
||||
|
||||
| 项 | Wolai Hermes | 本地 `3000` | 后续处理 |
|
||||
| --- | --- | --- | --- |
|
||||
| 首屏入口 | owner 登录态直接显示 `Hermes` 正文页,标题、正文、页面树同屏 | `task130` 后已改为根页直接进入 document shell + editor island;历史上曾停留在聚合 / 预览壳 | 后续仅继续收口侧栏数据、视觉密度与 owner/published 状态差异 |
|
||||
| 侧栏数据与选中态 | 真实空间树,当前页在 `个人 / 软件开发 / Hermes` 路径下,红色选中态明显 | anonymous 示例树,当前页为 `1111`,行密度和图标体系仍偏本地化 | Sidebar checklist 必须同时验视觉和真实 active path |
|
||||
| 搜索入口 | 左侧放大镜可打开全局搜索;本轮 `Ctrl+K` 未稳定打开,`Ctrl+P` 在另一轮可 toggle | 顶栏搜索可打开;侧栏搜索曾修为 modal;`Ctrl+P` 可 toggle | 搜索任务不得只测一个入口;需记录每个入口、快捷键和 URL |
|
||||
| 搜索控件 | modal 居中,遮罩、结果行、快捷键提示完整;switch 视觉可见 | modal 结构接近,`role="switch"` 可测;不同输入下结果为空或本地数据结果 | switch 默认状态必须同时记录截图和 `aria-checked`,结果态需拆数据差异与控件差异 |
|
||||
| 搜索结果 | 输入 `Hermes` 命中页面和块,结果有红色高亮和路径 | 本地同词可出现空态或示例数据结果,取决于当前页面数据 | 建立固定本地 fixture 或 smoke 种子,不能用随机工作区数据验视觉 |
|
||||
| 正文编辑区 | 首屏正文 `contenteditable=true`,标题也可编辑 | 当前已在首屏直接出现主编辑区;剩余差异转为标题区、正文密度与块交互细节 | 文档画布任务继续聚焦主编辑器体验,而不是首屏入口纠偏 |
|
||||
| 编辑焦点风险 | Wolai 标题和正文均可编辑,测试输入曾误落到标题,已清理 | 本地输入 / 撤销可完成,但页面结构不同 | editable-test mode 前必须先断言焦点 block 类型,优先在正文末尾新建唯一测试块 |
|
||||
| 块 hover | 正文块左侧出现轻量块控制入口,视觉很克制 | 本地显示 `+` 与拖拽/块句柄按钮,但位置、密度、内容列差异明显 | E2-E4 必须以截图和 hover 点位复核,不可只看按钮存在 |
|
||||
|
||||
这轮基线说明:当前最大偏差不是单个按钮,而是本地普通文档首屏仍像“聚合入口 / 预览页”,Wolai 则直接进入可读写的页面正文。后续 P0/P1 checklist 必须优先处理这个入口语义,否则搜索、块 hover、编辑器测试都会测到不同页面状态。
|
||||
|
||||
### 2.4 Wolai-aline 取证与验收统一口径
|
||||
|
||||
本节旧版“published / owner readonly / sandbox mutation”分层已经收口到 `wolai-aline` skill。后续执行不再从本文推导测试策略,统一遵循:
|
||||
|
||||
- Codex skill:`/home/lix/.codex/skills/wolai-aline`
|
||||
- 流程文档:`/mnt/Data1T/mnote/design/08-wolai-aline-test-flow/process/wolai-aline-test-flow-v1.md`
|
||||
- 失败模式:`/home/lix/.codex/skills/wolai-aline/references/failure-patterns.md`
|
||||
|
||||
现行要点:
|
||||
|
||||
- 每个 Wolai-aline 小任务都必须先取 Wolai 基线,再做本地 RED smoke,再实现,再由 subagent 浏览器复测,最后主线程复核截图和差异矩阵。
|
||||
- 浏览器取证必须使用 subagent;subagent 默认只读,但当任务明确需要编辑器行为时,可在当前 Hermes 测试页进入 `Hermes editable-test mode` 做最小编辑验证。
|
||||
- 当前 Hermes 测试页 `https://www.wolai.com/liaibo/ikFSSM1a4GgvmHBYFCNfVd` 已授权用于最小编辑验证;可以创建带唯一标记的临时测试块、输入少量测试文本、验证 slash / toolbar / block menu / 快捷键等编辑器行为。
|
||||
- 编辑验证必须记录编辑前后截图、动作链、输入内容和清理状态。若安全清理有风险,不扩大操作,报告残留测试内容。
|
||||
- 即使在 Hermes 测试页,也禁止未经确认地删除、移动、归档、发布、评论、改权限或批量修改既有非测试内容。
|
||||
- 其他 Wolai 页面仍默认只读;写入仍需用户提供沙盒页 URL 和明确授权。
|
||||
- 对标不能只看文案或 DOM 是否存在;必须主动检查截图中的控件形态、状态、hover/active、快捷键 toggle、关闭路径和 URL 跳转。
|
||||
|
||||
硬门槛:
|
||||
|
||||
- 不能把 subagent 文本结论当作最终对齐证据;主线程必须复核截图。
|
||||
- 如果截图里有肉眼可见差异,先补 smoke 捕获差异,再改实现。
|
||||
- 任何新发现的稳定失败模式,都要沉淀到 `wolai-aline` skill 的 `failure-patterns.md`。
|
||||
|
||||
### 2.5 `基本编辑能力` 操作矩阵
|
||||
|
||||
`https://www.wolai.com/wolai/qN1Bh9YjLAXs8bxCJoAJ6C` 是 Wolai 对“主编辑器基础操作”的官方内容页。本页不只是说明文字,它给出了后续 `3000` 必须能复现的主编辑器操作矩阵:
|
||||
|
||||
| 操作 | Wolai 参考页说明 | 当前对 `Hermes` 可自动化验证性 | `3000` 后续验收要求 |
|
||||
| --- | --- | --- | --- |
|
||||
| 普通文本输入 / 回车 / 光标移动 | 像普通文本编辑器一样输入、换行、移动光标 | 可用 Hermes editable-test mode 取基线 | 在本地编辑态 smoke 中验证输入、换行、保存后刷新保持 |
|
||||
| 块模型 | 文本、列表项、图片、文件等都是“块” | owner 态只读已可观察块 hover 手柄和 block menu | 本地每个块需有稳定 block id 与 hover target |
|
||||
| 拖动块 | 块左侧 `::` 图标可拖动,并显示辅助线 | owner 态只读可观察手柄;真实拖动是写入动作,按 Hermes editable-test mode 最小验证 | 本地必须验证拖拽预览、横向 drop line、分栏 drop line |
|
||||
| Esc 选中块 | 输入态按 `esc` 选中当前块,上下方向键切换,`shift + 上/下` 多选,`enter` 回编辑 | 可用 Hermes editable-test mode 取基线 | 本地必须有 block selection state、keyboard smoke |
|
||||
| `cmd/ctrl + A` | 输入态第一次选中当前块文字,第二次选中所有块;块选中态直接选中所有块 | 可用 Hermes editable-test mode 取基线 | 本地必须区分 text selection 与 block selection |
|
||||
| 上 / 下插入块 | hover 左侧 `::` 上方 / 下方点击 `+`;或 `esc` 选中块后按 `a` / `b` | owner 态可观察手柄;真实插入可用 Hermes editable-test mode 做最小验证 | Hermes editable-test mode 必须验证 before / after 插入与 `Esc, a` / `Esc, b` |
|
||||
| 块布局显示 | `cmd/ctrl + shift + U` 显示 / 隐藏块布局虚线框 | 可用 Hermes editable-test mode 取基线 | 本地需有布局 overlay smoke,至少验证虚线框开关 |
|
||||
| 分栏 | 拖块到另一个块左 / 右,辅助线由横线变竖线,松开形成分栏 | 可用 Hermes editable-test mode 取拖拽基线 | 本地可先做 drop preview,不要求完整分栏持久化一次到位 |
|
||||
| 转换块 | 通过块菜单 `转换为`、快捷键、或 slash 命令转换块类型 | owner 态已可打开块菜单并看到 `转换为`;执行转换可用 Hermes editable-test mode 做最小验证 | 本地必须验证块菜单中的 `转换为` 入口与至少 paragraph/headings/list 的转换 |
|
||||
| 复制 / 粘贴 | 文本或块选中后 `cmd/ctrl + C/V`,匹配样式为 `cmd/ctrl + shift + V` | 可用 Hermes editable-test mode 取基线 | 本地需验证纯文本粘贴与块复制的最小路径 |
|
||||
| 缩进 / 取消缩进 | `tab` 缩进成为上一块子元素,`shift + tab` 取消缩进 | 可用 Hermes editable-test mode 取基线 | 本地需验证 list / paragraph 缩进语义与 tree projection 不冲突 |
|
||||
| 文本样式工具条 | 选中文字出现工具条,支持粗体、斜体、下划线、删除线、行内代码、颜色、链接、页面引用、转换块类型 | 可用 Hermes editable-test mode 取基线 | 本地需验证 selection toolbar 出现、按钮视觉与 command dispatch |
|
||||
| slash 菜单 | 输入 `/` 唤起快捷命令菜单;空行左侧 `+` 也可创建块 | 可用 Hermes editable-test mode 取基线 | 本地需验证 `/` 菜单、搜索过滤、选择条目插入块 |
|
||||
|
||||
本轮已能在目标页 `Hermes` 自动化验证的 published 操作:
|
||||
|
||||
- 顶栏搜索:打开搜索 modal,输入 `技能`,出现 `共1条匹配结果`。
|
||||
- 开始编辑:点击后出现 `登录以编辑` 对话框,文案为“登录后,您才可以编辑该页面,是否继续?”,包含 `取消` 和 `继续`。
|
||||
- 评论:点击后打开右侧评论面板,tab 为 `未解决(0)`,空态为 `还没有评论`。
|
||||
- 演示模式:点击后进入 presentation view,隐藏侧栏 / 顶栏,画面只保留大字号内容和演示控件。
|
||||
- Good Night:右下角主题按钮切换暗色 token。
|
||||
- 正文块 hover:owner 态可在块左侧看到手柄,并可点击 `.hover-block-menu` 打开块菜单;上 / 下插入块等写入路径按 `wolai-aline` 的 Hermes editable-test mode 验收。
|
||||
|
||||
因此,后续 `3000` 的主编辑器验收不能只做“截图像”。每个主编辑器改动都必须绑定一个可执行操作:
|
||||
|
||||
- 如果是 published 态能力,必须先在 Wolai published 目标页执行同类操作,再在 `3000` 执行同类操作。
|
||||
- 如果是 owner/edit 态能力,必须先通过 `wolai-aline` 流程在 Wolai 目标页执行同类操作,再在 `3000` 执行同类操作。
|
||||
- 如果是 owner/edit 写入能力,当前 Hermes 测试页允许最小编辑验证;其他 Wolai 页面写入必须先取得沙盒页 URL 与明确授权。
|
||||
- 如果动作是在当前 Hermes 测试页做最小编辑验证,例如输入测试文本、创建临时测试块、验证 slash / toolbar / block menu / 快捷键,可按 `Hermes editable-test mode` 执行并记录前后证据;删除、移动、归档、发布、评论、改权限或批量修改既有非测试内容仍必须在动作发生前单独确认。
|
||||
|
||||
---
|
||||
|
||||
## 3. 当前代码落点
|
||||
|
||||
### 3.1 工作区壳与页面树
|
||||
|
||||
相关入口:
|
||||
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/(app)/layout.tsx`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/app-layout-shell.tsx`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/lib/server/sidebar-data.ts`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/hooks/use-sidebar-data.ts`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/lib/tree-stream/use-sidebar-tree-stream.ts`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/sidebar/sidebar.tsx`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/sidebar/tree-shell-surface.tsx`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/sidebar/tree-shell-host.tsx`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/sidebar/tree-shell-dom-host.tsx`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/lib/tree-projection.ts`
|
||||
|
||||
判断:
|
||||
|
||||
- `Sidebar` 外壳和 `tree-shell-*` 是页面树像素复刻的主战场。
|
||||
- `tree-shell-dom-host` 承担当前树行渲染与本地展开/选中/拖拽反馈,不应再回到旧 React 树渲染器上做长期改造。
|
||||
- `tree-projection` 是页面树 UI 的直接 projection 输入,不应在 UI 层重新推导排序、层级或合法性。
|
||||
|
||||
### 3.2 文档页与主编辑器
|
||||
|
||||
相关入口:
|
||||
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/(app)/documents/[id]/page.tsx`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/lib/documents/page-aggregate-loader.ts`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/lib/documents/page-aggregate-builder.ts`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/document-shell.tsx`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/document-content.tsx`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/document-read-view.tsx`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/leptos-tiptap-island-editor-host.tsx`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/lib/documents/page-option-semantics.ts`
|
||||
|
||||
判断:
|
||||
|
||||
- `document-content.tsx` 决定 Wolai 页面顶部留白、标题、正文容器、页面元信息、右侧 inspector 和 fallback banner。
|
||||
- `document-read-view.tsx` 决定阅读态块样式、任务块、标题层级、附件 / 引用 / 子页面等展示。
|
||||
- `leptos-tiptap-island-editor-host.tsx` 决定编辑态输入 surface、保存、slash、工具条、块菜单和 editor runtime page options 的接缝。
|
||||
- `page-option-semantics.ts` 是页面设置能否真实进入主编辑器运行时的判断边界,不能只改设置面板外观。
|
||||
|
||||
### 3.3 全局视觉 token
|
||||
|
||||
相关入口:
|
||||
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/globals.css`
|
||||
|
||||
判断:
|
||||
|
||||
- 像素复刻必须先固定字体、字号、行高、颜色、圆角、hover/active token。
|
||||
- 当前全局字体口径有冲突:前面偏 Wolai 系统字体,后面 `html` 又偏 `Inter`。若不先收口字体栈,后续所有标题宽度、行高、树行密度都会偏。
|
||||
|
||||
---
|
||||
|
||||
## 4. Wolai 视觉目标合同
|
||||
|
||||
### 4.1 字体与基础排版
|
||||
|
||||
目标字体栈:
|
||||
|
||||
- 优先中文系统字体:`PingFang SC`、`Noto Sans CJK SC`、`Microsoft YaHei UI`。
|
||||
- 英文字体跟随系统 sans-serif,不单独强行使用 `Inter` 作为全局主字体。
|
||||
|
||||
目标字号与行高:
|
||||
|
||||
| 区域 | 字号 | 行高 | 字重 |
|
||||
| --- | --- | --- | --- |
|
||||
| 页面主标题 | 34px | 44px | 600 |
|
||||
| 首页 / 帮助页大分组标题 | 28px | 36px | 600 |
|
||||
| 次级分组标题 | 18px | 26px | 600 |
|
||||
| 正文基础文字 | 16px | 24px | 400 |
|
||||
| 左侧树条目 | 14px | 24px | 400 |
|
||||
| 顶栏文字按钮 | 14px | 21px | 400 |
|
||||
| 搜索框文字 | 14px | 22px | 400 |
|
||||
|
||||
目标颜色:
|
||||
|
||||
| 语义 | 颜色 |
|
||||
| --- | --- |
|
||||
| 主标题 | `rgb(30, 30, 30)` |
|
||||
| 正文 | `rgba(0, 0, 0, 0.85)` |
|
||||
| 次级文字 | `rgba(0, 0, 0, 0.65)` |
|
||||
| 弱提示 | `rgba(0, 0, 0, 0.35)` |
|
||||
| 左侧背景 | `rgb(245, 245, 245)` |
|
||||
| 选中强调文字 | `rgb(207, 86, 89)` |
|
||||
| 选中强调背景 | `rgba(207, 86, 89, 0.15)` |
|
||||
| 顶栏 hover 背景 | `rgb(245, 245, 245)` |
|
||||
|
||||
### 4.2 左侧页面树
|
||||
|
||||
Wolai 目标特征:
|
||||
|
||||
- 左侧栏宽度约 `248px - 260px`,浅灰底。
|
||||
- 顶部空间名行高度约 `40px`,头像为 28px 左右方形圆角块。
|
||||
- 顶部快捷图标横排,图标弱色,hover 只加浅灰底。
|
||||
- 搜索框尺寸约 `236px x 32px`,圆角 `4px`,内边距约 `5px 32px 5px 12px`。
|
||||
- 树行高度约 `32px`,内容行内 icon / arrow / title 对齐。
|
||||
- 当前项使用红字 + 淡红底,背景覆盖整行可点击区域。
|
||||
- 未选中 hover 使用同一红色体系,但透明度更轻。
|
||||
- 缩进按层级稳定递增,不靠文本空格。
|
||||
- 滚动条细、贴右侧、弱灰色,不抢视觉。
|
||||
- 底部垃圾桶 / 模板中心固定在侧栏底部,边界线轻。
|
||||
|
||||
当前 mnote 允许保留:
|
||||
|
||||
- `我的页面 / Explorer` 双模式。
|
||||
- 文件树作为创新入口。
|
||||
- AI / Graph / 文件等自有入口。
|
||||
|
||||
但这些创新必须满足:
|
||||
|
||||
- 视觉密度服从 Wolai 左侧栏。
|
||||
- 选中态和 hover 态与页面树统一。
|
||||
- 不因为多了文件树而破坏 Wolai 的 32px 行节奏。
|
||||
|
||||
### 4.3 顶部导航与页面操作区
|
||||
|
||||
Wolai 目标特征:
|
||||
|
||||
- 顶栏高度约 `40px`。
|
||||
- 左侧是菜单按钮 + breadcrumb。
|
||||
- breadcrumb 使用图标 / 文本 / 分隔符,字号 `14px`,颜色弱。
|
||||
- 右侧按钮是弱文本 / 图标按钮,padding 约 `2px 7px`,圆角 `3px`。
|
||||
- owner 态可见:公开状态、收藏、演示、评论、关系、邀请、历史、更多。
|
||||
- published 态可见:演示模式、搜索、开始编辑、评论、复制、开始使用 wolai。
|
||||
- 顶栏 hover 不用重色按钮,只加浅灰底。
|
||||
|
||||
mnote 当前差距:
|
||||
|
||||
- 顶栏右侧已经有 Public、收藏、历史、AI、搜索、更多,但与 Wolai owner 态的 icon 序列和视觉轻重不同。
|
||||
- published 视角与 owner 视角没有明确组件分型。
|
||||
- breadcrumb 与页面内容标题之间的垂直节奏还需要更接近 Wolai。
|
||||
|
||||
### 4.4 主文档画布
|
||||
|
||||
Wolai 目标特征:
|
||||
|
||||
- 主内容列在大屏中不贴左,普通文档正文列宽约 `760px`。
|
||||
- 登录态 `Hermes` 页面首屏标题大约从 y=118px 开始。
|
||||
- 标题为 `34px / 44px / 600`,颜色接近 `rgb(30,30,30)`。
|
||||
- 标题下方正文块紧跟,不默认显示额外元信息行和横向分割线。
|
||||
- 普通正文块使用 `16px / 24px`。
|
||||
- 任务块 checkbox 轻边框,小尺寸,和文本基线对齐。
|
||||
- 空白区域非常克制,不用额外卡片、阴影或说明文案。
|
||||
|
||||
mnote 当前差距:
|
||||
|
||||
- 当前本地文档页标题偏靠左且偏低,内容列与真实 Wolai 相比没有稳定对齐。
|
||||
- 当前显示“个人空间 / 工作区首页”元信息和横向分割线,这不是 Wolai 普通文档页默认观感。
|
||||
- 历史上首屏正文首块曾是“打开当前页面”链接;`task130` 之后这条差异已收口,当前重点转向主编辑区密度、块 hover 和 toolbar 细节。
|
||||
- 右下角绿色 AI 浮动按钮比 Wolai 更重,真实 Wolai 是更轻的 AI 方形入口和问号入口。
|
||||
|
||||
### 4.5 主编辑器块级体验
|
||||
|
||||
Wolai / Notion-like 目标:
|
||||
|
||||
- slash 是主编辑入口之一,菜单跟随 caret。
|
||||
- 选中文本才出现浮动工具条。
|
||||
- hover 块时只露出左侧手柄,不自动弹菜单。
|
||||
- 点击左侧手柄才出现块菜单。
|
||||
- 块菜单核心动作是 `turn into / duplicate / delete / drag`。
|
||||
- `turn into` 同时存在于文本工具条语境和块菜单语境,但命令上下文不同。
|
||||
- 块 identity 由 Rust `EditorBlock.block_id` 持有;Tiptap `UniqueID` 只作为浏览器 runtime 辅助。
|
||||
- 页面引用、块引用、嵌入默认位置最终必须回到 Page Aggregate / Rust artifact 边界。
|
||||
|
||||
#### 4.5.1 hover 上 / 下插入块
|
||||
|
||||
用户补充的登录态截图显示,Wolai 的块 hover 体验还有一个非常关键的细节:当鼠标停在块左侧手柄区域时,当前块行会出现很轻的淡红背景,手柄上方和下方分别出现一条短横线;继续 hover 短横线时,短横线变成 `+`,并显示黑色 tooltip:
|
||||
|
||||
- `在上方插入块`,快捷键提示为 `Esc, a`。
|
||||
- `在下方插入块`,快捷键提示为 `Esc, b`。
|
||||
|
||||
这个交互不是装饰性按钮,而是 Wolai 块编辑器的“块级插入光标”。它要和块选中、拖拽手柄、块菜单、slash 菜单、快捷键一起设计,不能只在正文左侧放一个常驻加号。
|
||||
|
||||
当前仓库已有可复用参考:
|
||||
|
||||
- `/mnt/Data1T/mnote/recycle/wolai-frontend/src/components/editor/blocknote-editor.tsx` 中,旧 `BlockNoteView` 通过 `SideMenuController` 注入自定义 `CustomSideMenu`。
|
||||
- `/mnt/Data1T/mnote/recycle/wolai-frontend/src/components/editor/menus/CustomSideMenu.tsx` 中,`WolaiDragHandleWithInsert` 已实现上方插入、下方插入、手柄菜单、菜单冻结、空段落加号打开 slash、截图失焦状态重置。
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/leptos-tiptap-inline-editor-host.tsx` 当前已有较简化的块手柄和菜单,但没有复刻上 / 下短横线插入态。
|
||||
- `/mnt/Data1T/mnote/rust/spikes/leptos-tiptap-spike/src/lib.rs` 已有 leptos/tiptap 手柄雏形,但当前样式偏重,并且 `insert_top_level_paragraph_after_html` 只支持 after 插入。
|
||||
|
||||
旧 BlockNote 实现中最值得迁移的机制:
|
||||
|
||||
- 插入控件不是放在块容器上下边界,而是围绕手柄中心点计算位置:上方插入、手柄、下方插入三者垂直对齐。
|
||||
- 非 hover 状态显示 `12px` 左右短横线,hover 状态切换为 `Plus` 图标。
|
||||
- 插入按钮尺寸约 `16px`,手柄按钮约 `22px`,按钮之间保持小间距,避免插入按钮和拖拽手柄互相遮挡 pointer event。
|
||||
- `hoverArea` 要比按钮视觉范围更大,避免鼠标从手柄移到插入按钮时控件闪烁。
|
||||
- 手柄菜单打开时需要冻结 hover 状态,否则菜单会因鼠标离开块行而消失。
|
||||
- 空段落的中心 `+` 点击应等同打开 slash 菜单,而不是直接创建第二个空段落。
|
||||
- 截图、窗口失焦、页面隐藏时要重置 hover / frozen 状态,避免手柄卡住或消失。
|
||||
- 思维导图、在线表格、附件这类高块或内部接管鼠标事件的块,需要允许常驻或更稳定的插入控件显示策略。
|
||||
|
||||
迁移到 `leptos-tiptap` 时,目标行为如下:
|
||||
|
||||
- hover 普通块:左侧只显示三段控件,上短横线 / 拖拽手柄 / 下短横线;不自动打开块菜单。
|
||||
- hover 上短横线:短横线变 `+`,tooltip 显示 `在上方插入块` 和 `Esc, a`。
|
||||
- hover 下短横线:短横线变 `+`,tooltip 显示 `在下方插入块` 和 `Esc, b`。
|
||||
- 点击上方 `+`:在当前块前插入空段落,焦点进入新段落。
|
||||
- 点击下方 `+`:在当前块后插入空段落,焦点进入新段落。
|
||||
- 按 `Esc` 进入块选择 / 块聚焦状态后,按 `a` 执行 before 插入,按 `b` 执行 after 插入。
|
||||
- 点击拖拽手柄才打开块菜单;拖拽手柄本身不和上 / 下插入按钮抢 hover。
|
||||
|
||||
实现边界:
|
||||
|
||||
- `leptos-tiptap` 需要补齐 before / after 两个编辑器命令;不能继续只有 after HTML 插入。
|
||||
- 第一阶段可以先在 runtime 内插入空段落并保存正文,但正式命令口径应回到 Page Aggregate / Rust editor block model,例如 `page.body.insertBlockBefore` / `page.body.insertBlockAfter` 或等价 `EditorCommand::InsertBlock { position: Before | After }`。
|
||||
- UI 层可以持有当前 hover block、active block、tooltip、menu open 这些临时状态,但不能把插入后的块 id 只存在前端临时结构里。
|
||||
- 视觉上应优先复刻 Wolai:轻灰短横线、浅灰 hover 背景、黑色小 tooltip、淡红块 hover 底色;不要沿用 spike 中 32px、大圆角、重阴影、绿色 accent 的手柄样式。
|
||||
|
||||
首轮不追求:
|
||||
|
||||
- 完整协作光标。
|
||||
- 完整评论系统。
|
||||
- 完整表格编辑体验。
|
||||
- 完整图片 / 附件上传链。
|
||||
- 完整权限和发布链路。
|
||||
- 完整全局搜索索引。
|
||||
|
||||
### 4.6 操作态菜单与浮层
|
||||
|
||||
真实 Wolai 的体验不能只看静态首屏,必须把操作后出现的浮层也纳入复刻合同。
|
||||
|
||||
#### 4.6.1 页面树 owner 态菜单
|
||||
|
||||
用户提供的登录态截图显示,页面树当前页 `Hermes` 的管理菜单为白色浮层,宽约 220px,圆角和阴影都很轻,菜单项按语义分组:
|
||||
|
||||
- 打开位置:`在右侧边栏打开`,带快捷键提示。
|
||||
- 页面组织:`移动到...`、`嵌入到...`。
|
||||
- 引用复制:`复制访问链接`、`复制页面引用链接`、`复制页面ID`。
|
||||
- 页面副本:`拷贝副本`。
|
||||
- 危险或编辑动作:`重命名`、`删除`。
|
||||
|
||||
这说明页面树行不只是导航节点,还承载页面操作入口。mnote 复刻时必须把这些动作映射到正式命令:
|
||||
|
||||
- `在右侧边栏打开`:UI state + route / side panel state。
|
||||
- `移动到...`:`tree.subtree.move`。
|
||||
- `嵌入到...`:`tree.node.embed` 或 Page Aggregate embed plan。
|
||||
- `复制页面引用链接`:生成 page reference artifact,不应只复制 URL。
|
||||
- `重命名`:`page.head.updateTitle`,并同步 tree projection。
|
||||
- `删除`:必须走归档 / 删除确认链,不能前端直接移除节点。
|
||||
|
||||
首轮可以先复刻菜单壳、分组、hover、禁用态和快捷键提示;真实移动、嵌入、删除等高风险动作按后续命令合同逐步接入。
|
||||
|
||||
#### 4.6.2 搜索弹层
|
||||
|
||||
公开页搜索和帮助中心搜索都使用居中 modal overlay:
|
||||
|
||||
- 背景整体变暗,侧栏和正文保持原位但失焦。
|
||||
- 搜索框宽约 680px,高约 56px,白底,圆角轻,阴影明显但不厚重。
|
||||
- 输入后出现选项行:`仅匹配标题`、`精确匹配`、`页面内搜索`。
|
||||
- 结果区显示匹配数量和快捷键说明:`Ctrl + Enter 新窗口打开 / Alt + Enter 右侧边栏打开`。
|
||||
- 结果行左侧是页面 / 块图标,中间是标题与命中摘要,命中词使用红色高亮,右侧显示所在页面或空间。
|
||||
|
||||
mnote 需要把搜索拆成两层:
|
||||
|
||||
- 左侧栏 `快速筛选页面`:只过滤当前页面树,结果直接替换树列表。
|
||||
- 顶栏搜索 modal:跨页面 / 页面内搜索入口,支持结果列表、快捷键提示、打开方式提示。
|
||||
|
||||
首轮可以用本地 projection / 当前页面块快照生成结果,不必先接全局索引;但视觉结构、选项行和打开方式提示应先复刻。
|
||||
|
||||
#### 4.6.3 published 态操作
|
||||
|
||||
真实 published 态存在几类弱操作入口:
|
||||
|
||||
- `演示模式`:点击后隐藏侧栏和顶栏,画布变成大字号演示页,只保留主题切换入口。
|
||||
- `开始编辑`:未登录时弹出“登录以编辑”确认框,红色主按钮为继续,取消为弱按钮。
|
||||
- `评论`:右侧滑出评论面板,顶部有 `未解决(0)` tab、筛选下拉、关闭按钮,空态居中。
|
||||
- `Good Night`:右下角主题按钮直接切换暗色主题,暗色主题不重排页面,只替换 token。
|
||||
|
||||
mnote 首轮不必做完整发布权限,但应该保留这套状态分型:
|
||||
|
||||
- owner 编辑态。
|
||||
- published 阅读态。
|
||||
- presentation 演示态。
|
||||
- comment side panel。
|
||||
- light / dark token 切换。
|
||||
|
||||
这些状态大多是 UI state,但 `开始编辑` 是否可用、评论权限、发布状态必须来自页面权限或发布 projection,不应在前端硬猜。
|
||||
|
||||
### 4.7 帮助中心页的内在逻辑
|
||||
|
||||
`https://www.wolai.com/wolai/xhqeop8UHpVTMUSVmgz8nq` 不是普通内容页,它展示了 Wolai 页面系统的内在组织方式。
|
||||
|
||||
观察结论:
|
||||
|
||||
- 左侧页面树是帮助主题的完整目录,根页面下挂大量子页面,既是导航也是信息架构。
|
||||
- 正文入口页不是自由排版海报,而是由页面链接、分组标题、四列目录块、子页面引用组成的“文档门户”。
|
||||
- `快速上手 / 常见问题 / 视频教程 / 使用技巧短视频` 是高频入口。
|
||||
- “让我们先掌握一些基础”下的四列目录把页面按能力分组:基础操作、基础块类型、进阶块类型、媒体与文件。
|
||||
- 目录项本质上是页面引用 / 页面链接,不是普通文本。
|
||||
- 搜索可以跨整个帮助中心返回页面和块级结果,命中词红色高亮。
|
||||
- 搜索结果可以按快捷键在新窗口或右侧边栏打开,说明“右侧边栏打开”是 Wolai 页面导航模型的一等入口。
|
||||
|
||||
更重要的是,帮助中心中的“块引用”页直接定义了编辑器的语义模型:
|
||||
|
||||
- 行内块引用:在文字中间引用另一个块,底部有圆点虚线,不能直接编辑,点击跳转原始出处。
|
||||
- 嵌入块引用:以独立块引用另一个块,左侧有圆点虚线,可以直接查看或部分编辑,但不能删除被引用块本身。
|
||||
- 页面引用:当对象是页面时,菜单从“复制块引用链接”变为“复制页面引用链接”。
|
||||
- 行内页面引用:在文本内显示页面标题。
|
||||
- 嵌入页面引用:独立引用块显示页面图标和标题。
|
||||
- 复制粘贴路径:块菜单复制引用链接,再粘贴生成引用。
|
||||
- 快捷键路径:`esc` 选中块,`H` 复制行内块引用链接,`Q` 复制嵌入块引用链接。
|
||||
- 嵌入到路径:`cmd/alt + shift + G` 打开“嵌入到”弹窗,搜索目标页面,把块嵌入到目标页面。
|
||||
- 当前页面位置快速引用:编辑时输入 `[[` 搜索页面,添加页面行内引用。
|
||||
- 右侧边栏拖拽引用:从右侧边栏拖拽产生引用。
|
||||
- 引用预览和别名:悬浮行内块引用出现预览,小窗顶部可以设置别名,别名末尾显示箭头。
|
||||
|
||||
因此,mnote 不应把“引用”当成一个简单链接样式。它至少需要在 Page Aggregate / editor block model 中区分:
|
||||
|
||||
- `inline_page_reference`
|
||||
- `inline_block_reference`
|
||||
- `embedded_page_reference`
|
||||
- `embedded_block_reference`
|
||||
- `reference_alias`
|
||||
- `reference_preview`
|
||||
- `reference_source_block_id`
|
||||
- `reference_target_page_id`
|
||||
|
||||
首轮实现可以降级,但语义模型不能降级成普通 URL。
|
||||
|
||||
---
|
||||
|
||||
## 5. 复刻范围分级
|
||||
|
||||
### 5.1 P0:先做像素基线
|
||||
|
||||
P0 只做最影响首屏观感的部分:
|
||||
|
||||
- 全局字体栈与基础字号行高收口。
|
||||
- Sidebar 宽度、背景、顶部栏、树行高度、选中态、hover 态。
|
||||
- 顶栏高度、breadcrumb、右侧动作按钮轻量化。
|
||||
- 文档标题位置、字号、行高、正文列宽。
|
||||
- 默认隐藏普通文档页中的额外元信息行和分割线。
|
||||
- 右下角浮动入口减重。
|
||||
- 在 `1392 x 1213` 视口下对齐真实 Wolai 截图。
|
||||
|
||||
### 5.2 P1:补齐高频交互体验
|
||||
|
||||
P1 做用户会立刻感知的交互:
|
||||
|
||||
- 页面树展开 / 折叠动画和 hover action。
|
||||
- 当前页祖先链展开和选中态保持。
|
||||
- 搜索框视觉与本地过滤体验。
|
||||
- 树行上下文菜单的视觉壳和核心动作入口。
|
||||
- 顶栏搜索 modal 的选项行、结果行、命中高亮和快捷键提示。
|
||||
- 评论侧栏、登录编辑确认框、演示模式的基础状态切换。
|
||||
- 文档页进入编辑后,slash、浮动工具条、左侧手柄、上 / 下插入块、块菜单的基础可用体验。
|
||||
- 页面标题编辑后,标题 / breadcrumb / sidebar / page tree / file tree 保持一致。
|
||||
|
||||
### 5.3 P2:接近 Wolai 的产品完整感
|
||||
|
||||
P2 做复杂但可逐步推进的部分:
|
||||
|
||||
- 拖拽排序与 drop feedback。
|
||||
- page reference / block reference 的正式插入体验。
|
||||
- `[[` 页面引用搜索、`嵌入到...` 弹窗、右侧边栏打开 / 拖拽引用。
|
||||
- 引用预览和别名。
|
||||
- 右侧评论、历史、关系图入口的真实面板。
|
||||
- published / owner / edit 三态顶栏完整切换。
|
||||
- 页面设置项与 editor runtime 的完整联动。
|
||||
- 帮助中心类多列目录块的静态展示和后续编辑支持。
|
||||
|
||||
### 5.4 明确降级项
|
||||
|
||||
以下功能可以先只做视觉入口或最小实现:
|
||||
|
||||
- 评论协作。
|
||||
- 权限分享。
|
||||
- 发布与侵权投诉链路。
|
||||
- 全局搜索索引。
|
||||
- 完整演示模式。
|
||||
- 完整夜间主题。
|
||||
- 完整表格、图表、数据库。
|
||||
- 完整文件上传和媒体块管理。
|
||||
- 完整引用别名编辑器和跨页面引用预览缓存。
|
||||
|
||||
---
|
||||
|
||||
## 6. 单一真源边界
|
||||
|
||||
### 6.1 可以留在前端展示层的内容
|
||||
|
||||
以下内容允许作为前端展示或临时 UI state:
|
||||
|
||||
- 色彩、字号、行高、间距、圆角、阴影。
|
||||
- Sidebar 宽度与布局。
|
||||
- 树行 hover、focus、临时 selection、drag preview。
|
||||
- 顶栏按钮排列与 hover。
|
||||
- 文档画布宽度、标题留白、正文显示密度。
|
||||
- slash 菜单开合状态。
|
||||
- 浮动工具条开合状态。
|
||||
- 块手柄 hover 状态。
|
||||
- 右下角浮动入口显示状态。
|
||||
|
||||
### 6.2 必须回到 Rust / projection / Page Aggregate 的内容
|
||||
|
||||
以下内容不能只在前端复刻:
|
||||
|
||||
- 页面树真实层级。
|
||||
- 排序真相。
|
||||
- 当前页 active path。
|
||||
- 拖拽是否合法。
|
||||
- 新建、重命名、移动、归档、恢复结果。
|
||||
- 页面标题与 breadcrumb / sidebar / file tree 的一致性。
|
||||
- 正文保存与 conflict key。
|
||||
- 页面设置对 editor runtime 的正式语义。
|
||||
- page reference / block reference / embed 默认位置。
|
||||
- 引用类型、引用源、引用目标、引用别名、引用预览数据。
|
||||
- AI 写入标题、正文、页面设置的语义边界。
|
||||
|
||||
原则:
|
||||
|
||||
> **UI 可以复刻 Wolai,事实源不能复刻成第二份前端状态。**
|
||||
|
||||
### 6.3 命令口径
|
||||
|
||||
页面树动作继续沿 `tree.*`:
|
||||
|
||||
- `tree.node.create`
|
||||
- `tree.node.rename`
|
||||
- `tree.subtree.move`
|
||||
- `tree.node.archive`
|
||||
- `tree.node.restore`
|
||||
- `tree.node.embed`
|
||||
|
||||
页面动作继续沿 `page.*`:
|
||||
|
||||
- `page.head.updateTitle`
|
||||
- `page.layout.updateOptions`
|
||||
- `page.body.save`
|
||||
|
||||
兼容 `documents.*` 可以继续存在,但不应作为新增体验的正式命令面。
|
||||
|
||||
---
|
||||
|
||||
## 7. 推荐实施路线
|
||||
|
||||
### Phase A:建立 Wolai 视觉 token
|
||||
|
||||
目标:
|
||||
|
||||
> **先让字体、字号、行高、颜色和基础密度可被统一复用。**
|
||||
|
||||
主要文件:
|
||||
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/globals.css`
|
||||
|
||||
工作内容:
|
||||
|
||||
- 统一全局字体栈,避免 `Inter` 覆盖中文系统字体。
|
||||
- 建立 Wolai-like token:`--wolai-sidebar-bg`、`--wolai-active-fg`、`--wolai-active-bg`、`--wolai-text-primary`、`--wolai-text-secondary`。
|
||||
- 固定标题、正文、sidebar、topbar 的字号 / 行高。
|
||||
- 用 token 替代散落硬编码颜色。
|
||||
|
||||
验收:
|
||||
|
||||
- `local-3000` 截图中标题、树行和顶栏文字宽度明显接近真实 Wolai。
|
||||
- 中文字体不再出现 Inter 优先导致的字宽偏差。
|
||||
|
||||
### Phase B:复刻 Sidebar / 页面树首屏
|
||||
|
||||
目标:
|
||||
|
||||
> **让左侧页面树在密度、选中态、hover、滚动条、底部入口上接近 Wolai。**
|
||||
|
||||
主要文件:
|
||||
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/sidebar/sidebar.tsx`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/sidebar/tree-shell-surface.tsx`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/sidebar/tree-shell-host.tsx`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/sidebar/tree-shell-dom-host.tsx`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/lib/tree-projection.ts`
|
||||
|
||||
工作内容:
|
||||
|
||||
- 固定侧栏宽度和背景。
|
||||
- 调整 workspace header 高度、头像尺寸、空间名截断方式。
|
||||
- 调整快捷图标行尺寸与 hover。
|
||||
- 调整页面树行高到 32px 附近。
|
||||
- 当前页改为 Wolai 红字 + 淡红底。
|
||||
- hover 态与 active 态同色系但层级更轻。
|
||||
- 缩进只由层级和 icon slot 决定。
|
||||
- 底部垃圾桶 / 模板中心与 Wolai 对齐,同时保留 mnote 自有入口时不破坏密度。
|
||||
- `Explorer` 作为 mnote 创新保留,但视觉上降噪,避免抢过“我的页面”主路径。
|
||||
|
||||
验收:
|
||||
|
||||
- 对照用户提供的登录态截图,`Hermes` 选中行的颜色、行高、左侧缩进、滚动条位置接近 Wolai。
|
||||
- 页面树长列表滚动时不出现行高抖动。
|
||||
- 文件树创新入口仍可访问。
|
||||
|
||||
### Phase C:复刻顶栏与 breadcrumb
|
||||
|
||||
目标:
|
||||
|
||||
> **让顶栏在 owner / published 两种状态下都接近 Wolai 的轻量工具条。**
|
||||
|
||||
主要文件:
|
||||
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/app-layout-shell.tsx`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/document-content.tsx`
|
||||
|
||||
工作内容:
|
||||
|
||||
- 固定顶栏高度约 40px。
|
||||
- breadcrumb 文本、图标、分隔符弱化。
|
||||
- 右侧按钮统一使用轻量 icon/text button。
|
||||
- 区分 owner 态和 published 态的按钮组合。
|
||||
- 公开状态 pill 对齐 Wolai 的“全网公开 / Public”视觉。
|
||||
- 顶栏 hover 只加浅灰底,不使用重边框或大面积按钮。
|
||||
|
||||
验收:
|
||||
|
||||
- 登录态参考图中的顶栏图标序列能在 mnote 中找到对应视觉位置。
|
||||
- published 公共页参考图中的“演示模式 / 搜索 / 开始编辑 / 评论 / 复制 / 开始使用 wolai”可先作为视觉分型,不要求一次接齐功能。
|
||||
|
||||
### Phase D:复刻文档画布与阅读态
|
||||
|
||||
目标:
|
||||
|
||||
> **让普通文档页首屏像 Wolai,而不是像调试页或二级详情页。**
|
||||
|
||||
主要文件:
|
||||
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/document-content.tsx`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/document-read-view.tsx`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/lib/documents/page-option-semantics.ts`
|
||||
|
||||
工作内容:
|
||||
|
||||
- 标题使用 `34px / 44px / 600`。
|
||||
- 普通正文列宽约 760px。
|
||||
- 调整首屏 top padding,使标题起点接近 Wolai。
|
||||
- 默认隐藏普通文档页的“个人空间 / 工作区首页”元信息行和横向分割线。
|
||||
- 保留页面设置控制,但把“显示元信息 / 显示结构”这类调试或增强项作为显式选项,不默认露出。
|
||||
- 阅读态任务块、段落、标题、列表、引用、代码块按 Wolai 行高和颜色重调。
|
||||
- 右下角 AI / 帮助入口减重,避免绿色大按钮抢主编辑区视觉。
|
||||
|
||||
验收:
|
||||
|
||||
- `Hermes` 类普通文档页首屏只突出标题和正文块。
|
||||
- 页面不再默认出现明显不像 Wolai 的横线、meta row 或 debug outline。
|
||||
- 阅读态和编辑态在基础排版上不出现明显跳动。
|
||||
|
||||
### Phase E:复刻主编辑器高频块交互
|
||||
|
||||
目标:
|
||||
|
||||
> **让 `leptos-tiptap` 的输入体验从“能编辑”推进到“像 Wolai 的块编辑器”。**
|
||||
|
||||
主要文件:
|
||||
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/leptos-tiptap-island-editor-host.tsx`
|
||||
- `/mnt/Data1T/mnote/rust/crates/core-protocol/src/editor/model.rs`
|
||||
- `/mnt/Data1T/mnote/rust/crates/bridge-runtime/`
|
||||
- `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/tiptap-notion-like-registry/`
|
||||
|
||||
工作内容:
|
||||
|
||||
- slash 菜单锚定 caret。
|
||||
- selection 存在时显示浮动工具条。
|
||||
- hover 块只显示左侧手柄区,上方和下方短横线在 hover 时变成插入 `+`。
|
||||
- 点击上方 / 下方插入控件分别创建 before / after 空段落,并把焦点移动到新段落。
|
||||
- 支持 `Esc, a` 和 `Esc, b` 的块级插入快捷键。
|
||||
- 点击手柄打开块菜单。
|
||||
- 块菜单至少提供 `turn into / duplicate / delete` 的视觉与基础命令入口。
|
||||
- `turn into` 不直接绕过 Rust block model。
|
||||
- Tiptap `UniqueID` 只用于 runtime 辅助,最终块 id 对齐 Rust `EditorBlock.block_id`。
|
||||
- 页面引用 / 块引用可先做最小插入体验,复杂搜索和预览后置。
|
||||
|
||||
验收:
|
||||
|
||||
- 用户能在主编辑区通过 slash 插入常见块。
|
||||
- 用户选中文本时看到 Wolai-like 浮动工具条。
|
||||
- 用户 hover 块时不会被重工具条打扰,并能看到 Wolai-like 上 / 下插入短横线。
|
||||
- 用户可以通过鼠标或 `Esc, a` / `Esc, b` 在当前块前后插入空段落。
|
||||
- 块操作不会制造前端临时 id 作为持久化真相。
|
||||
|
||||
### Phase F:用 Page Aggregate 守住一致性
|
||||
|
||||
目标:
|
||||
|
||||
> **复刻体验时不牺牲单一真源。**
|
||||
|
||||
主要文件:
|
||||
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/lib/documents/page-aggregate-loader.ts`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/lib/documents/page-aggregate-builder.ts`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/lib/documents/page-command-client.ts`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/api/documents/page/route.ts`
|
||||
- `/mnt/Data1T/mnote/rust/crates/core-protocol/`
|
||||
- `/mnt/Data1T/mnote/rust/crates/bridge-runtime/`
|
||||
- `/mnt/Data1T/mnote/rust/crates/mnote-web/`
|
||||
|
||||
工作内容:
|
||||
|
||||
- 新增视觉状态时,先判断它是否只是 UI state。
|
||||
- 新增页面字段时,必须归入 `page_identity / page_head / page_layout / page_body / page_tree`。
|
||||
- 标题变更必须继续让页头、breadcrumb、sidebar、page tree、file tree 同步。
|
||||
- 正文保存继续走 `page.body.save`。
|
||||
- 页面设置继续按 `page-option-semantics` 判断是否进入 island runtime。
|
||||
- 页面树动作继续走 `tree.*`,不新增 `documents.*` 长期动作。
|
||||
|
||||
验收:
|
||||
|
||||
- 页面重命名后,所有消费者保持一致。
|
||||
- 切页、刷新、实时更新不会把 UI 压回旧标题。
|
||||
- 视觉复刻代码里没有新增第二套树排序或页面标题真相。
|
||||
|
||||
### Phase G:截图回归与像素验收
|
||||
|
||||
目标:
|
||||
|
||||
> **把“像 Wolai”变成可复查、可操作、可重复的截图基线。**
|
||||
|
||||
建议新增或复用:
|
||||
|
||||
- `/mnt/Data1T/mnote/scripts/task*-smoke.js`
|
||||
- `/mnt/Data1T/mnote/tmp/wolai-compare/`
|
||||
|
||||
验收视口:
|
||||
|
||||
- 桌面:`1392 x 1213`
|
||||
- 宽屏:`1440 x 900`
|
||||
- 窄屏:`390 x 844`
|
||||
|
||||
截图基线:
|
||||
|
||||
- 真实 Wolai 登录态长页面树。
|
||||
- 真实 Wolai `Hermes` 普通文档页。
|
||||
- 当前 mnote 同类页面。
|
||||
- mnote 文件树创新入口展开态。
|
||||
|
||||
验收方式:
|
||||
|
||||
- 每次验收必须按 `wolai-aline` skill 形成差异矩阵,并附 Wolai 截图、本地截图、动作链、DOM/ARIA 线索和剩余差异。
|
||||
- 浏览器取证必须由 subagent 执行;主线程必须复核截图,不能只接受 subagent 文本结论。
|
||||
- published 阅读态、owner 登录态、editable-test mode 都只表示取证上下文,不再作为三套独立测试方案维护。
|
||||
- 先用 Wolai 基线写出本地 RED smoke,再实现,再跑本地 smoke,再派 subagent 复测。
|
||||
- 若截图肉眼可见差异但 smoke 未捕获,先增强 smoke 或 checklist 断言,再继续实现。
|
||||
- 后续可加入截图 diff,但不能用截图 diff 替代真实交互烟测。
|
||||
- 每次改 Sidebar、topbar、document canvas、editor runtime 后都要重新截首屏,并同步更新连续 checklist 的状态。
|
||||
- 连续执行清单见 `/mnt/Data1T/mnote/design/05-editor-mainline/process/5-9-wolai-aline-continuous-checklist-v1.md`。
|
||||
|
||||
owner 态交互验收清单:
|
||||
|
||||
- 自动化打开 `https://www.wolai.com/liaibo/ikFSSM1a4GgvmHBYFCNfVd` 后确认进入登录 owner 态,而不是 published 态。
|
||||
- hover 页面树当前项 `Hermes`,确认 hover action 可见。
|
||||
- 打开页面树菜单,截图菜单项和分组。
|
||||
- hover 正文块 `事实上`,确认块背景、左侧手柄、上短横线、下短横线可见。
|
||||
- hover 上短横线,确认 tooltip 为 `在上方插入块` / `Esc, a`。
|
||||
- hover 下短横线,确认 tooltip 为 `在下方插入块` / `Esc, b`。
|
||||
- 打开块菜单,截图 `AI 助理 / 转换为 / 拷贝副本 / 删除 / 复制链接 / 移动/嵌入到... / 块历史... / 评论 / 颜色 / 文字居中 / 文字翻译 / 生成海报` 等入口。
|
||||
- 在 `3000` 执行同类操作并保存同视口截图。
|
||||
- 验收报告中列出 Wolai 截图、本地截图、差异结论和不可测项。
|
||||
|
||||
---
|
||||
|
||||
## 8. 验证命令建议
|
||||
|
||||
前端运行:
|
||||
|
||||
```bash
|
||||
cd /mnt/Data1T/mnote/wolai-frontend && pnpm dev
|
||||
```
|
||||
|
||||
前端相关测试:
|
||||
|
||||
```bash
|
||||
cd /mnt/Data1T/mnote/wolai-frontend && pnpm test src/components/sidebar/tree-shell-host.test.tsx src/components/sidebar/tree-shell-surface.test.tsx src/components/editor/document-content.test.ts src/components/editor/leptos-tiptap-island-editor-host.test.tsx
|
||||
```
|
||||
|
||||
projection / aggregate 测试:
|
||||
|
||||
```bash
|
||||
cd /mnt/Data1T/mnote/wolai-frontend && pnpm test src/lib/tree-projection.test.ts src/lib/tree-projection-contract.test.ts src/lib/documents/page-aggregate-builder.test.ts src/lib/documents/tree-command-client.test.ts
|
||||
```
|
||||
|
||||
Rust 侧测试:
|
||||
|
||||
```bash
|
||||
cd /mnt/Data1T/mnote && cargo test -p mnote-web workspace_shell
|
||||
cd /mnt/Data1T/mnote && cargo test -p bridge-runtime
|
||||
```
|
||||
|
||||
浏览器验收:
|
||||
|
||||
- 先按 `wolai-aline` skill 派 subagent 打开真实 Wolai 参考页和 `http://127.0.0.1:3000/`。
|
||||
- 统一视口至少覆盖 `1392 x 1213`;涉及响应式时补 `1440 x 900` 和 `390 x 844`。
|
||||
- 每个小任务都要保存 Wolai 与本地截图,并记录入口、关闭路径、URL 变化、控件形态、控件状态、快捷键、hover/active、DOM/ARIA 线索。
|
||||
- 主线程复核截图后,把结论写入差异矩阵;发现差异先补 smoke,再改实现。
|
||||
- 对照范围至少包含左侧栏宽度、树行高度、选中态、正文标题位置、顶栏按钮密度、搜索 modal、块 hover/插入入口、右下角浮动入口。
|
||||
|
||||
---
|
||||
|
||||
## 9. 禁止事项
|
||||
|
||||
为避免这条线跑偏,后续实现中禁止:
|
||||
|
||||
- 不把 `BlockNote` 写回默认主编辑器。
|
||||
- 不把 Next App Router 恢复成 `3000` 主入口。
|
||||
- 不在 UI 层重新拼第二份页面树、排序、标题或正文真相。
|
||||
- 不把 compat route 或 debug shell 当正式主链。
|
||||
- 不为了像素复刻绕开 `tree.*` / `page.*` 命令族。
|
||||
- 不把 Tiptap runtime 临时 id 倒灌成 Rust 持久化块 id。
|
||||
- 不把页面设置做成“保存了字段但 editor runtime 不消费”的假功能。
|
||||
- 不为了复刻 Wolai 顶栏而提前接入真实权限 / 分享 / 评论复杂链路。
|
||||
|
||||
---
|
||||
|
||||
## 10. 当前退出标准
|
||||
|
||||
这份方案只有在下面条件同时满足后,才能移入 `done`:
|
||||
|
||||
- `3000` 首屏在左侧页面树、顶栏、文档画布三块视觉上接近真实 Wolai。
|
||||
- 页面树保留 mnote 文件树创新,但不破坏 Wolai 主路径密度。
|
||||
- 普通文档页默认不再显得像调试页或二级详情页。
|
||||
- `leptos-tiptap` 主编辑器具备 slash、浮动工具条、左侧手柄、上 / 下插入块、块菜单的基础 Wolai-like 行为。
|
||||
- 标题 / 正文 / 页面设置 / 页面树仍服从 Page Aggregate 与 tree-first graph kernel 主线。
|
||||
- 相关 smoke / 单测 / 截图验收有记录。
|
||||
- 连续 checklist 中 P0/P1 任务均有 Wolai 基线、本地 RED/GREEN smoke、subagent 复测截图和主线程复核结论。
|
||||
|
||||
当前结论:
|
||||
|
||||
> **下一步应先做 P0 像素基线和 Sidebar / 文档画布首屏对齐,再推进编辑器块级交互。功能复杂项可以降级,但页面树和主编辑器的视觉节奏必须优先向真实 Wolai 收口。**
|
||||
@@ -6,7 +6,7 @@
|
||||
> - 本 checklist 的 Wolai 体验对标仍有效,但数据真相口径跟随 local-first workspace:本地 `.md` 是默认正文真相,Convex-backed 路径只作为兼容 / cloud source。
|
||||
> - 历史条目中“写回 Convex-backed 持久化底座”的表述只代表当时 smoke 的在线路径,不再作为新增编辑能力默认目标。
|
||||
>
|
||||
> 本清单拆自 `5-7-wolai-page-tree-main-editor-experience-restoration-v1.md`。它不是新的测试方案;执行口径统一服从 `/home/lix/.codex/skills/wolai-aline` 与 `/mnt/Data1T/mnote/design/08-wolai-aline-test-flow/process/wolai-aline-test-flow-v1.md`。
|
||||
> 本清单拆自 `5-7-wolai-page-tree-main-editor-experience-restoration-v1.md`。它不是新的测试方案;执行口径统一服从 `/home/lix/.codex/skills/wolai-aline` 与 `/mnt/Data1T/mnote/design/08-wolai-aline-test-flow/reference/wolai-aline-test-flow-v1.md`。
|
||||
|
||||
## 1. 使用方式
|
||||
|
||||
|
||||
Reference in New Issue
Block a user