@jokerized/decksmith 0.1.4 → 0.3.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 +161 -26
- package/dist/cli.js +3864 -1816
- package/dist/ds-morph.js +1 -0
- package/dist/index.js +2701 -780
- package/dist/mcp.js +2509 -633
- package/dist/types/emit/archetypes/annotated-figure.d.ts +3 -2
- package/dist/types/emit/archetypes/data-table.d.ts +0 -19
- package/dist/types/emit/archetypes/equation-morph.d.ts +2 -0
- package/dist/types/emit/archetypes/equation-walk.d.ts +63 -1
- package/dist/types/emit/archetypes/index.d.ts +1 -1
- package/dist/types/emit/archetypes/pipeline.d.ts +2 -2
- package/dist/types/emit/archetypes/stack.d.ts +20 -1
- package/dist/types/emit/archetypes/title.d.ts +31 -14
- package/dist/types/emit/camera.d.ts +25 -0
- package/dist/types/emit/composition.d.ts +20 -2
- package/dist/types/emit/depth.d.ts +95 -0
- package/dist/types/emit/kit.d.ts +153 -0
- package/dist/types/emit/morph-runtime.d.ts +178 -0
- package/dist/types/emit/svg.d.ts +77 -13
- package/dist/types/emit/theme.d.ts +1 -0
- package/dist/types/images/illustrate.d.ts +34 -0
- package/dist/types/images/providers.d.ts +108 -0
- package/dist/types/index.d.ts +27 -8
- package/dist/types/mcp/prereqs.d.ts +18 -0
- package/dist/types/mcp/tools.d.ts +23 -0
- package/dist/types/plan/codex.d.ts +25 -3
- package/dist/types/plan/duration.d.ts +190 -15
- package/dist/types/plan/prompt.d.ts +7 -1
- package/dist/types/plan/refs.d.ts +41 -3
- package/dist/types/prefs.d.ts +21 -6
- package/dist/types/render/capture.d.ts +99 -0
- package/dist/types/render/render.d.ts +18 -0
- package/dist/types/server/options.d.ts +7 -0
- package/dist/types/server/pipeline.d.ts +14 -0
- package/dist/types/server/queue.d.ts +1 -1
- package/dist/types/types.d.ts +294 -4
- package/dist/types/verify/apparent.d.ts +123 -0
- package/dist/types/verify/index.d.ts +114 -6
- package/dist/types/verify/typefloor.d.ts +3 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ storyboard rendered to a 9:16 short or an MP4 without replanning.
|
|
|
6
6
|
|
|
7
7
|
Rendering is bought, not built. A deck is a
|
|
8
8
|
[HyperFrames](https://github.com/heygen-com/hyperframes) composition (Apache-2.0, pinned
|
|
9
|
-
at 0.
|
|
9
|
+
at 0.8.27), so the animation runtime, the headless capture and the FFmpeg encode are
|
|
10
10
|
upstream's. What DeckSmith owns is the part nothing else does well: turning a document
|
|
11
11
|
into a *good explanation*, and being able to check that the explanation is true to its
|
|
12
12
|
source.
|
|
@@ -20,8 +20,9 @@ source.
|
|
|
20
20
|
- A Chromium build and ffmpeg to **render or check** one. `verify` drives the HyperFrames
|
|
21
21
|
gates in a headless browser, and the MP4 encode is ffmpeg's. Both arrive with the
|
|
22
22
|
hyperframes toolchain during `npm install`; there is nothing to install by hand.
|
|
23
|
-
-
|
|
24
|
-
|
|
23
|
+
- Three commands reach outside for their own reasons: `plan` runs the Codex CLI, `narrate`
|
|
24
|
+
runs edge-tts over the network, and `illustrate` asks an image backend or that same Codex
|
|
25
|
+
account for pictures — and draws its own when neither will.
|
|
25
26
|
|
|
26
27
|
```sh
|
|
27
28
|
npm install
|
|
@@ -37,9 +38,11 @@ hand-edit everything in between.
|
|
|
37
38
|
```sh
|
|
38
39
|
decksmith ingest analysis.md -o source.json # structure, figures, equations, provenance
|
|
39
40
|
decksmith plan source.json -o storyboard.json # beats — this is the one to read
|
|
41
|
+
decksmith illustrate storyboard.json --source source.json # optional, after plan --images
|
|
40
42
|
decksmith narrate storyboard.json --source source.json -o audio/ # optional
|
|
41
43
|
decksmith build storyboard.json --source source.json --format deck-16x9 -o out/
|
|
42
44
|
decksmith verify out/
|
|
45
|
+
decksmith frames out/ # PNGs of the holds, to look at
|
|
43
46
|
decksmith render out/ -o talk.mp4 # picture + narration + subtitles
|
|
44
47
|
decksmith drift out/ # render twice, compare frame by frame
|
|
45
48
|
|
|
@@ -62,15 +65,31 @@ decksmith unpack talk.deck -o reopened/
|
|
|
62
65
|
|
|
63
66
|
You can also skip it entirely. `storyboard.json` is just a file: hand-write it, or have
|
|
64
67
|
any assistant write it, and `build` cannot tell the difference.
|
|
68
|
+
- **illustrate** — the pictures the plan asked for. With `plan --images`, a beat that has
|
|
69
|
+
no figure to show may carry a brief instead of a `figureId`; this turns each brief into
|
|
70
|
+
a file under `assets/`, registers it as an ordinary figure in `source.json`, and points
|
|
71
|
+
the beat at it. Optional, and nothing downstream knows the picture was generated. See
|
|
72
|
+
"Illustrations" below.
|
|
65
73
|
- **narrate** — each beat's `narration` text, spoken by edge-tts, one audio file and one
|
|
66
|
-
set of subtitle cues per *stop*. Optional, and
|
|
67
|
-
See "Narration" below.
|
|
74
|
+
set of subtitle cues per *stop*. Optional, and needs the network. See "Narration" below.
|
|
68
75
|
- **build** — `Storyboard` to a composition, per format profile. Beats become scenes,
|
|
69
76
|
scenes become their own paused GSAP timelines, hold points become slideshow fragments.
|
|
70
77
|
Narration sitting beside the storyboard is picked up automatically.
|
|
71
78
|
- **verify** — runs the HyperFrames gates over a built directory and returns a `Verdict`:
|
|
72
79
|
lint, runtime, layout, motion and contrast. Read what that does *not* cover before
|
|
73
80
|
trusting it — see "What the gates do not check" below.
|
|
81
|
+
- **frames** — writes a PNG per hold (or per `--at <seconds>`) so you can look at the deck
|
|
82
|
+
rather than at a verdict. It makes the same three calls the `fidelity` gate makes — the
|
|
83
|
+
pinned runtime, `renderSeek(t, { suppressEvents: true })`, then a screenshot at the
|
|
84
|
+
renderer's own clip — which is the path that was measured against a real mp4 to within
|
|
85
|
+
0.11 percentage points of ink. It is the gate's own view of a deck, which is the thing
|
|
86
|
+
worth looking at when you are asking why a gate said what it said.
|
|
87
|
+
**It is not a substitute for watching the render.** On callback-driven motion the three
|
|
88
|
+
available views disagree, measured on a deck with a band painted only from a GSAP
|
|
89
|
+
`onUpdate`: `frames` leaves it at the background's RGB (11,13,17) because
|
|
90
|
+
`suppressEvents` stops the callback; `hyperframes snapshot` draws it mid-tween at
|
|
91
|
+
(143,4,5); and the render animates it smoothly to (205,0,0) over the tween's own three
|
|
92
|
+
seconds. See "What the gates do not check".
|
|
74
93
|
- **render** — a built deck to a finished video: capture, retime, mux, subtitles. The
|
|
75
94
|
retiming is the interesting part. The composition reveals everything a beat has to show
|
|
76
95
|
and then sits still for the rest of its window, so playing it back linearly puts the
|
|
@@ -79,7 +98,9 @@ decksmith unpack talk.deck -o reopened/
|
|
|
79
98
|
whatever the scene actually ends on. Needs Chromium and ffmpeg, and needs the
|
|
80
99
|
`timing.json` that `build` writes.
|
|
81
100
|
- **drift** — renders the deck twice and compares every frame. `--identical` fails on any
|
|
82
|
-
differing byte, which is only honest for an image-free deck with no camera
|
|
101
|
+
differing byte, which is only honest for an image-free deck with no camera — the demo
|
|
102
|
+
deck is not one, and fails it on both hyperframes pins (see
|
|
103
|
+
`.planning/2026-09-04-hyperframes-0.8.27.md`);
|
|
83
104
|
the default compares PSNR against a 40 dB floor. Costs two full renders, so it is a
|
|
84
105
|
thing you schedule rather than a thing you run per build. See
|
|
85
106
|
`.planning/EXPERIMENT-006-diagrams.md` for why byte-identical is not available in
|
|
@@ -94,7 +115,9 @@ out/
|
|
|
94
115
|
index.html the composition — what check, snapshot and render consume
|
|
95
116
|
deck.html open this: the navigable deck
|
|
96
117
|
hyperframes-player.global.js copied from the hyperframes package, so nothing needs a CDN
|
|
97
|
-
hyperframes.json
|
|
118
|
+
hyperframes.json
|
|
119
|
+
assets/ every figure a beat cites — generated ones included, under
|
|
120
|
+
the same name source.json records
|
|
98
121
|
audio/ only when the deck is narrated
|
|
99
122
|
```
|
|
100
123
|
|
|
@@ -116,7 +139,8 @@ exist only on the deck page: ambient motion is gated behind a class the composit
|
|
|
116
139
|
sets, so `render` output is byte-identical with or without it.
|
|
117
140
|
|
|
118
141
|
Navigation is ours rather than upstream's because upstream's is broken at 0.7.71/0.7.72
|
|
119
|
-
— `player.scenes` never populates, reproduced on HeyGen's own
|
|
142
|
+
and still broken at 0.8.27 — `player.scenes` never populates, reproduced on HeyGen's own
|
|
143
|
+
reference example, and at 0.8.27 the property is not on the player at all. The
|
|
120
144
|
same layer also paints the composition, because the standalone player moves its clock
|
|
121
145
|
without driving scene timelines or clip visibility. See
|
|
122
146
|
`.planning/EXPERIMENT-003-deck-mode.md` and `-004-step-layer.md`.
|
|
@@ -172,18 +196,19 @@ cut runs 4m07s.
|
|
|
172
196
|
```sh
|
|
173
197
|
decksmith build storyboard.json --source source.json --format short-9x16 -o short/
|
|
174
198
|
# build: 9 of 12 beats at 1080×1920 in ink → short/index.html
|
|
175
|
-
# build: cut
|
|
176
|
-
#
|
|
199
|
+
# build: cut b04 (grid, 19.7s) — Cut to fit short-9x16's 3m00s: 19.7s for weight 0.85
|
|
200
|
+
# is 0.043 weight per second, and the beats kept buy more per second.
|
|
177
201
|
# Its family (structure) still has 3 beat(s) in the cut.
|
|
178
202
|
# build: cut b06 (stack, 24.7s) — …
|
|
179
|
-
# build: cut
|
|
203
|
+
# build: cut b09 (data-table, 25.4s) — …
|
|
180
204
|
# PASS — 0 error(s), 2 warning(s)
|
|
181
205
|
```
|
|
182
206
|
|
|
183
207
|
The rule is `selectBeats` (`src/plan/select.ts`), and it is not "drop the lightest". In
|
|
184
208
|
priority order it keeps a cut **coherent**, then **covered** — the deck's first and last
|
|
185
|
-
beat,
|
|
186
|
-
|
|
209
|
+
beat, one beat of every archetype family the full deck used, and one beat carrying a
|
|
210
|
+
figure of the source's, because a deck that cuts its way to no picture at all cannot get
|
|
211
|
+
one back by rewording — then **fitting**, and only then heaviest. Author weight is the last tiebreak, because weight says how much a
|
|
187
212
|
beat matters and nothing about what it costs: two 14-second beats at 0.80 are worth more
|
|
188
213
|
to a three-minute budget than one 39-second beat at 0.95, and a threshold cannot say so.
|
|
189
214
|
|
|
@@ -238,8 +263,13 @@ decksmith: http://127.0.0.1:8475
|
|
|
238
263
|
decksmith: work /tmp/decksmith-server, one job at a time, 8 may wait
|
|
239
264
|
decksmith: no auth, no TLS. Bound to 127.0.0.1 — set DECKSMITH_HOST to open it, knowing that.
|
|
240
265
|
decksmith: codex, edge-tts and ffmpeg all found
|
|
266
|
+
decksmith: images via codex, then svg
|
|
241
267
|
```
|
|
242
268
|
|
|
269
|
+
The last line is where `illustrate` would get its pictures — `via openai` when a backend
|
|
270
|
+
is configured, or the reason it cannot be used. Reported, never fatal: a job that asks
|
|
271
|
+
for pictures still finishes, on the tool's own SVG if it has to.
|
|
272
|
+
|
|
243
273
|
### The HTTP surface
|
|
244
274
|
|
|
245
275
|
| Route | What it does |
|
|
@@ -251,7 +281,8 @@ decksmith: codex, edge-tts and ffmpeg all found
|
|
|
251
281
|
| `GET /d/:id/...` | the built deck, served statically; `/d/:id/deck.html` is the player |
|
|
252
282
|
|
|
253
283
|
Options on `POST`, all optional, all defaulted server-side: `format`, `width`+`height`,
|
|
254
|
-
`theme`, `slides`, `lang`, `tone`, `density`, `speed`, `narrate`, `voice`, `
|
|
284
|
+
`theme`, `slides`, `lang`, `tone`, `density`, `speed`, `narrate`, `voice`, `images`,
|
|
285
|
+
`video`. `images` lets the plan ask for pictures and runs the `illustrate` stage after it.
|
|
255
286
|
|
|
256
287
|
`width`/`height` override the named preset's canvas and **keep its pacing but not its
|
|
257
288
|
name** — `--format short-9x16 --width 1080 --height 1350` builds `custom-1080x1350` with
|
|
@@ -280,6 +311,10 @@ generous one.
|
|
|
280
311
|
| `DECKSMITH_REQS_PER_MIN` | `240` | Per IP. |
|
|
281
312
|
| `DECKSMITH_FETCH_FIGURES` | off | See below. |
|
|
282
313
|
| `DECKSMITH_DECK_SANDBOX` | on | Serves decks under `CSP: sandbox`. |
|
|
314
|
+
| `DECKSMITH_IMAGES` | unset | `openai` names a separate image backend for `illustrate`. Unset, pictures come from the Codex account, then the tool's own SVG. |
|
|
315
|
+
| `DECKSMITH_IMAGES_API_KEY` | | The backend's key. Environment only — never a preference, a config file, a `.deck`, or an error message. |
|
|
316
|
+
| `DECKSMITH_IMAGES_BASE_URL` | `https://api.openai.com/v1` | Any OpenAI-compatible `images/generations` endpoint. |
|
|
317
|
+
| `DECKSMITH_IMAGES_MODEL` | `gpt-image-2` | The backend's model. The Codex rung draws with the account's own. |
|
|
283
318
|
|
|
284
319
|
**Remote figures are off by default, and that is a security decision.** `fetchFigures`
|
|
285
320
|
does `readFile(src)` on anything that is not an http URL, so an uploaded document
|
|
@@ -344,7 +379,7 @@ Four tools:
|
|
|
344
379
|
|
|
345
380
|
| tool | what it does |
|
|
346
381
|
|---|---|
|
|
347
|
-
| `decksmith_capabilities` | formats, themes, every setting's range,
|
|
382
|
+
| `decksmith_capabilities` | formats, themes, every setting's range, which of Codex, edge-tts, ffmpeg and Chrome are installed, and where `illustrate` would get its pictures (`images: { backend, ok, why }`) |
|
|
348
383
|
| `decksmith_estimate_length` | what a duration/slides/density combination costs, and what it cannot buy — instant, no job |
|
|
349
384
|
| `decksmith_create_deck` | document + settings → a deck, and optionally an mp4 |
|
|
350
385
|
| `decksmith_job_status` | where a job got to |
|
|
@@ -362,7 +397,9 @@ they are the product telling you what your settings cost.
|
|
|
362
397
|
|
|
363
398
|
No setting has a default in the tool schema. Absence is the signal: a theme you did not
|
|
364
399
|
mention loses to the storyboard's own, and a language you did not mention loses to the
|
|
365
|
-
document's.
|
|
400
|
+
document's. `images: true` lets the plan ask for pictures and draws them before the build;
|
|
401
|
+
it is refused only when a backend is named in the environment and broken, because the
|
|
402
|
+
tool's own SVG means a request never fails for lack of one.
|
|
366
403
|
|
|
367
404
|
## Using it as a library
|
|
368
405
|
|
|
@@ -468,6 +505,7 @@ for it; call `verify` yourself when you want it.
|
|
|
468
505
|
|---|---|
|
|
469
506
|
| ingest | `parseMarkdown`, `fetchFigures`, `bundleFont` |
|
|
470
507
|
| plan | `codexPlanner` with its `Runner` type, `assertRefsResolve`, `systemPrompt`, `renderSource` |
|
|
508
|
+
| images | `illustrate`, `imageChain`, `resolveImageBackend`, `hasIllustrations`, with `ImageProvider`, `ImageRequest`, `ImageResult`, `IllustrateOpts` |
|
|
471
509
|
| narrate | `narrate`, `pickVoice`, `narratableLangs` |
|
|
472
510
|
| emit | `buildDeck`, `emitDeck`, `emitComposition`, `resolveTheme`, `THEMES`, `THEME_NAMES` |
|
|
473
511
|
| verify | `verify`, `check`, `parseCheckReport` |
|
|
@@ -480,6 +518,9 @@ an SDK instead of a subprocess, which is what a server actually wants — `codex
|
|
|
480
518
|
shells to the Codex CLI only because that is the right default at a terminal. And
|
|
481
519
|
`emitDeck` sits underneath `buildDeck` for callers writing to object storage or a response
|
|
482
520
|
body rather than a filesystem: it is pure, taking strings in and returning strings out.
|
|
521
|
+
`ImageProvider` is the same kind of seam as `Runner`: `illustrate` takes a `chain` of them,
|
|
522
|
+
so a server's tests draw with a fake instead of spawning Codex, and a deployment can add a
|
|
523
|
+
backend this package does not ship.
|
|
483
524
|
|
|
484
525
|
Anything not listed is deliberately absent, and adding to the list is a promise we cannot
|
|
485
526
|
quietly take back. `prefsFromFlags` is the clearest example: it translates commander's flag
|
|
@@ -504,6 +545,11 @@ invalidates a storyboard you have already edited.
|
|
|
504
545
|
| `narration.rate` | `+0%` | narrate | edge-tts prosody |
|
|
505
546
|
| `narration.pitch` | `+0Hz` | narrate | edge-tts prosody |
|
|
506
547
|
| `narration.subtitles` | `true` | narrate | write subtitle cues alongside the audio |
|
|
548
|
+
| `images.enabled` | `false` | plan, illustrate | the planner may ask for a picture where no figure fits |
|
|
549
|
+
| `images.provider` | `codex` | illustrate | `auto` · `codex` · `svg` — where the chain of rungs starts |
|
|
550
|
+
| `images.model` | unset | illustrate | model for the separate backend only; the Codex rung uses the account's own |
|
|
551
|
+
| `images.style` | `flat vector illustration` | illustrate | one phrase folded into every picture prompt |
|
|
552
|
+
| `images.max` | `4` | illustrate | most pictures drawn through the chain; the rest are the tool's SVG |
|
|
507
553
|
|
|
508
554
|
Three layers, in increasing precedence: these defaults, then the nearest
|
|
509
555
|
`decksmith.config.json` found by walking up from the working directory, then flags.
|
|
@@ -523,6 +569,12 @@ governs a deck built from a subdirectory without anyone naming a path.
|
|
|
523
569
|
"voice": "ko-KR-HyunsuMultilingualNeural",
|
|
524
570
|
"rate": "+8%",
|
|
525
571
|
"subtitles": true
|
|
572
|
+
},
|
|
573
|
+
"images": {
|
|
574
|
+
"enabled": true,
|
|
575
|
+
"provider": "codex",
|
|
576
|
+
"style": "woodcut, two colours",
|
|
577
|
+
"max": 4
|
|
526
578
|
}
|
|
527
579
|
}
|
|
528
580
|
```
|
|
@@ -536,8 +588,11 @@ decksmith.config.json: unknown preference "narration.speed". Valid: enabled, voi
|
|
|
536
588
|
```
|
|
537
589
|
|
|
538
590
|
On the command line: `--slides --lang --tone --density` on `plan`, `--theme --speed` on
|
|
539
|
-
`build`, `--voice --rate --pitch --no-subtitles` on `narrate`,
|
|
540
|
-
|
|
591
|
+
`build`, `--voice --rate --pitch --no-subtitles` on `narrate`, `--images --image-provider
|
|
592
|
+
--image-model --image-style --image-max` on `plan` and `illustrate`, and all but the image
|
|
593
|
+
flags on `pack`, which records the preferences the deck was made under — whether it was
|
|
594
|
+
illustrated is read off the storyboard itself, the way `narration.enabled` is read off the
|
|
595
|
+
narration beside it.
|
|
541
596
|
|
|
542
597
|
A preference sitting at its default says nothing, so a stored artifact wins over it and
|
|
543
598
|
loses to anything you type. `plan` stamps `lang` and `theme` into the storyboard it
|
|
@@ -596,8 +651,81 @@ directory; nothing else notices, because a missing file looks identical to a bro
|
|
|
596
651
|
declined to play.
|
|
597
652
|
|
|
598
653
|
`edge-tts` must be on your PATH, or installable as `python3 -m edge_tts`; set
|
|
599
|
-
`DECKSMITH_EDGE_TTS` to point at it directly.
|
|
600
|
-
the network, which is why
|
|
654
|
+
`DECKSMITH_EDGE_TTS` to point at it directly. Narration and illustration are the two parts
|
|
655
|
+
of DeckSmith that need the network, which is why each is its own command and not a step
|
|
656
|
+
inside `build`.
|
|
657
|
+
|
|
658
|
+
## Illustrations
|
|
659
|
+
|
|
660
|
+
```sh
|
|
661
|
+
decksmith plan source.json -o storyboard.json --images
|
|
662
|
+
decksmith illustrate storyboard.json --source source.json
|
|
663
|
+
decksmith build storyboard.json --source source.json -o out/
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
With `--images`, a beat that has nothing in the inventory to show — a `claim-figure`, or
|
|
667
|
+
either side of a `split-compare` — may carry an `illustration: { prompt, caption }` in
|
|
668
|
+
place of a `figureId`. The prompt describes a scene, never text, labels, numbers or charts:
|
|
669
|
+
nothing inside a picture can be read or checked, so the picture illustrates and the beat's
|
|
670
|
+
`evidence` still points at the section. `plan` says how many pictures the storyboard asks
|
|
671
|
+
for and the command to run; `build` and `pack` refuse the file until they exist.
|
|
672
|
+
|
|
673
|
+
`illustrate` turns each brief into a file under `assets/` beside `source.json`, registers
|
|
674
|
+
it as an ordinary figure, and sets the beat's `figureId`; the brief stays on the beat as
|
|
675
|
+
provenance. From there nothing downstream knows the picture was generated — `build`,
|
|
676
|
+
`pack`, `verify` and the server see a figure like any other. A deck that never asks for a
|
|
677
|
+
picture builds exactly as it always did, byte for byte.
|
|
678
|
+
|
|
679
|
+
Three rungs, tried in order, and every hop down is printed rather than swallowed:
|
|
680
|
+
|
|
681
|
+
1. **A separate image backend**, if you configured one AND asked for it with
|
|
682
|
+
`--image-provider auto`. `DECKSMITH_IMAGES=openai` names
|
|
683
|
+
any OpenAI-compatible `images/generations` endpoint (`DECKSMITH_IMAGES_BASE_URL`,
|
|
684
|
+
default `https://api.openai.com/v1` — LocalAI and gateways speak it too),
|
|
685
|
+
`DECKSMITH_IMAGES_API_KEY` is the key, and `DECKSMITH_IMAGES_MODEL` overrides the model
|
|
686
|
+
(default `gpt-image-2`). Environment only, like edge-tts: the key is never a preference,
|
|
687
|
+
never in a config file, never in a `.deck`, and never part of an error message. Naming
|
|
688
|
+
a backend without its key is an error where the backend is resolved — at `illustrate`,
|
|
689
|
+
in the server's startup banner, in `decksmith_capabilities` — and nowhere at import.
|
|
690
|
+
2. **The Codex account that planned the deck.** Codex 0.149 ships `image_generation` as a
|
|
691
|
+
stable feature, so the same `codex exec` that wrote the storyboard can draw a PNG.
|
|
692
|
+
`illustrate` runs it in a scratch directory it cannot write outside of and reads the
|
|
693
|
+
picture back — and checks it: the agent looks at what it drew, redraws once if the
|
|
694
|
+
picture has letters, words or numbers in it, and refuses rather than handing back a
|
|
695
|
+
picture with text. **Nothing downstream can see text inside a picture** — the 40px
|
|
696
|
+
audience floor measures the document's own text — so the check happens where the
|
|
697
|
+
image and an eye are in the same place, and a refusal simply falls to the next rung.
|
|
698
|
+
An account without the image tool says so, once, and the run falls through too.
|
|
699
|
+
Which ACCOUNT is the shell's business, not the deck's: `codex exec` inherits the
|
|
700
|
+
environment, so `CODEX_HOME=~/.codex-other decksmith illustrate …` draws on that home.
|
|
701
|
+
Note that Codex keeps its own copy of every picture it makes, under
|
|
702
|
+
`$CODEX_HOME/generated_images/<session>/`, and `--ephemeral` does not remove it — the
|
|
703
|
+
deck's own copy is the one under `assets/`, and that directory grows by roughly a
|
|
704
|
+
megabyte per picture until you empty it.
|
|
705
|
+
3. **An SVG the tool draws itself**: a deterministic, text-free composition seeded from
|
|
706
|
+
the brief. It cannot fail, so `illustrate` always finishes.
|
|
707
|
+
|
|
708
|
+
`images.provider` says where the chain starts. **`codex` is the default** — the account
|
|
709
|
+
that planned the deck draws the picture, and falls through to the tool's SVG; a metered
|
|
710
|
+
backend is never reached unless you ask for it, because a default should not be the
|
|
711
|
+
branch that spends money. `auto` puts a configured backend in front, and `svg` is the
|
|
712
|
+
tool alone — no network, no spend, and a deck whose every
|
|
713
|
+
picture is reproducible. A rung that fails is not asked again in the same run, so an
|
|
714
|
+
account with no image tool pays one `codex exec` to find out, not one per picture.
|
|
715
|
+
|
|
716
|
+
Pictures are content-addressed on the rung, model, aspect, style and prompt, the way
|
|
717
|
+
narration is on its text and voice: re-running after an edit redraws only the briefs that
|
|
718
|
+
moved, and a second run over a finished storyboard calls nothing at all. `images.max` caps
|
|
719
|
+
how many pictures go through the chain — a `split-compare` with two briefs spends two —
|
|
720
|
+
and the rest are the tool's SVG. **Pictures cost what narration does not**: a backend
|
|
721
|
+
bills per image and the Codex rung spends the account's own usage, so the cap is there
|
|
722
|
+
to bound a plan that asks for twelve. `--image-provider svg` is the way to try the layout
|
|
723
|
+
for free.
|
|
724
|
+
|
|
725
|
+
Not a rung, on purpose: asking the model to write SVG. An SVG shown through `<img>` can
|
|
726
|
+
carry SMIL or CSS animation, which runs on wall-clock time under capture — a
|
|
727
|
+
nondeterministic render that every gate passes. The tool's own SVG has no text, no
|
|
728
|
+
animation and no external references, and the same brief always draws the same bytes.
|
|
601
729
|
|
|
602
730
|
## The `.deck` container
|
|
603
731
|
|
|
@@ -659,7 +787,7 @@ names the figure or equation it rests on, a later pass can ask whether the anima
|
|
|
659
787
|
actually asserts what the source asserts. Prior art verifies that slides *look* fine;
|
|
660
788
|
nothing verifies that they are *true*.
|
|
661
789
|
|
|
662
|
-
## The
|
|
790
|
+
## The thirteen archetypes
|
|
663
791
|
|
|
664
792
|
The explanatory vocabulary. These came out of hand-building a real deck
|
|
665
793
|
(`.planning/EXPERIMENT-002-thinksr-korean.md`), not from guessing at what might be useful.
|
|
@@ -669,6 +797,7 @@ The explanatory vocabulary. These came out of hand-building a real deck
|
|
|
669
797
|
| `title` | opening or section break | `headline`, `eyebrow?`, `sub?` |
|
|
670
798
|
| `claim-figure` | one assertion beside the figure that supports it | `claim`, `figureId` |
|
|
671
799
|
| `equation-walk` | an equation explained symbol by symbol | `equationId`, `terms` (1–4) |
|
|
800
|
+
| `equation-morph` | one equation becoming the next, the shared terms carried across | `fromId`, `toId`, `terms` (1–4) |
|
|
672
801
|
| `data-table` | a results table with rows revealed in argument order | `tableId`, `highlight` |
|
|
673
802
|
| `line-chart` | a trend, with per-step deltas | `points`, `deltas?`, `readout?` |
|
|
674
803
|
| `callout` | 1–3 panels of prose: definitions, contrasts, takeaways | `panels`, `note?` |
|
|
@@ -693,7 +822,9 @@ learns what a camera frustum or an orderbook is.
|
|
|
693
822
|
surprise.
|
|
694
823
|
2. Write `src/emit/archetypes/<name>.ts` as an `Emitter<"<name>">`. It returns a `Scene`:
|
|
695
824
|
inner HTML, GSAP statements, hold points, and its own CSS. It owns one scene's insides
|
|
696
|
-
and nothing else.
|
|
825
|
+
and nothing else. If it draws labelled parts a camera could fly into, fill `Scene.parts`
|
|
826
|
+
in the same loop that gives them their ids — `inside.element` is an index, and that map
|
|
827
|
+
is the only thing that can tell the plan's third stage from the picture's.
|
|
697
828
|
3. Register it in `src/emit/archetypes/index.ts`.
|
|
698
829
|
|
|
699
830
|
Nothing else changes. The document shell, the deck runtime, the format profiles and the
|
|
@@ -702,9 +833,12 @@ verify gates never learn the new name.
|
|
|
702
833
|
## Deck navigation is ours
|
|
703
834
|
|
|
704
835
|
The step layer in `src/deck/` is DeckSmith code, not upstream's, because upstream deck
|
|
705
|
-
navigation does not work
|
|
836
|
+
navigation does not work. `player.scenes` never populates, so
|
|
706
837
|
`SlideshowController` has no slide-to-time map to bind and every key press is a no-op —
|
|
707
|
-
reproduced on HeyGen's own unmodified reference example, on
|
|
838
|
+
reproduced on HeyGen's own unmodified reference example, on 0.7.71 and 0.7.72,
|
|
839
|
+
measured again on 0.7.90 when the pin moved (`player.scenes` still 0; the slideshow
|
|
840
|
+
bundle byte-identical to 0.7.71's), and again on 0.8.27, where `scenes` is not a
|
|
841
|
+
property of the player at all — thirty minor versions and the hole is wider. What
|
|
708
842
|
does work, exactly as documented, is `player.seek(t)`. So we read the slideshow island,
|
|
709
843
|
map steps to absolute times, and drive `seek()` ourselves. It is about a hundred lines and
|
|
710
844
|
it stops the primary deliverable from being blocked on someone else's roadmap.
|
|
@@ -766,16 +900,17 @@ mechanically rather than free-hand.
|
|
|
766
900
|
|
|
767
901
|
```
|
|
768
902
|
src/index.ts the library surface — the only file consumers import
|
|
769
|
-
src/cli.ts the
|
|
903
|
+
src/cli.ts the ten verbs, argv and stderr
|
|
770
904
|
src/types.ts the contract: Source, Storyboard, Beat, Format, Verdict
|
|
771
905
|
src/prefs.ts the three-layer preference resolver
|
|
772
906
|
src/emit/kit.ts the seam between the deck shell and the archetype emitters
|
|
773
907
|
src/emit/archetypes/ one emitter per archetype
|
|
774
908
|
src/emit/themes/ one palette per file; the registry is the extension point
|
|
909
|
+
src/images/ the three rungs a brief is drawn through, and the illustrate step
|
|
775
910
|
src/narrate/ edge-tts, one segment per stop
|
|
776
911
|
src/pack/ the .deck container and its bake/link/embed policy
|
|
777
912
|
src/deck/ our step layer over player.seek(), and the subtitle reader
|
|
778
|
-
src/server/ `npm run serve` — routes, queue, the
|
|
913
|
+
src/server/ `npm run serve` — routes, queue, the six stages, and the one page
|
|
779
914
|
(ui.ts). Everything here reaches the library through ../index.js
|
|
780
915
|
only; see the note at the top of ui.ts for what breaks otherwise.
|
|
781
916
|
.planning/ the design sketch and the experiment writeups
|