@jokerized/decksmith 0.9.3 → 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 +13368 -8864
- package/dist/ds-animate.js +1 -0
- package/dist/index.js +13182 -8678
- package/dist/mcp.js +9805 -7587
- 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/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 +17 -0
- package/dist/types/verify/scenes.d.ts +185 -5
- package/package.json +1 -1
- package/dist/types/bespoke/callouts.d.ts +0 -76
|
@@ -56,6 +56,13 @@ export declare function isWriting(t: ReadText): boolean;
|
|
|
56
56
|
* language's pass). Prints one JSON line per file.
|
|
57
57
|
*/
|
|
58
58
|
export declare const OCR_SWIFT = "import Foundation\nimport Vision\nimport ImageIO\nfunc run(_ img: CGImage, _ langs: [String]) -> [[String: Any]] {\n let req = VNRecognizeTextRequest()\n req.recognitionLevel = .accurate\n req.usesLanguageCorrection = false\n req.recognitionLanguages = langs\n try? VNImageRequestHandler(cgImage: img, options: [:]).perform([req])\n var out: [[String: Any]] = []\n for o in req.results ?? [] {\n guard let c = o.topCandidates(1).first else { continue }\n let b = o.boundingBox\n out.append([\"s\": c.string, \"c\": c.confidence, \"b\": [b.minX, 1 - b.maxY, b.width, b.height], \"l\": langs[0]])\n }\n return out\n}\nfor path in CommandLine.arguments.dropFirst() {\n guard let src = CGImageSourceCreateWithURL(URL(fileURLWithPath: path) as CFURL, nil),\n let img = CGImageSourceCreateImageAtIndex(src, 0, nil) else {\n print(\"{\\\"error\\\":\\\"unreadable\\\"}\"); continue\n }\n var out: [[String: Any]] = []\n for langs in [[\"en-US\"], [\"ko-KR\"], [\"ja-JP\"], [\"zh-Hans\"]] { out += run(img, langs) }\n let data = try! JSONSerialization.data(withJSONObject: [\"texts\": out])\n print(String(data: data, encoding: .utf8)!)\n}\n";
|
|
59
|
+
export declare function exec(bin: string, args: string[], timeoutMs: number): Promise<string>;
|
|
60
|
+
/**
|
|
61
|
+
* A compiled Swift helper, `<dir>/<name>-<hash of its source>`, compiled on
|
|
62
|
+
* first use (MEASURED ~25s once; then ~0.5s a picture). Rejects with the
|
|
63
|
+
* reason when this machine cannot build it — no macOS, no Swift toolchain.
|
|
64
|
+
*/
|
|
65
|
+
export declare function swiftBinary(name: string, source: string, dir: string): Promise<string>;
|
|
59
66
|
/** The writing in a picture; rejects (with why) when nothing here can read it. */
|
|
60
67
|
export declare function readText(png: string, toolDir: string): Promise<ReadText[]>;
|
|
61
68
|
/** Everything the inspection says about one picture. */
|
|
@@ -65,5 +72,37 @@ export interface Inspection {
|
|
|
65
72
|
/** Writing found, or null when no reader was available (`unread` says why). */
|
|
66
73
|
text: string[] | null;
|
|
67
74
|
unread?: string;
|
|
75
|
+
/** The picture has a transparent ground (a subjects layer for a backdrop). */
|
|
76
|
+
cutout?: boolean;
|
|
77
|
+
/** The flattened copy the reader read, when the picture is a cutout. */
|
|
78
|
+
flatFile?: string;
|
|
68
79
|
}
|
|
69
|
-
|
|
80
|
+
/**
|
|
81
|
+
* Inspect a drawn picture. A subjects layer drawn on a transparent ground
|
|
82
|
+
* (round 5) is inspected as it will be seen against the pack's ground: laid
|
|
83
|
+
* on `ground` first, and the text reader reads that flattened copy, written
|
|
84
|
+
* beside `file` as `<file>.flat.png` (Vision reads a transparent pixel as
|
|
85
|
+
* black, which would hide dark writing).
|
|
86
|
+
*/
|
|
87
|
+
export declare function inspectPicture(bytes: Buffer, file: string, toolDir: string, ground?: readonly [number, number, number]): Promise<Inspection>;
|
|
88
|
+
/** "#rrggbb" (or "#rgb") as RGB; mid grey for anything else. */
|
|
89
|
+
export declare function hexRgb(hex: string): [number, number, number];
|
|
90
|
+
/** Share of an RGBA picture's pixels that are (nearly) transparent, sampled. */
|
|
91
|
+
export declare function transparentShare(f: Frame): number;
|
|
92
|
+
/** An RGBA picture laid over a flat ground, as RGB. */
|
|
93
|
+
export declare function flattenOn(f: Frame, ground: readonly number[]): Frame;
|
|
94
|
+
/** A minimal PNG encoder (8-bit RGB or RGBA, filter 0): the repository decodes PNGs, and the text reader needs one written. */
|
|
95
|
+
export declare function encodePng(f: Frame): Buffer;
|
|
96
|
+
/**
|
|
97
|
+
* The Swift helper for REPETITION across a deck's pictures: Vision's image
|
|
98
|
+
* feature print (`VNGenerateImageFeaturePrintRequest`), a learned embedding of
|
|
99
|
+
* what a picture shows, local and free. Prints one JSON line per file with the
|
|
100
|
+
* print as a float array. Two pictures of the same friendly robot sit close
|
|
101
|
+
* whatever their colours; two different scenes sit far apart (calibration in
|
|
102
|
+
* `SIMILAR_MAX`).
|
|
103
|
+
*/
|
|
104
|
+
export declare const PRINT_SWIFT = "import Foundation\nimport Vision\nimport ImageIO\nfor path in CommandLine.arguments.dropFirst() {\n guard let src = CGImageSourceCreateWithURL(URL(fileURLWithPath: path) as CFURL, nil),\n let img = CGImageSourceCreateImageAtIndex(src, 0, nil) else {\n print(\"{\\\"error\\\":\\\"unreadable\\\"}\"); continue\n }\n let req = VNGenerateImageFeaturePrintRequest()\n try? VNImageRequestHandler(cgImage: img, options: [:]).perform([req])\n guard let o = req.results?.first as? VNFeaturePrintObservation else {\n print(\"{\\\"error\\\":\\\"no print\\\"}\"); continue\n }\n var v = [Float](repeating: 0, count: o.elementCount)\n o.data.withUnsafeBytes { raw in\n let p = raw.bindMemory(to: Float.self)\n for i in 0..<min(o.elementCount, p.count) { v[i] = p[i] }\n }\n let data = try! JSONSerialization.data(withJSONObject: [\"v\": v.map { Double($0) }])\n print(String(data: data, encoding: .utf8)!)\n}\n";
|
|
105
|
+
/** Each picture's feature print; rejects when this machine has no Vision. */
|
|
106
|
+
export declare function featurePrints(pngs: readonly string[], toolDir: string): Promise<number[][]>;
|
|
107
|
+
/** Vision's distance between two prints (Euclidean, as `computeDistance`). */
|
|
108
|
+
export declare function printDistance(a: readonly number[], b: readonly number[]): number;
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import { type DeckNarration } from "../emit/composition.js";
|
|
2
|
-
import { type Runner } from "../plan/codex.js";
|
|
2
|
+
import { type Runner, type RunnerArgs } from "../plan/codex.js";
|
|
3
3
|
import type { Prefs } from "../prefs.js";
|
|
4
4
|
import type { Beat, Format, Source, Storyboard } from "../types.js";
|
|
5
5
|
import { type Layout } from "../verify/scenes.js";
|
|
6
|
-
import { type ArtCheck, type ArtCopies } from "./art.js";
|
|
7
|
-
import { type
|
|
6
|
+
import { type ArtCheck, type ArtCopies, type ArtRef, type DepthCopies } from "./art.js";
|
|
7
|
+
import { type DataBuild } from "./databuild.js";
|
|
8
|
+
import { type CutInput, type DepthInfo } from "./depth.js";
|
|
9
|
+
import { type Grammar, type Role } from "./grammar.js";
|
|
8
10
|
import { type Inspection } from "./inspect.js";
|
|
9
|
-
import { type Measured } from "./prompt.js";
|
|
11
|
+
import { type DeviceBeat, type Measured } from "./prompt.js";
|
|
10
12
|
import { type RepairNote } from "./repair.js";
|
|
11
13
|
import { type BespokeMap } from "./scene.js";
|
|
12
14
|
import { type Skip } from "./select.js";
|
|
13
15
|
import { type Shot } from "./shots.js";
|
|
14
16
|
export type BespokePrefs = NonNullable<Prefs["bespoke"]>;
|
|
17
|
+
export type { DeviceBeat };
|
|
15
18
|
/** What a gate round says about one beat's candidate scene. */
|
|
16
19
|
export interface GateResult {
|
|
17
20
|
/** One line per finding, fed verbatim to the critique round. */
|
|
@@ -31,7 +34,7 @@ export interface GateResult {
|
|
|
31
34
|
sid?: string;
|
|
32
35
|
}
|
|
33
36
|
/** Which probe: the drafts, the final candidates, or a repair round's. */
|
|
34
|
-
export type GateRound = "draft" | "final" | "repair";
|
|
37
|
+
export type GateRound = "draft" | "final" | "repair" | "second";
|
|
35
38
|
/** Builds a deck with these candidates, gates each, and says what it found — keyed by beat id. */
|
|
36
39
|
export type GateFn = (candidates: BespokeMap, round: GateRound) => Promise<Map<string, GateResult>>;
|
|
37
40
|
export interface BespokeInput {
|
|
@@ -54,12 +57,20 @@ export interface BespokeInput {
|
|
|
54
57
|
/** Where the image tool saves pictures. Default `$CODEX_HOME` or `~/.codex` (tests point it elsewhere). */
|
|
55
58
|
codexHome?: string;
|
|
56
59
|
/** Inspects a drawn picture (src/bespoke/inspect.ts); swappable so tests need no Swift or Vision. */
|
|
57
|
-
inspect?: (bytes: Buffer, file: string) => Promise<Inspection>;
|
|
60
|
+
inspect?: (bytes: Buffer, file: string, ground?: [number, number, number]) => Promise<Inspection>;
|
|
58
61
|
/** Draws the deck's WebP and the boxed copy (src/bespoke/sheet.ts); swappable so tests need no Chrome. */
|
|
59
62
|
copies?: (png: Buffer, boxes: Inspection["subjects"], box: {
|
|
60
63
|
width: number;
|
|
61
64
|
height: number;
|
|
65
|
+
}, opts: {
|
|
66
|
+
plate?: Buffer;
|
|
67
|
+
ground?: string;
|
|
68
|
+
cutout?: boolean;
|
|
62
69
|
}) => Promise<ArtCopies>;
|
|
70
|
+
/** Vision feature prints of pictures (src/bespoke/inspect.ts); swappable so tests need no Swift. */
|
|
71
|
+
prints?: (pngs: readonly string[]) => Promise<number[][]>;
|
|
72
|
+
/** Cuts a picture into depth planes (src/bespoke/depth.ts); swappable so tests need no Chrome or Core ML. */
|
|
73
|
+
cut?: (i: CutInput) => Promise<DepthCopies>;
|
|
63
74
|
}
|
|
64
75
|
export interface SceneReport {
|
|
65
76
|
beat: string;
|
|
@@ -87,9 +98,35 @@ export interface SceneReport {
|
|
|
87
98
|
depicts: string;
|
|
88
99
|
subjects?: number;
|
|
89
100
|
check?: ArtCheck;
|
|
101
|
+
/** Drawn with a backdrop (two layers), and the setting it shows. */
|
|
102
|
+
plate?: boolean;
|
|
103
|
+
setting?: string;
|
|
104
|
+
motifs?: string[];
|
|
105
|
+
/** Round 6: its depth planes' distances (far to near), the subjects', and how they were made. */
|
|
106
|
+
depth?: {
|
|
107
|
+
planes: number[];
|
|
108
|
+
subjects: number[];
|
|
109
|
+
scale: number;
|
|
110
|
+
info: DepthInfo;
|
|
111
|
+
};
|
|
90
112
|
};
|
|
113
|
+
/** Given a picture by the device pass's floor, not by the model's choice (`pictureFloor`). */
|
|
114
|
+
pictureForced?: boolean;
|
|
115
|
+
/** The camera grammar the scene was staged in (src/bespoke/grammar.ts), and its rhetorical role. */
|
|
116
|
+
grammar?: string;
|
|
117
|
+
role?: string;
|
|
118
|
+
/** What the camera did, one word per move (`shotSummary`). */
|
|
119
|
+
shots?: string;
|
|
120
|
+
/** Picture pixels per output pixel at the scene's closest shot (`effectiveAt`; gate `EFF_MIN`). */
|
|
121
|
+
effective?: number;
|
|
122
|
+
/** A data scene's build (src/bespoke/databuild.ts). */
|
|
123
|
+
build?: string;
|
|
91
124
|
/** A data beat: drawn as a chart, never on a picture. */
|
|
92
125
|
data?: boolean;
|
|
126
|
+
/** The scene's visual device (`assignDevices`). */
|
|
127
|
+
device?: string;
|
|
128
|
+
/** A second critique call was made for it (round 4: its first critique's scene failed). */
|
|
129
|
+
second?: boolean;
|
|
93
130
|
/** Why there is none, when one was wanted. */
|
|
94
131
|
artNote?: string;
|
|
95
132
|
/** What the deterministic repair did to the kept scene (src/bespoke/repair.ts). */
|
|
@@ -131,9 +168,28 @@ export interface BespokeReport {
|
|
|
131
168
|
rejectedText: number;
|
|
132
169
|
rejectedStyle: number;
|
|
133
170
|
rejectedSubjects: number;
|
|
171
|
+
/** Pictures refused for repeating another picture of the deck (a motif, a stock subject, or a near-identical print). */
|
|
172
|
+
rejectedRepeat: number;
|
|
173
|
+
/** Pictures drawn with a backdrop (two layers). */
|
|
174
|
+
plates: number;
|
|
175
|
+
};
|
|
176
|
+
/**
|
|
177
|
+
* How much the deck's pictures repeat each other: the closest pair's
|
|
178
|
+
* Vision feature-print distance (`SIMILAR_MAX` is the gate) and the mean,
|
|
179
|
+
* and every subject noun two pictures share.
|
|
180
|
+
*/
|
|
181
|
+
repetition?: {
|
|
182
|
+
minPrint?: number;
|
|
183
|
+
meanPrint?: number;
|
|
184
|
+
shared: string[];
|
|
134
185
|
};
|
|
135
186
|
/** Wall seconds of the pass's stages, for profiling (`build` adds its own). */
|
|
136
187
|
stages?: Record<string, number>;
|
|
188
|
+
/** The deck-order device pass: where its names came from, and why any were replaced. */
|
|
189
|
+
devices?: {
|
|
190
|
+
from: DeviceAssignment["from"];
|
|
191
|
+
note?: string;
|
|
192
|
+
};
|
|
137
193
|
scenes: SceneReport[];
|
|
138
194
|
skipped: Skip[];
|
|
139
195
|
}
|
|
@@ -141,12 +197,21 @@ export interface BespokeResult {
|
|
|
141
197
|
map: BespokeMap;
|
|
142
198
|
report: BespokeReport;
|
|
143
199
|
}
|
|
200
|
+
/**
|
|
201
|
+
* What a cached verdict was reached under: the browser's gates and the static
|
|
202
|
+
* `card_row`. A rejection stamped otherwise is asked about again.
|
|
203
|
+
*/
|
|
204
|
+
export declare const GATE_STAMP = "gates-7+cards-2:0.08/0.6/0.8/0.2/0.12";
|
|
205
|
+
/** A second critique starts only with this much of the wall-time cap left, seconds. */
|
|
206
|
+
export declare const SECOND_SECONDS = 420;
|
|
144
207
|
/** Repair rounds after the final gates, each one probe build of the repaired scenes only. */
|
|
145
208
|
export declare const REPAIR_ROUNDS = 2;
|
|
146
209
|
export declare class Budget {
|
|
147
210
|
readonly cap: number;
|
|
148
211
|
readonly deadline: number;
|
|
149
212
|
readonly now: () => number;
|
|
213
|
+
/** The per-call ceiling, seconds: the per-scene timeout. */
|
|
214
|
+
readonly callSeconds: number;
|
|
150
215
|
calls: number;
|
|
151
216
|
/** Illustration calls, capped apart from `calls` (`prefs.art`). */
|
|
152
217
|
art: number;
|
|
@@ -155,21 +220,45 @@ export declare class Budget {
|
|
|
155
220
|
rejectedText: number;
|
|
156
221
|
rejectedStyle: number;
|
|
157
222
|
rejectedSubjects: number;
|
|
223
|
+
rejectedRepeat: number;
|
|
224
|
+
/** Device-pass calls (one a deck at most). */
|
|
225
|
+
devices: number;
|
|
158
226
|
tokens: number;
|
|
159
227
|
quota: boolean;
|
|
160
|
-
constructor(cap: number, deadline: number, now: () => number
|
|
228
|
+
constructor(cap: number, deadline: number, now: () => number,
|
|
229
|
+
/** The per-call ceiling, seconds: the per-scene timeout. */
|
|
230
|
+
callSeconds?: number);
|
|
161
231
|
/** Undefined if a call may go ahead (and is counted), else why not. */
|
|
162
232
|
take(): string | undefined;
|
|
163
233
|
/** As `take`, for an illustration call against its own cap. */
|
|
164
234
|
takeArt(cap: number): string | undefined;
|
|
235
|
+
/** The deck's one device call: apart from the scene cap, but not past the quota or the clock. */
|
|
236
|
+
takeDevice(): string | undefined;
|
|
165
237
|
/** One redraw of a rejected picture: not against the art cap, but against the clock and the quota. */
|
|
166
238
|
takeRedraw(): string | undefined;
|
|
167
239
|
seconds(): number;
|
|
168
240
|
}
|
|
241
|
+
/**
|
|
242
|
+
* `run`, with at most `n` calls in flight across every kind — pictures, the
|
|
243
|
+
* device call and scenes alike. The lanes of `pool` bound the scene calls only;
|
|
244
|
+
* every beat's picture starts at once, and with every beat of a deck now drawn
|
|
245
|
+
* that would put a dozen Codex processes on the machine together.
|
|
246
|
+
*
|
|
247
|
+
* `admit` is asked once a lane is free, just before the call starts: a call
|
|
248
|
+
* can wait minutes for one (drafts queue behind 240s pictures), so a budget or
|
|
249
|
+
* timeout decided when it was queued is stale by then. It returns the args to
|
|
250
|
+
* run with, or throws to refuse.
|
|
251
|
+
*/
|
|
252
|
+
export declare function lanes(run: Runner, n: number, admit?: (args: RunnerArgs) => RunnerArgs): Runner;
|
|
253
|
+
/**
|
|
254
|
+
* The wall-time cap, applied when a call actually starts (`lanes`' admit): a
|
|
255
|
+
* call that waited past the deadline is refused, and none may run past it.
|
|
256
|
+
*/
|
|
257
|
+
export declare function deadlineAdmit(deadline: number, now: () => number): (args: RunnerArgs) => RunnerArgs;
|
|
258
|
+
/** Grammars that need two subjects or more: a one-subject picture is staged as a zoom-out instead. */
|
|
259
|
+
export declare const ONE_SUBJECT_UNSTAGEABLE: readonly string[];
|
|
169
260
|
/** Below this share of the frame, a cue "barely moves" by the rubric's measure (static_hold's floor is 0.015%). */
|
|
170
261
|
export declare const RUBRIC_CUE_CHANGE = 0.005;
|
|
171
|
-
/** The focal size the prompt asks for; the gate's floor is `KEY_TYPE_PX`. */
|
|
172
|
-
export declare const RUBRIC_FOCAL_PX = 88;
|
|
173
262
|
/**
|
|
174
263
|
* The share of its box a settled drawing should paint. MEASURED 2026-10-08:
|
|
175
264
|
* round 1's sixteen scenes painted 3.9-15.6% (median ~9%) — thin outlines and
|
|
@@ -198,10 +287,105 @@ export declare function rubricProbe(m: Measured | undefined, warnings?: readonly
|
|
|
198
287
|
*/
|
|
199
288
|
export declare const DATA_ARCHETYPES: readonly string[];
|
|
200
289
|
export declare function isDataBeat(beat: Pick<Beat, "archetype">): boolean;
|
|
201
|
-
/** A reply's labels, kept only where they are the shape the schema promised. */
|
|
202
|
-
export declare function readLabels(raw: unknown): Label[];
|
|
203
290
|
/** A reply's shot list, kept only where it is the shape the schema promised. */
|
|
204
291
|
export declare function readShots(raw: unknown): Shot[];
|
|
205
292
|
/** The paper the prompt may quote for this beat: its cited sections, equations and figure captions. */
|
|
206
293
|
export declare function contextFor(beat: Beat, source: Source): string;
|
|
294
|
+
/** One beat's device, as the deck-order pass decided it. */
|
|
295
|
+
export interface BeatDevice {
|
|
296
|
+
beatId: string;
|
|
297
|
+
/** Kebab-case name of the scene's main visual device. Unique in the deck. */
|
|
298
|
+
device: string;
|
|
299
|
+
/** Whether the beat gets an illustration; otherwise its scene is pure motion graphics. */
|
|
300
|
+
illustrate: boolean;
|
|
301
|
+
/** What the scene shows, how it is composed and how it moves, when the model gave it. */
|
|
302
|
+
idea?: string;
|
|
303
|
+
/** An illustrated beat's planned setting and subjects (round 5): the deck's pictures planned together. */
|
|
304
|
+
setting?: string;
|
|
305
|
+
subjects?: string[];
|
|
306
|
+
/** Where the name came from: the model, or the rule catalogue (no call, a bad or repeated name). */
|
|
307
|
+
from: "codex" | "rule";
|
|
308
|
+
/** Illustrated only because of the picture floor (`pictureFloor`), not by the model's choice. */
|
|
309
|
+
forced?: true;
|
|
310
|
+
/** What the beat's narration does (src/bespoke/grammar.ts `roleOf`). */
|
|
311
|
+
role: Role;
|
|
312
|
+
/** An illustrated beat's camera grammar (`chooseGrammar`), never the previous illustrated beat's. */
|
|
313
|
+
grammar?: Grammar;
|
|
314
|
+
/** A data beat's build (src/bespoke/databuild.ts `chooseBuild`), never one an earlier data beat used. */
|
|
315
|
+
build?: DataBuild;
|
|
316
|
+
}
|
|
317
|
+
/** What `assignDevices` returns: one entry per beat, in deck order. */
|
|
318
|
+
export interface DeviceAssignment {
|
|
319
|
+
beats: BeatDevice[];
|
|
320
|
+
/** `cache`: read back from a previous run, no call. */
|
|
321
|
+
from: "codex" | "cache" | "rule";
|
|
322
|
+
/** Why the model's answer was not used, or was used only in part. */
|
|
323
|
+
note?: string;
|
|
324
|
+
}
|
|
325
|
+
export interface DeviceOptions {
|
|
326
|
+
/** The model runner. Absent: the rule catalogue only, no call. */
|
|
327
|
+
run?: Runner;
|
|
328
|
+
/** Asked before the call: undefined lets it go ahead (src/bespoke/pipeline.ts `Budget`). */
|
|
329
|
+
take?: () => string | undefined;
|
|
330
|
+
/** Illustrations the deck may draw. */
|
|
331
|
+
artCap: number;
|
|
332
|
+
model?: string;
|
|
333
|
+
bin?: string;
|
|
334
|
+
config?: readonly string[];
|
|
335
|
+
/** Scratch for the prompt, schema and reply. */
|
|
336
|
+
work: string;
|
|
337
|
+
timeoutMs: number;
|
|
338
|
+
/** Where a decided assignment is kept, so a rerun asks nothing. */
|
|
339
|
+
cacheDir?: string;
|
|
340
|
+
onUsage?: (tokens: number) => void;
|
|
341
|
+
}
|
|
342
|
+
/** Bump with any change to the device prompt or the rules applied to its answer. */
|
|
343
|
+
export declare const DEVICE_VERSION = "devices-2";
|
|
344
|
+
/** Whether a device name is type or a UI element rather than a picture (`UI_DEVICE`). */
|
|
345
|
+
export declare function isUiDevice(name: string): boolean;
|
|
346
|
+
/**
|
|
347
|
+
* The fewest pictures a deck's device pass leaves it (round 6): every eligible
|
|
348
|
+
* beat, at most the art cap — the motion is the picture's own.
|
|
349
|
+
*/
|
|
350
|
+
export declare function pictureFloor(eligible: number, artCap: number): number;
|
|
351
|
+
/** A model's name made a kebab-case device name, or "" when nothing is left of it. */
|
|
352
|
+
export declare function deviceName(raw: unknown): string;
|
|
353
|
+
/**
|
|
354
|
+
* THE DECK-ORDER DEVICE PASS. Runs once, before any picture or scene is asked
|
|
355
|
+
* for — scenes generate two at a time, so their devices cannot come from
|
|
356
|
+
* finished scenes — and gives every bespoke beat one visual device, no two
|
|
357
|
+
* alike, with one line on how its scene is composed. One Codex call names them
|
|
358
|
+
* from the beats' content; whatever it does not name well (or at all, or
|
|
359
|
+
* twice), and every beat when there is no call, takes the next unused name from
|
|
360
|
+
* the rule catalogue. Illustration is decided here too: every beat with two
|
|
361
|
+
* cues, data beats included (round 6), at most `artCap`.
|
|
362
|
+
*
|
|
363
|
+
* STICKY PER BEAT. A decided device is also cached under its beat's own content,
|
|
364
|
+
* and a later run whose deck changed (one beat edited, a different call cap)
|
|
365
|
+
* keeps every unchanged beat's device and idea — the call is told they are
|
|
366
|
+
* decided — so the scenes cached under them still hit; only the changed beats
|
|
367
|
+
* are named anew. Every beat decided already: no call at all.
|
|
368
|
+
*
|
|
369
|
+
* Round 5 adds a camera grammar and a data build to this same pass, in deck
|
|
370
|
+
* order: keep it one function, one typed result. It never throws: no answer, a
|
|
371
|
+
* broken cache file or a cache it cannot write all end in names.
|
|
372
|
+
*/
|
|
373
|
+
export declare function assignDevices(beats: readonly DeviceBeat[], opts: DeviceOptions): Promise<DeviceAssignment>;
|
|
374
|
+
/**
|
|
375
|
+
* Two pictures of a deck closer than this by Vision's feature print are the
|
|
376
|
+
* same picture drawn twice. CALIBRATED 2026-10-10 on round 4's 38 pictures:
|
|
377
|
+
* pairs within a deck — all of them friendly robots, in other poses — sat at
|
|
378
|
+
* 0.49-0.89 (cross-deck 0.53-0.90), so a print alone does not see a repeated
|
|
379
|
+
* MOTIF; that is `sharedMotifs`' job. This bar catches the near-duplicate.
|
|
380
|
+
*/
|
|
381
|
+
export declare const SIMILAR_MAX = 0.45;
|
|
382
|
+
/** The subject nouns `motifs` shares with any of `others`. */
|
|
383
|
+
export declare function sharedMotifs(motifs: readonly string[], others: ReadonlyArray<readonly string[]>): string[];
|
|
384
|
+
/** The stock subjects (`STOCK_MOTIFS`) a picture draws that its beat does not name. */
|
|
385
|
+
export declare function stockDrawn(drawn: {
|
|
386
|
+
motifs: readonly string[];
|
|
387
|
+
depicts: string;
|
|
388
|
+
}, allowed: readonly string[]): string[];
|
|
389
|
+
/** How much a deck's kept pictures repeat each other (`BespokeReport.repetition`). */
|
|
390
|
+
export declare function repetitionOf(arts: readonly ArtRef[]): BespokeReport["repetition"];
|
|
207
391
|
export declare function bespokePass(input: BespokeInput): Promise<BespokeResult>;
|
|
@@ -27,8 +27,8 @@ export declare function attribute(f: Finding, windows: readonly SceneWindow[]):
|
|
|
27
27
|
export declare function pinPageErrors(findings: Finding[], candidates: BespokeMap, sidOf: ReadonlyMap<string, string>): Finding[];
|
|
28
28
|
/**
|
|
29
29
|
* What an illustrated scene's frames say about its staging: the distinct
|
|
30
|
-
* push-ins held at cue ends
|
|
31
|
-
*
|
|
30
|
+
* push-ins held at cue ends and whether it opens wide. Empty for a scene with
|
|
31
|
+
* no subjects.
|
|
32
32
|
*/
|
|
33
33
|
export declare function stagingMeasures(rows: readonly Layout[], cams: readonly CamSample[], open?: number): Partial<Measured>;
|
|
34
34
|
export declare function browserGate(deck: ProbeDeck): GateFn;
|
|
@@ -28,12 +28,14 @@
|
|
|
28
28
|
* The placement grid is Code2Video's visual anchor prompt (arXiv 2510.01174).
|
|
29
29
|
*/
|
|
30
30
|
import type { Theme } from "../emit/kit.js";
|
|
31
|
+
import { type DataBuild } from "./databuild.js";
|
|
32
|
+
import { type Grammar } from "./grammar.js";
|
|
31
33
|
import { type Box, CAMERA_MAX_SCALE } from "./shots.js";
|
|
32
34
|
export { CAMERA_MAX_SCALE };
|
|
33
35
|
/** Bump with any change to either prompt or to a reference: it is part of every cache key. */
|
|
34
|
-
export declare const PROMPT_VERSION = "bespoke-
|
|
36
|
+
export declare const PROMPT_VERSION = "bespoke-13";
|
|
35
37
|
/** Bump with any change to what `checkFragment` accepts. Also part of every key. */
|
|
36
|
-
export declare const CONTRACT_VERSION = "contract-
|
|
38
|
+
export declare const CONTRACT_VERSION = "contract-5";
|
|
37
39
|
/** What the model is told about one beat. */
|
|
38
40
|
export interface Brief {
|
|
39
41
|
lang: string;
|
|
@@ -65,30 +67,114 @@ export interface Brief {
|
|
|
65
67
|
/**
|
|
66
68
|
* The beat's illustration, attached to the call as an image (src/bespoke/art.ts),
|
|
67
69
|
* and its subjects in body-box px, left to right (src/bespoke/inspect.ts):
|
|
68
|
-
* what the
|
|
70
|
+
* what the shots frame.
|
|
69
71
|
*/
|
|
70
72
|
art?: {
|
|
71
73
|
depicts: string;
|
|
72
74
|
width: number;
|
|
73
75
|
height: number;
|
|
76
|
+
/** The picture has a backdrop layer the shell draws behind the box (round 5). */
|
|
77
|
+
plate?: boolean;
|
|
78
|
+
setting?: string;
|
|
74
79
|
subjects?: Box[];
|
|
75
|
-
/**
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
+
/**
|
|
81
|
+
* Round 6: the picture is a full-frame depth shot (src/bespoke/depth.ts) and
|
|
82
|
+
* the headline reaches this far down it, px; the scene's own layer is fixed.
|
|
83
|
+
*/
|
|
84
|
+
band?: number;
|
|
80
85
|
};
|
|
81
|
-
/** A data beat (a chart or a table)
|
|
86
|
+
/** A data beat (a chart or a table). Round 6 pictures it too; without a picture it builds its chart. */
|
|
82
87
|
data?: boolean;
|
|
88
|
+
/**
|
|
89
|
+
* The scene's main visual device, a short kebab-case name ('spike-train',
|
|
90
|
+
* 'fog-lift', 'edge-sweep'), given by the deck-order pass `assignDevices`
|
|
91
|
+
* (src/bespoke/pipeline.ts) before any scene is generated. Unique in a deck.
|
|
92
|
+
*/
|
|
93
|
+
device: string;
|
|
94
|
+
/**
|
|
95
|
+
* The device pass's one line on what this scene shows, how it is composed and
|
|
96
|
+
* how it moves. The pass sees the whole deck, so this line — not a list of
|
|
97
|
+
* the other scenes' names — is what keeps neighbouring scenes from being
|
|
98
|
+
* composed alike; and it is the beat's own, so editing one beat does not
|
|
99
|
+
* re-key every scene after it.
|
|
100
|
+
*/
|
|
101
|
+
idea?: string;
|
|
102
|
+
/** An illustrated scene's camera grammar (src/bespoke/grammar.ts), from the same pass. */
|
|
103
|
+
grammar?: Grammar;
|
|
104
|
+
/** A data scene's build (src/bespoke/databuild.ts), from the same pass. */
|
|
105
|
+
build?: DataBuild;
|
|
83
106
|
}
|
|
107
|
+
/** One beat as the deck-order device pass sees it. */
|
|
108
|
+
export interface DeviceBeat {
|
|
109
|
+
id: string;
|
|
110
|
+
archetype: string;
|
|
111
|
+
headline: string;
|
|
112
|
+
intent: string;
|
|
113
|
+
claim?: string;
|
|
114
|
+
narration?: string;
|
|
115
|
+
/** Narration cues the scene will be keyed to. Fewer than two: no picture (the shots need two). */
|
|
116
|
+
cues: number;
|
|
117
|
+
/** A chart or table beat: never illustrated. */
|
|
118
|
+
data: boolean;
|
|
119
|
+
}
|
|
120
|
+
/** The device pass's structured reply. */
|
|
121
|
+
export declare const DEVICE_SCHEMA: {
|
|
122
|
+
readonly type: "object";
|
|
123
|
+
readonly additionalProperties: false;
|
|
124
|
+
readonly required: readonly ["beats"];
|
|
125
|
+
readonly properties: {
|
|
126
|
+
readonly beats: {
|
|
127
|
+
readonly type: "array";
|
|
128
|
+
readonly items: {
|
|
129
|
+
readonly type: "object";
|
|
130
|
+
readonly additionalProperties: false;
|
|
131
|
+
readonly required: readonly ["id", "device", "illustrate", "idea", "setting", "subjects"];
|
|
132
|
+
readonly properties: {
|
|
133
|
+
readonly id: {
|
|
134
|
+
readonly type: "string";
|
|
135
|
+
};
|
|
136
|
+
readonly device: {
|
|
137
|
+
readonly type: "string";
|
|
138
|
+
};
|
|
139
|
+
readonly illustrate: {
|
|
140
|
+
readonly type: "boolean";
|
|
141
|
+
};
|
|
142
|
+
readonly idea: {
|
|
143
|
+
readonly type: "string";
|
|
144
|
+
};
|
|
145
|
+
readonly setting: {
|
|
146
|
+
readonly type: "string";
|
|
147
|
+
};
|
|
148
|
+
readonly subjects: {
|
|
149
|
+
readonly type: "array";
|
|
150
|
+
readonly items: {
|
|
151
|
+
readonly type: "string";
|
|
152
|
+
};
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
};
|
|
156
|
+
};
|
|
157
|
+
};
|
|
158
|
+
};
|
|
159
|
+
/**
|
|
160
|
+
* The deck-order device pass: one call that names, for every beat at once, the
|
|
161
|
+
* visual device its scene is built on — so no two beats of a deck look alike,
|
|
162
|
+
* which per-beat calls running two at a time cannot arrange among themselves.
|
|
163
|
+
*/
|
|
164
|
+
export declare function devicePrompt(beats: readonly DeviceBeat[], artCap: number, decided?: ReadonlyMap<string, {
|
|
165
|
+
device: string;
|
|
166
|
+
idea?: string;
|
|
167
|
+
}>): string;
|
|
84
168
|
/** What the probe measured about a candidate, quoted to the critique round. */
|
|
85
169
|
export interface Measured {
|
|
86
170
|
/** Bounding box of the drawing over the body box, settled frame. */
|
|
87
171
|
fill?: number;
|
|
88
172
|
/** Share of a 6x4 grid over the box something is drawn in. */
|
|
89
173
|
cells?: number;
|
|
90
|
-
/** Largest label at the settled frame, px. */
|
|
174
|
+
/** Largest label at the settled frame, px (as rendered). */
|
|
91
175
|
maxType?: number;
|
|
176
|
+
/** Largest font-size a label declares at the settled frame (KaTeX excluded), px. */
|
|
177
|
+
maxDeclared?: number;
|
|
92
178
|
/** Share of the box the settled drawing paints (filled shapes count, hairlines barely). */
|
|
93
179
|
mass?: number;
|
|
94
180
|
/** Share of the drawn parts left dimmed (under 0.6 opacity) at the end. */
|
|
@@ -101,18 +187,18 @@ export interface Measured {
|
|
|
101
187
|
subjects?: number;
|
|
102
188
|
/** Distinct push-ins the held frames show (illustrated scenes, `shotsOf`). */
|
|
103
189
|
shots?: number;
|
|
104
|
-
/** Whether the first cue opens
|
|
190
|
+
/** Whether the first cue opens as its grammar says (the whole picture; close, for a zoom-out). */
|
|
105
191
|
establishing?: boolean;
|
|
106
|
-
/**
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
|
|
192
|
+
/** The camera grammar the scene was staged in. */
|
|
193
|
+
grammar?: string;
|
|
194
|
+
/** Round 6: the scene's own tweens that animate UI into place (`ui_motion`). */
|
|
195
|
+
uiMotion?: readonly string[];
|
|
110
196
|
}
|
|
111
197
|
/** The structured reply. `--output-schema` holds the model to it. */
|
|
112
198
|
export declare const REPLY_SCHEMA: {
|
|
113
199
|
readonly type: "object";
|
|
114
200
|
readonly additionalProperties: false;
|
|
115
|
-
readonly required: readonly ["review", "plan", "markup", "css", "script", "shots"
|
|
201
|
+
readonly required: readonly ["review", "plan", "markup", "css", "script", "shots"];
|
|
116
202
|
readonly properties: {
|
|
117
203
|
readonly review: {
|
|
118
204
|
readonly type: "string";
|
|
@@ -129,22 +215,6 @@ export declare const REPLY_SCHEMA: {
|
|
|
129
215
|
readonly script: {
|
|
130
216
|
readonly type: "string";
|
|
131
217
|
};
|
|
132
|
-
readonly labels: {
|
|
133
|
-
readonly type: "array";
|
|
134
|
-
readonly items: {
|
|
135
|
-
readonly type: "object";
|
|
136
|
-
readonly additionalProperties: false;
|
|
137
|
-
readonly required: readonly ["subject", "text"];
|
|
138
|
-
readonly properties: {
|
|
139
|
-
readonly subject: {
|
|
140
|
-
readonly type: "integer";
|
|
141
|
-
};
|
|
142
|
-
readonly text: {
|
|
143
|
-
readonly type: "string";
|
|
144
|
-
};
|
|
145
|
-
};
|
|
146
|
-
};
|
|
147
|
-
};
|
|
148
218
|
readonly shots: {
|
|
149
219
|
readonly type: "array";
|
|
150
220
|
readonly items: {
|
|
@@ -168,15 +238,21 @@ export declare const REPLY_SCHEMA: {
|
|
|
168
238
|
};
|
|
169
239
|
/** The subjects as the prompt lists them: box px, left to right. */
|
|
170
240
|
export declare function subjectLines(subjects: readonly Box[]): string;
|
|
241
|
+
/**
|
|
242
|
+
* The beat's visual device, from the deck-order pass (`assignDevices`), and the
|
|
243
|
+
* devices the deck has already spent. Round 4's scenes kept arriving at the same
|
|
244
|
+
* labelled cards whatever the beat said; naming the device, and the ones not to
|
|
245
|
+
* reuse, is what makes neighbouring scenes differ.
|
|
246
|
+
*/
|
|
247
|
+
export declare function deviceSection(b: Pick<Brief, "device" | "idea" | "grammar" | "build">): string;
|
|
171
248
|
/** The first call: draw this beat. */
|
|
172
249
|
export declare function generatePrompt(b: Brief): string;
|
|
173
250
|
/** The rubric the critique round scores, and the probe reads off the measures. */
|
|
174
|
-
export declare const RUBRIC: readonly ["FILLS THE STAGE: the drawing uses the whole box (>= 80% of its area, no empty third or band) with visual mass — filled shapes, not hairlines", "ONE FOCUS PER CUE: in every cue one element is clearly what to look at; the rest is dimmed", "MOTION EXPLAINS: the moves are the idea (flow, transform, morph, camera, counter),
|
|
251
|
+
export declare const RUBRIC: readonly ["FILLS THE STAGE: the drawing uses the whole box (>= 80% of its area, no empty third or band) with visual mass — filled shapes, not hairlines", "ONE FOCUS PER CUE: in every cue one element is clearly what to look at; the rest is dimmed", "MOTION EXPLAINS: the moves are the idea (flow, transform, morph, camera, counter), nothing decorative, nothing still while the voice speaks; the camera goes where the voice is; NO UI MOTION — no label, plate, chip, card or bar popped, slid or grown into place", "QUIET TYPE: few words, 40-44px, one key number or word up to 56px, nothing bigger, high contrast, no collisions or clipping — the subtitles carry the words", "CONSISTENT WITH THE PACK: only the pack's colours, big flat shapes, strokes 5-8px, the deck's font", "IN SYNC: each cue's change starts within 0.5s of its words and nothing appears before the cue that names it"];
|
|
175
252
|
/** The second call: score the frames against the rubric, then fix what is wrong. */
|
|
176
253
|
export declare function critiquePrompt(b: Brief, current: {
|
|
177
254
|
markup: string;
|
|
178
255
|
css: string;
|
|
179
256
|
script: string;
|
|
180
257
|
shots?: readonly unknown[];
|
|
181
|
-
labels?: readonly unknown[];
|
|
182
258
|
}, findings: readonly string[], legend: string | undefined, measured?: Measured): string;
|
|
@@ -13,8 +13,14 @@
|
|
|
13
13
|
* motion-design habits round 1 lacked:
|
|
14
14
|
*
|
|
15
15
|
* - THE STAGE IS FILLED: every reference spans >= 85% of its box at the end.
|
|
16
|
-
* -
|
|
17
|
-
*
|
|
16
|
+
* - CUE 1 STANDS FROM THE FIRST FRAME (round 6): the seam cuts to it, so the
|
|
17
|
+
* stage is never empty between scenes (`seam_blank`).
|
|
18
|
+
* - EVERY TWEEN IS A fromTo (AGENTS.md invariant 2), its start written out; a
|
|
19
|
+
* later one on the same property has immediateRender:false.
|
|
20
|
+
* - QUIET TYPE (the v2 scale, `TYPE_SCALE`, since 2026-10-10): one focal
|
|
21
|
+
* label or number per cue at 56px at most; labels 40-44px; nothing under
|
|
22
|
+
* 40px. They were 88-120px focal and 48-60px labels, and the founder's
|
|
23
|
+
* verdict on that was "the fonts are too large".
|
|
18
24
|
* - ONE FOCUS PER CUE: what the voice is naming is lit (accent, full opacity);
|
|
19
25
|
* what it is not is dimmed to ~0.3 — never removed, so the viewer keeps place.
|
|
20
26
|
* - IDENTITY IS KEPT: a thing that changes state moves or morphs into the new
|
|
@@ -45,13 +51,6 @@ export interface Reference {
|
|
|
45
51
|
}>;
|
|
46
52
|
duration: number;
|
|
47
53
|
fragment: Fragment;
|
|
48
|
-
/** An illustrated reference's subjects, box px: what the prompt would list for its picture. */
|
|
49
|
-
subjects?: ReadonlyArray<{
|
|
50
|
-
x: number;
|
|
51
|
-
y: number;
|
|
52
|
-
w: number;
|
|
53
|
-
h: number;
|
|
54
|
-
}>;
|
|
55
54
|
}
|
|
56
55
|
/** The box every reference is drawn for. */
|
|
57
56
|
export declare const REFERENCE_BOX: {
|
|
@@ -59,15 +58,15 @@ export declare const REFERENCE_BOX: {
|
|
|
59
58
|
readonly height: 732;
|
|
60
59
|
};
|
|
61
60
|
export declare const REFERENCES: readonly Reference[];
|
|
62
|
-
/** Whether a reference places the beat's illustration (and so needs a beat that has one). */
|
|
63
|
-
export declare function placesArt(r: Reference): boolean;
|
|
64
61
|
/** A reference with its `{{token}}` colours filled from `theme`. */
|
|
65
62
|
export declare function paint(f: Fragment, theme: Theme): Fragment;
|
|
66
63
|
/**
|
|
67
64
|
* The two references a beat is shown: the best fit for its archetype first,
|
|
68
65
|
* then the best fit that teaches a DIFFERENT motion, so every prompt carries
|
|
69
|
-
* at least two verbs beyond fade-and-draw.
|
|
70
|
-
*
|
|
71
|
-
*
|
|
66
|
+
* at least two verbs beyond fade-and-draw. A beat with a picture is never shown
|
|
67
|
+
* a scene that moves its own camera (the shell's shots do that); a data beat
|
|
68
|
+
* leads with the chart that builds. (Round 6: a scene on a depth picture is
|
|
69
|
+
* shown no reference at all — src/bespoke/prompt.ts — and round 4's
|
|
70
|
+
* "illustrated" reference, labels and spotlight over the picture, is gone.)
|
|
72
71
|
*/
|
|
73
72
|
export declare function pickReferences(archetype: string, n?: number, art?: boolean, data?: boolean): Reference[];
|