@one2x/medeo-rte 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +209 -2
- package/dist/chunks/types-I0yc5X7i.js +18 -0
- package/dist/chunks/types-I0yc5X7i.js.map +1 -0
- package/dist/formula.js +186 -0
- package/dist/formula.js.map +1 -0
- package/dist/index.js +5503 -0
- package/dist/index.js.map +1 -0
- package/dist/style.css +718 -0
- package/dist/types/commands/index.d.ts +4 -0
- package/dist/types/commands/index.d.ts.map +1 -0
- package/dist/types/commands/selfcheck.d.ts +2 -0
- package/dist/types/commands/selfcheck.d.ts.map +1 -0
- package/dist/types/commands/shortcuts.d.ts +48 -0
- package/dist/types/commands/shortcuts.d.ts.map +1 -0
- package/dist/types/commands/types.d.ts +69 -0
- package/dist/types/commands/types.d.ts.map +1 -0
- package/dist/types/convert/clipboard.d.ts +7 -0
- package/dist/types/convert/clipboard.d.ts.map +1 -0
- package/dist/types/convert/markdown.d.ts +30 -0
- package/dist/types/convert/markdown.d.ts.map +1 -0
- package/dist/types/convert/mdPaste.d.ts +8 -0
- package/dist/types/convert/mdPaste.d.ts.map +1 -0
- package/dist/types/editor.d.ts +469 -0
- package/dist/types/editor.d.ts.map +1 -0
- package/dist/types/edits/editResult.d.ts +62 -0
- package/dist/types/edits/editResult.d.ts.map +1 -0
- package/dist/types/edits/index.d.ts +12 -0
- package/dist/types/edits/index.d.ts.map +1 -0
- package/dist/types/edits/selfcheck.d.ts +2 -0
- package/dist/types/edits/selfcheck.d.ts.map +1 -0
- package/dist/types/edits/transaction.d.ts +35 -0
- package/dist/types/edits/transaction.d.ts.map +1 -0
- package/dist/types/edits/undoStack.d.ts +50 -0
- package/dist/types/edits/undoStack.d.ts.map +1 -0
- package/dist/types/embed/code.d.ts +9 -0
- package/dist/types/embed/code.d.ts.map +1 -0
- package/dist/types/embed/formula-katex.d.ts +4 -0
- package/dist/types/embed/formula-katex.d.ts.map +1 -0
- package/dist/types/embed/formula-mathlive.d.ts +6 -0
- package/dist/types/embed/formula-mathlive.d.ts.map +1 -0
- package/dist/types/embed/formula.d.ts +8 -0
- package/dist/types/embed/formula.d.ts.map +1 -0
- package/dist/types/embed/host.d.ts +70 -0
- package/dist/types/embed/host.d.ts.map +1 -0
- package/dist/types/embed/image.d.ts +3 -0
- package/dist/types/embed/image.d.ts.map +1 -0
- package/dist/types/embed/index.d.ts +20 -0
- package/dist/types/embed/index.d.ts.map +1 -0
- package/dist/types/embed/ref.d.ts +25 -0
- package/dist/types/embed/ref.d.ts.map +1 -0
- package/dist/types/embed/registry.d.ts +6 -0
- package/dist/types/embed/registry.d.ts.map +1 -0
- package/dist/types/embed/slot.d.ts +15 -0
- package/dist/types/embed/slot.d.ts.map +1 -0
- package/dist/types/embed/types.d.ts +53 -0
- package/dist/types/embed/types.d.ts.map +1 -0
- package/dist/types/embed/video.d.ts +3 -0
- package/dist/types/embed/video.d.ts.map +1 -0
- package/dist/types/formula.d.ts +15 -0
- package/dist/types/formula.d.ts.map +1 -0
- package/dist/types/index.d.ts +39 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/input/ime.d.ts +27 -0
- package/dist/types/input/ime.d.ts.map +1 -0
- package/dist/types/input/keyboard.d.ts +21 -0
- package/dist/types/input/keyboard.d.ts.map +1 -0
- package/dist/types/layout/engine.d.ts +55 -0
- package/dist/types/layout/engine.d.ts.map +1 -0
- package/dist/types/layout/harness.d.ts +3 -0
- package/dist/types/layout/harness.d.ts.map +1 -0
- package/dist/types/layout/measure.d.ts +37 -0
- package/dist/types/layout/measure.d.ts.map +1 -0
- package/dist/types/layout/refMetrics.d.ts +5 -0
- package/dist/types/layout/refMetrics.d.ts.map +1 -0
- package/dist/types/layout/selfcheck.d.ts +2 -0
- package/dist/types/layout/selfcheck.d.ts.map +1 -0
- package/dist/types/layout/style.d.ts +4 -0
- package/dist/types/layout/style.d.ts.map +1 -0
- package/dist/types/layout/types.d.ts +133 -0
- package/dist/types/layout/types.d.ts.map +1 -0
- package/dist/types/layout/wrap.d.ts +65 -0
- package/dist/types/layout/wrap.d.ts.map +1 -0
- package/dist/types/model/blocks.d.ts +66 -0
- package/dist/types/model/blocks.d.ts.map +1 -0
- package/dist/types/model/document.d.ts +102 -0
- package/dist/types/model/document.d.ts.map +1 -0
- package/dist/types/model/fragment.d.ts +7 -0
- package/dist/types/model/fragment.d.ts.map +1 -0
- package/dist/types/model/markers.d.ts +14 -0
- package/dist/types/model/markers.d.ts.map +1 -0
- package/dist/types/model/types.d.ts +155 -0
- package/dist/types/model/types.d.ts.map +1 -0
- package/dist/types/model/virtualBlock.d.ts +44 -0
- package/dist/types/model/virtualBlock.d.ts.map +1 -0
- package/dist/types/mutations/apply.d.ts +14 -0
- package/dist/types/mutations/apply.d.ts.map +1 -0
- package/dist/types/mutations/index.d.ts +15 -0
- package/dist/types/mutations/index.d.ts.map +1 -0
- package/dist/types/mutations/invert.d.ts +21 -0
- package/dist/types/mutations/invert.d.ts.map +1 -0
- package/dist/types/mutations/ranges.d.ts +39 -0
- package/dist/types/mutations/ranges.d.ts.map +1 -0
- package/dist/types/mutations/registry.d.ts +27 -0
- package/dist/types/mutations/registry.d.ts.map +1 -0
- package/dist/types/mutations/selection.d.ts +12 -0
- package/dist/types/mutations/selection.d.ts.map +1 -0
- package/dist/types/mutations/selfcheck.d.ts +2 -0
- package/dist/types/mutations/selfcheck.d.ts.map +1 -0
- package/dist/types/mutations/transform.d.ts +33 -0
- package/dist/types/mutations/transform.d.ts.map +1 -0
- package/dist/types/mutations/types.d.ts +128 -0
- package/dist/types/mutations/types.d.ts.map +1 -0
- package/dist/types/operations/context.d.ts +47 -0
- package/dist/types/operations/context.d.ts.map +1 -0
- package/dist/types/operations/entity.d.ts +18 -0
- package/dist/types/operations/entity.d.ts.map +1 -0
- package/dist/types/operations/harness.d.ts +54 -0
- package/dist/types/operations/harness.d.ts.map +1 -0
- package/dist/types/operations/index.d.ts +20 -0
- package/dist/types/operations/index.d.ts.map +1 -0
- package/dist/types/operations/primitives.d.ts +35 -0
- package/dist/types/operations/primitives.d.ts.map +1 -0
- package/dist/types/operations/resolve.d.ts +31 -0
- package/dist/types/operations/resolve.d.ts.map +1 -0
- package/dist/types/operations/selfcheck.d.ts +2 -0
- package/dist/types/operations/selfcheck.d.ts.map +1 -0
- package/dist/types/operations/slice.d.ts +28 -0
- package/dist/types/operations/slice.d.ts.map +1 -0
- package/dist/types/operations/style.d.ts +20 -0
- package/dist/types/operations/style.d.ts.map +1 -0
- package/dist/types/operations/table.d.ts +14 -0
- package/dist/types/operations/table.d.ts.map +1 -0
- package/dist/types/operations/text.d.ts +20 -0
- package/dist/types/operations/text.d.ts.map +1 -0
- package/dist/types/operations/virtualBlock.d.ts +44 -0
- package/dist/types/operations/virtualBlock.d.ts.map +1 -0
- package/dist/types/paint/canvasPainter.d.ts +37 -0
- package/dist/types/paint/canvasPainter.d.ts.map +1 -0
- package/dist/types/paint/decorations.d.ts +19 -0
- package/dist/types/paint/decorations.d.ts.map +1 -0
- package/dist/types/paint/htmlPainter.d.ts +19 -0
- package/dist/types/paint/htmlPainter.d.ts.map +1 -0
- package/dist/types/paint/types.d.ts +36 -0
- package/dist/types/paint/types.d.ts.map +1 -0
- package/package.json +64 -4
package/README.md
CHANGED
|
@@ -1,3 +1,210 @@
|
|
|
1
|
-
#
|
|
1
|
+
# medeo-rte
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
自绘的文档渲染引擎。不使用 `contenteditable` —— 文档是一条字符流,排版自己算出每个字的坐标,绘制后端可替换。
|
|
4
|
+
|
|
5
|
+
同一份排版结果可以画到 DOM,也可以一键切到 Canvas,光标和选区不变。
|
|
6
|
+
|
|
7
|
+
在线 demo:<https://editor-html-demo.pages.dev/>
|
|
8
|
+
|
|
9
|
+
## 仓库结构
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
src/ SDK。发布出去的就是这部分
|
|
13
|
+
demo/ 演示应用。同时是 SDK 的用法样例,不参与发布
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
SDK 只负责编辑区本身。工具栏、状态栏、小地图、基线网格这些**宿主外壳**全在 `demo/`,
|
|
17
|
+
产品接入时应自己实现——照着 `demo/main.ts` 抄即可。模板混排样例在 `demo/vblocks.ts`。
|
|
18
|
+
|
|
19
|
+
## 在其它项目里使用
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm i @one2x/medeo-rte
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { Editor } from '@one2x/medeo-rte';
|
|
27
|
+
import '@one2x/medeo-rte/style.css';
|
|
28
|
+
|
|
29
|
+
const editor = new Editor(document.getElementById('app')!);
|
|
30
|
+
|
|
31
|
+
// 下命令
|
|
32
|
+
editor.commands.toggleBold();
|
|
33
|
+
|
|
34
|
+
// 同步自己的工具栏
|
|
35
|
+
editor.on('statechange', () => renderToolbar(editor.getState()));
|
|
36
|
+
|
|
37
|
+
// 存取内容(Slice 可 JSON 序列化)
|
|
38
|
+
const json = editor.getContent();
|
|
39
|
+
editor.setContent(json);
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**公式需要单独引入**,因为它依赖 KaTeX + MathLive,是包体积的大头:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { builtinEmbedPlugins, Editor } from '@one2x/medeo-rte';
|
|
46
|
+
import { formulaPlugin } from '@one2x/medeo-rte/formula';
|
|
47
|
+
|
|
48
|
+
new Editor(el, { plugins: [...builtinEmbedPlugins, formulaPlugin] });
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
不引入就完全不会打进产物(`katex` / `mathlive` 是可选 peer 依赖)。
|
|
52
|
+
|
|
53
|
+
## 主要能力
|
|
54
|
+
|
|
55
|
+
文字与段落样式、列表、表格、链接、图文绕排、Markdown 边打边转、撤销重做、中文 IME。
|
|
56
|
+
|
|
57
|
+
嵌入对象走同一套插件接口:图片、视频、代码块、公式、行内胶囊插槽、同步块引用、**host**(宿主自绘)。其中:
|
|
58
|
+
|
|
59
|
+
- **同步块** —— 任意选区提成一份内容,多处平等复用,改一处全变
|
|
60
|
+
- **host 胶囊** —— `*` 占位,内部 DOM 由宿主渲染(下拉、开关、上传、`@` 芯片)。整颗换行
|
|
61
|
+
- **虚拟块** —— 盖在字符流上的假块(region 皮 + 文本槽)。槽里的字跟外层句子一起折行,不是实体
|
|
62
|
+
|
|
63
|
+
模板句里「可填写的那截灰字」用虚拟块文本槽,不要用胶囊 `slotPlugin`。胶囊插槽是另一颗盒子,点进去才填,不能按字切开。
|
|
64
|
+
|
|
65
|
+
## 模板混排
|
|
66
|
+
|
|
67
|
+
插入一份模板(文案 + 文本槽 + 胶囊)走 `insertSlice`,不要再开子文档。
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
editor.insertSlice({
|
|
71
|
+
text: 'Write a prompt for Input a theme.\n',
|
|
72
|
+
styles: [],
|
|
73
|
+
entities: [],
|
|
74
|
+
virtualBlocks: [
|
|
75
|
+
{
|
|
76
|
+
id: 'region',
|
|
77
|
+
parentId: null,
|
|
78
|
+
kind: 'region',
|
|
79
|
+
start: 0,
|
|
80
|
+
end: 33,
|
|
81
|
+
chrome: { background: 'rgba(43,108,255,0.08)', radius: 8 }
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
id: 'slot',
|
|
85
|
+
parentId: 'region',
|
|
86
|
+
kind: 'slot',
|
|
87
|
+
start: 19,
|
|
88
|
+
end: 32,
|
|
89
|
+
placeholder: 'Input a theme',
|
|
90
|
+
empty: true,
|
|
91
|
+
chrome: { background: 'rgba(16,185,129,0.18)', placeholderColor: '#98a2b3' }
|
|
92
|
+
}
|
|
93
|
+
]
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
槽上的 Sample / AI 菜单是宿主浮层:看 `getState().vblockStack` 是否进了 editing,用 `getLayout()` 里该槽的 `vblocks` 矩形定位,写入走打字或再 `insertSlice` 一段纯文本。SDK 不认识业务菜单。
|
|
98
|
+
|
|
99
|
+
自定义芯片:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
const editor = new Editor(el, {
|
|
103
|
+
hostEmbed: {
|
|
104
|
+
mount(container, ctx) {
|
|
105
|
+
// 按 ctx.entity.hostType 画自己的组件
|
|
106
|
+
return { update(next) {}, destroy() {} };
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
editor.commands.insertHostEmbed('Upscale', { label: '@Upscale' }, { width: 88, height: 24 });
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
口径和交互见 [virtual-blocks.md](./docs/virtual-blocks.md)。
|
|
114
|
+
|
|
115
|
+
### API 一览
|
|
116
|
+
|
|
117
|
+
| 成员 | 说明 |
|
|
118
|
+
| --- | --- |
|
|
119
|
+
| `new Editor(el, options?)` | `doc` / `paint` / `plugins` / `hostEmbed` / `width` / `padding` / `placeholder` / `textDefaults` / `shortcuts` / `autofocus` / `chrome` |
|
|
120
|
+
| `commands` | 公开编辑命令(加粗、插表、插图、`insertHostEmbed`、同步块等),宿主工具栏调这些 |
|
|
121
|
+
| `on(event, fn)` | `change` / `selectionchange` / `statechange` / `layoutchange`,返回退订函数 |
|
|
122
|
+
| `getState()` | 当前状态快照(含 `vblockStack`),用于同步 UI |
|
|
123
|
+
| `getContent()` / `setContent()` | 整篇内容读写,`Slice` 结构(含 `virtualBlocks`) |
|
|
124
|
+
| `insertSlice(slice, at?)` | 插入切片;模板混排走这条 |
|
|
125
|
+
| `insertVirtualBlock` / `updateVirtualBlock` / `removeVirtualBlock` | 套皮、改 chrome、拆皮 |
|
|
126
|
+
| `insertHostEmbed(hostType, data?, size?, at?)` | 插入宿主自绘胶囊 |
|
|
127
|
+
| `getSelection()` / `setSelection()` | 选区读写 |
|
|
128
|
+
| `getLayout()` | 排版几何(只读),做标尺、小地图、槽上浮层用 |
|
|
129
|
+
| `getDocument()` | 文档模型,做批量操作或自定义命令用 |
|
|
130
|
+
| `setPaintMode('html'\|'canvas')` | 切换绘制后端 |
|
|
131
|
+
| `setChrome` / `setTextDefaults` | 光标选区外观;默认字号/颜色(主题切换) |
|
|
132
|
+
| `root` / `scroller` / `pageElement` | 根节点 / 滚动容器 / 内容定位上下文 |
|
|
133
|
+
| `focus()` / `destroy()` | |
|
|
134
|
+
| `setPlaceholder(text)` | 空态占位;组合输入一开始即隐藏 |
|
|
135
|
+
| `composing()` / `setDisabled()` | 组合态查询;禁用输入与快捷键 |
|
|
136
|
+
| `INPUT_SHORTCUTS` / `inputShortcuts()` | 输入框快捷键;默认 Enter 换行,Cmd+Enter 留给宿主 |
|
|
137
|
+
|
|
138
|
+
脚本化改文档也可 `import { operations, runOperation } from '@one2x/medeo-rte'`,不要直接改 `DocumentModel` 的内部表。
|
|
139
|
+
|
|
140
|
+
## 设计文档
|
|
141
|
+
|
|
142
|
+
`docs/` 下是分层架构与技术方案:
|
|
143
|
+
|
|
144
|
+
| 文档 | 内容 |
|
|
145
|
+
| --- | --- |
|
|
146
|
+
| [architecture.md](./docs/architecture.md) | 分层结构、五条不变式、样式模型 |
|
|
147
|
+
| [mutations.md](./docs/mutations.md) | 原子操作的类型、坐标语义、三套 handler 契约 |
|
|
148
|
+
| [operations.md](./docs/operations.md) | 编排层规范与 Operation 清单 |
|
|
149
|
+
| [edits.md](./docs/edits.md) | 事务边界、撤销栈、片段坐标空间、协同预留 |
|
|
150
|
+
| [rendering.md](./docs/rendering.md) | 块级增量排版、脏区带、绘制后端 |
|
|
151
|
+
| [virtual-blocks.md](./docs/virtual-blocks.md) | 虚拟块:模板混排、文本槽、按层选区 |
|
|
152
|
+
|
|
153
|
+
改动模型前先读 architecture.md 的不变式那一节。
|
|
154
|
+
|
|
155
|
+
## 本地开发
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
npm install
|
|
159
|
+
npm run dev # http://localhost:5273/ 跑 demo
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
| 命令 | 作用 |
|
|
163
|
+
| ------------------- | ----------------------------- |
|
|
164
|
+
| `npm run dev` | 起 demo 开发服务器 |
|
|
165
|
+
| `npm run build` | 构建 demo 站点 → `dist-demo/` |
|
|
166
|
+
| `npm run build:lib` | 构建 SDK → `dist/`(含 `.d.ts`) |
|
|
167
|
+
| `npm run selfcheck` | 分层自检:编辑语义、变换算术、撤销栈、快捷键表、增量排版 |
|
|
168
|
+
|
|
169
|
+
## 发布到 npm
|
|
170
|
+
|
|
171
|
+
包名 `@one2x/medeo-rte`(npm 组织 **one2x**)。CI:`.github/workflows/publish.yml`。
|
|
172
|
+
GitHub 仓库在 **one2x-ai/medeo-rte** —— 和 npm scope 不是同一个名字。
|
|
173
|
+
|
|
174
|
+
### 日常发版(main 打 tag)
|
|
175
|
+
|
|
176
|
+
1. 合进 **main**,并把 `package.json` 的 `version` 改成目标版本(如 `0.1.0`)
|
|
177
|
+
2. 在 **main** tip 打同名 tag 并推送:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
git checkout main && git pull
|
|
181
|
+
git tag v0.1.0
|
|
182
|
+
git push origin v0.1.0
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
3. Actions 里应出现 **Publish**;tag 必须指向 main 上的提交,且 `v*` 与 `package.json` version 一致,否则会失败
|
|
186
|
+
|
|
187
|
+
也可在 Actions → Publish → **Run workflow**(必须选 **main**;选 `dev` 会被拒绝)。
|
|
188
|
+
|
|
189
|
+
### 第一次上架(包还不存在)
|
|
190
|
+
|
|
191
|
+
Trusted Publishing 配不上尚未存在的包,先用 token:
|
|
192
|
+
|
|
193
|
+
1. [npm Access Tokens](https://www.npmjs.com/settings/~/tokens) → **Generate New Token** → **Granular Access Token**
|
|
194
|
+
- Permissions:**Read and write**
|
|
195
|
+
- Packages and scopes:选组织 **one2x** / 勾选 `@one2x`
|
|
196
|
+
- **Bypass two-factor authentication**:打开(给 CI)
|
|
197
|
+
2. GitHub 仓库 → Settings → Secrets and variables → Actions → New secret
|
|
198
|
+
名称:`NPM_TOKEN`,值:刚生成的 token
|
|
199
|
+
3. 按上面「日常发版」打 `v0.1.0`(或对 main 手动 Run workflow)
|
|
200
|
+
4. 确认 [npm 包页](https://www.npmjs.com/package/@one2x/medeo-rte) 出现 `0.1.0`
|
|
201
|
+
|
|
202
|
+
### 上架后改走 OIDC(可选,推荐)
|
|
203
|
+
|
|
204
|
+
1. 包 Settings → **Trusted Publisher** → GitHub Actions
|
|
205
|
+
- Organization:`one2x-ai`
|
|
206
|
+
- Repository:`medeo-rte`
|
|
207
|
+
- Workflow:`publish.yml`
|
|
208
|
+
- Environment:留空
|
|
209
|
+
- Allowed:勾选 `npm publish`
|
|
210
|
+
2. 删掉仓库 Secret `NPM_TOKEN`(publish 步骤带着 token 时不会走 OIDC)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const s = "mini-embed-change", n = "mini-embed-commit", a = "mini-embed-exit-edit", E = "mini-embed-measure";
|
|
2
|
+
function d(t, e) {
|
|
3
|
+
t.style.left = `${e.x}px`, t.style.top = `${e.y}px`, e.entity.kind === "host" ? (t.style.width = "auto", t.style.height = "auto") : (t.style.width = `${e.w}px`, t.classList.contains("is-editing") || (t.style.height = `${e.h}px`)), t.dataset.index = String(e.index), t.dataset.entity = e.entity.id;
|
|
4
|
+
}
|
|
5
|
+
function m(t, e, i = !1) {
|
|
6
|
+
t.dispatchEvent(
|
|
7
|
+
new CustomEvent(i ? n : s, { bubbles: !0, detail: e })
|
|
8
|
+
);
|
|
9
|
+
}
|
|
10
|
+
export {
|
|
11
|
+
a as E,
|
|
12
|
+
n as a,
|
|
13
|
+
s as b,
|
|
14
|
+
E as c,
|
|
15
|
+
m as e,
|
|
16
|
+
d as p
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=types-I0yc5X7i.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types-I0yc5X7i.js","sources":["../../src/embed/types.ts"],"sourcesContent":["import { DocumentModel } from '../model/document';\nimport { EmbedEntity, EmbedKind } from '../model/types';\nimport type { DocLayout } from '../layout/types';\n\nexport interface EmbedBox {\n index: number;\n x: number;\n y: number;\n w: number;\n h: number;\n entity: EmbedEntity;\n inner?: DocLayout;\n}\n\nexport const EMBED_CHANGE = 'mini-embed-change';\nexport const EMBED_COMMIT = 'mini-embed-commit';\nexport const EMBED_EXIT_EDIT = 'mini-embed-exit-edit';\n/**\n * 宿主渲染块回报自身布局尺寸。这是渲染副产物、不是用户意图,编辑器只据此重排,\n * 既不进撤销栈、也不开启编辑事务(区别于表示用户改动的 CHANGE / COMMIT)。\n */\nexport const EMBED_MEASURE = 'mini-embed-measure';\n\nexport interface EmbedMeasureDetail {\n id: string;\n width: number;\n height: number;\n}\n\nexport interface EmbedExitDetail {\n id: string;\n side: 'before' | 'after';\n}\n\nexport interface EmbedChangeDetail {\n id: string;\n patch: Partial<EmbedEntity>;\n before?: Partial<EmbedEntity>;\n}\n\nexport interface EmbedPlugin {\n kind: EmbedKind;\n keepAspectRatio: boolean;\n interactive: boolean;\n /** Canvas 上是否叠 DOM(视频/代码/公式);图片只画到 canvas */\n overlayOnCanvas: boolean;\n measure(entity: EmbedEntity, contentWidth: number, host?: DocumentModel): { width: number; height: number };\n create(box: EmbedBox): HTMLElement;\n update(el: HTMLElement, box: EmbedBox): void;\n /** 节点从文档移除前调用,供插件清理副作用(观察器、外部组件等)。可选。 */\n destroy?(el: HTMLElement): void;\n paintCanvasBackdrop(ctx: CanvasRenderingContext2D, box: EmbedBox, imageOf: (src: string) => HTMLImageElement): void;\n}\n\nexport function placeEmbed(el: HTMLElement, box: EmbedBox) {\n el.style.left = `${box.x}px`;\n el.style.top = `${box.y}px`;\n // host 外层若按尚未测到的 0×0 锁宽高,内层会被挤成竖排文字,ResizeObserver 再把竖排尺寸写回,恶性循环。\n if (box.entity.kind === 'host') {\n el.style.width = 'auto';\n el.style.height = 'auto';\n } else {\n el.style.width = `${box.w}px`;\n if (!el.classList.contains('is-editing')) el.style.height = `${box.h}px`;\n }\n el.dataset.index = String(box.index);\n el.dataset.entity = box.entity.id;\n}\n\nexport function emitEmbedChange(el: HTMLElement, detail: EmbedChangeDetail, commit = false) {\n el.dispatchEvent(\n new CustomEvent(commit ? EMBED_COMMIT : EMBED_CHANGE, { bubbles: true, detail })\n );\n}\n"],"names":["EMBED_CHANGE","EMBED_COMMIT","EMBED_EXIT_EDIT","EMBED_MEASURE","placeEmbed","el","box","emitEmbedChange","detail","commit"],"mappings":"AAcO,MAAMA,IAAe,qBACfC,IAAe,qBACfC,IAAkB,wBAKlBC,IAAgB;AAiCtB,SAASC,EAAWC,GAAiBC,GAAe;AACzD,EAAAD,EAAG,MAAM,OAAO,GAAGC,EAAI,CAAC,MACxBD,EAAG,MAAM,MAAM,GAAGC,EAAI,CAAC,MAEnBA,EAAI,OAAO,SAAS,UACtBD,EAAG,MAAM,QAAQ,QACjBA,EAAG,MAAM,SAAS,WAElBA,EAAG,MAAM,QAAQ,GAAGC,EAAI,CAAC,MACpBD,EAAG,UAAU,SAAS,YAAY,MAAGA,EAAG,MAAM,SAAS,GAAGC,EAAI,CAAC,QAEtED,EAAG,QAAQ,QAAQ,OAAOC,EAAI,KAAK,GACnCD,EAAG,QAAQ,SAASC,EAAI,OAAO;AACjC;AAEO,SAASC,EAAgBF,GAAiBG,GAA2BC,IAAS,IAAO;AAC1F,EAAAJ,EAAG;AAAA,IACD,IAAI,YAAYI,IAASR,IAAeD,GAAc,EAAE,SAAS,IAAM,QAAAQ,EAAA,CAAQ;AAAA,EAAA;AAEnF;"}
|
package/dist/formula.js
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import M from "katex";
|
|
2
|
+
import "katex/dist/katex.min.css";
|
|
3
|
+
import { MathfieldElement as E } from "mathlive";
|
|
4
|
+
import "mathlive/fonts.css";
|
|
5
|
+
import { e as w, E as L } from "./chunks/types-I0yc5X7i.js";
|
|
6
|
+
const C = {
|
|
7
|
+
"\\wideparen": "\\overgroup{#1}",
|
|
8
|
+
"\\overarc": "\\overgroup{#1}",
|
|
9
|
+
"\\overparen": "\\overgroup{#1}",
|
|
10
|
+
"\\exponentialE": "\\mathrm{e}",
|
|
11
|
+
"\\imaginaryI": "\\mathrm{i}",
|
|
12
|
+
"\\imaginaryJ": "\\mathrm{j}",
|
|
13
|
+
"\\differentialD": "\\mathrm{d}",
|
|
14
|
+
"\\capitalDifferentialD": "\\mathrm{D}"
|
|
15
|
+
}, D = [
|
|
16
|
+
{ name: "frac", build: (e, t) => `\\frac{${e}}{${t}}` },
|
|
17
|
+
{ name: "sqrt", build: (e, t) => `\\sqrt[${t}]{${e}}` },
|
|
18
|
+
{ name: "int", build: (e, t) => `\\int_{${t}}^{${e}}` },
|
|
19
|
+
{ name: "overbrace", build: (e, t) => `\\overbrace{${e}}^{${t}}` }
|
|
20
|
+
];
|
|
21
|
+
function $(e) {
|
|
22
|
+
let t = e;
|
|
23
|
+
for (const r of D) t = k(t, r);
|
|
24
|
+
return t = g(t, "index", (r) => `_{${r}}`), t.replace(
|
|
25
|
+
/\\(Uparrow|Downarrow|Updownarrow|uparrow|downarrow|updownarrow|Rightarrow|Leftarrow|Leftrightarrow)(?=[A-Za-z])/g,
|
|
26
|
+
"\\$1 "
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
function k(e, t) {
|
|
30
|
+
return g(e, t.name, (r) => {
|
|
31
|
+
const i = A(r);
|
|
32
|
+
return i === void 0 ? `\\${t.name}{${r}}` : t.build(i[0], i[1]);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
function g(e, t, r) {
|
|
36
|
+
const i = `\\${t}{`;
|
|
37
|
+
let n = "", o = 0;
|
|
38
|
+
for (; o < e.length; ) {
|
|
39
|
+
const s = e.indexOf(i, o);
|
|
40
|
+
if (s < 0) return n + e.slice(o);
|
|
41
|
+
const f = s + i.length - 1, l = b(e, f);
|
|
42
|
+
if (l < 0) return n + e.slice(o);
|
|
43
|
+
n += e.slice(o, s) + r(e.slice(f + 1, l)), o = l + 1;
|
|
44
|
+
}
|
|
45
|
+
return n;
|
|
46
|
+
}
|
|
47
|
+
function b(e, t) {
|
|
48
|
+
let r = 0;
|
|
49
|
+
for (let i = t; i < e.length; i += 1)
|
|
50
|
+
if (e[i] === "{" && !c(e, i) && (r += 1), e[i] === "}" && !c(e, i) && (r -= 1, r === 0))
|
|
51
|
+
return i;
|
|
52
|
+
return -1;
|
|
53
|
+
}
|
|
54
|
+
function A(e) {
|
|
55
|
+
let t = 0, r = -1;
|
|
56
|
+
for (let i = 0; i < e.length; i += 1)
|
|
57
|
+
if (e[i] === "{" && !c(e, i)) t += 1;
|
|
58
|
+
else if (e[i] === "}" && !c(e, i)) t -= 1;
|
|
59
|
+
else if (e[i] === "|" && t === 0 && !c(e, i)) {
|
|
60
|
+
if (r >= 0) return;
|
|
61
|
+
r = i;
|
|
62
|
+
}
|
|
63
|
+
return r < 0 ? void 0 : [e.slice(0, r), e.slice(r + 1)];
|
|
64
|
+
}
|
|
65
|
+
function c(e, t) {
|
|
66
|
+
let r = 0;
|
|
67
|
+
for (let i = t - 1; i >= 0 && e[i] === "\\"; i -= 1) r += 1;
|
|
68
|
+
return r % 2 === 1;
|
|
69
|
+
}
|
|
70
|
+
function p(e, t, r) {
|
|
71
|
+
e.replaceChildren();
|
|
72
|
+
const i = t.trim() || "\\square";
|
|
73
|
+
try {
|
|
74
|
+
M.render($(i), e, {
|
|
75
|
+
throwOnError: !1,
|
|
76
|
+
displayMode: r,
|
|
77
|
+
output: "html",
|
|
78
|
+
macros: C
|
|
79
|
+
});
|
|
80
|
+
} catch {
|
|
81
|
+
e.textContent = i;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
E.fontsDirectory = null;
|
|
85
|
+
function P() {
|
|
86
|
+
const e = new E();
|
|
87
|
+
return e.className = "mini-formula-math", e.mathVirtualKeyboardPolicy = "auto", e;
|
|
88
|
+
}
|
|
89
|
+
function y(e) {
|
|
90
|
+
return e.getValue("latex");
|
|
91
|
+
}
|
|
92
|
+
function N(e, t) {
|
|
93
|
+
const r = Math.max(48, e.width || Math.min(400, t));
|
|
94
|
+
if (typeof document > "u")
|
|
95
|
+
return { width: r, height: e.displayMode === !1 ? 32 : 56 };
|
|
96
|
+
const i = document.createElement("div");
|
|
97
|
+
i.style.cssText = "position:absolute;left:-9999px;top:0;visibility:hidden;", document.body.appendChild(i), p(i, e.latex ?? "", e.displayMode !== !1);
|
|
98
|
+
const n = i.getBoundingClientRect();
|
|
99
|
+
return i.remove(), {
|
|
100
|
+
width: Math.min(t, Math.max(48, Math.ceil(n.width) + 24)),
|
|
101
|
+
height: Math.max(36, Math.ceil(n.height) + 16)
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function m(e, t) {
|
|
105
|
+
return e.dataset.entity || t;
|
|
106
|
+
}
|
|
107
|
+
const K = {
|
|
108
|
+
kind: "formula",
|
|
109
|
+
keepAspectRatio: !1,
|
|
110
|
+
interactive: !0,
|
|
111
|
+
overlayOnCanvas: !0,
|
|
112
|
+
measure: N,
|
|
113
|
+
create(e) {
|
|
114
|
+
const t = document.createElement("div");
|
|
115
|
+
t.className = "mini-embed mini-formula mini-embed-interactive";
|
|
116
|
+
const r = document.createElement("div");
|
|
117
|
+
r.className = "mini-formula-view";
|
|
118
|
+
const i = document.createElement("div");
|
|
119
|
+
i.className = "mini-formula-host", i.hidden = !0, p(r, e.entity.latex ?? "", e.entity.displayMode !== !1), t.append(r, i);
|
|
120
|
+
let n = null, o = !1, s = e.entity.latex ?? "";
|
|
121
|
+
const f = () => n || (n = P(), i.appendChild(n), n.addEventListener("mount", () => {
|
|
122
|
+
o = !0, n == null || n.setValue(s, { silenceNotifications: !0 }), n == null || n.focus();
|
|
123
|
+
}, { once: !0 }), n.addEventListener("input", () => {
|
|
124
|
+
n && w(t, { id: m(t, e.entity.id), patch: { latex: y(n) } });
|
|
125
|
+
}), n.addEventListener("focusout", () => {
|
|
126
|
+
window.setTimeout(() => {
|
|
127
|
+
i.contains(document.activeElement) || u();
|
|
128
|
+
}, 0);
|
|
129
|
+
}), n.addEventListener(
|
|
130
|
+
"keydown",
|
|
131
|
+
(a) => {
|
|
132
|
+
const d = n;
|
|
133
|
+
if (d) {
|
|
134
|
+
if (a.key === "ArrowLeft" && d.selectionIsCollapsed && d.position === 0) {
|
|
135
|
+
a.preventDefault(), a.stopPropagation(), v("before");
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (a.key === "ArrowRight" && d.selectionIsCollapsed && d.position === d.lastOffset) {
|
|
139
|
+
a.preventDefault(), a.stopPropagation(), v("after");
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
a.key === "Enter" && !a.shiftKey && (a.preventDefault(), u()), a.key === "Escape" && (a.preventDefault(), x());
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
!0
|
|
146
|
+
), n), l = () => {
|
|
147
|
+
s = t.dataset.latex ?? s, t.classList.add("is-editing"), r.hidden = !0, i.hidden = !1;
|
|
148
|
+
const a = f();
|
|
149
|
+
o && (a.setValue(s, { silenceNotifications: !0 }), a.focus());
|
|
150
|
+
}, h = () => {
|
|
151
|
+
t.classList.remove("is-editing"), i.hidden = !0, r.hidden = !1;
|
|
152
|
+
}, u = () => {
|
|
153
|
+
if (!t.classList.contains("is-editing")) return;
|
|
154
|
+
const a = n && o ? y(n) : s;
|
|
155
|
+
s = a, t.dataset.latex = a, h(), w(t, { id: m(t, e.entity.id), patch: { latex: a } }, !0);
|
|
156
|
+
}, x = () => {
|
|
157
|
+
n && o && n.setValue(s, { silenceNotifications: !0 }), h();
|
|
158
|
+
}, v = (a) => {
|
|
159
|
+
u(), t.dispatchEvent(
|
|
160
|
+
new CustomEvent(L, {
|
|
161
|
+
bubbles: !0,
|
|
162
|
+
detail: { id: m(t, e.entity.id), side: a }
|
|
163
|
+
})
|
|
164
|
+
);
|
|
165
|
+
};
|
|
166
|
+
return t.addEventListener("dblclick", (a) => {
|
|
167
|
+
a.preventDefault(), l();
|
|
168
|
+
}), t.addEventListener("mini-formula-edit", () => l()), (e.entity.latex ?? "").trim() || l(), t;
|
|
169
|
+
},
|
|
170
|
+
update(e, t) {
|
|
171
|
+
if (e.dataset.latex = t.entity.latex ?? "", e.classList.contains("is-editing")) return;
|
|
172
|
+
const r = e.querySelector(".mini-formula-view");
|
|
173
|
+
r && p(r, t.entity.latex ?? "", t.entity.displayMode !== !1);
|
|
174
|
+
},
|
|
175
|
+
paintCanvasBackdrop(e, t) {
|
|
176
|
+
e.fillStyle = "#f7f8fa", e.fillRect(t.x, t.y, t.w, t.h);
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
export {
|
|
180
|
+
P as createFormulaMathfield,
|
|
181
|
+
K as formulaPlugin,
|
|
182
|
+
y as mathfieldLatex,
|
|
183
|
+
N as measureFormula,
|
|
184
|
+
p as renderKatex
|
|
185
|
+
};
|
|
186
|
+
//# sourceMappingURL=formula.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formula.js","sources":["../src/embed/formula-katex.ts","../src/embed/formula-mathlive.ts","../src/embed/formula.ts"],"sourcesContent":["import katex from 'katex';\nimport 'katex/dist/katex.min.css';\n\n/** MathLive 输出转成 KaTeX 能吃的宏。 */\nconst KATEX_COMPAT_MACROS = {\n '\\\\wideparen': '\\\\overgroup{#1}',\n '\\\\overarc': '\\\\overgroup{#1}',\n '\\\\overparen': '\\\\overgroup{#1}',\n '\\\\exponentialE': '\\\\mathrm{e}',\n '\\\\imaginaryI': '\\\\mathrm{i}',\n '\\\\imaginaryJ': '\\\\mathrm{j}',\n '\\\\differentialD': '\\\\mathrm{d}',\n '\\\\capitalDifferentialD': '\\\\mathrm{D}'\n} as const;\n\ntype PipeCommand = { name: string; build: (left: string, right: string) => string };\n\nconst PIPE_COMMANDS: PipeCommand[] = [\n { name: 'frac', build: (left, right) => `\\\\frac{${left}}{${right}}` },\n { name: 'sqrt', build: (left, right) => `\\\\sqrt[${right}]{${left}}` },\n { name: 'int', build: (left, right) => `\\\\int_{${right}}^{${left}}` },\n { name: 'overbrace', build: (left, right) => `\\\\overbrace{${left}}^{${right}}` }\n];\n\nexport function normalizeMathLiveLatexForKatex(latex: string): string {\n let normalized = latex;\n for (const command of PIPE_COMMANDS) normalized = rewritePipeCommand(normalized, command);\n normalized = rewriteSingleArgumentCommand(normalized, 'index', (value) => `_{${value}}`);\n return normalized.replace(\n /\\\\(Uparrow|Downarrow|Updownarrow|uparrow|downarrow|updownarrow|Rightarrow|Leftarrow|Leftrightarrow)(?=[A-Za-z])/g,\n '\\\\$1 '\n );\n}\n\nfunction rewritePipeCommand(input: string, command: PipeCommand): string {\n return rewriteSingleArgumentCommand(input, command.name, (argument) => {\n const parts = splitTopLevelPipe(argument);\n return parts === undefined ? `\\\\${command.name}{${argument}}` : command.build(parts[0], parts[1]);\n });\n}\n\nfunction rewriteSingleArgumentCommand(input: string, command: string, build: (argument: string) => string): string {\n const marker = `\\\\${command}{`;\n let result = '';\n let cursor = 0;\n while (cursor < input.length) {\n const start = input.indexOf(marker, cursor);\n if (start < 0) return result + input.slice(cursor);\n const open = start + marker.length - 1;\n const close = matchingBrace(input, open);\n if (close < 0) return result + input.slice(cursor);\n result += input.slice(cursor, start) + build(input.slice(open + 1, close));\n cursor = close + 1;\n }\n return result;\n}\n\nfunction matchingBrace(input: string, open: number): number {\n let depth = 0;\n for (let index = open; index < input.length; index += 1) {\n if (input[index] === '{' && !isEscaped(input, index)) depth += 1;\n if (input[index] === '}' && !isEscaped(input, index)) {\n depth -= 1;\n if (depth === 0) return index;\n }\n }\n return -1;\n}\n\nfunction splitTopLevelPipe(value: string): readonly [string, string] | undefined {\n let depth = 0;\n let split = -1;\n for (let index = 0; index < value.length; index += 1) {\n if (value[index] === '{' && !isEscaped(value, index)) depth += 1;\n else if (value[index] === '}' && !isEscaped(value, index)) depth -= 1;\n else if (value[index] === '|' && depth === 0 && !isEscaped(value, index)) {\n if (split >= 0) return undefined;\n split = index;\n }\n }\n return split < 0 ? undefined : [value.slice(0, split), value.slice(split + 1)];\n}\n\nfunction isEscaped(value: string, index: number): boolean {\n let slashes = 0;\n for (let cursor = index - 1; cursor >= 0 && value[cursor] === '\\\\'; cursor -= 1) slashes += 1;\n return slashes % 2 === 1;\n}\n\nexport function renderKatex(host: HTMLElement, latex: string, displayMode: boolean) {\n host.replaceChildren();\n const src = latex.trim() || '\\\\square';\n try {\n katex.render(normalizeMathLiveLatexForKatex(src), host, {\n throwOnError: false,\n displayMode,\n output: 'html',\n macros: KATEX_COMPAT_MACROS\n });\n } catch {\n host.textContent = src;\n }\n}\n","import { MathfieldElement } from 'mathlive';\nimport 'mathlive/fonts.css';\n\n/** 不走 CDN 字体目录,用打包进来的 fonts.css。 */\nMathfieldElement.fontsDirectory = null;\n\nexport type FormulaMathfield = MathfieldElement;\n\nexport function createFormulaMathfield(): FormulaMathfield {\n const field = new MathfieldElement();\n field.className = 'mini-formula-math';\n field.mathVirtualKeyboardPolicy = 'auto';\n return field;\n}\n\nexport function mathfieldLatex(field: FormulaMathfield): string {\n return field.getValue('latex');\n}\n","import { EmbedEntity } from '../model/types';\nimport { renderKatex } from './formula-katex';\nimport { createFormulaMathfield, FormulaMathfield, mathfieldLatex } from './formula-mathlive';\nimport { EMBED_EXIT_EDIT, EmbedBox, EmbedPlugin, emitEmbedChange } from './types';\n\nexport function measureFormula(entity: EmbedEntity, contentWidth: number): { width: number; height: number } {\n const width = Math.max(48, entity.width || Math.min(400, contentWidth));\n if (typeof document === 'undefined') {\n return { width, height: entity.displayMode === false ? 32 : 56 };\n }\n const probe = document.createElement('div');\n probe.style.cssText = 'position:absolute;left:-9999px;top:0;visibility:hidden;';\n document.body.appendChild(probe);\n renderKatex(probe, entity.latex ?? '', entity.displayMode !== false);\n const rect = probe.getBoundingClientRect();\n probe.remove();\n return {\n width: Math.min(contentWidth, Math.max(48, Math.ceil(rect.width) + 24)),\n height: Math.max(36, Math.ceil(rect.height) + 16)\n };\n}\n\nfunction formulaId(root: HTMLElement, fallback: string): string {\n return root.dataset.entity || fallback;\n}\n\nexport const formulaPlugin: EmbedPlugin = {\n kind: 'formula',\n keepAspectRatio: false,\n interactive: true,\n overlayOnCanvas: true,\n measure: measureFormula,\n create(box) {\n const root = document.createElement('div');\n root.className = 'mini-embed mini-formula mini-embed-interactive';\n const view = document.createElement('div');\n view.className = 'mini-formula-view';\n const host = document.createElement('div');\n host.className = 'mini-formula-host';\n host.hidden = true;\n renderKatex(view, box.entity.latex ?? '', box.entity.displayMode !== false);\n root.append(view, host);\n\n let field: FormulaMathfield | null = null;\n let mounted = false;\n let baseline = box.entity.latex ?? '';\n\n const ensureField = (): FormulaMathfield => {\n if (field) return field;\n field = createFormulaMathfield();\n host.appendChild(field);\n field.addEventListener('mount', () => {\n mounted = true;\n field?.setValue(baseline, { silenceNotifications: true });\n field?.focus();\n }, { once: true });\n field.addEventListener('input', () => {\n if (!field) return;\n emitEmbedChange(root, { id: formulaId(root, box.entity.id), patch: { latex: mathfieldLatex(field) } });\n });\n field.addEventListener('focusout', () => {\n window.setTimeout(() => {\n if (host.contains(document.activeElement)) return;\n commit();\n }, 0);\n });\n field.addEventListener(\n 'keydown',\n (e) => {\n const mf = field;\n if (!mf) return;\n if (e.key === 'ArrowLeft' && mf.selectionIsCollapsed && mf.position === 0) {\n e.preventDefault();\n e.stopPropagation();\n exitEdit('before');\n return;\n }\n if (\n e.key === 'ArrowRight' &&\n mf.selectionIsCollapsed &&\n mf.position === mf.lastOffset\n ) {\n e.preventDefault();\n e.stopPropagation();\n exitEdit('after');\n return;\n }\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n commit();\n }\n if (e.key === 'Escape') {\n e.preventDefault();\n cancel();\n }\n },\n true\n );\n return field;\n };\n\n const startEdit = () => {\n baseline = root.dataset.latex ?? baseline;\n root.classList.add('is-editing');\n view.hidden = true;\n host.hidden = false;\n const mf = ensureField();\n if (mounted) {\n mf.setValue(baseline, { silenceNotifications: true });\n mf.focus();\n }\n };\n\n const leaveEdit = () => {\n root.classList.remove('is-editing');\n host.hidden = true;\n view.hidden = false;\n };\n\n const commit = () => {\n if (!root.classList.contains('is-editing')) return;\n const latex = field && mounted ? mathfieldLatex(field) : baseline;\n baseline = latex;\n root.dataset.latex = latex;\n leaveEdit();\n emitEmbedChange(root, { id: formulaId(root, box.entity.id), patch: { latex } }, true);\n };\n\n const cancel = () => {\n if (field && mounted) field.setValue(baseline, { silenceNotifications: true });\n leaveEdit();\n };\n\n const exitEdit = (side: 'before' | 'after') => {\n commit();\n root.dispatchEvent(\n new CustomEvent(EMBED_EXIT_EDIT, {\n bubbles: true,\n detail: { id: formulaId(root, box.entity.id), side }\n })\n );\n };\n\n root.addEventListener('dblclick', (e) => {\n e.preventDefault();\n startEdit();\n });\n root.addEventListener('mini-formula-edit', () => startEdit());\n\n if (!(box.entity.latex ?? '').trim()) startEdit();\n return root;\n },\n update(el, box) {\n el.dataset.latex = box.entity.latex ?? '';\n if (el.classList.contains('is-editing')) return;\n const view = el.querySelector<HTMLElement>('.mini-formula-view');\n if (view) renderKatex(view, box.entity.latex ?? '', box.entity.displayMode !== false);\n },\n paintCanvasBackdrop(ctx, box) {\n ctx.fillStyle = '#f7f8fa';\n ctx.fillRect(box.x, box.y, box.w, box.h);\n }\n};\n"],"names":["KATEX_COMPAT_MACROS","PIPE_COMMANDS","left","right","normalizeMathLiveLatexForKatex","latex","normalized","command","rewritePipeCommand","rewriteSingleArgumentCommand","value","input","argument","parts","splitTopLevelPipe","build","marker","result","cursor","start","open","close","matchingBrace","depth","index","isEscaped","split","slashes","renderKatex","host","displayMode","src","katex","MathfieldElement","createFormulaMathfield","field","mathfieldLatex","measureFormula","entity","contentWidth","width","probe","rect","formulaId","root","fallback","formulaPlugin","box","view","mounted","baseline","ensureField","emitEmbedChange","commit","e","mf","exitEdit","cancel","startEdit","leaveEdit","side","EMBED_EXIT_EDIT","el","ctx"],"mappings":"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"}
|