feat: align local-first workspace direction
Document the VSCode-like local-first product shape, demote Convex to a control-plane role, and retire stale architecture drafts. Add local workspace migration/export references plus smoke coverage for no-Convex managed workspace startup, local markdown title/body/options persistence, asset upload behavior, and Convex fixture export. Verification: git diff --cached --check; node scripts/check-local-first-convex-guard.js --staged; node scripts/task444-convex-workspace-export-local-fixture-smoke.js; node scripts/task166-local-first-managed-workspace-no-convex-smoke.js; node scripts/task167-local-markdown-title-body-options-no-convex-smoke.js
This commit is contained in:
@@ -1,6 +1,13 @@
|
||||
# 01-05 当前主线与优先级总览
|
||||
|
||||
> 更新时间:2026-05-09
|
||||
> 更新时间:2026-05-19
|
||||
>
|
||||
> 2026-05-18 口径补充:
|
||||
> - 产品形态已调整为 `Local-first workspace`:本地文件夹是默认数据真相,Rust kernel 是唯一语义真相,Convex / 服务端降级为账号、分享、同步、协作和 AI 隔离控制面。
|
||||
> - 产品定义可进一步收口为:`VSCode 简化版工作区内核 + tiptap markdown 编辑器 + Hermes / Reasonix agent + simplemindmap / office 插件 + Wolai 风格 web 壳 + 鉴权控制面`。
|
||||
> - 上位设计已完成并迁入:`/mnt/Data1T/mnote/design/02-convex-rust-long-term-architecture/done/2-2-local-first-workspace-convex-control-plane-v1.md`。
|
||||
> - 本地 Markdown 图片与附件上传已新增本地 route 并避免 Convex media asset,见 `bugs/03-rust-web/done/3-16-local-markdown-upload-uses-convex-media-asset-v1.md`;真实浏览器上传 / 刷新 / 文件树可见 smoke 已由 `scripts/task443-local-markdown-asset-upload-smoke.js` 覆盖。
|
||||
> - 旧的“Convex 作为默认自托管存储 / 实时 / 文件底座”口径只作为当前代码过渡态理解,不再作为新增能力默认方向。
|
||||
>
|
||||
> 2026-05-16 口径补充:
|
||||
> - `5-4` 的默认页面内 `leptos-tiptap` island 主链切流已完成并迁入 `done/`;官方模板视觉和菜单细节继续由 `5-2 / 5-7 / 5-9` 承接。
|
||||
@@ -16,22 +23,39 @@
|
||||
下面三份仍然是当前架构判断的上位依据:
|
||||
|
||||
- `/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/process/2-tree-first-graph-convex-rust-long-term-architecture-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/03-rust-web/process/3-rust-web-long-term-architecture-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` 已降级为 `[recycle][legacy]` 历史过渡判断,只解释“不要无计划硬拆 Convex”,不再作为新增能力默认存储口径。
|
||||
|
||||
它们分别固定了四件事:
|
||||
|
||||
1. 长期事实源是 `tree-first graph kernel`
|
||||
2. `Convex` 保留为当前自托管存储 / 实时底座,不先拆
|
||||
3. `mnote-web` 是 Rust Web 承载层,长期继续承担 transport、projection 分发与切流
|
||||
2. 本地文件夹是早期产品默认数据真相,Convex / 服务端降级为控制面与可选同步协作层
|
||||
3. 旧 Convex 主存储链只作为当前代码过渡态,不再作为新增能力默认方向
|
||||
4. `mnote-web` 是 Rust Web 承载层,长期继续承担 transport、projection 分发与切流
|
||||
|
||||
## 2. 当前第一优先级
|
||||
|
||||
当前最优先的不是继续扩 UI,也不是继续大规模重写执行面,而是把页面域和树域的真相边界先收口。
|
||||
当前最优先的不是继续扩 UI,也不是继续大规模重写执行面,而是把页面域和树域的真相边界收口到本地优先工作区。
|
||||
|
||||
### 2.0 Local-First Workspace
|
||||
|
||||
当前第一优先级固定为:
|
||||
|
||||
- `/mnt/Data1T/mnote/design/02-convex-rust-long-term-architecture/done/2-2-local-first-workspace-convex-control-plane-v1.md`
|
||||
|
||||
原因:
|
||||
|
||||
- 前期产品是 10 人以内、个人笔记默认独立、可分享、AI 隔离的知识库,不是默认云协作平台
|
||||
- 本地 markdown 文件更适合作为 AI 编辑、上传下载、备份、git 管理和调试的默认数据形态
|
||||
- Convex 配置、schema、functions、实时订阅和 storage 不应继续阻塞本地个人工作区主路径
|
||||
- 迁移重点是让 LocalFS 成为默认 `WorkspaceSource`,Convex 保留为账号、分享、同步和协作控制面
|
||||
- 本地 Markdown 页面内上传已完成后端、HTTP 和真实浏览器 smoke 基线;下一步重点转为管理员目录授权、冲突合并 UI、agent diff 审计、本地索引和分享 / 同步闭环
|
||||
|
||||
### 2.1 Page Aggregate
|
||||
|
||||
当前第一优先级固定为:
|
||||
当前第二优先级固定为:
|
||||
|
||||
- `/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`
|
||||
@@ -47,7 +71,7 @@
|
||||
|
||||
### 2.2 Tree Command Cutover
|
||||
|
||||
当前第二优先级固定为:
|
||||
当前第三优先级固定为:
|
||||
|
||||
- `/mnt/Data1T/mnote/design/04-tree-domain/done/4-6-tree-command-protocol-cutover-stage2-v1.md`
|
||||
|
||||
@@ -59,7 +83,7 @@
|
||||
|
||||
### 2.3 Tree Realtime 主链
|
||||
|
||||
当前第三优先级固定为:
|
||||
当前第四优先级固定为:
|
||||
|
||||
- `/mnt/Data1T/mnote/design/03-rust-web/process/3-3-rust-web-tree-realtime-event-stream-v1.md`
|
||||
|
||||
@@ -103,18 +127,37 @@
|
||||
- 文档中的事实基线已经落后于当前默认主编辑器、page aggregate 入口与 island 主链
|
||||
- 继续保留在活跃 `process` 容易让后续工作按旧阶段推进
|
||||
|
||||
## 5. 当前执行顺序
|
||||
## 5. VSCode-like 产品缺口
|
||||
|
||||
`2-2` 已完成 local-first 骨架与迁移 checklist,后续新增能力应优先补齐这些产品闭环:
|
||||
|
||||
1. 管理员目录授权 UI / API:管理用户可读写目录、共享目录、只读/可写权限和 AI 目录权限。
|
||||
2. VSCode-like 冲突处理 UI:接受磁盘版本、保留当前编辑器版本、打开 diff / 合并。
|
||||
3. agent 写入审计:回收 changed files、diff summary、actor、workspace root、permission level、run id。
|
||||
4. 本地索引:全文搜索、反链、页面引用、资源引用、标签,不依赖 Convex search 才能搜索本地 workspace。
|
||||
5. 分享与同步闭环:share grants、shared workspace cache、shared AI session、离线权限收敛和同步冲突报告。
|
||||
6. 插件资源模型:simplemindmap / office 作为 Resource Tree 对象打开和保存,Markdown 只保留链接或嵌入引用。
|
||||
7. 旧 Convex 数据迁移产品化:真实导出入口、迁移进度、备份、冲突报告和回滚策略。
|
||||
|
||||
## 6. 当前执行顺序
|
||||
|
||||
当前推荐顺序固定为:
|
||||
|
||||
1. `Page Aggregate`
|
||||
2. `Tree Command Cutover Stage 2`
|
||||
3. `Tree Realtime Event Stream`
|
||||
4. 树域产品交互合同补齐
|
||||
5. 编辑器官方模板行为对齐
|
||||
1. 管理员目录授权 UI / API
|
||||
2. VSCode-like 冲突处理 UI
|
||||
3. agent changed files / diff 审计
|
||||
4. 本地搜索、反链和资源引用索引
|
||||
5. 分享与同步闭环
|
||||
6. 插件资源模型产品化
|
||||
7. 旧 Convex 数据迁移产品化
|
||||
8. Page Aggregate / tree command / realtime 兼容链继续瘦身
|
||||
|
||||
## 6. 一句话收口
|
||||
持续推进 checklist:
|
||||
|
||||
- `/mnt/Data1T/mnote/design/01-tree-first-graph-kernel/process/1-3-current-priority-execution-checklist-v1.md`
|
||||
|
||||
## 7. 一句话收口
|
||||
|
||||
当前 `design/01-05` 的真实主线,不是“继续证明 Rust Web 值不值得做”,也不是“继续证明 `leptos-tiptap` 能不能用”,而是:
|
||||
|
||||
> **先把页面域和树域收口到 Rust 主导的单一真源,再推进 tree realtime 和树域产品执行面。**
|
||||
> **先把页面域、树域、上传下载和 AI 编辑收口到本地优先工作区,由 Rust kernel 持有唯一语义,再把 Convex 降级为账号、分享、同步和协作控制面。**
|
||||
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
# 1-2 [process] Tree-First Graph 产品愿景进展评估 v1
|
||||
|
||||
> 更新时间:2026-05-19
|
||||
>
|
||||
> **目的**:对照原始产品愿景(Wolai 编辑体验 + VSCode 文件树 + simple-mindmap + OnlyOffice + AI 操作一切),评估当前代码在 1.0 MVP 后的实际进展和剩余的缺口。
|
||||
>
|
||||
> 关联:
|
||||
> - `design/01-05-current-priority-overview.md`
|
||||
> - `design/10-review/done/11-current-full-architecture-review-v1.md`
|
||||
> - `CURRENT_ARCHITECTURE.md`
|
||||
> - `design/11-wolai/help-center-reference.md` — Wolai 帮助中心功能对照基准
|
||||
|
||||
## 1. 一句话结论
|
||||
|
||||
核心链路已经全部立住,但 AI 对思维导图和 Office 文件的操作是明确的功能缺口,其余模块处于"已可用、收口中"状态。
|
||||
|
||||
---
|
||||
|
||||
## 2. 各模块状态
|
||||
|
||||
### 2.1 Wolai 编辑体验 + UI — ✅ 收口中
|
||||
|
||||
#### 架构层
|
||||
|
||||
| 子能力 | 状态 | 关键证据 |
|
||||
|--------|------|----------|
|
||||
| 默认编辑器已切到 leptos-tiptap island | DONE | `design/05-editor-mainline/done/5-4-leptos-tiptap-mainline-correction-v1.md` |
|
||||
| Page Aggregate 读链已切到 Rust projection | DONE | `design/05-editor-mainline/done/5-5-1-page-aggregate-contract-v1.md` |
|
||||
| 文档页入口优先消费 Rust page-aggregate 快照 | DONE | `CURRENT_ARCHITECTURE.md` §2.5 |
|
||||
| 前端壳已切换到 Rust SSR(`:3000`)| DONE | `CURRENT_ARCHITECTURE.md` §3.1 |
|
||||
| Page Aggregate 写链(标题/正文/设置)smoke 通过 | DONE | `scripts/task110-*`, `task-page-aggregate-*-smoke.js` |
|
||||
| 页面设置运行时语义 / 页头回流 / AI 设置面未完全闭环 | PROCESS | `design/05-editor-mainline/process/5-5-page-aggregate-single-truth-alignment-v1.md` |
|
||||
| Wolai UI 行为持续对齐 | PROCESS | `design/05-editor-mainline/process/5-9-wolai-aline-continuous-checklist-v1.md` |
|
||||
| `documents.content` 仍是兼容后备而非唯一真源 | PARTIAL | `CURRENT_ARCHITECTURE.md` §3.2 |
|
||||
|
||||
#### 基础块类型覆盖(对照 Wolai 帮助中心)
|
||||
|
||||
| 块类型 | Wolai | mnote | 备注 |
|
||||
|--------|-------|-------|------|
|
||||
| 文本 | ✅ | ✅ | |
|
||||
| 标题 H1/H2/H3 | ✅ | ✅ | |
|
||||
| 无序列表 | ✅ | ✅ | |
|
||||
| 有序列表 | ✅ | ✅ | |
|
||||
| 待办列表(checkbox)| ✅ | ✅ | |
|
||||
| **折叠列表(toggle)** | ✅ | ❌ | 缺口 |
|
||||
| 页面块(subpage)| ✅ | Partial | Tiptap 嵌入页 |
|
||||
| 引述文字(blockquote)| ✅ | ✅ | |
|
||||
| **着重文字(callout)** | ✅ | ❌ | 缺口 |
|
||||
| 代码片段 | ✅ | ✅ | |
|
||||
| 数学公式 | ✅ | ✅ | LaTeX |
|
||||
| 分割线 | ✅ | ✅ | |
|
||||
|
||||
#### 进阶块类型覆盖(仅基础级引用)
|
||||
|
||||
| 块类型 | Wolai | mnote | 备注 |
|
||||
|--------|-------|-------|------|
|
||||
| 简单表格 | ✅ | ✅ | Tiptap table |
|
||||
| **数据表格(database)** | ✅ | ❌ | 缺口,类似 Notion DB |
|
||||
| 统计图表 | ✅ | ❌ | |
|
||||
| Mermaid 绘图 | ✅ | ❌ | |
|
||||
| 思维导图 | ✅ | ✅ | Phase 6 leptos-mindmap |
|
||||
| 页面目录(TOC)| ✅ | ❌ | |
|
||||
| 网址书签 | ✅ | ✅ | |
|
||||
| 嵌入第三方应用 | ✅ | Partial | OnlyOffice 已接,其他待定 |
|
||||
| 文件附件 | ✅ | ✅ | OnlyOffice adapter |
|
||||
| 按钮 / 进度条 / 仪表数字 | ✅ | ❌ | 基础缺口 |
|
||||
|
||||
#### 基础操作覆盖
|
||||
|
||||
| 操作 | Wolai | mnote | 备注 |
|
||||
|------|-------|-------|------|
|
||||
| 基本编辑(输入/换行/拖拽/缩进)| ✅ | ✅ | |
|
||||
| `/` 快捷命令菜单 | ✅ | ✅ | Tiptap slash command |
|
||||
| **模板创建页面** | ✅ | ❌ | 缺口 |
|
||||
| 页面选项/设置 | ✅ | ✅ | Page Aggregate 写链已通 |
|
||||
| **页面关系图** | ✅ | ❌ | 缺口 |
|
||||
| 全局搜索(Ctrl+P)| ✅ | ✅ | Search modal smoke 通过 |
|
||||
| **导入/导出** | ✅ | ❌ | 缺口(Local/Cloud 桥接仅 P3) |
|
||||
| 垃圾桶 | ✅ | ✅ | `tree.resource.*` 实现 |
|
||||
| **回收站恢复位置+reveal** | ✅ | ✅ | 已实现(`design/04-tree-domain/process/4-28`) |
|
||||
| 个人设置(主题/语言)| ✅ | Partial | 暗色模式有,设置 UI 不全 |
|
||||
| 图标/表情选择器 | ✅ | Partial | |
|
||||
| **快捷方式/星标栏** | ✅ | ❌ | 缺口 |
|
||||
|
||||
完整对照表见 `design/11-wolai/help-center-reference.md`。
|
||||
|
||||
### 2.2 VSCode 文件树 — ✅ 大部完成
|
||||
|
||||
| 子能力 | 状态 | 关键证据 |
|
||||
|--------|------|----------|
|
||||
| VSCode Explorer 对标设计 | DONE | `design/04-tree-domain/done/4-20-vscode-explorer-file-tree-alignment-v1.md` |
|
||||
| 三层模型(Resource → File → Page Tree)固定 | DONE | `design/04-tree-domain/done/4-24-resource-tree-filetree-pagetree-source-contract-checklist-v1.md` |
|
||||
| `tree.*` 命令切流(Stage 2)| DONE | `design/04-tree-domain/done/4-6-tree-command-protocol-cutover-stage2-v1.md` |
|
||||
| 文件树资源行与页面命令分离 | DONE | `bugs/04-tree-domain/done/4-46-filetree-resource-row-document-command-leak-v1.md` |
|
||||
| 垃圾箱恢复位置记录 + reveal/focus | DONE | `design/04-tree-domain/process/4-28-trash-restore-location-reveal-focus-v1.md` §2 |
|
||||
| 资源生命周期命令 `tree.resource.*`(mindmap/table rename 未完成)| PARTIAL | `design/04-tree-domain/process/4-27-resource-lifecycle-command-cutover-v1.md` |
|
||||
| 双浏览器 no-refresh(页面 + 文件 + 垃圾箱实时同步)| PROCESS | `design/04-tree-domain/process/4-34-filetree-trash-dual-browser-no-refresh-v1.md` |
|
||||
| Local/Cloud 桥接 | P3 CANDIDATE | `design/04-tree-domain/process/4-23-local-cloud-explicit-bridge-p3-candidate-v1.md` |
|
||||
|
||||
### 2.3 Simple-mindmap — ✅ 编辑器完成,AI 未覆盖
|
||||
|
||||
| 子能力 | 状态 | 关键证据 |
|
||||
|--------|------|----------|
|
||||
| Phase 6 口径修正(Rust kernel + simple-mind-map adapter)| DONE | `design/06-mindmap/process/6-mindmap-kernel-phase6-projection-editor-v1.md` §2 |
|
||||
| Leptos UI shell | DONE | git `c64487c6` |
|
||||
| 即时显示修复 | DONE | git `3be102a4` |
|
||||
| Filetree 切换稳定 | DONE | git `b300562a` |
|
||||
| KMind 级详细验收 | DONE | `design/06-mindmap/done/6-mindmap-phase6-kmind-parity-detail-checklist-v1.md` |
|
||||
| **AI 读写思维导图(增删改节点)** | **GAP** | Hermes manifest 无 `mnote.mindmap.*` 工具 |
|
||||
|
||||
### 2.4 OnlyOffice — ✅ 打开/链路完成,AI 未覆盖
|
||||
|
||||
| 子能力 | 状态 | 关键证据 |
|
||||
|--------|------|----------|
|
||||
| adapter-onlyoffice crate | DONE | `rust/crates/adapter-onlyoffice/` |
|
||||
| Rust 路由 | DONE | `rust/crates/mnote-web/src/routes/onlyoffice.rs` |
|
||||
| Docker 部署 | DONE | `infra/onlyoffice/docker-compose.yml` |
|
||||
| 附件打开链路修复 | DONE | git `7bd0c9e9` |
|
||||
| Kernel 工具(session/签名/proxy/callback/forcesave)| DONE | `core-protocol/src/tool.rs` `ONLYOFFICE_TOOL_*` |
|
||||
| **AI 读写 Office 文件内容(.docx/.xlsx/.pptx)** | **GAP** | 无 `mnote.office.*` Hermes 工具 |
|
||||
|
||||
### 2.5 AI 操作文档 — ✅ 主线已完成
|
||||
|
||||
| 子能力 | 状态 | 关键证据 |
|
||||
|--------|------|----------|
|
||||
| 在线文档 markdown 级读写 | DONE | `mnote.doc.markdown_edit` + `mnote.doc.fetch`,smoke 验证通过 |
|
||||
| 本地 `.md` 文件读/写 | DONE/PARTIAL | `mnote.doc.fetch` 已支持本地文件,写侧仍在过渡(`design/03-rust-web/process/3-13`)|
|
||||
| 结构性块操作(移动/插入/删除)| DONE | `mnote.block.*` 工具群,Hermes 完整 smoke |
|
||||
| 工具权限 / dryRun / 幂等 / revision 约束 | DONE | `bugs/07-ai/done/7-22` ~ `7-30` |
|
||||
| ACP runtime + UI | DONE | `design/07-ai/done/7-25-acp-session-runtime-enhancement-plan-v1.md` |
|
||||
| Phase C(流式 apply / review session)| FROZEN | `design/07-ai/process/7-14` — 当前不实施 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 缺口汇总
|
||||
|
||||
| 缺口 | 分类 | 说明 |
|
||||
|------|------|------|
|
||||
| AI 操作思维导图 | 功能缺失 | Hermes 13 个工具没有 mindmap 操作。AI 无法在导图中增删改节点 |
|
||||
| AI 操作 Office 文件 | 功能缺失 | 无 `mnote.office.*` 工具。OnlyOffice 可手动编辑,但 AI 无法读写 `.docx`/`.xlsx`/`.pptx` 内容 |
|
||||
|
||||
用户确认 "日记" 即当前主编辑区(Tiptap / `.md` 文件),不构成独立缺口。
|
||||
|
||||
---
|
||||
|
||||
## 4. 当前架构收口主线(已完成进度 ~70%)
|
||||
|
||||
```
|
||||
第一优先:Page Aggregate 单一真源 ████████████░░░░░░ 70%
|
||||
└─ EditorBlockDocument 原生落库 ██████░░░░░░░░░░░░ 40%
|
||||
└─ 页面设置运行时闭环 ████████░░░░░░░░░░ 50%
|
||||
└─ AI 写入链走统一 markdown_edit 口径 ██████████████░░░░ 80%
|
||||
|
||||
第二优先:Tree Command Cutover ████████████░░░░░░ 70%
|
||||
└─ tree.resource.* 命令收口 ██████████░░░░░░░░ 60%
|
||||
└─ 资源 rename 补全 ████░░░░░░░░░░░░░░ 30%
|
||||
|
||||
第三优先:Tree Realtime Event Stream ██████████████░░░░ 80%
|
||||
└─ WS 主链已上线 (57ec8322) ████████████████░░ 90%
|
||||
└─ 双浏览器 no-refresh 统一 live cache ██████░░░░░░░░░░░░ 45%
|
||||
└─ SSE fallback / 补偿链减少 ██████████░░░░░░░░ 60%
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 后续方向建议
|
||||
|
||||
1. **继续推进三条架构收口主线**(Page Aggregate → Tree Command → Tree Realtime),这是当前最优先的工程投入点。
|
||||
2. **确认两个功能缺口是否需要本轮填补**:AI 操作思维导图和 Office 文件。如果需要在 MVP 后立即补齐,需要为 `mnote.mindmap.*` 和 `mnote.office.*` 工具做设计;如果可推迟,则当前架构收口完成后再说。
|
||||
3. Local/Cloud 桥接(P3 candidate)和 Phase C AI(流式 apply)属于明确的"下一轮"范围,不在此轮冲刺内。
|
||||
+1152
File diff suppressed because it is too large
Load Diff
+86
@@ -0,0 +1,86 @@
|
||||
# 2-3 [process] Local Workspace Access Control 产品化 checklist v1
|
||||
|
||||
> 创建时间:2026-05-19
|
||||
>
|
||||
> 当前状态:`PROCESS`
|
||||
>
|
||||
> 上位依据:
|
||||
> - `/mnt/Data1T/mnote/design/02-convex-rust-long-term-architecture/done/2-2-local-first-workspace-convex-control-plane-v1.md`
|
||||
> - `/mnt/Data1T/mnote/ARCHITECTURE.md`
|
||||
> - `/mnt/Data1T/mnote/CURRENT_ARCHITECTURE.md`
|
||||
>
|
||||
> 目标:把已存在的 admin / owner / grant 本地目录权限模型产品化,形成管理员可管理、agent 可复用、全入口不绕过的控制面。
|
||||
|
||||
---
|
||||
|
||||
## 1. 当前已具备底座
|
||||
|
||||
- [x] 管理员身份支持 `MNOTE_ADMIN_USER_IDS`。
|
||||
- [x] 目录授权文件默认位置为 `/mnt/Data1T/Mnote_data/control-plane/access-policy.json`。
|
||||
- [x] 本地目录访问已有 admin / owner / grant 三层模型。
|
||||
- [x] 普通用户不能访问未授权目录。
|
||||
- [x] read grant 只能读,write grant 可写。
|
||||
- [x] 读路径使用 `ensure_local_workspace_read_access`,写路径使用 `ensure_local_workspace_access`。
|
||||
|
||||
---
|
||||
|
||||
## 2. 本轮 P0:最小管理员控制面 API
|
||||
|
||||
- [x] A1. 新增 `GET /api/admin/access-policy`。
|
||||
- 目标:管理员查看当前策略、策略文件路径、有效管理员来源和 grant 列表。
|
||||
- 验证:单测覆盖非管理员 403、管理员可读。
|
||||
- 实现:`rust/crates/mnote-web/src/routes/local_folder_source.rs` + `rust/crates/mnote-web/src/routes/mod.rs`。
|
||||
- [x] A2. 新增 `POST /api/admin/access-policy/validate-root`。
|
||||
- 目标:管理员输入 `rootUri` 或 `rootPath` 后返回 canonical path / rootUri,并拒绝不存在或非目录路径。
|
||||
- 验证:单测覆盖合法目录、非法路径。
|
||||
- [x] A3. 新增 `POST /api/admin/access-policy/grants`。
|
||||
- 目标:管理员添加用户目录授权,写入 `access-policy.json`。
|
||||
- 字段:`userId`、`rootUri/rootPath`、`permission=read|write`、`recursive`、`capabilities=[ai|share]`。
|
||||
- 验证:单测覆盖创建 grant、重复 grant 拒绝、非管理员拒绝。
|
||||
- [x] A4. 新增 `DELETE /api/admin/access-policy/grants/{grantId}`。
|
||||
- 目标:管理员删除授权。
|
||||
- 验证:单测覆盖删除后普通用户失去对应访问权。
|
||||
|
||||
---
|
||||
|
||||
## 3. P1:权限覆盖审计
|
||||
|
||||
- [x] B1. 审计 local folder open / default workspace / file open / asset upload。
|
||||
- 结论:default workspace 走 owner manifest;asset upload 走 write access;file open 已从 write access 收口为 read access,并补 `local_file_open_allows_read_grant` / root escape 单测。
|
||||
- [x] B2. 审计 page body write / documents compat save / title / options。
|
||||
- 结论:`/api/page-body/write`、`documents/save`、title、options 在 local source 下统一走 `ensure_local_workspace_access` 后写本地 markdown / metadata。
|
||||
- [x] B3. 审计 tree command / trash / restore / purge。
|
||||
- 结论:tree snapshot 读路径走 read access;create / rename / copy / trash / restore / purge 写路径走 write access;已有 tree command local folder 单测覆盖。
|
||||
- [x] B4. 审计 Hermes / Reasonix run allowed roots 与 AI tools。
|
||||
- 结论:Hermes/Reasonix local ACP session 创建和 run 写入本地 private jsonl 前走 write access;`mnote.doc.fetch` 直接本地文件读取改走 `ensure_local_path_read_access`;`mnote.page.get` 传递 `sourceKind/rootUri` 并校验 `aiAccessScope.allowedResourceIds`。
|
||||
- [x] B5. 审计 shared AI session read/write 不扩大 workspace 权限。
|
||||
- 结论:AI tool 层补 `aiAccessScope.allowedResourceIds` 校验;共享只读场景下 markdown edit 仍禁止写入,验证见 `hermes_tools_markdown_edit_shared_read_is_forbidden`。
|
||||
|
||||
---
|
||||
|
||||
## 4. P2:后续产品化
|
||||
|
||||
- [ ] C1. 管理员 UI:列表、添加、删除、validate root。
|
||||
- [ ] C2. agent changed files / diff 审计落盘。
|
||||
- [ ] C3. 权限策略变更 audit 记录。
|
||||
- [ ] C4. share / ai capability 接入真实执行路径。
|
||||
|
||||
---
|
||||
|
||||
## 5. 本轮验证包
|
||||
|
||||
- [x] `cargo fmt --check --all`
|
||||
- [x] `cargo test -p mnote-web local_access_policy -- --nocapture`
|
||||
- [x] `cargo test -p mnote-web local_workspace_access -- --nocapture`
|
||||
- [x] `cargo test -p mnote-web local_folder -- --nocapture`
|
||||
- [x] `npm run check:local-first-convex-guard`
|
||||
- [x] `git diff --check -- <changed-files>`
|
||||
|
||||
补充验证:
|
||||
|
||||
- [x] `cargo test -p mnote-web local_path_read_access -- --nocapture`
|
||||
- [x] `cargo test -p mnote-web local_file_open -- --nocapture`
|
||||
- [x] `cargo test -p mnote-web hermes_tools_doc_fetch -- --nocapture`
|
||||
- [x] `cargo test -p mnote-web hermes_tools_page_get -- --nocapture`
|
||||
- [x] `cargo test -p mnote-web hermes_client_local_acp -- --nocapture`
|
||||
- [x] `cargo test -p mnote-web hermes_tools_markdown_edit_shared_read_is_forbidden -- --nocapture`
|
||||
@@ -0,0 +1,209 @@
|
||||
# 3-15 [process] 本地 Markdown 图片与附件上传相对路径设计 v1
|
||||
|
||||
> 创建时间:2026-05-18
|
||||
>
|
||||
> 状态:`[process]`(代码、单测和 HTTP smoke 已完成;真实浏览器 smoke 待补)
|
||||
>
|
||||
> 所属主线:`03-rust-web` / `local_folder` / `local markdown`
|
||||
>
|
||||
> 参考:`design/05-editor-mainline/reference-code/vscode`
|
||||
>
|
||||
> 实现与缺陷闭环:`/mnt/Data1T/mnote/bugs/03-rust-web/done/3-16-local-markdown-upload-uses-convex-media-asset-v1.md`
|
||||
|
||||
## 1. 背景
|
||||
|
||||
云端页面的图片与附件上传当前走 `/api/media/upload`,生成 Convex media asset,并在正文中写入 image block 或 OnlyOffice 附件链接。
|
||||
|
||||
本地文件夹模式不同:页面真源是本地 `.md` 文件,资源真源是同一 local root 下的真实文件。若本地 `.md` 页面内的“上传图片/附件”继续走云端 media asset,会导致 Markdown 文件不可迁移、离开 MNote 后链接失效,也会破坏 local folder 的文件系统可理解性。
|
||||
|
||||
当前本地 Markdown 读写已具备基础能力:
|
||||
|
||||
- `local_markdown_parser.rs` 能把非 `.md` 相对链接解析为 `media` 块。
|
||||
- `save_local_markdown_page` 能把 `media` 块写回 `[name](sourcePath)`。
|
||||
- 本地文件树已能显示 local root 下的普通资源文件,并支持资源生命周期。
|
||||
|
||||
缺口是:编辑器上传文件时,还没有把外部文件复制到本地 Markdown 资源目录,并把页面正文写成标准相对 Markdown 链接。
|
||||
|
||||
## 2. VS Code 参考结论
|
||||
|
||||
参考文件:
|
||||
|
||||
- `extensions/markdown-language-features/src/languageFeatures/copyFiles/newFilePathGenerator.ts`
|
||||
- `extensions/markdown-language-features/src/languageFeatures/copyFiles/copyFiles.ts`
|
||||
- `extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts`
|
||||
|
||||
VS Code 的关键做法:
|
||||
|
||||
- 复制文件时先计算目标路径,默认目标在当前 Markdown 文档旁边。
|
||||
- 支持 `markdown.copyFiles.destination` 配置,目标路径可包含 `${documentBaseName}`、`${fileName}` 等变量。
|
||||
- 冲突策略默认递增命名,如 `image.png`、`image-1.png`。
|
||||
- 插入 Markdown 时使用相对当前文档目录的路径。
|
||||
- 图片使用 ``,普通文件使用 `[text](relative/path)`。
|
||||
- 路径包含空格或括号不匹配时用 `<...>` 包裹。
|
||||
|
||||
MNote 不需要先完整复刻 VS Code 的配置系统,但应采用同一底层原则:本地 Markdown 写标准相对路径,资源文件留在 local root 内。
|
||||
|
||||
## 3. 目标
|
||||
|
||||
本阶段目标是给本地 `.md` 页面提供可验证的最小上传链路:
|
||||
|
||||
1. 在 `sourceKind=local_folder` 的文档编辑器中上传图片或附件时,不调用云端 `/api/media/upload`。
|
||||
2. 外部文件复制到当前 `.md` 文件旁的 `{documentBaseName}.assets/` 目录。
|
||||
3. 页面正文写入标准 Markdown 相对路径:
|
||||
- 图片:``
|
||||
- 附件:`[file.docx](README.assets/file.docx)`
|
||||
4. 保存后 `.md` 文件落盘包含相对路径;刷新页面能恢复图片/附件块。
|
||||
5. local file tree 能通过现有 watch / projection 看到新增资源文件。
|
||||
|
||||
非目标:
|
||||
|
||||
- 不做 VS Code 风格可配置 `copyFiles.destination`。
|
||||
- 不自动删除未引用资源文件。
|
||||
- 不把本地附件接入 Convex media asset。
|
||||
- 不在本阶段实现 OnlyOffice 对本地 Office 文件的完整编辑写回;可先作为本地文件链接/附件行打开或下载。
|
||||
|
||||
## 4. 目录与命名规则
|
||||
|
||||
默认目标目录:
|
||||
|
||||
```text
|
||||
<md-dir>/<md-base>.assets/
|
||||
```
|
||||
|
||||
示例:
|
||||
|
||||
```text
|
||||
docs/README.md
|
||||
docs/README.assets/image.png
|
||||
docs/README.assets/spec.docx
|
||||
```
|
||||
|
||||
命名规则:
|
||||
|
||||
- 原始文件名做 UTF-8 保留,但必须去掉路径分隔符和空名。
|
||||
- 若目标已存在且内容未判断相同,使用递增后缀:
|
||||
- `image.png`
|
||||
- `image-1.png`
|
||||
- `image-2.png`
|
||||
- 生成的相对路径使用 `/`,不使用平台分隔符。
|
||||
- 返回给编辑器的 `sourcePath` 必须是相对当前 `.md` 文件所在目录的路径。
|
||||
|
||||
## 5. 安全边界
|
||||
|
||||
所有本地上传必须在 Rust Web 层校验:
|
||||
|
||||
- `rootUri` 必须解析为已存在 local root。
|
||||
- `documentId` 必须解析到 local root 下的 `.md` 文件。
|
||||
- 目标目录和目标文件必须在 local root 内。
|
||||
- 禁止绝对目标路径、`..` 逃逸、空文件名、目录文件名。
|
||||
- 不覆盖已有文件,除非后续有显式配置;本阶段只递增命名。
|
||||
|
||||
## 6. 数据流
|
||||
|
||||
### 6.1 图片上传
|
||||
|
||||
```text
|
||||
编辑器 slash 图片 / 拖入图片
|
||||
→ local markdown asset upload route
|
||||
→ 复制文件到 README.assets/
|
||||
→ 返回 local asset descriptor
|
||||
→ 编辑器插入 image node,src=相对路径
|
||||
→ /api/documents/save?sourceKind=local_folder
|
||||
→ save_local_markdown_page 写 
|
||||
```
|
||||
|
||||
### 6.2 附件上传
|
||||
|
||||
```text
|
||||
编辑器 slash 附件 / 拖入附件
|
||||
→ local markdown asset upload route
|
||||
→ 复制文件到 README.assets/
|
||||
→ 返回 local asset descriptor
|
||||
→ 编辑器插入普通 link mark 或 media block
|
||||
→ /api/documents/save?sourceKind=local_folder
|
||||
→ save_local_markdown_page 写 [name](relative/path)
|
||||
```
|
||||
|
||||
## 7. 接口设计
|
||||
|
||||
新增本地上传入口:
|
||||
|
||||
```http
|
||||
POST /api/local-folder/assets/upload
|
||||
multipart/form-data
|
||||
```
|
||||
|
||||
字段:
|
||||
|
||||
- `file`: 上传文件。
|
||||
- `rootUri`: local folder root URI。
|
||||
- `documentId`: 当前 local markdown document id。
|
||||
- `kind`: `image | attachment`,可由 MIME 推断兜底。
|
||||
|
||||
响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"asset": {
|
||||
"id": "local:asset:docs/README.assets/image.png",
|
||||
"asset_type": "image",
|
||||
"file_name": "image.png",
|
||||
"mime_type": "image/png",
|
||||
"file_size": 1234,
|
||||
"file_url": "README.assets/image.png",
|
||||
"sourcePath": "README.assets/image.png",
|
||||
"document_id": "local-md:docs/README.md",
|
||||
"sourceKind": "local_folder"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
- `file_url` 在本地模式中不是 HTTP URL,而是给 editor 插入的 Markdown 相对路径。
|
||||
- 若前端需要实际预览,可用后续已有 local asset serving route 或补同源读取 route;正文落盘仍保持相对路径。
|
||||
|
||||
## 8. 前端分流
|
||||
|
||||
`uploadFileToMediaAsset` 在上传前判断:
|
||||
|
||||
- 若 `currentSourceKind() === "local_folder"`:调用 `/api/local-folder/assets/upload`。
|
||||
- 否则:继续调用 `/api/media/upload`。
|
||||
|
||||
`insertUploadedAssetIntoEditor` 对 local asset 的处理:
|
||||
|
||||
- 图片:`setImage({ src: sourcePath, alt: file_name, title: file_name })`。
|
||||
- 附件:插入 link,`href=sourcePath`,不生成 `/onlyoffice?...assetId=...`。
|
||||
|
||||
这能让现有 `inlineTextNodes -> styles.link -> save_local_markdown_page` 路径写出普通 Markdown 链接。
|
||||
|
||||
## 9. Checklist
|
||||
|
||||
- [x] 设计稿落入 `design/03-rust-web/process/`。
|
||||
- [x] Rust route 新增 local markdown asset upload handler。
|
||||
- [x] handler 能解析 `rootUri + documentId` 到本地 `.md` 文件路径。
|
||||
- [x] handler 能创建 `{mdBase}.assets/` 并递增命名避免覆盖。
|
||||
- [x] handler 拒绝 `..` 逃逸、空文件名、非 local folder 请求。
|
||||
- [x] 前端上传分流:local folder 走本地 route,云端继续走 `/api/media/upload`。
|
||||
- [x] 前端 local image 插入相对路径 image node。
|
||||
- [x] 前端 local attachment 插入相对路径 link,不生成 OnlyOffice assetId URL。
|
||||
- [x] `save_local_markdown_page` 写回 `` 与 `[name](path)`。
|
||||
- [x] 单测覆盖本地上传目标路径和冲突递增。
|
||||
- [x] 单测覆盖 local markdown 图片/附件 roundtrip。
|
||||
- [x] HTTP smoke 覆盖 `/api/local-folder/assets/upload` 真实 multipart 上传与落盘。
|
||||
- [ ] 浏览器 smoke 覆盖本地 `.md` 页面上传图片/附件、刷新后恢复、文件树可见。
|
||||
|
||||
## 10. 验收命令
|
||||
|
||||
```bash
|
||||
cargo fmt --manifest-path rust/Cargo.toml --all --check
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_markdown -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_folder -- --nocapture
|
||||
```
|
||||
|
||||
浏览器 smoke 后续新增:
|
||||
|
||||
```bash
|
||||
MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task-local-markdown-asset-upload-smoke.js
|
||||
```
|
||||
@@ -1,18 +1,26 @@
|
||||
# 3-3 [process] Rust Web Tree Realtime Event Stream 方案 v1
|
||||
|
||||
> 更新时间:2026-05-17(WS push 迁移后口径更新)
|
||||
> 更新时间:2026-05-18(local-first 口径更新)
|
||||
> 关联新设计稿:`design/03-rust-web/done/3-14-rust-web-tree-realtime-ws-push-v1.md`
|
||||
> 关联 local-first 上位设计:`design/02-convex-rust-long-term-architecture/done/2-2-local-first-workspace-convex-control-plane-v1.md`
|
||||
>
|
||||
> 2026-05-18 口径更新:
|
||||
> - 本文的 Convex realtime substrate 只适用于 `convex_workspace`、同步副本和后续协作场景。
|
||||
> - 当前早期产品默认 source 是 `local_folder`;本地树变化应优先通过 LocalFS watcher / rescan / Rust projection event 进入同一条前端 projection consumer。
|
||||
> - `/api/realtime/ws` 与 `/api/tree/events` 的长期职责是统一 transport;本地工作区的数据真相仍是 LocalFS / WorkspaceSource。
|
||||
>
|
||||
> 关联文档:
|
||||
> - `/mnt/Data1T/mnote/design/02-convex-rust-long-term-architecture/process/2-tree-first-graph-convex-rust-long-term-architecture-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/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. 目标
|
||||
|
||||
这份文档用于固定 Stage C-1 的正式实时链路口径:
|
||||
这份文档用于固定 Stage C-1 的正式实时链路口径。2026-05-18 后,它应按 `WorkspaceSource` 区分底层事件来源:
|
||||
|
||||
- 保留 Convex 作为 realtime substrate
|
||||
- `local_folder`:LocalFS watcher / rescan / command result 是默认事件来源
|
||||
- `convex_workspace`:Convex 作为 realtime substrate
|
||||
- Rust 成为 tree-first graph 的 semantic owner
|
||||
- Rust Web 负责正式页面 transport 与实时事件流
|
||||
- 前端只消费 projection 与 delta,不再消费实验壳真相
|
||||
@@ -26,18 +34,16 @@
|
||||
|
||||
## 2. 职责划分
|
||||
|
||||
### 2.1 Convex substrate
|
||||
### 2.1 Workspace event substrate
|
||||
|
||||
Convex 继续承担:
|
||||
不同 `WorkspaceSource` 使用不同 substrate:
|
||||
|
||||
- 持久化
|
||||
- mutation / query 底座
|
||||
- 实时订阅底座
|
||||
- 文件 / 对象存储协作
|
||||
- `local_folder`:本地文件系统、watcher、rescan、命令执行结果。
|
||||
- `convex_workspace`:Convex 持久化、mutation / query、实时订阅、对象存储协作。
|
||||
|
||||
Convex 在这里是:
|
||||
Convex 在这里是可选云端 / 协作 source 的 substrate,而不是所有工作区的默认 substrate:
|
||||
|
||||
> storage / realtime substrate
|
||||
> source-specific storage / realtime substrate
|
||||
|
||||
而不是页面树语义 owner。
|
||||
|
||||
@@ -100,13 +106,15 @@ Rust Web 负责:
|
||||
|
||||
正式链路建议固定为四层:
|
||||
|
||||
### 4.1 Convex 持久化/订阅底座
|
||||
### 4.1 Source-specific 持久化/订阅底座
|
||||
|
||||
这里负责:
|
||||
|
||||
- 命令落账
|
||||
- 持久化页面树状态
|
||||
- 输出 mutation 后可订阅的数据变化
|
||||
- 持久化页面树状态或本地文件状态
|
||||
- 输出 command / watcher / mutation 后可订阅的数据变化
|
||||
|
||||
对于 local-first 默认路径,这层是 LocalFS watcher / rescan / command result;对于云端、同步和协作路径,这层才是 Convex。
|
||||
|
||||
### 4.2 Rust kernel 语义 owner
|
||||
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
# 5-5 [process] 主编辑区与树域单一真源对齐方案 v1
|
||||
|
||||
> 更新时间:2026-05-09
|
||||
> 更新时间:2026-05-18
|
||||
>
|
||||
> 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`
|
||||
@@ -34,7 +42,7 @@
|
||||
|
||||
它现在更接近于:
|
||||
|
||||
> **`Rust snapshot 主读链 + Convex-backed + 前端本地 state 仍存在` 的混合态。**
|
||||
> **`Rust snapshot 主读链 + local-first / Convex-backed 双 source 过渡 + 前端本地 state 仍存在` 的混合态。**
|
||||
|
||||
因此,当前看到的这些“小问题”:
|
||||
|
||||
@@ -79,18 +87,22 @@
|
||||
|
||||
- 要不要 `Tiptap`
|
||||
- 要不要 `leptos-tiptap`
|
||||
- 要不要继续保留 Convex
|
||||
- 要不要继续保留 Convex 作为控制面和可选同步协作 source
|
||||
|
||||
这些结论都已经足够明确:
|
||||
|
||||
- `Tiptap` 继续作为浏览器输入 runtime
|
||||
- `leptos-tiptap` 继续作为 Leptos 内正式 editor island 接缝
|
||||
- `Convex` 继续作为当前存储 / 实时 / 协作底座
|
||||
- `local_folder` 作为默认页面数据真相,Convex / 服务端退居控制面和可选同步协作 source
|
||||
|
||||
当前真正要收口的是:
|
||||
|
||||
> **页面这一层,到底由谁持有聚合语义,前端到底应该消费什么,编辑器到底应该回发什么。**
|
||||
|
||||
local-first 后,答案进一步收紧为:
|
||||
|
||||
> **正文真相由本地 Markdown 文件持有;Page Aggregate 负责把文件投影成 UI 和 editor runtime 所需结构;写侧必须围绕文件版本做仲裁,而不是继续让 `documents/save` 兼容面决定谁的 revision 有效。**
|
||||
|
||||
### 2.3 后续主线应固定为 Page Aggregate,而不是继续零散补洞
|
||||
|
||||
从长期架构看,当前主线不应再描述成:
|
||||
@@ -238,6 +250,41 @@
|
||||
|
||||
> **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. 为什么这些问题会直接影响长期方向
|
||||
@@ -387,11 +434,12 @@ Rust 侧需要提供一个统一的页面聚合投影,至少包含:
|
||||
|
||||
### 5.5 Convex 的正确定位
|
||||
|
||||
当前不应该先拆 Convex。
|
||||
当前不应该把 Convex 当作默认页面主数据层继续扩写,也不应该无计划硬拆已有 Convex 路径。
|
||||
|
||||
正确口径是:
|
||||
|
||||
- Convex 继续作为存储 / 实时 / 协作底座
|
||||
- local folder 是早期产品默认页面数据真相
|
||||
- Convex / 服务端继续作为账号、分享、同步、协作和远端副本控制面
|
||||
- Rust 持有 canonical contract 与语义编排
|
||||
- Leptos / Next 负责消费 projection 与呈现 island
|
||||
|
||||
@@ -401,7 +449,7 @@ Rust 侧需要提供一个统一的页面聚合投影,至少包含:
|
||||
|
||||
而是:
|
||||
|
||||
- **Rust 持有语义单一真源,Convex 持有当前持久化底座。**
|
||||
- **Rust 持有语义单一真源,local-first workspace 持有默认数据真相,Convex 只作为控制面和可选同步协作 source。**
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
# 5-6 [process] Page Aggregate 单一真源对齐执行清单 v1
|
||||
|
||||
> 更新时间:2026-05-09
|
||||
> 更新时间:2026-05-18
|
||||
>
|
||||
> 2026-05-18 口径补充:
|
||||
> - 本清单继续有效,但 Page Aggregate 的默认落点调整为 local-first workspace;本地 `.md`、`.mnote/page-options.json` / frontmatter 和 `{mdBase}.assets/` 是早期产品默认闭环。
|
||||
> - Convex-backed Page Aggregate 只作为兼容 / cloud source 路径,不再作为新增能力默认主存储目标。
|
||||
>
|
||||
> 2026-05-19 口径补充:
|
||||
> - local-first 下 `.md` 文件是正文真相,`EditorBlockDocument` 是 runtime-native projection / cache,不是第二份正文真相。
|
||||
> - 写侧必须退出 `/api/documents/save` 长期主路径,收口到带 `expectedFileVersion` 的 `page.body.write` / LocalFS executor。
|
||||
> - AI 直改文件、tiptap autosave、外部编辑器修改必须共用 VSCode-like 文件版本冲突模型。
|
||||
>
|
||||
> 关联文档:
|
||||
> - `/mnt/Data1T/mnote/design/05-editor-mainline/process/5-5-page-aggregate-single-truth-alignment-v1.md`
|
||||
@@ -34,13 +43,16 @@
|
||||
- [x] 正文保存已经能按 `workspaceId/documentId` 正确落到对应页面。
|
||||
- [x] 当前主编辑区已具备可继续推进的基础交互能力。
|
||||
- [x] 当前问题已经不再是“能不能接入主编辑器”,而是“接入后如何收口为单一真源”。
|
||||
- [x] local-first 正文真相已明确为 `.md` 文件;tiptap / EditorBlockDocument / Page Aggregate 均不再被描述为独立正文真相。
|
||||
|
||||
### 2.2 还没有成立的事实
|
||||
|
||||
- [ ] 页面树 / 文件树 / 页面头部 / 页面设置 / 主编辑区还没有消费同一份 page aggregate projection。
|
||||
- [x] 标题 / 正文 / 页面设置已经开始统一到同一组 page aggregate command family。
|
||||
- [ ] 写侧还没有完全退出 `/api/documents/save` 兼容面;`page.body.write` / LocalFS executor 与文件版本冲突模型仍待落地。
|
||||
- [ ] tiptap dirty 状态与 AI / 外部文件变更之间还没有完整 VSCode-like conflict UI。
|
||||
- [ ] `pageOptions` 还没有整体收口到 `leptos-tiptap` island 的正式运行时语义层。
|
||||
- [ ] AI 写入口还没有完整对齐 page aggregate command family。
|
||||
- [ ] AI 写入口还没有完整对齐“授权文件引用 + 白名单目录 + 后台文件写入 + 前台同步”模型。
|
||||
|
||||
补充:本轮已新增前端统一 `page-command-client`,并把 `DocumentContent` 的标题 / 页面设置写入、AI 正文写回、以及 `BlockNote` / `leptos-tiptap` 各 host 的正文保存统一到 `page.head.updateTitle / page.layout.updateOptions / page.body.save`。同时,Next route 侧已新增统一 `page-write-command-adapter`,`/api/documents/title`、`/api/documents/options`、`/api/documents/save` 三条页面写链路已开始共用同一层执行器。这里勾选的是“命令执行面开始收口”,不等于页面域真相链已经完全统一。
|
||||
|
||||
@@ -58,6 +70,8 @@
|
||||
- [ ] 不先扩一批和 page aggregate 无关的编辑器花活。
|
||||
- [ ] 不先争论替换 `Tiptap`、替换 `leptos-tiptap`、移除 Convex。
|
||||
|
||||
补充:当前也不打算把 `documents/save` 继续扩成正文真相主入口。local-first 下它最多是 compat adapter;长期写侧必须按本地文件版本号仲裁,像 VSCode 一样处理 dirty / clean / external change 冲突。
|
||||
|
||||
---
|
||||
|
||||
## 4. Phase F:冻结 Page Aggregate Contract
|
||||
@@ -87,6 +101,12 @@
|
||||
|
||||
补充:2026-05-16 在 `http://127.0.0.1:3000` 的 Rust `mnote-web` 主入口实跑 Page Aggregate smoke。`task110-page-title-single-truth-smoke.js` 验证标题修改后页头、Breadcrumb、Sidebar、Page Tree、File Tree 与刷新后标题一致,证据为 `tmp/page-aggregate-single-truth-smoke/20260516-182244/task110.stdout.json`。文档打开 smoke 验证文档 HTML 包含 `data-page-aggregate-snapshot="mnote.page_aggregate.v1"` 与 `data-page-tree-source="page_aggregate.tree.pageSubtree"`,同一临时页 `/api/page-aggregate/:id` 回读 `schema=mnote.page_aggregate.v1`,证据为 `tmp/page-aggregate-single-truth-smoke/20260516-182244/page-open-snapshot.stdout.json` 与 `page-open-snapshot.png`。字段完整性 smoke 验证 `identity/head/body/tree/stats` 全部存在,证据为 `tmp/page-aggregate-single-truth-smoke/20260516-182244/page-aggregate-fields.stdout.json`。当前 `projectionSource=documents.content`,仍符合“Rust-first 读取链、非 kernel-native 落库完成态”的过渡口径。
|
||||
|
||||
补充:2026-05-18 继续推进 Page Aggregate 读侧块文档优先级。`build_page_aggregate_projection_result` 现在按 `editorDocument -> blockDocument -> documents.content` 构建 `body.blockDocument`,并用 `projectionSource` 暴露真实来源;同时 `body.content` 也会按原生来源重建,避免编辑器/session 下游继续从旧 legacy content 初始化。新增 `page_aggregate_get_prefers_editor_document_over_legacy_content`、`page_aggregate_get_projects_body_content_from_block_document_source` 与非法原生块文档防回退测试。验证命令:`cargo test --manifest-path rust/Cargo.toml -p bridge-runtime page_aggregate -- --nocapture`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web page_aggregate -- --nocapture`。这仍不等于 EditorBlockDocument 原生落库完成,`documents:updateContent` 兼容写入与底层 schema 收口继续留在后续阶段。
|
||||
|
||||
补充:2026-05-18 同步推进 `page.body.save` 写侧原生快照保留。`mnote-web` transport 不再在调用 `documents:updateContent` 前剥离 `editorDocument / tiptapDocument`,只剥离 Rust artifact 事件字段。验证命令:`cargo test --manifest-path rust/Cargo.toml -p mnote-web convex_command_args_keeps_editor_runtime_fields_for_page_body_save -- --nocapture`。真实部署时仍需确认当前 Convex substrate 的 schema/mutation 已接受并返回这两个字段。
|
||||
|
||||
补充:2026-05-18 继续收紧 `page.body.save` 原生快照失败语义。`editorDocument` 一旦出现在保存 payload 中就必须可解析;不可解析时返回 validation error,不再静默回退到 `tiptapDocument` 或 legacy `content`。验证命令:`cargo test --manifest-path rust/Cargo.toml -p bridge-runtime documents_save_command_plan -- --nocapture`。
|
||||
|
||||
补充:2026-05-16 继续新增 `scripts/task-page-aggregate-body-sync-smoke.js`,验证真实页面正文编辑后 `/api/documents/save` 与 `/api/page-aggregate/:id` 回读闭环。证据为 `tmp/page-aggregate-body-sync-smoke/mp87mgz7.json` 与 `mp87mgz7.png`:`body.revision` 从 `0` 到 `1`,`body.conflictDetectionKey` 从 `tree_1778927703753_1:0` 到 `tree_1778927703753_1:1`,`body.blockDocument.blocks[0]` 回读到新段落文本和 `revisionRef`。配套命令 `cargo test --manifest-path rust/Cargo.toml -p mnote-web documents_save_route_executes_page_body_save_command` 与 `cargo test --manifest-path rust/Cargo.toml -p bridge-runtime page_aggregate_get_projects_legacy_content_to_block_document` 已通过。
|
||||
|
||||
### 4.3 退出标准
|
||||
@@ -270,11 +290,12 @@
|
||||
|
||||
### 8.3 退出标准
|
||||
|
||||
- [x] AI 写入口已经可以被明确描述为“操作 page aggregate command family”,而不是“绕过系统写编辑器”。
|
||||
- [x] AI 写入口已经可以被明确描述为“必须经过授权 scope 与页面写入仲裁”,而不是“绕过系统写编辑器”。
|
||||
- [ ] local-first 下,普通正文 AI 写入必须进一步落到“授权文件 patch 或带 `expectedFileVersion` 的 `page.body.write`”,不能继续以 `/api/documents/save` 兼容面作为长期验收终点。
|
||||
|
||||
注:历史 `/api/ai-agent/run` 在 `Hermes tool.completed` 后,会优先尝试把 `slash_run / doc_insert_blocks / doc_replace_range` 恢复成 `mnote-web bridge-runtime` 的结构化 `tool_result`,不再只把 Hermes 事件当作薄日志。2026-05-14 起这只保留为过渡证据;新的主线已由 Hermes 发起 mnote plugin tool call,再由 Rust runtime / kernel 返回 tool result。其后:
|
||||
|
||||
- `doc_insert_blocks / doc_replace_range` 继续按 `page.body.save` 语义落到 `/api/documents/save`,再正式回显主编辑区 island。
|
||||
- `doc_insert_blocks / doc_replace_range` 这类历史 AI 写入曾按 `page.body.save` 语义落到 `/api/documents/save`;local-first 新口径下,该链路只作为 compat 证据,后续普通正文写入应改为授权文件 patch 或带 `expectedFileVersion` 的 `page.body.write`。
|
||||
- `slash_run(rename current page)` 会把结构化结果回接到当前页 `DocumentContent` 的同一条标题提交链,并继续广播 `emitDocumentsChanged(documentId)`,因此页头标题与树标题不再靠 AI 面板内部本地状态各自漂移。
|
||||
- 当前 AI 面板已经能消费结构化 `update_page_options` 结果,并把 `pageOptionsPatch` 回接到当前页 `DocumentContent` 的同一条 `patch_page_options + page.layout.updateOptions` 提交链;同时只允许 `runtimeSupport === "wired"` 的字段进入正式写回,避免 planned / ui_only 页面设置混入主链。对应最小回归测试为 `DocumentAiAgentPanel.runtime.test.tsx`、`document-content.test.ts`。
|
||||
- 历史 `mnote-cli host` 能在命中页面设置 patch 时直接执行 `page.layout.updateOptions`,并向前端回放结构化 `tool_call/tool_result` 事件;这意味着“服务端完全没有页面设置结构化写回结果”的状态已经结束。
|
||||
|
||||
+2
-1
@@ -9,7 +9,8 @@
|
||||
> - `/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/process/2-tree-first-graph-convex-rust-long-term-architecture-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`
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
# 5-9 [process] Wolai-aline 连续执行 checklist v1
|
||||
|
||||
> 更新时间:2026-04-30
|
||||
> 更新时间:2026-05-18
|
||||
>
|
||||
> 2026-05-18 口径补充:
|
||||
> - 本 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`。
|
||||
|
||||
|
||||
@@ -1,243 +0,0 @@
|
||||
# 7-5 [done] Hermes client proxy 合同 v1
|
||||
|
||||
> 更新时间:2026-05-14
|
||||
>
|
||||
> 上位依据:`design/07-ai/done/7-3-page-ai-hermes-panel-and-mnote-plugin-v1.md`、`design/07-ai/done/7-4-page-ai-hermes-panel-execution-checklist-v1.md`
|
||||
>
|
||||
> Hermes Web UI 参考:`packages/client/src/api/hermes/chat.ts`、`packages/client/src/api/hermes/sessions.ts`、`packages/server/src/routes/hermes/proxy-handler.ts`、`packages/server/src/services/hermes/chat-run-socket.ts`
|
||||
|
||||
## 1. 边界
|
||||
|
||||
- [x] 浏览器只访问 mnote-web 同源 `/api/hermes/client/*`。
|
||||
- [x] Hermes API key / gateway token 只存在 mnote-web 服务端环境变量。
|
||||
- [x] mnote-web proxy 只做 auth、同源安全、页面上下文注入、trace 注入和错误码标准化。
|
||||
- [x] mnote-web proxy 不保存 Hermes session、message、tool event、usage、model 真相。
|
||||
- [x] `pageContext` 是 run 输入上下文,不是 Hermes session 的长期事实源。
|
||||
- [x] 旧 `/api/ai-agent/run` 在并存期只作为 legacy endpoint,不是新 Hermes 面板主代理。
|
||||
|
||||
## 2. 路由
|
||||
|
||||
### `GET /api/hermes/client/sessions`
|
||||
|
||||
请求:
|
||||
|
||||
```http
|
||||
GET /api/hermes/client/sessions?workspaceId=ws_1&documentId=doc_1&limit=20
|
||||
```
|
||||
|
||||
响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"traceId": "trace_1",
|
||||
"sessions": [
|
||||
{
|
||||
"sessionId": "mnote_doc_1_20260514",
|
||||
"title": "当前页问答",
|
||||
"preview": "请总结当前页面",
|
||||
"messageCount": 2,
|
||||
"toolCallCount": 1,
|
||||
"updatedAt": 1778712000,
|
||||
"model": "hermes-agent"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/hermes/client/sessions`
|
||||
|
||||
请求:
|
||||
|
||||
```json
|
||||
{
|
||||
"workspaceId": "ws_1",
|
||||
"documentId": "doc_1",
|
||||
"traceId": "trace_1",
|
||||
"title": "当前页问答"
|
||||
}
|
||||
```
|
||||
|
||||
响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"sessionId": "mnote_doc_1_trace_1",
|
||||
"traceId": "trace_1",
|
||||
"persistence": "hermes_on_first_run"
|
||||
}
|
||||
```
|
||||
|
||||
说明:Hermes Web UI 参考实现没有独立 session create HTTP route,客户端生成 session id,首次 run 时由 Hermes 持久化。mnote 保留本 route 是为了同源客户端合同稳定,但不得在 mnote 保存聊天历史。
|
||||
|
||||
### `GET /api/hermes/client/sessions/{session_id}`
|
||||
|
||||
请求:
|
||||
|
||||
```http
|
||||
GET /api/hermes/client/sessions/mnote_doc_1_trace_1?workspaceId=ws_1&documentId=doc_1
|
||||
```
|
||||
|
||||
响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"sessionId": "mnote_doc_1_trace_1",
|
||||
"traceId": "trace_1",
|
||||
"messages": [
|
||||
{
|
||||
"messageId": "42",
|
||||
"role": "assistant",
|
||||
"content": "当前页标题是...",
|
||||
"toolCallId": null,
|
||||
"toolName": null,
|
||||
"timestamp": 1778712000,
|
||||
"reasoning": null
|
||||
}
|
||||
],
|
||||
"usage": {
|
||||
"inputTokens": 120,
|
||||
"outputTokens": 30,
|
||||
"totalTokens": 150
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/hermes/client/runs`
|
||||
|
||||
请求:
|
||||
|
||||
```json
|
||||
{
|
||||
"workspaceId": "ws_1",
|
||||
"documentId": "doc_1",
|
||||
"sessionId": "mnote_doc_1_trace_1",
|
||||
"message": "概括当前页面标题和第一段",
|
||||
"model": "hermes-agent",
|
||||
"pageContext": {
|
||||
"title": "项目计划",
|
||||
"outline": [],
|
||||
"pageOptions": { "wideLayout": true }
|
||||
},
|
||||
"selectedBlockId": null,
|
||||
"selectedText": null,
|
||||
"traceId": "trace_1"
|
||||
}
|
||||
```
|
||||
|
||||
响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"sessionId": "mnote_doc_1_trace_1",
|
||||
"runId": "run_123",
|
||||
"messageId": null,
|
||||
"events": [],
|
||||
"traceId": "trace_1"
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/hermes/client/events/{run_id}`
|
||||
|
||||
请求:
|
||||
|
||||
```http
|
||||
GET /api/hermes/client/events/run_123?sessionId=mnote_doc_1_trace_1
|
||||
```
|
||||
|
||||
响应:`text/event-stream`
|
||||
|
||||
```text
|
||||
data: {"event":"message.delta","run_id":"run_123","session_id":"mnote_doc_1_trace_1","delta":"当前页"}
|
||||
|
||||
data: {"event":"run.completed","run_id":"run_123","session_id":"mnote_doc_1_trace_1","output":"当前页...","usage":{"input_tokens":120,"output_tokens":30,"total_tokens":150}}
|
||||
```
|
||||
|
||||
### `POST /api/hermes/client/runs/{run_id}/abort`
|
||||
|
||||
请求:
|
||||
|
||||
```json
|
||||
{
|
||||
"workspaceId": "ws_1",
|
||||
"documentId": "doc_1",
|
||||
"sessionId": "mnote_doc_1_trace_1",
|
||||
"traceId": "trace_1"
|
||||
}
|
||||
```
|
||||
|
||||
响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"runId": "run_123",
|
||||
"sessionId": "mnote_doc_1_trace_1",
|
||||
"traceId": "trace_1",
|
||||
"events": [
|
||||
{ "event": "abort.started", "run_id": "run_123" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/hermes/client/models`
|
||||
|
||||
响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"traceId": "trace_1",
|
||||
"defaultModel": "hermes-agent",
|
||||
"models": [
|
||||
{ "id": "hermes-agent", "label": "Hermes Agent", "provider": "hermes" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/hermes/client/tools`
|
||||
|
||||
响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"traceId": "trace_1",
|
||||
"tools": [
|
||||
{ "name": "mnote.page.get", "scope": "page.read", "schemaVersion": "mnote.hermes_tool.v1" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 3. 错误响应
|
||||
|
||||
统一错误体:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": false,
|
||||
"code": "hermes_client_unconfigured",
|
||||
"message": "Hermes client proxy 未配置 upstream",
|
||||
"traceId": "trace_1",
|
||||
"requestId": "req_1"
|
||||
}
|
||||
```
|
||||
|
||||
稳定错误码:
|
||||
|
||||
- [x] `hermes_client_unauthorized`:未登录或缺少有效 mnote 会话。
|
||||
- [x] `hermes_client_unconfigured`:未配置 `MNOTE_WEB_HERMES_UPSTREAM_URL`。
|
||||
- [x] `hermes_client_bad_request`:请求 JSON 或必要字段错误。
|
||||
- [x] `hermes_client_upstream_unauthorized`:Hermes upstream 拒绝服务端 token。
|
||||
- [x] `hermes_client_upstream_rate_limited`:Hermes upstream 429。
|
||||
- [x] `hermes_client_upstream_unavailable`:Hermes upstream 连接失败或 5xx。
|
||||
|
||||
## 4. 并存期规则
|
||||
|
||||
- [x] 新页面 AI 面板只允许请求 `/api/hermes/client/*`。
|
||||
- [x] `/api/ai-agent/run` 保留为 legacy guard,不再承载 Hermes 页面 AI 主链。
|
||||
- [x] `provider=hermes` 不再通过旧 `/api/ai-agent/run` 表达。
|
||||
- [x] 所有新 smoke 应断言页面 AI 主链没有 `/api/ai-agent/run` 请求。
|
||||
@@ -1,404 +0,0 @@
|
||||
# 7-8 [process] 页面 AI Hermes Runtime BFF 下一阶段设计 v1
|
||||
|
||||
> 更新时间:2026-05-15
|
||||
>
|
||||
> 当前状态:`PROCESS`。本文承接 `7-3`、`7-4`、`7-5`、`7-6`、`7-7` 已完成的 Hermes 页面内客户端主线,写入对 `hermes-web-ui-0.5.18` Chat Runtime 架构的吸收结论。
|
||||
>
|
||||
> 上位依据:
|
||||
> - `/mnt/Data1T/mnote/ARCHITECTURE.md`
|
||||
> - `/mnt/Data1T/mnote/design/01-05-current-priority-overview.md`
|
||||
> - `/mnt/Data1T/mnote/design/03-rust-web/process/3-rust-web-long-term-architecture-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/reference-code/hermes-web-ui-0.5.18`
|
||||
> - `/mnt/Data1T/mnote/design/07-ai/done/7-3-page-ai-hermes-panel-and-mnote-plugin-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/07-ai/done/7-4-page-ai-hermes-panel-execution-checklist-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/07-ai/done/7-5-hermes-client-proxy-contract-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/07-ai/done/7-6-mnote-hermes-plugin-tool-contract-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/07-ai/done/7-7-page-ai-mini-hermes-control-surface-v1.md`
|
||||
|
||||
---
|
||||
|
||||
## 1. 结论
|
||||
|
||||
`hermes-web-ui-0.5.18` 对 mnote 有用,但不能原样照搬。
|
||||
|
||||
可吸收的是:
|
||||
|
||||
- Web 端应采用 `Browser -> BFF -> Hermes gateway`,不是浏览器直接 ACP。
|
||||
- BFF 应承担 runtime 管理:active run registry、resume、queue、abort、event normalization、gateway manager、错误规整。
|
||||
- 前端只展示 message、tool event、状态和输入,不持有 AI 编排真相。
|
||||
|
||||
不可吸收的是:
|
||||
|
||||
- 不把 mnote 的页面 AI 聊天真相改为 Convex、SQLite 或 mnote 自建 chat DB。
|
||||
- 不把 Hermes 降级成 `/v1/responses store=false` 的纯模型执行器。
|
||||
- 不让 mnote-web 根据自有数据库长期组装完整 `conversation_history`。
|
||||
|
||||
当前冻结方向:
|
||||
|
||||
> **mnote 学 `hermes-web-ui` 的 BFF/runtime 架构思想,但会话真相继续归 Hermes;mnote 只保存业务事实、audit、artifact、edge、page/body/title/options 结果。**
|
||||
|
||||
---
|
||||
|
||||
## 2. 当前实现定位
|
||||
|
||||
当前页面 AI 接入是:
|
||||
|
||||
```text
|
||||
Leptos 页面 AI 面板
|
||||
-> mnote-web /api/hermes/client/*
|
||||
-> Hermes gateway /v1/runs
|
||||
-> Hermes gateway /v1/runs/{run_id}/events
|
||||
```
|
||||
|
||||
同时:
|
||||
|
||||
- `profiles / skills / memory` 等设置能力由 `mnote-web` 本地 BFF 通过 Hermes CLI 或 `~/.hermes` 文件读写补齐。
|
||||
- `mnote.*` 工具由 mnote Rust runtime 承接,Hermes 只通过 skill/plugin/tool 调用。
|
||||
- 页面 AI 不再走旧 `/api/ai-agent/run`。
|
||||
|
||||
这已经满足最小闭环,但仍只是“最小 runtime 接入”,缺少 `hermes-web-ui` 已经具备的完整 Chat Runtime 能力。
|
||||
|
||||
---
|
||||
|
||||
## 3. ACP、gateway、responses 的取舍
|
||||
|
||||
### 3.1 ACP
|
||||
|
||||
ACP 适合:
|
||||
|
||||
- VSCode 插件
|
||||
- Tauri / Electron 桌面端
|
||||
- 本地 IDE host
|
||||
- 需要由宿主启动 `hermes acp` 并通过 stdio JSON-RPC 深度交互的场景
|
||||
|
||||
ACP 不适合作为 mnote 页面 AI 第一主链:
|
||||
|
||||
- 浏览器不能直接 `spawn hermes acp`。
|
||||
- 若 Web 使用 ACP,仍需 mnote-web 作为 ACP bridge 管理 stdio 子进程。
|
||||
- 这会引入长期进程、session、backpressure、并发、恢复和崩溃清理复杂度。
|
||||
- 当前 Hermes gateway 已提供 Web 更自然的 `/v1/*` 与 stream 能力。
|
||||
|
||||
结论:
|
||||
|
||||
> **ACP 后续只作为桌面端或高级 host bridge 候选,不作为页面 AI Web 主链。**
|
||||
|
||||
### 3.2 `/v1/runs`
|
||||
|
||||
`/v1/runs` 适合当前 mnote 主线:
|
||||
|
||||
- Hermes 持有 run / session / event 语义。
|
||||
- mnote 页面 AI 只是 Hermes client。
|
||||
- mnote 不需要长期组装 `conversation_history`。
|
||||
- 与“AI 会话归 Hermes,mnote 能力归 Rust kernel”一致。
|
||||
|
||||
风险:
|
||||
|
||||
- mnote-web 需要补 runtime 状态管理,否则刷新、断线、队列、abort 体验弱。
|
||||
- Hermes gateway 的非 `/v1/*` 管理面不足,需要 BFF 补 profiles/skills/memory/gateway manager。
|
||||
|
||||
结论:
|
||||
|
||||
> **短期继续以 `/v1/runs` 为默认页面 AI 主链,并在 mnote-web 上补 Runtime BFF。**
|
||||
|
||||
### 3.3 `/v1/responses`
|
||||
|
||||
`hermes-web-ui` 的产品化 Chat Runtime 使用 `/v1/responses stream=true`,典型形态是:
|
||||
|
||||
```text
|
||||
Browser Vue UI
|
||||
-> Socket.IO /chat-run
|
||||
-> hermes-web-ui server
|
||||
-> /v1/responses stream=true
|
||||
-> server 从本地 DB 组装 conversation_history
|
||||
-> server 把 response.* 事件转成 run/message/tool 事件
|
||||
-> server 写入 SQLite session/messages
|
||||
```
|
||||
|
||||
它适合“自建 Chat Runtime”:
|
||||
|
||||
- 后端自有 session/messages 数据库。
|
||||
- 后端自行压缩上下文。
|
||||
- 后端自行映射 tool event。
|
||||
- `store=false` 时 Hermes 不再是会话真相。
|
||||
|
||||
这与 mnote 当前页面 AI 方向有冲突。除非后续明确改变设计,把“AI 会话真相”从 Hermes 迁到 mnote,否则不能把 `/v1/responses store=false + mnote conversation_history` 写成默认主链。
|
||||
|
||||
允许使用 `/v1/responses` 的条件:
|
||||
|
||||
- 只作为特定 runtime 实验或 fallback。
|
||||
- 必须证明 Hermes session 仍能作为用户可见会话真相,或者另开设计稿明确改变会话真相。
|
||||
- 不得把 mnote page aggregate / Convex 页面数据变成聊天消息数据库。
|
||||
|
||||
结论:
|
||||
|
||||
> **`/v1/responses` 先作为参考实现和可评估候选,不作为当前页面 AI 默认主链。**
|
||||
|
||||
---
|
||||
|
||||
## 4. 目标架构
|
||||
|
||||
下一阶段目标不是改成 ACP,也不是直接改成 `/v1/responses`,而是在当前 gateway 主线上补一个 mnote-web Runtime BFF:
|
||||
|
||||
```text
|
||||
Leptos 页面 AI 面板
|
||||
-> mnote-web Hermes Runtime BFF
|
||||
- active run registry
|
||||
- session/run resume
|
||||
- queue
|
||||
- abort
|
||||
- event normalization
|
||||
- gateway/profile/skill/memory BFF
|
||||
- trace/audit 串联
|
||||
-> Hermes gateway /v1/runs
|
||||
-> Hermes session storage
|
||||
-> mnote skill/plugin/tool
|
||||
-> Rust runtime / kernel
|
||||
```
|
||||
|
||||
真相边界:
|
||||
|
||||
- Hermes:AI session、message、run、tool event、usage、model、profile。
|
||||
- mnote:页面、树、正文、artifact、edge、projection、audit、权限、idempotency。
|
||||
- mnote-web Runtime BFF:运行时连接和事件编排,不保存长期聊天真相。
|
||||
|
||||
---
|
||||
|
||||
## 5. Hermes Web UI 参考索引
|
||||
|
||||
参考根目录:
|
||||
|
||||
```text
|
||||
/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/hermes-web-ui-0.5.18
|
||||
```
|
||||
|
||||
只参考下表,不整站搬运:
|
||||
|
||||
| 参考主题 | 文件 | 搜索点 | mnote 中吸收什么 | mnote 中不采用什么 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| Socket runtime 主链 | `packages/server/src/services/hermes/chat-run-socket.ts` | `ChatRunSocket`、`onConnection`、`resumeSession`、`handleRun`、`dequeueNextQueuedRun`、`handleAbort` | active run registry、resume、queue、abort、前端刷新不丢运行态 | 不照搬 Socket.IO;mnote 可用 SSE / WebSocket / HTTP 状态轮询 |
|
||||
| `/v1/responses` 事件映射 | `packages/server/src/services/hermes/chat-run-socket.ts` | `/v1/responses`、`applyResponseStreamEvent`、`response.output_text.delta`、`response.output_item.done`、`response.completed` | 学习如何把上游事件标准化为 `message.delta`、`tool.started`、`tool.completed`、`run.completed` | 不把 `/v1/responses store=false` 写成默认主链 |
|
||||
| 后端 session store | `packages/server/src/db/hermes/session-store.ts` | `HermesSessionRow`、`HermesMessageRow`、`createSession`、`addMessage`、`getSessionDetail` | 理解 Hermes Web UI 如何建完整 chat DB,以及字段形态 | 不在 mnote 建第二套 chat DB;不把 Convex 当 AI 会话真相 |
|
||||
| 前端 chat store | `packages/client/src/stores/hermes/chat.ts` | `resumeServerWorkingRun`、`queueLengths`、`abortState`、`case 'message.delta'`、`case 'tool.started'`、`case 'run.completed'` | 前端状态机、队列提示、abort UI、tool event 归并 | 不复用 Vue/Pinia;不把前端 state 当真相 |
|
||||
| 前端 API | `packages/client/src/api/hermes/chat.ts` | `startRunViaSocket`、`resumeSession`、`registerSessionHandlers`、`RunEvent` | 客户端事件合同与 runtime event 类型 | 不让浏览器绕过 mnote-web 直连 Hermes |
|
||||
| VSCode ACP tool UI | `design/05-editor-mainline/reference-code/hermes-vscode-main/src/sessionManager.ts`、`src/protocol.ts`、`src/chatPanel.ts`、`src/webview/main.ts` | `tool_call`、`tool_call_update`、`parseToolCall`、`parseToolCallUpdate`、`case 'toolCall'`、`data-tool-id` | 学习按 `toolCallId` 追加工具行、再用后续 update 原地更新状态的 UI 模型 | 不采用 ACP 作为 Web 主链;只参考 tool event UI 状态机 |
|
||||
| gateway 管理 | `packages/server/src/services/hermes/gateway-manager.ts`、`packages/server/src/services/gateway-bootstrap.ts` | `getUpstream`、`start`、`stop`、`health` | profile 对应 gateway 的发现、启动、健康检查、错误提示 | 不让页面 AI 抽屉变成完整 gateway 管理台 |
|
||||
| proxy handler | `packages/server/src/routes/hermes/proxy-handler.ts` | `resolveUpstream`、`SSE_EVENTS_PATH`、`streamSSE`、`/v1/runs` | 路由改写、SSE 拦截、upstream 错误规整 | 不把全量 proxy catch-all 暴露给浏览器 |
|
||||
| skills 来源 | `packages/server/src/controllers/hermes/skills.ts` | `.bundled_manifest`、`.hub/lock.json`、`.usage.json`、`source`、`modified` | skill 来源与生成/安装/本地分类 | 不复制完整 skill 管理台 |
|
||||
|
||||
快速定位命令:
|
||||
|
||||
```bash
|
||||
cd /mnt/Data1T/mnote/design/05-editor-mainline/reference-code/hermes-web-ui-0.5.18
|
||||
rg -n "ChatRunSocket|resumeSession|handleRun|dequeueNextQueuedRun|handleAbort|/v1/responses|applyResponseStreamEvent|response\\.output_text\\.delta|response\\.output_item\\.done|response\\.completed|HermesSessionRow|startRunViaSocket|gateway-manager" packages/server/src packages/client/src
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. RuntimeEvent 合同
|
||||
|
||||
mnote-web BFF 对前端暴露的事件应稳定为 `mnote.hermes_runtime_event.v1`。
|
||||
|
||||
最小事件集:
|
||||
|
||||
```json
|
||||
{ "event": "run.started", "sessionId": "mnote_doc_1_trace_1", "runId": "run_1", "traceId": "trace_1" }
|
||||
```
|
||||
|
||||
```json
|
||||
{ "event": "message.delta", "sessionId": "mnote_doc_1_trace_1", "runId": "run_1", "delta": "当前页", "traceId": "trace_1" }
|
||||
```
|
||||
|
||||
```json
|
||||
{ "event": "tool.started", "sessionId": "mnote_doc_1_trace_1", "runId": "run_1", "toolCallId": "call_1", "toolName": "mnote.page.get", "traceId": "trace_1" }
|
||||
```
|
||||
|
||||
```json
|
||||
{ "event": "tool.completed", "sessionId": "mnote_doc_1_trace_1", "runId": "run_1", "toolCallId": "call_1", "toolName": "mnote.page.get", "summary": "读取当前页面", "auditId": "audit_1", "traceId": "trace_1" }
|
||||
```
|
||||
|
||||
```json
|
||||
{ "event": "run.completed", "sessionId": "mnote_doc_1_trace_1", "runId": "run_1", "output": "总结文本", "usage": { "inputTokens": 0, "outputTokens": 0 }, "traceId": "trace_1" }
|
||||
```
|
||||
|
||||
```json
|
||||
{ "event": "run.failed", "sessionId": "mnote_doc_1_trace_1", "runId": "run_1", "code": "hermes_client_upstream_error", "message": "Hermes upstream 返回错误", "traceId": "trace_1" }
|
||||
```
|
||||
|
||||
```json
|
||||
{ "event": "abort.started", "sessionId": "mnote_doc_1_trace_1", "runId": "run_1", "traceId": "trace_1" }
|
||||
```
|
||||
|
||||
```json
|
||||
{ "event": "abort.completed", "sessionId": "mnote_doc_1_trace_1", "runId": "run_1", "synced": true, "traceId": "trace_1" }
|
||||
```
|
||||
|
||||
如果上游是 `/v1/runs/{run_id}/events`,BFF 只做字段规整;如果后续试验 `/v1/responses`,BFF 负责把 `response.*` 映射为上述 RuntimeEvent。前端不直接依赖上游原始事件格式。
|
||||
|
||||
---
|
||||
|
||||
## 7. 下一阶段 checklist
|
||||
|
||||
### A. Runtime 状态注册表
|
||||
|
||||
- [x] 在 `mnote-web` 建立内存级 `sessionId -> active run state` registry。
|
||||
- [x] 记录 `sessionId`、`runId`、`profile`、`documentId`、`traceId`、`status`、`startedAt`、`lastEventAt`。
|
||||
- [x] registry 只保存运行态和最近事件摘要,不保存完整聊天历史。
|
||||
- [x] 服务重启后允许 registry 丢失,但页面必须能从 Hermes session detail 恢复已完成消息。
|
||||
|
||||
验收标准:
|
||||
|
||||
- [x] 页面刷新时,若 run 仍在 registry 中,前端能看到 `running/tool_calling` 状态。
|
||||
- [x] 关闭页面再打开,不会把 mnote local storage 当作聊天真相。
|
||||
|
||||
### B. Resume
|
||||
|
||||
- [x] 增加 `POST /api/hermes/client/sessions/{session_id}/resume` 或等价同源接口。
|
||||
- [x] 返回 Hermes session detail 中的 messages,并附带 registry 中当前运行态。
|
||||
- [x] 若 session 不在 registry 中,只返回 Hermes 已持久化状态。
|
||||
|
||||
验收标准:
|
||||
|
||||
- [x] run 过程中刷新页面,UI 能恢复 run 状态或明确显示“正在同步 Hermes 状态”。
|
||||
- [x] run 完成后刷新页面,消息来自 Hermes session detail / export,不来自 mnote 自建聊天表。
|
||||
|
||||
验证证据:
|
||||
|
||||
- `cargo test -p mnote-web hermes_client_ -- --nocapture`:通过 9 个 Hermes client 单测,覆盖 registry 不保存 `messages`、tool event 摘要和 `/resume` 返回 runtime。
|
||||
- `MNOTE_UI_BASE_URL=http://127.0.0.1:13000 node scripts/task-hermes-page-ai-smoke.js`:通过。13000 是本轮临时启动的新 `mnote-web`,用于避免打断当前 3000;验证后已关闭。
|
||||
- 当前 3000 运行的是本轮修改前的旧 `mnote-web` 二进制,`POST /api/hermes/client/sessions/{id}/resume` 返回 404;需要重启 3000 后再用同一 smoke 复验主入口。
|
||||
|
||||
### C. Queue
|
||||
|
||||
- [x] 同一 session 已有 active run 时,后续输入进入后端 queue。
|
||||
- [x] queue item 只保存待发送 input、profile、context snapshot 摘要和 trace,不保存长期聊天真相。
|
||||
- [x] 当前 run 结束后自动触发下一条。
|
||||
- [x] 前端显示 queue length 和可取消项。
|
||||
|
||||
验收标准:
|
||||
|
||||
- [x] 连续发送两条消息时,第二条显示 queued,而不是并发打到同一 Hermes session。
|
||||
- [x] 取消 queued item 后,不触发 Hermes run。
|
||||
|
||||
验证证据:
|
||||
|
||||
- `cargo test -p mnote-web hermes_client_ -- --nocapture`:通过 12 个 Hermes client 单测,新增覆盖 active run 时第二条入队、queue item 不返回完整 `messages/pageContext`、后端取消 queued item、终态后用 mock Hermes upstream 自动启动下一条 queued run。
|
||||
- `MNOTE_UI_BASE_URL=http://127.0.0.1:13000 node scripts/task-hermes-page-ai-queue-smoke.js`:通过。验证连续发送两条消息时第二条走 `queued` 响应,UI 显示队列长度,并能从 Runtime 面板取消 queued item。
|
||||
- `MNOTE_UI_BASE_URL=http://127.0.0.1:13000 node scripts/task-hermes-page-ai-smoke.js`:通过。确认 resume / tool / 完成态基础链路未被 queue 改动破坏。
|
||||
- 13000 是本轮临时启动的新 `mnote-web`,验证后已关闭;当前 3000 仍需重启后复验。
|
||||
|
||||
### D. Abort
|
||||
|
||||
- [x] abort 由 BFF 统一执行,调用 Hermes `/v1/runs/{run_id}/stop` 或中断当前 upstream request。
|
||||
- [x] abort 前后发出 `abort.started`、`abort.completed`。
|
||||
- [x] abort 后清理 registry 状态,并处理队列后续项。
|
||||
|
||||
验收标准:
|
||||
|
||||
- [x] 点击停止后,UI 不再继续追加该 run 的 assistant delta。
|
||||
- [x] 若 abort 后有队列项,后端按顺序继续处理或明确要求用户确认。
|
||||
|
||||
验证证据:
|
||||
|
||||
- `cargo test -p mnote-web hermes_client_ -- --nocapture`:通过 13 个 Hermes client 单测,新增覆盖 abort 响应返回 `abort.started/abort.completed`,并在 abort 后继续处理 queued run。
|
||||
- 临时 `mnote-web` 以 `MNOTE_WEB_HERMES_UPSTREAM_URL=http://127.0.0.1:3999` 启动后,执行 `MNOTE_UI_BASE_URL=http://127.0.0.1:13000 node scripts/task-hermes-page-ai-runtime-controls-smoke.js`:通过。验证停止 run 后 UI 状态进入 `aborted`,并且后续 `message.delta` 不再追加到聊天区。
|
||||
- `MNOTE_UI_BASE_URL=http://127.0.0.1:13000 node scripts/task-hermes-page-ai-queue-smoke.js` 与 `MNOTE_UI_BASE_URL=http://127.0.0.1:13000 node scripts/task-hermes-page-ai-smoke.js`:通过。确认 queue / resume 基础链路未被 abort 改动破坏。
|
||||
- 13000 是本轮临时启动的新 `mnote-web`,验证后已关闭;当前 3000 仍需重启后复验。
|
||||
|
||||
### E. Event normalization
|
||||
|
||||
- [x] mnote-web 将 Hermes 上游事件规整为 `mnote.hermes_runtime_event.v1`。
|
||||
- [x] 前端只消费标准事件,不直接分支处理多套 Hermes 原始事件。
|
||||
- [x] tool event 展示独立于 assistant 文本。
|
||||
- [x] 当前 UI 已能在聊天区输出 `工具` 消息并显示工具名,例如 `mnote.page.get`;下一阶段必须升级为按 `toolCallId` 合并的 tool event card。
|
||||
- [x] `tool.started` 创建或更新同一个工具卡,显示工具名、运行中状态、参数摘要。
|
||||
- [x] `tool.completed` 原地更新同一个工具卡,显示完成状态、结果摘要、audit/trace id。
|
||||
- [x] `tool.failed` 原地更新同一个工具卡,显示失败状态、错误码和可复制详情。
|
||||
- [x] Runtime 页的最近 tool call 摘要继续保留,但不能替代聊天区内联工具卡。
|
||||
|
||||
验收标准:
|
||||
|
||||
- [x] `message.delta`、`tool.started`、`tool.completed`、`run.completed`、`run.failed` 均有 smoke 覆盖。
|
||||
- [x] `tool.started -> tool.completed` 不产生两条互不关联的纯文本工具消息;同一 `toolCallId` 在 UI 中表现为同一张工具卡状态变化。
|
||||
- [x] 工具卡至少展示 `toolName`、`toolCallId`、状态、参数摘要、结果摘要、`traceId/auditId`。
|
||||
- [x] tool result 不混入正文编辑器,不写入 page body,除非 Hermes 明确调用 `mnote.page.save` 等写工具。
|
||||
|
||||
验证证据:
|
||||
|
||||
- `MNOTE_UI_BASE_URL=http://127.0.0.1:13000 node scripts/task-hermes-page-ai-smoke.js`:通过。smoke 发送同一 `toolCallId=call_smoke_page_get` 的 `tool.started -> tool.completed`,断言聊天区只有 1 张 completed 工具卡;同时发送 `toolCallId=call_smoke_page_save` 的 `tool.started -> tool.failed`,断言 failed 工具卡显示 `permission_denied`。
|
||||
- `MNOTE_UI_BASE_URL=http://127.0.0.1:13000 node scripts/task-hermes-page-ai-runtime-controls-smoke.js` 与 `MNOTE_UI_BASE_URL=http://127.0.0.1:13000 node scripts/task-hermes-page-ai-queue-smoke.js`:通过。确认工具卡改动未破坏 abort / queue。
|
||||
- `cargo test -p mnote-web hermes_client_ -- --nocapture`:通过 14 个 Hermes client 单测,新增覆盖 `assistant_message`、`tool.failed` 等上游事件被规整为 `mnote.hermes_runtime_event.v1`。
|
||||
- mnote-web `/client/events/{run_id}` 输出 `event: <canonical>` 与 `schemaVersion: "mnote.hermes_runtime_event.v1"`;前端消费路径只处理 `message.delta`、`run.completed`、`run.failed`、`run.aborted` 与 `tool.*` 标准事件名。
|
||||
|
||||
### F. Gateway manager
|
||||
|
||||
- [x] BFF 能检查当前 Hermes gateway health。
|
||||
- [x] BFF 能识别 profile 对应 gateway / model 状态。
|
||||
- [x] 缺 model/default 或 API key 时,错误提示指向 Hermes 设置,而不是只显示 502。
|
||||
|
||||
验收标准:
|
||||
|
||||
- [x] `hermes_client_upstream_error` 场景能显示具体修复建议,例如“当前 profile 缺 model.default”。
|
||||
- [x] profile/skill/settings API 不再错误代理到 Hermes gateway 非 `/v1/*` 路径。
|
||||
|
||||
验证证据:
|
||||
|
||||
- `mnote-web` 新增 `GET /api/hermes/client/gateway/health?profile=<name>`,只做只读探测,不启动/停止 gateway;返回 `gateway.configured/upstream/status/httpStatus`、`profile.modelDefault/provider/apiKeyConfigured` 与 `suggestions`。
|
||||
- `profile_detail_payload()` 与 fallback profiles 会从 Hermes profile `config.yaml` 读取 `model.default`、`model.provider`、provider `api_key/key_env` 摘要,但不返回 API key 原文。
|
||||
- `upstream_error()` 对 `hermes_client_upstream_error` 增加 Hermes 设置修复建议;当上游错误正文包含 `model.default`、`API key`、`profile/provider` 等线索时,错误 message 会指向 Hermes 设置。
|
||||
- Runtime 面板增加 gateway/profile 状态区;页面请求失败时前端显示后端 `message`,不再只显示错误 code。
|
||||
- `cargo test -p mnote-web hermes_client_ -- --nocapture`:通过 17 个 Hermes client 单测,新增覆盖 gateway 未配置、profile 缺 model/API key、`hermes_client_upstream_error` 错误建议。
|
||||
- 临时 `mnote-web` 以 `MNOTE_WEB_HERMES_UPSTREAM_URL=http://127.0.0.1:3999` 启动到 `127.0.0.1:13000` 后,`task-hermes-page-ai-smoke.js`、`task-hermes-page-ai-runtime-controls-smoke.js`、`task-hermes-page-ai-queue-smoke.js` 均通过;验证后已关闭 13000。
|
||||
- `MNOTE_UI_BASE_URL=http://127.0.0.1:13000 node scripts/task-hermes-page-ai-agent-skill-smoke.js`:通过。确认新增 gateway health 请求不影响 agent/profile 选择、SOUL.md 保存、skill toggle 与 run profile 透传。
|
||||
|
||||
### G. `/v1/responses` 评估,不默认切换
|
||||
|
||||
- [x] 增加只读 spike 文档或测试,验证当前 Hermes gateway 的 `/v1/responses stream=true` 事件格式。
|
||||
- [x] 明确 `/v1/responses` 是否能在不迁移会话真相的前提下复用 Hermes session。
|
||||
- [x] 若不能保持 Hermes session 真相,则不得进入默认页面 AI 主链。
|
||||
|
||||
验收标准:
|
||||
|
||||
- [x] 有明确结论:继续 `/v1/runs`,或另开设计稿说明为什么改变会话真相。
|
||||
- [x] 任何 `/v1/responses` 实验都不得把 mnote Convex / page aggregate 变成聊天消息真相。
|
||||
|
||||
只读 spike 结论:
|
||||
|
||||
- 参考 `hermes-web-ui-0.5.18/packages/server/src/services/hermes/chat-run-socket.ts`,其 `/v1/responses` 主链在发送前从后端 DB 组装 `conversation_history`,随后设置 `body.stream = true` 与 `body.store = false`,再请求 `${upstream}/v1/responses`。
|
||||
- 同一文件的 `applyResponseStreamEvent()` 表明 responses 事件格式主要是 `response.created`、`response.output_text.delta`、`response.output_item.added`、`response.output_item.done`、`response.completed`、`response.failed`,由上层 BFF 映射为 `run.started`、`message.delta`、`tool.started`、`tool.completed`、`run.completed`、`run.failed`。
|
||||
- `response.output_item.done` 中 `function_call` 会被映射为 `tool.started`;`function_call_output` 会被映射为 `tool.completed`。这对 mnote 的 tool card 状态机有参考价值,但不改变主链。
|
||||
- 本机只读探测 `http://127.0.0.1:3999/health`、`/v1/models`、`OPTIONS /v1/responses` 时,当前没有 gateway 监听,无法取得 live `/v1/responses stream=true` 事件样本;因此本轮不把 live event capture 写成验收依据。
|
||||
- 基于参考实现,`/v1/responses store=false + conversation_history` 的顺滑体验依赖上层 BFF/SQLite 自建 chat session 真相。除非后续确认 Hermes gateway 支持把 `/v1/responses` 直接绑定到 Hermes session storage,否则 mnote 不应把它切成默认主链。
|
||||
|
||||
当前决策:
|
||||
|
||||
> **继续 `/v1/runs` 作为页面 AI 默认主链;`/v1/responses` 只作为事件标准化与 tool card 的参考实现。若未来要切换,必须另开设计稿证明 Hermes session 仍是 AI 会话真相,或明确批准迁移会话真相。**
|
||||
|
||||
---
|
||||
|
||||
## 8. 非目标
|
||||
|
||||
- 不在本阶段引入 ACP 主链。
|
||||
- 不在本阶段把页面 AI 主链切到 `/v1/responses store=false`。
|
||||
- 不在 mnote 建 Hermes Web UI 那样的 SQLite chat DB。
|
||||
- 不把 Convex 作为 AI 会话真相。
|
||||
- 不把页面 AI 抽屉扩成完整 Hermes 管理台。
|
||||
- 不让 mnote plugin 直接写 Convex;写入仍回到 Rust runtime / kernel。
|
||||
|
||||
---
|
||||
|
||||
## 9. 最终验收口径
|
||||
|
||||
本稿完成时,应能同时成立:
|
||||
|
||||
- [x] 页面 AI 仍是 Hermes 页面内客户端。
|
||||
- [x] Hermes 仍是 AI session/message/tool event/usage/model/profile 真相。
|
||||
- [x] mnote-web 有 Runtime BFF 能力,支持 run resume、queue、abort 和事件标准化。
|
||||
- [x] 页面刷新或断线不导致 active run UI 状态丢失;完成后的消息从 Hermes 恢复。
|
||||
- [x] mnote 不保存完整聊天历史,只保存 mnote 业务写入结果和 audit。
|
||||
- [x] `/v1/responses` 是否进入主链已有单独评估结论,且没有绕过 Hermes session 真相。
|
||||
|
||||
一句话:
|
||||
|
||||
> **补齐 Web Chat Runtime 的工程能力,但不改变“AI 会话归 Hermes,mnote 事实归 Rust kernel”的长期边界。**
|
||||
@@ -29,15 +29,16 @@
|
||||
- `PageAIApplyController` 后续应收口为 review session / tool executor / readback controller,不能成为第二套 agent 编排中心。
|
||||
- 当前 `usedHermesRun=false` 的快路径只能理解为 deterministic shortcut,不代表 mnote 新建长期 agent runtime。
|
||||
|
||||
## 1.0.1 当前主路径修正(2026-05-18)
|
||||
## 1.0.1 当前主路径修正(2026-05-19)
|
||||
|
||||
`7-18` 到 `7-25` 修复后,本文中的页面 AI fast workflow 口径进一步收口:
|
||||
`2-2 local-first` 完成后,本文继续保留为页面块 / 结构性工具 checklist,但普通 Markdown 正文编辑主路径已经转为 VSCode-like 文件编辑:
|
||||
|
||||
- 普通正文编辑主路径是 `mnote.doc.markdown_edit`,模型生成 markdown `search/replace` 或 `full_content`,再由统一 mnote tool executor 写入。
|
||||
- `/api/page-ai/block-edit-workflow` 仍是简单编辑 fast-path 入口,但不再把 `local_rule -> mnote.doc.apply_block_ops` 作为当前主路径。
|
||||
- local-first 普通正文编辑默认给 Hermes / Reasonix 当前文件引用、selection 和 allowed roots,由 agent 使用自身 patch / diff / 文件编辑能力写本地 `.md`。
|
||||
- `mnote.doc.markdown_edit` 是 cloud / remote agent / compat fallback,不再是 local-first 普通 Markdown 编辑唯一主路径。
|
||||
- `/api/page-ai/block-edit-workflow` 只保留为兼容门面或非 local source 的快捷路径,不作为 local-first 默认主路径。
|
||||
- `mnote.doc.apply_block_ops` / `mnote.block.*` 保留为结构性块操作辅助,例如块移动、资源块、复杂子块或必须按 blockId 精确处理的场景。
|
||||
- Phase C 的 review session / streaming apply 仍冻结;本文只继续跟踪基础工具合同、上下文、冲突校验、幂等和审阅面边界。
|
||||
- 当前 page AI runtime 口径以 `design/07-ai/process/7-12-page-ai-hermes-tool-routing-and-review-surface-v1.md`、`design/10-review/done/10-current-mnote-ai-runtime-review-v1.md` 和 `bugs/07-ai/done/7-18` 到 `7-25` 为准。
|
||||
- 当前 page AI runtime 口径以 `design/02-convex-rust-long-term-architecture/done/2-2-local-first-workspace-convex-control-plane-v1.md`、`design/07-ai/process/7-14-online-local-ai-markdown-editing-convergence-v1.md` 和 `design/07-ai/process/7-15-page-ai-acp-agent-runtime-unified-layer-v1.md` 为准。
|
||||
|
||||
## 1.1 当前执行状态(2026-05-16)
|
||||
|
||||
@@ -262,14 +263,14 @@ cargo test -p mnote-web block_fetch
|
||||
- [x] 支持 `command=block_replace`。
|
||||
- [x] 支持 `command=block_insert_after`。
|
||||
- [x] 支持 `command=block_move_after` dry-run。
|
||||
- [x] `command=str_replace` 不再作为本 checklist 当前目标;普通正文 search/replace 已收口到 `mnote.doc.markdown_edit`,多重匹配/无法安全映射阻断由 markdown_edit 合同覆盖。
|
||||
- [x] `command=str_replace` 不再作为本 checklist 当前目标;cloud / remote / compat 的普通正文 search/replace 已收口到 `mnote.doc.markdown_edit`,多重匹配/无法安全映射阻断由 markdown_edit 合同覆盖。local-first 普通 Markdown 编辑默认走授权文件 + agent 原生 patch/diff。
|
||||
- [x] 缺少 `revision/conflictDetectionKey` 时只允许 dry-run。
|
||||
- [x] 返回 `planId`、`diff`、`warnings`、`risk`、`blocked`。
|
||||
|
||||
2026-05-18 口径修正:
|
||||
2026-05-19 口径修正:
|
||||
|
||||
- `7-18` 到 `7-25` 收口后,普通正文替换不再继续扩 `mnote.doc.plan_update command=str_replace`;当前实现路径是 `page_ai_workflow.rs -> mnote.doc.markdown_edit`。
|
||||
- `mnote.doc.markdown_edit` 已覆盖精确匹配、归一化匹配、多操作同块合并、无法安全映射时失败;相关测试由 `cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools -- --nocapture` 覆盖。
|
||||
- `7-18` 到 `7-25` 收口后,普通正文替换不再继续扩 `mnote.doc.plan_update command=str_replace`;compat 实现路径是 `page_ai_workflow.rs -> mnote.doc.markdown_edit`。
|
||||
- `mnote.doc.markdown_edit` 已覆盖精确匹配、归一化匹配、多操作同块合并、无法安全映射时失败;相关测试由 `cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools -- --nocapture` 覆盖。它不再代表 local-first 普通 Markdown 编辑默认入口。
|
||||
|
||||
验证命令:
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 7-12 [process] 页面 AI Hermes 工具路由与编辑审阅面设计 v1
|
||||
|
||||
> 更新时间:2026-05-16
|
||||
> 更新时间:2026-05-19
|
||||
>
|
||||
> 当前状态:`PROCESS`
|
||||
>
|
||||
@@ -21,7 +21,8 @@
|
||||
|
||||
本稿仍作为 Hermes 工具路由与审阅面设计保留在 `process/`,但以下口径已经更新:
|
||||
|
||||
- `/api/page-ai/block-edit-workflow` 当前不再以 `local_rule -> apply_block_ops` 作为主路径;简单正文编辑主路径已切到模型生成 markdown `search/replace` 或 `full_content`,再调用 `mnote.doc.markdown_edit`。
|
||||
- `/api/page-ai/block-edit-workflow` 当前不再以 `local_rule -> apply_block_ops` 作为主路径;local-first 普通 Markdown 编辑默认给 Hermes / Reasonix 授权文件引用,由 agent 使用自身 patch / diff / 文件编辑能力写本地 `.md`。
|
||||
- `mnote.doc.markdown_edit` 保留为 cloud / remote agent / compat fallback;模型生成 markdown `search/replace` 或 `full_content` 后调用该工具,只适用于 agent 不能直接访问授权文件或需要受控代理写入的场景。
|
||||
- `page_ai_workflow` 已复用统一 mnote tool executor,不再绕过 Hermes tool toggle / audit / write contract。
|
||||
- `mnote.doc.apply_block_ops` / `mnote.block.*` 保留为结构性块操作辅助,不再作为普通正文 search/replace 的优先入口。
|
||||
- 本稿中的 `PageAIReviewSession` 只定义 Phase C 的安全合同和状态机边界;当前 Phase C 仍冻结,不实施流式 apply 或新的审阅 UI。
|
||||
|
||||
@@ -4,13 +4,18 @@
|
||||
>
|
||||
> 更新时间:2026-05-16(v3:深度参考 CLI Main skill 系统,补全成熟度采纳清单)
|
||||
>
|
||||
> 2026-05-18 口径补充:
|
||||
> - local-first workspace 已成为早期产品默认形态;本地 `.md` 是默认 AI 编辑目标,在线 Convex 文档降级为可选 cloud / sync / share source。
|
||||
> - 本文早期把 `mnote.doc.markdown_edit` 描述为统一主路径;最新口径改为:local-first 普通 Markdown 编辑优先给 agent 授权文件引用,由 agent 使用自身成熟的 diff / apply_patch / 文件编辑能力完成;`mnote.doc.markdown_edit` 保留为 cloud / remote agent / compat fallback。
|
||||
> - 页面内图片 / 附件上传已在 local source 下写入 `{mdBase}.assets/` 并保存相对 Markdown 路径,AI 后续处理附件引用时也应保留相对路径,不改写为 Convex media asset。
|
||||
>
|
||||
> 当前状态:`PROCESS`
|
||||
>
|
||||
> 本稿目的:
|
||||
> 1. 纠正 7-9 / 7-10 / 7-12 / 7-13 中隐含的「块级编辑是 AI 唯一写入路径」假设
|
||||
> 2. 基于 CLI Main 参考实现,确立 mnote 的「文本级搜索替换为主 + 块级结构性操作为辅」两层模型
|
||||
> 2. 基于 CLI Main 参考实现,确立 mnote 的「agent 原生文件 patch/diff 为 local-first 默认路径,MNote 文本级兼容工具 + 块级结构性操作为 fallback / 辅助」两层模型
|
||||
> 3. 规划 BlockNote AI 流式/review 能力的远期方向(当前不实施)
|
||||
> 4. 统一在线 Convex 文档和本地 `.md` 文件的 AI 写入路径
|
||||
> 4. 统一 cloud / remote / compat 文档和本地 `.md` 文件的 AI 读取、权限、冲突与回读口径
|
||||
>
|
||||
> 关联文档:
|
||||
> - `/mnt/Data1T/mnote/design/07-ai/done/7-9-page-block-ai-tooling-roadmap-v1.md`
|
||||
@@ -37,7 +42,7 @@
|
||||
|
||||
## 1. 结论
|
||||
|
||||
**在线 Convex 文档和本地 `.md` 文件本质上是同一个东西:一段 markdown 文本,Tiptap 只是块级 UI 表现层。** 当前 07-ai 设计把 AI 编辑契约钉死在 `EditorBlockDocument` 的块级操作上,导致三个连锁问题:
|
||||
**在线 Convex 文档和本地 `.md` 文件本质上是同一个东西:一段 markdown 文本,Tiptap 只是块级 UI 表现层。** 进一步切到 local-first 后,本地 `.md` 已经是普通文件,因此不需要再为常规正文编辑发明一套 MNote 专用工具。当前 07-ai 设计把 AI 编辑契约钉死在 `EditorBlockDocument` 的块级操作上,导致三个连锁问题:
|
||||
|
||||
1. **AI 被迫在块级操作**:对「改写这段话」「补充一段总结」「调整语气」「把所有 TODO 改成 DONE」等自然请求,AI 必须产出 `{ op: "replace", blockId: "block_1", ... }` 格式的块操作,而不能直接产出修改后的文本或搜索替换对。这增加了 AI 的认知负担和出错概率。
|
||||
|
||||
@@ -55,15 +60,15 @@
|
||||
| **BlockNote AI** | 纯块级 `add/update/delete`(依赖 blockId),流式 apply + suggest/review | ✅ **远期参考**:流式/review 能力,Phase C 规划 |
|
||||
| **Tiptap AI Autocomplete** | 纯文本补全,单句接龙,无工具 | ⏳ 独立功能:内联 AI 补全(非本文讨论范围) |
|
||||
|
||||
**CLI Main 最关键的设计决策**:它不把 AI 钉死在某一层。`str_replace` 用于文本级修改(不需要 blockId),`block_*` 用于精确块结构调整。格式上,**XML 用于精确编辑场景,Markdown 用于整篇导入/导出**。mnote 的不同之处在于——在线文档和本地 `.md` 文件的共同分母是 **markdown 而非 XML**,因此我们的主格式是 markdown。
|
||||
**CLI Main 最关键的设计决策**:它不把 AI 钉死在某一层。`str_replace` 用于文本级修改(不需要 blockId),`block_*` 用于精确块结构调整。格式上,**XML 用于精确编辑场景,Markdown 用于整篇导入/导出**。mnote 的不同之处在于:local-first 之后,默认对象就是本地 `.md` 文件,因此可以直接复用 Codex / Hermes / Reasonix 自身成熟的 diff、apply_patch、文件编辑能力;MNote 的职责收口为权限沙箱、文件引用解析、审计和刷新。
|
||||
|
||||
### 正确方向
|
||||
|
||||
> mnote 的 AI 编辑路线:**CLI Main 的两层操作模型 + BlockNote AI 的流式/review 能力 + mnote 自己的 markdown 优先策略。**
|
||||
> mnote 的 AI 编辑路线:**本地授权文件 + agent 原生 patch/diff 为主;MNote 兼容工具为 cloud/remote/结构化辅助;BlockNote AI 的流式/review 只作为远期交互参考。**
|
||||
|
||||
具体:
|
||||
|
||||
- **当前实施**(Phase A/B):`mnote.doc.markdown_edit` 作为 AI 编辑主路径,文本级搜索替换为第一操作原语。`mnote.block.*` 保留为结构性辅助。
|
||||
- **当前实施**(Phase A/B):local-first 页面 AI 只传当前文件引用、可选 selection 和用户指令;MNote 校验 `AiAccessScope` 后让 agent 在受限目录中使用原生 patch/diff 编辑 `.md`。`mnote.doc.markdown_edit` 作为兼容 / 远端代理 fallback,`mnote.block.*` 作为复杂结构辅助。
|
||||
- **远期规划**(Phase C):BlockNote AI 的流式增量 apply + suggest/review 层。当前先设计,不实施。
|
||||
|
||||
---
|
||||
@@ -377,36 +382,37 @@ mnote 当前:Hermes 只知道 workspace/document 模型,不知道本地文
|
||||
|
||||
AI 最自然的编辑方式是对文本进行操作。块是 UI 概念,不是 AI 概念。在线文档的持久化格式和本地文件的持久化格式都可以投影为 markdown。
|
||||
|
||||
### 4.2 两层操作模型
|
||||
### 4.2 两层操作模型(兼容层)
|
||||
|
||||
| 层 | 工具 | 寻址方式 | 适用场景 | 占比 |
|
||||
|----|------|---------|---------|------|
|
||||
| **文本级(主)** | `mnote.doc.markdown_edit` | search/replace 文本对 | "把这段改简洁"、"把所有 TODO 改 DONE"、"补充一段总结" | 80%+ |
|
||||
| **文件级(主)** | agent 原生 `diff/apply_patch/文件编辑` | 授权文件引用 / selection | local-first 普通 Markdown 改写 | 80%+ |
|
||||
| **文本级(兼容)** | `mnote.doc.markdown_edit` | search/replace 文本对 | cloud / remote agent / 兼容旧页面 AI | 次要 |
|
||||
| **块级(辅助)** | `mnote.doc.apply_block_ops` | blockId / matchText | "把第三块拖到第一块后面"、"精确删除引用块" | <20% |
|
||||
|
||||
### 4.3 在线和本地共用同一条写入路径
|
||||
### 4.3 在线和本地的主写入路径
|
||||
|
||||
```
|
||||
mnote.doc.markdown_edit
|
||||
页面 AI / ACP
|
||||
→ resolve_source(documentId) → Convex | LocalFS
|
||||
→ 读取当前 markdown
|
||||
→ 应用 operations(搜索替换)
|
||||
→ 写入目标(Convex 或文件系统)
|
||||
→ 返回 delta
|
||||
→ local-first: 传授权文件引用给 agent runtime
|
||||
→ agent 原生 patch/diff 写入文件
|
||||
→ MNote 做权限 / 审计 / refresh
|
||||
→ cloud / remote fallback: mnote.doc.markdown_edit
|
||||
```
|
||||
|
||||
差异仅存在于 `resolve_source` 和 `write_target` 两个 adapter,中间的 markdown 操作逻辑完全共享。这和 CLI Main 的 `docs +update` 可以操作任何 `doc-token` 的设计一致。
|
||||
差异主要在执行层:local-first 直接让 agent 修改授权文件;cloud 或受限 remote runtime 无法直接访问本地文件时,再走 `mnote.doc.markdown_edit` 代理。这和 CLI Main 的 `docs +update` 可以操作任何 `doc-token` 的设计一致,只是 mnote 进一步把“编辑算法”让渡给 agent runtime。
|
||||
|
||||
### 4.4 Diff 是内部实现细节
|
||||
|
||||
AI **不**产出 unified diff(行号/上下文极易出错),也不调用独立的 diff/patch 工具。AI 产出两种形式之一:
|
||||
对兼容 `mnote.doc.markdown_edit` 来说,AI **不**产出 unified diff(行号/上下文极易出错),而是产出两种形式之一:
|
||||
|
||||
| 形式 | 适用场景 | AI 负担 |
|
||||
|------|---------|---------|
|
||||
| `operations: [{ search, replace }]` | 局部修改 | 低:只需找原文片段 |
|
||||
| `full_content: "..."` | 小文档全文改写 | 低:直接写完整 markdown |
|
||||
|
||||
服务端内部做 diff(用于 delta 推送和冲突检测),但 AI 无感知。
|
||||
而在 local-first 主路径中,agent runtime 自己可以安全使用成熟的 diff / apply_patch / 直接文件编辑能力;MNote 只要求这些写入被限制在授权路径内,并把 changed files / diff 摘要收回审计。
|
||||
|
||||
---
|
||||
|
||||
@@ -446,7 +452,7 @@ AI **不**产出 unified diff(行号/上下文极易出错),也不调用
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 `mnote.doc.markdown_edit`(新增,主路径)
|
||||
### 5.2 `mnote.doc.markdown_edit`(新增,兼容 / fallback)
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -580,11 +586,13 @@ GhostTextOverlay(新增,编辑器)
|
||||
|
||||
## 8. 实施阶段(当前)
|
||||
|
||||
### Phase A:`mnote.doc.fetch` 增强 + `mnote.doc.markdown_edit` 核心实现
|
||||
### Phase A:文件引用主路径 + `mnote.doc.*` 兼容层
|
||||
|
||||
- [x] `mnote.doc.fetch` 增加 `format: "markdown"`(在线文档 Page Aggregate → PageMarkdown)
|
||||
- [x] `mnote.doc.fetch` 增加本地文件 source 路由(自动检测 Convex vs 文件系统路径)
|
||||
- [x] 实现 `resolve_source(documentId)` — 本地文件路径 `local_fs` vs 其余走 Convex
|
||||
- [ ] 页面 AI / ACP 普通正文编辑默认只传当前文件引用、可选 selection 和用户指令,不再默认构造完整 page context
|
||||
- [ ] 本地 agent runtime 在 `allowed_roots / allowed_file_paths` 内执行 patch/diff,并把 changed files / diff 摘要回传 MNote
|
||||
- [x] 实现 `search_replace(text, operations)` — 四级匹配策略(精确→宽松→段落 fuzzy→失败)
|
||||
- [x] 实现 Convex 写入 adapter(复用 `doc_apply_block_ops` 链路)
|
||||
- [x] 实现本地文件写入 adapter(`mnote.doc.markdown_edit` 检测到本地文件路径时直接 `fs::write` 写回,不经过 Convex)
|
||||
@@ -599,7 +607,7 @@ GhostTextOverlay(新增,编辑器)
|
||||
### Phase B:`page_ai_workflow.rs` 收口
|
||||
|
||||
- [x] 退役 `direct_block_edit_operations`(正则抠「」的快路径,代码保留但路由跳过)
|
||||
- [x] `/api/page-ai/block-edit-workflow` 底层切换到 `mnote.doc.markdown_edit`
|
||||
- [x] `/api/page-ai/block-edit-workflow` 不再被当作 local-first 主路径
|
||||
- [x] 模型 system prompt 重构:从产块操作 JSON 改为产 search/replace 文本对
|
||||
- [x] 补全 operation schema:`extract_markdown_operations_from_model_text` 处理新旧格式
|
||||
- [x] 浏览器 smoke:`markdown_edit` 搜索替换通过,自然语言编辑路径可用
|
||||
@@ -620,7 +628,7 @@ GhostTextOverlay(新增,编辑器)
|
||||
|
||||
| CLI Main 概念 | mnote 对应 |
|
||||
|--------------|-----------|
|
||||
| `str_replace`(文本级) | `mnote.doc.markdown_edit`(主路径) |
|
||||
| `str_replace`(文本级) | `mnote.doc.markdown_edit`(兼容 / fallback) |
|
||||
| `block_replace/insert_after/delete/move_after`(块级) | `mnote.doc.apply_block_ops`(辅助路径) |
|
||||
| XML 用于精确编辑 | mnote 不用 XML(没有 XML 存储层),direct path 退役后全部走 markdown |
|
||||
| Markdown 用于导入/导出/对话引用 | mnote 全部 AI 交互走 markdown |
|
||||
@@ -640,7 +648,7 @@ GhostTextOverlay(新增,编辑器)
|
||||
|
||||
| 设计稿 | 关系 | 修正状态 |
|
||||
|--------|------|---------|
|
||||
| 7-9 路线图 | 块级编辑降级为辅助,markdown_edit 为主路径 | ✅ 已修正 |
|
||||
| 7-9 路线图 | 块级编辑降级为辅助,local-first 普通编辑改为授权文件 + agent patch/diff,markdown_edit 退到兼容层 | ✅ 已修正 |
|
||||
| 7-10 checklist | 新增 Phase 9 markdown_edit | ✅ 已修正 |
|
||||
| 7-12 工具路由 | PageAICommandRouter 主输出改为 markdown_edit | ✅ 已修正 |
|
||||
| 7-13 EditorRuntimeActor | 补充 markdown_edit 的 delta 适配 | ✅ 已修正 |
|
||||
@@ -651,10 +659,10 @@ GhostTextOverlay(新增,编辑器)
|
||||
## 10. 禁止项
|
||||
|
||||
- 不删除 `mnote.block.*` 工具(保留为辅助路径)。
|
||||
- 不让 AI 产出 unified diff(行号/上下文极易出错)。
|
||||
- 不强迫 local-first agent 产出 MNote 自定义 diff;Hermes / Reasonix 可使用自身成熟 patch / diff / apply_patch 能力,MNote 负责白名单权限、文件版本冲突和审计。
|
||||
- 不要求本地文件有稳定的 `blockId`(本地文件没有 block identity)。
|
||||
- 不在 markdown_edit 内部引入新的 AI 模型调用(diff 是确定性算法)。
|
||||
- 不改变 Convex `documents:updateContent` 的持久化链路(markdown_edit 复用现有保存路径)。
|
||||
- 不把 Convex `documents:updateContent` 重新提升为 local-first 正文主存储;`markdown_edit` 在 cloud / compat 场景可复用受控保存路径。
|
||||
- 不把 `mnote.page.save` 重新描述为精确编辑主入口(它仍是兜底工具)。
|
||||
- **不照搬 BlockNote AI 的纯 blockId 寻址模式**(与 mnote 的 markdown 优先策略冲突)。
|
||||
|
||||
@@ -663,10 +671,10 @@ GhostTextOverlay(新增,编辑器)
|
||||
## 11. 成功标准
|
||||
|
||||
- [x] `mnote.doc.fetch(documentId, format: "markdown")` 对在线文档返回正确 markdown
|
||||
- [ ] `mnote.doc.fetch(documentId, format: "markdown")` 对本地 `.md` 文件返回正确 markdown(读取已实现,待浏览器 smoke)
|
||||
- [ ] `mnote.doc.fetch(documentId, format: "markdown")` 对本地 `.md` 文件返回正确 markdown(兼容路径)
|
||||
- [x] `mnote.doc.markdown_edit` 的简单搜索替换(1 条 operation)浏览器 smoke 通过
|
||||
- [ ] `mnote.doc.markdown_edit` 的复杂改写(3+ 条 operations)成功率 > 80%
|
||||
- [ ] 本地 `.md` 文件通过页面 AI 面板可被读取和写入(读取已实现,写入待本地文件 adapter)
|
||||
- [ ] 本地 `.md` 文件通过页面 AI 面板可被读取和写入,且默认通过 agent 原生 patch/diff 完成
|
||||
- [x] 在线文档的 markdown_edit 不增加 Convex RTT(和当前块操作持平)
|
||||
- [x] `direct_block_edit_operations` 已退役(路由跳过,代码保留)
|
||||
- [x] `page_ai_workflow.rs` 的 system prompt 已补全 search/replace schema
|
||||
@@ -682,7 +690,7 @@ GhostTextOverlay(新增,编辑器)
|
||||
|
||||
| # | CLI Main 模式 | mnote 实施项 | 状态 | 参考文件 |
|
||||
|---|-------------|-------------|------|---------|
|
||||
| 1 | 两层操作模型 | `markdown_edit`(主)+ `apply_block_ops`(辅助)已在 7-10 Phase 9 登记 | 📋 设计完成,待实施 | `lark-doc-update.md` |
|
||||
| 1 | 两层操作模型 | local-first 默认 agent 原生 patch/diff;`markdown_edit`(compat fallback)+ `apply_block_ops`(结构辅助)已在 7-10 Phase 9 登记 | 📋 设计完成,已按 2-2 改口径 | `lark-doc-update.md` |
|
||||
| 2 | Scope 四级控制 | `mnote.doc.fetch` 增加 `scope: full/section/outline/keyword` | 🔜 Phase A | `lark-doc-fetch.md` |
|
||||
| 3 | Detail 三级控制 | `mnote.doc.fetch` 增加 `detail: simple/with_ids/full` | 🔜 Phase A | `lark-doc-fetch.md` |
|
||||
| 4 | 片段包装 | fetch 返回中标记 `<!-- fragment -->` / `<!-- excerpt -->` 告知 AI 部分视图 | 🔜 Phase A | `lark-doc-fetch.md`(fragment/excerpt 模式) |
|
||||
@@ -726,7 +734,7 @@ Phase A(当前立即)
|
||||
├── #3 Detail 三级控制 ← mnote.doc.fetch 增强
|
||||
├── #4 片段包装 ← fetch 返回值增强
|
||||
├── #8 Markdown 优先 ← 已确认
|
||||
└── mnote.doc.markdown_edit 核心实现
|
||||
└── 授权文件引用 + agent 原生 patch/diff 主路径;mnote.doc.markdown_edit 作为 compat fallback
|
||||
|
||||
Phase B(Phase A 完成后)
|
||||
├── #5 Code-Act Loop ← Hermes plugin SKILL.md
|
||||
|
||||
@@ -11,9 +11,10 @@
|
||||
> 4. 复用现有参考代码,最小化重复实现工作
|
||||
>
|
||||
> 关联文档:
|
||||
> - `/mnt/Data1T/mnote/design/07-ai/done/7-5-hermes-client-proxy-contract-v1.md`
|
||||
> - `/mnt/Data1T/mnote/recycle/design/07-ai/retired-http-hermes/7-5-hermes-client-proxy-contract-v1.md`(已退役历史背景)
|
||||
> - `/mnt/Data1T/mnote/design/07-ai/process/7-10-page-block-ai-tooling-execution-checklist-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/07-ai/process/7-14-online-local-ai-markdown-editing-convergence-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/07-ai/process/7-17-acp-session-convex-sharing-contract-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/hermes-vscode-main/`
|
||||
> - `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/DeepSeek-Reasonix-main/`
|
||||
|
||||
@@ -35,10 +36,10 @@ mnote-web (Rust Axum)
|
||||
│ get /api/hermes/runs/{id}/events
|
||||
│
|
||||
├─ hermes_tools.rs (mnote.doc.* / mnote.block.*)
|
||||
│ └─ Rust 工具实现,通过 HTTP/Convex 读写文档
|
||||
│ └─ Rust 兼容工具实现,通过 HTTP/Convex 或本地代理读写文档
|
||||
│
|
||||
└─ page_ai_workflow.rs (fast-path 块编辑)
|
||||
└─ local_rule planner, 不经过 Hermes
|
||||
└─ page_ai_workflow.rs (兼容门面)
|
||||
└─ local-first 下不再是主路径
|
||||
```
|
||||
|
||||
**问题:**
|
||||
@@ -67,15 +68,21 @@ mnote-web (Rust Axum)
|
||||
│ │ └─ session/update ≫ SSE 转发
|
||||
│ └─ 代理层:向下游工具通知
|
||||
│
|
||||
├─ hermes_tools.rs (不变)
|
||||
├─ hermes_tools.rs (兼容层)
|
||||
│ └─ mnote.doc.* / mnote.block.* / mnote.page.*
|
||||
│
|
||||
└─ page_ai_workflow.rs (不变)
|
||||
└─ fast-path 块编辑
|
||||
└─ page_ai_workflow.rs (兼容保留)
|
||||
└─ local-first 普通正文编辑默认不经过它
|
||||
```
|
||||
|
||||
ACP 是整个架构的支点——它是一个**开放协议**,不是某个产品的私有接口。
|
||||
|
||||
> 2026-05-18 local-first 口径补充:
|
||||
>
|
||||
> - 目标不是把 Hermes / Reasonix 再包进一层重型 MNote 工具系统,而是让它们尽量像在 VSCode 中那样直接面对授权后的本地工作区。
|
||||
> - MNote 主要负责:页面定位、白名单目录授权、ACP 会话管理、审计、文件变化同步到 tiptap / File Tree / Page Aggregate。
|
||||
> - local-first 普通 Markdown 编辑默认不要求 runtime 调 `mnote.doc.markdown_edit`;兼容工具只为 cloud / remote / 复杂结构场景保留。
|
||||
|
||||
---
|
||||
|
||||
## 2. ACP 协议标准
|
||||
@@ -216,6 +223,25 @@ profile "reasonix" → runtime "reasonix" (spawn node reasonix-acp-wrapper.mjs)
|
||||
|
||||
前端获取可用运行时列表:`GET /api/hermes/client/profiles`(现有接口,扩展字段)
|
||||
|
||||
### 4.1.1 白名单目录即 runtime 权限边界
|
||||
|
||||
对 local-first 而言,真正重要的不是再做一套“文档工具能力矩阵”,而是把 workspace 白名单准确传给 runtime:
|
||||
|
||||
```text
|
||||
登录用户
|
||||
-> 解析 access-policy.json / owner / admin / grant
|
||||
-> 得到 allowedRoots = ["/mnt/Data1T/Mnote_data/users/.../my-space", ...]
|
||||
-> 启动 Hermes / Reasonix 时把 allowedRoots / cwd / read-write scope 传入 runtime
|
||||
-> runtime 直接在这些目录里工作
|
||||
```
|
||||
|
||||
这与 VSCode / Codex 的工作模型一致:
|
||||
|
||||
- runtime 看到的是一个受限 workspace,而不是一堆抽象页面 API。
|
||||
- 页面 AI 只额外提供“当前文件是谁”以及可选选区信息。
|
||||
- 对 `.md` 的普通编辑不强制走 `mnote.doc.markdown_edit`。
|
||||
- 一旦文件写回磁盘,MNote 再负责把变化同步回前端显示。
|
||||
|
||||
### 4.2 会话生命周期 (ACP Session Manager)
|
||||
|
||||
```
|
||||
@@ -244,9 +270,9 @@ profile "reasonix" → runtime "reasonix" (spawn node reasonix-acp-wrapper.mjs)
|
||||
|
||||
### 4.3 工具桥接
|
||||
|
||||
当前 `hermes_tools.rs` 中注册的 mnote 工具(`mnote.doc.fetch`、`mnote.doc.markdown_edit`、`mnote.block.*`、`mnote.page.*`)对 ACP 来说只是一组 HTTP 端点。
|
||||
当前 `hermes_tools.rs` 中注册的 mnote 工具(`mnote.doc.fetch`、`mnote.doc.markdown_edit`、`mnote.block.*`、`mnote.page.*`)对 ACP 来说只是一组 HTTP 兼容端点,而不是 local-first 普通 Markdown 编辑的唯一主路径。
|
||||
|
||||
对于 Reasonix 作为 runtime 的场景,需要一个 Reasonix-side 的工具注册包装脚本,将 mnote 工具注册到 `ToolRegistry`:
|
||||
对于 Reasonix 作为 runtime 的场景,仍需要一个 Reasonix-side 的工具注册包装脚本,将 mnote 兼容工具注册到 `ToolRegistry`;但 local-first 默认应优先让 runtime 直接拿到授权文件引用,在受限 cwd 中使用自身成熟的 patch/diff/文件编辑能力。
|
||||
|
||||
```typescript
|
||||
// reasonix-acp-wrapper.mjs — ACP 包装层
|
||||
@@ -287,7 +313,27 @@ agent → tool call → (通过 TCP/localhost HTTP) → mnote-web Rust hermes_to
|
||||
→ Convex / 文档系统
|
||||
```
|
||||
|
||||
**不需要在 Rust 侧重新注册工具到 Reasonix。** mnote-web 的工具 HTTP 端点 (`/api/hermes/tools/mnote/call`) 不变,只通过 ACP 换掉了 agent runtime。
|
||||
**不需要在 Rust 侧重新注册工具到 Reasonix。** mnote-web 的工具 HTTP 端点 (`/api/hermes/tools/mnote/call`) 仍可保留,只通过 ACP 换掉了 agent runtime;但这些端点主要承担 cloud / remote agent / compat fallback,而不是把所有本地文件编辑都重新包成 mnote 工具。
|
||||
|
||||
### 4.3.1 local-first 默认工作流
|
||||
|
||||
local-first 页面 AI 的默认工作流应是:
|
||||
|
||||
```text
|
||||
当前页面 URL / documentId
|
||||
-> MNote 解析出真实 markdown 文件路径
|
||||
-> MNote 校验该路径是否落在 runtime allowedRoots 白名单内
|
||||
-> 把 currentFile / selection / allowedRoots 传给 runtime
|
||||
-> runtime 直接读写该文件
|
||||
-> watcher / refresh 触发前端 page aggregate 与 tiptap 更新
|
||||
```
|
||||
|
||||
只有在以下情况,runtime 才需要走 mnote 兼容工具:
|
||||
|
||||
- runtime 本身无法直接访问本地文件
|
||||
- 当前 source 是 cloud / sync replica
|
||||
- 当前对象不是普通 markdown,而是 mindmap / table / 资源块 / 分享受限对象
|
||||
- 需要显式审计某种结构化操作
|
||||
|
||||
### 4.4 前端 SSE 扩展
|
||||
|
||||
@@ -570,11 +616,11 @@ AiAgentPanel 增加下拉框 + 切换逻辑:
|
||||
|
||||
### 7.2 对 7-14 (markdown 编辑收敛) 的影响
|
||||
|
||||
7-14 确立的「两层操作模型」(`mnote.doc.markdown_edit` 主 + `mnote.block.*` 辅)不受影响——工具在 Rust 侧 `hermes_tools.rs` 实现不变。ACP 只是换掉了 driver(从 Hermes 换成 Reasonix),不改 driver 调用的工具。
|
||||
7-14 的最新口径是:local-first 普通 Markdown 编辑优先走“授权文件引用 + agent 原生 patch/diff”,`mnote.doc.markdown_edit` / `mnote.block.*` 退到兼容与辅助层。ACP 只是换掉 driver(从 Hermes 换成 Reasonix),不改变这个权限与执行边界。
|
||||
|
||||
### 7.3 对 `page_ai_workflow.rs` 的影响
|
||||
|
||||
不影响。`block_edit_workflow` 作为独立 fast-path 与 ACP 无关。
|
||||
最新口径下,`block_edit_workflow` 只保留为兼容门面;local-first 普通正文编辑不应再依赖它。ACP 主要服务 agent runtime 选择、权限隔离、事件桥接和审计。
|
||||
|
||||
---
|
||||
|
||||
@@ -638,7 +684,7 @@ AiAgentPanel 增加下拉框 + 切换逻辑:
|
||||
|
||||
| 改动 | 文件 | 说明 |
|
||||
|------|------|------|
|
||||
| ACP 下拉选择器 | `layout.rs` | Agent 标签页新增 `<select data-page-ai-acp-runtime>`,3 个选项:默认 (Hermes HTTP)、ACP · Hermes、ACP · Reasonix |
|
||||
| ACP 下拉选择器 | `layout.rs` | Agent 标签页新增 `<select data-page-ai-acp-runtime>`;2026-05-18 起只保留 ACP · Hermes / ACP · Reasonix,移除“默认 (Hermes HTTP)”选项 |
|
||||
| 状态存储 | `layout.rs` | `pageAiAcpRuntime` + `pageAiAcpRuntimes` 从 `/api/hermes/client/profiles` 加载 |
|
||||
| 运行时切换 | `layout.rs` | `acpRuntime` 只表示运行时/传输层;Hermes ACP 继续保留当前 Hermes profile,Reasonix ACP 使用 `profile=reasonix` |
|
||||
| UI 自适应 | `layout.rs` | ACP Hermes 模式下继续显示 Hermes profile 下拉;ACP Reasonix 模式下隐藏 Hermes profile 下拉;Agent 面板显示 ACP 配置 |
|
||||
@@ -909,7 +955,14 @@ node /home/lix/.codex/skills/page-ai-browser-verify/scripts/verify_mnote_page_ai
|
||||
|
||||
### [ ] Step 16:退役旧的 Hermes HTTP proxy 代码
|
||||
|
||||
> 待 ACP 路径稳定后执行(至少 1 周灰度观察期)。
|
||||
> 2026-05-18 已开始执行第一阶段退役:
|
||||
>
|
||||
> - 页面 AI 前端默认 `acpRuntime=reasonix`,不再以空 runtime 表示“默认 Hermes HTTP”;用户仍可在下拉中切换到 `ACP · Hermes`。
|
||||
> - mnote-web 服务端默认把 `/api/hermes/client/runs` 的空 `acpRuntime` 归入 ACP 默认 runtime(默认 `reasonix`),避免继续落到 `configured_upstream_for_profile()` 的 HTTP proxy 分支。
|
||||
> - `GET /api/hermes/client/gateway/health` 在 ACP 默认路径下返回 ACP transport 状态,不再探测 `8642/8644` HTTP gateway。
|
||||
> - 旧 Hermes HTTP proxy 合同与 `/v1/runs` 默认主链设计稿已移入 `recycle/design/07-ai/retired-http-hermes/`,避免干扰后续 ACP 主线判断。
|
||||
>
|
||||
> 剩余工作:删除或进一步隔离 `hermes_client.rs` 内的 HTTP proxy 兼容分支;当前阶段只保留显式兼容开关,避免一次性删除影响 profile/memory/tools 管理能力。
|
||||
|
||||
### [ ] Step 17:基准测试 — Reasonix 缓存收益量化
|
||||
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
|
||||
本文只承接这些剩余 smoke,不新增 AI 功能面,不改变当前主路径:
|
||||
|
||||
- 简单正文编辑主路径仍是 `mnote.doc.markdown_edit`。
|
||||
- local-first 普通 Markdown 编辑主路径仍是“授权文件引用 + agent 原生 patch/diff + watcher 同步”;本文只验证 mnote tools 的结构性辅助和 compat fallback。
|
||||
- `mnote.doc.markdown_edit` 只作为 cloud / remote agent / compat fallback 的 smoke 对象。
|
||||
- `mnote.block.*` 仍只作为结构性辅助。
|
||||
- `mnote.page.save` 仍只作为页面级粗粒度兜底。
|
||||
- Phase C review / streaming apply 仍冻结,当前只验证基础合同和安全边界。
|
||||
|
||||
@@ -0,0 +1,477 @@
|
||||
# 7-17 [process] ACP Session 与控制面账号作用域 / 分享合同 v1
|
||||
|
||||
> 创建时间:2026-05-18
|
||||
>
|
||||
> 当前状态:`PROCESS / FUTURE`
|
||||
>
|
||||
> 本稿目的:
|
||||
> 1. 明确 ACP session 必须绑定当前账号权限与控制面作用域,不能以内存态绕过权限。
|
||||
> 2. 冻结后续“复制会话 / 只读分享 / 多人共同会话”的产品语义,避免当前实现误扩。
|
||||
> 3. 区分 MNote 产品层 AI session 与 ACP Hermes / ACP Reasonix 执行层 session。
|
||||
> 4. 为后续项目基本完成后扩展共享能力预留 schema、API 和验证边界。
|
||||
>
|
||||
> 关联文档:
|
||||
> - `/mnt/Data1T/mnote/design/07-ai/process/7-15-page-ai-acp-agent-runtime-unified-layer-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/07-ai/done/7-25-acp-session-runtime-enhancement-plan-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/07-ai/process/7-14-online-local-ai-markdown-editing-convergence-v1.md`
|
||||
> - `/mnt/Data1T/mnote/recycle/design/07-ai/retired-http-hermes/`
|
||||
|
||||
---
|
||||
|
||||
## 0. 当前结论
|
||||
|
||||
ACP 默认主链已经替代旧 Hermes HTTP 主链,但这不意味着 ACP runtime session 可以成为产品层会话真相。
|
||||
|
||||
当前必须成立的规则:
|
||||
|
||||
- **控制面持有产品层 AI session 的账号作用域真相;在当前实现里它可以暂时落在 Convex,但长期不应把“Convex”写死成唯一真相。**
|
||||
- **ACP runtime session 只是执行层会话**,可以被控制面 session 绑定或索引,但不能直接作为跨用户共享对象。
|
||||
- **ACP session 记录必须带 `userId / workspaceId / documentId / sessionId / runId / actorId / acpRuntime / profile`**。
|
||||
- **写入控制面 session store 失败时,不允许静默降级为可继续写入的内存 session**。这会破坏账号隔离、审计和后续分享语义。
|
||||
- 当前只实现单用户页面 AI session 基础路径;复制、分享、多人共同会话全部是后续功能,不在当前阶段实施。
|
||||
|
||||
---
|
||||
|
||||
## 1. 术语边界
|
||||
|
||||
### 1.1 MNote AI Session
|
||||
|
||||
MNote AI Session 是产品层会话对象,长期应该由控制面持有;当前实现可以暂存在 Convex,但目标不是把消息全文和权限真相永久绑死在 Convex。
|
||||
|
||||
它回答:
|
||||
|
||||
- 谁能看到这段 AI 会话?
|
||||
- 这段会话属于哪个 workspace / document?
|
||||
- 它是否可被复制、分享、共同编辑?
|
||||
- 每条 run 是哪个账号触发的?
|
||||
- 哪些消息、工具调用、结果可以被其他用户看到?
|
||||
|
||||
MNote AI Session 的 id 可以稳定暴露给前端,例如 `mnote_{documentId}_{traceId}`,但它不是 ACP runtime 原生 session id。
|
||||
|
||||
### 1.2 ACP Runtime Session
|
||||
|
||||
ACP Runtime Session 是执行层对象,由 Hermes ACP 或 Reasonix ACP 子进程持有。
|
||||
|
||||
它回答:
|
||||
|
||||
- 某个 agent runtime 当前 prompt 属于哪个协议 session?
|
||||
- 运行时如何接收 `session/prompt`?
|
||||
- `session/update` 事件如何返回?
|
||||
- 运行时内部是否有本地上下文、缓存、memory、tool state?
|
||||
|
||||
ACP Runtime Session 不应作为产品层共享真相。它可以失效、重建、按用户隔离、按 runtime 隔离。
|
||||
|
||||
### 1.3 Run / Event / Tool Audit
|
||||
|
||||
Run 是一次用户触发的执行。
|
||||
|
||||
Event 是 runtime 或 mnote tool 在 run 中产生的结构化事件。
|
||||
|
||||
Tool Audit 是 mnote 侧工具执行与写入结果的审计记录,必须以触发账号为准,而不是 session owner 或 runtime owner。
|
||||
|
||||
---
|
||||
|
||||
## 2. 当前实现边界
|
||||
|
||||
当前代码可接受的最小边界:
|
||||
|
||||
- `POST /api/hermes/client/sessions` 在 ACP 默认路径下创建 Convex 侧 runtime session 索引。
|
||||
- `POST /api/hermes/client/runs` 创建 run 并持久化到 Convex runtime store。
|
||||
- `GET /api/hermes/client/events/{runId}` 将 ACP event 转成页面 SSE,并追加 runtime event。
|
||||
- `GET /api/hermes/client/gateway/health` 在 ACP 默认路径下返回 `transport=acp`,不再探测旧 Hermes HTTP gateway。
|
||||
|
||||
当前不应声称已完成:
|
||||
|
||||
- 跨账号共享会话。
|
||||
- 多人共同编辑同一个 AI session。
|
||||
- 复制会话后的上下文重建。
|
||||
- 完整消息历史作为产品层真相。
|
||||
- Hermes ACP 与 Reasonix ACP 的统一长期 resume 语义。
|
||||
- 分享链接、权限继承、脱敏导出。
|
||||
|
||||
---
|
||||
|
||||
## 3. 数据模型草案
|
||||
|
||||
### 3.1 `ai_sessions`
|
||||
|
||||
后续需要从当前 `acp_runtime_runs` 中抽出产品层 session 表。默认建议本地全文 + 控制面 metadata 的双层模型:
|
||||
|
||||
- 会话全文默认落本地 `ai-sessions/private/*.jsonl` 或 `ai-sessions/shared/*/*.jsonl`
|
||||
- 控制面只保存 metadata、分享关系、审计索引、同步状态
|
||||
- 只有显式开启同步或分享时,才把必要副本推到远端
|
||||
|
||||
建议字段:
|
||||
|
||||
| 字段 | 说明 |
|
||||
|---|---|
|
||||
| `id` | 控制面记录 id |
|
||||
| `session_id` | MNote 产品层 session id |
|
||||
| `owner_user_id` | 会话创建者 |
|
||||
| `workspace_id` | 所属 workspace,可为空但必须显式 |
|
||||
| `document_id` | 所属页面 / 文档 |
|
||||
| `title` | 会话标题 |
|
||||
| `visibility` | `private` / `workspace_read` / `shared_link` / `collaborative` |
|
||||
| `source` | `page_ai` / `local_file_ai` / 其他 |
|
||||
| `created_at` / `updated_at` / `deleted_at` | 生命周期 |
|
||||
|
||||
权限规则:
|
||||
|
||||
- 默认 `private`。
|
||||
- 任何 query/mutation 必须先按当前控制面 auth 解析 user,再判断 `owner_user_id`、membership 或 share grant。
|
||||
- 不允许只凭客户端传入的 `userId` 授权。
|
||||
|
||||
### 3.2 `ai_session_members`
|
||||
|
||||
多人共同会话需要独立成员表,不应把共享用户塞进 session JSON。
|
||||
|
||||
| 字段 | 说明 |
|
||||
|---|---|
|
||||
| `session_id` | 产品层 session |
|
||||
| `user_id` | 成员 |
|
||||
| `role` | `owner` / `editor` / `commenter` / `viewer` |
|
||||
| `added_by` | 添加者 |
|
||||
| `created_at` | 加入时间 |
|
||||
| `revoked_at` | 撤销时间 |
|
||||
|
||||
角色规则:
|
||||
|
||||
- `viewer` 只能读可共享消息和可共享 artifact。
|
||||
- `editor` 可以继续发起 run,但每次 run 的 `actor_id` 必须是本人。
|
||||
- `owner` 可以管理成员和删除会话。
|
||||
|
||||
### 3.3 `ai_runtime_bindings`
|
||||
|
||||
产品层 session 与运行时 session 的绑定必须独立建模。
|
||||
|
||||
| 字段 | 说明 |
|
||||
|---|---|
|
||||
| `binding_id` | 绑定 id |
|
||||
| `session_id` | MNote 产品层 session |
|
||||
| `run_id` | 当前 run,可选 |
|
||||
| `user_id` | 运行时所属用户 |
|
||||
| `runtime` | `hermes` / `reasonix` |
|
||||
| `profile` | Hermes profile 或 Reasonix preset |
|
||||
| `runtime_session_id` | ACP 对端 session id |
|
||||
| `status` | `active` / `closed` / `expired` / `failed` |
|
||||
| `created_at` / `expires_at` | 生命周期 |
|
||||
|
||||
关键规则:
|
||||
|
||||
- 同一个 MNote session 可以有多个 runtime binding。
|
||||
- 不同用户默认不能复用同一个 runtime binding。
|
||||
- 切换 Hermes / Reasonix runtime 时必须创建新 binding。
|
||||
- binding 是执行层缓存,不是会话权限真相。
|
||||
|
||||
### 3.4 `ai_session_messages`
|
||||
|
||||
后续如需完整历史,应单独建消息表,而不是只依赖 runtime events。
|
||||
|
||||
| 字段 | 说明 |
|
||||
|---|---|
|
||||
| `message_id` | 消息 id |
|
||||
| `session_id` | 产品层 session |
|
||||
| `run_id` | 关联 run |
|
||||
| `actor_user_id` | 触发者,可为空仅限系统消息 |
|
||||
| `role` | `user` / `assistant` / `tool` / `system` |
|
||||
| `content` | 可展示文本 |
|
||||
| `visibility` | `private` / `members` / `owner_only` |
|
||||
| `redaction` | 脱敏状态 |
|
||||
| `created_at` | 创建时间 |
|
||||
|
||||
当前阶段可以先不落完整 messages,但必须保证已有 run/event store 能按 user 过滤。
|
||||
|
||||
### 3.5 `ai_session_events`
|
||||
|
||||
Runtime event 和 tool event 应保留原始结构,但查询时必须做权限过滤。
|
||||
|
||||
| 字段 | 说明 |
|
||||
|---|---|
|
||||
| `event_id` | 事件 id |
|
||||
| `session_id` | 产品层 session |
|
||||
| `run_id` | run |
|
||||
| `actor_user_id` | 触发者 |
|
||||
| `runtime` | `hermes` / `reasonix` |
|
||||
| `event_type` | `message.delta` / `tool.started` / `tool.completed` 等 |
|
||||
| `payload` | 原始或规范化 payload |
|
||||
| `visibility` | 默认 `owner_only`,明确脱敏后才可扩大 |
|
||||
| `created_at` | 创建时间 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 权限模型
|
||||
|
||||
### 4.1 单用户私有会话
|
||||
|
||||
当前阶段只要求这个模式稳定。
|
||||
|
||||
规则:
|
||||
|
||||
- 创建 session 时使用当前 Convex Auth 解析出的真实 user id。
|
||||
- `userId` 只可作为服务端派生字段,不可由浏览器任意指定。
|
||||
- run / event / tool audit 都必须与同一 actor 绑定。
|
||||
- 当前请求无法解析用户时,应返回 `401` 或稳定错误,而不是创建匿名共享 session。
|
||||
|
||||
### 4.2 复制会话
|
||||
|
||||
复制不是共享同一个 runtime session。
|
||||
|
||||
复制语义:
|
||||
|
||||
- 生成新的 `session_id`。
|
||||
- 新 session 的 `owner_user_id` 是复制者。
|
||||
- 可以复制已脱敏、可共享的消息、摘要、artifact 引用。
|
||||
- 不复制底层 `runtime_session_id`。
|
||||
- 不复制另一个用户的 Hermes profile memory、Reasonix cache handle、tool permission state。
|
||||
- 复制后第一次继续对话时,为复制者创建新的 runtime binding。
|
||||
|
||||
适用场景:
|
||||
|
||||
- 用户把某段 AI 分析作为模板继续改。
|
||||
- 从只读分享页复制到自己的 workspace。
|
||||
|
||||
### 4.3 只读分享
|
||||
|
||||
只读分享只读产品层 session 的可共享投影。
|
||||
|
||||
必须隐藏:
|
||||
|
||||
- 本地路径、profile 配置路径、API key 状态。
|
||||
- tool args 中含有的敏感字段。
|
||||
- 未授权页面内容、选区内容、私有 workspace 信息。
|
||||
- actor 的内部 user id,除非产品明确展示成员身份。
|
||||
|
||||
分享链接不能恢复 ACP runtime session。
|
||||
|
||||
### 4.4 多人共同会话
|
||||
|
||||
多人共同会话是后续能力,不能直接复用当前 runtime session。
|
||||
|
||||
推荐语义:
|
||||
|
||||
```text
|
||||
共享 MNote AI Session
|
||||
-> 每个用户按自己的权限发起 run
|
||||
-> 每次 run 记录 actor_user_id
|
||||
-> runtime binding 默认按 actor_user_id 隔离
|
||||
-> 前端展示同一个产品层 transcript
|
||||
```
|
||||
|
||||
允许的实现策略:
|
||||
|
||||
- **每用户 runtime binding**:最安全,默认方案。每个成员继续对话时由自己的 runtime 处理。
|
||||
- **共享 transcript 重建上下文**:runtime 不共享,只把已授权 transcript 作为 prompt context 注入。
|
||||
- **共享 runtime binding**:默认禁止。只有当 runtime 明确是服务端多租户安全实例,并且 tool permission 已按 actor 隔离时才可考虑。
|
||||
|
||||
---
|
||||
|
||||
## 5. ACP Hermes 与 ACP Reasonix 差异
|
||||
|
||||
### 5.1 ACP Hermes
|
||||
|
||||
特点:
|
||||
|
||||
- Hermes profile、memory、skills、tools 往往绑定本机用户配置。
|
||||
- `mnoteai` profile 可能包含特定用户偏好、工具开关、记忆文件。
|
||||
- Hermes ACP 的底层 session 适合“当前用户私有 agent runtime”。
|
||||
|
||||
规则:
|
||||
|
||||
- 默认按用户隔离 runtime binding。
|
||||
- 共享会话不能让其他用户复用 owner 的 Hermes profile memory。
|
||||
- 复制会话时只复制可展示 transcript,不复制 Hermes profile 状态。
|
||||
- 如果后续允许团队 Hermes profile,必须单独建 workspace-level profile 权限模型。
|
||||
|
||||
### 5.2 ACP Reasonix
|
||||
|
||||
特点:
|
||||
|
||||
- Reasonix 强调 cache-first / preset / project cache。
|
||||
- 缓存可能跨 prompt 复用,隐私边界比普通 stateless model 更敏感。
|
||||
- Reasonix 的 session 与 cache handle 不应默认跨账号共享。
|
||||
|
||||
规则:
|
||||
|
||||
- 默认按 `userId + workspaceId + documentId + preset` 隔离缓存可见性。
|
||||
- 分享 transcript 不等于分享 Reasonix cache。
|
||||
- 多人共同会话如果要共享 Reasonix 缓存,必须先设计 cache grant。
|
||||
- 只读分享不得暴露 cache hit/miss 细节,除非确认无隐私风险。
|
||||
|
||||
### 5.3 统一抽象
|
||||
|
||||
MNote 不应把 Hermes ACP / Reasonix ACP 的内部 session 当成统一真相。
|
||||
|
||||
统一层只定义:
|
||||
|
||||
- 产品层 session。
|
||||
- run 与 actor。
|
||||
- runtime binding。
|
||||
- event/message 投影。
|
||||
- 权限与分享合同。
|
||||
|
||||
---
|
||||
|
||||
## 6. API 草案
|
||||
|
||||
当前阶段不实现以下 API,只冻结形状。
|
||||
|
||||
### 6.1 Session
|
||||
|
||||
```http
|
||||
GET /api/hermes/client/sessions
|
||||
POST /api/hermes/client/sessions
|
||||
GET /api/hermes/client/sessions/{sessionId}
|
||||
DELETE /api/hermes/client/sessions/{sessionId}
|
||||
POST /api/hermes/client/sessions/{sessionId}/rename
|
||||
POST /api/hermes/client/sessions/{sessionId}/auto-title
|
||||
```
|
||||
|
||||
要求:
|
||||
|
||||
- 所有接口服务端解析当前用户。
|
||||
- 查询默认只返回当前用户有权限访问的 session。
|
||||
- 删除默认软删除,不删除 runtime audit。
|
||||
|
||||
### 6.2 复制
|
||||
|
||||
```http
|
||||
POST /api/hermes/client/sessions/{sessionId}/copy
|
||||
```
|
||||
|
||||
请求:
|
||||
|
||||
```json
|
||||
{
|
||||
"targetWorkspaceId": "ws_1",
|
||||
"targetDocumentId": "doc_2",
|
||||
"copyMode": "summary_and_visible_messages"
|
||||
}
|
||||
```
|
||||
|
||||
响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"sessionId": "mnote_doc_2_copy_1",
|
||||
"sourceSessionId": "mnote_doc_1_original",
|
||||
"runtimeBindingCopied": false
|
||||
}
|
||||
```
|
||||
|
||||
### 6.3 分享
|
||||
|
||||
```http
|
||||
POST /api/hermes/client/sessions/{sessionId}/shares
|
||||
GET /api/hermes/client/sessions/{sessionId}/shares
|
||||
DELETE /api/hermes/client/sessions/{sessionId}/shares/{shareId}
|
||||
```
|
||||
|
||||
分享 grant 必须包含:
|
||||
|
||||
- `scope`: `user` / `workspace` / `link`
|
||||
- `role`: `viewer` / `editor`
|
||||
- `expiresAt`
|
||||
- `redactionPolicy`
|
||||
|
||||
### 6.4 多人会话成员
|
||||
|
||||
```http
|
||||
POST /api/hermes/client/sessions/{sessionId}/members
|
||||
GET /api/hermes/client/sessions/{sessionId}/members
|
||||
DELETE /api/hermes/client/sessions/{sessionId}/members/{userId}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 不变量
|
||||
|
||||
这些规则后续实现必须写成测试。
|
||||
|
||||
1. 账号 A 创建的 private session,账号 B 不能读取列表、详情、事件、消息。
|
||||
2. 账号 B 复制账号 A 分享给他的 session 后,得到新的 `sessionId` 和新的 owner。
|
||||
3. 复制后的 session 不包含源 session 的 `runtime_session_id`。
|
||||
4. 多人共同会话中,账号 B 发起 run 时 `actor_user_id=B`,不能写成 owner A。
|
||||
5. 只读 viewer 不能发起 run。
|
||||
6. Hermes profile memory 不随 session 分享。
|
||||
7. Reasonix cache handle 不随 session 分享。
|
||||
8. tool args 默认 `owner_only`,只有经过脱敏的摘要可进入 shared transcript。
|
||||
9. Convex store 写入失败时,run 创建必须失败或返回明确可恢复错误,不能静默创建匿名内存会话。
|
||||
10. 本地文件 AI session 分享必须重新核验目标用户是否能访问对应 local root;默认不支持跨用户分享本地文件内容。
|
||||
|
||||
---
|
||||
|
||||
## 8. 实施阶段
|
||||
|
||||
### Phase A:当前阶段,只做约束固化
|
||||
|
||||
状态:当前主线。
|
||||
|
||||
- ACP 默认主链可用。
|
||||
- session/run/event 必须账号作用域写入 Convex。
|
||||
- 旧 Hermes HTTP 主链进入 `recycle`。
|
||||
- 不实现复制、分享、多人会话。
|
||||
- 文档与测试明确禁止内存降级绕过权限。
|
||||
|
||||
### Phase B:项目基本完成后,补产品层 session 表
|
||||
|
||||
目标:
|
||||
|
||||
- 增加 `ai_sessions` / `ai_session_members` / `ai_runtime_bindings`。
|
||||
- 将现有 `acp_runtime_runs` 从“运行态索引”升级为 session 下的 run 记录。
|
||||
- UI 从 localStorage 历史逐步迁到 Convex session 列表。
|
||||
|
||||
### Phase C:复制与只读分享
|
||||
|
||||
目标:
|
||||
|
||||
- 实现 session copy。
|
||||
- 实现只读分享投影。
|
||||
- 建立脱敏策略。
|
||||
- 不实现多人共同编辑。
|
||||
|
||||
### Phase D:多人共同会话
|
||||
|
||||
目标:
|
||||
|
||||
- 成员管理。
|
||||
- 多 actor transcript。
|
||||
- 每 actor runtime binding。
|
||||
- 共享上下文重建策略。
|
||||
|
||||
### Phase E:runtime-specific 高级策略
|
||||
|
||||
目标:
|
||||
|
||||
- Hermes team profile 权限模型。
|
||||
- Reasonix cache grant / cache visibility。
|
||||
- workspace-level AI runtime policy。
|
||||
|
||||
---
|
||||
|
||||
## 9. 当前代码注意事项
|
||||
|
||||
当前代码中 `acp_runtime_runs` / `acp_runtime_events` 仍是运行态索引,不是完整产品层 session 模型。
|
||||
|
||||
因此后续修改时:
|
||||
|
||||
- 不要把 `ACP_RUN_PAYLOADS` 或 `ACP_ACTIVE_RUNS` 视为权限真相。
|
||||
- 不要因为 Convex 写入失败就退回“可继续写”的内存 session。
|
||||
- 不要把 `profile=mnoteai` 当成 user identity。
|
||||
- 不要让 `acpRuntime=hermes` 自动表示可访问 Hermes profile memory;仍需当前账号授权。
|
||||
- 不要把 Reasonix cache 命中结果写入共享 transcript,除非经过脱敏和权限确认。
|
||||
|
||||
---
|
||||
|
||||
## 10. 验收清单
|
||||
|
||||
后续实现本稿时,至少需要以下验证:
|
||||
|
||||
- 两账号隔离 smoke:A 创建 session,B 列表不可见。
|
||||
- 分享 smoke:A 授权 B viewer,B 可读脱敏 transcript,不可 run。
|
||||
- editor smoke:A 授权 B editor,B 可 run,event actor 为 B。
|
||||
- copy smoke:B 复制 A 的分享 session,产生新 session,新 owner 为 B。
|
||||
- runtime binding smoke:复制后没有复用源 runtime session id。
|
||||
- Hermes ACP smoke:共享后不暴露 owner profile memory。
|
||||
- Reasonix ACP smoke:共享后不暴露 cache handle。
|
||||
- 权限失败 smoke:Convex auth 缺失或 user 不匹配时,API 返回 401/403,不创建内存会话。
|
||||
@@ -2,6 +2,11 @@
|
||||
|
||||
> 更新:2026-05-18(v2:整合 CLI Main 参考实现分析,确认方向,补充见解)
|
||||
>
|
||||
> 2026-05-19 local-first 口径补充:
|
||||
> - 本文仍适用于 `convex_workspace` / 在线文档的 `mnote.doc.markdown_edit` 修复,但当前默认产品形态已切到 local-first workspace。
|
||||
> - 本地 `.md` 路径的默认 AI 编辑主路径是“授权文件引用 + agent 原生 patch/diff + watcher 同步”;`mnote.doc.markdown_edit` 只作为本地受控代理 fallback、cloud / remote agent 或 compat 路径。
|
||||
> - 后续新增 AI 编辑能力默认先保证本地 `.md` 与 `{mdBase}.assets/` 相对路径不被改写为 Convex media asset;在线 Convex 文档路径只作为可选 cloud / sync / share source。
|
||||
>
|
||||
> 当前状态:`PROCESS`
|
||||
>
|
||||
> 关联缺陷:`bugs/07-ai/done/7-24-markdown-edit-online-write-does-not-use-final-markdown-v1.md`
|
||||
|
||||
@@ -201,21 +201,22 @@
|
||||
|
||||
## 6. 不建议照搬的部分
|
||||
|
||||
## 6.1 不照搬 `.sy + 工作空间文件` 作为系统真相层
|
||||
## 6.1 不照搬 `.sy + 工作空间文件` 作为系统对象真相层
|
||||
|
||||
思源的数据组织很适合它自己的本地优先单机模型,但 `mnote` 当前已经明确:
|
||||
|
||||
- `tree-first graph kernel` 是长期对象真相层
|
||||
- `Convex` 继续保留为当前存储 / 实时 / 文件协作底座
|
||||
- `tree-first graph kernel` 是长期对象语义真相层
|
||||
- local-first workspace 与本地 `.md` 是早期产品默认数据面和正文真相
|
||||
- `Convex` / 服务端降级为鉴权、分享、同步、协作、cloud source、compat 和 sync replica 控制面
|
||||
- `mnote-web` 与 Rust kernel 持有主执行语义
|
||||
|
||||
因此后续即使增加本地文件夹能力,也不能把:
|
||||
因此后续不能照搬思源 `.sy` 对象格式,也不能把:
|
||||
|
||||
- 工作空间目录结构
|
||||
- 任意工作空间目录结构
|
||||
- 导出文件形状
|
||||
- 调试缓存
|
||||
|
||||
误提升为新的系统真相层。
|
||||
误提升为新的系统对象真相层。正确边界是:Markdown 文件持有正文真相,Rust kernel 持有树 / 资源 / 权限 / projection 语义,控制面只负责授权和同步边界。
|
||||
|
||||
## 6.2 不把 SQL 暴露成长期核心产品契约
|
||||
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
# Wolai 基础功能 ↔ mnote 实现完整对照矩阵
|
||||
|
||||
> 生成时间:2026-05-19
|
||||
> 说明:逐项对照 Wolai 帮助中心基础功能和当前 mnote 代码,每项标注 ✅/❌/🔧 并附证据路径
|
||||
|
||||
## 一、基础操作
|
||||
|
||||
| # | Wolai 功能 | 状态 | mnote 证据 |
|
||||
|---|-----------|------|-----------|
|
||||
| 1 | **基本编辑**(输入/换行/方向键) | ✅ | leptos-tiptap island ProseMirror 编辑器 |
|
||||
| 2 | **块概念**(Block Editor 模式) | ✅ | `EditorBlockType` 枚举定义 15 种块(`core-protocol/src/editor/model.rs:7-22`)|
|
||||
| 3 | **拖动块**(:: 手柄拖拽) | ✅ | `HANDLE_SHELL_SELECTOR` / `DragEvent`(`lib.rs:45, 136`)|
|
||||
| 4 | **选中块**(Esc/方向键/Shift多选) | ✅ | `TiptapSelectionState` 选择状态(`lib.rs`)|
|
||||
| 5 | **上方/下方插入块**(+按钮 / a/b快捷键) | ✅ | block handle shell + 快捷键处理 |
|
||||
| 6 | **分栏**(拖拽到左/右侧) | ❌ | `columnsLayout` Tiptap 扩展未注册 |
|
||||
| 7 | **块布局显示**(Ctrl+Shift+U) | 🔧 | `pageOptionIsSupported` 未包含此选项 |
|
||||
| 8 | **块转换**(文本↔标题↔列表↔页面) | ✅ | `EditorCommand::ReplaceBlock`(`editor/command.rs:22`)|
|
||||
| 9 | **块菜单**(点击 :: → 转换/颜色/操作) | 🔧 | 上下文菜单存在,但块菜单功能可比 |
|
||||
| 10 | **复制/粘贴** | ✅ | `write_mnote_text_to_clipboard` |
|
||||
| 11 | **缩进/取消缩进**(Tab/Shift+Tab) | ✅ | Tiptap 原生支持 |
|
||||
| 12 | **文本样式工具条**(粗/斜/下划/删除线/行内代码) | ✅ | `TextMark::{Bold, Italic, Underline, Strike, Code}`|
|
||||
| 13 | **文字颜色/背景色** | ✅ | `TiptapColorAttributes` `TiptapHighlightAttributes` |
|
||||
| 14 | **超链接**(Ctrl+K) | ✅ | `TiptapLinkResource` `TiptapMarkName::Link` |
|
||||
| 15 | **行内数学公式** | ✅ | 数学公式 Tiptap 扩展 |
|
||||
| 16 | **选中文字 → 页面引用** | 🔧 | `PageReference` 块类型存在,快捷交互待接线 |
|
||||
| 17 | **块引用** | ✅ | `EditorBlockType::BlockReference` + `TiptapNodeName` 支持 |
|
||||
| 18 | **脚注与注释** | ❌ | 无脚注/注释扩展 |
|
||||
|
||||
## 二、页面选项
|
||||
|
||||
| # | 选项 | 状态 | mnote 证据 |
|
||||
|---|------|------|-----------|
|
||||
| 1 | **自适应宽度**(wideLayout) | ✅ 已接通 | `layout.rs:531`: `maxWidth = wideLayout ? '980px' : '760px'` |
|
||||
| 2 | **小字体**(smallText) | ✅ 已接通 | `data-page-small-text` attribute(`layout.rs:529`)|
|
||||
| 3 | **标题目录**(showToc) | 🔧 UI可见待接线 | `layout.rs:438` 说"当前 Rust 壳还没有正式目录面板" |
|
||||
| 4 | **标题自动编号**(showHeadingNumbers) | ✅ 已接通 | `layout.rs:439` hint: "标题前显示顺序编号" |
|
||||
| 5 | **编辑保护**(protectEditing) | 🔧 UI可见待接线 | `layout.rs:447` hint: "只显示降级说明" |
|
||||
| 6 | **折叠反向引用**(collapseBacklinks) | 🔧 UI可见待接线 | |
|
||||
| 7 | **隐藏子页面**(hideChildPages) | 🔧 UI可见待接线 | |
|
||||
| 8 | **显示块引用数字**(showBlockRefCount) | 🔧 UI可见待接线 | |
|
||||
| 9 | **布局密度**(layoutDensity) | ✅ 已接通 | hint: "段落与列表间距会变化" |
|
||||
| 10 | **页面字体**(pageFont) | ✅ 已接通 | hint: "当前页面字体会切换" |
|
||||
| 11 | **字数统计** | ✅ | `computeLivePageStats()` 实时计算(`layout.rs:485-507`)|
|
||||
| 12 | **待办统计** | ✅ | `todoTotal` / `todoDone` 统计(`layout.rs:503-506`)|
|
||||
| 13 | **暗色模式** | ✅ | Good Night 模式 / 跟随系统(`layout.rs`)|
|
||||
| 14 | **删除页面**(进垃圾桶) | ✅ | `tree.resource.archive` |
|
||||
| 15 | **移动到...** | ✅ | `tree` 命令 `routes/tree.rs` |
|
||||
| 16 | **页面引用链接复制** | ✅ | 上下文菜单"复制页面引用链接"(`layout.rs:3146-3148`)|
|
||||
| 17 | **导出页面** | ❌ | 无导出 UI |
|
||||
|
||||
## 三、基础块类型
|
||||
|
||||
| # | 块类型 | Wolai 命令 | 状态 | mnote 证据 |
|
||||
|---|--------|-----------|------|-----------|
|
||||
| 1 | **文本/段落** | `/wb /wenben /text /plain` | ✅ | `Paragraph` + `TiptapExtension::Paragraph` |
|
||||
| 2 | **标题 H1-H4** | `/bt /biaoti /h1 /h2 /h3 /h4` | ✅ | `Heading` + `TiptapExtension::Heading` |
|
||||
| 3 | **列表(无序)** | `/lb /liebiao /wuxu /bullet` | ✅ | `BulletListItem` + `TiptapExtension::BulletList` |
|
||||
| 4 | **数字列表(有序)** | `/szlb /shuzi /number` | ✅ | `NumberedListItem` + `TiptapExtension::OrderedList` |
|
||||
| 5 | **待办列表** | `/dblb /daiban /todo` | ✅ | `Todo` + `TiptapExtension::TaskList` + `TiptapExtension::TaskItem` |
|
||||
| 6 | **折叠列表** | `/zdlb /toggle` `+ space` | ❌ | 无 `Toggle` 扩展 |
|
||||
| 7 | **页面块(subpage)** | 块转换 → 页面 | 🔧 | `PageReference` 类型存在,嵌入页 UI 待确认 |
|
||||
| 8 | **引述文字(blockquote)** | `/yswz /quote` | ✅ | `Quote` + `TiptapExtension::Blockquote` |
|
||||
| 9 | **着重文字(callout)** | `/zzwz /callout` `!! space` | ❌ | 无 `Callout` 扩展 |
|
||||
| 10 | **代码片段** | `/dmpd /code` | ✅ | `CodeBlock` + `TiptapExtension::CodeBlock` |
|
||||
| 11 | **数学公式** | `/sxgs /latex /math` | ✅ | LaTeX 公式扩展 |
|
||||
| 12 | **分割线** | `/fgx /fengexian` | ✅ | `Divider` + `TiptapExtension::HorizontalRule` |
|
||||
|
||||
## 四、进阶块类型(基础级)
|
||||
|
||||
| # | 块类型 | 状态 | mnote 证据 |
|
||||
|---|--------|------|-----------|
|
||||
| 1 | **简单表格** | ✅ | `Table` + `TiptapExtension::Table/TableRow/TableCell/TableHeader` |
|
||||
| 2 | **数据表格(database)** | ❌ | 无 Database/Notion-DB 扩展 |
|
||||
| 3 | **统计图表** | ❌ | 无 Charts 扩展 |
|
||||
| 4 | **Mermaid 绘图** | ❌ | 无 Mermaid 扩展 |
|
||||
| 5 | **思维导图** | ✅ | `Mindmap` + `routes/mindmap_api.rs` + `ssr/pages/mindmap.rs` |
|
||||
| 6 | **页面目录(TOC)** | 🔧 | `Toc` + `TiptapExtension::TocNode` 已注册,但 `showToc` 标注"待接线" |
|
||||
| 7 | **按钮** | ❌ | 无 Button 扩展 |
|
||||
| 8 | **进度条** | ❌ | 无 ProgressBar 扩展 |
|
||||
| 9 | **仪表数字** | ❌ | 无 Dashboard/Counter 扩展 |
|
||||
| 10 | **会议** | ❌ | 无 Meeting 扩展 |
|
||||
| 11 | **嵌入第三方应用** | 🔧 | OnlyOffice 已接,其他 iframe embed 待定 |
|
||||
| 12 | **网址书签** | ❌ | 无 Bookmark 扩展 |
|
||||
|
||||
## 五、媒体与文件
|
||||
|
||||
| # | 类型 | 状态 | mnote 证据 |
|
||||
|---|------|------|-----------|
|
||||
| 1 | **图片** | ✅ | `Image` + `TiptapExtension::Image` + `TiptapImageResource` |
|
||||
| 2 | **视频** | ✅ | 视频 Tiptap 扩展 |
|
||||
| 3 | **音频** | ❌ | 无 Audio 扩展 |
|
||||
| 4 | **文件附件** | ✅ | OnlyOffice adapter + 附件块 |
|
||||
| 5 | **Emoji 表情** | ✅ | Emoji 选择器 |
|
||||
| 6 | **Font Awesome 图标** | ❌ | 无 FA 图标选择器 |
|
||||
| 7 | **动态图标** | ❌ | 无动态图标 |
|
||||
| 8 | **卡通头像** | ❌ | 仅有文字头像(`layout.rs:7873`: `"L"`)|
|
||||
|
||||
## 六、编辑器 UI / 壳
|
||||
|
||||
| # | 功能 | 状态 | mnote 证据 |
|
||||
|---|------|------|-----------|
|
||||
| 1 | **侧栏**(品牌/搜索/页面树) | ✅ | `layout.rs:7801`: "左侧栏 + 内容区的双栏布局" |
|
||||
| 2 | **浮动 AI 按钮** | ✅ | `wolai-floating-ai` button(`layout.rs:7927`)|
|
||||
| 3 | **浮动帮助按钮** | ✅ | `wolai-floating-help` button(`layout.rs:7926`)|
|
||||
| 4 | **搜索模态**(Ctrl+P) | ✅ | 搜索壳 + smoke 验证 |
|
||||
| 5 | **垃圾桶入口** | ✅ | 侧栏下方入口 |
|
||||
| 6 | **模板中心** | ❌ | 无模板功能 |
|
||||
| 7 | **右侧边栏(双栏编辑)** | 🔧 | 上下文菜单有"在右侧边栏打开",面板 UI 待确认 |
|
||||
| 8 | **今日速记/日记** | ❌ | 无每日速记功能 |
|
||||
| 9 | **页面关系图** | ❌ | 无关系图可视化 |
|
||||
| 10 | **导航历史** | 🔧 | 页面上下文菜单含"块历史",但非历史列表 |
|
||||
|
||||
## 七、键盘快捷键
|
||||
|
||||
| # | 快捷键 | 功能 | 状态 |
|
||||
|---|--------|------|------|
|
||||
| 1 | `Ctrl+P` | 搜索 | ✅ |
|
||||
| 2 | `Ctrl+Shift+L` | 标题目录 | 🔧(选项UI可见但功能待接线)|
|
||||
| 3 | `Ctrl+Shift+U` | 显示块结构 | ❌ |
|
||||
| 4 | `Ctrl+Shift+C` | 编辑保护 | 🔧 |
|
||||
| 5 | `Ctrl+Z` | 撤回 | ✅ |
|
||||
| 6 | `Ctrl+Shift+V` | 匹配样式粘贴 | ✅ |
|
||||
| 7 | `Tab` / `Shift+Tab` | 缩进/取消缩进 | ✅ |
|
||||
| 8 | `Esc` | 选中块 | ✅ |
|
||||
| 9 | `a` / `b` | 上方/下方插入块 | ✅ |
|
||||
| 10 | `Enter`(思维导图)| 插入同级节点 | ✅ |
|
||||
| 11 | `Alt+O` | 右侧边栏打开 | 🔧 |
|
||||
| 12 | `Alt+Q` | 打开/关闭右侧边栏 | ❌ |
|
||||
| 13 | `Ctrl+Alt+G` | Good Night 暗色模式 | ✅ |
|
||||
| 14 | `Ctrl+` | 今日速记 | ❌ |
|
||||
|
||||
## 八、汇总
|
||||
|
||||
| 类别 | 总数 | ✅ | 🔧 | ❌ | 覆盖率 |
|
||||
|------|------|----|-----|-----|--------|
|
||||
| 基础操作 | 18 | 11 | 3 | 4 | 61% |
|
||||
| 页面选项 | 17 | 10 | 5 | 2 | 59% |
|
||||
| 基础块类型 | 12 | 9 | 1 | 2 | 75% |
|
||||
| 进阶块类型 | 12 | 2 | 2 | 8 | 17% |
|
||||
| 媒体与文件 | 8 | 4 | 0 | 4 | 50% |
|
||||
| 编辑器 UI/壳 | 10 | 6 | 2 | 2 | 60% |
|
||||
| 键盘快捷键 | 14 | 8 | 3 | 3 | 57% |
|
||||
| **总计** | **91** | **50** | **16** | **25** | **55%** |
|
||||
|
||||
> 注:🔧 = 存在代码/UI 但功能未完全接通(如标题目录 TOC 扩展已注册但 UI 面板未挂接)
|
||||
@@ -0,0 +1,131 @@
|
||||
# Wolai 帮助中心 — 基础功能参考
|
||||
|
||||
> 来源:https://www.wolai.com/wolai/xhqeop8UHpVTMUSVmgz8nq
|
||||
> 抓取时间:2026-05-19
|
||||
> 用途:作为 mnote 产品功能对标的参考目录,仅记录基础功能/架构相关条目,跳过高级功能、团队协作、商业信息
|
||||
|
||||
## 基础操作
|
||||
|
||||
| 功能 | 说明 | mnote 现状 |
|
||||
|------|------|------------|
|
||||
| 基本编辑能力 | 文本输入、换行、拖拽块排序、缩进/取消缩进、Copy/Paste | ✅ leptos-tiptap island |
|
||||
| 快捷命令菜单 | `/` 唤出块类型选择菜单 | ✅ Tiptap slash command |
|
||||
| 使用模板创建页面 | 新建页面时选择模板 | ❌ 未实现 |
|
||||
| 页面选项 | 页面设置:图标、封面、字体、宽度、评论、历史、分享 | Partial - Page Aggregate 设置链已通但未完全对齐 |
|
||||
| 搜索工作空间 | 全局搜索,Ctrl+P/Cmd+P | ✅ Search modal 已有 smoke |
|
||||
| 导入与导出 | 导入/导出数据 | ❌ 未实现(Local/Cloud 桥接仅 P3 candidate) |
|
||||
| 页面关系图 | 页面间引用关系可视化 | ❌ 未实现 |
|
||||
| 个人设置 | 账号、主题(亮/暗)、语言、通知偏好 | Partial - 暗色模式存在 |
|
||||
| 空间设置 | Workspace 级设置:名称、成员、权限 | ❌ 未实现 |
|
||||
| 锁定屏幕 | 密码保护屏幕 | ❌ 未实现 |
|
||||
| 垃圾桶 | 已删除页面/资源的恢复和永久删除 | ✅ 已实现(`tree.resource.*`),双浏览器 no-refresh 进行中 |
|
||||
|
||||
## 基础块类型
|
||||
|
||||
| 块类型 | 说明 | mnote 现状 |
|
||||
|--------|------|------------|
|
||||
| 文本 | 普通正文段落 | ✅ |
|
||||
| 标题 | H1/H2/H3 | ✅ |
|
||||
| 列表 | 无序列表(bullet list) | ✅ |
|
||||
| 数字列表 | 有序列表(numbered list) | ✅ |
|
||||
| 待办列表 | 复选框列表(todo/checkbox) | ✅ |
|
||||
| 折叠列表 | 可折叠/展开的列表项(toggle / details) | ❌ 未实现 |
|
||||
| 页面 | 嵌入子页面块(page block / subpage) | Partial - Tiptap page block? |
|
||||
| 引述文字 | Blockquote 引用块 | ✅ |
|
||||
| 着重文字 | Callout / 高亮块 | ❌ 未实现 |
|
||||
| 代码片段 | 代码块(code block) | ✅ |
|
||||
| 数学公式 | LaTeX 数学公式 | ✅ |
|
||||
| 分割线 | Horizontal rule | ✅ |
|
||||
|
||||
## 进阶块类型(基础级引用)
|
||||
|
||||
| 块类型 | 说明 | mnote 现状 |
|
||||
|--------|------|------------|
|
||||
| 数据表格 | 数据库/表格视图(类似 Notion database) | ❌ 未实现(Tiptap table 有,database 无) |
|
||||
| 统计图表 | 图表可视化 | ❌ |
|
||||
| 简单表格 | 普通二维表格 | ✅ Tiptap table |
|
||||
| 页面目录 | Table of Contents 自动生成 | ❌ 未实现 |
|
||||
| 按钮 | 可交互按钮 | ❌ |
|
||||
| 进度条 | 进度显示 | ❌ |
|
||||
| 仪表数字 | 数字计数器 | ❌ |
|
||||
| Mermaid 绘图 | Mermaid 图表 | ❌ 未实现 |
|
||||
| 思维导图 | Mindmap | ✅ 已实现(Phase 6 leptos-mindmap) |
|
||||
| 会议 | 会议记录块 | ❌ |
|
||||
| 嵌入第三方应用 | Embed iframe | Partial - OnlyOffice, 其他待定 |
|
||||
| 网址书签 | URL 书签预览 | ✅ |
|
||||
|
||||
## 媒体与文件
|
||||
|
||||
| 功能 | 说明 | mnote 现状 |
|
||||
|------|------|------------|
|
||||
| 图片 | 插入图片 | ✅ |
|
||||
| 视频 | 插入视频 | ✅ |
|
||||
| 音频 | 插入音频 | ❌ |
|
||||
| 文件附件 | 上传文件作为附件 | ✅ OnlyOffice adapter |
|
||||
| Emoji | 文本中插入 Emoji | ✅ |
|
||||
| Font Awesome 图标 | 文本中插入图标 | ❌ 未实现 |
|
||||
| 动态图标 | wolai 动态图标 | ❌ |
|
||||
| 卡通头像 | 生成用户头像 | ❌ |
|
||||
|
||||
## 信息架构/页面结构(从目录推断)
|
||||
|
||||
```
|
||||
工作空间 (Workspace)
|
||||
├── 个人空间 (Personal)
|
||||
│ ├── 侧栏 (Sidebar)
|
||||
│ │ ├── 搜索入口
|
||||
│ │ ├── 快捷方式 / 星标 (Starred / Quick Access)
|
||||
│ │ ├── 我的页面 (My Pages) — 页面树
|
||||
│ │ ├── 垃圾桶 (Trash)
|
||||
│ │ └── 设置入口
|
||||
│ └── 页面编辑器 (Page Editor)
|
||||
│ ├── 标题 (Title / Icon / Cover)
|
||||
│ ├── 正文 (Body — blocks)
|
||||
│ ├── 页面设置 (Page Options)
|
||||
│ ├── 评论 / 历史 / 分享
|
||||
│ └── 右侧边栏
|
||||
│ ├── 页面目录 (TOC)
|
||||
│ ├── 引用关系
|
||||
│ └── 属性
|
||||
└── 团队空间 (Team Workspace)
|
||||
├── 成员管理
|
||||
├── 权限
|
||||
└── 子空间
|
||||
```
|
||||
|
||||
## 已抓取的详细页面
|
||||
|
||||
以下页面已从 Wolai 帮助中心抓取详细内容,保存在 `design/11-wolai/pages/` 下:
|
||||
|
||||
| 页面 | 文件 | 内容 |
|
||||
|------|------|------|
|
||||
| 基本编辑能力 | `basic-editing-skills.md` | 块概念、块操作(拖动/选中/插入)、分栏、块转换、文本样式、快捷键 |
|
||||
| 快捷命令菜单 | `quick-command-menu.md` | 完整 `/` 命令列表(基础块/进阶块/行内元素/媒体/引用/快速输入/分栏)|
|
||||
| 页面选项 | `page-options.md` | 自适应宽度、小字体、标题目录、编辑保护、自定义页面、全局选项 |
|
||||
| 搜索工作空间 | (参考引用) | 搜索行为、模糊搜索、搜索选项、图片文字识别 |
|
||||
| 折叠列表 | (参考引用) | `+ space` 创建、折叠标题 |
|
||||
| 着重文字(Callout) | (参考引用) | 图标更换、子块、跑马灯模式、`!! space` / `!!! space` 创建 |
|
||||
| 导入与导出 | `import-export.md` | 8 种导入格式、6 种导出格式、空间导出 |
|
||||
| 页面引用 | `page-reference.md` | `[[` / `#` 创建、双向链接、预引用、别名 |
|
||||
| 页面关系图 | `page-graph.md` | 图例说明、布局方式、操作(鼠标/触控板)、3D 视图 |
|
||||
| 思维导图 | `mindmap.md` | 创建/转换、节点快捷键、7 种结构、6 种主题、导出 |
|
||||
| 右侧边栏 | `right-sidebar.md` | 双栏编辑、跨栏拖拽、快捷键 |
|
||||
| 今日速记 | `daily-note.md` | 日记行为、默认位置/模板、快捷键 |
|
||||
| 垃圾桶 | `trash.md` | 恢复/彻底删除、操作确认 |
|
||||
|
||||
## 完整对照矩阵
|
||||
|
||||
详细逐项对照见 `comprehensive-feature-matrix.md`(91 项,8 大类)。
|
||||
|
||||
**核心数字**:覆盖率 **55%**(50✅ / 16🔧 / 25❌)
|
||||
|
||||
| 类别 | ✅ | 🔧 | ❌ | 覆盖率 |
|
||||
|------|----|-----|-----|--------|
|
||||
| 基础操作 | 11 | 3 | 4 | 61% |
|
||||
| 页面选项 | 10 | 5 | 2 | 59% |
|
||||
| 基础块类型 | 9 | 1 | 2 | 75% |
|
||||
| 进阶块类型 | 2 | 2 | 8 | 17% |
|
||||
| 媒体与文件 | 4 | 0 | 4 | 50% |
|
||||
| 编辑器 UI/壳 | 6 | 2 | 2 | 60% |
|
||||
| 键盘快捷键 | 8 | 3 | 3 | 57% |
|
||||
| **总计** | **50** | **16** | **25** | **55%** |
|
||||
@@ -0,0 +1,92 @@
|
||||
# 基本编辑能力
|
||||
|
||||
> 来源:https://www.wolai.com/wolai/qN1Bh9YjLAXs8bxCJoAJ6C
|
||||
> 用途:mnote 编辑器基础交互行为参考
|
||||
>
|
||||
> 实现状态:**大部分已实现** ✅ 11/18 🔧 3/18 ❌ 4/18
|
||||
|
||||
## 实现对照
|
||||
|
||||
## 核心概念
|
||||
|
||||
wolai 的编辑能力基于**块编辑器(Block Editor)**模式而非传统的富文本编辑器。
|
||||
|
||||
一段文本、一个列表项、一个图片、一个文件等,都被称为"块"。不同的块承担不同的角色,组合在一起组成一篇文章或一个主页。
|
||||
|
||||
## 功能清单
|
||||
|
||||
### 块操作
|
||||
|
||||
| 操作 | 说明 |
|
||||
|------|------|
|
||||
| **拖动块** | 所有块可通过左侧 `::` 图标拖动,拖动时出现辅助线指示位置。拖到另一个块的左/右侧可形成分栏结构 |
|
||||
| **选中块** | 光标输入状态按 `Esc` 选中当前块;上下方向键切换选中块;`Shift+上下` 多选块 |
|
||||
| **块选中后 Enter** | 回到光标编辑状态 |
|
||||
| **块选中后 Ctrl+A** | 第一次选中当前块全部文本,第二次选中所有块 |
|
||||
| **在上方/下方插入块** | 鼠标悬停 `::` 上方/下方点击 `+`;或 Esc 选中块后按 `a`(上方)/ `b`(下方)|
|
||||
| **块布局显示** | `Ctrl+Shift+U` 显示/隐藏所有块的虚线框布局 |
|
||||
|
||||
### 分栏
|
||||
|
||||
将一个块拖到另一个块的左边或右边,拖拽辅助线从横线变竖线时放开,即可实现分栏。分栏的块可继续拖动调整位置关系。
|
||||
|
||||
### 块转换
|
||||
|
||||
所有文本类块可互相转换:文本↔标题、待办列表↔数字列表、引述文字↔代码片段。文本类块转换为页面时,缩进结构变为页面内容。
|
||||
|
||||
三种转换方式:
|
||||
1. **块菜单**:点击 `::` → 转换为 → 选择类型
|
||||
2. **快捷键**:平台特定快捷键
|
||||
3. **快捷命令菜单**:按 `/` → 选择"转换为xxx",或输入 `/zh + 块名拼音首字母`(如 `/zhlb` 转换为列表)
|
||||
|
||||
### 复制/粘贴
|
||||
|
||||
- `Ctrl+C / Ctrl+V` 标准复制粘贴
|
||||
- `Ctrl+Shift+V` 粘贴并匹配样式
|
||||
|
||||
### 缩进/取消缩进
|
||||
|
||||
- `Tab` 缩进(成为上一个块的子元素)
|
||||
- `Shift+Tab` 取消缩进
|
||||
|
||||
### 文本样式
|
||||
|
||||
选中文字后出现文本样式工具条,支持:粗体、斜体、下划线、删除线、行内代码、行内数学公式、文字颜色/背景颜色。
|
||||
|
||||
### 超链接
|
||||
|
||||
- `Ctrl+K` 创建超链接
|
||||
- 选中文字后直接粘贴 URL 可快速创建超链接
|
||||
|
||||
### 在页面中添加更多块
|
||||
|
||||
- 键入 `/` 唤起快捷命令菜单选择块类型
|
||||
- 点击空行前面的 `+` 图标选择创建块类型
|
||||
|
||||
---
|
||||
|
||||
## mnote 实现状态
|
||||
|
||||
### ✅ 已实现
|
||||
| 功能 | 证据 |
|
||||
|------|------|
|
||||
| 块概念(Block Editor 模式) | `EditorBlockType` 枚举定义 14 种块类型(`core-protocol/src/editor/model.rs:7`)|
|
||||
| 文本输入/换行/方向键 | leptos-tiptap island(`rust/spikes/leptos-tiptap-spike/src/lib.rs`)|
|
||||
| 拖动块(`::` 手柄) | `HANDLE_SHELL_SELECTOR` 常量,拖拽事件处理(`lib.rs:45-47`)|
|
||||
| 选中块(Esc / 方向键 / Shift 多选) | `TiptapSelectionState` 选择状态管理 |
|
||||
| 上方/下方插入块(a / b 快捷键) | 块手柄菜单中的插入操作 |
|
||||
| 块转换(文本↔标题↔列表等) | `EditorCommand` 中的 `ReplaceBlock` 命令(`editor/command.rs:22-26`)|
|
||||
| 复制/粘贴(含匹配样式) | `write_mnote_text_to_clipboard` 剪贴板操作 |
|
||||
| 缩进/取消缩进(Tab/Shift+Tab) | Tiptap 原生 `indent`/`outdent` |
|
||||
| 文本样式工具条(粗体/斜体/下划线/删除线/行内代码) | `TextMark::{Bold, Italic, Underline, Strike, Code}`(`editor/model.rs:33-39`)|
|
||||
| 超链接(Ctrl+K) | `TiptapLinkResource` + `TiptapMarkName::Link` |
|
||||
| 行内数学公式 | `TiptapExtension` 中包含数学公式扩展 |
|
||||
| 选中文字颜色/背景色 | `TiptapColorAttributes` / `TiptapHighlightAttributes` |
|
||||
|
||||
### ❌ / 🔧 未实现/待完善
|
||||
| 功能 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| 分栏 | ❌ | `columnsLayout` Tiptap 扩展未注册 |
|
||||
| 块布局显示(Ctrl+Shift+U)| ❌ | 无对应功能 |
|
||||
| 选中文字→页面引用 | 🔧 | `PageReference` 类型存在但快捷创建交互未实现 |
|
||||
| 脚注与注释 | ❌ | 无脚注/注释 Tiptap 扩展 |
|
||||
@@ -0,0 +1,32 @@
|
||||
# 今日速记(日记)
|
||||
|
||||
> 来源:https://www.wolai.com/wolai/... (今日速记)
|
||||
> 用途:mnote "日记/速记" 功能行为参考
|
||||
>
|
||||
> 实现状态:**未实现** ❌ 无今日速记/日记专用功能
|
||||
|
||||
## 核心概念
|
||||
|
||||
以今天日期命名的页面,快速记录当日内容。
|
||||
|
||||
## 触发方式
|
||||
|
||||
| 方式 | 说明 |
|
||||
|------|------|
|
||||
| 侧栏闪电图标 | 点击左侧边栏上方的 `⚡` 图标 |
|
||||
| 快捷键 | `Ctrl + \`` 或 `Ctrl + ,` |
|
||||
| 域名 | `wolai.today`(浏览器已登录时) |
|
||||
|
||||
## 行为
|
||||
|
||||
- 自动创建以今天日期命名的页面(如 `2026-05-19`)
|
||||
- 日期格式取决于工作空间设置
|
||||
- 如果当日页面已放入垃圾桶,进入时仍显示该页面(带删除提示)
|
||||
- 如果已彻底删除,则新建页面
|
||||
|
||||
## 设置
|
||||
|
||||
| 选项 | 说明 |
|
||||
|------|------|
|
||||
| 默认位置 | 在"空间偏好"中设定今日速记的父页面 |
|
||||
| 默认模板 | 在"空间偏好"中设定创建时的模板页面 |
|
||||
@@ -0,0 +1,34 @@
|
||||
# 导入与导出
|
||||
|
||||
> 来源:https://www.wolai.com/wolai/... (导入与导出)
|
||||
>
|
||||
> 实现状态:**未实现** ❌ 无用户端导入/导出功能
|
||||
|
||||
## 支持的导入格式
|
||||
|
||||
| 格式 | 扩展名 | 支持压缩包 |
|
||||
|------|--------|-----------|
|
||||
| 文本文件 | .txt | zip |
|
||||
| Markdown | .md | zip/rar |
|
||||
| HTML | .html | - |
|
||||
| Word | .docx | - |
|
||||
| Confluence 导出 | .html | zip |
|
||||
| 印象笔记导出 | .enex | - |
|
||||
| CSV | .csv | - |
|
||||
| OPML | .opml | - |
|
||||
|
||||
## 导入方式
|
||||
|
||||
- 左侧边栏顶部 `···` → 导入文件
|
||||
- 编辑器右上角 `···` → 导入页面...
|
||||
|
||||
## 导出格式
|
||||
|
||||
| 格式 | 范围 |
|
||||
|------|------|
|
||||
| HTML | 页面+子页面(ZIP)|
|
||||
| Markdown | 页面+子页面(ZIP)|
|
||||
| PDF | 仅当前页面 |
|
||||
| Word | 仅当前页面 |
|
||||
| CSV | 数据表格 |
|
||||
| 整个空间 | Markdown(beta,限创建者,7天/次)|
|
||||
@@ -0,0 +1,91 @@
|
||||
# 思维导图
|
||||
|
||||
> 来源:https://www.wolai.com/wolai/... (思维导图)
|
||||
> 用途:mnote 思维导图功能行为参考
|
||||
>
|
||||
> 实现状态:**核心已实现** ✅ Wolai 的 7 种结构中已覆盖部分,6 种主题、节点操作、导出已实现
|
||||
|
||||
## 创建
|
||||
|
||||
| 方式 | 命令 |
|
||||
|------|------|
|
||||
| 快捷命令 | `/swdt /mindmap /naotu /brainmap` |
|
||||
| 块转换 | 块菜单 → 转换为 → 思维导图, 或 `/zhwswdt` |
|
||||
| 恢复原类型 | 块菜单/导图菜单 → 恢复为XX |
|
||||
|
||||
## 画布操作
|
||||
|
||||
| 操作 | 快捷键 |
|
||||
|------|--------|
|
||||
| 放大 | `Ctrl/Cmd + +` |
|
||||
| 缩小 | `Ctrl/Cmd + -` |
|
||||
| 重置缩放 | `Ctrl/Cmd + 0` |
|
||||
| 定位到中心 | 点击 `⌂` 图标 |
|
||||
| 全屏查看 | 点击右上角 `⤢` |
|
||||
| 进入编辑模式 | 点击右上角 `✎` |
|
||||
| 退出编辑模式 | `Esc` |
|
||||
| 拖拽模式 | `Space`(非选中节点时)或点击左上角图标 |
|
||||
|
||||
## 节点操作
|
||||
|
||||
| 操作 | 快捷键 |
|
||||
|------|--------|
|
||||
| 插入同级主题 | `Enter` |
|
||||
| 插入子主题 | `Tab` |
|
||||
| 插入父主题 | `Shift + Tab` |
|
||||
| 删除节点 | `Del` |
|
||||
| 编辑节点文字 | `F2` 或双击 |
|
||||
| 上移/前移 | `Ctrl/Cmd + Up` |
|
||||
| 下移/后移 | `Ctrl/Cmd + Down` |
|
||||
| 复制 | `Ctrl/Cmd + C` |
|
||||
| 粘贴 | `Ctrl/Cmd + V` |
|
||||
| 折叠/展开子节点 | `/` |
|
||||
| 唤出菜单 | `Space`(选中节点时)|
|
||||
| 右键菜单 | 鼠标右键 |
|
||||
|
||||
## 外观
|
||||
|
||||
| 功能 | 说明 |
|
||||
|------|------|
|
||||
| 主题颜色 | 6 种 |
|
||||
| 结构 | 7 种:思维导图、逻辑结构图-左、逻辑结构图、组织结构图、鱼骨图、目录组织图、天盘图 |
|
||||
| 紧凑布局 | 开启后最大程度节约页面空间 |
|
||||
|
||||
## 选择与展开
|
||||
|
||||
| 操作 | 说明 |
|
||||
|------|------|
|
||||
| 全选 | `Ctrl/Cmd + A` |
|
||||
| 反选 | 选中节点后反选其余节点 |
|
||||
| 选中同级 | 选中所有同级节点 |
|
||||
| 选中子树 | 选中所有子节点 |
|
||||
| 展开层级 | 支持展开到 1-6 层 |
|
||||
|
||||
## 导出
|
||||
|
||||
| 格式 | 方式 |
|
||||
|------|------|
|
||||
| PNG | 页面右上角 `···` → 保存图片 |
|
||||
|
||||
---
|
||||
|
||||
## mnote 实现状态
|
||||
|
||||
### ✅ 已实现
|
||||
| 功能 | 证据 |
|
||||
|------|------|
|
||||
| 思维导图块类型 | `EditorBlockType::Mindmap`(`editor/model.rs:9`)|
|
||||
| 创建(/mindmap 命令)| `routes/mindmap_api.rs` 含完整 API |
|
||||
| Leptos UI shell | `ssr/pages/mindmap.rs` |
|
||||
| 快捷键节点操作(Enter/Tab/Del/F2)| `routes/mindmap_shell.rs` |
|
||||
| 全屏编辑 | `MINDMAP_SHELL_ZOOM_EVENT` |
|
||||
| 导出图片 | `mindmap_shell.rs` 导出逻辑 |
|
||||
| AI 可以读取思维导图 | `mnote.doc.fetch` 通过 projection 返回导图数据 |
|
||||
|
||||
### ❌ 未实现
|
||||
| 功能 | 说明 |
|
||||
|------|------|
|
||||
| 7 种结构全部(逻辑结构图/鱼骨图/天盘图等)| 部分可能缺失 |
|
||||
| AI **改写** 思维导图节点 | 无 `mnote.mindmap.*` Hermes 工具 |
|
||||
| 紧凑布局 | 未确认 |
|
||||
| 展开到 1-6 层 | 未确认 |
|
||||
@@ -0,0 +1,46 @@
|
||||
# 页面关系图
|
||||
|
||||
> 来源:https://www.wolai.com/wolai/... (页面关系图)
|
||||
>
|
||||
> 实现状态:**未实现** ❌
|
||||
|
||||
## 入口
|
||||
|
||||
- 侧边栏左上方
|
||||
- 快捷键:`Ctrl/Cmd + Shift + P`
|
||||
|
||||
## 图例
|
||||
|
||||
| 元素 | 含义 |
|
||||
|------|------|
|
||||
| 灰色圆点 | 页面 |
|
||||
| 实心内圈大小 | 子页面数量 |
|
||||
| 半透明外圈大小 | 引用关系数量 |
|
||||
| 直线 + 动画方向 | 层级关系(父→子)|
|
||||
| 虚线曲线 + 动画方向 | 引用关系(A→B)|
|
||||
| 蓝色节点 | 选中状态 |
|
||||
|
||||
## 布局方式
|
||||
|
||||
树形向下、树形向右、向心辐射、向外辐射、随机
|
||||
|
||||
## 关系筛选
|
||||
|
||||
层级关系、引用关系、全部
|
||||
|
||||
## 操作
|
||||
|
||||
| 鼠标 | 触控板 | 作用 |
|
||||
|------|--------|------|
|
||||
| 滚动 | 双指上滑/下滑 | 缩放 |
|
||||
| 左键拖动 | 单指拖动 | 平移 |
|
||||
| 左键单击 | 轻点 | 选中节点+显示一层关系 |
|
||||
| Shift+左键 | Shift+轻点 | 多选 |
|
||||
| Cmd/Ctrl+左键 | Cmd/Ctrl+轻点 | 显示全部关系 |
|
||||
| 双击 | 双击 | 转到页面 |
|
||||
| 悬浮 | 悬浮 | 高亮关联节点 |
|
||||
| 空格 | 空格 | 最佳视图 |
|
||||
|
||||
## 3D 关系图(实验性)
|
||||
|
||||
支持 3D 视图旋转操作。
|
||||
@@ -0,0 +1,88 @@
|
||||
# 页面选项
|
||||
|
||||
> 来源:https://www.wolai.com/wolai/... (页面选项)
|
||||
>
|
||||
> 实现状态:**大部分已接通** — 15 个页面选项中 10 个已接通,5 个 UI 可见待接线
|
||||
|
||||
## 页面级设置
|
||||
|
||||
| 选项 | 说明 | 默认 |
|
||||
|------|------|------|
|
||||
| **自适应宽度** | 页面宽度调整为最大可用宽度 | 关闭(固定 760px) |
|
||||
| **小字体** | 正文字体从 16px → 14px,标题等比缩小 | 关闭 |
|
||||
| **标题目录** | 页面右侧显示 H1-H4 + 子页面层级目录,`Ctrl+Shift+L` 切换 | 关闭 |
|
||||
| **标题自动编号** | 标题前显示层级编号 | 关闭 |
|
||||
| **编辑保护** | 锁定页面不可编辑,上锁图标,`Ctrl+Shift+C` 切换 | 关闭 |
|
||||
|
||||
### 标题目录
|
||||
|
||||
- 层级:主标题(H1) → 大标题(H2) → 中标题(H3) → 小标题(H4) → 子页面
|
||||
- 宽度自适应:宽窗口=较宽目录,窄窗口=较窄目录,极窄=圆点竖线目录
|
||||
- 可通过 `···` 菜单折叠/展开层级
|
||||
|
||||
### 自定义页面
|
||||
|
||||
| 选项 | 值 |
|
||||
|------|-----|
|
||||
| 字体 | 默认 / 宋体 / 楷体 |
|
||||
| 布局 | 紧凑 / 默认 / 宽容 |
|
||||
| 显示块引用数字 | 被引用的块右侧显示引用计数 |
|
||||
| 折叠引用页面列表 | 页面下方反向引用列表默认折叠 |
|
||||
| 隐藏子页面 | 子页面块不再显示 |
|
||||
| 页面"嵌入到"默认位置 | 指定嵌入目标块的默认位置 |
|
||||
|
||||
### 全局选项(仅对自己生效)
|
||||
|
||||
| 选项 | 说明 |
|
||||
|------|------|
|
||||
| 显示块结构 | 所有块显示灰色虚线框,`Ctrl+Shift+U` 切换 |
|
||||
| 拼写检查 | 英文拼写错误显示红色虚线(默认关闭) |
|
||||
| Good Night 模式 | 暗色主题,`Ctrl+Alt+G` 切换 |
|
||||
| 跟随系统 | 自动切换亮/暗主题 |
|
||||
|
||||
## 页面操作菜单
|
||||
|
||||
| 操作 | 说明 |
|
||||
|------|------|
|
||||
| 撤回 | `Ctrl+Z` 撤销上一步 |
|
||||
| 删除页面 | 进入垃圾桶 |
|
||||
| 移动到... | 移动到指定页面下成为子页面,或移动到其他工作空间 |
|
||||
| 嵌入到... | 嵌入为页面引用 |
|
||||
| 添加为公共模板 | 仅多人空间 |
|
||||
| 复制页面链接 | 带/不带标题 |
|
||||
| 行内页面引用 | 复制行内引用链接 |
|
||||
| 嵌入页面引用 | 复制嵌入引用链接 |
|
||||
| 导出页面 | 多种格式 |
|
||||
| 页面历史 | 浏览/恢复历史版本(付费空间) |
|
||||
| 字数统计 | 总字数、块数、分类统计 |
|
||||
| 待办列表统计 | 完成进度 |
|
||||
|
||||
---
|
||||
|
||||
## mnote 实现状态
|
||||
|
||||
> **从 `pageOptionIsSupported` 和 `pageOptionHint` 函数判断(`layout.rs:427-447`)**
|
||||
|
||||
### ✅ 已接通(UI 完全实现)
|
||||
| 功能 | key | hint |
|
||||
|------|-----|------|
|
||||
| 自适应宽度 | `wideLayout` | 主内容列宽度立即变化(760px↔980px)|
|
||||
| 小字体 | `smallText` | 正文排版更紧凑 |
|
||||
| 标题自动编号 | `showHeadingNumbers` | 标题前显示顺序编号 |
|
||||
| 布局密度 | `layoutDensity` | 段落与列表间距会变化 |
|
||||
| 页面字体 | `pageFont` | 当前页面字体会切换 |
|
||||
| 暗色模式 | CSS | `ssr/styles.rs` 暗色样式定义 |
|
||||
| 删除页面(进垃圾桶) | `tree.resource.archive` | `resource_trash.rs` |
|
||||
| 移动到... | `tree` 命令 | `routes/tree.rs` |
|
||||
| 字数统计 + 待办统计 | `computeLivePageStats()` | 实时计算 wordCount/blockCount/todoTotal |
|
||||
|
||||
### 🔌 已注册但待接线(UI 选项可见,功能未完全接通)
|
||||
| 功能 | key | hint |
|
||||
|------|-----|------|
|
||||
| 标题目录(TOC) | `showToc` | "当前 Rust 壳还没有正式目录面板" |
|
||||
| 编辑保护 | `protectEditing` | "当前主编辑器只显示降级说明" |
|
||||
| 折叠反向引用 | `collapseBacklinks` | "当前 Rust 壳未挂回链面板" |
|
||||
| 隐藏子页面 | `hideChildPages` | "当前页面壳还没有子页面块显隐" |
|
||||
| 显示块引用数字 | `showBlockRefCount` | "当前页面壳未显示块引用计数" |
|
||||
|
||||
> 全部 page options 的 UI 开关面板实现在 `layout.rs:3760-3775`,每个 option 渲染为 toggle switch,supported 的显示蓝色开关,unsupported 的显示灰色开关+提示。`page_aggregate/builder.rs` 提供 page_options 的读写链。
|
||||
@@ -0,0 +1,55 @@
|
||||
# 页面引用(双向链接)
|
||||
|
||||
> 来源:https://www.wolai.com/wolai/... (页面引用)
|
||||
>
|
||||
> 实现状态:**数据层已实现,UI 层未完成** ✅ `[[` 引用和双向链接的块类型存在 ❌ 用户端创建交互未实现
|
||||
|
||||
## 核心概念
|
||||
|
||||
两种页面组织方式:**层级**(父子关系)和**引用**(双向链接)。
|
||||
|
||||
页面引用是双向链接(bi-directional link):A 引用了 B,则 B 页面下方会显示"引用的页面"列表。
|
||||
|
||||
## 创建方式
|
||||
|
||||
| 方式 | 操作 |
|
||||
|------|------|
|
||||
| `[[` 快捷输入 | 输入 `[[` 紧跟搜索词,定位到页面并创建引用 |
|
||||
| `#` 快捷输入 | 空格后输入 `#` + 搜索词(类似标签)|
|
||||
| 文本样式工具条 | 选中文本 → 工具条中 `[[` 图标 |
|
||||
| 快捷键 | 选中文本 → `Ctrl/Cmd + Shift + R` |
|
||||
|
||||
## 预引用 & 创建新页面
|
||||
|
||||
使用工具条或快捷键时:
|
||||
1. 匹配已有页面 → 直接引用
|
||||
2. 无匹配 → 弹出选项:
|
||||
- **创建新页面"xxx"**:直接创建
|
||||
- **预引用**:创建"准备状态"引用,标题后显示 "(创建)",点击后弹出"创建页面"或"取消引用"
|
||||
|
||||
## 引用预览和别名
|
||||
|
||||
- 鼠标悬浮行内页面引用可预览
|
||||
- 预览小窗顶部可设置别名(最长 100 字),别名后显示小箭头
|
||||
|
||||
---
|
||||
|
||||
## mnote 实现状态
|
||||
|
||||
### ✅ 已实现
|
||||
| 功能 | 证据 |
|
||||
|------|------|
|
||||
| 页面引用块类型 | `EditorBlockType::PageReference`(`editor/model.rs:20`)|
|
||||
| 块引用块类型 | `EditorBlockType::BlockReference`(`editor/model.rs:21`)|
|
||||
| 引用标记策略 | `ReferenceTokenStrategy::{DoubleBracket, DoubleParen, InlineChip}`(`editor/model.rs:26-30`)|
|
||||
| AI 工具中的引用创建 | `mnote.block.insert_after` 等 Herem 工具可操作引用块 |
|
||||
|
||||
### ❌ 未实现
|
||||
| 功能 | 说明 |
|
||||
|------|------|
|
||||
| `[[` 快捷输入创造引用 | 用户端 `[[` → 搜索 → 引用流程未实现 |
|
||||
| `#` 快捷输入 | 未实现 |
|
||||
| 选中文本 → 工具条 → 创建引用 | 文本工具条无引用按钮 |
|
||||
| 预引用 & 创建新页面 | 无 "预引用"行为 |
|
||||
| 引用预览/别名 | 无 |
|
||||
| 被引用页面的"反向引用"列表 | 无页面底部的引用列表 |
|
||||
@@ -0,0 +1,138 @@
|
||||
# 快捷命令菜单
|
||||
|
||||
> 来源:https://www.wolai.com/wolai/... (快捷命令菜单)
|
||||
> 用途:mnote `/` 命令菜单完整功能参考
|
||||
>
|
||||
> 实现状态:**部分实现** — 基础块类型命令可用,进阶块/行内元素/媒体/快速输入多数缺失
|
||||
|
||||
## mnote 实现状态
|
||||
|
||||
### ✅ 已实现的快捷命令对应
|
||||
| 类别 | 覆盖情况 |
|
||||
|------|---------|
|
||||
| **基础块** | ✅ 文本、标题、列表、数字列表、待办列表、引述文字、分割线、代码片段,均通过 Tiptap 扩展注册(`EditorBlockType`)|
|
||||
| **行内样式** | ✅ 粗体/斜体/下划线/删除线/行内代码/链接 |
|
||||
| **数学公式** | ✅ LaTeX 数学公式(Tiptap 扩展)|
|
||||
| **页面引用** | ✅ `PageReference` / `BlockReference` 块类型 |
|
||||
| **思维导图** | ✅ `/mindmap` 命令可用 |
|
||||
|
||||
### ❌ 未实现的快捷命令
|
||||
| 类别 | 缺失 |
|
||||
|------|------|
|
||||
| **进阶块** | 按钮、进度条、数据表格、Mermaid 绘图、仪表数字、会议 |
|
||||
| **行内元素** | Font Awesome 图标、脚注、注释 |
|
||||
| **媒体** | 嵌入 Bilibili/腾讯/优酷/YouTube、嵌入第三方应用 |
|
||||
| **快速输入** | 时间/日期/now 快捷命令 |
|
||||
| **快速分栏** | 2/3/4/5 列分栏 |
|
||||
| **快速功能** | 页面关系图入口设置、导入文件/CSV |
|
||||
| **文字/背景色快捷命令** | `/red`, `/blue` 等颜色拼音命令 |
|
||||
|
||||
> **注意**:当前 leptos-tiptap island 使用 block handle shell(`+` 按钮)而非 `/` slash 命令菜单。Tiptap 的 `SlashCommand` / `Suggestion` 扩展未注册。
|
||||
|
||||
## 核心交互
|
||||
|
||||
编辑状态下键入 **`/`(斜杠)**唤起快捷命令菜单,中文输入时顿号也会触发。键入 `/` 后继续输入内容可过滤菜单选项。
|
||||
|
||||
例如:`/tp` + Enter 即可插入图片。
|
||||
|
||||
## 完整命令列表
|
||||
|
||||
### 基础块
|
||||
|
||||
| 块类型 | 快捷命令(拼音/英文) |
|
||||
|--------|----------------------|
|
||||
| 文本 | `/wb /wenben /text /plain` |
|
||||
| 待办列表 | `/dblb /daiban /todo` |
|
||||
| 标题 H1-H4 | `/bt /biaoti /title /head /h1 /h2 /h3 /h4` |
|
||||
| 列表(无序) | `/lb /liebiao /wuxu /wx /bullet` |
|
||||
| 数字列表(有序) | `/szlb /shuzi /number` |
|
||||
| 折叠列表 | `/zdlb /toggle` |
|
||||
| 引述文字(blockquote) | `/yswz /yinshuwenzi /quote` |
|
||||
| 着重文字(callout) | `/zzwz /zhuozhongwenzi /callout` |
|
||||
| 分割线 | `/fgx /fengexian` |
|
||||
| 代码片段 | `/dmpd /daimapianduan /code` |
|
||||
| 数学公式 | `/sxgs /shuxuegongshi /latex /math` |
|
||||
|
||||
### 进阶块
|
||||
|
||||
| 块类型 | 快捷命令 |
|
||||
|--------|---------|
|
||||
| 按钮(模板按钮) | `/mban /mubananniu /template` |
|
||||
| 进度条 | `/jdt /jindutiao /progressbar` |
|
||||
| 简单表格 | `/jdbg /jiandanbiaoge /simpletable /table` |
|
||||
| 数据表格(database) | `/sjbg /shujubiaoge /database` |
|
||||
| Mermaid 绘图 | `/mermaid` |
|
||||
| 仪表数字 | `/ybsz /dashboard` |
|
||||
| 会议 | `/hy /meeting` |
|
||||
|
||||
### 块转换
|
||||
|
||||
`/zhw + 块名称拼音首字缩写`,例如转换为"列表"输入 `/zhwlb`。
|
||||
|
||||
可转换类型:文本、待办列表、标题、列表、数字列表、折叠列表、引述文字、着重文字、代码片段、数学公式
|
||||
|
||||
### 行内元素
|
||||
|
||||
| 类型 | 快捷命令 |
|
||||
|------|---------|
|
||||
| 行内数学公式 | `/hngs` |
|
||||
| Emoji 表情 | `/bq /biaoqing /emoji` |
|
||||
| Font Awesome 图标 | `/tb /fa /fontawesome` |
|
||||
| 脚注 | `/jz /jiaozhu /footnote` |
|
||||
| 注释 | `/zs /zhushi` |
|
||||
|
||||
### 媒体与附件
|
||||
|
||||
| 类型 | 快捷命令 |
|
||||
|------|---------|
|
||||
| 图片 | `/tp /tupian /img /image /picture` |
|
||||
| 文件附件 | `/wj /wenjian /file` |
|
||||
| 视频 | `/sp /video` |
|
||||
| 音频 | `/yp /audio` |
|
||||
| 嵌入 Bilibili | `/bzsp /bilibili` |
|
||||
| 嵌入 腾讯视频 | `/txsp /tencent` |
|
||||
| 嵌入 优酷 | `/yksp /youku` |
|
||||
| 嵌入 YouTube | `/ygsp /youtube` |
|
||||
| 嵌入第三方应用 | `/qrnr /embed` |
|
||||
| 网址书签 | `/wzsq /bookmark` |
|
||||
|
||||
### 插入引用
|
||||
|
||||
| 类型 | 快捷命令 |
|
||||
|------|---------|
|
||||
| 页面引用链接 | `/ymyylj` |
|
||||
| 行内块引用 | `/hnkyy` |
|
||||
| 嵌入块引用 | `/qrkyy` |
|
||||
|
||||
### 快速输入
|
||||
|
||||
| 类型 | 快捷命令 |
|
||||
|------|---------|
|
||||
| 快速输入时间 | `/sj /shijian /time` |
|
||||
| 快速输入日期 | `/rq /riqi /date` |
|
||||
| 快速输入现在(日期+时间) | `/xz /xianzai /now` |
|
||||
|
||||
### 快速分栏
|
||||
|
||||
| 操作 | 快捷命令 |
|
||||
|------|---------|
|
||||
| 2 列分栏 | `/2` |
|
||||
| 3 列分栏 | `/3` |
|
||||
| 4 列分栏 | `/4` |
|
||||
| 5 列分栏 | `/5` |
|
||||
|
||||
### 快速功能
|
||||
|
||||
| 操作 | 快捷命令 |
|
||||
|------|---------|
|
||||
| 个人设置 | `/grsz /per` |
|
||||
| 空间设置 | `/kjsz /works` |
|
||||
| 打开搜索 | `/sou /search` |
|
||||
| 打开页面关系图 | `/ymgxt /network /diagram` |
|
||||
| 导入文件 | `/drwj` |
|
||||
| 导入数据表格(CSV) | `/drbg` |
|
||||
| 退出登录 | `/quit /exit /logout` |
|
||||
|
||||
### 文字颜色/背景色
|
||||
|
||||
`/ + 颜色拼音/缩写/英文`,如 `/hs` 或 `/red` 为红色。
|
||||
@@ -0,0 +1,57 @@
|
||||
# 右侧边栏
|
||||
|
||||
> 来源:https://www.wolai.com/wolai/... (右侧边栏)
|
||||
> 用途:mnote 双栏编辑行为参考
|
||||
>
|
||||
> 实现状态:**部分实现** ✅ 上下文菜单有"在右侧边栏打开"动作 ❌ 右侧面板 UI 完整度待确认
|
||||
|
||||
## 基本操作
|
||||
|
||||
| 操作 | 快捷键 |
|
||||
|------|--------|
|
||||
| 在右侧边栏打开链接 | `Alt/Opt + 点击` |
|
||||
| 打开/关闭右侧边栏 | `Alt/Opt + Q` |
|
||||
| 切换主/右侧编辑区 | `Alt/Opt + \`` |
|
||||
| 扩展右侧边栏为整页 | `Alt/Opt + F` |
|
||||
|
||||
## 顶部操作栏
|
||||
|
||||
1. 关闭右侧边栏
|
||||
2. 后退
|
||||
3. 前进
|
||||
4. 展开为整页
|
||||
5. 切换到主编辑区(左右互换)
|
||||
|
||||
## 调整大小
|
||||
|
||||
- 左右拖动左边线调整宽度
|
||||
- 双击左边线均分两侧宽度
|
||||
|
||||
## 跨栏拖拽
|
||||
|
||||
右侧边栏和主编辑区中的块可互相拖拽:
|
||||
|
||||
| 操作 | 说明 |
|
||||
|------|------|
|
||||
| 移动到这里 | 从一边移动到另一边 |
|
||||
| 拷贝副本到这里 | 复制到另一边 |
|
||||
| 嵌入块引用 | 嵌入引用到另一边 |
|
||||
| 行内块引用 | 停留片刻变为行内引用 |
|
||||
|
||||
---
|
||||
|
||||
## mnote 实现状态
|
||||
|
||||
### ✅ 已实现
|
||||
| 功能 | 证据 |
|
||||
|------|------|
|
||||
| "在右侧边栏打开" 上下文菜单 | 文件树/页面树/附件菜单均有 `open-right` 动作(`layout.rs:3127, 3136, 3144, 3164`)|
|
||||
| 快捷键提示 | `Alt + Enter` 在搜索结果中提示右侧边栏打开(`layout.rs:3643, 3670`)|
|
||||
| 事件派发 | `dispatchSidebarEvent('tree.page.open-right', ...)` 等事件(`layout.rs:2961, 2975`)|
|
||||
|
||||
### ❌ / Partial
|
||||
| 功能 | 说明 |
|
||||
|------|------|
|
||||
| 右侧面板容器 UI | 事件已派发,但双栏编辑面板的具体实现待确认 |
|
||||
| 跨栏拖拽 | 未实现 |
|
||||
| 右侧边栏编辑 | 未确认 |
|
||||
@@ -0,0 +1,43 @@
|
||||
# 垃圾桶
|
||||
|
||||
> 来源:https://www.wolai.com/wolai/... (垃圾桶)
|
||||
> 用途:mnote 回收站行为参考
|
||||
>
|
||||
> 实现状态:**核心已实现** ✅ 恢复/彻底删除/搜索 存在 UI 行为差异
|
||||
|
||||
## 入口
|
||||
|
||||
左侧边栏最下方打开"垃圾桶"。
|
||||
|
||||
## 功能
|
||||
|
||||
- 已删除页面存放在垃圾桶中
|
||||
- 支持搜索关键词查找垃圾桶中的页面
|
||||
- **恢复**:恢复到"我的页面"列表下
|
||||
- **彻底删除**:不可撤销,无法找回
|
||||
|
||||
## 操作确认
|
||||
|
||||
- 默认打开"操作确认"开关
|
||||
- 打开时:恢复/彻底删除需要二次确认
|
||||
- 关闭时:直接执行
|
||||
|
||||
---
|
||||
|
||||
## mnote 实现状态
|
||||
|
||||
### ✅ 已实现
|
||||
| 功能 | 证据 |
|
||||
|------|------|
|
||||
| 删除页面进垃圾桶 | `tree.resource.archive`(`routes/tree.rs`)|
|
||||
| 恢复(回到原位置)| `tree.resource.restore` + 恢复位置记录(`design/04-tree-domain/process/4-28`)|
|
||||
| 彻底删除 | `tree.resource.purge`(`routes/resource_trash.rs`)|
|
||||
| 垃圾桶搜索 | 已有搜索功能 |
|
||||
| 资源(附件/mindmap/table)垃圾桶 | `resource_trash.rs` 覆盖 3 种资源类型 |
|
||||
|
||||
### ❌ / Partial (相对于 Wolai)
|
||||
| 功能 | 说明 |
|
||||
|------|------|
|
||||
| 操作确认开关 | 未确认是否实现 |
|
||||
| 恢复后的 reveal/focus | `design/04-tree-domain/process/4-28` 已计划 |
|
||||
| 双浏览器 no-refresh | `design/04-tree-domain/process/4-34` 进行中 |
|
||||
+6
-1
@@ -1,6 +1,11 @@
|
||||
# design 设计稿索引
|
||||
|
||||
> 更新时间:2026-05-14
|
||||
> 更新时间:2026-05-19
|
||||
>
|
||||
> 当前产品口径:`VSCode 简化版工作区 + tiptap 的 Markdown 前端编辑器 + Hermes/Reasonix agent + simplemindmap/office 插件 + Wolai 主题 Web 壳 + 鉴权控制面`。
|
||||
>
|
||||
> 当前上位主线:local-first workspace 是默认数据形态,本地 `.md` 是页面正文真相,Convex / 服务端只作为控制面、cloud source、compat 和 sync replica。入口设计见:
|
||||
> `/mnt/Data1T/mnote/design/02-convex-rust-long-term-architecture/done/2-2-local-first-workspace-convex-control-plane-v1.md`
|
||||
>
|
||||
> 状态口径以当前仓库真实代码为准:
|
||||
> - `[done]`:对应阶段或收口目标已经在当前主线代码中成立
|
||||
|
||||
+16
-6
@@ -1,9 +1,15 @@
|
||||
# 2 [process] Convex 保留前提下的 Tree-First Graph 长期架构方案 v1
|
||||
# 2 [recycle][legacy] Convex 保留前提下的 Tree-First Graph 长期架构方案 v1
|
||||
|
||||
> 更新时间:2026-04-22
|
||||
> 更新时间:2026-05-18
|
||||
>
|
||||
> 2026-05-18 口径更新:
|
||||
> - 本文保留为历史过渡判断:它解释“不要在没有替代控制面、迁移工具和兼容策略时硬拆 Convex”。
|
||||
> - 当前新增能力的上位方向已由 `design/02-convex-rust-long-term-architecture/done/2-2-local-first-workspace-convex-control-plane-v1.md` 覆盖:本地文件夹是早期产品默认数据真相,Rust kernel 是唯一语义真相,Convex / 服务端降级为账号、分享、同步、协作和 AI 隔离控制面。
|
||||
> - 因此,本文中“Convex 作为默认存储 / 实时 / 文件底座”的表述只代表旧代码过渡态和历史决策背景,不再作为新增功能默认方向。
|
||||
>
|
||||
> 当前优先级入口:
|
||||
> - `/mnt/Data1T/mnote/design/01-05-current-priority-overview.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/01-tree-first-graph-kernel/process/1-tree-first-graph-kernel-v1.md`
|
||||
@@ -17,7 +23,7 @@
|
||||
|
||||
> **当 mnote 沿着 Tree-First Graph 与 Rust 主导路线继续重构时,是否要拆掉当前本地自托管 Convex。**
|
||||
|
||||
本文件给出的结论是:
|
||||
本文件在 2026-04-22 给出的历史结论是:
|
||||
|
||||
- **不建议把 Convex 从当前主线中拆掉。**
|
||||
- **长期要收口的是“语义主导权”和“统一执行面”,不是物理上把 Convex 替换掉。**
|
||||
@@ -250,7 +256,7 @@ Rust Web 不是第二套业务内核。
|
||||
|
||||
长期固定以下原则:
|
||||
|
||||
### 5.1 不拆 Convex 主底座
|
||||
### 5.1 历史阶段:不无计划硬拆 Convex 主底座
|
||||
|
||||
- 不为了“Rust 化”先拆掉当前自托管 Convex。
|
||||
- 不引入新的第二主数据库去与 Convex 长期双写对抗。
|
||||
@@ -427,7 +433,7 @@ Rust Web 不是第二套业务内核。
|
||||
|
||||
## 8.1 当前已完成
|
||||
|
||||
- [x] 固定长期口径:不拆 Convex,保留为 storage / realtime substrate。
|
||||
- [x] 固定历史过渡口径:不无计划硬拆 Convex,旧主链保留为 storage / realtime substrate。
|
||||
- [x] 固定长期口径:Rust 作为 tree-first graph 的 semantic owner。
|
||||
- [x] Sidebar 主链恢复为 Convex `useQuery` live subscription 优先,HTTP fallback 只作兜底。
|
||||
- [x] `/api/sidebar` 已接入 Rust query envelope,并通过 Convex transport 执行。
|
||||
@@ -487,10 +493,14 @@ Rust Web 不是第二套业务内核。
|
||||
|
||||
## 9. 最终固定口径
|
||||
|
||||
截至当前仓库状态,可以固定为:
|
||||
截至 2026-04-22 仓库状态,本文曾固定为:
|
||||
|
||||
> **mnote 的长期路线不是拆掉 Convex,而是在 Convex 继续作为底层 substrate 的前提下,让 Rust 逐步拿回 tree-first graph 的 query、command、projection 与 realtime 语义主导权。**
|
||||
|
||||
截至 2026-05-18,当前新增能力的上位口径已调整为:
|
||||
|
||||
> **mnote 的早期产品路线是 local-first workspace:本地文件夹是默认数据真相,Rust kernel 是唯一语义真相,Convex / 服务端降级为账号、分享、同步、协作和 AI 隔离控制面。**
|
||||
|
||||
当前已经完成的是:
|
||||
|
||||
> **主路径边界已基本纠正,tree shell 已降级为显式实验增强,Sidebar 与第一批 tree command 已进入 Convex substrate + Rust envelope 主链。**
|
||||
Reference in New Issue
Block a user