react-next-editor-js 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.
Files changed (102) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +877 -0
  3. package/dist/chunk-3QWXTDLY.cjs +486 -0
  4. package/dist/chunk-3QWXTDLY.cjs.map +1 -0
  5. package/dist/chunk-5F6SPYCN.cjs +180 -0
  6. package/dist/chunk-5F6SPYCN.cjs.map +1 -0
  7. package/dist/chunk-6NTSXJX4.js +174 -0
  8. package/dist/chunk-6NTSXJX4.js.map +1 -0
  9. package/dist/chunk-7VYJDBH7.js +261 -0
  10. package/dist/chunk-7VYJDBH7.js.map +1 -0
  11. package/dist/chunk-DBSFCCBG.cjs +1712 -0
  12. package/dist/chunk-DBSFCCBG.cjs.map +1 -0
  13. package/dist/chunk-EFE6RHDL.cjs +4 -0
  14. package/dist/chunk-EFE6RHDL.cjs.map +1 -0
  15. package/dist/chunk-G6YRIEK4.js +3 -0
  16. package/dist/chunk-G6YRIEK4.js.map +1 -0
  17. package/dist/chunk-GFNFJ3FL.cjs +119 -0
  18. package/dist/chunk-GFNFJ3FL.cjs.map +1 -0
  19. package/dist/chunk-IG2YLUFW.js +114 -0
  20. package/dist/chunk-IG2YLUFW.js.map +1 -0
  21. package/dist/chunk-JQXTWLHL.js +176 -0
  22. package/dist/chunk-JQXTWLHL.js.map +1 -0
  23. package/dist/chunk-NJCEHQV3.cjs +454 -0
  24. package/dist/chunk-NJCEHQV3.cjs.map +1 -0
  25. package/dist/chunk-O4GTLC3T.js +478 -0
  26. package/dist/chunk-O4GTLC3T.js.map +1 -0
  27. package/dist/chunk-ODHABIIC.cjs +82 -0
  28. package/dist/chunk-ODHABIIC.cjs.map +1 -0
  29. package/dist/chunk-PZ5AY32C.js +9 -0
  30. package/dist/chunk-PZ5AY32C.js.map +1 -0
  31. package/dist/chunk-Q7SFCCGT.cjs +11 -0
  32. package/dist/chunk-Q7SFCCGT.cjs.map +1 -0
  33. package/dist/chunk-QIUIYBCZ.js +80 -0
  34. package/dist/chunk-QIUIYBCZ.js.map +1 -0
  35. package/dist/chunk-QROUNVQK.js +450 -0
  36. package/dist/chunk-QROUNVQK.js.map +1 -0
  37. package/dist/chunk-T6FR37IC.js +41 -0
  38. package/dist/chunk-T6FR37IC.js.map +1 -0
  39. package/dist/chunk-TI44I654.cjs +265 -0
  40. package/dist/chunk-TI44I654.cjs.map +1 -0
  41. package/dist/chunk-TXPLBAH5.cjs +47 -0
  42. package/dist/chunk-TXPLBAH5.cjs.map +1 -0
  43. package/dist/chunk-U3O54IYI.cjs +187 -0
  44. package/dist/chunk-U3O54IYI.cjs.map +1 -0
  45. package/dist/chunk-VLC7SZMT.js +1669 -0
  46. package/dist/chunk-VLC7SZMT.js.map +1 -0
  47. package/dist/core/index.cjs +232 -0
  48. package/dist/core/index.cjs.map +1 -0
  49. package/dist/core/index.d.cts +122 -0
  50. package/dist/core/index.d.ts +122 -0
  51. package/dist/core/index.js +7 -0
  52. package/dist/core/index.js.map +1 -0
  53. package/dist/defaults-EQD5QKCU.js +4 -0
  54. package/dist/defaults-EQD5QKCU.js.map +1 -0
  55. package/dist/defaults-MLYXD2BG.cjs +49 -0
  56. package/dist/defaults-MLYXD2BG.cjs.map +1 -0
  57. package/dist/docx-BUrf4PFj.d.ts +49 -0
  58. package/dist/docx-DLfSdvXm.d.cts +49 -0
  59. package/dist/docx-LDETXV3L.js +5 -0
  60. package/dist/docx-LDETXV3L.js.map +1 -0
  61. package/dist/docx-N2LKIOK3.cjs +14 -0
  62. package/dist/docx-N2LKIOK3.cjs.map +1 -0
  63. package/dist/export/index.cjs +54 -0
  64. package/dist/export/index.cjs.map +1 -0
  65. package/dist/export/index.d.cts +60 -0
  66. package/dist/export/index.d.ts +60 -0
  67. package/dist/export/index.js +9 -0
  68. package/dist/export/index.js.map +1 -0
  69. package/dist/html-5BXJPQU3.js +7 -0
  70. package/dist/html-5BXJPQU3.js.map +1 -0
  71. package/dist/html-KU2KHLRF.cjs +24 -0
  72. package/dist/html-KU2KHLRF.cjs.map +1 -0
  73. package/dist/import/index.cjs +15 -0
  74. package/dist/import/index.cjs.map +1 -0
  75. package/dist/import/index.d.cts +37 -0
  76. package/dist/import/index.d.ts +37 -0
  77. package/dist/import/index.js +6 -0
  78. package/dist/import/index.js.map +1 -0
  79. package/dist/index.cjs +1035 -0
  80. package/dist/index.cjs.map +1 -0
  81. package/dist/index.d.cts +248 -0
  82. package/dist/index.d.ts +248 -0
  83. package/dist/index.js +885 -0
  84. package/dist/index.js.map +1 -0
  85. package/dist/persistence/index.cjs +37 -0
  86. package/dist/persistence/index.cjs.map +1 -0
  87. package/dist/persistence/index.d.cts +279 -0
  88. package/dist/persistence/index.d.ts +279 -0
  89. package/dist/persistence/index.js +4 -0
  90. package/dist/persistence/index.js.map +1 -0
  91. package/dist/sanitize-7IZ-SW1f.d.ts +361 -0
  92. package/dist/sanitize-CvmgqbsA.d.cts +361 -0
  93. package/dist/server/index.cjs +400 -0
  94. package/dist/server/index.cjs.map +1 -0
  95. package/dist/server/index.d.cts +229 -0
  96. package/dist/server/index.d.ts +229 -0
  97. package/dist/server/index.js +390 -0
  98. package/dist/server/index.js.map +1 -0
  99. package/dist/styles.css +680 -0
  100. package/dist/types-B4z0Quvv.d.cts +193 -0
  101. package/dist/types-B4z0Quvv.d.ts +193 -0
  102. package/package.json +183 -0
@@ -0,0 +1,261 @@
1
+ import { cssInteger, normalizeCssColor, cssAlign, cssNumber, cssFontFamily } from './chunk-T6FR37IC.js';
2
+ import { sanitizeUrl, sanitizeImageSrc } from './chunk-6NTSXJX4.js';
3
+ import { resolvePageDimensions } from './chunk-JQXTWLHL.js';
4
+
5
+ // src/export/html.ts
6
+ function escapeHtml(value) {
7
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
8
+ }
9
+ function escapeAttr(value) {
10
+ return escapeHtml(value).replace(/"/g, "&quot;");
11
+ }
12
+ var MARK_ORDER = [
13
+ "link",
14
+ "textColor",
15
+ "highlight",
16
+ "fontFamily",
17
+ "fontSize",
18
+ "strong",
19
+ "em",
20
+ "underline",
21
+ "strikethrough",
22
+ "superscript",
23
+ "subscript",
24
+ "code"
25
+ ];
26
+ function openMark(mark) {
27
+ const attrs = mark.attrs ?? {};
28
+ switch (mark.type) {
29
+ case "strong":
30
+ return "<strong>";
31
+ case "em":
32
+ return "<em>";
33
+ case "underline":
34
+ return "<u>";
35
+ case "strikethrough":
36
+ return "<s>";
37
+ case "superscript":
38
+ return "<sup>";
39
+ case "subscript":
40
+ return "<sub>";
41
+ case "code":
42
+ return "<code>";
43
+ case "link": {
44
+ const href = sanitizeUrl(String(attrs.href ?? "")) ?? "";
45
+ const title = attrs.title ? ` title="${escapeAttr(String(attrs.title))}"` : "";
46
+ return `<a href="${escapeAttr(href)}"${title} rel="noopener noreferrer nofollow">`;
47
+ }
48
+ case "fontFamily": {
49
+ const family = cssFontFamily(attrs.family);
50
+ return family ? `<span style="font-family: ${family}">` : "<span>";
51
+ }
52
+ case "fontSize": {
53
+ const size = cssNumber(attrs.size, 1, 1638);
54
+ return size ? `<span style="font-size: ${size}pt">` : "<span>";
55
+ }
56
+ case "textColor": {
57
+ const color = normalizeCssColor(attrs.color);
58
+ return color ? `<span style="color: ${color}">` : "<span>";
59
+ }
60
+ case "highlight": {
61
+ const color = normalizeCssColor(attrs.color) ?? "#fff2a8";
62
+ return `<mark style="background-color: ${color}">`;
63
+ }
64
+ default:
65
+ return "";
66
+ }
67
+ }
68
+ function closeMark(mark) {
69
+ switch (mark.type) {
70
+ case "strong":
71
+ return "</strong>";
72
+ case "em":
73
+ return "</em>";
74
+ case "underline":
75
+ return "</u>";
76
+ case "strikethrough":
77
+ return "</s>";
78
+ case "superscript":
79
+ return "</sup>";
80
+ case "subscript":
81
+ return "</sub>";
82
+ case "code":
83
+ return "</code>";
84
+ case "link":
85
+ return "</a>";
86
+ case "fontFamily":
87
+ case "fontSize":
88
+ case "textColor":
89
+ return "</span>";
90
+ case "highlight":
91
+ return "</mark>";
92
+ default:
93
+ return "";
94
+ }
95
+ }
96
+ function sortMarks(marks) {
97
+ return [...marks].sort((a, b) => {
98
+ const ia = MARK_ORDER.indexOf(a.type);
99
+ const ib = MARK_ORDER.indexOf(b.type);
100
+ return (ia === -1 ? 99 : ia) - (ib === -1 ? 99 : ib);
101
+ });
102
+ }
103
+ function serializeInline(content) {
104
+ if (!content) return "";
105
+ let out = "";
106
+ for (const node of content) {
107
+ if (node.type === "text") {
108
+ const marks = sortMarks(node.marks ?? []);
109
+ let text = escapeHtml(node.text ?? "");
110
+ for (let i = marks.length - 1; i >= 0; i--) text = openMark(marks[i]) + text;
111
+ for (const mark of marks) text += closeMark(mark);
112
+ out += text;
113
+ } else if (node.type === "hard_break") {
114
+ out += "<br>";
115
+ } else if (node.type === "image") {
116
+ out += serializeImage(node);
117
+ }
118
+ }
119
+ return out;
120
+ }
121
+ function serializeImage(node) {
122
+ const src = sanitizeImageSrc(String(node.attrs?.src ?? ""));
123
+ if (!src) return "";
124
+ const alt = node.attrs?.alt ? ` alt="${escapeAttr(String(node.attrs.alt))}"` : ' alt=""';
125
+ const title = node.attrs?.title ? ` title="${escapeAttr(String(node.attrs.title))}"` : "";
126
+ const safeWidth = cssInteger(node.attrs?.width, 1, 4e3);
127
+ const width = safeWidth ? ` style="width: ${safeWidth}px"` : "";
128
+ return `<img class="rne-image" src="${escapeAttr(src)}"${alt}${title}${width}>`;
129
+ }
130
+ function blockStyle(attrs) {
131
+ if (!attrs) return "";
132
+ const styles = [];
133
+ const align = cssAlign(attrs.align);
134
+ if (align) styles.push(`text-align: ${align}`);
135
+ const indent = cssInteger(attrs.indent, 0, 12);
136
+ if (indent && indent > 0) styles.push(`margin-left: ${indent * 3}em`);
137
+ const lineHeight = cssNumber(attrs.lineHeight, 0.1, 10);
138
+ if (lineHeight) styles.push(`line-height: ${lineHeight}`);
139
+ return styles.length ? ` style="${styles.join("; ")}"` : "";
140
+ }
141
+ function serializeBlock(node) {
142
+ switch (node.type) {
143
+ case "paragraph":
144
+ return `<p${blockStyle(node.attrs)}>${serializeInline(node.content) || "<br>"}</p>`;
145
+ case "heading": {
146
+ const level = Math.min(6, Math.max(1, Number(node.attrs?.level ?? 1)));
147
+ return `<h${level}${blockStyle(node.attrs)}>${serializeInline(node.content)}</h${level}>`;
148
+ }
149
+ case "blockquote":
150
+ return `<blockquote class="rne-blockquote">${(node.content ?? []).map(serializeBlock).join("")}</blockquote>`;
151
+ case "horizontal_rule":
152
+ return '<hr class="rne-hr">';
153
+ case "page_break":
154
+ return '<div class="rne-page-break"></div>';
155
+ case "bullet_list":
156
+ case "ordered_list":
157
+ return serializeList(node);
158
+ case "table":
159
+ return serializeTable(node);
160
+ default:
161
+ if (node.content) return node.content.map(serializeBlock).join("");
162
+ return "";
163
+ }
164
+ }
165
+ function serializeList(node) {
166
+ const ordered = node.type === "ordered_list";
167
+ const kind = node.attrs?.kind;
168
+ const tag = ordered ? "ol" : "ul";
169
+ const order = ordered ? Number(node.attrs?.order ?? 1) : 1;
170
+ const startAttr = ordered && order !== 1 ? ` start="${order}"` : "";
171
+ const classAttr = ordered ? ' class="rne-ordered-list"' : kind === "task" ? ' class="rne-task-list" data-type="task"' : ' class="rne-bullet-list"';
172
+ const items = (node.content ?? []).map((item) => {
173
+ if (item.type !== "list_item") return "";
174
+ const checked = item.attrs?.checked;
175
+ const inner = (item.content ?? []).map(serializeBlock).join("");
176
+ if (checked === true || checked === false) {
177
+ const box = checked ? "\u2611" : "\u2610";
178
+ return `<li class="rne-task-item" data-checked="${checked}"><span class="rne-task-checkbox">${box}</span><div class="rne-task-content">${inner}</div></li>`;
179
+ }
180
+ return `<li>${inner}</li>`;
181
+ }).join("");
182
+ return `<${tag}${classAttr}${startAttr}>${items}</${tag}>`;
183
+ }
184
+ function serializeTable(node) {
185
+ const rows = (node.content ?? []).map((row) => {
186
+ if (row.type !== "table_row") return "";
187
+ const cells = (row.content ?? []).map((cell) => {
188
+ const tag = cell.type === "table_header" ? "th" : "td";
189
+ const attrs = cell.attrs ?? {};
190
+ const parts = [];
191
+ const colspan = cssInteger(attrs.colspan, 1, 100);
192
+ if (colspan && colspan > 1) parts.push(`colspan="${colspan}"`);
193
+ const rowspan = cssInteger(attrs.rowspan, 1, 100);
194
+ if (rowspan && rowspan > 1) parts.push(`rowspan="${rowspan}"`);
195
+ const styles = [];
196
+ const bg = normalizeCssColor(attrs.background);
197
+ if (bg) styles.push(`background-color: ${bg}`);
198
+ const cellAlign = cssAlign(attrs.align);
199
+ if (cellAlign) styles.push(`text-align: ${cellAlign}`);
200
+ if (styles.length) parts.push(`style="${styles.join("; ")}"`);
201
+ const attrStr = parts.length ? ` ${parts.join(" ")}` : "";
202
+ return `<${tag}${attrStr}>${(cell.content ?? []).map(serializeBlock).join("")}</${tag}>`;
203
+ }).join("");
204
+ return `<tr>${cells}</tr>`;
205
+ }).join("");
206
+ return `<table class="rne-table"><tbody>${rows}</tbody></table>`;
207
+ }
208
+ function documentToHtml(doc) {
209
+ return (doc.content ?? []).map(serializeBlock).join("\n");
210
+ }
211
+ function printStylesheet(page) {
212
+ const { width, height } = resolvePageDimensions(page);
213
+ const { top, right, bottom, left } = page.margins;
214
+ return `
215
+ @page { size: ${width}mm ${height}mm; margin: ${top}mm ${right}mm ${bottom}mm ${left}mm; }
216
+ * { box-sizing: border-box; }
217
+ html, body { margin: 0; padding: 0; }
218
+ body {
219
+ font-family: 'Times New Roman', Georgia, serif;
220
+ font-size: 12pt;
221
+ line-height: 1.5;
222
+ color: #111;
223
+ }
224
+ .rne-print-page { width: ${width - left - right}mm; margin: 0 auto; }
225
+ h1 { font-size: 2em; } h2 { font-size: 1.5em; } h3 { font-size: 1.25em; }
226
+ h4 { font-size: 1.1em; } h5 { font-size: 1em; } h6 { font-size: 0.9em; }
227
+ p { margin: 0 0 0.6em; }
228
+ blockquote.rne-blockquote { border-left: 3px solid #ccc; margin: 0 0 0.6em; padding-left: 1em; color: #444; }
229
+ hr.rne-hr { border: none; border-top: 1px solid #ccc; margin: 1em 0; }
230
+ img.rne-image { max-width: 100%; height: auto; }
231
+ table.rne-table { border-collapse: collapse; width: 100%; margin: 0 0 0.8em; }
232
+ table.rne-table td, table.rne-table th { border: 1px solid #999; padding: 4px 8px; vertical-align: top; }
233
+ table.rne-table th { background: #f0f0f0; font-weight: bold; }
234
+ ul, ol { margin: 0 0 0.6em; padding-left: 1.6em; }
235
+ ul.rne-task-list { list-style: none; padding-left: 0.4em; }
236
+ .rne-task-item { display: flex; gap: 0.4em; align-items: flex-start; }
237
+ .rne-page-break { break-after: page; page-break-after: always; height: 0; }
238
+ code { font-family: 'Courier New', monospace; background: #f3f3f3; padding: 0 2px; }
239
+ `.trim();
240
+ }
241
+ function buildPrintDocument(doc, page, title = "Document") {
242
+ const body = documentToHtml(doc);
243
+ return `<!DOCTYPE html>
244
+ <html lang="en">
245
+ <head>
246
+ <meta charset="utf-8">
247
+ <meta name="viewport" content="width=device-width, initial-scale=1">
248
+ <title>${escapeHtml(title)}</title>
249
+ <style>${printStylesheet(page)}</style>
250
+ </head>
251
+ <body>
252
+ <div class="rne-print-page">
253
+ ${body}
254
+ </div>
255
+ </body>
256
+ </html>`;
257
+ }
258
+
259
+ export { buildPrintDocument, documentToHtml, printStylesheet };
260
+ //# sourceMappingURL=chunk-7VYJDBH7.js.map
261
+ //# sourceMappingURL=chunk-7VYJDBH7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/export/html.ts"],"names":[],"mappings":"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","file":"chunk-7VYJDBH7.js","sourcesContent":["import type { DocumentJSON, PageConfig } from '../config/types';\nimport { sanitizeImageSrc, sanitizeUrl } from '../security/sanitize';\nimport { cssAlign, cssFontFamily, cssInteger, cssNumber, normalizeCssColor } from '../security/css';\nimport { resolvePageDimensions } from '../config/defaults';\n\n/** Escape text for safe inclusion in HTML element content. */\nfunction escapeHtml(value: string): string {\n return value\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;');\n}\n\n/** Escape a value for inclusion in a double-quoted HTML attribute. */\nfunction escapeAttr(value: string): string {\n return escapeHtml(value).replace(/\"/g, '&quot;');\n}\n\n/** Order in which inline marks are nested (outermost first). */\nconst MARK_ORDER = [\n 'link',\n 'textColor',\n 'highlight',\n 'fontFamily',\n 'fontSize',\n 'strong',\n 'em',\n 'underline',\n 'strikethrough',\n 'superscript',\n 'subscript',\n 'code',\n];\n\ninterface MarkJSON {\n type: string;\n attrs?: Record<string, unknown>;\n}\n\nfunction openMark(mark: MarkJSON): string {\n const attrs = mark.attrs ?? {};\n switch (mark.type) {\n case 'strong':\n return '<strong>';\n case 'em':\n return '<em>';\n case 'underline':\n return '<u>';\n case 'strikethrough':\n return '<s>';\n case 'superscript':\n return '<sup>';\n case 'subscript':\n return '<sub>';\n case 'code':\n return '<code>';\n case 'link': {\n const href = sanitizeUrl(String(attrs.href ?? '')) ?? '';\n const title = attrs.title ? ` title=\"${escapeAttr(String(attrs.title))}\"` : '';\n return `<a href=\"${escapeAttr(href)}\"${title} rel=\"noopener noreferrer nofollow\">`;\n }\n case 'fontFamily': {\n const family = cssFontFamily(attrs.family);\n return family ? `<span style=\"font-family: ${family}\">` : '<span>';\n }\n case 'fontSize': {\n const size = cssNumber(attrs.size, 1, 1638);\n return size ? `<span style=\"font-size: ${size}pt\">` : '<span>';\n }\n case 'textColor': {\n const color = normalizeCssColor(attrs.color);\n return color ? `<span style=\"color: ${color}\">` : '<span>';\n }\n case 'highlight': {\n const color = normalizeCssColor(attrs.color) ?? '#fff2a8';\n return `<mark style=\"background-color: ${color}\">`;\n }\n default:\n return '';\n }\n}\n\nfunction closeMark(mark: MarkJSON): string {\n switch (mark.type) {\n case 'strong':\n return '</strong>';\n case 'em':\n return '</em>';\n case 'underline':\n return '</u>';\n case 'strikethrough':\n return '</s>';\n case 'superscript':\n return '</sup>';\n case 'subscript':\n return '</sub>';\n case 'code':\n return '</code>';\n case 'link':\n return '</a>';\n case 'fontFamily':\n case 'fontSize':\n case 'textColor':\n return '</span>';\n case 'highlight':\n return '</mark>';\n default:\n return '';\n }\n}\n\nfunction sortMarks(marks: MarkJSON[]): MarkJSON[] {\n return [...marks].sort((a, b) => {\n const ia = MARK_ORDER.indexOf(a.type);\n const ib = MARK_ORDER.indexOf(b.type);\n return (ia === -1 ? 99 : ia) - (ib === -1 ? 99 : ib);\n });\n}\n\nfunction serializeInline(content: DocumentJSON[] | undefined): string {\n if (!content) return '';\n let out = '';\n for (const node of content) {\n if (node.type === 'text') {\n const marks = sortMarks((node.marks ?? []) as MarkJSON[]);\n let text = escapeHtml(node.text ?? '');\n for (let i = marks.length - 1; i >= 0; i--) text = openMark(marks[i]!) + text;\n for (const mark of marks) text += closeMark(mark);\n out += text;\n } else if (node.type === 'hard_break') {\n out += '<br>';\n } else if (node.type === 'image') {\n out += serializeImage(node);\n }\n }\n return out;\n}\n\nfunction serializeImage(node: DocumentJSON): string {\n const src = sanitizeImageSrc(String(node.attrs?.src ?? ''));\n if (!src) return '';\n const alt = node.attrs?.alt ? ` alt=\"${escapeAttr(String(node.attrs.alt))}\"` : ' alt=\"\"';\n const title = node.attrs?.title ? ` title=\"${escapeAttr(String(node.attrs.title))}\"` : '';\n const safeWidth = cssInteger(node.attrs?.width, 1, 4000);\n const width = safeWidth ? ` style=\"width: ${safeWidth}px\"` : '';\n return `<img class=\"rne-image\" src=\"${escapeAttr(src)}\"${alt}${title}${width}>`;\n}\n\nfunction blockStyle(attrs: Record<string, unknown> | undefined): string {\n if (!attrs) return '';\n const styles: string[] = [];\n const align = cssAlign(attrs.align);\n if (align) styles.push(`text-align: ${align}`);\n const indent = cssInteger(attrs.indent, 0, 12);\n if (indent && indent > 0) styles.push(`margin-left: ${indent * 3}em`);\n const lineHeight = cssNumber(attrs.lineHeight, 0.1, 10);\n if (lineHeight) styles.push(`line-height: ${lineHeight}`);\n return styles.length ? ` style=\"${styles.join('; ')}\"` : '';\n}\n\nfunction serializeBlock(node: DocumentJSON): string {\n switch (node.type) {\n case 'paragraph':\n return `<p${blockStyle(node.attrs)}>${serializeInline(node.content) || '<br>'}</p>`;\n case 'heading': {\n const level = Math.min(6, Math.max(1, Number(node.attrs?.level ?? 1)));\n return `<h${level}${blockStyle(node.attrs)}>${serializeInline(node.content)}</h${level}>`;\n }\n case 'blockquote':\n return `<blockquote class=\"rne-blockquote\">${(node.content ?? []).map(serializeBlock).join('')}</blockquote>`;\n case 'horizontal_rule':\n return '<hr class=\"rne-hr\">';\n case 'page_break':\n return '<div class=\"rne-page-break\"></div>';\n case 'bullet_list':\n case 'ordered_list':\n return serializeList(node);\n case 'table':\n return serializeTable(node);\n default:\n if (node.content) return node.content.map(serializeBlock).join('');\n return '';\n }\n}\n\nfunction serializeList(node: DocumentJSON): string {\n const ordered = node.type === 'ordered_list';\n const kind = node.attrs?.kind;\n const tag = ordered ? 'ol' : 'ul';\n const order = ordered ? Number(node.attrs?.order ?? 1) : 1;\n const startAttr = ordered && order !== 1 ? ` start=\"${order}\"` : '';\n const classAttr = ordered\n ? ' class=\"rne-ordered-list\"'\n : kind === 'task'\n ? ' class=\"rne-task-list\" data-type=\"task\"'\n : ' class=\"rne-bullet-list\"';\n\n const items = (node.content ?? [])\n .map((item) => {\n if (item.type !== 'list_item') return '';\n const checked = item.attrs?.checked;\n const inner = (item.content ?? []).map(serializeBlock).join('');\n if (checked === true || checked === false) {\n const box = checked ? '☑' : '☐';\n return `<li class=\"rne-task-item\" data-checked=\"${checked}\"><span class=\"rne-task-checkbox\">${box}</span><div class=\"rne-task-content\">${inner}</div></li>`;\n }\n return `<li>${inner}</li>`;\n })\n .join('');\n return `<${tag}${classAttr}${startAttr}>${items}</${tag}>`;\n}\n\nfunction serializeTable(node: DocumentJSON): string {\n const rows = (node.content ?? [])\n .map((row) => {\n if (row.type !== 'table_row') return '';\n const cells = (row.content ?? [])\n .map((cell) => {\n const tag = cell.type === 'table_header' ? 'th' : 'td';\n const attrs = cell.attrs ?? {};\n const parts: string[] = [];\n const colspan = cssInteger(attrs.colspan, 1, 100);\n if (colspan && colspan > 1) parts.push(`colspan=\"${colspan}\"`);\n const rowspan = cssInteger(attrs.rowspan, 1, 100);\n if (rowspan && rowspan > 1) parts.push(`rowspan=\"${rowspan}\"`);\n const styles: string[] = [];\n const bg = normalizeCssColor(attrs.background);\n if (bg) styles.push(`background-color: ${bg}`);\n const cellAlign = cssAlign(attrs.align);\n if (cellAlign) styles.push(`text-align: ${cellAlign}`);\n if (styles.length) parts.push(`style=\"${styles.join('; ')}\"`);\n const attrStr = parts.length ? ` ${parts.join(' ')}` : '';\n return `<${tag}${attrStr}>${(cell.content ?? []).map(serializeBlock).join('')}</${tag}>`;\n })\n .join('');\n return `<tr>${cells}</tr>`;\n })\n .join('');\n return `<table class=\"rne-table\"><tbody>${rows}</tbody></table>`;\n}\n\n/** Serialize a document (JSON) to an HTML fragment (the document body content). */\nexport function documentToHtml(doc: DocumentJSON): string {\n return (doc.content ?? []).map(serializeBlock).join('\\n');\n}\n\n/** Print-oriented stylesheet matching the on-screen document surface. */\nexport function printStylesheet(page: PageConfig): string {\n const { width, height } = resolvePageDimensions(page);\n const { top, right, bottom, left } = page.margins;\n return `\n @page { size: ${width}mm ${height}mm; margin: ${top}mm ${right}mm ${bottom}mm ${left}mm; }\n * { box-sizing: border-box; }\n html, body { margin: 0; padding: 0; }\n body {\n font-family: 'Times New Roman', Georgia, serif;\n font-size: 12pt;\n line-height: 1.5;\n color: #111;\n }\n .rne-print-page { width: ${width - left - right}mm; margin: 0 auto; }\n h1 { font-size: 2em; } h2 { font-size: 1.5em; } h3 { font-size: 1.25em; }\n h4 { font-size: 1.1em; } h5 { font-size: 1em; } h6 { font-size: 0.9em; }\n p { margin: 0 0 0.6em; }\n blockquote.rne-blockquote { border-left: 3px solid #ccc; margin: 0 0 0.6em; padding-left: 1em; color: #444; }\n hr.rne-hr { border: none; border-top: 1px solid #ccc; margin: 1em 0; }\n img.rne-image { max-width: 100%; height: auto; }\n table.rne-table { border-collapse: collapse; width: 100%; margin: 0 0 0.8em; }\n table.rne-table td, table.rne-table th { border: 1px solid #999; padding: 4px 8px; vertical-align: top; }\n table.rne-table th { background: #f0f0f0; font-weight: bold; }\n ul, ol { margin: 0 0 0.6em; padding-left: 1.6em; }\n ul.rne-task-list { list-style: none; padding-left: 0.4em; }\n .rne-task-item { display: flex; gap: 0.4em; align-items: flex-start; }\n .rne-page-break { break-after: page; page-break-after: always; height: 0; }\n code { font-family: 'Courier New', monospace; background: #f3f3f3; padding: 0 2px; }\n `.trim();\n}\n\n/**\n * Build a complete, standalone HTML document for PDF rendering — used by both\n * the client print path and the server headless-browser renderer so output is\n * consistent (F-6.4, F-6.11).\n */\nexport function buildPrintDocument(\n doc: DocumentJSON,\n page: PageConfig,\n title = 'Document',\n): string {\n const body = documentToHtml(doc);\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>${escapeHtml(title)}</title>\n<style>${printStylesheet(page)}</style>\n</head>\n<body>\n<div class=\"rne-print-page\">\n${body}\n</div>\n</body>\n</html>`;\n}\n"]}