S1 核心内核实施方案(Mindmap 内核对标 KMind)
目标:在不改变现有 Next.js + BlockNote + Yjs + Supabase 架构的前提下,完成 Stage S1 中的 5 个核心能力,形成可落地的代码骨架与开发清单。
1. MindmapCanvas(画布容器 / 多根模式 / 懒渲染)
| 对标 |
MNOTE 方案 |
design/Kmind/js/0.js:719-1160 的 MindMap 类,负责容器初始化、SVG 分层、大小检测、延迟渲染、多根导图 |
在 src/components/mindmap/canvas/MindmapCanvas.tsx(新建)中封装一个 React 组件,内部通过 @xyflow/react 的 ReactFlowProvider + useReactFlow 控制视图。 |
关键设计
- 多根模式:允许
props.roots 为数组;在 BlockNote ↔ mindmap 同步时,若一个文档存在多个 mindmap,即 mindmap_meta 行 >1,则对每个根节点创建独立的 React Flow Node 子树,并在 MindmapCanvas 中渲染多个 root。
- 容器重试/懒加载:借鉴 KMind 的
containerSizeRetryConfig,封装 useElementRect hook,支持 IntersectionObserver 判断是否进入视窗,在进入前仅挂起渲染。
- SVG 分层:React Flow 默认使用单一
svg;需要添加自定义背景(连线)、节点容器及“其他层”(如选框、直觉按钮)。做法:通过 ReactFlow 的 nodeTypes + edgeTypes + 自定义 Background/Controls 组件来模拟 lineDraw/nodeDraw/otherDraw。
- 画布状态管理:使用 Zustand(
src/store/useMindmapStore.ts 新建)保存 scale, translate, activeNodes, multiRootMode 等状态,方便其他模块(快捷键、TextEdit)订阅。
输出物
src/components/mindmap/canvas/MindmapCanvas.tsx
src/components/mindmap/canvas/useElementRect.ts
src/store/useMindmapStore.ts
2. MindLayoutEngine(渲染管线与多布局)
| 对标 |
MNOTE 方案 |
Render 类(design/Kmind/js/0.js:11753-15120)整合布局、懒渲染、节点缓存和命令 |
拆成 MindLayoutEngine(纯 TS,负责布局计算)和 MindmapRenderer(React hook,负责 diff & 渲染)两部分。 |
关键设计
- 数据模型:使用
MindmapNode(包含 id, parentId, children, data, layout)与 MindmapTree(单根/多根)。使用 zod 校验。
- 布局策略:引入 elkjs(cjs 版本)或自己实现逻辑结构布局。第一阶段至少实现
LOGICAL_STRUCTURE 与 MIND_MAP,接口定义:
- 节点缓存:维护
nodeCache: Map<string, LayoutResultNode>,当节点数据未变化时复用位置。通过 useMemo + JSON.stringify diff 或者基于 hash.
- 命令注册:结合
MindmapCommandBus(Zustand store + typed events),将 INSERT_NODE / SET_NODE_STYLE 等命令映射到 MindLayoutEngine -> MindmapRenderer.
- Generalization & multi-root:沿用 KMind 的
generalization 数据结构:每个节点 data.generalization?: { range: [start,end]; text: string }[],在布局时对“概要节点”作为独立 branch 处理。
输出物
src/lib/mindmap/layout/MindLayoutEngine.ts
src/lib/mindmap/layout/engines/logicalStructure.ts
src/lib/mindmap/layout/engines/mindMap.ts
src/lib/mindmap/command/MindmapCommandBus.ts
3. MindmapShortcutController(快捷键与命令路由)
| 对标 |
MNOTE 方案 |
KeyCommand (design/Kmind/js/0.js:11709-11890) |
创建 src/components/mindmap/shortcut/MindmapShortcutController.ts,Hook 化 keydown 捕获、实例隔离、编辑模式白名单。 |
关键设计
- 多实例隔离:用一个全域
activeInstanceId(ref) + MindmapCanvas useEffect 控制,仅当前 canvas 响应快捷键。
- 捕获阶段阻断:在
document capture 阶段阻止 Ctrl+Z, Ctrl+V 等冒泡,若不在 mindmap 内部则放行。
- 映射:提供
registerShortcut(keyCombo, handler, options) API,内部用 Map<string, ShortcutEntry[]>,默认注册:新增节点、删除、展开、直觉按钮等。
- 自定义检查:暴露
shouldHandleEvent(e) 回调,允许 BlockNote/其它面板禁用快捷键。
输出物
src/components/mindmap/shortcut/MindmapShortcutController.ts
src/types/mindmap/shortcuts.ts
4. TextEdit 内嵌富文本
| 对标 |
MNOTE 方案 |
Quill history/input/keyboard + render/TextEdit |
在 src/components/mindmap/text 下实现 MindmapTextEditor(挂载于节点上方)与 useMindmapTextEdit Hook。 |
关键设计
- 编辑器选择:为了兼容 BlockNote,可采用
@blocknote/core 的 InlineContent 或 mini TipTap 实例。若沿用 Quill,需要自定义 bubble toolbar + history 模块。
- 实时测量:使用
ResizeObserver 或 canvas measure,模拟 KMind 的 node.createTextNode -> getNodeRect,在编辑时更新 width/height 并触发布局。
- 粘贴清洗:复用 KMind 的
handleInputPasteText、defenseXSS(位置 design/Kmind/js/0.js utils 部分),转成 TypeScript。
- 撤销栈:复刻
history 模块逻辑,处理 undo/redo 按键,保持与 MindmapShortcutController 协调。
输出物
src/components/mindmap/text/MindmapTextEditor.tsx
src/components/mindmap/text/useMindmapTextEdit.ts
src/lib/mindmap/text/pasteSanitizer.ts
5. Link / Attachment 解析层
| 对标 |
MNOTE 方案 |
checkSiyuanLinkFormatData / checkSiyuanPdfUrlFormatData (design/Kmind/js/0.js:12040-12380) |
在 src/lib/mindmap/parser/linkParser.ts 中封装同功能模块,输出标准化 MindmapLink. |
关键设计
- 类型定义:
- 粘贴入口:
MindmapTextEditor 在 onPaste 中调用 parser,将结果写入 node.data.link 并触发 Command SET_NODE_HYPERLINK。
- 附件扩展:在 parser 中同时识别
siyuan://plugins/kmind-plugin?data=...、siyuan://blocks/、((id 'title'))、PDF 标注、普通 URL。
输出物
src/lib/mindmap/parser/linkParser.ts
src/types/mindmap/link.ts
6. 调试与验证
- 单元测试:使用 Vitest 对
MindLayoutEngine, linkParser, MindmapCommandBus 编写测试用例(位于 src/lib/mindmap/__tests__)。
- Storybook/Playground:创建
src/components/mindmap/dev/MindmapPlayground.tsx,用于手动测试多根、快捷键、TextEdit。
- 集成测试:待 P4 同步控制器完成后,在 Playwright 场景中验证:新增节点 -> 更新 BlockNote -> 重新渲染。
Checklist(S1 完成判定)
建议的开发顺序与依赖
- 基础设施:搭建
useMindmapStore、MindmapCommandBus、类型定义(MindmapNode, MindmapTree, MindmapLink)。
- 布局引擎:实现
MindLayoutEngine + logicalStructure 布局,并在 Vitest 中用静态树校验输出。
- Canvas 集成:创建
MindmapCanvas,接入 React Flow,将布局结果映射成节点/连线,接通 store。
- 快捷键/命令:实现
MindmapShortcutController,注册插入/删除/折叠等命令,验证命令总线。
- TextEdit & Link Parser:落地
MindmapTextEditor 和粘贴解析,把链接写入节点数据。
- 调试工具:搭建
MindmapPlayground + Vitest/Storybook 场景,验证多根与富文本。
外部依赖/资产
@xyflow/react(React Flow 11+)
elkjs(或其他布局库)——生成自动布局
zustand, immer(状态管理)
@tiptap/react 或 Quill(富文本)
vitest, testing-library/react
下一步:按照本方案拆解具体任务,在 mindlist.md 的 S1 项目上依次打勾,并在备注表记录进度与提交。