@one2x/medeo-rte 0.1.0 → 0.2.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 +71 -46
- package/dist/chunks/{types-I0yc5X7i.js → types-Hb-uu8xL.js} +2 -2
- package/dist/chunks/{types-I0yc5X7i.js.map → types-Hb-uu8xL.js.map} +1 -1
- package/dist/formula.js +1 -1
- package/dist/index.js +122 -122
- package/dist/index.js.map +1 -1
- package/dist/types/model/types.d.ts +5 -4
- package/dist/types/model/types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -54,17 +54,37 @@ new Editor(el, { plugins: [...builtinEmbedPlugins, formulaPlugin] });
|
|
|
54
54
|
|
|
55
55
|
文字与段落样式、列表、表格、链接、图文绕排、Markdown 边打边转、撤销重做、中文 IME。
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
嵌入对象走同一套插件接口:图片、视频、代码块、公式、同步块引用、以及下面三类「槽 / 胶囊」。**名字都带 slot,用途完全不同,不要混用。**
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
- **host 胶囊** —— `*` 占位,内部 DOM 由宿主渲染(下拉、开关、上传、`@` 芯片)。整颗换行
|
|
61
|
-
- **虚拟块** —— 盖在字符流上的假块(region 皮 + 文本槽)。槽里的字跟外层句子一起折行,不是实体
|
|
59
|
+
### 三类 slot 对照(接入必读)
|
|
62
60
|
|
|
63
|
-
|
|
61
|
+
| | `internalSlot` | `hostSlot` | `virtualSlot`(+ `region`) |
|
|
62
|
+
| --- | --- | --- | --- |
|
|
63
|
+
| 是什么 | SDK 内置芯片(`slotPlugin`) | 宿主自绘芯片 | 虚拟块文本槽;外层皮是 `region` |
|
|
64
|
+
| 落在哪 | `EmbedEntity`,流上一个 `*` | 同上 | `virtualBlocks` 区间,**不占**额外字符 |
|
|
65
|
+
| 内容 | 实体字段(label / slotValue 等) | `hostType` + `data`,DOM 由宿主画 | 就是那段 `spacers` 文字 |
|
|
66
|
+
| 折行 | 整颗盒子,放不下换行 | 同左 | 槽里的字跟外层句子一起按字折 |
|
|
67
|
+
| 谁用 | demo / 简单内置控件 | **产品业务胶囊**(下拉、`@`、上传等) | **模板里可填灰字**、外层底色分组 |
|
|
68
|
+
| 插入 | `commands.insertSlot('text'\|'image'\|'select')` | `commands.insertHostEmbed(...)` 或 `insertSlice` 的 `entities` | `insertSlice` 的 `virtualBlocks`,或 `insertVirtualBlock` |
|
|
64
69
|
|
|
65
|
-
|
|
70
|
+
另外还有 **同步块**(fragment / ref):选区提成可复用内容,改一处全变——和上面三类无关。
|
|
66
71
|
|
|
67
|
-
|
|
72
|
+
产品模板句里「可填写的那截灰字」用 **`virtualSlot`**,不要用 `internalSlot` / `hostSlot`。后两者是另一颗盒子,点进去才填,不能按字切开。
|
|
73
|
+
|
|
74
|
+
## 虚拟块怎么用(业务侧)
|
|
75
|
+
|
|
76
|
+
虚拟块是盖在字符流上的假块:负责皮(背景、圆角)和「整段选中」交互。区间里的字仍是文档正文。用户打字**不会**新建虚拟块,只能由业务 API 插入。
|
|
77
|
+
|
|
78
|
+
两种 kind:
|
|
79
|
+
|
|
80
|
+
- **`region`**:一层皮 / 分组。整段选中时打字、删除会被忽略(保住模板壳)。没有 `editing` 模式。
|
|
81
|
+
- **`virtualSlot`**:最内层文本槽。可进 `editing`;空着时显示 `placeholder`,填了就是用户字;整段删回 placeholder,槽本身留下。
|
|
82
|
+
|
|
83
|
+
嵌套规则:只允许包含、不允许交叉;`virtualSlot` 必须是叶子。日常编辑里业务**不用维护** `[start, end)`,插删字时 SDK 自动挪区间。
|
|
84
|
+
|
|
85
|
+
### 路径 A:整段模板一次插入(推荐)
|
|
86
|
+
|
|
87
|
+
文案 + 槽 + 胶囊一起走 `insertSlice`。`virtualBlocks` 里的下标是**相对这段 `text`** 的半开区间:
|
|
68
88
|
|
|
69
89
|
```ts
|
|
70
90
|
editor.insertSlice({
|
|
@@ -83,7 +103,7 @@ editor.insertSlice({
|
|
|
83
103
|
{
|
|
84
104
|
id: 'slot',
|
|
85
105
|
parentId: 'region',
|
|
86
|
-
kind: '
|
|
106
|
+
kind: 'virtualSlot',
|
|
87
107
|
start: 19,
|
|
88
108
|
end: 32,
|
|
89
109
|
placeholder: 'Input a theme',
|
|
@@ -94,15 +114,47 @@ editor.insertSlice({
|
|
|
94
114
|
});
|
|
95
115
|
```
|
|
96
116
|
|
|
97
|
-
|
|
117
|
+
完整样例见 `demo/vblocks.ts`。
|
|
98
118
|
|
|
99
|
-
|
|
119
|
+
### 路径 B:给已有字套皮
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
// 不传 start/end → 用当前选区
|
|
123
|
+
editor.insertVirtualBlock({
|
|
124
|
+
kind: 'region',
|
|
125
|
+
chrome: { background: 'rgba(43,108,255,0.08)', radius: 8 }
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
editor.insertVirtualBlock({
|
|
129
|
+
kind: 'virtualSlot',
|
|
130
|
+
placeholder: 'Input a theme',
|
|
131
|
+
empty: true,
|
|
132
|
+
chrome: { background: 'rgba(16,185,129,0.18)', placeholderColor: '#98a2b3' }
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// 也可显式下标
|
|
136
|
+
editor.insertVirtualBlock({ kind: 'region', start: 0, end: 20, chrome: { ... } });
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
改皮 / 拆皮:`updateVirtualBlock(id, chrome)`、`removeVirtualBlock(id)`(只拆皮,字和胶囊留下)。
|
|
140
|
+
|
|
141
|
+
### 业务浮层(Sample / AI 等)
|
|
142
|
+
|
|
143
|
+
SDK 不画业务菜单。宿主自己:
|
|
144
|
+
|
|
145
|
+
1. 听 `statechange`,看 `getState().vblockStack` 是否进了某槽的 `editing`
|
|
146
|
+
2. 用 `getLayout()` 里该槽的 `vblocks` 矩形定位浮层
|
|
147
|
+
3. 写入:让用户打字,或再 `insertSlice` 一段纯文本
|
|
148
|
+
|
|
149
|
+
### 和 host 胶囊一起用
|
|
150
|
+
|
|
151
|
+
胶囊是流上的 `*`,可以落在 region 覆盖范围内,但**不是**虚拟块的子节点。自定义芯片:
|
|
100
152
|
|
|
101
153
|
```ts
|
|
102
154
|
const editor = new Editor(el, {
|
|
103
155
|
hostEmbed: {
|
|
104
156
|
mount(container, ctx) {
|
|
105
|
-
// 按 ctx.entity.hostType
|
|
157
|
+
// 按 ctx.entity.hostType 画自己的组件;实体 kind 为 'hostSlot'
|
|
106
158
|
return { update(next) {}, destroy() {} };
|
|
107
159
|
}
|
|
108
160
|
}
|
|
@@ -110,7 +162,7 @@ const editor = new Editor(el, {
|
|
|
110
162
|
editor.commands.insertHostEmbed('Upscale', { label: '@Upscale' }, { width: 88, height: 24 });
|
|
111
163
|
```
|
|
112
164
|
|
|
113
|
-
|
|
165
|
+
更细的模型与选区栈见 [virtual-blocks.md](./docs/virtual-blocks.md)。
|
|
114
166
|
|
|
115
167
|
### API 一览
|
|
116
168
|
|
|
@@ -168,43 +220,16 @@ npm run dev # http://localhost:5273/ 跑 demo
|
|
|
168
220
|
|
|
169
221
|
## 发布到 npm
|
|
170
222
|
|
|
171
|
-
包名 `@one2x/medeo-rte
|
|
172
|
-
GitHub 仓库在 **one2x-ai/medeo-rte** —— 和 npm scope 不是同一个名字。
|
|
173
|
-
|
|
174
|
-
### 日常发版(main 打 tag)
|
|
223
|
+
包名 `@one2x/medeo-rte`。在 **main** 上打与版本号一致的 tag,Actions 会自动发布。
|
|
175
224
|
|
|
176
|
-
1.
|
|
177
|
-
2.
|
|
225
|
+
1. 把要发的改动合进 **main**
|
|
226
|
+
2. 改 `package.json` 的 `version`(例如 `0.1.1`),一并合进 main
|
|
227
|
+
3. 在 **main** tip 打同名 tag 并推送:
|
|
178
228
|
|
|
179
229
|
```bash
|
|
180
230
|
git checkout main && git pull
|
|
181
|
-
git tag v0.1.
|
|
182
|
-
git push origin v0.1.
|
|
231
|
+
git tag v0.1.1
|
|
232
|
+
git push origin v0.1.1
|
|
183
233
|
```
|
|
184
234
|
|
|
185
|
-
|
|
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)
|
|
235
|
+
tag 必须指向 main 上的提交,且 `v*` 与 `package.json` 的 `version` 一致,否则 Publish 会失败。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const s = "mini-embed-change", n = "mini-embed-commit", a = "mini-embed-exit-edit", E = "mini-embed-measure";
|
|
2
2
|
function d(t, e) {
|
|
3
|
-
t.style.left = `${e.x}px`, t.style.top = `${e.y}px`, e.entity.kind === "
|
|
3
|
+
t.style.left = `${e.x}px`, t.style.top = `${e.y}px`, e.entity.kind === "hostSlot" ? (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
4
|
}
|
|
5
5
|
function m(t, e, i = !1) {
|
|
6
6
|
t.dispatchEvent(
|
|
@@ -15,4 +15,4 @@ export {
|
|
|
15
15
|
m as e,
|
|
16
16
|
d as p
|
|
17
17
|
};
|
|
18
|
-
//# sourceMappingURL=types-
|
|
18
|
+
//# sourceMappingURL=types-Hb-uu8xL.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types-
|
|
1
|
+
{"version":3,"file":"types-Hb-uu8xL.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 === 'hostSlot') {\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,cACtBD,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
CHANGED
|
@@ -2,7 +2,7 @@ import M from "katex";
|
|
|
2
2
|
import "katex/dist/katex.min.css";
|
|
3
3
|
import { MathfieldElement as E } from "mathlive";
|
|
4
4
|
import "mathlive/fonts.css";
|
|
5
|
-
import { e as w, E as L } from "./chunks/types-
|
|
5
|
+
import { e as w, E as L } from "./chunks/types-Hb-uu8xL.js";
|
|
6
6
|
const C = {
|
|
7
7
|
"\\wideparen": "\\overgroup{#1}",
|
|
8
8
|
"\\overarc": "\\overgroup{#1}",
|