rich-text-editor-renderer 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 +44 -0
- package/dist/index.cjs +592 -0
- package/dist/index.cjs.map +7 -0
- package/dist/index.global.js +594 -0
- package/dist/index.global.js.map +7 -0
- package/dist/index.js +572 -0
- package/dist/index.js.map +7 -0
- package/package.json +34 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.js"],
|
|
4
|
+
"sourcesContent": ["const DEFAULT_OPTIONS = {\n mathMode: 'tex',\n className: 'rte-render-root',\n injectStyles: true,\n autoMathJaxTypeset: true,\n};\n\nconst STYLE_ID = 'rich-text-editor-renderer-styles';\nconst MIN_IMAGE_WIDTH = 32;\nconst MAX_IMAGE_WIDTH = 720;\nconst MIN_IMAGE_HEIGHT = 28;\nconst MAX_IMAGE_HEIGHT = 720;\n\nexport function renderRichTextDocument({\n element,\n document,\n options = {},\n}) {\n if (!element) {\n throw new Error('renderRichTextDocument requires an element');\n }\n\n const resolvedOptions = { ...DEFAULT_OPTIONS, ...options };\n const documentModel = normalizeDocument(document);\n\n if (resolvedOptions.injectStyles) {\n ensureStyles();\n }\n\n element.innerHTML = '';\n element.classList.add(resolvedOptions.className);\n\n const flowRoot = document.createElement('div');\n flowRoot.className = 'rte-flow-root';\n const floatingRoot = document.createElement('div');\n floatingRoot.className = 'rte-floating-root';\n\n element.appendChild(flowRoot);\n element.appendChild(floatingRoot);\n\n renderStaticNodes(flowRoot, documentModel);\n rerenderLayout({ host: element, flowRoot, floatingRoot, documentModel });\n\n if (resolvedOptions.autoMathJaxTypeset) {\n requestMathTypeset(element);\n }\n\n return {\n rerender() {\n rerenderLayout({ host: element, flowRoot, floatingRoot, documentModel });\n if (resolvedOptions.autoMathJaxTypeset) {\n requestMathTypeset(element);\n }\n },\n destroy() {\n element.innerHTML = '';\n element.classList.remove(resolvedOptions.className);\n },\n };\n}\n\nexport function autoMountRichTextDocuments({\n selector = '[data-rich-text-json]',\n options = {},\n} = {}) {\n const mounts = [];\n document.querySelectorAll(selector).forEach((element) => {\n const raw = element.getAttribute('data-rich-text-json');\n if (!raw) {\n return;\n }\n mounts.push(\n renderRichTextDocument({\n element,\n document: JSON.parse(raw),\n options,\n }),\n );\n });\n return mounts;\n}\n\nfunction normalizeDocument(value) {\n if (typeof value === 'string') {\n return JSON.parse(value);\n }\n return value;\n}\n\nfunction ensureStyles() {\n if (document.getElementById(STYLE_ID)) {\n return;\n }\n const style = document.createElement('style');\n style.id = STYLE_ID;\n style.textContent = `\n .rte-render-root {\n position: relative;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n color: #1f2937;\n line-height: 1.6;\n word-break: break-word;\n }\n .rte-flow-root {\n position: relative;\n z-index: 1;\n }\n .rte-floating-root {\n position: absolute;\n inset: 0;\n pointer-events: none;\n z-index: 2;\n }\n .rte-node {\n margin-bottom: 12px;\n }\n .rte-paragraph,\n .rte-list-item {\n font-size: 16px;\n line-height: 1.4;\n font-weight: 400;\n }\n .rte-heading1 {\n font-size: 30px;\n line-height: 1.25;\n font-weight: 700;\n }\n .rte-heading2 {\n font-size: 22px;\n line-height: 1.3;\n font-weight: 600;\n }\n .rte-list {\n margin: 0 0 12px 0;\n padding-left: 24px;\n }\n .rte-line {\n display: flex;\n align-items: flex-start;\n min-height: 1em;\n }\n .rte-line-block {\n display: flex;\n align-items: flex-start;\n }\n .rte-token {\n white-space: pre;\n }\n .rte-token.bold {\n font-weight: 700;\n }\n .rte-token.italic {\n font-style: italic;\n }\n .rte-token.underline {\n text-decoration: underline;\n }\n .rte-token.link {\n color: #0a66c2;\n text-decoration: underline;\n }\n .rte-block-image,\n .rte-floating-image {\n overflow: hidden;\n border-radius: 12px;\n background: #e5e7eb;\n box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);\n }\n .rte-block-image img,\n .rte-floating-image img {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n .rte-floating-image {\n position: absolute;\n }\n .rte-wrap-block {\n display: flex;\n gap: 16px;\n align-items: flex-start;\n }\n .rte-wrap-block.right {\n flex-direction: row-reverse;\n }\n .rte-wrap-text {\n flex: 1;\n min-width: 0;\n }\n `;\n document.head.appendChild(style);\n}\n\nfunction renderStaticNodes(flowRoot, documentModel) {\n flowRoot.innerHTML = '';\n for (const node of documentModel.nodes || []) {\n switch (node.type) {\n case 'textBlock':\n renderStaticTextNode(flowRoot, node);\n break;\n case 'list':\n renderStaticListNode(flowRoot, node);\n break;\n case 'math':\n renderStaticMathNode(flowRoot, node);\n break;\n case 'image':\n renderStaticImageNode(flowRoot, node);\n break;\n default:\n break;\n }\n }\n}\n\nfunction renderStaticTextNode(flowRoot, node) {\n const wrapper = document.createElement('div');\n wrapper.className = 'rte-node';\n wrapper.dataset.nodeId = node.id;\n wrapper.dataset.nodeType = node.type;\n flowRoot.appendChild(wrapper);\n}\n\nfunction renderStaticListNode(flowRoot, node) {\n const list = document.createElement(node.style === 'ordered' ? 'ol' : 'ul');\n list.className = 'rte-node rte-list';\n list.dataset.nodeId = node.id;\n list.dataset.nodeType = node.type;\n (node.items || []).forEach((_, index) => {\n const item = document.createElement('li');\n item.className = 'rte-list-item';\n item.dataset.nodeId = `${node.id}::${index}`;\n item.dataset.parentNodeId = node.id;\n item.dataset.nodeType = 'list-item';\n list.appendChild(item);\n });\n flowRoot.appendChild(list);\n}\n\nfunction renderStaticMathNode(flowRoot, node) {\n const wrapper = document.createElement('div');\n wrapper.className = 'rte-node';\n wrapper.dataset.nodeId = node.id;\n wrapper.dataset.nodeType = node.type;\n wrapper.innerHTML = node.displayMode === 'inline'\n ? `<span data-node=\"math-inline\" data-latex=\"${escapeHtml(node.latex)}\">\\\\(${escapeHtml(node.latex)}\\\\)</span>`\n : `<div data-node=\"math-block\" data-latex=\"${escapeHtml(node.latex)}\">\\\\[${escapeHtml(node.latex)}\\\\]</div>`;\n flowRoot.appendChild(wrapper);\n}\n\nfunction renderStaticImageNode(flowRoot, node) {\n if (node.layoutMode === 'floating') {\n return;\n }\n\n const wrapper = document.createElement('div');\n wrapper.className = 'rte-node';\n wrapper.dataset.nodeId = node.id;\n wrapper.dataset.nodeType = node.type;\n\n const width = node.width ? ` style=\"width:${node.width}px\"` : '';\n const imageHtml = `<div class=\"rte-block-image\"${width}><img src=\"${escapeHtml(node.url)}\" alt=\"${escapeHtml(node.altText || '')}\"></div>`;\n\n if (node.wrapAlignment && node.wrapAlignment !== 'none' && hasWrapSegments(node.wrapSegments)) {\n wrapper.innerHTML = `<div class=\"rte-wrap-block ${node.wrapAlignment === 'right' ? 'right' : 'left'}\">${imageHtml}<div class=\"rte-wrap-text rte-paragraph\">${segmentsHtml(node.wrapSegments)}</div></div>`;\n } else {\n wrapper.innerHTML = imageHtml;\n }\n flowRoot.appendChild(wrapper);\n}\n\nfunction rerenderLayout({ host, flowRoot, floatingRoot, documentModel }) {\n const nodeRects = readNodeRects(host, flowRoot);\n const floatRects = buildFloatingRects(documentModel, nodeRects);\n renderFloatingImages(host, floatingRoot, floatRects);\n rerenderTextNodes(flowRoot, documentModel, nodeRects, floatRects);\n const nextRects = readNodeRects(host, flowRoot);\n const nextFloatRects = buildFloatingRects(documentModel, nextRects);\n renderFloatingImages(host, floatingRoot, nextFloatRects);\n rerenderTextNodes(flowRoot, documentModel, nextRects, nextFloatRects);\n}\n\nfunction readNodeRects(host, flowRoot) {\n const rects = {};\n const rootRect = host.getBoundingClientRect();\n flowRoot.querySelectorAll('[data-node-id]').forEach((element) => {\n const rect = element.getBoundingClientRect();\n rects[element.dataset.nodeId] = {\n left: rect.left - rootRect.left,\n top: rect.top - rootRect.top,\n width: rect.width,\n height: rect.height,\n right: rect.right - rootRect.left,\n bottom: rect.bottom - rootRect.top,\n };\n });\n return rects;\n}\n\nfunction buildFloatingRects(documentModel, nodeRects) {\n const rects = {};\n for (const node of documentModel.nodes || []) {\n if (node.type !== 'image' || node.layoutMode !== 'floating') {\n continue;\n }\n const anchor = node.anchorBlockId ? nodeRects[node.anchorBlockId] : null;\n const baseLeft = anchor ? anchor.left : 0;\n const baseTop = anchor ? anchor.top : 0;\n const width = clamp(node.width || 280, MIN_IMAGE_WIDTH, MAX_IMAGE_WIDTH);\n const height = clamp(node.height || width * 0.72, MIN_IMAGE_HEIGHT, MAX_IMAGE_HEIGHT);\n rects[node.id] = {\n node,\n left: baseLeft + (node.x || 0),\n top: baseTop + (node.y || 0),\n width,\n height,\n right: baseLeft + (node.x || 0) + width,\n bottom: baseTop + (node.y || 0) + height,\n };\n }\n return rects;\n}\n\nfunction renderFloatingImages(host, floatingRoot, floatRects) {\n floatingRoot.innerHTML = '';\n let maxBottom = 0;\n Object.values(floatRects)\n .sort((a, b) => (a.node.zIndex || 0) - (b.node.zIndex || 0))\n .forEach((entry) => {\n const element = document.createElement('div');\n element.className = 'rte-floating-image';\n element.style.left = `${entry.left}px`;\n element.style.top = `${entry.top}px`;\n element.style.width = `${entry.width}px`;\n element.style.height = `${entry.height}px`;\n element.style.zIndex = String(entry.node.zIndex || 0);\n if (entry.node.rotationDegrees) {\n element.style.transform = `rotate(${entry.node.rotationDegrees}deg)`;\n }\n element.innerHTML = `<img src=\"${escapeHtml(entry.node.url)}\" alt=\"${escapeHtml(entry.node.altText || '')}\">`;\n floatingRoot.appendChild(element);\n maxBottom = Math.max(maxBottom, entry.bottom);\n });\n host.style.minHeight = `${Math.max(flowRootHeight(host), maxBottom)}px`;\n}\n\nfunction flowRootHeight(host) {\n const flowRoot = host.querySelector('.rte-flow-root');\n return flowRoot ? flowRoot.scrollHeight : 0;\n}\n\nfunction rerenderTextNodes(flowRoot, documentModel, nodeRects, floatRects) {\n for (const node of documentModel.nodes || []) {\n if (node.type === 'textBlock') {\n const element = queryNode(flowRoot, node.id);\n const rect = nodeRects[node.id];\n if (!element || !rect) {\n continue;\n }\n const style = styleForTextBlock(node.style);\n const bands = buildBands(rect, floatRects);\n element.innerHTML = '';\n if (bands.length === 0) {\n const block = document.createElement(style.tag);\n block.className = style.className;\n block.innerHTML = segmentsHtml(node.segments);\n element.appendChild(block);\n } else {\n element.appendChild(buildWrappedLayout(node.segments, style, bands, rect.width));\n }\n continue;\n }\n\n if (node.type === 'list') {\n (node.items || []).forEach((item, index) => {\n const itemId = `${node.id}::${index}`;\n const element = queryNode(flowRoot, itemId);\n const rect = nodeRects[itemId];\n if (!element || !rect) {\n return;\n }\n const style = {\n ...styleForTextBlock('paragraph'),\n className: 'rte-list-item',\n };\n const bands = buildBands(rect, floatRects);\n element.innerHTML = '';\n if (bands.length === 0) {\n element.innerHTML = segmentsHtml(item);\n } else {\n element.appendChild(buildWrappedLayout(item, style, bands, rect.width));\n }\n });\n }\n }\n}\n\nfunction queryNode(root, id) {\n for (const element of root.querySelectorAll('[data-node-id]')) {\n if (element.dataset.nodeId === id) {\n return element;\n }\n }\n return null;\n}\n\nfunction buildBands(targetRect, floatRects) {\n const bands = [];\n Object.values(floatRects).forEach((entry) => {\n const overlapTop = Math.max(entry.top, targetRect.top);\n const overlapBottom = Math.min(entry.bottom, targetRect.bottom);\n if (overlapBottom <= overlapTop) {\n return;\n }\n bands.push({\n top: overlapTop - targetRect.top,\n bottom: overlapBottom - targetRect.top,\n blockedStart: clamp(entry.left - targetRect.left, 0, targetRect.width),\n blockedEnd: clamp(entry.right - targetRect.left, 0, targetRect.width),\n });\n });\n return bands.sort((a, b) => a.top - b.top);\n}\n\nfunction buildWrappedLayout(segments, style, bands, width) {\n const container = document.createElement('div');\n container.className = style.className;\n const tokens = tokenizeSegments(segments);\n const canvas = document.createElement('canvas');\n const context = canvas.getContext('2d');\n const lineHeight = style.fontSize * style.lineHeight;\n let tokenIndex = 0;\n let currentTop = 0;\n\n while (tokenIndex < tokens.length) {\n const band = bands.find((entry) => currentTop + lineHeight > entry.top && currentTop < entry.bottom);\n if (!band) {\n const built = takeTokens(tokens, tokenIndex, width, style, context);\n tokenIndex += built.consumed;\n const line = document.createElement('div');\n line.className = 'rte-line';\n built.accepted.forEach((token) => line.appendChild(buildTokenElement(token)));\n container.appendChild(line);\n currentTop += lineHeight;\n continue;\n }\n\n const leftWidth = clamp(band.blockedStart, 0, width);\n const blockedWidth = clamp(band.blockedEnd - band.blockedStart, 0, width - leftWidth);\n const rightWidth = clamp(width - leftWidth - blockedWidth, 0, width);\n const leftBuilt = leftWidth > 48 ? takeTokens(tokens, tokenIndex, leftWidth, style, context) : { accepted: [], consumed: 0 };\n tokenIndex += leftBuilt.consumed;\n const rightBuilt = rightWidth > 48 ? takeTokens(tokens, tokenIndex, rightWidth, style, context) : { accepted: [], consumed: 0 };\n tokenIndex += rightBuilt.consumed;\n\n const row = document.createElement('div');\n row.className = `rte-line-block ${style.className}`;\n\n const left = document.createElement('div');\n left.className = 'rte-line';\n left.style.width = `${leftWidth}px`;\n left.style.minWidth = `${leftWidth}px`;\n leftBuilt.accepted.forEach((token) => left.appendChild(buildTokenElement(token)));\n\n const blocked = document.createElement('div');\n blocked.style.width = `${blockedWidth}px`;\n blocked.style.minWidth = `${blockedWidth}px`;\n\n const right = document.createElement('div');\n right.className = 'rte-line';\n right.style.width = `${rightWidth}px`;\n right.style.minWidth = `${rightWidth}px`;\n rightBuilt.accepted.forEach((token) => right.appendChild(buildTokenElement(token)));\n\n row.appendChild(left);\n row.appendChild(blocked);\n row.appendChild(right);\n container.appendChild(row);\n currentTop += lineHeight;\n }\n\n if (tokens.length === 0) {\n const empty = document.createElement('div');\n empty.className = 'rte-line';\n empty.innerHTML = ' ';\n container.appendChild(empty);\n }\n\n return container;\n}\n\nfunction tokenizeSegments(segments = []) {\n const tokens = [];\n segments.forEach((segment, index) => {\n if (segment.inlineMathLatex) {\n tokens.push({\n value: `\\\\(${segment.inlineMathLatex}\\\\)`,\n bold: false,\n italic: false,\n underline: false,\n link: null,\n isMath: true,\n });\n if (index !== segments.length - 1) {\n tokens.push({ value: ' ', bold: false, italic: false, underline: false, link: null, isMath: false });\n }\n return;\n }\n\n const text = segment.text || '';\n const matches = text.match(/\\S+\\s*/g);\n if (!matches || matches.length === 0) {\n if (text.length > 0) {\n tokens.push(toTextToken(segment, text));\n }\n return;\n }\n matches.forEach((part) => tokens.push(toTextToken(segment, part)));\n });\n return tokens;\n}\n\nfunction toTextToken(segment, value) {\n return {\n value,\n bold: !!segment.bold,\n italic: !!segment.italic,\n underline: !!segment.underline,\n link: segment.link || null,\n isMath: false,\n };\n}\n\nfunction takeTokens(tokens, startIndex, width, style, context) {\n if (startIndex >= tokens.length || width <= 0) {\n return { accepted: [], consumed: 0 };\n }\n const accepted = [];\n let consumed = 0;\n let totalWidth = 0;\n\n for (let index = startIndex; index < tokens.length; index += 1) {\n const token = tokens[index];\n const tokenWidth = measureToken(token, style, context);\n if (accepted.length > 0 && totalWidth + tokenWidth > width) {\n break;\n }\n accepted.push(token);\n consumed += 1;\n totalWidth += tokenWidth;\n }\n\n if (consumed === 0) {\n return { accepted: [tokens[startIndex]], consumed: 1 };\n }\n return { accepted, consumed };\n}\n\nfunction measureToken(token, style, context) {\n if (!context) {\n return token.value.length * style.fontSize * 0.58;\n }\n context.font = `${token.bold ? 700 : style.fontWeight} ${token.italic ? 'italic ' : ''}${style.fontSize}px -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif`;\n return context.measureText(token.value).width;\n}\n\nfunction buildTokenElement(token) {\n const element = token.link ? document.createElement('a') : document.createElement('span');\n const classes = ['rte-token'];\n if (token.bold) classes.push('bold');\n if (token.italic) classes.push('italic');\n if (token.underline) classes.push('underline');\n if (token.link) classes.push('link');\n element.className = classes.join(' ');\n if (token.link) {\n element.href = token.link;\n }\n element.innerHTML = token.isMath ? token.value : escapeHtml(token.value);\n return element;\n}\n\nfunction styleForTextBlock(style) {\n if (style === 'heading1') {\n return { tag: 'h1', className: 'rte-heading1', fontSize: 30, lineHeight: 1.25, fontWeight: 700 };\n }\n if (style === 'heading2') {\n return { tag: 'h2', className: 'rte-heading2', fontSize: 22, lineHeight: 1.3, fontWeight: 600 };\n }\n return { tag: 'p', className: 'rte-paragraph', fontSize: 16, lineHeight: 1.4, fontWeight: 400 };\n}\n\nfunction segmentsHtml(segments = []) {\n return segments.map((segment) => {\n if (segment.inlineMathLatex) {\n return `<span data-node=\"math-inline\" data-latex=\"${escapeHtml(segment.inlineMathLatex)}\">\\\\(${escapeHtml(segment.inlineMathLatex)}\\\\)</span>`;\n }\n let content = escapeHtml(segment.text || '');\n if (segment.bold) content = `<strong>${content}</strong>`;\n if (segment.italic) content = `<em>${content}</em>`;\n if (segment.underline) content = `<u>${content}</u>`;\n if (segment.link) content = `<a href=\"${escapeHtml(segment.link)}\">${content}</a>`;\n return content;\n }).join('');\n}\n\nfunction hasWrapSegments(segments = []) {\n return segments.some((segment) => (segment.text || '').trim().length > 0 || segment.inlineMathLatex);\n}\n\nfunction requestMathTypeset(element) {\n if (window.MathJax && typeof window.MathJax.typesetPromise === 'function') {\n window.MathJax.typesetPromise([element]).catch(() => {});\n }\n}\n\nfunction clamp(value, min, max) {\n return Math.min(Math.max(value, min), max);\n}\n\nfunction escapeHtml(value) {\n return String(value)\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["document"]
|
|
7
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,572 @@
|
|
|
1
|
+
// src/index.js
|
|
2
|
+
var DEFAULT_OPTIONS = {
|
|
3
|
+
mathMode: "tex",
|
|
4
|
+
className: "rte-render-root",
|
|
5
|
+
injectStyles: true,
|
|
6
|
+
autoMathJaxTypeset: true
|
|
7
|
+
};
|
|
8
|
+
var STYLE_ID = "rich-text-editor-renderer-styles";
|
|
9
|
+
var MIN_IMAGE_WIDTH = 32;
|
|
10
|
+
var MAX_IMAGE_WIDTH = 720;
|
|
11
|
+
var MIN_IMAGE_HEIGHT = 28;
|
|
12
|
+
var MAX_IMAGE_HEIGHT = 720;
|
|
13
|
+
function renderRichTextDocument({
|
|
14
|
+
element,
|
|
15
|
+
document: document2,
|
|
16
|
+
options = {}
|
|
17
|
+
}) {
|
|
18
|
+
if (!element) {
|
|
19
|
+
throw new Error("renderRichTextDocument requires an element");
|
|
20
|
+
}
|
|
21
|
+
const resolvedOptions = { ...DEFAULT_OPTIONS, ...options };
|
|
22
|
+
const documentModel = normalizeDocument(document2);
|
|
23
|
+
if (resolvedOptions.injectStyles) {
|
|
24
|
+
ensureStyles();
|
|
25
|
+
}
|
|
26
|
+
element.innerHTML = "";
|
|
27
|
+
element.classList.add(resolvedOptions.className);
|
|
28
|
+
const flowRoot = document2.createElement("div");
|
|
29
|
+
flowRoot.className = "rte-flow-root";
|
|
30
|
+
const floatingRoot = document2.createElement("div");
|
|
31
|
+
floatingRoot.className = "rte-floating-root";
|
|
32
|
+
element.appendChild(flowRoot);
|
|
33
|
+
element.appendChild(floatingRoot);
|
|
34
|
+
renderStaticNodes(flowRoot, documentModel);
|
|
35
|
+
rerenderLayout({ host: element, flowRoot, floatingRoot, documentModel });
|
|
36
|
+
if (resolvedOptions.autoMathJaxTypeset) {
|
|
37
|
+
requestMathTypeset(element);
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
rerender() {
|
|
41
|
+
rerenderLayout({ host: element, flowRoot, floatingRoot, documentModel });
|
|
42
|
+
if (resolvedOptions.autoMathJaxTypeset) {
|
|
43
|
+
requestMathTypeset(element);
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
destroy() {
|
|
47
|
+
element.innerHTML = "";
|
|
48
|
+
element.classList.remove(resolvedOptions.className);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function autoMountRichTextDocuments({
|
|
53
|
+
selector = "[data-rich-text-json]",
|
|
54
|
+
options = {}
|
|
55
|
+
} = {}) {
|
|
56
|
+
const mounts = [];
|
|
57
|
+
document.querySelectorAll(selector).forEach((element) => {
|
|
58
|
+
const raw = element.getAttribute("data-rich-text-json");
|
|
59
|
+
if (!raw) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
mounts.push(
|
|
63
|
+
renderRichTextDocument({
|
|
64
|
+
element,
|
|
65
|
+
document: JSON.parse(raw),
|
|
66
|
+
options
|
|
67
|
+
})
|
|
68
|
+
);
|
|
69
|
+
});
|
|
70
|
+
return mounts;
|
|
71
|
+
}
|
|
72
|
+
function normalizeDocument(value) {
|
|
73
|
+
if (typeof value === "string") {
|
|
74
|
+
return JSON.parse(value);
|
|
75
|
+
}
|
|
76
|
+
return value;
|
|
77
|
+
}
|
|
78
|
+
function ensureStyles() {
|
|
79
|
+
if (document.getElementById(STYLE_ID)) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const style = document.createElement("style");
|
|
83
|
+
style.id = STYLE_ID;
|
|
84
|
+
style.textContent = `
|
|
85
|
+
.rte-render-root {
|
|
86
|
+
position: relative;
|
|
87
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
88
|
+
color: #1f2937;
|
|
89
|
+
line-height: 1.6;
|
|
90
|
+
word-break: break-word;
|
|
91
|
+
}
|
|
92
|
+
.rte-flow-root {
|
|
93
|
+
position: relative;
|
|
94
|
+
z-index: 1;
|
|
95
|
+
}
|
|
96
|
+
.rte-floating-root {
|
|
97
|
+
position: absolute;
|
|
98
|
+
inset: 0;
|
|
99
|
+
pointer-events: none;
|
|
100
|
+
z-index: 2;
|
|
101
|
+
}
|
|
102
|
+
.rte-node {
|
|
103
|
+
margin-bottom: 12px;
|
|
104
|
+
}
|
|
105
|
+
.rte-paragraph,
|
|
106
|
+
.rte-list-item {
|
|
107
|
+
font-size: 16px;
|
|
108
|
+
line-height: 1.4;
|
|
109
|
+
font-weight: 400;
|
|
110
|
+
}
|
|
111
|
+
.rte-heading1 {
|
|
112
|
+
font-size: 30px;
|
|
113
|
+
line-height: 1.25;
|
|
114
|
+
font-weight: 700;
|
|
115
|
+
}
|
|
116
|
+
.rte-heading2 {
|
|
117
|
+
font-size: 22px;
|
|
118
|
+
line-height: 1.3;
|
|
119
|
+
font-weight: 600;
|
|
120
|
+
}
|
|
121
|
+
.rte-list {
|
|
122
|
+
margin: 0 0 12px 0;
|
|
123
|
+
padding-left: 24px;
|
|
124
|
+
}
|
|
125
|
+
.rte-line {
|
|
126
|
+
display: flex;
|
|
127
|
+
align-items: flex-start;
|
|
128
|
+
min-height: 1em;
|
|
129
|
+
}
|
|
130
|
+
.rte-line-block {
|
|
131
|
+
display: flex;
|
|
132
|
+
align-items: flex-start;
|
|
133
|
+
}
|
|
134
|
+
.rte-token {
|
|
135
|
+
white-space: pre;
|
|
136
|
+
}
|
|
137
|
+
.rte-token.bold {
|
|
138
|
+
font-weight: 700;
|
|
139
|
+
}
|
|
140
|
+
.rte-token.italic {
|
|
141
|
+
font-style: italic;
|
|
142
|
+
}
|
|
143
|
+
.rte-token.underline {
|
|
144
|
+
text-decoration: underline;
|
|
145
|
+
}
|
|
146
|
+
.rte-token.link {
|
|
147
|
+
color: #0a66c2;
|
|
148
|
+
text-decoration: underline;
|
|
149
|
+
}
|
|
150
|
+
.rte-block-image,
|
|
151
|
+
.rte-floating-image {
|
|
152
|
+
overflow: hidden;
|
|
153
|
+
border-radius: 12px;
|
|
154
|
+
background: #e5e7eb;
|
|
155
|
+
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);
|
|
156
|
+
}
|
|
157
|
+
.rte-block-image img,
|
|
158
|
+
.rte-floating-image img {
|
|
159
|
+
display: block;
|
|
160
|
+
width: 100%;
|
|
161
|
+
height: 100%;
|
|
162
|
+
object-fit: cover;
|
|
163
|
+
}
|
|
164
|
+
.rte-floating-image {
|
|
165
|
+
position: absolute;
|
|
166
|
+
}
|
|
167
|
+
.rte-wrap-block {
|
|
168
|
+
display: flex;
|
|
169
|
+
gap: 16px;
|
|
170
|
+
align-items: flex-start;
|
|
171
|
+
}
|
|
172
|
+
.rte-wrap-block.right {
|
|
173
|
+
flex-direction: row-reverse;
|
|
174
|
+
}
|
|
175
|
+
.rte-wrap-text {
|
|
176
|
+
flex: 1;
|
|
177
|
+
min-width: 0;
|
|
178
|
+
}
|
|
179
|
+
`;
|
|
180
|
+
document.head.appendChild(style);
|
|
181
|
+
}
|
|
182
|
+
function renderStaticNodes(flowRoot, documentModel) {
|
|
183
|
+
flowRoot.innerHTML = "";
|
|
184
|
+
for (const node of documentModel.nodes || []) {
|
|
185
|
+
switch (node.type) {
|
|
186
|
+
case "textBlock":
|
|
187
|
+
renderStaticTextNode(flowRoot, node);
|
|
188
|
+
break;
|
|
189
|
+
case "list":
|
|
190
|
+
renderStaticListNode(flowRoot, node);
|
|
191
|
+
break;
|
|
192
|
+
case "math":
|
|
193
|
+
renderStaticMathNode(flowRoot, node);
|
|
194
|
+
break;
|
|
195
|
+
case "image":
|
|
196
|
+
renderStaticImageNode(flowRoot, node);
|
|
197
|
+
break;
|
|
198
|
+
default:
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function renderStaticTextNode(flowRoot, node) {
|
|
204
|
+
const wrapper = document.createElement("div");
|
|
205
|
+
wrapper.className = "rte-node";
|
|
206
|
+
wrapper.dataset.nodeId = node.id;
|
|
207
|
+
wrapper.dataset.nodeType = node.type;
|
|
208
|
+
flowRoot.appendChild(wrapper);
|
|
209
|
+
}
|
|
210
|
+
function renderStaticListNode(flowRoot, node) {
|
|
211
|
+
const list = document.createElement(node.style === "ordered" ? "ol" : "ul");
|
|
212
|
+
list.className = "rte-node rte-list";
|
|
213
|
+
list.dataset.nodeId = node.id;
|
|
214
|
+
list.dataset.nodeType = node.type;
|
|
215
|
+
(node.items || []).forEach((_, index) => {
|
|
216
|
+
const item = document.createElement("li");
|
|
217
|
+
item.className = "rte-list-item";
|
|
218
|
+
item.dataset.nodeId = `${node.id}::${index}`;
|
|
219
|
+
item.dataset.parentNodeId = node.id;
|
|
220
|
+
item.dataset.nodeType = "list-item";
|
|
221
|
+
list.appendChild(item);
|
|
222
|
+
});
|
|
223
|
+
flowRoot.appendChild(list);
|
|
224
|
+
}
|
|
225
|
+
function renderStaticMathNode(flowRoot, node) {
|
|
226
|
+
const wrapper = document.createElement("div");
|
|
227
|
+
wrapper.className = "rte-node";
|
|
228
|
+
wrapper.dataset.nodeId = node.id;
|
|
229
|
+
wrapper.dataset.nodeType = node.type;
|
|
230
|
+
wrapper.innerHTML = node.displayMode === "inline" ? `<span data-node="math-inline" data-latex="${escapeHtml(node.latex)}">\\(${escapeHtml(node.latex)}\\)</span>` : `<div data-node="math-block" data-latex="${escapeHtml(node.latex)}">\\[${escapeHtml(node.latex)}\\]</div>`;
|
|
231
|
+
flowRoot.appendChild(wrapper);
|
|
232
|
+
}
|
|
233
|
+
function renderStaticImageNode(flowRoot, node) {
|
|
234
|
+
if (node.layoutMode === "floating") {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const wrapper = document.createElement("div");
|
|
238
|
+
wrapper.className = "rte-node";
|
|
239
|
+
wrapper.dataset.nodeId = node.id;
|
|
240
|
+
wrapper.dataset.nodeType = node.type;
|
|
241
|
+
const width = node.width ? ` style="width:${node.width}px"` : "";
|
|
242
|
+
const imageHtml = `<div class="rte-block-image"${width}><img src="${escapeHtml(node.url)}" alt="${escapeHtml(node.altText || "")}"></div>`;
|
|
243
|
+
if (node.wrapAlignment && node.wrapAlignment !== "none" && hasWrapSegments(node.wrapSegments)) {
|
|
244
|
+
wrapper.innerHTML = `<div class="rte-wrap-block ${node.wrapAlignment === "right" ? "right" : "left"}">${imageHtml}<div class="rte-wrap-text rte-paragraph">${segmentsHtml(node.wrapSegments)}</div></div>`;
|
|
245
|
+
} else {
|
|
246
|
+
wrapper.innerHTML = imageHtml;
|
|
247
|
+
}
|
|
248
|
+
flowRoot.appendChild(wrapper);
|
|
249
|
+
}
|
|
250
|
+
function rerenderLayout({ host, flowRoot, floatingRoot, documentModel }) {
|
|
251
|
+
const nodeRects = readNodeRects(host, flowRoot);
|
|
252
|
+
const floatRects = buildFloatingRects(documentModel, nodeRects);
|
|
253
|
+
renderFloatingImages(host, floatingRoot, floatRects);
|
|
254
|
+
rerenderTextNodes(flowRoot, documentModel, nodeRects, floatRects);
|
|
255
|
+
const nextRects = readNodeRects(host, flowRoot);
|
|
256
|
+
const nextFloatRects = buildFloatingRects(documentModel, nextRects);
|
|
257
|
+
renderFloatingImages(host, floatingRoot, nextFloatRects);
|
|
258
|
+
rerenderTextNodes(flowRoot, documentModel, nextRects, nextFloatRects);
|
|
259
|
+
}
|
|
260
|
+
function readNodeRects(host, flowRoot) {
|
|
261
|
+
const rects = {};
|
|
262
|
+
const rootRect = host.getBoundingClientRect();
|
|
263
|
+
flowRoot.querySelectorAll("[data-node-id]").forEach((element) => {
|
|
264
|
+
const rect = element.getBoundingClientRect();
|
|
265
|
+
rects[element.dataset.nodeId] = {
|
|
266
|
+
left: rect.left - rootRect.left,
|
|
267
|
+
top: rect.top - rootRect.top,
|
|
268
|
+
width: rect.width,
|
|
269
|
+
height: rect.height,
|
|
270
|
+
right: rect.right - rootRect.left,
|
|
271
|
+
bottom: rect.bottom - rootRect.top
|
|
272
|
+
};
|
|
273
|
+
});
|
|
274
|
+
return rects;
|
|
275
|
+
}
|
|
276
|
+
function buildFloatingRects(documentModel, nodeRects) {
|
|
277
|
+
const rects = {};
|
|
278
|
+
for (const node of documentModel.nodes || []) {
|
|
279
|
+
if (node.type !== "image" || node.layoutMode !== "floating") {
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
const anchor = node.anchorBlockId ? nodeRects[node.anchorBlockId] : null;
|
|
283
|
+
const baseLeft = anchor ? anchor.left : 0;
|
|
284
|
+
const baseTop = anchor ? anchor.top : 0;
|
|
285
|
+
const width = clamp(node.width || 280, MIN_IMAGE_WIDTH, MAX_IMAGE_WIDTH);
|
|
286
|
+
const height = clamp(node.height || width * 0.72, MIN_IMAGE_HEIGHT, MAX_IMAGE_HEIGHT);
|
|
287
|
+
rects[node.id] = {
|
|
288
|
+
node,
|
|
289
|
+
left: baseLeft + (node.x || 0),
|
|
290
|
+
top: baseTop + (node.y || 0),
|
|
291
|
+
width,
|
|
292
|
+
height,
|
|
293
|
+
right: baseLeft + (node.x || 0) + width,
|
|
294
|
+
bottom: baseTop + (node.y || 0) + height
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
return rects;
|
|
298
|
+
}
|
|
299
|
+
function renderFloatingImages(host, floatingRoot, floatRects) {
|
|
300
|
+
floatingRoot.innerHTML = "";
|
|
301
|
+
let maxBottom = 0;
|
|
302
|
+
Object.values(floatRects).sort((a, b) => (a.node.zIndex || 0) - (b.node.zIndex || 0)).forEach((entry) => {
|
|
303
|
+
const element = document.createElement("div");
|
|
304
|
+
element.className = "rte-floating-image";
|
|
305
|
+
element.style.left = `${entry.left}px`;
|
|
306
|
+
element.style.top = `${entry.top}px`;
|
|
307
|
+
element.style.width = `${entry.width}px`;
|
|
308
|
+
element.style.height = `${entry.height}px`;
|
|
309
|
+
element.style.zIndex = String(entry.node.zIndex || 0);
|
|
310
|
+
if (entry.node.rotationDegrees) {
|
|
311
|
+
element.style.transform = `rotate(${entry.node.rotationDegrees}deg)`;
|
|
312
|
+
}
|
|
313
|
+
element.innerHTML = `<img src="${escapeHtml(entry.node.url)}" alt="${escapeHtml(entry.node.altText || "")}">`;
|
|
314
|
+
floatingRoot.appendChild(element);
|
|
315
|
+
maxBottom = Math.max(maxBottom, entry.bottom);
|
|
316
|
+
});
|
|
317
|
+
host.style.minHeight = `${Math.max(flowRootHeight(host), maxBottom)}px`;
|
|
318
|
+
}
|
|
319
|
+
function flowRootHeight(host) {
|
|
320
|
+
const flowRoot = host.querySelector(".rte-flow-root");
|
|
321
|
+
return flowRoot ? flowRoot.scrollHeight : 0;
|
|
322
|
+
}
|
|
323
|
+
function rerenderTextNodes(flowRoot, documentModel, nodeRects, floatRects) {
|
|
324
|
+
for (const node of documentModel.nodes || []) {
|
|
325
|
+
if (node.type === "textBlock") {
|
|
326
|
+
const element = queryNode(flowRoot, node.id);
|
|
327
|
+
const rect = nodeRects[node.id];
|
|
328
|
+
if (!element || !rect) {
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
const style = styleForTextBlock(node.style);
|
|
332
|
+
const bands = buildBands(rect, floatRects);
|
|
333
|
+
element.innerHTML = "";
|
|
334
|
+
if (bands.length === 0) {
|
|
335
|
+
const block = document.createElement(style.tag);
|
|
336
|
+
block.className = style.className;
|
|
337
|
+
block.innerHTML = segmentsHtml(node.segments);
|
|
338
|
+
element.appendChild(block);
|
|
339
|
+
} else {
|
|
340
|
+
element.appendChild(buildWrappedLayout(node.segments, style, bands, rect.width));
|
|
341
|
+
}
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
if (node.type === "list") {
|
|
345
|
+
(node.items || []).forEach((item, index) => {
|
|
346
|
+
const itemId = `${node.id}::${index}`;
|
|
347
|
+
const element = queryNode(flowRoot, itemId);
|
|
348
|
+
const rect = nodeRects[itemId];
|
|
349
|
+
if (!element || !rect) {
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
const style = {
|
|
353
|
+
...styleForTextBlock("paragraph"),
|
|
354
|
+
className: "rte-list-item"
|
|
355
|
+
};
|
|
356
|
+
const bands = buildBands(rect, floatRects);
|
|
357
|
+
element.innerHTML = "";
|
|
358
|
+
if (bands.length === 0) {
|
|
359
|
+
element.innerHTML = segmentsHtml(item);
|
|
360
|
+
} else {
|
|
361
|
+
element.appendChild(buildWrappedLayout(item, style, bands, rect.width));
|
|
362
|
+
}
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
function queryNode(root, id) {
|
|
368
|
+
for (const element of root.querySelectorAll("[data-node-id]")) {
|
|
369
|
+
if (element.dataset.nodeId === id) {
|
|
370
|
+
return element;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
function buildBands(targetRect, floatRects) {
|
|
376
|
+
const bands = [];
|
|
377
|
+
Object.values(floatRects).forEach((entry) => {
|
|
378
|
+
const overlapTop = Math.max(entry.top, targetRect.top);
|
|
379
|
+
const overlapBottom = Math.min(entry.bottom, targetRect.bottom);
|
|
380
|
+
if (overlapBottom <= overlapTop) {
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
bands.push({
|
|
384
|
+
top: overlapTop - targetRect.top,
|
|
385
|
+
bottom: overlapBottom - targetRect.top,
|
|
386
|
+
blockedStart: clamp(entry.left - targetRect.left, 0, targetRect.width),
|
|
387
|
+
blockedEnd: clamp(entry.right - targetRect.left, 0, targetRect.width)
|
|
388
|
+
});
|
|
389
|
+
});
|
|
390
|
+
return bands.sort((a, b) => a.top - b.top);
|
|
391
|
+
}
|
|
392
|
+
function buildWrappedLayout(segments, style, bands, width) {
|
|
393
|
+
const container = document.createElement("div");
|
|
394
|
+
container.className = style.className;
|
|
395
|
+
const tokens = tokenizeSegments(segments);
|
|
396
|
+
const canvas = document.createElement("canvas");
|
|
397
|
+
const context = canvas.getContext("2d");
|
|
398
|
+
const lineHeight = style.fontSize * style.lineHeight;
|
|
399
|
+
let tokenIndex = 0;
|
|
400
|
+
let currentTop = 0;
|
|
401
|
+
while (tokenIndex < tokens.length) {
|
|
402
|
+
const band = bands.find((entry) => currentTop + lineHeight > entry.top && currentTop < entry.bottom);
|
|
403
|
+
if (!band) {
|
|
404
|
+
const built = takeTokens(tokens, tokenIndex, width, style, context);
|
|
405
|
+
tokenIndex += built.consumed;
|
|
406
|
+
const line = document.createElement("div");
|
|
407
|
+
line.className = "rte-line";
|
|
408
|
+
built.accepted.forEach((token) => line.appendChild(buildTokenElement(token)));
|
|
409
|
+
container.appendChild(line);
|
|
410
|
+
currentTop += lineHeight;
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
413
|
+
const leftWidth = clamp(band.blockedStart, 0, width);
|
|
414
|
+
const blockedWidth = clamp(band.blockedEnd - band.blockedStart, 0, width - leftWidth);
|
|
415
|
+
const rightWidth = clamp(width - leftWidth - blockedWidth, 0, width);
|
|
416
|
+
const leftBuilt = leftWidth > 48 ? takeTokens(tokens, tokenIndex, leftWidth, style, context) : { accepted: [], consumed: 0 };
|
|
417
|
+
tokenIndex += leftBuilt.consumed;
|
|
418
|
+
const rightBuilt = rightWidth > 48 ? takeTokens(tokens, tokenIndex, rightWidth, style, context) : { accepted: [], consumed: 0 };
|
|
419
|
+
tokenIndex += rightBuilt.consumed;
|
|
420
|
+
const row = document.createElement("div");
|
|
421
|
+
row.className = `rte-line-block ${style.className}`;
|
|
422
|
+
const left = document.createElement("div");
|
|
423
|
+
left.className = "rte-line";
|
|
424
|
+
left.style.width = `${leftWidth}px`;
|
|
425
|
+
left.style.minWidth = `${leftWidth}px`;
|
|
426
|
+
leftBuilt.accepted.forEach((token) => left.appendChild(buildTokenElement(token)));
|
|
427
|
+
const blocked = document.createElement("div");
|
|
428
|
+
blocked.style.width = `${blockedWidth}px`;
|
|
429
|
+
blocked.style.minWidth = `${blockedWidth}px`;
|
|
430
|
+
const right = document.createElement("div");
|
|
431
|
+
right.className = "rte-line";
|
|
432
|
+
right.style.width = `${rightWidth}px`;
|
|
433
|
+
right.style.minWidth = `${rightWidth}px`;
|
|
434
|
+
rightBuilt.accepted.forEach((token) => right.appendChild(buildTokenElement(token)));
|
|
435
|
+
row.appendChild(left);
|
|
436
|
+
row.appendChild(blocked);
|
|
437
|
+
row.appendChild(right);
|
|
438
|
+
container.appendChild(row);
|
|
439
|
+
currentTop += lineHeight;
|
|
440
|
+
}
|
|
441
|
+
if (tokens.length === 0) {
|
|
442
|
+
const empty = document.createElement("div");
|
|
443
|
+
empty.className = "rte-line";
|
|
444
|
+
empty.innerHTML = " ";
|
|
445
|
+
container.appendChild(empty);
|
|
446
|
+
}
|
|
447
|
+
return container;
|
|
448
|
+
}
|
|
449
|
+
function tokenizeSegments(segments = []) {
|
|
450
|
+
const tokens = [];
|
|
451
|
+
segments.forEach((segment, index) => {
|
|
452
|
+
if (segment.inlineMathLatex) {
|
|
453
|
+
tokens.push({
|
|
454
|
+
value: `\\(${segment.inlineMathLatex}\\)`,
|
|
455
|
+
bold: false,
|
|
456
|
+
italic: false,
|
|
457
|
+
underline: false,
|
|
458
|
+
link: null,
|
|
459
|
+
isMath: true
|
|
460
|
+
});
|
|
461
|
+
if (index !== segments.length - 1) {
|
|
462
|
+
tokens.push({ value: " ", bold: false, italic: false, underline: false, link: null, isMath: false });
|
|
463
|
+
}
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
const text = segment.text || "";
|
|
467
|
+
const matches = text.match(/\S+\s*/g);
|
|
468
|
+
if (!matches || matches.length === 0) {
|
|
469
|
+
if (text.length > 0) {
|
|
470
|
+
tokens.push(toTextToken(segment, text));
|
|
471
|
+
}
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
matches.forEach((part) => tokens.push(toTextToken(segment, part)));
|
|
475
|
+
});
|
|
476
|
+
return tokens;
|
|
477
|
+
}
|
|
478
|
+
function toTextToken(segment, value) {
|
|
479
|
+
return {
|
|
480
|
+
value,
|
|
481
|
+
bold: !!segment.bold,
|
|
482
|
+
italic: !!segment.italic,
|
|
483
|
+
underline: !!segment.underline,
|
|
484
|
+
link: segment.link || null,
|
|
485
|
+
isMath: false
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
function takeTokens(tokens, startIndex, width, style, context) {
|
|
489
|
+
if (startIndex >= tokens.length || width <= 0) {
|
|
490
|
+
return { accepted: [], consumed: 0 };
|
|
491
|
+
}
|
|
492
|
+
const accepted = [];
|
|
493
|
+
let consumed = 0;
|
|
494
|
+
let totalWidth = 0;
|
|
495
|
+
for (let index = startIndex; index < tokens.length; index += 1) {
|
|
496
|
+
const token = tokens[index];
|
|
497
|
+
const tokenWidth = measureToken(token, style, context);
|
|
498
|
+
if (accepted.length > 0 && totalWidth + tokenWidth > width) {
|
|
499
|
+
break;
|
|
500
|
+
}
|
|
501
|
+
accepted.push(token);
|
|
502
|
+
consumed += 1;
|
|
503
|
+
totalWidth += tokenWidth;
|
|
504
|
+
}
|
|
505
|
+
if (consumed === 0) {
|
|
506
|
+
return { accepted: [tokens[startIndex]], consumed: 1 };
|
|
507
|
+
}
|
|
508
|
+
return { accepted, consumed };
|
|
509
|
+
}
|
|
510
|
+
function measureToken(token, style, context) {
|
|
511
|
+
if (!context) {
|
|
512
|
+
return token.value.length * style.fontSize * 0.58;
|
|
513
|
+
}
|
|
514
|
+
context.font = `${token.bold ? 700 : style.fontWeight} ${token.italic ? "italic " : ""}${style.fontSize}px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif`;
|
|
515
|
+
return context.measureText(token.value).width;
|
|
516
|
+
}
|
|
517
|
+
function buildTokenElement(token) {
|
|
518
|
+
const element = token.link ? document.createElement("a") : document.createElement("span");
|
|
519
|
+
const classes = ["rte-token"];
|
|
520
|
+
if (token.bold) classes.push("bold");
|
|
521
|
+
if (token.italic) classes.push("italic");
|
|
522
|
+
if (token.underline) classes.push("underline");
|
|
523
|
+
if (token.link) classes.push("link");
|
|
524
|
+
element.className = classes.join(" ");
|
|
525
|
+
if (token.link) {
|
|
526
|
+
element.href = token.link;
|
|
527
|
+
}
|
|
528
|
+
element.innerHTML = token.isMath ? token.value : escapeHtml(token.value);
|
|
529
|
+
return element;
|
|
530
|
+
}
|
|
531
|
+
function styleForTextBlock(style) {
|
|
532
|
+
if (style === "heading1") {
|
|
533
|
+
return { tag: "h1", className: "rte-heading1", fontSize: 30, lineHeight: 1.25, fontWeight: 700 };
|
|
534
|
+
}
|
|
535
|
+
if (style === "heading2") {
|
|
536
|
+
return { tag: "h2", className: "rte-heading2", fontSize: 22, lineHeight: 1.3, fontWeight: 600 };
|
|
537
|
+
}
|
|
538
|
+
return { tag: "p", className: "rte-paragraph", fontSize: 16, lineHeight: 1.4, fontWeight: 400 };
|
|
539
|
+
}
|
|
540
|
+
function segmentsHtml(segments = []) {
|
|
541
|
+
return segments.map((segment) => {
|
|
542
|
+
if (segment.inlineMathLatex) {
|
|
543
|
+
return `<span data-node="math-inline" data-latex="${escapeHtml(segment.inlineMathLatex)}">\\(${escapeHtml(segment.inlineMathLatex)}\\)</span>`;
|
|
544
|
+
}
|
|
545
|
+
let content = escapeHtml(segment.text || "");
|
|
546
|
+
if (segment.bold) content = `<strong>${content}</strong>`;
|
|
547
|
+
if (segment.italic) content = `<em>${content}</em>`;
|
|
548
|
+
if (segment.underline) content = `<u>${content}</u>`;
|
|
549
|
+
if (segment.link) content = `<a href="${escapeHtml(segment.link)}">${content}</a>`;
|
|
550
|
+
return content;
|
|
551
|
+
}).join("");
|
|
552
|
+
}
|
|
553
|
+
function hasWrapSegments(segments = []) {
|
|
554
|
+
return segments.some((segment) => (segment.text || "").trim().length > 0 || segment.inlineMathLatex);
|
|
555
|
+
}
|
|
556
|
+
function requestMathTypeset(element) {
|
|
557
|
+
if (window.MathJax && typeof window.MathJax.typesetPromise === "function") {
|
|
558
|
+
window.MathJax.typesetPromise([element]).catch(() => {
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
function clamp(value, min, max) {
|
|
563
|
+
return Math.min(Math.max(value, min), max);
|
|
564
|
+
}
|
|
565
|
+
function escapeHtml(value) {
|
|
566
|
+
return String(value).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
567
|
+
}
|
|
568
|
+
export {
|
|
569
|
+
autoMountRichTextDocuments,
|
|
570
|
+
renderRichTextDocument
|
|
571
|
+
};
|
|
572
|
+
//# sourceMappingURL=index.js.map
|