@vectojs/core 1.22.0 → 1.24.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/dist/index.js CHANGED
@@ -9,7 +9,7 @@
9
9
 
10
10
 
11
11
 
12
- var _chunkLHONR3GOjs = require('./chunk-LHONR3GO.js');
12
+ var _chunkKEBYJVD6js = require('./chunk-KEBYJVD6.js');
13
13
 
14
14
 
15
15
 
@@ -308,12 +308,16 @@ var ComputeParticleEntity = (_class = class extends _chunkAGP4VLF4js.Entity {
308
308
  * The backend holds one resident SoA store, so a Scene with multiple particle
309
309
  * entities reuses it sequentially — origin is therefore re-gathered each call
310
310
  * (not upload-once), a couple of extra f32 reads per particle.
311
+ *
312
+ * Returns `false` if the kernel declined the call and {@link updateCPU} ran
313
+ * instead, so the Scene can report which path actually simulated this frame
314
+ * rather than assuming an installed backend did the work.
311
315
  */
312
316
  stepWithBackend(backend, dt, mouseX, mouseY, width, height) {
313
317
  const count = this.maxParticles;
314
318
  backend.ensure(count);
315
319
  backend.gather(this.particleData, count, true);
316
- this._wasmPending = backend.step(count, {
320
+ const pending = backend.step(count, {
317
321
  dt,
318
322
  mouseX,
319
323
  mouseY,
@@ -325,8 +329,14 @@ var ComputeParticleEntity = (_class = class extends _chunkAGP4VLF4js.Entity {
325
329
  maxVelocity: this.maxVelocity,
326
330
  explosion: this.pendingExplosion
327
331
  });
332
+ if (pending === null) {
333
+ this.updateCPU(dt, mouseX, mouseY, width, height);
334
+ return false;
335
+ }
336
+ this._wasmPending = pending;
328
337
  backend.scatter(this.particleData, count);
329
338
  this.pendingExplosion = null;
339
+ return true;
330
340
  }
331
341
  destroy() {
332
342
  this.destroyGPUResources();
@@ -470,148 +480,6 @@ function buildTreeStore(root) {
470
480
  return { store, indexOf };
471
481
  }
472
482
 
473
- // src/wasm/hit-store.ts
474
- function gatherHitAABBs(root, currentFrame) {
475
- const slotEntity = [];
476
- const boundless = [];
477
- const minxs = [];
478
- const minys = [];
479
- const maxxs = [];
480
- const maxys = [];
481
- const scratch = { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 };
482
- const visit = (node) => {
483
- const index = slotEntity.length;
484
- slotEntity.push(node);
485
- const bounds = node.getBounds();
486
- if (bounds === null) {
487
- boundless.push({ entity: node, index });
488
- minxs.push(0);
489
- minys.push(0);
490
- maxxs.push(0);
491
- maxys.push(0);
492
- } else {
493
- if (!node._readWorldCache(currentFrame, scratch)) {
494
- const t = node.getWorldTransform();
495
- scratch.a = t.a;
496
- scratch.b = t.b;
497
- scratch.c = t.c;
498
- scratch.d = t.d;
499
- scratch.e = t.e;
500
- scratch.f = t.f;
501
- }
502
- const { a, b, c, d, e, f } = scratch;
503
- let minX = Infinity;
504
- let minY = Infinity;
505
- let maxX = -Infinity;
506
- let maxY = -Infinity;
507
- for (let i = 0; i < 4; i++) {
508
- const lx = i & 1 ? bounds.x + bounds.width : bounds.x;
509
- const ly = i & 2 ? bounds.y + bounds.height : bounds.y;
510
- const wx = a * lx + c * ly + e;
511
- const wy = b * lx + d * ly + f;
512
- if (wx < minX) minX = wx;
513
- if (wx > maxX) maxX = wx;
514
- if (wy < minY) minY = wy;
515
- if (wy > maxY) maxY = wy;
516
- }
517
- minxs.push(minX);
518
- minys.push(minY);
519
- maxxs.push(maxX);
520
- maxys.push(maxY);
521
- }
522
- const kids = node.children;
523
- for (let i = 0; i < kids.length; i++) visit(kids[i]);
524
- };
525
- visit(root);
526
- return {
527
- count: slotEntity.length,
528
- slotEntity,
529
- boundless,
530
- minx: Float64Array.from(minxs),
531
- miny: Float64Array.from(minys),
532
- maxx: Float64Array.from(maxxs),
533
- maxy: Float64Array.from(maxys)
534
- };
535
- }
536
-
537
- // src/wasm/hit-store-fused.ts
538
- function gatherHitAABBsFromStore(root, aabbs, storeSlotEntity, out) {
539
- const slotEntity = out.slotEntity;
540
- const boundless = out.boundless;
541
- slotEntity.length = 0;
542
- boundless.length = 0;
543
- let count = 0;
544
- let capacity = out.minx.length;
545
- let minx = out.minx;
546
- let miny = out.miny;
547
- let maxx = out.maxx;
548
- let maxy = out.maxy;
549
- const grow = (needed) => {
550
- if (needed <= capacity) return;
551
- let next = capacity || 256;
552
- while (next < needed) next *= 2;
553
- const nminx = new Float64Array(next);
554
- const nminy = new Float64Array(next);
555
- const nmaxx = new Float64Array(next);
556
- const nmaxy = new Float64Array(next);
557
- nminx.set(minx);
558
- nminy.set(miny);
559
- nmaxx.set(maxx);
560
- nmaxy.set(maxy);
561
- minx = nminx;
562
- miny = nminy;
563
- maxx = nmaxx;
564
- maxy = nmaxy;
565
- capacity = next;
566
- };
567
- let bailed = false;
568
- const visit = (node) => {
569
- if (bailed) return;
570
- const index = count;
571
- slotEntity.push(node);
572
- count++;
573
- grow(count);
574
- if (node.getBounds() === null) {
575
- boundless.push({ entity: node, index });
576
- minx[index] = 0;
577
- miny[index] = 0;
578
- maxx[index] = 0;
579
- maxy[index] = 0;
580
- } else {
581
- const slot = node._storeSlot;
582
- if (slot < 0 || slot >= aabbs.aminx.length || storeSlotEntity[slot] !== node) {
583
- bailed = true;
584
- return;
585
- }
586
- minx[index] = aabbs.aminx[slot];
587
- miny[index] = aabbs.aminy[slot];
588
- maxx[index] = aabbs.amaxx[slot];
589
- maxy[index] = aabbs.amaxy[slot];
590
- }
591
- const children = node.children;
592
- for (let i = 0; i < children.length; i++) visit(children[i]);
593
- };
594
- visit(root);
595
- if (bailed) return null;
596
- out.count = count;
597
- out.minx = minx;
598
- out.miny = miny;
599
- out.maxx = maxx;
600
- out.maxy = maxy;
601
- return out;
602
- }
603
- function createHitGatherBuffer() {
604
- return {
605
- count: 0,
606
- slotEntity: [],
607
- boundless: [],
608
- minx: new Float64Array(256),
609
- miny: new Float64Array(256),
610
- maxx: new Float64Array(256),
611
- maxy: new Float64Array(256)
612
- };
613
- }
614
-
615
483
  // src/wasm/backend.ts
616
484
  var WASM_STATUS = {
617
485
  OK: 0,
@@ -704,7 +572,10 @@ var WasmTransformBackend = (_class2 = class {
704
572
  return status;
705
573
  }
706
574
  /** Upload only the run table + count (topology), leaving per-entity inputs to
707
- * the resident views. Call when the tree structure changes, not per frame. */
575
+ * the resident views. Call when the tree structure changes, not per frame.
576
+ * Returns `false` if the crate rejected the run count, in which case the
577
+ * PREVIOUS topology is still published and any kernel run against it would
578
+ * compose the wrong tree — the caller must not proceed. */
708
579
  uploadRuns(store) {
709
580
  this.ensure(store.count, store.runCount);
710
581
  const rc = store.runCount;
@@ -712,6 +583,7 @@ var WasmTransformBackend = (_class2 = class {
712
583
  this.vrs.set(store.runStart.subarray(0, rc));
713
584
  this.vrl.set(store.runLen.subarray(0, rc));
714
585
  this.lastStatus = this.ex.set_run_count(rc);
586
+ return this.lastStatus === WASM_STATUS.OK;
715
587
  }
716
588
  /**
717
589
  * Status of the most recent kernel or run-table call. `WASM_STATUS.OK` unless
@@ -760,7 +632,8 @@ var WasmTransformBackend = (_class2 = class {
760
632
  this.vby.set(store.by.subarray(0, n));
761
633
  this.vbw.set(store.bw.subarray(0, n));
762
634
  this.vbh.set(store.bh.subarray(0, n));
763
- this.ex.compute_aabbs(n);
635
+ this.lastStatus = this.ex.compute_aabbs(n);
636
+ if (this.lastStatus !== WASM_STATUS.OK) return;
764
637
  store.aminx.set(this.vaminx.subarray(0, n));
765
638
  store.aminy.set(this.vaminy.subarray(0, n));
766
639
  store.amaxx.set(this.vamaxx.subarray(0, n));
@@ -768,9 +641,12 @@ var WasmTransformBackend = (_class2 = class {
768
641
  }
769
642
  /** Run the AABB pass only, over `count` entities already resident in wasm
770
643
  * memory (bounds written via {@link boundsView}, world matrices already
771
- * composed). No upload/readback — the per-frame resident path. */
644
+ * composed). No upload/readback — the per-frame resident path. Returns
645
+ * `false` if the kernel rejected `count` (beyond capacity, or uninitialized),
646
+ * in which case {@link aabbView} still holds the previous frame's bounds. */
772
647
  runAabbs(count) {
773
- this.ex.compute_aabbs(count);
648
+ this.lastStatus = this.ex.compute_aabbs(count);
649
+ return this.lastStatus === WASM_STATUS.OK;
774
650
  }
775
651
  /** Resident wasm local-bounds input views (`bx,by,bw,bh`) for the AABB pass. */
776
652
  boundsView() {
@@ -844,6 +720,148 @@ var WasmTransformBackend = (_class2 = class {
844
720
  }
845
721
  }, _class2);
846
722
 
723
+ // src/wasm/hit-store.ts
724
+ function gatherHitAABBs(root, currentFrame) {
725
+ const slotEntity = [];
726
+ const boundless = [];
727
+ const minxs = [];
728
+ const minys = [];
729
+ const maxxs = [];
730
+ const maxys = [];
731
+ const scratch = { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 };
732
+ const visit = (node) => {
733
+ const index = slotEntity.length;
734
+ slotEntity.push(node);
735
+ const bounds = node.getBounds();
736
+ if (bounds === null) {
737
+ boundless.push({ entity: node, index });
738
+ minxs.push(0);
739
+ minys.push(0);
740
+ maxxs.push(0);
741
+ maxys.push(0);
742
+ } else {
743
+ if (!node._readWorldCache(currentFrame, scratch)) {
744
+ const t = node.getWorldTransform();
745
+ scratch.a = t.a;
746
+ scratch.b = t.b;
747
+ scratch.c = t.c;
748
+ scratch.d = t.d;
749
+ scratch.e = t.e;
750
+ scratch.f = t.f;
751
+ }
752
+ const { a, b, c, d, e, f } = scratch;
753
+ let minX = Infinity;
754
+ let minY = Infinity;
755
+ let maxX = -Infinity;
756
+ let maxY = -Infinity;
757
+ for (let i = 0; i < 4; i++) {
758
+ const lx = i & 1 ? bounds.x + bounds.width : bounds.x;
759
+ const ly = i & 2 ? bounds.y + bounds.height : bounds.y;
760
+ const wx = a * lx + c * ly + e;
761
+ const wy = b * lx + d * ly + f;
762
+ if (wx < minX) minX = wx;
763
+ if (wx > maxX) maxX = wx;
764
+ if (wy < minY) minY = wy;
765
+ if (wy > maxY) maxY = wy;
766
+ }
767
+ minxs.push(minX);
768
+ minys.push(minY);
769
+ maxxs.push(maxX);
770
+ maxys.push(maxY);
771
+ }
772
+ const kids = node.children;
773
+ for (let i = 0; i < kids.length; i++) visit(kids[i]);
774
+ };
775
+ visit(root);
776
+ return {
777
+ count: slotEntity.length,
778
+ slotEntity,
779
+ boundless,
780
+ minx: Float64Array.from(minxs),
781
+ miny: Float64Array.from(minys),
782
+ maxx: Float64Array.from(maxxs),
783
+ maxy: Float64Array.from(maxys)
784
+ };
785
+ }
786
+
787
+ // src/wasm/hit-store-fused.ts
788
+ function gatherHitAABBsFromStore(root, aabbs, storeSlotEntity, out) {
789
+ const slotEntity = out.slotEntity;
790
+ const boundless = out.boundless;
791
+ slotEntity.length = 0;
792
+ boundless.length = 0;
793
+ let count = 0;
794
+ let capacity = out.minx.length;
795
+ let minx = out.minx;
796
+ let miny = out.miny;
797
+ let maxx = out.maxx;
798
+ let maxy = out.maxy;
799
+ const grow = (needed) => {
800
+ if (needed <= capacity) return;
801
+ let next = capacity || 256;
802
+ while (next < needed) next *= 2;
803
+ const nminx = new Float64Array(next);
804
+ const nminy = new Float64Array(next);
805
+ const nmaxx = new Float64Array(next);
806
+ const nmaxy = new Float64Array(next);
807
+ nminx.set(minx);
808
+ nminy.set(miny);
809
+ nmaxx.set(maxx);
810
+ nmaxy.set(maxy);
811
+ minx = nminx;
812
+ miny = nminy;
813
+ maxx = nmaxx;
814
+ maxy = nmaxy;
815
+ capacity = next;
816
+ };
817
+ let bailed = false;
818
+ const visit = (node) => {
819
+ if (bailed) return;
820
+ const index = count;
821
+ slotEntity.push(node);
822
+ count++;
823
+ grow(count);
824
+ if (node.getBounds() === null) {
825
+ boundless.push({ entity: node, index });
826
+ minx[index] = 0;
827
+ miny[index] = 0;
828
+ maxx[index] = 0;
829
+ maxy[index] = 0;
830
+ } else {
831
+ const slot = node._storeSlot;
832
+ if (slot < 0 || slot >= aabbs.aminx.length || storeSlotEntity[slot] !== node) {
833
+ bailed = true;
834
+ return;
835
+ }
836
+ minx[index] = aabbs.aminx[slot];
837
+ miny[index] = aabbs.aminy[slot];
838
+ maxx[index] = aabbs.amaxx[slot];
839
+ maxy[index] = aabbs.amaxy[slot];
840
+ }
841
+ const children = node.children;
842
+ for (let i = 0; i < children.length; i++) visit(children[i]);
843
+ };
844
+ visit(root);
845
+ if (bailed) return null;
846
+ out.count = count;
847
+ out.minx = minx;
848
+ out.miny = miny;
849
+ out.maxx = maxx;
850
+ out.maxy = maxy;
851
+ return out;
852
+ }
853
+ function createHitGatherBuffer() {
854
+ return {
855
+ count: 0,
856
+ slotEntity: [],
857
+ boundless: [],
858
+ minx: new Float64Array(256),
859
+ miny: new Float64Array(256),
860
+ maxx: new Float64Array(256),
861
+ maxy: new Float64Array(256)
862
+ };
863
+ }
864
+
847
865
  // src/wasm/anim-backend.ts
848
866
  var PAD3 = 8;
849
867
  var AnimBackend = (_class3 = class {
@@ -852,7 +870,7 @@ var AnimBackend = (_class3 = class {
852
870
  __init13() {this.tweenCap = 0}
853
871
 
854
872
 
855
- constructor(instance) {;_class3.prototype.__init12.call(this);_class3.prototype.__init13.call(this);
873
+ constructor(instance) {;_class3.prototype.__init12.call(this);_class3.prototype.__init13.call(this);_class3.prototype.__init14.call(this);
856
874
  this.ex = instance.exports;
857
875
  }
858
876
  /** The resident spring SoA input/output views, valid until the next capacity
@@ -878,14 +896,33 @@ var AnimBackend = (_class3 = class {
878
896
  this.ex.anim_init(this.springCap, this.tweenCap);
879
897
  this.refreshViews();
880
898
  }
881
- /** Advance `count` springs (from index 0) by `dtMs` milliseconds, in place. */
899
+ /**
900
+ * Advance `count` springs (from index 0) by `dtMs` milliseconds, in place.
901
+ * Returns `true` when the kernel ran. `false` means it rejected the call
902
+ * (count beyond the capacity {@link ensure} allocated, or no `anim_init` yet)
903
+ * and wrote nothing, so the caller must tick those drivers in JS instead of
904
+ * scattering back a pack the kernel never touched. See {@link lastStatus}.
905
+ */
882
906
  stepSprings(dtMs, count) {
883
- this.ex.spring_step(dtMs / 1e3, count);
907
+ this.lastStatus = this.ex.spring_step(dtMs / 1e3, count);
908
+ return this.lastStatus === WASM_STATUS.OK;
884
909
  }
885
- /** Advance `count` tweens (from index 0) by `dtMs` milliseconds, writing `val`. */
910
+ /**
911
+ * Advance `count` tweens (from index 0) by `dtMs` milliseconds, writing `val`.
912
+ * Returns `true` when the kernel ran; `false` means it rejected the call and
913
+ * wrote nothing (see {@link stepSprings}). `elapsed` is kernel-side state, so
914
+ * a rejected tween pack must not be read back — it is unadvanced, not
915
+ * partially advanced.
916
+ */
886
917
  stepTweens(dtMs, count) {
887
- this.ex.tween_step(dtMs, count);
918
+ this.lastStatus = this.ex.tween_step(dtMs, count);
919
+ return this.lastStatus === WASM_STATUS.OK;
888
920
  }
921
+ /**
922
+ * Status of the most recent kernel call — `WASM_STATUS.OK` unless the kernel
923
+ * declined it. Mirrors {@link TransformBackend.lastStatus}.
924
+ */
925
+ __init14() {this.lastStatus = WASM_STATUS.OK}
889
926
  refreshViews() {
890
927
  const buf = this.ex.memory.buffer;
891
928
  const sCap = this.springCap;
@@ -915,9 +952,9 @@ var PAD4 = 8;
915
952
  var CELLS_PER_ENTITY_HINT = 4;
916
953
  var HitTestBackend = (_class4 = class {
917
954
 
918
- __init14() {this.entityCap = 0}
919
- __init15() {this.cellCap = 0}
920
- __init16() {this.itemCap = 0}
955
+ __init15() {this.entityCap = 0}
956
+ __init16() {this.cellCap = 0}
957
+ __init17() {this.itemCap = 0}
921
958
 
922
959
 
923
960
 
@@ -926,10 +963,10 @@ var HitTestBackend = (_class4 = class {
926
963
 
927
964
 
928
965
  /** Grid geometry from the last {@link ensure} call. */
929
- __init17() {this.gridW = 0}
930
- __init18() {this.gridH = 0}
931
- __init19() {this.cellSize = 64}
932
- constructor(instance) {;_class4.prototype.__init14.call(this);_class4.prototype.__init15.call(this);_class4.prototype.__init16.call(this);_class4.prototype.__init17.call(this);_class4.prototype.__init18.call(this);_class4.prototype.__init19.call(this);
966
+ __init18() {this.gridW = 0}
967
+ __init19() {this.gridH = 0}
968
+ __init20() {this.cellSize = 64}
969
+ constructor(instance) {;_class4.prototype.__init15.call(this);_class4.prototype.__init16.call(this);_class4.prototype.__init17.call(this);_class4.prototype.__init18.call(this);_class4.prototype.__init19.call(this);_class4.prototype.__init20.call(this);
933
970
  this.ex = instance.exports;
934
971
  }
935
972
  /** The resident AABB input views (`minx/miny/maxx/maxy`), valid until the
@@ -1013,9 +1050,9 @@ var HitTestBackend = (_class4 = class {
1013
1050
  var PAD5 = 8;
1014
1051
  var ParticleBackend = (_class5 = class {
1015
1052
 
1016
- __init20() {this.cap = 0}
1053
+ __init21() {this.cap = 0}
1017
1054
 
1018
- constructor(instance) {;_class5.prototype.__init20.call(this);
1055
+ constructor(instance) {;_class5.prototype.__init21.call(this);_class5.prototype.__init22.call(this);
1019
1056
  this.ex = instance.exports;
1020
1057
  }
1021
1058
  /** The resident SoA views, valid until the next capacity growth. */
@@ -1036,6 +1073,12 @@ var ParticleBackend = (_class5 = class {
1036
1073
  * Advance `count` particles one step in place. Returns `true` when at least
1037
1074
  * one live particle is still moving or off-origin beyond epsilon (the fused
1038
1075
  * `hasPendingAnimations` flag), so the caller need not re-scan the buffer.
1076
+ *
1077
+ * Returns `null` when the kernel REJECTED the call — `count` beyond the
1078
+ * capacity {@link ensure} allocated, or no `particle_init` yet. Nothing was
1079
+ * written, so the caller must NOT {@link scatter} (that would write the
1080
+ * gathered pre-step values back and freeze the simulation) and should fall
1081
+ * back to the JS `updateCPU` path for this frame. See {@link lastStatus}.
1039
1082
  */
1040
1083
  step(count, p) {
1041
1084
  const e = p.explosion;
@@ -1055,8 +1098,18 @@ var ParticleBackend = (_class5 = class {
1055
1098
  e ? e.force : 0,
1056
1099
  count
1057
1100
  );
1101
+ if (flag < 0) {
1102
+ this.lastStatus = -flag;
1103
+ return null;
1104
+ }
1105
+ this.lastStatus = WASM_STATUS.OK;
1058
1106
  return flag !== 0;
1059
1107
  }
1108
+ /**
1109
+ * Status of the most recent {@link step} — `WASM_STATUS.OK` unless the kernel
1110
+ * declined it. Mirrors {@link TransformBackend.lastStatus}.
1111
+ */
1112
+ __init22() {this.lastStatus = WASM_STATUS.OK}
1060
1113
  /** Transpose the AoS stride-8 buffer into the SoA views (position/velocity/
1061
1114
  * life every frame; origin upload-once when `withOrigin`). */
1062
1115
  gather(data, count, withOrigin) {
@@ -1147,11 +1200,11 @@ async function loadCoreWasmModule(source) {
1147
1200
  }
1148
1201
  var CoreWasmRuntime = (_class6 = class {
1149
1202
 
1150
- __init21() {this.transformBackend = null}
1151
- __init22() {this.animBackend = null}
1152
- __init23() {this.hitBackend = null}
1153
- __init24() {this.particleBackendInstance = null}
1154
- constructor(instance) {;_class6.prototype.__init21.call(this);_class6.prototype.__init22.call(this);_class6.prototype.__init23.call(this);_class6.prototype.__init24.call(this);
1203
+ __init23() {this.transformBackend = null}
1204
+ __init24() {this.animBackend = null}
1205
+ __init25() {this.hitBackend = null}
1206
+ __init26() {this.particleBackendInstance = null}
1207
+ constructor(instance) {;_class6.prototype.__init23.call(this);_class6.prototype.__init24.call(this);_class6.prototype.__init25.call(this);_class6.prototype.__init26.call(this);
1155
1208
  this.instance = instance;
1156
1209
  }
1157
1210
  /**
@@ -1190,6 +1243,79 @@ async function loadCoreWasmRuntime(source) {
1190
1243
  return createCoreWasmRuntime(module);
1191
1244
  }
1192
1245
 
1246
+ // src/performance/UserTiming.ts
1247
+ var VECTO_USER_TIMING = {
1248
+ scene: {
1249
+ transform: "vecto:scene:transform",
1250
+ drawWalk: "vecto:scene:draw-walk",
1251
+ entityPaint: "vecto:scene:entity-paint",
1252
+ flush: "vecto:scene:flush",
1253
+ a11ySync: "vecto:scene:a11y-sync"
1254
+ },
1255
+ markdown: {
1256
+ parse: "vecto:markdown:parse"
1257
+ }
1258
+ };
1259
+ var nextSpanId = 0;
1260
+ function beginVectoUserTiming(name) {
1261
+ const candidate = globalThis.performance;
1262
+ if (typeof _optionalChain([candidate, 'optionalAccess', _13 => _13.mark]) !== "function" || typeof candidate.measure !== "function") {
1263
+ return null;
1264
+ }
1265
+ const id = nextSpanId++;
1266
+ const startMark = `${name}:start:${id}`;
1267
+ const endMark = `${name}:end:${id}`;
1268
+ try {
1269
+ candidate.mark(startMark);
1270
+ return {
1271
+ name,
1272
+ startMark,
1273
+ endMark,
1274
+ performance: candidate
1275
+ };
1276
+ } catch (e7) {
1277
+ return null;
1278
+ }
1279
+ }
1280
+ function endVectoUserTiming(span) {
1281
+ if (!span) return;
1282
+ const timing = span.performance;
1283
+ try {
1284
+ timing.mark(span.endMark);
1285
+ timing.measure(span.name, span.startMark, span.endMark);
1286
+ } catch (e8) {
1287
+ } finally {
1288
+ try {
1289
+ _optionalChain([timing, 'access', _14 => _14.clearMarks, 'optionalCall', _15 => _15(span.startMark)]);
1290
+ _optionalChain([timing, 'access', _16 => _16.clearMarks, 'optionalCall', _17 => _17(span.endMark)]);
1291
+ } catch (e9) {
1292
+ }
1293
+ }
1294
+ }
1295
+ function measureVectoUserTiming(name, durationMs) {
1296
+ const candidate = globalThis.performance;
1297
+ if (typeof _optionalChain([candidate, 'optionalAccess', _18 => _18.now]) !== "function" || typeof candidate.mark !== "function" || typeof candidate.measure !== "function") {
1298
+ return;
1299
+ }
1300
+ const id = nextSpanId++;
1301
+ const startMark = `${name}:start:${id}`;
1302
+ const endMark = `${name}:end:${id}`;
1303
+ const endTime = candidate.now();
1304
+ const startTime = Math.max(0, endTime - Math.max(0, durationMs));
1305
+ try {
1306
+ candidate.mark(startMark, { startTime });
1307
+ candidate.mark(endMark, { startTime: endTime });
1308
+ candidate.measure(name, startMark, endMark);
1309
+ } catch (e10) {
1310
+ } finally {
1311
+ try {
1312
+ _optionalChain([candidate, 'access', _19 => _19.clearMarks, 'optionalCall', _20 => _20(startMark)]);
1313
+ _optionalChain([candidate, 'access', _21 => _21.clearMarks, 'optionalCall', _22 => _22(endMark)]);
1314
+ } catch (e11) {
1315
+ }
1316
+ }
1317
+ }
1318
+
1193
1319
  // src/tree/Scene.ts
1194
1320
  var _text = require('@vectojs/text'); _createStarExport(_text);
1195
1321
  var RANGE_VALUE_ROLES = /* @__PURE__ */ new Set(["slider", "spinbutton", "progressbar", "scrollbar", "meter"]);
@@ -1204,9 +1330,47 @@ var INTERACTIVE_A11Y_ROLES = /* @__PURE__ */ new Set([
1204
1330
  "slider",
1205
1331
  "combobox"
1206
1332
  ]);
1333
+ var A11Y_REQUIRED_OWNED = /* @__PURE__ */ new Map([
1334
+ ["grid", /* @__PURE__ */ new Set(["row", "rowgroup"])],
1335
+ ["table", /* @__PURE__ */ new Set(["row", "rowgroup"])],
1336
+ ["treegrid", /* @__PURE__ */ new Set(["row", "rowgroup"])],
1337
+ ["rowgroup", /* @__PURE__ */ new Set(["row"])],
1338
+ ["row", /* @__PURE__ */ new Set(["cell", "columnheader", "gridcell", "rowheader"])],
1339
+ ["tablist", /* @__PURE__ */ new Set(["tab"])],
1340
+ ["tree", /* @__PURE__ */ new Set(["treeitem", "group"])],
1341
+ ["group", /* @__PURE__ */ new Set(["treeitem", "menuitem", "menuitemcheckbox", "menuitemradio", "option"])],
1342
+ ["menu", /* @__PURE__ */ new Set(["menuitem", "menuitemcheckbox", "menuitemradio", "group", "separator"])],
1343
+ ["menubar", /* @__PURE__ */ new Set(["menuitem", "menuitemcheckbox", "menuitemradio", "group", "separator"])],
1344
+ ["listbox", /* @__PURE__ */ new Set(["option", "group"])],
1345
+ ["list", /* @__PURE__ */ new Set(["listitem"])]
1346
+ ]);
1207
1347
  function isNativelyFocusable(element) {
1208
1348
  return element instanceof HTMLButtonElement || element instanceof HTMLInputElement || element instanceof HTMLSelectElement || element instanceof HTMLTextAreaElement || element instanceof HTMLAnchorElement && element.hasAttribute("href");
1209
1349
  }
1350
+ var REBASED_BOX = { left: 0, top: 0, matrix: "" };
1351
+ function rebaseChildBox(parent, parentOriginX, parentOriginY, child, childOriginX, childOriginY) {
1352
+ const det = parent.a * parent.d - parent.b * parent.c;
1353
+ if (!Number.isFinite(det) || Math.abs(det) < 1e-12) {
1354
+ REBASED_BOX.left = 0;
1355
+ REBASED_BOX.top = 0;
1356
+ REBASED_BOX.matrix = "matrix(1, 0, 0, 1, 0, 0)";
1357
+ return REBASED_BOX;
1358
+ }
1359
+ const ia = parent.d / det;
1360
+ const ib = -parent.b / det;
1361
+ const ic = -parent.c / det;
1362
+ const id = parent.a / det;
1363
+ const dx = childOriginX - parentOriginX;
1364
+ const dy = childOriginY - parentOriginY;
1365
+ REBASED_BOX.left = ia * dx + ic * dy;
1366
+ REBASED_BOX.top = ib * dx + id * dy;
1367
+ const a = ia * child.a + ic * child.b;
1368
+ const b = ib * child.a + id * child.b;
1369
+ const c = ia * child.c + ic * child.d;
1370
+ const d = ib * child.c + id * child.d;
1371
+ REBASED_BOX.matrix = `matrix(${a}, ${b}, ${c}, ${d}, 0, 0)`;
1372
+ return REBASED_BOX;
1373
+ }
1210
1374
  var REDUCED_MOTION_FPS = 30;
1211
1375
  function parseInlinePx(value) {
1212
1376
  if (!value || !value.endsWith("px")) return null;
@@ -1292,9 +1456,9 @@ function parseCssMatrix(transform) {
1292
1456
  }
1293
1457
  function clientToGridLocal(contentEl, canvas, clientX, clientY) {
1294
1458
  const line = contentEl.querySelector("[data-vecto-grid-line]");
1295
- const originMarker = _optionalChain([line, 'optionalAccess', _13 => _13.querySelector, 'call', _14 => _14('[data-vecto-grid-basis="origin"]')]);
1296
- const xMarker = _optionalChain([line, 'optionalAccess', _15 => _15.querySelector, 'call', _16 => _16('[data-vecto-grid-basis="x"]')]);
1297
- const yMarker = _optionalChain([line, 'optionalAccess', _17 => _17.querySelector, 'call', _18 => _18('[data-vecto-grid-basis="y"]')]);
1459
+ const originMarker = _optionalChain([line, 'optionalAccess', _23 => _23.querySelector, 'call', _24 => _24('[data-vecto-grid-basis="origin"]')]);
1460
+ const xMarker = _optionalChain([line, 'optionalAccess', _25 => _25.querySelector, 'call', _26 => _26('[data-vecto-grid-basis="x"]')]);
1461
+ const yMarker = _optionalChain([line, 'optionalAccess', _27 => _27.querySelector, 'call', _28 => _28('[data-vecto-grid-basis="y"]')]);
1298
1462
  if (line && originMarker && xMarker && yMarker) {
1299
1463
  const origin = originMarker.getBoundingClientRect();
1300
1464
  const xPoint = xMarker.getBoundingClientRect();
@@ -1378,7 +1542,7 @@ function nearestTextPositionInLine(line, x, y) {
1378
1542
  nearest = { position: { node, offset }, distance: candidate.distance };
1379
1543
  }
1380
1544
  }
1381
- return _nullishCoalesce(_optionalChain([nearest, 'optionalAccess', _19 => _19.position]), () => ( null));
1545
+ return _nullishCoalesce(_optionalChain([nearest, 'optionalAccess', _29 => _29.position]), () => ( null));
1382
1546
  }
1383
1547
  function nearestTextPositionInProjection(contentEl, canvas, x, y, eventTarget) {
1384
1548
  if (contentEl.dataset.vectoContentGrid !== void 0) {
@@ -1392,7 +1556,7 @@ function nearestTextPositionInProjection(contentEl, canvas, x, y, eventTarget) {
1392
1556
  }
1393
1557
  targetLine = targetLine.parentElement;
1394
1558
  }
1395
- if (_optionalChain([targetLine, 'optionalAccess', _20 => _20.parentElement]) === contentEl) {
1559
+ if (_optionalChain([targetLine, 'optionalAccess', _30 => _30.parentElement]) === contentEl) {
1396
1560
  return nearestTextPositionInLine(targetLine, x, y);
1397
1561
  }
1398
1562
  let bestLine = null;
@@ -1479,13 +1643,13 @@ function extendSelection(selection, anchor, focus) {
1479
1643
  try {
1480
1644
  selection.setBaseAndExtent(anchor.node, anchor.offset, focus.node, focus.offset);
1481
1645
  return;
1482
- } catch (e7) {
1646
+ } catch (e12) {
1483
1647
  }
1484
1648
  try {
1485
1649
  selection.collapse(anchor.node, anchor.offset);
1486
1650
  selection.extend(focus.node, focus.offset);
1487
1651
  return;
1488
- } catch (e8) {
1652
+ } catch (e13) {
1489
1653
  }
1490
1654
  const anchorRange = document.createRange();
1491
1655
  anchorRange.setStart(anchor.node, anchor.offset);
@@ -1520,15 +1684,15 @@ var Scene = (_class7 = class _Scene {
1520
1684
 
1521
1685
 
1522
1686
 
1523
- __init25() {this.isRunning = false}
1687
+ __init27() {this.isRunning = false}
1524
1688
  /** Whether the canvas is at least partially in the viewport. When it scrolls
1525
1689
  * fully off-screen the rAF loop pauses (stops rescheduling) instead of
1526
1690
  * burning frames on a scene nobody can see; an IntersectionObserver resumes
1527
1691
  * it on re-entry. Defaults true (and stays true where IntersectionObserver
1528
1692
  * is unavailable, e.g. SSR/jsdom, so behavior is unchanged there). */
1529
- __init26() {this._canvasOnScreen = true}
1530
- __init27() {this._canvasObserver = null}
1531
- __init28() {this.lastTime = 0}
1693
+ __init28() {this._canvasOnScreen = true}
1694
+ __init29() {this._canvasObserver = null}
1695
+ __init30() {this.lastTime = 0}
1532
1696
 
1533
1697
  /**
1534
1698
  * Redraw strategy:
@@ -1537,11 +1701,12 @@ var Scene = (_class7 = class _Scene {
1537
1701
  * {@link markDirty}) or while an animation is pending. Ideal for static /
1538
1702
  * event-driven UIs where idle frames should cost ~0.
1539
1703
  */
1540
- __init29() {this.renderMode = "always"}
1704
+ __init31() {this.renderMode = "always"}
1541
1705
  /** Cap on distinct recorded dirty reasons (see `recordDirtyReason`). */
1542
1706
  static __initStatic4() {this.MAX_DIRTY_REASONS = 200}
1543
- __init30() {this._phaseTiming = false}
1544
- __init31() {this._phaseTotals = /* @__PURE__ */ new Map()}
1707
+ __init32() {this._phaseTiming = false}
1708
+ __init33() {this._userTiming = false}
1709
+ __init34() {this._phaseTotals = /* @__PURE__ */ new Map()}
1545
1710
  /**
1546
1711
  * Start or stop per-phase render timing.
1547
1712
  *
@@ -1563,6 +1728,19 @@ var Scene = (_class7 = class _Scene {
1563
1728
  get phaseTiming() {
1564
1729
  return this._phaseTiming;
1565
1730
  }
1731
+ /**
1732
+ * Enable or disable browser User Timing phase instrumentation.
1733
+ *
1734
+ * Off by default. The disabled frame path performs only boolean checks and
1735
+ * emits no Performance Timeline entries.
1736
+ */
1737
+ setUserTiming(enabled) {
1738
+ this._userTiming = enabled;
1739
+ }
1740
+ /** Whether browser User Timing phase instrumentation is enabled. */
1741
+ get userTiming() {
1742
+ return this._userTiming;
1743
+ }
1566
1744
  /**
1567
1745
  * Accumulate one phase sample.
1568
1746
  *
@@ -1604,32 +1782,32 @@ var Scene = (_class7 = class _Scene {
1604
1782
  clearRenderPhases() {
1605
1783
  this._phaseTotals.clear();
1606
1784
  }
1607
- __init32() {this._dirtyTracking = false}
1608
- __init33() {this._dirtyReasons = /* @__PURE__ */ new Map()}
1609
- __init34() {this.dirty = true}
1785
+ __init35() {this._dirtyTracking = false}
1786
+ __init36() {this._dirtyReasons = /* @__PURE__ */ new Map()}
1787
+ __init37() {this.dirty = true}
1610
1788
  /** Whether to throttle rendering to 2 FPS when the scene is static to save power. */
1611
- __init35() {this.autoThrottle = true}
1789
+ __init38() {this.autoThrottle = true}
1612
1790
  // --- Frame telemetry (read via `frameStats`) ---------------------------
1613
1791
  /** Wall-clock ms spent inside the last `render()` call. */
1614
- __init36() {this._lastFrameMs = 0}
1792
+ __init39() {this._lastFrameMs = 0}
1615
1793
  /** Rolling exponential average of rendered-frame intervals, in ms. */
1616
- __init37() {this._avgFrameIntervalMs = 0}
1794
+ __init40() {this._avgFrameIntervalMs = 0}
1617
1795
  /** dt (ms) handed to the last rendered frame. */
1618
- __init38() {this._lastDt = 0}
1796
+ __init41() {this._lastDt = 0}
1619
1797
  /** Count of frames actually rendered since the loop started. */
1620
- __init39() {this._renderedFrames = 0}
1798
+ __init42() {this._renderedFrames = 0}
1621
1799
  /** Count of rAF ticks skipped (idle / capped) since the loop started. */
1622
- __init40() {this._skippedFrames = 0}
1800
+ __init43() {this._skippedFrames = 0}
1623
1801
  /** `time` of the previous *rendered* frame, for interval measurement. */
1624
- __init41() {this._lastRenderTick = 0}
1802
+ __init44() {this._lastRenderTick = 0}
1625
1803
  /**
1626
1804
  * Frame-rate cap (power saving). `0` = uncapped (native refresh). When set,
1627
1805
  * the loop renders at most `maxFPS` times per second; animations still run,
1628
1806
  * just less often. See {@link SceneOptions.maxFPS}.
1629
1807
  */
1630
- __init42() {this.maxFPS = 60}
1808
+ __init45() {this.maxFPS = 60}
1631
1809
  /** Whether the OS prefers-reduced-motion setting auto-caps the loop. */
1632
- __init43() {this.respectReducedMotion = true}
1810
+ __init46() {this.respectReducedMotion = true}
1633
1811
  /**
1634
1812
  * Reading direction for accessibility tab/traversal order (`'ltr'` default,
1635
1813
  * `'rtl'`). Controls the inline sort within a visual row in
@@ -1645,18 +1823,18 @@ var Scene = (_class7 = class _Scene {
1645
1823
  this.a11yNeedsReorder = true;
1646
1824
  }
1647
1825
  }
1648
- __init44() {this._readingDirection = "ltr"}
1826
+ __init47() {this._readingDirection = "ltr"}
1649
1827
  /** Cached media-query list; `.matches` is read live each frame. */
1650
- __init45() {this.reducedMotionQuery = null}
1828
+ __init48() {this.reducedMotionQuery = null}
1651
1829
  /** Cached `(forced-colors: active)` query (Windows High Contrast etc.). A
1652
1830
  * canvas gets NO automatic forced-colors treatment from the browser (it's
1653
1831
  * opaque pixels), so components must read {@link forcedColors} and repaint
1654
1832
  * with system colors themselves; a change listener repaints idle scenes. */
1655
- __init46() {this.forcedColorsQuery = null}
1656
- __init47() {this.forcedColorsChangeHandler = null}
1833
+ __init49() {this.forcedColorsQuery = null}
1834
+ __init50() {this.forcedColorsChangeHandler = null}
1657
1835
  /** True when the OS asks for reduced motion and we respect it. Read by the animation drivers. */
1658
1836
  get prefersReducedMotion() {
1659
- return this.respectReducedMotion && !!_optionalChain([this, 'access', _21 => _21.reducedMotionQuery, 'optionalAccess', _22 => _22.matches]);
1837
+ return this.respectReducedMotion && !!_optionalChain([this, 'access', _31 => _31.reducedMotionQuery, 'optionalAccess', _32 => _32.matches]);
1660
1838
  }
1661
1839
  /**
1662
1840
  * True when the OS is in a forced-colors mode (Windows High Contrast, and the
@@ -1667,28 +1845,28 @@ var Scene = (_class7 = class _Scene {
1667
1845
  * when the setting toggles.
1668
1846
  */
1669
1847
  get forcedColors() {
1670
- return !!_optionalChain([this, 'access', _23 => _23.forcedColorsQuery, 'optionalAccess', _24 => _24.matches]);
1848
+ return !!_optionalChain([this, 'access', _33 => _33.forcedColorsQuery, 'optionalAccess', _34 => _34.matches]);
1671
1849
  }
1672
1850
  /**
1673
1851
  * Throttle interval (ms) for the a11y/automation shadow sync. `0` = every
1674
1852
  * frame. See {@link SceneOptions.a11ySyncInterval}.
1675
1853
  */
1676
- __init48() {this.a11ySyncInterval = 0}
1854
+ __init51() {this.a11ySyncInterval = 0}
1677
1855
  /** Timestamp of the last a11y sync, for throttling. */
1678
- __init49() {this.lastA11ySync = -Infinity}
1856
+ __init52() {this.lastA11ySync = -Infinity}
1679
1857
  /** True if we skipped an a11y sync during animation and need to sync when at rest. */
1680
- __init50() {this.a11yPendingSyncAfterAnimation = false}
1858
+ __init53() {this.a11yPendingSyncAfterAnimation = false}
1681
1859
  // A11y / Automation Layer. `null` in non-DOM (SSR/Node) environments — the
1682
1860
  // whole projection degrades to a no-op so the engine's logic stays usable
1683
1861
  // server-side (e.g. headless layout / vector export) without jsdom.
1684
1862
 
1685
- __init51() {this.a11yElements = /* @__PURE__ */ new Map()}
1863
+ __init54() {this.a11yElements = /* @__PURE__ */ new Map()}
1686
1864
  /** DOM nodes mirroring static text content, keyed by entity id. */
1687
- __init52() {this.contentElements = /* @__PURE__ */ new Map()}
1865
+ __init55() {this.contentElements = /* @__PURE__ */ new Map()}
1688
1866
  /** Pending cold font-calibration frame per projected grid entity. */
1689
- __init53() {this.contentGridCalibrationFrames = /* @__PURE__ */ new Map()}
1867
+ __init56() {this.contentGridCalibrationFrames = /* @__PURE__ */ new Map()}
1690
1868
  /** Detached, untransformed font probes used by the cold calibration pass. */
1691
- __init54() {this.contentGridCalibrationProbes = /* @__PURE__ */ new Map()}
1869
+ __init57() {this.contentGridCalibrationProbes = /* @__PURE__ */ new Map()}
1692
1870
  /**
1693
1871
  * Monotonic stamp identifying the conditions grid cells were calibrated under.
1694
1872
  *
@@ -1709,69 +1887,71 @@ var Scene = (_class7 = class _Scene {
1709
1887
  * A plain incrementing integer rather than the descriptive calibration key,
1710
1888
  * because it goes into an attribute selector and must not need escaping.
1711
1889
  */
1712
- __init55() {this.contentGridCalibrationGeneration = 0}
1890
+ __init58() {this.contentGridCalibrationGeneration = 0}
1713
1891
  /** The `(fontEpoch, pageScale)` pair the current generation corresponds to. */
1714
- __init56() {this.contentGridCalibrationStamp = ""}
1892
+ __init59() {this.contentGridCalibrationStamp = ""}
1715
1893
  /** Invalidates grid font calibration after browser font availability changes. */
1716
- __init57() {this.contentFontEpoch = 0}
1894
+ __init60() {this.contentFontEpoch = 0}
1717
1895
  /** Cached Canvas-to-client scale for the current font/viewport epoch. */
1718
- __init58() {this.contentMetricScaleEpoch = -1}
1719
- __init59() {this.contentMetricScaleX = 1}
1720
- __init60() {this.contentProjectionEnabled = true}
1896
+ __init61() {this.contentMetricScaleEpoch = -1}
1897
+ __init62() {this.contentMetricScaleX = 1}
1898
+ __init63() {this.contentProjectionEnabled = true}
1721
1899
  // Virtualization margin (px) for content projection; `undefined` → one
1722
1900
  // viewport height, resolved at sync time. `Infinity` = materialize everything.
1723
- __init61() {this.contentProjectionMargin = void 0}
1901
+ __init64() {this.contentProjectionMargin = void 0}
1724
1902
  /**
1725
1903
  * True while a text-selection drag that started on a projection's blank
1726
1904
  * region (no text node under the press) is being driven manually — the
1727
1905
  * browser has no native anchor for it, so mousemove extends the Selection
1728
1906
  * from the position we resolved ourselves.
1729
1907
  */
1730
- __init62() {this.blankRegionSelectionDrag = false}
1731
- __init63() {this.contentSelectionAnchor = null}
1732
- __init64() {this.contentSelectionEndListener = null}
1908
+ __init65() {this.blankRegionSelectionDrag = false}
1909
+ __init66() {this.contentSelectionAnchor = null}
1910
+ __init67() {this.contentSelectionEndListener = null}
1733
1911
  // Animation/interactive flags collected during the render walk (tree-walk
1734
1912
  // fusion): the loop reads last frame's answers instead of re-walking the
1735
1913
  // tree up to 4× per tick. Start true so the first tick stays conservative.
1736
- __init65() {this.frameHadAnimation = true}
1737
- __init66() {this.frameHadInteractive = true}
1914
+ __init68() {this.frameHadAnimation = true}
1915
+ __init69() {this.frameHadInteractive = true}
1738
1916
 
1739
1917
  /** Active `(resolution: Ndppx)` media query watching for a runtime DPR change
1740
1918
  * (window moved between monitors, browser zoom) so the canvas backing store
1741
1919
  * can be re-scaled — otherwise it stays rasterized at the old DPR and blurs.
1742
1920
  * A resolution media query only fires when leaving its exact value, so the
1743
1921
  * handler re-arms a fresh query for the new DPR each time. */
1744
- __init67() {this.dprMediaQuery = null}
1922
+ __init70() {this.dprMediaQuery = null}
1745
1923
  /** For embedded (`disableWindowResize`) scenes: observes the canvas element so
1746
1924
  * a CSS/layout-driven size change re-runs `resize()`. A window `resize`
1747
1925
  * listener never fires for these (the window isn't what changed), so without
1748
1926
  * this an embedded canvas stayed at its initial size forever. */
1749
- __init68() {this.canvasResizeObserver = null}
1750
- __init69() {this.dprChangeHandler = null}
1751
- __init70() {this.focusedA11yElement = null}
1927
+ __init71() {this.canvasResizeObserver = null}
1928
+ __init72() {this.dprChangeHandler = null}
1929
+ __init73() {this.focusedA11yElement = null}
1752
1930
  /** Last geometry `syncOverlayGeometry` wrote, so an unchanged frame can skip the
1753
1931
  * style writes entirely. Reset to `null` to force the next sync (a new overlay
1754
1932
  * layer was created and has never been positioned). */
1755
- __init71() {this._overlayGeometry = null}
1933
+ __init74() {this._overlayGeometry = null}
1756
1934
  /** Shadow elements the pointer is currently inside. Lets a removal that happens
1757
1935
  * mid-hover synthesize the `pointerleave` the browser never sends for a
1758
1936
  * detached element, so the entity doesn't keep its hover state. */
1759
- __init72() {this.hoveredA11yElements = /* @__PURE__ */ new WeakSet()}
1937
+ __init75() {this.hoveredA11yElements = /* @__PURE__ */ new WeakSet()}
1760
1938
  /** Persistent tabindex=-1 element in a11yRoot. When the focused a11y mirror is
1761
1939
  * pruned (virtualization/streaming/removal) while it holds focus, we move
1762
1940
  * focus here instead of letting the browser drop it to <body> — keeping the
1763
1941
  * screen-reader virtual cursor inside the scene's a11y region. */
1764
- __init73() {this.focusSentinel = null}
1765
- __init74() {this.caretBlinkTimer = null}
1766
- __init75() {this.a11yNeedsReorder = true}
1767
- __init76() {this.portalRoot = null}
1768
- __init77() {this.fullViewportElements = []}
1769
- __init78() {this.normalElements = []}
1770
- __init79() {this.activeIds = /* @__PURE__ */ new Set()}
1771
- __init80() {this.activePortalsThisFrame = /* @__PURE__ */ new Set()}
1772
- __init81() {this.activePortalsPrevFrame = /* @__PURE__ */ new Set()}
1773
- __init82() {this.portalEntities = /* @__PURE__ */ new Map()}
1774
- __init83() {this.renderOrderCounter = 0}
1942
+ __init76() {this.focusSentinel = null}
1943
+ __init77() {this.caretBlinkTimer = null}
1944
+ __init78() {this.a11yNeedsReorder = true}
1945
+ __init79() {this.portalRoot = null}
1946
+ __init80() {this.fullViewportElements = []}
1947
+ __init81() {this.normalElements = []}
1948
+ __init82() {this.activeIds = /* @__PURE__ */ new Set()}
1949
+ /** Per-parent insertion cursor, reused by `enforceA11yDomOrder`. */
1950
+ __init83() {this.a11yOrderCursors = /* @__PURE__ */ new Map()}
1951
+ __init84() {this.activePortalsThisFrame = /* @__PURE__ */ new Set()}
1952
+ __init85() {this.activePortalsPrevFrame = /* @__PURE__ */ new Set()}
1953
+ __init86() {this.portalEntities = /* @__PURE__ */ new Map()}
1954
+ __init87() {this.renderOrderCounter = 0}
1775
1955
  /**
1776
1956
  * Monotonic render-frame counter, bumped once per authoritative `render()`
1777
1957
  * pass. Entities stamp their per-frame world-matrix cache with this value and
@@ -1780,7 +1960,7 @@ var Scene = (_class7 = class _Scene {
1780
1960
  * back to the ancestor walk. Public for the same reason `Entity._getTrig`/
1781
1961
  * `_setWorldCache` are: it is a cross-class render-internal contract.
1782
1962
  */
1783
- __init84() {this.currentFrame = 0}
1963
+ __init88() {this.currentFrame = 0}
1784
1964
  // ── WASM transform backend (invisible accelerator) ──────────────────────────
1785
1965
  // When `_transformBackend === 'wasm'`, the main render walk sources each
1786
1966
  // entity's world matrix from an SoA store composed by `_wasm` (see
@@ -1788,24 +1968,24 @@ var Scene = (_class7 = class _Scene {
1788
1968
  // fallback and the default: a null backend, a non-main renderer, or any entity
1789
1969
  // absent from the store all fall back to the JS composition, so WASM can only
1790
1970
  // ever change *how fast* a world matrix is produced, never *what* it is.
1791
- __init85() {this._wasm = null}
1792
- __init86() {this._transformBackend = "js"}
1971
+ __init89() {this._wasm = null}
1972
+ __init90() {this._transformBackend = "js"}
1793
1973
  // Resident store state (Stage 3). The store layout — slot assignment + sibling
1794
1974
  // runs — depends only on tree TOPOLOGY, so it is rebuilt only when the
1795
1975
  // structure changes (add/remove/reparent bump `_structureVersion`). Between
1796
1976
  // rebuilds the per-frame cost is: gather each entity's transform into the
1797
1977
  // resident wasm input view + run the kernel — no reallocation, no readback.
1798
- __init87() {this._treeStore = null}
1799
- __init88() {this._slotEntity = []}
1978
+ __init91() {this._treeStore = null}
1979
+ __init92() {this._slotEntity = []}
1800
1980
  // store slot -> entity (also validates slots)
1801
- __init89() {this._wasmInputs = null}
1802
- __init90() {this._wasmWorld = null}
1803
- __init91() {this._structureVersion = 0}
1804
- __init92() {this._storeStructureVersion = -1}
1981
+ __init93() {this._wasmInputs = null}
1982
+ __init94() {this._wasmWorld = null}
1983
+ __init95() {this._structureVersion = 0}
1984
+ __init96() {this._storeStructureVersion = -1}
1805
1985
  // Cached list of ComputeParticleEntity instances in the tree, keyed by the
1806
1986
  // structure version it was gathered at. Rebuilt only on a topology change.
1807
- __init93() {this._computeEntities = []}
1808
- __init94() {this._computeEntitiesVersion = -1}
1987
+ __init97() {this._computeEntities = []}
1988
+ __init98() {this._computeEntitiesVersion = -1}
1809
1989
  /** Invalidate the resident WASM store layout; the next wasm-mode frame rebuilds
1810
1990
  * it. Called by `Entity.add`/`remove` (topology changes only). */
1811
1991
  markStructureChanged() {
@@ -1867,10 +2047,13 @@ var Scene = (_class7 = class _Scene {
1867
2047
  return true;
1868
2048
  }
1869
2049
  // ── WASM hit-test backend (invisible accelerator, G3) ───────────────────────
1870
- // A separate WASM module instance from the transform backend (each crate
1871
- // export lives in independent linear memory per instance, so there is no
1872
- // shared-state hazard in running both) that indexes the main tree's world
1873
- // AABBs into a dense viewport grid for findEntityAt. The JS depth-first walk
2050
+ // Served by the same instance as every other accelerator (see
2051
+ // `_wasmRuntime`): the crate keeps transform/anim/hit/particle in distinct
2052
+ // statics, so one instance runs them all without aliasing. It indexes the
2053
+ // main tree's world AABBs into a dense viewport grid for findEntityAt. Note
2054
+ // that sharing one linear memory means an allocation here can grow it and
2055
+ // detach views built over the old buffer, so each backend re-checks buffer
2056
+ // identity (`revalidateViews`) before use. The JS depth-first walk
1874
2057
  // (findHitRecursively) is the permanent fallback: a null backend, a build
1875
2058
  // that overflows its item budget, or the overlay tree (never indexed — small
1876
2059
  // and rare, not worth accelerating) all fall through to it, so WASM can only
@@ -1887,7 +2070,7 @@ var Scene = (_class7 = class _Scene {
1887
2070
  * cached globally; the instance is per-Scene, which is the isolation that
1888
2071
  * actually matters.
1889
2072
  */
1890
- __init95() {this._wasmRuntime = null}
2073
+ __init99() {this._wasmRuntime = null}
1891
2074
  /**
1892
2075
  * Load (or reuse) this Scene's shared WASM runtime.
1893
2076
  *
@@ -1915,34 +2098,98 @@ var Scene = (_class7 = class _Scene {
1915
2098
  get wasmRuntime() {
1916
2099
  return this._wasmRuntime;
1917
2100
  }
1918
- __init96() {this._hitWasm = null}
2101
+ __init100() {this._hitWasm = null}
1919
2102
  // Cache key: which frame + structure version the grid was last (successfully,
1920
2103
  // non-overflowing) built for. findEntityAt is called ad-hoc (pointer
1921
2104
  // hover/click), not every frame, so the grid is refreshed lazily on demand
1922
2105
  // rather than proactively every render() — unlike the transform store, which
1923
2106
  // every frame's draw depends on.
1924
- __init97() {this._hitGridFrame = -1}
1925
- __init98() {this._hitGridOk = false}
1926
- __init99() {this._hitSlotEntity = []}
1927
- __init100() {this._hitBoundless = []}
2107
+ __init101() {this._hitGridFrame = -1}
2108
+ __init102() {this._hitGridOk = false}
2109
+ __init103() {this._hitSlotEntity = []}
2110
+ __init104() {this._hitBoundless = []}
1928
2111
  /** Reused buffer for the fused gather, so a pointer query allocates nothing. */
1929
- __init101() {this._hitGatherBuffer = null}
2112
+ __init105() {this._hitGatherBuffer = null}
1930
2113
  /**
1931
2114
  * Whether the last grid build sourced its AABBs from the WASM transform store
1932
2115
  * rather than recomputing them in JS. Diagnostic only — both paths must
1933
2116
  * produce the same entity for a given point.
1934
2117
  */
1935
- __init102() {this._hitFusedGather = false}
2118
+ __init106() {this._hitFusedGather = false}
1936
2119
  /**
1937
2120
  * Whether `compute_aabbs` has run against the current frame's world matrices.
1938
2121
  * The AABB pass is only meaningful after a `compose_*`, so the fused gather
1939
2122
  * must not read the views before then.
1940
2123
  */
1941
- __init103() {this._wasmAabbsFresh = false}
2124
+ __init107() {this._wasmAabbsFresh = false}
1942
2125
  /** Did the last hit-grid build use the fused (WASM-store) gather? */
1943
2126
  get hitGatherPath() {
1944
2127
  return this._hitFusedGather ? "fused" : "js";
1945
2128
  }
2129
+ /**
2130
+ * Why the transform accelerator did or did not run on the most recent frame.
2131
+ * Written by the render walk and `_syncWasmStore`.
2132
+ */
2133
+ __init108() {this._transformReason = "not-installed"}
2134
+ /** Why the batched-driver accelerator did or did not run. */
2135
+ __init109() {this._animReason = "not-installed"}
2136
+ /**
2137
+ * Why the hit-test accelerator did or did not serve the last pointer query.
2138
+ * The grid is built lazily on demand, not every frame, so this describes the
2139
+ * most recent BUILD. Starts at `'not-installed'` because that is the truth
2140
+ * before a backend exists; `_ensureHitGrid` moves it to `'not-applicable'`
2141
+ * once one is installed but nothing has queried yet.
2142
+ */
2143
+ __init110() {this._hitReason = "not-installed"}
2144
+ /** Why the particle accelerator did or did not run. */
2145
+ __init111() {this._particleReason = "not-applicable"}
2146
+ /** Which particle implementation actually simulated the most recent frame. */
2147
+ __init112() {this._particlePath = "none"}
2148
+ /**
2149
+ * Per-frame status of every invisible accelerator: whether each is installed,
2150
+ * whether it actually ran on the most recent frame, and why.
2151
+ *
2152
+ * This exists because the older per-accelerator getters
2153
+ * ({@link transformBackend}, {@link animBackend}, {@link hitTestBackend},
2154
+ * {@link particleBackend}) report only that a backend is INSTALLED. Reading
2155
+ * `'wasm'` from one of those and concluding the accelerator is doing work is
2156
+ * wrong whenever a gate never opens, a kernel rejects its arguments, or a
2157
+ * faster backend takes the pass instead. Read {@link AcceleratorStatus.reason}
2158
+ * for which of those happened.
2159
+ *
2160
+ * Reflects the most recent main-renderer frame; a secondary renderer (SVG
2161
+ * export, offscreen snapshot) does not overwrite it.
2162
+ */
2163
+ get accelerators() {
2164
+ return {
2165
+ transform: {
2166
+ available: this._wasm !== null && this._transformBackend === "wasm",
2167
+ activeThisFrame: this._transformReason === "active",
2168
+ reason: this._transformReason,
2169
+ path: this._transformReason === "active" ? "wasm" : "js"
2170
+ },
2171
+ animation: {
2172
+ available: this._animWasm !== null,
2173
+ activeThisFrame: this._animBatchedLastFrame,
2174
+ reason: this._animReason,
2175
+ path: this._animBatchedLastFrame ? "wasm" : "js"
2176
+ },
2177
+ hitTest: {
2178
+ available: this._hitWasm !== null,
2179
+ // The grid is built lazily on a pointer query, not every frame, so this
2180
+ // describes the last BUILD rather than the last frame.
2181
+ activeThisFrame: this._hitReason === "active",
2182
+ reason: this._hitReason,
2183
+ path: this._hitReason !== "active" ? "js" : this._hitFusedGather ? "wasm-fused" : "wasm"
2184
+ },
2185
+ particle: {
2186
+ available: this._particleWasm !== null || this.webgpuActive,
2187
+ activeThisFrame: this._particleReason === "active",
2188
+ reason: this._particleReason,
2189
+ path: this._particlePath
2190
+ }
2191
+ };
2192
+ }
1946
2193
  /** Which backend answers `findEntityAt` for the main tree. */
1947
2194
  get hitTestBackend() {
1948
2195
  return this._hitWasm ? "wasm" : "js";
@@ -1952,6 +2199,7 @@ var Scene = (_class7 = class _Scene {
1952
2199
  setHitTestBackend(backend) {
1953
2200
  this._hitWasm = backend;
1954
2201
  this._hitGridFrame = -1;
2202
+ this._hitReason = backend ? "not-applicable" : "not-installed";
1955
2203
  }
1956
2204
  /**
1957
2205
  * Asynchronously instantiate the WASM hit-test core and, on success, hot-swap
@@ -1978,7 +2226,10 @@ var Scene = (_class7 = class _Scene {
1978
2226
  */
1979
2227
  _ensureHitGrid() {
1980
2228
  const backend = this._hitWasm;
1981
- if (!backend) return false;
2229
+ if (!backend) {
2230
+ this._hitReason = "not-installed";
2231
+ return false;
2232
+ }
1982
2233
  if (this._hitGridFrame === this.currentFrame) return this._hitGridOk;
1983
2234
  let gathered = null;
1984
2235
  if (this._wasm && this._ensureWasmAabbs()) {
@@ -2007,6 +2258,7 @@ var Scene = (_class7 = class _Scene {
2007
2258
  this._hitBoundless = gathered.boundless;
2008
2259
  this._hitGridFrame = this.currentFrame;
2009
2260
  this._hitGridOk = ok;
2261
+ this._hitReason = ok ? "active" : "rejected";
2010
2262
  return ok;
2011
2263
  }
2012
2264
  /**
@@ -2029,7 +2281,7 @@ var Scene = (_class7 = class _Scene {
2029
2281
  const idx = cell[k];
2030
2282
  if (x < minx[idx] || x > maxx[idx] || y < miny[idx] || y > maxy[idx]) continue;
2031
2283
  const entity = this._hitSlotEntity[idx];
2032
- if (_optionalChain([entity, 'optionalAccess', _25 => _25.isPointInside, 'call', _26 => _26(x, y)]) && this.isHitEligible(entity, x, y)) {
2284
+ if (_optionalChain([entity, 'optionalAccess', _35 => _35.isPointInside, 'call', _36 => _36(x, y)]) && this.isHitEligible(entity, x, y)) {
2033
2285
  bestIndex = idx;
2034
2286
  bestEntity = entity;
2035
2287
  break;
@@ -2052,25 +2304,25 @@ var Scene = (_class7 = class _Scene {
2052
2304
  // EasingFn (which cannot cross into WASM) all fall through to it — WASM can
2053
2305
  // only ever change *how* a driver is advanced, never *what* value it lands
2054
2306
  // on.
2055
- __init104() {this._animWasm = null}
2307
+ __init113() {this._animWasm = null}
2056
2308
  // Entities with at least one active driver, added by Entity._spawnDriver.
2057
2309
  // Self-pruning: _tickBatchedDrivers drops an entry the first time it visits
2058
2310
  // an entity whose drivers have since all completed or been removed. This is
2059
2311
  // what lets the batch pass find its candidates in O(active drivers), not
2060
2312
  // O(tree size) — the exact mistake G3's first integrated benchmark made.
2061
- __init105() {this._activeDriverEntities = /* @__PURE__ */ new Set()}
2313
+ __init114() {this._activeDriverEntities = /* @__PURE__ */ new Set()}
2062
2314
  // Reused across frames instead of allocating a fresh array + N {entity,prop,
2063
2315
  // driver} objects every call — the integrated benchmark
2064
2316
  // (benchmarks/anim-wasm-scene) found that allocation churn was the
2065
2317
  // dominant integrated cost, not the wasm kernel itself. Parallel arrays,
2066
2318
  // truncated to the live count after each use so a stale tail slot never
2067
2319
  // pins a no-longer-active entity/driver in memory.
2068
- __init106() {this._springEntities = []}
2069
- __init107() {this._springProps = []}
2070
- __init108() {this._springDrivers = []}
2071
- __init109() {this._tweenEntities = []}
2072
- __init110() {this._tweenProps = []}
2073
- __init111() {this._tweenDrivers = []}
2320
+ __init115() {this._springEntities = []}
2321
+ __init116() {this._springProps = []}
2322
+ __init117() {this._springDrivers = []}
2323
+ __init118() {this._tweenEntities = []}
2324
+ __init119() {this._tweenProps = []}
2325
+ __init120() {this._tweenDrivers = []}
2074
2326
  /**
2075
2327
  * Minimum number of batchable (spring, or named-easing tween) active drivers
2076
2328
  * before a frame engages the WASM batch path at all; below it, every driver
@@ -2142,7 +2394,7 @@ var Scene = (_class7 = class _Scene {
2142
2394
  * Setting {@link animDriverGateCount} overwrites all three, so existing code
2143
2395
  * that tuned the single knob keeps working unchanged.
2144
2396
  */
2145
- __init112() {this._animBatchedLastFrame = false}
2397
+ __init121() {this._animBatchedLastFrame = false}
2146
2398
  /**
2147
2399
  * Whether the WASM batch path actually ran on the most recent frame.
2148
2400
  *
@@ -2154,7 +2406,7 @@ var Scene = (_class7 = class _Scene {
2154
2406
  get animBatchedLastFrame() {
2155
2407
  return this._animBatchedLastFrame;
2156
2408
  }
2157
- __init113() {this.animGate = {
2409
+ __init122() {this.animGate = {
2158
2410
  spring: 128,
2159
2411
  tween: 256,
2160
2412
  mixed: 128
@@ -2192,7 +2444,7 @@ var Scene = (_class7 = class _Scene {
2192
2444
  // (benchmarks/particle-wasm). f32 (matches the WGSL shader), bit-identical to
2193
2445
  // a JS f32 reference oracle; updateCPU (f64) stays the permanent fallback when
2194
2446
  // no backend is installed or a scene runs on WebGPU.
2195
- __init114() {this._particleWasm = null}
2447
+ __init123() {this._particleWasm = null}
2196
2448
  /** Which backend runs the CPU particle simulation. Reflects only whether a
2197
2449
  * backend is installed (the WebGPU compute path, when active, is used first
2198
2450
  * regardless). */
@@ -2272,7 +2524,10 @@ var Scene = (_class7 = class _Scene {
2272
2524
  * pre-pass.
2273
2525
  */
2274
2526
  _tickBatchedDrivers(dt) {
2275
- if (this._activeDriverEntities.size === 0) return;
2527
+ if (this._activeDriverEntities.size === 0) {
2528
+ this._animReason = "not-applicable";
2529
+ return;
2530
+ }
2276
2531
  let springBatchable = 0;
2277
2532
  let tweenBatchable = 0;
2278
2533
  for (const entity of this._activeDriverEntities) {
@@ -2289,10 +2544,17 @@ var Scene = (_class7 = class _Scene {
2289
2544
  const batchable = springBatchable + tweenBatchable;
2290
2545
  const backend = this._animWasm;
2291
2546
  this._animBatchedLastFrame = false;
2292
- if (!backend) return;
2547
+ if (!backend) {
2548
+ this._animReason = "not-installed";
2549
+ return;
2550
+ }
2293
2551
  const gate = springBatchable > 0 && tweenBatchable > 0 ? this.animGate.mixed : tweenBatchable > 0 ? this.animGate.tween : this.animGate.spring;
2294
- if (batchable < gate) return;
2552
+ if (batchable < gate) {
2553
+ this._animReason = "below-gate";
2554
+ return;
2555
+ }
2295
2556
  this._animBatchedLastFrame = true;
2557
+ this._animReason = "active";
2296
2558
  const sE = this._springEntities;
2297
2559
  const sP = this._springProps;
2298
2560
  const sD = this._springDrivers;
@@ -2339,8 +2601,13 @@ var Scene = (_class7 = class _Scene {
2339
2601
  sv.damp[i] = phys.damping;
2340
2602
  sv.mass[i] = phys.mass;
2341
2603
  }
2342
- backend.stepSprings(dt, springCount);
2343
- for (let i = 0; i < springCount; i++) sD[i].syncExternal(sv.val[i], sv.vel[i]);
2604
+ if (backend.stepSprings(dt, springCount)) {
2605
+ for (let i = 0; i < springCount; i++) sD[i].syncExternal(sv.val[i], sv.vel[i]);
2606
+ } else {
2607
+ for (let i = 0; i < springCount; i++) sD[i].tick(dt);
2608
+ this._animReason = "rejected";
2609
+ this._animBatchedLastFrame = false;
2610
+ }
2344
2611
  }
2345
2612
  if (tweenCount > 0) {
2346
2613
  const tv = backend.tweenView();
@@ -2353,8 +2620,13 @@ var Scene = (_class7 = class _Scene {
2353
2620
  tv.delay[i] = d.delayMs;
2354
2621
  tv.ease[i] = d.wasmEasingId;
2355
2622
  }
2356
- backend.stepTweens(dt, tweenCount);
2357
- for (let i = 0; i < tweenCount; i++) tD[i].syncExternal(tv.val[i], tv.elapsed[i]);
2623
+ if (backend.stepTweens(dt, tweenCount)) {
2624
+ for (let i = 0; i < tweenCount; i++) tD[i].syncExternal(tv.val[i], tv.elapsed[i]);
2625
+ } else {
2626
+ for (let i = 0; i < tweenCount; i++) tD[i].tick(dt);
2627
+ this._animReason = "rejected";
2628
+ this._animBatchedLastFrame = false;
2629
+ }
2358
2630
  }
2359
2631
  for (let i = 0; i < springCount; i++) sE[i]._applyDriverTick(sP[i], sD[i]);
2360
2632
  for (let i = 0; i < tweenCount; i++) tE[i]._applyDriverTick(tP[i], tD[i]);
@@ -2376,7 +2648,10 @@ var Scene = (_class7 = class _Scene {
2376
2648
  slotEntity2[slot] = entity;
2377
2649
  entity._storeSlot = slot;
2378
2650
  }
2379
- backend.uploadRuns(built.store);
2651
+ if (!backend.uploadRuns(built.store)) {
2652
+ this._transformReason = "rejected";
2653
+ return null;
2654
+ }
2380
2655
  this._treeStore = built.store;
2381
2656
  this._slotEntity = slotEntity2;
2382
2657
  this._wasmInputs = backend.inputView();
@@ -2401,8 +2676,12 @@ var Scene = (_class7 = class _Scene {
2401
2676
  inp.sin[slot] = trig.sin;
2402
2677
  inp.opacity[slot] = e.opacity;
2403
2678
  }
2404
- backend.runKernel("simd");
2679
+ if (backend.runKernel("simd") !== WASM_STATUS.OK) {
2680
+ this._transformReason = "rejected";
2681
+ return null;
2682
+ }
2405
2683
  this._wasmAabbsFresh = false;
2684
+ this._transformReason = "active";
2406
2685
  return this._wasmWorld;
2407
2686
  }
2408
2687
  /**
@@ -2432,7 +2711,7 @@ var Scene = (_class7 = class _Scene {
2432
2711
  bounds.bw[slot] = b ? b.width : 0;
2433
2712
  bounds.bh[slot] = b ? b.height : 0;
2434
2713
  }
2435
- backend.runAabbs(slotEntity.length);
2714
+ if (!backend.runAabbs(slotEntity.length)) return false;
2436
2715
  this._wasmAabbsFresh = true;
2437
2716
  return true;
2438
2717
  }
@@ -2442,24 +2721,24 @@ var Scene = (_class7 = class _Scene {
2442
2721
  * sync, so retaining the order prevents a newly opened overlay from spending
2443
2722
  * its first frame below previously projected controls.
2444
2723
  */
2445
- __init115() {this.a11yRenderOrders = /* @__PURE__ */ new Map()}
2724
+ __init124() {this.a11yRenderOrders = /* @__PURE__ */ new Map()}
2446
2725
  // Optional WebGL point-cloud layer (see SceneOptions.pointBackend).
2447
- __init116() {this.pointRenderer = null}
2448
- __init117() {this.glCanvas = null}
2449
- __init118() {this.glContextLostHandler = null}
2450
- __init119() {this.glContextRestoredHandler = null}
2726
+ __init125() {this.pointRenderer = null}
2727
+ __init126() {this.glCanvas = null}
2728
+ __init127() {this.glContextLostHandler = null}
2729
+ __init128() {this.glContextRestoredHandler = null}
2451
2730
 
2452
2731
 
2453
2732
 
2454
- __init120() {this.disableWindowResize = false}
2733
+ __init129() {this.disableWindowResize = false}
2455
2734
  /** See {@link SceneOptions.maxDPR}. `undefined` = uncapped (real DPR). */
2456
2735
 
2457
2736
  // WebGPU properties
2458
- __init121() {this.destroyed = false}
2459
- __init122() {this.device = null}
2460
- __init123() {this.deviceLost = false}
2461
- __init124() {this.particleBackend = "auto"}
2462
- __init125() {this._webgpuDisabled = false}
2737
+ __init130() {this.destroyed = false}
2738
+ __init131() {this.device = null}
2739
+ __init132() {this.deviceLost = false}
2740
+ __init133() {this.particleBackend = "auto"}
2741
+ __init134() {this._webgpuDisabled = false}
2463
2742
  get webgpuDisabled() {
2464
2743
  return this._webgpuDisabled || this.particleBackend === "cpu";
2465
2744
  }
@@ -2473,7 +2752,7 @@ var Scene = (_class7 = class _Scene {
2473
2752
  * active.
2474
2753
  */
2475
2754
  get webglDrawStats() {
2476
- return _nullishCoalesce(_optionalChain([this, 'access', _27 => _27.pointRenderer, 'optionalAccess', _28 => _28.stats, 'optionalCall', _29 => _29()]), () => ( null));
2755
+ return _nullishCoalesce(_optionalChain([this, 'access', _37 => _37.pointRenderer, 'optionalAccess', _38 => _38.stats, 'optionalCall', _39 => _39()]), () => ( null));
2477
2756
  }
2478
2757
  /**
2479
2758
  * Whether a WebGPU device is currently live for particle compute.
@@ -2487,22 +2766,22 @@ var Scene = (_class7 = class _Scene {
2487
2766
  set webgpuDisabled(value) {
2488
2767
  this._webgpuDisabled = value;
2489
2768
  }
2490
- __init126() {this.recoveryTimerId = null}
2491
- __init127() {this.manager = null}
2492
- __init128() {this.initializingWebGPU = false}
2493
- __init129() {this.gpuCanvas = null}
2494
- __init130() {this.gpuContext = null}
2769
+ __init135() {this.recoveryTimerId = null}
2770
+ __init136() {this.manager = null}
2771
+ __init137() {this.initializingWebGPU = false}
2772
+ __init138() {this.gpuCanvas = null}
2773
+ __init139() {this.gpuContext = null}
2495
2774
  /** True while the GPU canvas holds a presented particle frame (needs clearing when they leave). */
2496
- __init131() {this.gpuHasContent = false}
2497
- __init132() {this.mouseX = -9999}
2498
- __init133() {this.mouseY = -9999}
2499
- __init134() {this.pointerMoveListener = null}
2500
- __init135() {this.pointerLeaveListener = null}
2775
+ __init140() {this.gpuHasContent = false}
2776
+ __init141() {this.mouseX = -9999}
2777
+ __init142() {this.mouseY = -9999}
2778
+ __init143() {this.pointerMoveListener = null}
2779
+ __init144() {this.pointerLeaveListener = null}
2501
2780
  /** Element the pointer listeners are bound to (parent container if present,
2502
2781
  * else the canvas). Stored so `destroy()` detaches from the same element. */
2503
- __init136() {this.pointerEventTarget = null}
2504
- __init137() {this.hasWarnedZeroSize = false}
2505
- __init138() {this.fontLoadHandler = null}
2782
+ __init145() {this.pointerEventTarget = null}
2783
+ __init146() {this.hasWarnedZeroSize = false}
2784
+ __init147() {this.fontLoadHandler = null}
2506
2785
  // ── Dev-mode warning infrastructure ──────────────────────────────
2507
2786
  //
2508
2787
  // Enable with `Scene.devMode = true` or by setting `globalThis.__DEV__`.
@@ -2515,12 +2794,12 @@ var Scene = (_class7 = class _Scene {
2515
2794
  static _devModeDetected() {
2516
2795
  if (_Scene.devMode) return true;
2517
2796
  const gp = typeof globalThis !== "undefined" ? globalThis : void 0;
2518
- if (_optionalChain([gp, 'optionalAccess', _30 => _30.__DEV__])) return true;
2519
- if (_optionalChain([gp, 'optionalAccess', _31 => _31.process, 'optionalAccess', _32 => _32.env, 'optionalAccess', _33 => _33.NODE_ENV]) === "development") return true;
2797
+ if (_optionalChain([gp, 'optionalAccess', _40 => _40.__DEV__])) return true;
2798
+ if (_optionalChain([gp, 'optionalAccess', _41 => _41.process, 'optionalAccess', _42 => _42.env, 'optionalAccess', _43 => _43.NODE_ENV]) === "development") return true;
2520
2799
  return false;
2521
2800
  }
2522
2801
 
2523
- __init139() {this._devFrameCount = 0}
2802
+ __init148() {this._devFrameCount = 0}
2524
2803
  _devWarn(message) {
2525
2804
  if (!this._devActive) return;
2526
2805
  console.warn(`[vectojs/dev] ${message}`);
@@ -2547,9 +2826,9 @@ var Scene = (_class7 = class _Scene {
2547
2826
  let checked = 0;
2548
2827
  const walkProjections = (node) => {
2549
2828
  if (checked > 10) return;
2550
- const proj = _optionalChain([node, 'access', _34 => _34.getContentProjection, 'optionalCall', _35 => _35()]);
2551
- if (_optionalChain([proj, 'optionalAccess', _36 => _36.text]) && proj.selectable !== false) {
2552
- const el = _optionalChain([this, 'access', _37 => _37.contentElements, 'optionalAccess', _38 => _38.get, 'call', _39 => _39(node.id)]);
2829
+ const proj = _optionalChain([node, 'access', _44 => _44.getContentProjection, 'optionalCall', _45 => _45()]);
2830
+ if (_optionalChain([proj, 'optionalAccess', _46 => _46.text]) && proj.selectable !== false) {
2831
+ const el = _optionalChain([this, 'access', _47 => _47.contentElements, 'optionalAccess', _48 => _48.get, 'call', _49 => _49(node.id)]);
2553
2832
  if (el) {
2554
2833
  const projectedText = el.textContent || "";
2555
2834
  if (projectedText !== "" && projectedText !== proj.text) {
@@ -2564,14 +2843,14 @@ var Scene = (_class7 = class _Scene {
2564
2843
  };
2565
2844
  walkProjections(this.root);
2566
2845
  }
2567
- constructor(canvas, options = {}) {;_class7.prototype.__init25.call(this);_class7.prototype.__init26.call(this);_class7.prototype.__init27.call(this);_class7.prototype.__init28.call(this);_class7.prototype.__init29.call(this);_class7.prototype.__init30.call(this);_class7.prototype.__init31.call(this);_class7.prototype.__init32.call(this);_class7.prototype.__init33.call(this);_class7.prototype.__init34.call(this);_class7.prototype.__init35.call(this);_class7.prototype.__init36.call(this);_class7.prototype.__init37.call(this);_class7.prototype.__init38.call(this);_class7.prototype.__init39.call(this);_class7.prototype.__init40.call(this);_class7.prototype.__init41.call(this);_class7.prototype.__init42.call(this);_class7.prototype.__init43.call(this);_class7.prototype.__init44.call(this);_class7.prototype.__init45.call(this);_class7.prototype.__init46.call(this);_class7.prototype.__init47.call(this);_class7.prototype.__init48.call(this);_class7.prototype.__init49.call(this);_class7.prototype.__init50.call(this);_class7.prototype.__init51.call(this);_class7.prototype.__init52.call(this);_class7.prototype.__init53.call(this);_class7.prototype.__init54.call(this);_class7.prototype.__init55.call(this);_class7.prototype.__init56.call(this);_class7.prototype.__init57.call(this);_class7.prototype.__init58.call(this);_class7.prototype.__init59.call(this);_class7.prototype.__init60.call(this);_class7.prototype.__init61.call(this);_class7.prototype.__init62.call(this);_class7.prototype.__init63.call(this);_class7.prototype.__init64.call(this);_class7.prototype.__init65.call(this);_class7.prototype.__init66.call(this);_class7.prototype.__init67.call(this);_class7.prototype.__init68.call(this);_class7.prototype.__init69.call(this);_class7.prototype.__init70.call(this);_class7.prototype.__init71.call(this);_class7.prototype.__init72.call(this);_class7.prototype.__init73.call(this);_class7.prototype.__init74.call(this);_class7.prototype.__init75.call(this);_class7.prototype.__init76.call(this);_class7.prototype.__init77.call(this);_class7.prototype.__init78.call(this);_class7.prototype.__init79.call(this);_class7.prototype.__init80.call(this);_class7.prototype.__init81.call(this);_class7.prototype.__init82.call(this);_class7.prototype.__init83.call(this);_class7.prototype.__init84.call(this);_class7.prototype.__init85.call(this);_class7.prototype.__init86.call(this);_class7.prototype.__init87.call(this);_class7.prototype.__init88.call(this);_class7.prototype.__init89.call(this);_class7.prototype.__init90.call(this);_class7.prototype.__init91.call(this);_class7.prototype.__init92.call(this);_class7.prototype.__init93.call(this);_class7.prototype.__init94.call(this);_class7.prototype.__init95.call(this);_class7.prototype.__init96.call(this);_class7.prototype.__init97.call(this);_class7.prototype.__init98.call(this);_class7.prototype.__init99.call(this);_class7.prototype.__init100.call(this);_class7.prototype.__init101.call(this);_class7.prototype.__init102.call(this);_class7.prototype.__init103.call(this);_class7.prototype.__init104.call(this);_class7.prototype.__init105.call(this);_class7.prototype.__init106.call(this);_class7.prototype.__init107.call(this);_class7.prototype.__init108.call(this);_class7.prototype.__init109.call(this);_class7.prototype.__init110.call(this);_class7.prototype.__init111.call(this);_class7.prototype.__init112.call(this);_class7.prototype.__init113.call(this);_class7.prototype.__init114.call(this);_class7.prototype.__init115.call(this);_class7.prototype.__init116.call(this);_class7.prototype.__init117.call(this);_class7.prototype.__init118.call(this);_class7.prototype.__init119.call(this);_class7.prototype.__init120.call(this);_class7.prototype.__init121.call(this);_class7.prototype.__init122.call(this);_class7.prototype.__init123.call(this);_class7.prototype.__init124.call(this);_class7.prototype.__init125.call(this);_class7.prototype.__init126.call(this);_class7.prototype.__init127.call(this);_class7.prototype.__init128.call(this);_class7.prototype.__init129.call(this);_class7.prototype.__init130.call(this);_class7.prototype.__init131.call(this);_class7.prototype.__init132.call(this);_class7.prototype.__init133.call(this);_class7.prototype.__init134.call(this);_class7.prototype.__init135.call(this);_class7.prototype.__init136.call(this);_class7.prototype.__init137.call(this);_class7.prototype.__init138.call(this);_class7.prototype.__init139.call(this);
2846
+ constructor(canvas, options = {}) {;_class7.prototype.__init27.call(this);_class7.prototype.__init28.call(this);_class7.prototype.__init29.call(this);_class7.prototype.__init30.call(this);_class7.prototype.__init31.call(this);_class7.prototype.__init32.call(this);_class7.prototype.__init33.call(this);_class7.prototype.__init34.call(this);_class7.prototype.__init35.call(this);_class7.prototype.__init36.call(this);_class7.prototype.__init37.call(this);_class7.prototype.__init38.call(this);_class7.prototype.__init39.call(this);_class7.prototype.__init40.call(this);_class7.prototype.__init41.call(this);_class7.prototype.__init42.call(this);_class7.prototype.__init43.call(this);_class7.prototype.__init44.call(this);_class7.prototype.__init45.call(this);_class7.prototype.__init46.call(this);_class7.prototype.__init47.call(this);_class7.prototype.__init48.call(this);_class7.prototype.__init49.call(this);_class7.prototype.__init50.call(this);_class7.prototype.__init51.call(this);_class7.prototype.__init52.call(this);_class7.prototype.__init53.call(this);_class7.prototype.__init54.call(this);_class7.prototype.__init55.call(this);_class7.prototype.__init56.call(this);_class7.prototype.__init57.call(this);_class7.prototype.__init58.call(this);_class7.prototype.__init59.call(this);_class7.prototype.__init60.call(this);_class7.prototype.__init61.call(this);_class7.prototype.__init62.call(this);_class7.prototype.__init63.call(this);_class7.prototype.__init64.call(this);_class7.prototype.__init65.call(this);_class7.prototype.__init66.call(this);_class7.prototype.__init67.call(this);_class7.prototype.__init68.call(this);_class7.prototype.__init69.call(this);_class7.prototype.__init70.call(this);_class7.prototype.__init71.call(this);_class7.prototype.__init72.call(this);_class7.prototype.__init73.call(this);_class7.prototype.__init74.call(this);_class7.prototype.__init75.call(this);_class7.prototype.__init76.call(this);_class7.prototype.__init77.call(this);_class7.prototype.__init78.call(this);_class7.prototype.__init79.call(this);_class7.prototype.__init80.call(this);_class7.prototype.__init81.call(this);_class7.prototype.__init82.call(this);_class7.prototype.__init83.call(this);_class7.prototype.__init84.call(this);_class7.prototype.__init85.call(this);_class7.prototype.__init86.call(this);_class7.prototype.__init87.call(this);_class7.prototype.__init88.call(this);_class7.prototype.__init89.call(this);_class7.prototype.__init90.call(this);_class7.prototype.__init91.call(this);_class7.prototype.__init92.call(this);_class7.prototype.__init93.call(this);_class7.prototype.__init94.call(this);_class7.prototype.__init95.call(this);_class7.prototype.__init96.call(this);_class7.prototype.__init97.call(this);_class7.prototype.__init98.call(this);_class7.prototype.__init99.call(this);_class7.prototype.__init100.call(this);_class7.prototype.__init101.call(this);_class7.prototype.__init102.call(this);_class7.prototype.__init103.call(this);_class7.prototype.__init104.call(this);_class7.prototype.__init105.call(this);_class7.prototype.__init106.call(this);_class7.prototype.__init107.call(this);_class7.prototype.__init108.call(this);_class7.prototype.__init109.call(this);_class7.prototype.__init110.call(this);_class7.prototype.__init111.call(this);_class7.prototype.__init112.call(this);_class7.prototype.__init113.call(this);_class7.prototype.__init114.call(this);_class7.prototype.__init115.call(this);_class7.prototype.__init116.call(this);_class7.prototype.__init117.call(this);_class7.prototype.__init118.call(this);_class7.prototype.__init119.call(this);_class7.prototype.__init120.call(this);_class7.prototype.__init121.call(this);_class7.prototype.__init122.call(this);_class7.prototype.__init123.call(this);_class7.prototype.__init124.call(this);_class7.prototype.__init125.call(this);_class7.prototype.__init126.call(this);_class7.prototype.__init127.call(this);_class7.prototype.__init128.call(this);_class7.prototype.__init129.call(this);_class7.prototype.__init130.call(this);_class7.prototype.__init131.call(this);_class7.prototype.__init132.call(this);_class7.prototype.__init133.call(this);_class7.prototype.__init134.call(this);_class7.prototype.__init135.call(this);_class7.prototype.__init136.call(this);_class7.prototype.__init137.call(this);_class7.prototype.__init138.call(this);_class7.prototype.__init139.call(this);_class7.prototype.__init140.call(this);_class7.prototype.__init141.call(this);_class7.prototype.__init142.call(this);_class7.prototype.__init143.call(this);_class7.prototype.__init144.call(this);_class7.prototype.__init145.call(this);_class7.prototype.__init146.call(this);_class7.prototype.__init147.call(this);_class7.prototype.__init148.call(this);
2568
2847
  this.canvas = canvas;
2569
2848
  this.debugA11y = _nullishCoalesce(options.debugA11y, () => ( false));
2570
2849
  this.disableWindowResize = _nullishCoalesce(options.disableWindowResize, () => ( false));
2571
2850
  this.maxDPR = options.maxDPR;
2572
2851
  if (this.disableWindowResize) {
2573
- const styleWidth = parseInlinePx(_optionalChain([canvas, 'access', _40 => _40.style, 'optionalAccess', _41 => _41.width]));
2574
- const styleHeight = parseInlinePx(_optionalChain([canvas, 'access', _42 => _42.style, 'optionalAccess', _43 => _43.height]));
2852
+ const styleWidth = parseInlinePx(_optionalChain([canvas, 'access', _50 => _50.style, 'optionalAccess', _51 => _51.width]));
2853
+ const styleHeight = parseInlinePx(_optionalChain([canvas, 'access', _52 => _52.style, 'optionalAccess', _53 => _53.height]));
2575
2854
  this.width = _nullishCoalesce(styleWidth, () => ( (canvas.width || canvas.clientWidth || 0)));
2576
2855
  this.height = _nullishCoalesce(styleHeight, () => ( (canvas.height || canvas.clientHeight || 0)));
2577
2856
  } else {
@@ -2579,10 +2858,11 @@ var Scene = (_class7 = class _Scene {
2579
2858
  this.height = typeof window !== "undefined" ? window.innerHeight : canvas.clientHeight || canvas.height || 600;
2580
2859
  }
2581
2860
  const globalProcess = typeof globalThis !== "undefined" ? globalThis.process : void 0;
2582
- const isTest = globalProcess && (_optionalChain([globalProcess, 'access', _44 => _44.env, 'optionalAccess', _45 => _45.NODE_ENV]) === "test" || _optionalChain([globalProcess, 'access', _46 => _46.env, 'optionalAccess', _47 => _47.VITEST]) === "true");
2861
+ const isTest = globalProcess && (_optionalChain([globalProcess, 'access', _54 => _54.env, 'optionalAccess', _55 => _55.NODE_ENV]) === "test" || _optionalChain([globalProcess, 'access', _56 => _56.env, 'optionalAccess', _57 => _57.VITEST]) === "true");
2583
2862
  this.maxFPS = _nullishCoalesce(options.maxFPS, () => ( (isTest ? 0 : 60)));
2584
2863
  this.respectReducedMotion = _nullishCoalesce(options.respectReducedMotion, () => ( true));
2585
2864
  this.autoThrottle = _nullishCoalesce(options.autoThrottle, () => ( true));
2865
+ this._userTiming = _nullishCoalesce(options.userTiming, () => ( false));
2586
2866
  this.particleBackend = _nullishCoalesce(options.particleBackend, () => ( "auto"));
2587
2867
  this.a11ySyncInterval = _nullishCoalesce(options.a11ySyncInterval, () => ( 0));
2588
2868
  this.contentProjectionEnabled = _nullishCoalesce(options.contentProjection, () => ( true));
@@ -2593,7 +2873,7 @@ var Scene = (_class7 = class _Scene {
2593
2873
  if (typeof window !== "undefined" && typeof window.matchMedia === "function") {
2594
2874
  this.forcedColorsQuery = window.matchMedia("(forced-colors: active)");
2595
2875
  this.forcedColorsChangeHandler = () => this.markDirty();
2596
- _optionalChain([this, 'access', _48 => _48.forcedColorsQuery, 'access', _49 => _49.addEventListener, 'optionalCall', _50 => _50("change", this.forcedColorsChangeHandler)]);
2876
+ _optionalChain([this, 'access', _58 => _58.forcedColorsQuery, 'access', _59 => _59.addEventListener, 'optionalCall', _60 => _60("change", this.forcedColorsChangeHandler)]);
2597
2877
  }
2598
2878
  this.root = new class RootEntity extends _chunkAGP4VLF4js.Entity {
2599
2879
  isPointInside() {
@@ -2615,18 +2895,19 @@ var Scene = (_class7 = class _Scene {
2615
2895
  if (options.renderer) {
2616
2896
  this.renderer = options.renderer;
2617
2897
  } else {
2618
- this.renderer = new (0, _chunkLHONR3GOjs.CanvasRenderer)(
2898
+ this.renderer = new (0, _chunkKEBYJVD6js.CanvasRenderer)(
2619
2899
  canvas,
2620
2900
  this.disableWindowResize ? { width: this.width, height: this.height } : void 0,
2621
2901
  this.maxDPR
2622
2902
  );
2623
2903
  }
2624
- _optionalChain([this, 'access', _51 => _51.renderer, 'access', _52 => _52.onContextRestored, 'optionalCall', _53 => _53(() => {
2904
+ _optionalChain([this, 'access', _61 => _61.renderer, 'access', _62 => _62.onContextRestored, 'optionalCall', _63 => _63(() => {
2625
2905
  this.markDirty();
2626
- if (_optionalChain([this, 'access', _54 => _54.renderer, 'access', _55 => _55.isContextLost, 'optionalCall', _56 => _56()]) !== true) this.render(this.renderer);
2906
+ if (_optionalChain([this, 'access', _64 => _64.renderer, 'access', _65 => _65.isContextLost, 'optionalCall', _66 => _66()]) !== true) this.render(this.renderer);
2627
2907
  })]);
2628
2908
  if (typeof document !== "undefined") {
2629
2909
  this.a11yRoot = document.createElement("div");
2910
+ this.a11yRoot.setAttribute("data-vecto-a11y-root", "");
2630
2911
  this.a11yRoot.style.position = "absolute";
2631
2912
  this.a11yRoot.style.top = "0";
2632
2913
  this.a11yRoot.style.left = "0";
@@ -2792,7 +3073,7 @@ var Scene = (_class7 = class _Scene {
2792
3073
  watchDevicePixelRatio() {
2793
3074
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
2794
3075
  if (this.dprMediaQuery && this.dprChangeHandler) {
2795
- _optionalChain([this, 'access', _57 => _57.dprMediaQuery, 'access', _58 => _58.removeEventListener, 'optionalCall', _59 => _59("change", this.dprChangeHandler)]);
3076
+ _optionalChain([this, 'access', _67 => _67.dprMediaQuery, 'access', _68 => _68.removeEventListener, 'optionalCall', _69 => _69("change", this.dprChangeHandler)]);
2796
3077
  }
2797
3078
  const dpr = window.devicePixelRatio || 1;
2798
3079
  const query = window.matchMedia(`(resolution: ${dpr}dppx)`);
@@ -2800,7 +3081,7 @@ var Scene = (_class7 = class _Scene {
2800
3081
  this.resize(this.width, this.height);
2801
3082
  this.watchDevicePixelRatio();
2802
3083
  };
2803
- _optionalChain([query, 'access', _60 => _60.addEventListener, 'optionalCall', _61 => _61("change", handler)]);
3084
+ _optionalChain([query, 'access', _70 => _70.addEventListener, 'optionalCall', _71 => _71("change", handler)]);
2804
3085
  this.dprMediaQuery = query;
2805
3086
  this.dprChangeHandler = handler;
2806
3087
  }
@@ -2820,7 +3101,7 @@ var Scene = (_class7 = class _Scene {
2820
3101
  if (typeof gl.addEventListener !== "function") return;
2821
3102
  this.glContextLostHandler = (e) => {
2822
3103
  e.preventDefault();
2823
- _optionalChain([this, 'access', _62 => _62.pointRenderer, 'optionalAccess', _63 => _63.destroy, 'call', _64 => _64()]);
3104
+ _optionalChain([this, 'access', _72 => _72.pointRenderer, 'optionalAccess', _73 => _73.destroy, 'call', _74 => _74()]);
2824
3105
  this.pointRenderer = null;
2825
3106
  };
2826
3107
  this.glContextRestoredHandler = () => {
@@ -2850,26 +3131,26 @@ var Scene = (_class7 = class _Scene {
2850
3131
  * to the live DOM selection.
2851
3132
  */
2852
3133
  contentGridSelectionLine(el) {
2853
- const candidates = [_optionalChain([this, 'access', _65 => _65.contentSelectionAnchor, 'optionalAccess', _66 => _66.node])];
3134
+ const candidates = [_optionalChain([this, 'access', _75 => _75.contentSelectionAnchor, 'optionalAccess', _76 => _76.node])];
2854
3135
  if (typeof window !== "undefined" && typeof window.getSelection === "function") {
2855
3136
  const selection = window.getSelection();
2856
- candidates.push(_optionalChain([selection, 'optionalAccess', _67 => _67.anchorNode]), _optionalChain([selection, 'optionalAccess', _68 => _68.focusNode]));
3137
+ candidates.push(_optionalChain([selection, 'optionalAccess', _77 => _77.anchorNode]), _optionalChain([selection, 'optionalAccess', _78 => _78.focusNode]));
2857
3138
  }
2858
3139
  for (const candidate of candidates) {
2859
3140
  if (!candidate || !el.contains(candidate)) continue;
2860
3141
  let cursor = candidate;
2861
3142
  while (cursor && cursor.parentNode !== el) cursor = cursor.parentNode;
2862
- const lineIndex = _optionalChain([cursor, 'optionalAccess', _69 => _69.dataset, 'optionalAccess', _70 => _70.vectoGridLine]);
3143
+ const lineIndex = _optionalChain([cursor, 'optionalAccess', _79 => _79.dataset, 'optionalAccess', _80 => _80.vectoGridLine]);
2863
3144
  if (lineIndex !== void 0) return Number(lineIndex);
2864
3145
  }
2865
3146
  return null;
2866
3147
  }
2867
3148
  releaseContentSelectionForRebuild(el) {
2868
3149
  const selection = typeof window !== "undefined" && typeof window.getSelection === "function" ? window.getSelection() : null;
2869
- const ownsSelection = this.contentSelectionAnchor && el.contains(this.contentSelectionAnchor.node) || (_optionalChain([selection, 'optionalAccess', _71 => _71.anchorNode]) ? el.contains(selection.anchorNode) : false) || (_optionalChain([selection, 'optionalAccess', _72 => _72.focusNode]) ? el.contains(selection.focusNode) : false);
3150
+ const ownsSelection = this.contentSelectionAnchor && el.contains(this.contentSelectionAnchor.node) || (_optionalChain([selection, 'optionalAccess', _81 => _81.anchorNode]) ? el.contains(selection.anchorNode) : false) || (_optionalChain([selection, 'optionalAccess', _82 => _82.focusNode]) ? el.contains(selection.focusNode) : false);
2870
3151
  if (!ownsSelection) return;
2871
3152
  this.endContentSelectionDrag();
2872
- _optionalChain([selection, 'optionalAccess', _73 => _73.removeAllRanges, 'call', _74 => _74()]);
3153
+ _optionalChain([selection, 'optionalAccess', _83 => _83.removeAllRanges, 'call', _84 => _84()]);
2873
3154
  }
2874
3155
  /**
2875
3156
  * Rebuild a content-projection element's DOM (`rebuild`) while preserving a
@@ -2915,7 +3196,7 @@ var Scene = (_class7 = class _Scene {
2915
3196
  if (!anchor || !focus) return;
2916
3197
  try {
2917
3198
  selection.setBaseAndExtent(anchor.node, anchor.offset, focus.node, focus.offset);
2918
- } catch (e9) {
3199
+ } catch (e14) {
2919
3200
  }
2920
3201
  }
2921
3202
  /**
@@ -2928,7 +3209,7 @@ var Scene = (_class7 = class _Scene {
2928
3209
  }
2929
3210
  /** Convert browser viewport coordinates into this Scene's logical coordinates. */
2930
3211
  clientToScene(clientX, clientY) {
2931
- const rect = _optionalChain([this, 'access', _75 => _75.canvas, 'access', _76 => _76.getBoundingClientRect, 'optionalCall', _77 => _77()]);
3212
+ const rect = _optionalChain([this, 'access', _85 => _85.canvas, 'access', _86 => _86.getBoundingClientRect, 'optionalCall', _87 => _87()]);
2932
3213
  if (!rect) return { x: clientX, y: clientY };
2933
3214
  const cssWidth = rect.width || this.canvas.clientWidth || this.width;
2934
3215
  const cssHeight = rect.height || this.canvas.clientHeight || this.height;
@@ -2966,7 +3247,7 @@ var Scene = (_class7 = class _Scene {
2966
3247
  cancelAnimationFrame(calibrationFrame);
2967
3248
  }
2968
3249
  this.contentGridCalibrationFrames.delete(entityId);
2969
- _optionalChain([this, 'access', _78 => _78.contentGridCalibrationProbes, 'access', _79 => _79.get, 'call', _80 => _80(entityId), 'optionalAccess', _81 => _81.remove, 'call', _82 => _82()]);
3250
+ _optionalChain([this, 'access', _88 => _88.contentGridCalibrationProbes, 'access', _89 => _89.get, 'call', _90 => _90(entityId), 'optionalAccess', _91 => _91.remove, 'call', _92 => _92()]);
2970
3251
  this.contentGridCalibrationProbes.delete(entityId);
2971
3252
  delete el.dataset.vectoGridCalibrationPending;
2972
3253
  delete el.dataset.vectoGridCalibration;
@@ -3120,12 +3401,12 @@ var Scene = (_class7 = class _Scene {
3120
3401
  this.canvasResizeObserver = null;
3121
3402
  }
3122
3403
  if (this.dprMediaQuery && this.dprChangeHandler) {
3123
- _optionalChain([this, 'access', _83 => _83.dprMediaQuery, 'access', _84 => _84.removeEventListener, 'optionalCall', _85 => _85("change", this.dprChangeHandler)]);
3404
+ _optionalChain([this, 'access', _93 => _93.dprMediaQuery, 'access', _94 => _94.removeEventListener, 'optionalCall', _95 => _95("change", this.dprChangeHandler)]);
3124
3405
  this.dprMediaQuery = null;
3125
3406
  this.dprChangeHandler = null;
3126
3407
  }
3127
3408
  if (this.forcedColorsQuery && this.forcedColorsChangeHandler) {
3128
- _optionalChain([this, 'access', _86 => _86.forcedColorsQuery, 'access', _87 => _87.removeEventListener, 'optionalCall', _88 => _88("change", this.forcedColorsChangeHandler)]);
3409
+ _optionalChain([this, 'access', _96 => _96.forcedColorsQuery, 'access', _97 => _97.removeEventListener, 'optionalCall', _98 => _98("change", this.forcedColorsChangeHandler)]);
3129
3410
  this.forcedColorsQuery = null;
3130
3411
  this.forcedColorsChangeHandler = null;
3131
3412
  }
@@ -3143,9 +3424,9 @@ var Scene = (_class7 = class _Scene {
3143
3424
  }
3144
3425
  this.pointerEventTarget = null;
3145
3426
  }
3146
- _optionalChain([this, 'access', _89 => _89.a11yRoot, 'optionalAccess', _90 => _90.remove, 'call', _91 => _91()]);
3427
+ _optionalChain([this, 'access', _99 => _99.a11yRoot, 'optionalAccess', _100 => _100.remove, 'call', _101 => _101()]);
3147
3428
  this.focusSentinel = null;
3148
- _optionalChain([this, 'access', _92 => _92.portalRoot, 'optionalAccess', _93 => _93.remove, 'call', _94 => _94()]);
3429
+ _optionalChain([this, 'access', _102 => _102.portalRoot, 'optionalAccess', _103 => _103.remove, 'call', _104 => _104()]);
3149
3430
  this.a11yElements.clear();
3150
3431
  for (const el of this.contentElements.values()) el.remove();
3151
3432
  this.contentElements.clear();
@@ -3168,10 +3449,10 @@ var Scene = (_class7 = class _Scene {
3168
3449
  }
3169
3450
  this.glContextLostHandler = null;
3170
3451
  this.glContextRestoredHandler = null;
3171
- _optionalChain([this, 'access', _95 => _95.pointRenderer, 'optionalAccess', _96 => _96.destroy, 'call', _97 => _97()]);
3172
- _optionalChain([this, 'access', _98 => _98.renderer, 'access', _99 => _99.dispose, 'optionalCall', _100 => _100()]);
3173
- _optionalChain([this, 'access', _101 => _101.glCanvas, 'optionalAccess', _102 => _102.remove, 'call', _103 => _103()]);
3174
- _optionalChain([this, 'access', _104 => _104.gpuCanvas, 'optionalAccess', _105 => _105.remove, 'call', _106 => _106()]);
3452
+ _optionalChain([this, 'access', _105 => _105.pointRenderer, 'optionalAccess', _106 => _106.destroy, 'call', _107 => _107()]);
3453
+ _optionalChain([this, 'access', _108 => _108.renderer, 'access', _109 => _109.dispose, 'optionalCall', _110 => _110()]);
3454
+ _optionalChain([this, 'access', _111 => _111.glCanvas, 'optionalAccess', _112 => _112.remove, 'call', _113 => _113()]);
3455
+ _optionalChain([this, 'access', _114 => _114.gpuCanvas, 'optionalAccess', _115 => _115.remove, 'call', _116 => _116()]);
3175
3456
  this.gpuCanvas = null;
3176
3457
  this.gpuContext = null;
3177
3458
  if (this.recoveryTimerId) {
@@ -3183,7 +3464,7 @@ var Scene = (_class7 = class _Scene {
3183
3464
  this.manager = null;
3184
3465
  }
3185
3466
  if (this.device) {
3186
- _optionalChain([this, 'access', _107 => _107.device, 'access', _108 => _108.destroy, 'optionalCall', _109 => _109()]);
3467
+ _optionalChain([this, 'access', _117 => _117.device, 'access', _118 => _118.destroy, 'optionalCall', _119 => _119()]);
3187
3468
  this.device = null;
3188
3469
  }
3189
3470
  }
@@ -3479,7 +3760,7 @@ var Scene = (_class7 = class _Scene {
3479
3760
  shouldProjectA11y(node) {
3480
3761
  return node.interactive && (node.width > 0 || node.a11yFullViewport);
3481
3762
  }
3482
- syncA11y(node) {
3763
+ syncA11y(node, container = null) {
3483
3764
  if (!this.a11yRoot) return;
3484
3765
  if (node.isDOMPortal) {
3485
3766
  return;
@@ -3489,6 +3770,7 @@ var Scene = (_class7 = class _Scene {
3489
3770
  return;
3490
3771
  }
3491
3772
  const nodeStart = this._phaseTiming ? performance.now() : 0;
3773
+ let childContainer = container;
3492
3774
  if (this.shouldProjectA11y(node)) {
3493
3775
  let el = this.a11yElements.get(node.id);
3494
3776
  const attrs = node.getA11yAttributes();
@@ -3501,9 +3783,9 @@ var Scene = (_class7 = class _Scene {
3501
3783
  this.caretBlinkTimer = null;
3502
3784
  }
3503
3785
  }
3504
- if (el.parentNode === this.a11yRoot) {
3786
+ if (el.parentNode) {
3505
3787
  this.preserveFocusOnRemoval(el);
3506
- this.a11yRoot.removeChild(el);
3788
+ el.remove();
3507
3789
  }
3508
3790
  this.a11yElements.delete(node.id);
3509
3791
  el = void 0;
@@ -3615,7 +3897,7 @@ var Scene = (_class7 = class _Scene {
3615
3897
  el.addEventListener("compositionupdate", (e) => {
3616
3898
  const data = _nullishCoalesce(e.data, () => ( ""));
3617
3899
  composition = {
3618
- start: _nullishCoalesce(_optionalChain([composition, 'optionalAccess', _110 => _110.start]), () => ( 0)),
3900
+ start: _nullishCoalesce(_optionalChain([composition, 'optionalAccess', _120 => _120.start]), () => ( 0)),
3619
3901
  length: data.length
3620
3902
  };
3621
3903
  forward();
@@ -3688,7 +3970,7 @@ var Scene = (_class7 = class _Scene {
3688
3970
  this.syncOptionalAttribute(
3689
3971
  el,
3690
3972
  "href",
3691
- attrs.href === void 0 ? void 0 : _chunkLHONR3GOjs.sanitizeUrl.call(void 0, attrs.href)
3973
+ attrs.href === void 0 ? void 0 : _chunkKEBYJVD6js.sanitizeUrl.call(void 0, attrs.href)
3692
3974
  );
3693
3975
  this.syncOptionalAttribute(el, "target", attrs.target);
3694
3976
  }
@@ -3811,6 +4093,7 @@ var Scene = (_class7 = class _Scene {
3811
4093
  if (el.style.boxSizing !== "border-box") el.style.boxSizing = "border-box";
3812
4094
  if (el instanceof HTMLTextAreaElement) el.style.resize = "none";
3813
4095
  }
4096
+ const nestedIn = container && attrs.role && container.owned.has(attrs.role) ? container : null;
3814
4097
  if (node.a11yFullViewport) {
3815
4098
  el.style.left = "0px";
3816
4099
  el.style.top = "0px";
@@ -3821,11 +4104,36 @@ var Scene = (_class7 = class _Scene {
3821
4104
  } else {
3822
4105
  const worldTf = node.getWorldTransform();
3823
4106
  const { a, b, c, d, e, f } = worldTf;
3824
- el.style.left = `${e + node.a11yOffsetX}px`;
3825
- el.style.top = `${f + node.a11yOffsetY}px`;
4107
+ const originX = e + node.a11yOffsetX;
4108
+ const originY = f + node.a11yOffsetY;
4109
+ const parentEl = nestedIn && nestedIn.el !== el && nestedIn.el.isConnected ? nestedIn.el : this.a11yRoot;
4110
+ if (el.parentNode !== parentEl) {
4111
+ parentEl.appendChild(el);
4112
+ this.a11yNeedsReorder = true;
4113
+ }
4114
+ if (parentEl === this.a11yRoot) {
4115
+ el.style.left = `${originX}px`;
4116
+ el.style.top = `${originY}px`;
4117
+ el.style.transform = `matrix(${a}, ${b}, ${c}, ${d}, 0, 0)`;
4118
+ } else {
4119
+ const box = rebaseChildBox(
4120
+ nestedIn.transform,
4121
+ nestedIn.originX,
4122
+ nestedIn.originY,
4123
+ worldTf,
4124
+ originX,
4125
+ originY
4126
+ );
4127
+ el.style.left = `${box.left}px`;
4128
+ el.style.top = `${box.top}px`;
4129
+ el.style.transform = box.matrix;
4130
+ }
3826
4131
  el.style.width = `${node.width}px`;
3827
4132
  el.style.height = `${node.height}px`;
3828
- el.style.transform = `matrix(${a}, ${b}, ${c}, ${d}, 0, 0)`;
4133
+ const owned = attrs.role ? A11Y_REQUIRED_OWNED.get(attrs.role) : void 0;
4134
+ if (owned) {
4135
+ childContainer = { el, owned, transform: worldTf, originX, originY };
4136
+ }
3829
4137
  const visible = this.projectionBoxVisible(node, worldTf, 0);
3830
4138
  const display = visible ? "" : "none";
3831
4139
  if (el.style.display !== display) el.style.display = display;
@@ -3839,9 +4147,9 @@ var Scene = (_class7 = class _Scene {
3839
4147
  } else {
3840
4148
  this.syncContentProjection(node);
3841
4149
  }
3842
- for (const child of node.children) this.syncA11y(child);
4150
+ for (const child of node.children) this.syncA11y(child, childContainer);
3843
4151
  if (node === this.root) {
3844
- for (const overlay of this.overlayRoot.children) this.syncA11y(overlay);
4152
+ for (const overlay of this.overlayRoot.children) this.syncA11y(overlay, null);
3845
4153
  }
3846
4154
  }
3847
4155
  /**
@@ -4068,9 +4376,9 @@ var Scene = (_class7 = class _Scene {
4068
4376
  for (let lineIndex = 0; lineIndex < grid.lines.length; lineIndex++) {
4069
4377
  const gridLine = grid.lines[lineIndex];
4070
4378
  const projectedLine = projectionLines[lineIndex];
4071
- const lineHeight = _nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _111 => _111.lineHeight]), () => ( grid.lineHeight));
4072
- const baseline = _nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _112 => _112.baseline]), () => ( grid.baseline));
4073
- const lineFont = _nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _113 => _113.font]), () => ( grid.font));
4379
+ const lineHeight = _nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _121 => _121.lineHeight]), () => ( grid.lineHeight));
4380
+ const baseline = _nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _122 => _122.baseline]), () => ( grid.baseline));
4381
+ const lineFont = _nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _123 => _123.font]), () => ( grid.font));
4074
4382
  const lineSignature = contentGridLineSignature(
4075
4383
  grid,
4076
4384
  gridLine,
@@ -4090,8 +4398,8 @@ var Scene = (_class7 = class _Scene {
4090
4398
  lineElement.dir = "ltr";
4091
4399
  lineElement.dataset.vectoGridLine = `${lineIndex}`;
4092
4400
  lineElement.style.position = "absolute";
4093
- lineElement.style.left = `${_nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _114 => _114.x]), () => ( 0))}px`;
4094
- lineElement.style.top = `${(_nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _115 => _115.y]), () => ( lineIndex * grid.lineHeight))) + baseline - _text.cssLineBoxBaseline.call(void 0, lineFont, lineHeight)}px`;
4401
+ lineElement.style.left = `${_nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _124 => _124.x]), () => ( 0))}px`;
4402
+ lineElement.style.top = `${(_nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _125 => _125.y]), () => ( lineIndex * grid.lineHeight))) + baseline - _text.cssLineBoxBaseline.call(void 0, lineFont, lineHeight)}px`;
4095
4403
  lineElement.style.width = `${gridLine.width}px`;
4096
4404
  lineElement.style.height = `${lineHeight}px`;
4097
4405
  lineElement.style.whiteSpace = "pre";
@@ -4161,7 +4469,7 @@ var Scene = (_class7 = class _Scene {
4161
4469
  if (selectionLine !== null && selectionLine >= grid.lines.length) {
4162
4470
  rebuiltSelectionLine = true;
4163
4471
  }
4164
- _optionalChain([el, 'access', _116 => _116.lastElementChild, 'optionalAccess', _117 => _117.remove, 'call', _118 => _118()]);
4472
+ _optionalChain([el, 'access', _126 => _126.lastElementChild, 'optionalAccess', _127 => _127.remove, 'call', _128 => _128()]);
4165
4473
  }
4166
4474
  if (rebuiltSelectionLine) this.releaseContentSelectionForRebuild(el);
4167
4475
  el.dataset.vectoProjectionLines = signature;
@@ -4225,7 +4533,7 @@ var Scene = (_class7 = class _Scene {
4225
4533
  if (previous !== void 0 && typeof cancelAnimationFrame === "function") {
4226
4534
  cancelAnimationFrame(previous);
4227
4535
  }
4228
- _optionalChain([this, 'access', _119 => _119.contentGridCalibrationProbes, 'access', _120 => _120.get, 'call', _121 => _121(entityId), 'optionalAccess', _122 => _122.remove, 'call', _123 => _123()]);
4536
+ _optionalChain([this, 'access', _129 => _129.contentGridCalibrationProbes, 'access', _130 => _130.get, 'call', _131 => _131(entityId), 'optionalAccess', _132 => _132.remove, 'call', _133 => _133()]);
4229
4537
  this.contentGridCalibrationProbes.delete(entityId);
4230
4538
  const calibrationStart = typeof performance !== "undefined" ? performance.now() : 0;
4231
4539
  const probe = document.createElement("div");
@@ -4259,7 +4567,7 @@ var Scene = (_class7 = class _Scene {
4259
4567
  target.dataset.vectoGridCalib = generation;
4260
4568
  continue;
4261
4569
  }
4262
- const sourceText = _nullishCoalesce(_optionalChain([target, 'access', _124 => _124.textContent, 'optionalAccess', _125 => _125.slice, 'call', _126 => _126(0, sourceLength)]), () => ( ""));
4570
+ const sourceText = _nullishCoalesce(_optionalChain([target, 'access', _134 => _134.textContent, 'optionalAccess', _135 => _135.slice, 'call', _136 => _136(0, sourceLength)]), () => ( ""));
4263
4571
  if (!sourceText) {
4264
4572
  target.dataset.vectoGridCalib = generation;
4265
4573
  continue;
@@ -4399,9 +4707,9 @@ var Scene = (_class7 = class _Scene {
4399
4707
  this.caretBlinkTimer = null;
4400
4708
  }
4401
4709
  }
4402
- if (el.parentNode === this.a11yRoot) {
4710
+ if (el.parentNode) {
4403
4711
  this.preserveFocusOnRemoval(el);
4404
- this.a11yRoot.removeChild(el);
4712
+ el.remove();
4405
4713
  }
4406
4714
  this.a11yElements.delete(id);
4407
4715
  }
@@ -4414,23 +4722,39 @@ var Scene = (_class7 = class _Scene {
4414
4722
  const fullLen = this.fullViewportElements.length;
4415
4723
  const normalLen = this.normalElements.length;
4416
4724
  const totalLen = fullLen + normalLen;
4725
+ this.a11yOrderCursors.clear();
4417
4726
  for (let i = 0; i < totalLen; i++) {
4418
4727
  const expected = i < fullLen ? this.fullViewportElements[i] : this.normalElements[i - fullLen];
4419
- const current = this.a11yRoot.childNodes[i];
4728
+ const parent = expected.parentNode;
4729
+ if (!parent) continue;
4730
+ const at = _nullishCoalesce(this.a11yOrderCursors.get(parent), () => ( 0));
4731
+ this.a11yOrderCursors.set(parent, at + 1);
4732
+ const current = parent.childNodes[at];
4420
4733
  if (current !== expected) {
4421
- this.a11yRoot.insertBefore(expected, current || null);
4734
+ parent.insertBefore(expected, current || null);
4422
4735
  }
4423
4736
  }
4424
4737
  this.a11yNeedsReorder = false;
4425
4738
  }
4426
4739
  /**
4427
4740
  * Reorder `normalElements` (in place) into visual reading order using the
4428
- * world positions `syncA11y` already wrote to each element's inline style
4741
+ * positions `syncA11y` already wrote to each element's inline style
4429
4742
  * (`top`/`left`/`height`). Elements are grouped into rows top-to-bottom (an
4430
4743
  * element belongs to the current row while its top is above the row's
4431
4744
  * running bottom edge), then sorted within a row by `left` — ascending for
4432
4745
  * `'ltr'`, descending for `'rtl'`. The sort is stable, so entities at the
4433
4746
  * same position keep their scene-graph (collection) order as a tiebreak.
4747
+ *
4748
+ * Those inline values are world coordinates for a top-level mirror but
4749
+ * PARENT-RELATIVE for a nested one, so this list mixes coordinate spaces.
4750
+ * That is sound because the result is only ever applied per DOM parent
4751
+ * ({@link enforceA11yDomOrder} advances a cursor per parent), and all of one
4752
+ * parent's children share one space: a `grid`'s rows are all grid-relative, a
4753
+ * `row`'s cells all row-relative. Comparisons ACROSS spaces do happen while
4754
+ * banding, but they only affect the relative order of elements in different
4755
+ * parents, which no `insertBefore` ever acts on. Normalizing everything back
4756
+ * to world coordinates here would cost a transform per element per frame to
4757
+ * change nothing observable.
4434
4758
  */
4435
4759
  sortNormalElementsVisually() {
4436
4760
  const els = this.normalElements;
@@ -4470,12 +4794,12 @@ var Scene = (_class7 = class _Scene {
4470
4794
  syncOverlayGeometry() {
4471
4795
  const parent = this.canvas.parentElement;
4472
4796
  if (!parent) return;
4473
- const canvasRect = _optionalChain([this, 'access', _127 => _127.canvas, 'access', _128 => _128.getBoundingClientRect, 'optionalCall', _129 => _129()]);
4474
- const parentRect = _optionalChain([parent, 'access', _130 => _130.getBoundingClientRect, 'optionalCall', _131 => _131()]);
4475
- const cssWidth = _optionalChain([canvasRect, 'optionalAccess', _132 => _132.width]) || this.canvas.clientWidth || this.width;
4476
- const cssHeight = _optionalChain([canvasRect, 'optionalAccess', _133 => _133.height]) || this.canvas.clientHeight || this.height;
4477
- const left = (_nullishCoalesce(_optionalChain([canvasRect, 'optionalAccess', _134 => _134.left]), () => ( 0))) - (_nullishCoalesce(_optionalChain([parentRect, 'optionalAccess', _135 => _135.left]), () => ( 0))) - (parent.clientLeft || 0) + parent.scrollLeft;
4478
- const top = (_nullishCoalesce(_optionalChain([canvasRect, 'optionalAccess', _136 => _136.top]), () => ( 0))) - (_nullishCoalesce(_optionalChain([parentRect, 'optionalAccess', _137 => _137.top]), () => ( 0))) - (parent.clientTop || 0) + parent.scrollTop;
4797
+ const canvasRect = _optionalChain([this, 'access', _137 => _137.canvas, 'access', _138 => _138.getBoundingClientRect, 'optionalCall', _139 => _139()]);
4798
+ const parentRect = _optionalChain([parent, 'access', _140 => _140.getBoundingClientRect, 'optionalCall', _141 => _141()]);
4799
+ const cssWidth = _optionalChain([canvasRect, 'optionalAccess', _142 => _142.width]) || this.canvas.clientWidth || this.width;
4800
+ const cssHeight = _optionalChain([canvasRect, 'optionalAccess', _143 => _143.height]) || this.canvas.clientHeight || this.height;
4801
+ const left = (_nullishCoalesce(_optionalChain([canvasRect, 'optionalAccess', _144 => _144.left]), () => ( 0))) - (_nullishCoalesce(_optionalChain([parentRect, 'optionalAccess', _145 => _145.left]), () => ( 0))) - (parent.clientLeft || 0) + parent.scrollLeft;
4802
+ const top = (_nullishCoalesce(_optionalChain([canvasRect, 'optionalAccess', _146 => _146.top]), () => ( 0))) - (_nullishCoalesce(_optionalChain([parentRect, 'optionalAccess', _147 => _147.top]), () => ( 0))) - (parent.clientTop || 0) + parent.scrollTop;
4479
4803
  const scaleX = this.width > 0 ? cssWidth / this.width : 1;
4480
4804
  const scaleY = this.height > 0 ? cssHeight / this.height : 1;
4481
4805
  const prev = this._overlayGeometry;
@@ -4614,7 +4938,7 @@ var Scene = (_class7 = class _Scene {
4614
4938
  * (and {@link respectReducedMotion} is on). `0` means uncapped.
4615
4939
  */
4616
4940
  effectiveMaxFPS() {
4617
- const reduced = this.respectReducedMotion && !!_optionalChain([this, 'access', _138 => _138.reducedMotionQuery, 'optionalAccess', _139 => _139.matches]);
4941
+ const reduced = this.respectReducedMotion && !!_optionalChain([this, 'access', _148 => _148.reducedMotionQuery, 'optionalAccess', _149 => _149.matches]);
4618
4942
  if (reduced)
4619
4943
  return this.maxFPS > 0 ? Math.min(this.maxFPS, REDUCED_MOTION_FPS) : REDUCED_MOTION_FPS;
4620
4944
  return this.maxFPS;
@@ -4666,9 +4990,11 @@ var Scene = (_class7 = class _Scene {
4666
4990
  if ((hasInteractive || this.a11yElements.size > 0 || wantsContentSync) && (shouldSyncInterval || this.a11yPendingSyncAfterAnimation)) {
4667
4991
  this.lastA11ySync = time;
4668
4992
  if (hasInteractive || wantsContentSync) {
4993
+ const userTiming = this._userTiming ? beginVectoUserTiming(VECTO_USER_TIMING.scene.a11ySync) : null;
4669
4994
  const t0 = this._phaseTiming ? performance.now() : 0;
4670
4995
  this.syncA11y(this.root);
4671
4996
  if (this._phaseTiming) this._recordPhase("a11ySync", performance.now() - t0);
4997
+ if (userTiming) endVectoUserTiming(userTiming);
4672
4998
  }
4673
4999
  const t1 = this._phaseTiming ? performance.now() : 0;
4674
5000
  this.enforceA11yDomOrder();
@@ -4682,12 +5008,32 @@ var Scene = (_class7 = class _Scene {
4682
5008
  /**
4683
5009
  * Render the entire scene graph onto the specified renderer.
4684
5010
  *
5011
+ * Main-frame causal order is a correctness contract:
5012
+ *
5013
+ * 1. Browser/input callbacks finish before the scheduled frame begins.
5014
+ * 2. Batched property drivers and particle simulation advance.
5015
+ * 3. Entity `update()` hooks run.
5016
+ * 4. Transform inputs are gathered and world matrices are composed.
5017
+ * 5. Updated world bounds are tested for culling.
5018
+ * 6. Visible entities paint in scene-graph order.
5019
+ * 7. Canvas/GPU batches flush and retained renderers present.
5020
+ * 8. The rAF loop synchronizes content and accessibility projections after
5021
+ * this method returns.
5022
+ *
5023
+ * The causal order is fixed; physical walks may stay fused. The JavaScript
5024
+ * transform path interleaves update → compose → cull → paint per node in
5025
+ * pre-order. The WASM path updates the whole tree first, then gathers and
5026
+ * composes it in one store pass before the same cull/paint walk. Both must
5027
+ * expose an update's transform mutation in that same rendered frame.
5028
+ * Secondary renderers are read-only snapshots: they skip simulation and
5029
+ * updates, then compose/cull/paint/flush the current state.
5030
+ *
4685
5031
  * @param renderer - The renderer instance to draw to.
4686
5032
  * @param dt - Delta time in milliseconds (default 0).
4687
5033
  * @param time - Current absolute time in milliseconds (default 0).
4688
5034
  */
4689
5035
  render(renderer, dt = 0, time = 0) {
4690
- if (_optionalChain([renderer, 'access', _140 => _140.isContextLost, 'optionalCall', _141 => _141()])) return;
5036
+ if (_optionalChain([renderer, 'access', _150 => _150.isContextLost, 'optionalCall', _151 => _151()])) return;
4691
5037
  const isMainRenderer = renderer === this.renderer;
4692
5038
  if (isMainRenderer && this.a11yRoot && this.canvas.parentElement) {
4693
5039
  const parentStyle = this.canvas.parentElement.style;
@@ -4706,6 +5052,10 @@ var Scene = (_class7 = class _Scene {
4706
5052
  const computeEntities = this._computeEntitiesFor(this._structureVersion);
4707
5053
  if (computeEntities.length > 0) {
4708
5054
  const isMainRenderPath = renderer === this.renderer;
5055
+ if (isMainRenderPath) {
5056
+ this._particleReason = this._particleWasm ? "active" : "not-installed";
5057
+ this._particlePath = this._particleWasm ? "wasm" : "js";
5058
+ }
4709
5059
  if (isMainRenderPath && !this.device && !this.webgpuDisabled && !this.initializingWebGPU && !this.deviceLost) {
4710
5060
  this.initializingWebGPU = true;
4711
5061
  this.initWebGPUContext(computeEntities).then((newDevice) => {
@@ -4781,6 +5131,8 @@ var Scene = (_class7 = class _Scene {
4781
5131
  }
4782
5132
  this.device.queue.submit([commandEncoder.finish()]);
4783
5133
  if (this.gpuContext) this.gpuHasContent = true;
5134
+ this._particleReason = "active";
5135
+ this._particlePath = "webgpu";
4784
5136
  } catch (e) {
4785
5137
  console.error("WebGPU frame execution failed. Falling back.", e);
4786
5138
  this.deviceLost = true;
@@ -4802,18 +5154,30 @@ var Scene = (_class7 = class _Scene {
4802
5154
  }
4803
5155
  }
4804
5156
  if (this._particleWasm) {
4805
- entity.stepWithBackend(this._particleWasm, dt / 1e3, mx, my, this.width, this.height);
5157
+ if (!entity.stepWithBackend(
5158
+ this._particleWasm,
5159
+ dt / 1e3,
5160
+ mx,
5161
+ my,
5162
+ this.width,
5163
+ this.height
5164
+ )) {
5165
+ this._particleReason = "rejected";
5166
+ this._particlePath = "js";
5167
+ }
4806
5168
  } else {
4807
5169
  entity.updateCPU(dt / 1e3, mx, my, this.width, this.height);
4808
5170
  }
4809
5171
  }
4810
5172
  }
4811
5173
  } else if (isMainRenderer) {
5174
+ this._particleReason = "not-applicable";
5175
+ this._particlePath = "none";
4812
5176
  this.clearGPUCanvasIfStale();
4813
5177
  }
4814
5178
  renderer.clear();
4815
5179
  if (isMainRenderer) {
4816
- _optionalChain([this, 'access', _142 => _142.pointRenderer, 'optionalAccess', _143 => _143.begin, 'call', _144 => _144()]);
5180
+ _optionalChain([this, 'access', _152 => _152.pointRenderer, 'optionalAccess', _153 => _153.begin, 'call', _154 => _154()]);
4817
5181
  }
4818
5182
  const vw = this.width;
4819
5183
  const vh = this.height;
@@ -4837,6 +5201,9 @@ var Scene = (_class7 = class _Scene {
4837
5201
  }
4838
5202
  };
4839
5203
  const wasmMain = isMainRenderer && this._wasm !== null && this._transformBackend === "wasm";
5204
+ if (isMainRenderer) {
5205
+ this._transformReason = wasmMain ? "active" : "not-installed";
5206
+ }
4840
5207
  if (wasmMain) {
4841
5208
  const updateWalk = (node) => {
4842
5209
  runUpdate(node);
@@ -4846,10 +5213,13 @@ var Scene = (_class7 = class _Scene {
4846
5213
  updateWalk(this.root);
4847
5214
  for (const overlay of this.overlayRoot.children) updateWalk(overlay);
4848
5215
  }
5216
+ const transformTiming = this._userTiming ? beginVectoUserTiming(VECTO_USER_TIMING.scene.transform) : null;
4849
5217
  const wasmT0 = this._phaseTiming ? performance.now() : 0;
4850
5218
  const wasmWorld = wasmMain ? this._syncWasmStore() : null;
4851
5219
  if (this._phaseTiming) this._recordPhase("transform", performance.now() - wasmT0);
5220
+ if (transformTiming) endVectoUserTiming(transformTiming);
4852
5221
  const wasmSlotEntity = this._slotEntity;
5222
+ let userEntityPaintMs = 0;
4853
5223
  const renderNode = (node, pa, pb, pc, pd, pe, pf, parentOpacity) => {
4854
5224
  if (isMainRenderer && !wasmMain) {
4855
5225
  runUpdate(node);
@@ -4978,10 +5348,15 @@ var Scene = (_class7 = class _Scene {
4978
5348
  );
4979
5349
  }
4980
5350
  } else {
4981
- if (this._phaseTiming) {
5351
+ if (this._userTiming || this._phaseTiming) {
4982
5352
  const t0 = performance.now();
4983
- node.render(renderer);
4984
- this._recordPhase("entityPaint", performance.now() - t0);
5353
+ try {
5354
+ node.render(renderer);
5355
+ } finally {
5356
+ const elapsed = performance.now() - t0;
5357
+ if (this._userTiming) userEntityPaintMs += elapsed;
5358
+ if (this._phaseTiming) this._recordPhase("entityPaint", elapsed);
5359
+ }
4985
5360
  } else {
4986
5361
  node.render(renderer);
4987
5362
  }
@@ -4996,24 +5371,31 @@ var Scene = (_class7 = class _Scene {
4996
5371
  renderer.flush();
4997
5372
  renderer.restore();
4998
5373
  };
5374
+ const drawTiming = this._userTiming ? beginVectoUserTiming(VECTO_USER_TIMING.scene.drawWalk) : null;
4999
5375
  const drawT0 = this._phaseTiming ? performance.now() : 0;
5000
5376
  renderNode(this.root, 1, 0, 0, 1, 0, 0, 1);
5001
5377
  for (const overlay of this.overlayRoot.children) {
5002
5378
  renderNode(overlay, 1, 0, 0, 1, 0, 0, 1);
5003
5379
  }
5004
5380
  if (this._phaseTiming) this._recordPhase("drawWalk", performance.now() - drawT0);
5381
+ if (drawTiming) endVectoUserTiming(drawTiming);
5382
+ if (this._userTiming) {
5383
+ measureVectoUserTiming(VECTO_USER_TIMING.scene.entityPaint, userEntityPaintMs);
5384
+ }
5005
5385
  if (isMainRenderer) {
5006
5386
  this.frameHadAnimation = walkHadAnimation;
5007
5387
  this.frameHadInteractive = walkHadInteractive;
5008
5388
  this.reconcilePortals();
5009
5389
  }
5390
+ const flushTiming = this._userTiming ? beginVectoUserTiming(VECTO_USER_TIMING.scene.flush) : null;
5010
5391
  const flushT0 = this._phaseTiming ? performance.now() : 0;
5011
5392
  renderer.flush();
5012
5393
  if (isMainRenderer) {
5013
- _optionalChain([this, 'access', _145 => _145.pointRenderer, 'optionalAccess', _146 => _146.flush, 'call', _147 => _147()]);
5394
+ _optionalChain([this, 'access', _155 => _155.pointRenderer, 'optionalAccess', _156 => _156.flush, 'call', _157 => _157()]);
5014
5395
  }
5015
- _optionalChain([renderer, 'access', _148 => _148.present, 'optionalCall', _149 => _149()]);
5396
+ _optionalChain([renderer, 'access', _158 => _158.present, 'optionalCall', _159 => _159()]);
5016
5397
  if (this._phaseTiming) this._recordPhase("flush", performance.now() - flushT0);
5398
+ if (flushTiming) endVectoUserTiming(flushTiming);
5017
5399
  if (this._devActive) {
5018
5400
  this._devFrameCount++;
5019
5401
  this._devRunChecks();
@@ -5023,7 +5405,7 @@ var Scene = (_class7 = class _Scene {
5023
5405
  * Export the current scene state to a lightweight, flat SVG XML string.
5024
5406
  */
5025
5407
  toSVG() {
5026
- const renderer = new (0, _chunkLHONR3GOjs.SVGRenderer)(this.width, this.height);
5408
+ const renderer = new (0, _chunkKEBYJVD6js.SVGRenderer)(this.width, this.height);
5027
5409
  this.render(renderer, 0, 0);
5028
5410
  return renderer.toXMLString();
5029
5411
  }
@@ -5105,7 +5487,7 @@ var Scene = (_class7 = class _Scene {
5105
5487
  });
5106
5488
  pass.end();
5107
5489
  this.device.queue.submit([encoder.finish()]);
5108
- } catch (e10) {
5490
+ } catch (e15) {
5109
5491
  }
5110
5492
  this.gpuHasContent = false;
5111
5493
  }
@@ -5292,8 +5674,8 @@ function pointInBounds(b, x, y) {
5292
5674
  function contentGridLineSignature(grid, line, projected, lineHeight, baseline, font, isFirstLine) {
5293
5675
  const parts = [
5294
5676
  // Line box: position, size, and the font that resolves its baseline.
5295
- `${_nullishCoalesce(_optionalChain([projected, 'optionalAccess', _150 => _150.x]), () => ( 0))}`,
5296
- `${_nullishCoalesce(_optionalChain([projected, 'optionalAccess', _151 => _151.y]), () => ( ""))}`,
5677
+ `${_nullishCoalesce(_optionalChain([projected, 'optionalAccess', _160 => _160.x]), () => ( 0))}`,
5678
+ `${_nullishCoalesce(_optionalChain([projected, 'optionalAccess', _161 => _161.y]), () => ( ""))}`,
5297
5679
  `${lineHeight}`,
5298
5680
  `${baseline}`,
5299
5681
  font,
@@ -5340,15 +5722,15 @@ var TextEntity = (_class8 = class extends _chunkAGP4VLF4js.Entity {
5340
5722
 
5341
5723
 
5342
5724
 
5343
- __init140() {this.nodes = []}
5725
+ __init149() {this.nodes = []}
5344
5726
 
5345
- __init141() {this.fillStyle = "#94a3b8"}
5346
- __init142() {this.strokeStyle = null}
5347
- __init143() {this.hoveredFillStyle = "#ffffff"}
5348
- __init144() {this.lineWidth = 1}
5349
- __init145() {this.isHovered = false}
5727
+ __init150() {this.fillStyle = "#94a3b8"}
5728
+ __init151() {this.strokeStyle = null}
5729
+ __init152() {this.hoveredFillStyle = "#ffffff"}
5730
+ __init153() {this.lineWidth = 1}
5731
+ __init154() {this.isHovered = false}
5350
5732
  constructor(text, atlas, maxWidth, fontSize = 24) {
5351
- super();_class8.prototype.__init140.call(this);_class8.prototype.__init141.call(this);_class8.prototype.__init142.call(this);_class8.prototype.__init143.call(this);_class8.prototype.__init144.call(this);_class8.prototype.__init145.call(this);;
5733
+ super();_class8.prototype.__init149.call(this);_class8.prototype.__init150.call(this);_class8.prototype.__init151.call(this);_class8.prototype.__init152.call(this);_class8.prototype.__init153.call(this);_class8.prototype.__init154.call(this);;
5352
5734
  this.text = text;
5353
5735
  this.atlas = atlas;
5354
5736
  this.fontSize = fontSize;
@@ -5463,15 +5845,15 @@ var TextEntity = (_class8 = class extends _chunkAGP4VLF4js.Entity {
5463
5845
  // src/components/GridTextEntity.ts
5464
5846
  var GridTextEntity = (_class9 = class extends _chunkAGP4VLF4js.Entity {
5465
5847
 
5466
- __init146() {this.fillStyle = "#ffffff"}
5467
- __init147() {this.grid = []}
5848
+ __init155() {this.fillStyle = "#ffffff"}
5849
+ __init156() {this.grid = []}
5468
5850
  // Array of rows
5469
- __init148() {this.cols = 0}
5470
- __init149() {this.rows = 0}
5851
+ __init157() {this.cols = 0}
5852
+ __init158() {this.rows = 0}
5471
5853
 
5472
5854
 
5473
5855
  constructor(_atlas, fontSize = 10) {
5474
- super();_class9.prototype.__init146.call(this);_class9.prototype.__init147.call(this);_class9.prototype.__init148.call(this);_class9.prototype.__init149.call(this);;
5856
+ super();_class9.prototype.__init155.call(this);_class9.prototype.__init156.call(this);_class9.prototype.__init157.call(this);_class9.prototype.__init158.call(this);;
5475
5857
  this.fontSize = fontSize;
5476
5858
  this.charWidth = fontSize * 1;
5477
5859
  this.charHeight = fontSize * 1.1;
@@ -5480,7 +5862,7 @@ var GridTextEntity = (_class9 = class extends _chunkAGP4VLF4js.Entity {
5480
5862
  updateGrid(ascii) {
5481
5863
  this.grid = ascii;
5482
5864
  this.rows = ascii.length;
5483
- this.cols = _optionalChain([ascii, 'access', _152 => _152[0], 'optionalAccess', _153 => _153.length]) || 0;
5865
+ this.cols = _optionalChain([ascii, 'access', _162 => _162[0], 'optionalAccess', _163 => _163.length]) || 0;
5484
5866
  }
5485
5867
  isPointInside(_globalX, _globalY) {
5486
5868
  return false;
@@ -5563,23 +5945,23 @@ var SplineEntity = (_class10 = class extends _chunkAGP4VLF4js.Entity {
5563
5945
 
5564
5946
 
5565
5947
 
5566
- __init150() {this.offscreen = null}
5567
- __init151() {this.baked = false}
5948
+ __init159() {this.offscreen = null}
5949
+ __init160() {this.baked = false}
5568
5950
  /** Logical (CSS-pixel) size of the baked bitmap — the blit destination size. */
5569
- __init152() {this.bakedWidth = 0}
5570
- __init153() {this.bakedHeight = 0}
5951
+ __init161() {this.bakedWidth = 0}
5952
+ __init162() {this.bakedHeight = 0}
5571
5953
  /** Gradient strokes can't be baked to a solid-color bitmap; they render per-frame. */
5572
5954
 
5573
5955
  /** Lazily-flattened polylines (one Float32Array of [x,y,...] per segment) for hit-testing. */
5574
- __init154() {this.polylines = null}
5956
+ __init163() {this.polylines = null}
5575
5957
  /**
5576
5958
  * When `true`, the renderer draws a rounded-rect outline of the entity's
5577
5959
  * local bounds after painting the curves. Useful for drag feedback and
5578
5960
  * debugging hit areas. Defaults to `false`.
5579
5961
  */
5580
- __init155() {this.showBounds = false}
5962
+ __init164() {this.showBounds = false}
5581
5963
  constructor(doc, opts = {}) {
5582
- super();_class10.prototype.__init150.call(this);_class10.prototype.__init151.call(this);_class10.prototype.__init152.call(this);_class10.prototype.__init153.call(this);_class10.prototype.__init154.call(this);_class10.prototype.__init155.call(this);;
5964
+ super();_class10.prototype.__init159.call(this);_class10.prototype.__init160.call(this);_class10.prototype.__init161.call(this);_class10.prototype.__init162.call(this);_class10.prototype.__init163.call(this);_class10.prototype.__init164.call(this);;
5583
5965
  this.doc = doc;
5584
5966
  this.lineWidth = _nullishCoalesce(opts.lineWidth, () => ( 2));
5585
5967
  this.cache = _nullishCoalesce(opts.cache, () => ( true));
@@ -5590,7 +5972,7 @@ var SplineEntity = (_class10 = class extends _chunkAGP4VLF4js.Entity {
5590
5972
  this.width = this.bounds.width;
5591
5973
  this.height = this.bounds.height;
5592
5974
  const isGradient = (c) => c !== null && !Array.isArray(c);
5593
- this.containsGradient = (_nullishCoalesce(_optionalChain([this, 'access', _154 => _154.doc, 'access', _155 => _155.equations, 'optionalAccess', _156 => _156.some, 'call', _157 => _157((eq) => isGradient(eq.color_rgb))]), () => ( false))) || (_nullishCoalesce(_optionalChain([this, 'access', _158 => _158.doc, 'access', _159 => _159.paths, 'optionalAccess', _160 => _160.some, 'call', _161 => _161((p) => isGradient(p.color_rgb))]), () => ( false)));
5975
+ this.containsGradient = (_nullishCoalesce(_optionalChain([this, 'access', _164 => _164.doc, 'access', _165 => _165.equations, 'optionalAccess', _166 => _166.some, 'call', _167 => _167((eq) => isGradient(eq.color_rgb))]), () => ( false))) || (_nullishCoalesce(_optionalChain([this, 'access', _168 => _168.doc, 'access', _169 => _169.paths, 'optionalAccess', _170 => _170.some, 'call', _171 => _171((p) => isGradient(p.color_rgb))]), () => ( false)));
5594
5976
  this.interactive = true;
5595
5977
  }
5596
5978
  computeBounds() {
@@ -5960,19 +6342,19 @@ var _math = require('@vectojs/math'); _createStarExport(_math);
5960
6342
  // src/tree/DOMPortalEntity.ts
5961
6343
  var DOMPortalEntity = (_class11 = class extends _chunkAGP4VLF4js.Entity {
5962
6344
 
5963
- __init156() {this.isDOMPortal = true}
5964
- __init157() {this.domListeners = []}
5965
- __init158() {this.resizeObserver = null}
5966
- __init159() {this.domBound = false}
5967
- __init160() {this.cachedWidth = 100}
5968
- __init161() {this.cachedHeight = 100}
5969
- __init162() {this.lastWidth = ""}
5970
- __init163() {this.lastHeight = ""}
5971
- __init164() {this.lastTransform = ""}
5972
- __init165() {this.lastZIndex = ""}
5973
- __init166() {this.lastOpacity = ""}
6345
+ __init165() {this.isDOMPortal = true}
6346
+ __init166() {this.domListeners = []}
6347
+ __init167() {this.resizeObserver = null}
6348
+ __init168() {this.domBound = false}
6349
+ __init169() {this.cachedWidth = 100}
6350
+ __init170() {this.cachedHeight = 100}
6351
+ __init171() {this.lastWidth = ""}
6352
+ __init172() {this.lastHeight = ""}
6353
+ __init173() {this.lastTransform = ""}
6354
+ __init174() {this.lastZIndex = ""}
6355
+ __init175() {this.lastOpacity = ""}
5974
6356
  constructor(domElement, width, height, id) {
5975
- super(id);_class11.prototype.__init156.call(this);_class11.prototype.__init157.call(this);_class11.prototype.__init158.call(this);_class11.prototype.__init159.call(this);_class11.prototype.__init160.call(this);_class11.prototype.__init161.call(this);_class11.prototype.__init162.call(this);_class11.prototype.__init163.call(this);_class11.prototype.__init164.call(this);_class11.prototype.__init165.call(this);_class11.prototype.__init166.call(this);;
6357
+ super(id);_class11.prototype.__init165.call(this);_class11.prototype.__init166.call(this);_class11.prototype.__init167.call(this);_class11.prototype.__init168.call(this);_class11.prototype.__init169.call(this);_class11.prototype.__init170.call(this);_class11.prototype.__init171.call(this);_class11.prototype.__init172.call(this);_class11.prototype.__init173.call(this);_class11.prototype.__init174.call(this);_class11.prototype.__init175.call(this);;
5976
6358
  this.domElement = domElement;
5977
6359
  this.width = _nullishCoalesce(width, () => ( 0));
5978
6360
  this.height = _nullishCoalesce(height, () => ( 0));
@@ -6084,8 +6466,12 @@ var DOMPortalEntity = (_class11 = class extends _chunkAGP4VLF4js.Entity {
6084
6466
  }, _class11);
6085
6467
 
6086
6468
  // src/index.ts
6087
- Scene.registerWebGLPointRendererCreator(_chunkLHONR3GOjs.createWebGLPointRenderer);
6088
- Scene.registerWebGPUParticleSystemManager(_chunkLHONR3GOjs.WebGPUParticleSystemManager);
6469
+ Scene.registerWebGLPointRendererCreator(_chunkKEBYJVD6js.createWebGLPointRenderer);
6470
+ Scene.registerWebGPUParticleSystemManager(_chunkKEBYJVD6js.WebGPUParticleSystemManager);
6471
+
6472
+
6473
+
6474
+
6089
6475
 
6090
6476
 
6091
6477
 
@@ -6121,4 +6507,4 @@ Scene.registerWebGPUParticleSystemManager(_chunkLHONR3GOjs.WebGPUParticleSystemM
6121
6507
 
6122
6508
 
6123
6509
 
6124
- exports.CanvasRenderer = _chunkLHONR3GOjs.CanvasRenderer; exports.Circle = Circle; exports.ComputeParticleEntity = ComputeParticleEntity; exports.DOMPortalEntity = DOMPortalEntity; exports.Entity = _chunkAGP4VLF4js.Entity; exports.GlyphRasterAtlas = _chunkLHONR3GOjs.GlyphRasterAtlas; exports.GridTextEntity = GridTextEntity; exports.Group = Group; exports.MSDFTextEntity = _chunkAGP4VLF4js.MSDFTextEntity; exports.PARTICLE_OFFSET_LIFE = PARTICLE_OFFSET_LIFE; exports.PARTICLE_OFFSET_ORIGIN_X = PARTICLE_OFFSET_ORIGIN_X; exports.PARTICLE_OFFSET_ORIGIN_Y = PARTICLE_OFFSET_ORIGIN_Y; exports.PARTICLE_OFFSET_POSITION_X = PARTICLE_OFFSET_POSITION_X; exports.PARTICLE_OFFSET_POSITION_Y = PARTICLE_OFFSET_POSITION_Y; exports.PARTICLE_OFFSET_SIZE = PARTICLE_OFFSET_SIZE; exports.PARTICLE_OFFSET_VELOCITY_X = PARTICLE_OFFSET_VELOCITY_X; exports.PARTICLE_OFFSET_VELOCITY_Y = PARTICLE_OFFSET_VELOCITY_Y; exports.PARTICLE_STRIDE_FLOATS = PARTICLE_STRIDE_FLOATS; exports.REDUCED_MOTION_FPS = REDUCED_MOTION_FPS; exports.Rect = Rect; exports.SVGEntity = _chunkAGP4VLF4js.SVGEntity; exports.SVGRenderer = _chunkLHONR3GOjs.SVGRenderer; exports.Scene = Scene; exports.SplineEntity = SplineEntity; exports.TextEntity = TextEntity; exports.TextRasterCache = _chunkLHONR3GOjs.TextRasterCache; exports.VectoJSEvent = _chunkAGP4VLF4js.VectoJSEvent; exports.WebGPUParticleSystemManager = _chunkLHONR3GOjs.WebGPUParticleSystemManager; exports.createWebGLPointRenderer = _chunkLHONR3GOjs.createWebGLPointRenderer; exports.isSafeUrl = _chunkLHONR3GOjs.isSafeUrl; exports.loadSpline = loadSpline; exports.parseColorToRGBA = _chunkLHONR3GOjs.parseColorToRGBA; exports.polySegmentToBezier = polySegmentToBezier; exports.sanitizeUrl = _chunkLHONR3GOjs.sanitizeUrl;
6510
+ exports.CanvasRenderer = _chunkKEBYJVD6js.CanvasRenderer; exports.Circle = Circle; exports.ComputeParticleEntity = ComputeParticleEntity; exports.DOMPortalEntity = DOMPortalEntity; exports.Entity = _chunkAGP4VLF4js.Entity; exports.GlyphRasterAtlas = _chunkKEBYJVD6js.GlyphRasterAtlas; exports.GridTextEntity = GridTextEntity; exports.Group = Group; exports.MSDFTextEntity = _chunkAGP4VLF4js.MSDFTextEntity; exports.PARTICLE_OFFSET_LIFE = PARTICLE_OFFSET_LIFE; exports.PARTICLE_OFFSET_ORIGIN_X = PARTICLE_OFFSET_ORIGIN_X; exports.PARTICLE_OFFSET_ORIGIN_Y = PARTICLE_OFFSET_ORIGIN_Y; exports.PARTICLE_OFFSET_POSITION_X = PARTICLE_OFFSET_POSITION_X; exports.PARTICLE_OFFSET_POSITION_Y = PARTICLE_OFFSET_POSITION_Y; exports.PARTICLE_OFFSET_SIZE = PARTICLE_OFFSET_SIZE; exports.PARTICLE_OFFSET_VELOCITY_X = PARTICLE_OFFSET_VELOCITY_X; exports.PARTICLE_OFFSET_VELOCITY_Y = PARTICLE_OFFSET_VELOCITY_Y; exports.PARTICLE_STRIDE_FLOATS = PARTICLE_STRIDE_FLOATS; exports.REDUCED_MOTION_FPS = REDUCED_MOTION_FPS; exports.Rect = Rect; exports.SVGEntity = _chunkAGP4VLF4js.SVGEntity; exports.SVGRenderer = _chunkKEBYJVD6js.SVGRenderer; exports.Scene = Scene; exports.SplineEntity = SplineEntity; exports.TextEntity = TextEntity; exports.TextRasterCache = _chunkKEBYJVD6js.TextRasterCache; exports.VECTO_USER_TIMING = VECTO_USER_TIMING; exports.VectoJSEvent = _chunkAGP4VLF4js.VectoJSEvent; exports.WebGPUParticleSystemManager = _chunkKEBYJVD6js.WebGPUParticleSystemManager; exports.beginVectoUserTiming = beginVectoUserTiming; exports.createWebGLPointRenderer = _chunkKEBYJVD6js.createWebGLPointRenderer; exports.endVectoUserTiming = endVectoUserTiming; exports.isSafeUrl = _chunkKEBYJVD6js.isSafeUrl; exports.loadSpline = loadSpline; exports.measureVectoUserTiming = measureVectoUserTiming; exports.parseColorToRGBA = _chunkKEBYJVD6js.parseColorToRGBA; exports.polySegmentToBezier = polySegmentToBezier; exports.sanitizeUrl = _chunkKEBYJVD6js.sanitizeUrl;