@jokerized/decksmith 0.9.2 → 0.9.4
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 +449 -90
- package/dist/animate/LICENSE +21 -0
- package/dist/animate/NOTICE +32 -0
- package/dist/animate/core.js +530 -0
- package/dist/animate/cut-paper.js +206 -0
- package/dist/animate/morph.js +131 -0
- package/dist/cli.js +13464 -8876
- package/dist/ds-animate.js +1 -0
- package/dist/index.js +13245 -8657
- package/dist/mcp.js +9778 -7477
- package/dist/types/bespoke/art.d.ts +172 -6
- package/dist/types/bespoke/cache.d.ts +10 -0
- package/dist/types/bespoke/cards.d.ts +66 -0
- package/dist/types/bespoke/contract.d.ts +0 -8
- package/dist/types/bespoke/databuild.d.ts +50 -0
- package/dist/types/bespoke/depth.d.ts +127 -0
- package/dist/types/bespoke/grammar.d.ts +73 -0
- package/dist/types/bespoke/inspect.d.ts +40 -1
- package/dist/types/bespoke/pipeline.d.ts +195 -11
- package/dist/types/bespoke/probe.d.ts +2 -2
- package/dist/types/bespoke/prompt.d.ts +110 -34
- package/dist/types/bespoke/references.d.ts +13 -14
- package/dist/types/bespoke/repair.d.ts +23 -29
- package/dist/types/bespoke/scene.d.ts +55 -10
- package/dist/types/bespoke/select.d.ts +28 -21
- package/dist/types/bespoke/sheet.d.ts +66 -1
- package/dist/types/bespoke/shots.d.ts +225 -22
- package/dist/types/build/files.d.ts +7 -0
- package/dist/types/build/piece.d.ts +8 -0
- package/dist/types/emit/animate-runtime.d.ts +149 -0
- package/dist/types/emit/archetypes/claim-figure.d.ts +84 -1
- package/dist/types/emit/archetypes/equation-walk.d.ts +2 -2
- package/dist/types/emit/archetypes/hero-number.d.ts +21 -0
- package/dist/types/emit/archetypes/index.d.ts +2 -2
- package/dist/types/emit/archetypes/kinetic.d.ts +2 -0
- package/dist/types/emit/archetypes/pipeline.d.ts +14 -2
- package/dist/types/emit/archetypes/stage.d.ts +57 -0
- package/dist/types/emit/backdrop.d.ts +114 -0
- package/dist/types/emit/kit.d.ts +8 -0
- package/dist/types/emit/look.d.ts +14 -0
- package/dist/types/emit/motion.d.ts +78 -46
- package/dist/types/emit/type.d.ts +48 -5
- package/dist/types/images/providers.d.ts +6 -0
- package/dist/types/plan/codex.d.ts +4 -2
- package/dist/types/plan/direct.d.ts +1 -1
- package/dist/types/plan/duration.d.ts +1 -1
- package/dist/types/plan/prompt.d.ts +1 -1
- package/dist/types/plan/refs.d.ts +2 -2
- package/dist/types/plan/variety.d.ts +74 -0
- package/dist/types/prefs.d.ts +20 -1
- package/dist/types/render/captions.d.ts +4 -5
- package/dist/types/render/capture.d.ts +7 -0
- package/dist/types/source/fonts.d.ts +22 -0
- package/dist/types/types.d.ts +680 -4
- package/dist/types/verify/drift.d.ts +5 -0
- package/dist/types/verify/fidelity.d.ts +15 -1
- package/dist/types/verify/index.d.ts +27 -4
- package/dist/types/verify/scenes.d.ts +185 -5
- package/package.json +1 -1
- package/dist/types/bespoke/callouts.d.ts +0 -76
|
@@ -1,30 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Deterministic repair of a generated scene the gates refused for a reason a
|
|
3
|
-
* person would fix by hand in seconds — instead of throwing the scene away.
|
|
4
|
-
*
|
|
5
|
-
* ROUND 2 LOST 4 OF 21 BEATS to `text_overlap` / `graphic_crosses_text` after
|
|
6
|
-
* the critique round: big type collides more, and a model fixing one collision
|
|
7
|
-
* by rewriting coordinates makes the next one. Round 2 also kept three scenes
|
|
8
|
-
* that ended on a frame of ghosts. None of that needs another model call:
|
|
9
|
-
*
|
|
10
|
-
* - LABEL COLLISIONS are solved as constraint relaxation over the frames the
|
|
11
|
-
* probe measured. Each colliding label's unit — the `<text>`, or the
|
|
12
|
-
* smallest `<g>` holding it with its plate and no other label — gets ONE
|
|
13
|
-
* constant offset, the smallest (in rings of `STEP` px, sixteen directions
|
|
14
|
-
* in a fixed order) that clears every other label by `LABEL_GAP`, every
|
|
15
|
-
* stroke sample by `STROKE_GAP`, every shape painted over it, and stays in
|
|
16
|
-
* the box, at EVERY measured frame, with the camera's scale at each frame
|
|
17
|
-
* applied. The offset becomes a wrapping `<g transform="translate()">` in
|
|
18
|
-
* the markup, so the scene's own tweens on the unit are untouched.
|
|
19
|
-
* - A DIMMED END (`end_dimmed`): the elements dimming parts the scene had lit
|
|
20
|
-
* are tweened back to opacity 1 just before the scene ends.
|
|
21
|
-
* - A CAMERA LEFT ZOOMED (`camera_end`): the camera is tweened home.
|
|
22
|
-
*
|
|
23
|
-
* Every repair is appended as ordinary contract code (a `tl.to` with literal
|
|
24
|
-
* vars at a literal second, or a wrapper `<g>`), goes through `checkFragment`
|
|
25
|
-
* like a model's, and is gated again before it is used. Deterministic: the same
|
|
26
|
-
* geometry gives the same bytes, so a repaired scene caches like any other.
|
|
27
|
-
*/
|
|
28
1
|
import type { Geo, Layout } from "../verify/scenes.js";
|
|
29
2
|
import { type Fragment } from "./contract.js";
|
|
30
3
|
/** Error rules this module can fix. A scene failing anything else is not repaired. */
|
|
@@ -87,16 +60,37 @@ export declare function fitPlates(markup: string, frames: readonly Geo[] | Geo |
|
|
|
87
60
|
export declare function settleAt(duration: number, lastCueStart: number): number;
|
|
88
61
|
/**
|
|
89
62
|
* Bring the elements dimming lit parts back to full strength at `at`. Targets
|
|
90
|
-
* are the probe's `tag:index[#id]`, ids in the probe deck's form
|
|
91
|
-
* an element without an id gets one (`SCENEID-lit<k>`).
|
|
63
|
+
* are the probe's `tag:index[#id]@opacity`, ids in the probe deck's form
|
|
64
|
+
* (`sid-…`); an element without an id gets one (`SCENEID-lit<k>`). The opacity
|
|
65
|
+
* each holds is its tween's from state (every tween is a fromTo); a target
|
|
66
|
+
* without one is left alone.
|
|
92
67
|
*/
|
|
93
68
|
export declare function relight(f: Fragment, targets: readonly string[], at: number, sid: string): Fragment;
|
|
94
69
|
/** Send the camera home at `at`: the shell's wrapper, or the svg's viewBox. */
|
|
95
70
|
export declare function homeCamera(f: Fragment, camOff: string, at: number): Fragment;
|
|
96
71
|
/** What a repair did, for the report. */
|
|
72
|
+
/**
|
|
73
|
+
* The lowest opacity a word is dimmed to: dark ink at 0.6 over a pale ground
|
|
74
|
+
* is ~4:1, a pack tone ~3:1 (the `dim_text` floor). The prompt asks for it.
|
|
75
|
+
*/
|
|
76
|
+
export declare const TEXT_ALPHA_MIN = 0.6;
|
|
77
|
+
/** The ids in `markup` whose element is, or holds, words (an SVG <text>, or text of its own). */
|
|
78
|
+
export declare function textHolders(markup: string): Set<string>;
|
|
79
|
+
/**
|
|
80
|
+
* `dim_text`'s repair: every literal `opacity`/`autoAlpha` in (0, TEXT_ALPHA_MIN)
|
|
81
|
+
* in a `tl.*`/`gsap.set` whose targets — `#id` selectors, literally or through a
|
|
82
|
+
* top-level variable — include an element holding words, raised to
|
|
83
|
+
* TEXT_ALPHA_MIN. Undefined when there is nothing it can read to lift.
|
|
84
|
+
*/
|
|
85
|
+
export declare function liftText(f: Fragment): {
|
|
86
|
+
fragment: Fragment;
|
|
87
|
+
lifted: number;
|
|
88
|
+
} | undefined;
|
|
97
89
|
export interface RepairNote {
|
|
98
90
|
/** Overlapping tweens on one property were untangled (`seek_order`). */
|
|
99
91
|
untangled?: boolean;
|
|
92
|
+
/** Dimming opacities on words raised to `TEXT_ALPHA_MIN` (`dim_text`). */
|
|
93
|
+
lifted?: number;
|
|
100
94
|
moved: number;
|
|
101
95
|
/** Plates grown to hold their text (`fitPlates`). */
|
|
102
96
|
grown?: number;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import type { EmitContext, Scene } from "../emit/kit.js";
|
|
2
|
-
import type { Beat } from "../types.js";
|
|
1
|
+
import type { EmitContext, Scene, Theme } from "../emit/kit.js";
|
|
2
|
+
import type { Beat, Format } from "../types.js";
|
|
3
3
|
import { type ArtRef } from "./art.js";
|
|
4
|
-
import { type Zone } from "./callouts.js";
|
|
5
4
|
import { type Fragment } from "./contract.js";
|
|
6
|
-
import
|
|
5
|
+
import type { DataBuild } from "./databuild.js";
|
|
6
|
+
import type { Grammar } from "./grammar.js";
|
|
7
|
+
import { type Box, type Staging } from "./shots.js";
|
|
7
8
|
/** One beat's bespoke scene, ready to emit. */
|
|
8
9
|
export interface BespokeEntry {
|
|
9
10
|
/** Token form, already through `checkFragment`. */
|
|
@@ -24,6 +25,10 @@ export interface BespokeEntry {
|
|
|
24
25
|
}>;
|
|
25
26
|
duration: number;
|
|
26
27
|
};
|
|
28
|
+
/** The camera grammar an illustrated scene is staged in (src/bespoke/grammar.ts). Absent: "tour". */
|
|
29
|
+
grammar?: Grammar;
|
|
30
|
+
/** A data scene's build (src/bespoke/databuild.ts), stamped on the scene for the deck gate. */
|
|
31
|
+
build?: DataBuild;
|
|
27
32
|
}
|
|
28
33
|
/** The file `build` writes beside a deck with the bespoke pass's account of itself. */
|
|
29
34
|
export declare const BESPOKE_FILE = "bespoke.json";
|
|
@@ -36,8 +41,29 @@ export declare function bespokeRegion(beat: Beat, ctx: Pick<EmitContext, "format
|
|
|
36
41
|
width: number;
|
|
37
42
|
height: number;
|
|
38
43
|
};
|
|
44
|
+
/**
|
|
45
|
+
* ROUND 6: an illustrated scene is the whole frame. Its picture is a full-bleed
|
|
46
|
+
* shot behind the deck's own headline (which keeps its place and its entrance),
|
|
47
|
+
* so its box is the frame, and the headline's band at the top is the one place
|
|
48
|
+
* the scene's own words may not go (`chromeBand`).
|
|
49
|
+
*/
|
|
50
|
+
export declare function cineRegion(format: Format): {
|
|
51
|
+
width: number;
|
|
52
|
+
height: number;
|
|
53
|
+
};
|
|
54
|
+
/** How far down the frame the headline reaches, px: the scene's own words stay below it. */
|
|
55
|
+
export declare function chromeBand(beat: Beat, ctx: Pick<EmitContext, "format" | "theme">): number;
|
|
39
56
|
/** Whether a scene's script moves the shell's camera (`#SCENEID-cam`). */
|
|
40
57
|
export declare function usesCamera(script: string): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* The shell's (round 6): every word under a scene's own camera keeps the size it
|
|
60
|
+
* declares. For each camera tween — the wrapper's `scale`, or the svg's
|
|
61
|
+
* `viewBox` — the words get the inverse scale about their own centres over the
|
|
62
|
+
* same span, so a push-in enlarges the drawing and never the type (the r1
|
|
63
|
+
* review found labels at 75-80px under zoom; `type_scale` holds the rendered
|
|
64
|
+
* size). Tweens marked `data: "shell"`, which `ui_motion` does not count.
|
|
65
|
+
*/
|
|
66
|
+
export declare function quietWords(sid: string): string;
|
|
41
67
|
/**
|
|
42
68
|
* The illustration's href AND its placement, written by the shell into the
|
|
43
69
|
* scene's one `<image data-art="1">` — the contract refuses an href from the
|
|
@@ -49,7 +75,7 @@ export declare function usesCamera(script: string): boolean;
|
|
|
49
75
|
export declare function withArt(markup: string, art: ArtRef | undefined, box?: {
|
|
50
76
|
width: number;
|
|
51
77
|
height: number;
|
|
52
|
-
}): string;
|
|
78
|
+
}, clip?: string): string;
|
|
53
79
|
/**
|
|
54
80
|
* The subjects' boxes as the gates see them: invisible rects inside the camera
|
|
55
81
|
* (so they move with it), after the scene's markup (so the repair pass's
|
|
@@ -57,18 +83,37 @@ export declare function withArt(markup: string, art: ArtRef | undefined, box?: {
|
|
|
57
83
|
*/
|
|
58
84
|
export declare function subjectLayer(subjects: readonly Box[], width: number, height: number): string;
|
|
59
85
|
/**
|
|
60
|
-
* An illustrated entry's subjects in
|
|
61
|
-
*
|
|
62
|
-
*
|
|
86
|
+
* An illustrated entry's subjects in box px and the camera moves its shots
|
|
87
|
+
* compile to. In a depth scene (round 6) the subjects are where the depth
|
|
88
|
+
* planes put them.
|
|
63
89
|
*/
|
|
64
90
|
export declare function staging(entry: BespokeEntry, box: {
|
|
65
91
|
width: number;
|
|
66
92
|
height: number;
|
|
67
93
|
}): {
|
|
68
94
|
subjects: Box[];
|
|
69
|
-
|
|
70
|
-
moves: CamMove[];
|
|
95
|
+
staged: Staging;
|
|
71
96
|
};
|
|
97
|
+
/** The picture's subjects in box px: from its depth planes when it has them. */
|
|
98
|
+
export declare function subjectsOf(art: ArtRef, box: {
|
|
99
|
+
width: number;
|
|
100
|
+
height: number;
|
|
101
|
+
}): Box[];
|
|
102
|
+
/**
|
|
103
|
+
* ROUND 6: the picture as depth planes, far to near, each a frame-sized layer
|
|
104
|
+
* the camera script moves at its own distance (shots.ts `planeFrame`). The
|
|
105
|
+
* backdrop's planes cover the frame; each subject is its own plane, holding a
|
|
106
|
+
* `.ds-life` wrapper its breathing moves. Then the air in front of them: the
|
|
107
|
+
* light that sweeps, the vignette, and the scrims under the headline and the
|
|
108
|
+
* subtitles. Nothing here is text, and the farthest plane keeps round 5's
|
|
109
|
+
* `#sid-plate` id (the gates read the backdrop's parallax off it). A plane in
|
|
110
|
+
* a push-in is mostly outside the frame by design, so each carries
|
|
111
|
+
* hyperframes' `data-layout-allow-overflow` (its `escaped_container` warning
|
|
112
|
+
* would otherwise send every scene to a critique call).
|
|
113
|
+
*/
|
|
114
|
+
export declare function depthLayers(sid: string, art: ArtRef, W: number, H: number, theme: Theme, band: number): string;
|
|
115
|
+
/** `#rrggbb` with an alpha, as `rgba()`. */
|
|
116
|
+
export declare function hexA(hex: string, a: number): string;
|
|
72
117
|
/** CSS feather for an illustration's edges: 6% at the sides, 10% top and bottom. */
|
|
73
118
|
export declare const ART_FEATHER = "-webkit-mask-image:linear-gradient(to right,transparent,#000 6%,#000 94%,transparent),linear-gradient(to bottom,transparent,#000 10%,#000 90%,transparent);-webkit-mask-composite:source-in;mask-composite:intersect";
|
|
74
119
|
/** Whether a scene's script tweens `morphSVG` (MorphSVGPlugin must be registered first). */
|
|
@@ -1,29 +1,36 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Which beats get a bespoke scene
|
|
3
|
-
*
|
|
2
|
+
* Which beats get a bespoke scene: under `--design v2`, every beat that can
|
|
3
|
+
* have one — title, numbers, comparisons and closing claims as well as
|
|
4
|
+
* mechanisms. Until 2026-10-10 this picked 4-6 "mechanism" beats and left the
|
|
5
|
+
* rest to the archetype templates; the founder's verdict on those ("why do I
|
|
6
|
+
* need to see those hardcoded same UI blocks in every slide") is why the
|
|
7
|
+
* templates are now only the fallback for a scene that fails its gates.
|
|
4
8
|
*
|
|
5
|
-
* WHAT
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* seconds while the v2 archetype has finished building after five (measured in
|
|
9
|
-
* the spike: v2's s3 and s7 froze for 15-20s of speech). Title cards, callouts
|
|
10
|
-
* and tables say a thing rather than show one, so they never qualify.
|
|
11
|
-
*
|
|
12
|
-
* WHAT NEVER QUALIFIES, whatever it scores:
|
|
13
|
-
* - a beat with no narration: its cues are the keyframes, and without them a
|
|
14
|
-
* generated scene has nothing to stay in step with;
|
|
9
|
+
* WHAT NEVER QUALIFIES:
|
|
10
|
+
* - a beat with no narration cue: its cues are the keyframes, and without them
|
|
11
|
+
* a generated scene has nothing to stay in step with;
|
|
15
12
|
* - a beat a camera enters or leaves (`inside`): the dive is aimed at a part
|
|
16
13
|
* the archetype drew, which a bespoke scene does not draw;
|
|
17
|
-
* - a beat the planner marked `bespoke: false
|
|
14
|
+
* - a beat the planner marked `bespoke: false` that cites a figure or a table
|
|
15
|
+
* of the paper — the two reasons the planner is told a `false` is for (a
|
|
16
|
+
* real figure the viewer must see, a table read row by row).
|
|
17
|
+
*
|
|
18
|
+
* A `false` on a beat that cites NEITHER is not obeyed (2026-10-10). r3's
|
|
19
|
+
* b12 was one: a comparison citing only a section, which the planner kept as
|
|
20
|
+
* six grey row plates held still for 16 seconds — exactly the "hardcoded UI
|
|
21
|
+
* blocks" the founder named, and with nothing the archetype had to keep.
|
|
18
22
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
23
|
+
* Deterministic, so the same storyboard always asks about the same beats and a
|
|
24
|
+
* cache hit is a cache hit. When the call cap cannot pay two calls for every
|
|
25
|
+
* beat (`max`), the most mechanical beats are drawn and the rest keep their
|
|
26
|
+
* archetype. Picks come back in deck order.
|
|
21
27
|
*/
|
|
22
28
|
import type { z } from "zod";
|
|
23
29
|
import type { Beat, segmentSchema } from "../types.js";
|
|
24
30
|
type Segment = z.infer<typeof segmentSchema>;
|
|
25
31
|
export interface Pick {
|
|
26
32
|
beatId: string;
|
|
33
|
+
/** Priority when the cap cannot pay for every beat. */
|
|
27
34
|
score: number;
|
|
28
35
|
/** Why it scored what it did, for `bespoke.json`. */
|
|
29
36
|
why: string[];
|
|
@@ -39,14 +46,14 @@ export interface Selection {
|
|
|
39
46
|
export interface SelectOptions {
|
|
40
47
|
/** Hashed with each beat id to break ties, so two decks tie differently. */
|
|
41
48
|
seed: string;
|
|
42
|
-
/** Narration, by beat id. A beat without
|
|
49
|
+
/** Narration, by beat id. A beat without a cue is ineligible. */
|
|
43
50
|
narration?: Readonly<Record<string, readonly Segment[]>>;
|
|
44
|
-
/**
|
|
45
|
-
|
|
46
|
-
|
|
51
|
+
/**
|
|
52
|
+
* Most beats: what the call cap pays for at two calls a beat. When fewer
|
|
53
|
+
* than the eligible, the most mechanical win (a mechanism gains most from a
|
|
54
|
+
* scene that moves with the voice); the rest keep their archetype.
|
|
55
|
+
*/
|
|
47
56
|
max?: number;
|
|
48
57
|
}
|
|
49
|
-
/** The number of beats a deck of `n` kept beats aims for: a third, held to [min, max]. */
|
|
50
|
-
export declare function target(n: number, min?: number, max?: number): number;
|
|
51
58
|
export declare function selectBespoke(beats: readonly Beat[], opts: SelectOptions): Selection;
|
|
52
59
|
export {};
|
|
@@ -9,18 +9,83 @@ export declare function contactSheets(sheets: ReadonlyArray<{
|
|
|
9
9
|
frames: readonly SheetFrame[];
|
|
10
10
|
path: string;
|
|
11
11
|
}>, cols?: number): Promise<void>;
|
|
12
|
+
/** The resolution planes are stored at, as a multiple of the frame: sharp up to this push-in. */
|
|
13
|
+
export declare const PLANE_SCALE = 1.4;
|
|
14
|
+
/**
|
|
15
|
+
* How out of focus a plane's soft twin is, frame px. Rack focus cross-fades a
|
|
16
|
+
* plane with this twin instead of tweening a CSS blur: MEASURED 2026-10-10 in
|
|
17
|
+
* the gates' browser (SwiftShader), a CSS \`filter: blur()\` on six frame-sized
|
|
18
|
+
* layers cost ~1.5s a frame (2.2s against 0.55s without), which made one deck's
|
|
19
|
+
* motion gates take 672s.
|
|
20
|
+
*/
|
|
21
|
+
export declare const SOFT_PX = 6;
|
|
22
|
+
/** One backdrop plane as the deck stores it: frame-shaped, covering the frame at rest. */
|
|
23
|
+
export interface PlaneCopy {
|
|
24
|
+
webp: Buffer;
|
|
25
|
+
/** The same plane out of focus: blurred `SOFT_PX`, stored at a quarter of the size. */
|
|
26
|
+
soft: Buffer;
|
|
27
|
+
/** Mean relative inverse depth of the band it holds (0 far .. 1 near); absent when flat. */
|
|
28
|
+
u?: number;
|
|
29
|
+
}
|
|
30
|
+
/** One subject, cut out of the subjects' picture: its own WebP and its box in frame px. */
|
|
31
|
+
export interface SubjectCopy {
|
|
32
|
+
/** Which subject (0-based, the inspection's left-to-right order). */
|
|
33
|
+
index: number;
|
|
34
|
+
webp: Buffer;
|
|
35
|
+
soft: Buffer;
|
|
36
|
+
box: {
|
|
37
|
+
x: number;
|
|
38
|
+
y: number;
|
|
39
|
+
w: number;
|
|
40
|
+
h: number;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* ROUND 6. The backdrop sliced into depth planes (src/bespoke/depth.ts), and
|
|
45
|
+
* each subject cut out on its own, every one already cropped to what covers
|
|
46
|
+
* `frame` and stored at `PLANE_SCALE` times it. A plane holds its depth band's
|
|
47
|
+
* real pixels; where a NEARER band covers it, it holds colours pushed and
|
|
48
|
+
* pulled in from its own band at quarter resolution — what a nearer plane
|
|
49
|
+
* uncovers when it slides — and it is transparent where nothing at or behind
|
|
50
|
+
* its band is. The farthest plane is opaque everywhere. Without a depth map
|
|
51
|
+
* (`depth` undefined) the backdrop is one plane. The pixel work runs at a
|
|
52
|
+
* quarter of the stored size and the full-size planes are composited by the
|
|
53
|
+
* canvas, so a picture takes ~3s, not minutes (MEASURED 2026-10-10: a per-pixel
|
|
54
|
+
* pass at full size did not finish in 10 minutes).
|
|
55
|
+
*/
|
|
56
|
+
export declare function depthPlanes(plate: Buffer, depth: Buffer | undefined, cuts: readonly number[], subjects: Buffer, boxes: ReadonlyArray<readonly [number, number, number, number]>, frame: {
|
|
57
|
+
width: number;
|
|
58
|
+
height: number;
|
|
59
|
+
}, quality?: number): Promise<{
|
|
60
|
+
planes: PlaneCopy[];
|
|
61
|
+
subjects: SubjectCopy[];
|
|
62
|
+
scale: number;
|
|
63
|
+
}>;
|
|
12
64
|
/**
|
|
13
65
|
* The deck's copy of an illustration, and the draft call's copy with its
|
|
14
66
|
* subjects boxed and numbered, drawn on a canvas by the same Chrome. WebP,
|
|
15
67
|
* because a flat picture's PNG is 1-2 MB and its WebP at 0.84 is ~5-10% of
|
|
16
68
|
* that (round 3 shipped 7-10 MB of PNG per deck); the repository has no image
|
|
17
69
|
* encoder, and Chrome is already every gate's dependency.
|
|
70
|
+
*
|
|
71
|
+
* ROUND 5, two layers. With a backdrop (`plate`) the subjects stand in front
|
|
72
|
+
* of it: the subjects' copy keeps its transparent ground (a picture drawn on a
|
|
73
|
+
* flat ground after all is keyed: its ground made transparent), the backdrop
|
|
74
|
+
* gets the feathered edges, and `composite` is the two as the box shows them
|
|
75
|
+
* (each scaled to cover the box, centred) — what the draft call is shown, with
|
|
76
|
+
* `boxed` drawn over it in the same coordinates.
|
|
18
77
|
*/
|
|
19
78
|
export declare function pictureCopies(png: Buffer, boxes: ReadonlyArray<readonly [number, number, number, number]>, feather?: {
|
|
20
79
|
width: number;
|
|
21
80
|
height: number;
|
|
22
|
-
}, quality?: number
|
|
81
|
+
}, quality?: number, opts?: {
|
|
82
|
+
plate?: Buffer;
|
|
83
|
+
ground?: string;
|
|
84
|
+
cutout?: boolean;
|
|
85
|
+
}): Promise<{
|
|
23
86
|
webp: Buffer;
|
|
24
87
|
boxed: Buffer;
|
|
25
88
|
feathered: boolean;
|
|
89
|
+
plateWebp?: Buffer;
|
|
90
|
+
composite?: Buffer;
|
|
26
91
|
}>;
|
|
@@ -21,7 +21,16 @@
|
|
|
21
21
|
*
|
|
22
22
|
* Shots closer than `MIN_HOLD` to the previous one are dropped: a shot the
|
|
23
23
|
* audience cannot read is a shake.
|
|
24
|
+
*
|
|
25
|
+
* A SECOND GRAMMAR, `close-open` (2026-10-10): one grammar for every
|
|
26
|
+
* illustrated scene made every one of them move alike — r1's three illustrated
|
|
27
|
+
* scenes were the same tour of three subjects in a row, and read as a template.
|
|
28
|
+
* `close-open` OPENS CLOSE on the subject of the first shot, held from t=0, moves
|
|
29
|
+
* subject to subject, and opens out to the whole picture only at the reveal —
|
|
30
|
+
* the picture is discovered rather than surveyed. The pass alternates the two
|
|
31
|
+
* over a deck's illustrated beats in deck order.
|
|
24
32
|
*/
|
|
33
|
+
import type { Grammar } from "./grammar.js";
|
|
25
34
|
import type { UnitBox } from "./inspect.js";
|
|
26
35
|
/** One entry of a scene's shot list: on cue `cue` (1-based), `at` of the way in, frame `subject` (0 = the whole picture). */
|
|
27
36
|
export interface Shot {
|
|
@@ -44,8 +53,10 @@ export interface CamMove {
|
|
|
44
53
|
x: number;
|
|
45
54
|
y: number;
|
|
46
55
|
ease: string;
|
|
47
|
-
/** The subject framed (0 = the whole picture). */
|
|
56
|
+
/** The subject framed (0 = the whole picture, or a two-shot). */
|
|
48
57
|
subject: number;
|
|
58
|
+
/** What kind of move it is in its grammar (the report and the gates read it). */
|
|
59
|
+
kind?: "push" | "whip" | "track" | "cut" | "pull" | "two-shot" | "creep" | "reveal";
|
|
49
60
|
}
|
|
50
61
|
/** The camera's largest push-in: past it a picture turns to mush. */
|
|
51
62
|
export declare const CAMERA_MAX_SCALE = 2.2;
|
|
@@ -59,15 +70,14 @@ export declare const REVEAL = 1.3;
|
|
|
59
70
|
export declare const MIN_HOLD = 1.6;
|
|
60
71
|
/** The establishing shot holds at least this long into the first cue. */
|
|
61
72
|
export declare const ESTABLISH = 1.6;
|
|
73
|
+
/** A held shot creeps this much further in, so a hold is never a still frame. */
|
|
74
|
+
export declare const CREEP = 1.03;
|
|
62
75
|
/**
|
|
63
|
-
*
|
|
64
|
-
* clear
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* first full run, en s2: "observations" over a robot's eyes).
|
|
76
|
+
* Where the picture sits in the box: covering all of it. Round 4 kept a 120px
|
|
77
|
+
* band clear at the top for the shell's subject labels; round 6 draws no
|
|
78
|
+
* labels (the narration and the subtitles name the subjects), so the band and
|
|
79
|
+
* its labels are gone.
|
|
68
80
|
*/
|
|
69
|
-
export declare const ART_BAND = 120;
|
|
70
|
-
/** Where the picture sits in the body box: under the band, covering the rest. */
|
|
71
81
|
export declare function artPlacement(box: {
|
|
72
82
|
width: number;
|
|
73
83
|
height: number;
|
|
@@ -86,34 +96,208 @@ export declare function subjectsInBox(units: readonly UnitBox[], art: {
|
|
|
86
96
|
width: number;
|
|
87
97
|
height: number;
|
|
88
98
|
}): Box[];
|
|
89
|
-
/**
|
|
90
|
-
|
|
99
|
+
/**
|
|
100
|
+
* The camera's scale and offset that frame `b` (origin 0 0), held to the box's
|
|
101
|
+
* edges. `sMax` is the picture's own ceiling (`sharpMax`): past it the push-in
|
|
102
|
+
* shows the picture's pixels; `sMin` the closest a framing may be widened to.
|
|
103
|
+
*/
|
|
104
|
+
export declare function frameOn(b: Box, W: number, H: number, sMax?: number, sMin?: number): {
|
|
105
|
+
s: number;
|
|
106
|
+
x: number;
|
|
107
|
+
y: number;
|
|
108
|
+
};
|
|
109
|
+
/** The camera that puts point (cx, cy) of the box at the centre of the view at scale `s`, held to the box. */
|
|
110
|
+
export declare function frameAt(cx: number, cy: number, s: number, W: number, H: number): {
|
|
111
|
+
s: number;
|
|
112
|
+
x: number;
|
|
113
|
+
y: number;
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* EFFECTIVE RESOLUTION. The image tool draws ~1.57 megapixels whatever it is
|
|
117
|
+
* asked (MEASURED 2026-10-10: 1672x941, 1774x887, 1915x821 — no size control
|
|
118
|
+
* exists on the tool). Covering a ~1730px-wide box, one picture pixel already
|
|
119
|
+
* spans ~0.9-1.0 output pixels, so a 2.2x push-in shows each picture pixel
|
|
120
|
+
* over ~2.2 output pixels: round 4's push-ins looked soft. The shell now caps
|
|
121
|
+
* every push-in at the scale where a picture pixel spans at most 1/`EFF_MIN`
|
|
122
|
+
* output pixels (`sharpMax`), and the report and the gate read the effective
|
|
123
|
+
* resolution at the closest shot (`effectiveAt`).
|
|
124
|
+
*
|
|
125
|
+
* ROUND 6 raises the bar from 0.6 to 0.85: pictures are upscaled 2x before they
|
|
126
|
+
* are cut into planes (src/bespoke/depth.ts), so a 1.55x push-in can be sharp.
|
|
127
|
+
* A picture that could not be upscaled is still pushed in at `PUSH_FLOOR` (the
|
|
128
|
+
* staging wins) and the report says how soft it was.
|
|
129
|
+
*/
|
|
130
|
+
export declare const EFF_MIN = 0.85;
|
|
131
|
+
/** Picture pixels per output pixel when the picture covers `box`'s placement at camera scale `s`. */
|
|
132
|
+
export declare function effectiveAt(art: {
|
|
91
133
|
width: number;
|
|
92
134
|
height: number;
|
|
135
|
+
depth?: {
|
|
136
|
+
scale: number;
|
|
137
|
+
};
|
|
93
138
|
}, box: {
|
|
94
139
|
width: number;
|
|
95
140
|
height: number;
|
|
96
|
-
}):
|
|
97
|
-
/** The
|
|
98
|
-
export declare function
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
141
|
+
}, s: number): number;
|
|
142
|
+
/** The closest the camera may push in on this picture and stay at `EFF_MIN` or sharper. */
|
|
143
|
+
export declare function sharpMax(art: {
|
|
144
|
+
width: number;
|
|
145
|
+
height: number;
|
|
146
|
+
depth?: {
|
|
147
|
+
scale: number;
|
|
148
|
+
};
|
|
149
|
+
} | undefined, box: {
|
|
150
|
+
width: number;
|
|
151
|
+
height: number;
|
|
152
|
+
}): number;
|
|
103
153
|
/**
|
|
104
154
|
* The default shot list, for a scene whose own is missing or unusable: the
|
|
105
155
|
* subjects in order, one per cue after the first (the first cue is the
|
|
106
156
|
* establishing shot), so every subject the picture has is pointed at once.
|
|
107
157
|
*/
|
|
108
158
|
export declare function defaultShots(cues: number, subjects: number): Shot[];
|
|
159
|
+
/** One step of a wiped-on picture: from `t`, over `dur`, the picture shows up to `share` of the box's width. */
|
|
160
|
+
export interface WipeStep {
|
|
161
|
+
t: number;
|
|
162
|
+
dur: number;
|
|
163
|
+
share: number;
|
|
164
|
+
/** The subject this step uncovers (0: the rest of the picture). */
|
|
165
|
+
subject: number;
|
|
166
|
+
}
|
|
167
|
+
/** Everything the shell's camera does in one illustrated scene (src/bespoke/grammar.ts). */
|
|
168
|
+
export interface Staging {
|
|
169
|
+
grammar: Grammar;
|
|
170
|
+
/** Where the camera is at t=0: home, except a zoom-out, which opens close on `subject`. */
|
|
171
|
+
open: {
|
|
172
|
+
s: number;
|
|
173
|
+
x: number;
|
|
174
|
+
y: number;
|
|
175
|
+
subject?: number;
|
|
176
|
+
};
|
|
177
|
+
moves: CamMove[];
|
|
178
|
+
/** A wiped-on picture's steps (grammar "wipe"); empty otherwise. */
|
|
179
|
+
wipes: WipeStep[];
|
|
180
|
+
}
|
|
181
|
+
interface Timed {
|
|
182
|
+
t: number;
|
|
183
|
+
subject: number;
|
|
184
|
+
}
|
|
185
|
+
interface Ctx {
|
|
186
|
+
cues: ReadonlyArray<{
|
|
187
|
+
t0: number;
|
|
188
|
+
t1: number;
|
|
189
|
+
}>;
|
|
190
|
+
subjects: readonly Box[];
|
|
191
|
+
W: number;
|
|
192
|
+
H: number;
|
|
193
|
+
sMax: number;
|
|
194
|
+
first: {
|
|
195
|
+
t0: number;
|
|
196
|
+
t1: number;
|
|
197
|
+
};
|
|
198
|
+
last: {
|
|
199
|
+
t0: number;
|
|
200
|
+
t1: number;
|
|
201
|
+
};
|
|
202
|
+
/** Home, with the reveal finished, before the settled end frame. */
|
|
203
|
+
latest: number;
|
|
204
|
+
/** Nothing moves before the establishing shot has been held. */
|
|
205
|
+
earliest: number;
|
|
206
|
+
}
|
|
109
207
|
/**
|
|
110
|
-
* The
|
|
111
|
-
*
|
|
208
|
+
* The named subjects' shot times, fitted to the scene: each as the voice names
|
|
209
|
+
* it, but no earlier than the establishing shot or `gap` after the one before,
|
|
210
|
+
* and early enough that every later one still fits before the reveal — a
|
|
211
|
+
* subject named in the last second is shot a little early rather than not at
|
|
212
|
+
* all (MEASURED 2026-10-10: short scenes naming their second subject late held
|
|
213
|
+
* one push-in, and `shot_variety` sent them back). Subjects that cannot fit
|
|
214
|
+
* even so are dropped from the end.
|
|
112
215
|
*/
|
|
216
|
+
export declare function scheduled(list: readonly Timed[], c: Pick<Ctx, "earliest" | "latest">, gap: number): Timed[];
|
|
217
|
+
/**
|
|
218
|
+
* The camera of one illustrated scene in its grammar. `cues` on the scene's
|
|
219
|
+
* clock; `duration` the scene's length (the end frame is photographed at
|
|
220
|
+
* `duration - 0.5`); `sMax` the picture's sharpness ceiling (`sharpMax`).
|
|
221
|
+
*/
|
|
222
|
+
export declare function compileStaging(grammar: Grammar, shots: readonly Shot[], cues: ReadonlyArray<{
|
|
223
|
+
t0: number;
|
|
224
|
+
t1: number;
|
|
225
|
+
}>, duration: number, subjects: readonly Box[], W: number, H: number, sMax?: number): Staging;
|
|
226
|
+
/** Round 4's one grammar, `tour`: the moves alone. */
|
|
113
227
|
export declare function compileShots(shots: readonly Shot[], cues: ReadonlyArray<{
|
|
114
228
|
t0: number;
|
|
115
229
|
t1: number;
|
|
116
|
-
}>, duration: number, subjects: readonly Box[], W: number, H: number): CamMove[];
|
|
230
|
+
}>, duration: number, subjects: readonly Box[], W: number, H: number, sMax?: number): CamMove[];
|
|
231
|
+
/** The closest the camera gets in a staging (the open frame included). */
|
|
232
|
+
export declare function closest(st: Pick<Staging, "open" | "moves">): number;
|
|
233
|
+
/**
|
|
234
|
+
* ROUND 6: a depth plane's framing for a camera framing (src/bespoke/depth.ts).
|
|
235
|
+
*
|
|
236
|
+
* The camera's framing `f` is of the subjects' plane (distance 1): scale `s`
|
|
237
|
+
* and offset `x, y` with origin 0 0, which is what `#sid-cam` is given. Read as
|
|
238
|
+
* a pinhole camera looking at the frame's centre `c`, that framing is a dolly
|
|
239
|
+
* `tz = 1 - 1/s` and a truck `f·t = (c(1-s) - x) / s`. A plane at distance `z`
|
|
240
|
+
* then projects EXACTLY to scale `s_z = z / (z - tz)` and offset
|
|
241
|
+
* `c(1 - s_z) - f·t·s_z / z` — so a farther plane zooms and slides less, a
|
|
242
|
+
* nearer one more: parallax with no approximation (a multiplane image is a
|
|
243
|
+
* stack of homographies, and for fronto-parallel planes those are scale +
|
|
244
|
+
* offset). `z = 1` returns `f` itself.
|
|
245
|
+
*
|
|
246
|
+
* NO PLANE EVER NEEDS ENLARGING TO COVER THE FRAME. A plane's left edge lands
|
|
247
|
+
* at or left of the frame's exactly when `x <= 0` — the camera's own framing
|
|
248
|
+
* covers the box — whatever the plane's distance: substituting `s_z` and `f·t`,
|
|
249
|
+
* `x_z <= 0` reduces to `x / (s·z) <= 0`. The same holds at every edge, so the
|
|
250
|
+
* shell's framings (held to the box by `frameAt`) cover with every plane.
|
|
251
|
+
* (Round 6's first cut carried an overscan search; it never returned more than
|
|
252
|
+
* 1, and a reversed copy of it was caught by nothing — so it is gone, and the
|
|
253
|
+
* test holds this property instead.)
|
|
254
|
+
*/
|
|
255
|
+
export declare function planeFrame(f: {
|
|
256
|
+
s: number;
|
|
257
|
+
x: number;
|
|
258
|
+
y: number;
|
|
259
|
+
}, z: number, W: number, H: number): {
|
|
260
|
+
s: number;
|
|
261
|
+
x: number;
|
|
262
|
+
y: number;
|
|
263
|
+
};
|
|
264
|
+
/**
|
|
265
|
+
* RACK FOCUS (round 6): what tells the audience where to look, instead of a
|
|
266
|
+
* label plate. A plane at distance `z` is blurred by how far it sits from the
|
|
267
|
+
* plane in focus, in dioptres (|1/z - 1/zf|, as a lens's circle of confusion
|
|
268
|
+
* grows), more in a close shot than in the whole view (a longer lens has less
|
|
269
|
+
* depth of field). px at the plane's own scale.
|
|
270
|
+
*/
|
|
271
|
+
export declare const BLUR_WIDE = 3.5;
|
|
272
|
+
export declare const BLUR_CLOSE = 5;
|
|
273
|
+
export declare const BLUR_MAX = 6;
|
|
274
|
+
export declare function blurAt(z: number, focusZ: number, s: number): number;
|
|
275
|
+
/**
|
|
276
|
+
* The blur as the deck draws it: the opacity of the plane's out-of-focus twin
|
|
277
|
+
* (src/bespoke/sheet.ts, blurred `BLUR_MAX`px) over the sharp plane. A CSS
|
|
278
|
+
* blur tween cost ~1.5s a frame in the gates' browser; a cross-fade costs
|
|
279
|
+
* nothing measurable.
|
|
280
|
+
*/
|
|
281
|
+
export declare function softAt(z: number, focusZ: number, s: number): number;
|
|
282
|
+
/** What `cameraScript` moves besides the camera in a depth-staged scene (round 6). */
|
|
283
|
+
export interface DepthStage {
|
|
284
|
+
/** Backdrop planes, far to near: `#sid-plate` is the farthest, `#sid-planeN` the rest. */
|
|
285
|
+
planes: ReadonlyArray<{
|
|
286
|
+
z: number;
|
|
287
|
+
}>;
|
|
288
|
+
/** The subjects' planes `#sid-subjK` (K 1-based), with their distances. */
|
|
289
|
+
subjects: ReadonlyArray<{
|
|
290
|
+
subject: number;
|
|
291
|
+
z: number;
|
|
292
|
+
box: Box;
|
|
293
|
+
}>;
|
|
294
|
+
/** The scene's length: the light's sweep and the subjects' breathing end before it. */
|
|
295
|
+
duration: number;
|
|
296
|
+
}
|
|
297
|
+
/** The element id of backdrop plane `i` (far to near). The farthest keeps round 5's `-plate`. */
|
|
298
|
+
export declare function planeId(sid: string, i: number): string;
|
|
299
|
+
/** The element id of subject `k`'s plane (1-based). Not `-sK`: a scene's own ids use that. */
|
|
300
|
+
export declare function subjectId(sid: string, k: number): string;
|
|
117
301
|
/**
|
|
118
302
|
* The camera as GSAP source, for the shell to put before the scene's own
|
|
119
303
|
* script. Every tween is a `fromTo` with explicit from-values and
|
|
@@ -122,7 +306,26 @@ export declare function compileShots(shots: readonly Shot[], cues: ReadonlyArray
|
|
|
122
306
|
* centre of the view (x' = W/2 - (W/2 - x)·k), so a hold is never a still
|
|
123
307
|
* frame; the next move starts from where the creep ended. Nothing moves after
|
|
124
308
|
* the reveal: the end frame is the whole picture, exactly home.
|
|
309
|
+
*
|
|
310
|
+
* ROUND 6, with depth planes (`depth`): every camera tween has a twin on each
|
|
311
|
+
* plane at `planeFrame` of its values for the plane's distance — the
|
|
312
|
+
* parallax — with the plane's blur racked to the subject framed (`blurAt`);
|
|
313
|
+
* the light sweeps once; the subjects breathe; and wipe steps light the
|
|
314
|
+
* subjects in turn instead of uncovering a clip.
|
|
125
315
|
*/
|
|
126
|
-
export declare function cameraScript(sid: string, moves: readonly CamMove[], W: number, H: number
|
|
316
|
+
export declare function cameraScript(sid: string, moves: readonly CamMove[], W: number, H: number, opts?: {
|
|
317
|
+
open?: {
|
|
318
|
+
s: number;
|
|
319
|
+
x: number;
|
|
320
|
+
y: number;
|
|
321
|
+
subject?: number;
|
|
322
|
+
};
|
|
323
|
+
wipes?: readonly WipeStep[];
|
|
324
|
+
/** Round 6: the depth planes that move with the camera, each at its own distance. */
|
|
325
|
+
depth?: DepthStage;
|
|
326
|
+
}): string;
|
|
127
327
|
/** What a scene's camera does, for the report and the prompt: one word per move. */
|
|
128
|
-
export declare function shotSummary(moves: readonly CamMove[]
|
|
328
|
+
export declare function shotSummary(moves: readonly CamMove[], grammar?: string, open?: {
|
|
329
|
+
s: number;
|
|
330
|
+
}): string;
|
|
331
|
+
export {};
|
|
@@ -69,8 +69,15 @@ export declare function vendorScripts(out: string, composition: string): Promise
|
|
|
69
69
|
* downloadable file at all. Named here because this set is the ONLY thing that
|
|
70
70
|
* reaches the built deck — a poster left out is simply absent, and the deck's
|
|
71
71
|
* own runtime reports it as `http_error 404`, a long way from this line.
|
|
72
|
+
*
|
|
73
|
+
* A PIECE IS WRITTEN, NOT COPIED. Its `src` is the author's animate scene file,
|
|
74
|
+
* and what the deck loads under the same name is that file assembled with the
|
|
75
|
+
* vendored kit (`assemblePiece`, src/build/piece.ts) — the containment proof
|
|
76
|
+
* below applies to it exactly as to an image.
|
|
72
77
|
*/
|
|
73
78
|
export declare function copyAssets(sourceDir: string, out: string, figures: readonly {
|
|
79
|
+
id?: string;
|
|
80
|
+
kind?: string;
|
|
74
81
|
src: string;
|
|
75
82
|
poster?: string;
|
|
76
83
|
}[], step: Step): Promise<string[]>;
|