@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
package/src/renderers/svg.ts
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
import { sanitizeColor, sanitizeHref, sanitizeImageSrc } from
|
|
2
|
-
import type {
|
|
3
|
-
DocChild,
|
|
4
|
-
DocNode,
|
|
5
|
-
DocumentRenderer,
|
|
6
|
-
RenderOptions,
|
|
7
|
-
TableColumn,
|
|
8
|
-
} from '../types'
|
|
1
|
+
import { sanitizeColor, sanitizeHref, sanitizeImageSrc } from "../sanitize"
|
|
2
|
+
import type { DocChild, DocNode, DocumentRenderer, RenderOptions, TableColumn } from "../types"
|
|
9
3
|
|
|
10
4
|
/**
|
|
11
5
|
* SVG renderer — generates a standalone SVG document from the node tree.
|
|
@@ -14,23 +8,21 @@ import type {
|
|
|
14
8
|
*/
|
|
15
9
|
|
|
16
10
|
function resolveColumn(col: string | TableColumn): TableColumn {
|
|
17
|
-
return typeof col ===
|
|
11
|
+
return typeof col === "string" ? { header: col } : col
|
|
18
12
|
}
|
|
19
13
|
|
|
20
14
|
function escapeXml(str: string): string {
|
|
21
15
|
return str
|
|
22
|
-
.replace(/&/g,
|
|
23
|
-
.replace(/</g,
|
|
24
|
-
.replace(/>/g,
|
|
25
|
-
.replace(/"/g,
|
|
16
|
+
.replace(/&/g, "&")
|
|
17
|
+
.replace(/</g, "<")
|
|
18
|
+
.replace(/>/g, ">")
|
|
19
|
+
.replace(/"/g, """)
|
|
26
20
|
}
|
|
27
21
|
|
|
28
22
|
function getTextContent(children: DocChild[]): string {
|
|
29
23
|
return children
|
|
30
|
-
.map((c) =>
|
|
31
|
-
|
|
32
|
-
)
|
|
33
|
-
.join('')
|
|
24
|
+
.map((c) => (typeof c === "string" ? c : getTextContent((c as DocNode).children)))
|
|
25
|
+
.join("")
|
|
34
26
|
}
|
|
35
27
|
|
|
36
28
|
interface RenderContext {
|
|
@@ -42,22 +34,22 @@ interface RenderContext {
|
|
|
42
34
|
function renderNode(node: DocNode, ctx: RenderContext): string {
|
|
43
35
|
const p = node.props
|
|
44
36
|
const contentWidth = ctx.width - ctx.padding * 2
|
|
45
|
-
let svg =
|
|
37
|
+
let svg = ""
|
|
46
38
|
|
|
47
39
|
switch (node.type) {
|
|
48
|
-
case
|
|
49
|
-
case
|
|
50
|
-
case
|
|
51
|
-
case
|
|
52
|
-
case
|
|
40
|
+
case "document":
|
|
41
|
+
case "page":
|
|
42
|
+
case "section":
|
|
43
|
+
case "row":
|
|
44
|
+
case "column":
|
|
53
45
|
for (const child of node.children) {
|
|
54
|
-
if (typeof child !==
|
|
46
|
+
if (typeof child !== "string") {
|
|
55
47
|
svg += renderNode(child, ctx)
|
|
56
48
|
}
|
|
57
49
|
}
|
|
58
50
|
break
|
|
59
51
|
|
|
60
|
-
case
|
|
52
|
+
case "heading": {
|
|
61
53
|
const level = (p.level as number) ?? 1
|
|
62
54
|
const sizes: Record<number, number> = {
|
|
63
55
|
1: 28,
|
|
@@ -68,7 +60,7 @@ function renderNode(node: DocNode, ctx: RenderContext): string {
|
|
|
68
60
|
6: 14,
|
|
69
61
|
}
|
|
70
62
|
const size = sizes[level] ?? 24
|
|
71
|
-
const color = sanitizeColor((p.color as string) ??
|
|
63
|
+
const color = sanitizeColor((p.color as string) ?? "#000000")
|
|
72
64
|
const text = escapeXml(getTextContent(node.children))
|
|
73
65
|
ctx.y += size + 8
|
|
74
66
|
svg += `<text x="${ctx.padding}" y="${ctx.y}" font-size="${size}" font-weight="bold" fill="${color}" font-family="system-ui, -apple-system, sans-serif">${text}</text>`
|
|
@@ -76,11 +68,11 @@ function renderNode(node: DocNode, ctx: RenderContext): string {
|
|
|
76
68
|
break
|
|
77
69
|
}
|
|
78
70
|
|
|
79
|
-
case
|
|
71
|
+
case "text": {
|
|
80
72
|
const size = (p.size as number) ?? 14
|
|
81
|
-
const color = sanitizeColor((p.color as string) ??
|
|
82
|
-
const weight = p.bold ?
|
|
83
|
-
const style = p.italic ?
|
|
73
|
+
const color = sanitizeColor((p.color as string) ?? "#333333")
|
|
74
|
+
const weight = p.bold ? "bold" : "normal"
|
|
75
|
+
const style = p.italic ? "italic" : "normal"
|
|
84
76
|
const text = escapeXml(getTextContent(node.children))
|
|
85
77
|
ctx.y += size + 4
|
|
86
78
|
svg += `<text x="${ctx.padding}" y="${ctx.y}" font-size="${size}" font-weight="${weight}" font-style="${style}" fill="${color}" font-family="system-ui, -apple-system, sans-serif">${text}</text>`
|
|
@@ -88,27 +80,27 @@ function renderNode(node: DocNode, ctx: RenderContext): string {
|
|
|
88
80
|
break
|
|
89
81
|
}
|
|
90
82
|
|
|
91
|
-
case
|
|
83
|
+
case "link": {
|
|
92
84
|
const href = sanitizeHref(p.href as string)
|
|
93
85
|
const text = escapeXml(getTextContent(node.children))
|
|
94
|
-
const color = sanitizeColor((p.color as string) ??
|
|
86
|
+
const color = sanitizeColor((p.color as string) ?? "#4f46e5")
|
|
95
87
|
ctx.y += 18
|
|
96
88
|
svg += `<a href="${escapeXml(href)}"><text x="${ctx.padding}" y="${ctx.y}" font-size="14" fill="${color}" text-decoration="underline" font-family="system-ui, -apple-system, sans-serif">${text}</text></a>`
|
|
97
89
|
ctx.y += 10
|
|
98
90
|
break
|
|
99
91
|
}
|
|
100
92
|
|
|
101
|
-
case
|
|
93
|
+
case "image": {
|
|
102
94
|
const width = (p.width as number) ?? Math.min(contentWidth, 400)
|
|
103
95
|
const height = (p.height as number) ?? 200
|
|
104
96
|
const src = sanitizeImageSrc(p.src as string)
|
|
105
97
|
|
|
106
|
-
if (src.startsWith(
|
|
98
|
+
if (src.startsWith("data:") || src.startsWith("http")) {
|
|
107
99
|
svg += `<image x="${ctx.padding}" y="${ctx.y}" width="${width}" height="${height}" href="${escapeXml(src)}" />`
|
|
108
100
|
} else {
|
|
109
101
|
// Placeholder rectangle for local paths
|
|
110
102
|
svg += `<rect x="${ctx.padding}" y="${ctx.y}" width="${width}" height="${height}" fill="#f0f0f0" stroke="#ddd" rx="4" />`
|
|
111
|
-
svg += `<text x="${ctx.padding + width / 2}" y="${ctx.y + height / 2}" text-anchor="middle" dominant-baseline="middle" font-size="12" fill="#999" font-family="system-ui, sans-serif">${escapeXml((p.alt as string) ??
|
|
103
|
+
svg += `<text x="${ctx.padding + width / 2}" y="${ctx.y + height / 2}" text-anchor="middle" dominant-baseline="middle" font-size="12" fill="#999" font-family="system-ui, sans-serif">${escapeXml((p.alt as string) ?? "Image")}</text>`
|
|
112
104
|
}
|
|
113
105
|
ctx.y += height + 8
|
|
114
106
|
|
|
@@ -120,20 +112,16 @@ function renderNode(node: DocNode, ctx: RenderContext): string {
|
|
|
120
112
|
break
|
|
121
113
|
}
|
|
122
114
|
|
|
123
|
-
case
|
|
124
|
-
const columns = ((p.columns ?? []) as (string | TableColumn)[]).map(
|
|
125
|
-
resolveColumn,
|
|
126
|
-
)
|
|
115
|
+
case "table": {
|
|
116
|
+
const columns = ((p.columns ?? []) as (string | TableColumn)[]).map(resolveColumn)
|
|
127
117
|
const rows = (p.rows ?? []) as (string | number)[][]
|
|
128
|
-
const hs = p.headerStyle as
|
|
129
|
-
| { background?: string; color?: string }
|
|
130
|
-
| undefined
|
|
118
|
+
const hs = p.headerStyle as { background?: string; color?: string } | undefined
|
|
131
119
|
const striped = p.striped as boolean | undefined
|
|
132
120
|
|
|
133
121
|
const colWidth = contentWidth / columns.length
|
|
134
122
|
const rowHeight = 28
|
|
135
|
-
const headerBg = sanitizeColor(hs?.background ??
|
|
136
|
-
const headerColor = sanitizeColor(hs?.color ??
|
|
123
|
+
const headerBg = sanitizeColor(hs?.background ?? "#f5f5f5")
|
|
124
|
+
const headerColor = sanitizeColor(hs?.color ?? "#000000")
|
|
137
125
|
|
|
138
126
|
// Header
|
|
139
127
|
svg += `<rect x="${ctx.padding}" y="${ctx.y}" width="${contentWidth}" height="${rowHeight}" fill="${headerBg}" />`
|
|
@@ -150,7 +138,7 @@ function renderNode(node: DocNode, ctx: RenderContext): string {
|
|
|
150
138
|
svg += `<rect x="${ctx.padding}" y="${ctx.y}" width="${contentWidth}" height="${rowHeight}" fill="#f9f9f9" />`
|
|
151
139
|
}
|
|
152
140
|
for (let c = 0; c < columns.length; c++) {
|
|
153
|
-
svg += `<text x="${ctx.padding + c * colWidth + 8}" y="${ctx.y + 18}" font-size="12" fill="#333" font-family="system-ui, sans-serif">${escapeXml(String(rows[r]?.[c] ??
|
|
141
|
+
svg += `<text x="${ctx.padding + c * colWidth + 8}" y="${ctx.y + 18}" font-size="12" fill="#333" font-family="system-ui, sans-serif">${escapeXml(String(rows[r]?.[c] ?? ""))}</text>`
|
|
154
142
|
}
|
|
155
143
|
ctx.y += rowHeight
|
|
156
144
|
}
|
|
@@ -161,15 +149,13 @@ function renderNode(node: DocNode, ctx: RenderContext): string {
|
|
|
161
149
|
break
|
|
162
150
|
}
|
|
163
151
|
|
|
164
|
-
case
|
|
152
|
+
case "list": {
|
|
165
153
|
const ordered = p.ordered as boolean | undefined
|
|
166
|
-
const items = node.children.filter(
|
|
167
|
-
(c): c is DocNode => typeof c !== 'string',
|
|
168
|
-
)
|
|
154
|
+
const items = node.children.filter((c): c is DocNode => typeof c !== "string")
|
|
169
155
|
for (let i = 0; i < items.length; i++) {
|
|
170
156
|
const item = items[i]
|
|
171
157
|
if (!item) continue
|
|
172
|
-
const prefix = ordered ? `${i + 1}.` :
|
|
158
|
+
const prefix = ordered ? `${i + 1}.` : "•"
|
|
173
159
|
const text = escapeXml(getTextContent(item.children))
|
|
174
160
|
ctx.y += 18
|
|
175
161
|
svg += `<text x="${ctx.padding + 16}" y="${ctx.y}" font-size="13" fill="#333" font-family="system-ui, sans-serif">${prefix} ${text}</text>`
|
|
@@ -178,20 +164,20 @@ function renderNode(node: DocNode, ctx: RenderContext): string {
|
|
|
178
164
|
break
|
|
179
165
|
}
|
|
180
166
|
|
|
181
|
-
case
|
|
167
|
+
case "code": {
|
|
182
168
|
const text = getTextContent(node.children)
|
|
183
|
-
const lines = text.split(
|
|
169
|
+
const lines = text.split("\n")
|
|
184
170
|
const codeHeight = lines.length * 18 + 16
|
|
185
171
|
svg += `<rect x="${ctx.padding}" y="${ctx.y}" width="${contentWidth}" height="${codeHeight}" fill="#f5f5f5" rx="4" />`
|
|
186
172
|
for (let i = 0; i < lines.length; i++) {
|
|
187
|
-
svg += `<text x="${ctx.padding + 12}" y="${ctx.y + 20 + i * 18}" font-size="12" fill="#333" font-family="monospace">${escapeXml(lines[i] ??
|
|
173
|
+
svg += `<text x="${ctx.padding + 12}" y="${ctx.y + 20 + i * 18}" font-size="12" fill="#333" font-family="monospace">${escapeXml(lines[i] ?? "")}</text>`
|
|
188
174
|
}
|
|
189
175
|
ctx.y += codeHeight + 8
|
|
190
176
|
break
|
|
191
177
|
}
|
|
192
178
|
|
|
193
|
-
case
|
|
194
|
-
const color = sanitizeColor((p.color as string) ??
|
|
179
|
+
case "divider": {
|
|
180
|
+
const color = sanitizeColor((p.color as string) ?? "#ddd")
|
|
195
181
|
const thickness = (p.thickness as number) ?? 1
|
|
196
182
|
ctx.y += 12
|
|
197
183
|
svg += `<line x1="${ctx.padding}" y1="${ctx.y}" x2="${ctx.padding + contentWidth}" y2="${ctx.y}" stroke="${color}" stroke-width="${thickness}" />`
|
|
@@ -199,19 +185,19 @@ function renderNode(node: DocNode, ctx: RenderContext): string {
|
|
|
199
185
|
break
|
|
200
186
|
}
|
|
201
187
|
|
|
202
|
-
case
|
|
188
|
+
case "page-break":
|
|
203
189
|
ctx.y += 16
|
|
204
190
|
svg += `<line x1="${ctx.padding}" y1="${ctx.y}" x2="${ctx.padding + contentWidth}" y2="${ctx.y}" stroke="#ccc" stroke-width="2" stroke-dasharray="8,4" />`
|
|
205
191
|
ctx.y += 16
|
|
206
192
|
break
|
|
207
193
|
|
|
208
|
-
case
|
|
194
|
+
case "spacer":
|
|
209
195
|
ctx.y += (p.height as number) ?? 12
|
|
210
196
|
break
|
|
211
197
|
|
|
212
|
-
case
|
|
213
|
-
const bg = sanitizeColor((p.background as string) ??
|
|
214
|
-
const color = sanitizeColor((p.color as string) ??
|
|
198
|
+
case "button": {
|
|
199
|
+
const bg = sanitizeColor((p.background as string) ?? "#4f46e5")
|
|
200
|
+
const color = sanitizeColor((p.color as string) ?? "#ffffff")
|
|
215
201
|
const text = escapeXml(getTextContent(node.children))
|
|
216
202
|
const btnWidth = Math.min(text.length * 10 + 48, contentWidth)
|
|
217
203
|
const btnHeight = 40
|
|
@@ -222,8 +208,8 @@ function renderNode(node: DocNode, ctx: RenderContext): string {
|
|
|
222
208
|
break
|
|
223
209
|
}
|
|
224
210
|
|
|
225
|
-
case
|
|
226
|
-
const borderColor = sanitizeColor((p.borderColor as string) ??
|
|
211
|
+
case "quote": {
|
|
212
|
+
const borderColor = sanitizeColor((p.borderColor as string) ?? "#ddd")
|
|
227
213
|
const text = escapeXml(getTextContent(node.children))
|
|
228
214
|
ctx.y += 4
|
|
229
215
|
svg += `<rect x="${ctx.padding}" y="${ctx.y}" width="4" height="20" fill="${borderColor}" />`
|
|
@@ -245,7 +231,7 @@ export const svgRenderer: DocumentRenderer = {
|
|
|
245
231
|
const content = renderNode(node, ctx)
|
|
246
232
|
const height = ctx.y + padding
|
|
247
233
|
|
|
248
|
-
const dir = options?.direction ===
|
|
234
|
+
const dir = options?.direction === "rtl" ? ' direction="rtl"' : ""
|
|
249
235
|
|
|
250
236
|
return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}"${dir}>
|
|
251
237
|
<rect width="${width}" height="${height}" fill="#ffffff" />
|
package/src/renderers/teams.ts
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
import { sanitizeHref, sanitizeImageSrc } from
|
|
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
|
/**
|
|
11
5
|
* Microsoft Teams renderer — outputs Adaptive Cards JSON.
|
|
@@ -13,15 +7,13 @@ import type {
|
|
|
13
7
|
*/
|
|
14
8
|
|
|
15
9
|
function resolveColumn(col: string | TableColumn): TableColumn {
|
|
16
|
-
return typeof col ===
|
|
10
|
+
return typeof col === "string" ? { header: col } : col
|
|
17
11
|
}
|
|
18
12
|
|
|
19
13
|
function getTextContent(children: DocChild[]): string {
|
|
20
14
|
return children
|
|
21
|
-
.map((c) =>
|
|
22
|
-
|
|
23
|
-
)
|
|
24
|
-
.join('')
|
|
15
|
+
.map((c) => (typeof c === "string" ? c : getTextContent((c as DocNode).children)))
|
|
16
|
+
.join("")
|
|
25
17
|
}
|
|
26
18
|
|
|
27
19
|
interface AdaptiveElement {
|
|
@@ -34,99 +26,95 @@ function nodeToElements(node: DocNode): AdaptiveElement[] {
|
|
|
34
26
|
const elements: AdaptiveElement[] = []
|
|
35
27
|
|
|
36
28
|
switch (node.type) {
|
|
37
|
-
case
|
|
38
|
-
case
|
|
39
|
-
case
|
|
40
|
-
case
|
|
41
|
-
case
|
|
29
|
+
case "document":
|
|
30
|
+
case "page":
|
|
31
|
+
case "section":
|
|
32
|
+
case "row":
|
|
33
|
+
case "column":
|
|
42
34
|
for (const child of node.children) {
|
|
43
|
-
if (typeof child !==
|
|
35
|
+
if (typeof child !== "string") {
|
|
44
36
|
elements.push(...nodeToElements(child))
|
|
45
37
|
}
|
|
46
38
|
}
|
|
47
39
|
break
|
|
48
40
|
|
|
49
|
-
case
|
|
41
|
+
case "heading": {
|
|
50
42
|
const level = (p.level as number) ?? 1
|
|
51
43
|
const sizeMap: Record<number, string> = {
|
|
52
|
-
1:
|
|
53
|
-
2:
|
|
54
|
-
3:
|
|
55
|
-
4:
|
|
56
|
-
5:
|
|
57
|
-
6:
|
|
44
|
+
1: "extraLarge",
|
|
45
|
+
2: "large",
|
|
46
|
+
3: "medium",
|
|
47
|
+
4: "default",
|
|
48
|
+
5: "small",
|
|
49
|
+
6: "small",
|
|
58
50
|
}
|
|
59
51
|
elements.push({
|
|
60
|
-
type:
|
|
52
|
+
type: "TextBlock",
|
|
61
53
|
text: getTextContent(node.children),
|
|
62
|
-
size: sizeMap[level] ??
|
|
63
|
-
weight:
|
|
54
|
+
size: sizeMap[level] ?? "large",
|
|
55
|
+
weight: "bolder",
|
|
64
56
|
wrap: true,
|
|
65
57
|
})
|
|
66
58
|
break
|
|
67
59
|
}
|
|
68
60
|
|
|
69
|
-
case
|
|
61
|
+
case "text": {
|
|
70
62
|
let text = getTextContent(node.children)
|
|
71
63
|
if (p.bold) text = `**${text}**`
|
|
72
64
|
if (p.italic) text = `_${text}_`
|
|
73
65
|
if (p.strikethrough) text = `~~${text}~~`
|
|
74
66
|
elements.push({
|
|
75
|
-
type:
|
|
67
|
+
type: "TextBlock",
|
|
76
68
|
text,
|
|
77
69
|
wrap: true,
|
|
78
|
-
...(p.color ? { color:
|
|
79
|
-
...(p.size
|
|
80
|
-
? { size: (p.size as number) >= 18 ? 'large' : 'default' }
|
|
81
|
-
: {}),
|
|
70
|
+
...(p.color ? { color: "default" } : {}),
|
|
71
|
+
...(p.size ? { size: (p.size as number) >= 18 ? "large" : "default" } : {}),
|
|
82
72
|
})
|
|
83
73
|
break
|
|
84
74
|
}
|
|
85
75
|
|
|
86
|
-
case
|
|
76
|
+
case "link": {
|
|
87
77
|
const href = sanitizeHref(p.href as string)
|
|
88
78
|
const text = getTextContent(node.children)
|
|
89
79
|
elements.push({
|
|
90
|
-
type:
|
|
80
|
+
type: "TextBlock",
|
|
91
81
|
text: `[${text}](${href})`,
|
|
92
82
|
wrap: true,
|
|
93
83
|
})
|
|
94
84
|
break
|
|
95
85
|
}
|
|
96
86
|
|
|
97
|
-
case
|
|
87
|
+
case "image": {
|
|
98
88
|
const src = sanitizeImageSrc(p.src as string)
|
|
99
|
-
if (src.startsWith(
|
|
89
|
+
if (src.startsWith("http")) {
|
|
100
90
|
elements.push({
|
|
101
|
-
type:
|
|
91
|
+
type: "Image",
|
|
102
92
|
url: src,
|
|
103
|
-
altText: (p.alt as string) ??
|
|
104
|
-
size:
|
|
93
|
+
altText: (p.alt as string) ?? "Image",
|
|
94
|
+
size: "large",
|
|
105
95
|
})
|
|
106
96
|
}
|
|
107
97
|
break
|
|
108
98
|
}
|
|
109
99
|
|
|
110
|
-
case
|
|
111
|
-
const columns = ((p.columns ?? []) as (string | TableColumn)[]).map(
|
|
112
|
-
resolveColumn,
|
|
113
|
-
)
|
|
100
|
+
case "table": {
|
|
101
|
+
const columns = ((p.columns ?? []) as (string | TableColumn)[]).map(resolveColumn)
|
|
114
102
|
const rows = (p.rows ?? []) as (string | number)[][]
|
|
115
103
|
|
|
116
104
|
// Adaptive Cards have native Table support (schema 1.5+)
|
|
117
105
|
const tableColumns = columns.map((col) => ({
|
|
118
|
-
type:
|
|
119
|
-
width:
|
|
106
|
+
type: "Column",
|
|
107
|
+
width: "stretch",
|
|
120
108
|
items: [
|
|
121
109
|
{
|
|
122
|
-
type:
|
|
110
|
+
type: "TextBlock",
|
|
123
111
|
text: `**${col.header}**`,
|
|
124
|
-
weight:
|
|
112
|
+
weight: "bolder",
|
|
125
113
|
wrap: true,
|
|
126
114
|
},
|
|
127
115
|
...rows.map((row, i) => ({
|
|
128
|
-
type:
|
|
129
|
-
text: String(row[columns.indexOf(col)] ??
|
|
116
|
+
type: "TextBlock",
|
|
117
|
+
text: String(row[columns.indexOf(col)] ?? ""),
|
|
130
118
|
wrap: true,
|
|
131
119
|
separator: i === 0,
|
|
132
120
|
})),
|
|
@@ -134,80 +122,80 @@ function nodeToElements(node: DocNode): AdaptiveElement[] {
|
|
|
134
122
|
}))
|
|
135
123
|
|
|
136
124
|
elements.push({
|
|
137
|
-
type:
|
|
125
|
+
type: "ColumnSet",
|
|
138
126
|
columns: tableColumns,
|
|
139
127
|
})
|
|
140
128
|
break
|
|
141
129
|
}
|
|
142
130
|
|
|
143
|
-
case
|
|
131
|
+
case "list": {
|
|
144
132
|
const ordered = p.ordered as boolean | undefined
|
|
145
133
|
const items = node.children
|
|
146
|
-
.filter((c): c is DocNode => typeof c !==
|
|
134
|
+
.filter((c): c is DocNode => typeof c !== "string")
|
|
147
135
|
.map((item, i) => {
|
|
148
|
-
const prefix = ordered ? `${i + 1}.` :
|
|
136
|
+
const prefix = ordered ? `${i + 1}.` : "•"
|
|
149
137
|
return `${prefix} ${getTextContent(item.children)}`
|
|
150
138
|
})
|
|
151
|
-
.join(
|
|
139
|
+
.join("\n")
|
|
152
140
|
elements.push({
|
|
153
|
-
type:
|
|
141
|
+
type: "TextBlock",
|
|
154
142
|
text: items,
|
|
155
143
|
wrap: true,
|
|
156
144
|
})
|
|
157
145
|
break
|
|
158
146
|
}
|
|
159
147
|
|
|
160
|
-
case
|
|
148
|
+
case "code": {
|
|
161
149
|
const text = getTextContent(node.children)
|
|
162
150
|
elements.push({
|
|
163
|
-
type:
|
|
151
|
+
type: "TextBlock",
|
|
164
152
|
text: `\`\`\`\n${text}\n\`\`\``,
|
|
165
|
-
fontType:
|
|
153
|
+
fontType: "monospace",
|
|
166
154
|
wrap: true,
|
|
167
155
|
})
|
|
168
156
|
break
|
|
169
157
|
}
|
|
170
158
|
|
|
171
|
-
case
|
|
172
|
-
case
|
|
159
|
+
case "divider":
|
|
160
|
+
case "page-break":
|
|
173
161
|
elements.push({
|
|
174
|
-
type:
|
|
175
|
-
text:
|
|
162
|
+
type: "TextBlock",
|
|
163
|
+
text: " ",
|
|
176
164
|
separator: true,
|
|
177
165
|
})
|
|
178
166
|
break
|
|
179
167
|
|
|
180
|
-
case
|
|
168
|
+
case "spacer":
|
|
181
169
|
elements.push({
|
|
182
|
-
type:
|
|
183
|
-
text:
|
|
184
|
-
spacing:
|
|
170
|
+
type: "TextBlock",
|
|
171
|
+
text: " ",
|
|
172
|
+
spacing: "large",
|
|
185
173
|
})
|
|
186
174
|
break
|
|
187
175
|
|
|
188
|
-
case
|
|
176
|
+
case "button": {
|
|
189
177
|
elements.push({
|
|
190
|
-
type:
|
|
178
|
+
type: "ActionSet",
|
|
191
179
|
actions: [
|
|
192
180
|
{
|
|
193
|
-
type:
|
|
181
|
+
type: "Action.OpenUrl",
|
|
194
182
|
title: getTextContent(node.children),
|
|
195
183
|
url: sanitizeHref(p.href as string),
|
|
196
|
-
style:
|
|
184
|
+
style: "positive",
|
|
197
185
|
},
|
|
198
186
|
],
|
|
199
187
|
})
|
|
200
188
|
break
|
|
201
189
|
}
|
|
202
190
|
|
|
203
|
-
case
|
|
191
|
+
case "quote": {
|
|
204
192
|
const text = getTextContent(node.children)
|
|
205
193
|
elements.push({
|
|
206
|
-
type:
|
|
207
|
-
style:
|
|
194
|
+
type: "Container",
|
|
195
|
+
style: "emphasis",
|
|
208
196
|
items: [
|
|
209
197
|
{
|
|
210
|
-
type:
|
|
198
|
+
type: "TextBlock",
|
|
211
199
|
text: `_${text}_`,
|
|
212
200
|
wrap: true,
|
|
213
201
|
isSubtle: true,
|
|
@@ -225,9 +213,9 @@ export const teamsRenderer: DocumentRenderer = {
|
|
|
225
213
|
async render(node: DocNode, _options?: RenderOptions): Promise<string> {
|
|
226
214
|
const body = nodeToElements(node)
|
|
227
215
|
const card = {
|
|
228
|
-
type:
|
|
229
|
-
$schema:
|
|
230
|
-
version:
|
|
216
|
+
type: "AdaptiveCard",
|
|
217
|
+
$schema: "http://adaptivecards.io/schemas/adaptive-card.json",
|
|
218
|
+
version: "1.5",
|
|
231
219
|
body,
|
|
232
220
|
}
|
|
233
221
|
return JSON.stringify(card, null, 2)
|