@one2x/medeo-rte 0.4.6 → 0.4.8
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 +263 -174
- package/dist/index.js +2615 -1902
- package/dist/index.js.map +1 -1
- package/dist/style.css +1 -0
- package/dist/types/editor.d.ts +113 -5
- package/dist/types/editor.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/layout/engine.d.ts.map +1 -1
- package/dist/types/layout/measure.d.ts +3 -0
- package/dist/types/layout/measure.d.ts.map +1 -1
- package/dist/types/layout/style.d.ts +3 -1
- package/dist/types/layout/style.d.ts.map +1 -1
- package/dist/types/layout/types.d.ts +9 -8
- package/dist/types/layout/types.d.ts.map +1 -1
- package/dist/types/layout/wrap.d.ts +26 -1
- package/dist/types/layout/wrap.d.ts.map +1 -1
- package/dist/types/model/types.d.ts +42 -4
- package/dist/types/model/types.d.ts.map +1 -1
- package/dist/types/model/virtualBlock.d.ts +26 -0
- package/dist/types/model/virtualBlock.d.ts.map +1 -1
- package/dist/types/paint/canvasPainter.d.ts +4 -1
- package/dist/types/paint/canvasPainter.d.ts.map +1 -1
- package/dist/types/paint/htmlPainter.d.ts +5 -2
- package/dist/types/paint/htmlPainter.d.ts.map +1 -1
- package/dist/types/paint/markBox.d.ts +90 -0
- package/dist/types/paint/markBox.d.ts.map +1 -0
- package/dist/types/paint/types.d.ts +3 -1
- package/dist/types/paint/types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,22 +1,17 @@
|
|
|
1
1
|
# medeo-rte
|
|
2
2
|
|
|
3
|
-
自绘的文档渲染引擎。不使用 `contenteditable
|
|
4
|
-
|
|
5
|
-
同一份排版结果可以画到 DOM,也可以一键切到 Canvas,光标和选区不变。
|
|
3
|
+
自绘的文档渲染引擎。不使用 `contenteditable`。文档是一条字符流,排版自己算出每个字的坐标,绘制后端可在 HTML 和 Canvas 之间切换,光标和选区不变。
|
|
6
4
|
|
|
7
5
|
在线 demo:<https://editor-html-demo.pages.dev/>
|
|
8
6
|
|
|
9
|
-
## 仓库结构
|
|
10
|
-
|
|
11
7
|
```
|
|
12
8
|
src/ SDK。发布出去的就是这部分
|
|
13
|
-
demo/
|
|
9
|
+
demo/ 演示应用,也是用法样例,不参与发布
|
|
14
10
|
```
|
|
15
11
|
|
|
16
|
-
SDK
|
|
17
|
-
产品接入时应自己实现——照着 `demo/main.ts` 抄即可。模板混排样例在 `demo/vblocks.ts`。
|
|
12
|
+
SDK 只负责编辑区。工具栏、状态栏、小地图由宿主自己做,可参考 `demo/main.ts`。
|
|
18
13
|
|
|
19
|
-
##
|
|
14
|
+
## 安装
|
|
20
15
|
|
|
21
16
|
```bash
|
|
22
17
|
npm i @one2x/medeo-rte
|
|
@@ -27,19 +22,14 @@ import { Editor } from '@one2x/medeo-rte';
|
|
|
27
22
|
import '@one2x/medeo-rte/style.css';
|
|
28
23
|
|
|
29
24
|
const editor = new Editor(document.getElementById('app')!);
|
|
30
|
-
|
|
31
|
-
// 下命令
|
|
32
25
|
editor.commands.toggleBold();
|
|
33
|
-
|
|
34
|
-
// 同步自己的工具栏
|
|
35
26
|
editor.on('statechange', () => renderToolbar(editor.getState()));
|
|
36
27
|
|
|
37
|
-
// 存取内容(Slice 可 JSON 序列化)
|
|
38
28
|
const json = editor.getContent();
|
|
39
29
|
editor.setContent(json);
|
|
40
30
|
```
|
|
41
31
|
|
|
42
|
-
|
|
32
|
+
公式依赖 KaTeX 和 MathLive,需单独引入,否则不会打进产物:
|
|
43
33
|
|
|
44
34
|
```ts
|
|
45
35
|
import { builtinEmbedPlugins, Editor } from '@one2x/medeo-rte';
|
|
@@ -48,231 +38,330 @@ import { formulaPlugin } from '@one2x/medeo-rte/formula';
|
|
|
48
38
|
new Editor(el, { plugins: [...builtinEmbedPlugins, formulaPlugin] });
|
|
49
39
|
```
|
|
50
40
|
|
|
51
|
-
|
|
41
|
+
## 能力
|
|
42
|
+
|
|
43
|
+
- 文字与段落:加粗、斜体、下划线、删除线、字号、颜色、标题、对齐、列表、缩进、链接
|
|
44
|
+
- 整篇默认字号、字体、字重、行高(`textDefaults`)
|
|
45
|
+
- 表格、图片、视频、代码块、公式、图文绕排
|
|
46
|
+
- Markdown 边打边转、撤销重做、中文输入法
|
|
47
|
+
- 两种芯片:`internalSlot`(SDK 内置)、`hostSlot`(宿主自绘)。整颗换行
|
|
48
|
+
- `mark`:盖在正文上的样式区间,按字折行。底色、边框、圆角、内外边距、悬停、空态灰字、生成中状态
|
|
49
|
+
- 同步块:选区提成可复用内容,改一处全变
|
|
50
|
+
- 绘制后端:`html` / `canvas`
|
|
51
|
+
|
|
52
|
+
| | `internalSlot` | `hostSlot` | `mark` |
|
|
53
|
+
| --- | --- | --- | --- |
|
|
54
|
+
| 是什么 | SDK 内置芯片 | 宿主自绘芯片 | 样式叠层,字仍是正文 |
|
|
55
|
+
| 落在哪 | 流上一个 `*` | 同上 | 不占字符,区间 `[start, end)` |
|
|
56
|
+
| 折行 | 整颗盒子 | 整颗盒子 | 按字折行 |
|
|
57
|
+
| 插入 | `commands.insertSlot` | `insertHostEmbed` / `insertSlice` | `insertVirtualBlock` / `insertSlice` |
|
|
58
|
+
|
|
59
|
+
更细的 mark 模型见 [docs/virtual-blocks.md](./docs/virtual-blocks.md)。
|
|
60
|
+
|
|
61
|
+
## 构造参数
|
|
62
|
+
|
|
63
|
+
`new Editor(el, options?)`。
|
|
52
64
|
|
|
53
|
-
|
|
65
|
+
| 字段 | 默认 | 含义 |
|
|
66
|
+
| --- | --- | --- |
|
|
67
|
+
| `doc` | 空文档 | 初始 `DocumentModel` |
|
|
68
|
+
| `paint` | `'html'` | 绘制后端,`'html'` 或 `'canvas'` |
|
|
69
|
+
| `plugins` | `builtinEmbedPlugins` | 嵌入插件。公式要另外追加 `formulaPlugin` |
|
|
70
|
+
| `width` | `'fill'` | `'fill'` 撑满容器并随容器重排;数字是固定内容宽(像素) |
|
|
71
|
+
| `padding` | `{ x: 72, y: 56 }` | 版心内边距。紧凑输入框可传 `{ x: 0, y: 0 }` |
|
|
72
|
+
| `hostEmbed` | 无 | 宿主渲染器。提供后才能插入 `hostSlot` |
|
|
73
|
+
| `placeholder` | 无 | 空文档占位文案。组合输入一开始就隐藏 |
|
|
74
|
+
| `textDefaults` | 16px / `#1f2329` | 没写文字样式时的默认属性,见下表 |
|
|
75
|
+
| `shortcuts` | `SHORTCUTS` | 快捷键表。输入框用 `inputShortcuts()` |
|
|
76
|
+
| `autofocus` | `true` | 创建后是否聚焦。嵌在页面里应传 `false` |
|
|
77
|
+
| `chrome` | 实心光标、扁平选区 | 编辑器装饰,见「全局 chrome」。不是 mark 的皮 |
|
|
78
|
+
| `perfPanel` | `false` | 左下角性能面板。也可用 `setPerfPanel` |
|
|
79
|
+
|
|
80
|
+
### textDefaults
|
|
81
|
+
|
|
82
|
+
写在文档上,主题切换用 `setTextDefaults`。`fontFamily` 不要用 `system-ui`。`lineHeight` 只在这里生效,写在单个文字 span 上会被忽略。
|
|
83
|
+
|
|
84
|
+
| 字段 | 含义 |
|
|
85
|
+
| --- | --- |
|
|
86
|
+
| `fontSize` | 字号,像素。默认 16 |
|
|
87
|
+
| `fontFamily` | 字体栈。会写成 `--mini-font-family`,测宽和绘制用同一套 |
|
|
88
|
+
| `fontWeight` | 字重。默认 400。`bold: true` 时绘制仍用 700 |
|
|
89
|
+
| `color` | 默认字色 `#1f2329` |
|
|
90
|
+
| `bold` / `italic` | 默认是否加粗、斜体 |
|
|
91
|
+
| `lineHeight` | 数字是字号倍数;`'24px'` 是固定像素。不传按 `round(字号 × 1.6)` |
|
|
92
|
+
| `lineGap` | 行与行之间的额外间距,像素。留在行盒外面,下一行整行下移。不传是 0。用来让折行后的边框不再贴在一起 |
|
|
54
93
|
|
|
55
|
-
|
|
94
|
+
## CSS 变量
|
|
56
95
|
|
|
57
|
-
|
|
96
|
+
都写在 `.mini-editor` 上,不要写到 `:root`。
|
|
58
97
|
|
|
59
|
-
|
|
98
|
+
全局 chrome 和 `textDefaults` 会改其中一部分。光标高度、选区高度只有 CSS,没有 chrome 字段:宿主自己设在 `.mini-editor` 上。
|
|
60
99
|
|
|
61
|
-
| |
|
|
100
|
+
| 变量 | 谁来写 | 含义 |
|
|
62
101
|
| --- | --- | --- |
|
|
63
|
-
|
|
|
64
|
-
|
|
|
65
|
-
|
|
|
66
|
-
|
|
|
67
|
-
|
|
|
68
|
-
|
|
|
102
|
+
| `--mini-caret-color` | `chrome.caretColor` | 实心光标颜色。默认 `#09090b` |
|
|
103
|
+
| `--mini-caret-width` | `chrome.caretWidth` | 实心光标宽度。默认 `2px` |
|
|
104
|
+
| `--mini-caret-height` | 只能 CSS | 实心光标高度,像素。不设则铺满行高,设了就在行内居中收短。彩虹光标不用 |
|
|
105
|
+
| `--mini-sel-color` | `chrome.selectionColor` | 选区高亮色。默认 `rgba(43, 108, 255, 0.22)` |
|
|
106
|
+
| `--mini-sel-radius` | `chrome.selectionRadius` | 选区圆角。默认 `0px` |
|
|
107
|
+
| `--mini-sel-height` | 只能 CSS | 选区高亮和选区焦点标记的高度,像素。不设则铺满行高,设了就居中收短。焦点圆点贴着收短后的顶 |
|
|
108
|
+
| `--mini-sel-focus-color` | SDK 固定写入 | 选区焦点标记颜色,固定 `#2b6cff`。chrome 不能改 |
|
|
109
|
+
| `--mini-ime-underline-color` | `chrome.composeUnderlineColor` | 中文输入中间态下划线颜色。默认 `#1f2329` |
|
|
110
|
+
| `--mini-ime-underline-width` | `chrome.composeUnderlineWidth` | 下划线粗细。默认 `2px` |
|
|
111
|
+
| `--mini-font-family` | `textDefaults.fontFamily` | 编辑器默认字体栈 |
|
|
112
|
+
| `--mini-font-weight` | `textDefaults.fontWeight` | 编辑器默认字重 |
|
|
113
|
+
| `--mini-content-width` | SDK 按排版写入 | 内容宽。宿主不要自己设 |
|
|
69
114
|
|
|
70
|
-
|
|
115
|
+
另外:`data-caret="solid"|"rainbow"` 由 `chrome.caret` 写入;`data-surface="paper"|"transparent"` 由 `chrome.surfaceBackground` 写入。
|
|
71
116
|
|
|
72
|
-
|
|
117
|
+
画布和 `.mini-page-wrap` 左右各多留 5px(负 margin 抵消,文字位置不变),给贴边的 mark 内边距用。覆盖 `.mini-page-wrap` 的 `padding` 时保留 `padding-inline: 5px; margin-inline: -5px;`,否则文字会左移 5px,贴边的内边距会被裁。
|
|
73
118
|
|
|
74
|
-
|
|
119
|
+
## 全局 chrome
|
|
75
120
|
|
|
76
|
-
|
|
121
|
+
`EditorChrome`,构造时的 `chrome` 或之后的 `setChrome(patch)`。`setChrome` 只覆盖传入的字段。
|
|
77
122
|
|
|
78
|
-
|
|
123
|
+
这是编辑器装饰:光标、选区、输入法下划线、空文档占位、纸张底。和 mark 的 `chrome` 不是一回事。mark 的皮不能通过这里设,也不能通过上面的 CSS 变量设。
|
|
79
124
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
125
|
+
| 字段 | 默认 | 含义 |
|
|
126
|
+
| --- | --- | --- |
|
|
127
|
+
| `caret` | `'solid'` | `'solid'` 实心光标;`'rainbow'` 彩虹光标 |
|
|
128
|
+
| `caretColor` | `#09090b` | 实心光标颜色 |
|
|
129
|
+
| `caretWidth` | `2` | 实心光标宽度,像素 |
|
|
130
|
+
| `caretTrail` | `false` | 是否画光标残影 |
|
|
131
|
+
| `selectionColor` | `rgba(43, 108, 255, 0.22)` | 选区高亮色。彩虹光标且未指定时,demo 用渐变选区 |
|
|
132
|
+
| `selectionRadius` | `0` | 选区圆角,像素 |
|
|
133
|
+
| `composeUnderlineColor` | `#1f2329` | 中文输入中间态下划线颜色 |
|
|
134
|
+
| `composeUnderlineWidth` | `2` | 下划线粗细,像素 |
|
|
135
|
+
| `placeholderColor` | | 空文档占位颜色 |
|
|
136
|
+
| `placeholderOpacity` | | 空文档占位透明度,0–1 |
|
|
137
|
+
| `surfaceBackground` | `'paper'` | `'paper'` 纸张白;`'transparent'` 透出宿主背景 |
|
|
86
138
|
|
|
87
|
-
|
|
139
|
+
光标高度、选区高度不在这张表里,用 `--mini-caret-height`、`--mini-sel-height`。
|
|
88
140
|
|
|
89
|
-
|
|
141
|
+
## mark 的 chrome
|
|
90
142
|
|
|
91
|
-
|
|
92
|
-
const id = editor.insertVirtualBlock({
|
|
93
|
-
kind: 'mark',
|
|
94
|
-
placeholder: 'Describe a scene',
|
|
95
|
-
chrome: { background: '#fff3bf', placeholderColor: '#c5ccd6' }
|
|
96
|
-
});
|
|
97
|
-
// 光标在这块里(含空块、内侧边界)时:
|
|
98
|
-
editor.on('markenter', ({ id }) => {});
|
|
99
|
-
editor.on('markleave', ({ id }) => {});
|
|
100
|
-
// 点击这块。锁定中不发,但光标仍会落下。
|
|
101
|
-
editor.on('markclick', ({ id }) => openMenu(id));
|
|
102
|
-
```
|
|
143
|
+
写在每个 mark 的 `chrome` 上。`updateVirtualBlock(id, chrome)` 按字段合并,没传的字段保持原值。
|
|
103
144
|
|
|
104
|
-
|
|
145
|
+
静态底色和静态边框每一层都画。悬停时,只有指针下最上面、并且配了悬停底色或悬停边框的那一块换皮,它的每一行一起换。压在下面的 mark 继续画静态底色和静态边框,包括露在外面的其它行。灰字颜色仍是指针下每一层一起换。指针落在 mark 里的 `internalSlot` 或 `hostSlot` 上时,mark 不换悬停皮,只留芯片自己的悬停。
|
|
105
146
|
|
|
106
|
-
|
|
107
|
-
editor.setMarkStatus(id, { label: 'Generating…', locked: true, text: 'rainbow', border: 'pulse' });
|
|
108
|
-
editor.setMarkStatus(id, null);
|
|
109
|
-
editor.replaceMarkText(id, 'a red bicycle');
|
|
110
|
-
```
|
|
147
|
+
折行后的边:上下边每段都画;左边只在首段,右边只在末段。圆角是首段左侧两角、末段右侧两角;只有一段时四角都圆。
|
|
111
148
|
|
|
112
|
-
|
|
149
|
+
| 字段 | 默认 | 含义 |
|
|
150
|
+
| --- | --- | --- |
|
|
151
|
+
| `background` | 无 | 静态底色 |
|
|
152
|
+
| `hoverBackground` | 无 | 悬停底色。只换指针下最上面那一块,这一块的每一行一起换。不进撤销 |
|
|
153
|
+
| `border` | 无 | 静态边,`{ color, width? }`。`width` 不传是 1。不传就没有边 |
|
|
154
|
+
| `hoverBorder` | 沿用 `border` | 悬停边,形状同 `border`。只换指针下最上面那一块,这一块的每一行一起换。不进撤销 |
|
|
155
|
+
| `selectedBackground` | 无 | 方向键整段选中这块时的底色。光标只是停在里面不换。没传就仍用 `background`。不进撤销 |
|
|
156
|
+
| `selectedBorder` | 沿用 `border` | 同上时的边。指针正悬停在这块上时,悬停皮盖过选中皮。不进撤销 |
|
|
157
|
+
| `radius` | `3` | 圆角,像素 |
|
|
158
|
+
| `paddingX` | `4` | 水平内边距,留在底色里面。每一行文字都缩进;区间真正的首尾这段空白还占排版宽度。传 `0` 表示不留 |
|
|
159
|
+
| `paddingY` | `0` | 上下内边距,留在这块自己的底色里。盖住的行按同一行里最大的值变高,文字居中。没配的内层仍贴着文字。不画进相邻行 |
|
|
160
|
+
| `marginX` | `0` | 底色边缘到外面文字的间距,前后同一段。只加在区间真正的首尾 |
|
|
161
|
+
| `color` | | 文字颜色。这个字自己有颜色时用正文,否则用这里 |
|
|
162
|
+
| `fontSize` | | 字号。传了就盖过正文,没传才继承。占位灰字和生成中的文案同样 |
|
|
163
|
+
| `fontFamily` | | 字体栈。优先级同 `fontSize` |
|
|
164
|
+
| `fontWeight` | | 字重。优先级同 `fontSize` |
|
|
165
|
+
| `bold` | | 加粗。优先级同 `fontSize` |
|
|
166
|
+
| `italic` | | 斜体。优先级同 `fontSize` |
|
|
167
|
+
| `placeholderColor` | `#c5ccd6` | 空态灰字颜色。透明度写在颜色里 |
|
|
168
|
+
| `hoverPlaceholderColor` | 无 | 悬停时灰字颜色。不进撤销。生成中的文案不换 |
|
|
169
|
+
| `selectable` | `true` | 光标能否进入。`false` 时左右、上下和点击都不会把光标留在里面:mark 整段跳过,芯片不选中、停在旁边 |
|
|
113
170
|
|
|
114
|
-
|
|
171
|
+
多层重叠时,字体字段是内层盖过外层:内层写了的用内层,没写的用外层。
|
|
115
172
|
|
|
116
|
-
|
|
173
|
+
### mark 怎么用
|
|
117
174
|
|
|
118
|
-
|
|
175
|
+
虚拟块目前只有 `mark`。`parentId` 恒为 `null`,可以重叠。用户打字不会新建 mark。日常编辑不用自己维护 `[start, end)`,插删字时 SDK 会挪区间。
|
|
119
176
|
|
|
120
|
-
|
|
177
|
+
从外侧朝块内按方向键,只有配了 `selectedBackground` 或 `selectedBorder` 的 mark 会先整段选中,焦点留在来的这一侧;再朝内按一次才进入。没配选中皮的 mark 跳过这一步,方向键直接走进或走出。从内侧走到边界再往外,同样只对配了选中皮的块先整段选中。拖选或 Shift 不是这种选中态。上下键不走这套。重叠时,从外往里选范围最大、且配了选中皮的那一块,从里往外选范围最小、且配了选中皮的那一块。这种选中态不画选区高亮和焦点竖线,只换 mark 自己的选中皮。拖选和 Shift 仍画选区。光标只是停在 mark 里面时不换选中皮。
|
|
121
178
|
|
|
122
|
-
|
|
123
|
-
editor.insertSlice({
|
|
124
|
-
text: 'Write a prompt for a red bicycle.\n',
|
|
125
|
-
styles: [],
|
|
126
|
-
entities: [],
|
|
127
|
-
virtualBlocks: [
|
|
128
|
-
{
|
|
129
|
-
id: 'phrase',
|
|
130
|
-
parentId: null,
|
|
131
|
-
kind: 'mark',
|
|
132
|
-
start: 19,
|
|
133
|
-
end: 33,
|
|
134
|
-
placeholder: 'a red bicycle',
|
|
135
|
-
chrome: { background: '#fff3bf', placeholderColor: '#c5ccd6' }
|
|
136
|
-
}
|
|
137
|
-
]
|
|
138
|
-
});
|
|
139
|
-
```
|
|
179
|
+
光标落在块内的芯片上时,芯片用自己实体上的 `chrome` 换选中皮。选中变化发 `markselect` 或 `slotselect`。
|
|
140
180
|
|
|
141
|
-
|
|
181
|
+
边界分内外侧。内侧输入算在块里,外侧输入留在块外。选区正好盖住一块 mark 时,焦点竖线和普通选区一样,停在焦点那一端的光标位置。
|
|
142
182
|
|
|
143
|
-
|
|
183
|
+
没有 `placeholder` 时,区间里的字删光,块就拆掉。有 `placeholder` 时,只删这块里的字会收成零宽灰字,灰字不进正文。配了选中皮的零宽 mark,方向键会先停在选中态,再进灰字;没配的直接进出。选区比这块更大再删,或光标已经在灰字上、或正停在这块选中态时再按退格、删除,块会被拆掉。
|
|
144
184
|
|
|
145
185
|
```ts
|
|
146
|
-
|
|
147
|
-
editor.insertVirtualBlock({
|
|
186
|
+
const id = editor.insertVirtualBlock({
|
|
148
187
|
kind: 'mark',
|
|
149
188
|
placeholder: 'Describe a scene',
|
|
150
|
-
chrome: {
|
|
189
|
+
chrome: {
|
|
190
|
+
background: '#fff3bf',
|
|
191
|
+
hoverBackground: '#ffe066',
|
|
192
|
+
border: { color: '#d4a012', width: 1 },
|
|
193
|
+
hoverBorder: { color: '#926000', width: 1 },
|
|
194
|
+
selectedBackground: 'rgba(43, 108, 255, 0.16)',
|
|
195
|
+
selectedBorder: { color: '#2b6cff', width: 1 },
|
|
196
|
+
radius: 4,
|
|
197
|
+
paddingX: 5,
|
|
198
|
+
paddingY: 2,
|
|
199
|
+
marginX: 8,
|
|
200
|
+
placeholderColor: '#c5ccd6'
|
|
201
|
+
}
|
|
151
202
|
});
|
|
152
|
-
|
|
153
|
-
// 也可显式下标
|
|
154
|
-
editor.insertVirtualBlock({ kind: 'mark', start: 0, end: 20, chrome: { background: '#fff3bf' } });
|
|
155
203
|
```
|
|
156
204
|
|
|
157
|
-
|
|
205
|
+
生成中用 `setMarkStatus`,不进文档和撤销。有 `label` 就是锁定:有字的 mark 整块换成 `label`,里面的字、芯片、内层 mark 都不显示,宽度按 `label` 算,光标进不去,不能改,点击也不发 `markclick`;`locked` 已忽略。结束后先清状态,再用 `replaceMarkText` 写回,这一步单独占一次撤销。
|
|
158
206
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
1. 听 `markclick`,参数是 `{ id }`。锁定中的 mark 不发这个事件
|
|
166
|
-
2. `editor.getVirtualBlockRect(id)` 拿屏幕坐标(clientX/Y,含滚动)定位浮层:`left/top/width/height` 是外接矩形,折行的块还有 `lines`;下拉放在 `left` / `top + height` 即可。滚动或重排后(`layoutchange`)重新取。不需要点击点坐标
|
|
167
|
-
3. 生成中:`setMarkStatus(id, { label, locked: true, text: 'rainbow', border: 'pulse' })`
|
|
168
|
-
4. 写回:`setMarkStatus(id, null)` 然后 `replaceMarkText(id, text)`,这一步单独占一次撤销
|
|
207
|
+
```ts
|
|
208
|
+
editor.setMarkStatus(id, { label: 'Generating…', locked: true, text: 'rainbow', border: 'pulse' });
|
|
209
|
+
editor.setMarkStatus(id, null);
|
|
210
|
+
editor.replaceMarkText(id, 'a red bicycle');
|
|
211
|
+
```
|
|
169
212
|
|
|
170
|
-
|
|
213
|
+
`text: 'rainbow'` 只作用于 `label`。`border: 'pulse'` 是底色边缘的呼吸描边。块被拆掉或 `setContent` 之后,状态自动丢掉。
|
|
171
214
|
|
|
172
|
-
|
|
215
|
+
整段模板用 `insertSlice`,`virtualBlocks` 里的下标相对这段 `text`。已有文字用 `insertVirtualBlock`,不传 `start` / `end` 就是当前选区。
|
|
173
216
|
|
|
174
|
-
|
|
175
|
-
const editor = new Editor(el, {
|
|
176
|
-
hostEmbed: {
|
|
177
|
-
mount(container, ctx) {
|
|
178
|
-
// 按 ctx.entity.hostType 画自己的组件;实体 kind 为 'hostSlot'
|
|
179
|
-
return { update(next) {}, destroy() {} };
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
});
|
|
183
|
-
editor.commands.insertHostEmbed('Upscale', { label: '@Upscale' }, { width: 88, height: 24 });
|
|
184
|
-
```
|
|
217
|
+
点击后的菜单位置用 `getVirtualBlockRect(id)`,返回屏幕坐标。
|
|
185
218
|
|
|
186
|
-
|
|
219
|
+
## 编辑器 API
|
|
187
220
|
|
|
188
|
-
|
|
221
|
+
下面是 `Editor` 上宿主会用到的全部公开成员。
|
|
189
222
|
|
|
190
223
|
| 成员 | 说明 |
|
|
191
224
|
| --- | --- |
|
|
192
|
-
| `
|
|
193
|
-
| `
|
|
194
|
-
| `
|
|
195
|
-
| `
|
|
196
|
-
| `
|
|
197
|
-
| `
|
|
198
|
-
| `
|
|
199
|
-
| `
|
|
200
|
-
| `
|
|
201
|
-
| `
|
|
202
|
-
| `
|
|
203
|
-
| `
|
|
204
|
-
| `
|
|
205
|
-
| `
|
|
206
|
-
| `
|
|
207
|
-
| `
|
|
208
|
-
| `
|
|
209
|
-
| `
|
|
210
|
-
| `
|
|
211
|
-
| `
|
|
212
|
-
|
|
213
|
-
|
|
225
|
+
| `root` | 根节点 `.mini-editor` |
|
|
226
|
+
| `scroller` | 滚动容器 |
|
|
227
|
+
| `pageElement` | 内容定位上下文 |
|
|
228
|
+
| `paint` | 当前绘制后端,`'html'` 或 `'canvas'` |
|
|
229
|
+
| `commands` | 工具栏命令,见下表 |
|
|
230
|
+
| `perf` | 性能探针。`start()` / `report()` / `stop()`,默认关闭 |
|
|
231
|
+
| `on(event, fn)` | 订阅。返回退订函数。事件见下表 |
|
|
232
|
+
| `getState()` | 工具栏用的状态快照 |
|
|
233
|
+
| `getContent()` / `setContent(slice)` | 整篇读写。`setContent` 清空撤销栈,并丢掉全部 `setMarkStatus` |
|
|
234
|
+
| `getDocument()` | 文档模型。改文档走 `operations` + `runOperation`,不要直接改内部表 |
|
|
235
|
+
| `getLayout()` | 只读排版几何 |
|
|
236
|
+
| `getSelection()` / `setSelection(sel)` | 选区读写 |
|
|
237
|
+
| `insertSlice(slice, at?)` | 插入切片。不传 `at` 用当前光标 |
|
|
238
|
+
| `insertVirtualBlock(spec)` | 套一层 mark。返回 id;建不成(没有范围、也没有 placeholder)时返回 `null` |
|
|
239
|
+
| `updateVirtualBlock(id, chrome)` | 按字段合并这块的 chrome,进撤销 |
|
|
240
|
+
| `removeVirtualBlock(id)` | 拆掉皮,字留下 |
|
|
241
|
+
| `replaceMarkText(id, text)` | 换掉 mark 里的字,块和 id 留下,整步一次撤销。返回块是否存在 |
|
|
242
|
+
| `setMarkStatus(id, status \| null)` | 生成中状态。不进文档、撤销和 `change` |
|
|
243
|
+
| `getVirtualBlockRect(id)` | 屏幕坐标。`left/top/width/height` 是外接矩形,`lines` 是每一行。没有这块时返回 `null` |
|
|
244
|
+
| `insertHostEmbed(hostType, data?, size?, at?, chrome?)` | 插入宿主芯片。`at` 为下标、`'start'`、`'end'` 或 `'cursor'`(默认)。`chrome` 与 mark 同一套 `VirtualBlockChrome`。SDK 在芯片外框上画底色、边框、圆角和字体;悬停、选中的优先级和 mark 一样。`paddingX` / `paddingY` / `marginX` 和占位灰字只参与 mark 的排版 |
|
|
245
|
+
| `listEmbeds()` | 文档里全部嵌入对象,按位置排序 |
|
|
246
|
+
| `setPaintMode('html' \| 'canvas')` | 切换绘制后端 |
|
|
247
|
+
| `setChrome(patch)` | 合并全局 chrome |
|
|
248
|
+
| `setTextDefaults(patch)` | 合并默认文字属性 |
|
|
249
|
+
| `setPlaceholder(text)` | 改空文档占位 |
|
|
250
|
+
| `setPerfPanel(on)` | 开关性能面板 |
|
|
251
|
+
| `setDisabled(disabled)` | 禁用输入和快捷键 |
|
|
252
|
+
| `focus()` | 聚焦 |
|
|
253
|
+
| `composing()` | 是否正在中文输入组合 |
|
|
254
|
+
| `destroy()` | 卸下编辑器 |
|
|
255
|
+
|
|
256
|
+
### commands
|
|
257
|
+
|
|
258
|
+
宿主工具栏调这些。方向键、删除、全选也在 `commands` 上,但通常留给键盘。
|
|
259
|
+
|
|
260
|
+
| 命令 | 作用 |
|
|
261
|
+
| --- | --- |
|
|
262
|
+
| `toggleBold` / `toggleItalic` / `toggleUnderline` / `toggleStrike` | 切换文字样式 |
|
|
263
|
+
| `setFontSize(size)` / `setColor(color)` | 字号、颜色 |
|
|
264
|
+
| `setHeading(0\|1\|2\|3)` | 标题。`0` 是正文 |
|
|
265
|
+
| `setAlign('left'\|'center'\|'right')` | 对齐 |
|
|
266
|
+
| `toggleList('ul'\|'ol')` | 列表 |
|
|
267
|
+
| `indent(delta)` | 缩进。`delta` 为 `1` 或 `-1` |
|
|
268
|
+
| `insertImage(file)` / `insertVideo(file)` | 插入本地文件 |
|
|
269
|
+
| `insertCode()` / `insertFormula()` | 代码块、公式 |
|
|
270
|
+
| `insertSlot('text'\|'image'\|'select')` | 内置芯片 |
|
|
271
|
+
| `insertHostEmbed(hostType, data?, size?)` | 宿主芯片。要指定落点用编辑器上的 `insertHostEmbed` |
|
|
272
|
+
| `insertTable` / `addRow` / `addCol` / `removeRow` / `removeCol` | 表格 |
|
|
273
|
+
| `toggleLink` | 链接 |
|
|
274
|
+
| `promoteRef` / `insertRef` / `unlinkRef` | 把选区收成同步块、再插入一处、解除 |
|
|
275
|
+
| `undo` / `redo` | 撤销、重做 |
|
|
276
|
+
|
|
277
|
+
默认快捷键(`SHORTCUTS`):Cmd/Ctrl+Z 撤销,Shift+Cmd/Ctrl+Z 或 Cmd/Ctrl+Y 重做,Cmd/Ctrl+B/I/U/K/A 为加粗、斜体、下划线、链接、全选。Enter 换段,Backspace / Delete 删除,Tab 在列表里改层级、在表格里跳格。左右是字符,Option 是词,Cmd 左右是行首行尾,Option 上下是段,Cmd 上下是文首文尾。Shift 扩选。
|
|
278
|
+
|
|
279
|
+
输入框用 `inputShortcuts()`:保留撤销、选区、删除和方向键,吞掉加粗和链接。默认 Enter 换行,Cmd/Ctrl+Enter 不登记,留给宿主提交。
|
|
280
|
+
|
|
281
|
+
### 事件
|
|
282
|
+
|
|
283
|
+
| 事件 | 参数 | 何时 |
|
|
284
|
+
| --- | --- | --- |
|
|
285
|
+
| `change` | 无 | 文档内容变化 |
|
|
286
|
+
| `selectionchange` | 无 | 选区或光标移动 |
|
|
287
|
+
| `statechange` | 无 | `getState()` 里的字段可能变了,用来同步工具栏 |
|
|
288
|
+
| `layoutchange` | 无 | 重新排版完成 |
|
|
289
|
+
| `markclick` | `{ id }` | 点击 mark。锁定中不发,光标仍会落下 |
|
|
290
|
+
| `markenter` | `{ id }` | 光标进入 mark,含空块和内侧边界 |
|
|
291
|
+
| `markleave` | `{ id }` | 光标离开 mark |
|
|
292
|
+
| `markselect` | `{ id }` | 光标所在最上层 mark 变了。`id` 为 `null` 表示当前没有 mark 选中。不进撤销 |
|
|
293
|
+
| `slotselect` | `{ id }` | 光标落到 `internalSlot` 或 `hostSlot` 上,或从芯片上离开。芯片比盖住它的 mark 更靠上,这时 mark 不换选中皮 |
|
|
294
|
+
|
|
295
|
+
### getState()
|
|
296
|
+
|
|
297
|
+
| 字段 | 含义 |
|
|
298
|
+
| --- | --- |
|
|
299
|
+
| `bold` / `italic` / `underline` / `strikethrough` | 光标处是否已有该样式 |
|
|
300
|
+
| `fontSize` / `color` | 光标处字号、颜色 |
|
|
301
|
+
| `heading` | `0`–`3` |
|
|
302
|
+
| `align` | `'left'` / `'center'` / `'right'` |
|
|
303
|
+
| `list` | `'none'` / `'ul'` / `'ol'` |
|
|
304
|
+
| `inTable` | 光标是否在表格里 |
|
|
305
|
+
| `canUndo` / `canRedo` | 能否撤销、重做 |
|
|
306
|
+
| `paintMode` | `'html'` 或 `'canvas'` |
|
|
307
|
+
| `hasLink` | 光标是否在链接里 |
|
|
308
|
+
| `canPromote` / `canInsertRef` / `canUnlink` | 同步块三个按钮是否可用 |
|
|
309
|
+
| `selection` | 主文档选区 |
|
|
310
|
+
| `docLength` | 文档长度(spacer 数) |
|
|
311
|
+
| `inRefEdit` | 是否正在编辑同步块内部 |
|
|
312
|
+
| `refSelection` | 同步块内部选区。只在 `inRefEdit` 时有意义 |
|
|
313
|
+
|
|
314
|
+
脚本化改文档用 `import { operations, runOperation } from '@one2x/medeo-rte'`。
|
|
214
315
|
|
|
215
316
|
## 性能面板
|
|
216
317
|
|
|
217
|
-
常驻在页面左下角,每次输入上屏后显示这一次的**上屏延迟**(按键 → 下一帧画完)、**编辑器耗时**(编辑器自身同步 JS)和**排队时间**(事件等主线程的时间),每项后面的圆点按阈值显示绿 / 黄 / 红。3 秒没有输入显示 `—`。不存历史,关闭时零开销。
|
|
218
|
-
|
|
219
318
|
```ts
|
|
220
|
-
new Editor(el, { perfPanel: true });
|
|
319
|
+
new Editor(el, { perfPanel: true });
|
|
320
|
+
editor.setPerfPanel(false);
|
|
221
321
|
```
|
|
222
322
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
## 性能探针(开发用)
|
|
323
|
+
每次输入上屏后显示上屏延迟(按键到下一帧画完)、编辑器耗时(同步 JS)、排队时间(事件等主线程)。绿 / 黄 / 红按阈值。3 秒没有输入显示 `—`。demo 默认开启。
|
|
226
324
|
|
|
227
|
-
|
|
325
|
+
开发探针默认关闭:
|
|
228
326
|
|
|
229
327
|
```js
|
|
230
|
-
editor.perf.start()
|
|
231
|
-
|
|
232
|
-
editor.perf.report() // 输出各阶段耗时(平均 / p95 / 最大 ms)和帧率
|
|
328
|
+
editor.perf.start()
|
|
329
|
+
editor.perf.report()
|
|
233
330
|
editor.perf.stop()
|
|
234
331
|
```
|
|
235
332
|
|
|
236
|
-
|
|
333
|
+
读法见 [docs/performance.md](./docs/performance.md)。
|
|
237
334
|
|
|
238
335
|
## 设计文档
|
|
239
336
|
|
|
240
|
-
`docs/` 下是分层架构与技术方案:
|
|
241
|
-
|
|
242
337
|
| 文档 | 内容 |
|
|
243
338
|
| --- | --- |
|
|
244
|
-
| [architecture.md](./docs/architecture.md) |
|
|
245
|
-
| [mutations.md](./docs/mutations.md) |
|
|
246
|
-
| [operations.md](./docs/operations.md) |
|
|
247
|
-
| [edits.md](./docs/edits.md) |
|
|
248
|
-
| [rendering.md](./docs/rendering.md) |
|
|
249
|
-
| [virtual-blocks.md](./docs/virtual-blocks.md) |
|
|
250
|
-
| [agent-segment-mapping.md](./docs/agent-segment-mapping.md) |
|
|
251
|
-
| [performance.md](./docs/performance.md) |
|
|
252
|
-
|
|
253
|
-
改动模型前先读 architecture.md 的不变式那一节。
|
|
339
|
+
| [architecture.md](./docs/architecture.md) | 分层结构、不变式、样式模型 |
|
|
340
|
+
| [mutations.md](./docs/mutations.md) | 原子操作 |
|
|
341
|
+
| [operations.md](./docs/operations.md) | 编排层与 Operation 清单 |
|
|
342
|
+
| [edits.md](./docs/edits.md) | 事务、撤销栈 |
|
|
343
|
+
| [rendering.md](./docs/rendering.md) | 增量排版与绘制后端 |
|
|
344
|
+
| [virtual-blocks.md](./docs/virtual-blocks.md) | mark 的模型和交互 |
|
|
345
|
+
| [agent-segment-mapping.md](./docs/agent-segment-mapping.md) | 业务 segment 怎么对应到 API |
|
|
346
|
+
| [performance.md](./docs/performance.md) | 性能探针与优化记录 |
|
|
254
347
|
|
|
255
348
|
## 本地开发
|
|
256
349
|
|
|
257
350
|
```bash
|
|
258
351
|
npm install
|
|
259
|
-
npm run dev # http://localhost:5273/
|
|
352
|
+
npm run dev # http://localhost:5273/
|
|
260
353
|
```
|
|
261
354
|
|
|
262
|
-
| 命令
|
|
263
|
-
|
|
|
264
|
-
| `npm run dev`
|
|
265
|
-
| `npm run build`
|
|
266
|
-
| `npm run build:lib` | 构建 SDK → `dist
|
|
267
|
-
| `npm run selfcheck` |
|
|
268
|
-
|
|
269
|
-
## 发布到 npm
|
|
355
|
+
| 命令 | 作用 |
|
|
356
|
+
| --- | --- |
|
|
357
|
+
| `npm run dev` | demo 开发服务器 |
|
|
358
|
+
| `npm run build` | 构建 demo → `dist-demo/` |
|
|
359
|
+
| `npm run build:lib` | 构建 SDK → `dist/` |
|
|
360
|
+
| `npm run selfcheck` | 分层自检 |
|
|
270
361
|
|
|
271
|
-
|
|
362
|
+
## 发布
|
|
272
363
|
|
|
273
|
-
|
|
274
|
-
2. 改 `package.json` 的 `version`(例如 `0.1.1`),一并合进 main
|
|
275
|
-
3. 在 **main** tip 打同名 tag 并推送:
|
|
364
|
+
包名 `@one2x/medeo-rte`。在 main 上打与 `package.json` 的 `version` 一致的 tag,Actions 会发布。
|
|
276
365
|
|
|
277
366
|
```bash
|
|
278
367
|
git checkout main && git pull
|
|
@@ -280,4 +369,4 @@ git tag v0.1.1
|
|
|
280
369
|
git push origin v0.1.1
|
|
281
370
|
```
|
|
282
371
|
|
|
283
|
-
tag 必须指向 main
|
|
372
|
+
tag 必须指向 main,且和版本号一致,否则发布失败。
|