slotdeck 0.1.1 → 0.2.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 +94 -8
- package/dist/deck/alternatives.d.ts +34 -0
- package/dist/deck/alternatives.js +370 -0
- package/dist/deck/alternatives.js.map +1 -0
- package/dist/deck/design.d.ts +40 -0
- package/dist/deck/design.js +112 -0
- package/dist/deck/design.js.map +1 -0
- package/dist/deck/validate.d.ts +18 -0
- package/dist/deck/validate.js +44 -6
- package/dist/deck/validate.js.map +1 -1
- package/dist/index.d.ts +23 -1
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -1
- package/dist/llm/gemini.d.ts +4 -0
- package/dist/llm/gemini.js +43 -7
- package/dist/llm/gemini.js.map +1 -1
- package/dist/llm/provider.d.ts +12 -0
- package/dist/node.d.ts +7 -0
- package/dist/node.js +29 -0
- package/dist/node.js.map +1 -1
- package/dist/plan/catalog.d.ts +11 -1
- package/dist/plan/catalog.js +23 -8
- package/dist/plan/catalog.js.map +1 -1
- package/dist/plan/edit.js +14 -6
- package/dist/plan/edit.js.map +1 -1
- package/dist/plan/plan.d.ts +26 -2
- package/dist/plan/plan.js +58 -17
- package/dist/plan/plan.js.map +1 -1
- package/dist/plan/review.d.ts +43 -0
- package/dist/plan/review.js +141 -0
- package/dist/plan/review.js.map +1 -0
- package/dist/plan/write.d.ts +13 -1
- package/dist/plan/write.js +40 -7
- package/dist/plan/write.js.map +1 -1
- package/dist/preview/pagus.d.ts +30 -0
- package/dist/preview/pagus.js +45 -0
- package/dist/preview/pagus.js.map +1 -0
- package/dist/preview/patterns.d.ts +27 -0
- package/dist/preview/patterns.js +35 -0
- package/dist/preview/patterns.js.map +1 -0
- package/dist/preview/provider.d.ts +30 -0
- package/dist/preview/provider.js +11 -0
- package/dist/preview/provider.js.map +1 -0
- package/dist/render/render.d.ts +18 -1
- package/dist/render/render.js +32 -12
- package/dist/render/render.js.map +1 -1
- package/dist/render/repeat.d.ts +17 -2
- package/dist/render/repeat.js +150 -4
- package/dist/render/repeat.js.map +1 -1
- package/dist/render/text.d.ts +6 -0
- package/dist/render/text.js +24 -0
- package/dist/render/text.js.map +1 -1
- package/dist/template/budgets.d.ts +33 -0
- package/dist/template/budgets.js +38 -0
- package/dist/template/budgets.js.map +1 -0
- package/dist/template/change.d.ts +19 -1
- package/dist/template/change.js +45 -3
- package/dist/template/change.js.map +1 -1
- package/dist/template/check.d.ts +3 -0
- package/dist/template/check.js +53 -3
- package/dist/template/check.js.map +1 -1
- package/dist/template/derive.d.ts +54 -0
- package/dist/template/derive.js +402 -0
- package/dist/template/derive.js.map +1 -0
- package/dist/template/draft.d.ts +7 -0
- package/dist/template/draft.js +17 -1
- package/dist/template/draft.js.map +1 -1
- package/dist/template/instruct.js +15 -7
- package/dist/template/instruct.js.map +1 -1
- package/dist/template/manifest.d.ts +38 -0
- package/dist/template/manifest.js +43 -1
- package/dist/template/manifest.js.map +1 -1
- package/dist/template/sample.d.ts +17 -0
- package/dist/template/sample.js +31 -5
- package/dist/template/sample.js.map +1 -1
- package/dist/text/fontkit.d.ts +18 -0
- package/dist/text/fontkit.js +91 -0
- package/dist/text/fontkit.js.map +1 -0
- package/dist/text/fonts.d.ts +45 -0
- package/dist/text/fonts.js +59 -0
- package/dist/text/fonts.js.map +1 -0
- package/dist/text/layout.d.ts +12 -0
- package/dist/text/layout.js +104 -0
- package/dist/text/layout.js.map +1 -0
- package/dist/text/measure.d.ts +85 -0
- package/dist/text/measure.js +369 -0
- package/dist/text/measure.js.map +1 -0
- package/dist/text/styles.d.ts +123 -0
- package/dist/text/styles.js +417 -0
- package/dist/text/styles.js.map +1 -0
- package/dist/tools.d.ts +12 -0
- package/dist/tools.js +38 -6
- package/dist/tools.js.map +1 -1
- package/docs/README.md +16 -5
- package/docs/api-reference.md +291 -9
- package/docs/changelog.md +242 -0
- package/docs/chat-tools.md +26 -5
- package/docs/deck-format.md +6 -0
- package/docs/design-checks.md +216 -0
- package/docs/generating.md +43 -4
- package/docs/layout-freedom.md +278 -3
- package/docs/manifest-format.md +20 -3
- package/docs/preview.md +217 -0
- package/docs/providers.md +49 -4
- package/docs/rendering.md +8 -0
- package/docs/template-studio.md +121 -12
- package/docs/text-fit.md +456 -0
- package/docs/troubleshooting.md +21 -2
- package/package.json +7 -1
package/docs/providers.md
CHANGED
|
@@ -20,6 +20,8 @@ interface JsonRequest {
|
|
|
20
20
|
system: string; // the system prompt: the role and the rules
|
|
21
21
|
prompt: string; // the user message: the brief, the manifest's patterns, what to answer
|
|
22
22
|
schema: JsonSchema; // the JSON Schema the answer must follow
|
|
23
|
+
maxOutputTokens?: number; // a ceiling on the answer's size in tokens, reasoning not counted; see "Runaway answers"
|
|
24
|
+
effort?: 'low'; // set on short structural requests: use light reasoning if your model has a setting for it
|
|
23
25
|
images?: Array<{ mimeType: string; data: Uint8Array }>; // pictures to look at; only draftManifest sends them
|
|
24
26
|
signal?: AbortSignal; // pass it to your HTTP call so work can be cancelled
|
|
25
27
|
}
|
|
@@ -30,8 +32,8 @@ The contract:
|
|
|
30
32
|
- Resolve with the **parsed** value (an object), not a string of JSON.
|
|
31
33
|
- The value should follow `schema`. slotdeck checks what it gets back against the template's manifest and asks
|
|
32
34
|
again when content breaks a limit, but it expects the overall shape the schema describes.
|
|
33
|
-
- Reject (throw) when the call fails.
|
|
34
|
-
|
|
35
|
+
- Reject (throw) when the call fails. The planner does not retry failed calls; retries belong in the provider,
|
|
36
|
+
which knows which failures pass by themselves (see "Runaway answers" and the Gemini adapter below).
|
|
35
37
|
- One call is one request and one answer. There is no conversation state: every request carries everything it
|
|
36
38
|
needs.
|
|
37
39
|
|
|
@@ -106,7 +108,7 @@ Every top-level answer slotdeck asks for is a JSON object, so looking for the ou
|
|
|
106
108
|
|
|
107
109
|
### Choosing a model
|
|
108
110
|
|
|
109
|
-
- The requests are small. Measured on the demo template
|
|
111
|
+
- The requests are small. Measured on the demo template when it had 15 patterns: the outline request is a system prompt
|
|
110
112
|
of about 600 characters, a prompt of about 3,600 characters and a schema of about 700 characters. The writing
|
|
111
113
|
request for a 15-slide deck is a prompt of about 1,500 characters and a schema of about 9,800 characters. Any
|
|
112
114
|
current general-purpose model handles this.
|
|
@@ -117,6 +119,47 @@ Every top-level answer slotdeck asks for is a JSON object, so looking for the ou
|
|
|
117
119
|
- `draftManifest` can send a picture of each slide (`images`). A provider for a model that cannot see may ignore
|
|
118
120
|
them; the draft then relies on measurements alone.
|
|
119
121
|
|
|
122
|
+
### Runaway answers: always set an output limit
|
|
123
|
+
|
|
124
|
+
Now and then a model runs away inside one text field of its answer: it keeps adding sentence fragments to a
|
|
125
|
+
single string until something stops it. This was seen with a planning request whose proper answer is about 200
|
|
126
|
+
tokens: the model wrote more than 27,000 tokens into one field. Without a limit such a call takes minutes, costs
|
|
127
|
+
tens of thousands of output tokens, and ends in a timeout. It is a matter of chance: the same request usually
|
|
128
|
+
works the next time.
|
|
129
|
+
|
|
130
|
+
It was tied to reasoning. With the model's default reasoning, a request for a text-only plan ran away in 6 of
|
|
131
|
+
24 calls. With light reasoning it did not run away in 18 calls, and each took 4 seconds instead of about 35.
|
|
132
|
+
That is why planning requests carry `effort: 'low'`.
|
|
133
|
+
|
|
134
|
+
So a provider should do three things:
|
|
135
|
+
|
|
136
|
+
1. **Use light reasoning when a request says `effort: 'low'`**, if your model has such a setting.
|
|
137
|
+
2. **Limit the output of every call.** The planner passes `maxOutputTokens` with its requests: a generous
|
|
138
|
+
ceiling for that answer (for an outline of six slides, 1,360 tokens). Pass it to your model as its output
|
|
139
|
+
limit. If your model counts reasoning tokens against the same limit, add room for them. When a request
|
|
140
|
+
carries no estimate, use a default of your own rather than no limit.
|
|
141
|
+
3. **Ask once more when an answer is cut off at the limit.** A cut-off answer is not valid JSON. Retry the same
|
|
142
|
+
request once, and throw if it is cut off again.
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
async generateJson({ system, prompt, schema, maxOutputTokens, effort, signal }) {
|
|
146
|
+
const limit = (maxOutputTokens ?? 16_000) + 8_000; // room for reasoning, if your model counts it here
|
|
147
|
+
const reasoning = effort === 'low' ? 'low' : undefined; // whatever your model calls its light setting
|
|
148
|
+
for (let attempt = 1; attempt <= 2; attempt++) {
|
|
149
|
+
const answer = await callModel({ system, prompt, schema, limit, reasoning, signal });
|
|
150
|
+
if (!answer.cutOffAtLimit) return JSON.parse(answer.text);
|
|
151
|
+
}
|
|
152
|
+
throw new Error(`The model's answer was cut off at ${limit} tokens twice`);
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
The included Gemini adapter does all three. Its limit is the request's estimate plus 8,192 tokens for reasoning
|
|
157
|
+
(24,576 when there is no estimate, or its own `maxOutputTokens` option when set). It sends a cut-off request
|
|
158
|
+
once more. It uses thinking level `low` for requests with `effort: 'low'`, unless `thinkingLevel` is set on the
|
|
159
|
+
adapter, which then applies to every request. It also sends a request once more after a server error (500 to
|
|
160
|
+
504) or when the service's recitation filter blocks an ordinary answer, which happens at random; a bad key, a
|
|
161
|
+
quota or a bad request is not repeated.
|
|
162
|
+
|
|
120
163
|
### Timeouts and cancellation
|
|
121
164
|
|
|
122
165
|
slotdeck passes `signal` through when the caller gave one. Your provider should pass it to its HTTP call, and
|
|
@@ -203,7 +246,9 @@ Options of both:
|
|
|
203
246
|
| `fetch` | A custom `fetch`, for proxies and tests. |
|
|
204
247
|
| `timeoutMs` | Gives up on a request after this long. Default 180000 (3 minutes). `0` waits for ever. |
|
|
205
248
|
|
|
206
|
-
Only for `gemini`: `thinkingLevel` (`minimal`, `low`, `medium`, `high`) and `maxOutputTokens
|
|
249
|
+
Only for `gemini`: `thinkingLevel` (`minimal`, `low`, `medium`, `high`) and `maxOutputTokens`, the output limit
|
|
250
|
+
of a request with reasoning included. By default the limit is the request's own estimate plus 8,192 tokens, or
|
|
251
|
+
24,576 when the request has none.
|
|
207
252
|
|
|
208
253
|
Only for `geminiImages`: `imageSize` (`512`, `1K`, `2K`, `4K`; default `1K`) and `style`, a sentence appended to
|
|
209
254
|
every prompt, such as a house photo style.
|
package/docs/rendering.md
CHANGED
|
@@ -20,6 +20,12 @@ is fast: a 15-slide deck from the demo template takes about 100 ms.
|
|
|
20
20
|
It does not verify the manifest. Call `checkManifest(template)` when a template or manifest is loaded if you
|
|
21
21
|
want to catch a manifest that no longer matches its file (see [template-studio.md](template-studio.md)).
|
|
22
22
|
|
|
23
|
+
To get a picture of each slide of the result without PowerPoint, see [preview.md](preview.md).
|
|
24
|
+
|
|
25
|
+
A third argument, `{ fonts }`, gives the template the metrics of its fonts. Text is then measured as it will
|
|
26
|
+
be set: warnings say how many lines a text runs over, and text can be shrunk to fit. See
|
|
27
|
+
[text-fit.md](text-fit.md).
|
|
28
|
+
|
|
23
29
|
## `renderDeck(template, deck, options?)`
|
|
24
30
|
|
|
25
31
|
```ts
|
|
@@ -33,6 +39,7 @@ interface RenderOptions {
|
|
|
33
39
|
interface RenderResult {
|
|
34
40
|
pptx: Uint8Array;
|
|
35
41
|
warnings: Issue[]; // non-blocking findings, such as text over a slot's budget
|
|
42
|
+
shrunk: Array<{ slide: number; slot: string; size: number; scale: number }>; // see text-fit.md; empty unless the manifest has shrinkText
|
|
36
43
|
}
|
|
37
44
|
```
|
|
38
45
|
|
|
@@ -97,6 +104,7 @@ function validateDeck(manifest: TemplateManifest, deck: DeckSpec, options?: Vali
|
|
|
97
104
|
interface ValidateOptions {
|
|
98
105
|
layout?: 'strict' | 'adaptive';
|
|
99
106
|
assets?: Iterable<string>; // ids that will be available at render time; when given, image references are checked
|
|
107
|
+
measure?: TextMeasurer; // measures text with the template's fonts: see text-fit.md
|
|
100
108
|
}
|
|
101
109
|
|
|
102
110
|
interface Issue {
|
package/docs/template-studio.md
CHANGED
|
@@ -5,10 +5,10 @@ manifest with a model, checking it, seeing whether its budgets hold, and changin
|
|
|
5
5
|
|
|
6
6
|
```ts
|
|
7
7
|
import {
|
|
8
|
-
draftManifest, checkManifest, sampleDeck, changeManifest, editManifest,
|
|
8
|
+
draftManifest, checkManifest, sampleDeck, changeManifest, editManifest, patternVariants, VARIANTS,
|
|
9
9
|
studioView, describeManifest, describeSlot, inspectTemplate, ROLES, PptxPackage, loadTemplate, renderDeck,
|
|
10
10
|
} from 'slotdeck';
|
|
11
|
-
import type { ManifestChange, SlotPatch, ChangeResult, ManifestIssue, Role, StudioView } from 'slotdeck';
|
|
11
|
+
import type { ManifestChange, SlotPatch, ChangeResult, ManifestIssue, PatternVariant, Role, StudioView, VariantOption } from 'slotdeck';
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
## The workflow
|
|
@@ -21,8 +21,8 @@ import type { ManifestChange, SlotPatch, ChangeResult, ManifestIssue, Role, Stud
|
|
|
21
21
|
5. Repeat 2 to 4 until the sample looks right. Store the manifest as JSON next to the pptx.
|
|
22
22
|
|
|
23
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,
|
|
25
|
-
chart colours, and alignment choices for decks that read the other way.
|
|
24
|
+
adds: budgets tuned by eye, derived patterns (a mirrored or text-only variant, a native chart), wider rows,
|
|
25
|
+
picture areas, chart colours, and alignment choices for decks that read the other way.
|
|
26
26
|
|
|
27
27
|
## `draftManifest(pptx, options)`
|
|
28
28
|
|
|
@@ -32,6 +32,7 @@ const { manifest, skipped, issues } = await draftManifest(pptxBytes, {
|
|
|
32
32
|
name: 'marketing', // optional name for the manifest; default 'template'
|
|
33
33
|
slideImage: (slide) => thumbnails.get(slide), // optional: { mimeType, data } of a picture of slide N (1-based); may be async
|
|
34
34
|
slides: [1, 2, 5], // optional: only these slides; default all
|
|
35
|
+
variants: ['mirror', 'text-only'], // optional: also add these variants of every pattern that allows them; default none
|
|
35
36
|
concurrency: 4, // optional: slides sent to the model at once; default 4
|
|
36
37
|
onProgress: ({ done, total, slide }) => {}, // optional
|
|
37
38
|
signal, // optional
|
|
@@ -71,6 +72,18 @@ Any PNG or JPEG of the slide works; 480 pixels wide is enough.
|
|
|
71
72
|
To redo one slide after changing the pptx, call `draftManifest` with `slides: [n]` and add the pattern it
|
|
72
73
|
returns to your manifest.
|
|
73
74
|
|
|
75
|
+
### Variants in a draft
|
|
76
|
+
|
|
77
|
+
With `variants`, the draft also gets the mirror image and the text-only version of every drafted pattern whose
|
|
78
|
+
slide allows one (see `derivePattern` below). They are worked out by code after the model has labelled the
|
|
79
|
+
slides, so they cost no model call. A pattern that allows no variant simply gets none.
|
|
80
|
+
|
|
81
|
+
This is off by default, for three reasons. Every variant is one more layout to review in the sample deck.
|
|
82
|
+
Whether a mirrored slide suits a design is for a person to say. And a variant is only as good as the labels it
|
|
83
|
+
is derived from, which a draft has not had reviewed yet. The usual order is therefore: draft, review, then add
|
|
84
|
+
the variants you want with `derivePattern`. Use `variants` when a studio should show everything a template can
|
|
85
|
+
do in one pass. The `without` variant is never added by a draft, because it needs a slot to be named.
|
|
86
|
+
|
|
74
87
|
### Roles
|
|
75
88
|
|
|
76
89
|
`ROLES` lists them. A role says what a shape is for and decides the kind of slot it becomes.
|
|
@@ -106,6 +119,12 @@ kind of shape, names are unique, rows are consistent. The rules are listed in
|
|
|
106
119
|
[manifest-format.md](manifest-format.md). Run it when a template is loaded, so a template that someone changed
|
|
107
120
|
shows up there and not in the middle of a render.
|
|
108
121
|
|
|
122
|
+
For a template loaded with fonts it also tries every text budget out and warns about one that lets text run
|
|
123
|
+
over its box. A row with `widen` gives its texts a larger budget when a slide has fewer items; that budget is
|
|
124
|
+
tried in the wider box, for each number of items that widens the row, and a warning then says which
|
|
125
|
+
(`on a slide with 2 of its 4 items`). See "The studio: checking and setting budgets" in
|
|
126
|
+
[text-fit.md](text-fit.md).
|
|
127
|
+
|
|
109
128
|
## `sampleDeck(template, options?)`
|
|
110
129
|
|
|
111
130
|
```ts
|
|
@@ -120,6 +139,10 @@ filler words and plain coloured rectangles for pictures.
|
|
|
120
139
|
| --- | --- | --- |
|
|
121
140
|
| `fill` | `0.95` | How full to make each text slot, as a share of its budget. |
|
|
122
141
|
| `items` | `'drawn'` | For patterns with a row or grid: `'drawn'` uses the template's number, `'max'` the most allowed, `'min'` the fewest. |
|
|
142
|
+
| `fit` | `'budget'` | What "full" means for a text slot: `'budget'` is its character budget; `'box'` is what its box holds as measured with the template's fonts (see [text-fit.md](text-fit.md)). |
|
|
143
|
+
|
|
144
|
+
To look at the sample deck without opening PowerPoint, draw it with a preview provider:
|
|
145
|
+
`previewDeck(template, deck, preview, { assets, layout: 'adaptive' })`. See [preview.md](preview.md).
|
|
123
146
|
|
|
124
147
|
## `changeManifest(template, changes)`
|
|
125
148
|
|
|
@@ -145,9 +168,12 @@ Every change is an object with `op`. `pattern` is a pattern id, `slot` a slot na
|
|
|
145
168
|
#### `setManifest`
|
|
146
169
|
|
|
147
170
|
```ts
|
|
148
|
-
{ op: 'setManifest', name?: string, language?: string, direction?: 'ltr' | 'rtl', alignOnDirectionChange?: 'flip' | 'keep' }
|
|
171
|
+
{ op: 'setManifest', name?: string, language?: string, direction?: 'ltr' | 'rtl', alignOnDirectionChange?: 'flip' | 'keep', textScale?: number | null }
|
|
149
172
|
```
|
|
150
173
|
|
|
174
|
+
`textScale` enlarges the body text of every deck made from the template (`1.3` for 30% larger); `null` or `1`
|
|
175
|
+
returns to the designed size.
|
|
176
|
+
|
|
151
177
|
#### `addPattern`
|
|
152
178
|
|
|
153
179
|
```ts
|
|
@@ -164,7 +190,38 @@ instead to get a complete pattern.
|
|
|
164
190
|
{ op: 'copyPattern', pattern: string, id: string, description?: string }
|
|
165
191
|
```
|
|
166
192
|
|
|
167
|
-
A second pattern from the same slide, to turn into a variant with further operations.
|
|
193
|
+
A second pattern from the same slide, to turn into a variant by hand with further operations. For the mirror
|
|
194
|
+
image, a text-only version or a version without an optional line, use `derivePattern`, which works out the
|
|
195
|
+
positions.
|
|
196
|
+
|
|
197
|
+
#### `derivePattern`
|
|
198
|
+
|
|
199
|
+
```ts
|
|
200
|
+
{ op: 'derivePattern', pattern: string, variant: 'mirror' | 'text-only' | 'without', slot?: string, id?: string, description?: string }
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
A second pattern from the same slide, laid out by the code. You name the variant; every position and budget is
|
|
204
|
+
worked out from the shapes of the slide. `VARIANTS` lists the variant names.
|
|
205
|
+
|
|
206
|
+
| Variant | What it makes | Default id |
|
|
207
|
+
| --- | --- | --- |
|
|
208
|
+
| `mirror` | The picture and what sits beside it change sides. | `<pattern>-mirrored` |
|
|
209
|
+
| `text-only` | The picture is left out, and the text beside it widens over the room it leaves. | `<pattern>-text-only` |
|
|
210
|
+
| `without` | The optional text named in `slot` is left out, and what sits under it moves up. | `<pattern>-no-<slot>` |
|
|
211
|
+
|
|
212
|
+
- `slot` is needed for `without` only, and must name an optional text slot (`"empty": "remove"`).
|
|
213
|
+
- `id` and `description` replace the defaults. The default description is the source's with a sentence added,
|
|
214
|
+
such as `Mirrored: here the picture is on the left and the text on the right. Alternate it with "photo-stat" for variety.`
|
|
215
|
+
Write your own when the source's description names a side ("a photo on the right"), so that the planner
|
|
216
|
+
reads one clear statement.
|
|
217
|
+
- The new pattern is placed right after its source. The source is not changed.
|
|
218
|
+
- A variant can be derived from a variant: the mirror image, then that without its subtitle.
|
|
219
|
+
- A variant the slide does not allow is refused with the reason, such as
|
|
220
|
+
`"cover" has no photo, graphic or chart of its own, so there is nothing to change sides with`.
|
|
221
|
+
|
|
222
|
+
How each variant is worked out, what it leaves alone and when it is refused is described under "Variants the
|
|
223
|
+
code works out" in [layout-freedom.md](layout-freedom.md). `patternVariants` (below) says in advance which
|
|
224
|
+
variants a pattern allows.
|
|
168
225
|
|
|
169
226
|
#### `removePattern`
|
|
170
227
|
|
|
@@ -231,13 +288,18 @@ A property that does not apply to the slot's kind is refused (`"title" is a text
|
|
|
231
288
|
#### `setRepeat`
|
|
232
289
|
|
|
233
290
|
```ts
|
|
234
|
-
{ op: 'setRepeat', pattern: string, min?: number, max?: number, item?: string, span?: { left: number, right: number } | null }
|
|
291
|
+
{ op: 'setRepeat', pattern: string, min?: number, max?: number, item?: string, span?: { left: number, right: number } | null, widen?: number | null }
|
|
235
292
|
```
|
|
236
293
|
|
|
237
294
|
Sets how many items a row or grid takes. When the pattern has no row yet, it is found from numbered slot names:
|
|
238
295
|
name the slots of each item alike with a number (`card1Heading`, `card1Text`, `card2Heading`, `card2Text`) and
|
|
239
296
|
they become a row.
|
|
240
297
|
|
|
298
|
+
`widen` lets the items grow wider when a line holds fewer of them than the template draws, so that two or
|
|
299
|
+
three cards fill a row drawn for four. It is the most an item may grow, as a factor on its drawn width,
|
|
300
|
+
between 1 and 4; `null` or `1` turns it off. See "Fewer items that fill the row" in
|
|
301
|
+
[layout-freedom.md](layout-freedom.md). Render `sampleDeck(template, { items: 'min' })` to look at the result.
|
|
302
|
+
|
|
241
303
|
#### `removeRepeat`
|
|
242
304
|
|
|
243
305
|
```ts
|
|
@@ -256,6 +318,8 @@ The items stop being a row whose number can change.
|
|
|
256
318
|
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`.
|
|
257
319
|
- **Row items stay alike.** A `setSlot` on one item's slot (`card1Icon`) is made to that slot of every item.
|
|
258
320
|
Slots of a row cannot be renamed, moved or removed one by one; remove the row first.
|
|
321
|
+
- **Variants are laid out.** `derivePattern` moves and resizes the shapes of a variant by itself, measures
|
|
322
|
+
the budgets of every text that changed size, and writes a description the planner can choose the variant by.
|
|
259
323
|
- **Nothing is left broken.** After each change the manifest is checked against the pptx. A change that
|
|
260
324
|
introduces a new error is refused.
|
|
261
325
|
|
|
@@ -280,22 +344,47 @@ A native chart in place of a sample chart picture, as a second pattern:
|
|
|
280
344
|
]
|
|
281
345
|
```
|
|
282
346
|
|
|
283
|
-
|
|
347
|
+
The mirror image of a slide with a photo, for variety in a deck with several such slides:
|
|
348
|
+
|
|
349
|
+
```ts
|
|
350
|
+
{ op: 'derivePattern', pattern: 'photo-stat', variant: 'mirror', description: 'One key figure with a title and a short explanation on the left, and a photo on the right.' }
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
A text-only slide from a slide with a photo, then larger text with room for three paragraphs (the budget
|
|
354
|
+
follows the new size):
|
|
284
355
|
|
|
285
356
|
```ts
|
|
286
357
|
[
|
|
287
|
-
{ op: '
|
|
288
|
-
{ op: '
|
|
289
|
-
{ op: 'setSlot', pattern: 'text-only', slot: 'body', box: { x: 0.04, y: 0.49, w: 0.72, h: 0.39 }, fontSize: 20, maxParagraphs: 3 },
|
|
358
|
+
{ op: 'derivePattern', pattern: 'text-and-photo', variant: 'text-only', id: 'text-only' },
|
|
359
|
+
{ op: 'setSlot', pattern: 'text-only', slot: 'body', fontSize: 20, maxParagraphs: 3, paragraphGap: 12 },
|
|
290
360
|
]
|
|
291
361
|
```
|
|
292
362
|
|
|
363
|
+
The same slide for decks that have no subtitle, with the body moved up into its place:
|
|
364
|
+
|
|
365
|
+
```ts
|
|
366
|
+
{ op: 'derivePattern', pattern: 'text-and-photo', variant: 'without', slot: 'subtitle' }
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
Two or three cards that fill a row drawn for four, each holding more text:
|
|
370
|
+
|
|
371
|
+
```ts
|
|
372
|
+
{ op: 'setRepeat', pattern: 'icon-cards', widen: 2.2 }
|
|
373
|
+
```
|
|
374
|
+
|
|
293
375
|
Smaller table text and fewer columns (the cell budget is measured for you):
|
|
294
376
|
|
|
295
377
|
```ts
|
|
296
378
|
{ op: 'setSlot', pattern: 'table', slot: 'table', maxColumns: 5, bodyText: { size: 9 }, headerText: { size: 10, bold: true } }
|
|
297
379
|
```
|
|
298
380
|
|
|
381
|
+
Larger body text in every deck, for a template whose own body text is small (render the sample deck afterwards
|
|
382
|
+
to see that everything still fits):
|
|
383
|
+
|
|
384
|
+
```ts
|
|
385
|
+
{ op: 'setManifest', textScale: 1.3 }
|
|
386
|
+
```
|
|
387
|
+
|
|
299
388
|
Mirror alignment for right-to-left decks, but keep the line beside the logo where it is:
|
|
300
389
|
|
|
301
390
|
```ts
|
|
@@ -319,7 +408,10 @@ by hand. One model call, and a second when some operations were refused, so the
|
|
|
319
408
|
|
|
320
409
|
- Instructions can be in any language.
|
|
321
410
|
- A person may refer to a slide by number or to a pattern by id.
|
|
322
|
-
- The model cannot set positions: `box` and `span` exist only for changes made by hand.
|
|
411
|
+
- The model cannot set positions: `box` and `span` exist only for changes made by hand. It can ask for a
|
|
412
|
+
variant by name ("add a mirrored version of the photo slides", "a version of slide 2 without the picture"),
|
|
413
|
+
which becomes a `derivePattern` operation: the model names the variant, and the code works out where
|
|
414
|
+
everything goes.
|
|
323
415
|
- An operation the model got wrong shows up in `rejected` with the reason; nothing of it is applied.
|
|
324
416
|
|
|
325
417
|
`describeManifest(template)` returns the text the model reads: every pattern with its slots and limits, and the
|
|
@@ -352,6 +444,23 @@ that are slots, and on a click offers the shape's `roles` (sending `addSlot`) or
|
|
|
352
444
|
|
|
353
445
|
`describeSlot(slot)` gives a slot in a few words, such as `text, up to 40 characters, required`.
|
|
354
446
|
|
|
447
|
+
## `patternVariants(template, pattern)`
|
|
448
|
+
|
|
449
|
+
```ts
|
|
450
|
+
const options = patternVariants(template, 'text-and-photo');
|
|
451
|
+
// [
|
|
452
|
+
// { variant: 'mirror', available: true, note: 'the mirror image of "text-and-photo", with the picture on the left and what sat beside it on the right. ...' },
|
|
453
|
+
// { variant: 'text-only', available: true, note: '"text-and-photo" without its picture "photo". Widened over the freed area, with budgets to match: ...' },
|
|
454
|
+
// { variant: 'without', slot: 'subtitle', available: true, note: '"text-and-photo" without "subtitle": body moves up and has more room (431 characters).' },
|
|
455
|
+
// ]
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
No model, and nothing is changed. It lists the variants `derivePattern` could make from a pattern: the mirror
|
|
459
|
+
image, the text-only version, and one `without` for each optional text slot. An entry with `available: false`
|
|
460
|
+
carries the reason in `note`; an available one says there what the variant would do. A studio screen can show
|
|
461
|
+
the available ones as buttons that send `{ op: 'derivePattern', pattern, variant, slot }`. It throws when the
|
|
462
|
+
template has no pattern of that id.
|
|
463
|
+
|
|
355
464
|
## `inspectTemplate(pkg)`
|
|
356
465
|
|
|
357
466
|
```ts
|