deckjsx 0.6.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 `.add()`, `.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,20 +28,21 @@ 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
 
34
35
  ```tsx
35
- import { Deck, Slide } from "deckjsx";
36
+ import { Deck } from "deckjsx";
36
37
 
37
38
  const deck = new Deck({
38
39
  layout: { width: 13.333, height: 7.5, unit: "in" },
39
40
  meta: { title: "Quarterly Review", author: "deckjsx" },
40
41
  });
41
42
 
42
- deck.add(({ composition }) => (
43
- <Slide name={`Slide ${composition.slideIndex + 1}`} style={{ backgroundColor: "#F8FAFC" }}>
43
+ deck.slide(
44
+ { name: "Quarterly Review", style: { backgroundColor: "#F8FAFC" } },
45
+ ({ composition }) => (
44
46
  <main
45
47
  style={{
46
48
  x: 0.7,
@@ -69,27 +71,47 @@ deck.add(({ composition }) => (
69
71
 
70
72
  <footer>
71
73
  <p
72
- style={{ width: "100%", height: 0.3, fontSize: 11, color: "#64748B", textAlign: "right" }}
74
+ style={{
75
+ width: "100%",
76
+ height: 0.3,
77
+ fontSize: 11,
78
+ color: "#64748B",
79
+ textAlign: "right",
80
+ }}
73
81
  >
74
82
  {composition.slideIndex + 1} / {composition.totalSlides}
75
83
  </p>
76
84
  </footer>
77
85
  </main>
78
- </Slide>
79
- ));
86
+ ),
87
+ );
80
88
 
81
- const project = deck.project();
89
+ const project = await deck.project();
82
90
  await deck.render({ output: "quarterly-review.pptx" });
83
91
  ```
84
92
 
85
- Use `deck.compile()` for authoring semantics, `deck.project()` for output-facing inspection, and
86
- `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
+ ```
87
109
 
88
110
  ## JSX elements
89
111
 
90
- `deckjsx` supports both the original capitalized components and a typed HTML-like JSX surface.
112
+ `deckjsx` exposes a typed HTML-like JSX authoring surface.
91
113
 
92
- View-like elements compile to grouped layout containers:
114
+ View-like lowercase elements compile to grouped layout containers:
93
115
 
94
116
  ```tsx
95
117
  <main>
@@ -103,7 +125,7 @@ View-like elements compile to grouped layout containers:
103
125
  </main>
104
126
  ```
105
127
 
106
- Text-like elements compile to text boxes:
128
+ Text-like lowercase elements compile to text boxes:
107
129
 
108
130
  ```tsx
109
131
  <h1>Title</h1>
@@ -111,12 +133,85 @@ Text-like elements compile to text boxes:
111
133
  <p>Body copy</p>
112
134
  ```
113
135
 
114
- Image elements compile to images and require either `src` or `data`:
136
+ Image lowercase elements compile to images and require either `src` or `data`:
115
137
 
116
138
  ```tsx
117
139
  <img src="diagram.png" style={{ width: 4, height: 2.5, fit: "contain" }} />
118
140
  ```
119
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
+
120
215
  Primitive string and number children inside view-like elements are normalized to implicit text
121
216
  nodes. Inline rich text uses `span` inside text-like elements:
122
217
 
@@ -126,18 +221,18 @@ nodes. Inline rich text uses `span` inside text-like elements:
126
221
  </p>
127
222
  ```
128
223
 
129
- ## View Layout Semantics
224
+ ## View-like Layout Semantics
130
225
 
131
- `View` is a containing block for its children. Child `x`, `y`, `left`, `top`, `right`,
132
- `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
133
228
  the slide, so authors can build panels with local coordinates. Percentage lengths use
134
229
  the parent frame as their reference.
135
230
 
136
231
  ```tsx
137
- <View style={{ x: 1, y: 1, width: 6, height: 3 }}>
138
- <Text style={{ x: "10%", y: "20%", width: "50%", height: "25%" }}>local percent frame</Text>
139
- <Text style={{ left: "55%", top: "10%", right: "10%", bottom: "60%" }}>inset frame</Text>
140
- </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>
141
236
  ```
142
237
 
143
238
  For `display: "flex"` and `display: "grid"`, normal-flow children are laid out inside
@@ -147,13 +242,33 @@ rendering. Absolutely positioned children inside flex or grid containers also us
147
242
  container content frame, including padding, as their containing block.
148
243
 
149
244
  Use direct slide children when you want slide-global absolute placement. Use children
150
- 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.
151
257
 
152
258
  ## Development
153
259
 
154
260
  ```bash
155
261
  vp install
156
262
  vp check
263
+ bun run build
264
+ npm ci --prefix sample
265
+ npm run --prefix sample smoke
157
266
  vp test
158
- 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
159
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.