@jokerized/decksmith 0.7.0 → 0.9.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 +144 -8
- package/dist/cli.js +4582 -712
- package/dist/deck-runtime.js +70 -2
- package/dist/index.js +4608 -708
- package/dist/mcp.js +4254 -759
- package/dist/types/build/files.d.ts +13 -1
- package/dist/types/deck/motion.d.ts +64 -0
- package/dist/types/deck/playback.d.ts +206 -0
- package/dist/types/deck/repack.d.ts +10 -0
- package/dist/types/deck/runtime.d.ts +96 -1
- package/dist/types/deck/subtitles.d.ts +29 -1
- package/dist/types/emit/archetypes/annotated-figure.d.ts +1 -1
- package/dist/types/emit/archetypes/bar-compare.d.ts +0 -28
- package/dist/types/emit/archetypes/callout.d.ts +0 -5
- package/dist/types/emit/archetypes/claim-figure.d.ts +15 -0
- package/dist/types/emit/archetypes/equation-morph.d.ts +40 -0
- package/dist/types/emit/archetypes/equation-walk.d.ts +60 -1
- package/dist/types/emit/archetypes/grid.d.ts +0 -14
- package/dist/types/emit/archetypes/line-chart.d.ts +0 -8
- package/dist/types/emit/archetypes/pipeline.d.ts +15 -1
- package/dist/types/emit/archetypes/stack.d.ts +1 -1
- package/dist/types/emit/composition.d.ts +47 -2
- package/dist/types/emit/faces.d.ts +22 -0
- package/dist/types/emit/fit.d.ts +127 -0
- package/dist/types/emit/kit.d.ts +104 -2
- package/dist/types/emit/look.d.ts +138 -0
- package/dist/types/emit/motion.d.ts +244 -0
- package/dist/types/emit/svg.d.ts +54 -16
- package/dist/types/emit/tex.d.ts +39 -0
- package/dist/types/emit/theme.d.ts +1 -1
- package/dist/types/emit/themes/index.d.ts +19 -1
- package/dist/types/emit/themes/packs.d.ts +15 -0
- package/dist/types/emit/themes/pick.d.ts +104 -0
- package/dist/types/emit/type.d.ts +111 -0
- package/dist/types/index.d.ts +39 -2
- package/dist/types/plan/direct.d.ts +66 -0
- package/dist/types/plan/prompt.d.ts +0 -12
- package/dist/types/prefs.d.ts +15 -1
- package/dist/types/render/capture.d.ts +24 -0
- package/dist/types/render/timing.d.ts +16 -0
- package/dist/types/source/fonts.d.ts +11 -1
- package/dist/types/types.d.ts +38 -0
- package/dist/types/verify/budget.d.ts +8 -1
- package/dist/types/verify/fidelity.d.ts +53 -0
- package/dist/types/verify/fill.d.ts +205 -0
- package/dist/types/verify/index.d.ts +17 -0
- package/dist/types/verify/motion.d.ts +33 -0
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -13,13 +13,19 @@ source.
|
|
|
13
13
|
|
|
14
14
|
## Prerequisites
|
|
15
15
|
|
|
16
|
-
- Node 22 or later, and nothing else to **generate** a deck. `ingest
|
|
17
|
-
`unpack`
|
|
18
|
-
|
|
19
|
-
Node image.
|
|
16
|
+
- Node 22 or later, and nothing else to **generate** a deck. `ingest` of a file, `build`,
|
|
17
|
+
`pack` and `unpack` run in plain Node. `build` also opens each stop in Chrome when it can
|
|
18
|
+
find one, and warns `not_measured` when it cannot. Decide your deployment on that line —
|
|
19
|
+
a service that turns papers into decks runs in a plain Node image.
|
|
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
|
+
- On Linux, Chrome's sandbox needs unprivileged user namespaces, and Ubuntu 23.10 and later
|
|
24
|
+
deny them by default. DeckSmith then opens its own pages, the gates' view of a deck and a
|
|
25
|
+
caption band, without the sandbox, as HyperFrames' render always does, and prints a
|
|
26
|
+
`chrome:` line saying so. `ingest` of a URL refuses instead, because that page comes from
|
|
27
|
+
the web. `sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0`, or an AppArmor
|
|
28
|
+
profile for Chrome, gives the sandbox back.
|
|
23
29
|
- Three commands reach outside for their own reasons: `plan` runs the Codex CLI, `narrate`
|
|
24
30
|
runs edge-tts over the network, and `illustrate` asks an image backend or that same Codex
|
|
25
31
|
account for pictures — and draws its own when neither will.
|
|
@@ -214,6 +220,49 @@ Arrow keys, Space and PageUp/PageDown step; clicking the left or right third doe
|
|
|
214
220
|
notes; `f` is fullscreen; `m` mutes the voice and `s` hides the subtitles. Every step is
|
|
215
221
|
deep-linkable (`#3` is slide 3, `#3.2` its second reveal).
|
|
216
222
|
|
|
223
|
+
### The v2 player (`--design v2`)
|
|
224
|
+
|
|
225
|
+
`build --design v2` (or `"design": "v2"` in the storyboard or config) gives `deck.html` the
|
|
226
|
+
v2 player; everything else about the build is unchanged. A deck that is already built
|
|
227
|
+
gets it without rebuilding:
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
decksmith repack out/ # writes out/deck2.html beside deck.html, never over it
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
`repack` swaps the inlined runtime, marks the page and checks that every JSON island is
|
|
234
|
+
byte-identical — no Chrome, no TTS, no re-render.
|
|
235
|
+
|
|
236
|
+
- **One control bar**: play/pause, the counter, narration speed (0.75–2×), `CC`, caption
|
|
237
|
+
size (S/M/L/XL) and fullscreen — or, in a window too narrow for those beside a caption,
|
|
238
|
+
play and one settings menu. **It never covers text.** It docks under the slide in spare
|
|
239
|
+
letterbox (a portrait phone); over the slide's empty bottom padding when that holds a
|
|
240
|
+
40px target (a slide about 720px tall or more), where it hides 2.5s after the last
|
|
241
|
+
pointer move while the deck plays; otherwise in a band under the slide that it shares
|
|
242
|
+
side by side with the captions, whose text is padded in past the buttons.
|
|
243
|
+
`test/deck-page.test.ts` measures controls against caption text and against every glyph
|
|
244
|
+
the slide draws, at six viewports.
|
|
245
|
+
- **Play/pause** pauses the voice where it is and carries on from the same word. A deck
|
|
246
|
+
opens paused; stepping while paused moves the slide silently.
|
|
247
|
+
- **Speed** sets the narration's `playbackRate` live, on the sentence already playing, and
|
|
248
|
+
divides the reveal glides and every autoplay wait by the same factor so they keep pace.
|
|
249
|
+
A stop with no narration holds for its reveal and 1.5s at 1×, not the author's gap.
|
|
250
|
+
- **Captions** are sized from the slide (3% of its height, 13px floor; L and XL keep floors
|
|
251
|
+
of 16 and 19px so the size control works on a phone) and the slide gives up only one
|
|
252
|
+
two-line strip for them: 84% of the frame at 1080p, 83% in an 800×450 embed (v0.8.0: 77%
|
|
253
|
+
and 69%), 100% in a portrait letterbox. Long cues are cut to two lines of the strip's
|
|
254
|
+
actual width, which also splits Japanese and Chinese.
|
|
255
|
+
- **Keys:** Space steps forward (Space is the presenter's key, not play); `Enter` plays and
|
|
256
|
+
pauses; `c` (or `s`) captions; `<`/`>` speed; `+`/`-` caption size. Letter keys fall back
|
|
257
|
+
to the physical key under a Korean or Japanese IME. Anything held with Cmd, Ctrl or Alt
|
|
258
|
+
is left to the browser.
|
|
259
|
+
- **Preferences** — speed, captions on/off, size — come from `?speed=1.25&cc=0&ccsize=l`
|
|
260
|
+
(this visit only), else from `localStorage` (`decksmith.prefs.v1`), else the defaults.
|
|
261
|
+
When the viewer changes one, the deck posts
|
|
262
|
+
`{ type: "decksmith:prefs", speed, cc, ccsize }` to its parent so an embedding site can
|
|
263
|
+
keep it per user (a cross-site iframe's storage is partitioned), and it applies the same
|
|
264
|
+
message when the parent sends it. Check `event.origin` on your side.
|
|
265
|
+
|
|
217
266
|
Stepping forward *plays* the reveal rather than cutting to it — the step layer sweeps the
|
|
218
267
|
composition's timelines across frames instead of seeking once. Backward steps, `Home`/`End`
|
|
219
268
|
and deep links cut, because entrance tweens run in reverse look like elements un-drawing
|
|
@@ -778,8 +827,11 @@ invalidates a storyboard you have already edited.
|
|
|
778
827
|
| `lang` | the source's | plan | BCP-47. Drives the copy, the voice, and the font subset |
|
|
779
828
|
| `tone` | `plain` | plan | `plain` · `academic` · `conversational` · `punchy` |
|
|
780
829
|
| `density` | `normal` | plan | `sparse` · `normal` · `dense` — how much text a slide may carry |
|
|
781
|
-
| `theme` | `ink` | emit | `ink` · `paper` · `mono
|
|
830
|
+
| `theme` | `ink` | emit | `ink` · `paper` · `mono`, or a v2 style pack by name (see Themes) |
|
|
831
|
+
| `design` | `classic` | narrate, emit | `classic` is the v0.8.0 look byte for byte · `v2` is the redesign: the v2 player (see "The v2 player") and a style pack per deck when no theme is named |
|
|
832
|
+
| `packSeed` | the source id | narrate, emit | what `design: v2` hashes to pick a pack; pass a paper id so its languages share one |
|
|
782
833
|
| `animationSpeed` | `1` | emit | multiplies every duration, hold and beat length. Below 1 is faster |
|
|
834
|
+
| `design` | `classic` | emit | `classic` (the 0.8 look, byte-identical) · `v2` (the redesign — see [v2 motion](#v2-motion)) |
|
|
783
835
|
| `narration.voice` | picked for `lang`+`tone` | narrate | an explicit edge-tts voice id |
|
|
784
836
|
| `narration.rate` | `+0%` | narrate | edge-tts prosody |
|
|
785
837
|
| `narration.pitch` | `+0Hz` | narrate | edge-tts prosody |
|
|
@@ -826,8 +878,8 @@ why `slideCount` did nothing.
|
|
|
826
878
|
decksmith.config.json: unknown preference "narration.speed". Valid: enabled, voice, rate, pitch, subtitles.
|
|
827
879
|
```
|
|
828
880
|
|
|
829
|
-
On the command line: `--slides --lang --tone --density` on `plan`, `--theme --
|
|
830
|
-
`build
|
|
881
|
+
On the command line: `--slides --lang --tone --density` on `plan`, `--theme --design
|
|
882
|
+
--pack-seed --speed` on `build` (`--design --pack-seed` on `narrate` too), `--voice --rate --pitch --no-subtitles` on `narrate`, `--images --image-provider
|
|
831
883
|
--image-model --image-style --image-max` on `plan` and `illustrate`, and all but the image
|
|
832
884
|
flags on `pack`, which records the preferences the deck was made under — whether it was
|
|
833
885
|
illustrated is read off the storyboard itself, the way `narration.enabled` is read off the
|
|
@@ -835,9 +887,43 @@ narration beside it.
|
|
|
835
887
|
|
|
836
888
|
A preference sitting at its default says nothing, so a stored artifact wins over it and
|
|
837
889
|
loses to anything you type. `plan` stamps `lang` and `theme` into the storyboard it
|
|
838
|
-
writes; `build` then uses the storyboard's
|
|
890
|
+
writes (and `design`, only when one was asked for); `build` then uses the storyboard's
|
|
891
|
+
unless `--theme`/`--design` or a config file restates one.
|
|
839
892
|
Language is never overridden at build time — it describes copy that is already written.
|
|
840
893
|
|
|
894
|
+
`--design v2` (or `"design": "v2"` in the config file) lets the build vary each beat's
|
|
895
|
+
layout: bars as rows or columns, a pipeline as a row, a stair or a column, a claim beside,
|
|
896
|
+
mirrored against or above its figure, a comparison as columns or rows, and the headline on
|
|
897
|
+
top, in a left rail or under the body. The choice is made by code, deterministically, per
|
|
898
|
+
paper — never by the planner — and it never moves a stop, so narration stays aligned.
|
|
899
|
+
`build` writes the choices to `out/look.json`. Without the flag the deck is the classic one,
|
|
900
|
+
byte for byte. See `.planning/2026-10-07-v2-layout-director.md`.
|
|
901
|
+
|
|
902
|
+
## v2 motion
|
|
903
|
+
|
|
904
|
+
`--design v2` replaces the 0.8 deck's single motion — every scene fading up the same
|
|
905
|
+
eyebrow and headline, every seam the same 0.4s dissolve, nothing moving once a slide has
|
|
906
|
+
built — with a grammar planned per deck (`src/emit/motion.ts`):
|
|
907
|
+
|
|
908
|
+
- **Entrances.** Six verbs — `rise`, `slide`, `snap`, `focus`, `wipe`, `mask` — one per
|
|
909
|
+
scene, chosen from the source id and beat id, never the same twice in a row, with the
|
|
910
|
+
stock `rise` capped at a quarter of the deck.
|
|
911
|
+
- **Seams.** `dissolve`, `push`, `lift`, `wipe`, `zoom`, picked from how two neighbouring
|
|
912
|
+
beats relate (same family → push, a role boundary → zoom, a title → lift, into the close
|
|
913
|
+
→ dissolve), never repeated back to back, and at least three kinds in a deck of ten or
|
|
914
|
+
more beats. A beat `inside` the one before keeps the camera dive.
|
|
915
|
+
- **Emphasis while the narrator talks.** The part a sentence is about pulses, glows or is
|
|
916
|
+
underlined, starting on a cue boundary of that sentence inside the quiet stretch after
|
|
917
|
+
its stop, and is back at rest before the next reveal — so every frame a gate captures
|
|
918
|
+
at a stop is unchanged.
|
|
919
|
+
|
|
920
|
+
All of it is `fromTo` tweens with no callbacks, seeded rather than random, and keeps every
|
|
921
|
+
hold, every scene window and `timing.json` exactly as `classic` writes them. In `deck.html`
|
|
922
|
+
a v2 deck also glides through the seam into the next slide (0.8 cut to its first stop,
|
|
923
|
+
already built) and, while a stop's audio plays, seeks the scene through that stop's quiet
|
|
924
|
+
stretch on the audio clock so the emphasis lands on the same word as in the video. Both
|
|
925
|
+
are off under `prefers-reduced-motion`.
|
|
926
|
+
|
|
841
927
|
## Themes
|
|
842
928
|
|
|
843
929
|
Three, each a position rather than a hue.
|
|
@@ -856,6 +942,41 @@ naming a family the deck does not declare falls back silently.
|
|
|
856
942
|
A theme is a name and a palette, and that is the whole extension point: a new one is a
|
|
857
943
|
file in `src/emit/themes/` plus a line in `THEMES`. No archetype learns it exists.
|
|
858
944
|
|
|
945
|
+
### v2 style packs
|
|
946
|
+
|
|
947
|
+
Six more, each a whole look rather than a palette: its own typeface pairing and type
|
|
948
|
+
scale, ground and accent, eyebrow treatment, figure framing and surface.
|
|
949
|
+
|
|
950
|
+
| Pack | Ground | Headline / body | Title | Lists | Bars | Leans to |
|
|
951
|
+
|---|---|---|---|---|---|---|
|
|
952
|
+
| `signal` | violet-black, Magma accent | Space Grotesk / Inter | glowing headline | ticks | pills | foot headlines |
|
|
953
|
+
| `blueprint` | navy, cyan | IBM Plex Sans / IBM Plex Sans | drawing frame with corner marks | numbers | square, on dashed tracks | rail headlines |
|
|
954
|
+
| `atlas` | espresso, amber | Source Serif 4 / Inter | centred, frontispiece rule | dots | slightly rounded | top headlines |
|
|
955
|
+
| `folio` | cream, oxblood | Source Serif 4 / Source Serif 4 | masthead rules | numbers | square, no tracks | top headlines, versus |
|
|
956
|
+
| `chalk` | cool white, ultramarine | Space Grotesk / IBM Plex Sans | highlighter under each word | cards | rounded, outlined | foot headlines, tables |
|
|
957
|
+
| `journal` | sage, forest | IBM Plex Sans / Source Serif 4 | side bar down the title | ruled rows | slightly rounded, outlined tracks | rail headlines |
|
|
958
|
+
|
|
959
|
+
The forms are the pack's `forms` (list marks, Director affinity) and its `skin` (title,
|
|
960
|
+
bars), which may set only paint: colours, shadows, radii, outlines and SVG stroke/fill
|
|
961
|
+
opacity, never a box. In a Korean, Japanese or Chinese deck a pack's serif roles are set
|
|
962
|
+
in Noto Serif KR/JP/SC, bundled beside the sans.
|
|
963
|
+
|
|
964
|
+
`--theme <pack>` forces one. `--design v2` picks one when nobody named a theme (a
|
|
965
|
+
storyboard's default `ink` counts as nobody): a weighted, deterministic hash of the
|
|
966
|
+
source id (or `--pack-seed`), leaning mildly toward packs that suit the deck's mix of
|
|
967
|
+
archetypes. Over the 176 HypePaper storyboards on disk the busiest pack carries 20.5% of
|
|
968
|
+
decks. With narration on disk, a pack that would stage a beat with a different stop count
|
|
969
|
+
than the narration was recorded at, or that would leave out a beat the storyboard's own
|
|
970
|
+
theme draws, is skipped for the next one; if none fits, the storyboard's own theme is kept.
|
|
971
|
+
So `--design v2` never breaks a narrated rebuild and never costs a slide.
|
|
972
|
+
|
|
973
|
+
Each pack's faces ship beside a Latin deck, vendored from `@fontsource-variable/*` like
|
|
974
|
+
Inter, and are measured by their own width tables (`src/emit/faces.ts`, written by
|
|
975
|
+
`node scripts/measure-faces.mjs --write`). A CJK deck keeps its Noto bundle first in every
|
|
976
|
+
stack — Noto Serif for a role the pack sets in a serif, Noto Sans otherwise — so the glyph
|
|
977
|
+
shapes change with the pack in every language.
|
|
978
|
+
The packs' interfaces are written up in `.planning/2026-10-07-v2-style-packs.md`.
|
|
979
|
+
|
|
859
980
|
## Narration
|
|
860
981
|
|
|
861
982
|
```sh
|
|
@@ -1199,6 +1320,19 @@ mechanically rather than free-hand.
|
|
|
1199
1320
|
root timeline holds only a dummy tween spanning the deck.
|
|
1200
1321
|
- KaTeX renders with `output: "html"`. The default also emits a hidden MathML mirror that
|
|
1201
1322
|
the layout inspector reads as overlapping text.
|
|
1323
|
+
- No TeX reaches a `katex.render` call that KaTeX has not already parsed in Node, with
|
|
1324
|
+
`throwOnError` and the deck's own options (`src/emit/tex.ts`). A formula that fails gets
|
|
1325
|
+
a repair only where it cannot change what the formula says — a missing `\right.`, an
|
|
1326
|
+
unclosed brace, a dropped `\label`, a paper's undefined macro drawn as its name — and the
|
|
1327
|
+
repair is printed at `plan` and again at `build`. One no repair saves is shown as its
|
|
1328
|
+
source in plain text by `equation-walk`, and costs the beat, by name, in
|
|
1329
|
+
`equation-morph`. Until this existed the first parser a formula met was the browser at
|
|
1330
|
+
`verify`, after the deck had been built and narrated: 26 of 92 failed HypePaper builds
|
|
1331
|
+
in the week to 2026-10-07, 25 of them from `wrapTerms` cutting a `\left`/`\right` pair or
|
|
1332
|
+
a superscript in half.
|
|
1333
|
+
- `equation-walk` measures its display after fonts and fits it to its box: smaller down to
|
|
1334
|
+
the 40px floor, then broken after top-level relations and operators. One that still does
|
|
1335
|
+
not fit fails `fidelity` as `math_unfit`, with the formula in the message.
|
|
1202
1336
|
- No `Date.now()`, no `Math.random()`, no network at render time *in the composition*.
|
|
1203
1337
|
Two renders of an image-free deck must be byte-identical. `deck.html` is exempt: it is
|
|
1204
1338
|
presented, never rendered, and its subtitle loop legitimately reads a clock.
|
|
@@ -1259,6 +1393,8 @@ src/prefs.ts the three-layer preference resolver
|
|
|
1259
1393
|
src/emit/kit.ts the seam between the deck shell and the archetype emitters
|
|
1260
1394
|
src/emit/archetypes/ one emitter per archetype
|
|
1261
1395
|
src/emit/themes/ one palette per file; the registry is the extension point
|
|
1396
|
+
src/emit/type.ts type specs: the v2 packs' faces and chrome scale, read by chromeHeight and chromeCss
|
|
1397
|
+
src/emit/faces.ts measured width tables for the packs' Latin faces (generated)
|
|
1262
1398
|
src/images/ the three rungs a brief is drawn through, and the illustrate step
|
|
1263
1399
|
src/narrate/ edge-tts, one segment per stop
|
|
1264
1400
|
src/pack/ the .deck container and its bake/link/embed policy
|