@jokerized/decksmith 0.1.4 → 0.3.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 +161 -26
- package/dist/cli.js +3864 -1816
- package/dist/ds-morph.js +1 -0
- package/dist/index.js +2701 -780
- package/dist/mcp.js +2509 -633
- 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/equation-morph.d.ts +2 -0
- package/dist/types/emit/archetypes/equation-walk.d.ts +63 -1
- package/dist/types/emit/archetypes/index.d.ts +1 -1
- package/dist/types/emit/archetypes/pipeline.d.ts +2 -2
- package/dist/types/emit/archetypes/stack.d.ts +20 -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/depth.d.ts +95 -0
- package/dist/types/emit/kit.d.ts +153 -0
- package/dist/types/emit/morph-runtime.d.ts +178 -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 +294 -4
- package/dist/types/verify/apparent.d.ts +123 -0
- package/dist/types/verify/index.d.ts +114 -6
- package/dist/types/verify/typefloor.d.ts +3 -1
- package/package.json +2 -2
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The keyed equation morph — TransformMatchingTex, for the DOM.
|
|
3
|
+
*
|
|
4
|
+
* Two KaTeX renderings in, one seek-evaluable plan out. `build()` runs once in
|
|
5
|
+
* the browser, inside the ready gate (Seam B), and bakes every number; the
|
|
6
|
+
* plan is then driven by a GSAP PLUGIN — `render(ratio)` is part of being
|
|
7
|
+
* seeked, so it fires under `suppressEvents` where an `onUpdate` would not
|
|
8
|
+
* (invariant 11). Nothing here runs on a callback.
|
|
9
|
+
*
|
|
10
|
+
* Four phases, kept separate on purpose:
|
|
11
|
+
* 1. LIFT — pull every ink-bearing leaf out of KaTeX's nested boxes into a
|
|
12
|
+
* flat absolutely-positioned overlay. Removes clipping, vlist
|
|
13
|
+
* stacking and transform-on-inline entirely.
|
|
14
|
+
* 2. GROUP — collect leaves into the units that move as one body. An author
|
|
15
|
+
* key makes a group; an unkeyed leaf is its own group.
|
|
16
|
+
* 3. MATCH — decide which group of A becomes which group of B.
|
|
17
|
+
* 4. PLAN — a similarity transform per group, as per-leaf segments.
|
|
18
|
+
*
|
|
19
|
+
* `evaluate()` turns a plan and a progress into styles. It is a pure function
|
|
20
|
+
* of its arguments — two seeks to one time write the same strings — which is
|
|
21
|
+
* what makes the render byte-identical across browser processes, measured on
|
|
22
|
+
* the spike this is ported from (`experiments/013-vocabulary/morph/`).
|
|
23
|
+
*
|
|
24
|
+
* Bundled to an IIFE by `scripts/build.mjs`, vendored beside GSAP by the CLI,
|
|
25
|
+
* and loaded only by a deck that has a morph in it. The pure halves — `group`,
|
|
26
|
+
* `match`, `plan`, `evaluate` — are exported for the tests, which is why the
|
|
27
|
+
* window registration at the bottom is guarded.
|
|
28
|
+
*/
|
|
29
|
+
/** The class prefix an author key is carried on: `\htmlClass{ds-k-<key>}{...}`. */
|
|
30
|
+
export declare const KEY_PREFIX = "ds-k-";
|
|
31
|
+
export interface Leaf {
|
|
32
|
+
el: HTMLElement;
|
|
33
|
+
key: string;
|
|
34
|
+
sig: string;
|
|
35
|
+
text: string;
|
|
36
|
+
/** Font size in layout px. */
|
|
37
|
+
fs: number;
|
|
38
|
+
/** Centre and box, in the host's own layout px. */
|
|
39
|
+
cx: number;
|
|
40
|
+
cy: number;
|
|
41
|
+
w: number;
|
|
42
|
+
h: number;
|
|
43
|
+
}
|
|
44
|
+
export interface Group {
|
|
45
|
+
ident: string;
|
|
46
|
+
key: string;
|
|
47
|
+
leaves: Leaf[];
|
|
48
|
+
cx: number;
|
|
49
|
+
cy: number;
|
|
50
|
+
w: number;
|
|
51
|
+
h: number;
|
|
52
|
+
fs: number;
|
|
53
|
+
text: string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The unit that moves as one body.
|
|
57
|
+
*
|
|
58
|
+
* This is the whole quality argument of the file. Matching GLYPH BY GLYPH is
|
|
59
|
+
* what a naive reading of TransformMatchingTex suggests and it is visibly
|
|
60
|
+
* wrong: in `a^2+b^2=c^2 -> c^2-b^2=a^2` the three `2`s are interchangeable
|
|
61
|
+
* under any text-plus-class identity, so they pair left-to-right and stay put
|
|
62
|
+
* while their bases cross over. The audience sees the letters swap and the
|
|
63
|
+
* exponents refuse to follow, which asserts something false about the algebra.
|
|
64
|
+
*
|
|
65
|
+
* Manim does not avoid this by being cleverer — `MathTex("a^2", "+", "b^2",
|
|
66
|
+
* ...)` is the AUTHOR splitting the expression into the parts meant to
|
|
67
|
+
* survive. `\htmlClass{ds-k-<key>}{...}` is the same act, and it is the only
|
|
68
|
+
* mechanism here that produces a correct morph on an ambiguous pair.
|
|
69
|
+
*
|
|
70
|
+
* A group's identity is its key alone, not its contents, so `ds-k-exp` can
|
|
71
|
+
* carry `x^2` onto `x^3`: the body moves and its contents dissolve.
|
|
72
|
+
*/
|
|
73
|
+
export declare function group(items: Leaf[]): Group[];
|
|
74
|
+
export interface Match {
|
|
75
|
+
pairs: [number, number][];
|
|
76
|
+
dropA: number[];
|
|
77
|
+
addB: number[];
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Bucket by identity, then pair NEAREST FIRST within a bucket.
|
|
81
|
+
*
|
|
82
|
+
* Document order — the spike's rule — sends the wrong twin across the slide:
|
|
83
|
+
* `F = E(I), X = W(F)` has two `=` and its successor one, and pairing the
|
|
84
|
+
* first `=` with it flew a glyph 800px over the equation while the `=` sitting
|
|
85
|
+
* 40px from its destination faded out. Nearest-first is what a viewer expects
|
|
86
|
+
* of a symbol that did not move. Ties fall to document order, so it is still a
|
|
87
|
+
* pure function of the two layouts.
|
|
88
|
+
*/
|
|
89
|
+
export declare function match(A: Group[], B: Group[]): Match;
|
|
90
|
+
export type Prop = "x" | "y" | "s" | "o";
|
|
91
|
+
export type Ease = "none" | "power1.in" | "power1.out" | "power2.in" | "power2.out" | "power2.inOut";
|
|
92
|
+
/**
|
|
93
|
+
* One property of one leaf, over one slice of the morph.
|
|
94
|
+
*
|
|
95
|
+
* Times are FRACTIONS of the morph, not seconds: the plan is evaluated against
|
|
96
|
+
* the plugin tween's ratio, so `pace()` scaling the tween's `duration` scales
|
|
97
|
+
* all of this with it, and the plan never has to know how long it takes.
|
|
98
|
+
*/
|
|
99
|
+
export interface Step {
|
|
100
|
+
el: HTMLElement;
|
|
101
|
+
prop: Prop;
|
|
102
|
+
from: number;
|
|
103
|
+
to: number;
|
|
104
|
+
at: number;
|
|
105
|
+
dur: number;
|
|
106
|
+
ease: Ease;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* One similarity transform per matched group, expressed per leaf.
|
|
110
|
+
*
|
|
111
|
+
* A group's leaves move RIGIDLY: every leaf gets the same scale and the
|
|
112
|
+
* translation that the group's own centre-to-centre move implies for its
|
|
113
|
+
* position within the body. `a^2` therefore arrives with its exponent still
|
|
114
|
+
* attached, which is the whole reason groups exist.
|
|
115
|
+
*
|
|
116
|
+
* The B side is placed by the INVERSE transform — B's leaves start where they
|
|
117
|
+
* would sit inside A's box — so when the contents differ the two renderings
|
|
118
|
+
* dissolve into each other while sharing one trajectory.
|
|
119
|
+
*
|
|
120
|
+
* `arc` bows the path. Two glyphs swapping ends of an equation travel the same
|
|
121
|
+
* straight line in opposite directions and pile up in the middle, which reads
|
|
122
|
+
* as a collision rather than an exchange. The bow is two segments on `y` — out
|
|
123
|
+
* over the first half, back over the second — against one on `x` that runs
|
|
124
|
+
* the whole way.
|
|
125
|
+
*/
|
|
126
|
+
export declare function plan(A: Group[], B: Group[], m: Match, opt: {
|
|
127
|
+
arc?: boolean;
|
|
128
|
+
}): Step[];
|
|
129
|
+
export interface MorphPlan {
|
|
130
|
+
leaves: {
|
|
131
|
+
el: HTMLElement;
|
|
132
|
+
side: "a" | "b";
|
|
133
|
+
}[];
|
|
134
|
+
steps: Step[];
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Write the morph at progress `v` in [0, 1] into every leaf's style.
|
|
138
|
+
*
|
|
139
|
+
* Every leaf is written every time, from its resting state forward — A drawn,
|
|
140
|
+
* B hidden, nothing moved — so a cold seek to any progress produces the same
|
|
141
|
+
* strings as a walk to it. That is the property the capture depends on.
|
|
142
|
+
*/
|
|
143
|
+
export declare function evaluate(p: MorphPlan, v: number): void;
|
|
144
|
+
export interface Stats {
|
|
145
|
+
groupsA: number;
|
|
146
|
+
groupsB: number;
|
|
147
|
+
leavesA: number;
|
|
148
|
+
leavesB: number;
|
|
149
|
+
matched: number;
|
|
150
|
+
dropped: number;
|
|
151
|
+
added: number;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Build the morph inside `host`, which must already contain two rendered KaTeX
|
|
155
|
+
* roots marked `data-morph="a"` and `data-morph="b"`. Runs in `measure`, after
|
|
156
|
+
* fonts; the plan is kept for the plugin tween on the same host to find.
|
|
157
|
+
*/
|
|
158
|
+
export declare function build(host: HTMLElement, opt?: {
|
|
159
|
+
arc?: boolean;
|
|
160
|
+
}): Stats;
|
|
161
|
+
interface PluginState {
|
|
162
|
+
plan: MorphPlan;
|
|
163
|
+
end: number;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* `dsMorph` as a GSAP property: `tl.fromTo(host, { dsMorph: 0 }, { dsMorph: 1 })`.
|
|
167
|
+
*
|
|
168
|
+
* A plugin's `render` is called by the timeline as part of being seeked, so
|
|
169
|
+
* this runs under `suppressEvents` — the capture path — where an `onUpdate`
|
|
170
|
+
* does not. The tween's own ease must be "none": the plan carries its eases
|
|
171
|
+
* per segment, and a second ease on top would warp every one of them.
|
|
172
|
+
*/
|
|
173
|
+
export declare const DSMorphPlugin: {
|
|
174
|
+
name: string;
|
|
175
|
+
init(this: PluginState, target: Element, value: unknown): void;
|
|
176
|
+
render(ratio: number, d: PluginState): void;
|
|
177
|
+
};
|
|
178
|
+
export {};
|
package/dist/types/emit/svg.d.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* that composes these primitives, and putting it here is how a primitive layer
|
|
14
14
|
* turns into six hand-rolled one-offs wearing a shared import.
|
|
15
15
|
*/
|
|
16
|
-
import { type Vars } from "./kit.js";
|
|
16
|
+
import { type Tween, type Vars } from "./kit.js";
|
|
17
17
|
export interface Pt {
|
|
18
18
|
x: number;
|
|
19
19
|
y: number;
|
|
@@ -47,18 +47,54 @@ export declare function n(v: number): string;
|
|
|
47
47
|
*/
|
|
48
48
|
export declare function nv(v: number): number;
|
|
49
49
|
/**
|
|
50
|
-
* The `from` vars of a stroke draw-on,
|
|
50
|
+
* The `from` and `to` vars of a stroke draw-on, measured by the browser.
|
|
51
51
|
*
|
|
52
|
-
* The
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
52
|
+
* The `drawFrom` this replaced needed the emitter to know the path's length at
|
|
53
|
+
* BUILD time —
|
|
54
|
+
* `Math.hypot(knee.x - start.x, knee.y - start.y) + b.w` in annotated-figure,
|
|
55
|
+
* `perimeter(b)` in grid — which is arithmetic about a shape the browser is
|
|
56
|
+
* going to measure anyway, kept in step by hand. DrawSVG asks the path.
|
|
57
|
+
*
|
|
58
|
+
* It also retires a bug the hand-computed version had. GSAP writes
|
|
59
|
+
* `strokeDashoffset` back as an integer px, so a leader of length 720.21 came to
|
|
60
|
+
* rest at an offset of 720 and left 0.21px of dash exposed at the path start,
|
|
61
|
+
* which `stroke-linecap: round` paints as a full-width dot: a yellow dot sat on
|
|
62
|
+
* the grid one reveal before its own region was drawn, inside the frame, above
|
|
63
|
+
* the type floor, and invisible to every gate. That is why the old `drawFrom`
|
|
64
|
+
* rounded up and added a pixel of slack. A percentage has no remainder to leave.
|
|
65
|
+
*
|
|
66
|
+
* Safe under capture because a plugin's `render()` is part of being seeked,
|
|
67
|
+
* unlike a callback — see `DRAWSVG_SRC` in composition.ts for the measurement.
|
|
68
|
+
* Anything drawn this way still obeys invariant 2: it is a `fromTo`, always.
|
|
69
|
+
*/
|
|
70
|
+
export declare const DRAW_FROM: Vars;
|
|
71
|
+
export declare const DRAW_TO: Vars;
|
|
72
|
+
/**
|
|
73
|
+
* Something travels a polyline — a pulse along an arrow, a marker ring along a
|
|
74
|
+
* curve, a highlight down a divider: one `x`/`y` `fromTo` per leg, each leg's
|
|
75
|
+
* share of `seconds` proportional to its length, `ease: "none"` until the last
|
|
76
|
+
* leg, which settles with `power2.out`. Only the first leg renders
|
|
77
|
+
* immediately; every later one is `immediateRender: false` and starts exactly
|
|
78
|
+
* where the leg before it ended, so the route reads as one motion under any
|
|
79
|
+
* seek. `x`/`y` are TRANSLATIONS, so author the traveller at the origin —
|
|
80
|
+
* `<circle r="6">` — and give the route in the same user space as the parts it
|
|
81
|
+
* passes. A transform rather than `attr: { cx, cy }` so one verb moves an SVG
|
|
82
|
+
* dot and an HTML rule alike; not MotionPath, which is not loaded.
|
|
83
|
+
*
|
|
84
|
+
* ONE CALL PER ELEMENT. The first leg's immediate render is the one that
|
|
85
|
+
* (element, x/y) gets; a second route on the same element would need
|
|
86
|
+
* `immediateRender: false` and a `from` equal to the first route's end, which
|
|
87
|
+
* is not where a second route starts. A pulse per arrow, not one pulse reused.
|
|
88
|
+
*
|
|
89
|
+
* Every leg starts where the last one ended, printed at two places, and lint
|
|
90
|
+
* reads `at + duration` back in floating point: 0.1 + 0.2 is 0.30000000000000004,
|
|
91
|
+
* which `overlapping_gsap_tweens` reports as a 4e-17s overlap with the leg at
|
|
92
|
+
* 0.3 (measured on 0.7.90). So a leg whose float sum overshoots its successor
|
|
93
|
+
* is shortened by a hundredth — a rest at the corner under one frame long —
|
|
94
|
+
* and the corner itself, and the arrival at `at + seconds`, stay where they
|
|
95
|
+
* were scheduled.
|
|
60
96
|
*/
|
|
61
|
-
export declare function
|
|
97
|
+
export declare function travel(target: string, route: readonly Pt[], at: number, seconds: number): Tween[];
|
|
62
98
|
/** Scene-scoped element id. Every timeline selector is built from one of these. */
|
|
63
99
|
export declare function id(sid: string, part: string, i?: number): string;
|
|
64
100
|
/**
|
|
@@ -68,13 +104,36 @@ export declare function id(sid: string, part: string, i?: number): string;
|
|
|
68
104
|
* Every archetype sizes its boxes and padding through this function. When two of
|
|
69
105
|
* them disagree about how wide "Reconstruction" is, one of them clips.
|
|
70
106
|
*/
|
|
71
|
-
|
|
107
|
+
/**
|
|
108
|
+
* Which face actually draws a run.
|
|
109
|
+
*
|
|
110
|
+
* A deck that bundles a CJK family sets EVERY run in it, ASCII included, because
|
|
111
|
+
* `fontStack` puts that family ahead of Inter and it covers Latin. So the face is
|
|
112
|
+
* a property of the DECK, not of the characters in one run — and a run of pure
|
|
113
|
+
* ASCII inside a Korean deck is drawn by the Korean face even though nothing in
|
|
114
|
+
* it is Korean.
|
|
115
|
+
*
|
|
116
|
+
* `"latin"` is not "no CJK in this string"; it is "this deck bundles no CJK
|
|
117
|
+
* family". Passing it for a run that does contain CJK would be a lie, which is
|
|
118
|
+
* why the auto-detection below still runs underneath: the parameter can only
|
|
119
|
+
* widen what a run is charged, never narrow it.
|
|
120
|
+
*/
|
|
121
|
+
export type Face = "latin" | "cjk" | "hangul";
|
|
122
|
+
/**
|
|
123
|
+
* The face a theme's stack will use, from the family `familyFor` put in front.
|
|
124
|
+
*
|
|
125
|
+
* Korean is split out because it is the one character the four bundled families
|
|
126
|
+
* disagree about: KR draws the middle dot at 0.561em where JP, SC and TC draw it
|
|
127
|
+
* on the em grid at 1.0.
|
|
128
|
+
*/
|
|
129
|
+
export declare function faceOf(fontStack: string): Face;
|
|
130
|
+
export declare function textWidth(text: string, fontSize: number, weight?: number, tracking?: number, tabular?: boolean, face?: Face): number;
|
|
72
131
|
/**
|
|
73
132
|
* Greedy word wrap to `maxWidth`. A word wider than the line is broken by
|
|
74
133
|
* character — which is also how Korean and Chinese wrap, since they arrive as
|
|
75
134
|
* one unbroken "word".
|
|
76
135
|
*/
|
|
77
|
-
export declare function wrap(text: string, fontSize: number, maxWidth: number, weight?: number, tracking?: number): string[];
|
|
136
|
+
export declare function wrap(text: string, fontSize: number, maxWidth: number, weight?: number, tracking?: number, face?: Face): string[];
|
|
78
137
|
/** The SVG root. Width and height match the viewBox so px and user units agree. */
|
|
79
138
|
export declare function svg(elementId: string, w: number, h: number, body: string): string;
|
|
80
139
|
export declare function rect(b: Box, a?: Attrs): string;
|
|
@@ -100,6 +159,11 @@ export interface TextOptions {
|
|
|
100
159
|
vAlign?: "baseline" | "middle";
|
|
101
160
|
class?: string;
|
|
102
161
|
id?: string;
|
|
162
|
+
/**
|
|
163
|
+
* The face the deck's stack will draw this in. Only consulted when `maxWidth`
|
|
164
|
+
* makes this text wrap — an unwrapped run is measured by nothing here.
|
|
165
|
+
*/
|
|
166
|
+
face?: Face;
|
|
103
167
|
}
|
|
104
168
|
export declare function text(content: string, at: Pt, o: TextOptions): string;
|
|
105
169
|
export interface ArrowOptions {
|
|
@@ -16,6 +16,7 @@ import { type Scene } from "./kit.js";
|
|
|
16
16
|
import type { DeckTheme } from "./themes/index.js";
|
|
17
17
|
export { type DeckTheme, ink, mono, paper, resolveTheme, THEME_NAMES, THEMES, } from "./themes/index.js";
|
|
18
18
|
/** Where `ingest` writes the subsetted font bundle, relative to the deck. */
|
|
19
|
+
export declare const FONT_BUNDLE_DIR = "assets/fonts";
|
|
19
20
|
export declare const FONT_BUNDLE_HREF = "assets/fonts/fonts.css";
|
|
20
21
|
/** A luminance breath. Safe on an element an entrance tween already moves. */
|
|
21
22
|
export declare const BREATHE = "ds-breathe 6s ease-in-out infinite alternate";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Prefs } from "../prefs.js";
|
|
2
|
+
import { type Source, type Storyboard } from "../types.js";
|
|
3
|
+
import { type ImageProvider } from "./providers.js";
|
|
4
|
+
export interface IllustrateOpts {
|
|
5
|
+
prefs: Prefs;
|
|
6
|
+
/** `<dir of source.json>/assets`. Created if missing. */
|
|
7
|
+
assetsDir: string;
|
|
8
|
+
/** Tests inject; the default is `imageChain(prefs.images, resolveImageBackend())`. */
|
|
9
|
+
chain?: ImageProvider[];
|
|
10
|
+
/** Progress lines. Silent by default, for the reason `buildDeck` gives. */
|
|
11
|
+
onStep?: (message: string) => void;
|
|
12
|
+
}
|
|
13
|
+
export interface Illustrated {
|
|
14
|
+
beatId: string;
|
|
15
|
+
figureId: string;
|
|
16
|
+
/** Which rung drew it. */
|
|
17
|
+
provider: string;
|
|
18
|
+
/** The figure's `src`: a file name under `assetsDir`. */
|
|
19
|
+
src: string;
|
|
20
|
+
/** Found on disk from an earlier run; no provider was asked. */
|
|
21
|
+
cached: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Draw every pending brief. Always finishes: the tool rung is last and pure.
|
|
25
|
+
*
|
|
26
|
+
* Returns fresh objects parsed through the schemas; the ones passed in are not
|
|
27
|
+
* touched, so a caller that keeps the old storyboard around still holds the
|
|
28
|
+
* pending one.
|
|
29
|
+
*/
|
|
30
|
+
export declare function illustrate(storyboard: Storyboard, source: Source, opts: IllustrateOpts): Promise<{
|
|
31
|
+
storyboard: Storyboard;
|
|
32
|
+
source: Source;
|
|
33
|
+
illustrated: Illustrated[];
|
|
34
|
+
}>;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { type Runner } from "../plan/codex.js";
|
|
2
|
+
import type { ImagesPrefs } from "../types.js";
|
|
3
|
+
export type ImageAspect = "landscape" | "square" | "portrait";
|
|
4
|
+
export interface ImageRequest {
|
|
5
|
+
/** The scene. Never text, labels, numbers or charts — nothing in a picture can be read or checked. */
|
|
6
|
+
prompt: string;
|
|
7
|
+
/** `images.style`, folded into every prompt. */
|
|
8
|
+
style: string;
|
|
9
|
+
aspect: ImageAspect;
|
|
10
|
+
/**
|
|
11
|
+
* `images.model`, for the separate backend only; the Codex and tool rungs
|
|
12
|
+
* ignore it. Carried on the request rather than baked into the provider
|
|
13
|
+
* because the provider is resolved from the environment before any
|
|
14
|
+
* preferences are known, and a `--image-model` that changed only the cache
|
|
15
|
+
* key would be a flag that does nothing.
|
|
16
|
+
*/
|
|
17
|
+
model?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface ImageResult {
|
|
20
|
+
bytes: Buffer;
|
|
21
|
+
mime: "image/png" | "image/jpeg" | "image/svg+xml";
|
|
22
|
+
width: number;
|
|
23
|
+
height: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* One rung. `check` is separate from `generate` for the reason `SpeechProvider`
|
|
27
|
+
* splits them: a missing key is reported before a picture is asked for, not
|
|
28
|
+
* two minutes into one.
|
|
29
|
+
*/
|
|
30
|
+
export interface ImageProvider {
|
|
31
|
+
readonly id: string;
|
|
32
|
+
/** Throws with an actionable sentence. */
|
|
33
|
+
check(): Promise<void>;
|
|
34
|
+
generate(req: ImageRequest): Promise<ImageResult>;
|
|
35
|
+
}
|
|
36
|
+
/** Pixels per aspect — the backend's `size`, and the tool's viewBox. */
|
|
37
|
+
export declare const SIZE: Readonly<Record<ImageAspect, {
|
|
38
|
+
width: number;
|
|
39
|
+
height: number;
|
|
40
|
+
}>>;
|
|
41
|
+
export interface OpenAiImagesOptions {
|
|
42
|
+
apiKey: string;
|
|
43
|
+
/** `POST {baseUrl}/images/generations`. LocalAI and most gateways speak this. */
|
|
44
|
+
baseUrl?: string;
|
|
45
|
+
/** Default when the request names none. */
|
|
46
|
+
model?: string;
|
|
47
|
+
/** Injected by tests. */
|
|
48
|
+
fetch?: typeof fetch;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Any OpenAI-compatible images endpoint.
|
|
52
|
+
*
|
|
53
|
+
* Errors are shaped here rather than passed through: the message is status plus
|
|
54
|
+
* the API's `error.code` (or `type`), never its text and never a URL, so it can
|
|
55
|
+
* go straight into a job log a stranger reads. The key is used exactly once,
|
|
56
|
+
* on the generation request — a `url` in the answer is fetched bare, and only
|
|
57
|
+
* when it sits on the backend's own origin, because following an arbitrary URL
|
|
58
|
+
* with or without the key is how a gateway's answer becomes an open proxy.
|
|
59
|
+
*/
|
|
60
|
+
export declare function openaiImages(opts: OpenAiImagesOptions): ImageProvider;
|
|
61
|
+
export interface CodexImagesOptions {
|
|
62
|
+
/** Injected by tests; `runCodex` otherwise. */
|
|
63
|
+
run?: Runner;
|
|
64
|
+
timeoutMs?: number;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The main account, drawing with its own image tool.
|
|
68
|
+
*
|
|
69
|
+
* `codex exec` runs in a scratch directory under `workspace-write`, which
|
|
70
|
+
* `codexCommand` fences to that directory and nothing else, and is asked to
|
|
71
|
+
* leave one PNG there. An account without the tool answers `ok:false` and says
|
|
72
|
+
* why; that reason is the error, verbatim, because it is the one thing the
|
|
73
|
+
* user can act on. Whether non-interactive `codex exec` exposes the tool at all
|
|
74
|
+
* is the hypothesis the design's live run settles — this code is written for
|
|
75
|
+
* either answer.
|
|
76
|
+
*/
|
|
77
|
+
export declare function codexImages(opts?: CodexImagesOptions): ImageProvider;
|
|
78
|
+
/**
|
|
79
|
+
* The picture the tool draws for itself: six to ten overlapping shapes in one
|
|
80
|
+
* accent and the ink, seeded from the brief. Not an illustration of the scene —
|
|
81
|
+
* a stable, text-free composition that keeps the slide's layout honest until
|
|
82
|
+
* something better draws it. Same request, same bytes.
|
|
83
|
+
*/
|
|
84
|
+
export declare function drawSvg(req: ImageRequest): string;
|
|
85
|
+
/** The size the tool wrote into its own SVG. `imageSize` is raster-only on purpose. */
|
|
86
|
+
export declare function svgSize(bytes: Buffer): {
|
|
87
|
+
width: number;
|
|
88
|
+
height: number;
|
|
89
|
+
};
|
|
90
|
+
/** The last rung. Pure, so it cannot fail, so `illustrate` always finishes. */
|
|
91
|
+
export declare function toolSvg(): ImageProvider;
|
|
92
|
+
/**
|
|
93
|
+
* The backend the environment names, or nothing.
|
|
94
|
+
*
|
|
95
|
+
* Environment only, mirroring `DECKSMITH_TTS`: a key is never a preference,
|
|
96
|
+
* never in a config file and never in a `.deck`. Naming a backend without its
|
|
97
|
+
* key, or naming one this build does not have, throws HERE — at `illustrate`
|
|
98
|
+
* on the CLI, in the server's preflight, in MCP `capabilities` — and nowhere
|
|
99
|
+
* at import, so a broken deployment is a sentence in the log rather than a
|
|
100
|
+
* process that will not start.
|
|
101
|
+
*/
|
|
102
|
+
export declare function resolveImageBackend(env?: NodeJS.ProcessEnv): ImageProvider | undefined;
|
|
103
|
+
/**
|
|
104
|
+
* The rungs, in the order `illustrate` tries them, for one preference.
|
|
105
|
+
* `auto` is all three; `codex` skips the backend; `svg` is the tool alone — no
|
|
106
|
+
* network, no spend, and a deck whose every picture is reproducible.
|
|
107
|
+
*/
|
|
108
|
+
export declare function imageChain(images: ImagesPrefs, backend?: ImageProvider): ImageProvider[];
|
package/dist/types/index.d.ts
CHANGED
|
@@ -34,7 +34,7 @@ export type { CodexOptions, Runner } from "./plan/codex.js";
|
|
|
34
34
|
* not contain. `buildDeck` does not call it — a caller assembling a storyboard
|
|
35
35
|
* by hand wants this before it spends a render on a dangling ref.
|
|
36
36
|
*/
|
|
37
|
-
export { assertInsideResolves, assertRefsResolve } from "./plan/refs.js";
|
|
37
|
+
export { assertInsideResolves, assertRefsResolve, hasIllustrations } from "./plan/refs.js";
|
|
38
38
|
/** The prompt, so a consumer driving its own model can reproduce our planning. */
|
|
39
39
|
export { renderSource, systemPrompt } from "./plan/prompt.js";
|
|
40
40
|
/**
|
|
@@ -42,8 +42,22 @@ export { renderSource, systemPrompt } from "./plan/prompt.js";
|
|
|
42
42
|
* turned into the two derived ones. Pure arithmetic over preferences, so a
|
|
43
43
|
* caller can ask what a target costs before spending a plan on it.
|
|
44
44
|
*/
|
|
45
|
-
export { COMFORTABLE_CPS, durationPlan, slidesFor, LAST_HOLD_SECONDS, MAX_PLAYBACK, MIN_SENTENCE_CHARS, MOTION_SHARE, p95CueRate, playbackFactor, playbackWarning, SPEAKING_STOPS, SPEECH_CPS, STOPS_PER_BEAT, tempoChain, } from "./plan/duration.js";
|
|
45
|
+
export { COMFORTABLE_CPS, durationPlan, slidesFor, LAST_HOLD_SECONDS, MAX_PLAYBACK, MIN_SENTENCE_CHARS, MOTION_SHARE, p95CueRate, playbackFactor, playbackRefusal, playbackWarning, SPEAKING_STOPS, SPEECH_CPS, STOPS_PER_BEAT, tempoChain, } from "./plan/duration.js";
|
|
46
46
|
export type { DurationPlan } from "./plan/duration.js";
|
|
47
|
+
/**
|
|
48
|
+
* Pictures for the beats that asked for one. `illustrate` is a plan-time step
|
|
49
|
+
* like `narrate` — it writes files beside the source and rewrites both
|
|
50
|
+
* documents — so a server runs it between `plan` and `build` and hands the
|
|
51
|
+
* returned objects on; nothing after it knows a figure was generated.
|
|
52
|
+
* `resolveImageBackend` and `imageChain` are the two halves of "where the
|
|
53
|
+
* pictures come from": the environment names a backend, the preference says
|
|
54
|
+
* where the chain starts, and a caller that wants neither injects its own
|
|
55
|
+
* `ImageProvider[]` — which is how a server's tests never spawn Codex.
|
|
56
|
+
*/
|
|
57
|
+
export { illustrate } from "./images/illustrate.js";
|
|
58
|
+
export type { Illustrated, IllustrateOpts } from "./images/illustrate.js";
|
|
59
|
+
export { imageChain, resolveImageBackend } from "./images/providers.js";
|
|
60
|
+
export type { ImageAspect, ImageProvider, ImageRequest, ImageResult, } from "./images/providers.js";
|
|
47
61
|
/**
|
|
48
62
|
* Text-to-speech over a storyboard. Needs the `edge-tts` binary on PATH, which
|
|
49
63
|
* is why it is a separate call and not a `buildDeck` option.
|
|
@@ -87,12 +101,17 @@ export type { DeckTheme } from "./emit/themes/index.js";
|
|
|
87
101
|
*/
|
|
88
102
|
export { verify } from "./verify/index.js";
|
|
89
103
|
/**
|
|
90
|
-
* The storyboard
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
|
|
95
|
-
|
|
104
|
+
* The storyboard advisories `plan` prints: a headline that recites the visual's
|
|
105
|
+
* own labels as a list, one object drawn twice, a name spoken before it is drawn,
|
|
106
|
+
* a source figure the deck never uses, and a plan that came back under the slide
|
|
107
|
+
* count it was asked for. Exported because they are pure over a storyboard — no
|
|
108
|
+
* browser, no built deck — so a caller can run them on a plan before spending
|
|
109
|
+
* anything on it, which is the only moment they are worth acting on. All but
|
|
110
|
+
* `scanBeatCount` also fold into `verify`; that one needs the preferences the
|
|
111
|
+
* deck was asked for, which a built directory does not carry — and
|
|
112
|
+
* `scanUnusedFigures` folds in only where the source was passed alongside.
|
|
113
|
+
*/
|
|
114
|
+
export { scanBeatCount, scanHeadlines, scanNarrationLead, scanRepeatedObject, scanUnusedFigures, } from "./verify/index.js";
|
|
96
115
|
export { check, parseCheckReport, sampleTimes } from "./verify/check.js";
|
|
97
116
|
export type { CheckOptions } from "./verify/check.js";
|
|
98
117
|
/**
|
|
@@ -15,6 +15,24 @@ export interface Prereq {
|
|
|
15
15
|
* probe is therefore advisory — hence the honest `neededFor` naming both stages.
|
|
16
16
|
*/
|
|
17
17
|
export declare function prereqs(): Promise<Prereq[]>;
|
|
18
|
+
/**
|
|
19
|
+
* Where pictures would come from, asked the same way and for the same reason.
|
|
20
|
+
*
|
|
21
|
+
* Not a `Prereq`, because nothing has to be installed: the last rung is an SVG
|
|
22
|
+
* the tool draws itself, so a request for illustrations never fails for lack of
|
|
23
|
+
* a backend. What CAN be wrong is a backend the environment names and cannot
|
|
24
|
+
* use — DECKSMITH_IMAGES set, the key not — and that is a misconfiguration to
|
|
25
|
+
* report before the plan is paid for, not a stage to skip. `why` is the
|
|
26
|
+
* library's own sentence, which names the variable and never carries the key.
|
|
27
|
+
*
|
|
28
|
+
* `env` is injected in tests, like `probe`: which backend this machine happens
|
|
29
|
+
* to name is not what a test is about.
|
|
30
|
+
*/
|
|
31
|
+
export declare function imageBackend(env?: NodeJS.ProcessEnv): {
|
|
32
|
+
backend: string | null;
|
|
33
|
+
ok: boolean;
|
|
34
|
+
why?: string;
|
|
35
|
+
};
|
|
18
36
|
/** The ones a job with these options actually needs, and which are missing. */
|
|
19
37
|
export declare function missingFor(found: readonly Prereq[], opts: {
|
|
20
38
|
narrate: boolean;
|
|
@@ -39,6 +39,7 @@ export declare const settingsSchema: z.ZodObject<{
|
|
|
39
39
|
low: "low";
|
|
40
40
|
}>>;
|
|
41
41
|
video: z.ZodOptional<z.ZodBoolean>;
|
|
42
|
+
images: z.ZodOptional<z.ZodBoolean>;
|
|
42
43
|
}, z.core.$strip>;
|
|
43
44
|
export type Settings = z.infer<typeof settingsSchema>;
|
|
44
45
|
export declare const capabilitiesSchema: z.ZodObject<{}, z.core.$strip>;
|
|
@@ -75,6 +76,7 @@ export declare const estimateSchema: z.ZodObject<{
|
|
|
75
76
|
low: "low";
|
|
76
77
|
}>>;
|
|
77
78
|
video: z.ZodOptional<z.ZodBoolean>;
|
|
79
|
+
images: z.ZodOptional<z.ZodBoolean>;
|
|
78
80
|
}, z.core.$strip>;
|
|
79
81
|
}, z.core.$strip>;
|
|
80
82
|
export declare const createSchema: z.ZodObject<{
|
|
@@ -112,6 +114,7 @@ export declare const createSchema: z.ZodObject<{
|
|
|
112
114
|
low: "low";
|
|
113
115
|
}>>;
|
|
114
116
|
video: z.ZodOptional<z.ZodBoolean>;
|
|
117
|
+
images: z.ZodOptional<z.ZodBoolean>;
|
|
115
118
|
}, z.core.$strip>>;
|
|
116
119
|
wait_seconds: z.ZodOptional<z.ZodNumber>;
|
|
117
120
|
}, z.core.$strip>;
|
|
@@ -132,6 +135,13 @@ export interface McpOptions {
|
|
|
132
135
|
* probe, so the server itself is unchanged.
|
|
133
136
|
*/
|
|
134
137
|
probe?: () => Promise<Prereq[]>;
|
|
138
|
+
/**
|
|
139
|
+
* The environment the image backend is resolved from, for `capabilities` and
|
|
140
|
+
* the refusal in `create`. Injected for the reason `probe` is. The pipeline
|
|
141
|
+
* itself still reads `process.env` when its `illustrate` stage runs — in a
|
|
142
|
+
* server the two are the same object, and a test never lets a job get there.
|
|
143
|
+
*/
|
|
144
|
+
env?: NodeJS.ProcessEnv;
|
|
135
145
|
}
|
|
136
146
|
/**
|
|
137
147
|
* The four tools, over one queue.
|
|
@@ -143,6 +153,11 @@ export interface McpOptions {
|
|
|
143
153
|
export declare function deckTools(opts: McpOptions): {
|
|
144
154
|
/** Formats, themes, every setting's range, and what is actually installed. */
|
|
145
155
|
capabilities(): Promise<{
|
|
156
|
+
images: {
|
|
157
|
+
backend: string | null;
|
|
158
|
+
ok: boolean;
|
|
159
|
+
why?: string;
|
|
160
|
+
};
|
|
146
161
|
prerequisites: Prereq[];
|
|
147
162
|
root: string;
|
|
148
163
|
work: string;
|
|
@@ -155,6 +170,14 @@ export declare function deckTools(opts: McpOptions): {
|
|
|
155
170
|
* the author what their settings cost ("60s over 12 slides leaves 85
|
|
156
171
|
* characters a slide, which is one sentence"), and an MCP that swallowed
|
|
157
172
|
* them would ship the exact failure this project keeps having.
|
|
173
|
+
*
|
|
174
|
+
* EVERY NUMBER HERE IS AT THE REQUESTED SLIDE COUNT, which is the only one a
|
|
175
|
+
* pre-flight has — there is no plan yet, so there are no beats to strike the
|
|
176
|
+
* budget at. `create` below builds the deck at the count the planner returns
|
|
177
|
+
* (see `durationPlan`'s header), so a plan that comes back short reports
|
|
178
|
+
* different numbers than this did, and `scanBeatCount` says by how much. The
|
|
179
|
+
* field names carry it: `slides` is the request, and everything `_per_slide`
|
|
180
|
+
* is per requested slide.
|
|
158
181
|
*/
|
|
159
182
|
estimate(input: z.infer<typeof estimateSchema>): {
|
|
160
183
|
slides: number;
|
|
@@ -13,12 +13,34 @@ export interface CodexOptions {
|
|
|
13
13
|
/** Swappable so a test can drive the real parse path without spawning anything. */
|
|
14
14
|
run?: Runner;
|
|
15
15
|
}
|
|
16
|
-
|
|
17
|
-
export type Runner = (args: {
|
|
16
|
+
export interface RunnerArgs {
|
|
18
17
|
prompt: string;
|
|
19
18
|
schemaPath: string;
|
|
20
19
|
outPath: string;
|
|
21
20
|
model?: string;
|
|
22
21
|
timeoutMs: number;
|
|
23
|
-
|
|
22
|
+
/** Where the agent runs. Absent means wherever this process is, which planning never cares about. */
|
|
23
|
+
cwd?: string;
|
|
24
|
+
/**
|
|
25
|
+
* `read-only` unless said otherwise. `workspace-write` exists for the one
|
|
26
|
+
* caller that needs a file back — `illustrate`'s Codex rung — and needs a
|
|
27
|
+
* `cwd` to fence the writes to.
|
|
28
|
+
*/
|
|
29
|
+
sandbox?: "read-only" | "workspace-write";
|
|
30
|
+
}
|
|
31
|
+
/** What the planner needs from the outside world: a prompt in, a final message out. */
|
|
32
|
+
export type Runner = (args: RunnerArgs) => Promise<void>;
|
|
24
33
|
export declare function codexPlanner(source: Source, opts?: CodexOptions): Promise<Storyboard>;
|
|
34
|
+
/**
|
|
35
|
+
* The `codex` command line for a run, and the environment it needs.
|
|
36
|
+
*
|
|
37
|
+
* Pure, and exported for that reason: the argv is a contract with a binary
|
|
38
|
+
* nothing in the test suite may spawn, so it is pinned by reading this rather
|
|
39
|
+
* than by running it.
|
|
40
|
+
*/
|
|
41
|
+
export declare function codexCommand(args: RunnerArgs): {
|
|
42
|
+
argv: string[];
|
|
43
|
+
env?: NodeJS.ProcessEnv;
|
|
44
|
+
};
|
|
45
|
+
/** The production `Runner`: `codex exec` on PATH, stdin in, a file out. */
|
|
46
|
+
export declare function runCodex(args: RunnerArgs): Promise<void>;
|