pixi-effects 0.5.0 → 0.7.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 (54) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +17 -10
  3. package/ai/SKILL.md +1 -1
  4. package/ai/reference/cheatsheet.md +8 -2
  5. package/ai/reference/pitfalls.md +1 -0
  6. package/ai/reference/recipes.md +36 -1
  7. package/ai/template.html +4 -3
  8. package/ai/tools/check.mjs +14 -5
  9. package/ai/tools/render.mjs +202 -0
  10. package/ai/tools/view.mjs +269 -0
  11. package/dist/{Base-DnRAxroQ.d.cts → Base-CSIulyIq.d.cts} +1 -1
  12. package/dist/{Base-CPSFBkPm.d.ts → Base-DVgwyIUj.d.ts} +1 -1
  13. package/dist/Composition-3KHQPRBA.cjs +14 -0
  14. package/dist/{Composition-63EXHNJJ.cjs.map → Composition-3KHQPRBA.cjs.map} +1 -1
  15. package/dist/Composition-DT657FO5.js +5 -0
  16. package/dist/{Composition-TUCRSMJU.js.map → Composition-DT657FO5.js.map} +1 -1
  17. package/dist/Controller.cjs +77 -2
  18. package/dist/Controller.cjs.map +1 -1
  19. package/dist/Controller.d.cts +16 -2
  20. package/dist/Controller.d.ts +16 -2
  21. package/dist/Controller.js +77 -2
  22. package/dist/Controller.js.map +1 -1
  23. package/dist/{Movie-CuU_aaCm.d.cts → Movie-B0OUKlZI.d.ts} +54 -2
  24. package/dist/{Movie-VyDiUYOj.d.ts → Movie-BK9daiTt.d.cts} +54 -2
  25. package/dist/{chunk-5Q5XZG7Z.js → chunk-36Z4RIRK.js} +3 -3
  26. package/dist/{chunk-5Q5XZG7Z.js.map → chunk-36Z4RIRK.js.map} +1 -1
  27. package/dist/{chunk-UBPZLGLU.js → chunk-4O2NYDJG.js} +365 -44
  28. package/dist/chunk-4O2NYDJG.js.map +1 -0
  29. package/dist/{chunk-23M5QTWW.cjs → chunk-4VSIJLU3.cjs} +21 -21
  30. package/dist/{chunk-23M5QTWW.cjs.map → chunk-4VSIJLU3.cjs.map} +1 -1
  31. package/dist/{chunk-2RR43OIN.cjs → chunk-6I55T4OH.cjs} +403 -62
  32. package/dist/chunk-6I55T4OH.cjs.map +1 -0
  33. package/dist/index.cjs +234 -24
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.d.cts +4 -4
  36. package/dist/index.d.ts +4 -4
  37. package/dist/index.js +226 -16
  38. package/dist/index.js.map +1 -1
  39. package/dist/three.cjs +6 -5
  40. package/dist/three.cjs.map +1 -1
  41. package/dist/three.d.cts +2 -2
  42. package/dist/three.d.ts +2 -2
  43. package/dist/three.js +2 -1
  44. package/dist/three.js.map +1 -1
  45. package/dist/{types-7VtTEKUW.d.ts → types-oJijbn-K.d.cts} +36 -7
  46. package/dist/{types-7VtTEKUW.d.cts → types-oJijbn-K.d.ts} +36 -7
  47. package/docs/api.md +7 -0
  48. package/docs/dsl.md +74 -8
  49. package/llms-full.txt +127 -12
  50. package/package.json +7 -4
  51. package/dist/Composition-63EXHNJJ.cjs +0 -13
  52. package/dist/Composition-TUCRSMJU.js +0 -4
  53. package/dist/chunk-2RR43OIN.cjs.map +0 -1
  54. package/dist/chunk-UBPZLGLU.js.map +0 -1
@@ -1,9 +1,28 @@
1
1
  'use strict';
2
2
 
3
- var pixi_js = require('pixi.js');
3
+ var chunkUQBGW5DO_cjs = require('./chunk-UQBGW5DO.cjs');
4
+ var PIXI = require('pixi.js');
4
5
  var gsap = require('gsap');
5
6
 
6
- // src/sequences/Base.ts
7
+ function _interopNamespace(e) {
8
+ if (e && e.__esModule) return e;
9
+ var n = Object.create(null);
10
+ if (e) {
11
+ Object.keys(e).forEach(function (k) {
12
+ if (k !== 'default') {
13
+ var d = Object.getOwnPropertyDescriptor(e, k);
14
+ Object.defineProperty(n, k, d.get ? d : {
15
+ enumerable: true,
16
+ get: function () { return e[k]; }
17
+ });
18
+ }
19
+ });
20
+ }
21
+ n.default = e;
22
+ return Object.freeze(n);
23
+ }
24
+
25
+ var PIXI__namespace = /*#__PURE__*/_interopNamespace(PIXI);
7
26
 
8
27
  // src/expr/Parser.ts
9
28
  var FUNCS = {
@@ -639,10 +658,10 @@ fn mainFragment(@location(0) uv : vec2<f32>) -> @location(0) vec4<f32> {
639
658
  `;
640
659
  function toRgb01(input) {
641
660
  if (Array.isArray(input)) return [input[0], input[1], input[2]];
642
- const c = new pixi_js.Color(input);
661
+ const c = new PIXI.Color(input);
643
662
  return [c.red, c.green, c.blue];
644
663
  }
645
- var ChromaKeyFilter = class extends pixi_js.Filter {
664
+ var ChromaKeyFilter = class extends PIXI.Filter {
646
665
  constructor(options = {}) {
647
666
  const {
648
667
  keyColor = "#00ff00",
@@ -650,12 +669,12 @@ var ChromaKeyFilter = class extends pixi_js.Filter {
650
669
  smoothing = 0.1,
651
670
  spill = 0.2
652
671
  } = options;
653
- const glProgram = pixi_js.GlProgram.from({
654
- vertex: pixi_js.defaultFilterVert,
672
+ const glProgram = PIXI.GlProgram.from({
673
+ vertex: PIXI.defaultFilterVert,
655
674
  fragment: GL_FRAGMENT,
656
675
  name: "chroma-key-filter"
657
676
  });
658
- const gpuProgram = pixi_js.GpuProgram.from({
677
+ const gpuProgram = PIXI.GpuProgram.from({
659
678
  vertex: { source: WGSL_SOURCE, entryPoint: "mainVertex" },
660
679
  fragment: { source: WGSL_SOURCE, entryPoint: "mainFragment" }
661
680
  });
@@ -663,7 +682,7 @@ var ChromaKeyFilter = class extends pixi_js.Filter {
663
682
  glProgram,
664
683
  gpuProgram,
665
684
  resources: {
666
- chromaUniforms: new pixi_js.UniformGroup({
685
+ chromaUniforms: new PIXI.UniformGroup({
667
686
  uKeyColor: { value: toRgb01(keyColor), type: "vec3<f32>" },
668
687
  uThreshold: { value: threshold, type: "f32" },
669
688
  uSmoothing: { value: smoothing, type: "f32" },
@@ -698,6 +717,124 @@ var ChromaKeyFilter = class extends pixi_js.Filter {
698
717
  u[2] = rgb[2];
699
718
  }
700
719
  };
720
+ var CORE = { blur: "BlurFilter", noise: "NoiseFilter", alpha: "AlphaFilter" };
721
+ var CORE_TYPES = [...Object.keys(CORE), "colorMatrix"];
722
+ var LIBRARY_TYPES = [
723
+ "adjustment",
724
+ "advancedBloom",
725
+ "ascii",
726
+ "backdropBlur",
727
+ "bevel",
728
+ "bloom",
729
+ "bulgePinch",
730
+ "colorGradient",
731
+ "colorMap",
732
+ "colorOverlay",
733
+ "colorReplace",
734
+ "convolution",
735
+ "crossHatch",
736
+ "crt",
737
+ "dot",
738
+ "dropShadow",
739
+ "emboss",
740
+ "glitch",
741
+ "glow",
742
+ "godray",
743
+ "grayscale",
744
+ "hslAdjustment",
745
+ "kawaseBlur",
746
+ "motionBlur",
747
+ "multiColorReplace",
748
+ "oldFilm",
749
+ "outline",
750
+ "pixelate",
751
+ "radialBlur",
752
+ "reflection",
753
+ "rgbSplit",
754
+ "shockwave",
755
+ "simpleLightmap",
756
+ "simplexNoise",
757
+ "tiltShift",
758
+ "tiltShiftAxis",
759
+ "twist",
760
+ "zoomBlur"
761
+ ];
762
+ var FILTER_TYPES = [...CORE_TYPES, ...LIBRARY_TYPES];
763
+ var NOT_NAMED = /* @__PURE__ */ new Set(["custom", "chromaKey"]);
764
+ var lower = (s) => s.toLowerCase();
765
+ var isLibraryType = (t) => LIBRARY_TYPES.some((n) => lower(n) === lower(t));
766
+ var isCoreType = (t) => CORE_TYPES.some((n) => lower(n) === lower(t));
767
+ function isNamedFilterType(t) {
768
+ return !NOT_NAMED.has(t);
769
+ }
770
+ function unknownType(t) {
771
+ const guess = chunkUQBGW5DO_cjs.suggestName(t, FILTER_TYPES);
772
+ return new Error(`pixi-effects: unknown filter type "${t}"${guess ? ` \u2014 did you mean "${guess}"?` : ""} (valid: ${[...FILTER_TYPES, "chromaKey", "custom"].join(", ")})`);
773
+ }
774
+ var library = null;
775
+ function registerFilterLibrary(mod) {
776
+ library = /* @__PURE__ */ new Map();
777
+ for (const [key, value] of Object.entries(mod)) {
778
+ if (key.endsWith("Filter") && typeof value === "function") library.set(lower(key.slice(0, -"Filter".length)), value);
779
+ }
780
+ }
781
+ var MISSING = (type, why) => new Error(
782
+ `pixi-effects: the filter "${type}" comes from the pixi-filters package${why ? ` (${why})` : ""}. Add it to your import map ("pixi-filters": "https://esm.sh/pixi-filters@6.1.5?external=pixi.js") or run \`npm i pixi-filters\`.`
783
+ );
784
+ function needsFilterLibrary(spec) {
785
+ let needs = false;
786
+ const visit = (node) => {
787
+ if (!node || typeof node !== "object") return;
788
+ if (Array.isArray(node)) {
789
+ node.forEach(visit);
790
+ return;
791
+ }
792
+ const o = node;
793
+ if (Array.isArray(o.filters)) {
794
+ for (const f of o.filters) {
795
+ if (typeof f?.type !== "string" || !isNamedFilterType(f.type)) continue;
796
+ if (isLibraryType(f.type)) needs = true;
797
+ else if (!isCoreType(f.type)) throw unknownType(f.type);
798
+ }
799
+ }
800
+ for (const key of ["sequences", "mask"]) visit(o[key]);
801
+ };
802
+ visit(spec);
803
+ return needs;
804
+ }
805
+ async function ensureFilterLibrary(spec, load = () => import('pixi-filters')) {
806
+ if (!needsFilterLibrary(spec) || library) return;
807
+ try {
808
+ registerFilterLibrary(await load());
809
+ } catch (e) {
810
+ throw MISSING("(see the type in your filters)", e.message);
811
+ }
812
+ }
813
+ var SINGLE_VALUE = { pixelate: "size", emboss: "strength" };
814
+ var MATRIX_PRESETS = ["sepia", "grayscale", "negative", "polaroid", "technicolor", "vintage", "kodachrome", "browni"];
815
+ function colorMatrix(params) {
816
+ const f = new PIXI__namespace.ColorMatrixFilter();
817
+ const preset = params.preset;
818
+ if (preset !== void 0) {
819
+ if (!MATRIX_PRESETS.includes(preset)) {
820
+ const guess = chunkUQBGW5DO_cjs.suggestName(preset, MATRIX_PRESETS);
821
+ throw new Error(`pixi-effects: colorMatrix preset "${preset}" is not one of ${MATRIX_PRESETS.join(", ")}${guess ? ` \u2014 did you mean "${guess}"?` : ""}`);
822
+ }
823
+ f[preset === "grayscale" ? "desaturate" : preset].call(f);
824
+ }
825
+ if (Array.isArray(params.matrix)) f.matrix = params.matrix;
826
+ return f;
827
+ }
828
+ function createNamedFilter(type, params) {
829
+ if (lower(type) === "colormatrix") return colorMatrix(params);
830
+ const core = Object.entries(CORE).find(([k]) => lower(k) === lower(type));
831
+ if (core) return new PIXI__namespace[core[1]](params);
832
+ if (!isLibraryType(type)) throw unknownType(type);
833
+ const Ctor = library?.get(lower(type));
834
+ if (!Ctor) throw MISSING(type, library ? "not found in the loaded package" : "not loaded yet");
835
+ const single = SINGLE_VALUE[Object.keys(SINGLE_VALUE).find((k) => lower(k) === lower(type)) ?? ""];
836
+ return single ? new Ctor(params[single]) : new Ctor(params);
837
+ }
701
838
 
702
839
  // src/filters/index.ts
703
840
  var registry = {
@@ -716,10 +853,9 @@ function createFilter(spec) {
716
853
  named._name = spec.name;
717
854
  return named;
718
855
  }
719
- const Cls = registry[spec.type];
720
- if (!Cls) throw new Error(`pixi-effects: unknown filter type "${spec.type}"`);
721
- const { type: _t, name, ...params } = spec;
722
- const inst = new Cls(params);
856
+ const { type, name, ...params } = spec;
857
+ const Cls = registry[type];
858
+ const inst = Cls ? new Cls(params) : createNamedFilter(type, params);
723
859
  inst._name = name;
724
860
  return inst;
725
861
  }
@@ -768,7 +904,7 @@ var Sequence = class {
768
904
  }
769
905
  const fa = this.spec.filterArea;
770
906
  if (fa && this.target) {
771
- this.target.filterArea = new pixi_js.Rectangle(fa.x, fa.y, fa.width, fa.height);
907
+ this.target.filterArea = new PIXI.Rectangle(fa.x, fa.y, fa.width, fa.height);
772
908
  }
773
909
  }
774
910
  scope() {
@@ -861,10 +997,12 @@ function summarizeWarnings(messages, shown = 3) {
861
997
  }
862
998
  function lintText(spec, warn = defaultWarn) {
863
999
  if (spec.type !== "text") return;
864
- if ((spec.text ?? "").includes("{value}") && !collectPropKeys(spec).has("value")) {
1000
+ const strings = [spec.text ?? "", ...(spec.keyframes ?? []).map((kf) => String(kf.set?.text ?? ""))];
1001
+ const hasPlaceholder = strings.some((t) => t.includes("{value}"));
1002
+ if (hasPlaceholder && !collectPropKeys(spec).has("value")) {
865
1003
  warn(`pixi-effects: ${describeLayer(spec)}: the text has a {value} placeholder but nothing sets or animates \`value\`, so it prints 0. Set initial: { value: \u2026 } or animate it; {value} is reserved for counters (there is no way to print a literal "{value}").`);
866
1004
  }
867
- if (collectPropKeys(spec).has("value") && !(spec.text ?? "").includes("{value}")) {
1005
+ if (collectPropKeys(spec).has("value") && !hasPlaceholder) {
868
1006
  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').`);
869
1007
  }
870
1008
  }
@@ -993,7 +1131,7 @@ var CameraSequence = class extends Sequence {
993
1131
  this.duration = this.parent?.duration ?? this.root.duration;
994
1132
  }
995
1133
  const home = homeCamera(compW, this.compH);
996
- const carrier = new pixi_js.Container();
1134
+ const carrier = new PIXI.Container();
997
1135
  carrier.x = home.x;
998
1136
  carrier.y = home.y;
999
1137
  carrier.z = home.z;
@@ -1221,8 +1359,8 @@ function tweenColor(timeline, target, key, fromValue, toValue, duration, ease, a
1221
1359
  // src/sequences/Image.ts
1222
1360
  var ImageSequence = class extends Sequence {
1223
1361
  async build() {
1224
- const texture = await pixi_js.Assets.get(this.spec.asset);
1225
- const sprite = new pixi_js.Sprite({ texture, label: this.spec.name });
1362
+ const texture = await PIXI.Assets.get(this.spec.asset);
1363
+ const sprite = new PIXI.Sprite({ texture, label: this.spec.name });
1226
1364
  sprite.cullable = true;
1227
1365
  this.target = sprite;
1228
1366
  this.intrinsicWidth = texture.width;
@@ -1333,14 +1471,21 @@ function formatValue(v, f) {
1333
1471
  return sign + int + (frac !== void 0 ? "." + frac : "");
1334
1472
  }
1335
1473
  var TextSequence = class extends Sequence {
1336
- /** Current number for the `{value}` placeholder (tweened by `value` keyframes). */
1337
- _value = { value: 0 };
1474
+ /** `value`: the number for the `{value}` placeholder; `visibleChars`: how many characters show (typewriter). Both tweened by keyframes. */
1475
+ _counters = { value: 0, visibleChars: 0 };
1476
+ /** The string now on the layer (before `{value}` / `visibleChars`): `text`, or the last `set: { text }`. */
1477
+ _template = "";
1478
+ /** `visibleChars` is in play (initial or animated): otherwise the whole text shows. */
1479
+ _typed = false;
1338
1480
  _lastText = "";
1339
- /** Rewrite the displayed string from the template and the current `value`. */
1481
+ /** A typewriter that has typed nothing yet: the layer is empty on purpose (inspect must not call that "no size"). */
1482
+ get showsNothingYet() {
1483
+ return this._typed && this._lastText === "" && this._template !== "";
1484
+ }
1485
+ /** Rewrite the displayed string from the template, the current `value` and `visibleChars`. */
1340
1486
  _refreshText() {
1341
- const template = this.spec.text ?? "";
1342
- if (!template.includes("{value}")) return;
1343
- const next = template.split("{value}").join(formatValue(this._value.value, this.spec.format));
1487
+ let next = this._template.includes("{value}") ? this._template.split("{value}").join(formatValue(this._counters.value, this.spec.format)) : this._template;
1488
+ if (this._typed) next = Array.from(next).slice(0, Math.max(0, Math.floor(this._counters.visibleChars))).join("");
1344
1489
  if (next === this._lastText) return;
1345
1490
  this._lastText = next;
1346
1491
  this.target.text = next;
@@ -1352,8 +1497,10 @@ var TextSequence = class extends Sequence {
1352
1497
  fill: "#ffffff",
1353
1498
  align: "center"
1354
1499
  };
1355
- const text = new pixi_js.Text({
1356
- text: this.spec.text ?? "",
1500
+ const initialProps = this.spec.initial ?? {};
1501
+ this._template = String(initialProps.text ?? this.spec.text ?? "");
1502
+ const text = new PIXI.Text({
1503
+ text: this._template,
1357
1504
  style: baseStyle,
1358
1505
  label: this.spec.name
1359
1506
  });
@@ -1365,10 +1512,10 @@ var TextSequence = class extends Sequence {
1365
1512
  this.duration = this.parent?.duration ?? this.root.duration;
1366
1513
  }
1367
1514
  if (this.spec.style) {
1368
- const scope = this.scope();
1515
+ const scope2 = this.scope();
1369
1516
  const resolved = normalizeProps(
1370
1517
  this.spec.style,
1371
- scope,
1518
+ scope2,
1372
1519
  { skipKeys: STYLE_OPAQUE_KEYS }
1373
1520
  );
1374
1521
  for (const k of Object.keys(resolved)) {
@@ -1377,22 +1524,36 @@ var TextSequence = class extends Sequence {
1377
1524
  this.intrinsicWidth = text.width;
1378
1525
  this.intrinsicHeight = text.height;
1379
1526
  }
1380
- const initialValue = this.spec.initial?.value;
1381
- if (initialValue !== void 0) this._value.value = resolveNumber(initialValue, this.scope());
1527
+ const scope = this.scope();
1528
+ if (initialProps.value !== void 0) this._counters.value = resolveNumber(initialProps.value, scope);
1529
+ this._typed = initialProps.visibleChars !== void 0 || animates(this.spec.keyframes, "visibleChars");
1530
+ if (initialProps.visibleChars !== void 0) this._counters.visibleChars = resolveNumber(initialProps.visibleChars, scope);
1531
+ const typed = this._typed;
1532
+ this._typed = false;
1382
1533
  this._refreshText();
1383
1534
  if (this._lastText !== "") {
1384
1535
  this.intrinsicWidth = text.width;
1385
1536
  this.intrinsicHeight = text.height;
1386
1537
  }
1538
+ this._typed = typed;
1539
+ this._refreshText();
1387
1540
  this.buildFilters();
1388
1541
  }
1389
1542
  bindTimeline(timeline, offset = 0) {
1390
1543
  if (!this.target) return;
1391
1544
  const colorSpace = this.spec.colorSpace ?? "rgb";
1392
- const stripped = stripKeys2(this.spec, ["fill", "value"]);
1545
+ const stripped = stripKeys2(this.spec, ["fill", "value", "text", "visibleChars"]);
1393
1546
  runSuperWithStrippedSpec(this, stripped, timeline, offset);
1394
- bindFillKeyframes(timeline, this.target, this.spec.keyframes ?? [], this.duration, offset + this.at, colorSpace);
1395
- bindValueKeyframes(timeline, this._value, () => this._refreshText(), this.spec.keyframes ?? [], this.duration, offset + this.at, this.scope());
1547
+ const keyframes = this.spec.keyframes ?? [];
1548
+ const origin = offset + this.at;
1549
+ bindFillKeyframes(timeline, this.target, keyframes, this.duration, origin, colorSpace);
1550
+ const refresh = () => this._refreshText();
1551
+ bindCounterKeyframes(timeline, this._counters, "value", refresh, keyframes, this.duration, origin, this.scope());
1552
+ bindCounterKeyframes(timeline, this._counters, "visibleChars", refresh, keyframes, this.duration, origin, this.scope());
1553
+ bindTextKeyframes(timeline, keyframes, this.duration, origin, () => this._template, (t) => {
1554
+ this._template = t;
1555
+ refresh();
1556
+ }, describeLayer(this.spec));
1396
1557
  }
1397
1558
  };
1398
1559
  function runSuperWithStrippedSpec(inst, strippedSpec, timeline, offset) {
@@ -1428,11 +1589,11 @@ function resolveNumber(v, scope) {
1428
1589
  const out = normalizeProps({ v }, scope).v;
1429
1590
  return typeof out === "number" ? out : Number(out) || 0;
1430
1591
  }
1431
- function bindValueKeyframes(timeline, state, refresh, keyframes, parentDuration, origin, scope) {
1592
+ function bindCounterKeyframes(timeline, state, key, refresh, keyframes, parentDuration, origin, scope) {
1432
1593
  for (const kf of keyframes) {
1433
- const setV = kf.set?.value;
1434
- const fromV = kf.from?.value;
1435
- const toV = kf.to?.value;
1594
+ const setV = kf.set?.[key];
1595
+ const fromV = kf.from?.[key];
1596
+ const toV = kf.to?.[key];
1436
1597
  if (setV === void 0 && fromV === void 0 && toV === void 0) continue;
1437
1598
  const at = origin + resolveAt(kf.at, parentDuration);
1438
1599
  const duration = kf.duration ?? 0;
@@ -1440,25 +1601,40 @@ function bindValueKeyframes(timeline, state, refresh, keyframes, parentDuration,
1440
1601
  const loop = loopVars(kf);
1441
1602
  if (setV !== void 0) {
1442
1603
  const v = resolveNumber(setV, scope);
1443
- revertibleSet(timeline, at, () => state.value, (n) => {
1444
- state.value = n;
1604
+ revertibleSet(timeline, at, () => state[key], (n) => {
1605
+ state[key] = n;
1445
1606
  refresh();
1446
1607
  }, v);
1447
1608
  }
1448
1609
  if (toV !== void 0 && fromV !== void 0) {
1449
1610
  timeline.fromTo(
1450
1611
  state,
1451
- { value: resolveNumber(fromV, scope) },
1452
- { value: resolveNumber(toV, scope), duration, ease, ...loop, onUpdate: refresh },
1612
+ { [key]: resolveNumber(fromV, scope) },
1613
+ { [key]: resolveNumber(toV, scope), duration, ease, ...loop, onUpdate: refresh },
1453
1614
  at
1454
1615
  );
1455
1616
  } else if (toV !== void 0) {
1456
- timeline.to(state, { value: resolveNumber(toV, scope), duration, ease, ...loop, onUpdate: refresh }, at);
1617
+ timeline.to(state, { [key]: resolveNumber(toV, scope), duration, ease, ...loop, onUpdate: refresh }, at);
1457
1618
  } else if (fromV !== void 0) {
1458
- timeline.from(state, { value: resolveNumber(fromV, scope), duration, ease, ...loop, onUpdate: refresh }, at);
1619
+ timeline.from(state, { [key]: resolveNumber(fromV, scope), duration, ease, ...loop, onUpdate: refresh }, at);
1459
1620
  }
1460
1621
  }
1461
1622
  }
1623
+ function bindTextKeyframes(timeline, keyframes, parentDuration, origin, read, write, who) {
1624
+ for (const kf of keyframes) {
1625
+ for (const [what, props] of [["to", kf.to], ["from", kf.from]]) {
1626
+ if (props && props.text !== void 0) {
1627
+ console.warn(`pixi-effects: ${who}: a keyframe's ${what}: { text } cannot tween a string: use set: { text: '\u2026' } at the time it should change`);
1628
+ }
1629
+ }
1630
+ const t = kf.set?.text;
1631
+ if (t === void 0) continue;
1632
+ revertibleSet(timeline, origin + resolveAt(kf.at, parentDuration), read, write, String(t));
1633
+ }
1634
+ }
1635
+ function animates(keyframes, key) {
1636
+ return (keyframes ?? []).some((kf) => [kf.set, kf.to, kf.from].some((p) => p && p[key] !== void 0));
1637
+ }
1462
1638
  function bindFillKeyframes(timeline, text, keyframes, parentDuration, offset, colorSpace) {
1463
1639
  const state = { fill: text.style.fill };
1464
1640
  const writeFill = () => {
@@ -1509,7 +1685,7 @@ var AudioSequence = class extends Sequence {
1509
1685
  if (this.spec.sfx !== void 0) return this._buildSfx(this.spec);
1510
1686
  const spec = this.spec;
1511
1687
  this._source = `asset "${spec.asset}"`;
1512
- const data = await pixi_js.Assets.get(spec.asset);
1688
+ const data = await PIXI.Assets.get(spec.asset);
1513
1689
  this._audioBuffer = data.audioBuffer;
1514
1690
  if (this.duration === void 0) {
1515
1691
  const rest = Math.max(0, (this.parent?.duration ?? this.root.duration) - this.at);
@@ -1680,7 +1856,7 @@ var VideoSequence = class extends Sequence {
1680
1856
  _ctx = null;
1681
1857
  _drawSeq = 0;
1682
1858
  async build() {
1683
- const data = await pixi_js.Assets.get(this.spec.asset);
1859
+ const data = await PIXI.Assets.get(this.spec.asset);
1684
1860
  this._sourceDuration = data.duration;
1685
1861
  this._audioBuffer = this.spec.audio !== false ? data.audioBuffer : null;
1686
1862
  this._cache = new FrameCache(data.sink, { capacity: 30 });
@@ -1693,8 +1869,8 @@ var VideoSequence = class extends Sequence {
1693
1869
  this._canvas = canvas;
1694
1870
  this._ctx = canvas.getContext("2d", { alpha: true });
1695
1871
  if (probeFrame && this._ctx) this._ctx.drawImage(probeFrame, 0, 0);
1696
- const texture = pixi_js.Texture.from(canvas);
1697
- const sprite = new pixi_js.Sprite({ texture, label: this.spec.name });
1872
+ const texture = PIXI.Texture.from(canvas);
1873
+ const sprite = new PIXI.Sprite({ texture, label: this.spec.name });
1698
1874
  sprite.cullable = true;
1699
1875
  this.target = sprite;
1700
1876
  this.intrinsicWidth = w;
@@ -1817,23 +1993,110 @@ function makeGradientFill(g) {
1817
1993
  const ctx = canvas.getContext("2d");
1818
1994
  if (ctx) {
1819
1995
  paintRadial(ctx, RADIAL_SIZE, g);
1820
- return { texture: pixi_js.Texture.from(canvas), textureSpace: "local" };
1996
+ return { texture: PIXI.Texture.from(canvas), textureSpace: "local" };
1821
1997
  }
1822
1998
  }
1823
- return new pixi_js.FillGradient(gradientOptions(g));
1999
+ return new PIXI.FillGradient(gradientOptions(g));
2000
+ }
2001
+ var CURVE_STEPS = 24;
2002
+ var ELLIPSE_STEPS = 128;
2003
+ function flattenSvgPath(d) {
2004
+ const prims = new PIXI.GraphicsPath(d).shapePath.shapePrimitives;
2005
+ const out = [];
2006
+ for (const { shape } of prims) {
2007
+ const poly = shape;
2008
+ if (poly.points && poly.points.length >= 4) out.push({ pts: poly.points.slice(), closed: poly.closePath === true });
2009
+ }
2010
+ return out;
2011
+ }
2012
+ function rectOutline(x, y, w, h, radius) {
2013
+ const r = Math.max(0, Math.min(radius, Math.abs(w) / 2, Math.abs(h) / 2));
2014
+ if (r === 0) return { pts: [x, y, x + w, y, x + w, y + h, x, y + h], closed: true };
2015
+ const pts = [];
2016
+ const corner = (cx, cy, from) => {
2017
+ for (let i = 0; i <= CURVE_STEPS; i++) {
2018
+ const a = from + i / CURVE_STEPS * (Math.PI / 2);
2019
+ pts.push(cx + r * Math.cos(a), cy + r * Math.sin(a));
2020
+ }
2021
+ };
2022
+ corner(x + w - r, y + r, -Math.PI / 2);
2023
+ corner(x + w - r, y + h - r, 0);
2024
+ corner(x + r, y + h - r, Math.PI / 2);
2025
+ corner(x + r, y + r, Math.PI);
2026
+ const startAt = pts.length - 2;
2027
+ const rotated = pts.slice(startAt).concat(pts.slice(0, startAt));
2028
+ return { pts: rotated, closed: true };
2029
+ }
2030
+ function ellipseOutline(cx, cy, rx, ry) {
2031
+ const pts = [];
2032
+ for (let i = 0; i < ELLIPSE_STEPS; i++) {
2033
+ const a = -Math.PI / 2 + i / ELLIPSE_STEPS * Math.PI * 2;
2034
+ pts.push(cx + rx * Math.cos(a), cy + ry * Math.sin(a));
2035
+ }
2036
+ return { pts, closed: true };
2037
+ }
2038
+ function trimPolylines(lines, start, end) {
2039
+ const s = Math.max(0, Math.min(1, start)), e = Math.max(0, Math.min(1, end));
2040
+ if (!(e > s)) return [];
2041
+ const lengths = lines.map(polylineLength);
2042
+ const total = lengths.reduce((a, b) => a + b, 0);
2043
+ if (!(total > 0)) return [];
2044
+ const from = s * total, to = e * total;
2045
+ const out = [];
2046
+ let offset = 0;
2047
+ lines.forEach((line, i) => {
2048
+ const len = lengths[i];
2049
+ const a = Math.max(from - offset, 0), b = Math.min(to - offset, len);
2050
+ offset += len;
2051
+ if (!(b > a) || len === 0) return;
2052
+ if (line.closed && a <= 0 && b >= len) {
2053
+ out.push({ pts: line.pts.slice(), closed: true });
2054
+ return;
2055
+ }
2056
+ out.push({ pts: slice(line, a, b), closed: false });
2057
+ });
2058
+ return out;
2059
+ }
2060
+ function points(line) {
2061
+ return line.closed ? [...line.pts, line.pts[0], line.pts[1]] : line.pts;
2062
+ }
2063
+ function polylineLength(line) {
2064
+ const p = points(line);
2065
+ let len = 0;
2066
+ for (let i = 2; i < p.length; i += 2) len += Math.hypot(p[i] - p[i - 2], p[i + 1] - p[i - 1]);
2067
+ return len;
2068
+ }
2069
+ function slice(line, a, b) {
2070
+ const p = points(line);
2071
+ const out = [];
2072
+ let at = 0;
2073
+ for (let i = 2; i < p.length; i += 2) {
2074
+ const x0 = p[i - 2], y0 = p[i - 1], x1 = p[i], y1 = p[i + 1];
2075
+ const seg = Math.hypot(x1 - x0, y1 - y0);
2076
+ const segEnd = at + seg;
2077
+ if (segEnd > a && at < b && seg > 0) {
2078
+ const t0 = Math.max(a - at, 0) / seg, t1 = Math.min(b - at, seg) / seg;
2079
+ if (out.length === 0) out.push(x0 + (x1 - x0) * t0, y0 + (y1 - y0) * t0);
2080
+ out.push(x0 + (x1 - x0) * t1, y0 + (y1 - y0) * t1);
2081
+ }
2082
+ at = segEnd;
2083
+ if (at >= b) break;
2084
+ }
2085
+ return out;
1824
2086
  }
1825
2087
 
1826
2088
  // src/sequences/Shape.ts
1827
2089
  var STYLE_KEYS = ["fillColor", "fillAlpha", "strokeColor", "strokeAlpha", "strokeWidth"];
1828
2090
  var COLOR_KEYS = /* @__PURE__ */ new Set(["fillColor", "strokeColor"]);
2091
+ var TRIM_KEYS = ["trimStart", "trimEnd"];
1829
2092
  var GEOMETRY_KEYS_BY_SHAPE = {
1830
- rect: ["width", "height", "cornerRadius", "anchorX", "anchorY"],
1831
- circle: ["radius", "anchorX", "anchorY"],
1832
- ellipse: ["radiusX", "radiusY", "anchorX", "anchorY"],
2093
+ rect: ["width", "height", "cornerRadius", "anchorX", "anchorY", ...TRIM_KEYS],
2094
+ circle: ["radius", "anchorX", "anchorY", ...TRIM_KEYS],
2095
+ ellipse: ["radiusX", "radiusY", "anchorX", "anchorY", ...TRIM_KEYS],
1833
2096
  arc: ["radius", "innerRadius", "startAngle", "endAngle"],
1834
- line: [],
1835
- polygon: [],
1836
- path: []
2097
+ line: [...TRIM_KEYS],
2098
+ polygon: [...TRIM_KEYS],
2099
+ path: [...TRIM_KEYS]
1837
2100
  };
1838
2101
  var ANCHORED_SHAPES = /* @__PURE__ */ new Set(["rect", "circle", "ellipse", "arc"]);
1839
2102
  var ShapeSequence = class extends Sequence {
@@ -1851,7 +2114,7 @@ var ShapeSequence = class extends Sequence {
1851
2114
  };
1852
2115
  _liveKeys = new Set(STYLE_KEYS);
1853
2116
  async build() {
1854
- const graphics = new pixi_js.Graphics({ label: this.spec.name });
2117
+ const graphics = new PIXI.Graphics({ label: this.spec.name });
1855
2118
  graphics.cullable = true;
1856
2119
  this.target = graphics;
1857
2120
  if (this.duration === void 0) {
@@ -1866,13 +2129,26 @@ var ShapeSequence = class extends Sequence {
1866
2129
  if (this.spec.shape === "arc") {
1867
2130
  this._state.startAngle ??= 0;
1868
2131
  this._state.endAngle ??= 360;
2132
+ const top = this.spec, ini = this.spec.initial ?? {};
2133
+ if ("trimStart" in top || "trimEnd" in top || "trimStart" in ini || "trimEnd" in ini) {
2134
+ console.warn(`pixi-effects: ${describeLayer(this.spec)}: trimStart / trimEnd do not apply to an arc: animate its endAngle (and startAngle) instead`);
2135
+ }
2136
+ } else {
2137
+ this._state.trimStart ??= 0;
2138
+ this._state.trimEnd ??= 1;
1869
2139
  }
1870
2140
  const gradientSpec = this.spec.fillGradient ?? this.spec.initial?.fillGradient;
1871
2141
  if (gradientSpec) this._state.fillStyle = makeGradientFill(gradientSpec);
1872
2142
  this._drawGeometry = makeDrawGeometry(this.spec, scope, this._state);
2143
+ this._outline = makeOutline(this.spec, scope, this._state);
2144
+ const { trimStart, trimEnd } = this._state;
2145
+ this._state.trimStart = 0;
2146
+ this._state.trimEnd = 1;
1873
2147
  this._drawGeometry(graphics);
1874
2148
  applyState(graphics, this._state);
1875
2149
  const bounds = graphics.getLocalBounds();
2150
+ this._state.trimStart = trimStart;
2151
+ this._state.trimEnd = trimEnd;
1876
2152
  this.intrinsicWidth = bounds.width;
1877
2153
  this.intrinsicHeight = bounds.height;
1878
2154
  if (!ANCHORED_SHAPES.has(this.spec.shape)) {
@@ -1890,14 +2166,37 @@ var ShapeSequence = class extends Sequence {
1890
2166
  }
1891
2167
  this._redraw();
1892
2168
  };
2169
+ this._redraw();
1893
2170
  this.buildFilters();
1894
2171
  }
1895
2172
  _fresh = false;
2173
+ _outline = null;
1896
2174
  _redraw() {
1897
2175
  const graphics = this.target;
2176
+ const s = this._state;
1898
2177
  graphics.clear();
1899
- this._drawGeometry(graphics);
1900
- applyState(graphics, this._state);
2178
+ const trimmed = (s.trimStart ?? 0) > 0 || (s.trimEnd ?? 1) < 1;
2179
+ if (!trimmed || !this._outline || !(s.strokeColor !== void 0 && s.strokeWidth > 0)) {
2180
+ this._drawGeometry(graphics);
2181
+ applyState(graphics, s);
2182
+ return;
2183
+ }
2184
+ if (s.fillStyle || s.fillColor !== void 0) {
2185
+ this._drawGeometry(graphics);
2186
+ applyFill(graphics, s);
2187
+ graphics.beginPath();
2188
+ }
2189
+ const parts = trimPolylines(this._outline(), s.trimStart ?? 0, s.trimEnd ?? 1);
2190
+ if (parts.length === 0) return;
2191
+ for (const part of parts) {
2192
+ if (part.closed) {
2193
+ graphics.poly(part.pts, true);
2194
+ continue;
2195
+ }
2196
+ graphics.moveTo(part.pts[0], part.pts[1]);
2197
+ for (let i = 2; i < part.pts.length; i += 2) graphics.lineTo(part.pts[i], part.pts[i + 1]);
2198
+ }
2199
+ applyStroke(graphics, s);
1901
2200
  }
1902
2201
  /**
1903
2202
  * Draw from the tweened `_state` before the culler runs. The culler measures the geometry as last
@@ -2000,11 +2299,17 @@ function resolveLiveValue(key, value, scope) {
2000
2299
  return numOrZero(value, scope);
2001
2300
  }
2002
2301
  function applyState(g, s) {
2302
+ applyFill(g, s);
2303
+ applyStroke(g, s);
2304
+ }
2305
+ function applyFill(g, s) {
2003
2306
  if (s.fillStyle) {
2004
2307
  g.fill(s.fillStyle);
2005
2308
  } else if (s.fillColor !== void 0) {
2006
2309
  g.fill({ color: s.fillColor, alpha: s.fillAlpha });
2007
2310
  }
2311
+ }
2312
+ function applyStroke(g, s) {
2008
2313
  if (s.strokeColor !== void 0 && s.strokeWidth > 0) {
2009
2314
  g.stroke({
2010
2315
  color: s.strokeColor,
@@ -2089,7 +2394,7 @@ function makePolygonDraw(spec, scope) {
2089
2394
  }
2090
2395
  function makePathDraw(spec) {
2091
2396
  return (g) => {
2092
- g.path(new pixi_js.GraphicsPath(spec.d));
2397
+ g.path(new PIXI.GraphicsPath(spec.d));
2093
2398
  };
2094
2399
  }
2095
2400
  var STATE_DRAWERS = {
@@ -2112,6 +2417,41 @@ function makeDrawGeometry(spec, scope, state) {
2112
2417
  return () => {
2113
2418
  };
2114
2419
  }
2420
+ function makeOutline(spec, scope, s) {
2421
+ switch (spec.shape) {
2422
+ case "rect":
2423
+ return () => {
2424
+ const w = s.width ?? 0, h = s.height ?? 0;
2425
+ return [rectOutline(0 - (s.anchorX ?? 0.5) * w, 0 - (s.anchorY ?? 0.5) * h, w, h, s.cornerRadius ?? 0)];
2426
+ };
2427
+ case "circle":
2428
+ return () => {
2429
+ const r = s.radius ?? 0;
2430
+ return [ellipseOutline((0.5 - (s.anchorX ?? 0.5)) * 2 * r, (0.5 - (s.anchorY ?? 0.5)) * 2 * r, r, r)];
2431
+ };
2432
+ case "ellipse":
2433
+ return () => {
2434
+ const rx = s.radiusX ?? 0, ry = s.radiusY ?? 0;
2435
+ return [ellipseOutline((0.5 - (s.anchorX ?? 0.5)) * 2 * rx, (0.5 - (s.anchorY ?? 0.5)) * 2 * ry, rx, ry)];
2436
+ };
2437
+ case "line": {
2438
+ const lines = [{ pts: [num(spec.from[0], scope), num(spec.from[1], scope), num(spec.to[0], scope), num(spec.to[1], scope)], closed: false }];
2439
+ return () => lines;
2440
+ }
2441
+ case "polygon": {
2442
+ const pts = [];
2443
+ for (const [x, y] of spec.points) pts.push(num(x, scope), num(y, scope));
2444
+ const lines = pts.length >= 4 ? [{ pts, closed: !spec.open }] : [];
2445
+ return () => lines;
2446
+ }
2447
+ case "path": {
2448
+ let lines = null;
2449
+ return () => lines ??= flattenSvgPath(spec.d);
2450
+ }
2451
+ default:
2452
+ return null;
2453
+ }
2454
+ }
2115
2455
  function bindLiveKeyframes(timeline, state, liveKeys, keyframes, parentDuration, scope, offset, colorSpace) {
2116
2456
  for (const kf of keyframes) {
2117
2457
  const at = offset + resolveAt(kf.at, parentDuration);
@@ -2185,7 +2525,7 @@ function numOrZero(v, scope) {
2185
2525
  var _CompositionSequence = null;
2186
2526
  async function getCompositionSequence() {
2187
2527
  if (!_CompositionSequence) {
2188
- _CompositionSequence = (await import('./Composition-63EXHNJJ.cjs')).CompositionSequence;
2528
+ _CompositionSequence = (await import('./Composition-3KHQPRBA.cjs')).CompositionSequence;
2189
2529
  }
2190
2530
  return _CompositionSequence;
2191
2531
  }
@@ -2232,6 +2572,7 @@ exports.buildSequenceTree = buildSequenceTree;
2232
2572
  exports.cameraBasis = cameraBasis;
2233
2573
  exports.collectPropKeys = collectPropKeys;
2234
2574
  exports.describeLayer = describeLayer;
2575
+ exports.ensureFilterLibrary = ensureFilterLibrary;
2235
2576
  exports.evaluateExpr = evaluateExpr;
2236
2577
  exports.homeCamera = homeCamera;
2237
2578
  exports.homeDistance = homeDistance;
@@ -2241,5 +2582,5 @@ exports.projectLayer = projectLayer;
2241
2582
  exports.registerSequenceType = registerSequenceType;
2242
2583
  exports.resolveAt = resolveAt;
2243
2584
  exports.summarizeWarnings = summarizeWarnings;
2244
- //# sourceMappingURL=chunk-2RR43OIN.cjs.map
2245
- //# sourceMappingURL=chunk-2RR43OIN.cjs.map
2585
+ //# sourceMappingURL=chunk-6I55T4OH.cjs.map
2586
+ //# sourceMappingURL=chunk-6I55T4OH.cjs.map