@jokerized/decksmith 0.2.0 → 0.3.1
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 +2 -1
- package/dist/cli.js +679 -132
- package/dist/ds-morph.js +1 -0
- package/dist/index.js +661 -130
- package/dist/mcp.js +374 -39
- 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/stack.d.ts +9 -0
- package/dist/types/emit/depth.d.ts +95 -0
- package/dist/types/emit/kit.d.ts +7 -0
- package/dist/types/emit/morph-runtime.d.ts +178 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/mcp/tools.d.ts +12 -0
- package/dist/types/plan/arc.d.ts +82 -0
- package/dist/types/plan/codex.d.ts +6 -0
- package/dist/types/prefs.d.ts +1 -0
- package/dist/types/render/ffmpeg.d.ts +0 -14
- package/dist/types/types.d.ts +412 -0
- package/dist/types/verify/apparent.d.ts +123 -0
- package/dist/types/verify/index.d.ts +64 -0
- package/dist/types/verify/typefloor.d.ts +3 -1
- package/package.json +1 -1
|
@@ -1,2 +1,64 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* An equation, walked one symbol at a time.
|
|
3
|
+
*
|
|
4
|
+
* This is the most valuable thing the vocabulary does: the reader is never shown
|
|
5
|
+
* a wall of TeX and left to find the symbol being discussed. Each term is wrapped
|
|
6
|
+
* in `\htmlClass{term t-<tone>}{...}` so KaTeX emits a real element for it, which
|
|
7
|
+
* GSAP then tints and swells in step with its legend row.
|
|
8
|
+
*/
|
|
9
|
+
import type { Term } from "../../types.js";
|
|
10
|
+
import type { Emitter, Theme } from "../kit.js";
|
|
11
|
+
export declare const OPTS = "{ displayMode: true, trust: function (c) { return c.command === \"\\\\htmlClass\"; }, strict: false, output: \"html\" }";
|
|
12
|
+
export declare const INLINE_OPTS = "{ displayMode: false, trust: function (c) { return c.command === \"\\\\htmlClass\"; }, strict: false, output: \"html\" }";
|
|
13
|
+
/** Where `term` sits in `tex`, comparing normal forms. Null when it is absent. */
|
|
14
|
+
export declare function locate(tex: string, term: string): {
|
|
15
|
+
start: number;
|
|
16
|
+
end: number;
|
|
17
|
+
} | null;
|
|
18
|
+
/**
|
|
19
|
+
* Wrap each term where it first occurs, and report which ones were wrapped.
|
|
20
|
+
*
|
|
21
|
+
* Segments are tracked as raw/wrapped so a later term cannot match inside an
|
|
22
|
+
* earlier term's `\htmlClass{...}` and produce nested markup that highlights the
|
|
23
|
+
* wrong span.
|
|
24
|
+
*
|
|
25
|
+
* A term that cannot be found is DROPPED rather than thrown on, and the caller
|
|
26
|
+
* drops its legend row with it — the two go together, which is what keeps this
|
|
27
|
+
* honest. Silently highlighting nothing is the failure this archetype exists to
|
|
28
|
+
* avoid, and a legend line pointing at an unhighlighted symbol is that failure;
|
|
29
|
+
* a shorter legend is not. Losing an entire deck to one mis-spelled term is a
|
|
30
|
+
* worse answer than either. If NOTHING matches, the beat has no work to do and
|
|
31
|
+
* that is still an error.
|
|
32
|
+
*/
|
|
33
|
+
export declare function wrapTerms(tex: string, terms: Term[], beatId: string,
|
|
34
|
+
/** The class the wrapper carries; the morph adds its key to the walk's tint. */
|
|
35
|
+
cls?: (t: Term) => string): {
|
|
36
|
+
tex: string;
|
|
37
|
+
used: Term[];
|
|
38
|
+
missing: Term[];
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Display equations want to live between 68px and 108px.
|
|
42
|
+
*
|
|
43
|
+
* The equation is the whole argument of this archetype and it was the smallest
|
|
44
|
+
* thing on the slide — 72px of TeX centred in a 1700px box, measured at 46% fill
|
|
45
|
+
* with a 1920x316 band under it. It is sized off the source length rather than
|
|
46
|
+
* off `textWidth` because TeX is not the string that gets set: `\mathcal{W}(F)`
|
|
47
|
+
* is fourteen characters and three glyphs.
|
|
48
|
+
*
|
|
49
|
+
* This is the *wanted* size, not the final one. The old comment here claimed
|
|
50
|
+
* "KaTeX's own `\displaystyle` box will shrink to the container if the estimate
|
|
51
|
+
* runs wide". It does not — it overflows, silently, because the composition is
|
|
52
|
+
* the size it says it is and no gate reads past the canvas edge. At 9:16 that
|
|
53
|
+
* truncated `X = \mathcal{W}(F)` to "X =" and then left the legend explaining a
|
|
54
|
+
* symbol the viewer could not see, which is worse than a clip: the slide
|
|
55
|
+
* asserted something false. `statements` and the `size` cap below are what make
|
|
56
|
+
* the claim true.
|
|
57
|
+
*/
|
|
58
|
+
export declare function equationSize(tex: string): number;
|
|
59
|
+
/** Estimated rendered width of a display, in ems. */
|
|
60
|
+
export declare function texUnits(tex: string): number;
|
|
61
|
+
/** One row per term: a chip KaTeX fills in `setup`, and the label. Shared with the morph. */
|
|
62
|
+
export declare function legendRows(sid: string, terms: Term[], theme: Theme): string;
|
|
63
|
+
export declare function legendCss(theme: Theme): string;
|
|
2
64
|
export declare const equationWalk: Emitter<"equation-walk">;
|
|
@@ -12,7 +12,7 @@ export declare const emitters: {
|
|
|
12
12
|
};
|
|
13
13
|
/**
|
|
14
14
|
* Dispatch a beat to its emitter. The cast is the one place the pairing is taken
|
|
15
|
-
* on trust: `emitters[beat.archetype]` is a union of
|
|
15
|
+
* on trust: `emitters[beat.archetype]` is a union of thirteen emitters and TypeScript
|
|
16
16
|
* will not narrow the key and the beat together. The table above already proves
|
|
17
17
|
* every archetype has exactly one emitter of the right shape.
|
|
18
18
|
*/
|
|
@@ -25,7 +25,16 @@ import type { BeatOf, Format } from "../../types.js";
|
|
|
25
25
|
import type { Emitter } from "../kit.js";
|
|
26
26
|
import { type Face } from "../svg.js";
|
|
27
27
|
type Params = BeatOf<"stack">["params"];
|
|
28
|
+
/** Where the numerals' right edge lands once the type floor has moved. */
|
|
29
|
+
export declare function numSpine(floor: number): number;
|
|
28
30
|
export interface StackLayout {
|
|
31
|
+
/**
|
|
32
|
+
* The type floor this layout solved against, which is `MIN_FONT` for a flat
|
|
33
|
+
* beat and larger for a tilted one. The emitter reads it back so the notes it
|
|
34
|
+
* draws are set at the same floor the solver reserved room for — they were
|
|
35
|
+
* pinned to `MIN_FONT` directly, which under a tilt drew them below it.
|
|
36
|
+
*/
|
|
37
|
+
floor: number;
|
|
29
38
|
/**
|
|
30
39
|
* False when the rise the canvas allows is shorter than the tallest label
|
|
31
40
|
* block — i.e. adjacent labels would overlap. `stackLayout` re-composes rather
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tilting a plane, and paying for it in type size.
|
|
3
|
+
*
|
|
4
|
+
* Gap 2 wants depth as exposition — a diagram tilting to reveal a layer. The
|
|
5
|
+
* expensive half is not the transform. It is that perspective shrinks the type on
|
|
6
|
+
* the far half of the plane, and invariant 5 is about what the AUDIENCE sees, so
|
|
7
|
+
* a tilt that leaves a 40px label drawing 31px has broken the floor whatever the
|
|
8
|
+
* source says. `src/verify/apparent.ts` is the gate that catches it; this module
|
|
9
|
+
* is how an archetype avoids tripping it, by declaring bigger type up front.
|
|
10
|
+
*
|
|
11
|
+
* WHAT IS AND IS NOT SUPPORTED HERE. One transform on one plane — the whole
|
|
12
|
+
* scene's content tilts as a unit. NOT per-element depth: measured in Chrome,
|
|
13
|
+
* `translateZ` on an SVG `<g>` under a `preserve-3d` ancestor is ignored
|
|
14
|
+
* completely (z=0, z=+200 and z=-200 paint the identical rect, while an HTML
|
|
15
|
+
* sibling in the same test moves and scales correctly). An exploding stack needs
|
|
16
|
+
* sibling HTML planes and is a later slice; a tilting plate is this one.
|
|
17
|
+
*
|
|
18
|
+
* THE MODEL. For a plane rotated by `t` about its horizontal centre line under
|
|
19
|
+
* CSS `perspective: d`, a point `dy` below the plane's centre is drawn at
|
|
20
|
+
*
|
|
21
|
+
* scale(dy) = cos(t) * (d / (d - dy * sin(t)))^2
|
|
22
|
+
*
|
|
23
|
+
* — `cos(t)` for the foreshortening and the square for the perspective divide
|
|
24
|
+
* applying to both the glyph's height and its distance from the eye. Fitted
|
|
25
|
+
* against Chrome at `rotateX(30deg)`, `perspective: 1400px`:
|
|
26
|
+
*
|
|
27
|
+
* dy = -340 (near the top) model 0.6886 Chrome 0.6820 +0.97%
|
|
28
|
+
* dy = +360 (near the bottom) model 1.1404 Chrome 1.1260 +1.28%
|
|
29
|
+
*
|
|
30
|
+
* The model runs about 1% OPTIMISTIC, so `worstScale` rounds its answer down by
|
|
31
|
+
* 2% before anyone divides by it. Erring large costs a couple of type px; erring
|
|
32
|
+
* small ships a slide under the floor, which is the whole failure this exists to
|
|
33
|
+
* prevent.
|
|
34
|
+
*/
|
|
35
|
+
/** How a scene's plane is tilted. Degrees, and CSS `perspective` in px. */
|
|
36
|
+
export interface Pose {
|
|
37
|
+
/** Rotation about the horizontal centre line. Positive tips the top away. */
|
|
38
|
+
rotateX: number;
|
|
39
|
+
/** CSS `perspective`, in reference px. Smaller is a stronger effect. */
|
|
40
|
+
perspective: number;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The default a depth archetype gets when it asks for one.
|
|
44
|
+
*
|
|
45
|
+
* 12 degrees, not the 30 the spike reached for. The table in `worstScale`'s note
|
|
46
|
+
* is why: at 30 degrees a 40px audience floor needs 67px declared, which is more
|
|
47
|
+
* than a headline can spend and still fit its own line. At 12 it needs 48.7px,
|
|
48
|
+
* which archetypes can and do pay. Depth here is a lean, not a dive.
|
|
49
|
+
*/
|
|
50
|
+
export declare const DEFAULT_POSE: Pose;
|
|
51
|
+
/** Apparent scale at `dy` reference px below the plane's centre. */
|
|
52
|
+
export declare function scaleAt(pose: Pose, dy: number): number;
|
|
53
|
+
/**
|
|
54
|
+
* The smallest scale anywhere on a `height`-tall plane, already discounted.
|
|
55
|
+
*
|
|
56
|
+
* The smallest scale is at the TOP edge, furthest from the eye. The BOTTOM edge
|
|
57
|
+
* is the one that can swing through the eye entirely, and when it does the plane
|
|
58
|
+
* is not being tilted any more — it is being turned inside out — so the pose is
|
|
59
|
+
* refused rather than priced. Checking only the top would have returned a
|
|
60
|
+
* perfectly reasonable-looking floor for a pose that cannot be drawn.
|
|
61
|
+
*
|
|
62
|
+
* What it buys, at `perspective: 1400` on a 1080 canvas, AFTER the 2% discount:
|
|
63
|
+
*
|
|
64
|
+
* rotateX(6) 0.901 -> a 40px floor needs 44.4px declared
|
|
65
|
+
* rotateX(12) 0.821 -> 48.7px
|
|
66
|
+
* rotateX(18) 0.744 -> 53.8px
|
|
67
|
+
* rotateX(24) 0.669 -> 59.8px
|
|
68
|
+
* rotateX(30) 0.597 -> 67.0px
|
|
69
|
+
*
|
|
70
|
+
* That table is the tilt budget. Depth in this project is bounded by the type
|
|
71
|
+
* floor rather than by taste, and the bound is tighter than it looks.
|
|
72
|
+
*/
|
|
73
|
+
export declare function worstScale(pose: Pose, height: number): number;
|
|
74
|
+
/**
|
|
75
|
+
* The type floor an archetype must solve against to LAND on `floor` once tilted.
|
|
76
|
+
*
|
|
77
|
+
* Archetypes already solve against a floor — `stack` passes `MIN_FONT` into its
|
|
78
|
+
* own fit — so depth costs them one substitution rather than a second solver.
|
|
79
|
+
*/
|
|
80
|
+
export declare function tiltedFloor(pose: Pose, height: number, floor: number): number;
|
|
81
|
+
/**
|
|
82
|
+
* The CSS a tilted scene needs, scoped to its own id (invariant 3).
|
|
83
|
+
*
|
|
84
|
+
* `perspective` sits on the scene and the rotation on ONE part of it, named by
|
|
85
|
+
* `part`, because tilting a whole scene tilts its chrome too. Built that way
|
|
86
|
+
* first and it was obviously wrong the moment a frame was opened: the headline
|
|
87
|
+
* came out sheared, leaning like a mistake rather than a design, and the stack's
|
|
88
|
+
* probe marker — a straight rule with a dot under it — bent into a hook. The
|
|
89
|
+
* diagram is what gains by leaning; the words are what the audience reads.
|
|
90
|
+
*
|
|
91
|
+
* Nothing here animates: a pose is a static property of the beat, and a tilt that
|
|
92
|
+
* moved would put every label at a different apparent size on every frame while
|
|
93
|
+
* `apparent` only measures at the declared stops.
|
|
94
|
+
*/
|
|
95
|
+
export declare function depthCss(sid: string, pose: Pose, part: string): string;
|
package/dist/types/emit/kit.d.ts
CHANGED
|
@@ -285,6 +285,13 @@ export interface Scene {
|
|
|
285
285
|
* values.
|
|
286
286
|
*/
|
|
287
287
|
measure?: string[];
|
|
288
|
+
/**
|
|
289
|
+
* Vendored runtimes this scene's tweens need registered before its script
|
|
290
|
+
* runs — `"dsMorph"` for the equation morph. The shell loads a runtime only
|
|
291
|
+
* when some scene names it, so a deck that names none is byte-for-byte what
|
|
292
|
+
* it was.
|
|
293
|
+
*/
|
|
294
|
+
plugins?: readonly string[];
|
|
288
295
|
/**
|
|
289
296
|
* Hold points in seconds from the scene's start — where a presenter should
|
|
290
297
|
* pause. The shell converts these to absolute island fragment times.
|
|
@@ -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/index.d.ts
CHANGED
|
@@ -111,7 +111,7 @@ export { verify } from "./verify/index.js";
|
|
|
111
111
|
* deck was asked for, which a built directory does not carry — and
|
|
112
112
|
* `scanUnusedFigures` folds in only where the source was passed alongside.
|
|
113
113
|
*/
|
|
114
|
-
export { scanBeatCount, scanHeadlines, scanNarrationLead, scanRepeatedObject, scanUnusedFigures, } from "./verify/index.js";
|
|
114
|
+
export { scanBeatCount, scanNarrationDrift, scanPaperArc, scanHeadlines, scanNarrationLead, scanRepeatedObject, scanUnusedFigures, } from "./verify/index.js";
|
|
115
115
|
export { check, parseCheckReport, sampleTimes } from "./verify/check.js";
|
|
116
116
|
export type { CheckOptions } from "./verify/check.js";
|
|
117
117
|
/**
|
|
@@ -26,6 +26,10 @@ export declare const settingsSchema: z.ZodObject<{
|
|
|
26
26
|
normal: "normal";
|
|
27
27
|
dense: "dense";
|
|
28
28
|
}>>;
|
|
29
|
+
genre: z.ZodOptional<z.ZodEnum<{
|
|
30
|
+
general: "general";
|
|
31
|
+
paper: "paper";
|
|
32
|
+
}>>;
|
|
29
33
|
duration: z.ZodOptional<z.ZodNumber>;
|
|
30
34
|
slides: z.ZodOptional<z.ZodInt>;
|
|
31
35
|
animation_speed: z.ZodOptional<z.ZodNumber>;
|
|
@@ -63,6 +67,10 @@ export declare const estimateSchema: z.ZodObject<{
|
|
|
63
67
|
normal: "normal";
|
|
64
68
|
dense: "dense";
|
|
65
69
|
}>>;
|
|
70
|
+
genre: z.ZodOptional<z.ZodEnum<{
|
|
71
|
+
general: "general";
|
|
72
|
+
paper: "paper";
|
|
73
|
+
}>>;
|
|
66
74
|
duration: z.ZodOptional<z.ZodNumber>;
|
|
67
75
|
slides: z.ZodOptional<z.ZodInt>;
|
|
68
76
|
animation_speed: z.ZodOptional<z.ZodNumber>;
|
|
@@ -101,6 +109,10 @@ export declare const createSchema: z.ZodObject<{
|
|
|
101
109
|
normal: "normal";
|
|
102
110
|
dense: "dense";
|
|
103
111
|
}>>;
|
|
112
|
+
genre: z.ZodOptional<z.ZodEnum<{
|
|
113
|
+
general: "general";
|
|
114
|
+
paper: "paper";
|
|
115
|
+
}>>;
|
|
104
116
|
duration: z.ZodOptional<z.ZodNumber>;
|
|
105
117
|
slides: z.ZodOptional<z.ZodInt>;
|
|
106
118
|
animation_speed: z.ZodOptional<z.ZodNumber>;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The paper arc: what a research-talk deck must open and close with.
|
|
3
|
+
*
|
|
4
|
+
* A research talk has a shape a general explainer does not. It opens on the
|
|
5
|
+
* problem and on the ground the work stands on, and it closes on what the work
|
|
6
|
+
* does NOT do and then on what to take away. DeckSmith's planner writes the
|
|
7
|
+
* opening well already — 13 of 15 committed full-deck runs open with a `title` —
|
|
8
|
+
* and the ending badly: 10 of 15 close with a `callout`, but ZERO of 15 carry
|
|
9
|
+
* two, because the limitation arrives as a subordinate clause inside the
|
|
10
|
+
* conclusion ("The idea works, but the strongest lightweight models remain
|
|
11
|
+
* ahead") rather than as its own slide. Splitting that clause into a slide is
|
|
12
|
+
* the actual behaviour change this module asks for.
|
|
13
|
+
*
|
|
14
|
+
* WHY THIS FILE EXISTS AT ALL, rather than the rule living where it is enforced:
|
|
15
|
+
* three directories read this definition and none of them can own it — the
|
|
16
|
+
* prompt (src/plan/prompt.ts) asks the model for the arc, the scan
|
|
17
|
+
* (src/verify/index.ts) reports where a plan missed it, and the cut
|
|
18
|
+
* (src/plan/select.ts) refuses to delete a beat that carries a role. Restating
|
|
19
|
+
* the rule three times is how the three fall out of step; deriving it from one
|
|
20
|
+
* table is how they cannot.
|
|
21
|
+
*
|
|
22
|
+
* WHAT THIS MODULE DELIBERATELY DOES NOT DO. It never decides that a document is
|
|
23
|
+
* a research paper. `prefs.genre` is declared by the author and nothing here
|
|
24
|
+
* sniffs it — see the field's own note in src/types.ts for the measurement that
|
|
25
|
+
* settled that. And it never requires a beat the source cannot support: RULE 3
|
|
26
|
+
* forbids inventing a baseline or a result, so a source that says nothing about
|
|
27
|
+
* prior work honestly yields no `background` beat, and this file's job is then
|
|
28
|
+
* to report the gap rather than to have manufactured one.
|
|
29
|
+
*/
|
|
30
|
+
import type { Prefs } from "../prefs.js";
|
|
31
|
+
import type { Beat, BeatRole, Storyboard } from "../types.js";
|
|
32
|
+
/** Every structural job, in the order a deck performs them. */
|
|
33
|
+
export declare const ARC_ROLES: readonly BeatRole[];
|
|
34
|
+
/**
|
|
35
|
+
* Roles required at a given deck length, and why the list shortens.
|
|
36
|
+
*
|
|
37
|
+
* Four reserved slides out of five is not a deck, it is a table of contents. At
|
|
38
|
+
* `--duration 60` a deck is about five beats, so asking for the full arc there
|
|
39
|
+
* is asking for something nobody could deliver, and a gate that fires on the
|
|
40
|
+
* impossible is one people learn to ignore. The thresholds:
|
|
41
|
+
*
|
|
42
|
+
* n >= 8 the full arc. 8 is this project's own definition of a full-deck
|
|
43
|
+
* run — the corpus count of "15 committed full-deck plans" is beats
|
|
44
|
+
* >= 8 — not a number chosen here.
|
|
45
|
+
* 5..7 the ending only. An `intro` is what the planner already writes
|
|
46
|
+
* unprompted 13 times in 15, and `background` is the beat most likely
|
|
47
|
+
* to have no source material behind it, so those two are the ones to
|
|
48
|
+
* give up first when the budget is short.
|
|
49
|
+
* n < 5 nothing. There is no room for a shape.
|
|
50
|
+
*
|
|
51
|
+
* Read off the beat count the plan actually came back with, not off
|
|
52
|
+
* `prefs.slides`: the floor the author asked for and the deck the planner
|
|
53
|
+
* returned are different numbers, and `scanBeatCount` already owns the gap
|
|
54
|
+
* between them.
|
|
55
|
+
*/
|
|
56
|
+
export declare function requiredRoles(beatCount: number): readonly BeatRole[];
|
|
57
|
+
/** Whether the paper arc was asked for at all. Declared, never sniffed. */
|
|
58
|
+
export declare function paperArcRequested(prefs: Pick<Prefs, "genre">): boolean;
|
|
59
|
+
/** The beats carrying a structural role, by role. Later duplicates are reported, not kept. */
|
|
60
|
+
export declare function arcBeats(storyboard: Storyboard): Map<BeatRole, Beat[]>;
|
|
61
|
+
/**
|
|
62
|
+
* Ids of every beat carrying a role — what the cut refuses to release.
|
|
63
|
+
*
|
|
64
|
+
* Takes a beat list rather than a Storyboard because the cut works over the
|
|
65
|
+
* surviving beats, not over the plan.
|
|
66
|
+
*/
|
|
67
|
+
export declare function arcIds(beats: readonly Beat[]): Set<string>;
|
|
68
|
+
/**
|
|
69
|
+
* Where a plan departs from the arc it was asked for, as sentences.
|
|
70
|
+
*
|
|
71
|
+
* ORDER IS CHECKED, NOT JUST PRESENCE, and only where order is the point. The
|
|
72
|
+
* user's requirement is specifically that the deck END on the conclusion with
|
|
73
|
+
* limitations immediately before it — an ending is a position, not a topic — so
|
|
74
|
+
* those two are checked against the last two slots. The opening pair is checked
|
|
75
|
+
* for presence and for being early, because "the first couple of slides" is a
|
|
76
|
+
* region rather than an index, and a deck that opens title, problem, background
|
|
77
|
+
* is not wrong.
|
|
78
|
+
*
|
|
79
|
+
* Returns an empty array when the arc was not requested, when the deck is too
|
|
80
|
+
* short to carry it, or when the plan satisfied it.
|
|
81
|
+
*/
|
|
82
|
+
export declare function arcProblems(storyboard: Storyboard, prefs: Pick<Prefs, "genre" | "slides">): string[];
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import type { Prefs } from "../prefs.js";
|
|
2
2
|
import { type Source, type Storyboard } from "../types.js";
|
|
3
|
+
/** The schema for one run. `role` is present only when the paper arc was asked for. */
|
|
4
|
+
export declare function schemaFor(prefs: Pick<Prefs, "genre">): unknown;
|
|
5
|
+
/**
|
|
6
|
+
* The default-preferences schema, unchanged and still exported: `general` hides
|
|
7
|
+
* `role`, so these bytes are what they have always been.
|
|
8
|
+
*/
|
|
3
9
|
export declare const SCHEMA: unknown;
|
|
4
10
|
export interface CodexOptions {
|
|
5
11
|
/** Left unset by default: use whatever model the user's Codex is configured for. */
|
package/dist/types/prefs.d.ts
CHANGED
|
@@ -60,20 +60,6 @@ export interface AudioInput {
|
|
|
60
60
|
file: string;
|
|
61
61
|
delayMs: number;
|
|
62
62
|
}
|
|
63
|
-
/**
|
|
64
|
-
* Delay every segment onto one track and sum them.
|
|
65
|
-
*
|
|
66
|
-
* `amix` with `normalize=0` sums rather than averaging: the default divides by
|
|
67
|
-
* the input count, which on a 37-segment deck would render the narration 31 dB
|
|
68
|
-
* down and sound exactly like a bug in the TTS. The segments never overlap — the
|
|
69
|
-
* timing model gives each one the video's undivided attention — so summing is
|
|
70
|
-
* safe. `dropout_transition=0` stops amix ramping the gain as inputs end.
|
|
71
|
-
*
|
|
72
|
-
* Every input is resampled and laid out identically first, because amix refuses
|
|
73
|
-
* a mismatch and edge-tts emits 24 kHz mono while the video wants 48 kHz.
|
|
74
|
-
* `apad` runs the track out to the video's length so the mux does not have to
|
|
75
|
-
* choose between a short audio stream and `-shortest` truncating the picture.
|
|
76
|
-
*/
|
|
77
63
|
export declare function audioGraph(inputs: readonly AudioInput[], seconds: number,
|
|
78
64
|
/**
|
|
79
65
|
* ffmpeg input index of the first mp3. 1 when the video is the only other
|