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