Skip to content

Design Decisions

韩梦楠 edited this page Nov 6, 2026 · 2 revisions

设计决策与已知边界

这一页解释「为什么是这样」——尤其是与官方行为或标准语法不同的地方。它们是决策,不是缺陷。

$ 永远是货币符

标准 markdown 数学扩展普遍使用 $x$ 作为内联公式。本库刻意不用:业务文本(财报、价格、预算)里 $ 是货币符号,把 $24.4B 误判为公式是真实事故。因此:

  • 内联公式用 $$…$$,块公式用换行包裹的 $$
  • 输入 $ / $$ 永远只产生文本,$24.4B$ 也不是公式
  • 导出时货币 $ 转义为 \$

已拍板保持现状。公式只能从工具栏「更多」菜单插入,无打字快捷方式。

付费组件不进入 MIT 包

Tiptap 官方 UI 组件分免费与付费(对照清单见 README 说明;判定方法:付费组件的注册接口返回 401 / npm 上不存在)。本库的应对分三类:

  1. 免费部分直接使用:表格的增删/表头/合并命令、ImageUploadNode 交互、查找替换官方扩展、text-align
  2. 付费/不存在的自研替代:表格悬停手柄(官方 table-node 是付费组件,且拖拽重排逻辑在其内部源码里)、图片悬停对齐条(官方 image-align-button 付费)、图片图注(extension-figure 在 npm 上不存在)
  3. 「移植逻辑,不 vendor 代码」:LinkPopover 按官方 MIT 组件的逻辑重写,无 Tailwind、无额外运行时依赖,并顺带修了官方实现里的两个 bug(被拒地址仍改动正文、改链接文字被换成地址)

第三方内容(Lucide 图标、Tiptap UI 组件逻辑、KaTeX 样式与字体)的署名见 THIRD_PARTY_LICENSES.md。

GFM 没有 colspan,所以不暴露合并单元格

mergeCells / splitCell 产生的合并结构在 GFM 里无处安放(导出为 | 1<br>2 |),重新加载必然丢结构。所以工具栏刻意不提供这两个命令,只提供清空内容(保留单元格,表头不会降级为数据行)和重置单元格样式。

依赖精确锁版本(不带 ^)

所有 @tiptap/* 依赖使用精确版本号。原因:pnpm 宿主项目里,caret 范围可能解析到与宿主锁文件不一致的小版本,ProseMirror 插件状态跨小版本可能不兼容(0.19.1 的教训)。新增依赖后自查 package.json。

「只在用到时」才写 HTML

图片缩放/对齐/图注、段落样式没有 markdown 语法,但库不因此把所有内容 HTML 化:只有该样式真的被设置时才导出 <img …> / <p style>,未触碰的内容永远保持原生 markdown。这样 GitHub 等其他渲染器净化 style 时丢的是装饰,不是内容。详见Markdown 往返规则。

工具栏显隐与点击能力分开

按钮出不出现只看 groups 和 hidden,不看有没有上传、查找或历史回调。

  • 不传 groups 时用 defaultToolbarGroups。hidden 从这份默认清单里划掉内置 id。空组连分隔线一起消失。
  • 传了 groups 就整栏替换,顺序是拍平后的数组,组与组之间一条竖线。{ more: [...] } 是唯一的「更多」。
  • image、search、history 不在默认清单里。只传 onImageUpload / onSearch / onHistory 不会出现按钮。写进布局但没有回调时,按钮仍渲染,点击没有反应。
  • 下拉内部(标题级别、字号、对齐、编号格式、上下标)不能单独关,要关就关整颗按钮。
  • showImport / showFullscreen 已删除。全屏钩子是否生效,看解析后的布局里还有没有 fullscreen。

已在 1.12.0 发布。复制走的 groups 不会自动获得以后新增的按钮;不传 groups、只用 hidden 的宿主会在默认清单更新时看到新按钮。

UI 自绘、零 UI 框架

工具栏、调色板、代码块、目录、表格手柄、链接浮层都是包内自绘(Radix primitives + 内联 SVG),主题通过 --tmr-* CSS 变量暴露;中英文 locale 内置,无 labels prop、无 Context provider。