@pyreon/document 0.0.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.
Files changed (80) hide show
  1. package/LICENSE +21 -0
  2. package/lib/analysis/index.js.html +5406 -0
  3. package/lib/chunk-ErZ26oRB.js +48 -0
  4. package/lib/confluence-Va8e7RxQ.js +192 -0
  5. package/lib/confluence-Va8e7RxQ.js.map +1 -0
  6. package/lib/csv-2c38ub-Y.js +32 -0
  7. package/lib/csv-2c38ub-Y.js.map +1 -0
  8. package/lib/discord-DAoUZqvE.js +134 -0
  9. package/lib/discord-DAoUZqvE.js.map +1 -0
  10. package/lib/dist-BsqdI2nY.js +20179 -0
  11. package/lib/dist-BsqdI2nY.js.map +1 -0
  12. package/lib/docx-CorFwEH9.js +450 -0
  13. package/lib/docx-CorFwEH9.js.map +1 -0
  14. package/lib/email-Bn_Brjdp.js +131 -0
  15. package/lib/email-Bn_Brjdp.js.map +1 -0
  16. package/lib/exceljs-BoIDUUaw.js +34377 -0
  17. package/lib/exceljs-BoIDUUaw.js.map +1 -0
  18. package/lib/google-chat-B6I017I1.js +125 -0
  19. package/lib/google-chat-B6I017I1.js.map +1 -0
  20. package/lib/html-De_iS_f0.js +151 -0
  21. package/lib/html-De_iS_f0.js.map +1 -0
  22. package/lib/index.js +619 -0
  23. package/lib/index.js.map +1 -0
  24. package/lib/markdown-BYC_3C9i.js +75 -0
  25. package/lib/markdown-BYC_3C9i.js.map +1 -0
  26. package/lib/notion-DHaQHO6P.js +187 -0
  27. package/lib/notion-DHaQHO6P.js.map +1 -0
  28. package/lib/pdf-CDPc5Itc.js +419 -0
  29. package/lib/pdf-CDPc5Itc.js.map +1 -0
  30. package/lib/pdfmake-DnmLxK4Q.js +55511 -0
  31. package/lib/pdfmake-DnmLxK4Q.js.map +1 -0
  32. package/lib/pptx-DKQU6bjq.js +252 -0
  33. package/lib/pptx-DKQU6bjq.js.map +1 -0
  34. package/lib/pptxgen.es-COcgXsyx.js +5697 -0
  35. package/lib/pptxgen.es-COcgXsyx.js.map +1 -0
  36. package/lib/slack-CJRJgkag.js +139 -0
  37. package/lib/slack-CJRJgkag.js.map +1 -0
  38. package/lib/svg-BM8biZmL.js +187 -0
  39. package/lib/svg-BM8biZmL.js.map +1 -0
  40. package/lib/teams-S99tonRG.js +176 -0
  41. package/lib/teams-S99tonRG.js.map +1 -0
  42. package/lib/telegram-CbEO_2PN.js +77 -0
  43. package/lib/telegram-CbEO_2PN.js.map +1 -0
  44. package/lib/text-B5U8ucRr.js +75 -0
  45. package/lib/text-B5U8ucRr.js.map +1 -0
  46. package/lib/types/index.d.ts +528 -0
  47. package/lib/types/index.d.ts.map +1 -0
  48. package/lib/vfs_fonts-Df1kkZ4Y.js +19 -0
  49. package/lib/vfs_fonts-Df1kkZ4Y.js.map +1 -0
  50. package/lib/whatsapp-DJ2D1jGG.js +64 -0
  51. package/lib/whatsapp-DJ2D1jGG.js.map +1 -0
  52. package/lib/xlsx-D47x-gZ5.js +199 -0
  53. package/lib/xlsx-D47x-gZ5.js.map +1 -0
  54. package/package.json +62 -0
  55. package/src/builder.ts +266 -0
  56. package/src/download.ts +76 -0
  57. package/src/env.d.ts +17 -0
  58. package/src/index.ts +98 -0
  59. package/src/nodes.ts +315 -0
  60. package/src/render.ts +222 -0
  61. package/src/renderers/confluence.ts +231 -0
  62. package/src/renderers/csv.ts +67 -0
  63. package/src/renderers/discord.ts +192 -0
  64. package/src/renderers/docx.ts +612 -0
  65. package/src/renderers/email.ts +230 -0
  66. package/src/renderers/google-chat.ts +211 -0
  67. package/src/renderers/html.ts +225 -0
  68. package/src/renderers/markdown.ts +144 -0
  69. package/src/renderers/notion.ts +264 -0
  70. package/src/renderers/pdf.ts +427 -0
  71. package/src/renderers/pptx.ts +353 -0
  72. package/src/renderers/slack.ts +192 -0
  73. package/src/renderers/svg.ts +254 -0
  74. package/src/renderers/teams.ts +234 -0
  75. package/src/renderers/telegram.ts +137 -0
  76. package/src/renderers/text.ts +154 -0
  77. package/src/renderers/whatsapp.ts +121 -0
  78. package/src/renderers/xlsx.ts +342 -0
  79. package/src/tests/document.test.ts +2920 -0
  80. package/src/types.ts +291 -0
@@ -0,0 +1,125 @@
1
+ //#region src/renderers/google-chat.ts
2
+ /**
3
+ * Google Chat renderer — outputs Card V2 JSON for Google Chat API.
4
+ * Cards can be sent via webhooks, Chat API, or Apps Script.
5
+ */
6
+ function resolveColumn(col) {
7
+ return typeof col === "string" ? { header: col } : col;
8
+ }
9
+ function getTextContent(children) {
10
+ return children.map((c) => typeof c === "string" ? c : getTextContent(c.children)).join("");
11
+ }
12
+ function nodeToWidgets(node) {
13
+ const p = node.props;
14
+ const widgets = [];
15
+ switch (node.type) {
16
+ case "document":
17
+ case "page":
18
+ case "section":
19
+ case "row":
20
+ case "column":
21
+ for (const child of node.children) if (typeof child !== "string") widgets.push(...nodeToWidgets(child));
22
+ break;
23
+ case "heading": {
24
+ const text = getTextContent(node.children);
25
+ widgets.push({ decoratedText: {
26
+ topLabel: "",
27
+ text: `<b>${text}</b>`,
28
+ wrapText: true
29
+ } });
30
+ break;
31
+ }
32
+ case "text": {
33
+ let text = getTextContent(node.children);
34
+ if (p.bold) text = `<b>${text}</b>`;
35
+ if (p.italic) text = `<i>${text}</i>`;
36
+ if (p.strikethrough) text = `<s>${text}</s>`;
37
+ widgets.push({ textParagraph: { text } });
38
+ break;
39
+ }
40
+ case "link": {
41
+ const href = p.href;
42
+ const text = getTextContent(node.children);
43
+ widgets.push({ textParagraph: { text: `<a href="${href}">${text}</a>` } });
44
+ break;
45
+ }
46
+ case "image": {
47
+ const src = p.src;
48
+ if (src.startsWith("http")) widgets.push({ image: {
49
+ imageUrl: src,
50
+ altText: p.alt ?? "Image"
51
+ } });
52
+ break;
53
+ }
54
+ case "table": {
55
+ const columns = (p.columns ?? []).map(resolveColumn);
56
+ const rows = p.rows ?? [];
57
+ const header = columns.map((c) => `<b>${c.header}</b>`).join(" | ");
58
+ const body = rows.map((row) => row.map((c) => String(c ?? "")).join(" | ")).join("\n");
59
+ widgets.push({ textParagraph: { text: `${header}\n${body}` } });
60
+ break;
61
+ }
62
+ case "list": {
63
+ const ordered = p.ordered;
64
+ const items = node.children.filter((c) => typeof c !== "string").map((item, i) => {
65
+ return `${ordered ? `${i + 1}.` : "•"} ${getTextContent(item.children)}`;
66
+ }).join("\n");
67
+ widgets.push({ textParagraph: { text: items } });
68
+ break;
69
+ }
70
+ case "code": {
71
+ const text = getTextContent(node.children);
72
+ widgets.push({ textParagraph: { text: `<font color="#333333"><code>${text}</code></font>` } });
73
+ break;
74
+ }
75
+ case "divider":
76
+ case "page-break":
77
+ widgets.push({ divider: {} });
78
+ break;
79
+ case "spacer": break;
80
+ case "button": {
81
+ const href = p.href;
82
+ const text = getTextContent(node.children);
83
+ widgets.push({ buttonList: { buttons: [{
84
+ text,
85
+ onClick: { openLink: { url: href } },
86
+ color: {
87
+ red: .31,
88
+ green: .27,
89
+ blue: .89,
90
+ alpha: 1
91
+ }
92
+ }] } });
93
+ break;
94
+ }
95
+ case "quote": {
96
+ const text = getTextContent(node.children);
97
+ widgets.push({ textParagraph: { text: `<i>"${text}"</i>` } });
98
+ break;
99
+ }
100
+ }
101
+ return widgets;
102
+ }
103
+ const googleChatRenderer = { async render(node, _options) {
104
+ const widgets = nodeToWidgets(node);
105
+ let title = node.props.title ?? "";
106
+ if (!title) {
107
+ const firstHeading = node.children.find((c) => typeof c !== "string" && c.type === "heading");
108
+ if (firstHeading) title = getTextContent(firstHeading.children);
109
+ }
110
+ const card = { cardsV2: [{
111
+ cardId: "document",
112
+ card: {
113
+ header: title ? {
114
+ title,
115
+ subtitle: node.props.subject ?? void 0
116
+ } : void 0,
117
+ sections: [{ widgets }]
118
+ }
119
+ }] };
120
+ return JSON.stringify(card, null, 2);
121
+ } };
122
+
123
+ //#endregion
124
+ export { googleChatRenderer };
125
+ //# sourceMappingURL=google-chat-B6I017I1.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google-chat-B6I017I1.js","names":[],"sources":["../src/renderers/google-chat.ts"],"sourcesContent":["import type {\n DocChild,\n DocNode,\n DocumentRenderer,\n RenderOptions,\n TableColumn,\n} from '../types'\n\n/**\n * Google Chat renderer — outputs Card V2 JSON for Google Chat API.\n * Cards can be sent via webhooks, Chat API, or Apps Script.\n */\n\nfunction resolveColumn(col: string | TableColumn): TableColumn {\n return typeof col === 'string' ? { header: col } : col\n}\n\nfunction getTextContent(children: DocChild[]): string {\n return children\n .map((c) =>\n typeof c === 'string' ? c : getTextContent((c as DocNode).children),\n )\n .join('')\n}\n\ninterface CardWidget {\n [key: string]: unknown\n}\n\nfunction nodeToWidgets(node: DocNode): CardWidget[] {\n const p = node.props\n const widgets: CardWidget[] = []\n\n switch (node.type) {\n case 'document':\n case 'page':\n case 'section':\n case 'row':\n case 'column':\n for (const child of node.children) {\n if (typeof child !== 'string') {\n widgets.push(...nodeToWidgets(child))\n }\n }\n break\n\n case 'heading': {\n const text = getTextContent(node.children)\n widgets.push({\n decoratedText: {\n topLabel: '',\n text: `<b>${text}</b>`,\n wrapText: true,\n },\n })\n break\n }\n\n case 'text': {\n let text = getTextContent(node.children)\n if (p.bold) text = `<b>${text}</b>`\n if (p.italic) text = `<i>${text}</i>`\n if (p.strikethrough) text = `<s>${text}</s>`\n widgets.push({\n textParagraph: { text },\n })\n break\n }\n\n case 'link': {\n const href = p.href as string\n const text = getTextContent(node.children)\n widgets.push({\n textParagraph: { text: `<a href=\"${href}\">${text}</a>` },\n })\n break\n }\n\n case 'image': {\n const src = p.src as string\n if (src.startsWith('http')) {\n widgets.push({\n image: {\n imageUrl: src,\n altText: (p.alt as string) ?? 'Image',\n },\n })\n }\n break\n }\n\n case 'table': {\n const columns = ((p.columns ?? []) as (string | TableColumn)[]).map(\n resolveColumn,\n )\n const rows = (p.rows ?? []) as (string | number)[][]\n\n // Google Chat Cards don't have native tables — use grid or formatted text\n const header = columns.map((c) => `<b>${c.header}</b>`).join(' | ')\n const body = rows\n .map((row) => row.map((c) => String(c ?? '')).join(' | '))\n .join('\\n')\n\n widgets.push({\n textParagraph: { text: `${header}\\n${body}` },\n })\n break\n }\n\n case 'list': {\n const ordered = p.ordered as boolean | undefined\n const items = node.children\n .filter((c): c is DocNode => typeof c !== 'string')\n .map((item, i) => {\n const prefix = ordered ? `${i + 1}.` : '•'\n return `${prefix} ${getTextContent(item.children)}`\n })\n .join('\\n')\n widgets.push({\n textParagraph: { text: items },\n })\n break\n }\n\n case 'code': {\n const text = getTextContent(node.children)\n widgets.push({\n textParagraph: {\n text: `<font color=\"#333333\"><code>${text}</code></font>`,\n },\n })\n break\n }\n\n case 'divider':\n case 'page-break':\n widgets.push({ divider: {} })\n break\n\n case 'spacer':\n // No direct equivalent — skip\n break\n\n case 'button': {\n const href = p.href as string\n const text = getTextContent(node.children)\n widgets.push({\n buttonList: {\n buttons: [\n {\n text,\n onClick: { openLink: { url: href } },\n color: {\n red: 0.31,\n green: 0.27,\n blue: 0.89,\n alpha: 1,\n },\n },\n ],\n },\n })\n break\n }\n\n case 'quote': {\n const text = getTextContent(node.children)\n widgets.push({\n textParagraph: { text: `<i>\"${text}\"</i>` },\n })\n break\n }\n }\n\n return widgets\n}\n\nexport const googleChatRenderer: DocumentRenderer = {\n async render(node: DocNode, _options?: RenderOptions): Promise<string> {\n const widgets = nodeToWidgets(node)\n\n // Extract title from first heading or document title\n let title = (node.props.title as string) ?? ''\n if (!title) {\n const firstHeading = node.children.find(\n (c): c is DocNode => typeof c !== 'string' && c.type === 'heading',\n )\n if (firstHeading) title = getTextContent(firstHeading.children)\n }\n\n const card = {\n cardsV2: [\n {\n cardId: 'document',\n card: {\n header: title\n ? { title, subtitle: (node.props.subject as string) ?? undefined }\n : undefined,\n sections: [\n {\n widgets,\n },\n ],\n },\n },\n ],\n }\n\n return JSON.stringify(card, null, 2)\n },\n}\n"],"mappings":"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"}
@@ -0,0 +1,151 @@
1
+ //#region src/renderers/html.ts
2
+ function escapeHtml(str) {
3
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
4
+ }
5
+ function resolveColumn(col) {
6
+ return typeof col === "string" ? { header: col } : col;
7
+ }
8
+ function styleStr(styles) {
9
+ const parts = [];
10
+ for (const [k, v] of Object.entries(styles)) if (v != null && v !== "") {
11
+ const prop = k.replace(/([A-Z])/g, "-$1").toLowerCase();
12
+ parts.push(`${prop}:${typeof v === "number" ? `${v}px` : v}`);
13
+ }
14
+ return parts.length > 0 ? ` style="${parts.join(";")}"` : "";
15
+ }
16
+ function padStr(pad) {
17
+ if (pad == null) return void 0;
18
+ if (typeof pad === "number") return `${pad}px`;
19
+ if (pad.length === 2) return `${pad[0]}px ${pad[1]}px`;
20
+ return `${pad[0]}px ${pad[1]}px ${pad[2]}px ${pad[3]}px`;
21
+ }
22
+ function renderChild(child) {
23
+ if (typeof child === "string") return escapeHtml(child);
24
+ return renderNode(child);
25
+ }
26
+ function renderChildren(children) {
27
+ return children.map(renderChild).join("");
28
+ }
29
+ function renderNode(node) {
30
+ const p = node.props;
31
+ switch (node.type) {
32
+ case "document": return `<!DOCTYPE html><html lang="${p.language ?? "en"}"><head><meta charset="utf-8">${p.title ? `<title>${escapeHtml(p.title)}</title>` : ""}<meta name="viewport" content="width=device-width,initial-scale=1"></head><body>${renderChildren(node.children)}</body></html>`;
33
+ case "page": {
34
+ const margin = padStr(p.margin);
35
+ return `<div${styleStr({
36
+ maxWidth: "800px",
37
+ margin: margin ?? "0 auto",
38
+ padding: margin ?? "40px"
39
+ })}>${renderChildren(node.children)}</div>`;
40
+ }
41
+ case "section": {
42
+ const dir = p.direction ?? "column";
43
+ return `<div${styleStr({
44
+ display: dir === "row" ? "flex" : "block",
45
+ flexDirection: dir === "row" ? "row" : void 0,
46
+ gap: p.gap,
47
+ padding: padStr(p.padding),
48
+ background: p.background,
49
+ borderRadius: p.borderRadius
50
+ })}>${renderChildren(node.children)}</div>`;
51
+ }
52
+ case "row": return `<div${styleStr({
53
+ display: "flex",
54
+ gap: p.gap,
55
+ alignItems: p.align
56
+ })}>${renderChildren(node.children)}</div>`;
57
+ case "column": return `<div${styleStr({
58
+ flex: p.width ? void 0 : "1",
59
+ width: p.width,
60
+ textAlign: p.align
61
+ })}>${renderChildren(node.children)}</div>`;
62
+ case "heading": {
63
+ const level = p.level ?? 1;
64
+ const tag = `h${Math.min(Math.max(level, 1), 6)}`;
65
+ return `<${tag}${styleStr({
66
+ color: p.color,
67
+ textAlign: p.align
68
+ })}>${renderChildren(node.children)}</${tag}>`;
69
+ }
70
+ case "text": return `<p${styleStr({
71
+ fontSize: p.size,
72
+ color: p.color,
73
+ fontWeight: p.bold ? "bold" : void 0,
74
+ fontStyle: p.italic ? "italic" : void 0,
75
+ textDecoration: p.underline ? "underline" : p.strikethrough ? "line-through" : void 0,
76
+ textAlign: p.align,
77
+ lineHeight: p.lineHeight
78
+ })}>${renderChildren(node.children)}</p>`;
79
+ case "link": return `<a href="${escapeHtml(p.href)}"${styleStr({ color: p.color })}>${renderChildren(node.children)}</a>`;
80
+ case "image": {
81
+ const alignStyle = p.align === "center" ? "display:block;margin:0 auto" : p.align === "right" ? "display:block;margin-left:auto" : "";
82
+ const img = `<img src="${escapeHtml(p.src)}"${p.width ? ` width="${p.width}"` : ""}${p.height ? ` height="${p.height}"` : ""}${p.alt ? ` alt="${escapeHtml(p.alt)}"` : ""}${alignStyle ? ` style="${alignStyle}"` : ""} />`;
83
+ if (p.caption) return `<figure${p.align === "center" ? " style=\"text-align:center\"" : ""}>${img}<figcaption>${escapeHtml(p.caption)}</figcaption></figure>`;
84
+ return img;
85
+ }
86
+ case "table": {
87
+ const columns = (p.columns ?? []).map(resolveColumn);
88
+ const rows = p.rows ?? [];
89
+ const hs = p.headerStyle;
90
+ const striped = p.striped;
91
+ const bordered = p.bordered;
92
+ let html = `<table style="width:100%;${bordered ? "border:1px solid #ddd;border-collapse:collapse;" : "border-collapse:collapse;"}">`;
93
+ if (p.caption) html += `<caption>${escapeHtml(p.caption)}</caption>`;
94
+ html += "<thead><tr>";
95
+ for (const col of columns) {
96
+ const cellBorder = bordered ? "border:1px solid #ddd;" : "";
97
+ const bgStyle = hs?.background ? `background:${hs.background};` : "";
98
+ const colorStyle = hs?.color ? `color:${hs.color};` : "";
99
+ const fontStyle = hs?.bold !== false ? "font-weight:bold;" : "";
100
+ const alignStyle = col.align ? `text-align:${col.align};` : "";
101
+ const widthStyle = col.width ? `width:${typeof col.width === "number" ? `${col.width}px` : col.width};` : "";
102
+ html += `<th style="${cellBorder}${bgStyle}${colorStyle}${fontStyle}${alignStyle}${widthStyle}padding:8px">${escapeHtml(col.header)}</th>`;
103
+ }
104
+ html += "</tr></thead>";
105
+ html += "<tbody>";
106
+ for (let i = 0; i < rows.length; i++) {
107
+ const rowBg = striped && i % 2 === 1 ? " style=\"background:#f9f9f9\"" : "";
108
+ html += `<tr${rowBg}>`;
109
+ for (let j = 0; j < columns.length; j++) {
110
+ const cellBorder = bordered ? "border:1px solid #ddd;" : "";
111
+ const col = columns[j];
112
+ const alignStyle = col?.align ? `text-align:${col.align};` : "";
113
+ html += `<td style="${cellBorder}${alignStyle}padding:8px">${escapeHtml(String(rows[i]?.[j] ?? ""))}</td>`;
114
+ }
115
+ html += "</tr>";
116
+ }
117
+ html += "</tbody></table>";
118
+ return html;
119
+ }
120
+ case "list": {
121
+ const tag = p.ordered ? "ol" : "ul";
122
+ return `<${tag}>${renderChildren(node.children)}</${tag}>`;
123
+ }
124
+ case "list-item": return `<li>${renderChildren(node.children)}</li>`;
125
+ case "code": return `<pre style="background:#f5f5f5;padding:12px;border-radius:4px;overflow-x:auto"><code>${escapeHtml(renderChildren(node.children))}</code></pre>`;
126
+ case "divider": {
127
+ const color = p.color ?? "#ddd";
128
+ return `<hr style="border:none;border-top:${p.thickness ?? 1}px solid ${color};margin:16px 0" />`;
129
+ }
130
+ case "page-break": return "<div style=\"page-break-after:always;break-after:page\"></div>";
131
+ case "spacer": return `<div style="height:${p.height}px"></div>`;
132
+ case "button": {
133
+ const bg = p.background ?? "#4f46e5";
134
+ const color = p.color ?? "#fff";
135
+ const radius = p.borderRadius ?? 4;
136
+ const pad = padStr(p.padding ?? [12, 24]);
137
+ return `<div style="text-align:${p.align ?? "left"}"><a href="${escapeHtml(p.href)}" style="display:inline-block;background:${bg};color:${color};padding:${pad};border-radius:${radius}px;text-decoration:none;font-weight:bold">${renderChildren(node.children)}</a></div>`;
138
+ }
139
+ case "quote": return `<blockquote style="margin:0;padding:12px 20px;border-left:4px solid ${p.borderColor ?? "#ddd"};color:#555">${renderChildren(node.children)}</blockquote>`;
140
+ default: return renderChildren(node.children);
141
+ }
142
+ }
143
+ const htmlRenderer = { async render(node, options) {
144
+ let html = renderNode(node);
145
+ if (options?.direction === "rtl") html = html.replace("<body>", "<body dir=\"rtl\" style=\"direction:rtl\">");
146
+ return html;
147
+ } };
148
+
149
+ //#endregion
150
+ export { htmlRenderer };
151
+ //# sourceMappingURL=html-De_iS_f0.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html-De_iS_f0.js","names":[],"sources":["../src/renderers/html.ts"],"sourcesContent":["import type {\n DocChild,\n DocNode,\n DocumentRenderer,\n RenderOptions,\n TableColumn,\n} from '../types'\n\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n}\n\nfunction resolveColumn(col: string | TableColumn): TableColumn {\n return typeof col === 'string' ? { header: col } : col\n}\n\nfunction styleStr(styles: Record<string, string | number | undefined>): string {\n const parts: string[] = []\n for (const [k, v] of Object.entries(styles)) {\n if (v != null && v !== '') {\n const prop = k.replace(/([A-Z])/g, '-$1').toLowerCase()\n parts.push(`${prop}:${typeof v === 'number' ? `${v}px` : v}`)\n }\n }\n return parts.length > 0 ? ` style=\"${parts.join(';')}\"` : ''\n}\n\nfunction padStr(\n pad: number | [number, number] | [number, number, number, number] | undefined,\n): string | undefined {\n if (pad == null) return undefined\n if (typeof pad === 'number') return `${pad}px`\n if (pad.length === 2) return `${pad[0]}px ${pad[1]}px`\n return `${pad[0]}px ${pad[1]}px ${pad[2]}px ${pad[3]}px`\n}\n\nfunction renderChild(child: DocChild): string {\n if (typeof child === 'string') return escapeHtml(child)\n return renderNode(child)\n}\n\nfunction renderChildren(children: DocChild[]): string {\n return children.map(renderChild).join('')\n}\n\nfunction renderNode(node: DocNode): string {\n const p = node.props\n\n switch (node.type) {\n case 'document': {\n const lang = (p.language as string) ?? 'en'\n const title = p.title\n ? `<title>${escapeHtml(p.title as string)}</title>`\n : ''\n return `<!DOCTYPE html><html lang=\"${lang}\"><head><meta charset=\"utf-8\">${title}<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"></head><body>${renderChildren(node.children)}</body></html>`\n }\n\n case 'page': {\n const margin = padStr(p.margin as PageMargin)\n return `<div${styleStr({ maxWidth: '800px', margin: margin ?? '0 auto', padding: margin ?? '40px' })}>${renderChildren(node.children)}</div>`\n }\n\n case 'section': {\n const dir = (p.direction as string) ?? 'column'\n return `<div${styleStr({\n display: dir === 'row' ? 'flex' : 'block',\n flexDirection: dir === 'row' ? 'row' : undefined,\n gap: p.gap as number | undefined,\n padding: padStr(p.padding as PageMargin),\n background: p.background as string | undefined,\n borderRadius: p.borderRadius as number | undefined,\n })}>${renderChildren(node.children)}</div>`\n }\n\n case 'row':\n return `<div${styleStr({ display: 'flex', gap: p.gap as number | undefined, alignItems: p.align as string | undefined })}>${renderChildren(node.children)}</div>`\n\n case 'column':\n return `<div${styleStr({ flex: p.width ? undefined : '1', width: p.width as string | undefined, textAlign: p.align as string | undefined })}>${renderChildren(node.children)}</div>`\n\n case 'heading': {\n const level = (p.level as number) ?? 1\n const tag = `h${Math.min(Math.max(level, 1), 6)}`\n return `<${tag}${styleStr({ color: p.color as string | undefined, textAlign: p.align as string | undefined })}>${renderChildren(node.children)}</${tag}>`\n }\n\n case 'text': {\n return `<p${styleStr({\n fontSize: p.size as number | undefined,\n color: p.color as string | undefined,\n fontWeight: p.bold ? 'bold' : undefined,\n fontStyle: p.italic ? 'italic' : undefined,\n textDecoration: p.underline\n ? 'underline'\n : p.strikethrough\n ? 'line-through'\n : undefined,\n textAlign: p.align as string | undefined,\n lineHeight: p.lineHeight as number | undefined,\n })}>${renderChildren(node.children)}</p>`\n }\n\n case 'link':\n return `<a href=\"${escapeHtml(p.href as string)}\"${styleStr({ color: p.color as string | undefined })}>${renderChildren(node.children)}</a>`\n\n case 'image': {\n const alignStyle =\n p.align === 'center'\n ? 'display:block;margin:0 auto'\n : p.align === 'right'\n ? 'display:block;margin-left:auto'\n : ''\n const img = `<img src=\"${escapeHtml(p.src as string)}\"${p.width ? ` width=\"${p.width}\"` : ''}${p.height ? ` height=\"${p.height}\"` : ''}${p.alt ? ` alt=\"${escapeHtml(p.alt as string)}\"` : ''}${alignStyle ? ` style=\"${alignStyle}\"` : ''} />`\n if (p.caption) {\n return `<figure${p.align === 'center' ? ' style=\"text-align:center\"' : ''}>${img}<figcaption>${escapeHtml(p.caption as string)}</figcaption></figure>`\n }\n return img\n }\n\n case 'table': {\n const columns = ((p.columns ?? []) as (string | TableColumn)[]).map(\n resolveColumn,\n )\n const rows = (p.rows ?? []) as (string | number)[][]\n const hs = p.headerStyle as\n | { background?: string; color?: string; bold?: boolean }\n | undefined\n const striped = p.striped as boolean | undefined\n const bordered = p.bordered as boolean | undefined\n const borderStyle = bordered\n ? 'border:1px solid #ddd;border-collapse:collapse;'\n : 'border-collapse:collapse;'\n\n let html = `<table style=\"width:100%;${borderStyle}\">`\n if (p.caption)\n html += `<caption>${escapeHtml(p.caption as string)}</caption>`\n\n html += '<thead><tr>'\n for (const col of columns) {\n const cellBorder = bordered ? 'border:1px solid #ddd;' : ''\n const bgStyle = hs?.background ? `background:${hs.background};` : ''\n const colorStyle = hs?.color ? `color:${hs.color};` : ''\n const fontStyle = hs?.bold !== false ? 'font-weight:bold;' : ''\n const alignStyle = col.align ? `text-align:${col.align};` : ''\n const widthStyle = col.width\n ? `width:${typeof col.width === 'number' ? `${col.width}px` : col.width};`\n : ''\n html += `<th style=\"${cellBorder}${bgStyle}${colorStyle}${fontStyle}${alignStyle}${widthStyle}padding:8px\">${escapeHtml(col.header)}</th>`\n }\n html += '</tr></thead>'\n\n html += '<tbody>'\n for (let i = 0; i < rows.length; i++) {\n const rowBg =\n striped && i % 2 === 1 ? ' style=\"background:#f9f9f9\"' : ''\n html += `<tr${rowBg}>`\n for (let j = 0; j < columns.length; j++) {\n const cellBorder = bordered ? 'border:1px solid #ddd;' : ''\n const col = columns[j]\n const alignStyle = col?.align ? `text-align:${col.align};` : ''\n html += `<td style=\"${cellBorder}${alignStyle}padding:8px\">${escapeHtml(String(rows[i]?.[j] ?? ''))}</td>`\n }\n html += '</tr>'\n }\n html += '</tbody></table>'\n return html\n }\n\n case 'list': {\n const tag = p.ordered ? 'ol' : 'ul'\n return `<${tag}>${renderChildren(node.children)}</${tag}>`\n }\n\n case 'list-item':\n return `<li>${renderChildren(node.children)}</li>`\n\n case 'code':\n return `<pre style=\"background:#f5f5f5;padding:12px;border-radius:4px;overflow-x:auto\"><code>${escapeHtml(renderChildren(node.children))}</code></pre>`\n\n case 'divider': {\n const color = (p.color as string) ?? '#ddd'\n const thickness = (p.thickness as number) ?? 1\n return `<hr style=\"border:none;border-top:${thickness}px solid ${color};margin:16px 0\" />`\n }\n\n case 'page-break':\n return '<div style=\"page-break-after:always;break-after:page\"></div>'\n\n case 'spacer':\n return `<div style=\"height:${p.height}px\"></div>`\n\n case 'button': {\n const bg = (p.background as string) ?? '#4f46e5'\n const color = (p.color as string) ?? '#fff'\n const radius = (p.borderRadius as number) ?? 4\n const pad = padStr((p.padding ?? [12, 24]) as [number, number])\n const align = (p.align as string) ?? 'left'\n return `<div style=\"text-align:${align}\"><a href=\"${escapeHtml(p.href as string)}\" style=\"display:inline-block;background:${bg};color:${color};padding:${pad};border-radius:${radius}px;text-decoration:none;font-weight:bold\">${renderChildren(node.children)}</a></div>`\n }\n\n case 'quote': {\n const borderColor = (p.borderColor as string) ?? '#ddd'\n return `<blockquote style=\"margin:0;padding:12px 20px;border-left:4px solid ${borderColor};color:#555\">${renderChildren(node.children)}</blockquote>`\n }\n\n default:\n return renderChildren(node.children)\n }\n}\n\ntype PageMargin = number | [number, number] | [number, number, number, number]\n\nexport const htmlRenderer: DocumentRenderer = {\n async render(node: DocNode, options?: RenderOptions): Promise<string> {\n let html = renderNode(node)\n if (options?.direction === 'rtl') {\n html = html.replace('<body>', '<body dir=\"rtl\" style=\"direction:rtl\">')\n }\n return html\n },\n}\n"],"mappings":"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"}