@mteditor/renderer-web 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/README.md +65 -2
- package/dist/index.cjs +69 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +165 -0
- package/dist/index.d.ts +165 -0
- package/dist/index.global.js +3 -0
- package/dist/index.global.js.map +1 -0
- package/dist/index.js +60 -0
- package/dist/index.js.map +1 -0
- package/package.json +66 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 MtEditor contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,66 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @mteditor/renderer-web
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Web 端只读渲染器 —— 把 `MtDocument` 渲染成 HTML / DOM,**不加载编辑器**。
|
|
4
|
+
|
|
5
|
+
## 定位
|
|
6
|
+
|
|
7
|
+
适用于详情页、SEO 静态页、邮件预览、SSR / 边缘函数等「只看不编辑」的场景。
|
|
8
|
+
因为不挂载编辑器内核,体积远小于 `core`,可以在任意页面按需引入。
|
|
9
|
+
|
|
10
|
+
## 安装
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm i @mteditor/renderer-web @mteditor/theme-default
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
`theme-default` 提供 `.mt-content` 排版样式,**与编辑器共用同一套**,保证「详情页看到的」
|
|
17
|
+
与「编辑器里看到的」一致。
|
|
18
|
+
|
|
19
|
+
## 用法
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { renderToDOM, renderToString } from '@mteditor/renderer-web'
|
|
23
|
+
import '@mteditor/theme-default/index.css'
|
|
24
|
+
|
|
25
|
+
// SSR / 服务端:只要字符串(全程不触碰 DOM)
|
|
26
|
+
const { html } = renderToString(doc)
|
|
27
|
+
|
|
28
|
+
// 详情页:挂载到容器(重复调用幂等,会整体替换容器子节点)
|
|
29
|
+
const { element, report } = renderToDOM(doc, document.getElementById('view')!)
|
|
30
|
+
|
|
31
|
+
// 需要局部固定暗色时
|
|
32
|
+
renderToString(doc, { theme: 'dark', className: 'mt-content article-body' })
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## API
|
|
36
|
+
|
|
37
|
+
| API | 说明 |
|
|
38
|
+
|---|---|
|
|
39
|
+
| `renderToString(doc, options)` | 输出 `{ html, report, stats }`;**全程不触碰 DOM**,SSR / 边缘函数可用 |
|
|
40
|
+
| `renderToDOM(doc, container, options)` | 同上一份结果 + `element`,并挂载进 `container` |
|
|
41
|
+
| `resolveContainerAttrs` / `containerAttrsToString` / `applyContainerAttrs` / `wrapContainer` | 容器属性装配原语,字符串出口与 DOM 出口共用 |
|
|
42
|
+
|
|
43
|
+
### 选项
|
|
44
|
+
|
|
45
|
+
| 选项 | 缺省 | 说明 |
|
|
46
|
+
|---|---|---|
|
|
47
|
+
| `className` | `'mt-content'` | 容器的**完整** `class` 值;传 `''` 则不输出 `class` |
|
|
48
|
+
| `theme` | 不写属性 | 跟随宿主 `data-mt-theme`;显式传 `'light' \| 'dark'` 时写在容器上做局部覆盖 |
|
|
49
|
+
| `preRendered` | — | 预渲染产物索引(公式 SVG / 代码高亮) |
|
|
50
|
+
| `indent` | `0` | `> 0` 时启用换行美化,仅影响可读性 |
|
|
51
|
+
| `report` | `true` | 置 `false` 等于主动放弃降级信息 |
|
|
52
|
+
|
|
53
|
+
## 注意事项
|
|
54
|
+
|
|
55
|
+
- **与编辑器共用真相源**:HTML 一律由 `@mteditor/document` 的序列化器产出,本包只做
|
|
56
|
+
容器包装、主题接线与 DOM 挂载,不实现第二套 HTML 序列化器,也不改写序列化产物
|
|
57
|
+
(不注入 `loading="lazy"`、不追加 `data-mt-*`)。这保证「详情页看到的」与「存库 / 导出 /
|
|
58
|
+
小程序端渲染的」来自同一份真相。
|
|
59
|
+
- 本包**不依赖 `core`**:只想渲染的页面不会因此加载编辑器内核。
|
|
60
|
+
- 挂载容器须处于**普通文档流**,不得置于 `position: fixed` 容器内(iOS 光标跳变)。
|
|
61
|
+
- 降级信息复用 `document` 的降级报告:非白名单节点不允许静默丢弃;不需要时用
|
|
62
|
+
`report: false` 显式关掉。
|
|
63
|
+
|
|
64
|
+
## 许可
|
|
65
|
+
|
|
66
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var document = require('@mteditor/document');
|
|
4
|
+
|
|
5
|
+
// src/options.ts
|
|
6
|
+
var MT_CONTENT_CLASS = "mt-content";
|
|
7
|
+
var MT_THEME_ATTR = "data-mt-theme";
|
|
8
|
+
var resolveContainerAttrs = (options) => {
|
|
9
|
+
const attrs = [];
|
|
10
|
+
const className = options.className ?? MT_CONTENT_CLASS;
|
|
11
|
+
if (className.length > 0) attrs.push(["class", className]);
|
|
12
|
+
if (options.theme !== void 0) attrs.push([MT_THEME_ATTR, options.theme]);
|
|
13
|
+
return attrs;
|
|
14
|
+
};
|
|
15
|
+
var containerAttrsToString = (options) => resolveContainerAttrs(options).map(([name, value]) => ` ${name}="${document.escapeAttr(value)}"`).join("");
|
|
16
|
+
var applyContainerAttrs = (element, options) => {
|
|
17
|
+
for (const [name, value] of resolveContainerAttrs(options)) {
|
|
18
|
+
element.setAttribute(name, value);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
var wrapContainer = (output, options) => `<div${containerAttrsToString(options)}>${output}</div>`;
|
|
22
|
+
var renderContent = (doc, options) => {
|
|
23
|
+
const serialized = document.serializeDocumentToHtml(doc, {
|
|
24
|
+
preRendered: options.preRendered,
|
|
25
|
+
indent: options.indent
|
|
26
|
+
});
|
|
27
|
+
return {
|
|
28
|
+
output: serialized.output,
|
|
29
|
+
// 只有调用方**显式**要求关闭时才丢弃报告
|
|
30
|
+
report: options.report === false ? [] : serialized.report,
|
|
31
|
+
stats: serialized.stats
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// src/render-to-string.ts
|
|
36
|
+
var renderToString = (doc, options = {}) => {
|
|
37
|
+
const content = renderContent(doc, options);
|
|
38
|
+
return {
|
|
39
|
+
html: wrapContainer(content.output, options),
|
|
40
|
+
report: content.report,
|
|
41
|
+
stats: content.stats
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// src/render-to-dom.ts
|
|
46
|
+
var renderToDOM = (doc, container, options = {}) => {
|
|
47
|
+
const content = renderContent(doc, options);
|
|
48
|
+
const element = container.ownerDocument.createElement("div");
|
|
49
|
+
applyContainerAttrs(element, options);
|
|
50
|
+
element.innerHTML = content.output;
|
|
51
|
+
container.replaceChildren(element);
|
|
52
|
+
return {
|
|
53
|
+
html: wrapContainer(content.output, options),
|
|
54
|
+
report: content.report,
|
|
55
|
+
stats: content.stats,
|
|
56
|
+
element
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
exports.MT_CONTENT_CLASS = MT_CONTENT_CLASS;
|
|
61
|
+
exports.MT_THEME_ATTR = MT_THEME_ATTR;
|
|
62
|
+
exports.applyContainerAttrs = applyContainerAttrs;
|
|
63
|
+
exports.containerAttrsToString = containerAttrsToString;
|
|
64
|
+
exports.renderToDOM = renderToDOM;
|
|
65
|
+
exports.renderToString = renderToString;
|
|
66
|
+
exports.resolveContainerAttrs = resolveContainerAttrs;
|
|
67
|
+
exports.wrapContainer = wrapContainer;
|
|
68
|
+
//# sourceMappingURL=index.cjs.map
|
|
69
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/options.ts","../src/container.ts","../src/render-content.ts","../src/render-to-string.ts","../src/render-to-dom.ts"],"names":["escapeAttr","serializeDocumentToHtml"],"mappings":";;;;;AAOO,IAAM,gBAAA,GAAmB;AAGzB,IAAM,aAAA,GAAgB;ACiBtB,IAAM,qBAAA,GAAwB,CAAC,OAAA,KAAgD;AACpF,EAAA,MAAM,QAA2B,EAAC;AAClC,EAAA,MAAM,SAAA,GAAY,QAAQ,SAAA,IAAa,gBAAA;AACvC,EAAA,IAAI,SAAA,CAAU,SAAS,CAAA,EAAG,KAAA,CAAM,KAAK,CAAC,OAAA,EAAS,SAAS,CAAC,CAAA;AACzD,EAAA,IAAI,OAAA,CAAQ,UAAU,MAAA,EAAW,KAAA,CAAM,KAAK,CAAC,aAAA,EAAe,OAAA,CAAQ,KAAK,CAAC,CAAA;AAC1E,EAAA,OAAO,KAAA;AACT;AAaO,IAAM,sBAAA,GAAyB,CAAC,OAAA,KACrC,qBAAA,CAAsB,OAAO,CAAA,CAC1B,GAAA,CAAI,CAAC,CAAC,IAAA,EAAM,KAAK,CAAA,KAAM,CAAA,CAAA,EAAI,IAAI,CAAA,EAAA,EAAKA,mBAAA,CAAW,KAAK,CAAC,CAAA,CAAA,CAAG,CAAA,CACxD,IAAA,CAAK,EAAE;AAQL,IAAM,mBAAA,GAAsB,CAAC,OAAA,EAAkB,OAAA,KAAmC;AACvF,EAAA,KAAA,MAAW,CAAC,IAAA,EAAM,KAAK,CAAA,IAAK,qBAAA,CAAsB,OAAO,CAAA,EAAG;AAC1D,IAAA,OAAA,CAAQ,YAAA,CAAa,MAAM,KAAK,CAAA;AAAA,EAClC;AACF;AAiBO,IAAM,aAAA,GAAgB,CAAC,MAAA,EAAgB,OAAA,KAC5C,OAAO,sBAAA,CAAuB,OAAO,CAAC,CAAA,CAAA,EAAI,MAAM,CAAA,MAAA;AC9C3C,IAAM,aAAA,GAAgB,CAAC,GAAA,EAAiB,OAAA,KAAgD;AAC7F,EAAA,MAAM,UAAA,GAAaC,iCAAwB,GAAA,EAAK;AAAA,IAC9C,aAAa,OAAA,CAAQ,WAAA;AAAA,IACrB,QAAQ,OAAA,CAAQ;AAAA,GACjB,CAAA;AAED,EAAA,OAAO;AAAA,IACL,QAAQ,UAAA,CAAW,MAAA;AAAA;AAAA,IAEnB,QAAQ,OAAA,CAAQ,MAAA,KAAW,KAAA,GAAQ,KAAK,UAAA,CAAW,MAAA;AAAA,IACnD,OAAO,UAAA,CAAW;AAAA,GACpB;AACF,CAAA;;;ACZO,IAAM,cAAA,GAAiB,CAAC,GAAA,EAAiB,OAAA,GAA2B,EAAC,KAAsB;AAChG,EAAA,MAAM,OAAA,GAAU,aAAA,CAAc,GAAA,EAAK,OAAO,CAAA;AAC1C,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,aAAA,CAAc,OAAA,CAAQ,MAAA,EAAQ,OAAO,CAAA;AAAA,IAC3C,QAAQ,OAAA,CAAQ,MAAA;AAAA,IAChB,OAAO,OAAA,CAAQ;AAAA,GACjB;AACF;;;ACTO,IAAM,cAAc,CACzB,GAAA,EACA,SAAA,EACA,OAAA,GAA2B,EAAC,KACN;AACtB,EAAA,MAAM,OAAA,GAAU,aAAA,CAAc,GAAA,EAAK,OAAO,CAAA;AAC1C,EAAA,MAAM,OAAA,GAAU,SAAA,CAAU,aAAA,CAAc,aAAA,CAAc,KAAK,CAAA;AAE3D,EAAA,mBAAA,CAAoB,SAAS,OAAO,CAAA;AAIpC,EAAA,OAAA,CAAQ,YAAY,OAAA,CAAQ,MAAA;AAE5B,EAAA,SAAA,CAAU,gBAAgB,OAAO,CAAA;AAEjC,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,aAAA,CAAc,OAAA,CAAQ,MAAA,EAAQ,OAAO,CAAA;AAAA,IAC3C,QAAQ,OAAA,CAAQ,MAAA;AAAA,IAChB,OAAO,OAAA,CAAQ,KAAA;AAAA,IACf;AAAA,GACF;AACF","file":"index.cjs","sourcesContent":["/**\n * `@mteditor/renderer-web` 的公开选项与结果类型。\n */\n\nimport type { MtDegradationEntry, MtPreRenderedIndex, MtSerializeStats } from '@mteditor/document'\n\n/** 内容容器类名 —— 与 `@mteditor/theme-default` 的 `.mt-content` 约定一致 */\nexport const MT_CONTENT_CLASS = 'mt-content'\n\n/** 主题属性名 —— 主题层只认这一个属性 */\nexport const MT_THEME_ATTR = 'data-mt-theme'\n\n/** 主题取值;不开放任意字符串,避免写出主题层无法识别的属性值 */\nexport type MtTheme = 'light' | 'dark'\n\n/** 渲染选项;全部字段可选 */\nexport interface MtRenderOptions {\n /**\n * 容器的**完整** `class` 值,缺省 `'mt-content'`。\n *\n * 需要追加自己的类名时请一并写出,例如 `'mt-content article-body'`;\n * 传空字符串则完全不输出 `class` 属性(此时将失去主题排版样式)。\n */\n className?: string\n /**\n * 主题。**缺省不写 `data-mt-theme`**,跟随宿主(主题模型)。\n *\n * 只在需要**局部覆盖**时显式传入,例如邮件预览固定亮色。\n */\n theme?: MtTheme\n /** 预渲染产物索引(公式 SVG / 代码高亮) */\n preRendered?: MtPreRenderedIndex\n /** 缩进层级,缺省 `0`;仅影响 HTML 可读性,不参与语义 */\n indent?: number\n /** 是否返回降级报告,缺省 `true`。**置 `false` 等于主动放弃降级信息** */\n report?: boolean\n}\n\n/** 渲染结果(`renderToString` 的返回值) */\nexport interface MtRenderResult {\n /** 完整 HTML,**含外层容器** */\n html: string\n /** 降级报告;`report: false` 时为空数组 */\n report: MtDegradationEntry[]\n /** 序列化统计(**不含**外层容器,即 `document` 序列化器的原始统计) */\n stats: MtSerializeStats\n}\n\n/** DOM 渲染结果(`renderToDOM` 的返回值) */\nexport interface MtDomRenderResult extends MtRenderResult {\n /** 实际挂载进容器的元素 */\n element: HTMLElement\n}\n","/**\n * 渲染容器的属性装配。\n *\n * 容器属性有**两条出口**:字符串出口(`renderToString`)与真实元素出口(`renderToDOM`)。\n * 两者必须产出同一组属性,因此都从本模块的 `resolveContainerAttrs` 取值——\n * 这是「SSR 字符串与客户端 DOM 一致」的实现基础。\n */\n\nimport { escapeAttr } from '@mteditor/document'\n\nimport { MT_CONTENT_CLASS, MT_THEME_ATTR, type MtRenderOptions } from './options'\n\n/** 属性对:`[属性名, 属性值]`,值必为字符串(无「缺省」概念,缺省即不产出该对) */\ntype MtContainerAttr = readonly [name: string, value: string]\n\n/**\n * 解析容器应当产出的属性对(顺序固定:`class` 在前)。\n *\n * @param options 渲染选项\n * @returns 属性对列表;可能为空数组\n *\n * @example\n * ```ts\n * resolveContainerAttrs({ className: '' }) // []\n * resolveContainerAttrs({ theme: 'dark' }) // [['class', 'mt-content'], ['data-mt-theme', 'dark']]\n * ```\n */\nexport const resolveContainerAttrs = (options: MtRenderOptions): MtContainerAttr[] => {\n const attrs: MtContainerAttr[] = []\n const className = options.className ?? MT_CONTENT_CLASS\n if (className.length > 0) attrs.push(['class', className])\n if (options.theme !== undefined) attrs.push([MT_THEME_ATTR, options.theme])\n return attrs\n}\n\n/**\n * 把容器属性序列化为 HTML 属性串。\n *\n * @param options 渲染选项\n * @returns 形如 ` class=\"mt-content\"` 的字符串;无属性时为空串\n *\n * @example\n * ```ts\n * containerAttrsToString({}) // ' class=\"mt-content\"'\n * ```\n */\nexport const containerAttrsToString = (options: MtRenderOptions): string =>\n resolveContainerAttrs(options)\n .map(([name, value]) => ` ${name}=\"${escapeAttr(value)}\"`)\n .join('')\n\n/**\n * 把容器属性写到真实元素上。\n *\n * @param element 目标元素\n * @param options 渲染选项\n */\nexport const applyContainerAttrs = (element: Element, options: MtRenderOptions): void => {\n for (const [name, value] of resolveContainerAttrs(options)) {\n element.setAttribute(name, value)\n }\n}\n\n/**\n * 用容器包裹内容 HTML。\n *\n * `renderToString`(字符串出口)与 `renderToDOM`(DOM 出口)共用本函数,\n * 因此两者返回的 `html` **必然逐字符相同**——这是「SSR 产物与客户端产物一致」的保证。\n *\n * @param output 容器内部的 HTML\n * @param options 渲染选项\n * @returns 含容器的完整 HTML\n *\n * @example\n * ```ts\n * wrapContainer('<p>hi</p>', {}) // '<div class=\"mt-content\"><p>hi</p></div>'\n * ```\n */\nexport const wrapContainer = (output: string, options: MtRenderOptions): string =>\n `<div${containerAttrsToString(options)}>${output}</div>`\n","/**\n * 内容序列化的**唯一入口**。\n *\n * 本模块是 `renderToString` 与 `renderToDOM` 的共同上游:两个公开函数\n * 都从这里拿内容,保证「字符串出口」与「DOM 出口」不可能产出不同的节点结构。\n *\n * ⚠️ 本模块**不得做任何改写**:不加 `loading=\"lazy\"`、不注入额外的 `data-mt-*`、\n * 不做降级高亮。任何改写都会让 Web 只读渲染与小程序端 / 编辑器的节点结构\n * 不再逐字符一致。\n */\n\nimport { serializeDocumentToHtml } from '@mteditor/document'\nimport type { MtDegradationEntry, MtDocument, MtSerializeStats } from '@mteditor/document'\n\nimport type { MtRenderOptions } from './options'\n\n/** 内容序列化结果(不含外层容器) */\nexport interface MtRenderedContent {\n /** 容器内部的 HTML(即 `document` 序列化器的原样产物) */\n output: string\n /** 降级报告;`report: false` 时为空数组 */\n report: MtDegradationEntry[]\n /** 序列化统计 */\n stats: MtSerializeStats\n}\n\n/**\n * 把 `MtDocument` 序列化为容器内部的 HTML。\n *\n * @param doc 文档\n * @param options 渲染选项\n * @returns 内容与报告、统计\n */\nexport const renderContent = (doc: MtDocument, options: MtRenderOptions): MtRenderedContent => {\n const serialized = serializeDocumentToHtml(doc, {\n preRendered: options.preRendered,\n indent: options.indent,\n })\n\n return {\n output: serialized.output,\n // 只有调用方**显式**要求关闭时才丢弃报告\n report: options.report === false ? [] : serialized.report,\n stats: serialized.stats,\n }\n}\n","/**\n * `renderToString` —— 输出 HTML 字符串,**全程不触碰 DOM**(SSR / 邮件 / 静态页)。\n */\n\nimport type { MtDocument } from '@mteditor/document'\n\nimport { wrapContainer } from './container'\nimport type { MtRenderOptions, MtRenderResult } from './options'\nimport { renderContent } from './render-content'\n\n/**\n * 把 `MtDocument` 渲染为 HTML 字符串。\n *\n * 产物 = `<div class=\"mt-content\">{document 序列化产物}</div>`,\n * 内容部分与 `@mteditor/document` 的 `serializeDocumentToHtml` **逐字符一致**,\n * 因此不存在「详情页与编辑器渲染不一致」的可能。\n *\n * 本函数不使用 `window` / `document` / `innerHTML`,可在 Node、边缘函数、\n * 小程序服务端等任何无 DOM 环境执行。\n *\n * @param doc 文档\n * @param options 渲染选项\n * @returns 渲染结果(`html` / `report` / `stats`)\n *\n * @example\n * ```ts\n * const { html, report } = renderToString(doc)\n * // 需要局部固定暗色时:\n * renderToString(doc, { theme: 'dark', className: 'mt-content article-body' }).html\n * // 只要字符串:\n * const str = renderToString(doc).html\n * ```\n */\nexport const renderToString = (doc: MtDocument, options: MtRenderOptions = {}): MtRenderResult => {\n const content = renderContent(doc, options)\n return {\n html: wrapContainer(content.output, options),\n report: content.report,\n stats: content.stats,\n }\n}\n","/**\n * `renderToDOM` —— 把 `MtDocument` 渲染为真实 DOM 节点并挂载进容器。\n *\n * 与 `renderToString` 的关系:**同一份内容、同一套容器属性**,\n * 因此 `renderToDOM(doc, el).html === renderToString(doc).html` 恒成立。\n */\n\nimport type { MtDocument } from '@mteditor/document'\n\nimport { applyContainerAttrs, wrapContainer } from './container'\nimport type { MtDomRenderResult, MtRenderOptions } from './options'\nimport { renderContent } from './render-content'\n\n/**\n * 把 `MtDocument` 渲染为 DOM 并挂载到 `container`。\n *\n * **挂载语义**:调用后 `container` 的子节点被**整体替换**为渲染出的容器元素\n * (即重复调用是幂等的,不会累积)。`container` 自身的属性不被修改——\n * 主题由宿主控制,需要局部覆盖时请用 `options.theme`。\n *\n * @param doc 文档\n * @param container 挂载容器(普通文档流中的元素,不得位于 `position: fixed` 容器内)\n * @param options 渲染选项\n * @returns 渲染结果 + 实际挂载的元素\n *\n * @example\n * ```ts\n * const { element, report } = renderToDOM(doc, document.getElementById('view')!)\n * element.dataset.rendered // 可通过返回的元素继续做懒加载等增强\n * ```\n */\nexport const renderToDOM = (\n doc: MtDocument,\n container: HTMLElement,\n options: MtRenderOptions = {},\n): MtDomRenderResult => {\n const content = renderContent(doc, options)\n const element = container.ownerDocument.createElement('div')\n\n applyContainerAttrs(element, options)\n // 安全性:content.output 由 @mteditor/document 的序列化器产出——文本与属性值\n // 均已转义,仅白名单内的标签/属性可存活(10 份 XSS 载荷基线为证)。\n // 此处不接受任何未经清洗的外部输入。\n element.innerHTML = content.output\n\n container.replaceChildren(element)\n\n return {\n html: wrapContainer(content.output, options),\n report: content.report,\n stats: content.stats,\n element,\n }\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { MtDegradationEntry, MtSerializeStats, MtPreRenderedIndex, MtDocument } from '@mteditor/document';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `@mteditor/renderer-web` 的公开选项与结果类型。
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/** 内容容器类名 —— 与 `@mteditor/theme-default` 的 `.mt-content` 约定一致 */
|
|
8
|
+
declare const MT_CONTENT_CLASS = "mt-content";
|
|
9
|
+
/** 主题属性名 —— 主题层只认这一个属性 */
|
|
10
|
+
declare const MT_THEME_ATTR = "data-mt-theme";
|
|
11
|
+
/** 主题取值;不开放任意字符串,避免写出主题层无法识别的属性值 */
|
|
12
|
+
type MtTheme = 'light' | 'dark';
|
|
13
|
+
/** 渲染选项;全部字段可选 */
|
|
14
|
+
interface MtRenderOptions {
|
|
15
|
+
/**
|
|
16
|
+
* 容器的**完整** `class` 值,缺省 `'mt-content'`。
|
|
17
|
+
*
|
|
18
|
+
* 需要追加自己的类名时请一并写出,例如 `'mt-content article-body'`;
|
|
19
|
+
* 传空字符串则完全不输出 `class` 属性(此时将失去主题排版样式)。
|
|
20
|
+
*/
|
|
21
|
+
className?: string;
|
|
22
|
+
/**
|
|
23
|
+
* 主题。**缺省不写 `data-mt-theme`**,跟随宿主(主题模型)。
|
|
24
|
+
*
|
|
25
|
+
* 只在需要**局部覆盖**时显式传入,例如邮件预览固定亮色。
|
|
26
|
+
*/
|
|
27
|
+
theme?: MtTheme;
|
|
28
|
+
/** 预渲染产物索引(公式 SVG / 代码高亮) */
|
|
29
|
+
preRendered?: MtPreRenderedIndex;
|
|
30
|
+
/** 缩进层级,缺省 `0`;仅影响 HTML 可读性,不参与语义 */
|
|
31
|
+
indent?: number;
|
|
32
|
+
/** 是否返回降级报告,缺省 `true`。**置 `false` 等于主动放弃降级信息** */
|
|
33
|
+
report?: boolean;
|
|
34
|
+
}
|
|
35
|
+
/** 渲染结果(`renderToString` 的返回值) */
|
|
36
|
+
interface MtRenderResult {
|
|
37
|
+
/** 完整 HTML,**含外层容器** */
|
|
38
|
+
html: string;
|
|
39
|
+
/** 降级报告;`report: false` 时为空数组 */
|
|
40
|
+
report: MtDegradationEntry[];
|
|
41
|
+
/** 序列化统计(**不含**外层容器,即 `document` 序列化器的原始统计) */
|
|
42
|
+
stats: MtSerializeStats;
|
|
43
|
+
}
|
|
44
|
+
/** DOM 渲染结果(`renderToDOM` 的返回值) */
|
|
45
|
+
interface MtDomRenderResult extends MtRenderResult {
|
|
46
|
+
/** 实际挂载进容器的元素 */
|
|
47
|
+
element: HTMLElement;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* `renderToString` —— 输出 HTML 字符串,**全程不触碰 DOM**(SSR / 邮件 / 静态页)。
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 把 `MtDocument` 渲染为 HTML 字符串。
|
|
56
|
+
*
|
|
57
|
+
* 产物 = `<div class="mt-content">{document 序列化产物}</div>`,
|
|
58
|
+
* 内容部分与 `@mteditor/document` 的 `serializeDocumentToHtml` **逐字符一致**,
|
|
59
|
+
* 因此不存在「详情页与编辑器渲染不一致」的可能。
|
|
60
|
+
*
|
|
61
|
+
* 本函数不使用 `window` / `document` / `innerHTML`,可在 Node、边缘函数、
|
|
62
|
+
* 小程序服务端等任何无 DOM 环境执行。
|
|
63
|
+
*
|
|
64
|
+
* @param doc 文档
|
|
65
|
+
* @param options 渲染选项
|
|
66
|
+
* @returns 渲染结果(`html` / `report` / `stats`)
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```ts
|
|
70
|
+
* const { html, report } = renderToString(doc)
|
|
71
|
+
* // 需要局部固定暗色时:
|
|
72
|
+
* renderToString(doc, { theme: 'dark', className: 'mt-content article-body' }).html
|
|
73
|
+
* // 只要字符串:
|
|
74
|
+
* const str = renderToString(doc).html
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
declare const renderToString: (doc: MtDocument, options?: MtRenderOptions) => MtRenderResult;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* `renderToDOM` —— 把 `MtDocument` 渲染为真实 DOM 节点并挂载进容器。
|
|
81
|
+
*
|
|
82
|
+
* 与 `renderToString` 的关系:**同一份内容、同一套容器属性**,
|
|
83
|
+
* 因此 `renderToDOM(doc, el).html === renderToString(doc).html` 恒成立。
|
|
84
|
+
*/
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 把 `MtDocument` 渲染为 DOM 并挂载到 `container`。
|
|
88
|
+
*
|
|
89
|
+
* **挂载语义**:调用后 `container` 的子节点被**整体替换**为渲染出的容器元素
|
|
90
|
+
* (即重复调用是幂等的,不会累积)。`container` 自身的属性不被修改——
|
|
91
|
+
* 主题由宿主控制,需要局部覆盖时请用 `options.theme`。
|
|
92
|
+
*
|
|
93
|
+
* @param doc 文档
|
|
94
|
+
* @param container 挂载容器(普通文档流中的元素,不得位于 `position: fixed` 容器内)
|
|
95
|
+
* @param options 渲染选项
|
|
96
|
+
* @returns 渲染结果 + 实际挂载的元素
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* const { element, report } = renderToDOM(doc, document.getElementById('view')!)
|
|
101
|
+
* element.dataset.rendered // 可通过返回的元素继续做懒加载等增强
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
declare const renderToDOM: (doc: MtDocument, container: HTMLElement, options?: MtRenderOptions) => MtDomRenderResult;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* 渲染容器的属性装配。
|
|
108
|
+
*
|
|
109
|
+
* 容器属性有**两条出口**:字符串出口(`renderToString`)与真实元素出口(`renderToDOM`)。
|
|
110
|
+
* 两者必须产出同一组属性,因此都从本模块的 `resolveContainerAttrs` 取值——
|
|
111
|
+
* 这是「SSR 字符串与客户端 DOM 一致」的实现基础。
|
|
112
|
+
*/
|
|
113
|
+
|
|
114
|
+
/** 属性对:`[属性名, 属性值]`,值必为字符串(无「缺省」概念,缺省即不产出该对) */
|
|
115
|
+
type MtContainerAttr = readonly [name: string, value: string];
|
|
116
|
+
/**
|
|
117
|
+
* 解析容器应当产出的属性对(顺序固定:`class` 在前)。
|
|
118
|
+
*
|
|
119
|
+
* @param options 渲染选项
|
|
120
|
+
* @returns 属性对列表;可能为空数组
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```ts
|
|
124
|
+
* resolveContainerAttrs({ className: '' }) // []
|
|
125
|
+
* resolveContainerAttrs({ theme: 'dark' }) // [['class', 'mt-content'], ['data-mt-theme', 'dark']]
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
declare const resolveContainerAttrs: (options: MtRenderOptions) => MtContainerAttr[];
|
|
129
|
+
/**
|
|
130
|
+
* 把容器属性序列化为 HTML 属性串。
|
|
131
|
+
*
|
|
132
|
+
* @param options 渲染选项
|
|
133
|
+
* @returns 形如 ` class="mt-content"` 的字符串;无属性时为空串
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```ts
|
|
137
|
+
* containerAttrsToString({}) // ' class="mt-content"'
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
declare const containerAttrsToString: (options: MtRenderOptions) => string;
|
|
141
|
+
/**
|
|
142
|
+
* 把容器属性写到真实元素上。
|
|
143
|
+
*
|
|
144
|
+
* @param element 目标元素
|
|
145
|
+
* @param options 渲染选项
|
|
146
|
+
*/
|
|
147
|
+
declare const applyContainerAttrs: (element: Element, options: MtRenderOptions) => void;
|
|
148
|
+
/**
|
|
149
|
+
* 用容器包裹内容 HTML。
|
|
150
|
+
*
|
|
151
|
+
* `renderToString`(字符串出口)与 `renderToDOM`(DOM 出口)共用本函数,
|
|
152
|
+
* 因此两者返回的 `html` **必然逐字符相同**——这是「SSR 产物与客户端产物一致」的保证。
|
|
153
|
+
*
|
|
154
|
+
* @param output 容器内部的 HTML
|
|
155
|
+
* @param options 渲染选项
|
|
156
|
+
* @returns 含容器的完整 HTML
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* ```ts
|
|
160
|
+
* wrapContainer('<p>hi</p>', {}) // '<div class="mt-content"><p>hi</p></div>'
|
|
161
|
+
* ```
|
|
162
|
+
*/
|
|
163
|
+
declare const wrapContainer: (output: string, options: MtRenderOptions) => string;
|
|
164
|
+
|
|
165
|
+
export { MT_CONTENT_CLASS, MT_THEME_ATTR, type MtDomRenderResult, type MtRenderOptions, type MtRenderResult, type MtTheme, applyContainerAttrs, containerAttrsToString, renderToDOM, renderToString, resolveContainerAttrs, wrapContainer };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { MtDegradationEntry, MtSerializeStats, MtPreRenderedIndex, MtDocument } from '@mteditor/document';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `@mteditor/renderer-web` 的公开选项与结果类型。
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/** 内容容器类名 —— 与 `@mteditor/theme-default` 的 `.mt-content` 约定一致 */
|
|
8
|
+
declare const MT_CONTENT_CLASS = "mt-content";
|
|
9
|
+
/** 主题属性名 —— 主题层只认这一个属性 */
|
|
10
|
+
declare const MT_THEME_ATTR = "data-mt-theme";
|
|
11
|
+
/** 主题取值;不开放任意字符串,避免写出主题层无法识别的属性值 */
|
|
12
|
+
type MtTheme = 'light' | 'dark';
|
|
13
|
+
/** 渲染选项;全部字段可选 */
|
|
14
|
+
interface MtRenderOptions {
|
|
15
|
+
/**
|
|
16
|
+
* 容器的**完整** `class` 值,缺省 `'mt-content'`。
|
|
17
|
+
*
|
|
18
|
+
* 需要追加自己的类名时请一并写出,例如 `'mt-content article-body'`;
|
|
19
|
+
* 传空字符串则完全不输出 `class` 属性(此时将失去主题排版样式)。
|
|
20
|
+
*/
|
|
21
|
+
className?: string;
|
|
22
|
+
/**
|
|
23
|
+
* 主题。**缺省不写 `data-mt-theme`**,跟随宿主(主题模型)。
|
|
24
|
+
*
|
|
25
|
+
* 只在需要**局部覆盖**时显式传入,例如邮件预览固定亮色。
|
|
26
|
+
*/
|
|
27
|
+
theme?: MtTheme;
|
|
28
|
+
/** 预渲染产物索引(公式 SVG / 代码高亮) */
|
|
29
|
+
preRendered?: MtPreRenderedIndex;
|
|
30
|
+
/** 缩进层级,缺省 `0`;仅影响 HTML 可读性,不参与语义 */
|
|
31
|
+
indent?: number;
|
|
32
|
+
/** 是否返回降级报告,缺省 `true`。**置 `false` 等于主动放弃降级信息** */
|
|
33
|
+
report?: boolean;
|
|
34
|
+
}
|
|
35
|
+
/** 渲染结果(`renderToString` 的返回值) */
|
|
36
|
+
interface MtRenderResult {
|
|
37
|
+
/** 完整 HTML,**含外层容器** */
|
|
38
|
+
html: string;
|
|
39
|
+
/** 降级报告;`report: false` 时为空数组 */
|
|
40
|
+
report: MtDegradationEntry[];
|
|
41
|
+
/** 序列化统计(**不含**外层容器,即 `document` 序列化器的原始统计) */
|
|
42
|
+
stats: MtSerializeStats;
|
|
43
|
+
}
|
|
44
|
+
/** DOM 渲染结果(`renderToDOM` 的返回值) */
|
|
45
|
+
interface MtDomRenderResult extends MtRenderResult {
|
|
46
|
+
/** 实际挂载进容器的元素 */
|
|
47
|
+
element: HTMLElement;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* `renderToString` —— 输出 HTML 字符串,**全程不触碰 DOM**(SSR / 邮件 / 静态页)。
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 把 `MtDocument` 渲染为 HTML 字符串。
|
|
56
|
+
*
|
|
57
|
+
* 产物 = `<div class="mt-content">{document 序列化产物}</div>`,
|
|
58
|
+
* 内容部分与 `@mteditor/document` 的 `serializeDocumentToHtml` **逐字符一致**,
|
|
59
|
+
* 因此不存在「详情页与编辑器渲染不一致」的可能。
|
|
60
|
+
*
|
|
61
|
+
* 本函数不使用 `window` / `document` / `innerHTML`,可在 Node、边缘函数、
|
|
62
|
+
* 小程序服务端等任何无 DOM 环境执行。
|
|
63
|
+
*
|
|
64
|
+
* @param doc 文档
|
|
65
|
+
* @param options 渲染选项
|
|
66
|
+
* @returns 渲染结果(`html` / `report` / `stats`)
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```ts
|
|
70
|
+
* const { html, report } = renderToString(doc)
|
|
71
|
+
* // 需要局部固定暗色时:
|
|
72
|
+
* renderToString(doc, { theme: 'dark', className: 'mt-content article-body' }).html
|
|
73
|
+
* // 只要字符串:
|
|
74
|
+
* const str = renderToString(doc).html
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
declare const renderToString: (doc: MtDocument, options?: MtRenderOptions) => MtRenderResult;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* `renderToDOM` —— 把 `MtDocument` 渲染为真实 DOM 节点并挂载进容器。
|
|
81
|
+
*
|
|
82
|
+
* 与 `renderToString` 的关系:**同一份内容、同一套容器属性**,
|
|
83
|
+
* 因此 `renderToDOM(doc, el).html === renderToString(doc).html` 恒成立。
|
|
84
|
+
*/
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 把 `MtDocument` 渲染为 DOM 并挂载到 `container`。
|
|
88
|
+
*
|
|
89
|
+
* **挂载语义**:调用后 `container` 的子节点被**整体替换**为渲染出的容器元素
|
|
90
|
+
* (即重复调用是幂等的,不会累积)。`container` 自身的属性不被修改——
|
|
91
|
+
* 主题由宿主控制,需要局部覆盖时请用 `options.theme`。
|
|
92
|
+
*
|
|
93
|
+
* @param doc 文档
|
|
94
|
+
* @param container 挂载容器(普通文档流中的元素,不得位于 `position: fixed` 容器内)
|
|
95
|
+
* @param options 渲染选项
|
|
96
|
+
* @returns 渲染结果 + 实际挂载的元素
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* const { element, report } = renderToDOM(doc, document.getElementById('view')!)
|
|
101
|
+
* element.dataset.rendered // 可通过返回的元素继续做懒加载等增强
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
declare const renderToDOM: (doc: MtDocument, container: HTMLElement, options?: MtRenderOptions) => MtDomRenderResult;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* 渲染容器的属性装配。
|
|
108
|
+
*
|
|
109
|
+
* 容器属性有**两条出口**:字符串出口(`renderToString`)与真实元素出口(`renderToDOM`)。
|
|
110
|
+
* 两者必须产出同一组属性,因此都从本模块的 `resolveContainerAttrs` 取值——
|
|
111
|
+
* 这是「SSR 字符串与客户端 DOM 一致」的实现基础。
|
|
112
|
+
*/
|
|
113
|
+
|
|
114
|
+
/** 属性对:`[属性名, 属性值]`,值必为字符串(无「缺省」概念,缺省即不产出该对) */
|
|
115
|
+
type MtContainerAttr = readonly [name: string, value: string];
|
|
116
|
+
/**
|
|
117
|
+
* 解析容器应当产出的属性对(顺序固定:`class` 在前)。
|
|
118
|
+
*
|
|
119
|
+
* @param options 渲染选项
|
|
120
|
+
* @returns 属性对列表;可能为空数组
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```ts
|
|
124
|
+
* resolveContainerAttrs({ className: '' }) // []
|
|
125
|
+
* resolveContainerAttrs({ theme: 'dark' }) // [['class', 'mt-content'], ['data-mt-theme', 'dark']]
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
declare const resolveContainerAttrs: (options: MtRenderOptions) => MtContainerAttr[];
|
|
129
|
+
/**
|
|
130
|
+
* 把容器属性序列化为 HTML 属性串。
|
|
131
|
+
*
|
|
132
|
+
* @param options 渲染选项
|
|
133
|
+
* @returns 形如 ` class="mt-content"` 的字符串;无属性时为空串
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```ts
|
|
137
|
+
* containerAttrsToString({}) // ' class="mt-content"'
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
declare const containerAttrsToString: (options: MtRenderOptions) => string;
|
|
141
|
+
/**
|
|
142
|
+
* 把容器属性写到真实元素上。
|
|
143
|
+
*
|
|
144
|
+
* @param element 目标元素
|
|
145
|
+
* @param options 渲染选项
|
|
146
|
+
*/
|
|
147
|
+
declare const applyContainerAttrs: (element: Element, options: MtRenderOptions) => void;
|
|
148
|
+
/**
|
|
149
|
+
* 用容器包裹内容 HTML。
|
|
150
|
+
*
|
|
151
|
+
* `renderToString`(字符串出口)与 `renderToDOM`(DOM 出口)共用本函数,
|
|
152
|
+
* 因此两者返回的 `html` **必然逐字符相同**——这是「SSR 产物与客户端产物一致」的保证。
|
|
153
|
+
*
|
|
154
|
+
* @param output 容器内部的 HTML
|
|
155
|
+
* @param options 渲染选项
|
|
156
|
+
* @returns 含容器的完整 HTML
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* ```ts
|
|
160
|
+
* wrapContainer('<p>hi</p>', {}) // '<div class="mt-content"><p>hi</p></div>'
|
|
161
|
+
* ```
|
|
162
|
+
*/
|
|
163
|
+
declare const wrapContainer: (output: string, options: MtRenderOptions) => string;
|
|
164
|
+
|
|
165
|
+
export { MT_CONTENT_CLASS, MT_THEME_ATTR, type MtDomRenderResult, type MtRenderOptions, type MtRenderResult, type MtTheme, applyContainerAttrs, containerAttrsToString, renderToDOM, renderToString, resolveContainerAttrs, wrapContainer };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
"use strict";var MtRendererWeb=(()=>{var C=Object.defineProperty;var U=Object.getOwnPropertyDescriptor;var G=Object.getOwnPropertyNames;var j=Object.prototype.hasOwnProperty;var q=(t,r)=>{for(var e in r)C(t,e,{get:r[e],enumerable:!0})},z=(t,r,e,n)=>{if(r&&typeof r=="object"||typeof r=="function")for(let a of G(r))!j.call(t,a)&&a!==e&&C(t,a,{get:()=>r[a],enumerable:!(n=U(r,a))||n.enumerable});return t};var H=t=>z(C({},"__esModule",{value:!0}),t);var Dt={};q(Dt,{MT_CONTENT_CLASS:()=>E,MT_THEME_ATTR:()=>A,applyContainerAttrs:()=>k,containerAttrsToString:()=>b,renderToDOM:()=>x,renderToString:()=>P,resolveContainerAttrs:()=>D,wrapContainer:()=>p});var E="mt-content",A="data-mt-theme";var W="blk_",K="0123456789abcdefghijklmnopqrstuvwxyz",V=(t={})=>{let r=t.now??Date.now(),e=t.random??Math.random,n=Math.max(2,t.suffixLength??4),a=Math.max(0,Math.floor(r)).toString(36),s=new Set;return()=>{for(let u=0;u<128;u+=1){let d="";for(let h=0;h<n;h+=1)d+=K.charAt(Math.floor(e()*36));let c=`${W}${a}_${d}`;if(!s.has(c))return s.add(c),c}throw new Error("\u8282\u70B9 ID \u751F\u6210\u5931\u8D25\uFF1A\u968F\u673A\u540E\u7F00\u8FDE\u7EED\u51B2\u7A81\uFF0C\u8BF7\u68C0\u67E5\u6CE8\u5165\u7684 random \u5B9E\u73B0")}};var Ct=V();var M=["textAlign","lineHeight"],S=["colspan","rowspan","colwidth","align","background",...M],N=[{name:"doc",group:[],content:"block+",isAtom:!1,needsId:!1,requiredAttrs:[],allowedAttrs:[],allowsMarks:!1},{name:"paragraph",group:["block"],content:"inline*",isAtom:!1,needsId:!0,requiredAttrs:[],allowedAttrs:["id","indent",...M],allowsMarks:!0},{name:"heading",group:["block"],content:"inline*",isAtom:!1,needsId:!0,requiredAttrs:["level"],allowedAttrs:["id","level",...M],allowsMarks:!0},{name:"blockquote",group:["block"],content:"block+",isAtom:!1,needsId:!0,requiredAttrs:[],allowedAttrs:["id"],allowsMarks:!1},{name:"bulletList",group:["block"],content:"listItem+",isAtom:!1,needsId:!0,requiredAttrs:[],allowedAttrs:["id"],allowsMarks:!1},{name:"orderedList",group:["block"],content:"listItem+",isAtom:!1,needsId:!0,requiredAttrs:[],allowedAttrs:["id","start","type"],allowsMarks:!1},{name:"listItem",group:["listItem"],content:"paragraph block*",isAtom:!1,needsId:!0,requiredAttrs:[],allowedAttrs:["id"],allowsMarks:!1},{name:"taskList",group:["block"],content:"taskItem+",isAtom:!1,needsId:!0,requiredAttrs:[],allowedAttrs:["id"],allowsMarks:!1},{name:"taskItem",group:["listItem"],content:"paragraph block*",isAtom:!1,needsId:!0,requiredAttrs:[],allowedAttrs:["id","checked"],allowsMarks:!1},{name:"codeBlock",group:["block"],content:"text*",isAtom:!1,needsId:!0,requiredAttrs:[],allowedAttrs:["id","language","showLineNumbers"],allowsMarks:!1},{name:"horizontalRule",group:["block"],content:null,isAtom:!0,needsId:!0,requiredAttrs:[],allowedAttrs:["id"],allowsMarks:!1},{name:"image",group:["block"],content:null,isAtom:!0,needsId:!0,requiredAttrs:["src"],allowedAttrs:["id","src","alt","title","width","height","align"],allowsMarks:!1},{name:"video",group:["block"],content:null,isAtom:!0,needsId:!0,requiredAttrs:["src"],allowedAttrs:["id","src","poster","width","height","align"],allowsMarks:!1},{name:"audio",group:["block"],content:null,isAtom:!0,needsId:!0,requiredAttrs:["src"],allowedAttrs:["id","src","title"],allowsMarks:!1},{name:"attachment",group:["block"],content:null,isAtom:!0,needsId:!0,requiredAttrs:["src","name"],allowedAttrs:["id","src","name","size","mime"],allowsMarks:!1},{name:"mathBlock",group:["block"],content:null,isAtom:!0,needsId:!0,requiredAttrs:["tex"],allowedAttrs:["id","tex","svg","numbering"],allowsMarks:!1},{name:"table",group:["block"],content:"tableRow+",isAtom:!1,needsId:!0,requiredAttrs:[],allowedAttrs:["id","colWidths"],allowsMarks:!1},{name:"tableRow",group:[],content:"(tableCell | tableHeader)+",isAtom:!1,needsId:!1,requiredAttrs:[],allowedAttrs:[],allowsMarks:!1},{name:"tableCell",group:["tableCell"],content:"block+",isAtom:!1,needsId:!1,requiredAttrs:[],allowedAttrs:[...S],allowsMarks:!0},{name:"tableHeader",group:["tableCell"],content:"block+",isAtom:!1,needsId:!1,requiredAttrs:[],allowedAttrs:[...S],allowsMarks:!0},{name:"text",group:["inline"],content:null,isAtom:!0,needsId:!1,requiredAttrs:["text"],allowedAttrs:[],allowsMarks:!0},{name:"hardBreak",group:["inline"],content:null,isAtom:!0,needsId:!1,requiredAttrs:[],allowedAttrs:[],allowsMarks:!1},{name:"mention",group:["inline"],content:null,isAtom:!0,needsId:!1,requiredAttrs:["id","label"],allowedAttrs:["id","label","kind"],allowsMarks:!1},{name:"inlineMath",group:["inline"],content:null,isAtom:!0,needsId:!1,requiredAttrs:["tex"],allowedAttrs:["tex","svg"],allowsMarks:!1},{name:"emoji",group:["inline"],content:null,isAtom:!0,needsId:!1,requiredAttrs:["name","unicode"],allowedAttrs:["name","unicode"],allowsMarks:!1}],Mt=new Map(N.map(t=>[t.name,t])),F=t=>N.filter(r=>r.group.includes(t)).map(r=>r.name),wt={block:F("block"),inline:F("inline"),listItem:F("listItem"),tableCell:F("tableCell")};var X=["fontFamily","fontSize","color","backgroundColor","lineHeight","letterSpacing"],Y=[{name:"link",priority:1e3,requiredAttrs:["href"],allowedAttrs:["href","target","rel","title"],exclusive:["link"],singleton:!0},{name:"code",priority:900,requiredAttrs:[],allowedAttrs:[],exclusive:["textStyle","highlight"],singleton:!0},{name:"textStyle",priority:800,requiredAttrs:[],allowedAttrs:[...X],exclusive:[],singleton:!0},{name:"bold",priority:700,requiredAttrs:[],allowedAttrs:[],exclusive:[],singleton:!0},{name:"italic",priority:650,requiredAttrs:[],allowedAttrs:[],exclusive:[],singleton:!0},{name:"underline",priority:600,requiredAttrs:[],allowedAttrs:[],exclusive:[],singleton:!0},{name:"strike",priority:550,requiredAttrs:[],allowedAttrs:[],exclusive:[],singleton:!0},{name:"highlight",priority:500,requiredAttrs:["color"],allowedAttrs:["color"],exclusive:["code"],singleton:!0},{name:"subscript",priority:400,requiredAttrs:[],allowedAttrs:[],exclusive:["superscript"],singleton:!0},{name:"superscript",priority:350,requiredAttrs:[],allowedAttrs:[],exclusive:["subscript"],singleton:!0}],Tt=new Map(Y.map(t=>[t.name,t])),J="_blank",Z="noopener noreferrer";var o={TAG_NOT_ALLOWED:"MT_DEG_001",UNSUPPORTED_NODE:"MT_DEG_002",ATTR_DROPPED:"MT_DEG_003",LOSSY_CONVERSION:"MT_DEG_004",MISSING_PRERENDER:"MT_DEG_005",MARK_CONFLICT:"MT_DEG_006",DANGEROUS_URL:"MT_DEG_007",EMOJI_UNSUPPORTED:"MT_DEG_008",UNKNOWN_MARK:"MT_DEG_009",MARK_NOT_ALLOWED:"MT_DEG_010",EMPTY_CONTAINER:"MT_DEG_011"},l={UNKNOWN_NODE:"MT_DOC_001",MISSING_ATTR:"MT_DOC_002",CONTENT_MISMATCH:"MT_DOC_003",UNKNOWN_MARK:"MT_DOC_004",MARK_CONFLICT:"MT_DOC_005",BAD_URL:"MT_DOC_006",BAD_TABLE:"MT_DOC_007",UNSUPPORTED_VERSION:"MT_DOC_008",DUPLICATE_ID:"MT_DOC_009",MARKS_IN_CODE_BLOCK:"MT_DOC_010"},Q={[o.TAG_NOT_ALLOWED]:"\u6807\u7B7E\u4E0D\u5728\u767D\u540D\u5355\uFF0C\u88AB\u89E3\u5305\u6216\u4E22\u5F03",[o.UNSUPPORTED_NODE]:"\u8282\u70B9\u5728\u76EE\u6807\u7AEF\u65E0\u6CD5\u8868\u8FBE",[o.ATTR_DROPPED]:"\u5C5E\u6027\u88AB\u4E22\u5F03",[o.LOSSY_CONVERSION]:"\u683C\u5F0F\u8F6C\u6362\u6709\u635F",[o.MISSING_PRERENDER]:"\u7F3A\u5C11\u9884\u6E32\u67D3\u4EA7\u7269",[o.MARK_CONFLICT]:"\u51B2\u7A81\u6807\u8BB0\u88AB\u5F52\u4E00\u5316",[o.DANGEROUS_URL]:"\u5371\u9669 URL \u534F\u8BAE\u88AB\u7F6E\u7A7A",[o.EMOJI_UNSUPPORTED]:"emoji \u5728\u5F53\u524D\u8FD0\u884C\u65F6\u65E0\u6CD5\u6B63\u786E\u62FC\u5408",[o.UNKNOWN_MARK]:"\u672A\u77E5\u6807\u8BB0\u7C7B\u578B\u88AB\u4E22\u5F03",[o.MARK_NOT_ALLOWED]:"\u8BE5\u4F4D\u7F6E\u4E0D\u5141\u8BB8\u6807\u8BB0\uFF0C\u6807\u8BB0\u88AB\u6E05\u9664",[o.EMPTY_CONTAINER]:"\u7A7A\u5BB9\u5668\u8282\u70B9\u65E0\u5185\u5BB9\uFF0C\u5DF2\u8865\u5360\u4F4D\u6216\u4E22\u5F03"},bt={[l.UNKNOWN_NODE]:"\u672A\u77E5\u8282\u70B9\u7C7B\u578B",[l.MISSING_ATTR]:"\u7F3A\u5C11\u5FC5\u586B\u5C5E\u6027",[l.CONTENT_MISMATCH]:"content \u4E0D\u5339\u914D\u5185\u5BB9\u8868\u8FBE\u5F0F",[l.UNKNOWN_MARK]:"\u672A\u77E5 mark \u7C7B\u578B",[l.MARK_CONFLICT]:"mark \u4E92\u65A5\u51B2\u7A81",[l.BAD_URL]:"\u975E\u6CD5 URL \u534F\u8BAE",[l.BAD_TABLE]:"\u8868\u683C\u7ED3\u6784\u975E\u6CD5",[l.UNSUPPORTED_VERSION]:"\u4E0D\u652F\u6301\u7684 schemaVersion",[l.DUPLICATE_ID]:"\u8282\u70B9 ID \u91CD\u590D",[l.MARKS_IN_CODE_BLOCK]:"codeBlock \u5185\u51FA\u73B0 mark"},w=(t,r,e,n,a)=>({code:t,type:r,path:e,message:`${Q[t]??t}\uFF1A${n}`,strategy:a});var tt=new Map([["&","&"],["<","<"],[">",">"]]),et=new Map([["&","&"],["<","<"],[">",">"],['"',"""]]),I=(t,r)=>{let e="",n=!1;for(let a=0;a<t.length;a+=1){let s=t.charAt(a),u=r.get(s);u===void 0?e+=s:(e+=u,n=!0)}return n?e:t},m=t=>I(t,tt),T=t=>I(t,et);var rt=["address","article","aside","blockquote","details","div","dl","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","hr","main","menu","nav","ol","p","pre","section","table","ul"],g=new Set(["caption","colgroup","thead","tbody","tfoot","tr","td","th"]),_t=new Map([...rt.map(t=>[t,new Set(["p"])]),["li",new Set(["li"])],["dt",new Set(["dt","dd"])],["dd",new Set(["dt","dd"])],["option",new Set(["option","optgroup"])],["optgroup",new Set(["optgroup"])],["caption",g],["colgroup",g],["thead",g],["tbody",g],["tfoot",g],["tr",new Set(["tr","td","th"])],["td",new Set(["td","th"])],["th",new Set(["td","th"])]]);var nt="em";var at="px";var O=(t,r)=>`${t}${r}`;var st=2166136261,it=16777619,ot=t=>{let r=st;for(let e=0;e<t.length;e+=1)r^=t.charCodeAt(e),r=Math.imul(r,it)>>>0;return r.toString(16).padStart(8,"0")},ut="content",lt=(t,r)=>`${t}[${r}]`,ct=(t,r,e=ut)=>{let n=(a,s,u,d)=>{for(let c=0;c<a.length;c+=1){let h=a[c];if(h===void 0)continue;let _=lt(d,c);r({node:h,path:_,depth:u,parent:s,index:c});let $=h.content;Array.isArray($)&&n($,h,u+1,`${_}.content`)}};n(t,void 0,0,e)},dt=t=>{let r=0,e=0;return ct(t,({depth:n})=>{r+=1,n+1>e&&(e=n+1)}),{nodeCount:r,maxDepth:e}},ht=t=>{let r=0;for(let e=0;e<t.length;e+=1){let n=t.charCodeAt(e);n<128?r+=1:n<2048?r+=2:n>=55296&&n<=56319?(r+=4,e+=1):r+=3}return r};var i=t=>{let r="";for(let[e,n]of t)n!=null&&(r+=` ${e}="${T(n)}"`);return r},v=t=>{let r=[];for(let[e,n]of t)n!=null&&r.push(`${e}: ${n}`);return r.length>0?r.join("; "):void 0},ft=new Map([["bold","strong"],["italic","em"],["underline","u"],["strike","s"],["code","code"],["subscript","sub"],["superscript","sup"]]),B=(t,r)=>{switch(t.type){case"link":{let e=i([["href",t.attrs.href],["target",t.attrs.target??J],["rel",t.attrs.rel??Z],["title",t.attrs.title]]);return r?`<a${e}>`:"</a>"}case"textStyle":{let e=v([["font-family",t.attrs.fontFamily??void 0],["font-size",t.attrs.fontSize??void 0],["color",t.attrs.color??void 0],["background-color",t.attrs.backgroundColor??void 0],["line-height",t.attrs.lineHeight??void 0],["letter-spacing",t.attrs.letterSpacing??void 0]]);return r?`<span${i([["style",e]])}>`:"</span>"}case"highlight":return r?`<mark${i([["style",v([["background-color",t.attrs.color]])]])}>`:"</mark>";default:{let e=ft.get(t.type)??"span";return r?`<${e}>`:`</${e}>`}}},pt=(t,r)=>{if(!r||r.length===0)return t;let e=t;for(let n=r.length-1;n>=0;n-=1){let a=r[n];a!==void 0&&(e=`${B(a,!0)}${e}${B(a,!1)}`)}return e},gt=(t,r)=>{switch(t.type){case"text":return pt(m(t.text),t.marks);case"hardBreak":return"<br>";case"mention":{let e=t.attrs.kind;return`<span${i([["data-mt-mention",""],["data-mt-id",t.attrs.id],["data-mt-kind",e===void 0?void 0:e]])}>${m(t.attrs.label)}</span>`}case"inlineMath":{let e=t.attrs.svg??r.preRendered.math?.[t.attrs.tex];return(e==null||e.length===0)&&r.degradations.push(w(o.MISSING_PRERENDER,"inlineMath","inline",`\u884C\u5185\u516C\u5F0F "${t.attrs.tex}" \u7F3A\u5C11\u9884\u6E32\u67D3 SVG`,"replaced")),`<span${i([["data-mt-math-inline",""],["data-tex",t.attrs.tex]])}>${e??""}</span>`}case"emoji":return`<span${i([["data-mt-emoji",""],["data-mt-name",t.attrs.name]])}>${m(t.attrs.unicode)}</span>`;default:return""}},R=(t,r)=>{if(!t)return"";let e="";for(let n of t)e+=gt(n,r);return e},mt=t=>{let r=t[0];return r!==void 0&&r.type==="text"?r.text:""},vt=(t,r)=>{let e="";for(let n of t.content??[]){let a=n.type==="tableHeader"?"th":"td",s=n.attrs,u=v([["text-align",s?.textAlign??void 0],["line-height",s?.lineHeight===void 0||s.lineHeight===null?void 0:String(s.lineHeight)],["background-color",s?.background??void 0],["width",s?.colwidth===void 0||s.colwidth===null?void 0:O(s.colwidth,at)]]),d=i([["colspan",s?.colspan===void 0||s.colspan===null?void 0:String(s.colspan)],["rowspan",s?.rowspan===void 0||s.rowspan===null?void 0:String(s.rowspan)],["data-mt-align",s?.align??void 0],["style",u]]);e+=`<${a}${d}>${f(n.content??[],r)}</${a}>`}return`<tr>${e}</tr>`},Et=(t,r)=>{switch(t.type){case"horizontalRule":return`<hr${i([["data-mt-id",t.attrs.id]])}>`;case"image":return`<img${i([["data-mt-id",t.attrs.id],["src",t.attrs.src],["alt",t.attrs.alt??void 0],["title",t.attrs.title??void 0],["width",t.attrs.width===void 0||t.attrs.width===null?void 0:String(t.attrs.width)],["height",t.attrs.height===void 0||t.attrs.height===null?void 0:String(t.attrs.height)],["data-mt-align",t.attrs.align??void 0]])}>`;case"video":return`<video${i([["data-mt-id",t.attrs.id],["src",t.attrs.src],["poster",t.attrs.poster??void 0],["width",t.attrs.width===void 0||t.attrs.width===null?void 0:String(t.attrs.width)],["height",t.attrs.height===void 0||t.attrs.height===null?void 0:String(t.attrs.height)],["data-mt-align",t.attrs.align??void 0],["controls",""]])}></video>`;case"audio":return`<audio${i([["data-mt-id",t.attrs.id],["src",t.attrs.src],["title",t.attrs.title??void 0],["controls",""]])}></audio>`;case"attachment":return`<a${i([["data-mt-attachment",""],["data-mt-id",t.attrs.id],["href",t.attrs.src],["data-mt-name",t.attrs.name],["data-mt-size",t.attrs.size===void 0||t.attrs.size===null?void 0:String(t.attrs.size)],["data-mt-mime",t.attrs.mime??void 0]])}>${m(t.attrs.name)}</a>`;case"mathBlock":{let e=t.attrs.svg??r.preRendered.math?.[t.attrs.tex];return(e==null||e.length===0)&&r.degradations.push(w(o.MISSING_PRERENDER,"mathBlock","block",`\u516C\u5F0F "${t.attrs.tex}" \u7F3A\u5C11\u9884\u6E32\u67D3 SVG`,"replaced")),`<div${i([["data-mt-math",""],["data-mt-id",t.attrs.id],["data-tex",t.attrs.tex],["data-mt-numbering",t.attrs.numbering??void 0]])}>${e??""}</div>`}default:return""}},At=(t,r)=>{let e=mt(t.content??[]),n=t.attrs.language,a=r.preRendered.code?.[ot(e)]??r.preRendered.code?.[e];a===void 0&&e.length>0&&n!==void 0&&n!==null&&n.length>0&&r.degradations.push(w(o.MISSING_PRERENDER,"codeBlock","block",`\u4EE3\u7801\u5757 "${n??""}" \u7F3A\u5C11\u9884\u6E32\u67D3\u9AD8\u4EAE\u4EA7\u7269`,"literalized"));let s=i([["data-mt-id",t.attrs.id],["data-mt-language",n==null||n.length===0?void 0:n],["data-mt-line-numbers",t.attrs.showLineNumbers===void 0||t.attrs.showLineNumbers===null?void 0:String(t.attrs.showLineNumbers)]]),u=i([["class",n==null||n.length===0?void 0:`language-${n}`]]),d=a??m(e);return`<pre${s}><code${u}>${d}</code></pre>`},Ft=(t,r)=>{switch(t.type){case"paragraph":{let e=t.attrs,n=v([["text-align",e.textAlign??void 0],["line-height",e.lineHeight===void 0||e.lineHeight===null?void 0:String(e.lineHeight)],["padding-left",e.indent===void 0||e.indent===null?void 0:O(e.indent,nt)]]);return`<p${i([["data-mt-id",e.id],["style",n]])}>${R(t.content,r)}</p>`}case"heading":{let e=t.attrs,n=v([["text-align",e.textAlign??void 0],["line-height",e.lineHeight===void 0||e.lineHeight===null?void 0:String(e.lineHeight)]]),a=i([["data-mt-id",e.id],["style",n]]);return`<h${e.level}${a}>${R(t.content,r)}</h${e.level}>`}case"blockquote":return`<blockquote${i([["data-mt-id",t.attrs.id]])}>${f(t.content??[],r)}</blockquote>`;case"bulletList":{let e="";for(let n of t.content??[])e+=`<li${i([["data-mt-id",n.attrs.id]])}>${f(n.content??[],r)}</li>`;return`<ul${i([["data-mt-id",t.attrs.id]])}>${e}</ul>`}case"orderedList":{let e="";for(let a of t.content??[])e+=`<li${i([["data-mt-id",a.attrs.id]])}>${f(a.content??[],r)}</li>`;return`<ol${i([["data-mt-id",t.attrs.id],["start",t.attrs.start===void 0||t.attrs.start===null?void 0:String(t.attrs.start)],["type",t.attrs.type??void 0]])}>${e}</ol>`}case"taskList":{let e="";for(let n of t.content??[]){let a=n.attrs.checked===void 0||n.attrs.checked===null?void 0:String(n.attrs.checked);e+=`<li${i([["data-mt-id",n.attrs.id],["data-mt-checked",a]])}>${f(n.content??[],r)}</li>`}return`<ul${i([["data-mt-task-list",""],["data-mt-id",t.attrs.id]])}>${e}</ul>`}case"codeBlock":return At(t,r);case"table":{let e=t.attrs.colWidths,n=e&&e.length>0?`<colgroup>${e.map(s=>`<col${i([["width",String(s)]])}>`).join("")}</colgroup>`:"",a=(t.content??[]).map(s=>vt(s,r)).join("");return`<table${i([["data-mt-id",t.attrs.id]])}>${n}<tbody>${a}</tbody></table>`}default:return Et(t,r)}};function f(t,r){let e=r.pretty?`
|
|
2
|
+
`:"",n=[];for(let a of t)n.push(Ft(a,r));return n.join(e)}var L=(t,r={})=>{let e={degradations:[],preRendered:r.preRendered??{},pretty:(r.indent??0)>0},n=f(t.content,e),{nodeCount:a,maxDepth:s}=dt(t.content);return{output:n,report:e.degradations,stats:{nodeCount:a,maxDepth:s,bytes:ht(n)}}};var D=t=>{let r=[],e=t.className??E;return e.length>0&&r.push(["class",e]),t.theme!==void 0&&r.push([A,t.theme]),r},b=t=>D(t).map(([r,e])=>` ${r}="${T(e)}"`).join(""),k=(t,r)=>{for(let[e,n]of D(r))t.setAttribute(e,n)},p=(t,r)=>`<div${b(r)}>${t}</div>`;var y=(t,r)=>{let e=L(t,{preRendered:r.preRendered,indent:r.indent});return{output:e.output,report:r.report===!1?[]:e.report,stats:e.stats}};var P=(t,r={})=>{let e=y(t,r);return{html:p(e.output,r),report:e.report,stats:e.stats}};var x=(t,r,e={})=>{let n=y(t,e),a=r.ownerDocument.createElement("div");return k(a,e),a.innerHTML=n.output,r.replaceChildren(a),{html:p(n.output,e),report:n.report,stats:n.stats,element:a}};return H(Dt);})();
|
|
3
|
+
//# sourceMappingURL=index.global.js.map
|