pixi-effects 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +15 -5
  3. package/ai/SKILL.md +44 -0
  4. package/ai/reference/cheatsheet.md +120 -0
  5. package/ai/reference/pitfalls.md +54 -0
  6. package/ai/reference/recipes.md +375 -0
  7. package/ai/template.html +82 -0
  8. package/ai/tools/save-image.py +24 -0
  9. package/dist/{Base-B1Tdn0Cv.d.cts → Base-BDw6dKRB.d.cts} +1 -1
  10. package/dist/{Base-Bh_VSusi.d.ts → Base-Ba4Ta4ap.d.ts} +1 -1
  11. package/dist/Composition-6FDH5OPM.cjs +13 -0
  12. package/dist/{Composition-BI5HZJFL.cjs.map → Composition-6FDH5OPM.cjs.map} +1 -1
  13. package/dist/Composition-FEYAUMFI.js +4 -0
  14. package/dist/{Composition-IO7ZN32J.js.map → Composition-FEYAUMFI.js.map} +1 -1
  15. package/dist/Controller.d.cts +2 -2
  16. package/dist/Controller.d.ts +2 -2
  17. package/dist/Movie-DMpaT67V.d.ts +179 -0
  18. package/dist/Movie-W8ISiEBB.d.cts +179 -0
  19. package/dist/{chunk-H55V3U56.js → chunk-DIJG2RSF.js} +28 -11
  20. package/dist/chunk-DIJG2RSF.js.map +1 -0
  21. package/dist/{chunk-VJCDG6YG.js → chunk-PN5A6QA7.js} +248 -64
  22. package/dist/chunk-PN5A6QA7.js.map +1 -0
  23. package/dist/{chunk-7OIWYXGV.cjs → chunk-SIRVULFF.cjs} +251 -62
  24. package/dist/chunk-SIRVULFF.cjs.map +1 -0
  25. package/dist/{chunk-64IHCYYN.cjs → chunk-ZL262ZRM.cjs} +37 -20
  26. package/dist/chunk-ZL262ZRM.cjs.map +1 -0
  27. package/dist/index.cjs +324 -24
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.d.cts +61 -31
  30. package/dist/index.d.ts +61 -31
  31. package/dist/index.js +321 -22
  32. package/dist/index.js.map +1 -1
  33. package/dist/three.cjs +5 -5
  34. package/dist/three.d.cts +2 -2
  35. package/dist/three.d.ts +2 -2
  36. package/dist/three.js +1 -1
  37. package/dist/{types-CNBilhpz.d.cts → types-3c8Vymgw.d.cts} +53 -6
  38. package/dist/{types-CNBilhpz.d.ts → types-3c8Vymgw.d.ts} +53 -6
  39. package/docs/api.md +333 -0
  40. package/docs/dsl.md +1017 -0
  41. package/llms-full.txt +1966 -0
  42. package/llms.txt +30 -0
  43. package/package.json +4 -3
  44. package/dist/Composition-BI5HZJFL.cjs +0 -13
  45. package/dist/Composition-IO7ZN32J.js +0 -4
  46. package/dist/Movie-CcR6h2jO.d.cts +0 -82
  47. package/dist/Movie-D-n8glA6.d.ts +0 -82
  48. package/dist/chunk-64IHCYYN.cjs.map +0 -1
  49. package/dist/chunk-7OIWYXGV.cjs.map +0 -1
  50. package/dist/chunk-H55V3U56.js.map +0 -1
  51. package/dist/chunk-VJCDG6YG.js.map +0 -1
@@ -246,6 +246,23 @@ function looksLikeColorString(s) {
246
246
  }
247
247
 
248
248
  // src/core/Timeline.ts
249
+ function loopVars(kf) {
250
+ const out = {};
251
+ let repeat = 0;
252
+ if (kf.repeat !== void 0) {
253
+ if (Number.isFinite(kf.repeat) && kf.repeat >= 0) {
254
+ repeat = Math.floor(kf.repeat);
255
+ out.repeat = repeat;
256
+ } else {
257
+ console.warn(`pixi-effects: keyframe repeat ${String(kf.repeat)} ignored \u2014 repeat must be a finite count >= 0 (the timeline needs a fixed length; for endless motion repeat many times).`);
258
+ }
259
+ }
260
+ if (repeat > 0) {
261
+ if (kf.yoyo !== void 0) out.yoyo = kf.yoyo;
262
+ if (kf.repeatDelay !== void 0 && kf.repeatDelay > 0) out.repeatDelay = kf.repeatDelay;
263
+ }
264
+ return out;
265
+ }
249
266
  function resolveAt(at, duration) {
250
267
  if (at === void 0 || at === null) return 0;
251
268
  return at < 0 ? duration + at : at;
@@ -263,7 +280,7 @@ function normalizeKeyframe(kf, parentDuration) {
263
280
  else if (hasFrom) kind = "from";
264
281
  else if (hasTo) kind = "to";
265
282
  else kind = "to";
266
- return { at, duration, ease, kind, set: kf.set, from: kf.from, to: kf.to };
283
+ return { at, duration, ease, loop: loopVars(kf), kind, set: kf.set, from: kf.from, to: kf.to };
267
284
  }
268
285
  function partitionProps(props) {
269
286
  const ownProps = {};
@@ -373,27 +390,27 @@ function applyKeyframes(timeline, target, keyframes, parentDuration, scope, skip
373
390
  const resolved = normalizeProps(kf.to, scope, { skipKeys });
374
391
  const { rest, routed } = splitRouted(resolved, routers);
375
392
  for (const r of routed)
376
- timeline.to(r.target, { [r.prop]: r.value, duration: kf.duration, ease: kf.ease }, at);
393
+ timeline.to(r.target, { [r.prop]: r.value, duration: kf.duration, ease: kf.ease, ...kf.loop }, at);
377
394
  const { ownProps, filterProps } = partitionProps(rest);
378
395
  if (Object.keys(ownProps).length > 0)
379
- timeline.to(target, { ...pixiwrap(ownProps), duration: kf.duration, ease: kf.ease }, at);
396
+ timeline.to(target, { ...pixiwrap(ownProps), duration: kf.duration, ease: kf.ease, ...kf.loop }, at);
380
397
  for (const [name, props] of Object.entries(filterProps)) {
381
398
  const f = findFilter(target, name);
382
399
  if (!f) continue;
383
- timeline.to(f, { ...props, duration: kf.duration, ease: kf.ease }, at);
400
+ timeline.to(f, { ...props, duration: kf.duration, ease: kf.ease, ...kf.loop }, at);
384
401
  }
385
402
  } else if (kf.kind === "from") {
386
403
  const resolved = normalizeProps(kf.from, scope, { skipKeys });
387
404
  const { rest, routed } = splitRouted(resolved, routers);
388
405
  for (const r of routed)
389
- timeline.from(r.target, { [r.prop]: r.value, duration: kf.duration, ease: kf.ease }, at);
406
+ timeline.from(r.target, { [r.prop]: r.value, duration: kf.duration, ease: kf.ease, ...kf.loop }, at);
390
407
  const { ownProps, filterProps } = partitionProps(rest);
391
408
  if (Object.keys(ownProps).length > 0)
392
- timeline.from(target, { ...pixiwrap(ownProps), duration: kf.duration, ease: kf.ease }, at);
409
+ timeline.from(target, { ...pixiwrap(ownProps), duration: kf.duration, ease: kf.ease, ...kf.loop }, at);
393
410
  for (const [name, props] of Object.entries(filterProps)) {
394
411
  const f = findFilter(target, name);
395
412
  if (!f) continue;
396
- timeline.from(f, { ...props, duration: kf.duration, ease: kf.ease }, at);
413
+ timeline.from(f, { ...props, duration: kf.duration, ease: kf.ease, ...kf.loop }, at);
397
414
  }
398
415
  } else {
399
416
  const fromResolved = normalizeProps(kf.from, scope, { skipKeys });
@@ -408,15 +425,15 @@ function applyKeyframes(timeline, target, keyframes, parentDuration, scope, skip
408
425
  timeline.fromTo(
409
426
  r.target,
410
427
  { [r.prop]: f.value },
411
- { [r.prop]: r.value, duration: kf.duration, ease: kf.ease },
428
+ { [r.prop]: r.value, duration: kf.duration, ease: kf.ease, ...kf.loop },
412
429
  at
413
430
  );
414
431
  } else {
415
- timeline.to(r.target, { [r.prop]: r.value, duration: kf.duration, ease: kf.ease }, at);
432
+ timeline.to(r.target, { [r.prop]: r.value, duration: kf.duration, ease: kf.ease, ...kf.loop }, at);
416
433
  }
417
434
  }
418
435
  for (const f of fromByKey.values())
419
- timeline.from(f.target, { [f.prop]: f.value, duration: kf.duration, ease: kf.ease }, at);
436
+ timeline.from(f.target, { [f.prop]: f.value, duration: kf.duration, ease: kf.ease, ...kf.loop }, at);
420
437
  const fromSplit = partitionProps(fromRouted.rest);
421
438
  const toSplit = partitionProps(toRouted.rest);
422
439
  const ownKeys = /* @__PURE__ */ new Set([...Object.keys(fromSplit.ownProps), ...Object.keys(toSplit.ownProps)]);
@@ -424,7 +441,7 @@ function applyKeyframes(timeline, target, keyframes, parentDuration, scope, skip
424
441
  timeline.fromTo(
425
442
  target,
426
443
  { ...pixiwrap(fromSplit.ownProps) },
427
- { ...pixiwrap(toSplit.ownProps), duration: kf.duration, ease: kf.ease },
444
+ { ...pixiwrap(toSplit.ownProps), duration: kf.duration, ease: kf.ease, ...kf.loop },
428
445
  at
429
446
  );
430
447
  }
@@ -438,7 +455,7 @@ function applyKeyframes(timeline, target, keyframes, parentDuration, scope, skip
438
455
  timeline.fromTo(
439
456
  f,
440
457
  { ...fromSplit.filterProps[name] ?? {} },
441
- { ...toSplit.filterProps[name] ?? {}, duration: kf.duration, ease: kf.ease },
458
+ { ...toSplit.filterProps[name] ?? {}, duration: kf.duration, ease: kf.ease, ...kf.loop },
442
459
  at
443
460
  );
444
461
  }
@@ -763,9 +780,9 @@ var Sequence = class {
763
780
  if (!this.target) return;
764
781
  const scope = this.scope();
765
782
  const routers = this.pathRouters();
766
- applyInitial(this.target, this.spec.initial, scope, [], routers);
767
- applyKeyframes(timeline, this.target, this.spec.keyframes, this.duration, scope, [], offset, routers);
768
783
  const startTime = offset + this.at;
784
+ applyInitial(this.target, this.spec.initial, scope, [], routers);
785
+ applyKeyframes(timeline, this.target, this.spec.keyframes, this.duration, scope, [], startTime, routers);
769
786
  const endTime = startTime + this.duration;
770
787
  this.absoluteStart = startTime;
771
788
  this.target.renderable = startTime <= 0;
@@ -789,6 +806,46 @@ var Sequence = class {
789
806
  }
790
807
  };
791
808
 
809
+ // src/space/specKeys.ts
810
+ function collectPropKeys(spec) {
811
+ const out = /* @__PURE__ */ new Set();
812
+ for (const k of Object.keys(spec.initial ?? {})) out.add(k);
813
+ for (const kf of spec.keyframes ?? []) {
814
+ for (const bag of [kf.set, kf.to, kf.from]) {
815
+ for (const k of Object.keys(bag ?? {})) out.add(k);
816
+ }
817
+ }
818
+ return out;
819
+ }
820
+
821
+ // src/core/lint.ts
822
+ var defaultWarn = (m) => console.warn(m);
823
+ function describeLayer(spec) {
824
+ return spec.name ? `layer "${spec.name}"` : `unnamed ${spec.type} layer`;
825
+ }
826
+ function lintTiming(spec, parentDuration, warn = defaultWarn) {
827
+ const who = describeLayer(spec);
828
+ const at = spec.at ?? 0;
829
+ if (at >= parentDuration) {
830
+ warn(`pixi-effects: ${who} starts at ${at}s, after its composition ends (${parentDuration}s), so it is never visible`);
831
+ }
832
+ const duration = spec.duration ?? parentDuration;
833
+ (spec.keyframes ?? []).forEach((kf, i) => {
834
+ const kat = kf.at ?? 0;
835
+ if (kat >= duration) {
836
+ warn(
837
+ `pixi-effects: ${who}: keyframes[${i}] starts at ${kat}s, after the layer ends (duration ${duration}s), so it never plays. Keyframe \`at\` is measured from the start of this layer, not from the start of the composition.`
838
+ );
839
+ }
840
+ });
841
+ }
842
+ function lintText(spec, warn = defaultWarn) {
843
+ if (spec.type !== "text") return;
844
+ if (collectPropKeys(spec).has("value") && !(spec.text ?? "").includes("{value}")) {
845
+ warn(`pixi-effects: ${describeLayer(spec)}: \`value\` is set or animated but the text has no {value} placeholder, so nothing shows it. Use text: '{value}' (or e.g. '{value} users').`);
846
+ }
847
+ }
848
+
792
849
  // src/space/math.ts
793
850
  var DEG = Math.PI / 180;
794
851
  var DEFAULT_FOV = 40;
@@ -900,20 +957,6 @@ function projectLayer(t, frame, b) {
900
957
  const origin = projectPoint(b, { x: t.x, y: t.y, z: t.z });
901
958
  return { corners, depth: origin.depth, visible };
902
959
  }
903
-
904
- // src/space/specKeys.ts
905
- function collectPropKeys(spec) {
906
- const out = /* @__PURE__ */ new Set();
907
- for (const k of Object.keys(spec.initial ?? {})) out.add(k);
908
- for (const kf of spec.keyframes ?? []) {
909
- for (const bag of [kf.set, kf.to, kf.from]) {
910
- for (const k of Object.keys(bag ?? {})) out.add(k);
911
- }
912
- }
913
- return out;
914
- }
915
-
916
- // src/space/CameraSequence.ts
917
960
  var CameraSequence = class extends Sequence {
918
961
  /** True when `z` is never specified anywhere: z then follows `fov`. */
919
962
  autoZ = true;
@@ -1096,7 +1139,7 @@ function shortestHueDelta(from, to) {
1096
1139
  }
1097
1140
 
1098
1141
  // src/expr/colorTween.ts
1099
- function tweenColor(timeline, target, key, fromValue, toValue, duration, ease, at, colorSpace, onUpdate) {
1142
+ function tweenColor(timeline, target, key, fromValue, toValue, duration, ease, at, colorSpace, onUpdate, loop = {}) {
1100
1143
  let interp = null;
1101
1144
  const proxy = { p: 0 };
1102
1145
  timeline.fromTo(
@@ -1106,6 +1149,7 @@ function tweenColor(timeline, target, key, fromValue, toValue, duration, ease, a
1106
1149
  p: 1,
1107
1150
  duration,
1108
1151
  ease,
1152
+ ...loop,
1109
1153
  onStart: () => {
1110
1154
  const start = fromValue !== void 0 ? fromValue : target[key];
1111
1155
  if (start === void 0) return;
@@ -1144,14 +1188,15 @@ var ImageSequence = class extends Sequence {
1144
1188
  return;
1145
1189
  }
1146
1190
  const scope = this.scope();
1191
+ const startTime = offset + this.at;
1147
1192
  const initial = stripKeys(this.spec.initial, ["tint"]);
1148
1193
  const keyframes = this.spec.keyframes ? this.spec.keyframes.map((kf) => stripKeysInKeyframe(kf, ["tint"])) : void 0;
1149
1194
  applyInitial(this.target, initial, scope);
1150
- applyKeyframes(timeline, this.target, keyframes, this.duration, scope, [], offset);
1195
+ applyKeyframes(timeline, this.target, keyframes, this.duration, scope, [], startTime);
1151
1196
  const initTint = this.spec.initial?.tint;
1152
1197
  if (initTint !== void 0) this.target.tint = initTint;
1153
1198
  for (const kf of this.spec.keyframes ?? []) {
1154
- const at = offset + resolveAt(kf.at, this.duration);
1199
+ const at = startTime + resolveAt(kf.at, this.duration);
1155
1200
  const duration = kf.duration ?? 0;
1156
1201
  const ease = kf.ease ?? "none";
1157
1202
  const fromTint = kf.from?.tint;
@@ -1172,11 +1217,12 @@ var ImageSequence = class extends Sequence {
1172
1217
  duration,
1173
1218
  ease,
1174
1219
  at,
1175
- colorSpace
1220
+ colorSpace,
1221
+ void 0,
1222
+ loopVars(kf)
1176
1223
  );
1177
1224
  }
1178
1225
  }
1179
- const startTime = offset + this.at;
1180
1226
  this.absoluteStart = startTime;
1181
1227
  const endTime = startTime + this.duration;
1182
1228
  this.target.renderable = startTime <= 0;
@@ -1207,7 +1253,25 @@ function stripKeysInKeyframe(kf, keys) {
1207
1253
  };
1208
1254
  }
1209
1255
  var STYLE_OPAQUE_KEYS = ["fontFamily", "fill", "align", "fontStyle", "fontWeight"];
1256
+ function formatValue(v, f) {
1257
+ const fixed = v.toFixed(Math.max(0, Math.floor(f?.decimals ?? 0)));
1258
+ if (!f?.grouping) return fixed;
1259
+ const [int, frac] = fixed.split(".");
1260
+ return int.replace(/\B(?=(\d{3})+(?!\d))/g, ",") + (frac !== void 0 ? "." + frac : "");
1261
+ }
1210
1262
  var TextSequence = class extends Sequence {
1263
+ /** Current number for the `{value}` placeholder (tweened by `value` keyframes). */
1264
+ _value = { value: 0 };
1265
+ _lastText = "";
1266
+ /** Rewrite the displayed string from the template and the current `value`. */
1267
+ _refreshText() {
1268
+ const template = this.spec.text ?? "";
1269
+ if (!template.includes("{value}")) return;
1270
+ const next = template.split("{value}").join(formatValue(this._value.value, this.spec.format));
1271
+ if (next === this._lastText) return;
1272
+ this._lastText = next;
1273
+ this.target.text = next;
1274
+ }
1211
1275
  async build() {
1212
1276
  const baseStyle = {
1213
1277
  fontFamily: "Arial",
@@ -1237,15 +1301,25 @@ var TextSequence = class extends Sequence {
1237
1301
  for (const k of Object.keys(resolved)) {
1238
1302
  text.style[k] = resolved[k];
1239
1303
  }
1304
+ this.intrinsicWidth = text.width;
1305
+ this.intrinsicHeight = text.height;
1306
+ }
1307
+ const initialValue = this.spec.initial?.value;
1308
+ if (initialValue !== void 0) this._value.value = resolveNumber(initialValue, this.scope());
1309
+ this._refreshText();
1310
+ if (this._lastText !== "") {
1311
+ this.intrinsicWidth = text.width;
1312
+ this.intrinsicHeight = text.height;
1240
1313
  }
1241
1314
  this.buildFilters();
1242
1315
  }
1243
1316
  bindTimeline(timeline, offset = 0) {
1244
1317
  if (!this.target) return;
1245
1318
  const colorSpace = this.spec.colorSpace ?? "rgb";
1246
- const stripped = stripFill(this.spec);
1319
+ const stripped = stripKeys2(this.spec, ["fill", "value"]);
1247
1320
  runSuperWithStrippedSpec(this, stripped, timeline, offset);
1248
- bindFillKeyframes(timeline, this.target, this.spec.keyframes ?? [], this.duration, offset, colorSpace);
1321
+ bindFillKeyframes(timeline, this.target, this.spec.keyframes ?? [], this.duration, offset + this.at, colorSpace);
1322
+ bindValueKeyframes(timeline, this._value, () => this._refreshText(), this.spec.keyframes ?? [], this.duration, offset + this.at, this.scope());
1249
1323
  }
1250
1324
  };
1251
1325
  function runSuperWithStrippedSpec(inst, strippedSpec, timeline, offset) {
@@ -1257,25 +1331,61 @@ function runSuperWithStrippedSpec(inst, strippedSpec, timeline, offset) {
1257
1331
  inst.spec = originalSpec;
1258
1332
  }
1259
1333
  }
1260
- function stripFill(spec) {
1334
+ function stripKeys2(spec, keys) {
1261
1335
  return {
1262
1336
  ...spec,
1263
- initial: stripKey(spec.initial, "fill"),
1337
+ initial: stripProps(spec.initial, keys),
1264
1338
  keyframes: spec.keyframes ? spec.keyframes.map((kf) => ({
1265
1339
  ...kf,
1266
- set: stripKey(kf.set, "fill"),
1267
- to: stripKey(kf.to, "fill"),
1268
- from: stripKey(kf.from, "fill")
1340
+ set: stripProps(kf.set, keys),
1341
+ to: stripProps(kf.to, keys),
1342
+ from: stripProps(kf.from, keys)
1269
1343
  })) : void 0
1270
1344
  };
1271
1345
  }
1272
- function stripKey(props, key) {
1346
+ function stripProps(props, keys) {
1273
1347
  if (!props) return props;
1274
- if (!(key in props)) return props;
1348
+ if (!keys.some((k) => k in props)) return props;
1275
1349
  const out = {};
1276
- for (const k of Object.keys(props)) if (k !== key) out[k] = props[k];
1350
+ for (const k of Object.keys(props)) if (!keys.includes(k)) out[k] = props[k];
1277
1351
  return out;
1278
1352
  }
1353
+ function resolveNumber(v, scope) {
1354
+ if (typeof v === "number") return v;
1355
+ const out = normalizeProps({ v }, scope).v;
1356
+ return typeof out === "number" ? out : Number(out) || 0;
1357
+ }
1358
+ function bindValueKeyframes(timeline, state, refresh, keyframes, parentDuration, origin, scope) {
1359
+ for (const kf of keyframes) {
1360
+ const setV = kf.set?.value;
1361
+ const fromV = kf.from?.value;
1362
+ const toV = kf.to?.value;
1363
+ if (setV === void 0 && fromV === void 0 && toV === void 0) continue;
1364
+ const at = origin + resolveAt(kf.at, parentDuration);
1365
+ const duration = kf.duration ?? 0;
1366
+ const ease = kf.ease ?? "none";
1367
+ const loop = loopVars(kf);
1368
+ if (setV !== void 0) {
1369
+ const v = resolveNumber(setV, scope);
1370
+ timeline.call(() => {
1371
+ state.value = v;
1372
+ refresh();
1373
+ }, [], at);
1374
+ }
1375
+ if (toV !== void 0 && fromV !== void 0) {
1376
+ timeline.fromTo(
1377
+ state,
1378
+ { value: resolveNumber(fromV, scope) },
1379
+ { value: resolveNumber(toV, scope), duration, ease, ...loop, onUpdate: refresh },
1380
+ at
1381
+ );
1382
+ } else if (toV !== void 0) {
1383
+ timeline.to(state, { value: resolveNumber(toV, scope), duration, ease, ...loop, onUpdate: refresh }, at);
1384
+ } else if (fromV !== void 0) {
1385
+ timeline.from(state, { value: resolveNumber(fromV, scope), duration, ease, ...loop, onUpdate: refresh }, at);
1386
+ }
1387
+ }
1388
+ }
1279
1389
  function bindFillKeyframes(timeline, text, keyframes, parentDuration, offset, colorSpace) {
1280
1390
  const state = { fill: text.style.fill };
1281
1391
  const writeFill = () => {
@@ -1305,7 +1415,8 @@ function bindFillKeyframes(timeline, text, keyframes, parentDuration, offset, co
1305
1415
  ease,
1306
1416
  at,
1307
1417
  colorSpace,
1308
- writeFill
1418
+ writeFill,
1419
+ loopVars(kf)
1309
1420
  );
1310
1421
  }
1311
1422
  }
@@ -1324,6 +1435,11 @@ var AudioSequence = class extends Sequence {
1324
1435
  if (this.duration === void 0) {
1325
1436
  this.duration = this.spec.duration ?? data.duration ?? this.root.duration;
1326
1437
  }
1438
+ if (!this.spec.loop && data.duration !== void 0 && this.duration > data.duration + 0.05) {
1439
+ console.warn(
1440
+ `pixi-effects: ${describeLayer(this.spec)}: audio asset "${this.spec.asset}" is ${data.duration.toFixed(1)}s but the layer lasts ${this.duration.toFixed(1)}s, so it goes silent after ${data.duration.toFixed(1)}s. Add loop: true, or shorten the layer's duration.`
1441
+ );
1442
+ }
1327
1443
  this.target = null;
1328
1444
  }
1329
1445
  bindTimeline(_timeline) {
@@ -1493,14 +1609,15 @@ var VideoSequence = class extends Sequence {
1493
1609
  });
1494
1610
  this.buildFilters();
1495
1611
  }
1496
- bindTimeline(timeline) {
1497
- super.bindTimeline(timeline);
1612
+ bindTimeline(timeline, offset = 0) {
1613
+ super.bindTimeline(timeline, offset);
1498
1614
  const playDuration = this.spec.loop ? this.duration : Math.min(this.duration, this._sourceDuration);
1499
1615
  timeline.fromTo(
1500
1616
  this.target,
1501
1617
  { currentTime: 0 },
1502
1618
  { currentTime: playDuration, duration: playDuration, ease: "none" },
1503
- this.at
1619
+ offset + this.at
1620
+ // offset = start of the enclosing composition(s); dropping it misplaced videos inside nested compositions
1504
1621
  );
1505
1622
  }
1506
1623
  collectAudio(out, baseTime) {
@@ -1541,6 +1658,55 @@ var VideoSequence = class extends Sequence {
1541
1658
  super.destroy();
1542
1659
  }
1543
1660
  };
1661
+
1662
+ // src/sequences/gradient.ts
1663
+ function cssColor(c) {
1664
+ return typeof c === "number" ? "#" + c.toString(16).padStart(6, "0") : c;
1665
+ }
1666
+ function normalizedStops(g) {
1667
+ const stops = g.stops ?? [];
1668
+ if (stops.length < 2) throw new Error('pixi-effects: fillGradient needs at least two colour stops, e.g. stops: [[0, "#000"], [1, "#fff"]]');
1669
+ return stops.map((s) => Array.isArray(s) ? { offset: s[0], color: s[1] } : { offset: s.offset, color: s.color });
1670
+ }
1671
+ function gradientOptions(g) {
1672
+ const colorStops = normalizedStops(g);
1673
+ const a = (g.angle ?? 90) * Math.PI / 180;
1674
+ const dx = Math.cos(a), dy = Math.sin(a);
1675
+ const h = (Math.abs(dx) + Math.abs(dy)) / 2;
1676
+ return {
1677
+ type: "linear",
1678
+ start: { x: 0.5 - dx * h, y: 0.5 - dy * h },
1679
+ end: { x: 0.5 + dx * h, y: 0.5 + dy * h },
1680
+ colorStops,
1681
+ textureSpace: "local"
1682
+ };
1683
+ }
1684
+ function paintRadial(ctx, size, g) {
1685
+ const stops = normalizedStops(g);
1686
+ const [cx, cy] = g.center ?? [0.5, 0.5];
1687
+ const grad = ctx.createRadialGradient(cx * size, cy * size, (g.innerRadius ?? 0) * size, cx * size, cy * size, (g.radius ?? 0.5) * size);
1688
+ for (const s of stops) grad.addColorStop(s.offset, cssColor(s.color));
1689
+ ctx.fillStyle = grad;
1690
+ ctx.fillRect(0, 0, size, size);
1691
+ }
1692
+
1693
+ // src/sequences/gradientFill.ts
1694
+ var RADIAL_SIZE = 256;
1695
+ function makeGradientFill(g) {
1696
+ if (g.type === "radial") {
1697
+ const canvas = document.createElement("canvas");
1698
+ canvas.width = RADIAL_SIZE;
1699
+ canvas.height = RADIAL_SIZE;
1700
+ const ctx = canvas.getContext("2d");
1701
+ if (ctx) {
1702
+ paintRadial(ctx, RADIAL_SIZE, g);
1703
+ return { texture: pixi_js.Texture.from(canvas), textureSpace: "local" };
1704
+ }
1705
+ }
1706
+ return new pixi_js.FillGradient(gradientOptions(g));
1707
+ }
1708
+
1709
+ // src/sequences/Shape.ts
1544
1710
  var STYLE_KEYS = ["fillColor", "fillAlpha", "strokeColor", "strokeAlpha", "strokeWidth"];
1545
1711
  var COLOR_KEYS = /* @__PURE__ */ new Set(["fillColor", "strokeColor"]);
1546
1712
  var GEOMETRY_KEYS_BY_SHAPE = {
@@ -1578,6 +1744,8 @@ var ShapeSequence = class extends Sequence {
1578
1744
  for (const k of geomKeys) this._liveKeys.add(k);
1579
1745
  seedGeometry(this._state, this.spec, scope);
1580
1746
  seedStyle(this._state, this.spec.initial ?? {}, scope);
1747
+ const gradientSpec = this.spec.fillGradient ?? this.spec.initial?.fillGradient;
1748
+ if (gradientSpec) this._state.fillStyle = makeGradientFill(gradientSpec);
1581
1749
  this._drawGeometry = makeDrawGeometry(this.spec, scope, this._state);
1582
1750
  this._drawGeometry(graphics);
1583
1751
  applyState(graphics, this._state);
@@ -1585,7 +1753,12 @@ var ShapeSequence = class extends Sequence {
1585
1753
  this.intrinsicWidth = bounds.width;
1586
1754
  this.intrinsicHeight = bounds.height;
1587
1755
  if (!ANCHORED_SHAPES.has(this.spec.shape)) {
1588
- graphics.pivot.set(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2);
1756
+ const cx = bounds.x + bounds.width / 2;
1757
+ const cy = bounds.y + bounds.height / 2;
1758
+ graphics.pivot.set(cx, cy);
1759
+ const init = this.spec.initial ?? {};
1760
+ if (init.x === void 0) graphics.x = cx;
1761
+ if (init.y === void 0) graphics.y = cy;
1589
1762
  }
1590
1763
  graphics.onRender = () => {
1591
1764
  graphics.clear();
@@ -1597,13 +1770,13 @@ var ShapeSequence = class extends Sequence {
1597
1770
  bindTimeline(timeline, offset = 0) {
1598
1771
  if (!this.target) return;
1599
1772
  const scope = this.scope();
1600
- const initial = stripLive(this.spec.initial, this._liveKeys);
1773
+ const startTime = offset + this.at;
1774
+ const initial = stripLive(stripGradient(this.spec.initial), this._liveKeys);
1601
1775
  const keyframes = this.spec.keyframes ? this.spec.keyframes.map((kf) => stripLiveKeyframe(kf, this._liveKeys)) : void 0;
1602
1776
  applyInitial(this.target, initial, scope);
1603
- applyKeyframes(timeline, this.target, keyframes, this.duration, scope, [], offset);
1777
+ applyKeyframes(timeline, this.target, keyframes, this.duration, scope, [], startTime);
1604
1778
  const colorSpace = this.spec.colorSpace ?? "rgb";
1605
- bindLiveKeyframes(timeline, this._state, this._liveKeys, this.spec.keyframes ?? [], this.duration, scope, offset, colorSpace);
1606
- const startTime = offset + this.at;
1779
+ bindLiveKeyframes(timeline, this._state, this._liveKeys, this.spec.keyframes ?? [], this.duration, scope, startTime, colorSpace);
1607
1780
  this.absoluteStart = startTime;
1608
1781
  const endTime = startTime + this.duration;
1609
1782
  this.target.renderable = startTime <= 0;
@@ -1613,8 +1786,9 @@ var ShapeSequence = class extends Sequence {
1613
1786
  };
1614
1787
  function seedGeometry(state, spec, scope) {
1615
1788
  const keys = GEOMETRY_KEYS_BY_SHAPE[spec.shape];
1789
+ const initial = spec.initial ?? {};
1616
1790
  for (const k of keys) {
1617
- const v = spec[k];
1791
+ const v = spec[k] ?? initial[k];
1618
1792
  if (v !== void 0) state[k] = num(v, scope);
1619
1793
  }
1620
1794
  }
@@ -1625,6 +1799,11 @@ function seedStyle(state, initial, scope) {
1625
1799
  state[k] = COLOR_KEYS.has(k) ? v : numOrZero(v, scope);
1626
1800
  }
1627
1801
  }
1802
+ function stripGradient(props) {
1803
+ if (!props || !("fillGradient" in props)) return props;
1804
+ const { fillGradient: _drop, ...rest } = props;
1805
+ return rest;
1806
+ }
1628
1807
  function stripLive(props, liveKeys) {
1629
1808
  if (!props) return props;
1630
1809
  const out = {};
@@ -1663,7 +1842,9 @@ function resolveLiveValue(key, value, scope) {
1663
1842
  return numOrZero(value, scope);
1664
1843
  }
1665
1844
  function applyState(g, s) {
1666
- if (s.fillColor !== void 0) {
1845
+ if (s.fillStyle) {
1846
+ g.fill(s.fillStyle);
1847
+ } else if (s.fillColor !== void 0) {
1667
1848
  g.fill({ color: s.fillColor, alpha: s.fillAlpha });
1668
1849
  }
1669
1850
  if (s.strokeColor !== void 0 && s.strokeWidth > 0) {
@@ -1743,6 +1924,7 @@ function bindLiveKeyframes(timeline, state, liveKeys, keyframes, parentDuration,
1743
1924
  const at = offset + resolveAt(kf.at, parentDuration);
1744
1925
  const duration = kf.duration ?? 0;
1745
1926
  const ease = kf.ease ?? "none";
1927
+ const loop = loopVars(kf);
1746
1928
  if (kf.set) {
1747
1929
  const live = pickLive(kf.set, liveKeys);
1748
1930
  if (live) {
@@ -1766,11 +1948,11 @@ function bindLiveKeyframes(timeline, state, liveKeys, keyframes, parentDuration,
1766
1948
  const toRaw = toLive?.[key];
1767
1949
  const fromValue = fromRaw !== void 0 ? resolveLiveValue(key, fromRaw, scope) : void 0;
1768
1950
  const toValue = toRaw !== void 0 ? resolveLiveValue(key, toRaw, scope) : void 0;
1769
- tweenLiveKey(timeline, state, key, fromValue, toValue, duration, ease, at, colorSpace);
1951
+ tweenLiveKey(timeline, state, key, fromValue, toValue, duration, ease, at, colorSpace, loop);
1770
1952
  }
1771
1953
  }
1772
1954
  }
1773
- function tweenLiveKey(timeline, state, key, fromValue, toValue, duration, ease, at, colorSpace) {
1955
+ function tweenLiveKey(timeline, state, key, fromValue, toValue, duration, ease, at, colorSpace, loop) {
1774
1956
  if (COLOR_KEYS.has(key) && toValue !== void 0) {
1775
1957
  tweenColor(
1776
1958
  timeline,
@@ -1781,13 +1963,15 @@ function tweenLiveKey(timeline, state, key, fromValue, toValue, duration, ease,
1781
1963
  duration,
1782
1964
  ease,
1783
1965
  at,
1784
- colorSpace
1966
+ colorSpace,
1967
+ void 0,
1968
+ loop
1785
1969
  );
1786
1970
  } else if (toValue !== void 0) {
1787
1971
  if (fromValue !== void 0) {
1788
- timeline.fromTo(state, { [key]: fromValue }, { [key]: toValue, duration, ease }, at);
1972
+ timeline.fromTo(state, { [key]: fromValue }, { [key]: toValue, duration, ease, ...loop }, at);
1789
1973
  } else {
1790
- timeline.to(state, { [key]: toValue, duration, ease }, at);
1974
+ timeline.to(state, { [key]: toValue, duration, ease, ...loop }, at);
1791
1975
  }
1792
1976
  }
1793
1977
  }
@@ -1807,7 +1991,7 @@ function numOrZero(v, scope) {
1807
1991
  var _CompositionSequence = null;
1808
1992
  async function getCompositionSequence() {
1809
1993
  if (!_CompositionSequence) {
1810
- _CompositionSequence = (await import('./Composition-BI5HZJFL.cjs')).CompositionSequence;
1994
+ _CompositionSequence = (await import('./Composition-6FDH5OPM.cjs')).CompositionSequence;
1811
1995
  }
1812
1996
  return _CompositionSequence;
1813
1997
  }
@@ -1845,16 +2029,21 @@ async function buildSequenceTree(specs, parent, root) {
1845
2029
  }
1846
2030
 
1847
2031
  exports.CameraSequence = CameraSequence;
2032
+ exports.DEFAULT_FOV = DEFAULT_FOV;
1848
2033
  exports.DEG = DEG;
1849
2034
  exports.Sequence = Sequence;
1850
2035
  exports.buildScope = buildScope;
1851
2036
  exports.buildSequenceTree = buildSequenceTree;
1852
2037
  exports.cameraBasis = cameraBasis;
1853
2038
  exports.collectPropKeys = collectPropKeys;
2039
+ exports.describeLayer = describeLayer;
1854
2040
  exports.evaluateExpr = evaluateExpr;
1855
2041
  exports.homeCamera = homeCamera;
2042
+ exports.homeDistance = homeDistance;
2043
+ exports.lintText = lintText;
2044
+ exports.lintTiming = lintTiming;
1856
2045
  exports.projectLayer = projectLayer;
1857
2046
  exports.registerSequenceType = registerSequenceType;
1858
2047
  exports.resolveAt = resolveAt;
1859
- //# sourceMappingURL=chunk-7OIWYXGV.cjs.map
1860
- //# sourceMappingURL=chunk-7OIWYXGV.cjs.map
2048
+ //# sourceMappingURL=chunk-SIRVULFF.cjs.map
2049
+ //# sourceMappingURL=chunk-SIRVULFF.cjs.map