@jokerized/decksmith 0.1.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 +794 -0
- package/dist/cli.js +8906 -0
- package/dist/deck-runtime.js +55 -0
- package/dist/index.js +8590 -0
- package/dist/mcp.js +7809 -0
- package/dist/server/errors.js +73 -0
- package/dist/server/http.js +504 -0
- package/dist/server/main.js +91 -0
- package/dist/server/options.js +198 -0
- package/dist/server/pipeline.js +356 -0
- package/dist/server/queue.js +195 -0
- package/dist/server/ui.js +1614 -0
- package/dist/server/upload.js +232 -0
- package/dist/types/cli.d.ts +1 -0
- package/dist/types/deck/runtime.d.ts +59 -0
- package/dist/types/deck/subtitles.d.ts +101 -0
- package/dist/types/emit/archetypes/annotated-figure.d.ts +103 -0
- package/dist/types/emit/archetypes/bar-compare.d.ts +30 -0
- package/dist/types/emit/archetypes/callout.d.ts +7 -0
- package/dist/types/emit/archetypes/claim-figure.d.ts +9 -0
- package/dist/types/emit/archetypes/data-table.d.ts +21 -0
- package/dist/types/emit/archetypes/equation-walk.d.ts +2 -0
- package/dist/types/emit/archetypes/grid.d.ts +16 -0
- package/dist/types/emit/archetypes/index.d.ts +19 -0
- package/dist/types/emit/archetypes/line-chart.d.ts +22 -0
- package/dist/types/emit/archetypes/pipeline.d.ts +81 -0
- package/dist/types/emit/archetypes/split-compare.d.ts +2 -0
- package/dist/types/emit/archetypes/stack.d.ts +93 -0
- package/dist/types/emit/archetypes/title.d.ts +188 -0
- package/dist/types/emit/camera.d.ts +397 -0
- package/dist/types/emit/composition.d.ts +191 -0
- package/dist/types/emit/island.d.ts +19 -0
- package/dist/types/emit/kit.d.ts +256 -0
- package/dist/types/emit/svg.d.ts +177 -0
- package/dist/types/emit/theme.d.ts +65 -0
- package/dist/types/emit/themes/index.d.ts +40 -0
- package/dist/types/emit/themes/ink.d.ts +12 -0
- package/dist/types/emit/themes/mono.d.ts +20 -0
- package/dist/types/emit/themes/paper.d.ts +18 -0
- package/dist/types/index.d.ts +200 -0
- package/dist/types/mcp/main.d.ts +2 -0
- package/dist/types/mcp/prereqs.d.ts +22 -0
- package/dist/types/mcp/tools.d.ts +212 -0
- package/dist/types/narrate/narrate.d.ts +87 -0
- package/dist/types/narrate/tts.d.ts +134 -0
- package/dist/types/narrate/voices.d.ts +29 -0
- package/dist/types/pack/media.d.ts +61 -0
- package/dist/types/pack/pack.d.ts +16 -0
- package/dist/types/plan/codex.d.ts +24 -0
- package/dist/types/plan/duration.d.ts +394 -0
- package/dist/types/plan/prompt.d.ts +47 -0
- package/dist/types/plan/refs.d.ts +22 -0
- package/dist/types/plan/select.d.ts +116 -0
- package/dist/types/prefs.d.ts +43 -0
- package/dist/types/render/captions.d.ts +108 -0
- package/dist/types/render/ffmpeg.d.ts +129 -0
- package/dist/types/render/render.d.ts +123 -0
- package/dist/types/render/timing.d.ts +290 -0
- package/dist/types/server/errors.d.ts +11 -0
- package/dist/types/server/http.d.ts +57 -0
- package/dist/types/server/main.d.ts +1 -0
- package/dist/types/server/options.d.ts +90 -0
- package/dist/types/server/pipeline.d.ts +21 -0
- package/dist/types/server/queue.d.ts +105 -0
- package/dist/types/server/ui.d.ts +9 -0
- package/dist/types/server/upload.d.ts +107 -0
- package/dist/types/source/assets.d.ts +11 -0
- package/dist/types/source/fonts.d.ts +15 -0
- package/dist/types/source/markdown.d.ts +8 -0
- package/dist/types/types.d.ts +1992 -0
- package/dist/types/verify/budget.d.ts +41 -0
- package/dist/types/verify/check.d.ts +78 -0
- package/dist/types/verify/drift.d.ts +158 -0
- package/dist/types/verify/fidelity.d.ts +247 -0
- package/dist/types/verify/index.d.ts +207 -0
- package/dist/types/verify/overprint.d.ts +133 -0
- package/dist/types/verify/typefloor.d.ts +50 -0
- package/package.json +84 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The seam between the deck shell and the explanatory vocabulary.
|
|
3
|
+
*
|
|
4
|
+
* `composition.ts` owns the document: head, theme CSS, scene wrappers, the root
|
|
5
|
+
* timeline, the slideshow island. An archetype emitter owns one scene's insides
|
|
6
|
+
* and nothing else. Adding a domain means adding an emitter — the shell never
|
|
7
|
+
* learns what a camera frustum or an orderbook is.
|
|
8
|
+
*/
|
|
9
|
+
import type { Archetype, BeatOf, Format, Source } from "../types.js";
|
|
10
|
+
/**
|
|
11
|
+
* `.scene`'s padding, in reference px. `contentW`/`contentH` are what is left.
|
|
12
|
+
*
|
|
13
|
+
* They live here rather than in `theme.ts` or `title.ts` because both of those
|
|
14
|
+
* need them and neither may import the other: `baseCss` writes the padding into
|
|
15
|
+
* the stylesheet, every archetype does arithmetic against what is left. They
|
|
16
|
+
* were the same two numbers in both places by maintenance rather than by
|
|
17
|
+
* construction, and a stylesheet that disagrees with the arithmetic clips at the
|
|
18
|
+
* canvas edge, which no gate reads.
|
|
19
|
+
*/
|
|
20
|
+
export declare const PAD_X = 110;
|
|
21
|
+
export declare const PAD_Y = 84;
|
|
22
|
+
/**
|
|
23
|
+
* The canvas every absolute measurement in this codebase was chosen against.
|
|
24
|
+
*
|
|
25
|
+
* The type scale, the padding, the font floor, the gaps and the stroke weights
|
|
26
|
+
* were all picked by eye on a 1920x1080 slide. That was fine while 1920x1080 was
|
|
27
|
+
* the only canvas, and became wrong the moment a second one existed: a 76px
|
|
28
|
+
* headline is 3.96% of the width at 1920 and 7.04% at 1080, so the same slide
|
|
29
|
+
* design arrives 78% heavier in portrait. It reads as shouting, and it is why a
|
|
30
|
+
* vertical deck cannot carry anything complicated — the type has eaten the room
|
|
31
|
+
* the content needed.
|
|
32
|
+
*/
|
|
33
|
+
export declare const REF_W = 1920;
|
|
34
|
+
/**
|
|
35
|
+
* HOW FAR A NARROW CANVAS TRAVELS BACK TOWARDS THE REFERENCE ONE. The whole
|
|
36
|
+
* typographic argument of this file is this one number.
|
|
37
|
+
*
|
|
38
|
+
* An archetype lays out in REFERENCE SPACE — one unit system, `refWidth` wide, at
|
|
39
|
+
* the target's aspect ratio — and `baseCss` scales the finished scene onto the
|
|
40
|
+
* real canvas exactly once. So every absolute measurement in the vocabulary (the
|
|
41
|
+
* type scale, the 40px floor, ~171 gap/radius/stroke literals) becomes a fraction
|
|
42
|
+
* of the canvas rather than a count of its pixels, without any of them being
|
|
43
|
+
* touched.
|
|
44
|
+
*
|
|
45
|
+
* `refWidth = REF_W^k · width^(1-k)`, so:
|
|
46
|
+
*
|
|
47
|
+
* k = 0 reference space IS the canvas. What shipped before this existed.
|
|
48
|
+
* k = 1 the portrait deck is the landscape deck, photographically reduced:
|
|
49
|
+
* type is the same PERCENTAGE of the width in both.
|
|
50
|
+
*
|
|
51
|
+
* k = 1 is the tempting answer and it is too far. Equal percentage-of-width only
|
|
52
|
+
* means equal legibility if the two screens subtend the same angle, and they do
|
|
53
|
+
* not: a 13" laptop is ~29cm wide at ~55cm (0.52 rad), a phone ~7cm at ~30cm
|
|
54
|
+
* (0.23 rad). The phone is worth about half. Halving the angular size of every
|
|
55
|
+
* glyph to buy room is trading the wrong way — 40px at k=1 lands at 8.1 CSS px on
|
|
56
|
+
* a 390px-wide phone, which is not text any more.
|
|
57
|
+
*
|
|
58
|
+
* k = 1/2 is the geometric mean of the two readings, and it is what the 390px
|
|
59
|
+
* screenshots support: the floor lands at 30 canvas px (10.8 CSS px on the phone,
|
|
60
|
+
* against 14.4 today) and the content box grows 1.33x in each direction — 1.78x
|
|
61
|
+
* the area, which is the room the extra table rows and pipeline stages come from.
|
|
62
|
+
*
|
|
63
|
+
* Identity at 1920 for every k, since `1920^k · 1920^(1-k) = 1920`. That is what
|
|
64
|
+
* keeps the shipping 16:9 deck byte-identical: at k anything, `zoomOf` returns
|
|
65
|
+
* exactly 1 there and `baseCss` emits no scaling rule at all.
|
|
66
|
+
*/
|
|
67
|
+
export declare const REF_PULL = 0.5;
|
|
68
|
+
/**
|
|
69
|
+
* The width of the unit system an archetype lays out in.
|
|
70
|
+
*
|
|
71
|
+
* Rounded to an integer so `contentW` is an integer and the emitted geometry is
|
|
72
|
+
* the same kind of number it has always been — `n()` prints two decimals, and a
|
|
73
|
+
* content box of 1272.7924 would put a long tail on half the coordinates in the
|
|
74
|
+
* document for no benefit.
|
|
75
|
+
*/
|
|
76
|
+
export declare function refWidth(format: Format): number;
|
|
77
|
+
/**
|
|
78
|
+
* The reference canvas keeps the TARGET's aspect ratio, not the reference one.
|
|
79
|
+
*
|
|
80
|
+
* This is what lets `isPortrait` and every branch under it keep working: the
|
|
81
|
+
* reference canvas for `short-9x16` is 1440x2560, which is still 9:16. Reference
|
|
82
|
+
* space is a change of unit, never a change of shape.
|
|
83
|
+
*/
|
|
84
|
+
export declare function refHeight(format: Format): number;
|
|
85
|
+
/**
|
|
86
|
+
* Reference px per canvas px — what `baseCss` scales the scene by.
|
|
87
|
+
*
|
|
88
|
+
* WIDTH, not height and not diagonal. Text wraps against width, so width is what
|
|
89
|
+
* decides how much a given type size costs; a taller canvas at the same width
|
|
90
|
+
* buys lines, not room per line.
|
|
91
|
+
*
|
|
92
|
+
* Exactly 1 at 1920 wide, because `refWidth` returns exactly 1920 there.
|
|
93
|
+
*/
|
|
94
|
+
export declare function zoomOf(format: Format): number;
|
|
95
|
+
/**
|
|
96
|
+
* `.scene`'s padding, and the box it leaves an archetype to draw in.
|
|
97
|
+
*
|
|
98
|
+
* These live here rather than in `theme.ts` or `title.ts` because both need them
|
|
99
|
+
* and neither may import the other: `baseCss` writes the padding into the
|
|
100
|
+
* stylesheet, every archetype does arithmetic against what is left. They were the
|
|
101
|
+
* same two numbers in both places by maintenance rather than construction, and a
|
|
102
|
+
* stylesheet that disagrees with the arithmetic clips at the canvas edge, which
|
|
103
|
+
* no gate reads.
|
|
104
|
+
*
|
|
105
|
+
* Reference px, so the gutter is the same FRACTION of every canvas without
|
|
106
|
+
* anything here knowing the canvas: 110 of 1440 is 110 of 1920 once the scene has
|
|
107
|
+
* been scaled. A canvas-px gutter took 20.4% of the width at 1080 against 11.5%
|
|
108
|
+
* at 1920 — nearly twice the proportional margin on the canvas that can least
|
|
109
|
+
* afford it.
|
|
110
|
+
*/
|
|
111
|
+
export declare function contentW(format: Format): number;
|
|
112
|
+
export declare function contentH(format: Format): number;
|
|
113
|
+
export interface Theme {
|
|
114
|
+
bg: string;
|
|
115
|
+
fg: string;
|
|
116
|
+
muted: string;
|
|
117
|
+
dim: string;
|
|
118
|
+
rule: string;
|
|
119
|
+
panel: string;
|
|
120
|
+
accent: string;
|
|
121
|
+
/** Highlight tones, addressed by `tone: "a" | "b" | "c" | "d"`. */
|
|
122
|
+
tones: {
|
|
123
|
+
a: string;
|
|
124
|
+
b: string;
|
|
125
|
+
c: string;
|
|
126
|
+
d: string;
|
|
127
|
+
};
|
|
128
|
+
fontStack: string;
|
|
129
|
+
}
|
|
130
|
+
export interface EmitContext {
|
|
131
|
+
source: Source;
|
|
132
|
+
format: Format;
|
|
133
|
+
theme: Theme;
|
|
134
|
+
/**
|
|
135
|
+
* This scene's root element id, e.g. `"s3"`. Every GSAP selector must be
|
|
136
|
+
* scoped with it — an unscoped class selector reaches into other scenes and
|
|
137
|
+
* `hyperframes lint` rejects it (`unscoped_gsap_selector`).
|
|
138
|
+
*/
|
|
139
|
+
sid: string;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* A value in a GSAP vars payload that is JavaScript rather than data.
|
|
143
|
+
*
|
|
144
|
+
* `ease: dsZoom` names a function the scene's own `measure` declared, and
|
|
145
|
+
* `scale: dsFramed.k` reads a number it measured. Neither is expressible as a
|
|
146
|
+
* number or a string — a string would be quoted, and `ease: "dsZoom"` is a GSAP
|
|
147
|
+
* ease NAME that does not exist, so GSAP falls back to `power1.out` and the
|
|
148
|
+
* camera lands on the wrong curve with every gate green. So raw JS is a distinct
|
|
149
|
+
* kind of value, spelled at the call site, rather than a string the serialiser
|
|
150
|
+
* has to guess about.
|
|
151
|
+
*/
|
|
152
|
+
export interface Raw {
|
|
153
|
+
readonly __raw: string;
|
|
154
|
+
}
|
|
155
|
+
export declare function raw(js: string): Raw;
|
|
156
|
+
export type VarValue = number | string | boolean | Raw | Vars | readonly VarValue[];
|
|
157
|
+
/** A GSAP vars payload — the `{ opacity: 0, y: 14 }` half of a `fromTo`. */
|
|
158
|
+
export interface Vars {
|
|
159
|
+
readonly [key: string]: VarValue;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* ONE TWEEN. Invariant 2 — "every tween is `fromTo`" — is this interface.
|
|
163
|
+
*
|
|
164
|
+
* It used to be a line of GSAP source text, which had three consequences worth
|
|
165
|
+
* the change. A `from()` was a review question rather than a type error. The
|
|
166
|
+
* animation vocabulary was invisible to the type checker: `duration` and
|
|
167
|
+
* `duraiton` were the same string. And `pace()` had to recover the position
|
|
168
|
+
* argument by running a regex over the emitted statement —
|
|
169
|
+
* `/,\s*(-?\d*\.?\d+)\s*\)\s*;?\s*$/` — which is a parser for a language nobody
|
|
170
|
+
* had written a grammar for, sitting on the path every deck's timing goes
|
|
171
|
+
* through.
|
|
172
|
+
*
|
|
173
|
+
* `from` is REQUIRED and there is no other constructor, so a tween that does
|
|
174
|
+
* not declare where it starts cannot be written down. That is what makes the
|
|
175
|
+
* invariant checkable by `tsc` instead of by a human reading a diff.
|
|
176
|
+
*
|
|
177
|
+
* `at` is a position in seconds from the scene's start, ALREADY ROUNDED by
|
|
178
|
+
* whoever built the tween (invariant 10). The serialiser prints it and rounds
|
|
179
|
+
* nothing: two roundings on one number is how a byte moves.
|
|
180
|
+
*/
|
|
181
|
+
export interface Tween {
|
|
182
|
+
readonly target: string;
|
|
183
|
+
readonly from: Vars;
|
|
184
|
+
readonly to: Vars;
|
|
185
|
+
readonly at: number;
|
|
186
|
+
}
|
|
187
|
+
export declare function fromTo(target: string, from: Vars, to: Vars, at: number): Tween;
|
|
188
|
+
/**
|
|
189
|
+
* THE ONE PLACE A TWEEN BECOMES GSAP TEXT.
|
|
190
|
+
*
|
|
191
|
+
* Byte-for-byte what fifty-eight call sites used to build by hand: one space
|
|
192
|
+
* inside each brace, `", "` between entries, `key: value`. The spacing is not
|
|
193
|
+
* cosmetic — the deck's regression test is that two builds of one storyboard
|
|
194
|
+
* are byte-identical, and the cheapest proof this refactor changed nothing is
|
|
195
|
+
* that it also matches every build made before it.
|
|
196
|
+
*/
|
|
197
|
+
export declare function tweenText(t: Tween): string;
|
|
198
|
+
export interface Scene {
|
|
199
|
+
/** The scene's inner HTML. The wrapper `<div class="scene clip">` is added by the shell. */
|
|
200
|
+
html: string;
|
|
201
|
+
/**
|
|
202
|
+
* Tweens appended to this scene's own paused timeline, with times RELATIVE to
|
|
203
|
+
* the scene's start. `Tween` is a `fromTo` by construction — `from()` records
|
|
204
|
+
* its end state when the timeline is built and breaks under the arbitrary
|
|
205
|
+
* seeking that deck navigation performs.
|
|
206
|
+
*/
|
|
207
|
+
tl: Tween[];
|
|
208
|
+
/** Statements run as the document parses, before anything else, e.g.
|
|
209
|
+
* `katex.render(...)`. Anything that CHANGES layout belongs here, so that a
|
|
210
|
+
* `measure` below reads the finished document. */
|
|
211
|
+
setup?: string[];
|
|
212
|
+
/**
|
|
213
|
+
* SEAM B. Statements run once inside the ready gate's barrier — after
|
|
214
|
+
* `document.fonts.ready` and after every image has decoded — and immediately
|
|
215
|
+
* before this scene's timeline is built, in the same closure, so a `tl` entry
|
|
216
|
+
* can read a variable declared here.
|
|
217
|
+
*
|
|
218
|
+
* Declaring any makes the scene DEFERRED: `sceneHtml` wraps the timeline in a
|
|
219
|
+
* builder that the gate awaits instead of registering it during parse. That is
|
|
220
|
+
* how a scene measures the rendered document — font metrics have arrived, and
|
|
221
|
+
* the answer is taken at one instant for the whole deck rather than whenever
|
|
222
|
+
* some tween first happens to render.
|
|
223
|
+
*
|
|
224
|
+
* WHY IT CANNOT BE DONE IN `setup`, AND WHY NOT LAZILY EITHER. `setup` runs
|
|
225
|
+
* during parse, before webfonts resolve, so it measures fallback metrics —
|
|
226
|
+
* measured at 8.6px of travel error in `experiments/014-seam-b`. Lazily, on a
|
|
227
|
+
* tween's first render, is worse: `hyperframes render` shards frames
|
|
228
|
+
* contiguously across workers, so "first render" is a different point in the
|
|
229
|
+
* deck in every worker, and the same input then renders differently at 1 worker
|
|
230
|
+
* and at 2 — measured at 39 of 594 frames and 14.96 dB, a camera landing 18px
|
|
231
|
+
* off, against 7 frames of antialiasing once deferred. Both failures are
|
|
232
|
+
* invisible to every gate. `cameraMeasure` carries the numbers and the controls.
|
|
233
|
+
*
|
|
234
|
+
* INVARIANT 11 IS THE TRAP HERE. This is not a callback on a tween. `seek()`
|
|
235
|
+
* passes `suppressEvents`, so measuring from an `onUpdate` — the obvious way to
|
|
236
|
+
* "measure late" — renders a frozen video with every gate green. Measurement
|
|
237
|
+
* happens before the timeline exists, and what it produces is ordinary tween
|
|
238
|
+
* values.
|
|
239
|
+
*/
|
|
240
|
+
measure?: string[];
|
|
241
|
+
/**
|
|
242
|
+
* Hold points in seconds from the scene's start — where a presenter should
|
|
243
|
+
* pause. The shell converts these to absolute island fragment times.
|
|
244
|
+
*/
|
|
245
|
+
holds: number[];
|
|
246
|
+
/** CSS this archetype needs. Deduplicated by the shell, emitted once. */
|
|
247
|
+
css?: string;
|
|
248
|
+
}
|
|
249
|
+
export type Emitter<A extends Archetype> = (beat: BeatOf<A>, ctx: EmitContext) => Scene;
|
|
250
|
+
/** Escape text destined for HTML text content or a quoted attribute. */
|
|
251
|
+
export declare function esc(s: string): string;
|
|
252
|
+
export declare function mathy(raw: string): string;
|
|
253
|
+
/** Whether a scene needs the KaTeX pass. Cheap enough to ask per scene. */
|
|
254
|
+
export declare const TEX_MARK = "ds-tex";
|
|
255
|
+
/** Escape a string for embedding inside a single-quoted JS literal. */
|
|
256
|
+
export declare function js(s: string): string;
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Geometry, text metrics and SVG strings. Nothing above that.
|
|
3
|
+
*
|
|
4
|
+
* Six diagrammatic archetypes draw vector graphics that the source document
|
|
5
|
+
* never contained. Left to themselves each would re-derive the same four things
|
|
6
|
+
* — how wide a label renders, where N things go across a canvas, how to draw an
|
|
7
|
+
* arrowhead, how to escape a caption — and they would each get them slightly
|
|
8
|
+
* wrong in a different way. `line-chart` shipped 3.6px from clipping because it
|
|
9
|
+
* guessed its own em-factor; that guess now lives here, once.
|
|
10
|
+
*
|
|
11
|
+
* This module knows about characters, boxes and paths. It does not know what a
|
|
12
|
+
* pipeline stage or a receptive field is — that knowledge belongs to the emitter
|
|
13
|
+
* that composes these primitives, and putting it here is how a primitive layer
|
|
14
|
+
* turns into six hand-rolled one-offs wearing a shared import.
|
|
15
|
+
*/
|
|
16
|
+
import { type Vars } from "./kit.js";
|
|
17
|
+
export interface Pt {
|
|
18
|
+
x: number;
|
|
19
|
+
y: number;
|
|
20
|
+
}
|
|
21
|
+
export interface Box {
|
|
22
|
+
x: number;
|
|
23
|
+
y: number;
|
|
24
|
+
w: number;
|
|
25
|
+
h: number;
|
|
26
|
+
}
|
|
27
|
+
/** Raw SVG attribute names to values. `undefined` and `""` are dropped. */
|
|
28
|
+
export type Attrs = Record<string, string | number | undefined>;
|
|
29
|
+
/**
|
|
30
|
+
* Audience text is never smaller than this at 1920x1080. Below it a slide passes
|
|
31
|
+
* every automated gate and is unreadable from the third row, which is the only
|
|
32
|
+
* test that matters and the only one we cannot run.
|
|
33
|
+
*/
|
|
34
|
+
export declare const MIN_FONT = 40;
|
|
35
|
+
/**
|
|
36
|
+
* Two decimals. Floating point makes `x(3)` print as `412.00000000000006`, and a
|
|
37
|
+
* render that differs from the last one by a digit is a regression test we can
|
|
38
|
+
* no longer run — byte-identical output is the cheapest correctness signal here.
|
|
39
|
+
*/
|
|
40
|
+
export declare function n(v: number): string;
|
|
41
|
+
/**
|
|
42
|
+
* `n`, where a NUMBER rather than its text is wanted — a tween's vars payload is
|
|
43
|
+
* now a typed object, so a coordinate that used to be interpolated into a string
|
|
44
|
+
* has to arrive as the value it is. Same rounding, so `String(nv(x)) === n(x)`
|
|
45
|
+
* by construction and geometry that goes through a tween still prints exactly
|
|
46
|
+
* what the same geometry in an SVG attribute prints.
|
|
47
|
+
*/
|
|
48
|
+
export declare function nv(v: number): number;
|
|
49
|
+
/**
|
|
50
|
+
* The `from` vars of a stroke draw-on, with the path fully hidden.
|
|
51
|
+
*
|
|
52
|
+
* The length is rounded UP rather than to two decimals, and then given a pixel
|
|
53
|
+
* of slack, because GSAP writes `strokeDashoffset` back as an integer px: a
|
|
54
|
+
* leader of length 720.21 came to rest at an offset of 720 and left 0.21px of
|
|
55
|
+
* the dash exposed at the path start, which `stroke-linecap: round` paints as a
|
|
56
|
+
* full-width dot. A yellow dot therefore sat on the grid one reveal before its
|
|
57
|
+
* own region was drawn — inside the frame, above the type floor, and invisible
|
|
58
|
+
* to every gate. Any offset between the length and twice it is still inside the
|
|
59
|
+
* pattern's gap, so the slack costs nothing.
|
|
60
|
+
*/
|
|
61
|
+
export declare function drawFrom(length: number): Vars;
|
|
62
|
+
/** Scene-scoped element id. Every timeline selector is built from one of these. */
|
|
63
|
+
export declare function id(sid: string, part: string, i?: number): string;
|
|
64
|
+
/**
|
|
65
|
+
* Estimated rendered width in px. Linear in `fontSize`, which is what lets
|
|
66
|
+
* `fitBoxes` solve for a size instead of searching for one.
|
|
67
|
+
*
|
|
68
|
+
* Every archetype sizes its boxes and padding through this function. When two of
|
|
69
|
+
* them disagree about how wide "Reconstruction" is, one of them clips.
|
|
70
|
+
*/
|
|
71
|
+
export declare function textWidth(text: string, fontSize: number, weight?: number, tracking?: number, tabular?: boolean): number;
|
|
72
|
+
/**
|
|
73
|
+
* Greedy word wrap to `maxWidth`. A word wider than the line is broken by
|
|
74
|
+
* character — which is also how Korean and Chinese wrap, since they arrive as
|
|
75
|
+
* one unbroken "word".
|
|
76
|
+
*/
|
|
77
|
+
export declare function wrap(text: string, fontSize: number, maxWidth: number, weight?: number, tracking?: number): string[];
|
|
78
|
+
/** The SVG root. Width and height match the viewBox so px and user units agree. */
|
|
79
|
+
export declare function svg(elementId: string, w: number, h: number, body: string): string;
|
|
80
|
+
export declare function rect(b: Box, a?: Attrs): string;
|
|
81
|
+
export declare function roundRect(b: Box, r: number, a?: Attrs): string;
|
|
82
|
+
export declare function line(from: Pt, to: Pt, a?: Attrs): string;
|
|
83
|
+
export declare function path(d: string, a?: Attrs): string;
|
|
84
|
+
export declare function circle(c: Pt, r: number, a?: Attrs): string;
|
|
85
|
+
export declare function group(children: string | string[], a?: Attrs): string;
|
|
86
|
+
export interface TextOptions {
|
|
87
|
+
size: number;
|
|
88
|
+
fill?: string;
|
|
89
|
+
weight?: number;
|
|
90
|
+
anchor?: "start" | "middle" | "end";
|
|
91
|
+
/** Wrap to this width. Omit for a single line. */
|
|
92
|
+
maxWidth?: number;
|
|
93
|
+
/** Baseline separation as a multiple of `size`. */
|
|
94
|
+
lineHeight?: number;
|
|
95
|
+
/**
|
|
96
|
+
* `"baseline"` (default) puts the first baseline at `y`. `"middle"` centres the
|
|
97
|
+
* whole block on `y` — the one a caller labelling a box wants, and the one that
|
|
98
|
+
* is wrong in a different way for every line count if each emitter derives it.
|
|
99
|
+
*/
|
|
100
|
+
vAlign?: "baseline" | "middle";
|
|
101
|
+
class?: string;
|
|
102
|
+
id?: string;
|
|
103
|
+
}
|
|
104
|
+
export declare function text(content: string, at: Pt, o: TextOptions): string;
|
|
105
|
+
export interface ArrowOptions {
|
|
106
|
+
stroke: string;
|
|
107
|
+
width?: number;
|
|
108
|
+
/** e.g. `"14 12"` for a feedback loop that should read as a return path. */
|
|
109
|
+
dash?: string;
|
|
110
|
+
/** Pull the head back off the target's edge. */
|
|
111
|
+
inset?: number;
|
|
112
|
+
class?: string;
|
|
113
|
+
id?: string;
|
|
114
|
+
}
|
|
115
|
+
/** Emit once per scene, with every colour that scene's arrows use. */
|
|
116
|
+
export declare function arrowDefs(sid: string, colors: string[]): string;
|
|
117
|
+
/** A straight connector with a head at `to`. */
|
|
118
|
+
export declare function arrow(sid: string, from: Pt, to: Pt, o: ArrowOptions): string;
|
|
119
|
+
export interface ElbowOptions extends ArrowOptions {
|
|
120
|
+
/** The coordinate of the long middle leg: a y when `axis` is `"v"`, else an x. */
|
|
121
|
+
via: number;
|
|
122
|
+
/** `"v"` leaves and arrives vertically — the shape a feedback loop under a row wants. */
|
|
123
|
+
axis?: "v" | "h";
|
|
124
|
+
/** Corner radius, clamped to half the shortest leg. */
|
|
125
|
+
radius?: number;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* A three-leg orthogonal connector: out, along, back in. This is what a feedback
|
|
129
|
+
* arrow needs — a straight line from the last stage to the first would cut
|
|
130
|
+
* through every box between them.
|
|
131
|
+
*/
|
|
132
|
+
export declare function elbow(sid: string, from: Pt, to: Pt, o: ElbowOptions): string;
|
|
133
|
+
export interface Track {
|
|
134
|
+
x: number;
|
|
135
|
+
w: number;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* `count` equal tracks spanning `width`, separated by `gap`. Use it for columns
|
|
139
|
+
* or, with the result read as `y`/`h`, for rows — the arithmetic is the same and
|
|
140
|
+
* having it twice is how a diagram ends up 1px off its own grid.
|
|
141
|
+
*/
|
|
142
|
+
export declare function tracks(width: number, count: number, gap: number, x0?: number): Track[];
|
|
143
|
+
export interface FitRequest {
|
|
144
|
+
labels: string[];
|
|
145
|
+
width: number;
|
|
146
|
+
/** Preferred type size. Only reduced once `gap` has already reached `minGap`. */
|
|
147
|
+
size: number;
|
|
148
|
+
gap: number;
|
|
149
|
+
minGap?: number;
|
|
150
|
+
/** Inner padding per side, as a multiple of the type size. */
|
|
151
|
+
padEm?: number;
|
|
152
|
+
weight?: number;
|
|
153
|
+
x0?: number;
|
|
154
|
+
}
|
|
155
|
+
export interface Fit {
|
|
156
|
+
/**
|
|
157
|
+
* False when the labels cannot be set at `MIN_FONT` in `width`. The geometry is
|
|
158
|
+
* still returned so a caller can see by how much, but it will clip — the point
|
|
159
|
+
* of reporting is that the caller picks a different composition (stack them,
|
|
160
|
+
* split the beat) instead of shipping a slide nobody can read.
|
|
161
|
+
*/
|
|
162
|
+
ok: boolean;
|
|
163
|
+
size: number;
|
|
164
|
+
gap: number;
|
|
165
|
+
boxes: Track[];
|
|
166
|
+
/** Width the composition would need at the floor. Present only when `!ok`. */
|
|
167
|
+
needed?: number;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Fit N labelled boxes across a width.
|
|
171
|
+
*
|
|
172
|
+
* Gaps go first because whitespace is cheaper than legibility, and type stops at
|
|
173
|
+
* `MIN_FONT` because below it the slide passes every gate and fails the room.
|
|
174
|
+
* `textWidth` is linear in size, so the largest size that fits is a division
|
|
175
|
+
* rather than a search.
|
|
176
|
+
*/
|
|
177
|
+
export declare function fitBoxes(req: FitRequest): Fit;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shared stylesheet, the ambient gate, and the deck's pacing.
|
|
3
|
+
*
|
|
4
|
+
* The palettes themselves moved to `./themes/` once there was more than one;
|
|
5
|
+
* this file stays the door to them, so nothing that imports `ambient` or `ink`
|
|
6
|
+
* had to be touched to gain a registry.
|
|
7
|
+
*
|
|
8
|
+
* What is left here is everything a theme does NOT decide per-theme: the reset,
|
|
9
|
+
* the scene box, the two ambient keyframes, and `pace`. Typography belongs to
|
|
10
|
+
* the archetype that draws it, not to this file. Two stylesheets describing one
|
|
11
|
+
* `.figwrap` is how a rule ends up being decided by emission order, so nothing
|
|
12
|
+
* an emitter styles is styled here as well.
|
|
13
|
+
*/
|
|
14
|
+
import type { Format } from "../types.js";
|
|
15
|
+
import { type Scene } from "./kit.js";
|
|
16
|
+
import type { DeckTheme } from "./themes/index.js";
|
|
17
|
+
export { type DeckTheme, ink, mono, paper, resolveTheme, THEME_NAMES, THEMES, } from "./themes/index.js";
|
|
18
|
+
/** Where `ingest` writes the subsetted font bundle, relative to the deck. */
|
|
19
|
+
export declare const FONT_BUNDLE_HREF = "assets/fonts/fonts.css";
|
|
20
|
+
/** A luminance breath. Safe on an element an entrance tween already moves. */
|
|
21
|
+
export declare const BREATHE = "ds-breathe 6s ease-in-out infinite alternate";
|
|
22
|
+
/** A 1.2% swell. Only for an element no `fromTo` targets — it writes `transform`. */
|
|
23
|
+
export declare const DRIFT = "ds-drift 11s ease-in-out infinite alternate";
|
|
24
|
+
/**
|
|
25
|
+
* One ambient rule for one focal element.
|
|
26
|
+
*
|
|
27
|
+
* `rest` is glued straight onto `#<sid>`, so the selector cannot escape its own
|
|
28
|
+
* scene however the caller writes it — `" .dot:last-of-type"` and `"-r2 td"` are
|
|
29
|
+
* both scoped. Reduced motion is the default: stillness needs no gate, motion
|
|
30
|
+
* does.
|
|
31
|
+
*/
|
|
32
|
+
export declare function ambient(sid: string, rest: string, animation: string): string;
|
|
33
|
+
export declare function baseCss(theme: DeckTheme, format: Format): string;
|
|
34
|
+
/**
|
|
35
|
+
* Every time in a finished scene, multiplied by `speed`.
|
|
36
|
+
*
|
|
37
|
+
* `prefs.animationSpeed` sits under `look` beside `theme` for a reason: it is
|
|
38
|
+
* the same kind of decision, and this is the same place to apply it. It is
|
|
39
|
+
* applied to the emitted Scene rather than inside `tween()` because half of a
|
|
40
|
+
* timeline's arithmetic never passes through `tween()` — an emitter works out
|
|
41
|
+
* `settled = rowsIn + stagger * rows + 0.45`, hands the result to `holdsWithin`,
|
|
42
|
+
* and only the two endpoints ever reach a helper. Scaling the finished scene
|
|
43
|
+
* catches the tween, its stagger and its position with one rule, and scaling
|
|
44
|
+
* `holds` by the identical factor is what keeps a hold on the frame it was
|
|
45
|
+
* authored for. A hold that drifts off its tween lands navigation on a
|
|
46
|
+
* half-built slide, which no gate can see.
|
|
47
|
+
*
|
|
48
|
+
* This used to scale the emitted STATEMENT TEXT, and recovered the position
|
|
49
|
+
* argument with `/,\s*(-?\d*\.?\d+)\s*\)\s*;?\s*$/` — a regex parsing GSAP out
|
|
50
|
+
* of a string, on the path every deck's timing goes through. `Tween` is a typed
|
|
51
|
+
* object now, so the position is a field and the durations are fields, and this
|
|
52
|
+
* is the arithmetic it always wanted to be.
|
|
53
|
+
*
|
|
54
|
+
* The caller must scale the beat's own `seconds` by the same factor, or a
|
|
55
|
+
* slowed deck cuts its last reveal off and a hurried one sits on a finished
|
|
56
|
+
* slide. That is the shell's arithmetic, not a scene's, so it is not done here.
|
|
57
|
+
*
|
|
58
|
+
* Ambient CSS is left alone: it loops forever and nothing waits on it, so its
|
|
59
|
+
* period is a property of the room rather than of the deck's pace.
|
|
60
|
+
*
|
|
61
|
+
* `speed === 1` returns the scene untouched — identity by construction, not by
|
|
62
|
+
* arithmetic that happens to round back. That is what keeps a default deck
|
|
63
|
+
* byte-identical to one built before this existed.
|
|
64
|
+
*/
|
|
65
|
+
export declare function pace(scene: Scene, speed: number): Scene;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The theme registry.
|
|
3
|
+
*
|
|
4
|
+
* A theme is a name and a palette, and that is the whole extension point: a new
|
|
5
|
+
* one is a file here plus a line in `THEMES`, and nothing in the shell or in any
|
|
6
|
+
* archetype learns it exists. Emitters read `ctx.theme` and never a theme name —
|
|
7
|
+
* the moment one branches on `name === "paper"` the registry has stopped being a
|
|
8
|
+
* registry and the archetype has acquired a second, invisible stylesheet.
|
|
9
|
+
*
|
|
10
|
+
* Three of them, each a position rather than a hue: dark room, lit room, and the
|
|
11
|
+
* room where colour does not survive.
|
|
12
|
+
*/
|
|
13
|
+
import type { Theme } from "../kit.js";
|
|
14
|
+
import { ink } from "./ink.js";
|
|
15
|
+
import { mono } from "./mono.js";
|
|
16
|
+
import { paper } from "./paper.js";
|
|
17
|
+
/**
|
|
18
|
+
* `Theme` plus what only the shared stylesheet reads.
|
|
19
|
+
*
|
|
20
|
+
* `Theme` in `kit.ts` is the archetypes' contract and stays exactly as wide as
|
|
21
|
+
* they need. Body weight is not theirs — every archetype sets its own weights —
|
|
22
|
+
* it belongs to the one `body` rule in `baseCss`, so it lives out here where a
|
|
23
|
+
* theme can carry it and an emitter cannot see it.
|
|
24
|
+
*/
|
|
25
|
+
export interface DeckTheme extends Theme {
|
|
26
|
+
/** `body`'s font-weight. Omitted means 400, which is what `ink` and `paper` want. */
|
|
27
|
+
bodyWeight?: number;
|
|
28
|
+
}
|
|
29
|
+
export declare const THEMES: Readonly<Record<string, DeckTheme>>;
|
|
30
|
+
/** Sorted, so an error message and a `--help` listing agree without coordinating. */
|
|
31
|
+
export declare const THEME_NAMES: readonly string[];
|
|
32
|
+
/**
|
|
33
|
+
* A theme by name, or a failure that says what the names are.
|
|
34
|
+
*
|
|
35
|
+
* Throwing rather than falling back to `ink`: a misspelt theme that silently
|
|
36
|
+
* renders in the default is a whole deck built wrong, discovered at the point
|
|
37
|
+
* someone presents it.
|
|
38
|
+
*/
|
|
39
|
+
export declare function resolveTheme(name: string): DeckTheme;
|
|
40
|
+
export { ink, mono, paper };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The original: a deep blue-black ground with cool light on it.
|
|
3
|
+
*
|
|
4
|
+
* Written for a dark room and a projector that has one job. The four tones are
|
|
5
|
+
* four hues at roughly one value — nothing here is "the important one", because
|
|
6
|
+
* an archetype assigns them by position in a list, not by rank.
|
|
7
|
+
*
|
|
8
|
+
* Its numbers are frozen. Every render this project has ever compared against
|
|
9
|
+
* came out of them, so a nicer blue is a broken regression test.
|
|
10
|
+
*/
|
|
11
|
+
import type { DeckTheme } from "./index.js";
|
|
12
|
+
export declare const ink: DeckTheme;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Near-monochrome: white ground, black type, one red, used rarely.
|
|
3
|
+
*
|
|
4
|
+
* For the two rooms where colour is a lie — the conference projector with a
|
|
5
|
+
* blown lamp and a washed-out gamma, and the greyscale printer. Both destroy
|
|
6
|
+
* hue and keep value, so this theme encodes everything in value and spends its
|
|
7
|
+
* single hue on the one thing per slide that must be found.
|
|
8
|
+
*
|
|
9
|
+
* That is what makes the tones different in kind from the other two themes'.
|
|
10
|
+
* `a` is the accent; `b`, `c`, `d` are a ladder of greys about two stops apart,
|
|
11
|
+
* so they stay told apart after a photocopier has finished with them. Four hues
|
|
12
|
+
* would have collapsed into one grey, which is the failure this theme exists to
|
|
13
|
+
* avoid. The ladder bottoms out at #6f6f6f because a lighter grey drops below
|
|
14
|
+
* 4.5:1 on the panel.
|
|
15
|
+
*
|
|
16
|
+
* `bodyWeight` is the other half of it: a stop of extra weight is what keeps
|
|
17
|
+
* type legible once a bad projector has eaten the thin end of every stroke.
|
|
18
|
+
*/
|
|
19
|
+
import type { DeckTheme } from "./index.js";
|
|
20
|
+
export declare const mono: DeckTheme;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Light: a warm off-white ground, near-black text, one quiet accent.
|
|
3
|
+
*
|
|
4
|
+
* For a lit room, a handout, and the screenshot that ends up in a doc. It is not
|
|
5
|
+
* `ink` inverted — inverting a dark theme yields pastel tones that vanish on
|
|
6
|
+
* white, because a hue bright enough to read on #0b0d10 is by definition close
|
|
7
|
+
* to the light end.
|
|
8
|
+
*
|
|
9
|
+
* So the tones are re-picked rather than flipped, and picked to one rule: each
|
|
10
|
+
* clears 4.5:1 on both the ground and the panel, and all four sit at nearly the
|
|
11
|
+
* same value, so they read as four hues rather than as a ranking. That is the
|
|
12
|
+
* mirror of `ink`'s arrangement, arrived at from the opposite direction.
|
|
13
|
+
*
|
|
14
|
+
* The accent is deliberately dull for a light theme. Saturated blue on warm
|
|
15
|
+
* white is the one combination that looks like a default rather than a choice.
|
|
16
|
+
*/
|
|
17
|
+
import type { DeckTheme } from "./index.js";
|
|
18
|
+
export declare const paper: DeckTheme;
|