@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.
Files changed (76) hide show
  1. package/lib/analysis/index.js.html +1 -1
  2. package/lib/confluence-Bd3ua1Ut.js.map +1 -1
  3. package/lib/csv-COrS4qdy.js.map +1 -1
  4. package/lib/discord-BLUnkEh9.js.map +1 -1
  5. package/lib/{dist-BsqdI2nY.js → dist-CYL41kqQ.js} +2 -2
  6. package/lib/dist-CYL41kqQ.js.map +1 -0
  7. package/lib/{docx-BEBOihjl.js → docx-uNAel545.js} +7 -2
  8. package/lib/docx-uNAel545.js.map +1 -0
  9. package/lib/email-D0bbfWq4.js.map +1 -1
  10. package/lib/{exceljs-BoIDUUaw.js → exceljs-BYETsesT.js} +314 -314
  11. package/lib/exceljs-BYETsesT.js.map +1 -0
  12. package/lib/google-chat-CkKCBUWC.js.map +1 -1
  13. package/lib/html-B5biprN2.js.map +1 -1
  14. package/lib/index.js +17 -8
  15. package/lib/index.js.map +1 -1
  16. package/lib/markdown-CdtlFGC0.js.map +1 -1
  17. package/lib/notion-iG2C5bEY.js.map +1 -1
  18. package/lib/{pdf-DIUQUEdj.js → pdf-IuBgTb3T.js} +9 -3
  19. package/lib/pdf-IuBgTb3T.js.map +1 -0
  20. package/lib/{pdfmake-DnmLxK4Q.js → pdfmake-CKMX5URW.js} +2 -4
  21. package/lib/pdfmake-CKMX5URW.js.map +1 -0
  22. package/lib/{pptx-Dd33oL3_.js → pptx-DXiMiYFM.js} +7 -2
  23. package/lib/pptx-DXiMiYFM.js.map +1 -0
  24. package/lib/{pptxgen.es-COcgXsyx.js → pptxgen.es-FsqHs8mD.js} +3 -6
  25. package/lib/pptxgen.es-FsqHs8mD.js.map +1 -0
  26. package/lib/sanitize-O_3j1mNJ.js.map +1 -1
  27. package/lib/slack-BI3EQwYm.js.map +1 -1
  28. package/lib/svg-BKxumy-p.js.map +1 -1
  29. package/lib/teams-Cwz9lce0.js.map +1 -1
  30. package/lib/telegram-gYFqyMXb.js.map +1 -1
  31. package/lib/text-l1XNXBOC.js.map +1 -1
  32. package/lib/types/index.d.ts +43 -39
  33. package/lib/types/index.d.ts.map +1 -1
  34. package/lib/{vfs_fonts-Df1kkZ4Y.js → vfs_fonts-Cap07Jg3.js} +2 -2
  35. package/lib/vfs_fonts-Cap07Jg3.js.map +1 -0
  36. package/lib/whatsapp-CjSGoOKx.js.map +1 -1
  37. package/lib/{xlsx-Bb5TWyXQ.js → xlsx-Cvu4LBNy.js} +8 -2
  38. package/lib/xlsx-Cvu4LBNy.js.map +1 -0
  39. package/package.json +19 -7
  40. package/src/builder.ts +53 -44
  41. package/src/download.ts +32 -36
  42. package/src/env.d.ts +3 -17
  43. package/src/index.ts +6 -8
  44. package/src/nodes.ts +45 -45
  45. package/src/render.ts +45 -118
  46. package/src/renderers/confluence.ts +64 -80
  47. package/src/renderers/csv.ts +11 -18
  48. package/src/renderers/discord.ts +38 -50
  49. package/src/renderers/docx.ts +78 -120
  50. package/src/renderers/email.ts +73 -92
  51. package/src/renderers/google-chat.ts +35 -47
  52. package/src/renderers/html.ts +78 -101
  53. package/src/renderers/markdown.ts +43 -53
  54. package/src/renderers/notion.ts +63 -85
  55. package/src/renderers/pdf.ts +92 -115
  56. package/src/renderers/pptx.ts +60 -66
  57. package/src/renderers/slack.ts +49 -61
  58. package/src/renderers/svg.ts +49 -63
  59. package/src/renderers/teams.ts +68 -80
  60. package/src/renderers/telegram.ts +40 -54
  61. package/src/renderers/text.ts +44 -66
  62. package/src/renderers/whatsapp.ts +34 -48
  63. package/src/renderers/xlsx.ts +47 -61
  64. package/src/sanitize.ts +21 -25
  65. package/src/tests/document.test.ts +1337 -1385
  66. package/src/tests/stress.test.ts +111 -111
  67. package/src/types.ts +66 -65
  68. package/lib/dist-BsqdI2nY.js.map +0 -1
  69. package/lib/docx-BEBOihjl.js.map +0 -1
  70. package/lib/exceljs-BoIDUUaw.js.map +0 -1
  71. package/lib/pdf-DIUQUEdj.js.map +0 -1
  72. package/lib/pdfmake-DnmLxK4Q.js.map +0 -1
  73. package/lib/pptx-Dd33oL3_.js.map +0 -1
  74. package/lib/pptxgen.es-COcgXsyx.js.map +0 -1
  75. package/lib/vfs_fonts-Df1kkZ4Y.js.map +0 -1
  76. package/lib/xlsx-Bb5TWyXQ.js.map +0 -1
@@ -1,80 +1,67 @@
1
- import {
2
- sanitizeColor,
3
- sanitizeHref,
4
- sanitizeImageSrc,
5
- sanitizeStyle,
6
- } from '../sanitize'
7
- import type {
8
- DocChild,
9
- DocNode,
10
- DocumentRenderer,
11
- RenderOptions,
12
- TableColumn,
13
- } from '../types'
1
+ import { sanitizeColor, sanitizeHref, sanitizeImageSrc, sanitizeStyle } from "../sanitize"
2
+ import type { DocChild, DocNode, DocumentRenderer, RenderOptions, TableColumn } from "../types"
14
3
 
15
4
  function escapeHtml(str: string): string {
16
5
  return str
17
- .replace(/&/g, '&')
18
- .replace(/</g, '&lt;')
19
- .replace(/>/g, '&gt;')
20
- .replace(/"/g, '&quot;')
6
+ .replace(/&/g, "&amp;")
7
+ .replace(/</g, "&lt;")
8
+ .replace(/>/g, "&gt;")
9
+ .replace(/"/g, "&quot;")
21
10
  }
22
11
 
23
12
  function resolveColumn(col: string | TableColumn): TableColumn {
24
- return typeof col === 'string' ? { header: col } : col
13
+ return typeof col === "string" ? { header: col } : col
25
14
  }
26
15
 
27
16
  function styleStr(styles: Record<string, string | number | undefined>): string {
28
17
  const parts: string[] = []
29
18
  for (const [k, v] of Object.entries(styles)) {
30
- if (v != null && v !== '') {
31
- const prop = k.replace(/([A-Z])/g, '-$1').toLowerCase()
32
- parts.push(`${prop}:${typeof v === 'number' ? `${v}px` : v}`)
19
+ if (v != null && v !== "") {
20
+ const prop = k.replace(/([A-Z])/g, "-$1").toLowerCase()
21
+ parts.push(`${prop}:${typeof v === "number" ? `${v}px` : v}`)
33
22
  }
34
23
  }
35
- return parts.length > 0 ? ` style="${parts.join(';')}"` : ''
24
+ return parts.length > 0 ? ` style="${parts.join(";")}"` : ""
36
25
  }
37
26
 
38
27
  function padStr(
39
28
  pad: number | [number, number] | [number, number, number, number] | undefined,
40
29
  ): string | undefined {
41
30
  if (pad == null) return undefined
42
- if (typeof pad === 'number') return `${pad}px`
31
+ if (typeof pad === "number") return `${pad}px`
43
32
  if (pad.length === 2) return `${pad[0]}px ${pad[1]}px`
44
33
  return `${pad[0]}px ${pad[1]}px ${pad[2]}px ${pad[3]}px`
45
34
  }
46
35
 
47
36
  function renderChild(child: DocChild): string {
48
- if (typeof child === 'string') return escapeHtml(child)
37
+ if (typeof child === "string") return escapeHtml(child)
49
38
  return renderNode(child)
50
39
  }
51
40
 
52
41
  function renderChildren(children: DocChild[]): string {
53
- return children.map(renderChild).join('')
42
+ return children.map(renderChild).join("")
54
43
  }
55
44
 
56
45
  function renderNode(node: DocNode): string {
57
46
  const p = node.props
58
47
 
59
48
  switch (node.type) {
60
- case 'document': {
61
- const lang = (p.language as string) ?? 'en'
62
- const title = p.title
63
- ? `<title>${escapeHtml(p.title as string)}</title>`
64
- : ''
49
+ case "document": {
50
+ const lang = (p.language as string) ?? "en"
51
+ const title = p.title ? `<title>${escapeHtml(p.title as string)}</title>` : ""
65
52
  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>`
66
53
  }
67
54
 
68
- case 'page': {
55
+ case "page": {
69
56
  const margin = padStr(p.margin as PageMargin)
70
- return `<div${styleStr({ maxWidth: '800px', margin: margin ?? '0 auto', padding: margin ?? '40px' })}>${renderChildren(node.children)}</div>`
57
+ return `<div${styleStr({ maxWidth: "800px", margin: margin ?? "0 auto", padding: margin ?? "40px" })}>${renderChildren(node.children)}</div>`
71
58
  }
72
59
 
73
- case 'section': {
74
- const dir = (p.direction as string) ?? 'column'
60
+ case "section": {
61
+ const dir = (p.direction as string) ?? "column"
75
62
  return `<div${styleStr({
76
- display: dir === 'row' ? 'flex' : 'block',
77
- flexDirection: dir === 'row' ? 'row' : undefined,
63
+ display: dir === "row" ? "flex" : "block",
64
+ flexDirection: dir === "row" ? "row" : undefined,
78
65
  gap: p.gap as number | undefined,
79
66
  padding: padStr(p.padding as PageMargin),
80
67
  background: sanitizeColor(p.background as string | undefined),
@@ -82,55 +69,49 @@ function renderNode(node: DocNode): string {
82
69
  })}>${renderChildren(node.children)}</div>`
83
70
  }
84
71
 
85
- case 'row':
86
- return `<div${styleStr({ display: 'flex', gap: p.gap as number | undefined, alignItems: p.align as string | undefined })}>${renderChildren(node.children)}</div>`
72
+ case "row":
73
+ return `<div${styleStr({ display: "flex", gap: p.gap as number | undefined, alignItems: p.align as string | undefined })}>${renderChildren(node.children)}</div>`
87
74
 
88
- case 'column':
89
- return `<div${styleStr({ flex: p.width ? undefined : '1', width: p.width as string | undefined, textAlign: p.align as string | undefined })}>${renderChildren(node.children)}</div>`
75
+ case "column":
76
+ return `<div${styleStr({ flex: p.width ? undefined : "1", width: p.width as string | undefined, textAlign: p.align as string | undefined })}>${renderChildren(node.children)}</div>`
90
77
 
91
- case 'heading': {
78
+ case "heading": {
92
79
  const level = (p.level as number) ?? 1
93
80
  const tag = `h${Math.min(Math.max(level, 1), 6)}`
94
81
  return `<${tag}${styleStr({ color: sanitizeColor(p.color as string | undefined), textAlign: p.align as string | undefined })}>${renderChildren(node.children)}</${tag}>`
95
82
  }
96
83
 
97
- case 'text': {
84
+ case "text": {
98
85
  return `<p${styleStr({
99
86
  fontSize: p.size as number | undefined,
100
87
  color: sanitizeColor(p.color as string | undefined),
101
- fontWeight: p.bold ? 'bold' : undefined,
102
- fontStyle: p.italic ? 'italic' : undefined,
103
- textDecoration: p.underline
104
- ? 'underline'
105
- : p.strikethrough
106
- ? 'line-through'
107
- : undefined,
88
+ fontWeight: p.bold ? "bold" : undefined,
89
+ fontStyle: p.italic ? "italic" : undefined,
90
+ textDecoration: p.underline ? "underline" : p.strikethrough ? "line-through" : undefined,
108
91
  textAlign: p.align as string | undefined,
109
92
  lineHeight: p.lineHeight as number | undefined,
110
93
  })}>${renderChildren(node.children)}</p>`
111
94
  }
112
95
 
113
- case 'link':
96
+ case "link":
114
97
  return `<a href="${escapeHtml(sanitizeHref(p.href as string))}"${styleStr({ color: sanitizeColor(p.color as string | undefined) })}>${renderChildren(node.children)}</a>`
115
98
 
116
- case 'image': {
99
+ case "image": {
117
100
  const alignStyle =
118
- p.align === 'center'
119
- ? 'display:block;margin:0 auto'
120
- : p.align === 'right'
121
- ? 'display:block;margin-left:auto'
122
- : ''
123
- const img = `<img src="${escapeHtml(sanitizeImageSrc(p.src as string))}"${p.width ? ` width="${p.width}"` : ''}${p.height ? ` height="${p.height}"` : ''}${p.alt ? ` alt="${escapeHtml(p.alt as string)}"` : ''}${alignStyle ? ` style="${sanitizeStyle(alignStyle)}"` : ''} />`
101
+ p.align === "center"
102
+ ? "display:block;margin:0 auto"
103
+ : p.align === "right"
104
+ ? "display:block;margin-left:auto"
105
+ : ""
106
+ const img = `<img src="${escapeHtml(sanitizeImageSrc(p.src as string))}"${p.width ? ` width="${p.width}"` : ""}${p.height ? ` height="${p.height}"` : ""}${p.alt ? ` alt="${escapeHtml(p.alt as string)}"` : ""}${alignStyle ? ` style="${sanitizeStyle(alignStyle)}"` : ""} />`
124
107
  if (p.caption) {
125
- return `<figure${p.align === 'center' ? ' style="text-align:center"' : ''}>${img}<figcaption>${escapeHtml(p.caption as string)}</figcaption></figure>`
108
+ return `<figure${p.align === "center" ? ' style="text-align:center"' : ""}>${img}<figcaption>${escapeHtml(p.caption as string)}</figcaption></figure>`
126
109
  }
127
110
  return img
128
111
  }
129
112
 
130
- case 'table': {
131
- const columns = ((p.columns ?? []) as (string | TableColumn)[]).map(
132
- resolveColumn,
133
- )
113
+ case "table": {
114
+ const columns = ((p.columns ?? []) as (string | TableColumn)[]).map(resolveColumn)
134
115
  const rows = (p.rows ?? []) as (string | number)[][]
135
116
  const hs = p.headerStyle as
136
117
  | { background?: string; color?: string; bold?: boolean }
@@ -138,80 +119,76 @@ function renderNode(node: DocNode): string {
138
119
  const striped = p.striped as boolean | undefined
139
120
  const bordered = p.bordered as boolean | undefined
140
121
  const borderStyle = bordered
141
- ? 'border:1px solid #ddd;border-collapse:collapse;'
142
- : 'border-collapse:collapse;'
122
+ ? "border:1px solid #ddd;border-collapse:collapse;"
123
+ : "border-collapse:collapse;"
143
124
 
144
125
  let html = `<table style="width:100%;${borderStyle}">`
145
- if (p.caption)
146
- html += `<caption>${escapeHtml(p.caption as string)}</caption>`
126
+ if (p.caption) html += `<caption>${escapeHtml(p.caption as string)}</caption>`
147
127
 
148
- html += '<thead><tr>'
128
+ html += "<thead><tr>"
149
129
  for (const col of columns) {
150
- const cellBorder = bordered ? 'border:1px solid #ddd;' : ''
151
- const bgStyle = hs?.background
152
- ? `background:${sanitizeColor(hs.background)};`
153
- : ''
154
- const colorStyle = hs?.color ? `color:${sanitizeColor(hs.color)};` : ''
155
- const fontStyle = hs?.bold !== false ? 'font-weight:bold;' : ''
156
- const alignStyle = col.align ? `text-align:${col.align};` : ''
130
+ const cellBorder = bordered ? "border:1px solid #ddd;" : ""
131
+ const bgStyle = hs?.background ? `background:${sanitizeColor(hs.background)};` : ""
132
+ const colorStyle = hs?.color ? `color:${sanitizeColor(hs.color)};` : ""
133
+ const fontStyle = hs?.bold !== false ? "font-weight:bold;" : ""
134
+ const alignStyle = col.align ? `text-align:${col.align};` : ""
157
135
  const widthStyle = col.width
158
- ? `width:${typeof col.width === 'number' ? `${col.width}px` : col.width};`
159
- : ''
136
+ ? `width:${typeof col.width === "number" ? `${col.width}px` : col.width};`
137
+ : ""
160
138
  html += `<th style="${cellBorder}${bgStyle}${colorStyle}${fontStyle}${alignStyle}${widthStyle}padding:8px">${escapeHtml(col.header)}</th>`
161
139
  }
162
- html += '</tr></thead>'
140
+ html += "</tr></thead>"
163
141
 
164
- html += '<tbody>'
142
+ html += "<tbody>"
165
143
  for (let i = 0; i < rows.length; i++) {
166
- const rowBg =
167
- striped && i % 2 === 1 ? ' style="background:#f9f9f9"' : ''
144
+ const rowBg = striped && i % 2 === 1 ? ' style="background:#f9f9f9"' : ""
168
145
  html += `<tr${rowBg}>`
169
146
  for (let j = 0; j < columns.length; j++) {
170
- const cellBorder = bordered ? 'border:1px solid #ddd;' : ''
147
+ const cellBorder = bordered ? "border:1px solid #ddd;" : ""
171
148
  const col = columns[j]
172
- const alignStyle = col?.align ? `text-align:${col.align};` : ''
173
- html += `<td style="${cellBorder}${alignStyle}padding:8px">${escapeHtml(String(rows[i]?.[j] ?? ''))}</td>`
149
+ const alignStyle = col?.align ? `text-align:${col.align};` : ""
150
+ html += `<td style="${cellBorder}${alignStyle}padding:8px">${escapeHtml(String(rows[i]?.[j] ?? ""))}</td>`
174
151
  }
175
- html += '</tr>'
152
+ html += "</tr>"
176
153
  }
177
- html += '</tbody></table>'
154
+ html += "</tbody></table>"
178
155
  return html
179
156
  }
180
157
 
181
- case 'list': {
182
- const tag = p.ordered ? 'ol' : 'ul'
158
+ case "list": {
159
+ const tag = p.ordered ? "ol" : "ul"
183
160
  return `<${tag}>${renderChildren(node.children)}</${tag}>`
184
161
  }
185
162
 
186
- case 'list-item':
163
+ case "list-item":
187
164
  return `<li>${renderChildren(node.children)}</li>`
188
165
 
189
- case 'code':
166
+ case "code":
190
167
  return `<pre style="background:#f5f5f5;padding:12px;border-radius:4px;overflow-x:auto"><code>${escapeHtml(renderChildren(node.children))}</code></pre>`
191
168
 
192
- case 'divider': {
193
- const color = sanitizeColor((p.color as string) ?? '#ddd')
169
+ case "divider": {
170
+ const color = sanitizeColor((p.color as string) ?? "#ddd")
194
171
  const thickness = (p.thickness as number) ?? 1
195
172
  return `<hr style="border:none;border-top:${thickness}px solid ${color};margin:16px 0" />`
196
173
  }
197
174
 
198
- case 'page-break':
175
+ case "page-break":
199
176
  return '<div style="page-break-after:always;break-after:page"></div>'
200
177
 
201
- case 'spacer':
178
+ case "spacer":
202
179
  return `<div style="height:${p.height}px"></div>`
203
180
 
204
- case 'button': {
205
- const bg = sanitizeColor((p.background as string) ?? '#4f46e5')
206
- const color = sanitizeColor((p.color as string) ?? '#fff')
181
+ case "button": {
182
+ const bg = sanitizeColor((p.background as string) ?? "#4f46e5")
183
+ const color = sanitizeColor((p.color as string) ?? "#fff")
207
184
  const radius = (p.borderRadius as number) ?? 4
208
185
  const pad = padStr((p.padding ?? [12, 24]) as [number, number])
209
- const align = (p.align as string) ?? 'left'
186
+ const align = (p.align as string) ?? "left"
210
187
  return `<div style="text-align:${align}"><a href="${escapeHtml(sanitizeHref(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>`
211
188
  }
212
189
 
213
- case 'quote': {
214
- const borderColor = sanitizeColor((p.borderColor as string) ?? '#ddd')
190
+ case "quote": {
191
+ const borderColor = sanitizeColor((p.borderColor as string) ?? "#ddd")
215
192
  return `<blockquote style="margin:0;padding:12px 20px;border-left:4px solid ${borderColor};color:#555">${renderChildren(node.children)}</blockquote>`
216
193
  }
217
194
 
@@ -225,8 +202,8 @@ type PageMargin = number | [number, number] | [number, number, number, number]
225
202
  export const htmlRenderer: DocumentRenderer = {
226
203
  async render(node: DocNode, options?: RenderOptions): Promise<string> {
227
204
  let html = renderNode(node)
228
- if (options?.direction === 'rtl') {
229
- html = html.replace('<body>', '<body dir="rtl" style="direction:rtl">')
205
+ if (options?.direction === "rtl") {
206
+ html = html.replace("<body>", '<body dir="rtl" style="direction:rtl">')
230
207
  }
231
208
  return html
232
209
  },
@@ -1,53 +1,47 @@
1
- import { sanitizeHref, sanitizeImageSrc } from '../sanitize'
2
- import type {
3
- DocChild,
4
- DocNode,
5
- DocumentRenderer,
6
- RenderOptions,
7
- TableColumn,
8
- } from '../types'
1
+ import { sanitizeHref, sanitizeImageSrc } from "../sanitize"
2
+ import type { DocChild, DocNode, DocumentRenderer, RenderOptions, TableColumn } from "../types"
9
3
 
10
4
  function resolveColumn(col: string | TableColumn): TableColumn {
11
- return typeof col === 'string' ? { header: col } : col
5
+ return typeof col === "string" ? { header: col } : col
12
6
  }
13
7
 
14
8
  function renderChild(child: DocChild): string {
15
- if (typeof child === 'string') return child
9
+ if (typeof child === "string") return child
16
10
  return renderNode(child)
17
11
  }
18
12
 
19
13
  function renderChildren(children: DocChild[]): string {
20
- return children.map(renderChild).join('')
14
+ return children.map(renderChild).join("")
21
15
  }
22
16
 
23
17
  function renderInline(children: DocChild[]): string {
24
- return children.map(renderChild).join('')
18
+ return children.map(renderChild).join("")
25
19
  }
26
20
 
27
21
  function renderNode(node: DocNode): string {
28
22
  const p = node.props
29
23
 
30
24
  switch (node.type) {
31
- case 'document':
25
+ case "document":
32
26
  return renderChildren(node.children)
33
27
 
34
- case 'page':
28
+ case "page":
35
29
  return renderChildren(node.children)
36
30
 
37
- case 'section':
31
+ case "section":
38
32
  return `${renderChildren(node.children)}\n`
39
33
 
40
- case 'row':
41
- case 'column':
34
+ case "row":
35
+ case "column":
42
36
  return renderChildren(node.children)
43
37
 
44
- case 'heading': {
38
+ case "heading": {
45
39
  const level = (p.level as number) ?? 1
46
- const prefix = '#'.repeat(Math.min(Math.max(level, 1), 6))
40
+ const prefix = "#".repeat(Math.min(Math.max(level, 1), 6))
47
41
  return `${prefix} ${renderInline(node.children)}\n\n`
48
42
  }
49
43
 
50
- case 'text': {
44
+ case "text": {
51
45
  let text = renderInline(node.children)
52
46
  if (p.bold) text = `**${text}**`
53
47
  if (p.italic) text = `*${text}*`
@@ -55,82 +49,78 @@ function renderNode(node: DocNode): string {
55
49
  return `${text}\n\n`
56
50
  }
57
51
 
58
- case 'link':
52
+ case "link":
59
53
  return `[${renderInline(node.children)}](${sanitizeHref(p.href as string)})`
60
54
 
61
- case 'image': {
62
- const alt = (p.alt as string) ?? ''
55
+ case "image": {
56
+ const alt = (p.alt as string) ?? ""
63
57
  let md = `![${alt}](${sanitizeImageSrc(p.src as string)})`
64
58
  if (p.caption) md += `\n*${p.caption}*`
65
59
  return `${md}\n\n`
66
60
  }
67
61
 
68
- case 'table': {
69
- const columns = ((p.columns ?? []) as (string | TableColumn)[]).map(
70
- resolveColumn,
71
- )
62
+ case "table": {
63
+ const columns = ((p.columns ?? []) as (string | TableColumn)[]).map(resolveColumn)
72
64
  const rows = (p.rows ?? []) as (string | number)[][]
73
65
 
74
- if (columns.length === 0) return ''
66
+ if (columns.length === 0) return ""
75
67
 
76
68
  // Header
77
- const header = `| ${columns.map((c) => c.header).join(' | ')} |`
69
+ const header = `| ${columns.map((c) => c.header).join(" | ")} |`
78
70
 
79
71
  // Separator with alignment
80
72
  const separator = `| ${columns
81
73
  .map((c) => {
82
- const align = c.align ?? 'left'
83
- if (align === 'center') return ':---:'
84
- if (align === 'right') return '---:'
85
- return '---'
74
+ const align = c.align ?? "left"
75
+ if (align === "center") return ":---:"
76
+ if (align === "right") return "---:"
77
+ return "---"
86
78
  })
87
- .join(' | ')} |`
79
+ .join(" | ")} |`
88
80
 
89
81
  // Rows
90
82
  const body = rows
91
- .map(
92
- (row) => `| ${row.map((cell) => String(cell ?? '')).join(' | ')} |`,
93
- )
94
- .join('\n')
83
+ .map((row) => `| ${row.map((cell) => String(cell ?? "")).join(" | ")} |`)
84
+ .join("\n")
95
85
 
96
86
  let md = `${header}\n${separator}\n${body}\n\n`
97
87
  if (p.caption) md = `*${p.caption}*\n\n${md}`
98
88
  return md
99
89
  }
100
90
 
101
- case 'list': {
91
+ case "list": {
102
92
  const ordered = p.ordered as boolean | undefined
103
93
  return `${node.children
104
- .filter((c): c is DocNode => typeof c !== 'string')
94
+ .filter((c): c is DocNode => typeof c !== "string")
105
95
  .map((item, i) => {
106
- const prefix = ordered ? `${i + 1}.` : '-'
96
+ const prefix = ordered ? `${i + 1}.` : "-"
107
97
  return `${prefix} ${renderInline(item.children)}`
108
98
  })
109
- .join('\n')}\n\n`
99
+ .join("\n")}\n\n`
110
100
  }
111
101
 
112
- case 'list-item':
102
+ case "list-item":
113
103
  return renderInline(node.children)
114
104
 
115
- case 'code': {
116
- const lang = (p.language as string) ?? ''
105
+ case "code": {
106
+ const lang = (p.language as string) ?? ""
117
107
  const content = renderInline(node.children)
118
108
  return `\`\`\`${lang}\n${content}\n\`\`\`\n\n`
119
109
  }
120
110
 
121
- case 'divider':
122
- return '---\n\n'
111
+ case "divider":
112
+ return "---\n\n"
123
113
 
124
- case 'page-break':
125
- return '---\n\n'
114
+ case "page-break":
115
+ return "---\n\n"
126
116
 
127
- case 'spacer':
128
- return '\n'
117
+ case "spacer":
118
+ return "\n"
129
119
 
130
- case 'button':
120
+ case "button":
131
121
  return `[${renderInline(node.children)}](${sanitizeHref(p.href as string)})\n\n`
132
122
 
133
- case 'quote':
123
+ case "quote":
134
124
  return `> ${renderInline(node.children)}\n\n`
135
125
 
136
126
  default: