MarkText 的 muya 编辑器内核 ROADMAP 解读:从 2020 年块系统到 2023 年 TypeScript 重构的完整演进
2026/9/5 20:41:38 网站建设 项目流程

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/下即对应这批基础块:paragraphatxHeadingsetextHeadingblockQuotethematicBreakcodeBlockhtml等,统一经 src/block/index.ts 中ScrollPage.register(...)注册。事件处理方面,Editor持有经 RxJS 合并的 DOM 事件流(clickinputkeydownkeyupcompositionstart/end),并路由到活跃块的处理函数——这正是"各类事件处理"条目在 TS 重写后的实现形态。

2020 年 7 月:更多块,支持输入与输出

原始目标清单:

  • 行内与段落的复制粘贴;
  • 多选段落复制粘贴;
  • 有序列表、无序列表、任务列表;
  • 列表拖拽,并把完成项自动移至末尾;
  • 代码块;
  • HTML 块;
  • 表格块;
  • 历史记录(undo/redo);
  • 与其他文件类型(markdown 与 html)的输入输出。

这些能力在当前代码中的落点:

  • 列表与任务列表:src/block/gfm/taskList.ts 同目录下的orderListbulletListlistItem(commonMark 侧)与TaskListItemTaskListCheckbox附件节点;
  • 表格块:src/block/gfm/table/下的TableTableInnerTableRowCellTableCellContent
  • 代码块与 HTML 块:src/block/commonMark/codeBlocksrc/block/commonMark/html(含htmlContainerhtmlPreview);
  • 历史记录: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.tscopyData.tscut.ts及 27 个单测文件。

2020 年 8 月:全功能版本

原始目标清单:

  • 数学公式块;
  • mermaid;
  • flowchartsequence(划掉,后续弃用);
  • footnote(原清单未勾选);
  • front matter;
  • 上标、下标、数学公式等行内元素;
  • 段前菜单(paragraph front menu);
  • 快速插入菜单(quick insert menu)。

对照现状:

  • 数学公式块:src/block/extra/math/MathBlockMathContainerMathPreview),KaTeX 在 package.json 依赖中;
  • 图表块:src/block/extra/diagram/DiagramBlockDiagramContainerDiagramPreview),图表渲染集成在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 的buildlint:types: tsc --noEmit)。关于第二项的"no any",从 e2e 侧规范看,e2e/README.md 明确写道该项目禁止anyts/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(生态更好、更易读)。

逐项源码佐证:

  1. marked 补丁化:packages/muya/src/utils/marked/ 是一个成体系的本地化 marked 集成,包含lexBlock.tswalkTokens.tsfrontMatter.tscompatibleTaskList.tsextensions/等,而非直接调用 npm 包默认行为——即"patch the latest marked to muya"的落地形态。markedmarked-highlight仍出现在 package.json 依赖中,补丁层构建在其上。
  2. 移除 axios / XMLHttpRequest:在packages/muya/src内检索axios|XMLHttpRequest无任何命中,证实该条目完成——引擎不再在核心中做网络请求。
  3. 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 条目互相印证。
  4. 弃用 sequence 与 flowchart:当前图表链路保留 mermaid(含 sequence/flowchart 的 e2e 测试e2e/tests/diagrams/中仍有sequence.spec.tsflowchart.spec.ts等场景用于回归),从源码结构看,2020 年清单中划掉的旧 flowchart/sequence 独立实现(对应 muyajs 时代的 Snap.svg 方案)已被弃用,统一收敛到src/utils/diagram/的渲染器集成。
  5. turndown 优化src/utils/turndownService/目录承载定制化的 turndown 封装,配合src/state/htmlToMarkdown.ts完成 HTML→Markdown 的粘贴转换;turndownjoplin-turndown-plugin-gfm均在依赖中。
  6. 未决项: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。

证据:

  1. vite 替换 webpack:packages/muya/vite.config.ts 即当前构建配置,package.json 的build脚本为tsc && vite build,产物为lib/{es,umd,cjs}lib/typesvite-plugin-dts负责声明文件,@laynezh/vite-plugin-lib-assets负责图标与字体资源路由);开发侧examplese2e/host均跑 Vite dev server。
  2. CI 侧:仓库.github/workflows/下已有muya-build.ymlmuya-test.ymlmuya-spec.ymlmuya-e2e.ymlmuya-lint.ymlmuya-circular.yml等一整套 muya 专属流水线;madge 循环依赖检查(pnpm -C packages/muya check-circular,即madge --circular src/index.ts)也在 CI 中强制执行。
  3. 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.typedelay: 0时会丢字符——muya 的 content-change 管线每次按键同步重渲染,超过 4 个字符的输入应使用tests/helpers/keyboard.ts中的slowType()(每字符 30ms);
  • 浮动插件用opacity: 0隐藏而非display: noneexpect(...).toBeHidden()无效,需断言计算后的 opacity;
  • getMarkdown()在最后一次击键后异步读取 state,读 Markdown 前先用expect(domNode).toContainText(...)作为同步屏障;
  • 优先用公共 API(getMarkdown/getState/getTOC)断言,而非 DOM 正则——Markdown 序列化是确定性的,snabbdom 输出则可能漂移。

路线图全景核对表

把 ROADMAP 原清单汇总为一张当前状态核对表(状态以本仓库源码与文档为准):

方向条目原状态当前仓库证据
OptimizationJS 转 TS完成整个src/为 TS,tsc && vite build
Optimizationno any / strict未完成e2e 侧ts/no-explicit-any: error;主包以lint:types+ ESLint 把关
Code Designmarked 补丁化完成src/utils/marked/本地集成层
Code Design移除 axios/XHR完成src/内无相关引用
Code Designconstructor mixin完成src/block/mixins/*.ts
Code Design弃用 sequence/flowchart完成图表统一收敛至src/utils/diagram/+ mermaid 等
Code Designturndown 优化完成src/utils/turndownService/+ GFM 插件
Code DesignmarkdownToHtml 清理 / DI / TSX未完成渲染层仍为 snabbdom,UI 体系未变
CompatibilityFirefox完成e2e firefox 矩阵 + CI 配置
CompatibilitySafari / Edge未完成仓库中无相应适配证据
Documents官网 / 文档推进中monorepo 含packages/websiteexamples
CI/CDvite 替换 webpack完成vite.config.ts、e2e/examples 均 Vite
CI/CDGitHub Action 自动发 npm未完成@muyajs/core不从本仓库发布(CLAUDE.md)
Test单元测试已完成vitest,386 tests / 43 files(0.2.0 changelog)
Teste2e 测试已完成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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询