从“想做一个 Craft”到 ArkBlocks:一次 AI 协作开发原生 Block Editor 的心路历程
最开始,我只是想做一个类似 Craft.do 的笔记编辑器。
准确一点说,我想在 HarmonyOS NEXT 手机上,用 ArkTS 原生开发一个真正好用的 Block Editor。不是 WebView,不是简单套一个网页编辑器,也不是把 React 生态里的东西硬搬到鸿蒙上,而是尽量用 HarmonyOS 自己的能力,做一个适合手机端使用的原生编辑器。
刚开始的时候,这个目标听起来其实很大,也很模糊。
Craft 很好看,交互也很舒服。它的文档、卡片、折叠、拖拽、内联样式、图片排版都很迷人。但如果真的要自己做,问题马上就来了:
一个 Block Editor 到底应该怎么设计?
它的 Document Model 是什么?
Block 和 Inline 应该怎么组织?
Undo / Redo 怎么做?
拖拽怎么做?
图片、卡片、Callout 这些能力,是写死在编辑器里,还是做成插件?
AI 能不能帮我把现有的开源编辑器,比如 BlockNote,迁移到 ArkTS?
这就是整个 ArkBlocks 项目的起点。
第一阶段:从“移植 BlockNote”到“理解编辑器”
一开始,我的想法很直接:BlockNote 是开源的,能不能让 AI 去读它的源码,然后把它迁移到 ArkTS?
这个想法很自然,但后来我们慢慢意识到,直接“迁移”不是最好的路。
BlockNote 的实现依赖 React、Tiptap、ProseMirror、DOM、Selection API、contenteditable 等一整套 Web 体系。HarmonyOS NEXT 是另一套世界。ArkUI 有自己的声明式 UI,RichEditor 有自己的输入机制,鸿蒙的手势、键盘、剪贴板、生命周期也都和浏览器不一样。
所以我们逐渐把目标从:
迁移 BlockNote
调整成:
学习 BlockNote 的架构思想,然后为 HarmonyOS 重新设计一个原生 Block Editor Framework。
这一步非常关键。
它让项目不再是“翻译代码”,而是进入了“抽象设计”的阶段。
我们先让 AI 阅读 BlockNote,输出了几份研究文档:
- Editor Architecture
- Document Model
- Editor Design Principles
这些文档不是为了抄代码,而是为了回答几个根本问题:
- 为什么现代编辑器要用 Block?
- Document、Block、Inline、Style、Selection 之间是什么关系?
- 哪些是平台无关的核心能力?
- 哪些是浏览器环境下的实现细节?
- 如果换成 ArkTS,哪些东西应该保留,哪些东西应该重新设计?
这一步之后,我开始意识到:真正值得做的不是一个“鸿蒙版 Craft”,而是一个 HarmonyOS 原生 Block Editor Framework。
后来这个项目就有了名字:
ArkBlocks
第二阶段:先做架构,而不是急着写代码
很多 AI Coding 项目容易犯一个错误:刚想清楚一点方向,就立刻让 AI 写代码。
我们这次反而刻意踩了刹车。
先建立了 AGENTS.md,让所有 AI 知道这个项目是什么,不是什么。ArkBlocks 不是一个普通笔记 App,而是一个可复用的编辑器框架。
然后建立了 docs/research、docs/design、docs/rfc、docs/spec、docs/tests 等文档体系。
当时我们做了几件现在回头看非常有价值的事情:
1. 架构设计
我们让 AI 设计了 ArkBlocks 的总体架构,明确了:
- Document
- Block
- Inline
- Selection
- Transaction
- History
- Command
- Renderer
- Adapter
- Runtime
这些模块之间的关系。
2. RFC
我们建立了 RFC-0001:Document Canonical Model。
这份 RFC 冻结了整个项目最重要的决策:
Document Block Tree 是 ArkBlocks 的唯一真相来源。
也就是说:
- Renderer 不拥有文档状态
- Selection 不复制文档内容
- History 不直接持有活动 Block 对象
- 所有修改都必须通过 Transaction
- Document Tree 是整个编辑器的核心数据结构
3. SPEC
后来我们又做了 SPEC-0001:Document Operations Specification。
这份文档详细定义了:
- InsertBlock
- DeleteBlock
- MoveBlock
- DuplicateBlock
- SplitBlock
- MergeBlock
- ReplaceBlock
- InsertInline
- DeleteInline
- UpdateProps
- Indent
- Outdent
每一个操作都要说明前置条件、输入输出、状态变化、Selection 行为、History 行为、失败条件和复杂度。
4. SPEC Test
之后我们又写了 SPEC-0001-Test-Suite,把行为规范转成测试用例。
这一步很重要。因为它让项目不只是“文档说得漂亮”,而是开始有了可验证的契约。
从现在看,这套流程非常像大型框架项目:
Research ↓ Architecture ↓ RFC ↓ SPEC ↓ SPEC Test ↓ Implementation ↓ Code Review这和普通的“让 AI 写功能”完全不同。
第三阶段:从骨架工程到真正可运行
有了架构之后,我们开始让 AI 搭建 Framework Skeleton。
这个阶段不是为了实现功能,而是为了把架构变成真实的代码结构。
AI 创建了大量模块:
- core/types
- schema
- document
- transaction
- history
- command
- renderer
- adapter
- plugin
- editor
一开始这些模块很多只是接口、空实现、TODO,但它们的意义很大:
AI 后面写代码时,不再凭空发明架构,而是在既有框架里补实现。
之后我们开始真正实现 Document Index System,也就是 PR-0001。
这个 PR 解决了一个非常关键的问题:
Block 查找必须是 O(1)。
如果每次编辑都递归遍历整个 Block Tree,编辑器以后一定会在大文档里崩掉。所以 Document 维护了:
- blockIndex
- parentIndex
这一步看起来很底层,但它奠定了整个编辑器的性能基础。
PR-0001 之后我们还做了 Code Review,发现了 Transaction 原子性、索引外部可变等问题,于是又做了 PR-0001A 修复。
