slotdeck 0.1.0 → 0.1.2

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.
Files changed (51) hide show
  1. package/README.md +16 -1
  2. package/dist/deck/validate.js +12 -5
  3. package/dist/deck/validate.js.map +1 -1
  4. package/dist/index.d.ts +1 -0
  5. package/dist/index.js +1 -0
  6. package/dist/index.js.map +1 -1
  7. package/dist/llm/gemini.d.ts +4 -0
  8. package/dist/llm/gemini.js +43 -7
  9. package/dist/llm/gemini.js.map +1 -1
  10. package/dist/llm/provider.d.ts +12 -0
  11. package/dist/plan/catalog.d.ts +11 -1
  12. package/dist/plan/catalog.js +20 -7
  13. package/dist/plan/catalog.js.map +1 -1
  14. package/dist/plan/edit.js +9 -5
  15. package/dist/plan/edit.js.map +1 -1
  16. package/dist/plan/plan.d.ts +16 -2
  17. package/dist/plan/plan.js +50 -17
  18. package/dist/plan/plan.js.map +1 -1
  19. package/dist/plan/write.d.ts +8 -1
  20. package/dist/plan/write.js +29 -3
  21. package/dist/plan/write.js.map +1 -1
  22. package/dist/render/render.js +4 -4
  23. package/dist/render/render.js.map +1 -1
  24. package/dist/template/change.d.ts +1 -0
  25. package/dist/template/change.js +12 -0
  26. package/dist/template/change.js.map +1 -1
  27. package/dist/template/instruct.js +4 -2
  28. package/dist/template/instruct.js.map +1 -1
  29. package/dist/template/manifest.d.ts +16 -0
  30. package/dist/template/manifest.js +26 -0
  31. package/dist/template/manifest.js.map +1 -1
  32. package/dist/template/sample.js +4 -3
  33. package/dist/template/sample.js.map +1 -1
  34. package/dist/tools.js +6 -0
  35. package/dist/tools.js.map +1 -1
  36. package/docs/README.md +77 -0
  37. package/docs/api-reference.md +680 -0
  38. package/docs/changelog.md +107 -0
  39. package/docs/chat-tools.md +257 -0
  40. package/docs/concepts.md +126 -0
  41. package/docs/deck-format.md +225 -0
  42. package/docs/generating.md +254 -0
  43. package/docs/getting-started.md +139 -0
  44. package/docs/languages.md +83 -0
  45. package/docs/layout-freedom.md +136 -0
  46. package/docs/manifest-format.md +208 -0
  47. package/docs/providers.md +281 -0
  48. package/docs/rendering.md +172 -0
  49. package/docs/template-studio.md +379 -0
  50. package/docs/troubleshooting.md +174 -0
  51. package/package.json +3 -2
@@ -0,0 +1,379 @@
1
+ # Template studio
2
+
3
+ The studio functions cover everything needed to turn a new `.pptx` into a usable template: drafting its
4
+ manifest with a model, checking it, seeing whether its budgets hold, and changing it by hand or by instruction.
5
+
6
+ ```ts
7
+ import {
8
+ draftManifest, checkManifest, sampleDeck, changeManifest, editManifest,
9
+ studioView, describeManifest, describeSlot, inspectTemplate, ROLES, PptxPackage, loadTemplate, renderDeck,
10
+ } from 'slotdeck';
11
+ import type { ManifestChange, SlotPatch, ChangeResult, ManifestIssue, Role, StudioView } from 'slotdeck';
12
+ ```
13
+
14
+ ## The workflow
15
+
16
+ 1. **Draft** with `draftManifest`. A model labels the shapes; the code measures everything.
17
+ 2. **Check** with `checkManifest`. It should report nothing.
18
+ 3. **Look** at a sample: `sampleDeck` then `renderDeck`, and open the file. Every slot is filled to its budget,
19
+ so text that overflows here will overflow in real decks, and shapes that should not be there are easy to see.
20
+ 4. **Change** what is wrong: `changeManifest` with operations, or `editManifest` with an instruction.
21
+ 5. Repeat 2 to 4 until the sample looks right. Store the manifest as JSON next to the pptx.
22
+
23
+ A draft is a starting point for review, not a finished manifest. What a draft does not contain, and a person
24
+ adds: budgets tuned by eye, derived patterns (a text-only variant, a native chart), wider rows, picture areas,
25
+ chart colours, and alignment choices for decks that read the other way.
26
+
27
+ ## `draftManifest(pptx, options)`
28
+
29
+ ```ts
30
+ const { manifest, skipped, issues } = await draftManifest(pptxBytes, {
31
+ llm, // required: an LlmProvider
32
+ name: 'marketing', // optional name for the manifest; default 'template'
33
+ slideImage: (slide) => thumbnails.get(slide), // optional: { mimeType, data } of a picture of slide N (1-based); may be async
34
+ slides: [1, 2, 5], // optional: only these slides; default all
35
+ concurrency: 4, // optional: slides sent to the model at once; default 4
36
+ onProgress: ({ done, total, slide }) => {}, // optional
37
+ signal, // optional
38
+ });
39
+ // manifest: TemplateManifest
40
+ // skipped: [{ slide, reason }] slides that did not become patterns, and why
41
+ // issues: ManifestIssue[] what checkManifest finds in the draft
42
+ ```
43
+
44
+ Model calls: one about the deck as a whole, then one per slide.
45
+
46
+ What the code works out without the model:
47
+
48
+ - Every shape's id, kind, position, size, font size and sample text.
49
+ - **Frame elements**: whatever sits in the same place on many slides (a logo, a rule), and page-number, date,
50
+ footer and header placeholders. They are left alone and not shown to the model. (Spotting what repeats needs
51
+ a template of at least six slides; with fewer, only the placeholders are treated as frame.)
52
+ - **Deck fields**: texts that appear on many slides become one value for the whole deck.
53
+ - The reading direction, from the letters of the sample text.
54
+ - A character budget for each text box, from its size and font size, and never less than its sample text.
55
+ - A table's columns and rows, and how many characters fit a cell before the table leaves the slide.
56
+ - Rows and grids of like items, from numbered slot names (`card1Heading`, `card2Heading`).
57
+
58
+ What the model decides, for each slide:
59
+
60
+ - Whether the slide is a reusable layout at all. A sheet of brand assets or a broken slide is skipped, with a
61
+ reason.
62
+ - An id and a description for the pattern, and whether it is built around numbers.
63
+ - A role, a name and "optional or not" for each shape.
64
+
65
+ A label for a shape that is not on the slide is discarded, so the model cannot make the manifest point at
66
+ something that does not exist.
67
+
68
+ `slideImage` is optional. With it, the model sees the slide; without it, it judges from the measurements alone.
69
+ Any PNG or JPEG of the slide works; 480 pixels wide is enough.
70
+
71
+ To redo one slide after changing the pptx, call `draftManifest` with `slides: [n]` and add the pattern it
72
+ returns to your manifest.
73
+
74
+ ### Roles
75
+
76
+ `ROLES` lists them. A role says what a shape is for and decides the kind of slot it becomes.
77
+
78
+ | Role | For | Becomes |
79
+ | --- | --- | --- |
80
+ | `title`, `subtitle` | The headings of the slide | Text slot |
81
+ | `text` | A paragraph, or a short label | Text slot |
82
+ | `list` | A bulleted or multi-line list, one paragraph per item | Text slot with `maxParagraphs` |
83
+ | `number` | A large figure | Text slot whose budget is its sample's length |
84
+ | `caption` | A few words under a figure, icon or picture | Text slot |
85
+ | `photo` | A photograph to replace | Image slot, `imageKind: "photo"` |
86
+ | `icon` | A small pictogram to replace | Image slot, `imageKind: "icon"` |
87
+ | `graphic` | A chart, diagram or screenshot picture to replace | Image slot, `imageKind: "graphic"` |
88
+ | `chart` | A native chart in place of a picture (not offered by the drafter; set it yourself) | Chart slot |
89
+ | `table` | A table | Table slot |
90
+ | `decoration` | Something drawn for the sample content, to remove from every slide made with the pattern | Slot with `empty: "drop"` |
91
+ | `fixed` | Part of the design that stays exactly as it is | No slot |
92
+
93
+ Which roles a shape can take depends on what it is: a text box can be any text role or a decoration; a picture
94
+ can be `photo`, `icon`, `graphic`, `chart` or `decoration`; a table only `table`; a line, a group or a drawn shape
95
+ without text only `decoration`.
96
+
97
+ ## `checkManifest(template)`
98
+
99
+ ```ts
100
+ const issues = checkManifest(loadTemplate(pptxBytes, manifest));
101
+ // [{ severity: 'error' | 'warning', pattern?: string, slot?: string, message: string }]
102
+ ```
103
+
104
+ No model. It verifies that the manifest matches the pptx: slides and shapes exist, slots point at the right
105
+ kind of shape, names are unique, rows are consistent. The rules are listed in
106
+ [manifest-format.md](manifest-format.md). Run it when a template is loaded, so a template that someone changed
107
+ shows up there and not in the middle of a render.
108
+
109
+ ## `sampleDeck(template, options?)`
110
+
111
+ ```ts
112
+ const { deck, assets } = sampleDeck(template, { fill: 0.95, items: 'max' });
113
+ const { pptx } = renderDeck(template, deck, { assets, layout: 'adaptive' });
114
+ ```
115
+
116
+ Builds a deck that uses every pattern once and fills every slot close to its limit, with the template's own
117
+ filler words and plain coloured rectangles for pictures.
118
+
119
+ | Option | Default | Meaning |
120
+ | --- | --- | --- |
121
+ | `fill` | `0.95` | How full to make each text slot, as a share of its budget. |
122
+ | `items` | `'drawn'` | For patterns with a row or grid: `'drawn'` uses the template's number, `'max'` the most allowed, `'min'` the fewest. |
123
+
124
+ ## `changeManifest(template, changes)`
125
+
126
+ ```ts
127
+ const result = changeManifest(template, [
128
+ { op: 'setSlot', pattern: 'cover', slot: 'title', maxChars: 40 },
129
+ { op: 'setRepeat', pattern: 'icon-cards', max: 5 },
130
+ ]);
131
+ // result.manifest the new manifest; the template that was passed in is not touched
132
+ // result.applied [{ index, change, message }] what was done, each with a sentence saying what is now true
133
+ // result.rejected [{ index, change, reason }] what was refused, and why
134
+ // result.issues checkManifest on the result
135
+ ```
136
+
137
+ No model. Changes are applied in order. One that cannot be made, or that would leave the manifest pointing at
138
+ something the pptx does not have, is refused with a reason and skipped; the rest are still applied. To continue
139
+ from the result, load it: `loadTemplate(pptxBytes, result.manifest)` or `{ pkg: template.pkg, manifest: result.manifest }`.
140
+
141
+ ### Operations
142
+
143
+ Every change is an object with `op`. `pattern` is a pattern id, `slot` a slot name, `shape` a shape id (string).
144
+
145
+ #### `setManifest`
146
+
147
+ ```ts
148
+ { op: 'setManifest', name?: string, language?: string, direction?: 'ltr' | 'rtl', alignOnDirectionChange?: 'flip' | 'keep', textScale?: number | null }
149
+ ```
150
+
151
+ `textScale` enlarges the body text of every deck made from the template (`1.3` for 30% larger); `null` or `1`
152
+ returns to the designed size.
153
+
154
+ #### `addPattern`
155
+
156
+ ```ts
157
+ { op: 'addPattern', slide: number, id: string, description?: string }
158
+ ```
159
+
160
+ Makes a slide a pattern with no content slots yet. Texts that other patterns already treat as deck fields are
161
+ filled in as deck-field slots. Follow it with `addSlot` operations, or use `draftManifest` with `slides: [n]`
162
+ instead to get a complete pattern.
163
+
164
+ #### `copyPattern`
165
+
166
+ ```ts
167
+ { op: 'copyPattern', pattern: string, id: string, description?: string }
168
+ ```
169
+
170
+ A second pattern from the same slide, to turn into a variant with further operations.
171
+
172
+ #### `removePattern`
173
+
174
+ ```ts
175
+ { op: 'removePattern', pattern: string }
176
+ ```
177
+
178
+ The last pattern cannot be removed.
179
+
180
+ #### `setPattern`
181
+
182
+ ```ts
183
+ { op: 'setPattern', pattern: string, id?: string, description?: string, figures?: boolean }
184
+ ```
185
+
186
+ `id` renames the pattern. `figures: true` offers it only for briefs that contain numbers.
187
+
188
+ #### `addSlot`
189
+
190
+ ```ts
191
+ { op: 'addSlot', pattern: string, shape: string, role: Role, name?: string, optional?: boolean }
192
+ ```
193
+
194
+ Makes a shape a slot in a role, with limits measured from the shape (budget from the box and font, table limits
195
+ from the table). On a shape that already is a slot, it replaces that slot, keeping its name unless `name` is
196
+ given: this is how a role is changed. Role `fixed` removes the slot. `name` defaults to the role.
197
+
198
+ #### `removeSlot`
199
+
200
+ ```ts
201
+ { op: 'removeSlot', pattern: string, slot: string }
202
+ ```
203
+
204
+ The shape goes back to being part of the design.
205
+
206
+ #### `setSlot`
207
+
208
+ ```ts
209
+ { op: 'setSlot', pattern: string, slot: string, ...SlotPatch }
210
+ ```
211
+
212
+ `SlotPatch` properties (all optional; `null` clears an optional setting):
213
+
214
+ | Property | Applies to | Meaning |
215
+ | --- | --- | --- |
216
+ | `name` | any | Renames the slot. |
217
+ | `description` | any | Guidance for the planning model. |
218
+ | `empty` | any | `error` (required), `remove` (optional), `keep`, `drop`. |
219
+ | `maxChars`, `maxParagraphs` | text | Budgets. |
220
+ | `deckField` | text | Ties the text to a deck-level field. |
221
+ | `alignOnDirectionChange` | text | `flip` or `keep` for this slot. |
222
+ | `styleRun` | text | `first` or `last`. |
223
+ | `fontSize` | text | A font size in points other than the template's. |
224
+ | `paragraphGap` | text | Space after each paragraph, in points. |
225
+ | `scales` | text | Whether a deck's text size applies. |
226
+ | `box` | text, image, chart | Another area for the shape: `{ x, y, w, h }` as fractions of the slide. |
227
+ | `imageKind`, `fit` | image | `photo`, `icon` or `graphic`; `cover` or `contain`. |
228
+ | `maxColumns`, `maxRows`, `maxCellChars` | table | Limits. |
229
+ | `headerText`, `bodyText` | table | `{ size?, bold?, color?, font? }`, merged into what is already set. |
230
+ | `maxCategories`, `maxSeries`, `colors`, `text`, `lineColor` | chart | Limits and styling. |
231
+
232
+ A property that does not apply to the slot's kind is refused (`"title" is a text slot; maxColumns does not apply to it`).
233
+
234
+ #### `setRepeat`
235
+
236
+ ```ts
237
+ { op: 'setRepeat', pattern: string, min?: number, max?: number, item?: string, span?: { left: number, right: number } | null }
238
+ ```
239
+
240
+ Sets how many items a row or grid takes. When the pattern has no row yet, it is found from numbered slot names:
241
+ name the slots of each item alike with a number (`card1Heading`, `card1Text`, `card2Heading`, `card2Text`) and
242
+ they become a row.
243
+
244
+ #### `removeRepeat`
245
+
246
+ ```ts
247
+ { op: 'removeRepeat', pattern: string }
248
+ ```
249
+
250
+ The items stop being a row whose number can change.
251
+
252
+ ### What the code adds to each change
253
+
254
+ - **Budgets follow.** After a new `fontSize` or `box` on a text slot, or a new text size, column count or row
255
+ count on a table, the character budget is worked out again, unless the same change states one. The message
256
+ says so: `maxChars follows at 324`.
257
+ - **Rows are checked for room.** Asking for more items than the row draws widens the row as far as the slide
258
+ allows: out to the slide's margin, or to the next shape beside the row. Asking for more than fit is refused
259
+ with the most that do: `5 cards do not fit side by side: each is 21% of the slide wide and the row has 93%. The most is 4`.
260
+ - **Row items stay alike.** A `setSlot` on one item's slot (`card1Icon`) is made to that slot of every item.
261
+ Slots of a row cannot be renamed, moved or removed one by one; remove the row first.
262
+ - **Nothing is left broken.** After each change the manifest is checked against the pptx. A change that
263
+ introduces a new error is refused.
264
+
265
+ ### Recipes
266
+
267
+ The pattern ids, slot names and shape ids below are those of the demo template in the package's repository;
268
+ with your template, use your own.
269
+
270
+ Remove a shape from every slide made with a pattern (a sticker, a stray page number, an ornament):
271
+
272
+ ```ts
273
+ { op: 'addSlot', pattern: 'quote', shape: '5', role: 'decoration' }
274
+ ```
275
+
276
+ A native chart in place of a sample chart picture, as a second pattern:
277
+
278
+ ```ts
279
+ [
280
+ { op: 'copyPattern', pattern: 'chart-with-takeaway', id: 'chart-native', description: 'A native, editable chart with a short explanation.' },
281
+ { op: 'addSlot', pattern: 'chart-native', shape: '6', role: 'chart', name: 'chart' },
282
+ { op: 'setSlot', pattern: 'chart-native', slot: 'chart', colors: ['0E7C7B', 'F4A259'], text: { size: 12, color: '5C6B73' } },
283
+ ]
284
+ ```
285
+
286
+ A text-only slide from a slide with a photo:
287
+
288
+ ```ts
289
+ [
290
+ { op: 'copyPattern', pattern: 'text-and-photo', id: 'text-only' },
291
+ { op: 'addSlot', pattern: 'text-only', shape: '2', role: 'decoration' }, // the photo
292
+ { op: 'setSlot', pattern: 'text-only', slot: 'body', box: { x: 0.04, y: 0.49, w: 0.72, h: 0.39 }, fontSize: 20, maxParagraphs: 3 },
293
+ ]
294
+ ```
295
+
296
+ Smaller table text and fewer columns (the cell budget is measured for you):
297
+
298
+ ```ts
299
+ { op: 'setSlot', pattern: 'table', slot: 'table', maxColumns: 5, bodyText: { size: 9 }, headerText: { size: 10, bold: true } }
300
+ ```
301
+
302
+ Larger body text in every deck, for a template whose own body text is small (render the sample deck afterwards
303
+ to see that everything still fits):
304
+
305
+ ```ts
306
+ { op: 'setManifest', textScale: 1.3 }
307
+ ```
308
+
309
+ Mirror alignment for right-to-left decks, but keep the line beside the logo where it is:
310
+
311
+ ```ts
312
+ [
313
+ { op: 'setManifest', alignOnDirectionChange: 'flip' },
314
+ { op: 'setSlot', pattern: 'cover', slot: 'organizationDepartment', alignOnDirectionChange: 'keep' },
315
+ ]
316
+ ```
317
+
318
+ ## `editManifest(template, instruction, options)`
319
+
320
+ ```ts
321
+ const result = await editManifest(template, 'Let the cards take up to six and make the subtitle optional everywhere', { llm, signal });
322
+ // the same fields as changeManifest's result, plus:
323
+ // result.note what the model could not do or needs to know, in the person's language; often ''
324
+ ```
325
+
326
+ The model reads the manifest next to the measured shapes of each pattern's slide and answers with operations
327
+ for `changeManifest`. It writes no manifest itself, so its answer goes through the same checks as a change made
328
+ by hand. One model call, and a second when some operations were refused, so the model can correct them.
329
+
330
+ - Instructions can be in any language.
331
+ - A person may refer to a slide by number or to a pattern by id.
332
+ - The model cannot set positions: `box` and `span` exist only for changes made by hand.
333
+ - An operation the model got wrong shows up in `rejected` with the reason; nothing of it is applied.
334
+
335
+ `describeManifest(template)` returns the text the model reads: every pattern with its slots and limits, and the
336
+ shapes of its slide that are not slots. An app can show it to its own chat model, or print it for review.
337
+
338
+ ## `studioView(template)`
339
+
340
+ For a screen that edits a manifest by hand.
341
+
342
+ ```ts
343
+ const view = studioView(template);
344
+ // view.aspectRatio slide width divided by height
345
+ // view.slides[i].slide 1-based slide number
346
+ // view.slides[i].patterns ids of the patterns made from this slide ([] when unused)
347
+ // view.slides[i].shapes[j]:
348
+ // id the id a change names the shape by
349
+ // kind 'text' | 'picture' | 'shape' | 'line' | 'group' | 'table' | 'other'
350
+ // box { x, y, w, h } as fractions of the slide: lay it over a picture of the slide
351
+ // groupId the group the shape sits in, when it does
352
+ // text sample text, when it has some
353
+ // fontSize largest font size of the sample text, in points
354
+ // frame true when the shape repeats across slides (logo, page number): rarely content
355
+ // roles the roles addSlot accepts for this shape
356
+ // slots [{ pattern, name, kind, empty, summary }] slots that point at the shape
357
+ ```
358
+
359
+ A typical screen draws a picture of the slide, lays a rectangle over each shape from `box`, colours the ones
360
+ that are slots, and on a click offers the shape's `roles` (sending `addSlot`) or the slot's settings (sending
361
+ `setSlot`). Nothing is stored in the view: call it again after every change.
362
+
363
+ `describeSlot(slot)` gives a slot in a few words, such as `text, up to 40 characters, required`.
364
+
365
+ ## `inspectTemplate(pkg)`
366
+
367
+ ```ts
368
+ const info = inspectTemplate(PptxPackage.load(pptxBytes));
369
+ // info.slideWidth, info.slideHeight in EMU (914400 per inch)
370
+ // info.defaultFontSize points
371
+ // info.slides[i] = { number, part, layout, shapes: ShapeInfo[] }
372
+ ```
373
+
374
+ The raw list of shapes, without a manifest. `studioView` is this joined with a manifest.
375
+
376
+ ## Storing a manifest
377
+
378
+ A manifest is plain JSON. Keep it next to the pptx it describes, and keep the two in step: if the pptx is
379
+ edited in PowerPoint, shape ids can change, and `checkManifest` will say which slots no longer match.
@@ -0,0 +1,174 @@
1
+ # Troubleshooting
2
+
3
+ Every message slotdeck produces, what it means, and what to do. Then the known limits.
4
+
5
+ ## Setup problems
6
+
7
+ | Symptom | Cause | Fix |
8
+ | --- | --- | --- |
9
+ | `ERR_REQUIRE_ESM`, or `require() of ES Module` | The package is ESM only. | Use `import`. Set `"type": "module"` in your `package.json`, or use `.mjs` files, or `await import('slotdeck')` from CommonJS. |
10
+ | `Cannot find module 'slotdeck/node'` in a bundler | `slotdeck/node` uses `node:fs` and is for Node only. | Import it only in server code. Everything else is in `slotdeck`. |
11
+ | `Not an Office Open XML package: [Content_Types].xml is missing` | The bytes passed to `loadTemplate` are not a `.pptx` (an old `.ppt`, a PDF, an HTML error page saved as a file). | Pass the bytes of a real `.pptx`. Save a `.ppt` as `.pptx` in PowerPoint first. |
12
+ | Types are not found | TypeScript module resolution is too old for package `exports`. | Use `"moduleResolution": "bundler"`, `"node16"` or `"nodenext"`. |
13
+
14
+ ## Deck errors
15
+
16
+ These make `renderDeck` throw `DeckValidationError` and are returned by `validateDeck` with `severity: 'error'`.
17
+ Each has the slide number and usually the slot name.
18
+
19
+ | Message | Meaning | Fix |
20
+ | --- | --- | --- |
21
+ | `the deck has no slides` | `slides` is missing or empty. | Give at least one slide. |
22
+ | `unknown pattern "x" (available: a, b, c)` | The slide names a pattern the manifest does not have. | Use one of the listed ids. |
23
+ | `pattern "x" takes 2 to 4 cards, not 5` | `items` is outside the pattern's range. | Use a count in the range, or raise `max` in the manifest if the template has room. |
24
+ | `this slot is required` | A required slot has no value (or an empty string), or a required deck field was not given. | Give the slot a value, or add the field to `deck.fields`. |
25
+ | `leaving this slot empty needs layout: 'adaptive'` | An optional slot was left out in `strict` layout. | Render with `layout: 'adaptive'`, or fill the slot. |
26
+ | `expected text` | A text slot got a picture, table or chart value. | Give a string or an array of strings. |
27
+ | `expected an image reference such as { "asset": "id" }` | An image slot got something else. | Give `{ "asset": "<id>" }`. |
28
+ | `expected a table such as { "header": [...], "rows": [[...]] }` | A table slot got something else. | Give a `header` array and a `rows` array of arrays. |
29
+ | `the table has no columns` | `header` is empty. | Give at least one column heading. |
30
+ | `row 2 has 3 cells; the table has 4 columns` | A row's length differs from the header's. | Give every row one cell per column. Use `""` for an empty cell. |
31
+ | `expected a chart such as { "type": "column", ... }` | A chart slot got something else. | Give `type`, `categories` and `series`. |
32
+ | `unknown chart type "x"` | `type` is not `column`, `bar`, `line` or `pie`. | Use one of the four. |
33
+ | `the chart has no categories` / `the chart has no series` | An empty array. | Give at least one of each. |
34
+ | `series "x" has 3 values; the chart has 4 categories` | A series' length differs from the categories'. | One value per category. |
35
+ | `series "x" has a value that is not a number` | A value is `null`, a string that is not a number, or missing. | Give numbers. |
36
+ | `a pie chart takes exactly one series` | A pie with several series. | Give one series, or use another type. |
37
+ | `asset "x" was not provided` | The deck refers to a picture whose bytes were not passed. | Add it to `options.assets` under that id. With the planner, pass the same `icons` and `assets` to rendering as to planning, or use `resolveAssets`. |
38
+
39
+ ## Deck warnings
40
+
41
+ These do not stop rendering. `renderDeck` and the planner return them.
42
+
43
+ | Message | Meaning | What to do |
44
+ | --- | --- | --- |
45
+ | `text is 230 characters; the slot fits about 211` | Text over the slot's `maxChars`. It is rendered and may overflow its box. | Shorten the text, or raise the budget in the manifest if the sample deck shows room. |
46
+ | `text has 5 paragraphs; the slot fits 4` | Over `maxParagraphs`. | Fewer paragraphs. |
47
+ | `the table has 6 columns; the slot fits 5` (also rows) | Over the table's limits. The table may leave the slide. | A smaller table. |
48
+ | `a cell is 40 characters; cells fit about 22` | A long cell makes its row taller. | Shorter cells. |
49
+ | `the chart has 12 categories; the slot fits 8` (also series) | Over the chart's limits. Labels may crowd. | Fewer categories. |
50
+ | `pattern "x" has no such slot; ignored` | The slide gives a slot name the pattern does not have. | Check the name against the manifest. With `items`, slots of items beyond the count are reported this way. |
51
+ | `pattern "x" has a fixed number of items; "items" is ignored` | `items` on a pattern without a `repeat`. | Remove `items`. |
52
+ | `chart format "x" is not known and is ignored; use "number" or "percent"` | An unknown `format`. | Use one of the two, or leave it out. |
53
+ | `chart values not stated in the brief: 89` | (Planner only.) The model charted a number the brief does not contain. It may be invented, or worked out from the brief. | Check the number. Put the figures you want charted into the brief. |
54
+
55
+ ## Planner errors (`PlanError`)
56
+
57
+ | Message | Meaning | Fix |
58
+ | --- | --- | --- |
59
+ | `No pattern of template "x" can be used: ...` | Nothing in the manifest can be filled with what was supplied. The reasons follow. | See the reasons below. |
60
+ | `The model did not produce a usable outline: ...` | The model's answer did not have the expected shape. | Retry. If it repeats, check that your provider returns parsed JSON that follows the schema. |
61
+ | `Template "x" has no plain text pattern, so a text-only deck cannot be planned from it` | `textOnly` was asked for, and no pattern of the manifest is plain text. | Add a plain text pattern to the manifest (see [layout-freedom.md](layout-freedom.md)), or plan without `textOnly`. |
62
+ | `The plan uses patterns that cannot be filled with what was supplied: a, b` | A plan was made with icons, assets or fields that are not passed now. | Pass the same `icons`, `assets`, `fields` and `layout` to every step. |
63
+ | `Slide 3: pattern "x" needs icons, and no icon set was supplied` | The same, for an edit. | As above. |
64
+
65
+ Reasons a pattern is not offered:
66
+
67
+ | Reason | Fix |
68
+ | --- | --- |
69
+ | `is built around numbers, and the brief gives none` | Put figures in the brief, or set `numberPatterns: 'always'`. |
70
+ | `needs a photo, and images are turned off` | Give an `ImageProvider` or photo assets, and an `imageDensity` other than `none`. |
71
+ | `needs icons, and no icon set was supplied` | Pass `icons`. |
72
+ | `needs a graphic asset, and none was supplied` | Pass an asset of kind `graphic`. |
73
+ | `needs the deck field "x"` | Add it to `brief.fields`. |
74
+
75
+ ## Manifest problems (`checkManifest`)
76
+
77
+ | Message | Fix |
78
+ | --- | --- |
79
+ | `points at slide 9, but the template has 5 slides` | Correct `sourceSlide`, or the pptx lost slides. |
80
+ | `points at shape #99, which is not on slide 1` | The shape id is wrong, or the slide was edited. List ids with `inspectTemplate` or `studioView`. |
81
+ | `is an image slot, but shape #6 is a text box` (and similar) | The slot's kind does not match the shape. Change the kind or the shape id. |
82
+ | `another pattern has the same id` / `another slot of the pattern has the same name` | Rename one. |
83
+ | `has a box, but its shape is inside a group` | A `box` only works for a shape placed directly on the slide. Remove the `box`, or ungroup in the template. |
84
+ | `is a chart slot, but its shape is inside a group` | A native chart cannot replace a picture inside a group. Ungroup in the template. |
85
+ | `repeat expects a slot named "card1Text"` | A row's name template has no matching slot for one of the drawn items. Add the slot or correct the template name. |
86
+ | `repeat item 2 points at shape #77, which is not on slide 4` | Correct the shape ids in `repeat.shapes`. |
87
+ | `repeat items must all sit directly on the slide, or all in the same group` | Items in different groups cannot be laid out together. |
88
+ | `repeat range 3 to 2 is not valid` | `min` must be at least 1 and not more than `max`. |
89
+ | warning: `has no character budget, so nothing stops text that does not fit` | Add `maxChars`. |
90
+ | warning: `points at the same shape as "x"` | Two slots on one shape: usually a mistake. |
91
+ | warning: `a grid cannot hold more items than it draws` | Lower `max` to the number drawn. |
92
+
93
+ ## Refusals from `changeManifest`
94
+
95
+ `rejected[i].reason` is one of the manifest messages above, or one of these:
96
+
97
+ | Reason | Meaning |
98
+ | --- | --- |
99
+ | `there is no pattern "x"` / `"p" has no slot "x"` / `slide 4 has no shape #99` | The change names something that does not exist. |
100
+ | `5 cards do not fit side by side: each is 21% of the slide wide and the row has 93%. The most is 4` | The row cannot hold that many. Use the stated maximum. |
101
+ | `a grid holds at most the 6 cards it draws; 8 would need a new line of them` | A grid's `max` is the number drawn. |
102
+ | `"title" is a text slot; maxColumns does not apply to it` | A setting for another kind of slot. |
103
+ | `maxChars must be a whole number of 1 or more` | A budget of zero, a fraction or a negative number. |
104
+ | `"card1Text" is part of the row of cards, so it cannot be renamed, moved or tied to a deck field; remove the row first` | Row slots must stay alike. Use `removeRepeat` first if you really need to. |
105
+ | `shape #4 is a picture; it can be photo, icon, graphic, chart, decoration` | The role does not suit the shape. |
106
+ | `"x" has no row of like items. Name the slots of each item alike with a number ...` | `setRepeat` found no numbered slots to make a row from. |
107
+ | `another pattern is already called "x"` | Choose another id. |
108
+ | `a template needs at least one pattern` | The last pattern cannot be removed. |
109
+
110
+ ## Rendering errors that are not about the deck
111
+
112
+ Plain `Error`s from `renderDeck`. They mean the manifest and the pptx disagree, or a picture is not usable.
113
+ `checkManifest` catches the first group ahead of time.
114
+
115
+ | Message | Fix |
116
+ | --- | --- |
117
+ | `Pattern "x": slot "y" points at shape #12, which is not on template slide 3` | Fix the manifest (see above). |
118
+ | `Pattern "x" points at slide 9, but the template has 5 slides` | Fix `sourceSlide`. |
119
+ | `Unsupported image format: expected PNG, JPEG or GIF` | Convert the picture (WebP, SVG, HEIC and others are not supported). |
120
+ | `sample tables with merged cells are not supported` | The template's sample table has merged cells. Unmerge them in the template. |
121
+ | `a chart slot must point at a shape that is not inside a group` | Ungroup the sample picture in the template. |
122
+
123
+ ## Gemini adapter errors (`GeminiError`)
124
+
125
+ | Message | Meaning |
126
+ | --- | --- |
127
+ | `Gemini request failed (401): ...` | The key or token is wrong or missing. `error.status` has the HTTP status. |
128
+ | `Gemini request failed (429): ...` | Rate limit or quota. Retry later. |
129
+ | `Gemini request failed (400): The specified schema produces a constraint that has too many states for serving` | The service rejected a schema as too complex. This was seen during development with length limits on nested arrays, which the shipped schemas do not use. If it appears with a long deck, asking for fewer slides may help. |
130
+ | `Gemini did not answer within 180 seconds` | The request hung. Retry, or raise `timeoutMs`. |
131
+ | `Gemini did not return valid JSON: ...` | The model's text was not JSON. Retry. |
132
+ | `Gemini interaction ended with status "..."` | The service did not complete the request; the reason follows when it gives one. |
133
+ | `Gemini's answer was cut off at the limit of 9552 tokens, twice. ...` | The model wrote until the output limit, twice in a row. It is nearly always a model running away inside one text field (see "Runaway answers" in [providers.md](providers.md)). Try again. If it keeps happening, set `thinkingLevel: 'low'`; if the limit is too small for a legitimately large answer, set `maxOutputTokens`. |
134
+ | `Gemini request failed (400): Request blocked due to copyright/recitation content` | The service's recitation filter tripped on an ordinary answer. It happens at random. The adapter already sent the request twice; try again. |
135
+ | `Gemini request failed (400): Thinking level ... is not supported for this model` | The `thinkingLevel` set on the adapter is not one the model accepts (`minimal` is refused by some models). Use `low`, or leave it unset. |
136
+ | `Gemini request failed (429): Your project has exceeded its monthly spending cap` | The key's project reached the spending cap set for it. Nothing works until the cap is raised in the provider's console. |
137
+ | `Gemini returned no image` | The image model answered without a picture, often because the prompt was refused. |
138
+
139
+ ## The result looks wrong
140
+
141
+ | Observation | Likely cause | Fix |
142
+ | --- | --- | --- |
143
+ | Text runs out of its box or over a neighbour | The slot's budget is too generous, or the deck ignored a budget warning. | Render `sampleDeck` to see every slot at its budget, then lower `maxChars` with `changeManifest`. |
144
+ | A leftover sample element appears (a sticker, a second page number, a sample caption) | The shape is not a slot, so it is copied as it is. | Mark it as a decoration: `{ op: 'addSlot', pattern, shape, role: 'decoration' }`. |
145
+ | A picture is cropped badly | `cover` crops to the frame. | Supply a picture closer to the frame's proportions, or set `fit: 'contain'` on the slot. |
146
+ | Cards are in reverse order in a right-to-left deck | The pattern's items are not declared as a row. | Add a `repeat` (`setRepeat`) or `sequences`. |
147
+ | Hebrew or Arabic text is left-aligned | The manifest keeps alignment. | Set `alignOnDirectionChange: 'flip'` on the manifest or the slot. |
148
+ | A font changes for another script | The template's font lacks that script. | Set a complex-script font in the template. |
149
+ | Body text looks small | The engine keeps the template's own font sizes, and the template was designed with small body text (9 pt on a 10-inch slide is common in decks exported from Google Slides). | For every deck: set `textScale` on the manifest, such as `1.3`. For one deck: `textSize: 'large'` on the brief, `text_size` in the chat tool, or `textScale` on the deck. See "Larger text" in [layout-freedom.md](layout-freedom.md). |
150
+ | The planner never uses a pattern | It is not offered. | See "Reasons a pattern is not offered". Also check the pattern's `description`: the model chooses by it. |
151
+ | A deck that was asked to be "text only" has cards, tables or charts | The wish did not reach the planner as a wish about slides: only "no pictures" was passed (`imageDensity: 'none'`, or `image_density` in the chat tool), which still allows cards, tables and charts. | Set `textOnly: true` on the brief, or `text_only: true` in `plan_presentation`. See "Asking for a kind of slide" in [generating.md](generating.md). |
152
+ | A text-only deck uses one layout for every content slide | The template has one plain text pattern besides its opening and closing. | Add more plain text patterns to the manifest as derived patterns: a list without its photo, a quotation, two paragraphs side by side. |
153
+ | A slide states a number or contact detail that nobody gave | The model invented it. | Figures are guarded only for patterns marked `figures` and for charts. State details in the brief and review the deck. |
154
+
155
+ ## Known limits
156
+
157
+ - **Shapes need their own position.** The engine measures the shapes on a slide. A shape that takes its position
158
+ and font size from a layout placeholder, without stating them on the slide, is filled correctly but measured
159
+ as empty: a drafted budget for it is only as long as its sample text, and `box` cannot be applied to it. Decks
160
+ exported from Google Slides and decks built from free-standing text boxes work as they are.
161
+ - **Budgets are estimates.** A character budget cannot see lines lost to wrapping. Leave room in budgets for
162
+ slots with several paragraphs.
163
+ - **Text boxes do not resize.** Text longer than its box overflows.
164
+ - **Charts:** `column`, `bar`, `line` and `pie` only. No stacked or combined charts.
165
+ - **Tables:** the sample table must not have merged cells.
166
+ - **Pictures:** PNG, JPEG and GIF only.
167
+ - **Not carried over from the template:** speaker notes, named sections and revision history.
168
+ - **Animations** on template slides were not tested. A slide whose animated shape is removed may make PowerPoint
169
+ offer to repair the file; use templates without animations, or keep animated shapes out of optional slots.
170
+ - **Not read back:** changes made to a rendered file in PowerPoint. The deck JSON is the source of truth.
171
+ - **Not possible:** swapping the side of a photo; positioning a shape from a deck.
172
+ - **What was tested.** Output was checked by rendering in PowerPoint on Windows. Other programs that open pptx
173
+ files were not tested. The Gemini adapters were run against the Gemini Developer API; the Vertex AI endpoint
174
+ is supported but was not run live.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slotdeck",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Turn a PowerPoint template into new decks: a model plans the slides, the code clones the template's own slides and fills them.",
5
5
  "keywords": [
6
6
  "pptx",
@@ -43,7 +43,8 @@
43
43
  }
44
44
  },
45
45
  "files": [
46
- "dist"
46
+ "dist",
47
+ "docs/*.md"
47
48
  ],
48
49
  "scripts": {
49
50
  "build": "tsc -p tsconfig.build.json",