@pyreon/connector-document 0.21.0 → 0.23.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/README.md +102 -4
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -1,16 +1,114 @@
|
|
|
1
1
|
# @pyreon/connector-document
|
|
2
2
|
|
|
3
|
-
Bridge between `@pyreon/ui-system`
|
|
3
|
+
Bridge between `@pyreon/ui-system` JSX trees and `@pyreon/document` for multi-format export.
|
|
4
|
+
|
|
5
|
+
`@pyreon/connector-document` walks a Pyreon JSX tree of document-primitive components (`DocDocument`, `DocHeading`, `DocText`, …) and produces a serializable `DocNode` tree that `@pyreon/document` can render to PDF, DOCX, XLSX, PPTX, email, Markdown, HTML, and 10+ other formats. Components carry `_documentType` markers (set via `attrs().statics()` on rocketstyle primitives, or directly on user components); the extractor finds them, resolves `_documentProps` and `$rocketstyle` styles, and recursively walks children. The hot path is fast — for real rocketstyle primitives it runs the accumulated `.attrs()` chain directly instead of invoking the full component (no JSX tree creation, no dimension resolution).
|
|
4
6
|
|
|
5
7
|
## Install
|
|
6
8
|
|
|
7
9
|
```bash
|
|
8
|
-
bun add @pyreon/connector-document
|
|
10
|
+
bun add @pyreon/connector-document @pyreon/document @pyreon/core
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick start
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { extractDocumentTree } from '@pyreon/connector-document'
|
|
17
|
+
import { render } from '@pyreon/document'
|
|
18
|
+
import { DocDocument, DocHeading, DocText } from '@pyreon/document-primitives'
|
|
19
|
+
|
|
20
|
+
const vnode = (
|
|
21
|
+
<DocDocument title="Q4 Report" author="Acme Inc.">
|
|
22
|
+
<DocHeading level={1}>Summary</DocHeading>
|
|
23
|
+
<DocText>Revenue was up 12%.</DocText>
|
|
24
|
+
</DocDocument>
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
const docTree = extractDocumentTree(vnode)
|
|
28
|
+
const pdf = await render(docTree, 'pdf') // Buffer
|
|
29
|
+
const docx = await render(docTree, 'docx') // Buffer
|
|
30
|
+
const md = await render(docTree, 'markdown') // string
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
In practice, you'll usually call `extractDocNode` from `@pyreon/document-primitives` — a one-step alias that wraps a template function in an extraction-friendly shape — rather than `extractDocumentTree` directly. Use `extractDocumentTree` when you already have a vnode in hand (a captured render result, a test fixture).
|
|
34
|
+
|
|
35
|
+
## API
|
|
36
|
+
|
|
37
|
+
### `extractDocumentTree(vnode, options?)`
|
|
38
|
+
|
|
39
|
+
Walk a JSX vnode and produce a `DocNode` tree.
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
const tree = extractDocumentTree(vnode, {
|
|
43
|
+
rootSize: 16, // base font size for rem→px (default 16)
|
|
44
|
+
includeStyles: true, // resolve $rocketstyle into the DocNode.styles field (default true)
|
|
45
|
+
})
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Returns `DocNode | DocChild[] | null` (re-exported from `@pyreon/document`). String/number children are inlined as text; reactive accessors (`() => signal()`) are resolved at extraction time, so calling `extractDocumentTree` again after a signal change produces a fresh tree reflecting the live values.
|
|
49
|
+
|
|
50
|
+
**Extraction resolution order for `_documentProps`** (per primitive):
|
|
51
|
+
|
|
52
|
+
1. **Pre-resolved on the vnode** — for test fixtures that hand-attach `_documentProps` directly.
|
|
53
|
+
2. **Hoisted-attrs fast path** — real rocketstyle primitives expose `__rs_attrs` (the accumulated `.attrs()` callback chain) as a typed static. The extractor runs the chain directly: `chain.reduce((acc, fn) => Object.assign(acc, fn(props)), {})`. No styled-wrapper invocation, no dimension resolution. Production path for every Pyreon doc primitive.
|
|
54
|
+
3. **Full component invocation** — legacy fallback for hand-rolled `_documentType`-marked components that don't go through rocketstyle.
|
|
55
|
+
|
|
56
|
+
### `resolveStyles(rocketstyle, rootSize?)`
|
|
57
|
+
|
|
58
|
+
Convert a `$rocketstyle` theme object into a `ResolvedStyles` (typography, color, spacing, borders) compatible with `@pyreon/document`. Properties the document renderer doesn't support (transitions, cursor, display) are silently dropped.
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
import { resolveStyles } from '@pyreon/connector-document'
|
|
62
|
+
|
|
63
|
+
const styles = resolveStyles({
|
|
64
|
+
fontSize: '1.5rem',
|
|
65
|
+
fontWeight: 'bold',
|
|
66
|
+
color: '#222',
|
|
67
|
+
padding: '12px 16px',
|
|
68
|
+
}, 16)
|
|
69
|
+
// → { fontSize: 24, fontWeight: 700, color: '#222', paddingTop: 12, paddingRight: 16, ... }
|
|
9
70
|
```
|
|
10
71
|
|
|
11
|
-
|
|
72
|
+
### CSS value parsers
|
|
73
|
+
|
|
74
|
+
Low-level helpers that `resolveStyles` uses internally. Useful when you need to bridge values from elsewhere into the document model.
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
import {
|
|
78
|
+
parseCssDimension, // '1.5rem' → 24 (with rootSize=16)
|
|
79
|
+
parseBoxModel, // '12px 16px' → { top: 12, right: 16, bottom: 12, left: 16 }
|
|
80
|
+
parseFontWeight, // 'bold' → 700; 'normal' → 400; numeric strings → number
|
|
81
|
+
parseLineHeight, // '1.5' → { ratio: 1.5 }; '24px' → { px: 24 }
|
|
82
|
+
} from '@pyreon/connector-document'
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Marker contract
|
|
86
|
+
|
|
87
|
+
A component is extractable when one of these holds:
|
|
88
|
+
|
|
89
|
+
- It's a rocketstyle primitive with `_documentType` in `.meta` (set via `.statics({ _documentType: 'document' | 'heading' | ... })`).
|
|
90
|
+
- It's a plain function with `_documentType` as a direct static property.
|
|
91
|
+
|
|
92
|
+
`@pyreon/document-primitives` ships 18 such primitives ready to use; you can add your own following the same marker convention.
|
|
93
|
+
|
|
94
|
+
## Types
|
|
95
|
+
|
|
96
|
+
`DocNode`, `DocChild`, `NodeType`, `ResolvedStyles` are re-exported from `@pyreon/document` — your trees stay assignment-compatible across the boundary.
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
import type { DocNode, DocChild, NodeType, ResolvedStyles } from '@pyreon/connector-document'
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Gotchas
|
|
103
|
+
|
|
104
|
+
- **Reactive accessor children are resolved at extraction time, not subscribed.** Each `extractDocumentTree(vnode)` call reads the live value once. To produce a document that reflects later signal changes, call extract again.
|
|
105
|
+
- **`$rocketstyle` is keyed by object identity.** A theme that was constructed fresh on every render won't share the same resolved-styles bundle across extractions — minor perf concern only.
|
|
106
|
+
- **Unsupported CSS is silently dropped** in `resolveStyles`. `transition`, `cursor`, `display`, animations, and any non-document property fall away. The same primitive will render correctly in the browser and produce a clean PDF without modification.
|
|
107
|
+
- **Function values in `_documentProps` are resolved on every extraction.** `DocDocument`'s `title?: string | (() => string)` reads the LIVE accessor value at extraction time — perfect for "export current state" buttons.
|
|
108
|
+
|
|
109
|
+
## Documentation
|
|
12
110
|
|
|
13
|
-
|
|
111
|
+
Full docs: [docs.pyreon.dev/docs/connector-document](https://docs.pyreon.dev/docs/connector-document) (or `docs/docs/connector-document.md` in this repo).
|
|
14
112
|
|
|
15
113
|
## License
|
|
16
114
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pyreon/connector-document",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "Bridge between @pyreon/pyreon styled components and @pyreon/document rendering",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -42,19 +42,19 @@
|
|
|
42
42
|
"typecheck": "tsc --noEmit"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@pyreon/core": "^0.
|
|
46
|
-
"@pyreon/document": "^0.
|
|
47
|
-
"@pyreon/reactivity": "^0.
|
|
48
|
-
"@pyreon/test-utils": "^0.13.
|
|
49
|
-
"@pyreon/typescript": "^0.
|
|
45
|
+
"@pyreon/core": "^0.23.0",
|
|
46
|
+
"@pyreon/document": "^0.23.0",
|
|
47
|
+
"@pyreon/reactivity": "^0.23.0",
|
|
48
|
+
"@pyreon/test-utils": "^0.13.10",
|
|
49
|
+
"@pyreon/typescript": "^0.23.0",
|
|
50
50
|
"@vitest/browser-playwright": "^4.1.4",
|
|
51
|
-
"@vitus-labs/tools-rolldown": "^2.
|
|
51
|
+
"@vitus-labs/tools-rolldown": "^2.4.0"
|
|
52
52
|
},
|
|
53
53
|
"engines": {
|
|
54
54
|
"node": ">= 22"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@pyreon/core": "^0.
|
|
58
|
-
"@pyreon/document": "^0.
|
|
57
|
+
"@pyreon/core": "^0.23.0",
|
|
58
|
+
"@pyreon/document": "^0.23.0"
|
|
59
59
|
}
|
|
60
60
|
}
|