@jokerized/decksmith 0.1.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +159 -25
- package/dist/cli.js +3441 -1738
- package/dist/index.js +2226 -647
- package/dist/mcp.js +2250 -604
- package/dist/types/emit/archetypes/annotated-figure.d.ts +3 -2
- package/dist/types/emit/archetypes/data-table.d.ts +0 -19
- package/dist/types/emit/archetypes/pipeline.d.ts +2 -2
- package/dist/types/emit/archetypes/stack.d.ts +13 -1
- package/dist/types/emit/archetypes/title.d.ts +31 -14
- package/dist/types/emit/camera.d.ts +25 -0
- package/dist/types/emit/composition.d.ts +20 -2
- package/dist/types/emit/kit.d.ts +146 -0
- package/dist/types/emit/svg.d.ts +77 -13
- package/dist/types/emit/theme.d.ts +1 -0
- package/dist/types/images/illustrate.d.ts +34 -0
- package/dist/types/images/providers.d.ts +108 -0
- package/dist/types/index.d.ts +27 -8
- package/dist/types/mcp/prereqs.d.ts +18 -0
- package/dist/types/mcp/tools.d.ts +23 -0
- package/dist/types/plan/codex.d.ts +25 -3
- package/dist/types/plan/duration.d.ts +190 -15
- package/dist/types/plan/prompt.d.ts +7 -1
- package/dist/types/plan/refs.d.ts +41 -3
- package/dist/types/prefs.d.ts +21 -6
- package/dist/types/render/capture.d.ts +99 -0
- package/dist/types/render/render.d.ts +18 -0
- package/dist/types/server/options.d.ts +7 -0
- package/dist/types/server/pipeline.d.ts +14 -0
- package/dist/types/server/queue.d.ts +1 -1
- package/dist/types/types.d.ts +152 -4
- package/dist/types/verify/index.d.ts +114 -6
- package/package.json +2 -2
package/dist/types/prefs.d.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import type { z } from "zod";
|
|
2
|
-
import { prefsSchema } from "./types.js";
|
|
2
|
+
import { prefsSchema, type Source } from "./types.js";
|
|
3
3
|
export type Prefs = z.infer<typeof prefsSchema>;
|
|
4
4
|
/**
|
|
5
|
-
* A partial Prefs. `narration`
|
|
6
|
-
* a patch merges rather than replaces: `--voice` must not
|
|
7
|
-
* config file set
|
|
5
|
+
* A partial Prefs. `narration` and `images` are the two nested objects, so they
|
|
6
|
+
* are the two places a patch merges rather than replaces: `--voice` must not
|
|
7
|
+
* wipe a `rate` the config file set, and `--image-style` must not wipe its
|
|
8
|
+
* `provider`.
|
|
8
9
|
*/
|
|
9
|
-
export type PrefsPatch = Partial<Omit<Prefs, "narration">> & {
|
|
10
|
+
export type PrefsPatch = Partial<Omit<Prefs, "narration" | "images">> & {
|
|
10
11
|
narration?: Partial<Prefs["narration"]>;
|
|
12
|
+
images?: Partial<Prefs["images"]>;
|
|
11
13
|
};
|
|
12
14
|
export declare const CONFIG_FILE = "decksmith.config.json";
|
|
13
15
|
/**
|
|
@@ -16,8 +18,15 @@ export declare const CONFIG_FILE = "decksmith.config.json";
|
|
|
16
18
|
* `cwd` is where the search for a config file starts; the search stops at the
|
|
17
19
|
* filesystem root. Returns a fully-populated Prefs — every consumer downstream
|
|
18
20
|
* reads fields, never optionals.
|
|
21
|
+
*
|
|
22
|
+
* `source` is the document the deck is being planned FROM, when the caller has
|
|
23
|
+
* already read one. It is optional because most callers have not: `build`,
|
|
24
|
+
* `narrate` and `pack` work from a storyboard that was planned long ago, and
|
|
25
|
+
* `illustrate` never needed a beat count. Only the planning path passes it, and
|
|
26
|
+
* only so `slidesFor` can size the deck to what the document contains rather
|
|
27
|
+
* than to a clock — see its header for what changes when it is absent.
|
|
19
28
|
*/
|
|
20
|
-
export declare function loadPrefs(overrides?: PrefsPatch, cwd?: string): Promise<Prefs>;
|
|
29
|
+
export declare function loadPrefs(overrides?: PrefsPatch, cwd?: string, source?: Source): Promise<Prefs>;
|
|
21
30
|
/**
|
|
22
31
|
* Turn CLI flags into a patch. Values arrive as strings from commander and are
|
|
23
32
|
* not validated here beyond the numbers: `loadPrefs` runs them through the
|
|
@@ -40,4 +49,10 @@ export interface PrefFlags {
|
|
|
40
49
|
subtitles?: boolean;
|
|
41
50
|
/** `--narration-density`. Spelled apart from `density`, which is a slide's. */
|
|
42
51
|
narrationDensity?: string;
|
|
52
|
+
/** `--images`. A boolean like `narrate`, so `flags()` in cli.ts reads it beside `--no-subtitles`. */
|
|
53
|
+
images?: boolean;
|
|
54
|
+
imageProvider?: string;
|
|
55
|
+
imageModel?: string;
|
|
56
|
+
imageStyle?: string;
|
|
57
|
+
imageMax?: string | number;
|
|
43
58
|
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The capture path, as a thing other code can drive.
|
|
3
|
+
*
|
|
4
|
+
* "Look at the artifact" is this project's strongest gate — six of its bugs
|
|
5
|
+
* shipped past green gates and every one was caught by a human looking at a
|
|
6
|
+
* frame. So the frame a human looks at has to be the frame the renderer would
|
|
7
|
+
* produce, and `hyperframes snapshot` is NOT that frame: it calls
|
|
8
|
+
* `player.renderSeek(t)` with no options, so `suppressEvents` is falsy and a
|
|
9
|
+
* GSAP `onUpdate` FIRES. Invariant 11 says the capture path suppresses events,
|
|
10
|
+
* so callback-driven motion plays under snapshot and renders frozen. Snapshot is
|
|
11
|
+
* permissive in exactly the direction that hides the project's most dangerous
|
|
12
|
+
* failure shape, and it was measured lying about CSS 3D as well — at t=3.9s it
|
|
13
|
+
* produced a flat, un-rotated frame where the render produced correct
|
|
14
|
+
* perspective (see `.planning/2026-09-04-css-3d-spike.md`).
|
|
15
|
+
*
|
|
16
|
+
* `fidelity` already drove the right path privately, and was measured doing so:
|
|
17
|
+
* against `experiments/015-decision/out/vocab-18.mp4`, a real render, at all
|
|
18
|
+
* twelve of its holds, it agreed to a worst case of 0.11 and a mean of 0.03
|
|
19
|
+
* percentage points — the residual being H.264 quantisation. This module is that
|
|
20
|
+
* code lifted out from under the ink arithmetic so the gate and a person asking
|
|
21
|
+
* for a PNG go through the same three calls: inject the pinned runtime,
|
|
22
|
+
* `renderSeek(t, { suppressEvents: true })`, then `Page.captureScreenshot` with
|
|
23
|
+
* the renderer's own clip.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* The browser `render` already uses.
|
|
27
|
+
*
|
|
28
|
+
* `@puppeteer/browsers` and `puppeteer-core` are DIRECT dependencies of this
|
|
29
|
+
* package. They arrive with hyperframes too — a hard dependency of any verb that
|
|
30
|
+
* opens a browser — but relying on that made module imports resolve through
|
|
31
|
+
* somebody else's dependency tree, and the day hyperframes swaps its automation
|
|
32
|
+
* library those verbs break for a reason nothing in this repo mentions. They are
|
|
33
|
+
* still imported DYNAMICALLY, which is a different concern: a machine that
|
|
34
|
+
* cannot supply a browser must degrade rather than throw after the capture has
|
|
35
|
+
* already run.
|
|
36
|
+
*
|
|
37
|
+
* `need` completes the error message, because the three callers want different
|
|
38
|
+
* advice from it — captions can fall back to a sidecar, `frames` cannot. This
|
|
39
|
+
* was two copies until `frames` became the third caller the older of them said
|
|
40
|
+
* to promote it on.
|
|
41
|
+
*/
|
|
42
|
+
export declare function chromePath(need?: string): Promise<string>;
|
|
43
|
+
type Browser = Awaited<ReturnType<typeof import("puppeteer-core").launch>>;
|
|
44
|
+
type Page = Awaited<ReturnType<Browser["newPage"]>>;
|
|
45
|
+
/** An open deck, seekable and photographable. Close it. */
|
|
46
|
+
export interface DeckPage {
|
|
47
|
+
/** The canvas the deck declares, which is also the screenshot's clip. */
|
|
48
|
+
width: number;
|
|
49
|
+
height: number;
|
|
50
|
+
/** Commit the frame at an absolute time, events suppressed. */
|
|
51
|
+
seek(t: number): Promise<void>;
|
|
52
|
+
/** PNG bytes of the frame currently committed. */
|
|
53
|
+
shoot(): Promise<Buffer>;
|
|
54
|
+
/** For callers that need to read the DOM at a stop, as `fidelity` does. */
|
|
55
|
+
page: Page;
|
|
56
|
+
close(): Promise<void>;
|
|
57
|
+
}
|
|
58
|
+
export interface OpenOptions {
|
|
59
|
+
timeoutMs?: number;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Open `dir`'s deck in the renderer's own browser, ready to seek.
|
|
63
|
+
*
|
|
64
|
+
* Throws for every environmental reason — no Chrome, no `index.html`, no canvas
|
|
65
|
+
* size. Callers that must not fail a build over the environment (the `fidelity`
|
|
66
|
+
* gate) catch it; callers a person invoked directly (`frames`) let it surface,
|
|
67
|
+
* because a person who asked for a PNG and got nothing needs to hear why.
|
|
68
|
+
*/
|
|
69
|
+
export declare function openDeck(dir: string, opts?: OpenOptions): Promise<DeckPage>;
|
|
70
|
+
/** One written frame. */
|
|
71
|
+
export interface CapturedFrame {
|
|
72
|
+
t: number;
|
|
73
|
+
path: string;
|
|
74
|
+
bytes: number;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* What one frame is called.
|
|
78
|
+
*
|
|
79
|
+
* The index leads, zero-padded to the width of the largest, because a directory
|
|
80
|
+
* listing is how these actually get looked at and lexical order is what a file
|
|
81
|
+
* browser gives you: `t10.000s` sorts before `t3.900s`, so the reader opens the
|
|
82
|
+
* wrong frame and believes it. Padding the INDEX rather than the time keeps the
|
|
83
|
+
* two orders — asked-for and on-disk — identical without deciding how many
|
|
84
|
+
* digits a deck's duration might need.
|
|
85
|
+
*
|
|
86
|
+
* The time is still in the name, to 3 decimals, because that is the thing a
|
|
87
|
+
* reader is checking against and invariant 10 says 3 decimals is what the
|
|
88
|
+
* renderer rounds to. A frame whose filename disagreed with the timeline would
|
|
89
|
+
* be worse than one with no time in it at all.
|
|
90
|
+
*/
|
|
91
|
+
export declare function frameName(index: number, t: number, total: number): string;
|
|
92
|
+
/**
|
|
93
|
+
* Write a PNG per requested time into `outDir`.
|
|
94
|
+
*
|
|
95
|
+
* Times are rounded to 3 decimals to match invariant 10, so a filename names
|
|
96
|
+
* exactly the instant the renderer would.
|
|
97
|
+
*/
|
|
98
|
+
export declare function captureFrames(dir: string, times: readonly number[], outDir: string, opts?: OpenOptions): Promise<CapturedFrame[]>;
|
|
99
|
+
export {};
|
|
@@ -103,8 +103,20 @@ export interface RenderOptions {
|
|
|
103
103
|
* length is decided at plan time by how much it says, and this closes whatever
|
|
104
104
|
* gap survived that. A video already inside the target is left alone rather
|
|
105
105
|
* than padded — dead air is worse than eight seconds short.
|
|
106
|
+
*
|
|
107
|
+
* A gap wider than `MAX_PLAYBACK` is refused, not closed; see the check after
|
|
108
|
+
* `readTiming` and `playbackRefusal`.
|
|
106
109
|
*/
|
|
107
110
|
targetSeconds?: number;
|
|
111
|
+
/**
|
|
112
|
+
* Speed past `MAX_PLAYBACK` anyway.
|
|
113
|
+
*
|
|
114
|
+
* For the person who has read the refusal, cannot re-plan, and wants the fast
|
|
115
|
+
* video regardless. It does not silence anything: both clauses of
|
|
116
|
+
* `playbackWarning` still print, and the summary still carries the factor and
|
|
117
|
+
* the caption rate it bought.
|
|
118
|
+
*/
|
|
119
|
+
allowFastPlayback?: boolean;
|
|
108
120
|
/** Leave the per-piece intermediates on disk. */
|
|
109
121
|
keep?: boolean;
|
|
110
122
|
/** Progress, one line at a time. */
|
|
@@ -119,5 +131,11 @@ export interface RenderResult {
|
|
|
119
131
|
burned: boolean;
|
|
120
132
|
/** What `targetSeconds` cost, if anything. 1 means the file was not respeeded. */
|
|
121
133
|
playback: number;
|
|
134
|
+
/**
|
|
135
|
+
* p95 characters per second of the subtitles AS SHIPPED — measured off the
|
|
136
|
+
* cues that were written, so at `playback > 1` it is already the sped-up rate
|
|
137
|
+
* a viewer actually reads at rather than the deck's rate at rest.
|
|
138
|
+
*/
|
|
139
|
+
captionCps: number;
|
|
122
140
|
}
|
|
123
141
|
export declare function render(opts: RenderOptions): Promise<RenderResult>;
|
|
@@ -61,6 +61,13 @@ export interface JobOptions {
|
|
|
61
61
|
prefs: Prefs;
|
|
62
62
|
narrate: boolean;
|
|
63
63
|
video: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Run the `illustrate` stage between `plan` and `build`. The same bit lands in
|
|
66
|
+
* `prefs.images.enabled`, which is what lets the planner write a brief; this
|
|
67
|
+
* copy is what `stagesFor` reads, the way `narrate` is read beside
|
|
68
|
+
* `prefs.narration.enabled`.
|
|
69
|
+
*/
|
|
70
|
+
images: boolean;
|
|
64
71
|
/**
|
|
65
72
|
* Whether the REQUEST said so, as opposed to the schema defaulting.
|
|
66
73
|
*
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ImageProvider, type Runner } from "../index.js";
|
|
1
2
|
import type { JobOptions } from "./options.js";
|
|
2
3
|
import type { JobHandle, JobResult, Stage } from "./queue.js";
|
|
3
4
|
import { type Upload } from "./upload.js";
|
|
@@ -15,6 +16,19 @@ export interface PipelineInput {
|
|
|
15
16
|
* owner's own box, own papers), it is fetched with a count and a timeout.
|
|
16
17
|
*/
|
|
17
18
|
fetchRemoteFigures: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* The rungs `illustrate` draws through. A test injects the tool's own SVG and
|
|
21
|
+
* nothing else; absent, the stage resolves its providers from the environment
|
|
22
|
+
* at call time, like `narrate` does — so there is no server option to plumb
|
|
23
|
+
* and nothing to configure but the env vars.
|
|
24
|
+
*/
|
|
25
|
+
imageChain?: ImageProvider[];
|
|
26
|
+
/**
|
|
27
|
+
* The planner's `Runner`, handed straight to `codexPlanner`. The seam
|
|
28
|
+
* test/plan.test.ts already drives the parse path through; here it is what
|
|
29
|
+
* lets a test carry a job PAST `plan` — into `illustrate` — without a Codex.
|
|
30
|
+
*/
|
|
31
|
+
run?: Runner;
|
|
18
32
|
}
|
|
19
33
|
/** Which rows the step list should have, decided before anything runs. */
|
|
20
34
|
export declare function stagesFor(options: JobOptions): Stage[];
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { type JobError } from "./errors.js";
|
|
17
17
|
export type JobState = "queued" | "running" | "done" | "error";
|
|
18
|
-
export type Stage = "ingest" | "plan" | "narrate" | "build" | "render";
|
|
18
|
+
export type Stage = "ingest" | "plan" | "illustrate" | "narrate" | "build" | "render";
|
|
19
19
|
export type StepState = "pending" | "running" | "done" | "skipped" | "error";
|
|
20
20
|
export interface StepView {
|
|
21
21
|
name: Stage;
|