@pyreon/document 0.10.0 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/analysis/index.js.html +1 -1
- package/lib/confluence-Bd3ua1Ut.js.map +1 -1
- package/lib/csv-COrS4qdy.js.map +1 -1
- package/lib/discord-BLUnkEh9.js.map +1 -1
- package/lib/{dist-BsqdI2nY.js → dist-CYL41kqQ.js} +2 -2
- package/lib/dist-CYL41kqQ.js.map +1 -0
- package/lib/{docx-BEBOihjl.js → docx-uNAel545.js} +7 -2
- package/lib/docx-uNAel545.js.map +1 -0
- package/lib/email-D0bbfWq4.js.map +1 -1
- package/lib/{exceljs-BoIDUUaw.js → exceljs-BYETsesT.js} +314 -314
- package/lib/exceljs-BYETsesT.js.map +1 -0
- package/lib/google-chat-CkKCBUWC.js.map +1 -1
- package/lib/html-B5biprN2.js.map +1 -1
- package/lib/index.js +17 -8
- package/lib/index.js.map +1 -1
- package/lib/markdown-CdtlFGC0.js.map +1 -1
- package/lib/notion-iG2C5bEY.js.map +1 -1
- package/lib/{pdf-DIUQUEdj.js → pdf-IuBgTb3T.js} +9 -3
- package/lib/pdf-IuBgTb3T.js.map +1 -0
- package/lib/{pdfmake-DnmLxK4Q.js → pdfmake-CKMX5URW.js} +2 -4
- package/lib/pdfmake-CKMX5URW.js.map +1 -0
- package/lib/{pptx-Dd33oL3_.js → pptx-DXiMiYFM.js} +7 -2
- package/lib/pptx-DXiMiYFM.js.map +1 -0
- package/lib/{pptxgen.es-COcgXsyx.js → pptxgen.es-FsqHs8mD.js} +3 -6
- package/lib/pptxgen.es-FsqHs8mD.js.map +1 -0
- package/lib/sanitize-O_3j1mNJ.js.map +1 -1
- package/lib/slack-BI3EQwYm.js.map +1 -1
- package/lib/svg-BKxumy-p.js.map +1 -1
- package/lib/teams-Cwz9lce0.js.map +1 -1
- package/lib/telegram-gYFqyMXb.js.map +1 -1
- package/lib/text-l1XNXBOC.js.map +1 -1
- package/lib/types/index.d.ts +43 -39
- package/lib/types/index.d.ts.map +1 -1
- package/lib/{vfs_fonts-Df1kkZ4Y.js → vfs_fonts-Cap07Jg3.js} +2 -2
- package/lib/vfs_fonts-Cap07Jg3.js.map +1 -0
- package/lib/whatsapp-CjSGoOKx.js.map +1 -1
- package/lib/{xlsx-Bb5TWyXQ.js → xlsx-Cvu4LBNy.js} +8 -2
- package/lib/xlsx-Cvu4LBNy.js.map +1 -0
- package/package.json +19 -7
- package/src/builder.ts +53 -44
- package/src/download.ts +32 -36
- package/src/env.d.ts +3 -17
- package/src/index.ts +6 -8
- package/src/nodes.ts +45 -45
- package/src/render.ts +45 -118
- package/src/renderers/confluence.ts +64 -80
- package/src/renderers/csv.ts +11 -18
- package/src/renderers/discord.ts +38 -50
- package/src/renderers/docx.ts +78 -120
- package/src/renderers/email.ts +73 -92
- package/src/renderers/google-chat.ts +35 -47
- package/src/renderers/html.ts +78 -101
- package/src/renderers/markdown.ts +43 -53
- package/src/renderers/notion.ts +63 -85
- package/src/renderers/pdf.ts +92 -115
- package/src/renderers/pptx.ts +60 -66
- package/src/renderers/slack.ts +49 -61
- package/src/renderers/svg.ts +49 -63
- package/src/renderers/teams.ts +68 -80
- package/src/renderers/telegram.ts +40 -54
- package/src/renderers/text.ts +44 -66
- package/src/renderers/whatsapp.ts +34 -48
- package/src/renderers/xlsx.ts +47 -61
- package/src/sanitize.ts +21 -25
- package/src/tests/document.test.ts +1337 -1385
- package/src/tests/stress.test.ts +111 -111
- package/src/types.ts +66 -65
- package/lib/dist-BsqdI2nY.js.map +0 -1
- package/lib/docx-BEBOihjl.js.map +0 -1
- package/lib/exceljs-BoIDUUaw.js.map +0 -1
- package/lib/pdf-DIUQUEdj.js.map +0 -1
- package/lib/pdfmake-DnmLxK4Q.js.map +0 -1
- package/lib/pptx-Dd33oL3_.js.map +0 -1
- package/lib/pptxgen.es-COcgXsyx.js.map +0 -1
- package/lib/vfs_fonts-Df1kkZ4Y.js.map +0 -1
- package/lib/xlsx-Bb5TWyXQ.js.map +0 -1
|
@@ -290,7 +290,12 @@ function renderButtonOrQuote(ctx, n) {
|
|
|
290
290
|
}));
|
|
291
291
|
}
|
|
292
292
|
const docxRenderer = { async render(node, _options) {
|
|
293
|
-
|
|
293
|
+
let docx;
|
|
294
|
+
try {
|
|
295
|
+
docx = await import("./dist-CYL41kqQ.js");
|
|
296
|
+
} catch {
|
|
297
|
+
throw new Error("[@pyreon/document] DOCX renderer requires \"docx\" package. Install it: bun add docx");
|
|
298
|
+
}
|
|
294
299
|
const children = [];
|
|
295
300
|
let listCounter = 0;
|
|
296
301
|
function alignmentMap(align) {
|
|
@@ -448,4 +453,4 @@ const docxRenderer = { async render(node, _options) {
|
|
|
448
453
|
|
|
449
454
|
//#endregion
|
|
450
455
|
export { docxRenderer };
|
|
451
|
-
//# sourceMappingURL=docx-
|
|
456
|
+
//# sourceMappingURL=docx-uNAel545.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docx-uNAel545.js","names":[],"sources":["../src/renderers/docx.ts"],"sourcesContent":["import { sanitizeHref, sanitizeXmlColor } from \"../sanitize\"\nimport type {\n DocChild,\n DocNode,\n DocumentRenderer,\n PageOrientation,\n PageSize,\n RenderOptions,\n TableColumn,\n} from \"../types\"\n\n/**\n * DOCX renderer — lazy-loads the 'docx' npm package on first use.\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) => (typeof c === \"string\" ? c : getTextContent((c as DocNode).children)))\n .join(\"\")\n}\n\n/** Parse a data URL and return the base64 data and media type, or null for external URLs. */\nfunction parseDataUrl(src: string): { data: string; mime: string } | null {\n const match = src.match(/^data:(image\\/[^;]+);base64,(.+)$/)\n if (!match) return null\n return { mime: match[1]!, data: match[2]! }\n}\n\n/** Convert page size name to DOCX page dimensions in twips (1 inch = 1440 twips). */\nfunction getPageSize(\n size?: PageSize,\n orientation?: PageOrientation,\n): { width: number; height: number } | undefined {\n if (!size) return undefined\n const sizes: Record<string, { width: number; height: number }> = {\n A4: { width: 11906, height: 16838 },\n A3: { width: 16838, height: 23811 },\n A5: { width: 8391, height: 11906 },\n letter: { width: 12240, height: 15840 },\n legal: { width: 12240, height: 20160 },\n tabloid: { width: 15840, height: 24480 },\n }\n const dims = sizes[size]\n if (!dims) return undefined\n if (orientation === \"landscape\") {\n return { width: dims.height, height: dims.width }\n }\n return dims\n}\n\n/** Convert margin prop to DOCX section margin (in twips, 1pt ~= 20 twips). */\nfunction getPageMargins(\n margin?: number | [number, number] | [number, number, number, number],\n): object | undefined {\n if (margin == null) return undefined\n if (typeof margin === \"number\") {\n const twips = margin * 20\n return { top: twips, right: twips, bottom: twips, left: twips }\n }\n if (margin.length === 2) {\n return {\n top: margin[0] * 20,\n right: margin[1] * 20,\n bottom: margin[0] * 20,\n left: margin[1] * 20,\n }\n }\n return {\n top: margin[0] * 20,\n right: margin[1] * 20,\n bottom: margin[2] * 20,\n left: margin[3] * 20,\n }\n}\n\n/** Map percentage column width to DOCX table column width. */\nfunction getColumnWidth(width?: number | string): { size: number; type: unknown } | undefined {\n if (width == null) return undefined\n if (typeof width === \"number\") return undefined\n const match = width.match(/^(\\d+)%$/)\n if (!match) return undefined\n return { size: Number.parseInt(match[1]!, 10) * 100, type: \"pct\" as unknown }\n}\n\n/** Shared context passed to per-node-type render helpers. */\ninterface DocxCtx {\n docx: typeof import(\"docx\")\n children: unknown[]\n alignmentMap: (align?: string) => unknown\n processListItems: (n: DocNode, listRef: string, level: number, ordered: boolean) => void\n nextListId: () => string\n}\n\nfunction renderHeading(ctx: DocxCtx, n: DocNode): void {\n const { docx, children, alignmentMap } = ctx\n const p = n.props\n const level = (p.level as number) ?? 1\n const headingMap: Record<number, unknown> = {\n 1: docx.HeadingLevel.HEADING_1,\n 2: docx.HeadingLevel.HEADING_2,\n 3: docx.HeadingLevel.HEADING_3,\n 4: docx.HeadingLevel.HEADING_4,\n 5: docx.HeadingLevel.HEADING_5,\n 6: docx.HeadingLevel.HEADING_6,\n }\n children.push(\n new docx.Paragraph({\n heading: (headingMap[level] ?? docx.HeadingLevel.HEADING_1) as any,\n children: [\n new docx.TextRun({\n text: getTextContent(n.children),\n bold: true,\n color: sanitizeXmlColor(p.color as string),\n }),\n ],\n alignment: alignmentMap(p.align as string) as any,\n }),\n )\n}\n\nfunction renderTextNode(ctx: DocxCtx, n: DocNode): void {\n const { docx, children, alignmentMap } = ctx\n const p = n.props\n children.push(\n new docx.Paragraph({\n children: [\n new docx.TextRun({\n text: getTextContent(n.children),\n ...(p.bold != null ? { bold: p.bold as boolean } : {}),\n ...(p.italic != null ? { italics: p.italic as boolean } : {}),\n ...(p.underline ? { underline: {} } : {}),\n ...(p.strikethrough != null ? { strike: p.strikethrough as boolean } : {}),\n ...(p.size != null ? { size: (p.size as number) * 2 } : {}),\n color: sanitizeXmlColor(p.color as string, \"333333\"),\n }),\n ],\n alignment: alignmentMap(p.align as string) as any,\n spacing: { after: 120 },\n }),\n )\n}\n\nfunction renderLink(ctx: DocxCtx, n: DocNode): void {\n const { docx, children } = ctx\n const p = n.props\n children.push(\n new docx.Paragraph({\n children: [\n new docx.ExternalHyperlink({\n link: sanitizeHref(p.href as string),\n children: [\n new docx.TextRun({\n text: getTextContent(n.children),\n color: sanitizeXmlColor(p.color as string, \"4f46e5\"),\n underline: { type: docx.UnderlineType.SINGLE },\n }),\n ],\n }),\n ],\n }),\n )\n}\n\nfunction renderImage(ctx: DocxCtx, n: DocNode): void {\n const { docx, children, alignmentMap } = ctx\n const p = n.props\n const src = p.src as string\n const parsed = parseDataUrl(src)\n\n if (parsed) {\n const imgWidth = (p.width as number) ?? 300\n const imgHeight = (p.height as number) ?? 200\n children.push(\n new docx.Paragraph({\n children: [\n new docx.ImageRun({\n data: Buffer.from(parsed.data, \"base64\"),\n transformation: { width: imgWidth, height: imgHeight },\n type: parsed.mime === \"image/png\" ? \"png\" : \"jpg\",\n }),\n ],\n alignment: alignmentMap(p.align as string) as any,\n }),\n )\n if (p.caption) {\n children.push(\n new docx.Paragraph({\n children: [\n new docx.TextRun({\n text: p.caption as string,\n italics: true,\n size: 20,\n color: \"666666\",\n }),\n ],\n alignment: alignmentMap(p.align as string) as any,\n spacing: { after: 120 },\n }),\n )\n }\n } else {\n const alt = (p.alt as string) ?? \"Image\"\n const caption = p.caption ? ` — ${p.caption}` : \"\"\n children.push(\n new docx.Paragraph({\n children: [\n new docx.TextRun({\n text: `[${alt}${caption}]`,\n italics: true,\n color: \"999999\",\n }),\n ],\n }),\n )\n }\n}\n\nfunction renderDocxTable(ctx: DocxCtx, n: DocNode): void {\n const { docx, children, alignmentMap } = ctx\n const p = n.props\n const columns = ((p.columns ?? []) as (string | TableColumn)[]).map(resolveColumn)\n const rows = (p.rows ?? []) as (string | number)[][]\n const hs = p.headerStyle as { background?: string; color?: string } | undefined\n const bordered = p.bordered as boolean | undefined\n const borderStyle = bordered\n ? { style: docx.BorderStyle.SINGLE, size: 1, color: \"DDDDDD\" }\n : undefined\n const cellBorders = borderStyle\n ? {\n top: borderStyle,\n bottom: borderStyle,\n left: borderStyle,\n right: borderStyle,\n }\n : undefined\n\n const headerRow = new docx.TableRow({\n tableHeader: true,\n children: columns.map(\n (col) =>\n new docx.TableCell({\n children: [\n new docx.Paragraph({\n children: [\n new docx.TextRun({\n text: col.header,\n bold: true,\n color: sanitizeXmlColor(hs?.color),\n }),\n ],\n alignment: alignmentMap(col.align) as any,\n }),\n ],\n ...(hs?.background\n ? {\n shading: {\n fill: sanitizeXmlColor(hs.background),\n type: docx.ShadingType.SOLID,\n },\n }\n : {}),\n ...(cellBorders != null ? { borders: cellBorders } : {}),\n width: getColumnWidth(col.width as string | undefined) as any,\n }),\n ),\n })\n\n const dataRows = rows.map(\n (row, rowIdx) =>\n new docx.TableRow({\n children: columns.map(\n (col, colIdx) =>\n new docx.TableCell({\n children: [\n new docx.Paragraph({\n children: [new docx.TextRun({ text: String(row[colIdx] ?? \"\") })],\n alignment: alignmentMap(col.align) as any,\n }),\n ],\n ...(p.striped && rowIdx % 2 === 1\n ? { shading: { fill: \"F9F9F9\", type: docx.ShadingType.SOLID } }\n : {}),\n ...(cellBorders != null ? { borders: cellBorders } : {}),\n width: getColumnWidth(col.width as string | undefined) as any,\n }),\n ),\n }),\n )\n\n if (p.caption) {\n children.push(\n new docx.Paragraph({\n children: [\n new docx.TextRun({\n text: p.caption as string,\n italics: true,\n size: 20,\n }),\n ],\n spacing: { after: 60 },\n }),\n )\n }\n\n children.push(\n new docx.Table({\n rows: [headerRow, ...dataRows],\n width: { size: 100, type: docx.WidthType.PERCENTAGE },\n }),\n )\n children.push(new docx.Paragraph({ text: \"\", spacing: { after: 120 } }))\n}\n\nfunction renderList(ctx: DocxCtx, n: DocNode): void {\n const { docx, children, processListItems, nextListId } = ctx\n const ordered = n.props.ordered as boolean | undefined\n const listRef = nextListId()\n processListItems(n, listRef, 0, ordered ?? false)\n children.push(new docx.Paragraph({ text: \"\", spacing: { after: 60 } }))\n}\n\nfunction renderButtonOrQuote(ctx: DocxCtx, n: DocNode): void {\n const { docx, children } = ctx\n const p = n.props\n const text = getTextContent(n.children)\n if (n.type === \"button\") {\n children.push(\n new docx.Paragraph({\n children: [\n new docx.ExternalHyperlink({\n link: sanitizeHref(p.href as string),\n children: [\n new docx.TextRun({\n text,\n bold: true,\n color: \"4F46E5\",\n underline: { type: docx.UnderlineType.SINGLE },\n }),\n ],\n }),\n ],\n spacing: { after: 120 },\n }),\n )\n } else {\n children.push(\n new docx.Paragraph({\n children: [new docx.TextRun({ text, italics: true, color: \"555555\" })],\n indent: { left: 720 },\n border: {\n left: {\n style: docx.BorderStyle.SINGLE,\n size: 6,\n color: sanitizeXmlColor(p.borderColor as string, \"DDDDDD\"),\n },\n },\n spacing: { after: 120 },\n }),\n )\n }\n}\n\nexport const docxRenderer: DocumentRenderer = {\n async render(node: DocNode, _options?: RenderOptions): Promise<Uint8Array> {\n let docx: typeof import(\"docx\")\n try {\n docx = await import(\"docx\")\n } catch {\n throw new Error(\n '[@pyreon/document] DOCX renderer requires \"docx\" package. Install it: bun add docx',\n )\n }\n const children: unknown[] = []\n let listCounter = 0\n\n function alignmentMap(align?: string): unknown {\n if (!align) return undefined\n const map: Record<string, unknown> = {\n left: docx.AlignmentType.LEFT,\n center: docx.AlignmentType.CENTER,\n right: docx.AlignmentType.RIGHT,\n justify: docx.AlignmentType.JUSTIFIED,\n }\n return map[align]\n }\n\n function processListItems(n: DocNode, listRef: string, level: number, ordered: boolean): void {\n const items = n.children.filter((c): c is DocNode => typeof c !== \"string\")\n for (const item of items) {\n const nestedList = item.children.find(\n (c): c is DocNode => typeof c !== \"string\" && (c as DocNode).type === \"list\",\n )\n const textChildren = item.children.filter(\n (c) => typeof c === \"string\" || (c as DocNode).type !== \"list\",\n )\n children.push(\n new docx.Paragraph({\n children: [new docx.TextRun({ text: getTextContent(textChildren) })],\n ...(ordered ? { numbering: { reference: listRef, level } } : { bullet: { level } }),\n }),\n )\n if (nestedList) {\n const nestedOrdered = (nestedList as DocNode).props.ordered as boolean | undefined\n processListItems(nestedList as DocNode, listRef, level + 1, nestedOrdered ?? false)\n }\n }\n }\n\n const ctx: DocxCtx = {\n docx,\n children,\n alignmentMap,\n processListItems,\n nextListId: () => `list-${listCounter++}`,\n }\n\n function processNode(n: DocNode): void {\n switch (n.type) {\n case \"document\":\n case \"page\":\n case \"section\":\n case \"row\":\n case \"column\":\n for (const child of n.children) {\n if (typeof child !== \"string\") processNode(child)\n else children.push(new docx.Paragraph({ text: child }))\n }\n break\n case \"heading\":\n renderHeading(ctx, n)\n break\n case \"text\":\n renderTextNode(ctx, n)\n break\n case \"link\":\n renderLink(ctx, n)\n break\n case \"image\":\n renderImage(ctx, n)\n break\n case \"table\":\n renderDocxTable(ctx, n)\n break\n case \"list\":\n renderList(ctx, n)\n break\n case \"code\":\n children.push(\n new docx.Paragraph({\n children: [\n new docx.TextRun({\n text: getTextContent(n.children),\n font: \"Courier New\",\n size: 20,\n }),\n ],\n shading: { fill: \"F5F5F5\", type: docx.ShadingType.SOLID },\n spacing: { after: 120 },\n }),\n )\n break\n case \"divider\":\n children.push(\n new docx.Paragraph({\n border: {\n bottom: {\n style: docx.BorderStyle.SINGLE,\n size: (n.props.thickness as number | undefined) ?? 1,\n color: sanitizeXmlColor(n.props.color as string, \"DDDDDD\"),\n },\n },\n spacing: { before: 120, after: 120 },\n }),\n )\n break\n case \"spacer\":\n children.push(\n new docx.Paragraph({\n text: \"\",\n spacing: { after: (n.props.height as number) * 20 },\n }),\n )\n break\n case \"button\":\n case \"quote\":\n renderButtonOrQuote(ctx, n)\n break\n }\n }\n\n processNode(node)\n\n // Build numbering configs for all lists\n const numberingConfigs: unknown[] = []\n for (let i = 0; i < listCounter; i++) {\n numberingConfigs.push({\n reference: `list-${i}`,\n levels: Array.from({ length: 9 }, (_, level) => ({\n level,\n format: docx.LevelFormat.DECIMAL,\n text: `%${level + 1}.`,\n alignment: docx.AlignmentType.LEFT,\n style: {\n paragraph: { indent: { left: 720 * (level + 1), hanging: 360 } },\n },\n })),\n })\n }\n\n // Extract page properties from first page node\n const pageNode =\n node.type === \"document\"\n ? (node.children.find(\n (c): c is DocNode => typeof c !== \"string\" && (c as DocNode).type === \"page\",\n ) as DocNode | undefined)\n : node.type === \"page\"\n ? node\n : undefined\n\n const pageProps = pageNode?.props ?? {}\n const pageDims = getPageSize(\n pageProps.size as PageSize | undefined,\n pageProps.orientation as PageOrientation | undefined,\n )\n const pageMargins = getPageMargins(\n pageProps.margin as number | [number, number] | [number, number, number, number] | undefined,\n )\n\n function buildHeaderFooter(contentNode: DocNode | undefined): unknown[] | undefined {\n if (!contentNode) return undefined\n const text = getTextContent(contentNode.children)\n if (!text) return undefined\n return [\n new docx.Paragraph({\n children: [new docx.TextRun({ text, size: 18, color: \"999999\" })],\n alignment: docx.AlignmentType.CENTER,\n }),\n ]\n }\n\n const headerContent = buildHeaderFooter(pageProps.header as DocNode | undefined)\n const footerContent = buildHeaderFooter(pageProps.footer as DocNode | undefined)\n\n const sectionProperties: Record<string, unknown> = {}\n if (pageDims) {\n sectionProperties.page = { size: pageDims, margin: pageMargins }\n } else if (pageMargins) {\n sectionProperties.page = { margin: pageMargins }\n }\n\n const doc = new docx.Document({\n numbering: (numberingConfigs.length > 0 ? { config: numberingConfigs } : undefined) as any,\n sections: [\n {\n properties: sectionProperties,\n ...(headerContent\n ? {\n headers: {\n default: new docx.Header({ children: headerContent as any }),\n },\n }\n : {}),\n ...(footerContent\n ? {\n footers: {\n default: new docx.Footer({ children: footerContent as any }),\n },\n }\n : {}),\n children: children as any,\n },\n ],\n })\n\n const buffer = await docx.Packer.toBuffer(doc)\n return new Uint8Array(buffer)\n },\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email-D0bbfWq4.js","names":[],"sources":["../src/renderers/email.ts"],"sourcesContent":["import { sanitizeColor, sanitizeHref, sanitizeImageSrc } from '../sanitize'\nimport type {\n DocChild,\n DocNode,\n DocumentRenderer,\n RenderOptions,\n TableColumn,\n} from '../types'\n\n/**\n * Email renderer — generates table-based HTML with inline styles\n * that works across Gmail, Outlook, Apple Mail, and other email clients.\n *\n * Key constraints:\n * - No CSS classes (Gmail strips <style> tags)\n * - Table-based layout (no flexbox/grid)\n * - All styles inline\n * - VML buttons for Outlook\n * - Max width 600px for compatibility\n */\n\nfunction esc(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n}\n\nfunction resolveColumn(col: string | TableColumn): TableColumn {\n return typeof col === 'string' ? { header: col } : col\n}\n\nfunction renderChild(child: DocChild): string {\n if (typeof child === 'string') return esc(child)\n return renderNode(child)\n}\n\nfunction renderChildren(children: DocChild[]): string {\n return children.map(renderChild).join('')\n}\n\nfunction wrapInTable(content: string, style = ''): string {\n return `<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\"${style ? ` style=\"${style}\"` : ''}><tr><td>${content}</td></tr></table>`\n}\n\nfunction renderNode(node: DocNode): string {\n const p = node.props\n\n switch (node.type) {\n case 'document': {\n const title = p.title ? `<title>${esc(p.title as string)}</title>` : ''\n const preview = p.subject\n ? `<div style=\"display:none;max-height:0;overflow:hidden\">${esc(p.subject as string)}</div>`\n : ''\n return `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">${title}<!--[if mso]><noscript><xml><o:OfficeDocumentSettings><o:PixelsPerInch>96</o:PixelsPerInch></o:OfficeDocumentSettings></xml></noscript><![endif]--></head><body style=\"margin:0;padding:0;background-color:#f4f4f4;font-family:Arial,Helvetica,sans-serif\">${preview}<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" style=\"background-color:#f4f4f4\"><tr><td align=\"center\" style=\"padding:20px 0\"><table width=\"600\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" style=\"background-color:#ffffff;max-width:600px;width:100%\"><tr><td>${renderChildren(node.children)}</td></tr></table></td></tr></table></body></html>`\n }\n\n case 'page':\n // In email, pages are just content sections\n return renderChildren(node.children)\n\n case 'section': {\n const bg = p.background\n ? `background-color:${sanitizeColor(p.background as string)};`\n : ''\n const pad = p.padding\n ? `padding:${typeof p.padding === 'number' ? `${p.padding}px` : Array.isArray(p.padding) ? (p.padding as number[]).map((v) => `${v}px`).join(' ') : '0'}`\n : 'padding:0'\n const radius = p.borderRadius ? `border-radius:${p.borderRadius}px;` : ''\n\n if (p.direction === 'row') {\n // Row layout via nested table\n const children = node.children.filter(\n (c): c is DocNode => typeof c !== 'string',\n )\n const colWidth = Math.floor(100 / Math.max(children.length, 1))\n return `<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" style=\"${bg}${radius}${pad}\"><tr>${children.map((child) => `<td width=\"${colWidth}%\" valign=\"top\" style=\"padding:${(p.gap as number | undefined) ? `0 ${(p.gap as number) / 2}px` : '0'}\">${renderNode(child)}</td>`).join('')}</tr></table>`\n }\n\n return wrapInTable(renderChildren(node.children), `${bg}${radius}${pad}`)\n }\n\n case 'row': {\n const children = node.children.filter(\n (c): c is DocNode => typeof c !== 'string',\n )\n const gap = (p.gap as number) ?? 0\n return `<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\"><tr>${children.map((child) => `<td valign=\"top\" style=\"padding:0 ${gap / 2}px\">${renderNode(child)}</td>`).join('')}</tr></table>`\n }\n\n case 'column':\n return renderChildren(node.children)\n\n case 'heading': {\n const level = (p.level as number) ?? 1\n const sizes: Record<number, number> = {\n 1: 28,\n 2: 24,\n 3: 20,\n 4: 18,\n 5: 16,\n 6: 14,\n }\n const size = sizes[level] ?? 24\n const color = sanitizeColor((p.color as string) ?? '#000000')\n const align = (p.align as string) ?? 'left'\n return `<h${level} style=\"margin:0 0 12px 0;font-size:${size}px;color:${color};text-align:${align};font-weight:bold;line-height:1.3\">${renderChildren(node.children)}</h${level}>`\n }\n\n case 'text': {\n const size = (p.size as number) ?? 14\n const color = sanitizeColor((p.color as string) ?? '#333333')\n const weight = p.bold ? 'bold' : 'normal'\n const style = p.italic ? 'italic' : 'normal'\n const decoration = p.underline\n ? 'underline'\n : p.strikethrough\n ? 'line-through'\n : 'none'\n const align = (p.align as string) ?? 'left'\n const lh = (p.lineHeight as number) ?? 1.5\n return `<p style=\"margin:0 0 12px 0;font-size:${size}px;color:${color};font-weight:${weight};font-style:${style};text-decoration:${decoration};text-align:${align};line-height:${lh}\">${renderChildren(node.children)}</p>`\n }\n\n case 'link':\n return `<a href=\"${esc(sanitizeHref(p.href as string))}\" style=\"color:${sanitizeColor((p.color as string) ?? '#4f46e5')};text-decoration:underline\" target=\"_blank\">${renderChildren(node.children)}</a>`\n\n case 'image': {\n const align = (p.align as string) ?? 'left'\n const img = `<img src=\"${esc(sanitizeImageSrc(p.src as string))}\"${p.width ? ` width=\"${p.width}\"` : ''}${p.height ? ` height=\"${p.height}\"` : ''} alt=\"${esc((p.alt as string) ?? '')}\" style=\"display:block;outline:none;border:none;text-decoration:none${p.width ? `;max-width:${p.width}px` : ''}\" />`\n if (p.caption) {\n return `<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\"${align === 'center' ? ' align=\"center\"' : ''}><tr><td>${img}</td></tr><tr><td style=\"font-size:12px;color:#666;padding-top:4px;text-align:center\">${esc(p.caption as string)}</td></tr></table>`\n }\n if (align === 'center')\n return `<div style=\"text-align:center\">${img}</div>`\n if (align === 'right') return `<div style=\"text-align:right\">${img}</div>`\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\n let html =\n '<table width=\"100%\" cellpadding=\"8\" cellspacing=\"0\" border=\"0\" style=\"border-collapse:collapse\">'\n if (p.caption)\n html += `<caption style=\"font-size:12px;color:#666;padding:8px;text-align:left\">${esc(p.caption as string)}</caption>`\n\n html += '<tr>'\n for (const col of columns) {\n const bg = hs?.background\n ? `background-color:${sanitizeColor(hs.background)};`\n : 'background-color:#f5f5f5;'\n const color = hs?.color ? `color:${sanitizeColor(hs.color)};` : ''\n const align = col.align ? `text-align:${col.align};` : ''\n const width = col.width\n ? `width:${typeof col.width === 'number' ? `${col.width}px` : col.width};`\n : ''\n html += `<th style=\"${bg}${color}font-weight:bold;${align}${width}padding:8px;border-bottom:2px solid #ddd\">${esc(col.header)}</th>`\n }\n html += '</tr>'\n\n for (let i = 0; i < rows.length; i++) {\n const bg = striped && i % 2 === 1 ? 'background-color:#f9f9f9;' : ''\n html += '<tr>'\n for (let j = 0; j < columns.length; j++) {\n const col = columns[j]\n const align = col?.align ? `text-align:${col.align};` : ''\n html += `<td style=\"${bg}${align}padding:8px;border-bottom:1px solid #eee\">${esc(String(rows[i]?.[j] ?? ''))}</td>`\n }\n html += '</tr>'\n }\n html += '</table>'\n return html\n }\n\n case 'list': {\n const tag = p.ordered ? 'ol' : 'ul'\n return `<${tag} style=\"margin:0 0 12px 0;padding-left:24px\">${renderChildren(node.children)}</${tag}>`\n }\n\n case 'list-item':\n return `<li style=\"margin:0 0 4px 0;font-size:14px;color:#333\">${renderChildren(node.children)}</li>`\n\n case 'code':\n return `<pre style=\"background-color:#f5f5f5;padding:12px;border-radius:4px;font-family:Courier New,monospace;font-size:13px;color:#333;overflow-x:auto;margin:0 0 12px 0\"><code>${esc(renderChildren(node.children))}</code></pre>`\n\n case 'divider': {\n const color = sanitizeColor((p.color as string) ?? '#dddddd')\n const thickness = (p.thickness as number) ?? 1\n return `<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" style=\"margin:16px 0\"><tr><td style=\"border-top:${thickness}px solid ${color};font-size:0;line-height:0\"> </td></tr></table>`\n }\n\n case 'page-break':\n return `<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" style=\"margin:24px 0\"><tr><td style=\"border-top:2px solid #dddddd;font-size:0;line-height:0\"> </td></tr></table>`\n\n case 'spacer':\n return `<div style=\"height:${p.height}px;line-height:${p.height}px;font-size:0\"> </div>`\n\n case 'button': {\n const bg = sanitizeColor((p.background as string) ?? '#4f46e5')\n const color = sanitizeColor((p.color as string) ?? '#ffffff')\n const radius = (p.borderRadius as number) ?? 4\n const href = esc(sanitizeHref(p.href as string))\n const text = renderChildren(node.children)\n const align = (p.align as string) ?? 'left'\n\n // Bulletproof button — works in Outlook via VML, CSS everywhere else\n return `<div style=\"text-align:${align};margin:12px 0\"><!--[if mso]><v:roundrect xmlns:v=\"urn:schemas-microsoft-com:vml\" xmlns:w=\"urn:schemas-microsoft-com:office:word\" href=\"${href}\" style=\"height:44px;v-text-anchor:middle;width:200px\" arcsize=\"10%\" strokecolor=\"${bg}\" fillcolor=\"${bg}\"><w:anchorlock/><center style=\"color:${color};font-family:Arial,sans-serif;font-size:14px;font-weight:bold\">${text}</center></v:roundrect><![endif]--><!--[if !mso]><!--><a href=\"${href}\" style=\"display:inline-block;background-color:${bg};color:${color};padding:12px 24px;border-radius:${radius}px;text-decoration:none;font-weight:bold;font-size:14px;font-family:Arial,sans-serif\" target=\"_blank\">${text}</a><!--<![endif]--></div>`\n }\n\n case 'quote': {\n const borderColor = sanitizeColor((p.borderColor as string) ?? '#dddddd')\n return `<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" style=\"margin:12px 0\"><tr><td style=\"border-left:4px solid ${borderColor};padding:12px 20px;color:#555555;font-style:italic\">${renderChildren(node.children)}</td></tr></table>`\n }\n\n default:\n return renderChildren(node.children)\n }\n}\n\nexport const emailRenderer: DocumentRenderer = {\n async render(node: DocNode, _options?: RenderOptions): Promise<string> {\n return renderNode(node)\n },\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"email-D0bbfWq4.js","names":[],"sources":["../src/renderers/email.ts"],"sourcesContent":["import { sanitizeColor, sanitizeHref, sanitizeImageSrc } from \"../sanitize\"\nimport type { DocChild, DocNode, DocumentRenderer, RenderOptions, TableColumn } from \"../types\"\n\n/**\n * Email renderer — generates table-based HTML with inline styles\n * that works across Gmail, Outlook, Apple Mail, and other email clients.\n *\n * Key constraints:\n * - No CSS classes (Gmail strips <style> tags)\n * - Table-based layout (no flexbox/grid)\n * - All styles inline\n * - VML buttons for Outlook\n * - Max width 600px for compatibility\n */\n\nfunction esc(str: string): string {\n return str\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n}\n\nfunction resolveColumn(col: string | TableColumn): TableColumn {\n return typeof col === \"string\" ? { header: col } : col\n}\n\nfunction renderChild(child: DocChild): string {\n if (typeof child === \"string\") return esc(child)\n return renderNode(child)\n}\n\nfunction renderChildren(children: DocChild[]): string {\n return children.map(renderChild).join(\"\")\n}\n\nfunction wrapInTable(content: string, style = \"\"): string {\n return `<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\"${style ? ` style=\"${style}\"` : \"\"}><tr><td>${content}</td></tr></table>`\n}\n\nfunction renderNode(node: DocNode): string {\n const p = node.props\n\n switch (node.type) {\n case \"document\": {\n const title = p.title ? `<title>${esc(p.title as string)}</title>` : \"\"\n const preview = p.subject\n ? `<div style=\"display:none;max-height:0;overflow:hidden\">${esc(p.subject as string)}</div>`\n : \"\"\n return `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">${title}<!--[if mso]><noscript><xml><o:OfficeDocumentSettings><o:PixelsPerInch>96</o:PixelsPerInch></o:OfficeDocumentSettings></xml></noscript><![endif]--></head><body style=\"margin:0;padding:0;background-color:#f4f4f4;font-family:Arial,Helvetica,sans-serif\">${preview}<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" style=\"background-color:#f4f4f4\"><tr><td align=\"center\" style=\"padding:20px 0\"><table width=\"600\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" style=\"background-color:#ffffff;max-width:600px;width:100%\"><tr><td>${renderChildren(node.children)}</td></tr></table></td></tr></table></body></html>`\n }\n\n case \"page\":\n // In email, pages are just content sections\n return renderChildren(node.children)\n\n case \"section\": {\n const bg = p.background ? `background-color:${sanitizeColor(p.background as string)};` : \"\"\n const pad = p.padding\n ? `padding:${typeof p.padding === \"number\" ? `${p.padding}px` : Array.isArray(p.padding) ? (p.padding as number[]).map((v) => `${v}px`).join(\" \") : \"0\"}`\n : \"padding:0\"\n const radius = p.borderRadius ? `border-radius:${p.borderRadius}px;` : \"\"\n\n if (p.direction === \"row\") {\n // Row layout via nested table\n const children = node.children.filter((c): c is DocNode => typeof c !== \"string\")\n const colWidth = Math.floor(100 / Math.max(children.length, 1))\n return `<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" style=\"${bg}${radius}${pad}\"><tr>${children.map((child) => `<td width=\"${colWidth}%\" valign=\"top\" style=\"padding:${(p.gap as number | undefined) ? `0 ${(p.gap as number) / 2}px` : \"0\"}\">${renderNode(child)}</td>`).join(\"\")}</tr></table>`\n }\n\n return wrapInTable(renderChildren(node.children), `${bg}${radius}${pad}`)\n }\n\n case \"row\": {\n const children = node.children.filter((c): c is DocNode => typeof c !== \"string\")\n const gap = (p.gap as number) ?? 0\n return `<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\"><tr>${children.map((child) => `<td valign=\"top\" style=\"padding:0 ${gap / 2}px\">${renderNode(child)}</td>`).join(\"\")}</tr></table>`\n }\n\n case \"column\":\n return renderChildren(node.children)\n\n case \"heading\": {\n const level = (p.level as number) ?? 1\n const sizes: Record<number, number> = {\n 1: 28,\n 2: 24,\n 3: 20,\n 4: 18,\n 5: 16,\n 6: 14,\n }\n const size = sizes[level] ?? 24\n const color = sanitizeColor((p.color as string) ?? \"#000000\")\n const align = (p.align as string) ?? \"left\"\n return `<h${level} style=\"margin:0 0 12px 0;font-size:${size}px;color:${color};text-align:${align};font-weight:bold;line-height:1.3\">${renderChildren(node.children)}</h${level}>`\n }\n\n case \"text\": {\n const size = (p.size as number) ?? 14\n const color = sanitizeColor((p.color as string) ?? \"#333333\")\n const weight = p.bold ? \"bold\" : \"normal\"\n const style = p.italic ? \"italic\" : \"normal\"\n const decoration = p.underline ? \"underline\" : p.strikethrough ? \"line-through\" : \"none\"\n const align = (p.align as string) ?? \"left\"\n const lh = (p.lineHeight as number) ?? 1.5\n return `<p style=\"margin:0 0 12px 0;font-size:${size}px;color:${color};font-weight:${weight};font-style:${style};text-decoration:${decoration};text-align:${align};line-height:${lh}\">${renderChildren(node.children)}</p>`\n }\n\n case \"link\":\n return `<a href=\"${esc(sanitizeHref(p.href as string))}\" style=\"color:${sanitizeColor((p.color as string) ?? \"#4f46e5\")};text-decoration:underline\" target=\"_blank\">${renderChildren(node.children)}</a>`\n\n case \"image\": {\n const align = (p.align as string) ?? \"left\"\n const img = `<img src=\"${esc(sanitizeImageSrc(p.src as string))}\"${p.width ? ` width=\"${p.width}\"` : \"\"}${p.height ? ` height=\"${p.height}\"` : \"\"} alt=\"${esc((p.alt as string) ?? \"\")}\" style=\"display:block;outline:none;border:none;text-decoration:none${p.width ? `;max-width:${p.width}px` : \"\"}\" />`\n if (p.caption) {\n return `<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\"${align === \"center\" ? ' align=\"center\"' : \"\"}><tr><td>${img}</td></tr><tr><td style=\"font-size:12px;color:#666;padding-top:4px;text-align:center\">${esc(p.caption as string)}</td></tr></table>`\n }\n if (align === \"center\") return `<div style=\"text-align:center\">${img}</div>`\n if (align === \"right\") return `<div style=\"text-align:right\">${img}</div>`\n return img\n }\n\n case \"table\": {\n const columns = ((p.columns ?? []) as (string | TableColumn)[]).map(resolveColumn)\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\n let html =\n '<table width=\"100%\" cellpadding=\"8\" cellspacing=\"0\" border=\"0\" style=\"border-collapse:collapse\">'\n if (p.caption)\n html += `<caption style=\"font-size:12px;color:#666;padding:8px;text-align:left\">${esc(p.caption as string)}</caption>`\n\n html += \"<tr>\"\n for (const col of columns) {\n const bg = hs?.background\n ? `background-color:${sanitizeColor(hs.background)};`\n : \"background-color:#f5f5f5;\"\n const color = hs?.color ? `color:${sanitizeColor(hs.color)};` : \"\"\n const align = col.align ? `text-align:${col.align};` : \"\"\n const width = col.width\n ? `width:${typeof col.width === \"number\" ? `${col.width}px` : col.width};`\n : \"\"\n html += `<th style=\"${bg}${color}font-weight:bold;${align}${width}padding:8px;border-bottom:2px solid #ddd\">${esc(col.header)}</th>`\n }\n html += \"</tr>\"\n\n for (let i = 0; i < rows.length; i++) {\n const bg = striped && i % 2 === 1 ? \"background-color:#f9f9f9;\" : \"\"\n html += \"<tr>\"\n for (let j = 0; j < columns.length; j++) {\n const col = columns[j]\n const align = col?.align ? `text-align:${col.align};` : \"\"\n html += `<td style=\"${bg}${align}padding:8px;border-bottom:1px solid #eee\">${esc(String(rows[i]?.[j] ?? \"\"))}</td>`\n }\n html += \"</tr>\"\n }\n html += \"</table>\"\n return html\n }\n\n case \"list\": {\n const tag = p.ordered ? \"ol\" : \"ul\"\n return `<${tag} style=\"margin:0 0 12px 0;padding-left:24px\">${renderChildren(node.children)}</${tag}>`\n }\n\n case \"list-item\":\n return `<li style=\"margin:0 0 4px 0;font-size:14px;color:#333\">${renderChildren(node.children)}</li>`\n\n case \"code\":\n return `<pre style=\"background-color:#f5f5f5;padding:12px;border-radius:4px;font-family:Courier New,monospace;font-size:13px;color:#333;overflow-x:auto;margin:0 0 12px 0\"><code>${esc(renderChildren(node.children))}</code></pre>`\n\n case \"divider\": {\n const color = sanitizeColor((p.color as string) ?? \"#dddddd\")\n const thickness = (p.thickness as number) ?? 1\n return `<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" style=\"margin:16px 0\"><tr><td style=\"border-top:${thickness}px solid ${color};font-size:0;line-height:0\"> </td></tr></table>`\n }\n\n case \"page-break\":\n return `<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" style=\"margin:24px 0\"><tr><td style=\"border-top:2px solid #dddddd;font-size:0;line-height:0\"> </td></tr></table>`\n\n case \"spacer\":\n return `<div style=\"height:${p.height}px;line-height:${p.height}px;font-size:0\"> </div>`\n\n case \"button\": {\n const bg = sanitizeColor((p.background as string) ?? \"#4f46e5\")\n const color = sanitizeColor((p.color as string) ?? \"#ffffff\")\n const radius = (p.borderRadius as number) ?? 4\n const href = esc(sanitizeHref(p.href as string))\n const text = renderChildren(node.children)\n const align = (p.align as string) ?? \"left\"\n\n // Bulletproof button — works in Outlook via VML, CSS everywhere else\n return `<div style=\"text-align:${align};margin:12px 0\"><!--[if mso]><v:roundrect xmlns:v=\"urn:schemas-microsoft-com:vml\" xmlns:w=\"urn:schemas-microsoft-com:office:word\" href=\"${href}\" style=\"height:44px;v-text-anchor:middle;width:200px\" arcsize=\"10%\" strokecolor=\"${bg}\" fillcolor=\"${bg}\"><w:anchorlock/><center style=\"color:${color};font-family:Arial,sans-serif;font-size:14px;font-weight:bold\">${text}</center></v:roundrect><![endif]--><!--[if !mso]><!--><a href=\"${href}\" style=\"display:inline-block;background-color:${bg};color:${color};padding:12px 24px;border-radius:${radius}px;text-decoration:none;font-weight:bold;font-size:14px;font-family:Arial,sans-serif\" target=\"_blank\">${text}</a><!--<![endif]--></div>`\n }\n\n case \"quote\": {\n const borderColor = sanitizeColor((p.borderColor as string) ?? \"#dddddd\")\n return `<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" style=\"margin:12px 0\"><tr><td style=\"border-left:4px solid ${borderColor};padding:12px 20px;color:#555555;font-style:italic\">${renderChildren(node.children)}</td></tr></table>`\n }\n\n default:\n return renderChildren(node.children)\n }\n}\n\nexport const emailRenderer: DocumentRenderer = {\n async render(node: DocNode, _options?: RenderOptions): Promise<string> {\n return renderNode(node)\n },\n}\n"],"mappings":"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"}
|