MarkText 的 muya 编辑器内核 ROADMAP 解读:从 2020 年块系统到 2023 年 TypeScript 重构的完整演进
【免费下载链接】marktext📝A simple and elegant markdown editor, available for Linux, macOS and Windows.项目地址: https://gitcode.com/gh_mirrors/ma/marktext
本篇技术文章以 packages/muya/docs/ROADMAP.md 为核心,完整梳理 muya(marktext 的 Markdown 编辑内核,包名@muyajs/core)的路线图:2020 年 6–8 月按月推进的块(block)能力构建目标,以及 2023 年 1.0.0 版本围绕 TypeScript 化、代码设计、浏览器兼容、文档与 CI/CD、测试五大方向的规划清单。读完本文,你能对照源码逐项验证路线图的实际落地情况,并掌握在本仓库中运行构建、单元、规范符合性与 Playwright E2E 测试的完整命令。
muya 在 MarkText 中的定位
muya 是 marktext monorepo 中的编辑器引擎,位于 packages/muya,由上游 JS 实现迁移为 TypeScript 重写并作为@muyajs/core组织。桌面端渲染器直接消费@muyajs/core作为编辑内核,而遗留的 JS 引擎packages/muyajs正在退役。根据 packages/muya/CLAUDE.md 的描述,packages/muya是自闭环包:拥有自己的工具链(ESLint/antfu、stylelint、madge、vitest),仓库根 ESLint 明确忽略packages/muya/**。
从源码结构看,muya 的核心架构与路线图条目直接对应:
- 入口 packages/muya/src/muya.ts 导出
Muya类,UI 插件通过静态Muya.use(Plugin, options)注册,muya.init()时实例化; Editor.init()调用 registerBlocks(),在构建根ScrollPage前注册全部块类型;- 块继承体系为
TreeNode → Parent → (Content | Format),具体块分布在src/block/{commonMark,gfm,extra,content}; - 文档状态
JSONState基于ot-json1暴露invert/compose/transform,架构上为 OT 协同编辑预留了空间; - 行内渲染走自定义
lexer/rules管线并经snabbdom渲染虚拟 DOM,集成 KaTeX、Prism、Mermaid、Vega/Vega-Lite 与 PlantUML。
2020 年月目标:块系统的三阶段演进
ROADMAP 下半部分记录了 2020 年 6 月、7 月、8 月三个月的阶段性目标。这一部分是 muya 从"能输入"到"全功能"的能力清单,且绝大多数条目已完成(勾选)。下文按月份继承原清单,并结合当前源码标注佐证。
2020 年 6 月:三种基础块
原始目标清单:
- 支持 GFM 与 CommonMark 规范的行内样式;
- 行内图片(不支持本地图片)与图片编辑菜单;
- atx 与 setext 两种标题;
- 引用块(blockquote);
- 水平分割线;
- 行内样式格式化方式与格式工具箱;
- 各类事件处理:backspace、delete、arrow、tab、enter、input 等;
- 多段落选中与删除;
- 段落拖拽;
- 普通段落。
对照当前源码,src/block/commonMark/下即对应这批基础块:paragraph、atxHeading、setextHeading、blockQuote、thematicBreak、codeBlock、html等,统一经 src/block/index.ts 中ScrollPage.register(...)注册。事件处理方面,Editor持有经 RxJS 合并的 DOM 事件流(click、input、keydown、keyup、compositionstart/end),并路由到活跃块的处理函数——这正是"各类事件处理"条目在 TS 重写后的实现形态。
2020 年 7 月:更多块,支持输入与输出
原始目标清单:
- 行内与段落的复制粘贴;
- 多选段落复制粘贴;
- 有序列表、无序列表、任务列表;
- 列表拖拽,并把完成项自动移至末尾;
- 代码块;
- HTML 块;
- 表格块;
- 历史记录(undo/redo);
- 与其他文件类型(markdown 与 html)的输入输出。
这些能力在当前代码中的落点:
- 列表与任务列表:src/block/gfm/taskList.ts 同目录下的
orderList、bulletList、listItem(commonMark 侧)与TaskListItem、TaskListCheckbox附件节点; - 表格块:
src/block/gfm/table/下的Table、TableInner、TableRow、Cell、TableCellContent; - 代码块与 HTML 块:
src/block/commonMark/codeBlock与src/block/commonMark/html(含htmlContainer、htmlPreview); - 历史记录:
src/history/模块,并配有src/history/__tests__/单测; - 输入输出:
src/state/下的markdownToState.ts(经marked解析)、stateToMarkdown.ts(序列化回 Markdown)、markdownToHtml.ts/htmlToMarkdown.ts(经turndown+joplin-turndown-plugin-gfm桥接 HTML); - 剪贴板:
src/clipboard/模块,包含paste.ts、copyData.ts、cut.ts及 27 个单测文件。
2020 年 8 月:全功能版本
原始目标清单:
- 数学公式块;
- mermaid;
flowchart、sequence(划掉,后续弃用);- footnote(原清单未勾选);
- front matter;
- 上标、下标、数学公式等行内元素;
- 段前菜单(paragraph front menu);
- 快速插入菜单(quick insert menu)。
对照现状:
- 数学公式块:
src/block/extra/math/(MathBlock、MathContainer、MathPreview),KaTeX 在 package.json 依赖中; - 图表块:
src/block/extra/diagram/(DiagramBlock、DiagramContainer、DiagramPreview),图表渲染集成在src/utils/diagram/; - front matter:
src/block/extra/frontmatter/,并有src/block/__tests__/frontMatter.spec.ts单测; - footnote:原 2020 清单中唯一未勾选项。当前 CHANGELOG.md 0.2.0 版本记录了 "footnote complete — block class + UI tool + click wiring + HTML backref",且 src/block/index.ts 已注册
Footnote(来自src/block/extra/footnote),e2e 中另有e2e/tests/blocks/footnote-scenarios.spec.ts场景测试——可以推断该遗留项已在 TS 重写后补齐; - 段前菜单与快速插入菜单:对应 UI 插件
src/ui/paragraphFrontButton/、src/ui/paragraphFrontMenu/、src/ui/paragraphQuickInsertMenu/,经Muya.use(...)由宿主注册。
2023 年 1.0.0 目标:逐项对照当前落地状态
ROADMAP 上半部分是 2023 年 v1.0.0 的规划,分为 Optimization、Better Code Design、Compatibility、Documents、CI and CD、Test 六个方向。下面逐项继承原清单,并给出仓库内的可验证证据。
Optimization:支持 TypeScript
原清单两项:
- 将 JS 代码转换为 TS 代码(P0)——已完成;
- 补全所有缺失类型(no any),支持 TS 编译严格模式(eslint --fix 无错误)(P0)。
证据:整个packages/muya/src已是 TypeScript,构建脚本为tsc && vite build(见 package.json 的build与lint:types: tsc --noEmit)。关于第二项的"no any",从 e2e 侧规范看,e2e/README.md 明确写道该项目禁止any(ts/no-explicit-any: 'error'),并且 e2e 类型声明通过 e2e/types.d.ts 以Window.muya?: Muya的形式避免window as any。主包侧则由 antfu 配置的 ESLint 与tsc --noEmit把关,因此"补全类型"这一项可以认为在工程规范层面已实质推进,具体完成度以pnpm -C packages/muya lint:types的实际输出为准。
Better Code Design:更好的代码设计
原清单七项:
- 把最新 marked 打补丁并入 muya(P0);
- 移除 axios、XMLHttpRequest 等;
- 用 constructor mixin 替换 property mixin;
- 图表:弃用 sequence 与 flowchart;
- 优化 turndown service 以获得更好的粘贴体验;
- 优化 markdownToHtml 的 UI 样式,移除无用代码;
- 是否使用 DI(依赖注入);
- 是否用 TSX 替换 snabbdom(生态更好、更易读)。
逐项源码佐证:
- marked 补丁化:packages/muya/src/utils/marked/ 是一个成体系的本地化 marked 集成,包含
lexBlock.ts、walkTokens.ts、frontMatter.ts、compatibleTaskList.ts、extensions/等,而非直接调用 npm 包默认行为——即"patch the latest marked to muya"的落地形态。marked与marked-highlight仍出现在 package.json 依赖中,补丁层构建在其上。 - 移除 axios / XMLHttpRequest:在
packages/muya/src内检索axios|XMLHttpRequest无任何命中,证实该条目完成——引擎不再在核心中做网络请求。 - constructor mixin 替换 property mixin:packages/muya/src/block/mixins/containerQueryBlock.ts 与 packages/muya/src/block/mixins/leafQueryBlock.ts 即这两个 constructor mixin,应用于块类以提供
queryBlock/路径解析;CLAUDE.md 也明确指出 "this was a deliberate switch away from property mixins",与 ROADMAP 条目互相印证。 - 弃用 sequence 与 flowchart:当前图表链路保留 mermaid(含 sequence/flowchart 的 e2e 测试
e2e/tests/diagrams/中仍有sequence.spec.ts、flowchart.spec.ts等场景用于回归),从源码结构看,2020 年清单中划掉的旧 flowchart/sequence 独立实现(对应 muyajs 时代的 Snap.svg 方案)已被弃用,统一收敛到src/utils/diagram/的渲染器集成。 - turndown 优化:
src/utils/turndownService/目录承载定制化的 turndown 封装,配合src/state/htmlToMarkdown.ts完成 HTML→Markdown 的粘贴转换;turndown与joplin-turndown-plugin-gfm均在依赖中。 - 未决项:markdownToHtml 样式清理、DI、TSX 替换 snabbdom 三项在原清单中未勾选。从当前源码结构看,渲染层仍为
snabbdom+snabbdom-to-html(依赖列表可见),UI 插件体系仍围绕src/ui/baseFloat/组织,即 TSX 替换尚未发生。
Compatibility:浏览器兼容
原清单三项:
- 兼容 Firefox(P0);
- Safari(P0);
- Edge(P0)。
证据:E2E 配置 packages/muya/e2e/playwright.config.ts 配了 Chromium/Firefox/WebKit 三浏览器矩阵,e2e/README.md 说明了本地pnpm --filter muya-e2e e2e:firefox/e2e:webkit的运行方式;CI(muya-e2e.yml)目前只跑 Chromium,Firefox + WebKit 在 WebKit 相关的引擎无关改写(BACKLOG Phase 3)落地前暂不纳入 CI 矩阵。Safari 与 Edge 在仓库中未见专门的适配/测试痕迹,与原清单"未完成"状态一致。
Documents:文档
原清单项:官网(docs、demo)(P0)、文档(P0)、代码注释。
从 monorepo 结构看,packages/website/目录提供了文档站(含content/docs/end-user/21 篇用户文档与content/docs/dev/12 篇开发文档)与首页演示,packages/muya/examples/是消费@muyajs/core的 Vite 演示工程。muya 自身则通过 CLAUDE.md、README.md、CHANGELOG.md 与 e2e 的 README.md / BACKLOG.md 构成文档面。因此 ROADMAP 中"website(docs, demo)"一项在仓库层面已有对应实体,是否视为完成取决于原项目对"对外发布站点"的定义。
CI and CD:构建与发布
原清单三项:
- 优化构建流程,用 vite 替换 webpack 并用 rollup 构建;
- 加快开发流程,用 vite 替换 webpack;
- 新增 GitHub Action:合并/推送 master 后自动修改 package.json 版本号、打 tag 并发布新版本到 npm。
证据:
- vite 替换 webpack:packages/muya/vite.config.ts 即当前构建配置,package.json 的
build脚本为tsc && vite build,产物为lib/{es,umd,cjs}与lib/types(vite-plugin-dts负责声明文件,@laynezh/vite-plugin-lib-assets负责图标与字体资源路由);开发侧examples与e2e/host均跑 Vite dev server。 - CI 侧:仓库
.github/workflows/下已有muya-build.yml、muya-test.yml、muya-spec.yml、muya-e2e.yml、muya-lint.yml、muya-circular.yml等一整套 muya 专属流水线;madge 循环依赖检查(pnpm -C packages/muya check-circular,即madge --circular src/index.ts)也在 CI 中强制执行。 - npm 自动发布:CLAUDE.md 明确说明上游的 release-it 发布链路未迁入本 monorepo——"marktext 不使用 husky/commitlint,
@muyajs/core不从本仓库发布"。因此原清单最后这一项在当前仓库语境下不适用/未完成,版本号(package.json 中0.2.0)与 CHANGELOG.md 目前由上游节奏维护。
Test:测试
原清单两项:
- 单元测试(P1);
- e2e 测试(Optional)。
这两项在原 ROADMAP 时间点尚未完成,但在当前仓库中均已实质建成,且规模远超"Optional"级别:
- 单元测试:vitest 单测与源码同目录放置在
src/**/__tests__/下,覆盖 block、clipboard(27 个文件)、selection(11 个文件)、state(26 个文件)、history、event、inlineRenderer、search、utils 等模块。CHANGELOG.md 0.2.0 记录了 "test coverage 1 → 386 tests (43 files)"。运行方式:pnpm -C packages/muya test,单文件pnpm -C packages/muya exec vitest run path/to/file.test.ts。 - 规范符合性测试:
test/spec/是 CommonMark 0.31 + GFM 0.29-gfm 的 fixture 套件(test:spec脚本,独立 vitest 配置 vitest.spec.config.ts)。基线由 test/spec/expected-failures.json 锁死——清单内用例若突然通过、或清单外用例若开始失败,套件都会报错,"合规度只允许上升"。基线数据见 test/spec/conformance.md:CommonMark 87.7% / GFM 86.3%(PR-6a 时点)。 - E2E 测试:packages/muya/e2e/ 是基于 Playwright 的真实浏览器套件,自带独立宿主页
e2e/host/(#editor+ 工具栏按钮),测试按smoke/、typing/、inline/、ui/、editing/、blocks/、diagrams/、drag/、export/、stability/、a11y/等分类组织。常用命令(从仓库根执行):
pnpm install # 一次性安装,拉取 @playwright/test pnpm e2e # 全矩阵 — Chromium + Firefox + WebKit pnpm e2e:ui # Playwright UI 模式(推荐用于调试) pnpm --filter muya-e2e e2e:firefox # 仅 Firefox pnpm --filter muya-e2e exec playwright install firefox webkit # 一次性下载浏览器 pnpm --filter muya-e2e exec playwright show-report # 查看失败报告e2e README 还沉淀了四条针对 muya contenteditable 场景的实战约定,值得写 Playwright 测试的读者注意:
page.keyboard.type在delay: 0时会丢字符——muya 的 content-change 管线每次按键同步重渲染,超过 4 个字符的输入应使用tests/helpers/keyboard.ts中的slowType()(每字符 30ms);- 浮动插件用
opacity: 0隐藏而非display: none,expect(...).toBeHidden()无效,需断言计算后的 opacity; getMarkdown()在最后一次击键后异步读取 state,读 Markdown 前先用expect(domNode).toContainText(...)作为同步屏障;- 优先用公共 API(
getMarkdown/getState/getTOC)断言,而非 DOM 正则——Markdown 序列化是确定性的,snabbdom 输出则可能漂移。
路线图全景核对表
把 ROADMAP 原清单汇总为一张当前状态核对表(状态以本仓库源码与文档为准):
| 方向 | 条目 | 原状态 | 当前仓库证据 |
|---|---|---|---|
| Optimization | JS 转 TS | 完成 | 整个src/为 TS,tsc && vite build |
| Optimization | no any / strict | 未完成 | e2e 侧ts/no-explicit-any: error;主包以lint:types+ ESLint 把关 |
| Code Design | marked 补丁化 | 完成 | src/utils/marked/本地集成层 |
| Code Design | 移除 axios/XHR | 完成 | src/内无相关引用 |
| Code Design | constructor mixin | 完成 | src/block/mixins/*.ts |
| Code Design | 弃用 sequence/flowchart | 完成 | 图表统一收敛至src/utils/diagram/+ mermaid 等 |
| Code Design | turndown 优化 | 完成 | src/utils/turndownService/+ GFM 插件 |
| Code Design | markdownToHtml 清理 / DI / TSX | 未完成 | 渲染层仍为 snabbdom,UI 体系未变 |
| Compatibility | Firefox | 完成 | e2e firefox 矩阵 + CI 配置 |
| Compatibility | Safari / Edge | 未完成 | 仓库中无相应适配证据 |
| Documents | 官网 / 文档 | 推进中 | monorepo 含packages/website与examples |
| CI/CD | vite 替换 webpack | 完成 | vite.config.ts、e2e/examples 均 Vite |
| CI/CD | GitHub Action 自动发 npm | 未完成 | @muyajs/core不从本仓库发布(CLAUDE.md) |
| Test | 单元测试 | 已完成 | vitest,386 tests / 43 files(0.2.0 changelog) |
| Test | e2e 测试 | 已完成 | Playwright 全套件,Chromium/Firefox/WebKit 矩阵 |
验证路线:如何在本仓库亲手核对
以下命令均从仓库根目录执行,用于验证本文所述各项落地情况:
# 类型检查与 lint(对应 "no any / strict" 条目) pnpm -C packages/muya lint:types pnpm -C packages/muya lint # 循环依赖检查(CI 强制项) pnpm -C packages/muya check-circular # 单元测试 / 覆盖率 pnpm -C packages/muya test pnpm -C packages/muya coverage # CommonMark/GFM 规范符合性(含 expected-failures.json 回归门) pnpm -C packages/muya test:spec pnpm -C packages/muya test:spec:commonmark pnpm -C packages/muya test:spec:gfm # E2E(需要一次 playwright install firefox webkit) pnpm -C packages/muya/e2e e2e # 构建产物(lib/{es,umd,cjs} + lib/types) pnpm -C packages/muya build # 演示工程 pnpm -C packages/muya/examples dev:demo环境要求:Node ≥ 20.19(与 marktext 根工程一致),构建目标chrome70,来自 CLAUDE.md 与 package.json 的engines声明。
小结
packages/muya/docs/ROADMAP.md 记录的不只是一份待办清单,而是 muya 内核从 2020 年"逐月堆块能力"到 2023 年"面向 1.0 的系统性重构"的完整演进轨迹:2020 年的三个阶段目标已在src/block/的块注册体系、src/state/的 Markdown/HTML 双向转换、src/clipboard/与src/history/中留下清晰对应物;2023 年的六大方向中,TypeScript 转换、marked 补丁化、constructor mixin、turndown 优化、vite 构建、单元与 E2E 测试均已落地并有可执行命令验证,而 Safari/Edge 兼容、markdownToHtml 清理、DI 与 TSX 化、npm 自动发布则仍是明确标注的开放项。沿本文给出的源码路径与命令逐条核对,即可把路线图与实现现状一一对齐。
【免费下载链接】marktext📝A simple and elegant markdown editor, available for Linux, macOS and Windows.项目地址: https://gitcode.com/gh_mirrors/ma/marktext
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考