deckjsx 0.7.0 → 0.8.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 CHANGED
@@ -14,8 +14,9 @@ JSX
14
14
  ```
15
15
 
16
16
  This project is being designed as a compiler, not as a thin `PptxGenJS` wrapper.
17
- The API uses a class-based compiler with callback-based `.slide()`, `.compile()`, `.project()`, and
18
- `.render()`. Authoring uses typed JSX elements with CSS-like style and class semantics.
17
+ The API uses a class-based compiler with callback-based `.slide()`, synchronous `.compile()`, async
18
+ `.project()`, and async `.render()`. Authoring uses typed JSX elements with CSS-like style and class
19
+ semantics.
19
20
 
20
21
  The implementation preserves the compiler model with explicit module boundaries for authoring,
21
22
  semantic graph construction, style resolution, output projection, writer adapters, and runtime
@@ -27,7 +28,7 @@ output.
27
28
  npm install deckjsx
28
29
  ```
29
30
 
30
- The package currently targets PPTX output and ships a temporary `pptxgenjs` writer adapter.
31
+ The package currently targets PPTX output through deckjsx's direct PPTX writer.
31
32
 
32
33
  ## Usage
33
34
 
@@ -42,66 +43,75 @@ const deck = new Deck({
42
43
  deck.slide(
43
44
  { name: "Quarterly Review", style: { backgroundColor: "#F8FAFC" } },
44
45
  ({ composition }) => (
45
- <>
46
- <main
47
- style={{
48
- x: 0.7,
49
- y: 0.5,
50
- width: 11.9,
51
- height: 6.3,
52
- display: "grid",
53
- gridTemplateRows: ["0.9in", "1fr", "0.4in"],
54
- rowGap: 0.25,
55
- }}
56
- >
57
- <header>
58
- <h1
59
- style={{ width: "100%", height: 0.6, fontSize: 28, fontWeight: 700, color: "#0F172A" }}
60
- >
61
- Quarterly Review
62
- </h1>
63
- </header>
64
-
65
- <section style={{ display: "grid", gridTemplateColumns: "1fr 1fr", columnGap: 0.35 }}>
66
- <p style={{ fontSize: 18, color: "#334155", fit: "shrink" }}>
67
- Author slides with typed JSX, inspect the projected document model, and render PPTX
68
- files.
69
- </p>
70
- <figure style={{ backgroundColor: "#E0F2FE", borderRadius: 0.15, padding: 0.25 }}>
71
- <img src="chart.png" style={{ width: "100%", height: "100%", fit: "contain" }} />
72
- </figure>
73
- </section>
74
-
75
- <footer>
76
- <p
77
- style={{
78
- width: "100%",
79
- height: 0.3,
80
- fontSize: 11,
81
- color: "#64748B",
82
- textAlign: "right",
83
- }}
84
- >
85
- {composition.slideIndex + 1} / {composition.totalSlides}
86
- </p>
87
- </footer>
88
- </main>
89
- </>
46
+ <main
47
+ style={{
48
+ x: 0.7,
49
+ y: 0.5,
50
+ width: 11.9,
51
+ height: 6.3,
52
+ display: "grid",
53
+ gridTemplateRows: ["0.9in", "1fr", "0.4in"],
54
+ rowGap: 0.25,
55
+ }}
56
+ >
57
+ <header>
58
+ <h1 style={{ width: "100%", height: 0.6, fontSize: 28, fontWeight: 700, color: "#0F172A" }}>
59
+ Quarterly Review
60
+ </h1>
61
+ </header>
62
+
63
+ <section style={{ display: "grid", gridTemplateColumns: "1fr 1fr", columnGap: 0.35 }}>
64
+ <p style={{ fontSize: 18, color: "#334155", fit: "shrink" }}>
65
+ Author slides with typed JSX, inspect the projected document model, and render PPTX files.
66
+ </p>
67
+ <figure style={{ backgroundColor: "#E0F2FE", borderRadius: 0.15, padding: 0.25 }}>
68
+ <img src="chart.png" style={{ width: "100%", height: "100%", fit: "contain" }} />
69
+ </figure>
70
+ </section>
71
+
72
+ <footer>
73
+ <p
74
+ style={{
75
+ width: "100%",
76
+ height: 0.3,
77
+ fontSize: 11,
78
+ color: "#64748B",
79
+ textAlign: "right",
80
+ }}
81
+ >
82
+ {composition.slideIndex + 1} / {composition.totalSlides}
83
+ </p>
84
+ </footer>
85
+ </main>
90
86
  ),
91
87
  );
92
88
 
93
- const project = deck.project();
89
+ const project = await deck.project();
94
90
  await deck.render({ output: "quarterly-review.pptx" });
95
91
  ```
96
92
 
97
- Use `deck.compile()` for authoring semantics, `deck.project()` for output-facing inspection, and
98
- `deck.render({ output })` when writing a PowerPoint file.
93
+ Use `deck.compile()` for authoring semantics, `await deck.project()` for output-facing inspection,
94
+ and `await deck.render({ output })` when writing a PowerPoint file.
95
+
96
+ When a hot path only needs the projected model or rendered artifact, inspection summaries can be
97
+ skipped with `await deck.project({ inspection: "none" })` or
98
+ `await deck.render({ inspection: "none" })`.
99
+
100
+ The default render path uses deckjsx's built-in direct PPTX writer. If an explicit writer adapter is
101
+ needed, import `pptx()` from `deckjsx/adapter`; writer internals such as XML emitters, ZIP settings,
102
+ and sinks are intentionally not part of the public API.
103
+
104
+ ```tsx
105
+ import { pptx } from "deckjsx/adapter";
106
+
107
+ await deck.render(pptx({ output: "quarterly-review.pptx" }));
108
+ ```
99
109
 
100
110
  ## JSX elements
101
111
 
102
- `deckjsx` supports both the original capitalized components and a typed HTML-like JSX surface.
112
+ `deckjsx` exposes a typed HTML-like JSX authoring surface.
103
113
 
104
- View-like elements compile to grouped layout containers:
114
+ View-like lowercase elements compile to grouped layout containers:
105
115
 
106
116
  ```tsx
107
117
  <main>
@@ -115,7 +125,7 @@ View-like elements compile to grouped layout containers:
115
125
  </main>
116
126
  ```
117
127
 
118
- Text-like elements compile to text boxes:
128
+ Text-like lowercase elements compile to text boxes:
119
129
 
120
130
  ```tsx
121
131
  <h1>Title</h1>
@@ -123,12 +133,85 @@ Text-like elements compile to text boxes:
123
133
  <p>Body copy</p>
124
134
  ```
125
135
 
126
- Image elements compile to images and require either `src` or `data`:
136
+ Image lowercase elements compile to images and require either `src` or `data`:
127
137
 
128
138
  ```tsx
129
139
  <img src="diagram.png" style={{ width: 4, height: 2.5, fit: "contain" }} />
130
140
  ```
131
141
 
142
+ The lowercase `shape` element compiles to PPTX shapes:
143
+
144
+ ```tsx
145
+ <shape shape="rect" style={{ width: 2, height: 1, fill: "#2563EB" }} />
146
+ ```
147
+
148
+ ## Slide Templates
149
+
150
+ Deck templates describe reusable slide structure without asking authors to write PowerPoint
151
+ placeholder ids. Define named Template Areas on the Deck, then place authored content through the
152
+ typed `template` handle passed to templated slide factories:
153
+
154
+ ```tsx
155
+ const deck = new Deck({
156
+ layout: { width: 13.333, height: 7.5, unit: "in" },
157
+ templates: {
158
+ report: {
159
+ areas: {
160
+ title: { kind: "title", frame: { x: 0.7, y: 0.6, width: 8, height: 0.8 } },
161
+ body: { frame: { x: 0.7, y: 1.6, width: 8, height: 4.8 } },
162
+ },
163
+ },
164
+ },
165
+ });
166
+
167
+ deck.slide({ template: "report" }, ({ template }) => (
168
+ <main>
169
+ <h1 area={template.title}>Quarterly Review</h1>
170
+ <section area={template.body}>Performance highlights</section>
171
+ </main>
172
+ ));
173
+ ```
174
+
175
+ `TemplateArea.kind` is an authoring-level hint such as `"title"`, `"body"`, `"picture"`, or
176
+ `"generic"`. Missing kinds default to `"generic"` and are not inferred from area names. Project keeps
177
+ Template Area anchors visible in the Pptx Package Model inspection surface, while the writer decides
178
+ how to serialize the corresponding PPTX slide layout structure.
179
+
180
+ ## Assets
181
+
182
+ Image sources are resolved through the asset loading boundary. The core package includes
183
+ multi-runtime handling for data/bytes and absolute URL-like sources, while filesystem paths,
184
+ framework-public assets, authenticated URLs, and app media stores should be provided with
185
+ `deck.useAssets(loader)`.
186
+ For built-in data, bytes, and absolute URL-like image sources, Project probes PNG, GIF, JPEG, and
187
+ SVG dimensions into media metadata without putting media bytes into the Pptx Package Model.
188
+
189
+ ```tsx
190
+ import type { AssetLoader } from "deckjsx";
191
+
192
+ const publicAssets = {
193
+ name: "public-assets",
194
+ async probe({ source }) {
195
+ if (source.kind !== "path") return undefined;
196
+ return { mediaType: "image/png", extension: "png", width: 1200, height: 800 };
197
+ },
198
+ async load({ source }) {
199
+ if (source.kind !== "path") return undefined;
200
+ const bytes = await loadFromYourRuntime(source.path);
201
+ return { bytes, mediaType: "image/png", extension: "png", width: 1200, height: 800 };
202
+ },
203
+ } satisfies AssetLoader;
204
+
205
+ deck.useAssets(publicAssets);
206
+ ```
207
+
208
+ Registered loaders run in registration order before the built-in fallback. Project uses `probe()` for
209
+ metadata needed by the Pptx Package Model, and Render uses the same winning resolver scope for
210
+ `load()` so media metadata and bytes come from the same runtime assumptions.
211
+ If a loader claims an image source but cannot provide dimensions, treat that as an asset data
212
+ retrieval failure and report it through Project diagnostics rather than waiting for the writer to
213
+ guess.
214
+
132
215
  Primitive string and number children inside view-like elements are normalized to implicit text
133
216
  nodes. Inline rich text uses `span` inside text-like elements:
134
217
 
@@ -138,18 +221,18 @@ nodes. Inline rich text uses `span` inside text-like elements:
138
221
  </p>
139
222
  ```
140
223
 
141
- ## View Layout Semantics
224
+ ## View-like Layout Semantics
142
225
 
143
- `View` is a containing block for its children. Child `x`, `y`, `left`, `top`, `right`,
144
- `bottom`, `width`, and `height` values are resolved relative to the parent `View`, not
226
+ View-like elements are containing blocks for their children. Child `x`, `y`, `left`, `top`, `right`,
227
+ `bottom`, `width`, and `height` values are resolved relative to the parent view-like element, not
145
228
  the slide, so authors can build panels with local coordinates. Percentage lengths use
146
229
  the parent frame as their reference.
147
230
 
148
231
  ```tsx
149
- <View style={{ x: 1, y: 1, width: 6, height: 3 }}>
150
- <Text style={{ x: "10%", y: "20%", width: "50%", height: "25%" }}>local percent frame</Text>
151
- <Text style={{ left: "55%", top: "10%", right: "10%", bottom: "60%" }}>inset frame</Text>
152
- </View>
232
+ <div style={{ x: 1, y: 1, width: 6, height: 3 }}>
233
+ <p style={{ x: "10%", y: "20%", width: "50%", height: "25%" }}>local percent frame</p>
234
+ <p style={{ left: "55%", top: "10%", right: "10%", bottom: "60%" }}>inset frame</p>
235
+ </div>
153
236
  ```
154
237
 
155
238
  For `display: "flex"` and `display: "grid"`, normal-flow children are laid out inside
@@ -159,13 +242,33 @@ rendering. Absolutely positioned children inside flex or grid containers also us
159
242
  container content frame, including padding, as their containing block.
160
243
 
161
244
  Use direct slide children when you want slide-global absolute placement. Use children
162
- inside a `View` when you want a local, web-like layout region.
245
+ inside a view-like element when you want a local, web-like layout region.
246
+
247
+ `overflow: "hidden"` is projected as clipping metadata rather than treated as an authoring error.
248
+ When CSS-like clipping, transform, opacity, or compositing behavior cannot be represented exactly in
249
+ PPTX yet, Project reports nonblocking warnings and preserves the observable projected values for
250
+ inspection.
251
+
252
+ Unsupported CSS-like meanings that can still produce a structurally valid PPTX are reported through
253
+ Project diagnostics and the inspection surface rather than treated as authoring errors. These records
254
+ include the unsupported feature, the projected value, and a fallback strategy describing which values
255
+ were preserved and which behavior is still missing. Malformed projected unsupported-semantic payloads
256
+ from custom projections fail before Render emits bytes.
163
257
 
164
258
  ## Development
165
259
 
166
260
  ```bash
167
261
  vp install
168
262
  vp check
263
+ bun run build
264
+ npm ci --prefix sample
265
+ npm run --prefix sample smoke
169
266
  vp test
170
- vp pack
267
+ bun run benchmark:pptx -- --iterations 1 --strict
268
+ bun run verify:render -- --skip-raster
269
+ npm run --prefix .github/compat/pptxgenjs compare
171
270
  ```
271
+
272
+ For output or public-surface changes, keep the direct PPTX writer as the documented built-in path.
273
+ `pptxgenjs` should appear only in isolated regression tooling, not in runtime dependencies or public
274
+ adapter examples.