@pyreon/document 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"whatsapp-CjSGoOKx.js","names":[],"sources":["../src/renderers/whatsapp.ts"],"sourcesContent":["import { sanitizeHref } from
|
|
1
|
+
{"version":3,"file":"whatsapp-CjSGoOKx.js","names":[],"sources":["../src/renderers/whatsapp.ts"],"sourcesContent":["import { sanitizeHref } from \"../sanitize\"\nimport type { DocChild, DocNode, DocumentRenderer, RenderOptions, TableColumn } from \"../types\"\n\n/**\n * WhatsApp renderer — outputs formatted text using WhatsApp's markup.\n * WhatsApp supports: *bold*, _italic_, ~strikethrough~, ```code```, > quote\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\nfunction renderNode(node: DocNode): string {\n const p = node.props\n\n switch (node.type) {\n case \"document\":\n case \"page\":\n case \"section\":\n case \"row\":\n case \"column\":\n return node.children.map((c) => (typeof c === \"string\" ? c : renderNode(c))).join(\"\")\n\n case \"heading\": {\n const text = getTextContent(node.children)\n return `*${text}*\\n\\n`\n }\n\n case \"text\": {\n let text = getTextContent(node.children)\n if (p.bold) text = `*${text}*`\n if (p.italic) text = `_${text}_`\n if (p.strikethrough) text = `~${text}~`\n return `${text}\\n\\n`\n }\n\n case \"link\": {\n const href = sanitizeHref(p.href as string)\n const text = getTextContent(node.children)\n return `${text}: ${href}\\n\\n`\n }\n\n case \"image\":\n // WhatsApp doesn't support inline images in text\n return \"\"\n\n case \"table\": {\n const columns = ((p.columns ?? []) as (string | TableColumn)[]).map(resolveColumn)\n const rows = (p.rows ?? []) as (string | number)[][]\n\n const header = columns.map((c) => `*${c.header}*`).join(\" | \")\n const body = rows.map((row) => row.map((c) => String(c ?? \"\")).join(\" | \")).join(\"\\n\")\n\n let result = `${header}\\n${body}\\n\\n`\n if (p.caption) result = `_${p.caption}_\\n${result}`\n return result\n }\n\n case \"list\": {\n const ordered = p.ordered as boolean | undefined\n return `${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\\n`\n }\n\n case \"code\": {\n const text = getTextContent(node.children)\n return `\\`\\`\\`${text}\\`\\`\\`\\n\\n`\n }\n\n case \"divider\":\n case \"page-break\":\n return \"───────────\\n\\n\"\n\n case \"spacer\":\n return \"\\n\"\n\n case \"button\": {\n const href = sanitizeHref(p.href as string)\n const text = getTextContent(node.children)\n return `*${text}*: ${href}\\n\\n`\n }\n\n case \"quote\": {\n const text = getTextContent(node.children)\n return `> ${text}\\n\\n`\n }\n\n default:\n return \"\"\n }\n}\n\nexport const whatsappRenderer: DocumentRenderer = {\n async render(node: DocNode, _options?: RenderOptions): Promise<string> {\n return renderNode(node).trim()\n },\n}\n"],"mappings":";;;;;;;AAQA,SAAS,cAAc,KAAwC;AAC7D,QAAO,OAAO,QAAQ,WAAW,EAAE,QAAQ,KAAK,GAAG;;AAGrD,SAAS,eAAe,UAA8B;AACpD,QAAO,SACJ,KAAK,MAAO,OAAO,MAAM,WAAW,IAAI,eAAgB,EAAc,SAAS,CAAE,CACjF,KAAK,GAAG;;AAGb,SAAS,WAAW,MAAuB;CACzC,MAAM,IAAI,KAAK;AAEf,SAAQ,KAAK,MAAb;EACE,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,SACH,QAAO,KAAK,SAAS,KAAK,MAAO,OAAO,MAAM,WAAW,IAAI,WAAW,EAAE,CAAE,CAAC,KAAK,GAAG;EAEvF,KAAK,UAEH,QAAO,IADM,eAAe,KAAK,SAAS,CAC1B;EAGlB,KAAK,QAAQ;GACX,IAAI,OAAO,eAAe,KAAK,SAAS;AACxC,OAAI,EAAE,KAAM,QAAO,IAAI,KAAK;AAC5B,OAAI,EAAE,OAAQ,QAAO,IAAI,KAAK;AAC9B,OAAI,EAAE,cAAe,QAAO,IAAI,KAAK;AACrC,UAAO,GAAG,KAAK;;EAGjB,KAAK,QAAQ;GACX,MAAM,OAAO,aAAa,EAAE,KAAe;AAE3C,UAAO,GADM,eAAe,KAAK,SAAS,CAC3B,IAAI,KAAK;;EAG1B,KAAK,QAEH,QAAO;EAET,KAAK,SAAS;GACZ,MAAM,WAAY,EAAE,WAAW,EAAE,EAA+B,IAAI,cAAc;GAClF,MAAM,OAAQ,EAAE,QAAQ,EAAE;GAK1B,IAAI,SAAS,GAHE,QAAQ,KAAK,MAAM,IAAI,EAAE,OAAO,GAAG,CAAC,KAAK,MAAM,CAGvC,IAFV,KAAK,KAAK,QAAQ,IAAI,KAAK,MAAM,OAAO,KAAK,GAAG,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,KAAK,KAAK,CAEtD;AAChC,OAAI,EAAE,QAAS,UAAS,IAAI,EAAE,QAAQ,KAAK;AAC3C,UAAO;;EAGT,KAAK,QAAQ;GACX,MAAM,UAAU,EAAE;AAClB,UAAO,GAAG,KAAK,SACZ,QAAQ,MAAoB,OAAO,MAAM,SAAS,CAClD,KAAK,MAAM,MAAM;AAEhB,WAAO,GADQ,UAAU,GAAG,IAAI,EAAE,KAAK,IACtB,GAAG,eAAe,KAAK,SAAS;KACjD,CACD,KAAK,KAAK,CAAC;;EAGhB,KAAK,OAEH,QAAO,SADM,eAAe,KAAK,SAAS,CACrB;EAGvB,KAAK;EACL,KAAK,aACH,QAAO;EAET,KAAK,SACH,QAAO;EAET,KAAK,UAAU;GACb,MAAM,OAAO,aAAa,EAAE,KAAe;AAE3C,UAAO,IADM,eAAe,KAAK,SAAS,CAC1B,KAAK,KAAK;;EAG5B,KAAK,QAEH,QAAO,KADM,eAAe,KAAK,SAAS,CACzB;EAGnB,QACE,QAAO;;;AAIb,MAAa,mBAAqC,EAChD,MAAM,OAAO,MAAe,UAA2C;AACrE,QAAO,WAAW,KAAK,CAAC,MAAM;GAEjC"}
|
|
@@ -169,7 +169,13 @@ function autoFitColumns(ws) {
|
|
|
169
169
|
}
|
|
170
170
|
}
|
|
171
171
|
const xlsxRenderer = { async render(node, _options) {
|
|
172
|
-
|
|
172
|
+
let ExcelJS;
|
|
173
|
+
try {
|
|
174
|
+
ExcelJS = await import("./exceljs-BYETsesT.js");
|
|
175
|
+
} catch {
|
|
176
|
+
throw new Error("[@pyreon/document] XLSX renderer requires \"exceljs\" package. Install it: bun add exceljs");
|
|
177
|
+
}
|
|
178
|
+
const workbook = new ExcelJS.default.Workbook();
|
|
173
179
|
workbook.creator = node.props.author ?? "";
|
|
174
180
|
workbook.title = node.props.title ?? "";
|
|
175
181
|
const sheets = extractSheets(node);
|
|
@@ -196,4 +202,4 @@ const xlsxRenderer = { async render(node, _options) {
|
|
|
196
202
|
|
|
197
203
|
//#endregion
|
|
198
204
|
export { xlsxRenderer };
|
|
199
|
-
//# sourceMappingURL=xlsx-
|
|
205
|
+
//# sourceMappingURL=xlsx-Cvu4LBNy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xlsx-Cvu4LBNy.js","names":[],"sources":["../src/renderers/xlsx.ts"],"sourcesContent":["import type { DocChild, DocNode, DocumentRenderer, RenderOptions, TableColumn } from \"../types\"\n\n/**\n * XLSX renderer — lazy-loads ExcelJS on first use.\n * Extracts tables from the document and renders each as a worksheet.\n * Non-table content (headings, text) becomes header rows.\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\ninterface ExtractedSheet {\n name: string\n headings: string[]\n tables: DocNode[]\n}\n\n/** Walk the tree and group content into sheets (one per page, or one global). */\nfunction extractSheets(node: DocNode): ExtractedSheet[] {\n const sheets: ExtractedSheet[] = []\n let currentSheet: ExtractedSheet = {\n name: \"Sheet 1\",\n headings: [],\n tables: [],\n }\n\n function walk(n: DocNode): void {\n switch (n.type) {\n case \"document\":\n walkChildren(n)\n break\n\n case \"page\":\n pushCurrentSheet()\n currentSheet = {\n name: `Sheet ${sheets.length + 1}`,\n headings: [],\n tables: [],\n }\n walkChildren(n)\n break\n\n case \"heading\":\n addHeading(n)\n break\n\n case \"table\":\n currentSheet.tables.push(n)\n break\n\n default:\n walkChildren(n)\n }\n }\n\n function walkChildren(n: DocNode): void {\n for (const child of n.children) {\n if (typeof child !== \"string\") walk(child)\n }\n }\n\n function pushCurrentSheet(): void {\n if (currentSheet.tables.length > 0 || currentSheet.headings.length > 0) {\n sheets.push(currentSheet)\n }\n }\n\n function addHeading(n: DocNode): void {\n const text = getTextContent(n.children)\n currentSheet.headings.push(text)\n if (currentSheet.headings.length === 1) {\n currentSheet.name = text.slice(0, 31) // Excel sheet name max 31 chars\n }\n }\n\n walk(node)\n pushCurrentSheet()\n\n return sheets\n}\n\n/** Parse a cell value, handling currencies, percentages, and plain numbers. */\nfunction parseCellValue(value: string | number | undefined): string | number {\n if (value == null) return \"\"\n if (typeof value === \"number\") return value\n\n const trimmed = value.trim()\n\n // Percentage: \"45%\" or \"12.5%\"\n if (/^-?\\d+(\\.\\d+)?%$/.test(trimmed)) {\n return Number.parseFloat(trimmed) / 100\n }\n\n // Currency: \"$1,234.56\", \"$1234\", \"-$500\"\n const currencyMatch = trimmed.match(/^-?\\$[\\d,]+(\\.\\d+)?$/)\n if (currencyMatch) {\n return Number.parseFloat(trimmed.replace(/[$,]/g, \"\"))\n }\n\n // Plain number: \"1,234.56\", \"1234\", \"-500.5\"\n const plainNum = Number(trimmed.replace(/,/g, \"\"))\n if (!Number.isNaN(plainNum) && /^-?[\\d,]+(\\.\\d+)?$/.test(trimmed)) {\n return plainNum\n }\n\n return value\n}\n\n/** Get ExcelJS number format string for a value. */\nfunction getCellFormat(originalValue: string | number | undefined): string | undefined {\n if (typeof originalValue !== \"string\") return undefined\n const trimmed = originalValue.trim()\n\n if (/^-?\\d+(\\.\\d+)?%$/.test(trimmed)) return \"0.00%\"\n if (/^-?\\$/.test(trimmed)) return \"$#,##0.00\"\n return undefined\n}\n\n/** Map alignment string to ExcelJS horizontal alignment. */\nfunction mapAlignment(align?: string): \"left\" | \"center\" | \"right\" | undefined {\n if (align === \"left\" || align === \"center\" || align === \"right\") return align\n return undefined\n}\n\n/** Thin border style for ExcelJS. */\nfunction thinBorder(): { style: \"thin\"; color: { argb: string } } {\n return { style: \"thin\", color: { argb: \"FFDDDDDD\" } }\n}\n\n/** Apply header styling to a cell. */\nfunction styleHeaderCell(\n cell: {\n font: unknown\n fill: unknown\n alignment: unknown\n border: unknown\n value: unknown\n },\n col: TableColumn,\n hs: { background?: string; color?: string } | undefined,\n bordered: boolean,\n): void {\n cell.value = col.header\n cell.font = {\n bold: true,\n color: { argb: hs?.color?.replace(\"#\", \"FF\") ?? \"FF000000\" },\n }\n if (hs?.background) {\n cell.fill = {\n type: \"pattern\",\n pattern: \"solid\",\n fgColor: { argb: hs.background.replace(\"#\", \"FF\") },\n }\n }\n cell.alignment = { horizontal: mapAlignment(col.align) ?? \"left\" }\n if (bordered) {\n cell.border = {\n top: thinBorder(),\n bottom: thinBorder(),\n left: thinBorder(),\n right: thinBorder(),\n }\n }\n}\n\n/** Apply data cell value and styling. */\nfunction styleDataCell(\n cell: {\n value: unknown\n numFmt: unknown\n alignment: unknown\n fill: unknown\n border: unknown\n },\n rawValue: string | number | undefined,\n col: TableColumn,\n striped: boolean,\n isOddRow: boolean,\n bordered: boolean,\n): void {\n cell.value = parseCellValue(rawValue)\n const fmt = getCellFormat(rawValue)\n if (fmt) cell.numFmt = fmt\n cell.alignment = { horizontal: mapAlignment(col.align) ?? \"left\" }\n if (striped && isOddRow) {\n cell.fill = {\n type: \"pattern\",\n pattern: \"solid\",\n fgColor: { argb: \"FFF9F9F9\" },\n }\n }\n if (bordered) {\n cell.border = {\n top: thinBorder(),\n bottom: thinBorder(),\n left: thinBorder(),\n right: thinBorder(),\n }\n }\n}\n\n/** Render a single table node into the worksheet starting at the given row. Returns the next row number. */\nfunction renderTable(\n ws: {\n getRow: (n: number) => { getCell: (n: number) => Record<string, unknown> }\n columns: unknown[]\n },\n tableNode: DocNode,\n startRow: number,\n): number {\n let rowNum = startRow\n const columns = ((tableNode.props.columns ?? []) as (string | TableColumn)[]).map(resolveColumn)\n const rows = (tableNode.props.rows ?? []) as (string | number)[][]\n const hs = tableNode.props.headerStyle as { background?: string; color?: string } | undefined\n const bordered = (tableNode.props.bordered as boolean) ?? false\n\n // Caption\n if (tableNode.props.caption) {\n const captionRow = ws.getRow(rowNum)\n const captionCell = captionRow.getCell(1)\n captionCell.value = tableNode.props.caption as string\n captionCell.font = { italic: true, size: 10 }\n rowNum++\n }\n\n // Header row\n const headerRow = ws.getRow(rowNum)\n for (let i = 0; i < columns.length; i++) {\n const col = columns[i]\n if (!col) continue\n styleHeaderCell(headerRow.getCell(i + 1) as any, col, hs, bordered)\n }\n rowNum++\n\n // Data rows\n for (let r = 0; r < rows.length; r++) {\n const dataRow = ws.getRow(rowNum)\n for (let c = 0; c < columns.length; c++) {\n const col = columns[c]\n if (!col) continue\n styleDataCell(\n dataRow.getCell(c + 1) as any,\n rows[r]?.[c],\n col,\n (tableNode.props.striped as boolean) ?? false,\n r % 2 === 1,\n bordered,\n )\n }\n rowNum++\n }\n\n return rowNum + 1 // gap after table\n}\n\n/** Auto-fit column widths based on content. */\nfunction autoFitColumns(ws: {\n columns: {\n width: number\n eachCell?: (opts: { includeEmpty: boolean }, cb: (cell: { value: unknown }) => void) => void\n }[]\n}): void {\n for (const col of ws.columns) {\n let maxLen = 10\n col.eachCell?.({ includeEmpty: false }, (cell) => {\n const len = String(cell.value ?? \"\").length\n if (len > maxLen) maxLen = len\n })\n col.width = Math.min(maxLen + 2, 50)\n }\n}\n\nexport const xlsxRenderer: DocumentRenderer = {\n async render(node: DocNode, _options?: RenderOptions): Promise<Uint8Array> {\n let ExcelJS: any\n try {\n ExcelJS = await import(\"exceljs\")\n } catch {\n throw new Error(\n '[@pyreon/document] XLSX renderer requires \"exceljs\" package. Install it: bun add exceljs',\n )\n }\n const workbook = new ExcelJS.default.Workbook()\n\n workbook.creator = (node.props.author as string) ?? \"\"\n workbook.title = (node.props.title as string) ?? \"\"\n\n const sheets = extractSheets(node)\n\n if (sheets.length === 0) {\n workbook.addWorksheet(\"Sheet 1\")\n }\n\n for (const sheet of sheets) {\n const ws = workbook.addWorksheet(sheet.name)\n\n let rowNum = 1\n\n // Add headings as title rows\n for (const heading of sheet.headings) {\n const row = ws.getRow(rowNum)\n row.getCell(1).value = heading\n row.getCell(1).font = { bold: true, size: 14 }\n rowNum++\n }\n\n if (sheet.headings.length > 0) rowNum++ // gap after headings\n\n // Add tables\n for (const tableNode of sheet.tables) {\n rowNum = renderTable(ws as unknown as Parameters<typeof renderTable>[0], tableNode, rowNum)\n }\n\n // Auto-fit columns (approximate)\n autoFitColumns(ws as unknown as Parameters<typeof autoFitColumns>[0])\n }\n\n const buffer = await workbook.xlsx.writeBuffer()\n return new Uint8Array(buffer as ArrayBuffer)\n },\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pyreon/document",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Universal document rendering for Pyreon — one template, every output format (HTML, PDF, DOCX, email, XLSX, Markdown, and more)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
|
-
"url": "https://github.com/pyreon/
|
|
9
|
-
"directory": "packages/document"
|
|
8
|
+
"url": "https://github.com/pyreon/pyreon.git",
|
|
9
|
+
"directory": "packages/fundamentals/document"
|
|
10
10
|
},
|
|
11
11
|
"homepage": "https://github.com/pyreon/fundamentals/tree/main/packages/document#readme",
|
|
12
12
|
"bugs": {
|
|
13
|
-
"url": "https://github.com/pyreon/
|
|
13
|
+
"url": "https://github.com/pyreon/pyreon/issues"
|
|
14
14
|
},
|
|
15
15
|
"publishConfig": {
|
|
16
16
|
"access": "public"
|
|
@@ -37,16 +37,28 @@
|
|
|
37
37
|
"build": "vl_rolldown_build",
|
|
38
38
|
"dev": "vl_rolldown_build-watch",
|
|
39
39
|
"test": "vitest run",
|
|
40
|
-
"typecheck": "tsc --noEmit"
|
|
40
|
+
"typecheck": "tsc --noEmit",
|
|
41
|
+
"lint": "biome check ."
|
|
41
42
|
},
|
|
42
43
|
"peerDependencies": {
|
|
43
|
-
"@pyreon/core": "
|
|
44
|
-
"@pyreon/reactivity": "
|
|
44
|
+
"@pyreon/core": "^0.11.0",
|
|
45
|
+
"@pyreon/reactivity": "^0.11.0"
|
|
45
46
|
},
|
|
46
47
|
"optionalDependencies": {
|
|
47
48
|
"docx": "^9.6.0",
|
|
48
49
|
"exceljs": "^4.4.0",
|
|
49
50
|
"pdfmake": "^0.3.7",
|
|
50
51
|
"pptxgenjs": "^4.0.1"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@happy-dom/global-registrator": "^20.8.3",
|
|
55
|
+
"@pyreon/core": "^0.11.0",
|
|
56
|
+
"@pyreon/reactivity": "^0.11.0",
|
|
57
|
+
"@types/pdfmake": "^0.3.2",
|
|
58
|
+
"@vitus-labs/tools-lint": "^1.11.0",
|
|
59
|
+
"docx": "^9.6.0",
|
|
60
|
+
"exceljs": "^4.4.0",
|
|
61
|
+
"pdfmake": "^0.3.7",
|
|
62
|
+
"pptxgenjs": "^4.0.1"
|
|
51
63
|
}
|
|
52
64
|
}
|
package/src/builder.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { download } from
|
|
1
|
+
import { download } from "./download"
|
|
2
2
|
import {
|
|
3
3
|
Button,
|
|
4
4
|
Code,
|
|
@@ -12,11 +12,12 @@ import {
|
|
|
12
12
|
Page,
|
|
13
13
|
PageBreak,
|
|
14
14
|
Quote,
|
|
15
|
+
Section,
|
|
15
16
|
Spacer,
|
|
16
17
|
Table,
|
|
17
18
|
Text,
|
|
18
|
-
} from
|
|
19
|
-
import { render } from
|
|
19
|
+
} from "./nodes"
|
|
20
|
+
import { render } from "./render"
|
|
20
21
|
import type {
|
|
21
22
|
ButtonProps,
|
|
22
23
|
CodeProps,
|
|
@@ -32,7 +33,7 @@ import type {
|
|
|
32
33
|
RenderOptions,
|
|
33
34
|
TableProps,
|
|
34
35
|
TextProps,
|
|
35
|
-
} from
|
|
36
|
+
} from "./types"
|
|
36
37
|
|
|
37
38
|
/**
|
|
38
39
|
* Create a document using the builder pattern — no JSX needed.
|
|
@@ -56,21 +57,21 @@ export function createDocument(props: DocumentProps = {}): DocumentBuilder {
|
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
const builder: DocumentBuilder = {
|
|
59
|
-
heading(text: string, p?: Omit<HeadingProps,
|
|
60
|
+
heading(text: string, p?: Omit<HeadingProps, "children">) {
|
|
60
61
|
sections.push(Heading({ ...p, children: text }))
|
|
61
62
|
return builder
|
|
62
63
|
},
|
|
63
64
|
|
|
64
|
-
text(text: string, p?: Omit<TextProps,
|
|
65
|
+
text(text: string, p?: Omit<TextProps, "children">) {
|
|
65
66
|
sections.push(Text({ ...p, children: text }))
|
|
66
67
|
return builder
|
|
67
68
|
},
|
|
68
69
|
|
|
69
|
-
paragraph(text: string, p?: Omit<TextProps,
|
|
70
|
+
paragraph(text: string, p?: Omit<TextProps, "children">) {
|
|
70
71
|
return builder.text(text, p)
|
|
71
72
|
},
|
|
72
73
|
|
|
73
|
-
image(src: string, p?: Omit<ImageProps,
|
|
74
|
+
image(src: string, p?: Omit<ImageProps, "src">) {
|
|
74
75
|
sections.push(Image({ src, ...p }))
|
|
75
76
|
return builder
|
|
76
77
|
},
|
|
@@ -80,7 +81,7 @@ export function createDocument(props: DocumentProps = {}): DocumentBuilder {
|
|
|
80
81
|
return builder
|
|
81
82
|
},
|
|
82
83
|
|
|
83
|
-
list(items: string[], p?: Omit<ListProps,
|
|
84
|
+
list(items: string[], p?: Omit<ListProps, "children">) {
|
|
84
85
|
sections.push(
|
|
85
86
|
List({
|
|
86
87
|
...p,
|
|
@@ -90,7 +91,7 @@ export function createDocument(props: DocumentProps = {}): DocumentBuilder {
|
|
|
90
91
|
return builder
|
|
91
92
|
},
|
|
92
93
|
|
|
93
|
-
code(text: string, p?: Omit<CodeProps,
|
|
94
|
+
code(text: string, p?: Omit<CodeProps, "children">) {
|
|
94
95
|
sections.push(Code({ ...p, children: text }))
|
|
95
96
|
return builder
|
|
96
97
|
},
|
|
@@ -105,17 +106,17 @@ export function createDocument(props: DocumentProps = {}): DocumentBuilder {
|
|
|
105
106
|
return builder
|
|
106
107
|
},
|
|
107
108
|
|
|
108
|
-
quote(text: string, p?: Omit<QuoteProps,
|
|
109
|
+
quote(text: string, p?: Omit<QuoteProps, "children">) {
|
|
109
110
|
sections.push(Quote({ ...p, children: text }))
|
|
110
111
|
return builder
|
|
111
112
|
},
|
|
112
113
|
|
|
113
|
-
button(text: string, p: Omit<ButtonProps,
|
|
114
|
+
button(text: string, p: Omit<ButtonProps, "children">) {
|
|
114
115
|
sections.push(Button({ ...p, children: text }))
|
|
115
116
|
return builder
|
|
116
117
|
},
|
|
117
118
|
|
|
118
|
-
link(text: string, p: Omit<LinkProps,
|
|
119
|
+
link(text: string, p: Omit<LinkProps, "children">) {
|
|
119
120
|
sections.push(Link({ ...p, children: text }))
|
|
120
121
|
return builder
|
|
121
122
|
},
|
|
@@ -125,14 +126,25 @@ export function createDocument(props: DocumentProps = {}): DocumentBuilder {
|
|
|
125
126
|
return builder
|
|
126
127
|
},
|
|
127
128
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
add(node) {
|
|
130
|
+
if (Array.isArray(node)) {
|
|
131
|
+
sections.push(...node)
|
|
132
|
+
} else {
|
|
133
|
+
sections.push(node)
|
|
134
|
+
}
|
|
135
|
+
return builder
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
section(children) {
|
|
139
|
+
sections.push(Section({ children }))
|
|
140
|
+
return builder
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
chart(instance: unknown, p?: { width?: number; height?: number; caption?: string }) {
|
|
132
144
|
// Try to get data URL from chart instance
|
|
133
145
|
const inst = instance as { getDataURL?: (opts: unknown) => string }
|
|
134
146
|
if (inst?.getDataURL) {
|
|
135
|
-
const dataUrl = inst.getDataURL({ type:
|
|
147
|
+
const dataUrl = inst.getDataURL({ type: "png", pixelRatio: 2 })
|
|
136
148
|
sections.push(
|
|
137
149
|
Image({
|
|
138
150
|
src: dataUrl,
|
|
@@ -144,19 +156,16 @@ export function createDocument(props: DocumentProps = {}): DocumentBuilder {
|
|
|
144
156
|
} else {
|
|
145
157
|
sections.push(
|
|
146
158
|
Text({
|
|
147
|
-
children:
|
|
159
|
+
children: "[Chart]",
|
|
148
160
|
italic: true,
|
|
149
|
-
color:
|
|
161
|
+
color: "#999",
|
|
150
162
|
} as TextProps & { children: string }),
|
|
151
163
|
)
|
|
152
164
|
}
|
|
153
165
|
return builder
|
|
154
166
|
},
|
|
155
167
|
|
|
156
|
-
flow(
|
|
157
|
-
instance: unknown,
|
|
158
|
-
p?: { width?: number; height?: number; caption?: string },
|
|
159
|
-
) {
|
|
168
|
+
flow(instance: unknown, p?: { width?: number; height?: number; caption?: string }) {
|
|
160
169
|
// Try to get SVG from flow instance
|
|
161
170
|
const inst = instance as { toSVG?: () => string }
|
|
162
171
|
if (inst?.toSVG) {
|
|
@@ -172,9 +181,9 @@ export function createDocument(props: DocumentProps = {}): DocumentBuilder {
|
|
|
172
181
|
} else {
|
|
173
182
|
sections.push(
|
|
174
183
|
Text({
|
|
175
|
-
children:
|
|
184
|
+
children: "[Flow Diagram]",
|
|
176
185
|
italic: true,
|
|
177
|
-
color:
|
|
186
|
+
color: "#999",
|
|
178
187
|
} as TextProps & { children: string }),
|
|
179
188
|
)
|
|
180
189
|
}
|
|
@@ -186,75 +195,75 @@ export function createDocument(props: DocumentProps = {}): DocumentBuilder {
|
|
|
186
195
|
},
|
|
187
196
|
|
|
188
197
|
async toHtml(options?: RenderOptions) {
|
|
189
|
-
return render(getNode(),
|
|
198
|
+
return render(getNode(), "html", options) as Promise<string>
|
|
190
199
|
},
|
|
191
200
|
|
|
192
201
|
async toPdf(options?: RenderOptions) {
|
|
193
|
-
return render(getNode(),
|
|
202
|
+
return render(getNode(), "pdf", options) as Promise<Uint8Array>
|
|
194
203
|
},
|
|
195
204
|
|
|
196
205
|
async toDocx(options?: RenderOptions) {
|
|
197
|
-
return render(getNode(),
|
|
206
|
+
return render(getNode(), "docx", options) as Promise<Uint8Array>
|
|
198
207
|
},
|
|
199
208
|
|
|
200
209
|
async toEmail(options?: RenderOptions) {
|
|
201
|
-
return render(getNode(),
|
|
210
|
+
return render(getNode(), "email", options) as Promise<string>
|
|
202
211
|
},
|
|
203
212
|
|
|
204
213
|
async toPptx(options?: RenderOptions) {
|
|
205
|
-
return render(getNode(),
|
|
214
|
+
return render(getNode(), "pptx", options) as Promise<Uint8Array>
|
|
206
215
|
},
|
|
207
216
|
|
|
208
217
|
async toXlsx(options?: RenderOptions) {
|
|
209
|
-
return render(getNode(),
|
|
218
|
+
return render(getNode(), "xlsx", options) as Promise<Uint8Array>
|
|
210
219
|
},
|
|
211
220
|
|
|
212
221
|
async toMarkdown(options?: RenderOptions) {
|
|
213
|
-
return render(getNode(),
|
|
222
|
+
return render(getNode(), "md", options) as Promise<string>
|
|
214
223
|
},
|
|
215
224
|
|
|
216
225
|
async toText(options?: RenderOptions) {
|
|
217
|
-
return render(getNode(),
|
|
226
|
+
return render(getNode(), "text", options) as Promise<string>
|
|
218
227
|
},
|
|
219
228
|
|
|
220
229
|
async toCsv(options?: RenderOptions) {
|
|
221
|
-
return render(getNode(),
|
|
230
|
+
return render(getNode(), "csv", options) as Promise<string>
|
|
222
231
|
},
|
|
223
232
|
|
|
224
233
|
async toSlack(options?: RenderOptions) {
|
|
225
|
-
return render(getNode(),
|
|
234
|
+
return render(getNode(), "slack", options) as Promise<string>
|
|
226
235
|
},
|
|
227
236
|
|
|
228
237
|
async toSvg(options?: RenderOptions) {
|
|
229
|
-
return render(getNode(),
|
|
238
|
+
return render(getNode(), "svg", options) as Promise<string>
|
|
230
239
|
},
|
|
231
240
|
|
|
232
241
|
async toTeams(options?: RenderOptions) {
|
|
233
|
-
return render(getNode(),
|
|
242
|
+
return render(getNode(), "teams", options) as Promise<string>
|
|
234
243
|
},
|
|
235
244
|
|
|
236
245
|
async toDiscord(options?: RenderOptions) {
|
|
237
|
-
return render(getNode(),
|
|
246
|
+
return render(getNode(), "discord", options) as Promise<string>
|
|
238
247
|
},
|
|
239
248
|
|
|
240
249
|
async toTelegram(options?: RenderOptions) {
|
|
241
|
-
return render(getNode(),
|
|
250
|
+
return render(getNode(), "telegram", options) as Promise<string>
|
|
242
251
|
},
|
|
243
252
|
|
|
244
253
|
async toNotion(options?: RenderOptions) {
|
|
245
|
-
return render(getNode(),
|
|
254
|
+
return render(getNode(), "notion", options) as Promise<string>
|
|
246
255
|
},
|
|
247
256
|
|
|
248
257
|
async toConfluence(options?: RenderOptions) {
|
|
249
|
-
return render(getNode(),
|
|
258
|
+
return render(getNode(), "confluence", options) as Promise<string>
|
|
250
259
|
},
|
|
251
260
|
|
|
252
261
|
async toWhatsApp(options?: RenderOptions) {
|
|
253
|
-
return render(getNode(),
|
|
262
|
+
return render(getNode(), "whatsapp", options) as Promise<string>
|
|
254
263
|
},
|
|
255
264
|
|
|
256
265
|
async toGoogleChat(options?: RenderOptions) {
|
|
257
|
-
return render(getNode(),
|
|
266
|
+
return render(getNode(), "google-chat", options) as Promise<string>
|
|
258
267
|
},
|
|
259
268
|
|
|
260
269
|
async download(filename: string, options?: RenderOptions) {
|
package/src/download.ts
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
import { render } from
|
|
2
|
-
import type { DocNode, RenderOptions } from
|
|
1
|
+
import { render } from "./render"
|
|
2
|
+
import type { DocNode, RenderOptions } from "./types"
|
|
3
3
|
|
|
4
4
|
const FORMAT_MAP: Record<string, string> = {
|
|
5
|
-
html:
|
|
6
|
-
htm:
|
|
7
|
-
pdf:
|
|
8
|
-
docx:
|
|
9
|
-
doc:
|
|
10
|
-
xlsx:
|
|
11
|
-
xls:
|
|
12
|
-
pptx:
|
|
13
|
-
ppt:
|
|
14
|
-
md:
|
|
15
|
-
txt:
|
|
16
|
-
csv:
|
|
17
|
-
svg:
|
|
5
|
+
html: "html",
|
|
6
|
+
htm: "html",
|
|
7
|
+
pdf: "pdf",
|
|
8
|
+
docx: "docx",
|
|
9
|
+
doc: "docx",
|
|
10
|
+
xlsx: "xlsx",
|
|
11
|
+
xls: "xlsx",
|
|
12
|
+
pptx: "pptx",
|
|
13
|
+
ppt: "pptx",
|
|
14
|
+
md: "md",
|
|
15
|
+
txt: "text",
|
|
16
|
+
csv: "csv",
|
|
17
|
+
svg: "svg",
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
const MIME_TYPES: Record<string, string> = {
|
|
21
|
-
html:
|
|
22
|
-
pdf:
|
|
23
|
-
docx:
|
|
24
|
-
xlsx:
|
|
25
|
-
pptx:
|
|
26
|
-
email:
|
|
27
|
-
md:
|
|
28
|
-
text:
|
|
29
|
-
csv:
|
|
30
|
-
svg:
|
|
21
|
+
html: "text/html",
|
|
22
|
+
pdf: "application/pdf",
|
|
23
|
+
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
24
|
+
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
25
|
+
pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
26
|
+
email: "text/html",
|
|
27
|
+
md: "text/markdown",
|
|
28
|
+
text: "text/plain",
|
|
29
|
+
csv: "text/csv",
|
|
30
|
+
svg: "image/svg+xml",
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
/**
|
|
@@ -44,17 +44,15 @@ export async function download(
|
|
|
44
44
|
filename: string,
|
|
45
45
|
options?: RenderOptions,
|
|
46
46
|
): Promise<void> {
|
|
47
|
-
const ext = filename.split(
|
|
47
|
+
const ext = filename.split(".").pop()?.toLowerCase()
|
|
48
48
|
if (!ext) {
|
|
49
|
-
throw new Error(
|
|
50
|
-
'[@pyreon/document] Filename must have an extension (e.g., report.pdf).',
|
|
51
|
-
)
|
|
49
|
+
throw new Error("[@pyreon/document] Filename must have an extension (e.g., report.pdf).")
|
|
52
50
|
}
|
|
53
51
|
|
|
54
52
|
const format = FORMAT_MAP[ext]
|
|
55
53
|
if (!format) {
|
|
56
54
|
throw new Error(
|
|
57
|
-
`[@pyreon/document] Unknown file extension '.${ext}'. Supported: ${Object.keys(FORMAT_MAP).join(
|
|
55
|
+
`[@pyreon/document] Unknown file extension '.${ext}'. Supported: ${Object.keys(FORMAT_MAP).join(", ")}`,
|
|
58
56
|
)
|
|
59
57
|
}
|
|
60
58
|
|
|
@@ -64,17 +62,15 @@ export async function download(
|
|
|
64
62
|
result instanceof Uint8Array
|
|
65
63
|
? new Blob([result as BlobPart])
|
|
66
64
|
: new Blob([result], {
|
|
67
|
-
type: MIME_TYPES[format] ??
|
|
65
|
+
type: MIME_TYPES[format] ?? "application/octet-stream",
|
|
68
66
|
})
|
|
69
67
|
|
|
70
|
-
if (typeof document ===
|
|
71
|
-
throw new Error(
|
|
72
|
-
'[@pyreon/document] download() requires a browser environment.',
|
|
73
|
-
)
|
|
68
|
+
if (typeof document === "undefined") {
|
|
69
|
+
throw new Error("[@pyreon/document] download() requires a browser environment.")
|
|
74
70
|
}
|
|
75
71
|
|
|
76
72
|
const url = URL.createObjectURL(blob)
|
|
77
|
-
const a = document.createElement(
|
|
73
|
+
const a = document.createElement("a")
|
|
78
74
|
a.href = url
|
|
79
75
|
a.download = filename
|
|
80
76
|
a.click()
|
package/src/env.d.ts
CHANGED
|
@@ -1,17 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
createPdf: (docDefinition: Record<string, unknown>) => {
|
|
5
|
-
getBuffer: (callback: (buffer: ArrayBuffer) => void) => void
|
|
6
|
-
}
|
|
7
|
-
}
|
|
8
|
-
export default pdfMake
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
declare module 'pdfmake/build/vfs_fonts' {
|
|
12
|
-
const fonts: {
|
|
13
|
-
pdfMake?: { vfs: Record<string, string> }
|
|
14
|
-
vfs?: Record<string, string>
|
|
15
|
-
}
|
|
16
|
-
export default fonts
|
|
17
|
-
}
|
|
1
|
+
// Type declarations for optional dependencies are provided by:
|
|
2
|
+
// - @types/pdfmake (pdfmake/build/pdfmake, pdfmake/build/vfs_fonts)
|
|
3
|
+
// - docx, exceljs, pptxgenjs ship their own types
|
package/src/index.ts
CHANGED
|
@@ -28,8 +28,9 @@
|
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
30
|
// Builder
|
|
31
|
-
export { createDocument } from
|
|
32
|
-
|
|
31
|
+
export { createDocument } from "./builder"
|
|
32
|
+
// Download (browser)
|
|
33
|
+
export { download } from "./download"
|
|
33
34
|
// Primitives
|
|
34
35
|
export {
|
|
35
36
|
Button,
|
|
@@ -51,10 +52,7 @@ export {
|
|
|
51
52
|
Spacer,
|
|
52
53
|
Table,
|
|
53
54
|
Text,
|
|
54
|
-
} from
|
|
55
|
-
|
|
56
|
-
// Download (browser)
|
|
57
|
-
export { download } from './download'
|
|
55
|
+
} from "./nodes"
|
|
58
56
|
|
|
59
57
|
// Render
|
|
60
58
|
export {
|
|
@@ -62,7 +60,7 @@ export {
|
|
|
62
60
|
registerRenderer,
|
|
63
61
|
render,
|
|
64
62
|
unregisterRenderer,
|
|
65
|
-
} from
|
|
63
|
+
} from "./render"
|
|
66
64
|
|
|
67
65
|
// Types
|
|
68
66
|
export type {
|
|
@@ -95,4 +93,4 @@ export type {
|
|
|
95
93
|
TableColumn,
|
|
96
94
|
TableProps,
|
|
97
95
|
TextProps,
|
|
98
|
-
} from
|
|
96
|
+
} from "./types"
|