@scenerok/sdk 0.9.0 → 0.11.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 CHANGED
@@ -55,7 +55,7 @@ JavaScript has no `t[0, end]` dual key (comma operator). Use:
55
55
  | `t.range(start, end, value)` | Explicit call |
56
56
  | `t['-'] = value` / `t.push(value)` | Auto-append at playhead |
57
57
 
58
- `start` / `end` accept numbers (seconds), `s(3)` / `ms(300)`, or strings (`'3s'`, `'1:30'`). Full JS arithmetic works: `clip.duration + 0.5`.
58
+ `start` / `end` accept numbers (seconds), `s(3)` / `ms(300)`, `t.frames(48)`, or strings (`'3s'`, `'1:30'`, `'48f'`, `'01:00:00:00'`). Full JS arithmetic works: `clip.duration + 0.5`. Windows snap to the timeline's frame grid, as VidScript blocks do; NTSC rates (`fps: 23.976`) are exact.
59
59
 
60
60
  No `video` / `text` keywords after `=` — the RHS type is inferred from factories.
61
61
 
@@ -65,6 +65,7 @@ No `video` / `text` keywords after `=` — the RHS type is inferred from factori
65
65
  |-----|----------|
66
66
  | Short promos, human edits, templates | VidScript (`.vid`) |
67
67
  | Loops, branches, computed timing, agents | TypeScript + `@scenerok/sdk` |
68
+ | An edit with one data-driven part | VidScript importing a `.ts` module (below) |
68
69
 
69
70
  ## AI plugins
70
71
 
@@ -116,6 +117,67 @@ t[5.6][10] = video('./b.mp4', { duration: 4.4 })
116
117
 
117
118
  Same 29 shaders as VidScript. `toIR()` binds neighboring clips automatically.
118
119
 
120
+ ## Values, methods, and placement
121
+
122
+ The SDK speaks the current VidScript language and lowers to the same IR:
123
+
124
+ ```ts
125
+ import { timeline, video, image, text, audio, solid, gap, marker, fadeIn, volumeRamp } from '@scenerok/sdk'
126
+
127
+ const t = timeline({ width: 1920, height: 1080, fps: 30 })
128
+ const beach = video('./beach.mp4', { duration: 60 })
129
+ const vo = audio('./vo.mp3', { duration: 9 })
130
+
131
+ t[0][6] = beach.trim(10, 20).speed(1.5).crop(0, 0, 1280, 720) // methods return copies
132
+ t[0][6] = video('./flare.mp4', { blend: 'screen', opacity: 0.6 }) // blend over what is below
133
+ t[1][4] = image('./logo.png', { width: 240, height: 240, x: 60, y: 60, radius: 120 })
134
+ t[6][9] = beach.trim(30, 33).mask(solid('#fff', { width: 800, height: 800, x: 560, y: 140, radius: 400 }))
135
+ t.push(gap(), 1)
136
+ t[4][4] = marker('act 2')
137
+ t[0][9] = vo
138
+ t[0][9] = audio('./bed.mp3', { duration: 60, volume: 0.4, duckUnder: vo, animate: volumeRamp(0, 0.4, 2) })
139
+ ```
140
+
141
+ Values: `video`, `imageSequence`, `image`, `text`, `audio`, `solid`, `gap`, `marker`, `filter`, `shader`. Methods: `trim`, `speed`, `loop`, `resize`, `opacity`, `filter`, `crop`, `mask`, `animate`, `with`. Placement options on every picture: `x`, `y`, `width`, `height`, `area`, `opacity`, `rotation`, `radius`, `corners`, `blend`, `motionBlur`, `enabled`, `animate`.
142
+
143
+ A timeline is a value too. Placed bare its items join the parent; `place(opts)`, `opacity`, `resize`, `filter`, `crop`, or `mask` draw it as one group:
144
+
145
+ ```ts
146
+ const card = timeline({ name: 'card' })
147
+ card[0][3] = solid('#101820', { height: 200, y: 880 })
148
+ card[0][3] = text('Konkan Day 1', { y: '88%' }).animate(fadeIn(0.3))
149
+
150
+ t[2][5] = card // flat
151
+ t[6][9] = card.opacity(0.8).place({ x: 40 }) // one group
152
+ ```
153
+
154
+ ## Use TypeScript from VidScript
155
+
156
+ A `.vid` can import a module and place its exports as VidScript values:
157
+
158
+ ```ts
159
+ // kit.ts
160
+ import { video, text, timeline, solid } from '@scenerok/sdk'
161
+ export const hero = video('beach.mp4', { duration: 12 }).trim(2, 8)
162
+ export const title = text('Konkan Day 1', { size: 72, area: 'title' })
163
+ export function lowerThird(name: string, opts: { color?: string } = {}) {
164
+ const t = timeline()
165
+ t[0][3] = solid(opts.color ?? '#101820', { height: 160, y: 880 })
166
+ t[0][3] = text(name, { y: '88%' })
167
+ return t
168
+ }
169
+ ```
170
+
171
+ ```
172
+ import kit from "./kit.ts"
173
+ [0s .. 6s] = kit.hero
174
+ [1s .. 4s] = kit.title
175
+ [3s .. 6s] = kit.lowerThird("Nidheesh", color: "#ff5500")
176
+ output to "reel.mp4", resolution: "1920x1080"
177
+ ```
178
+
179
+ A `timeline()` without a size takes the importing script's canvas. `describeValue(value)` is the bridge the host uses; you do not call it yourself.
180
+
119
181
  ## Validate / render
120
182
 
121
183
  ```bash
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Plain-data descriptors of SDK values, for hosts that place them as VidScript
3
+ * values (`import kit from "./kit.ts"` then `[0s .. 3s] = kit.title`).
4
+ *
5
+ * A descriptor is JSON — it crosses a VM boundary — and its params use
6
+ * VidScript's property names (`stroke_width`, `fade_in`, …), so the compiler
7
+ * places it exactly like the VidScript spelling of the same value.
8
+ */
9
+ import type { IRCrop, IREffect, IRMaskMode, IRTimeline } from './ir.js';
10
+ export type DescriptorParams = Record<string, unknown>;
11
+ export interface MaskDescriptor {
12
+ matte: SdkValueDescriptor;
13
+ mode: IRMaskMode;
14
+ }
15
+ export type SdkValueDescriptor = {
16
+ kind: 'video' | 'image';
17
+ source: string;
18
+ /** Media length in seconds, when known. */
19
+ duration?: number;
20
+ /** Frame rate to interpret the footage at. */
21
+ rate?: number;
22
+ sequence?: {
23
+ first: number;
24
+ count: number;
25
+ rate?: number;
26
+ };
27
+ /** The clip's own sound comes along when placed. */
28
+ nativeAudio?: boolean;
29
+ edits: {
30
+ trimStart?: number;
31
+ trimEnd?: number;
32
+ speed?: number;
33
+ loop?: number;
34
+ opacity?: number;
35
+ width?: number;
36
+ height?: number;
37
+ effects?: IREffect[];
38
+ crop?: IRCrop;
39
+ mask?: MaskDescriptor;
40
+ };
41
+ props: DescriptorParams;
42
+ } | {
43
+ kind: 'text';
44
+ content: string;
45
+ params: DescriptorParams;
46
+ } | {
47
+ kind: 'audio';
48
+ source: string;
49
+ duration?: number;
50
+ trimEnd?: number;
51
+ params: DescriptorParams;
52
+ } | {
53
+ kind: 'solid';
54
+ color: string;
55
+ props: DescriptorParams;
56
+ } | {
57
+ kind: 'gap';
58
+ } | {
59
+ kind: 'marker';
60
+ name: string;
61
+ metadata?: Record<string, unknown>;
62
+ } | {
63
+ kind: 'timeline';
64
+ name: string;
65
+ /** Lowered on the host canvas, in timeline-local time (0 is the placement start). */
66
+ ir: IRTimeline;
67
+ props: DescriptorParams;
68
+ effects: IREffect[];
69
+ crop?: IRCrop;
70
+ mask?: MaskDescriptor;
71
+ }
72
+ /** Plain data: numbers (Durations as seconds), strings, booleans, arrays, objects. */
73
+ | {
74
+ kind: 'data';
75
+ value: unknown;
76
+ }
77
+ /** A value with no VidScript placement (filters, shaders, transitions as values). */
78
+ | {
79
+ kind: 'unsupported';
80
+ sdkKind: string;
81
+ };
82
+ /**
83
+ * Describe any SDK value — or plain data — as JSON a host can place. Timelines
84
+ * lower on the host canvas (`globalThis.__scenerokCanvas`).
85
+ */
86
+ export declare function describeValue(value: unknown): SdkValueDescriptor;
87
+ //# sourceMappingURL=describe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"describe.d.ts","sourceRoot":"","sources":["../src/describe.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAexE,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEvD,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,kBAAkB,CAAC;IAC1B,IAAI,EAAE,UAAU,CAAC;CAClB;AAED,MAAM,MAAM,kBAAkB,GAC1B;IACA,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3D,oDAAoD;IACpD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,EAAE;QACL,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,QAAQ,EAAE,CAAC;QACrB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,cAAc,CAAC;KACvB,CAAC;IACF,KAAK,EAAE,gBAAgB,CAAC;CACzB,GACC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAA;CAAE,GAC3D;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAA;CAAE,GAChG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,gBAAgB,CAAA;CAAE,GACzD;IAAE,IAAI,EAAE,KAAK,CAAA;CAAE,GACf;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GACpE;IACA,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,qFAAqF;IACrF,EAAE,EAAE,UAAU,CAAC;IACf,KAAK,EAAE,gBAAgB,CAAC;IACxB,OAAO,EAAE,QAAQ,EAAE,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,cAAc,CAAC;CACvB;AACD,sFAAsF;GACpF;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE;AAClC,qFAAqF;GACnF;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AA2K7C;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,kBAAkB,CAyBhE"}
@@ -0,0 +1,210 @@
1
+ /**
2
+ * Plain-data descriptors of SDK values, for hosts that place them as VidScript
3
+ * values (`import kit from "./kit.ts"` then `[0s .. 3s] = kit.title`).
4
+ *
5
+ * A descriptor is JSON — it crosses a VM boundary — and its params use
6
+ * VidScript's property names (`stroke_width`, `fade_in`, …), so the compiler
7
+ * places it exactly like the VidScript spelling of the same value.
8
+ */
9
+ import { Duration, toSeconds } from './duration.js';
10
+ import { bindTransitions, hostCanvas, lowerTimeline, timelineRecordOf } from './ir-builder.js';
11
+ import { isSdkNode } from './types.js';
12
+ function defined(entries) {
13
+ const out = {};
14
+ for (const [key, value] of Object.entries(entries)) {
15
+ if (value !== undefined)
16
+ out[key] = value;
17
+ }
18
+ return out;
19
+ }
20
+ function animateParam(animate) {
21
+ if (!animate)
22
+ return undefined;
23
+ const list = Array.isArray(animate) ? animate : [animate];
24
+ if (list.length === 0)
25
+ return undefined;
26
+ return list.map(({ type, from, to, duration, align, easing, loop, plugin }) => defined({ type, from, to, duration, align, easing, loop, plugin }));
27
+ }
28
+ /** Placement options in VidScript's property names. */
29
+ function placementParams(opts) {
30
+ return defined({
31
+ x: opts.x,
32
+ y: opts.y,
33
+ width: opts.width,
34
+ height: opts.height,
35
+ opacity: opts.opacity,
36
+ rotation: opts.rotation,
37
+ radius: opts.radius,
38
+ corners: opts.corners,
39
+ area: opts.area,
40
+ blend: opts.blend,
41
+ motion_blur: opts.motionBlur,
42
+ enabled: opts.enabled,
43
+ animate: animateParam(opts.animate),
44
+ input_colorspace: opts.inputColorspace,
45
+ output_colorspace: opts.outputColorspace,
46
+ volume: opts.volume,
47
+ });
48
+ }
49
+ function describeMask(mask) {
50
+ if (!mask)
51
+ return undefined;
52
+ return { matte: describeValue(mask.matte), mode: mask.mode };
53
+ }
54
+ function describeMedia(node) {
55
+ const { trimStart, trimEnd, speed, loop, opacity, width, height, effects, crop, mask } = node.edits;
56
+ const video = node.kind === 'video' ? node : null;
57
+ return {
58
+ kind: node.kind,
59
+ source: node.source,
60
+ ...(video?.mediaDuration !== undefined ? { duration: video.mediaDuration } : {}),
61
+ ...(video?.options.rate !== undefined && !video.sequence ? { rate: video.options.rate } : {}),
62
+ ...(video?.sequence ? { sequence: video.sequence } : {}),
63
+ ...(video?.options.nativeAudio ? { nativeAudio: true } : {}),
64
+ edits: defined({
65
+ trimStart, trimEnd, speed, loop, opacity, width, height,
66
+ effects: effects?.length ? effects : undefined,
67
+ crop,
68
+ mask: describeMask(mask),
69
+ }),
70
+ props: placementParams(node.options),
71
+ };
72
+ }
73
+ function describeText(node) {
74
+ const o = node.options;
75
+ return {
76
+ kind: 'text',
77
+ content: node.content,
78
+ params: defined({
79
+ font: o.font,
80
+ size: o.size,
81
+ min_size: o.minSize,
82
+ color: o.color,
83
+ stroke: o.stroke,
84
+ stroke_width: o.strokeWidth,
85
+ position: o.position,
86
+ bold: o.bold,
87
+ italic: o.italic,
88
+ x: o.x,
89
+ y: o.y,
90
+ rotation: o.rotation,
91
+ opacity: o.opacity,
92
+ align: o.align,
93
+ line_height: o.lineHeight,
94
+ letter_spacing: o.letterSpacing,
95
+ shadow_color: o.shadowColor,
96
+ shadow_blur: o.shadowBlur,
97
+ shadow_offset_x: o.shadowOffsetX,
98
+ shadow_offset_y: o.shadowOffsetY,
99
+ fit: o.fit,
100
+ overflow: o.overflow,
101
+ safe_area: o.safeArea,
102
+ max_width: o.maxWidth,
103
+ max_height: o.maxHeight,
104
+ max_lines: o.maxLines,
105
+ anchor: o.anchor,
106
+ area: o.area,
107
+ stagger: o.stagger,
108
+ blend: o.blend,
109
+ animate: animateParam(node.animations),
110
+ }),
111
+ };
112
+ }
113
+ function describeAudio(node) {
114
+ const o = node.options;
115
+ const ebu = o.loudness === 'ebu-r128' || o.loudnessNormalization === 'ebu-r128';
116
+ const duck = typeof o.duckUnder === 'string' ? o.duckUnder : o.duckUnder?.source;
117
+ return {
118
+ kind: 'audio',
119
+ source: node.source,
120
+ ...(node.mediaDuration !== undefined ? { duration: node.mediaDuration } : {}),
121
+ params: defined({
122
+ volume: o.volume,
123
+ fade_in: o.fadeIn !== undefined ? toSeconds(o.fadeIn) : undefined,
124
+ fade_out: o.fadeOut !== undefined ? toSeconds(o.fadeOut) : undefined,
125
+ trim_start: o.trimStart !== undefined && toSeconds(o.trimStart) > 0 ? toSeconds(o.trimStart) : undefined,
126
+ pan: o.pan,
127
+ duck_under: duck,
128
+ loudness: ebu ? 'ebu-r128' : undefined,
129
+ animate: animateParam(o.animate),
130
+ }),
131
+ };
132
+ }
133
+ function describeSolid(node) {
134
+ return { kind: 'solid', color: node.color, props: placementParams(node.options) };
135
+ }
136
+ function lowerForHost(timeline) {
137
+ const state = lowerTimeline(timelineRecordOf(timeline), hostCanvas());
138
+ bindTransitions(state.ir);
139
+ return JSON.parse(JSON.stringify(state.ir));
140
+ }
141
+ function describeTimeline(value) {
142
+ const view = 'placement' in value ? value : null;
143
+ const timeline = view ? view.timeline : value;
144
+ return {
145
+ kind: 'timeline',
146
+ name: timeline.options.name ?? 'timeline',
147
+ ir: lowerForHost(timeline),
148
+ props: view ? placementParams(view.placement) : {},
149
+ effects: view ? view.effects : [],
150
+ ...(view?.cropRect ? { crop: view.cropRect } : {}),
151
+ ...(view?.maskSpec ? { mask: describeMask(view.maskSpec) } : {}),
152
+ };
153
+ }
154
+ function plainData(value, seen) {
155
+ if (value === null || typeof value === 'string' || typeof value === 'boolean')
156
+ return value;
157
+ if (typeof value === 'number')
158
+ return Number.isFinite(value) ? value : null;
159
+ if (Duration.isDuration(value))
160
+ return value.seconds;
161
+ if (typeof value !== 'object')
162
+ return undefined;
163
+ if (seen.has(value))
164
+ throw new TypeError('Cannot describe a value that refers to itself');
165
+ seen.add(value);
166
+ try {
167
+ if (Array.isArray(value))
168
+ return value.map((item) => plainData(item, seen));
169
+ const out = {};
170
+ for (const [key, item] of Object.entries(value)) {
171
+ const data = plainData(item, seen);
172
+ if (data !== undefined)
173
+ out[key] = data;
174
+ }
175
+ return out;
176
+ }
177
+ finally {
178
+ seen.delete(value);
179
+ }
180
+ }
181
+ /**
182
+ * Describe any SDK value — or plain data — as JSON a host can place. Timelines
183
+ * lower on the host canvas (`globalThis.__scenerokCanvas`).
184
+ */
185
+ export function describeValue(value) {
186
+ if (isSdkNode(value)) {
187
+ switch (value.kind) {
188
+ case 'video':
189
+ case 'image':
190
+ return describeMedia(value);
191
+ case 'text':
192
+ return describeText(value);
193
+ case 'audio':
194
+ return describeAudio(value);
195
+ case 'solid':
196
+ return describeSolid(value);
197
+ case 'gap':
198
+ return { kind: 'gap' };
199
+ case 'marker': {
200
+ const marker = value;
201
+ return { kind: 'marker', name: marker.name, ...(marker.metadata ? { metadata: marker.metadata } : {}) };
202
+ }
203
+ case 'timeline':
204
+ return describeTimeline(value);
205
+ default:
206
+ return { kind: 'unsupported', sdkKind: value.kind };
207
+ }
208
+ }
209
+ return { kind: 'data', value: plainData(value, new Set()) ?? null };
210
+ }
@@ -17,13 +17,19 @@ export declare class Duration {
17
17
  export declare function s(seconds: number): Duration;
18
18
  /** Milliseconds helper: `ms(300)` → 0.3s. */
19
19
  export declare function ms(milliseconds: number): Duration;
20
- /** Frame count at a given fps (default 30). */
20
+ /**
21
+ * A frame count at `fps` (exact for NTSC: `frames(24, 23.976)` is `24 * 1001 / 24000`).
22
+ * To count frames of the timeline's own rate, use `t.frames(n)` or the string `'48f'`.
23
+ */
21
24
  export declare function frames(count: number, fps?: number): Duration;
22
25
  /**
23
26
  * Parse a time value to seconds.
24
- * Accepts numbers (already seconds), Duration, or strings like `3s`, `300ms`, `1:30`, `0:01:30`.
27
+ * Accepts numbers (already seconds), Duration, or strings like `3s`, `300ms`,
28
+ * `1:30`, `0:01:30`, frame counts `48f`, and timecodes `01:00:00:00@24`.
29
+ * Frame counts and timecodes without `@rate` count frames of `fps` — a
30
+ * timeline passes its own — and throw without one.
25
31
  */
26
- export declare function toSeconds(value: TimeInput): number;
32
+ export declare function toSeconds(value: TimeInput, fps?: number): number;
27
33
  export declare function isAutoPlayheadKey(key: PropertyKey): boolean;
28
34
  export {};
29
35
  //# sourceMappingURL=duration.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"duration.d.ts","sourceRoot":"","sources":["../src/duration.ts"],"names":[],"mappings":"AAAA,qEAAqE;AAErE,QAAA,MAAM,cAAc,eAAkC,CAAC;AAEvD,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEnD,qBAAa,QAAQ;IACnB,QAAQ,CAAC,CAAC,cAAc,CAAC,EAAG,IAAI,CAAU;IAC1C,yBAAyB;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEb,OAAO,EAAE,MAAM;IAO3B,OAAO,IAAI,MAAM;IAIjB,QAAQ,IAAI,MAAM;IAIlB,GAAG,CAAC,KAAK,EAAE,SAAS,GAAG,QAAQ;IAI/B,GAAG,CAAC,KAAK,EAAE,SAAS,GAAG,QAAQ;IAI/B,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ;IAI7B,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,QAAQ;CAOrD;AAED,mCAAmC;AACnC,wBAAgB,CAAC,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,CAE3C;AAED,6CAA6C;AAC7C,wBAAgB,EAAE,CAAC,YAAY,EAAE,MAAM,GAAG,QAAQ,CAEjD;AAED,+CAA+C;AAC/C,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,SAAK,GAAG,QAAQ,CAGxD;AAKD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CA4BlD;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAE3D"}
1
+ {"version":3,"file":"duration.d.ts","sourceRoot":"","sources":["../src/duration.ts"],"names":[],"mappings":"AAAA,qEAAqE;AAIrE,QAAA,MAAM,cAAc,eAAkC,CAAC;AAEvD,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEnD,qBAAa,QAAQ;IACnB,QAAQ,CAAC,CAAC,cAAc,CAAC,EAAG,IAAI,CAAU;IAC1C,yBAAyB;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEb,OAAO,EAAE,MAAM;IAO3B,OAAO,IAAI,MAAM;IAIjB,QAAQ,IAAI,MAAM;IAIlB,GAAG,CAAC,KAAK,EAAE,SAAS,GAAG,QAAQ;IAI/B,GAAG,CAAC,KAAK,EAAE,SAAS,GAAG,QAAQ;IAI/B,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ;IAI7B,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,QAAQ;CAOrD;AAED,mCAAmC;AACnC,wBAAgB,CAAC,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,CAE3C;AAED,6CAA6C;AAC7C,wBAAgB,EAAE,CAAC,YAAY,EAAE,MAAM,GAAG,QAAQ,CAEjD;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,SAAK,GAAG,QAAQ,CAGxD;AAOD;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAoChE;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAE3D"}
package/dist/duration.js CHANGED
@@ -1,4 +1,5 @@
1
1
  /** Duration helpers — seconds as the canonical unit (matches IR). */
2
+ import { exactFpsOf, isTimecodeString, timecodeToSeconds } from './frame-rate.js';
2
3
  const DURATION_BRAND = Symbol.for('scenerok.Duration');
3
4
  export class Duration {
4
5
  [DURATION_BRAND] = true;
@@ -39,18 +40,25 @@ export function s(seconds) {
39
40
  export function ms(milliseconds) {
40
41
  return new Duration(milliseconds / 1000);
41
42
  }
42
- /** Frame count at a given fps (default 30). */
43
+ /**
44
+ * A frame count at `fps` (exact for NTSC: `frames(24, 23.976)` is `24 * 1001 / 24000`).
45
+ * To count frames of the timeline's own rate, use `t.frames(n)` or the string `'48f'`.
46
+ */
43
47
  export function frames(count, fps = 30) {
44
48
  if (fps <= 0)
45
49
  throw new TypeError('fps must be positive');
46
- return new Duration(count / fps);
50
+ return new Duration(count / exactFpsOf(fps));
47
51
  }
52
+ const FRAME_STRING_RE = /^(\d+)\s*(?:f|frame|frames)$/i;
48
53
  const TIME_STRING_RE = /^(?:(?<h>\d+):(?<m>\d{2}):(?<s>\d{2}(?:\.\d+)?))$|^(?:(?<mm>\d+):(?<ss>\d{2}(?:\.\d+)?))$|^(?<num>\d+(?:\.\d+)?)\s*(?<unit>s|sec|secs|seconds|ms|milliseconds)?$/i;
49
54
  /**
50
55
  * Parse a time value to seconds.
51
- * Accepts numbers (already seconds), Duration, or strings like `3s`, `300ms`, `1:30`, `0:01:30`.
56
+ * Accepts numbers (already seconds), Duration, or strings like `3s`, `300ms`,
57
+ * `1:30`, `0:01:30`, frame counts `48f`, and timecodes `01:00:00:00@24`.
58
+ * Frame counts and timecodes without `@rate` count frames of `fps` — a
59
+ * timeline passes its own — and throw without one.
52
60
  */
53
- export function toSeconds(value) {
61
+ export function toSeconds(value, fps) {
54
62
  if (typeof value === 'number') {
55
63
  if (!Number.isFinite(value))
56
64
  throw new TypeError(`Invalid time number: ${value}`);
@@ -63,6 +71,15 @@ export function toSeconds(value) {
63
71
  if (trimmed === '-' || trimmed === '') {
64
72
  throw new TypeError(`Cannot convert auto-playhead marker '${value}' via toSeconds; use timeline.push / t['-']`);
65
73
  }
74
+ if (isTimecodeString(trimmed))
75
+ return timecodeToSeconds(trimmed, fps);
76
+ const frameMatch = FRAME_STRING_RE.exec(trimmed);
77
+ if (frameMatch) {
78
+ if (fps === undefined) {
79
+ throw new TypeError(`Frame time '${value}' needs a frame rate; schedule it on a timeline or use frames(n, fps)`);
80
+ }
81
+ return Number(frameMatch[1]) / exactFpsOf(fps);
82
+ }
66
83
  const match = TIME_STRING_RE.exec(trimmed);
67
84
  if (!match || !match.groups) {
68
85
  throw new TypeError(`Unrecognized time string: ${JSON.stringify(value)}`);
@@ -83,5 +100,5 @@ export function toSeconds(value) {
83
100
  throw new TypeError(`Unsupported time input: ${String(value)}`);
84
101
  }
85
102
  export function isAutoPlayheadKey(key) {
86
- return key === '-' || key === '-';
103
+ return key === '-';
87
104
  }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Rational frame rates — the SDK's copy of the platform's `src/lib/time/frame-rate.ts`.
3
+ *
4
+ * IR times stay float seconds. A frame is `num / den` seconds, so 23.976 is
5
+ * `1001 / 24000` rather than a rounded float, exactly as VidScript computes it.
6
+ */
7
+ /** Frame duration in seconds: `num / den`. */
8
+ export interface TimeBase {
9
+ num: number;
10
+ den: number;
11
+ }
12
+ /** Frame digits a timecode counts per second: 23.976 counts 24. */
13
+ export declare function nominalFps(fps: number): number;
14
+ /** `{ 1001, nominal * 1000 }` for NTSC rates (23.976, 29.97, …), `{ 1, fps }` otherwise. */
15
+ export declare function frameRateOf(fps: number): TimeBase;
16
+ /** True for the NTSC family (a time base that is not `1 / integer`). */
17
+ export declare function isFractionalTimeBase(timeBase: TimeBase): boolean;
18
+ /** The exact frames per second: `24000 / 1001` for 23.976. */
19
+ export declare function exactFpsOf(fps: number): number;
20
+ /** Round seconds to the nearest frame boundary (ties up). */
21
+ export declare function snapToFrame(seconds: number, timeBase: TimeBase): number;
22
+ /** True for an `HH:MM:SS:FF` (optionally `@rate`) string. */
23
+ export declare function isTimecodeString(value: string): boolean;
24
+ /**
25
+ * `HH:MM:SS:FF[@rate]` in seconds. Non-drop-frame only; FF must be below the
26
+ * nominal rate and hours run 0-23. Without `@rate`, `fps` is the rate; with
27
+ * neither the timecode has no meaning and this throws.
28
+ */
29
+ export declare function timecodeToSeconds(value: string, fps?: number, label?: string): number;
30
+ //# sourceMappingURL=frame-rate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frame-rate.d.ts","sourceRoot":"","sources":["../src/frame-rate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,8CAA8C;AAC9C,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAKD,mEAAmE;AACnE,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED,4FAA4F;AAC5F,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAMjD;AAED,wEAAwE;AACxE,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAEhE;AAED,8DAA8D;AAC9D,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAG9C;AAED,6DAA6D;AAC7D,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAIvE;AAID,6DAA6D;AAC7D,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,KAAK,SAAa,GAAG,MAAM,CAsBzF"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Rational frame rates — the SDK's copy of the platform's `src/lib/time/frame-rate.ts`.
3
+ *
4
+ * IR times stay float seconds. A frame is `num / den` seconds, so 23.976 is
5
+ * `1001 / 24000` rather than a rounded float, exactly as VidScript computes it.
6
+ */
7
+ const NTSC_TOLERANCE = 0.001;
8
+ const NTSC_NOMINAL = [24, 30, 48, 60, 120];
9
+ /** Frame digits a timecode counts per second: 23.976 counts 24. */
10
+ export function nominalFps(fps) {
11
+ return Math.max(1, Math.round(fps));
12
+ }
13
+ /** `{ 1001, nominal * 1000 }` for NTSC rates (23.976, 29.97, …), `{ 1, fps }` otherwise. */
14
+ export function frameRateOf(fps) {
15
+ for (const nominal of NTSC_NOMINAL) {
16
+ const ntsc = (nominal * 1000) / 1001;
17
+ if (Math.abs(fps - ntsc) <= NTSC_TOLERANCE)
18
+ return { num: 1001, den: nominal * 1000 };
19
+ }
20
+ return { num: 1, den: fps };
21
+ }
22
+ /** True for the NTSC family (a time base that is not `1 / integer`). */
23
+ export function isFractionalTimeBase(timeBase) {
24
+ return timeBase.num !== 1;
25
+ }
26
+ /** The exact frames per second: `24000 / 1001` for 23.976. */
27
+ export function exactFpsOf(fps) {
28
+ const base = frameRateOf(fps);
29
+ return base.den / base.num;
30
+ }
31
+ /** Round seconds to the nearest frame boundary (ties up). */
32
+ export function snapToFrame(seconds, timeBase) {
33
+ if (!Number.isFinite(seconds))
34
+ return seconds;
35
+ const frames = Math.round((seconds * timeBase.den) / timeBase.num);
36
+ return (frames * timeBase.num) / timeBase.den;
37
+ }
38
+ const TIMECODE_RE = /^(\d{2}):(\d{2}):(\d{2})([:;])(\d{2})(?:@(\d+(?:\.\d+)?))?$/;
39
+ /** True for an `HH:MM:SS:FF` (optionally `@rate`) string. */
40
+ export function isTimecodeString(value) {
41
+ return TIMECODE_RE.test(value.trim());
42
+ }
43
+ /**
44
+ * `HH:MM:SS:FF[@rate]` in seconds. Non-drop-frame only; FF must be below the
45
+ * nominal rate and hours run 0-23. Without `@rate`, `fps` is the rate; with
46
+ * neither the timecode has no meaning and this throws.
47
+ */
48
+ export function timecodeToSeconds(value, fps, label = 'timecode') {
49
+ const match = TIMECODE_RE.exec(value.trim());
50
+ if (!match) {
51
+ throw new TypeError(`${label} "${value}" is not HH:MM:SS:FF (two digits each, for example "01:00:00:00")`);
52
+ }
53
+ if (match[4] === ';') {
54
+ throw new TypeError(`${label} "${value}" is drop-frame; only non-drop-frame HH:MM:SS:FF is supported`);
55
+ }
56
+ const rate = match[6] !== undefined ? Number(match[6]) : fps;
57
+ if (rate === undefined || !Number.isFinite(rate) || rate <= 0) {
58
+ throw new TypeError(`${label} "${value}" needs a frame rate: write it as "${value}@24", or schedule it on a timeline`);
59
+ }
60
+ const [hours, minutes, seconds, frames] = [match[1], match[2], match[3], match[5]].map(Number);
61
+ if (hours > 23)
62
+ throw new RangeError(`${label} "${value}" has hours ${hours}; hours run 0-23`);
63
+ if (minutes > 59)
64
+ throw new RangeError(`${label} "${value}" has minutes ${minutes}; minutes run 0-59`);
65
+ if (seconds > 59)
66
+ throw new RangeError(`${label} "${value}" has seconds ${seconds}; seconds run 0-59`);
67
+ const nominal = nominalFps(rate);
68
+ if (frames >= nominal) {
69
+ throw new RangeError(`${label} "${value}" has frame ${frames}, but the rate is ${rate}; frames run 0-${nominal - 1}`);
70
+ }
71
+ const count = ((hours * 60 + minutes) * 60 + seconds) * nominal + frames;
72
+ return snapToFrame(count / exactFpsOf(rate), frameRateOf(rate));
73
+ }
package/dist/index.d.ts CHANGED
@@ -4,15 +4,18 @@
4
4
  * Agents use this for complex, data-driven scenes. Humans keep VidScript for
5
5
  * short declarative scripts. Both emit the same IRTimeline for Pass 2.
6
6
  */
7
- export type { IRTimeline, IRClip, IRLayer, IRTextLayer, IRSurface, IREffect, IRAudioTrack, IROutput, IRAnimation, IRMotionTrack, } from './ir.js';
7
+ export type { IRTimeline, IRClip, IRLayer, IRTextLayer, IRTextStyle, IRSurface, IREffect, IRAudioTrack, IROutput, IRAnimation, IRMotionTrack, IRGroup, IRMarker, IRCrop, IRMaskMode, IRMotionBlur, IRImageSequence, } from './ir.js';
8
8
  export { defaultIRTimeline, defaultIROutput } from './ir.js';
9
9
  export { Duration, s, ms, frames, toSeconds, type TimeInput, } from './duration.js';
10
- export type { VideoClip, VideoOptions, TextLayer, TextOptions, AudioTrack, AudioOptions, FilterEffect, ShaderEffect, TransitionEffect, TransitionOptions, AnimationDescriptor, Schedulable, TimelineOptions, TimelineLike, SdkHost, } from './types.js';
11
- export { isSdkNode, isSchedulable } from './types.js';
12
- export { video, loadVideo, text, audio, loadAudio, filter, shader } from './media.js';
13
- export { fadeIn, fadeOut, slideY, slideX, scale, popIn } from './motion.js';
10
+ export { frameRateOf, exactFpsOf, snapToFrame, timecodeToSeconds, type TimeBase } from './frame-rate.js';
11
+ export type { VideoClip, VideoOptions, ImageClip, ImageOptions, ImageSequenceOptions, MediaMethods, ClipEdits, MaskOptions, PlacementOptions, BlendMode, MotionBlurOption, TextLayer, TextOptions, AudioTrack, AudioOptions, SolidNode, SolidOptions, GapNode, MarkerNode, FilterEffect, ShaderEffect, TransitionEffect, TransitionOptions, AnimationDescriptor, PictureNode, Schedulable, OutputOptions, TimelineOptions, TimelineLike, TimelineView, TimelinePlacementOptions, SdkHost, } from './types.js';
12
+ export { isSdkNode, isSchedulable, isPictureNode } from './types.js';
13
+ export { video, loadVideo, image, imageSequence, text, audio, loadAudio, solid, gap, marker, filter, shader, } from './media.js';
14
+ export { animation, fadeIn, fadeOut, slideY, slideX, scale, popIn, riseIn, swingIn, glitchIn, float, typewriter, rotate, volumeRamp, type AnimationOptions, } from './motion.js';
14
15
  export { timeline, isTimeline, type Timeline } from './timeline.js';
15
- export { createIrBuilder, scheduleAt, resetIrBuilderIds, inferAutoDuration, assertSchedulable, bindTransitions, setGrid, type IrBuilderState, } from './ir-builder.js';
16
+ export { createIrBuilder, scheduleAt, resetIrBuilderIds, inferAutoDuration, assertSchedulable, bindTransitions, setGrid, hostCanvas, type Canvas, type IrBuilderState, } from './ir-builder.js';
17
+ export { animationToMotionTracks, animationsToMotionTracks } from './motion-tracks.js';
18
+ export { describeValue, type SdkValueDescriptor, type MaskDescriptor } from './describe.js';
16
19
  export { defaultGridSpec, resolveGridAreas, placementFromGridArea, placeInArea, type GridSpec, type GridAreaRect, type GridAreaPlacement, } from './grid.js';
17
20
  export { getVideoLength, getAudioLength, getMediaInfo, getDimensions, getFps, getLoudness, hasAudio, mediaKind, grabFrame, grabFrames, grabFrameAtFraction, measureText, fitContain, fitCover, clampTime, parseTime, timecode, beats, type MediaSource, type MediaInfo, type Dimensions, type LoudnessInfo, type FitBox, type GrabFrameOptions, type MeasureTextStyle, type TextMetrics, type StdlibMediaKind, type StdlibHostApi, } from './stdlib.js';
18
21
  export { default as xai } from './plugins/xai.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY,EACV,UAAU,EACV,MAAM,EACN,OAAO,EACP,WAAW,EACX,WAAW,EACX,SAAS,EACT,QAAQ,EACR,YAAY,EACZ,QAAQ,EACR,WAAW,EACX,aAAa,EACb,OAAO,EACP,QAAQ,EACR,MAAM,EACN,UAAU,EACV,YAAY,EACZ,eAAe,GAChB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE7D,OAAO,EACL,QAAQ,EACR,CAAC,EACD,EAAE,EACF,MAAM,EACN,SAAS,EACT,KAAK,SAAS,GACf,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,iBAAiB,EAAE,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAEzG,YAAY,EACV,SAAS,EACT,YAAY,EACZ,SAAS,EACT,YAAY,EACZ,oBAAoB,EACpB,YAAY,EACZ,SAAS,EACT,WAAW,EACX,gBAAgB,EAChB,SAAS,EACT,gBAAgB,EAChB,SAAS,EACT,WAAW,EACX,UAAU,EACV,YAAY,EACZ,SAAS,EACT,YAAY,EACZ,OAAO,EACP,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,WAAW,EACX,WAAW,EACX,aAAa,EACb,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,wBAAwB,EACxB,OAAO,GACR,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAErE,OAAO,EACL,KAAK,EACL,SAAS,EACT,KAAK,EACL,aAAa,EACb,IAAI,EACJ,KAAK,EACL,SAAS,EACT,KAAK,EACL,GAAG,EACH,MAAM,EACN,MAAM,EACN,MAAM,GACP,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,SAAS,EACT,MAAM,EACN,OAAO,EACP,MAAM,EACN,MAAM,EACN,KAAK,EACL,KAAK,EACL,MAAM,EACN,OAAO,EACP,QAAQ,EACR,KAAK,EACL,UAAU,EACV,MAAM,EACN,UAAU,EACV,KAAK,gBAAgB,GACtB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,EACL,eAAe,EACf,UAAU,EACV,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,OAAO,EACP,UAAU,EACV,KAAK,MAAM,EACX,KAAK,cAAc,GACpB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AACvF,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAE5F,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,qBAAqB,EACrB,WAAW,EACX,KAAK,QAAQ,EACb,KAAK,YAAY,EACjB,KAAK,iBAAiB,GACvB,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,cAAc,EACd,cAAc,EACd,YAAY,EACZ,aAAa,EACb,MAAM,EACN,WAAW,EACX,QAAQ,EACR,SAAS,EACT,SAAS,EACT,UAAU,EACV,mBAAmB,EACnB,WAAW,EACX,UAAU,EACV,QAAQ,EACR,SAAS,EACT,SAAS,EACT,QAAQ,EACR,KAAK,EACL,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,MAAM,EACX,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,aAAa,GACnB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,OAAO,IAAI,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAClD,YAAY,EACV,SAAS,EACT,eAAe,EACf,eAAe,EACf,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,kBAAkB,EAClB,kBAAkB,EAClB,UAAU,EACV,WAAW,GACZ,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAChE,YAAY,EACV,gBAAgB,EAChB,sBAAsB,EACtB,sBAAsB,EACtB,sBAAsB,EACtB,wBAAwB,EACxB,wBAAwB,EACxB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAC3E,YAAY,EACV,qBAAqB,EACrB,sBAAsB,EACtB,yBAAyB,EACzB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC1D,YAAY,EACV,aAAa,EACb,cAAc,GACf,MAAM,sBAAsB,CAAC;AAE9B,YAAY,EACV,WAAW,EACX,WAAW,EACX,UAAU,GACX,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAC5E,YAAY,EACV,iBAAiB,EACjB,sBAAsB,EACtB,gBAAgB,GACjB,MAAM,wBAAwB,CAAC"}
package/dist/index.js CHANGED
@@ -6,11 +6,14 @@
6
6
  */
7
7
  export { defaultIRTimeline, defaultIROutput } from './ir.js';
8
8
  export { Duration, s, ms, frames, toSeconds, } from './duration.js';
9
- export { isSdkNode, isSchedulable } from './types.js';
10
- export { video, loadVideo, text, audio, loadAudio, filter, shader } from './media.js';
11
- export { fadeIn, fadeOut, slideY, slideX, scale, popIn } from './motion.js';
9
+ export { frameRateOf, exactFpsOf, snapToFrame, timecodeToSeconds } from './frame-rate.js';
10
+ export { isSdkNode, isSchedulable, isPictureNode } from './types.js';
11
+ export { video, loadVideo, image, imageSequence, text, audio, loadAudio, solid, gap, marker, filter, shader, } from './media.js';
12
+ export { animation, fadeIn, fadeOut, slideY, slideX, scale, popIn, riseIn, swingIn, glitchIn, float, typewriter, rotate, volumeRamp, } from './motion.js';
12
13
  export { timeline, isTimeline } from './timeline.js';
13
- export { createIrBuilder, scheduleAt, resetIrBuilderIds, inferAutoDuration, assertSchedulable, bindTransitions, setGrid, } from './ir-builder.js';
14
+ export { createIrBuilder, scheduleAt, resetIrBuilderIds, inferAutoDuration, assertSchedulable, bindTransitions, setGrid, hostCanvas, } from './ir-builder.js';
15
+ export { animationToMotionTracks, animationsToMotionTracks } from './motion-tracks.js';
16
+ export { describeValue } from './describe.js';
14
17
  export { defaultGridSpec, resolveGridAreas, placementFromGridArea, placeInArea, } from './grid.js';
15
18
  // Media stdlib — host-injected at compile time (`@scenerok/stdlib` alias)
16
19
  export { getVideoLength, getAudioLength, getMediaInfo, getDimensions, getFps, getLoudness, hasAudio, mediaKind, grabFrame, grabFrames, grabFrameAtFraction, measureText, fitContain, fitCover, clampTime, parseTime, timecode, beats, } from './stdlib.js';