MemoFlow · Editor 模块开发笔记
记录 Editor 的版本化方案:Monaco、CodeMirror 6,以及 Vault / Obsidian 的技术选择与实现。
Editor 模块目标
专注于 md 文件编辑、阅读的模块
用来编辑或者阅读文件
我想在 MemoFlow(目前还叫 DailyUse)里做一个 Markdown 编辑与阅读模块。最基本的是打开文件、修改内容、保存和预览;界面希望接近 VS Code 或 Obsidian,有文件列表、标签页和编辑区,还能查看图片等资源。Repository 管理文件和资源,Editor 负责编辑体验。
V1.0.0
Markdown 编辑器先选择 Monaco Editor。
Monaco 是微软开源的浏览器代码编辑器,也是 VS Code 使用的编辑器核心。它并不是一个能直接嵌入网页的完整 VS Code,但语法高亮、行号、光标操作、查找、补全等编辑基础已经很成熟。看到它的知名度和功能之后,我暂时不准备花太多时间比较其他方案,先用 Monaco 把基础界面做出来。
在 Vue 中使用 monaco-editor-vue3 封装组件,配置 Markdown 语言、自动换行、主题、行号和编辑器自动布局。编辑和预览先分开处理:Monaco 负责输入 Markdown 源文本,Markdown-it 负责把文本解析成预览内容。再加上编辑 / 预览 / 分屏切换、文件读取、保存按钮和 Ctrl+S 快捷键,基本上就可以在应用里直接修改一个 Markdown 文件。
这里要分清编辑器和渲染器:Monaco 的语法高亮只是让 Markdown 源码更容易阅读,并不会自动把 **粗体**、图片或标题显示成完整的 Markdown 阅读页面。预览内容需要单独渲染,原始 HTML 和外部链接也需要注意安全处理。
v1.1.0
普通文字能编辑了,现在想解决图片粘贴、资源插入和自动保存。Markdown 笔记不只有文字,图片也应该能直接粘贴进去,而不是每次都先手动上传再写链接。
粘贴图片时,需要从剪贴板事件拿到图片文件,再转换成可以插入编辑器的内容。这里有两个方案:
- Base64 内嵌: 不用额外管理图片路径,粘贴后可以直接显示;但图片编码会让 Markdown 正文变得很大,也不方便复用和管理。
- 图片保存为独立文件: 正文只插入相对路径的
,比较适合长期维护的笔记库;不过需要一起处理文件存储、相对路径和资源移动后的引用问题。
在 Monaco 里插入内容还要考虑光标位置和选区,例如通过 Range、executeEdits 把生成的 Markdown 放到正确位置。编辑器实例本身也不适合被 Vue 深度代理,使用 markRaw 保留原始实例更稳妥。
保存方面先采用内容变更检测、快捷键保存和延迟自动保存。现在 Monaco 的代码编辑能力足够,但 Markdown 的图片、文件链接、预览切换都要自己额外组织。接下来需要认真考虑:这里究竟是在做代码编辑器,还是一个以 Markdown 阅读和写作为中心的编辑器?
V2.0.0
我希望 Markdown 的写作体验更接近 Obsidian:编辑正文时就能看到部分排版效果,而不是一直在纯源码和独立预览页之间切换。现在需要重新评估编辑器内核,重点比较 Monaco Editor 与 CodeMirror 6。
Monaco 的优势在于开箱即用的代码编辑体验。它具有 VS Code 编辑器的技术基础,代码语言支持、提示、诊断和差异比较等能力比较完整。缺点是这些能力有一部分更适合代码 IDE;它还涉及语言服务 Worker 等集成工作,官方也不支持移动浏览器。要实现 Markdown 的即时排版,仍需要自己处理语法标记和预览交互。
CodeMirror 6 更像一套可以组合的编辑器底层组件,而不是现成的完整 Markdown 产品。它的核心可以分成几部分:
EditorState保存文档、光标选区与编辑状态。更新内容时通过 Transaction 描述变更,而不是随意修改 DOM。EditorView负责把状态展示到页面上、接收输入并处理可视区域。- Extensions 用来组合 Markdown 语言支持、快捷键、历史记录、主题、自动补全等功能,不必一开始就加载整套 IDE 能力。
- Markdown 语法支持可以结合 Lezer 解析树识别标题、强调、链接等结构;再使用 Decoration、Widget 和 ViewPlugin 控制编辑区如何显示这些内容。
对我来说,区别不只是哪个库“更轻”。Monaco 可以快速获得成熟的代码编辑界面;CodeMirror 6 则更方便把编辑区按 Markdown 的需要逐步定制。它也有代价:许多视觉效果要自己用扩展组合,不能指望安装 CodeMirror 就自动拥有 Obsidian 的实时预览。
这一版决定使用 CodeMirror 6。先把输入、选区、快捷键和 Markdown 扩展迁入新的编辑区,再做 Source / Live 两种模式。Source 保留原始 Markdown;Live 则通过自定义扩展,把部分 Markdown 标记转换为可阅读的样式或控件,并照顾光标进入标记区域时的编辑行为。模式切换使用 Compartment 重配置扩展,不需要销毁整个编辑器实例。
v2.1.0
CodeMirror 的编辑区基本成形,现在需要把它放回完整的 Repository 工作区。只支持单篇文件还不够,我希望能够从资源列表打开文档、切换多个标签页,并且在正文里插入已有的图片和文件。
这版把工作分成两层:Editor 管输入、选区、标签页和未保存状态;Repository 管文档内容和资源读写。编辑器只负责把改动交给保存接口,不直接承担文件系统与服务端的数据访问。
阅读方面继续增加 WikiLink 输入提示、反向链接、链接图、图片与其他资源预览。工作区则用 Workspace、Session、Group、Tab 等概念组织打开的文档和界面状态。自动保存需要区分正在保存、已保存、失败与内容冲突;关闭未保存的标签时,也应该有明确提醒。
现在的主要难点已经不是选择文本编辑控件,而是多个文档、资源引用和保存状态如何保持一致。这套工作区能够覆盖更多使用场景,但每增加一个编辑能力,也意味着要维护相应的状态和冲突处理。
V3.0.0
现在要重新考虑 Markdown 文件的归属:如果 Obsidian 本地 Vault、Git 仓库和 MemoFlow 数据库都能分别修改同一篇笔记,到底以哪一份内容为准?
自研 Editor 的一个好处是所有交互都可以控制,但代价也很明显。编辑器之外,还要负责文件读写、多端同步、会话状态、冲突处理、图片资源和 Markdown 兼容。Obsidian 已经具备成熟的本地 Markdown 编辑能力,没有必要为了在 MemoFlow 里展示知识库而重复实现全部功能。
这一版决定调整边界:
- Vault / Git 中的 Markdown 文件是内容事实源。 数据库中的索引与投影可以用于搜索、预览和关联,但不另存一份可独立编辑的正文。
- Desktop 负责浏览本地 Vault。 需要真正编辑时,通过
obsidian://将文件交给 Obsidian,而不是在 MemoFlow 中继续维护完整的 CodeMirror 工作区。 - Web 负责展示 GitHub 知识投影。 先做好目录、搜索、正文预览和文档关系;已有笔记不开放无约束的全文写入。
这是一项产品边界的取舍,不是说 CodeMirror 6 不好。CodeMirror 适合需要自定义编辑体验的应用,Obsidian 则直接提供完整的笔记编辑产品。 当前需求更偏向连接、理解和管理知识,所以选择让它们各自负责擅长的部分。
v3.1.0
按照 Vault / Git 的方案,继续完善 MemoFlow 内部的知识阅读与受控写入。
Desktop 使用本地 Vault 工作区扫描、检索和安全预览文件;打开已有笔记时校验目标路径,再交给 Obsidian。Web 使用 GitHub 仓库的知识投影展示目录、Markdown 正文、搜索结果和关联信息。Markdown 阅读由独立的安全渲染能力处理,包括代码高亮、WikiLink 等,而不是复用已经不需要的全文编辑器。
创建新笔记与编辑已有笔记也要区分。创建时可以先在 MemoFlow 中确认标题、路径和内容,再通过 Git 提交写入;已有文件的在线编辑涉及基线版本、并发修改与冲突处理,暂时不作为普通全文编辑功能开放。
这一版继续完善知识浏览、预览和创建体验。Mobile 的知识阅读入口还没有完成;如果需要增加 Web 在线编辑,先确认文件版本和权限边界,再决定采用哪种编辑组件。