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,83 @@
1
+ # Languages and reading direction
2
+
3
+ A template is designed for one reading direction (`direction` in the manifest: `ltr` or `rtl`). A deck states
4
+ its own (`direction` in the deck, defaulting to the template's). When they differ, the text flips and the layout
5
+ stays.
6
+
7
+ This works both ways: a Hebrew or Arabic deck on a left-to-right template, and an English deck on a
8
+ right-to-left template.
9
+
10
+ ## Setting language and direction
11
+
12
+ In a deck:
13
+
14
+ ```json
15
+ { "version": 1, "language": "he", "direction": "rtl", "slides": [ ... ] }
16
+ ```
17
+
18
+ In a brief for the planner, `language` is enough: the direction follows from it.
19
+
20
+ ```ts
21
+ await generateDeck(template, { prompt: '...', language: 'he' }, options);
22
+ ```
23
+
24
+ `directionOf('he')` returns `'rtl'`; `directionOf('en')` returns `'ltr'`.
25
+
26
+ ## What changes in a deck that reads the other way
27
+
28
+ | Thing | What happens |
29
+ | --- | --- |
30
+ | Paragraph direction | Every paragraph gets the direction of its own text, from its first letter. A Hebrew deck may hold an English line, and that line is left to right. Text with no letters ("78%") keeps the template paragraph's direction. |
31
+ | Alignment | Left and right alignment are swapped (`alignOnDirectionChange: "flip"`, the default). A paragraph that states no alignment counts as left-aligned. Centred text stays centred. |
32
+ | Rows and grids of items | Filled mirror-wise within each line, so the deck's first item is where its readers start: at the right in a right-to-left deck. Shapes do not move; the values go to the mirrored positions. |
33
+ | Tables | Run in the deck's direction, with text aligned to its start. |
34
+ | Charts | Labels are set right to left. |
35
+ | Run language | Right-to-left text is marked with a right-to-left language. |
36
+ | Shapes | Do not move. A photo on the right stays on the right. Bullets and their indent follow the paragraph direction automatically. |
37
+
38
+ ## Keeping alignment
39
+
40
+ Mirroring alignment suits templates whose text boxes are as wide as the area they belong to. It does not suit a
41
+ template whose text boxes share one edge but not the other: flipped text would then start at uneven positions or
42
+ run into a neighbouring shape.
43
+
44
+ - For the whole template: `"alignOnDirectionChange": "keep"` in the manifest.
45
+ - For one slot: `"alignOnDirectionChange": "keep"` (or `"flip"`) on that text slot.
46
+
47
+ A common case for a per-slot `keep` is a line next to a logo: it should stay beside the logo in either
48
+ direction.
49
+
50
+ ## Why runs are marked with a language
51
+
52
+ PowerPoint decides where punctuation and spaces go by the language of the run they are in, not only by the
53
+ paragraph's direction. In a run marked as English, the text
54
+
55
+ ```
56
+ שאלות: name@example.org
57
+ ```
58
+
59
+ is displayed with the colon on the far side of the address. slotdeck therefore marks right-to-left runs with a
60
+ right-to-left language (`he-IL` for Hebrew, `ar-SA` for Arabic) unless the template already gave the run one.
61
+ You do not need to do anything for this.
62
+
63
+ ## Fonts
64
+
65
+ Text keeps the template's fonts. If the template's font has no glyphs for the deck's script, PowerPoint
66
+ substitutes another font. For a template that will be used in two scripts, set a complex-script font on its text
67
+ (in PowerPoint: the font for "complex scripts"), or choose fonts that cover both.
68
+
69
+ ## Mirrored item order in the deck JSON
70
+
71
+ The deck always numbers items in its own reading order: `card1` is the first card for the reader. You do not
72
+ reverse anything yourself. Validation messages name the slot as the deck wrote it.
73
+
74
+ ## Language of generated text
75
+
76
+ The planner writes in `brief.language`. Slot names and pattern ids stay as the manifest defines them (they are
77
+ identifiers, not content). Translating an existing deck is an edit:
78
+
79
+ ```ts
80
+ await editDeck(template, deck, 'Translate the deck to English', { llm });
81
+ ```
82
+
83
+ This rewrites every slide, sets the direction, and translates deck-level fields.
@@ -0,0 +1,136 @@
1
+ # Layout freedom
2
+
3
+ A deck can depart from the template slide in four bounded ways. In each, the choice is a small value in the deck
4
+ JSON, the manifest says what is allowed, and the code moves the shapes. Nothing is ever positioned by a model.
5
+
6
+ ## 1. A different number of items
7
+
8
+ A pattern with a `repeat` in the manifest has a row or grid of like items: cards, figures, steps. The deck says
9
+ how many with `items`.
10
+
11
+ Manifest:
12
+
13
+ ```json
14
+ "repeat": {
15
+ "item": "card",
16
+ "slots": ["card{n}Icon", "card{n}Heading", "card{n}Text"],
17
+ "shapes": [["6"], ["12"], ["18"], ["24"]],
18
+ "min": 2,
19
+ "max": 4
20
+ }
21
+ ```
22
+
23
+ Deck:
24
+
25
+ ```json
26
+ { "pattern": "icon-cards", "items": 3, "slots": { "title": "...", "card1Icon": {"asset": "clock"}, "card1Heading": "...", "card1Text": "...", "card2Icon": {"asset": "shield"}, "card2Heading": "...", "card2Text": "...", "card3Icon": {"asset": "people"}, "card3Heading": "...", "card3Text": "..." } }
27
+ ```
28
+
29
+ What the renderer does:
30
+
31
+ - **Fewer items than drawn:** the surplus items' shapes are removed.
32
+ - **More items than drawn:** the last item is copied. A copy's slots have the next numbers (`card5Icon`,
33
+ `card5Heading`, ...) and take the last drawn item's limits.
34
+ - **Spacing:** the items are laid out again around the centre of the area the row occupied. They keep the
35
+ template's spacing when there is room and move closer together when there is not. With a `span`, the row may
36
+ spread out to those limits.
37
+ - Without `items`, the slide has the number of items drawn on the template slide.
38
+
39
+ An `items` value outside `min` to `max` is an error:
40
+ `pattern "icon-cards" takes 2 to 4 cards, not 5`.
41
+
42
+ How many items fit is a property of the template: `changeManifest` refuses a `max` that does not fit side by
43
+ side and says the most that do (see [template-studio.md](template-studio.md)).
44
+
45
+ ## 2. Grids
46
+
47
+ With `columns` in the `repeat`, the items form a grid. `columns` is how many items one line holds on the
48
+ template slide.
49
+
50
+ - Items fill lines in reading order and use as few lines as the count needs.
51
+ - They are spread evenly over those lines: in a three-column grid, four items make two lines of two (not three
52
+ and one), five make three and two, three make one line.
53
+ - Each line is centred, and a single line moves to the middle of the grid's area.
54
+ - A grid cannot hold more items than it draws.
55
+
56
+ ## 3. Larger text
57
+
58
+ A deck can ask for larger body text:
59
+
60
+ ```json
61
+ { "version": 1, "textScale": 1.25, "slides": [ ... ] }
62
+ ```
63
+
64
+ or, on a brief for the planner, `textSize: 'large'` (1.25) or `'x-large'` (1.5). The named sizes are in
65
+ `TEXT_SIZES`: `{ normal: 1, large: 1.25, 'x-large': 1.5 }`.
66
+
67
+ - It applies to **body text**: by default, text slots whose template size is 12 pt or under and that are not
68
+ deck fields. Titles and large figures keep their size. A slot can opt in or out with `scales` in the
69
+ manifest.
70
+ - Boxes do not grow, so larger text fits less. The character budget of a scaled slot is divided by the square
71
+ of the scale (a 300-character slot holds 192 at 1.25), and the paragraph budget by the scale. The planner
72
+ shows the model the reduced budgets, so it writes less.
73
+ - Through `editDeck`, "make the text larger" sets the scale and rewrites only the slides whose text no longer
74
+ fits.
75
+ - Table text and chart labels follow the scale where the manifest states their size (`bodyText.size`,
76
+ `headerText.size`, `text.size`), and a table cell then holds less.
77
+
78
+ ### Larger text for a whole template
79
+
80
+ The engine keeps the template's own font sizes. A template designed with small body text, such as 9 pt on a
81
+ 10-inch-wide slide, therefore gives decks with small body text. To change that once, for every deck, set
82
+ `textScale` on the manifest:
83
+
84
+ ```json
85
+ { "version": 1, "name": "corporate", "direction": "rtl", "textScale": 1.3, "patterns": [ ... ] }
86
+ ```
87
+
88
+ With `changeManifest`: `{ op: 'setManifest', textScale: 1.3 }`.
89
+
90
+ - It works exactly like a deck's `textScale` and applies to the same slots (body text: 12 pt and under by
91
+ default), but for every deck made from the template.
92
+ - Budgets shrink to match, in validation, in what the planner shows the model, and in `sampleDeck`: a slot
93
+ that held 240 characters holds 142 at 1.3. Render the sample deck after setting it to see that everything
94
+ still fits.
95
+ - A deck's own text size goes on top. With a template at 1.3, a deck at `large` (1.25) shows body text at about
96
+ 1.6 times the designed size.
97
+ - A slot that should keep its size can opt out with `"scales": false`.
98
+
99
+ ## 4. Derived patterns
100
+
101
+ Several patterns may point at the same template slide and use it differently. A derived pattern is made by
102
+ copying a pattern and changing some of its slots:
103
+
104
+ | Change | Manifest property | Example |
105
+ | --- | --- | --- |
106
+ | Remove a shape always | `"empty": "drop"` on a slot that points at it | A text-only slide from a slide with a photo |
107
+ | Move or resize a text box | `box` on the text slot | Widen the text over the area the photo left free |
108
+ | Set a font size | `fontSize` | Larger body text for a slide with little on it |
109
+ | Space paragraphs | `paragraphGap` | Air between two or three short paragraphs |
110
+ | Give a picture another area | `box` on the image slot | A wide diagram in place of a small sample chart |
111
+ | A native chart in place of a picture | a `chart` slot on the picture's shape | The chart slide with editable data |
112
+
113
+ Example: a text-only pattern derived from a photo pattern.
114
+
115
+ ```json
116
+ {
117
+ "id": "text-only",
118
+ "sourceSlide": 2,
119
+ "description": "A title, an optional supporting line and two or three short paragraphs, with no picture.",
120
+ "slots": [
121
+ { "name": "photo", "kind": "image", "imageKind": "graphic", "shapeId": "2", "empty": "drop" },
122
+ { "name": "title", "kind": "text", "shapeId": "5", "size": 34, "maxChars": 83, "box": { "x": 0.0375, "y": 0.2, "w": 0.925, "h": 0.187 } },
123
+ { "name": "body", "kind": "text", "shapeId": "7", "size": 18, "maxChars": 331, "maxParagraphs": 3, "fontSize": 20, "paragraphGap": 12, "box": { "x": 0.0375, "y": 0.4933, "w": 0.72, "h": 0.3867 } }
124
+ ]
125
+ }
126
+ ```
127
+
128
+ With `changeManifest` this is three operations (`copyPattern`, `addSlot` with role `decoration`, `setSlot` with
129
+ a `box`), and the budgets are worked out for you: see [template-studio.md](template-studio.md).
130
+
131
+ ## What is not possible
132
+
133
+ - Moving a shape to an arbitrary place from a deck. Positions come only from the template and the manifest.
134
+ - Swapping the side of a photo.
135
+ - A grid with more items than the template slide draws.
136
+ - New kinds of slides that the template has no sample for.
@@ -0,0 +1,208 @@
1
+ # Manifest format
2
+
3
+ A manifest is a JSON object of type `TemplateManifest`. It describes one template: which of its slides are
4
+ reusable patterns and which shapes on them are slots.
5
+
6
+ ```ts
7
+ import type { TemplateManifest, PatternDef, SlotDef, TextSlotDef, ImageSlotDef, TableSlotDef, ChartSlotDef, RepeatDef } from 'slotdeck';
8
+ ```
9
+
10
+ A manifest is written once per template. You can have a model draft it (`draftManifest`), change it with
11
+ operations (`changeManifest`) or by instruction (`editManifest`), and verify it (`checkManifest`): see
12
+ [template-studio.md](template-studio.md). This page is the reference for what the JSON means.
13
+
14
+ ## Example
15
+
16
+ ```json
17
+ {
18
+ "version": 1,
19
+ "name": "demo",
20
+ "language": "en",
21
+ "direction": "ltr",
22
+ "alignOnDirectionChange": "flip",
23
+ "patterns": [
24
+ {
25
+ "id": "cover",
26
+ "sourceSlide": 1,
27
+ "description": "Opening slide with the presentation's title and a subtitle.",
28
+ "slots": [
29
+ { "name": "title", "kind": "text", "shapeId": "5", "size": 50, "maxChars": 36 },
30
+ { "name": "subtitle", "kind": "text", "shapeId": "6", "size": 20, "maxChars": 86, "empty": "remove" },
31
+ { "name": "organizationDepartment", "kind": "text", "shapeId": "7", "size": 13, "maxChars": 61, "deckField": "organizationDepartment", "alignOnDirectionChange": "keep" }
32
+ ]
33
+ },
34
+ {
35
+ "id": "icon-cards",
36
+ "sourceSlide": 5,
37
+ "description": "A row of cards, each with an icon, a heading and a short text.",
38
+ "slots": [
39
+ { "name": "title", "kind": "text", "shapeId": "5", "size": 34, "maxChars": 41 },
40
+ { "name": "card1Icon", "kind": "image", "imageKind": "icon", "shapeId": "9" },
41
+ { "name": "card1Heading", "kind": "text", "shapeId": "10", "size": 19, "maxChars": 15 },
42
+ { "name": "card1Text", "kind": "text", "shapeId": "11", "size": 15, "maxChars": 117 },
43
+ { "name": "card2Icon", "kind": "image", "imageKind": "icon", "shapeId": "15" },
44
+ { "name": "card2Heading", "kind": "text", "shapeId": "16", "size": 19, "maxChars": 15 },
45
+ { "name": "card2Text", "kind": "text", "shapeId": "17", "size": 15, "maxChars": 117 }
46
+ ],
47
+ "repeat": {
48
+ "item": "card",
49
+ "slots": ["card{n}Icon", "card{n}Heading", "card{n}Text"],
50
+ "shapes": [["6"], ["12"]],
51
+ "min": 2,
52
+ "max": 4
53
+ }
54
+ }
55
+ ]
56
+ }
57
+ ```
58
+
59
+ ## `TemplateManifest`
60
+
61
+ | Field | Type | Required | Meaning |
62
+ | --- | --- | --- | --- |
63
+ | `version` | `1` | yes | Always 1. |
64
+ | `name` | string | yes | A name for the template. Used in messages. |
65
+ | `direction` | `"ltr"` or `"rtl"` | yes | The reading direction the template was designed for. |
66
+ | `patterns` | `PatternDef[]` | yes | The reusable layouts. |
67
+ | `language` | string | no | BCP 47 tag of the template's sample text. Decks default to it. |
68
+ | `alignOnDirectionChange` | `"flip"` or `"keep"` | no | What happens to left and right paragraph alignment in a deck that reads the other way. `flip` (default) mirrors it. `keep` leaves it as designed. A text slot can override this. |
69
+ | `textScale` | number | no | Enlarges the body text of every deck made from this template, as a factor on the designed size: `1.3` turns 9 pt into about 12 pt. For a template whose body text is too small to read comfortably. Budgets shrink to match. See "Larger text" in [layout-freedom.md](layout-freedom.md). |
70
+
71
+ ## `PatternDef`
72
+
73
+ | Field | Type | Required | Meaning |
74
+ | --- | --- | --- | --- |
75
+ | `id` | string | yes | What decks call the pattern. Unique in the manifest. Lower-case words joined by hyphens is the convention. |
76
+ | `sourceSlide` | integer | yes | Number of the template slide the pattern is cloned from, counted from 1. |
77
+ | `description` | string | yes | What the pattern is for. The planning model reads it to choose patterns, so write it for an author: what the slide holds and when to use it. |
78
+ | `slots` | `SlotDef[]` | yes | The shapes a deck fills. |
79
+ | `figures` | boolean | no | The pattern is built around numbers. The planner offers it only when the brief contains figures, so a model is not led to invent statistics. |
80
+ | `repeat` | `RepeatDef` | no | A row or grid of like items whose number a deck may choose. See below. |
81
+ | `sequences` | string[][][] | no | Rows of items with a fixed count, for mirroring in decks that read the other way. Each row lists its items first to last in the template's direction, and each item lists its slot names. Not needed for slots covered by `repeat`. |
82
+
83
+ ## Slots: properties every kind has
84
+
85
+ | Field | Type | Required | Meaning |
86
+ | --- | --- | --- | --- |
87
+ | `name` | string | yes | What decks call the slot. Unique within the pattern. Letters and digits, starting with a lower-case letter. |
88
+ | `kind` | `"text"`, `"image"`, `"table"` or `"chart"` | yes | What the slot holds. |
89
+ | `shapeId` | string | yes | The id of the shape on the source slide (the `id` attribute of the shape's `p:cNvPr` element, as a string). `inspectTemplate` and `studioView` list them. |
90
+ | `description` | string | no | Guidance for the planning model about this slot, such as "One line". |
91
+ | `empty` | `"error"`, `"remove"`, `"keep"` or `"drop"` | no | What happens without a value. Default `error` (required). See [concepts.md](concepts.md). |
92
+
93
+ Slots with `empty` of `keep` or `drop` are never offered to the planning model.
94
+
95
+ ## Text slot (`kind: "text"`)
96
+
97
+ | Field | Type | Meaning |
98
+ | --- | --- | --- |
99
+ | `maxChars` | integer | Soft limit on the total characters of the slot. Exceeding it gives a warning. |
100
+ | `maxParagraphs` | integer | Soft limit on the number of paragraphs. When it is more than 1, the planner asks the model for a list of paragraphs. |
101
+ | `deckField` | string | Take the value from `deck.fields[deckField]` when the slide does not set it. |
102
+ | `size` | number | Font size of the slot's text on the template slide, in points. Informational: it decides whether a deck's `textScale` applies (see `scales`). |
103
+ | `scales` | boolean | Whether a deck's `textScale` applies to this slot. Default: yes for text of 12 pt and under that is not a deck field. |
104
+ | `fontSize` | number | Sets the font size outright, in points. For a derived pattern that uses the shape differently. |
105
+ | `box` | `{ x, y, w, h }` | Moves and resizes the text box, as fractions of the slide (0 to 1). Only for a shape that is not inside a group. |
106
+ | `paragraphGap` | number | Space after each paragraph, in points. |
107
+ | `alignOnDirectionChange` | `"flip"` or `"keep"` | Overrides the manifest's setting for this slot. |
108
+ | `styleRun` | `"first"` or `"last"` | Which run of the template paragraph supplies the formatting, when the template paragraph mixes styles (a bold label followed by plain text). Default `first`. |
109
+
110
+ ## Image slot (`kind: "image"`)
111
+
112
+ | Field | Type | Meaning |
113
+ | --- | --- | --- |
114
+ | `imageKind` | `"photo"`, `"icon"` or `"graphic"` | Required. `photo`: a generated or supplied photo. `icon`: one of the app's icon set. `graphic`: a picture the app supplies, such as a diagram. |
115
+ | `fit` | `"cover"` or `"contain"` | `cover` crops the picture to fill the frame. `contain` shows the whole picture inside the frame's area. Default: `cover` for photos, `contain` for icons and graphics. |
116
+ | `box` | `{ x, y, w, h }` | The area the picture may use, when other than the sample picture's frame. Fractions of the slide. Only for a picture that is not inside a group. |
117
+
118
+ ## Table slot (`kind: "table"`)
119
+
120
+ The shape must be a table.
121
+
122
+ | Field | Type | Meaning |
123
+ | --- | --- | --- |
124
+ | `maxColumns` | integer | Soft limit on columns. |
125
+ | `maxRows` | integer | Soft limit on body rows (the header row is not counted). |
126
+ | `maxCellChars` | integer | Soft limit on the characters in one cell. |
127
+ | `headerText` | `{ size?, bold?, color?, font? }` | Formatting for header cells, where the sample table defines none or it should differ. `color` is hex without `#`. |
128
+ | `bodyText` | `{ size?, bold?, color?, font? }` | The same for body cells. |
129
+
130
+ ## Chart slot (`kind: "chart"`)
131
+
132
+ The shape is the sample picture of a chart. At render time it is replaced by a native chart in the same place.
133
+ The shape must not be inside a group.
134
+
135
+ | Field | Type | Meaning |
136
+ | --- | --- | --- |
137
+ | `box` | `{ x, y, w, h }` | The area the chart may use, when other than the sample picture's frame. |
138
+ | `maxCategories` | integer | Soft limit on categories. |
139
+ | `maxSeries` | integer | Soft limit on series. |
140
+ | `colors` | string[] | Series colours in order, hex without `#`. They repeat when there are more series. |
141
+ | `text` | `{ size?, bold?, color?, font? }` | Formatting of axis labels, data labels and the legend. |
142
+ | `lineColor` | string | Colour of gridlines and axis lines, hex without `#`. |
143
+
144
+ ## `RepeatDef`: a row or grid of items
145
+
146
+ ```json
147
+ "repeat": {
148
+ "item": "card",
149
+ "slots": ["card{n}Icon", "card{n}Heading", "card{n}Text"],
150
+ "shapes": [["6"], ["12"], ["18"], ["24"]],
151
+ "min": 2,
152
+ "max": 4,
153
+ "span": { "left": 0.04, "right": 0.96 },
154
+ "columns": 3
155
+ }
156
+ ```
157
+
158
+ | Field | Type | Required | Meaning |
159
+ | --- | --- | --- | --- |
160
+ | `item` | string | yes | What one item is called in prompts, such as `card` or `figure`. |
161
+ | `slots` | string[] | yes | Slot name templates of one item. `{n}` stands for the item number, from 1. For every item drawn on the slide the pattern must have a slot with each of these names (`card1Icon`, `card2Icon`, ...). |
162
+ | `shapes` | string[][] | yes | For each item drawn on the template slide, first in reading order first: the ids of the shapes that move together as that item. Usually the group around a card. A background shape that sits outside the group belongs in the list too. |
163
+ | `min`, `max` | integer | no | Fewest and most items a deck may ask for. Both default to the number drawn. |
164
+ | `span` | `{ left, right }` | no | Left and right limits of the row, as fractions of the slide width. Defaults to the row as drawn. Needed when `max` is more than the drawn items can fit in the drawn width. Only applies to shapes placed directly on the slide. |
165
+ | `columns` | integer | no | For a grid: how many items one line holds on the template slide. Leave it out for a single row. A grid cannot hold more items than it draws. |
166
+
167
+ How the items are laid out for a given count is described in [layout-freedom.md](layout-freedom.md).
168
+
169
+ Slots of copied items do not appear in the manifest. They are derived: item 5 of a row that draws 4 gets copies
170
+ of item 4's slots, named with the next number.
171
+
172
+ ## Rules a manifest must satisfy
173
+
174
+ `checkManifest(template)` reports violations of these:
175
+
176
+ - Every `sourceSlide` exists in the pptx.
177
+ - Every `shapeId` is a shape on that slide, and is the right kind of shape for the slot (a text slot points at
178
+ a shape with text, an image slot at a picture, a table slot at a table). A slot with `"empty": "drop"` may
179
+ point at any shape, because the shape is only ever removed.
180
+ - Pattern ids are unique. Slot names are unique within a pattern.
181
+ - A `box` is not set on a shape inside a group. A chart slot's shape is not inside a group.
182
+ - For a `repeat`: a slot exists for every name template and every drawn item; every shape id exists; all items
183
+ sit directly on the slide or all in the same group; `min` is at least 1 and not more than `max`.
184
+
185
+ It also warns about text slots without `maxChars` and about two slots that point at the same shape.
186
+
187
+ ## Finding shape ids
188
+
189
+ ```ts
190
+ import { inspectTemplate, PptxPackage } from 'slotdeck';
191
+
192
+ const info = inspectTemplate(PptxPackage.load(pptxBytes));
193
+ for (const shape of info.slides[4].shapes) {
194
+ // slide 5: index 4
195
+ console.log(shape.id, shape.kind, shape.box, shape.groupId ?? '', shape.paragraphs?.join(' / ') ?? '');
196
+ }
197
+ ```
198
+
199
+ `shape.box` is `{ x, y, w, h }` as fractions of the slide, already worked out through any groups. `shape.kind`
200
+ is `text`, `picture`, `shape` (a drawn shape without text), `line`, `group`, `table` or `other`.
201
+
202
+ ## Writing good descriptions
203
+
204
+ The planning model chooses a pattern by its `description` and its slots' names and limits. A description should
205
+ say what the slide holds and when to use it, in one or two sentences, without numbers that the deck may change:
206
+
207
+ - Good: "A row of cards, each with an icon, a heading and a short text: features, steps or categories."
208
+ - Poor: "Four cards." (the count can vary, and it does not say what the cards are for)