Skip to content
This repository was archived by the owner on Mar 26, 2026. It is now read-only.

Commit 0bdcf89

Browse files
authored
Merge pull request #17 from pyreon/docs/add-document
docs: add @pyreon/document documentation
2 parents ebbcf6f + 7b7eba2 commit 0bdcf89

1 file changed

Lines changed: 219 additions & 0 deletions

File tree

content/docs/document/index.mdx

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
---
2+
title: Document
3+
description: Universal document rendering for Pyreon — one template, 14 output formats.
4+
---
5+
6+
`@pyreon/document` renders a single document template to 14+ output formats — HTML, PDF, DOCX, email, XLSX, PPTX, Slack, Teams, Discord, Telegram, Notion, Confluence, WhatsApp, Google Chat, SVG, Markdown, plain text, and CSV. Heavy renderers are lazy-loaded. Custom formats are pluggable.
7+
8+
<PackageBadge name="@pyreon/document" href="/docs/document" />
9+
10+
## Installation
11+
12+
```package-install
13+
@pyreon/document
14+
```
15+
16+
## Quick Start — Builder Pattern
17+
18+
```tsx
19+
import { createDocument } from '@pyreon/document'
20+
21+
const doc = createDocument({ title: 'Sales Report' })
22+
.heading('Q4 Sales Report')
23+
.text('Revenue grew 25% quarter over quarter.')
24+
.table({
25+
columns: ['Region', 'Revenue', 'Growth'],
26+
rows: [
27+
['US', '$1.2M', '+30%'],
28+
['EU', '$800K', '+15%'],
29+
['APAC', '$500K', '+40%'],
30+
],
31+
striped: true,
32+
headerStyle: { background: '#1a1a2e', color: '#fff' },
33+
})
34+
.text('Total: $2.5M', { bold: true, align: 'right' })
35+
36+
// Export to any format
37+
await doc.toPdf() // PDF buffer
38+
await doc.toDocx() // Word document
39+
await doc.toEmail() // Outlook-safe HTML
40+
await doc.toSlack() // Slack Block Kit JSON
41+
await doc.toNotion() // Notion blocks
42+
await doc.download('report.pdf') // Browser download
43+
```
44+
45+
## Quick Start — JSX Pattern
46+
47+
```tsx
48+
import { Document, Page, Heading, Text, Table, Button, render } from '@pyreon/document'
49+
50+
function Invoice({ data }) {
51+
return (
52+
<Document title={`Invoice #${data.id}`}>
53+
<Page size="A4" margin={40}>
54+
<Heading>Invoice #{data.id}</Heading>
55+
<Text color="#666">{data.date}</Text>
56+
<Table
57+
columns={[
58+
{ header: 'Item', width: '50%' },
59+
{ header: 'Qty', align: 'center' },
60+
{ header: 'Price', align: 'right' },
61+
]}
62+
rows={data.items.map(i => [i.name, i.qty, `$${i.price}`])}
63+
striped
64+
/>
65+
<Text bold align="right" size={18}>Total: ${data.total}</Text>
66+
<Button href={data.payUrl} background="#4f46e5" align="center">
67+
Pay Now
68+
</Button>
69+
</Page>
70+
</Document>
71+
)
72+
}
73+
74+
// Same template → any format
75+
const pdf = await render(<Invoice data={invoiceData} />, 'pdf')
76+
const email = await render(<Invoice data={invoiceData} />, 'email')
77+
const docx = await render(<Invoice data={invoiceData} />, 'docx')
78+
```
79+
80+
## Output Formats
81+
82+
### Documents
83+
84+
| Format | Method | Library | Lazy |
85+
|---|---|---|---|
86+
| HTML | `render(doc, 'html')` | Built-in | No |
87+
| PDF | `render(doc, 'pdf')` | pdfmake (~300KB) | Yes |
88+
| DOCX | `render(doc, 'docx')` | docx (~100KB) | Yes |
89+
| XLSX | `render(doc, 'xlsx')` | exceljs (~500KB) | Yes |
90+
| PPTX | `render(doc, 'pptx')` | pptxgenjs (~200KB) | Yes |
91+
| SVG | `render(doc, 'svg')` | Built-in | No |
92+
93+
### Communication
94+
95+
| Format | Method | Output |
96+
|---|---|---|
97+
| Email | `render(doc, 'email')` | Outlook-safe table-based HTML with VML buttons |
98+
| Slack | `render(doc, 'slack')` | Block Kit JSON |
99+
| Teams | `render(doc, 'teams')` | Adaptive Cards JSON |
100+
| Discord | `render(doc, 'discord')` | Embed JSON |
101+
| Telegram | `render(doc, 'telegram')` | HTML subset |
102+
| WhatsApp | `render(doc, 'whatsapp')` | Formatted text (`*bold*`, `_italic_`) |
103+
| Google Chat | `render(doc, 'google-chat')` | Card V2 JSON |
104+
105+
### Knowledge Bases
106+
107+
| Format | Method | Output |
108+
|---|---|---|
109+
| Notion | `render(doc, 'notion')` | Block JSON for Notion API |
110+
| Confluence/Jira | `render(doc, 'confluence')` | Atlassian Document Format (ADF) |
111+
112+
### Data
113+
114+
| Format | Method | Output |
115+
|---|---|---|
116+
| Markdown | `render(doc, 'md')` | Markdown with pipe tables |
117+
| Plain text | `render(doc, 'text')` | Aligned ASCII tables |
118+
| CSV | `render(doc, 'csv')` | Comma-separated values |
119+
120+
## Primitives
121+
122+
| Primitive | Description |
123+
|---|---|
124+
| `<Document>` | Root container with metadata (title, author) |
125+
| `<Page>` | Page with size, orientation, margin, header, footer |
126+
| `<Section>` | Layout container with direction, gap, padding, background |
127+
| `<Row>` / `<Column>` | Horizontal layout |
128+
| `<Heading>` | Headings h1–h6 |
129+
| `<Text>` | Paragraph with bold, italic, color, size, align |
130+
| `<Link>` | Hyperlink |
131+
| `<Image>` | Image with width, height, alt, caption |
132+
| `<Table>` | Data table with columns, rows, striped, bordered, headerStyle |
133+
| `<List>` / `<ListItem>` | Ordered or unordered list |
134+
| `<Code>` | Code block with language |
135+
| `<Divider>` | Horizontal rule |
136+
| `<Spacer>` | Vertical gap |
137+
| `<Button>` | CTA button (VML in email, styled link in PDF) |
138+
| `<Quote>` | Block quote |
139+
| `<PageBreak>` | Force page break (PDF/DOCX) |
140+
141+
## Table Options
142+
143+
```tsx
144+
<Table
145+
columns={[
146+
{ header: 'Name', width: '50%' },
147+
{ header: 'Price', align: 'right', width: '25%' },
148+
{ header: 'Qty', align: 'center', width: '25%' },
149+
]}
150+
rows={[['Widget', '$10', '5'], ['Gadget', '$20', '3']]}
151+
striped // alternating row colors
152+
bordered // cell borders
153+
keepTogether // avoid page breaks within table (PDF)
154+
headerStyle={{ background: '#1a1a2e', color: '#fff' }}
155+
caption="Order Items"
156+
/>
157+
```
158+
159+
## Chart and Flow Integration
160+
161+
Embed charts and flow diagrams from other Pyreon packages:
162+
163+
```tsx
164+
// Builder pattern
165+
const doc = createDocument()
166+
.heading('Dashboard')
167+
.chart(chartInstance, { width: 500, height: 300 })
168+
.flow(flowInstance, { width: 600, height: 400 })
169+
170+
// Charts use instance.getDataURL() → PNG
171+
// Flow diagrams use instance.toSVG() → SVG
172+
```
173+
174+
## Custom Renderers
175+
176+
```tsx
177+
import { registerRenderer } from '@pyreon/document'
178+
179+
registerRenderer('thermal', {
180+
async render(node, options) {
181+
// Walk node tree → ESC/POS commands for receipt printers
182+
return escPosBuffer
183+
},
184+
})
185+
186+
await render(doc, 'thermal')
187+
```
188+
189+
## Browser Download
190+
191+
```tsx
192+
import { download } from '@pyreon/document'
193+
194+
// File extension determines format
195+
await download(doc, 'report.pdf')
196+
await download(doc, 'report.docx')
197+
await download(doc, 'data.xlsx')
198+
await download(doc, 'slides.pptx')
199+
```
200+
201+
## Render Options
202+
203+
```tsx
204+
await render(doc, 'html', {
205+
direction: 'rtl', // RTL text direction
206+
fonts: { // Custom PDF fonts
207+
MyFont: { normal: 'path/to/font.ttf' },
208+
},
209+
})
210+
```
211+
212+
## Security
213+
214+
All renderers include built-in sanitization:
215+
216+
- **CSS injection**`sanitizeColor()` validates all color/background values
217+
- **XML injection**`sanitizeXmlColor()` validates hex colors for DOCX/PPTX
218+
- **Protocol validation**`sanitizeHref()` blocks `javascript:`, `vbscript:` in all links
219+
- **Image validation**`sanitizeImageSrc()` validates image sources

0 commit comments

Comments
 (0)