odori 0.0.1 → 0.0.2
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/LICENSE +22 -0
- package/README.md +33 -1
- package/dist/brand-D71KbhAe.d.ts +253 -0
- package/dist/chunk-2SROZSZM.js +953 -0
- package/dist/chunk-2SROZSZM.js.map +1 -0
- package/dist/chunk-PXG45HYV.js +68 -0
- package/dist/chunk-PXG45HYV.js.map +1 -0
- package/dist/chunk-VQLDJUK4.js +62 -0
- package/dist/chunk-VQLDJUK4.js.map +1 -0
- package/dist/index.d.ts +464 -0
- package/dist/index.js +889 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest-CEuFXG0U.d.ts +398 -0
- package/dist/manifest.d.ts +4 -0
- package/dist/manifest.js +9 -0
- package/dist/manifest.js.map +1 -0
- package/dist/preview.d.ts +92 -0
- package/dist/preview.js +156 -0
- package/dist/preview.js.map +1 -0
- package/dist/schema-DXxRezrk.d.ts +54 -0
- package/package.json +53 -6
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
import { V as VideoEntry, a as VideoLayout, C as CompiledTimeline, A as AudioTrack } from './manifest-CEuFXG0U.js';
|
|
2
|
+
export { b as Audio, c as AudioCue, d as AudioPolicy, e as AudioProps, f as CompiledScene, g as CreateManifestOptions, D as DUCK_GAIN, h as DUCK_RAMP_FRAMES, E as EnvelopePoint, i as ExportJob, F as Fill, j as Freeze, L as Loop, M as ManifestAsset, k as ManifestAudioCue, l as ManifestFont, m as MotionPolicy, O as OdoriRuntime, n as OdoriRuntimeProps, R as RenderManifest, S as SafeArea, o as SafeAreaPolicy, p as Scene, q as SceneTransition, r as Stagger, s as Video, t as VideoFormat, u as VideoLayoutInput, v as createRenderManifest, w as cueId, x as defaultLayout, y as defineVideoLayout, z as duckEnvelope, B as entryDurationInFrames, G as envelopeAtFrame, H as gainAtFrame, I as isAssetReference, J as resolveEntryLayout, K as sampleGainCurve, N as sortCues, P as trackDuration, Q as trackGainAtFrame, T as useSceneTransition } from './manifest-CEuFXG0U.js';
|
|
3
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
4
|
+
import { CSSProperties, RefObject } from 'react';
|
|
5
|
+
import { B as Brand, S as Signal, D as Duration } from './brand-D71KbhAe.js';
|
|
6
|
+
export { a as BrandColors, b as BrandFont, c as BrandInput, d as BrandTypography, C as ChordQuality, e as CueContext, f as CueDefinition, E as Envelope, g as SAMPLE_RATE, h as Step, i as brandCssVariables, j as chord, k as cueSamples, l as cueSignature, m as cueUrl, n as defaultBrand, o as defineBrand, p as defineCue, q as formatTimecode, r as framesFromDuration, s as gain, t as highPass, u as isCueDefinition, v as loop, w as lowPass, x as mix, y as noise, z as normalize, A as note, F as pad, G as seamless, H as seconds, I as secondsFromFrames, J as seeded, K as sequence, L as shape, M as silence, N as sine, O as step, P as sweep, Q as triangle } from './brand-D71KbhAe.js';
|
|
7
|
+
import { P as ParsableSchema } from './schema-DXxRezrk.js';
|
|
8
|
+
export { F as FieldDescriptor, I as InputSchema, d as defineInputSchema, i as isOdoriSchema, p as parseWithSchema } from './schema-DXxRezrk.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Integrated loudness, ITU-R BS.1770-4.
|
|
12
|
+
*
|
|
13
|
+
* The number a mix is judged by is not peak or RMS: it is K-weighted, gated
|
|
14
|
+
* loudness. Studio measures what it is about to hand the encoder so the
|
|
15
|
+
* brand's target is something you can mix toward rather than discover after an
|
|
16
|
+
* export.
|
|
17
|
+
*/
|
|
18
|
+
type Biquad = {
|
|
19
|
+
b0: number;
|
|
20
|
+
b1: number;
|
|
21
|
+
b2: number;
|
|
22
|
+
a1: number;
|
|
23
|
+
a2: number;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Channel weights for stereo. Surround weights the surround channels higher;
|
|
27
|
+
* a video mix is stereo, so both channels count equally.
|
|
28
|
+
*/
|
|
29
|
+
declare const integratedLufs: (channels: Float32Array[], sampleRate: number) => number | null;
|
|
30
|
+
|
|
31
|
+
type PlayerProps = {
|
|
32
|
+
entry: VideoEntry;
|
|
33
|
+
input?: Record<string, unknown>;
|
|
34
|
+
prepared?: unknown;
|
|
35
|
+
assets?: Array<{
|
|
36
|
+
reference: string;
|
|
37
|
+
url: string;
|
|
38
|
+
}>;
|
|
39
|
+
layout?: VideoLayout;
|
|
40
|
+
initialFrame?: number;
|
|
41
|
+
autoPlay?: boolean;
|
|
42
|
+
loop?: boolean;
|
|
43
|
+
controls?: boolean;
|
|
44
|
+
style?: CSSProperties;
|
|
45
|
+
muted?: boolean;
|
|
46
|
+
onFrame?: (frame: number) => void;
|
|
47
|
+
onTimeline?: (timeline: CompiledTimeline) => void;
|
|
48
|
+
onAudio?: (track: AudioTrack) => void;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* The seekable frame clock. Playback advances a fractional frame counter from
|
|
52
|
+
* wall-clock deltas, but React only ever sees an integer frame, so a paused
|
|
53
|
+
* player and a render worker produce identical output.
|
|
54
|
+
*/
|
|
55
|
+
declare const Player: ({ entry, input, prepared, assets, layout, initialFrame, autoPlay, loop, controls, muted, style, onFrame, onTimeline, onAudio, }: PlayerProps) => react_jsx_runtime.JSX.Element;
|
|
56
|
+
|
|
57
|
+
type AudioPlaybackOptions = {
|
|
58
|
+
track: AudioTrack | null;
|
|
59
|
+
frame: number;
|
|
60
|
+
fps: number;
|
|
61
|
+
playing: boolean;
|
|
62
|
+
muted?: boolean;
|
|
63
|
+
masterGain?: number;
|
|
64
|
+
/** Cue id to hear alone, or null for the whole mix. */
|
|
65
|
+
soloCue?: string | null;
|
|
66
|
+
/** True while the playhead is being dragged, which auditions under the cursor. */
|
|
67
|
+
scrubbing?: boolean;
|
|
68
|
+
/** Playback rate, so cues stay with the frame clock when it is sped up. */
|
|
69
|
+
rate?: number;
|
|
70
|
+
/**
|
|
71
|
+
* Called when the browser refuses to start a cue without a user gesture, and
|
|
72
|
+
* again when it relents. Autoplay policy is the difference between a silent
|
|
73
|
+
* preview and a broken one, so it is reported rather than swallowed.
|
|
74
|
+
*/
|
|
75
|
+
onBlocked?: (blocked: boolean) => void;
|
|
76
|
+
/**
|
|
77
|
+
* Called with the cues whose files failed to load or decode. A cue pointing
|
|
78
|
+
* at a missing or wrong file is silence with no other symptom, so it is
|
|
79
|
+
* reported rather than swallowed.
|
|
80
|
+
*/
|
|
81
|
+
onFailed?: (sources: string[]) => void;
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Drives one HTMLAudioElement per cue from the frame clock.
|
|
85
|
+
*
|
|
86
|
+
* Audio is the one thing that cannot be derived from a frame index, so preview
|
|
87
|
+
* playback resyncs whenever the element drifts more than a frame from where the
|
|
88
|
+
* timeline says it should be. The exported mix is built separately by the
|
|
89
|
+
* encoder from the same cues, which keeps the file frame accurate.
|
|
90
|
+
*/
|
|
91
|
+
declare const useAudioPlayback: ({ track, frame, fps, playing, muted, masterGain, soloCue, scrubbing, rate, onBlocked, onFailed, }: AudioPlaybackOptions) => void;
|
|
92
|
+
|
|
93
|
+
type PlaybackOptions = {
|
|
94
|
+
fps: number;
|
|
95
|
+
durationInFrames: number;
|
|
96
|
+
initialFrame?: number;
|
|
97
|
+
autoPlay?: boolean;
|
|
98
|
+
loop?: boolean;
|
|
99
|
+
/** Wall-clock multiplier. The frame clock keeps its rate; only time moves. */
|
|
100
|
+
rate?: number;
|
|
101
|
+
onFrame?: (frame: number) => void;
|
|
102
|
+
};
|
|
103
|
+
type Playback = {
|
|
104
|
+
frame: number;
|
|
105
|
+
playing: boolean;
|
|
106
|
+
rate: number;
|
|
107
|
+
play(): void;
|
|
108
|
+
pause(): void;
|
|
109
|
+
toggle(): void;
|
|
110
|
+
seek(frame: number): void;
|
|
111
|
+
step(delta: number): void;
|
|
112
|
+
restart(): void;
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* The seekable frame clock. Playback advances a fractional counter from
|
|
116
|
+
* wall-clock deltas, but React only ever sees an integer frame, so a paused
|
|
117
|
+
* player, a still, and the export worker agree by construction.
|
|
118
|
+
*/
|
|
119
|
+
/**
|
|
120
|
+
* How far the clock moves for a wall-clock delta.
|
|
121
|
+
*
|
|
122
|
+
* Rate scales elapsed time, never the frame index, so frame 90 is the same
|
|
123
|
+
* image at 0.25x, 1x, and 4x, and an export ignores rate entirely.
|
|
124
|
+
*/
|
|
125
|
+
declare const advanceFrames: (fractional: number, deltaMs: number, fps: number, rate?: number) => number;
|
|
126
|
+
declare const usePlayback: ({ fps, durationInFrames, initialFrame, autoPlay, loop, rate, onFrame, }: PlaybackOptions) => Playback;
|
|
127
|
+
|
|
128
|
+
declare global {
|
|
129
|
+
interface Window {
|
|
130
|
+
__ODORI_SET_FRAME__?: (frame: number) => void;
|
|
131
|
+
__ODORI_TIMELINE__?: CompiledTimeline;
|
|
132
|
+
__ODORI_AUDIO__?: AudioTrack;
|
|
133
|
+
__ODORI_READY__?: boolean;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* The surface the render worker drives. It exposes an explicit frame setter
|
|
138
|
+
* and a readiness handshake instead of relying on timing heuristics.
|
|
139
|
+
*/
|
|
140
|
+
declare const RenderSurface: ({ entry, initialFrame, input, prepared, assets, layout, }: {
|
|
141
|
+
entry: VideoEntry;
|
|
142
|
+
initialFrame?: number;
|
|
143
|
+
input?: Record<string, unknown>;
|
|
144
|
+
prepared?: unknown;
|
|
145
|
+
assets?: Array<{
|
|
146
|
+
reference: string;
|
|
147
|
+
url: string;
|
|
148
|
+
}>;
|
|
149
|
+
layout?: VideoLayout;
|
|
150
|
+
}) => react_jsx_runtime.JSX.Element;
|
|
151
|
+
|
|
152
|
+
type FrameState = {
|
|
153
|
+
frame: number;
|
|
154
|
+
fps: number;
|
|
155
|
+
width: number;
|
|
156
|
+
height: number;
|
|
157
|
+
durationInFrames: number;
|
|
158
|
+
};
|
|
159
|
+
type SceneState = {
|
|
160
|
+
id: string;
|
|
161
|
+
name?: string;
|
|
162
|
+
index: number;
|
|
163
|
+
start: number;
|
|
164
|
+
durationInFrames: number;
|
|
165
|
+
};
|
|
166
|
+
type AssetRegistry = {
|
|
167
|
+
resolve(reference: string): string;
|
|
168
|
+
list(): Array<{
|
|
169
|
+
reference: string;
|
|
170
|
+
url: string;
|
|
171
|
+
}>;
|
|
172
|
+
ready: boolean;
|
|
173
|
+
};
|
|
174
|
+
type Readiness = {
|
|
175
|
+
/** Hold the frame open. Call the returned function when the work is done. */
|
|
176
|
+
hold(): () => void;
|
|
177
|
+
};
|
|
178
|
+
/**
|
|
179
|
+
* Block the frame until asynchronous work finishes.
|
|
180
|
+
*
|
|
181
|
+
* The driver waits for the frame attribute, and the runtime only writes it once
|
|
182
|
+
* every held handle is released. Without this a component that decodes an image
|
|
183
|
+
* would be screenshotted before it had anything to show.
|
|
184
|
+
*/
|
|
185
|
+
declare const useReadiness: () => Readiness;
|
|
186
|
+
/** The frame index local to the nearest scene, or the video frame at the top level. */
|
|
187
|
+
declare const useFrame: () => number;
|
|
188
|
+
declare const useVideo: () => Omit<FrameState, "frame">;
|
|
189
|
+
/**
|
|
190
|
+
* A single number components multiply their design values by.
|
|
191
|
+
*
|
|
192
|
+
* Sizing from the shorter side keeps type and spacing readable when the same
|
|
193
|
+
* component is composed into a 16:9, 9:16, or 1:1 frame. A width-derived scale
|
|
194
|
+
* would shrink a vertical cut into unreadable text.
|
|
195
|
+
*/
|
|
196
|
+
declare const useDesignScale: (reference?: number) => number;
|
|
197
|
+
declare const useBrand: () => Brand;
|
|
198
|
+
declare const useLayout: () => VideoLayout;
|
|
199
|
+
declare const useScene: () => SceneState;
|
|
200
|
+
declare const useAssets: () => AssetRegistry;
|
|
201
|
+
declare const createAssetRegistry: (entries: Array<{
|
|
202
|
+
reference: string;
|
|
203
|
+
url: string;
|
|
204
|
+
}>) => AssetRegistry;
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Signal to a 16 bit PCM WAV. Small, lossless, and readable by FFmpeg without
|
|
208
|
+
* a decoder, which is all the mix needs from a generated cue.
|
|
209
|
+
*
|
|
210
|
+
* Encoding lives in the runtime rather than the CLI so preview and export
|
|
211
|
+
* share it: the browser can hand the same bytes to an AudioContext that the
|
|
212
|
+
* render worker writes to disk.
|
|
213
|
+
*/
|
|
214
|
+
declare const encodeWav: (signal: Signal) => Uint8Array;
|
|
215
|
+
|
|
216
|
+
type EasingFunction = (value: number) => number;
|
|
217
|
+
declare const Easing: {
|
|
218
|
+
linear: (value: number) => number;
|
|
219
|
+
bezier: (x1: number, y1: number, x2: number, y2: number) => EasingFunction;
|
|
220
|
+
standard: EasingFunction;
|
|
221
|
+
quad: (value: number) => number;
|
|
222
|
+
cubic: (value: number) => number;
|
|
223
|
+
in: (easing: EasingFunction) => EasingFunction;
|
|
224
|
+
out: (easing: EasingFunction) => EasingFunction;
|
|
225
|
+
inOut: (easing: EasingFunction) => EasingFunction;
|
|
226
|
+
};
|
|
227
|
+
type InterpolateOptions = {
|
|
228
|
+
easing?: EasingFunction;
|
|
229
|
+
extrapolateLeft?: "extend" | "clamp";
|
|
230
|
+
extrapolateRight?: "extend" | "clamp";
|
|
231
|
+
};
|
|
232
|
+
declare function interpolate(value: number, input: number[], output: number[], options?: InterpolateOptions): number;
|
|
233
|
+
declare function interpolate(value: number, input: number[], output: string[], options?: InterpolateOptions): string;
|
|
234
|
+
type SpringOptions = {
|
|
235
|
+
frame: number;
|
|
236
|
+
fps: number;
|
|
237
|
+
from?: number;
|
|
238
|
+
to?: number;
|
|
239
|
+
stiffness?: number;
|
|
240
|
+
damping?: number;
|
|
241
|
+
mass?: number;
|
|
242
|
+
delayInFrames?: number;
|
|
243
|
+
};
|
|
244
|
+
/**
|
|
245
|
+
* A deterministic damped-spring solve. The value depends only on the frame
|
|
246
|
+
* index, so preview and render always agree.
|
|
247
|
+
*/
|
|
248
|
+
declare const spring: ({ frame, fps, from, to, stiffness, damping, mass, delayInFrames, }: SpringOptions) => number;
|
|
249
|
+
|
|
250
|
+
type CursorStop = {
|
|
251
|
+
/** Frame this stop is reached, from the start of the enclosing scene. */
|
|
252
|
+
frame: number;
|
|
253
|
+
/** Canvas coordinates, in the composition's own pixels. */
|
|
254
|
+
x: number;
|
|
255
|
+
y: number;
|
|
256
|
+
/**
|
|
257
|
+
* A click landing on this stop. The press is drawn at the stop's frame and
|
|
258
|
+
* decays over a few frames, so the pointer visibly does the thing the UI is
|
|
259
|
+
* about to react to.
|
|
260
|
+
*/
|
|
261
|
+
click?: boolean;
|
|
262
|
+
/** Hold here until this many frames have passed before moving on. */
|
|
263
|
+
hold?: number;
|
|
264
|
+
};
|
|
265
|
+
type CursorState = {
|
|
266
|
+
x: number;
|
|
267
|
+
y: number;
|
|
268
|
+
/** 0 before the path starts and after it ends, 1 while it is on screen. */
|
|
269
|
+
visible: number;
|
|
270
|
+
/** 1 at the instant of a click, decaying to 0. Drives the press ring. */
|
|
271
|
+
pressed: number;
|
|
272
|
+
/** True while a click is within its press window, for a UI to react to. */
|
|
273
|
+
clicking: boolean;
|
|
274
|
+
};
|
|
275
|
+
/**
|
|
276
|
+
* Where an authored pointer is at this frame.
|
|
277
|
+
*
|
|
278
|
+
* Recording a real cursor would make a video that cannot be re-rendered: the
|
|
279
|
+
* path would live in a file, not in the composition, and a change of copy or
|
|
280
|
+
* canvas would leave it pointing at nothing. An authored path is source — it
|
|
281
|
+
* diffs, it survives a reflow, and it produces the same pixels every run.
|
|
282
|
+
*
|
|
283
|
+
* Movement eases between stops rather than running linearly, because a pointer
|
|
284
|
+
* that travels at constant speed reads as a machine. A `hold` keeps the
|
|
285
|
+
* pointer still without needing a duplicate stop at the same coordinates.
|
|
286
|
+
*/
|
|
287
|
+
declare const cursorAt: (stops: CursorStop[], frame: number) => CursorState;
|
|
288
|
+
/** The last frame an authored path is still on screen, for sizing a scene. */
|
|
289
|
+
declare const cursorDuration: (stops: CursorStop[]) => number;
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Randomness that survives a re-render.
|
|
293
|
+
*
|
|
294
|
+
* A frame is a pure function of its number. `Math.random()` breaks that in the
|
|
295
|
+
* quietest possible way: the preview looks fine, every export looks fine, and
|
|
296
|
+
* the two are different — and so are two chunks of the same export, because a
|
|
297
|
+
* render is parallel and each worker rolls its own numbers. Fifty particles
|
|
298
|
+
* that jump between chunk boundaries is the usual symptom, found late.
|
|
299
|
+
*
|
|
300
|
+
* So a composition asks for a number by name instead. The same seed always
|
|
301
|
+
* gives the same value, on every machine and in every worker, and a seed that
|
|
302
|
+
* includes the frame gives motion that is random-looking and reproducible.
|
|
303
|
+
*/
|
|
304
|
+
/**
|
|
305
|
+
* A number in `[0, 1)` for a seed. The same seed always returns the same
|
|
306
|
+
* number, which is the whole point.
|
|
307
|
+
*
|
|
308
|
+
* ```tsx
|
|
309
|
+
* const drift = random(`particle-${index}`) * 40;
|
|
310
|
+
* const jitter = random([frame, index]) - 0.5;
|
|
311
|
+
* ```
|
|
312
|
+
*
|
|
313
|
+
* An array seed is joined, which is the convenient way to say "this thing, on
|
|
314
|
+
* this frame" without building the string by hand.
|
|
315
|
+
*/
|
|
316
|
+
declare const random: (seed: number | string | Array<number | string>) => number;
|
|
317
|
+
/** A number in `[min, max)`, for a seed. */
|
|
318
|
+
declare const randomBetween: (seed: number | string | Array<number | string>, min: number, max: number) => number;
|
|
319
|
+
/** One item from a list, for a seed. Empty lists return undefined. */
|
|
320
|
+
declare const randomPick: <T>(seed: number | string | Array<number | string>, items: readonly T[]) => T | undefined;
|
|
321
|
+
/**
|
|
322
|
+
* A shuffled copy, for a seed. Fisher-Yates driven by the same generator, so
|
|
323
|
+
* the order is arbitrary but fixed — a list that reshuffles every frame is an
|
|
324
|
+
* animation nobody asked for.
|
|
325
|
+
*/
|
|
326
|
+
declare const randomOrder: <T>(seed: number | string | Array<number | string>, items: readonly T[]) => T[];
|
|
327
|
+
|
|
328
|
+
type CanvasDraw = (context: CanvasRenderingContext2D, state: {
|
|
329
|
+
frame: number;
|
|
330
|
+
width: number;
|
|
331
|
+
height: number;
|
|
332
|
+
}) => void;
|
|
333
|
+
/**
|
|
334
|
+
* Draw to a canvas from the frame clock.
|
|
335
|
+
*
|
|
336
|
+
* The contract a video runs on is that frame N produces the same pixels every
|
|
337
|
+
* time. A canvas is where that is easiest to lose: the obvious way to animate
|
|
338
|
+
* one is `requestAnimationFrame`, which is wall time, and wall time means the
|
|
339
|
+
* export samples wherever the loop happened to be. Two workers rendering
|
|
340
|
+
* neighbouring chunks then disagree, and the seam shows.
|
|
341
|
+
*
|
|
342
|
+
* So the draw is a pure function of the frame, called synchronously before the
|
|
343
|
+
* browser paints, and the frame is held until it has run. The capture waits on
|
|
344
|
+
* the same readiness handshake an image decode uses, which is what makes the
|
|
345
|
+
* screenshot see finished pixels rather than an empty buffer.
|
|
346
|
+
*/
|
|
347
|
+
declare const useCanvas: (draw: CanvasDraw, dependencies?: readonly unknown[]) => RefObject<HTMLCanvasElement | null>;
|
|
348
|
+
/**
|
|
349
|
+
* Rasterize HTML into a canvas, deterministically.
|
|
350
|
+
*
|
|
351
|
+
* The browser will draw an SVG containing a `foreignObject` onto a canvas, and
|
|
352
|
+
* a `foreignObject` can hold ordinary markup. That is the whole trick, and the
|
|
353
|
+
* reason it needs care: the image decode is asynchronous, so the frame has to
|
|
354
|
+
* be held until it lands, and the markup has to carry its own styles because
|
|
355
|
+
* nothing outside the SVG reaches into it.
|
|
356
|
+
*
|
|
357
|
+
* Fonts are the sharp edge. A face that is not loaded when this runs will fall
|
|
358
|
+
* back, and the fallback is what gets baked into the pixels — which is why the
|
|
359
|
+
* caller waits on `document.fonts.ready` before drawing.
|
|
360
|
+
*/
|
|
361
|
+
declare const drawHtml: (context: CanvasRenderingContext2D, html: string, options: {
|
|
362
|
+
width: number;
|
|
363
|
+
height: number;
|
|
364
|
+
style?: string;
|
|
365
|
+
}) => Promise<void>;
|
|
366
|
+
|
|
367
|
+
type PlaceholderOptions = {
|
|
368
|
+
width?: number;
|
|
369
|
+
height?: number;
|
|
370
|
+
/** Drawn across the middle, so a fixture says what it is standing in for. */
|
|
371
|
+
label?: string;
|
|
372
|
+
/** Two colours the gradient runs between. */
|
|
373
|
+
from?: string;
|
|
374
|
+
to?: string;
|
|
375
|
+
/** Seed for the scatter, so two placeholders differ without differing runs. */
|
|
376
|
+
seed?: string;
|
|
377
|
+
};
|
|
378
|
+
/**
|
|
379
|
+
* A picture that ships as code.
|
|
380
|
+
*
|
|
381
|
+
* A component that shows media needs media to show, and a fixture that ships a
|
|
382
|
+
* JPEG cannot be reviewed in a diff, cannot be recoloured by a brand, and adds
|
|
383
|
+
* a binary to a repository forever. Generating an SVG instead keeps the
|
|
384
|
+
* registry's rule intact — install copies source — and makes the picture do
|
|
385
|
+
* something a file cannot: describe itself.
|
|
386
|
+
*
|
|
387
|
+
* It is deliberately obviously a placeholder. A fixture that looks like real
|
|
388
|
+
* photography invites someone to ship it.
|
|
389
|
+
*/
|
|
390
|
+
declare const placeholderSvg: ({ width, height, label, from, to, seed, }?: PlaceholderOptions) => string;
|
|
391
|
+
/**
|
|
392
|
+
* The same picture as a data URL, which is what an `<img>` or a canvas draw
|
|
393
|
+
* wants. Inline rather than fetched: a fixture that needs the network is a
|
|
394
|
+
* fixture that fails on a plane, in CI, and in a sandboxed render.
|
|
395
|
+
*/
|
|
396
|
+
declare const placeholderImage: (options?: PlaceholderOptions) => string;
|
|
397
|
+
/**
|
|
398
|
+
* A frame of a placeholder "clip": the same picture with a moving marker and a
|
|
399
|
+
* timecode, so a component that plays media has something to play that visibly
|
|
400
|
+
* advances and is still a pure function of the frame.
|
|
401
|
+
*/
|
|
402
|
+
declare const placeholderFrame: (frame: number, options?: PlaceholderOptions & {
|
|
403
|
+
fps?: number;
|
|
404
|
+
}) => string;
|
|
405
|
+
|
|
406
|
+
type VideoMetadata<Input = Record<string, unknown>> = {
|
|
407
|
+
readonly kind: "odori-video-metadata";
|
|
408
|
+
id: string;
|
|
409
|
+
title: string;
|
|
410
|
+
description?: string;
|
|
411
|
+
duration?: Duration;
|
|
412
|
+
layout?: VideoLayout;
|
|
413
|
+
schema?: ParsableSchema<Input>;
|
|
414
|
+
defaultProps?: Partial<Input>;
|
|
415
|
+
tags?: string[];
|
|
416
|
+
thumbnailFrame?: number;
|
|
417
|
+
};
|
|
418
|
+
type VideoMetadataInput<Input = Record<string, unknown>> = Omit<VideoMetadata<Input>, "kind" | "id"> & {
|
|
419
|
+
/**
|
|
420
|
+
* Defaults to the entry's path under `videos/`, so the directory names a
|
|
421
|
+
* video the way a route names a page. Set it to keep an id stable across a
|
|
422
|
+
* directory move.
|
|
423
|
+
*/
|
|
424
|
+
id?: string;
|
|
425
|
+
};
|
|
426
|
+
declare const isValidVideoId: (id: string) => boolean;
|
|
427
|
+
/**
|
|
428
|
+
* An id left unset is resolved from the filesystem by discovery. The empty
|
|
429
|
+
* string is the unresolved state: no entry reaches a manifest, a render, or
|
|
430
|
+
* Studio without an id stamped in.
|
|
431
|
+
*/
|
|
432
|
+
declare const resolveVideoId: (id: string | undefined, pathId: string) => string;
|
|
433
|
+
declare const defineVideoMetadata: <Input = Record<string, unknown>>(metadata: VideoMetadataInput<Input>) => VideoMetadata<Input>;
|
|
434
|
+
type PrepareContext<Input> = {
|
|
435
|
+
input: Input;
|
|
436
|
+
assets: {
|
|
437
|
+
resolve(reference: string): Promise<string>;
|
|
438
|
+
};
|
|
439
|
+
cache: {
|
|
440
|
+
getOrSet<Value>(key: string, factory: () => Promise<Value>): Promise<Value>;
|
|
441
|
+
};
|
|
442
|
+
signal?: AbortSignal;
|
|
443
|
+
};
|
|
444
|
+
type PrepareFunction<Input = Record<string, unknown>, Prepared = unknown> = {
|
|
445
|
+
readonly kind: "odori-prepare";
|
|
446
|
+
version: string;
|
|
447
|
+
run(context: PrepareContext<Input>): Promise<Prepared>;
|
|
448
|
+
};
|
|
449
|
+
declare const definePrepare: <Input = Record<string, unknown>, Prepared = unknown>(run: (context: PrepareContext<Input>) => Promise<Prepared>, options?: {
|
|
450
|
+
version?: string;
|
|
451
|
+
}) => PrepareFunction<Input, Prepared>;
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Deterministic, dependency-free content hash.
|
|
455
|
+
*
|
|
456
|
+
* The renderer only needs stable identity for cache keys and frozen manifests,
|
|
457
|
+
* so a 128-bit FNV-1a variant over canonical JSON is sufficient and runs
|
|
458
|
+
* identically in Node and the browser.
|
|
459
|
+
*/
|
|
460
|
+
declare const canonicalJson: (value: unknown) => string;
|
|
461
|
+
declare const hashString: (input: string) => string;
|
|
462
|
+
declare const hashValue: (value: unknown) => string;
|
|
463
|
+
|
|
464
|
+
export { type AssetRegistry, type AudioPlaybackOptions, AudioTrack, type Biquad, Brand, type CanvasDraw, CompiledTimeline, type CursorState, type CursorStop, Duration, Easing, type EasingFunction, type FrameState, type InterpolateOptions, ParsableSchema, type PlaceholderOptions, type Playback, type PlaybackOptions, Player, type PlayerProps, type PrepareContext, type PrepareFunction, type Readiness, RenderSurface, type SceneState, Signal, type SpringOptions, VideoEntry, VideoLayout, type VideoMetadata, type VideoMetadataInput, advanceFrames, canonicalJson, createAssetRegistry, cursorAt, cursorDuration, definePrepare, defineVideoMetadata, drawHtml, encodeWav, hashString, hashValue, integratedLufs, interpolate, isValidVideoId, placeholderFrame, placeholderImage, placeholderSvg, random, randomBetween, randomOrder, randomPick, resolveVideoId, spring, useAssets, useAudioPlayback, useBrand, useCanvas, useDesignScale, useFrame, useLayout, usePlayback, useReadiness, useScene, useVideo };
|