@pixerate/flair 0.0.0-stage → 0.1.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/CHANGELOG.md +7 -0
- package/LICENSE +21 -0
- package/README.md +254 -2
- package/dist/catalog/index.d.ts +74 -0
- package/dist/catalog/index.js +47 -0
- package/dist/catalog/index.js.map +1 -0
- package/dist/chunk-36DTNSHU.js +160 -0
- package/dist/chunk-36DTNSHU.js.map +1 -0
- package/dist/chunk-3FAUZOGL.js +331 -0
- package/dist/chunk-3FAUZOGL.js.map +1 -0
- package/dist/chunk-CFCLT2XL.js +1 -0
- package/dist/chunk-CFCLT2XL.js.map +1 -0
- package/dist/chunk-EHDJ3CPJ.js +327 -0
- package/dist/chunk-EHDJ3CPJ.js.map +1 -0
- package/dist/chunk-G5FNZDTA.js +536 -0
- package/dist/chunk-G5FNZDTA.js.map +1 -0
- package/dist/chunk-HKXOHRCD.js +763 -0
- package/dist/chunk-HKXOHRCD.js.map +1 -0
- package/dist/chunk-J55UBNAT.js +164 -0
- package/dist/chunk-J55UBNAT.js.map +1 -0
- package/dist/chunk-JWZCPMFG.js +2074 -0
- package/dist/chunk-JWZCPMFG.js.map +1 -0
- package/dist/chunk-MWVTQFV5.js +1894 -0
- package/dist/chunk-MWVTQFV5.js.map +1 -0
- package/dist/chunk-UQQNZSYL.js +594 -0
- package/dist/chunk-UQQNZSYL.js.map +1 -0
- package/dist/chunk-WMZQCUFJ.js +73 -0
- package/dist/chunk-WMZQCUFJ.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +331 -0
- package/dist/cli.js.map +1 -0
- package/dist/clock-BhdXWH-n.d.ts +47 -0
- package/dist/controls-DdXMbovi.d.ts +97 -0
- package/dist/editor/index.d.ts +413 -0
- package/dist/editor/index.js +1009 -0
- package/dist/editor/index.js.map +1 -0
- package/dist/export-FEBPEEO7.js +22 -0
- package/dist/export-FEBPEEO7.js.map +1 -0
- package/dist/index.d.ts +1431 -0
- package/dist/index.js +1263 -0
- package/dist/index.js.map +1 -0
- package/dist/motion-D0TVv2CF.d.ts +148 -0
- package/dist/node.d.ts +24 -0
- package/dist/node.js +10 -0
- package/dist/node.js.map +1 -0
- package/dist/recorder-WJTWMEQN.js +10 -0
- package/dist/recorder-WJTWMEQN.js.map +1 -0
- package/dist/renderer-Oyynz-fs.d.ts +293 -0
- package/dist/types-CkKkavvB.d.ts +71 -0
- package/dist/vector/index.d.ts +83 -0
- package/dist/vector/index.js +39 -0
- package/dist/vector/index.js.map +1 -0
- package/package.json +96 -4
package/dist/index.js
ADDED
|
@@ -0,0 +1,1263 @@
|
|
|
1
|
+
import "./chunk-CFCLT2XL.js";
|
|
2
|
+
import {
|
|
3
|
+
EASES,
|
|
4
|
+
STEP_MS,
|
|
5
|
+
SVG_PICTURE,
|
|
6
|
+
SvgPlayer,
|
|
7
|
+
defaultDataKey,
|
|
8
|
+
lerpAttr,
|
|
9
|
+
lerpData,
|
|
10
|
+
parseSvg,
|
|
11
|
+
stepData,
|
|
12
|
+
stepProgress,
|
|
13
|
+
stepStart,
|
|
14
|
+
svgDuration,
|
|
15
|
+
svgFrame,
|
|
16
|
+
svgMotionIssues,
|
|
17
|
+
svgOpeningTag,
|
|
18
|
+
svgView
|
|
19
|
+
} from "./chunk-G5FNZDTA.js";
|
|
20
|
+
import {
|
|
21
|
+
ALL_BLEND_MODES,
|
|
22
|
+
EFFECT_FADE_MS,
|
|
23
|
+
EFFECT_TYPES,
|
|
24
|
+
MemorySceneBoard,
|
|
25
|
+
NO_CHECK,
|
|
26
|
+
SMALL_TEXT_PX,
|
|
27
|
+
ShaderFrames,
|
|
28
|
+
ShaderRenderer,
|
|
29
|
+
ThumbnailPool,
|
|
30
|
+
blendRgba,
|
|
31
|
+
checkShot,
|
|
32
|
+
effectGone,
|
|
33
|
+
effectOpacity,
|
|
34
|
+
evaluateBehavior,
|
|
35
|
+
fractalWiggle,
|
|
36
|
+
getGlslBlendFunction,
|
|
37
|
+
hash64,
|
|
38
|
+
mixSeeds,
|
|
39
|
+
noise1d,
|
|
40
|
+
noise3d,
|
|
41
|
+
playScene,
|
|
42
|
+
quinticFade,
|
|
43
|
+
recordGif,
|
|
44
|
+
registerThumbnail,
|
|
45
|
+
reviewScene,
|
|
46
|
+
sharedThumbnailPool,
|
|
47
|
+
simulateScene,
|
|
48
|
+
standaloneHtml,
|
|
49
|
+
standaloneSceneHtml,
|
|
50
|
+
titleBox,
|
|
51
|
+
touchedIds,
|
|
52
|
+
unitFloat,
|
|
53
|
+
validateScene
|
|
54
|
+
} from "./chunk-JWZCPMFG.js";
|
|
55
|
+
import {
|
|
56
|
+
BUILTIN_UNIFORMS,
|
|
57
|
+
ImageLoadError,
|
|
58
|
+
SVG_EDGE,
|
|
59
|
+
ShaderCompileError,
|
|
60
|
+
TextureCache,
|
|
61
|
+
VERTEX_SOURCE,
|
|
62
|
+
applyAscCdl,
|
|
63
|
+
applyLiftGammaGain,
|
|
64
|
+
applyTextureSettings,
|
|
65
|
+
applyVibrance,
|
|
66
|
+
compileShader,
|
|
67
|
+
convertColorSpace,
|
|
68
|
+
createContext,
|
|
69
|
+
drawFrame,
|
|
70
|
+
formatCompileError,
|
|
71
|
+
getColorGradingGLSL,
|
|
72
|
+
getOklabGLSL,
|
|
73
|
+
getWhiteBalanceGains,
|
|
74
|
+
interpolateOklab,
|
|
75
|
+
interpolateOklch,
|
|
76
|
+
isSvgUrl,
|
|
77
|
+
linearRgbToSrgb,
|
|
78
|
+
linearSrgbToOklab,
|
|
79
|
+
linearToSrgb,
|
|
80
|
+
loadImage,
|
|
81
|
+
oklabToLinearSrgb,
|
|
82
|
+
oklabToOklch,
|
|
83
|
+
oklabToSrgb,
|
|
84
|
+
oklchToOklab,
|
|
85
|
+
parseCompileLog,
|
|
86
|
+
preloadImages,
|
|
87
|
+
releaseImages,
|
|
88
|
+
resetGlCaches,
|
|
89
|
+
sizeSvg,
|
|
90
|
+
srgbToLinear,
|
|
91
|
+
srgbToLinearRgb,
|
|
92
|
+
srgbToOklab,
|
|
93
|
+
wrapSource
|
|
94
|
+
} from "./chunk-HKXOHRCD.js";
|
|
95
|
+
import {
|
|
96
|
+
ALL_TRANSITION_TYPES,
|
|
97
|
+
blendTransitionPixel,
|
|
98
|
+
getTransitionGLSL,
|
|
99
|
+
sampleTransition
|
|
100
|
+
} from "./chunk-EHDJ3CPJ.js";
|
|
101
|
+
import {
|
|
102
|
+
ManualClock,
|
|
103
|
+
jitter,
|
|
104
|
+
pageHidden,
|
|
105
|
+
realClock
|
|
106
|
+
} from "./chunk-WMZQCUFJ.js";
|
|
107
|
+
import {
|
|
108
|
+
calculateShotTimings,
|
|
109
|
+
createLottieEase,
|
|
110
|
+
exportCmx3600Edl,
|
|
111
|
+
exportFcp7Xml,
|
|
112
|
+
exportLottie,
|
|
113
|
+
exportMotn,
|
|
114
|
+
framesToTimecode,
|
|
115
|
+
timecodeToFrames
|
|
116
|
+
} from "./chunk-UQQNZSYL.js";
|
|
117
|
+
import {
|
|
118
|
+
CAMERA_MS,
|
|
119
|
+
DEFAULT_STAGE,
|
|
120
|
+
areaOf,
|
|
121
|
+
between,
|
|
122
|
+
boardHeight,
|
|
123
|
+
boundsOf,
|
|
124
|
+
calculateFramingRect,
|
|
125
|
+
centreOf,
|
|
126
|
+
easeInOutCubic,
|
|
127
|
+
frame,
|
|
128
|
+
isDeleteCommand,
|
|
129
|
+
isElementCommand,
|
|
130
|
+
isUpdateCommand,
|
|
131
|
+
overlapArea,
|
|
132
|
+
viewAt,
|
|
133
|
+
visibleRect
|
|
134
|
+
} from "./chunk-36DTNSHU.js";
|
|
135
|
+
import {
|
|
136
|
+
checkControlValue,
|
|
137
|
+
coerceControlValue,
|
|
138
|
+
componentCount,
|
|
139
|
+
controlValue,
|
|
140
|
+
describeValue,
|
|
141
|
+
hexToRgb,
|
|
142
|
+
humanise,
|
|
143
|
+
imageUrls,
|
|
144
|
+
isImageUrl,
|
|
145
|
+
parseControls,
|
|
146
|
+
rgbToHex
|
|
147
|
+
} from "./chunk-J55UBNAT.js";
|
|
148
|
+
|
|
149
|
+
// src/time/retiming.ts
|
|
150
|
+
function loopTime(t, duration, start = 0) {
|
|
151
|
+
if (duration <= 0) return start;
|
|
152
|
+
const rel = t - start;
|
|
153
|
+
const mod = (rel % duration + duration) % duration;
|
|
154
|
+
return start + mod;
|
|
155
|
+
}
|
|
156
|
+
function pingPongTime(t, duration, start = 0) {
|
|
157
|
+
if (duration <= 0) return start;
|
|
158
|
+
const period = 2 * duration;
|
|
159
|
+
const rel = t - start;
|
|
160
|
+
const mod = (rel % period + period) % period;
|
|
161
|
+
const offset = mod <= duration ? mod : period - mod;
|
|
162
|
+
return start + offset;
|
|
163
|
+
}
|
|
164
|
+
function holdTime(_t, holdAt) {
|
|
165
|
+
return holdAt;
|
|
166
|
+
}
|
|
167
|
+
function strobeTime(t, fps, unit = "ms") {
|
|
168
|
+
if (fps <= 0) return t;
|
|
169
|
+
const interval = (unit === "ms" ? 1e3 : 1) / fps;
|
|
170
|
+
return Math.floor(t / interval) * interval;
|
|
171
|
+
}
|
|
172
|
+
function speedRampTime(t, start, duration, ease = "easeInOut") {
|
|
173
|
+
if (duration <= 0 || t <= start) return t;
|
|
174
|
+
if (t >= start + duration) return t;
|
|
175
|
+
const p = (t - start) / duration;
|
|
176
|
+
let eased;
|
|
177
|
+
switch (ease) {
|
|
178
|
+
case "linear":
|
|
179
|
+
eased = p;
|
|
180
|
+
break;
|
|
181
|
+
case "easeIn":
|
|
182
|
+
eased = p * p;
|
|
183
|
+
break;
|
|
184
|
+
case "easeOut":
|
|
185
|
+
eased = p * (2 - p);
|
|
186
|
+
break;
|
|
187
|
+
case "easeInOut":
|
|
188
|
+
default:
|
|
189
|
+
eased = p < 0.5 ? 2 * p * p : -1 + (4 - 2 * p) * p;
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
return start + eased * duration;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// src/time/keyframe.ts
|
|
196
|
+
var EASY_EASE = {
|
|
197
|
+
speed: 0,
|
|
198
|
+
influence: 1 / 3
|
|
199
|
+
};
|
|
200
|
+
function bez1d(p0, p1, p2, p3, u) {
|
|
201
|
+
const v = 1 - u;
|
|
202
|
+
return v * v * v * p0 + 3 * v * v * u * p1 + 3 * v * u * u * p2 + u * u * u * p3;
|
|
203
|
+
}
|
|
204
|
+
function bez1dDerivative(p0, p1, p2, p3, u) {
|
|
205
|
+
const v = 1 - u;
|
|
206
|
+
return 3 * v * v * (p1 - p0) + 6 * v * u * (p2 - p1) + 3 * u * u * (p3 - p2);
|
|
207
|
+
}
|
|
208
|
+
function solveBezierU(x0, x1, x2, x3, x) {
|
|
209
|
+
if (x <= x0) return 0;
|
|
210
|
+
if (x >= x3) return 1;
|
|
211
|
+
if (Math.abs(x1 - x3) < 1e-12 && Math.abs(x2 - x0) < 1e-12) {
|
|
212
|
+
const progress = (x - x0) / (x3 - x0);
|
|
213
|
+
return 0.5 + Math.cbrt((progress - 0.5) * 0.25);
|
|
214
|
+
}
|
|
215
|
+
let u = (x - x0) / (x3 - x0);
|
|
216
|
+
for (let iter = 0; iter < 8; iter++) {
|
|
217
|
+
const f = bez1d(x0, x1, x2, x3, u) - x;
|
|
218
|
+
const d = bez1dDerivative(x0, x1, x2, x3, u);
|
|
219
|
+
if (Math.abs(d) < 1e-12) break;
|
|
220
|
+
if (Math.abs(f / d) < 1e-12) return u;
|
|
221
|
+
const nextU = u - f / d;
|
|
222
|
+
if (nextU < 0 || nextU > 1) break;
|
|
223
|
+
u = nextU;
|
|
224
|
+
}
|
|
225
|
+
let lo = 0;
|
|
226
|
+
let hi = 1;
|
|
227
|
+
for (let iter = 0; iter < 60; iter++) {
|
|
228
|
+
const mid = (lo + hi) * 0.5;
|
|
229
|
+
if (bez1d(x0, x1, x2, x3, mid) < x) {
|
|
230
|
+
lo = mid;
|
|
231
|
+
} else {
|
|
232
|
+
hi = mid;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return (lo + hi) * 0.5;
|
|
236
|
+
}
|
|
237
|
+
function easeProgress(outSpeedNorm, outInfluence, inSpeedNorm, inInfluence, x) {
|
|
238
|
+
const i0 = Math.min(1, Math.max(0, outInfluence));
|
|
239
|
+
const i1 = Math.min(1, Math.max(0, inInfluence));
|
|
240
|
+
const u = solveBezierU(0, i0, 1 - i1, 1, x);
|
|
241
|
+
return bez1d(0, outSpeedNorm * i0, 1 - inSpeedNorm * i1, 1, u);
|
|
242
|
+
}
|
|
243
|
+
function evaluateKeyframeSegment(from, to, duration, t, outEase = EASY_EASE, inEase = EASY_EASE) {
|
|
244
|
+
if (duration <= 0 || t <= 0) return from;
|
|
245
|
+
if (t >= duration) return to;
|
|
246
|
+
const delta = to - from;
|
|
247
|
+
const x = t / duration;
|
|
248
|
+
if (Math.abs(delta) < 1e-12) {
|
|
249
|
+
return from;
|
|
250
|
+
}
|
|
251
|
+
const outSpeedNorm = outEase.speed * duration / delta;
|
|
252
|
+
const inSpeedNorm = inEase.speed * duration / delta;
|
|
253
|
+
const progress = easeProgress(outSpeedNorm, outEase.influence, inSpeedNorm, inEase.influence, x);
|
|
254
|
+
return from + progress * delta;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// src/time/timecode.ts
|
|
258
|
+
var COMMON_FRAME_RATES = {
|
|
259
|
+
FPS_23_976: { num: 24e3, den: 1001 },
|
|
260
|
+
FPS_24: { num: 24, den: 1 },
|
|
261
|
+
FPS_25: { num: 25, den: 1 },
|
|
262
|
+
FPS_29_97: { num: 3e4, den: 1001 },
|
|
263
|
+
FPS_30: { num: 30, den: 1 },
|
|
264
|
+
FPS_48: { num: 48, den: 1 },
|
|
265
|
+
FPS_50: { num: 50, den: 1 },
|
|
266
|
+
FPS_59_94: { num: 6e4, den: 1001 },
|
|
267
|
+
FPS_60: { num: 60, den: 1 },
|
|
268
|
+
FPS_120: { num: 120, den: 1 }
|
|
269
|
+
};
|
|
270
|
+
function gcd(a, b) {
|
|
271
|
+
let x = Math.abs(Math.round(a));
|
|
272
|
+
let y = Math.abs(Math.round(b));
|
|
273
|
+
while (y !== 0) {
|
|
274
|
+
const t = y;
|
|
275
|
+
y = x % y;
|
|
276
|
+
x = t;
|
|
277
|
+
}
|
|
278
|
+
return x || 1;
|
|
279
|
+
}
|
|
280
|
+
function createFrameRate(num, den = 1) {
|
|
281
|
+
if (den <= 0 || num <= 0) {
|
|
282
|
+
return { num: 30, den: 1 };
|
|
283
|
+
}
|
|
284
|
+
const g = gcd(num, den);
|
|
285
|
+
return { num: Math.round(num / g), den: Math.round(den / g) };
|
|
286
|
+
}
|
|
287
|
+
function getTimecodeBase(rate) {
|
|
288
|
+
return Math.ceil(rate.num / rate.den);
|
|
289
|
+
}
|
|
290
|
+
function supportsDropFrame(rate) {
|
|
291
|
+
return rate.den === 1001 && getTimecodeBase(rate) % 30 === 0;
|
|
292
|
+
}
|
|
293
|
+
function getDropFrameCount(rate) {
|
|
294
|
+
const base = getTimecodeBase(rate);
|
|
295
|
+
return Math.floor(base / 15);
|
|
296
|
+
}
|
|
297
|
+
function framesToSMPTE(frame2, rate = COMMON_FRAME_RATES.FPS_30, dropFrame = false) {
|
|
298
|
+
const isDrop = dropFrame && supportsDropFrame(rate);
|
|
299
|
+
const neg = frame2 < 0;
|
|
300
|
+
let n = Math.abs(Math.round(frame2));
|
|
301
|
+
const base = getTimecodeBase(rate);
|
|
302
|
+
const drop = getDropFrameCount(rate);
|
|
303
|
+
if (isDrop) {
|
|
304
|
+
const perMinute = base * 60 - drop;
|
|
305
|
+
const per10Minutes = perMinute * 10 + drop;
|
|
306
|
+
const d = Math.floor(n / per10Minutes);
|
|
307
|
+
const m = n % per10Minutes;
|
|
308
|
+
n += drop * 9 * d;
|
|
309
|
+
if (m > drop) {
|
|
310
|
+
n += drop * Math.floor((m - drop) / perMinute);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
const frames = n % base;
|
|
314
|
+
const seconds = Math.floor(n / base) % 60;
|
|
315
|
+
const minutes = Math.floor(n / (base * 60)) % 60;
|
|
316
|
+
const hours = Math.floor(n / (base * 3600));
|
|
317
|
+
return { negative: neg, hours, minutes, seconds, frames };
|
|
318
|
+
}
|
|
319
|
+
function smpteToFrames(fields, rate = COMMON_FRAME_RATES.FPS_30, dropFrame = false) {
|
|
320
|
+
const isDrop = dropFrame && supportsDropFrame(rate);
|
|
321
|
+
const base = getTimecodeBase(rate);
|
|
322
|
+
const drop = getDropFrameCount(rate);
|
|
323
|
+
const totalSecs = fields.hours * 3600 + fields.minutes * 60 + fields.seconds;
|
|
324
|
+
let n = totalSecs * base + fields.frames;
|
|
325
|
+
if (isDrop) {
|
|
326
|
+
const totalMinutes = fields.hours * 60 + fields.minutes;
|
|
327
|
+
n -= drop * (totalMinutes - Math.floor(totalMinutes / 10));
|
|
328
|
+
}
|
|
329
|
+
return n;
|
|
330
|
+
}
|
|
331
|
+
function formatTimecode(frame2, rate = COMMON_FRAME_RATES.FPS_30, dropFrame = false) {
|
|
332
|
+
const isDrop = dropFrame && supportsDropFrame(rate);
|
|
333
|
+
const { negative, hours, minutes, seconds, frames } = framesToSMPTE(frame2, rate, isDrop);
|
|
334
|
+
const sep = isDrop ? ";" : ":";
|
|
335
|
+
const base = getTimecodeBase(rate);
|
|
336
|
+
const frameWidth = base >= 100 ? 3 : 2;
|
|
337
|
+
const hStr = String(hours).padStart(2, "0");
|
|
338
|
+
const mStr = String(minutes).padStart(2, "0");
|
|
339
|
+
const sStr = String(seconds).padStart(2, "0");
|
|
340
|
+
const fStr = String(frames).padStart(frameWidth, "0");
|
|
341
|
+
return `${negative ? "-" : ""}${hStr}${sep}${mStr}${sep}${sStr}${sep}${fStr}`;
|
|
342
|
+
}
|
|
343
|
+
function parseTimecode(input, rate = COMMON_FRAME_RATES.FPS_30, dropFrame = false, currentFrame = 0) {
|
|
344
|
+
const s = input.trim();
|
|
345
|
+
if (!s) {
|
|
346
|
+
throw new Error("Empty timecode string");
|
|
347
|
+
}
|
|
348
|
+
let relativeSign = null;
|
|
349
|
+
let body = s;
|
|
350
|
+
if (s.startsWith("+")) {
|
|
351
|
+
relativeSign = 1;
|
|
352
|
+
body = s.slice(1);
|
|
353
|
+
} else if (s.startsWith("-")) {
|
|
354
|
+
relativeSign = -1;
|
|
355
|
+
body = s.slice(1);
|
|
356
|
+
}
|
|
357
|
+
const parts = body.split(/[:;.,]/);
|
|
358
|
+
let parsedFrame = 0;
|
|
359
|
+
if (parts.length === 1) {
|
|
360
|
+
const digits = parts[0];
|
|
361
|
+
if (!/^\d+$/.test(digits)) {
|
|
362
|
+
throw new Error(`Invalid timecode: "${input}"`);
|
|
363
|
+
}
|
|
364
|
+
if (relativeSign !== null) {
|
|
365
|
+
const count = parseInt(digits, 10);
|
|
366
|
+
return currentFrame + relativeSign * count;
|
|
367
|
+
}
|
|
368
|
+
const nums = [];
|
|
369
|
+
let end = digits.length;
|
|
370
|
+
while (end > 0) {
|
|
371
|
+
const start = Math.max(0, end - 2);
|
|
372
|
+
nums.unshift(parseInt(digits.slice(start, end), 10));
|
|
373
|
+
end = start;
|
|
374
|
+
}
|
|
375
|
+
const padded = [0, 0, 0, 0];
|
|
376
|
+
const offset = 4 - nums.length;
|
|
377
|
+
for (let i = 0; i < nums.length; i++) {
|
|
378
|
+
padded[offset + i] = nums[i];
|
|
379
|
+
}
|
|
380
|
+
const [h, m, sec, fr] = padded;
|
|
381
|
+
parsedFrame = smpteToFrames(
|
|
382
|
+
{ hours: h, minutes: m, seconds: sec, frames: fr },
|
|
383
|
+
rate,
|
|
384
|
+
dropFrame
|
|
385
|
+
);
|
|
386
|
+
} else {
|
|
387
|
+
if (parts.length > 4) {
|
|
388
|
+
throw new Error(`Too many timecode fields in "${input}"`);
|
|
389
|
+
}
|
|
390
|
+
const nums = parts.map((p) => p.length === 0 ? 0 : parseInt(p, 10));
|
|
391
|
+
if (nums.some((n) => isNaN(n))) {
|
|
392
|
+
throw new Error(`Invalid number in timecode: "${input}"`);
|
|
393
|
+
}
|
|
394
|
+
const padded = [0, 0, 0, 0];
|
|
395
|
+
const offset = 4 - nums.length;
|
|
396
|
+
for (let i = 0; i < nums.length; i++) {
|
|
397
|
+
padded[offset + i] = nums[i];
|
|
398
|
+
}
|
|
399
|
+
const [h, m, sec, fr] = padded;
|
|
400
|
+
parsedFrame = smpteToFrames(
|
|
401
|
+
{ hours: h, minutes: m, seconds: sec, frames: fr },
|
|
402
|
+
rate,
|
|
403
|
+
dropFrame
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
if (relativeSign !== null) {
|
|
407
|
+
return currentFrame + relativeSign * parsedFrame;
|
|
408
|
+
}
|
|
409
|
+
return parsedFrame;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// src/scene/rig.ts
|
|
413
|
+
function setRigWidget(rig, widgetName, value) {
|
|
414
|
+
const widget = rig.widgets[widgetName];
|
|
415
|
+
if (!widget) {
|
|
416
|
+
throw new Error(`Rig widget "${widgetName}" not found.`);
|
|
417
|
+
}
|
|
418
|
+
const updatedWidgets = { ...rig.widgets };
|
|
419
|
+
if (widget.type === "slider") {
|
|
420
|
+
if (typeof value !== "number") {
|
|
421
|
+
throw new Error(`Slider widget "${widgetName}" expects numeric value.`);
|
|
422
|
+
}
|
|
423
|
+
updatedWidgets[widgetName] = { ...widget, value };
|
|
424
|
+
} else if (widget.type === "popup") {
|
|
425
|
+
if (typeof value !== "string") {
|
|
426
|
+
throw new Error(`Popup widget "${widgetName}" expects string value.`);
|
|
427
|
+
}
|
|
428
|
+
updatedWidgets[widgetName] = { ...widget, selected: value };
|
|
429
|
+
} else if (widget.type === "toggle") {
|
|
430
|
+
if (typeof value !== "boolean") {
|
|
431
|
+
throw new Error(`Toggle widget "${widgetName}" expects boolean value.`);
|
|
432
|
+
}
|
|
433
|
+
updatedWidgets[widgetName] = { ...widget, enabled: value };
|
|
434
|
+
}
|
|
435
|
+
return { widgets: updatedWidgets };
|
|
436
|
+
}
|
|
437
|
+
function resolveWidgetValues(widget) {
|
|
438
|
+
if (widget.type === "popup") {
|
|
439
|
+
return widget.options[widget.selected] ?? {};
|
|
440
|
+
}
|
|
441
|
+
if (widget.type === "toggle") {
|
|
442
|
+
return widget.enabled ? widget.on : widget.off;
|
|
443
|
+
}
|
|
444
|
+
if (widget.type === "slider") {
|
|
445
|
+
const { snapshots, value } = widget;
|
|
446
|
+
if (!snapshots || snapshots.length === 0) return {};
|
|
447
|
+
const sorted = [...snapshots].sort((a, b) => a.at - b.at);
|
|
448
|
+
if (value <= sorted[0].at) {
|
|
449
|
+
return { ...sorted[0].values };
|
|
450
|
+
}
|
|
451
|
+
if (value >= sorted[sorted.length - 1].at) {
|
|
452
|
+
return { ...sorted[sorted.length - 1].values };
|
|
453
|
+
}
|
|
454
|
+
let idx = 0;
|
|
455
|
+
while (idx < sorted.length - 1 && sorted[idx + 1].at < value) {
|
|
456
|
+
idx++;
|
|
457
|
+
}
|
|
458
|
+
const s0 = sorted[idx];
|
|
459
|
+
const s1 = sorted[idx + 1];
|
|
460
|
+
const span = s1.at - s0.at;
|
|
461
|
+
const t = span > 0 ? (value - s0.at) / span : 0;
|
|
462
|
+
const result = {};
|
|
463
|
+
const allKeys = /* @__PURE__ */ new Set([...Object.keys(s0.values), ...Object.keys(s1.values)]);
|
|
464
|
+
for (const k of allKeys) {
|
|
465
|
+
const v0 = s0.values[k];
|
|
466
|
+
const v1 = s1.values[k];
|
|
467
|
+
if (typeof v0 === "number" && typeof v1 === "number") {
|
|
468
|
+
result[k] = v0 + (v1 - v0) * t;
|
|
469
|
+
} else if (v0 !== void 0 && v1 !== void 0) {
|
|
470
|
+
result[k] = t < 0.5 ? v0 : v1;
|
|
471
|
+
} else {
|
|
472
|
+
result[k] = v1 !== void 0 ? v1 : v0;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
return result;
|
|
476
|
+
}
|
|
477
|
+
return {};
|
|
478
|
+
}
|
|
479
|
+
function applyRig(rig, scene) {
|
|
480
|
+
const nextMedia = {};
|
|
481
|
+
if (scene.media) {
|
|
482
|
+
for (const [id, m] of Object.entries(scene.media)) {
|
|
483
|
+
nextMedia[id] = {
|
|
484
|
+
...m,
|
|
485
|
+
values: { ...m.values ?? {} }
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
const nextShots = scene.shots.map((shot) => {
|
|
490
|
+
if (!shot.run) return { ...shot };
|
|
491
|
+
return {
|
|
492
|
+
...shot,
|
|
493
|
+
run: shot.run.map((cmd) => {
|
|
494
|
+
if (cmd.type === "element" && "element" in cmd && cmd.element) {
|
|
495
|
+
return { ...cmd, element: { ...cmd.element } };
|
|
496
|
+
}
|
|
497
|
+
if (cmd.type === "update" && "props" in cmd && cmd.props) {
|
|
498
|
+
return { ...cmd, props: { ...cmd.props } };
|
|
499
|
+
}
|
|
500
|
+
return { ...cmd };
|
|
501
|
+
})
|
|
502
|
+
};
|
|
503
|
+
});
|
|
504
|
+
const resolvedAll = {};
|
|
505
|
+
for (const widget of Object.values(rig.widgets)) {
|
|
506
|
+
Object.assign(resolvedAll, resolveWidgetValues(widget));
|
|
507
|
+
}
|
|
508
|
+
for (const [targetKey, val] of Object.entries(resolvedAll)) {
|
|
509
|
+
if (targetKey.startsWith("media.")) {
|
|
510
|
+
const parts = targetKey.split(".");
|
|
511
|
+
const mediaId = parts[1];
|
|
512
|
+
const prop = parts.length > 3 && parts[2] === "values" ? parts[3] : parts[2];
|
|
513
|
+
if (nextMedia[mediaId]) {
|
|
514
|
+
nextMedia[mediaId].values[prop] = val;
|
|
515
|
+
}
|
|
516
|
+
} else if (targetKey.startsWith("element.")) {
|
|
517
|
+
const parts = targetKey.split(".");
|
|
518
|
+
const elId = parts[1];
|
|
519
|
+
const prop = parts[2];
|
|
520
|
+
for (const shot of nextShots) {
|
|
521
|
+
if (shot.run) {
|
|
522
|
+
for (const cmd of shot.run) {
|
|
523
|
+
if (cmd.type === "element" && "element" in cmd && cmd.element?.id === elId) {
|
|
524
|
+
cmd.element[prop] = val;
|
|
525
|
+
} else if (cmd.type === "update" && "id" in cmd && cmd.id === elId) {
|
|
526
|
+
cmd.props[prop] = val;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
} else {
|
|
532
|
+
const uniformName = targetKey.replace(/^uniforms\./, "");
|
|
533
|
+
for (const m of Object.values(nextMedia)) {
|
|
534
|
+
m.values[uniformName] = val;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
return {
|
|
539
|
+
...scene,
|
|
540
|
+
media: Object.keys(nextMedia).length > 0 ? nextMedia : scene.media,
|
|
541
|
+
shots: nextShots,
|
|
542
|
+
rig
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// src/scene/replicator.ts
|
|
547
|
+
function expandReplicator(prototype, config) {
|
|
548
|
+
const count = Math.max(0, Math.floor(config.count));
|
|
549
|
+
if (count === 0) return [];
|
|
550
|
+
const originX = config.origin ? config.origin[0] : prototype.x;
|
|
551
|
+
const originY = config.origin ? config.origin[1] : prototype.y;
|
|
552
|
+
const elements = [];
|
|
553
|
+
for (let i = 0; i < count; i++) {
|
|
554
|
+
const el = {
|
|
555
|
+
...prototype,
|
|
556
|
+
id: `${prototype.id}_${i}`
|
|
557
|
+
};
|
|
558
|
+
let px = originX;
|
|
559
|
+
let py = originY;
|
|
560
|
+
switch (config.pattern) {
|
|
561
|
+
case "grid": {
|
|
562
|
+
const cols = config.columns ?? Math.ceil(Math.sqrt(count));
|
|
563
|
+
const colSpacing = config.columnSpacing ?? prototype.width + 20;
|
|
564
|
+
const rowSpacing = config.rowSpacing ?? prototype.height + 20;
|
|
565
|
+
const col = i % cols;
|
|
566
|
+
const row = Math.floor(i / cols);
|
|
567
|
+
px = originX + col * colSpacing;
|
|
568
|
+
py = originY + row * rowSpacing;
|
|
569
|
+
break;
|
|
570
|
+
}
|
|
571
|
+
case "line": {
|
|
572
|
+
if (config.startPoint && config.endPoint) {
|
|
573
|
+
const t = count > 1 ? i / (count - 1) : 0;
|
|
574
|
+
px = config.startPoint[0] + (config.endPoint[0] - config.startPoint[0]) * t;
|
|
575
|
+
py = config.startPoint[1] + (config.endPoint[1] - config.startPoint[1]) * t;
|
|
576
|
+
} else {
|
|
577
|
+
const spacing = config.spacing ?? prototype.width + 20;
|
|
578
|
+
px = originX + i * spacing;
|
|
579
|
+
py = originY;
|
|
580
|
+
}
|
|
581
|
+
break;
|
|
582
|
+
}
|
|
583
|
+
case "circle": {
|
|
584
|
+
const radius = config.radius ?? 150;
|
|
585
|
+
const angleStep = 2 * Math.PI / count;
|
|
586
|
+
const angle = (config.startAngle ?? 0) + i * angleStep;
|
|
587
|
+
px = originX + radius * Math.cos(angle);
|
|
588
|
+
py = originY + radius * Math.sin(angle);
|
|
589
|
+
break;
|
|
590
|
+
}
|
|
591
|
+
case "spiral": {
|
|
592
|
+
const baseRadius = config.radius ?? 40;
|
|
593
|
+
const tightness = config.spiralTightness ?? 12;
|
|
594
|
+
const angleStep = Math.PI / 4;
|
|
595
|
+
const angle = (config.startAngle ?? 0) + i * angleStep;
|
|
596
|
+
const r = baseRadius + tightness * i;
|
|
597
|
+
px = originX + r * Math.cos(angle);
|
|
598
|
+
py = originY + r * Math.sin(angle);
|
|
599
|
+
break;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
el.x = px;
|
|
603
|
+
el.y = py;
|
|
604
|
+
if (config.sequence && config.sequence.offset !== void 0) {
|
|
605
|
+
const { property, offset } = config.sequence;
|
|
606
|
+
if (property === "opacity") {
|
|
607
|
+
const base = prototype.opacity ?? 1;
|
|
608
|
+
el.opacity = Math.max(0, Math.min(1, base + i * offset));
|
|
609
|
+
} else if (property === "rotation") {
|
|
610
|
+
const base = prototype.rotation ?? 0;
|
|
611
|
+
el.rotation = base + i * offset;
|
|
612
|
+
} else if (property === "scale") {
|
|
613
|
+
const factor = 1 + i * offset;
|
|
614
|
+
el.width = Math.max(1, prototype.width * factor);
|
|
615
|
+
el.height = Math.max(1, prototype.height * factor);
|
|
616
|
+
} else if (property === "x") {
|
|
617
|
+
el.x += i * offset;
|
|
618
|
+
} else if (property === "y") {
|
|
619
|
+
el.y += i * offset;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
elements.push(el);
|
|
623
|
+
}
|
|
624
|
+
return elements;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
// src/scene/motionpath.ts
|
|
628
|
+
function toVec3(p) {
|
|
629
|
+
if (!p) return [0, 0, 0];
|
|
630
|
+
return [p[0], p[1], p[2] ?? 0];
|
|
631
|
+
}
|
|
632
|
+
function cubicPos(p0, p1, p2, p3, t) {
|
|
633
|
+
const u = 1 - t;
|
|
634
|
+
const a = u * u * u;
|
|
635
|
+
const b = 3 * u * u * t;
|
|
636
|
+
const c = 3 * u * t * t;
|
|
637
|
+
const d = t * t * t;
|
|
638
|
+
return [
|
|
639
|
+
a * p0[0] + b * p1[0] + c * p2[0] + d * p3[0],
|
|
640
|
+
a * p0[1] + b * p1[1] + c * p2[1] + d * p3[1],
|
|
641
|
+
a * p0[2] + b * p1[2] + c * p2[2] + d * p3[2]
|
|
642
|
+
];
|
|
643
|
+
}
|
|
644
|
+
function cubicVel(p0, p1, p2, p3, t) {
|
|
645
|
+
const u = 1 - t;
|
|
646
|
+
const a = -3 * u * u;
|
|
647
|
+
const b = 3 * u * u - 6 * u * t;
|
|
648
|
+
const c = 6 * u * t - 3 * t * t;
|
|
649
|
+
const d = 3 * t * t;
|
|
650
|
+
return [
|
|
651
|
+
a * p0[0] + b * p1[0] + c * p2[0] + d * p3[0],
|
|
652
|
+
a * p0[1] + b * p1[1] + c * p2[1] + d * p3[1],
|
|
653
|
+
a * p0[2] + b * p1[2] + c * p2[2] + d * p3[2]
|
|
654
|
+
];
|
|
655
|
+
}
|
|
656
|
+
function cubicAcc(p0, p1, p2, p3, t) {
|
|
657
|
+
const u = 1 - t;
|
|
658
|
+
const a = 6 * u;
|
|
659
|
+
const b = -12 * u + 6 * t;
|
|
660
|
+
const c = 6 * u - 12 * t;
|
|
661
|
+
const d = 6 * t;
|
|
662
|
+
return [
|
|
663
|
+
a * p0[0] + b * p1[0] + c * p2[0] + d * p3[0],
|
|
664
|
+
a * p0[1] + b * p1[1] + c * p2[1] + d * p3[1],
|
|
665
|
+
a * p0[2] + b * p1[2] + c * p2[2] + d * p3[2]
|
|
666
|
+
];
|
|
667
|
+
}
|
|
668
|
+
function dist3(a, b) {
|
|
669
|
+
const dx = b[0] - a[0];
|
|
670
|
+
const dy = b[1] - a[1];
|
|
671
|
+
const dz = b[2] - a[2];
|
|
672
|
+
return Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
673
|
+
}
|
|
674
|
+
function createMotionPath(waypoints, options) {
|
|
675
|
+
if (waypoints.length < 2) {
|
|
676
|
+
throw new Error("SpatialMotionPath requires at least 2 waypoints.");
|
|
677
|
+
}
|
|
678
|
+
const closed = options?.closed ?? false;
|
|
679
|
+
const segmentCount = closed ? waypoints.length : waypoints.length - 1;
|
|
680
|
+
const segments = [];
|
|
681
|
+
let totalLength = 0;
|
|
682
|
+
for (let i = 0; i < segmentCount; i++) {
|
|
683
|
+
const nextIdx = (i + 1) % waypoints.length;
|
|
684
|
+
const w0 = waypoints[i];
|
|
685
|
+
const w1 = waypoints[nextIdx];
|
|
686
|
+
const p0 = toVec3(w0.point);
|
|
687
|
+
const outT = toVec3(w0.outTangent);
|
|
688
|
+
const p1 = [p0[0] + outT[0], p0[1] + outT[1], p0[2] + outT[2]];
|
|
689
|
+
const p3 = toVec3(w1.point);
|
|
690
|
+
const inT = toVec3(w1.inTangent);
|
|
691
|
+
const p2 = [p3[0] + inT[0], p3[1] + inT[1], p3[2] + inT[2]];
|
|
692
|
+
const chord = dist3(p0, p1) + dist3(p1, p2) + dist3(p2, p3);
|
|
693
|
+
const stepCount = Math.max(16, Math.min(256, Math.ceil(chord / (options?.resolution ?? 2))));
|
|
694
|
+
const samples = [];
|
|
695
|
+
let segLen = 0;
|
|
696
|
+
let prev = p0;
|
|
697
|
+
samples.push({ t: 0, s: 0, pos: p0 });
|
|
698
|
+
for (let k = 1; k <= stepCount; k++) {
|
|
699
|
+
const t = k / stepCount;
|
|
700
|
+
const pos = cubicPos(p0, p1, p2, p3, t);
|
|
701
|
+
segLen += dist3(prev, pos);
|
|
702
|
+
samples.push({ t, s: segLen, pos });
|
|
703
|
+
prev = pos;
|
|
704
|
+
}
|
|
705
|
+
segments.push({
|
|
706
|
+
p0,
|
|
707
|
+
p1,
|
|
708
|
+
p2,
|
|
709
|
+
p3,
|
|
710
|
+
length: segLen,
|
|
711
|
+
samples
|
|
712
|
+
});
|
|
713
|
+
totalLength += segLen;
|
|
714
|
+
}
|
|
715
|
+
return {
|
|
716
|
+
waypoints,
|
|
717
|
+
closed,
|
|
718
|
+
totalLength,
|
|
719
|
+
segments
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
function sampleMotionPath(path, distanceFraction) {
|
|
723
|
+
const clampedFraction = Math.max(0, Math.min(1, distanceFraction));
|
|
724
|
+
const targetDistance = clampedFraction * path.totalLength;
|
|
725
|
+
if (path.totalLength === 0) {
|
|
726
|
+
const p = toVec3(path.waypoints[0].point);
|
|
727
|
+
return {
|
|
728
|
+
position: p,
|
|
729
|
+
tangent: [1, 0, 0],
|
|
730
|
+
heading: 0,
|
|
731
|
+
normal: [0, 1, 0],
|
|
732
|
+
curvature: 0,
|
|
733
|
+
distance: 0
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
let accumulated = 0;
|
|
737
|
+
let targetSeg = path.segments[0];
|
|
738
|
+
let segDistance = targetDistance;
|
|
739
|
+
for (const seg of path.segments) {
|
|
740
|
+
if (accumulated + seg.length >= targetDistance || seg === path.segments[path.segments.length - 1]) {
|
|
741
|
+
targetSeg = seg;
|
|
742
|
+
segDistance = Math.max(0, Math.min(seg.length, targetDistance - accumulated));
|
|
743
|
+
break;
|
|
744
|
+
}
|
|
745
|
+
accumulated += seg.length;
|
|
746
|
+
}
|
|
747
|
+
const samples = targetSeg.samples;
|
|
748
|
+
let low = 0;
|
|
749
|
+
let high = samples.length - 1;
|
|
750
|
+
while (low <= high) {
|
|
751
|
+
const mid = low + high >> 1;
|
|
752
|
+
if (samples[mid].s < segDistance) {
|
|
753
|
+
low = mid + 1;
|
|
754
|
+
} else {
|
|
755
|
+
high = mid - 1;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
const idx = Math.max(0, Math.min(samples.length - 2, high));
|
|
759
|
+
const s0 = samples[idx];
|
|
760
|
+
const s1 = samples[idx + 1];
|
|
761
|
+
const segRange = s1.s - s0.s;
|
|
762
|
+
const factor = segRange > 1e-7 ? (segDistance - s0.s) / segRange : 0;
|
|
763
|
+
const t = s0.t + factor * (s1.t - s0.t);
|
|
764
|
+
const pos = cubicPos(targetSeg.p0, targetSeg.p1, targetSeg.p2, targetSeg.p3, t);
|
|
765
|
+
const vel = cubicVel(targetSeg.p0, targetSeg.p1, targetSeg.p2, targetSeg.p3, t);
|
|
766
|
+
const acc = cubicAcc(targetSeg.p0, targetSeg.p1, targetSeg.p2, targetSeg.p3, t);
|
|
767
|
+
const speed = Math.sqrt(vel[0] * vel[0] + vel[1] * vel[1] + vel[2] * vel[2]);
|
|
768
|
+
const safeSpeed = speed > 1e-6 ? speed : 1;
|
|
769
|
+
const tangent = [vel[0] / safeSpeed, vel[1] / safeSpeed, vel[2] / safeSpeed];
|
|
770
|
+
const heading = Math.atan2(tangent[1], tangent[0]);
|
|
771
|
+
const normal = [-tangent[1], tangent[0], 0];
|
|
772
|
+
const cx = vel[1] * acc[2] - vel[2] * acc[1];
|
|
773
|
+
const cy = vel[2] * acc[0] - vel[0] * acc[2];
|
|
774
|
+
const cz = vel[0] * acc[1] - vel[1] * acc[0];
|
|
775
|
+
const crossMag = Math.sqrt(cx * cx + cy * cy + cz * cz);
|
|
776
|
+
const curvature = crossMag / (safeSpeed * safeSpeed * safeSpeed);
|
|
777
|
+
return {
|
|
778
|
+
position: pos,
|
|
779
|
+
tangent,
|
|
780
|
+
heading,
|
|
781
|
+
normal,
|
|
782
|
+
curvature,
|
|
783
|
+
distance: targetDistance
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
function sampleMotionPathAtTime(path, time, duration, ease) {
|
|
787
|
+
const distanceFraction = evaluateKeyframeSegment(
|
|
788
|
+
0,
|
|
789
|
+
1,
|
|
790
|
+
duration,
|
|
791
|
+
time,
|
|
792
|
+
ease?.easeOut,
|
|
793
|
+
ease?.easeIn
|
|
794
|
+
);
|
|
795
|
+
return sampleMotionPath(path, distanceFraction);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// src/agent/tools.ts
|
|
799
|
+
var FLAIR_AGENT_TOOLS = [
|
|
800
|
+
{
|
|
801
|
+
name: "get_scene",
|
|
802
|
+
description: "Returns the current scene JSON and any static validation issues.",
|
|
803
|
+
input_schema: {
|
|
804
|
+
type: "object",
|
|
805
|
+
properties: {},
|
|
806
|
+
additionalProperties: false
|
|
807
|
+
}
|
|
808
|
+
},
|
|
809
|
+
{
|
|
810
|
+
name: "write_scene",
|
|
811
|
+
description: "Overwrites the entire scene with a new specification and validates it.",
|
|
812
|
+
input_schema: {
|
|
813
|
+
type: "object",
|
|
814
|
+
properties: {
|
|
815
|
+
scene: {
|
|
816
|
+
type: "object",
|
|
817
|
+
description: "Full Scene object with name, stage, media, and shots."
|
|
818
|
+
}
|
|
819
|
+
},
|
|
820
|
+
required: ["scene"],
|
|
821
|
+
additionalProperties: false
|
|
822
|
+
}
|
|
823
|
+
},
|
|
824
|
+
{
|
|
825
|
+
name: "update_shot",
|
|
826
|
+
description: "Updates fields of a specific shot (1-based index) without rewriting the whole scene.",
|
|
827
|
+
input_schema: {
|
|
828
|
+
type: "object",
|
|
829
|
+
properties: {
|
|
830
|
+
shot: { type: "integer", minimum: 1, description: "1-based index of the shot to update." },
|
|
831
|
+
set: { type: "object", description: "Fields to set or replace on this shot." },
|
|
832
|
+
remove: {
|
|
833
|
+
type: "array",
|
|
834
|
+
items: { type: "string" },
|
|
835
|
+
description: "Field names to delete from this shot."
|
|
836
|
+
}
|
|
837
|
+
},
|
|
838
|
+
required: ["shot"],
|
|
839
|
+
additionalProperties: false
|
|
840
|
+
}
|
|
841
|
+
},
|
|
842
|
+
{
|
|
843
|
+
name: "insert_shots",
|
|
844
|
+
description: "Inserts one or more shots at a given position.",
|
|
845
|
+
input_schema: {
|
|
846
|
+
type: "object",
|
|
847
|
+
properties: {
|
|
848
|
+
after: {
|
|
849
|
+
type: "integer",
|
|
850
|
+
minimum: 0,
|
|
851
|
+
description: "0 to insert at the beginning, or 1-based index of shot to insert after."
|
|
852
|
+
},
|
|
853
|
+
shots: {
|
|
854
|
+
type: "array",
|
|
855
|
+
items: { type: "object" },
|
|
856
|
+
description: "Array of Shot objects to insert."
|
|
857
|
+
}
|
|
858
|
+
},
|
|
859
|
+
required: ["after", "shots"],
|
|
860
|
+
additionalProperties: false
|
|
861
|
+
}
|
|
862
|
+
},
|
|
863
|
+
{
|
|
864
|
+
name: "delete_shots",
|
|
865
|
+
description: "Deletes one or more shots by 1-based index.",
|
|
866
|
+
input_schema: {
|
|
867
|
+
type: "object",
|
|
868
|
+
properties: {
|
|
869
|
+
shots: {
|
|
870
|
+
type: "array",
|
|
871
|
+
items: { type: "integer", minimum: 1 },
|
|
872
|
+
description: "1-based shot numbers to delete."
|
|
873
|
+
}
|
|
874
|
+
},
|
|
875
|
+
required: ["shots"],
|
|
876
|
+
additionalProperties: false
|
|
877
|
+
}
|
|
878
|
+
},
|
|
879
|
+
{
|
|
880
|
+
name: "set_media",
|
|
881
|
+
description: "Adds, updates, or removes embedded media (e.g. bespoke WebGL shaders) by ID.",
|
|
882
|
+
input_schema: {
|
|
883
|
+
type: "object",
|
|
884
|
+
properties: {
|
|
885
|
+
id: { type: "string", description: "Media identifier." },
|
|
886
|
+
media: {
|
|
887
|
+
type: ["object", "null"],
|
|
888
|
+
description: 'SceneMedia object (e.g. { type: "shader", source: "..." }), or null to remove.'
|
|
889
|
+
}
|
|
890
|
+
},
|
|
891
|
+
required: ["id", "media"],
|
|
892
|
+
additionalProperties: false
|
|
893
|
+
}
|
|
894
|
+
},
|
|
895
|
+
{
|
|
896
|
+
name: "preview_scene",
|
|
897
|
+
description: "Simulates the scene fast and off-screen, returning timing, layout checks, and review report.",
|
|
898
|
+
input_schema: {
|
|
899
|
+
type: "object",
|
|
900
|
+
properties: {},
|
|
901
|
+
additionalProperties: false
|
|
902
|
+
}
|
|
903
|
+
},
|
|
904
|
+
{
|
|
905
|
+
name: "set_rig_widget",
|
|
906
|
+
description: "Adjusts a high-level creative rig widget (slider, popup, or toggle) and propagates parameters to shaders and elements.",
|
|
907
|
+
input_schema: {
|
|
908
|
+
type: "object",
|
|
909
|
+
properties: {
|
|
910
|
+
name: { type: "string", description: "Rig widget identifier/name." },
|
|
911
|
+
value: {
|
|
912
|
+
type: ["number", "string", "boolean"],
|
|
913
|
+
description: "New value (number for slider, string for popup, boolean for toggle)."
|
|
914
|
+
}
|
|
915
|
+
},
|
|
916
|
+
required: ["name", "value"],
|
|
917
|
+
additionalProperties: false
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
];
|
|
921
|
+
async function applySceneTool(scene, toolName, input = {}) {
|
|
922
|
+
switch (toolName) {
|
|
923
|
+
case "get_scene": {
|
|
924
|
+
return { ok: true, scene, issues: validateScene(scene) };
|
|
925
|
+
}
|
|
926
|
+
case "write_scene": {
|
|
927
|
+
if (!input.scene || typeof input.scene !== "object") {
|
|
928
|
+
return { ok: false, error: "Expected scene object in arguments." };
|
|
929
|
+
}
|
|
930
|
+
const newScene = input.scene;
|
|
931
|
+
const issues = validateScene(newScene);
|
|
932
|
+
return { ok: true, scene: newScene, issues };
|
|
933
|
+
}
|
|
934
|
+
case "update_shot": {
|
|
935
|
+
const idx = input.shot - 1;
|
|
936
|
+
if (idx < 0 || idx >= scene.shots.length) {
|
|
937
|
+
return { ok: false, error: `Invalid shot number ${input.shot}; scene has ${scene.shots.length} shots.` };
|
|
938
|
+
}
|
|
939
|
+
const existing = scene.shots[idx];
|
|
940
|
+
const updated = { ...existing, ...input.set || {} };
|
|
941
|
+
if (Array.isArray(input.remove)) {
|
|
942
|
+
for (const k of input.remove) {
|
|
943
|
+
delete updated[k];
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
const shots = [...scene.shots];
|
|
947
|
+
shots[idx] = updated;
|
|
948
|
+
const newScene = { ...scene, shots };
|
|
949
|
+
return { ok: true, scene: newScene, issues: validateScene(newScene) };
|
|
950
|
+
}
|
|
951
|
+
case "insert_shots": {
|
|
952
|
+
const after = input.after ?? scene.shots.length;
|
|
953
|
+
if (after < 0 || after > scene.shots.length) {
|
|
954
|
+
return { ok: false, error: `Invalid after index ${after}.` };
|
|
955
|
+
}
|
|
956
|
+
if (!Array.isArray(input.shots) || input.shots.length === 0) {
|
|
957
|
+
return { ok: false, error: "Expected non-empty shots array to insert." };
|
|
958
|
+
}
|
|
959
|
+
const shots = [...scene.shots.slice(0, after), ...input.shots, ...scene.shots.slice(after)];
|
|
960
|
+
const newScene = { ...scene, shots };
|
|
961
|
+
return { ok: true, scene: newScene, issues: validateScene(newScene) };
|
|
962
|
+
}
|
|
963
|
+
case "delete_shots": {
|
|
964
|
+
if (!Array.isArray(input.shots) || input.shots.length === 0) {
|
|
965
|
+
return { ok: false, error: "Expected non-empty shots array to delete." };
|
|
966
|
+
}
|
|
967
|
+
const toDelete = new Set(input.shots.map((n) => n - 1));
|
|
968
|
+
const shots = scene.shots.filter((_, i) => !toDelete.has(i));
|
|
969
|
+
if (shots.length === 0) {
|
|
970
|
+
return { ok: false, error: "Cannot delete all shots in scene." };
|
|
971
|
+
}
|
|
972
|
+
const newScene = { ...scene, shots };
|
|
973
|
+
return { ok: true, scene: newScene, issues: validateScene(newScene) };
|
|
974
|
+
}
|
|
975
|
+
case "set_media": {
|
|
976
|
+
if (!input.id || typeof input.id !== "string") {
|
|
977
|
+
return { ok: false, error: "Expected string media id." };
|
|
978
|
+
}
|
|
979
|
+
const media = { ...scene.media || {} };
|
|
980
|
+
if (input.media === null) {
|
|
981
|
+
delete media[input.id];
|
|
982
|
+
} else {
|
|
983
|
+
media[input.id] = input.media;
|
|
984
|
+
}
|
|
985
|
+
const newScene = { ...scene, media };
|
|
986
|
+
return { ok: true, scene: newScene, issues: validateScene(newScene) };
|
|
987
|
+
}
|
|
988
|
+
case "preview_scene": {
|
|
989
|
+
const { review } = await simulateScene(scene);
|
|
990
|
+
return { ok: true, scene, issues: validateScene(scene), review };
|
|
991
|
+
}
|
|
992
|
+
case "set_rig_widget": {
|
|
993
|
+
if (!scene.rig || !scene.rig.widgets || !scene.rig.widgets[input.name]) {
|
|
994
|
+
return { ok: false, error: `Scene does not have a rig widget named "${input.name}".` };
|
|
995
|
+
}
|
|
996
|
+
try {
|
|
997
|
+
const updatedRig = setRigWidget(scene.rig, input.name, input.value);
|
|
998
|
+
const newScene = applyRig(updatedRig, scene);
|
|
999
|
+
return { ok: true, scene: newScene, issues: validateScene(newScene) };
|
|
1000
|
+
} catch (err) {
|
|
1001
|
+
return { ok: false, error: err.message || String(err) };
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
default:
|
|
1005
|
+
return { ok: false, error: `Unknown tool name: ${toolName}` };
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
// src/agent/batch.ts
|
|
1010
|
+
function getByPath(obj, path) {
|
|
1011
|
+
if (!obj || typeof obj !== "object") return void 0;
|
|
1012
|
+
const parts = path.replace(/\[(\w+)\]/g, ".$1").split(".");
|
|
1013
|
+
let curr = obj;
|
|
1014
|
+
for (const part of parts) {
|
|
1015
|
+
if (curr === void 0 || curr === null) return void 0;
|
|
1016
|
+
curr = curr[part];
|
|
1017
|
+
}
|
|
1018
|
+
return curr;
|
|
1019
|
+
}
|
|
1020
|
+
function substituteVariables(value, stepResults) {
|
|
1021
|
+
if (typeof value === "string") {
|
|
1022
|
+
const match = /^\$(\d+)\.(.+)$/.exec(value);
|
|
1023
|
+
if (match) {
|
|
1024
|
+
const stepIdx = parseInt(match[1], 10);
|
|
1025
|
+
const path = match[2];
|
|
1026
|
+
const prevResult = stepResults.get(stepIdx);
|
|
1027
|
+
if (prevResult) {
|
|
1028
|
+
const resolved = getByPath(prevResult, path);
|
|
1029
|
+
if (resolved !== void 0) {
|
|
1030
|
+
return resolved;
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
return value.replace(/\$(\d+)\.([a-zA-Z0-9_.[\]]+)/g, (fullMatch, stepStr, path) => {
|
|
1035
|
+
const stepIdx = parseInt(stepStr, 10);
|
|
1036
|
+
const prevResult = stepResults.get(stepIdx);
|
|
1037
|
+
if (prevResult) {
|
|
1038
|
+
const resolved = getByPath(prevResult, path);
|
|
1039
|
+
if (resolved !== void 0) {
|
|
1040
|
+
return String(resolved);
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
return fullMatch;
|
|
1044
|
+
});
|
|
1045
|
+
}
|
|
1046
|
+
if (Array.isArray(value)) {
|
|
1047
|
+
return value.map((item) => substituteVariables(item, stepResults));
|
|
1048
|
+
}
|
|
1049
|
+
if (value !== null && typeof value === "object") {
|
|
1050
|
+
const copy = {};
|
|
1051
|
+
for (const [k, v] of Object.entries(value)) {
|
|
1052
|
+
copy[k] = substituteVariables(v, stepResults);
|
|
1053
|
+
}
|
|
1054
|
+
return copy;
|
|
1055
|
+
}
|
|
1056
|
+
return value;
|
|
1057
|
+
}
|
|
1058
|
+
async function applySceneBatch(scene, commands) {
|
|
1059
|
+
let currentScene = scene;
|
|
1060
|
+
const stepResults = /* @__PURE__ */ new Map();
|
|
1061
|
+
const executedSteps = [];
|
|
1062
|
+
for (let i = 0; i < commands.length; i++) {
|
|
1063
|
+
const stepNumber = i + 1;
|
|
1064
|
+
const cmd = commands[i];
|
|
1065
|
+
const resolvedParams = substituteVariables(cmd.params || {}, stepResults);
|
|
1066
|
+
if (cmd.tool === "batch_scene_tool") {
|
|
1067
|
+
return {
|
|
1068
|
+
ok: false,
|
|
1069
|
+
scene,
|
|
1070
|
+
// Rollback
|
|
1071
|
+
steps: executedSteps,
|
|
1072
|
+
error: `Nested batch_scene_tool is not allowed at step ${stepNumber}.`,
|
|
1073
|
+
failedStep: stepNumber
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
const stepResult = await applySceneTool(currentScene, cmd.tool, resolvedParams);
|
|
1077
|
+
stepResults.set(stepNumber, stepResult);
|
|
1078
|
+
executedSteps.push({
|
|
1079
|
+
step: stepNumber,
|
|
1080
|
+
tool: cmd.tool,
|
|
1081
|
+
result: stepResult
|
|
1082
|
+
});
|
|
1083
|
+
if (!stepResult.ok) {
|
|
1084
|
+
return {
|
|
1085
|
+
ok: false,
|
|
1086
|
+
scene,
|
|
1087
|
+
// Rollback to initial
|
|
1088
|
+
steps: executedSteps,
|
|
1089
|
+
error: `Step ${stepNumber} (${cmd.tool}) failed: ${stepResult.error}`,
|
|
1090
|
+
failedStep: stepNumber
|
|
1091
|
+
};
|
|
1092
|
+
}
|
|
1093
|
+
currentScene = stepResult.scene;
|
|
1094
|
+
}
|
|
1095
|
+
return {
|
|
1096
|
+
ok: true,
|
|
1097
|
+
scene: currentScene,
|
|
1098
|
+
steps: executedSteps
|
|
1099
|
+
};
|
|
1100
|
+
}
|
|
1101
|
+
export {
|
|
1102
|
+
ALL_BLEND_MODES,
|
|
1103
|
+
ALL_TRANSITION_TYPES,
|
|
1104
|
+
BUILTIN_UNIFORMS,
|
|
1105
|
+
CAMERA_MS,
|
|
1106
|
+
COMMON_FRAME_RATES,
|
|
1107
|
+
DEFAULT_STAGE,
|
|
1108
|
+
EASES,
|
|
1109
|
+
EASY_EASE,
|
|
1110
|
+
EFFECT_FADE_MS,
|
|
1111
|
+
EFFECT_TYPES,
|
|
1112
|
+
FLAIR_AGENT_TOOLS,
|
|
1113
|
+
ImageLoadError,
|
|
1114
|
+
ManualClock,
|
|
1115
|
+
MemorySceneBoard,
|
|
1116
|
+
NO_CHECK,
|
|
1117
|
+
SMALL_TEXT_PX,
|
|
1118
|
+
STEP_MS,
|
|
1119
|
+
SVG_EDGE,
|
|
1120
|
+
SVG_PICTURE,
|
|
1121
|
+
ShaderCompileError,
|
|
1122
|
+
ShaderFrames,
|
|
1123
|
+
ShaderRenderer,
|
|
1124
|
+
SvgPlayer,
|
|
1125
|
+
TextureCache,
|
|
1126
|
+
ThumbnailPool,
|
|
1127
|
+
VERTEX_SOURCE,
|
|
1128
|
+
applyAscCdl,
|
|
1129
|
+
applyLiftGammaGain,
|
|
1130
|
+
applyRig,
|
|
1131
|
+
applySceneBatch,
|
|
1132
|
+
applySceneTool,
|
|
1133
|
+
applyTextureSettings,
|
|
1134
|
+
applyVibrance,
|
|
1135
|
+
areaOf,
|
|
1136
|
+
between,
|
|
1137
|
+
bez1d,
|
|
1138
|
+
bez1dDerivative,
|
|
1139
|
+
blendRgba,
|
|
1140
|
+
blendTransitionPixel,
|
|
1141
|
+
boardHeight,
|
|
1142
|
+
boundsOf,
|
|
1143
|
+
calculateFramingRect,
|
|
1144
|
+
calculateShotTimings,
|
|
1145
|
+
centreOf,
|
|
1146
|
+
checkControlValue,
|
|
1147
|
+
checkShot,
|
|
1148
|
+
coerceControlValue,
|
|
1149
|
+
compileShader,
|
|
1150
|
+
componentCount,
|
|
1151
|
+
controlValue,
|
|
1152
|
+
convertColorSpace,
|
|
1153
|
+
createContext,
|
|
1154
|
+
createFrameRate,
|
|
1155
|
+
createLottieEase,
|
|
1156
|
+
createMotionPath,
|
|
1157
|
+
defaultDataKey,
|
|
1158
|
+
describeValue,
|
|
1159
|
+
drawFrame,
|
|
1160
|
+
easeInOutCubic,
|
|
1161
|
+
easeProgress,
|
|
1162
|
+
effectGone,
|
|
1163
|
+
effectOpacity,
|
|
1164
|
+
evaluateBehavior,
|
|
1165
|
+
evaluateKeyframeSegment,
|
|
1166
|
+
expandReplicator,
|
|
1167
|
+
exportCmx3600Edl,
|
|
1168
|
+
exportFcp7Xml,
|
|
1169
|
+
exportLottie,
|
|
1170
|
+
exportMotn,
|
|
1171
|
+
formatCompileError,
|
|
1172
|
+
formatTimecode,
|
|
1173
|
+
fractalWiggle,
|
|
1174
|
+
frame,
|
|
1175
|
+
framesToSMPTE,
|
|
1176
|
+
framesToTimecode,
|
|
1177
|
+
getColorGradingGLSL,
|
|
1178
|
+
getDropFrameCount,
|
|
1179
|
+
getGlslBlendFunction,
|
|
1180
|
+
getOklabGLSL,
|
|
1181
|
+
getTimecodeBase,
|
|
1182
|
+
getTransitionGLSL,
|
|
1183
|
+
getWhiteBalanceGains,
|
|
1184
|
+
hash64,
|
|
1185
|
+
hexToRgb,
|
|
1186
|
+
holdTime,
|
|
1187
|
+
humanise,
|
|
1188
|
+
imageUrls,
|
|
1189
|
+
interpolateOklab,
|
|
1190
|
+
interpolateOklch,
|
|
1191
|
+
isDeleteCommand,
|
|
1192
|
+
isElementCommand,
|
|
1193
|
+
isImageUrl,
|
|
1194
|
+
isSvgUrl,
|
|
1195
|
+
isUpdateCommand,
|
|
1196
|
+
jitter,
|
|
1197
|
+
lerpAttr,
|
|
1198
|
+
lerpData,
|
|
1199
|
+
linearRgbToSrgb,
|
|
1200
|
+
linearSrgbToOklab,
|
|
1201
|
+
linearToSrgb,
|
|
1202
|
+
loadImage,
|
|
1203
|
+
loopTime,
|
|
1204
|
+
mixSeeds,
|
|
1205
|
+
noise1d,
|
|
1206
|
+
noise3d,
|
|
1207
|
+
oklabToLinearSrgb,
|
|
1208
|
+
oklabToOklch,
|
|
1209
|
+
oklabToSrgb,
|
|
1210
|
+
oklchToOklab,
|
|
1211
|
+
overlapArea,
|
|
1212
|
+
pageHidden,
|
|
1213
|
+
parseCompileLog,
|
|
1214
|
+
parseControls,
|
|
1215
|
+
parseSvg,
|
|
1216
|
+
parseTimecode,
|
|
1217
|
+
pingPongTime,
|
|
1218
|
+
playScene,
|
|
1219
|
+
preloadImages,
|
|
1220
|
+
quinticFade,
|
|
1221
|
+
realClock,
|
|
1222
|
+
recordGif,
|
|
1223
|
+
registerThumbnail,
|
|
1224
|
+
releaseImages,
|
|
1225
|
+
resetGlCaches,
|
|
1226
|
+
reviewScene,
|
|
1227
|
+
rgbToHex,
|
|
1228
|
+
sampleMotionPath,
|
|
1229
|
+
sampleMotionPathAtTime,
|
|
1230
|
+
sampleTransition,
|
|
1231
|
+
setRigWidget,
|
|
1232
|
+
sharedThumbnailPool,
|
|
1233
|
+
simulateScene,
|
|
1234
|
+
sizeSvg,
|
|
1235
|
+
smpteToFrames,
|
|
1236
|
+
solveBezierU,
|
|
1237
|
+
speedRampTime,
|
|
1238
|
+
srgbToLinear,
|
|
1239
|
+
srgbToLinearRgb,
|
|
1240
|
+
srgbToOklab,
|
|
1241
|
+
standaloneHtml,
|
|
1242
|
+
standaloneSceneHtml,
|
|
1243
|
+
stepData,
|
|
1244
|
+
stepProgress,
|
|
1245
|
+
stepStart,
|
|
1246
|
+
strobeTime,
|
|
1247
|
+
substituteVariables,
|
|
1248
|
+
supportsDropFrame,
|
|
1249
|
+
svgDuration,
|
|
1250
|
+
svgFrame,
|
|
1251
|
+
svgMotionIssues,
|
|
1252
|
+
svgOpeningTag,
|
|
1253
|
+
svgView,
|
|
1254
|
+
timecodeToFrames,
|
|
1255
|
+
titleBox,
|
|
1256
|
+
touchedIds,
|
|
1257
|
+
unitFloat,
|
|
1258
|
+
validateScene,
|
|
1259
|
+
viewAt,
|
|
1260
|
+
visibleRect,
|
|
1261
|
+
wrapSource
|
|
1262
|
+
};
|
|
1263
|
+
//# sourceMappingURL=index.js.map
|