@vectojs/core 1.38.1 → 1.39.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
@@ -12,14 +12,14 @@
12
12
 
13
13
 
14
14
 
15
- var _chunkZYYP2M5Djs = require('./chunk-ZYYP2M5D.js');
15
+ var _chunkKRUDTGNRjs = require('./chunk-KRUDTGNR.js');
16
16
 
17
17
 
18
18
 
19
19
 
20
20
 
21
21
 
22
- var _chunkSRXU3ZXKjs = require('./chunk-SRXU3ZXK.js');
22
+ var _chunkPOPXUPKRjs = require('./chunk-POPXUPKR.js');
23
23
 
24
24
  // src/tree/ComputeParticleEntity.ts
25
25
  var PARTICLE_STRIDE_FLOATS = 8;
@@ -31,7 +31,7 @@ var PARTICLE_OFFSET_ORIGIN_X = 4;
31
31
  var PARTICLE_OFFSET_ORIGIN_Y = 5;
32
32
  var PARTICLE_OFFSET_SIZE = 6;
33
33
  var PARTICLE_OFFSET_LIFE = 7;
34
- var ComputeParticleEntity = (_class = class extends _chunkSRXU3ZXKjs.Entity {
34
+ var ComputeParticleEntity = (_class = class extends _chunkPOPXUPKRjs.Entity {
35
35
 
36
36
 
37
37
 
@@ -225,14 +225,14 @@ var ComputeParticleEntity = (_class = class extends _chunkSRXU3ZXKjs.Entity {
225
225
  const bounceDamping = Math.max(0, Math.min(1, this.bounceDamping));
226
226
  const maxVelocity = Math.max(1, this.maxVelocity);
227
227
  for (let i = 0; i < this.maxParticles; i++) {
228
- const offset = i * 8;
229
- let px = this.particleData[offset];
230
- let py = this.particleData[offset + 1];
231
- let vx = this.particleData[offset + 2];
232
- let vy = this.particleData[offset + 3];
233
- const ox = this.particleData[offset + 4];
234
- const oy = this.particleData[offset + 5];
235
- const life = this.particleData[offset + 7];
228
+ const offset = i * PARTICLE_STRIDE_FLOATS;
229
+ let px = this.particleData[offset + PARTICLE_OFFSET_POSITION_X];
230
+ let py = this.particleData[offset + PARTICLE_OFFSET_POSITION_Y];
231
+ let vx = this.particleData[offset + PARTICLE_OFFSET_VELOCITY_X];
232
+ let vy = this.particleData[offset + PARTICLE_OFFSET_VELOCITY_Y];
233
+ const ox = this.particleData[offset + PARTICLE_OFFSET_ORIGIN_X];
234
+ const oy = this.particleData[offset + PARTICLE_OFFSET_ORIGIN_Y];
235
+ const life = this.particleData[offset + PARTICLE_OFFSET_LIFE];
236
236
  if (isNaN(px)) px = ox;
237
237
  if (isNaN(py)) py = oy;
238
238
  if (isNaN(vx)) vx = 0;
@@ -290,11 +290,11 @@ var ComputeParticleEntity = (_class = class extends _chunkSRXU3ZXKjs.Entity {
290
290
  if (life >= 0) {
291
291
  nlife = Math.max(0, life - safeDt * 0.5);
292
292
  }
293
- this.particleData[offset] = npx;
294
- this.particleData[offset + 1] = npy;
295
- this.particleData[offset + 2] = nvx;
296
- this.particleData[offset + 3] = nvy;
297
- this.particleData[offset + 7] = nlife;
293
+ this.particleData[offset + PARTICLE_OFFSET_POSITION_X] = npx;
294
+ this.particleData[offset + PARTICLE_OFFSET_POSITION_Y] = npy;
295
+ this.particleData[offset + PARTICLE_OFFSET_VELOCITY_X] = nvx;
296
+ this.particleData[offset + PARTICLE_OFFSET_VELOCITY_Y] = nvy;
297
+ this.particleData[offset + PARTICLE_OFFSET_LIFE] = nlife;
298
298
  }
299
299
  this.pendingExplosion = null;
300
300
  }
@@ -594,7 +594,12 @@ var ContentGridProjector = class {
594
594
  lineIndex === 0
595
595
  );
596
596
  const reusable = existingLines[domIndex];
597
- if (reusable !== void 0 && reusable.dataset.vectoGridLineSig === lineSignature && reusable.dataset.vectoGridLine === `${lineIndex}`) {
597
+ if (reusable !== void 0 && reusable.dataset.vectoGridLineSig === lineSignature) {
598
+ const indexLabel = `${lineIndex}`;
599
+ if (reusable.dataset.vectoGridLine !== indexLabel) {
600
+ reusable.dataset.vectoGridLine = indexLabel;
601
+ reusable.style.top = `${(_nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _34 => _34.y]), () => ( lineIndex * grid.lineHeight))) + baseline - _text.cssLineBoxBaseline.call(void 0, lineFont, lineHeight)}px`;
602
+ }
598
603
  continue;
599
604
  }
600
605
  if (selectionLine !== null && selectionLine === lineIndex) rebuiltSelectionLine = true;
@@ -603,8 +608,8 @@ var ContentGridProjector = class {
603
608
  lineElement.dir = "ltr";
604
609
  lineElement.dataset.vectoGridLine = `${lineIndex}`;
605
610
  lineElement.style.position = "absolute";
606
- lineElement.style.left = `${_nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _34 => _34.x]), () => ( 0))}px`;
607
- lineElement.style.top = `${(_nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _35 => _35.y]), () => ( lineIndex * grid.lineHeight))) + baseline - _text.cssLineBoxBaseline.call(void 0, lineFont, lineHeight)}px`;
611
+ lineElement.style.left = `${_nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _35 => _35.x]), () => ( 0))}px`;
612
+ lineElement.style.top = `${(_nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _36 => _36.y]), () => ( lineIndex * grid.lineHeight))) + baseline - _text.cssLineBoxBaseline.call(void 0, lineFont, lineHeight)}px`;
608
613
  lineElement.style.width = `${gridLine.width}px`;
609
614
  lineElement.style.height = `${lineHeight}px`;
610
615
  lineElement.style.whiteSpace = "pre";
@@ -673,7 +678,7 @@ var ContentGridProjector = class {
673
678
  }
674
679
  const windowLength = gridWindow.end - gridWindow.start;
675
680
  while (el.children.length > windowLength) {
676
- _optionalChain([el, 'access', _36 => _36.lastElementChild, 'optionalAccess', _37 => _37.remove, 'call', _38 => _38()]);
681
+ _optionalChain([el, 'access', _37 => _37.lastElementChild, 'optionalAccess', _38 => _38.remove, 'call', _39 => _39()]);
677
682
  }
678
683
  if (rebuiltSelectionLine) {
679
684
  this.contentProjection.restoreGridSelection(el, selectionSnapshot);
@@ -713,8 +718,8 @@ var ContentGridProjector = class {
713
718
  function contentGridLineSignature(grid, line, projected, lineHeight, baseline, font, isFirstLine) {
714
719
  const parts = [
715
720
  // Line box: position, size, and the font that resolves its baseline.
716
- `${_nullishCoalesce(_optionalChain([projected, 'optionalAccess', _39 => _39.x]), () => ( 0))}`,
717
- `${_nullishCoalesce(_optionalChain([projected, 'optionalAccess', _40 => _40.y]), () => ( ""))}`,
721
+ `${_nullishCoalesce(_optionalChain([projected, 'optionalAccess', _40 => _40.x]), () => ( 0))}`,
722
+ `${_nullishCoalesce(_optionalChain([projected, 'optionalAccess', _41 => _41.y]), () => ( ""))}`,
718
723
  `${lineHeight}`,
719
724
  `${baseline}`,
720
725
  font,
@@ -780,7 +785,13 @@ function projectionCaretAt(root, absoluteOffset, affinity) {
780
785
  }
781
786
 
782
787
  // src/tree/scene/A11yProjectionManager.ts
783
- var A11yProjectionManager = (_class2 = class {constructor() { _class2.prototype.__init8.call(this);_class2.prototype.__init9.call(this);_class2.prototype.__init10.call(this);_class2.prototype.__init11.call(this);_class2.prototype.__init12.call(this);_class2.prototype.__init13.call(this);_class2.prototype.__init14.call(this);_class2.prototype.__init15.call(this); }
788
+ function orderBandBottom(r) {
789
+ return r.container ? r.top + 4 : r.top + Math.max(Number.parseFloat(r.el.style.height) || 0, 4);
790
+ }
791
+ var byTopThenIndex = (p, q) => p.top - q.top || p.i - q.i;
792
+ var byLeftAscThenIndex = (p, q) => p.left - q.left || p.i - q.i;
793
+ var byLeftDescThenIndex = (p, q) => q.left - p.left || p.i - q.i;
794
+ var A11yProjectionManager = (_class2 = class {constructor() { _class2.prototype.__init8.call(this);_class2.prototype.__init9.call(this);_class2.prototype.__init10.call(this);_class2.prototype.__init11.call(this);_class2.prototype.__init12.call(this);_class2.prototype.__init13.call(this);_class2.prototype.__init14.call(this);_class2.prototype.__init15.call(this);_class2.prototype.__init16.call(this);_class2.prototype.__init17.call(this);_class2.prototype.__init18.call(this);_class2.prototype.__init19.call(this); }
784
795
  /**
785
796
  * Set by anything that changes which elements exist or how they nest, and
786
797
  * cleared by the reorder pass. Public so `Scene` can expose it under its
@@ -821,6 +832,18 @@ var A11yProjectionManager = (_class2 = class {constructor() { _class2.prototype.
821
832
  * implicit root region. See {@link sortNormalElementsVisually}.
822
833
  */
823
834
  __init15() {this.orderRegions = /* @__PURE__ */ new Map()}
835
+ /**
836
+ * Decoration records for {@link sortNormalElementsVisually}: one per ordered
837
+ * element, pooled across passes and rewritten in place — the sort runs on
838
+ * structure-change frames, where the module's zero-GC policy applies.
839
+ */
840
+ __init16() {this.orderDecorated = []}
841
+ /** Region → decorated elements, reused by {@link sortNormalElementsVisually}. */
842
+ __init17() {this.orderBucketByRegion = /* @__PURE__ */ new Map()}
843
+ /** Pool of bucket arrays backing {@link orderBucketByRegion} across passes. */
844
+ __init18() {this.orderBuckets = []}
845
+ /** Scratch for one visual row inside {@link sortNormalElementsVisually}. */
846
+ __init19() {this.orderRowScratch = []}
824
847
  /** Mark the projected DOM as needing a reorder on the next pass. */
825
848
  markNeedsReorder() {
826
849
  this.needsReorder = true;
@@ -878,11 +901,12 @@ var A11yProjectionManager = (_class2 = class {constructor() { _class2.prototype.
878
901
  this.orderCursors.set(parent, at + 1);
879
902
  const current = parent.childNodes[at];
880
903
  if (current !== expected) {
881
- const refocus = document.activeElement === expected;
904
+ const active = document.activeElement;
905
+ const refocusTarget = active && (active === expected || expected.contains(active)) ? active : null;
882
906
  if (!selectionSnapshotTaken) {
883
907
  selectionSnapshotTaken = true;
884
908
  const live = typeof window !== "undefined" && typeof window.getSelection === "function" ? window.getSelection() : null;
885
- if (_optionalChain([live, 'optionalAccess', _41 => _41.anchorNode]) && live.focusNode) {
909
+ if (_optionalChain([live, 'optionalAccess', _42 => _42.anchorNode]) && live.focusNode) {
886
910
  selection = live;
887
911
  selAnchorNode = live.anchorNode;
888
912
  selFocusNode = live.focusNode;
@@ -894,7 +918,7 @@ var A11yProjectionManager = (_class2 = class {constructor() { _class2.prototype.
894
918
  selectionMoved = true;
895
919
  }
896
920
  parent.insertBefore(expected, current || null);
897
- if (refocus) expected.focus({ preventScroll: true });
921
+ if (refocusTarget) refocusTarget.focus({ preventScroll: true });
898
922
  }
899
923
  }
900
924
  if (selectionMoved && selection && selAnchorNode && selFocusNode) {
@@ -941,7 +965,6 @@ var A11yProjectionManager = (_class2 = class {constructor() { _class2.prototype.
941
965
  sortNormalElementsVisually(rtl) {
942
966
  const els = this.normalElements;
943
967
  if (els.length < 2) return;
944
- const heightOf = (el) => Math.max(Number.parseFloat(el.style.height) || 0, 4);
945
968
  const members = this.orderMembers;
946
969
  const containers = this.orderContainers;
947
970
  members.clear();
@@ -952,52 +975,70 @@ var A11yProjectionManager = (_class2 = class {constructor() { _class2.prototype.
952
975
  if (members.has(p)) containers.add(p);
953
976
  }
954
977
  }
955
- const absolute = (el) => {
978
+ const decorated = this.orderDecorated;
979
+ while (decorated.length < els.length) {
980
+ decorated.push({ el: els[0], i: 0, top: 0, left: 0, container: false });
981
+ }
982
+ for (let i = 0; i < els.length; i++) {
983
+ const rec = decorated[i];
956
984
  let top = 0;
957
985
  let left = 0;
958
- for (let node = el; node; node = node.parentElement) {
986
+ for (let node = els[i]; node; node = node.parentElement) {
959
987
  top += Number.parseFloat(node.style.top) || 0;
960
988
  left += Number.parseFloat(node.style.left) || 0;
961
989
  const parent = node.parentElement;
962
990
  if (!parent || !members.has(parent)) break;
963
991
  }
964
- return { top, left };
965
- };
966
- const decorated = els.map((el, i) => {
967
- const { top, left } = absolute(el);
968
- return { el, i, top, left, container: containers.has(el) };
969
- });
992
+ rec.el = els[i];
993
+ rec.i = i;
994
+ rec.top = top;
995
+ rec.left = left;
996
+ rec.container = containers.has(els[i]);
997
+ }
970
998
  const regions = this.orderRegions;
971
- const buckets = /* @__PURE__ */ new Map();
972
- for (const d of decorated) {
973
- const key = _nullishCoalesce(regions.get(d.el), () => ( null));
974
- const bucket = buckets.get(key);
975
- if (bucket) bucket.push(d);
976
- else buckets.set(key, [d]);
977
- }
978
- const bandBottom = (r) => r.top + (r.container ? 4 : heightOf(r.el));
979
- const sorted = [];
999
+ const buckets = this.orderBucketByRegion;
1000
+ const bucketPool = this.orderBuckets;
1001
+ let bucketsUsed = 0;
1002
+ buckets.clear();
1003
+ for (let i = 0; i < els.length; i++) {
1004
+ const rec = decorated[i];
1005
+ const key = _nullishCoalesce(regions.get(rec.el), () => ( null));
1006
+ let bucket = buckets.get(key);
1007
+ if (!bucket) {
1008
+ bucket = bucketPool[bucketsUsed];
1009
+ if (bucket === void 0) {
1010
+ bucket = [];
1011
+ bucketPool.push(bucket);
1012
+ }
1013
+ bucketsUsed++;
1014
+ bucket.length = 0;
1015
+ buckets.set(key, bucket);
1016
+ }
1017
+ bucket.push(rec);
1018
+ }
1019
+ let out = 0;
980
1020
  for (const order of buckets.values()) {
981
- order.sort((p, q) => p.top - q.top || p.i - q.i);
1021
+ order.sort(byTopThenIndex);
982
1022
  let rowStart = 0;
983
- let rowBottom = order.length ? bandBottom(order[0]) : 0;
1023
+ let rowBottom = order.length ? orderBandBottom(order[0]) : 0;
984
1024
  const flushRow = (end) => {
985
- const row = order.slice(rowStart, end);
986
- row.sort((p, q) => (rtl ? q.left - p.left : p.left - q.left) || p.i - q.i);
987
- for (const r of row) sorted.push(r.el);
1025
+ const row = this.orderRowScratch;
1026
+ row.length = 0;
1027
+ for (let m = rowStart; m < end; m++) row.push(order[m]);
1028
+ row.sort(rtl ? byLeftDescThenIndex : byLeftAscThenIndex);
1029
+ for (const r of row) els[out++] = r.el;
988
1030
  };
989
1031
  for (let k = 1; k < order.length; k++) {
990
1032
  if (order[k].top < rowBottom) {
991
- rowBottom = Math.max(rowBottom, bandBottom(order[k]));
1033
+ rowBottom = Math.max(rowBottom, orderBandBottom(order[k]));
992
1034
  } else {
993
1035
  flushRow(k);
994
1036
  rowStart = k;
995
- rowBottom = bandBottom(order[k]);
1037
+ rowBottom = orderBandBottom(order[k]);
996
1038
  }
997
1039
  }
998
1040
  flushRow(order.length);
999
1041
  }
1000
- for (let i = 0; i < sorted.length; i++) els[i] = sorted[i];
1001
1042
  }
1002
1043
  }, _class2);
1003
1044
 
@@ -1009,15 +1050,15 @@ var CanvasGeometry = (_class3 = class {
1009
1050
  /** Last geometry {@link syncOverlay} wrote, so an unchanged frame can skip the
1010
1051
  * style writes entirely. Cleared by {@link invalidateOverlay} to force the next
1011
1052
  * sync (a new overlay layer was created and has never been positioned). */
1012
- __init16() {this.overlayGeometry = null}
1013
- constructor(canvas, a11yRoot, portalRoot) {;_class3.prototype.__init16.call(this);
1053
+ __init20() {this.overlayGeometry = null}
1054
+ constructor(canvas, a11yRoot, portalRoot) {;_class3.prototype.__init20.call(this);
1014
1055
  this.canvas = canvas;
1015
1056
  this.a11yRoot = a11yRoot;
1016
1057
  this.portalRoot = portalRoot;
1017
1058
  }
1018
1059
  /** Convert browser viewport coordinates into the scene's logical coordinates. */
1019
1060
  clientToScene(clientX, clientY, width, height) {
1020
- const rect = _optionalChain([this, 'access', _42 => _42.canvas, 'access', _43 => _43.getBoundingClientRect, 'optionalCall', _44 => _44()]);
1061
+ const rect = _optionalChain([this, 'access', _43 => _43.canvas, 'access', _44 => _44.getBoundingClientRect, 'optionalCall', _45 => _45()]);
1021
1062
  if (!rect) return { x: clientX, y: clientY };
1022
1063
  const cssWidth = rect.width || this.canvas.clientWidth || width;
1023
1064
  const cssHeight = rect.height || this.canvas.clientHeight || height;
@@ -1055,14 +1096,14 @@ var CanvasGeometry = (_class3 = class {
1055
1096
  syncOverlay(width, height, glCanvas, gpuCanvas) {
1056
1097
  const parent = this.canvas.parentElement;
1057
1098
  if (!parent) return;
1058
- const canvasRect = _optionalChain([this, 'access', _45 => _45.canvas, 'access', _46 => _46.getBoundingClientRect, 'optionalCall', _47 => _47()]);
1059
- const parentRect = _optionalChain([parent, 'access', _48 => _48.getBoundingClientRect, 'optionalCall', _49 => _49()]);
1060
- const cssWidth = _optionalChain([canvasRect, 'optionalAccess', _50 => _50.width]) || this.canvas.clientWidth || width;
1061
- const cssHeight = _optionalChain([canvasRect, 'optionalAccess', _51 => _51.height]) || this.canvas.clientHeight || height;
1099
+ const canvasRect = _optionalChain([this, 'access', _46 => _46.canvas, 'access', _47 => _47.getBoundingClientRect, 'optionalCall', _48 => _48()]);
1100
+ const parentRect = _optionalChain([parent, 'access', _49 => _49.getBoundingClientRect, 'optionalCall', _50 => _50()]);
1101
+ const cssWidth = _optionalChain([canvasRect, 'optionalAccess', _51 => _51.width]) || this.canvas.clientWidth || width;
1102
+ const cssHeight = _optionalChain([canvasRect, 'optionalAccess', _52 => _52.height]) || this.canvas.clientHeight || height;
1062
1103
  const canvasPosition = typeof getComputedStyle === "function" ? getComputedStyle(this.canvas).position : "";
1063
1104
  const position = canvasPosition === "fixed" ? "fixed" : "absolute";
1064
- const left = position === "fixed" ? _nullishCoalesce(_optionalChain([canvasRect, 'optionalAccess', _52 => _52.left]), () => ( 0)) : (_nullishCoalesce(_optionalChain([canvasRect, 'optionalAccess', _53 => _53.left]), () => ( 0))) - (_nullishCoalesce(_optionalChain([parentRect, 'optionalAccess', _54 => _54.left]), () => ( 0))) - (parent.clientLeft || 0) + parent.scrollLeft;
1065
- const top = position === "fixed" ? _nullishCoalesce(_optionalChain([canvasRect, 'optionalAccess', _55 => _55.top]), () => ( 0)) : (_nullishCoalesce(_optionalChain([canvasRect, 'optionalAccess', _56 => _56.top]), () => ( 0))) - (_nullishCoalesce(_optionalChain([parentRect, 'optionalAccess', _57 => _57.top]), () => ( 0))) - (parent.clientTop || 0) + parent.scrollTop;
1105
+ const left = position === "fixed" ? _nullishCoalesce(_optionalChain([canvasRect, 'optionalAccess', _53 => _53.left]), () => ( 0)) : (_nullishCoalesce(_optionalChain([canvasRect, 'optionalAccess', _54 => _54.left]), () => ( 0))) - (_nullishCoalesce(_optionalChain([parentRect, 'optionalAccess', _55 => _55.left]), () => ( 0))) - (parent.clientLeft || 0) + parent.scrollLeft;
1106
+ const top = position === "fixed" ? _nullishCoalesce(_optionalChain([canvasRect, 'optionalAccess', _56 => _56.top]), () => ( 0)) : (_nullishCoalesce(_optionalChain([canvasRect, 'optionalAccess', _57 => _57.top]), () => ( 0))) - (_nullishCoalesce(_optionalChain([parentRect, 'optionalAccess', _58 => _58.top]), () => ( 0))) - (parent.clientTop || 0) + parent.scrollTop;
1066
1107
  const scaleX = width > 0 ? cssWidth / width : 1;
1067
1108
  const scaleY = height > 0 ? cssHeight / height : 1;
1068
1109
  const prev = this.overlayGeometry;
@@ -1100,12 +1141,12 @@ var CanvasGeometry = (_class3 = class {
1100
1141
  }, _class3);
1101
1142
 
1102
1143
  // src/tree/scene/DirtyTracker.ts
1103
- var DirtyTracker = (_class4 = class _DirtyTracker {constructor() { _class4.prototype.__init17.call(this);_class4.prototype.__init18.call(this);_class4.prototype.__init19.call(this); }
1144
+ var DirtyTracker = (_class4 = class _DirtyTracker {constructor() { _class4.prototype.__init21.call(this);_class4.prototype.__init22.call(this);_class4.prototype.__init23.call(this); }
1104
1145
  /** Cap on distinct recorded dirty reasons (see {@link record}). */
1105
1146
  static __initStatic() {this.MAX_DIRTY_REASONS = 200}
1106
- __init17() {this.tracking = false}
1107
- __init18() {this.reasons = /* @__PURE__ */ new Map()}
1108
- __init19() {this.isDirty = true}
1147
+ __init21() {this.tracking = false}
1148
+ __init22() {this.reasons = /* @__PURE__ */ new Map()}
1149
+ __init23() {this.isDirty = true}
1109
1150
  /** Whether the next frame must redraw. */
1110
1151
  get dirty() {
1111
1152
  return this.isDirty;
@@ -1187,20 +1228,20 @@ var DriverTicker = (_class5 = class {
1187
1228
  // an entity whose drivers have since all completed or been removed. This is
1188
1229
  // what lets the batch pass find its candidates in O(active drivers), not
1189
1230
  // O(tree size) — the exact mistake G3's first integrated benchmark made.
1190
- __init20() {this.activeEntities = /* @__PURE__ */ new Set()}
1231
+ __init24() {this.activeEntities = /* @__PURE__ */ new Set()}
1191
1232
  // Reused across frames instead of allocating a fresh array + N {entity,prop,
1192
1233
  // driver} objects every call — the integrated benchmark
1193
1234
  // (benchmarks/anim-wasm-scene) found that allocation churn was the
1194
1235
  // dominant integrated cost, not the wasm kernel itself. Parallel arrays,
1195
1236
  // truncated to the live count after each use so a stale tail slot never
1196
1237
  // pins a no-longer-active entity/driver in memory.
1197
- __init21() {this.springEntities = []}
1198
- __init22() {this.springProps = []}
1199
- __init23() {this.springDrivers = []}
1200
- __init24() {this.tweenEntities = []}
1201
- __init25() {this.tweenProps = []}
1202
- __init26() {this.tweenDrivers = []}
1203
- constructor(backends) {;_class5.prototype.__init20.call(this);_class5.prototype.__init21.call(this);_class5.prototype.__init22.call(this);_class5.prototype.__init23.call(this);_class5.prototype.__init24.call(this);_class5.prototype.__init25.call(this);_class5.prototype.__init26.call(this);
1238
+ __init25() {this.springEntities = []}
1239
+ __init26() {this.springProps = []}
1240
+ __init27() {this.springDrivers = []}
1241
+ __init28() {this.tweenEntities = []}
1242
+ __init29() {this.tweenProps = []}
1243
+ __init30() {this.tweenDrivers = []}
1244
+ constructor(backends) {;_class5.prototype.__init24.call(this);_class5.prototype.__init25.call(this);_class5.prototype.__init26.call(this);_class5.prototype.__init27.call(this);_class5.prototype.__init28.call(this);_class5.prototype.__init29.call(this);_class5.prototype.__init30.call(this);
1204
1245
  this.backends = backends;
1205
1246
  }
1206
1247
  /** The candidate set, for the delegating accessor `Scene` keeps for its tests. */
@@ -1328,6 +1369,8 @@ var DriverTicker = (_class5 = class {
1328
1369
  tD.length = tweenCount;
1329
1370
  backend.ensure(springCount, tweenCount);
1330
1371
  backend.revalidateViews();
1372
+ let springsRejected = false;
1373
+ let tweensRejected = false;
1331
1374
  if (springCount > 0) {
1332
1375
  const sv = backend.springView();
1333
1376
  for (let i = 0; i < springCount; i++) {
@@ -1343,8 +1386,7 @@ var DriverTicker = (_class5 = class {
1343
1386
  for (let i = 0; i < springCount; i++) sD[i].syncExternal(sv.val[i], sv.vel[i]);
1344
1387
  } else {
1345
1388
  for (let i = 0; i < springCount; i++) sD[i].tick(dt);
1346
- this.backends.animReason = "rejected";
1347
- this.backends.animBatchedLastFrame = false;
1389
+ springsRejected = true;
1348
1390
  }
1349
1391
  }
1350
1392
  if (tweenCount > 0) {
@@ -1362,10 +1404,13 @@ var DriverTicker = (_class5 = class {
1362
1404
  for (let i = 0; i < tweenCount; i++) tD[i].syncExternal(tv.val[i], tv.elapsed[i]);
1363
1405
  } else {
1364
1406
  for (let i = 0; i < tweenCount; i++) tD[i].tick(dt);
1365
- this.backends.animReason = "rejected";
1366
- this.backends.animBatchedLastFrame = false;
1407
+ tweensRejected = true;
1367
1408
  }
1368
1409
  }
1410
+ if (springsRejected || tweensRejected) {
1411
+ this.backends.animReason = springsRejected && tweensRejected ? "rejected" : springsRejected ? "springs-rejected" : "tweens-rejected";
1412
+ this.backends.animBatchedLastFrame = springCount > 0 && !springsRejected || tweenCount > 0 && !tweensRejected;
1413
+ }
1369
1414
  for (let i = 0; i < springCount; i++) sE[i]._applyDriverTick(sP[i], sD[i]);
1370
1415
  for (let i = 0; i < tweenCount; i++) tE[i]._applyDriverTick(tP[i], tD[i]);
1371
1416
  }
@@ -1526,20 +1571,17 @@ function intersectBounds(a, b) {
1526
1571
  height: Math.max(0, bottom - y)
1527
1572
  };
1528
1573
  }
1529
- function pointInBounds(b, x, y) {
1530
- return x >= b.x && x <= b.x + b.width && y >= b.y && y <= b.y + b.height;
1531
- }
1532
1574
  var HitTester = (_class6 = class {
1533
1575
 
1534
1576
 
1535
1577
 
1536
1578
  /** Slot index → entity, as built by the most recent grid build. */
1537
- __init27() {this.slotEntity = []}
1579
+ __init31() {this.slotEntity = []}
1538
1580
  /** Entities with no `getBounds()`, which the grid cannot index. */
1539
- __init28() {this.boundless = []}
1581
+ __init32() {this.boundless = []}
1540
1582
  /** Reused buffer for the fused gather, so a pointer query allocates nothing. */
1541
- __init29() {this.gatherBuffer = null}
1542
- constructor(root, overlayRoot, backends) {;_class6.prototype.__init27.call(this);_class6.prototype.__init28.call(this);_class6.prototype.__init29.call(this);
1583
+ __init33() {this.gatherBuffer = null}
1584
+ constructor(root, overlayRoot, backends) {;_class6.prototype.__init31.call(this);_class6.prototype.__init32.call(this);_class6.prototype.__init33.call(this);
1543
1585
  this.root = root;
1544
1586
  this.overlayRoot = overlayRoot;
1545
1587
  this.backends = backends;
@@ -1575,7 +1617,7 @@ var HitTester = (_class6 = class {
1575
1617
  this.backends.hitReason = "not-installed";
1576
1618
  return false;
1577
1619
  }
1578
- if (this.backends.hitGridFrame === frame) {
1620
+ if (this.backends.hitGridFrame === frame && this.backends.hitGridStructureVersion === this.backends.structureVersion) {
1579
1621
  return this.backends.hitGridOk;
1580
1622
  }
1581
1623
  let gathered = null;
@@ -1606,6 +1648,7 @@ var HitTester = (_class6 = class {
1606
1648
  this.slotEntity = gathered.slotEntity;
1607
1649
  this.boundless = gathered.boundless;
1608
1650
  this.backends.hitGridFrame = frame;
1651
+ this.backends.hitGridStructureVersion = this.backends.structureVersion;
1609
1652
  this.backends.hitGridOk = ok;
1610
1653
  this.backends.hitReason = ok ? "active" : "rejected";
1611
1654
  return ok;
@@ -1630,7 +1673,7 @@ var HitTester = (_class6 = class {
1630
1673
  const idx = cell[k];
1631
1674
  if (x < minx[idx] || x > maxx[idx] || y < miny[idx] || y > maxy[idx]) continue;
1632
1675
  const entity = this.slotEntity[idx];
1633
- if (_optionalChain([entity, 'optionalAccess', _58 => _58.isPointInside, 'call', _59 => _59(x, y)]) && this.isHitEligible(entity, x, y)) {
1676
+ if (_optionalChain([entity, 'optionalAccess', _59 => _59.isPointInside, 'call', _60 => _60(x, y)]) && this.isHitEligible(entity, x, y)) {
1634
1677
  bestIndex = idx;
1635
1678
  bestEntity = entity;
1636
1679
  break;
@@ -1656,7 +1699,7 @@ var HitTester = (_class6 = class {
1656
1699
  const hit = this.findHitRecursively(node.children[i], x, y, childClip);
1657
1700
  if (hit) return hit;
1658
1701
  }
1659
- if (node.isPointInside && node.isPointInside(x, y) && (!clip || pointInBounds(clip, x, y)) && !this.isPointerTransparent(node)) {
1702
+ if (node.isPointInside && node.isPointInside(x, y) && this.isInsideAllClippers(node, x, y) && !this.isPointerTransparent(node)) {
1660
1703
  return node;
1661
1704
  }
1662
1705
  return null;
@@ -1668,6 +1711,25 @@ var HitTester = (_class6 = class {
1668
1711
  const attrs = node.getA11yAttributes();
1669
1712
  return attrs.disabled === true || attrs.pointerEvents === "none";
1670
1713
  }
1714
+ /**
1715
+ * Exact rotation-aware `clipChildren` test shared by BOTH hit paths: the
1716
+ * point must lie inside every clipChildren ancestor's local rect — the same
1717
+ * shape rendering clips to — not merely inside the ancestor's world AABB.
1718
+ * For a rotated clipper those disagree, and until both paths used the exact
1719
+ * rect a query's answer depended on which backend was active (#680). The
1720
+ * clip-stack AABB intersection in {@link findHitRecursively} remains purely
1721
+ * as a subtree-pruning pre-filter; this is the authoritative gate.
1722
+ */
1723
+ isInsideAllClippers(node, x, y) {
1724
+ for (let ancestor = node.parent; ancestor; ancestor = ancestor.parent) {
1725
+ if (!ancestor.clipChildren) continue;
1726
+ const local = ancestor.worldToLocal(x, y);
1727
+ if (!local || local.x < 0 || local.y < 0 || local.x > ancestor.width || local.y > ancestor.height) {
1728
+ return false;
1729
+ }
1730
+ }
1731
+ return true;
1732
+ }
1671
1733
  /**
1672
1734
  * Whether a confirmed geometric hit on `node` at world `(x, y)` is a REAL hit,
1673
1735
  * applying the same visibility/input gating as {@link findHitRecursively} but
@@ -1682,14 +1744,8 @@ var HitTester = (_class6 = class {
1682
1744
  if (node.opacity <= 0) return false;
1683
1745
  for (let ancestor = node.parent; ancestor; ancestor = ancestor.parent) {
1684
1746
  if (ancestor.opacity <= 0) return false;
1685
- if (ancestor.clipChildren && ancestor.width > 0 && ancestor.height > 0) {
1686
- const local = ancestor.worldToLocal(x, y);
1687
- if (!local || local.x < 0 || local.y < 0 || local.x > ancestor.width || local.y > ancestor.height) {
1688
- return false;
1689
- }
1690
- }
1691
1747
  }
1692
- return true;
1748
+ return this.isInsideAllClippers(node, x, y);
1693
1749
  }
1694
1750
  }, _class6);
1695
1751
 
@@ -1709,9 +1765,9 @@ var ContentProjectionManager = (_class7 = class {
1709
1765
  * Mid-drag the browser is authoritative, so a rebuild must not try to preserve
1710
1766
  * anything.
1711
1767
  */
1712
- __init30() {this.blankRegionDrag = false}
1768
+ __init34() {this.blankRegionDrag = false}
1713
1769
  /** Tracked drag anchor. Survives a drag, unlike the live DOM selection. */
1714
- __init31() {this.anchor = null}
1770
+ __init35() {this.anchor = null}
1715
1771
  /**
1716
1772
  * Memo for {@link selectionPresent}, valid for one sync walk.
1717
1773
  *
@@ -1719,20 +1775,20 @@ var ContentProjectionManager = (_class7 = class {
1719
1775
  * is resolved once per walk rather than once per rebuilt element. `null` means
1720
1776
  * "not yet asked this walk".
1721
1777
  */
1722
- __init32() {this.presentThisSync = null}
1778
+ __init36() {this.presentThisSync = null}
1723
1779
  // --- grid calibration ------------------------------------------------------
1724
1780
  /** Pending calibration rAF handle per projected grid entity. */
1725
- __init33() {this.calibrationFrameHandles = /* @__PURE__ */ new Map()}
1781
+ __init37() {this.calibrationFrameHandles = /* @__PURE__ */ new Map()}
1726
1782
  /** Detached, untransformed font probes used by the cold calibration pass. */
1727
- __init34() {this.calibrationProbes = /* @__PURE__ */ new Map()}
1783
+ __init38() {this.calibrationProbes = /* @__PURE__ */ new Map()}
1728
1784
  /**
1729
1785
  * Bumped when the conditions a measurement depends on change, which
1730
1786
  * invalidates every existing per-cell `scaleX` at once without touching them.
1731
1787
  */
1732
- __init35() {this.calibrationGeneration = 0}
1788
+ __init39() {this.calibrationGeneration = 0}
1733
1789
  /** The conditions the current generation was measured under. */
1734
- __init36() {this.calibrationStamp = ""}
1735
- constructor(a11yRoot, phases) {;_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);
1790
+ __init40() {this.calibrationStamp = ""}
1791
+ constructor(a11yRoot, phases) {;_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);
1736
1792
  this.a11yRoot = a11yRoot;
1737
1793
  this.phases = phases;
1738
1794
  }
@@ -1786,16 +1842,16 @@ var ContentProjectionManager = (_class7 = class {
1786
1842
  * to the live DOM selection.
1787
1843
  */
1788
1844
  gridSelectionLine(el) {
1789
- const candidates = [_optionalChain([this, 'access', _60 => _60.anchor, 'optionalAccess', _61 => _61.node])];
1845
+ const candidates = [_optionalChain([this, 'access', _61 => _61.anchor, 'optionalAccess', _62 => _62.node])];
1790
1846
  if (typeof window !== "undefined" && typeof window.getSelection === "function") {
1791
1847
  const selection = window.getSelection();
1792
- candidates.push(_optionalChain([selection, 'optionalAccess', _62 => _62.anchorNode]), _optionalChain([selection, 'optionalAccess', _63 => _63.focusNode]));
1848
+ candidates.push(_optionalChain([selection, 'optionalAccess', _63 => _63.anchorNode]), _optionalChain([selection, 'optionalAccess', _64 => _64.focusNode]));
1793
1849
  }
1794
1850
  for (const candidate of candidates) {
1795
1851
  if (!candidate || !el.contains(candidate)) continue;
1796
1852
  let cursor = candidate;
1797
1853
  while (cursor && cursor.parentNode !== el) cursor = cursor.parentNode;
1798
- const lineIndex = _optionalChain([cursor, 'optionalAccess', _64 => _64.dataset, 'optionalAccess', _65 => _65.vectoGridLine]);
1854
+ const lineIndex = _optionalChain([cursor, 'optionalAccess', _65 => _65.dataset, 'optionalAccess', _66 => _66.vectoGridLine]);
1799
1855
  if (lineIndex !== void 0) return Number(lineIndex);
1800
1856
  }
1801
1857
  return null;
@@ -1820,10 +1876,10 @@ var ContentProjectionManager = (_class7 = class {
1820
1876
  releaseSelectionForRebuild(el) {
1821
1877
  if (!this.anchor && !this.selectionPresent()) return;
1822
1878
  const selection = typeof window !== "undefined" && typeof window.getSelection === "function" ? window.getSelection() : null;
1823
- const ownsSelection = this.anchor && el.contains(this.anchor.node) || (_optionalChain([selection, 'optionalAccess', _66 => _66.anchorNode]) ? el.contains(selection.anchorNode) : false) || (_optionalChain([selection, 'optionalAccess', _67 => _67.focusNode]) ? el.contains(selection.focusNode) : false);
1879
+ const ownsSelection = this.anchor && el.contains(this.anchor.node) || (_optionalChain([selection, 'optionalAccess', _67 => _67.anchorNode]) ? el.contains(selection.anchorNode) : false) || (_optionalChain([selection, 'optionalAccess', _68 => _68.focusNode]) ? el.contains(selection.focusNode) : false);
1824
1880
  if (!ownsSelection) return;
1825
1881
  this.endDrag();
1826
- _optionalChain([selection, 'optionalAccess', _68 => _68.removeAllRanges, 'call', _69 => _69()]);
1882
+ _optionalChain([selection, 'optionalAccess', _69 => _69.removeAllRanges, 'call', _70 => _70()]);
1827
1883
  this.presentThisSync = null;
1828
1884
  }
1829
1885
  /**
@@ -1898,7 +1954,7 @@ var ContentProjectionManager = (_class7 = class {
1898
1954
  let cursor = node;
1899
1955
  while (cursor && cursor !== el) {
1900
1956
  const cell = cursor;
1901
- const start = _optionalChain([cell, 'access', _70 => _70.dataset, 'optionalAccess', _71 => _71.vectoGridSourceStart]);
1957
+ const start = _optionalChain([cell, 'access', _71 => _71.dataset, 'optionalAccess', _72 => _72.vectoGridSourceStart]);
1902
1958
  if (start !== void 0) {
1903
1959
  const sourceStart = Number(start);
1904
1960
  const sourceLength = Number(_nullishCoalesce(cell.dataset.vectoGridSourceLength, () => ( 0)));
@@ -1992,7 +2048,7 @@ var ContentProjectionManager = (_class7 = class {
1992
2048
  cancelAnimationFrame(calibrationFrame);
1993
2049
  }
1994
2050
  this.calibrationFrameHandles.delete(entityId);
1995
- _optionalChain([this, 'access', _72 => _72.calibrationProbes, 'access', _73 => _73.get, 'call', _74 => _74(entityId), 'optionalAccess', _75 => _75.remove, 'call', _76 => _76()]);
2051
+ _optionalChain([this, 'access', _73 => _73.calibrationProbes, 'access', _74 => _74.get, 'call', _75 => _75(entityId), 'optionalAccess', _76 => _76.remove, 'call', _77 => _77()]);
1996
2052
  this.calibrationProbes.delete(entityId);
1997
2053
  delete el.dataset.vectoGridCalibrationPending;
1998
2054
  delete el.dataset.vectoGridCalibration;
@@ -2056,7 +2112,7 @@ var ContentProjectionManager = (_class7 = class {
2056
2112
  if (previous !== void 0 && typeof cancelAnimationFrame === "function") {
2057
2113
  cancelAnimationFrame(previous);
2058
2114
  }
2059
- _optionalChain([this, 'access', _77 => _77.calibrationProbes, 'access', _78 => _78.get, 'call', _79 => _79(entityId), 'optionalAccess', _80 => _80.remove, 'call', _81 => _81()]);
2115
+ _optionalChain([this, 'access', _78 => _78.calibrationProbes, 'access', _79 => _79.get, 'call', _80 => _80(entityId), 'optionalAccess', _81 => _81.remove, 'call', _82 => _82()]);
2060
2116
  this.calibrationProbes.delete(entityId);
2061
2117
  const calibrationStart = typeof performance !== "undefined" ? performance.now() : 0;
2062
2118
  const probe = document.createElement("div");
@@ -2090,7 +2146,7 @@ var ContentProjectionManager = (_class7 = class {
2090
2146
  target.dataset.vectoGridCalib = generation;
2091
2147
  continue;
2092
2148
  }
2093
- const sourceText = _nullishCoalesce(_optionalChain([target, 'access', _82 => _82.textContent, 'optionalAccess', _83 => _83.slice, 'call', _84 => _84(0, sourceLength)]), () => ( ""));
2149
+ const sourceText = _nullishCoalesce(_optionalChain([target, 'access', _83 => _83.textContent, 'optionalAccess', _84 => _84.slice, 'call', _85 => _85(0, sourceLength)]), () => ( ""));
2094
2150
  if (!sourceText) {
2095
2151
  target.dataset.vectoGridCalib = generation;
2096
2152
  continue;
@@ -2201,17 +2257,17 @@ var ContentProjectionManager = (_class7 = class {
2201
2257
  }, _class7);
2202
2258
 
2203
2259
  // src/tree/scene/PhaseTimer.ts
2204
- var PhaseTimer = (_class8 = class {constructor() { _class8.prototype.__init37.call(this);_class8.prototype.__init38.call(this);_class8.prototype.__init39.call(this); }
2260
+ var PhaseTimer = (_class8 = class {constructor() { _class8.prototype.__init41.call(this);_class8.prototype.__init42.call(this);_class8.prototype.__init43.call(this); }
2205
2261
  /**
2206
2262
  * Whether per-phase timing is being recorded.
2207
2263
  *
2208
2264
  * A field rather than an accessor: it is tested on the frame path once per
2209
2265
  * probe, and the disabled cost has to be a single boolean read.
2210
2266
  */
2211
- __init37() {this.enabled = false}
2267
+ __init41() {this.enabled = false}
2212
2268
  /** Whether browser User Timing instrumentation is enabled. */
2213
- __init38() {this.userTiming = false}
2214
- __init39() {this.totals = /* @__PURE__ */ new Map()}
2269
+ __init42() {this.userTiming = false}
2270
+ __init43() {this.totals = /* @__PURE__ */ new Map()}
2215
2271
  /** Start or stop recording. Disabling also drops what was collected. */
2216
2272
  setEnabled(enabled) {
2217
2273
  this.enabled = enabled;
@@ -2389,10 +2445,10 @@ function viewsStale(cap, probe, memory) {
2389
2445
  }
2390
2446
  var PAD2 = 8;
2391
2447
  var WasmTransformBackend = (_class9 = class {
2392
- __init40() {this.available = true}
2448
+ __init44() {this.available = true}
2393
2449
 
2394
- __init41() {this.cap = 0}
2395
- __init42() {this.runCap = 0}
2450
+ __init45() {this.cap = 0}
2451
+ __init46() {this.runCap = 0}
2396
2452
  // Views over wasm linear memory, valid until the next init().
2397
2453
 
2398
2454
 
@@ -2419,7 +2475,7 @@ var WasmTransformBackend = (_class9 = class {
2419
2475
 
2420
2476
 
2421
2477
 
2422
- constructor(instance) {;_class9.prototype.__init40.call(this);_class9.prototype.__init41.call(this);_class9.prototype.__init42.call(this);_class9.prototype.__init43.call(this);
2478
+ constructor(instance) {;_class9.prototype.__init44.call(this);_class9.prototype.__init45.call(this);_class9.prototype.__init46.call(this);_class9.prototype.__init47.call(this);
2423
2479
  this.ex = instance.exports;
2424
2480
  }
2425
2481
  /** Compose world matrices for `store` in WASM, writing back into its
@@ -2442,7 +2498,8 @@ var WasmTransformBackend = (_class9 = class {
2442
2498
  this.lastStatus = WASM_STATUS.CAPACITY;
2443
2499
  return;
2444
2500
  }
2445
- const status = kernel === "scalar" ? this.ex.compose_scalar() : this.ex.compose_simd();
2501
+ const simd = kernel === "simd" && typeof this.ex.compose_simd === "function";
2502
+ const status = simd ? this.ex.compose_simd() : this.ex.compose_scalar();
2446
2503
  this.lastStatus = status;
2447
2504
  if (status !== WASM_STATUS.OK) return;
2448
2505
  store.wa.set(this.vwa.subarray(0, n));
@@ -2464,7 +2521,8 @@ var WasmTransformBackend = (_class9 = class {
2464
2521
  * {@link uploadRuns} after a topology change.
2465
2522
  */
2466
2523
  runKernel(kernel = "simd") {
2467
- const status = kernel === "scalar" ? this.ex.compose_scalar() : this.ex.compose_simd();
2524
+ const simd = kernel === "simd" && typeof this.ex.compose_simd === "function";
2525
+ const status = simd ? this.ex.compose_simd() : this.ex.compose_scalar();
2468
2526
  this.lastStatus = status;
2469
2527
  return status;
2470
2528
  }
@@ -2486,7 +2544,7 @@ var WasmTransformBackend = (_class9 = class {
2486
2544
  * Status of the most recent kernel or run-table call. `WASM_STATUS.OK` unless
2487
2545
  * the crate rejected its arguments, in which case that call was a no-op.
2488
2546
  */
2489
- __init43() {this.lastStatus = WASM_STATUS.OK}
2547
+ __init47() {this.lastStatus = WASM_STATUS.OK}
2490
2548
  /** The resident wasm input views (`x,y,sx,sy,cos,sin,opacity`), valid until
2491
2549
  * the next capacity growth. Writing here is what makes uploads unnecessary. */
2492
2550
  inputView() {
@@ -2516,21 +2574,21 @@ var WasmTransformBackend = (_class9 = class {
2516
2574
  /**
2517
2575
  * Compute world-space AABBs for `store` in WASM (G1+), writing back into its
2518
2576
  * `aminx/aminy/amaxx/amaxy` arrays. Uploads the local bounds, runs the AABB
2519
- * pass, reads results back. Result is bit-identical to `computeAabbsJS(store)`.
2520
- * `compose` (or `runKernel`) must have populated the world matrices first —
2521
- * this pass reads them. For the resident (no-copy) integration, write bounds
2522
- * via {@link boundsView} and read via {@link aabbView} + call
2523
- * {@link runAabbs} instead.
2577
+ * pass, reads results back. Result is bit-identical to `computeAabbsJS(store)`
2578
+ * for both kernels. `compose` (or `runKernel`) must have populated the world
2579
+ * matrices first — this pass reads them. For the resident (no-copy)
2580
+ * integration, write bounds via {@link boundsView} and read via
2581
+ * {@link aabbView} + call {@link runAabbs} instead.
2524
2582
  */
2525
- computeAabbs(store) {
2583
+ computeAabbs(store, kernel = "simd") {
2526
2584
  this.ensure(store.count, store.runCount);
2527
2585
  const n = store.count;
2528
2586
  this.vbx.set(store.bx.subarray(0, n));
2529
2587
  this.vby.set(store.by.subarray(0, n));
2530
2588
  this.vbw.set(store.bw.subarray(0, n));
2531
2589
  this.vbh.set(store.bh.subarray(0, n));
2532
- this.lastStatus = this.ex.compute_aabbs(n);
2533
- if (this.lastStatus !== WASM_STATUS.OK) return;
2590
+ const ok = this.runAabbs(n, kernel);
2591
+ if (!ok) return;
2534
2592
  store.aminx.set(this.vaminx.subarray(0, n));
2535
2593
  store.aminy.set(this.vaminy.subarray(0, n));
2536
2594
  store.amaxx.set(this.vamaxx.subarray(0, n));
@@ -2541,8 +2599,9 @@ var WasmTransformBackend = (_class9 = class {
2541
2599
  * composed). No upload/readback — the per-frame resident path. Returns
2542
2600
  * `false` if the kernel rejected `count` (beyond capacity, or uninitialized),
2543
2601
  * in which case {@link aabbView} still holds the previous frame's bounds. */
2544
- runAabbs(count) {
2545
- this.lastStatus = this.ex.compute_aabbs(count);
2602
+ runAabbs(count, kernel = "simd") {
2603
+ const simd = kernel === "simd" && typeof this.ex.compute_aabbs_simd === "function";
2604
+ this.lastStatus = simd ? this.ex.compute_aabbs_simd(count) : this.ex.compute_aabbs(count);
2546
2605
  return this.lastStatus === WASM_STATUS.OK;
2547
2606
  }
2548
2607
  /** Resident wasm local-bounds input views (`bx,by,bw,bh`) for the AABB pass. */
@@ -2627,11 +2686,11 @@ var WasmTransformBackend = (_class9 = class {
2627
2686
  var PAD3 = 8;
2628
2687
  var AnimBackend = (_class10 = class {
2629
2688
 
2630
- __init44() {this.springCap = 0}
2631
- __init45() {this.tweenCap = 0}
2689
+ __init48() {this.springCap = 0}
2690
+ __init49() {this.tweenCap = 0}
2632
2691
 
2633
2692
 
2634
- constructor(instance) {;_class10.prototype.__init44.call(this);_class10.prototype.__init45.call(this);_class10.prototype.__init46.call(this);
2693
+ constructor(instance) {;_class10.prototype.__init48.call(this);_class10.prototype.__init49.call(this);_class10.prototype.__init50.call(this);
2635
2694
  this.ex = instance.exports;
2636
2695
  }
2637
2696
  /** The resident spring SoA input/output views, valid until the next capacity
@@ -2698,7 +2757,7 @@ var AnimBackend = (_class10 = class {
2698
2757
  * Status of the most recent kernel call — `WASM_STATUS.OK` unless the kernel
2699
2758
  * declined it. Mirrors {@link TransformBackend.lastStatus}.
2700
2759
  */
2701
- __init46() {this.lastStatus = WASM_STATUS.OK}
2760
+ __init50() {this.lastStatus = WASM_STATUS.OK}
2702
2761
  refreshViews() {
2703
2762
  const buf = this.ex.memory.buffer;
2704
2763
  const sCap = this.springCap;
@@ -2728,9 +2787,9 @@ var PAD4 = 8;
2728
2787
  var CELLS_PER_ENTITY_HINT = 4;
2729
2788
  var HitTestBackend = (_class11 = class {
2730
2789
 
2731
- __init47() {this.entityCap = 0}
2732
- __init48() {this.cellCap = 0}
2733
- __init49() {this.itemCap = 0}
2790
+ __init51() {this.entityCap = 0}
2791
+ __init52() {this.cellCap = 0}
2792
+ __init53() {this.itemCap = 0}
2734
2793
 
2735
2794
 
2736
2795
 
@@ -2739,10 +2798,10 @@ var HitTestBackend = (_class11 = class {
2739
2798
 
2740
2799
 
2741
2800
  /** Grid geometry from the last {@link ensure} call. */
2742
- __init50() {this.gridW = 0}
2743
- __init51() {this.gridH = 0}
2744
- __init52() {this.cellSize = 64}
2745
- constructor(instance) {;_class11.prototype.__init47.call(this);_class11.prototype.__init48.call(this);_class11.prototype.__init49.call(this);_class11.prototype.__init50.call(this);_class11.prototype.__init51.call(this);_class11.prototype.__init52.call(this);
2801
+ __init54() {this.gridW = 0}
2802
+ __init55() {this.gridH = 0}
2803
+ __init56() {this.cellSize = 64}
2804
+ constructor(instance) {;_class11.prototype.__init51.call(this);_class11.prototype.__init52.call(this);_class11.prototype.__init53.call(this);_class11.prototype.__init54.call(this);_class11.prototype.__init55.call(this);_class11.prototype.__init56.call(this);
2746
2805
  this.ex = instance.exports;
2747
2806
  }
2748
2807
  /** The resident AABB input views (`minx/miny/maxx/maxy`), valid until the
@@ -2843,9 +2902,9 @@ var HitTestBackend = (_class11 = class {
2843
2902
  var PAD5 = 8;
2844
2903
  var ParticleBackend = (_class12 = class {
2845
2904
 
2846
- __init53() {this.cap = 0}
2905
+ __init57() {this.cap = 0}
2847
2906
 
2848
- constructor(instance) {;_class12.prototype.__init53.call(this);_class12.prototype.__init54.call(this);
2907
+ constructor(instance) {;_class12.prototype.__init57.call(this);_class12.prototype.__init58.call(this);
2849
2908
  this.ex = instance.exports;
2850
2909
  }
2851
2910
  /** The resident SoA views, valid until the next capacity growth. */
@@ -2916,7 +2975,7 @@ var ParticleBackend = (_class12 = class {
2916
2975
  * Status of the most recent {@link step} — `WASM_STATUS.OK` unless the kernel
2917
2976
  * declined it. Mirrors {@link TransformBackend.lastStatus}.
2918
2977
  */
2919
- __init54() {this.lastStatus = WASM_STATUS.OK}
2978
+ __init58() {this.lastStatus = WASM_STATUS.OK}
2920
2979
  /** Transpose the AoS stride-8 buffer into the SoA views (position/velocity/
2921
2980
  * life every frame; origin upload-once when `withOrigin`). */
2922
2981
  gather(data, count, withOrigin) {
@@ -3007,11 +3066,11 @@ async function loadCoreWasmModule(source) {
3007
3066
  }
3008
3067
  var CoreWasmRuntime = (_class13 = class {
3009
3068
 
3010
- __init55() {this.transformBackend = null}
3011
- __init56() {this.animBackend = null}
3012
- __init57() {this.hitBackend = null}
3013
- __init58() {this.particleBackendInstance = null}
3014
- constructor(instance) {;_class13.prototype.__init55.call(this);_class13.prototype.__init56.call(this);_class13.prototype.__init57.call(this);_class13.prototype.__init58.call(this);
3069
+ __init59() {this.transformBackend = null}
3070
+ __init60() {this.animBackend = null}
3071
+ __init61() {this.hitBackend = null}
3072
+ __init62() {this.particleBackendInstance = null}
3073
+ constructor(instance) {;_class13.prototype.__init59.call(this);_class13.prototype.__init60.call(this);_class13.prototype.__init61.call(this);_class13.prototype.__init62.call(this);
3015
3074
  this.instance = instance;
3016
3075
  }
3017
3076
  /**
@@ -3063,7 +3122,7 @@ var WasmBackendFacade = (_class14 = class {
3063
3122
  * path, so the overlay root is not needed here.
3064
3123
  */
3065
3124
 
3066
- constructor(root) {;_class14.prototype.__init59.call(this);_class14.prototype.__init60.call(this);_class14.prototype.__init61.call(this);_class14.prototype.__init62.call(this);_class14.prototype.__init63.call(this);_class14.prototype.__init64.call(this);_class14.prototype.__init65.call(this);_class14.prototype.__init66.call(this);_class14.prototype.__init67.call(this);_class14.prototype.__init68.call(this);_class14.prototype.__init69.call(this);_class14.prototype.__init70.call(this);_class14.prototype.__init71.call(this);_class14.prototype.__init72.call(this);_class14.prototype.__init73.call(this);_class14.prototype.__init74.call(this);_class14.prototype.__init75.call(this);_class14.prototype.__init76.call(this);_class14.prototype.__init77.call(this);_class14.prototype.__init78.call(this);_class14.prototype.__init79.call(this);_class14.prototype.__init80.call(this);_class14.prototype.__init81.call(this);_class14.prototype.__init82.call(this);
3125
+ constructor(root) {;_class14.prototype.__init63.call(this);_class14.prototype.__init64.call(this);_class14.prototype.__init65.call(this);_class14.prototype.__init66.call(this);_class14.prototype.__init67.call(this);_class14.prototype.__init68.call(this);_class14.prototype.__init69.call(this);_class14.prototype.__init70.call(this);_class14.prototype.__init71.call(this);_class14.prototype.__init72.call(this);_class14.prototype.__init73.call(this);_class14.prototype.__init74.call(this);_class14.prototype.__init75.call(this);_class14.prototype.__init76.call(this);_class14.prototype.__init77.call(this);_class14.prototype.__init78.call(this);_class14.prototype.__init79.call(this);_class14.prototype.__init80.call(this);_class14.prototype.__init81.call(this);_class14.prototype.__init82.call(this);_class14.prototype.__init83.call(this);_class14.prototype.__init84.call(this);_class14.prototype.__init85.call(this);_class14.prototype.__init86.call(this);_class14.prototype.__init87.call(this);
3067
3126
  this.root = root;
3068
3127
  }
3069
3128
  // ── WASM transform backend (invisible accelerator) ──────────────────────────
@@ -3073,34 +3132,34 @@ var WasmBackendFacade = (_class14 = class {
3073
3132
  // default: a null backend, a non-main renderer, or any entity absent from the
3074
3133
  // store all fall back to the JS composition, so WASM can only ever change
3075
3134
  // *how fast* a world matrix is produced, never *what* it is.
3076
- __init59() {this._transform = null}
3077
- __init60() {this._mode = "js"}
3135
+ __init63() {this._transform = null}
3136
+ __init64() {this._mode = "js"}
3078
3137
  // Resident store state (Stage 3). The store layout — slot assignment + sibling
3079
3138
  // runs — depends only on tree TOPOLOGY, so it is rebuilt only when the
3080
3139
  // structure changes (add/remove/reparent bump `structureVersion`). Between
3081
3140
  // rebuilds the per-frame cost is: gather each entity's transform into the
3082
3141
  // resident wasm input view + run the kernel — no reallocation, no readback.
3083
- __init61() {this._treeStore = null}
3084
- __init62() {this._slotEntity = []}
3142
+ __init65() {this._treeStore = null}
3143
+ __init66() {this._slotEntity = []}
3085
3144
  // store slot -> entity (also validates slots)
3086
- __init63() {this._inputs = null}
3087
- __init64() {this._world = null}
3088
- __init65() {this._structureVersion = 0}
3089
- __init66() {this._storeStructureVersion = -1}
3145
+ __init67() {this._inputs = null}
3146
+ __init68() {this._world = null}
3147
+ __init69() {this._structureVersion = 0}
3148
+ __init70() {this._storeStructureVersion = -1}
3090
3149
  /**
3091
3150
  * Consecutive `uploadRuns` rejections. Reset by any success and by
3092
3151
  * {@link setTransform}; at {@link WASM_UPLOAD_REJECT_LIMIT} the backend mode
3093
3152
  * flips to `'js'` permanently.
3094
3153
  */
3095
- __init67() {this._uploadRejections = 0}
3154
+ __init71() {this._uploadRejections = 0}
3096
3155
  /** Latch for the permanent-fallback warning, which fires from a per-frame path. */
3097
- __init68() {this.hasWarnedUploadFallback = false}
3156
+ __init72() {this.hasWarnedUploadFallback = false}
3098
3157
  /**
3099
3158
  * Whether `compute_aabbs` has run against the current frame's world matrices.
3100
3159
  * The AABB pass is only meaningful after a `compose_*`, so the fused gather
3101
3160
  * must not read the views before then.
3102
3161
  */
3103
- __init69() {this._aabbsFresh = false}
3162
+ __init73() {this._aabbsFresh = false}
3104
3163
  /** The installed transform backend, or `null` on the JS path. */
3105
3164
  get transform() {
3106
3165
  return this._transform;
@@ -3165,7 +3224,7 @@ var WasmBackendFacade = (_class14 = class {
3165
3224
  * cached globally; the instance is per-Scene, which is the isolation that
3166
3225
  * actually matters.
3167
3226
  */
3168
- __init70() {this._runtime = null}
3227
+ __init74() {this._runtime = null}
3169
3228
  /** The shared WASM runtime, if one has been loaded. */
3170
3229
  get runtime() {
3171
3230
  return this._runtime;
@@ -3194,9 +3253,9 @@ var WasmBackendFacade = (_class14 = class {
3194
3253
  return runtime;
3195
3254
  }
3196
3255
  // ── The other three backend handles ─────────────────────────────────────────
3197
- __init71() {this._hit = null}
3198
- __init72() {this._anim = null}
3199
- __init73() {this._particle = null}
3256
+ __init75() {this._hit = null}
3257
+ __init76() {this._anim = null}
3258
+ __init77() {this._particle = null}
3200
3259
  /** The installed hit-test backend, or `null` for the JS depth-first walk. */
3201
3260
  get hit() {
3202
3261
  return this._hit;
@@ -3205,6 +3264,7 @@ var WasmBackendFacade = (_class14 = class {
3205
3264
  setHit(backend) {
3206
3265
  this._hit = backend;
3207
3266
  this.hitGridFrame = -1;
3267
+ this.hitGridStructureVersion = -1;
3208
3268
  this.hitReason = backend ? "not-applicable" : "not-installed";
3209
3269
  }
3210
3270
  /** The installed batched-animation backend, or `null` for the JS tick. */
@@ -3231,9 +3291,9 @@ var WasmBackendFacade = (_class14 = class {
3231
3291
  * Why the transform accelerator did or did not run on the most recent frame.
3232
3292
  * Written by the render walk and {@link syncStore}.
3233
3293
  */
3234
- __init74() {this.transformReason = "not-installed"}
3294
+ __init78() {this.transformReason = "not-installed"}
3235
3295
  /** Why the batched-driver accelerator did or did not run. */
3236
- __init75() {this.animReason = "not-installed"}
3296
+ __init79() {this.animReason = "not-installed"}
3237
3297
  /**
3238
3298
  * Why the hit-test accelerator did or did not serve the last pointer query.
3239
3299
  * The grid is built lazily on demand, not every frame, so this describes the
@@ -3241,28 +3301,35 @@ var WasmBackendFacade = (_class14 = class {
3241
3301
  * before a backend exists; the grid build moves it to `'not-applicable'` once
3242
3302
  * one is installed but nothing has queried yet.
3243
3303
  */
3244
- __init76() {this.hitReason = "not-installed"}
3304
+ __init80() {this.hitReason = "not-installed"}
3245
3305
  /** Why the particle accelerator did or did not run. */
3246
- __init77() {this.particleReason = "not-applicable"}
3306
+ __init81() {this.particleReason = "not-applicable"}
3247
3307
  /** Which particle implementation actually simulated the most recent frame. */
3248
- __init78() {this.particlePath = "none"}
3308
+ __init82() {this.particlePath = "none"}
3249
3309
  /**
3250
3310
  * Whether the last grid build sourced its AABBs from the WASM transform store
3251
3311
  * rather than recomputing them in JS. Diagnostic only — both paths must
3252
3312
  * produce the same entity for a given point.
3253
3313
  */
3254
- __init79() {this.hitFusedGather = false}
3314
+ __init83() {this.hitFusedGather = false}
3255
3315
  /** Whether the WASM batch path actually ran on the most recent frame. */
3256
- __init80() {this.animBatchedLastFrame = false}
3316
+ __init84() {this.animBatchedLastFrame = false}
3257
3317
  // Hit-grid cache key: which frame + structure version the grid was last
3258
3318
  // (successfully, non-overflowing) built for. Owned here rather than by
3259
3319
  // `HitTester` because {@link setHit} has to invalidate it, and a backend swap
3260
3320
  // is this facade's job. The grid contents themselves stay with the hit-test
3261
3321
  // domain.
3262
3322
  /** Frame the hit grid was last built for; `-1` forces a rebuild. */
3263
- __init81() {this.hitGridFrame = -1}
3323
+ __init85() {this.hitGridFrame = -1}
3324
+ /**
3325
+ * Structure version the hit grid was built for; `-1` forces a rebuild.
3326
+ * Half of the cache key the comment above promises: without it, a pointer
3327
+ * query in the same frame as a structural mutation would resolve against
3328
+ * pre-mutation geometry while the JS walk sees live state.
3329
+ */
3330
+ __init86() {this.hitGridStructureVersion = -1}
3264
3331
  /** Whether that build succeeded (did not overflow its item budget). */
3265
- __init82() {this.hitGridOk = false}
3332
+ __init87() {this.hitGridOk = false}
3266
3333
  /**
3267
3334
  * Per-frame status of every invisible accelerator: whether each is installed,
3268
3335
  * whether it actually ran on the most recent frame, and why.
@@ -3400,6 +3467,38 @@ var WasmBackendFacade = (_class14 = class {
3400
3467
  }
3401
3468
  }, _class14);
3402
3469
 
3470
+ // src/tree/scene/keyboard.ts
3471
+ function normalizeChord(input) {
3472
+ if (typeof input === "string") {
3473
+ const parts = input.split("+").map((p) => p.trim()).filter(Boolean);
3474
+ const mods = /* @__PURE__ */ new Set();
3475
+ let key2 = "";
3476
+ for (const p of parts) {
3477
+ const low = p.toLowerCase();
3478
+ if (low === "ctrl" || low === "control") mods.add("Control");
3479
+ else if (low === "alt") mods.add("Alt");
3480
+ else if (low === "shift") mods.add("Shift");
3481
+ else if (low === "meta" || low === "cmd" || low === "super") mods.add("Meta");
3482
+ else key2 = p.length === 1 ? p.toUpperCase() : p;
3483
+ }
3484
+ const ordered2 = ["Control", "Alt", "Shift", "Meta"].filter((m) => mods.has(m));
3485
+ return key2 ? [...ordered2, key2].join("+") : ordered2.join("+");
3486
+ }
3487
+ const ordered = [];
3488
+ if (input.ctrlKey) ordered.push("Control");
3489
+ if (input.altKey) ordered.push("Alt");
3490
+ if (input.shiftKey) ordered.push("Shift");
3491
+ if (input.metaKey) ordered.push("Meta");
3492
+ let key = input.key;
3493
+ if (key === " ") key = "Space";
3494
+ else if (key.length === 1) key = key.toUpperCase();
3495
+ if (["Control", "Alt", "Shift", "Meta"].includes(key)) {
3496
+ return ordered.join("+");
3497
+ }
3498
+ ordered.push(key);
3499
+ return ordered.join("+");
3500
+ }
3501
+
3403
3502
  // src/tree/Scene.ts
3404
3503
  var RANGE_VALUE_ROLES = /* @__PURE__ */ new Set(["slider", "spinbutton", "progressbar", "scrollbar", "meter"]);
3405
3504
  var INTERACTIVE_A11Y_ROLES = /* @__PURE__ */ new Set([
@@ -3413,6 +3512,24 @@ var INTERACTIVE_A11Y_ROLES = /* @__PURE__ */ new Set([
3413
3512
  "slider",
3414
3513
  "combobox"
3415
3514
  ]);
3515
+ var KEYBOARD_OWNING_ROLES = /* @__PURE__ */ new Set([
3516
+ ...INTERACTIVE_A11Y_ROLES,
3517
+ "option",
3518
+ "listbox",
3519
+ "textbox",
3520
+ "searchbox",
3521
+ "spinbutton"
3522
+ ]);
3523
+ function ownsKeyboard(el) {
3524
+ if (!el) return false;
3525
+ if (el === document.body || el === document.documentElement) return false;
3526
+ if (el.hasAttribute("data-vecto-a11y-root")) return false;
3527
+ const tag = el.tagName;
3528
+ if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
3529
+ if (el.isContentEditable) return true;
3530
+ const role = el.getAttribute("role");
3531
+ return role !== null && KEYBOARD_OWNING_ROLES.has(role);
3532
+ }
3416
3533
  var A11Y_REQUIRED_OWNED = /* @__PURE__ */ new Map([
3417
3534
  ["grid", /* @__PURE__ */ new Set(["row", "rowgroup"])],
3418
3535
  ["table", /* @__PURE__ */ new Set(["row", "rowgroup"])],
@@ -3561,9 +3678,9 @@ function parseCssMatrix(transform) {
3561
3678
  }
3562
3679
  function clientToGridLocal(contentEl, canvas, clientX, clientY) {
3563
3680
  const line = contentEl.querySelector("[data-vecto-grid-line]");
3564
- const originMarker = _optionalChain([line, 'optionalAccess', _85 => _85.querySelector, 'call', _86 => _86('[data-vecto-grid-basis="origin"]')]);
3565
- const xMarker = _optionalChain([line, 'optionalAccess', _87 => _87.querySelector, 'call', _88 => _88('[data-vecto-grid-basis="x"]')]);
3566
- const yMarker = _optionalChain([line, 'optionalAccess', _89 => _89.querySelector, 'call', _90 => _90('[data-vecto-grid-basis="y"]')]);
3681
+ const originMarker = _optionalChain([line, 'optionalAccess', _86 => _86.querySelector, 'call', _87 => _87('[data-vecto-grid-basis="origin"]')]);
3682
+ const xMarker = _optionalChain([line, 'optionalAccess', _88 => _88.querySelector, 'call', _89 => _89('[data-vecto-grid-basis="x"]')]);
3683
+ const yMarker = _optionalChain([line, 'optionalAccess', _90 => _90.querySelector, 'call', _91 => _91('[data-vecto-grid-basis="y"]')]);
3567
3684
  if (line && originMarker && xMarker && yMarker) {
3568
3685
  const origin = originMarker.getBoundingClientRect();
3569
3686
  const xPoint = xMarker.getBoundingClientRect();
@@ -3647,7 +3764,7 @@ function nearestTextPositionInLine(line, x, y) {
3647
3764
  nearest = { position: { node, offset }, distance: candidate.distance };
3648
3765
  }
3649
3766
  }
3650
- return _nullishCoalesce(_optionalChain([nearest, 'optionalAccess', _91 => _91.position]), () => ( null));
3767
+ return _nullishCoalesce(_optionalChain([nearest, 'optionalAccess', _92 => _92.position]), () => ( null));
3651
3768
  }
3652
3769
  function nearestTextPositionInProjection(contentEl, canvas, x, y, eventTarget) {
3653
3770
  if (contentEl.dataset.vectoContentGrid !== void 0) {
@@ -3661,7 +3778,7 @@ function nearestTextPositionInProjection(contentEl, canvas, x, y, eventTarget) {
3661
3778
  }
3662
3779
  targetLine = targetLine.parentElement;
3663
3780
  }
3664
- if (_optionalChain([targetLine, 'optionalAccess', _92 => _92.parentElement]) === contentEl) {
3781
+ if (_optionalChain([targetLine, 'optionalAccess', _93 => _93.parentElement]) === contentEl) {
3665
3782
  return nearestTextPositionInLine(targetLine, x, y);
3666
3783
  }
3667
3784
  let bestLine = null;
@@ -3766,15 +3883,15 @@ var Scene = (_class15 = class _Scene {
3766
3883
 
3767
3884
 
3768
3885
 
3769
- __init83() {this.isRunning = false}
3886
+ __init88() {this.isRunning = false}
3770
3887
  /** Whether the canvas is at least partially in the viewport. When it scrolls
3771
3888
  * fully off-screen the rAF loop pauses (stops rescheduling) instead of
3772
3889
  * burning frames on a scene nobody can see; an IntersectionObserver resumes
3773
3890
  * it on re-entry. Defaults true (and stays true where IntersectionObserver
3774
3891
  * is unavailable, e.g. SSR/jsdom, so behavior is unchanged there). */
3775
- __init84() {this._canvasOnScreen = true}
3776
- __init85() {this._canvasObserver = null}
3777
- __init86() {this.lastTime = 0}
3892
+ __init89() {this._canvasOnScreen = true}
3893
+ __init90() {this._canvasObserver = null}
3894
+ __init91() {this.lastTime = 0}
3778
3895
 
3779
3896
  // --- domain: render-scheduler — mode, phase timing, frame telemetry, FPS caps ---
3780
3897
  /**
@@ -3784,7 +3901,7 @@ var Scene = (_class15 = class _Scene {
3784
3901
  * {@link markDirty}) or while an animation is pending. Ideal for static /
3785
3902
  * event-driven UIs where idle frames should cost ~0.
3786
3903
  */
3787
- __init87() {this.renderMode = "always"}
3904
+ __init92() {this.renderMode = "always"}
3788
3905
  /**
3789
3906
  * Per-phase frame timing and the browser User Timing flag.
3790
3907
  *
@@ -3794,7 +3911,7 @@ var Scene = (_class15 = class _Scene {
3794
3911
  * domains write phases, so this is a shared leaf rather than any one domain's
3795
3912
  * property — see `DEC-0021`.
3796
3913
  */
3797
- __init88() {this.phases = new PhaseTimer()}
3914
+ __init93() {this.phases = new PhaseTimer()}
3798
3915
  /**
3799
3916
  * Start or stop per-phase render timing.
3800
3917
  *
@@ -3849,7 +3966,7 @@ var Scene = (_class15 = class _Scene {
3849
3966
  * injected input, so it follows {@link phases} and {@link a11yOrder} rather
3850
3967
  * than the definite-assignment collaborators.
3851
3968
  */
3852
- __init89() {this._dirty = new DirtyTracker()}
3969
+ __init94() {this._dirty = new DirtyTracker()}
3853
3970
  /**
3854
3971
  * The redraw-pending flag, under its original name.
3855
3972
  *
@@ -3869,34 +3986,34 @@ var Scene = (_class15 = class _Scene {
3869
3986
  * Whether to throttle the `'always'` loop when the scene is static to save
3870
3987
  * power. The idle floor is {@link Scene.idleFPS} (default 60).
3871
3988
  */
3872
- __init90() {this.autoThrottle = true}
3989
+ __init95() {this.autoThrottle = true}
3873
3990
  /**
3874
3991
  * Frame-rate floor for an idle `'always'` scene when {@link Scene.autoThrottle}
3875
3992
  * is on. Default `60`. Set `2` for the legacy aggressive idle sleep, `0` to
3876
3993
  * keep the scene's `maxFPS` cadence while idle.
3877
3994
  */
3878
- __init91() {this.idleFPS = 60}
3995
+ __init96() {this.idleFPS = 60}
3879
3996
  // --- Frame telemetry (read via `frameStats`) ---------------------------
3880
3997
  /** Wall-clock ms spent inside the last `render()` call. */
3881
- __init92() {this._lastFrameMs = 0}
3998
+ __init97() {this._lastFrameMs = 0}
3882
3999
  /** Rolling exponential average of rendered-frame intervals, in ms. */
3883
- __init93() {this._avgFrameIntervalMs = 0}
4000
+ __init98() {this._avgFrameIntervalMs = 0}
3884
4001
  /** dt (ms) handed to the last rendered frame. */
3885
- __init94() {this._lastDt = 0}
4002
+ __init99() {this._lastDt = 0}
3886
4003
  /** Count of frames actually rendered since the loop started. */
3887
- __init95() {this._renderedFrames = 0}
4004
+ __init100() {this._renderedFrames = 0}
3888
4005
  /** Count of rAF ticks skipped (idle / capped) since the loop started. */
3889
- __init96() {this._skippedFrames = 0}
4006
+ __init101() {this._skippedFrames = 0}
3890
4007
  /** `time` of the previous *rendered* frame, for interval measurement. */
3891
- __init97() {this._lastRenderTick = 0}
4008
+ __init102() {this._lastRenderTick = 0}
3892
4009
  /**
3893
4010
  * Frame-rate cap (power saving). `0` = uncapped (native refresh). When set,
3894
4011
  * the loop renders at most `maxFPS` times per second; animations still run,
3895
4012
  * just less often. See {@link SceneOptions.maxFPS}.
3896
4013
  */
3897
- __init98() {this.maxFPS = 60}
4014
+ __init103() {this.maxFPS = 60}
3898
4015
  /** Whether the OS prefers-reduced-motion setting auto-caps the loop. */
3899
- __init99() {this.respectReducedMotion = true}
4016
+ __init104() {this.respectReducedMotion = true}
3900
4017
  /**
3901
4018
  * Reading direction for accessibility tab/traversal order (`'ltr'` default,
3902
4019
  * `'rtl'`). Controls the inline sort within a visual row in
@@ -3912,18 +4029,18 @@ var Scene = (_class15 = class _Scene {
3912
4029
  this.a11yOrder.markNeedsReorder();
3913
4030
  }
3914
4031
  }
3915
- __init100() {this._readingDirection = "ltr"}
4032
+ __init105() {this._readingDirection = "ltr"}
3916
4033
  /** Cached media-query list; `.matches` is read live each frame. */
3917
- __init101() {this.reducedMotionQuery = null}
4034
+ __init106() {this.reducedMotionQuery = null}
3918
4035
  /** Cached `(forced-colors: active)` query (Windows High Contrast etc.). A
3919
4036
  * canvas gets NO automatic forced-colors treatment from the browser (it's
3920
4037
  * opaque pixels), so components must read {@link forcedColors} and repaint
3921
4038
  * with system colors themselves; a change listener repaints idle scenes. */
3922
- __init102() {this.forcedColorsQuery = null}
3923
- __init103() {this.forcedColorsChangeHandler = null}
4039
+ __init107() {this.forcedColorsQuery = null}
4040
+ __init108() {this.forcedColorsChangeHandler = null}
3924
4041
  /** True when the OS asks for reduced motion and we respect it. Read by the animation drivers. */
3925
4042
  get prefersReducedMotion() {
3926
- return this.respectReducedMotion && !!_optionalChain([this, 'access', _93 => _93.reducedMotionQuery, 'optionalAccess', _94 => _94.matches]);
4043
+ return this.respectReducedMotion && !!_optionalChain([this, 'access', _94 => _94.reducedMotionQuery, 'optionalAccess', _95 => _95.matches]);
3927
4044
  }
3928
4045
  /**
3929
4046
  * True when the OS is in a forced-colors mode (Windows High Contrast, and the
@@ -3934,26 +4051,29 @@ var Scene = (_class15 = class _Scene {
3934
4051
  * when the setting toggles.
3935
4052
  */
3936
4053
  get forcedColors() {
3937
- return !!_optionalChain([this, 'access', _95 => _95.forcedColorsQuery, 'optionalAccess', _96 => _96.matches]);
4054
+ return !!_optionalChain([this, 'access', _96 => _96.forcedColorsQuery, 'optionalAccess', _97 => _97.matches]);
3938
4055
  }
3939
4056
  // --- domain: a11y-projection — sync throttle and shadow-DOM state ---
3940
4057
  /**
3941
4058
  * Throttle interval (ms) for the a11y/automation shadow sync. `0` = every
3942
4059
  * frame. See {@link SceneOptions.a11ySyncInterval}.
3943
4060
  */
3944
- __init104() {this.a11ySyncInterval = 0}
4061
+ __init109() {this.a11ySyncInterval = 0}
3945
4062
  /** Timestamp of the last a11y sync, for throttling. */
3946
- __init105() {this.lastA11ySync = -Infinity}
4063
+ __init110() {this.lastA11ySync = -Infinity}
3947
4064
  /** True if we skipped an a11y sync during animation and need to sync when at rest. */
3948
- __init106() {this.a11yPendingSyncAfterAnimation = false}
4065
+ __init111() {this.a11yPendingSyncAfterAnimation = false}
3949
4066
  // A11y / Automation Layer. `null` in non-DOM (SSR/Node) environments — the
3950
4067
  // whole projection degrades to a no-op so the engine's logic stays usable
3951
4068
  // server-side (e.g. headless layout / vector export) without jsdom.
3952
4069
 
3953
- __init107() {this.a11yElements = /* @__PURE__ */ new Map()}
4070
+ __init112() {this.a11yElements = /* @__PURE__ */ new Map()}
4071
+ /** Elements that already have the synthetic Enter/Space activation handler
4072
+ * (see the syncA11y refresh pass) — install-once bookkeeping (#694). */
4073
+ __init113() {this.keyboardActivatedA11y = /* @__PURE__ */ new WeakSet()}
3954
4074
  // --- domain: content-projection — projected DOM, sync state, calibration, selection ---
3955
4075
  /** DOM nodes mirroring static text content, keyed by entity id. */
3956
- __init108() {this.contentElements = /* @__PURE__ */ new Map()}
4076
+ __init114() {this.contentElements = /* @__PURE__ */ new Map()}
3957
4077
  /**
3958
4078
  * What the last completed content-projection sync was built from, per entity.
3959
4079
  *
@@ -3962,9 +4082,9 @@ var Scene = (_class15 = class _Scene {
3962
4082
  * build. Only populated for entities that opt in via
3963
4083
  * {@link Entity.getContentEpoch}. (carryctx CTX-0199)
3964
4084
  */
3965
- __init109() {this.contentSyncState = /* @__PURE__ */ new Map()}
4085
+ __init115() {this.contentSyncState = /* @__PURE__ */ new Map()}
3966
4086
  /** Invalidates grid font calibration after browser font availability changes. */
3967
- __init110() {this.contentFontEpoch = 0}
4087
+ __init116() {this.contentFontEpoch = 0}
3968
4088
  /**
3969
4089
  * Bumped whenever the viewport itself changes shape, which re-tiers blocks
3970
4090
  * without moving any of them.
@@ -3977,7 +4097,7 @@ var Scene = (_class15 = class _Scene {
3977
4097
  * carriers it no longer needs. Scrolling needs no such epoch: it moves the root,
3978
4098
  * so every block's parent world transform changes and the check fails honestly.
3979
4099
  */
3980
- __init111() {this.contentViewportEpoch = 0}
4100
+ __init117() {this.contentViewportEpoch = 0}
3981
4101
  /**
3982
4102
  * One-entry memo for a parent's world transform, held as scalars.
3983
4103
  *
@@ -3992,16 +4112,16 @@ var Scene = (_class15 = class _Scene {
3992
4112
  * sync, so a memo can never survive into a frame in which the parent has
3993
4113
  * moved.
3994
4114
  */
3995
- __init112() {this._pwNode = null}
3996
- __init113() {this._pwSerial = -1}
3997
- __init114() {this._pwa = 1}
3998
- __init115() {this._pwb = 0}
3999
- __init116() {this._pwc = 0}
4000
- __init117() {this._pwd = 1}
4001
- __init118() {this._pwe = 0}
4002
- __init119() {this._pwf = 0}
4115
+ __init118() {this._pwNode = null}
4116
+ __init119() {this._pwSerial = -1}
4117
+ __init120() {this._pwa = 1}
4118
+ __init121() {this._pwb = 0}
4119
+ __init122() {this._pwc = 0}
4120
+ __init123() {this._pwd = 1}
4121
+ __init124() {this._pwe = 0}
4122
+ __init125() {this._pwf = 0}
4003
4123
  /** Incremented once per top-level `syncA11y`, invalidating `_pwNode`. */
4004
- __init120() {this._syncSerial = 0}
4124
+ __init126() {this._syncSerial = 0}
4005
4125
  /**
4006
4126
  * Disables the settled-walk fast path in {@link syncContentProjection}.
4007
4127
  *
@@ -4013,38 +4133,38 @@ var Scene = (_class15 = class _Scene {
4013
4133
  * Not part of the public API and not documented as an option: turning it on only
4014
4134
  * makes a settled document slower, never more correct.
4015
4135
  */
4016
- __init121() {this.disableSettledFastPath = false}
4136
+ __init127() {this.disableSettledFastPath = false}
4017
4137
  /** Cached Canvas-to-client scale for the current font/viewport epoch. */
4018
- __init122() {this.contentMetricScaleEpoch = -1}
4019
- __init123() {this.contentMetricScaleX = 1}
4020
- __init124() {this.contentProjectionEnabled = true}
4138
+ __init128() {this.contentMetricScaleEpoch = -1}
4139
+ __init129() {this.contentMetricScaleX = 1}
4140
+ __init130() {this.contentProjectionEnabled = true}
4021
4141
  // Virtualization margin (px) for the content-projection CARRIER band;
4022
4142
  // `undefined` → one viewport height, resolved at sync time. `Infinity` is
4023
4143
  // unsupported here: it unwindows every carrier, which is O(total glyphs).
4024
- __init125() {this.contentProjectionMargin = void 0}
4144
+ __init131() {this.contentProjectionMargin = void 0}
4025
4145
  // Virtualization margin (px) for the SEMANTIC tier — whether a block has
4026
4146
  // any projected DOM. `undefined` → falls back to contentProjectionMargin, so
4027
4147
  // the default keeps one gate. `Infinity` = every block keeps resident text.
4028
- __init126() {this.contentSemanticMargin = void 0}
4148
+ __init132() {this.contentSemanticMargin = void 0}
4029
4149
  // How many coarse-tier blocks may be MATERIALIZED per sync, spreading the
4030
4150
  // resident tier's document-open cost across frames. `Infinity` = one
4031
4151
  // synchronous pass.
4032
- __init127() {this.contentSemanticBudget = DEFAULT_CONTENT_SEMANTIC_BUDGET}
4152
+ __init133() {this.contentSemanticBudget = DEFAULT_CONTENT_SEMANTIC_BUDGET}
4033
4153
  // Remaining materializations in the CURRENT sync. Reset at the start of each
4034
4154
  // a11y walk; decremented per coarse block that creates its element.
4035
- __init128() {this.contentSemanticBudgetLeft = 0}
4155
+ __init134() {this.contentSemanticBudgetLeft = 0}
4036
4156
  // Set when a sync deferred at least one block, so the scene knows to keep
4037
4157
  // drawing frames until the resident tier is complete. Without it a static
4038
4158
  // scene in `onDemand` mode would stop rendering with the document half
4039
4159
  // materialized and never finish.
4040
- __init129() {this.contentSemanticDeferred = false}
4041
- __init130() {this.contentSelectionEndListener = null}
4160
+ __init135() {this.contentSemanticDeferred = false}
4161
+ __init136() {this.contentSelectionEndListener = null}
4042
4162
  // --- domain: render-scheduler — per-frame walk flags ---
4043
4163
  // Animation/interactive flags collected during the render walk (tree-walk
4044
4164
  // fusion): the loop reads last frame's answers instead of re-walking the
4045
4165
  // tree up to 4× per tick. Start true so the first tick stays conservative.
4046
- __init131() {this.frameHadAnimation = true}
4047
- __init132() {this.frameHadInteractive = true}
4166
+ __init137() {this.frameHadAnimation = true}
4167
+ __init138() {this.frameHadInteractive = true}
4048
4168
  // --- domain: context-and-resize — resize handler, DPR query, canvas observer ---
4049
4169
 
4050
4170
  /** Active `(resolution: Ndppx)` media query watching for a runtime DPR change
@@ -4052,15 +4172,15 @@ var Scene = (_class15 = class _Scene {
4052
4172
  * can be re-scaled — otherwise it stays rasterized at the old DPR and blurs.
4053
4173
  * A resolution media query only fires when leaving its exact value, so the
4054
4174
  * handler re-arms a fresh query for the new DPR each time. */
4055
- __init133() {this.dprMediaQuery = null}
4175
+ __init139() {this.dprMediaQuery = null}
4056
4176
  /** For embedded (`disableWindowResize`) scenes: observes the canvas element so
4057
4177
  * a CSS/layout-driven size change re-runs `resize()`. A window `resize`
4058
4178
  * listener never fires for these (the window isn't what changed), so without
4059
4179
  * this an embedded canvas stayed at its initial size forever. */
4060
- __init134() {this.canvasResizeObserver = null}
4061
- __init135() {this.dprChangeHandler = null}
4180
+ __init140() {this.canvasResizeObserver = null}
4181
+ __init141() {this.dprChangeHandler = null}
4062
4182
  // --- domain: a11y-projection — focus, overlay geometry, DOM ordering, portals ---
4063
- __init136() {this.focusedA11yElement = null}
4183
+ __init142() {this.focusedA11yElement = null}
4064
4184
  /**
4065
4185
  * Canvas box geometry: the CSS↔logical mapping, overlay layer alignment, and
4066
4186
  * the DPR math (extraction 5, `DEC-0024`).
@@ -4087,7 +4207,7 @@ var Scene = (_class15 = class _Scene {
4087
4207
  /** Shadow elements the pointer is currently inside. Lets a removal that happens
4088
4208
  * mid-hover synthesize the `pointerleave` the browser never sends for a
4089
4209
  * detached element, so the entity doesn't keep its hover state. */
4090
- __init137() {this.hoveredA11yElements = /* @__PURE__ */ new WeakSet()}
4210
+ __init143() {this.hoveredA11yElements = /* @__PURE__ */ new WeakSet()}
4091
4211
  /**
4092
4212
  * Entity ids the application has pinned via {@link requestA11yProjection}.
4093
4213
  *
@@ -4095,13 +4215,13 @@ var Scene = (_class15 = class _Scene {
4095
4215
  * a stale id simply never matches. Cleared per-entity by
4096
4216
  * {@link releaseA11yProjection}.
4097
4217
  */
4098
- __init138() {this.a11yProjectionRequests = /* @__PURE__ */ new Set()}
4218
+ __init144() {this.a11yProjectionRequests = /* @__PURE__ */ new Set()}
4099
4219
  /** Persistent tabindex=-1 element in a11yRoot. When the focused a11y mirror is
4100
4220
  * pruned (virtualization/streaming/removal) while it holds focus, we move
4101
4221
  * focus here instead of letting the browser drop it to <body> — keeping the
4102
4222
  * screen-reader virtual cursor inside the scene's a11y region. */
4103
- __init139() {this.focusSentinel = null}
4104
- __init140() {this.caretBlinkTimer = null}
4223
+ __init145() {this.focusSentinel = null}
4224
+ __init146() {this.caretBlinkTimer = null}
4105
4225
  /**
4106
4226
  * The projected DOM's ordering engine — the reorder flag, the per-pass scratch
4107
4227
  * collections, the visual reading-order sort and the cursor-based
@@ -4111,7 +4231,7 @@ var Scene = (_class15 = class _Scene {
4111
4231
  * delegate here; the collect-and-prune walk stays behind because it needs
4112
4232
  * {@link shouldProjectA11y} and the focus/caret state.
4113
4233
  */
4114
- __init141() {this.a11yOrder = new A11yProjectionManager()}
4234
+ __init147() {this.a11yOrder = new A11yProjectionManager()}
4115
4235
  /**
4116
4236
  * The content projection's selection preservation and grid calibration
4117
4237
  * (extraction 3, `DEC-0022`).
@@ -4163,11 +4283,11 @@ var Scene = (_class15 = class _Scene {
4163
4283
  set a11yNeedsReorder(value) {
4164
4284
  this.a11yOrder.needsReorder = value;
4165
4285
  }
4166
- __init142() {this.portalRoot = null}
4167
- __init143() {this.activePortalsThisFrame = /* @__PURE__ */ new Set()}
4168
- __init144() {this.activePortalsPrevFrame = /* @__PURE__ */ new Set()}
4169
- __init145() {this.portalEntities = /* @__PURE__ */ new Map()}
4170
- __init146() {this.renderOrderCounter = 0}
4286
+ __init148() {this.portalRoot = null}
4287
+ __init149() {this.activePortalsThisFrame = /* @__PURE__ */ new Set()}
4288
+ __init150() {this.activePortalsPrevFrame = /* @__PURE__ */ new Set()}
4289
+ __init151() {this.portalEntities = /* @__PURE__ */ new Map()}
4290
+ __init152() {this.renderOrderCounter = 0}
4171
4291
  // --- domain: render-scheduler — authoritative frame counter ---
4172
4292
  /**
4173
4293
  * Monotonic render-frame counter, bumped once per authoritative `render()`
@@ -4177,7 +4297,7 @@ var Scene = (_class15 = class _Scene {
4177
4297
  * back to the ancestor walk. Public for the same reason `Entity._getTrig`/
4178
4298
  * `_setWorldCache` are: it is a cross-class render-internal contract.
4179
4299
  */
4180
- __init147() {this.currentFrame = 0}
4300
+ __init153() {this.currentFrame = 0}
4181
4301
  // --- domain: wasm-backend-facade — EXTRACTED (see scene/WasmBackendFacade.ts) ---
4182
4302
  /**
4183
4303
  * The four invisible WASM accelerators and the resident transform store.
@@ -4247,8 +4367,8 @@ var Scene = (_class15 = class _Scene {
4247
4367
  // Stays on `Scene`: it walks `root` *and* `overlayRoot` for entities the
4248
4368
  // render walk consumes, so only its cache key is store state. The facade owns
4249
4369
  // the key; the walk stays here.
4250
- __init148() {this._computeEntities = []}
4251
- __init149() {this._computeEntitiesVersion = -1}
4370
+ __init154() {this._computeEntities = []}
4371
+ __init155() {this._computeEntitiesVersion = -1}
4252
4372
  /** Invalidate the resident WASM store layout; the next wasm-mode frame rebuilds
4253
4373
  * it. Called by `Entity.add`/`remove` (topology changes only). */
4254
4374
  markStructureChanged() {
@@ -4487,7 +4607,7 @@ var Scene = (_class15 = class _Scene {
4487
4607
  get animBatchedLastFrame() {
4488
4608
  return this._wasmBackend.animBatchedLastFrame;
4489
4609
  }
4490
- __init150() {this.animGate = {
4610
+ __init156() {this.animGate = {
4491
4611
  spring: 128,
4492
4612
  tween: 256,
4493
4613
  mixed: 128
@@ -4575,6 +4695,15 @@ var Scene = (_class15 = class _Scene {
4575
4695
  _registerActiveDriverSubtree(entity) {
4576
4696
  this._driverTicker.registerSubtree(entity);
4577
4697
  }
4698
+ /**
4699
+ * The `dt` of the frame currently executing its update walk (the WASM-mode
4700
+ * pre-pass or the JS-mode interleaved render walk), or `null` outside it.
4701
+ * Read by `Entity._spawnDriver`: a driver spawned mid-walk onto an entity the
4702
+ * batched pass already claimed this frame must be advanced once here, or
4703
+ * `tickDrivers()`'s per-frame stamp would skip it a full frame — a one-frame
4704
+ * JS/WASM divergence. Written in {@link render} next to `_tickBatchedDrivers`.
4705
+ */
4706
+ __init157() {this._updateWalkDt = null}
4578
4707
  // --- domain: render-scheduler — batched driver tick (extraction 6), reads the anim backend ---
4579
4708
  /**
4580
4709
  * Advance every registered entity's active drivers for this frame, batching
@@ -4602,25 +4731,25 @@ var Scene = (_class15 = class _Scene {
4602
4731
  * sync, so retaining the order prevents a newly opened overlay from spending
4603
4732
  * its first frame below previously projected controls.
4604
4733
  */
4605
- __init151() {this.a11yRenderOrders = /* @__PURE__ */ new Map()}
4734
+ __init158() {this.a11yRenderOrders = /* @__PURE__ */ new Map()}
4606
4735
  // --- domain: context-and-resize — GL/GPU canvases, device state, pointer, size warnings ---
4607
4736
  // Optional WebGL point-cloud layer (see SceneOptions.pointBackend).
4608
- __init152() {this.pointRenderer = null}
4609
- __init153() {this.glCanvas = null}
4610
- __init154() {this.glContextLostHandler = null}
4611
- __init155() {this.glContextRestoredHandler = null}
4737
+ __init159() {this.pointRenderer = null}
4738
+ __init160() {this.glCanvas = null}
4739
+ __init161() {this.glContextLostHandler = null}
4740
+ __init162() {this.glContextRestoredHandler = null}
4612
4741
 
4613
4742
 
4614
4743
 
4615
- __init156() {this.disableWindowResize = false}
4744
+ __init163() {this.disableWindowResize = false}
4616
4745
  /** See {@link SceneOptions.maxDPR}. `undefined` = uncapped (real DPR). */
4617
4746
 
4618
4747
  // WebGPU properties
4619
- __init157() {this.destroyed = false}
4620
- __init158() {this.device = null}
4621
- __init159() {this.deviceLost = false}
4622
- __init160() {this.particleBackend = "auto"}
4623
- __init161() {this._webgpuDisabled = false}
4748
+ __init164() {this.destroyed = false}
4749
+ __init165() {this.device = null}
4750
+ __init166() {this.deviceLost = false}
4751
+ __init167() {this.particleBackend = "auto"}
4752
+ __init168() {this._webgpuDisabled = false}
4624
4753
  get webgpuDisabled() {
4625
4754
  return this._webgpuDisabled || this.particleBackend === "cpu";
4626
4755
  }
@@ -4634,7 +4763,7 @@ var Scene = (_class15 = class _Scene {
4634
4763
  * active.
4635
4764
  */
4636
4765
  get webglDrawStats() {
4637
- return _nullishCoalesce(_optionalChain([this, 'access', _97 => _97.pointRenderer, 'optionalAccess', _98 => _98.stats, 'optionalCall', _99 => _99()]), () => ( null));
4766
+ return _nullishCoalesce(_optionalChain([this, 'access', _98 => _98.pointRenderer, 'optionalAccess', _99 => _99.stats, 'optionalCall', _100 => _100()]), () => ( null));
4638
4767
  }
4639
4768
  /**
4640
4769
  * Whether a WebGPU device is currently live for particle compute.
@@ -4648,21 +4777,28 @@ var Scene = (_class15 = class _Scene {
4648
4777
  set webgpuDisabled(value) {
4649
4778
  this._webgpuDisabled = value;
4650
4779
  }
4651
- __init162() {this.recoveryTimerId = null}
4652
- __init163() {this.manager = null}
4653
- __init164() {this.initializingWebGPU = false}
4654
- __init165() {this.gpuCanvas = null}
4655
- __init166() {this.gpuContext = null}
4780
+ __init169() {this.recoveryTimerId = null}
4781
+ __init170() {this.manager = null}
4782
+ __init171() {this.initializingWebGPU = false}
4783
+ __init172() {this.gpuCanvas = null}
4784
+ __init173() {this.gpuContext = null}
4656
4785
  /** True while the GPU canvas holds a presented particle frame (needs clearing when they leave). */
4657
- __init167() {this.gpuHasContent = false}
4658
- __init168() {this.mouseX = -9999}
4659
- __init169() {this.mouseY = -9999}
4660
- __init170() {this.pointerMoveListener = null}
4661
- __init171() {this.pointerLeaveListener = null}
4786
+ __init174() {this.gpuHasContent = false}
4787
+ __init175() {this.mouseX = -9999}
4788
+ __init176() {this.mouseY = -9999}
4789
+ __init177() {this.pointerMoveListener = null}
4790
+ __init178() {this.pointerLeaveListener = null}
4662
4791
  /** Element the pointer listeners are bound to (parent container if present,
4663
4792
  * else the canvas). Stored so `destroy()` detaches from the same element. */
4664
- __init172() {this.pointerEventTarget = null}
4665
- __init173() {this.hasWarnedZeroSize = false}
4793
+ __init179() {this.pointerEventTarget = null}
4794
+ // --- scene-level keyboard channel state (see on/off/registerShortcut) ---
4795
+ __init180() {this.keydownHandlers = []}
4796
+ __init181() {this.keyupHandlers = []}
4797
+ /** Chord-normalized shortcut table, matched on keydown only. */
4798
+ __init182() {this.shortcuts = []}
4799
+ __init183() {this.windowKeyDownHandler = null}
4800
+ __init184() {this.windowKeyUpHandler = null}
4801
+ __init185() {this.hasWarnedZeroSize = false}
4666
4802
  /**
4667
4803
  * Latch for {@link Scene.resize}'s invalid-dimension warning.
4668
4804
  *
@@ -4672,8 +4808,8 @@ var Scene = (_class15 = class _Scene {
4672
4808
  * because `resize()` is commonly driven from a `ResizeObserver`, which would
4673
4809
  * otherwise warn on every frame of a drag.
4674
4810
  */
4675
- __init174() {this.hasWarnedInvalidResize = false}
4676
- __init175() {this.fontLoadHandler = null}
4811
+ __init186() {this.hasWarnedInvalidResize = false}
4812
+ __init187() {this.fontLoadHandler = null}
4677
4813
  // --- domain: scene-facade — dev-mode diagnostics ---
4678
4814
  // ── Dev-mode warning infrastructure ──────────────────────────────
4679
4815
  //
@@ -4698,17 +4834,17 @@ var Scene = (_class15 = class _Scene {
4698
4834
  }
4699
4835
  static set devMode(active) {
4700
4836
  _Scene._devMode = active;
4701
- _chunkZYYP2M5Djs.setRendererDevMode.call(void 0, active);
4837
+ _chunkKRUDTGNRjs.setRendererDevMode.call(void 0, active);
4702
4838
  }
4703
4839
  static _devModeDetected() {
4704
4840
  if (_Scene._devMode) return true;
4705
4841
  const gp = typeof globalThis !== "undefined" ? globalThis : void 0;
4706
- if (_optionalChain([gp, 'optionalAccess', _100 => _100.__DEV__])) return true;
4707
- if (_optionalChain([gp, 'optionalAccess', _101 => _101.process, 'optionalAccess', _102 => _102.env, 'optionalAccess', _103 => _103.NODE_ENV]) === "development") return true;
4842
+ if (_optionalChain([gp, 'optionalAccess', _101 => _101.__DEV__])) return true;
4843
+ if (_optionalChain([gp, 'optionalAccess', _102 => _102.process, 'optionalAccess', _103 => _103.env, 'optionalAccess', _104 => _104.NODE_ENV]) === "development") return true;
4708
4844
  return false;
4709
4845
  }
4710
4846
 
4711
- __init176() {this._devFrameCount = 0}
4847
+ __init188() {this._devFrameCount = 0}
4712
4848
  _devWarn(message) {
4713
4849
  if (!this._devActive) return;
4714
4850
  console.warn(`[vectojs/dev] ${message}`);
@@ -4766,9 +4902,9 @@ var Scene = (_class15 = class _Scene {
4766
4902
  let checked = 0;
4767
4903
  const walkProjections = (node) => {
4768
4904
  if (checked > 10) return;
4769
- const proj = _optionalChain([node, 'access', _104 => _104.getContentProjection, 'optionalCall', _105 => _105()]);
4770
- if (_optionalChain([proj, 'optionalAccess', _106 => _106.text]) && proj.selectable !== false) {
4771
- const el = _optionalChain([this, 'access', _107 => _107.contentElements, 'optionalAccess', _108 => _108.get, 'call', _109 => _109(node.id)]);
4905
+ const proj = _optionalChain([node, 'access', _105 => _105.getContentProjection, 'optionalCall', _106 => _106()]);
4906
+ if (_optionalChain([proj, 'optionalAccess', _107 => _107.text]) && proj.selectable !== false) {
4907
+ const el = _optionalChain([this, 'access', _108 => _108.contentElements, 'optionalAccess', _109 => _109.get, 'call', _110 => _110(node.id)]);
4772
4908
  if (el) {
4773
4909
  const projectedText = el.textContent || "";
4774
4910
  const windowed = el.dataset.vectoProjectionWindow !== void 0;
@@ -4786,14 +4922,14 @@ var Scene = (_class15 = class _Scene {
4786
4922
  walkProjections(this.root);
4787
4923
  }
4788
4924
  // --- domain: scene-facade — construction (wires every collaborator) ---
4789
- constructor(canvas, options = {}) {;_class15.prototype.__init83.call(this);_class15.prototype.__init84.call(this);_class15.prototype.__init85.call(this);_class15.prototype.__init86.call(this);_class15.prototype.__init87.call(this);_class15.prototype.__init88.call(this);_class15.prototype.__init89.call(this);_class15.prototype.__init90.call(this);_class15.prototype.__init91.call(this);_class15.prototype.__init92.call(this);_class15.prototype.__init93.call(this);_class15.prototype.__init94.call(this);_class15.prototype.__init95.call(this);_class15.prototype.__init96.call(this);_class15.prototype.__init97.call(this);_class15.prototype.__init98.call(this);_class15.prototype.__init99.call(this);_class15.prototype.__init100.call(this);_class15.prototype.__init101.call(this);_class15.prototype.__init102.call(this);_class15.prototype.__init103.call(this);_class15.prototype.__init104.call(this);_class15.prototype.__init105.call(this);_class15.prototype.__init106.call(this);_class15.prototype.__init107.call(this);_class15.prototype.__init108.call(this);_class15.prototype.__init109.call(this);_class15.prototype.__init110.call(this);_class15.prototype.__init111.call(this);_class15.prototype.__init112.call(this);_class15.prototype.__init113.call(this);_class15.prototype.__init114.call(this);_class15.prototype.__init115.call(this);_class15.prototype.__init116.call(this);_class15.prototype.__init117.call(this);_class15.prototype.__init118.call(this);_class15.prototype.__init119.call(this);_class15.prototype.__init120.call(this);_class15.prototype.__init121.call(this);_class15.prototype.__init122.call(this);_class15.prototype.__init123.call(this);_class15.prototype.__init124.call(this);_class15.prototype.__init125.call(this);_class15.prototype.__init126.call(this);_class15.prototype.__init127.call(this);_class15.prototype.__init128.call(this);_class15.prototype.__init129.call(this);_class15.prototype.__init130.call(this);_class15.prototype.__init131.call(this);_class15.prototype.__init132.call(this);_class15.prototype.__init133.call(this);_class15.prototype.__init134.call(this);_class15.prototype.__init135.call(this);_class15.prototype.__init136.call(this);_class15.prototype.__init137.call(this);_class15.prototype.__init138.call(this);_class15.prototype.__init139.call(this);_class15.prototype.__init140.call(this);_class15.prototype.__init141.call(this);_class15.prototype.__init142.call(this);_class15.prototype.__init143.call(this);_class15.prototype.__init144.call(this);_class15.prototype.__init145.call(this);_class15.prototype.__init146.call(this);_class15.prototype.__init147.call(this);_class15.prototype.__init148.call(this);_class15.prototype.__init149.call(this);_class15.prototype.__init150.call(this);_class15.prototype.__init151.call(this);_class15.prototype.__init152.call(this);_class15.prototype.__init153.call(this);_class15.prototype.__init154.call(this);_class15.prototype.__init155.call(this);_class15.prototype.__init156.call(this);_class15.prototype.__init157.call(this);_class15.prototype.__init158.call(this);_class15.prototype.__init159.call(this);_class15.prototype.__init160.call(this);_class15.prototype.__init161.call(this);_class15.prototype.__init162.call(this);_class15.prototype.__init163.call(this);_class15.prototype.__init164.call(this);_class15.prototype.__init165.call(this);_class15.prototype.__init166.call(this);_class15.prototype.__init167.call(this);_class15.prototype.__init168.call(this);_class15.prototype.__init169.call(this);_class15.prototype.__init170.call(this);_class15.prototype.__init171.call(this);_class15.prototype.__init172.call(this);_class15.prototype.__init173.call(this);_class15.prototype.__init174.call(this);_class15.prototype.__init175.call(this);_class15.prototype.__init176.call(this);
4925
+ constructor(canvas, options = {}) {;_class15.prototype.__init88.call(this);_class15.prototype.__init89.call(this);_class15.prototype.__init90.call(this);_class15.prototype.__init91.call(this);_class15.prototype.__init92.call(this);_class15.prototype.__init93.call(this);_class15.prototype.__init94.call(this);_class15.prototype.__init95.call(this);_class15.prototype.__init96.call(this);_class15.prototype.__init97.call(this);_class15.prototype.__init98.call(this);_class15.prototype.__init99.call(this);_class15.prototype.__init100.call(this);_class15.prototype.__init101.call(this);_class15.prototype.__init102.call(this);_class15.prototype.__init103.call(this);_class15.prototype.__init104.call(this);_class15.prototype.__init105.call(this);_class15.prototype.__init106.call(this);_class15.prototype.__init107.call(this);_class15.prototype.__init108.call(this);_class15.prototype.__init109.call(this);_class15.prototype.__init110.call(this);_class15.prototype.__init111.call(this);_class15.prototype.__init112.call(this);_class15.prototype.__init113.call(this);_class15.prototype.__init114.call(this);_class15.prototype.__init115.call(this);_class15.prototype.__init116.call(this);_class15.prototype.__init117.call(this);_class15.prototype.__init118.call(this);_class15.prototype.__init119.call(this);_class15.prototype.__init120.call(this);_class15.prototype.__init121.call(this);_class15.prototype.__init122.call(this);_class15.prototype.__init123.call(this);_class15.prototype.__init124.call(this);_class15.prototype.__init125.call(this);_class15.prototype.__init126.call(this);_class15.prototype.__init127.call(this);_class15.prototype.__init128.call(this);_class15.prototype.__init129.call(this);_class15.prototype.__init130.call(this);_class15.prototype.__init131.call(this);_class15.prototype.__init132.call(this);_class15.prototype.__init133.call(this);_class15.prototype.__init134.call(this);_class15.prototype.__init135.call(this);_class15.prototype.__init136.call(this);_class15.prototype.__init137.call(this);_class15.prototype.__init138.call(this);_class15.prototype.__init139.call(this);_class15.prototype.__init140.call(this);_class15.prototype.__init141.call(this);_class15.prototype.__init142.call(this);_class15.prototype.__init143.call(this);_class15.prototype.__init144.call(this);_class15.prototype.__init145.call(this);_class15.prototype.__init146.call(this);_class15.prototype.__init147.call(this);_class15.prototype.__init148.call(this);_class15.prototype.__init149.call(this);_class15.prototype.__init150.call(this);_class15.prototype.__init151.call(this);_class15.prototype.__init152.call(this);_class15.prototype.__init153.call(this);_class15.prototype.__init154.call(this);_class15.prototype.__init155.call(this);_class15.prototype.__init156.call(this);_class15.prototype.__init157.call(this);_class15.prototype.__init158.call(this);_class15.prototype.__init159.call(this);_class15.prototype.__init160.call(this);_class15.prototype.__init161.call(this);_class15.prototype.__init162.call(this);_class15.prototype.__init163.call(this);_class15.prototype.__init164.call(this);_class15.prototype.__init165.call(this);_class15.prototype.__init166.call(this);_class15.prototype.__init167.call(this);_class15.prototype.__init168.call(this);_class15.prototype.__init169.call(this);_class15.prototype.__init170.call(this);_class15.prototype.__init171.call(this);_class15.prototype.__init172.call(this);_class15.prototype.__init173.call(this);_class15.prototype.__init174.call(this);_class15.prototype.__init175.call(this);_class15.prototype.__init176.call(this);_class15.prototype.__init177.call(this);_class15.prototype.__init178.call(this);_class15.prototype.__init179.call(this);_class15.prototype.__init180.call(this);_class15.prototype.__init181.call(this);_class15.prototype.__init182.call(this);_class15.prototype.__init183.call(this);_class15.prototype.__init184.call(this);_class15.prototype.__init185.call(this);_class15.prototype.__init186.call(this);_class15.prototype.__init187.call(this);_class15.prototype.__init188.call(this);
4790
4926
  this.canvas = canvas;
4791
4927
  this.debugA11y = _nullishCoalesce(options.debugA11y, () => ( false));
4792
4928
  this.disableWindowResize = _nullishCoalesce(options.disableWindowResize, () => ( false));
4793
4929
  this.maxDPR = options.maxDPR;
4794
4930
  if (this.disableWindowResize) {
4795
- const styleWidth = parseInlinePx(_optionalChain([canvas, 'access', _110 => _110.style, 'optionalAccess', _111 => _111.width]));
4796
- const styleHeight = parseInlinePx(_optionalChain([canvas, 'access', _112 => _112.style, 'optionalAccess', _113 => _113.height]));
4931
+ const styleWidth = parseInlinePx(_optionalChain([canvas, 'access', _111 => _111.style, 'optionalAccess', _112 => _112.width]));
4932
+ const styleHeight = parseInlinePx(_optionalChain([canvas, 'access', _113 => _113.style, 'optionalAccess', _114 => _114.height]));
4797
4933
  this.width = _nullishCoalesce(styleWidth, () => ( (canvas.width || canvas.clientWidth || 0)));
4798
4934
  this.height = _nullishCoalesce(styleHeight, () => ( (canvas.height || canvas.clientHeight || 0)));
4799
4935
  } else {
@@ -4801,7 +4937,7 @@ var Scene = (_class15 = class _Scene {
4801
4937
  this.height = typeof window !== "undefined" ? window.innerHeight : canvas.clientHeight || canvas.height || 600;
4802
4938
  }
4803
4939
  const globalProcess = typeof globalThis !== "undefined" ? globalThis.process : void 0;
4804
- const isTest = globalProcess && (_optionalChain([globalProcess, 'access', _114 => _114.env, 'optionalAccess', _115 => _115.NODE_ENV]) === "test" || _optionalChain([globalProcess, 'access', _116 => _116.env, 'optionalAccess', _117 => _117.VITEST]) === "true");
4940
+ const isTest = globalProcess && (_optionalChain([globalProcess, 'access', _115 => _115.env, 'optionalAccess', _116 => _116.NODE_ENV]) === "test" || _optionalChain([globalProcess, 'access', _117 => _117.env, 'optionalAccess', _118 => _118.VITEST]) === "true");
4805
4941
  this.maxFPS = _nullishCoalesce(options.maxFPS, () => ( (isTest ? 0 : 60)));
4806
4942
  this.respectReducedMotion = _nullishCoalesce(options.respectReducedMotion, () => ( true));
4807
4943
  this.autoThrottle = _nullishCoalesce(options.autoThrottle, () => ( true));
@@ -4816,15 +4952,15 @@ var Scene = (_class15 = class _Scene {
4816
4952
  this.readingDirection = _nullishCoalesce(options.readingDirection, () => ( "ltr"));
4817
4953
  this.renderMode = _nullishCoalesce(options.renderMode, () => ( "always"));
4818
4954
  this._devActive = _Scene._devModeDetected();
4819
- _chunkZYYP2M5Djs.setRendererDevMode.call(void 0, this._devActive);
4955
+ _chunkKRUDTGNRjs.setRendererDevMode.call(void 0, this._devActive);
4820
4956
  this._warnUnknownOptions(options);
4821
4957
  this.reducedMotionQuery = typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia("(prefers-reduced-motion: reduce)") : null;
4822
4958
  if (typeof window !== "undefined" && typeof window.matchMedia === "function") {
4823
4959
  this.forcedColorsQuery = window.matchMedia("(forced-colors: active)");
4824
4960
  this.forcedColorsChangeHandler = () => this.markDirty();
4825
- _optionalChain([this, 'access', _118 => _118.forcedColorsQuery, 'access', _119 => _119.addEventListener, 'optionalCall', _120 => _120("change", this.forcedColorsChangeHandler)]);
4961
+ _optionalChain([this, 'access', _119 => _119.forcedColorsQuery, 'access', _120 => _120.addEventListener, 'optionalCall', _121 => _121("change", this.forcedColorsChangeHandler)]);
4826
4962
  }
4827
- this.root = new class RootEntity extends _chunkSRXU3ZXKjs.Entity {
4963
+ this.root = new class RootEntity extends _chunkPOPXUPKRjs.Entity {
4828
4964
  isPointInside() {
4829
4965
  return false;
4830
4966
  }
@@ -4834,7 +4970,7 @@ var Scene = (_class15 = class _Scene {
4834
4970
  }("root");
4835
4971
  this.root._scene = this;
4836
4972
  this._wasmBackend = new WasmBackendFacade(this.root);
4837
- this.overlayRoot = new class OverlayRoot extends _chunkSRXU3ZXKjs.Entity {
4973
+ this.overlayRoot = new class OverlayRoot extends _chunkPOPXUPKRjs.Entity {
4838
4974
  isPointInside() {
4839
4975
  return false;
4840
4976
  }
@@ -4847,15 +4983,15 @@ var Scene = (_class15 = class _Scene {
4847
4983
  if (options.renderer) {
4848
4984
  this.renderer = options.renderer;
4849
4985
  } else {
4850
- this.renderer = new (0, _chunkZYYP2M5Djs.CanvasRenderer)(
4986
+ this.renderer = new (0, _chunkKRUDTGNRjs.CanvasRenderer)(
4851
4987
  canvas,
4852
4988
  this.disableWindowResize ? { width: this.width, height: this.height } : void 0,
4853
4989
  this.maxDPR
4854
4990
  );
4855
4991
  }
4856
- _optionalChain([this, 'access', _121 => _121.renderer, 'access', _122 => _122.onContextRestored, 'optionalCall', _123 => _123(() => {
4992
+ _optionalChain([this, 'access', _122 => _122.renderer, 'access', _123 => _123.onContextRestored, 'optionalCall', _124 => _124(() => {
4857
4993
  this.markDirty();
4858
- if (_optionalChain([this, 'access', _124 => _124.renderer, 'access', _125 => _125.isContextLost, 'optionalCall', _126 => _126()]) !== true) this.render(this.renderer);
4994
+ if (_optionalChain([this, 'access', _125 => _125.renderer, 'access', _126 => _126.isContextLost, 'optionalCall', _127 => _127()]) !== true) this.render(this.renderer);
4859
4995
  })]);
4860
4996
  if (typeof document !== "undefined") {
4861
4997
  this.a11yRoot = document.createElement("div");
@@ -5028,16 +5164,16 @@ var Scene = (_class15 = class _Scene {
5028
5164
  watchDevicePixelRatio() {
5029
5165
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
5030
5166
  if (this.dprMediaQuery && this.dprChangeHandler) {
5031
- _optionalChain([this, 'access', _127 => _127.dprMediaQuery, 'access', _128 => _128.removeEventListener, 'optionalCall', _129 => _129("change", this.dprChangeHandler)]);
5167
+ _optionalChain([this, 'access', _128 => _128.dprMediaQuery, 'access', _129 => _129.removeEventListener, 'optionalCall', _130 => _130("change", this.dprChangeHandler)]);
5032
5168
  }
5033
5169
  const dpr = window.devicePixelRatio || 1;
5034
5170
  const query = window.matchMedia(`(resolution: ${dpr}dppx)`);
5035
5171
  const handler = () => {
5036
5172
  this.resize(this.width, this.height);
5037
- if (_optionalChain([this, 'access', _130 => _130.renderer, 'access', _131 => _131.isContextLost, 'optionalCall', _132 => _132()]) !== true) this.render(this.renderer);
5173
+ if (_optionalChain([this, 'access', _131 => _131.renderer, 'access', _132 => _132.isContextLost, 'optionalCall', _133 => _133()]) !== true) this.render(this.renderer);
5038
5174
  this.watchDevicePixelRatio();
5039
5175
  };
5040
- _optionalChain([query, 'access', _133 => _133.addEventListener, 'optionalCall', _134 => _134("change", handler)]);
5176
+ _optionalChain([query, 'access', _134 => _134.addEventListener, 'optionalCall', _135 => _135("change", handler)]);
5041
5177
  this.dprMediaQuery = query;
5042
5178
  this.dprChangeHandler = handler;
5043
5179
  }
@@ -5057,7 +5193,7 @@ var Scene = (_class15 = class _Scene {
5057
5193
  if (typeof gl.addEventListener !== "function") return;
5058
5194
  this.glContextLostHandler = (e) => {
5059
5195
  e.preventDefault();
5060
- _optionalChain([this, 'access', _135 => _135.pointRenderer, 'optionalAccess', _136 => _136.destroy, 'call', _137 => _137()]);
5196
+ _optionalChain([this, 'access', _136 => _136.pointRenderer, 'optionalAccess', _137 => _137.destroy, 'call', _138 => _138()]);
5061
5197
  this.pointRenderer = null;
5062
5198
  };
5063
5199
  this.glContextRestoredHandler = () => {
@@ -5154,7 +5290,7 @@ var Scene = (_class15 = class _Scene {
5154
5290
  }
5155
5291
  if (this.hoveredA11yElements.has(el)) {
5156
5292
  this.hoveredA11yElements.delete(el);
5157
- node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("pointerleave", node, void 0, false));
5293
+ node.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)("pointerleave", node, void 0, false));
5158
5294
  }
5159
5295
  this.preserveFocusOnRemoval(el);
5160
5296
  el.remove();
@@ -5254,12 +5390,12 @@ var Scene = (_class15 = class _Scene {
5254
5390
  this.canvasResizeObserver = null;
5255
5391
  }
5256
5392
  if (this.dprMediaQuery && this.dprChangeHandler) {
5257
- _optionalChain([this, 'access', _138 => _138.dprMediaQuery, 'access', _139 => _139.removeEventListener, 'optionalCall', _140 => _140("change", this.dprChangeHandler)]);
5393
+ _optionalChain([this, 'access', _139 => _139.dprMediaQuery, 'access', _140 => _140.removeEventListener, 'optionalCall', _141 => _141("change", this.dprChangeHandler)]);
5258
5394
  this.dprMediaQuery = null;
5259
5395
  this.dprChangeHandler = null;
5260
5396
  }
5261
5397
  if (this.forcedColorsQuery && this.forcedColorsChangeHandler) {
5262
- _optionalChain([this, 'access', _141 => _141.forcedColorsQuery, 'access', _142 => _142.removeEventListener, 'optionalCall', _143 => _143("change", this.forcedColorsChangeHandler)]);
5398
+ _optionalChain([this, 'access', _142 => _142.forcedColorsQuery, 'access', _143 => _143.removeEventListener, 'optionalCall', _144 => _144("change", this.forcedColorsChangeHandler)]);
5263
5399
  this.forcedColorsQuery = null;
5264
5400
  this.forcedColorsChangeHandler = null;
5265
5401
  }
@@ -5268,6 +5404,19 @@ var Scene = (_class15 = class _Scene {
5268
5404
  window.removeEventListener("blur", this.contentSelectionEndListener);
5269
5405
  this.contentSelectionEndListener = null;
5270
5406
  }
5407
+ if (typeof window !== "undefined") {
5408
+ if (this.windowKeyDownHandler) {
5409
+ window.removeEventListener("keydown", this.windowKeyDownHandler);
5410
+ }
5411
+ if (this.windowKeyUpHandler) {
5412
+ window.removeEventListener("keyup", this.windowKeyUpHandler);
5413
+ }
5414
+ }
5415
+ this.windowKeyDownHandler = null;
5416
+ this.windowKeyUpHandler = null;
5417
+ this.keydownHandlers.length = 0;
5418
+ this.keyupHandlers.length = 0;
5419
+ this.shortcuts.length = 0;
5271
5420
  if (typeof window !== "undefined" && this.pointerEventTarget && typeof this.pointerEventTarget.removeEventListener === "function") {
5272
5421
  if (this.pointerMoveListener) {
5273
5422
  this.pointerEventTarget.removeEventListener("pointermove", this.pointerMoveListener);
@@ -5277,9 +5426,9 @@ var Scene = (_class15 = class _Scene {
5277
5426
  }
5278
5427
  this.pointerEventTarget = null;
5279
5428
  }
5280
- _optionalChain([this, 'access', _144 => _144.a11yRoot, 'optionalAccess', _145 => _145.remove, 'call', _146 => _146()]);
5429
+ _optionalChain([this, 'access', _145 => _145.a11yRoot, 'optionalAccess', _146 => _146.remove, 'call', _147 => _147()]);
5281
5430
  this.focusSentinel = null;
5282
- _optionalChain([this, 'access', _147 => _147.portalRoot, 'optionalAccess', _148 => _148.remove, 'call', _149 => _149()]);
5431
+ _optionalChain([this, 'access', _148 => _148.portalRoot, 'optionalAccess', _149 => _149.remove, 'call', _150 => _150()]);
5283
5432
  this.a11yElements.clear();
5284
5433
  for (const el of this.contentElements.values()) el.remove();
5285
5434
  this.contentElements.clear();
@@ -5295,10 +5444,10 @@ var Scene = (_class15 = class _Scene {
5295
5444
  }
5296
5445
  this.glContextLostHandler = null;
5297
5446
  this.glContextRestoredHandler = null;
5298
- _optionalChain([this, 'access', _150 => _150.pointRenderer, 'optionalAccess', _151 => _151.destroy, 'call', _152 => _152()]);
5299
- _optionalChain([this, 'access', _153 => _153.renderer, 'access', _154 => _154.dispose, 'optionalCall', _155 => _155()]);
5300
- _optionalChain([this, 'access', _156 => _156.glCanvas, 'optionalAccess', _157 => _157.remove, 'call', _158 => _158()]);
5301
- _optionalChain([this, 'access', _159 => _159.gpuCanvas, 'optionalAccess', _160 => _160.remove, 'call', _161 => _161()]);
5447
+ _optionalChain([this, 'access', _151 => _151.pointRenderer, 'optionalAccess', _152 => _152.destroy, 'call', _153 => _153()]);
5448
+ _optionalChain([this, 'access', _154 => _154.renderer, 'access', _155 => _155.dispose, 'optionalCall', _156 => _156()]);
5449
+ _optionalChain([this, 'access', _157 => _157.glCanvas, 'optionalAccess', _158 => _158.remove, 'call', _159 => _159()]);
5450
+ _optionalChain([this, 'access', _160 => _160.gpuCanvas, 'optionalAccess', _161 => _161.remove, 'call', _162 => _162()]);
5302
5451
  this.gpuCanvas = null;
5303
5452
  this.gpuContext = null;
5304
5453
  if (this.recoveryTimerId) {
@@ -5310,7 +5459,7 @@ var Scene = (_class15 = class _Scene {
5310
5459
  this.manager = null;
5311
5460
  }
5312
5461
  if (this.device) {
5313
- _optionalChain([this, 'access', _162 => _162.device, 'access', _163 => _163.destroy, 'optionalCall', _164 => _164()]);
5462
+ _optionalChain([this, 'access', _163 => _163.device, 'access', _164 => _164.destroy, 'optionalCall', _165 => _165()]);
5314
5463
  this.device = null;
5315
5464
  }
5316
5465
  }
@@ -5367,6 +5516,7 @@ var Scene = (_class15 = class _Scene {
5367
5516
  this.lastTime = typeof performance !== "undefined" ? performance.now() : 0;
5368
5517
  this.watchCanvasVisibility();
5369
5518
  this.scheduleFrame();
5519
+ this.attachWindowKeyListeners();
5370
5520
  const isTextFocused = this.focusedA11yElement instanceof HTMLInputElement || this.focusedA11yElement instanceof HTMLTextAreaElement;
5371
5521
  if (isTextFocused && this.renderMode === "onDemand" && !this.caretBlinkTimer) {
5372
5522
  this.caretBlinkTimer = setInterval(() => {
@@ -5434,6 +5584,132 @@ var Scene = (_class15 = class _Scene {
5434
5584
  }
5435
5585
  this._canvasOnScreen = true;
5436
5586
  }
5587
+ // --- domain: input — scene-level keyboard channel ---
5588
+ /**
5589
+ * Register a listener on the scene-level keyboard channel.
5590
+ *
5591
+ * Mirrors {@link Entity.on} minus the options parameter (bubble-only, and
5592
+ * there is no capture phase for a single window tap). Handlers fire only
5593
+ * when ALL of these hold:
5594
+ *
5595
+ * 1. the native event was not already `defaultPrevented`,
5596
+ * 2. it is not an auto-repeat (`e.repeat`),
5597
+ * 3. `document.activeElement` does not own the keyboard
5598
+ * (see {@link ownsKeyboard}) — typing in an `<input>` or focusing a
5599
+ * slider-role element suppresses the scene channel, unconditionally,
5600
+ * including modifier chords.
5601
+ *
5602
+ * The listeners are window-level BUBBLE-phase taps, so an entity handler
5603
+ * that calls `nativeEvent.stopPropagation()` keeps the key from ever
5604
+ * reaching this channel. Deliberately not registered in the capture phase:
5605
+ * a window-capture listener would preempt Modal's document-capture trap.
5606
+ *
5607
+ * Listeners attach at `start()` and survive `stop()`; only `destroy()`
5608
+ * removes them.
5609
+ *
5610
+ * @param event - `'keydown'` or `'keyup'`; anything else is rejected with a
5611
+ * dev warning (guards JS callers — TypeScript already narrows this).
5612
+ * @param callback - Handler invoked with a {@link SceneKeyEvent}.
5613
+ * @returns `this` for method chaining.
5614
+ */
5615
+ on(event, callback) {
5616
+ if (event !== "keydown" && event !== "keyup") {
5617
+ console.warn(
5618
+ `[VectoJS] Scene.on: unsupported event '${String(event)}'. Only 'keydown' and 'keyup' are supported.`
5619
+ );
5620
+ return this;
5621
+ }
5622
+ (event === "keydown" ? this.keydownHandlers : this.keyupHandlers).push(callback);
5623
+ return this;
5624
+ }
5625
+ /**
5626
+ * Remove a listener previously registered with {@link Scene.on}. Silent
5627
+ * no-op for unknown handlers, unsupported event names, or after
5628
+ * {@link Scene.destroy}.
5629
+ *
5630
+ * @returns `this` for method chaining.
5631
+ */
5632
+ off(event, callback) {
5633
+ if (event !== "keydown" && event !== "keyup") {
5634
+ console.warn(
5635
+ `[VectoJS] Scene.off: unsupported event '${String(event)}'. Only 'keydown' and 'keyup' are supported.`
5636
+ );
5637
+ return this;
5638
+ }
5639
+ const handlers = event === "keydown" ? this.keydownHandlers : this.keyupHandlers;
5640
+ const idx = handlers.indexOf(callback);
5641
+ if (idx !== -1) handlers.splice(idx, 1);
5642
+ return this;
5643
+ }
5644
+ /**
5645
+ * Sugar over the keyboard channel: invoke `spec.handler` whenever a
5646
+ * non-repeat `keydown` whose normalized chord matches `spec.chord`
5647
+ * (via {@link normalizeChord}) passes the same gates as {@link Scene.on} —
5648
+ * including the unconditional `ownsKeyboard(activeElement)` suppression.
5649
+ *
5650
+ * @param spec - `{ chord, handler }`; the chord is normalized once at
5651
+ * registration, so `'ctrl+n'`, `'Control+n'` and `'Ctrl+N'` are aliases.
5652
+ * @returns `this` for method chaining.
5653
+ */
5654
+ registerShortcut(spec) {
5655
+ this.shortcuts.push({ chord: normalizeChord(spec.chord), handler: spec.handler });
5656
+ return this;
5657
+ }
5658
+ /**
5659
+ * Remove a shortcut previously registered with {@link Scene.registerShortcut}.
5660
+ * Matches on normalized chord + exact handler reference; silent no-op when
5661
+ * nothing matches.
5662
+ *
5663
+ * @param spec - The same spec object (chord spelling may differ; it is
5664
+ * re-normalized before matching).
5665
+ * @returns `this` for method chaining.
5666
+ */
5667
+ unregisterShortcut(spec) {
5668
+ const chord = normalizeChord(spec.chord);
5669
+ const idx = this.shortcuts.findIndex((s) => s.chord === chord && s.handler === spec.handler);
5670
+ if (idx !== -1) this.shortcuts.splice(idx, 1);
5671
+ return this;
5672
+ }
5673
+ /** Attach the window bubble-phase keyboard listeners exactly once. */
5674
+ attachWindowKeyListeners() {
5675
+ if (this.windowKeyDownHandler || typeof window === "undefined") return;
5676
+ this.windowKeyDownHandler = (e) => this.dispatchKeyboard(e, "keydown");
5677
+ this.windowKeyUpHandler = (e) => this.dispatchKeyboard(e, "keyup");
5678
+ window.addEventListener("keydown", this.windowKeyDownHandler);
5679
+ window.addEventListener("keyup", this.windowKeyUpHandler);
5680
+ }
5681
+ /**
5682
+ * Gate a native keyboard event and fan it out to scene handlers +
5683
+ * chord-matched shortcuts. See {@link Scene.on} for the gate contract.
5684
+ */
5685
+ dispatchKeyboard(e, type) {
5686
+ if (e.defaultPrevented) return;
5687
+ if (e.repeat) return;
5688
+ const active = typeof document !== "undefined" ? document.activeElement : null;
5689
+ if (ownsKeyboard(active)) return;
5690
+ const event = {
5691
+ type,
5692
+ key: e.key,
5693
+ code: e.code,
5694
+ repeat: e.repeat,
5695
+ ctrlKey: e.ctrlKey,
5696
+ altKey: e.altKey,
5697
+ shiftKey: e.shiftKey,
5698
+ metaKey: e.metaKey,
5699
+ target: e.target,
5700
+ nativeEvent: e,
5701
+ stopPropagation: () => e.stopPropagation(),
5702
+ preventDefault: () => e.preventDefault()
5703
+ };
5704
+ const handlers = type === "keydown" ? this.keydownHandlers : this.keyupHandlers;
5705
+ for (const handler of [...handlers]) handler(event);
5706
+ if (type === "keydown") {
5707
+ const chord = normalizeChord(e);
5708
+ for (const shortcut of [...this.shortcuts]) {
5709
+ if (shortcut.chord === chord) shortcut.handler(event);
5710
+ }
5711
+ }
5712
+ }
5437
5713
  // --- domain: scene-facade — tree accessors ---
5438
5714
  /**
5439
5715
  * Manually advance the scene clock by `dt` milliseconds and render synchronously.
@@ -5635,7 +5911,7 @@ var Scene = (_class15 = class _Scene {
5635
5911
  if (this.mouseX > -9e3 && !this.projectsSelectableText(node)) {
5636
5912
  if (node.isPointInside(this.mouseX, this.mouseY)) return true;
5637
5913
  }
5638
- const existing = _optionalChain([this, 'access', _165 => _165.a11yElements, 'optionalAccess', _166 => _166.get, 'call', _167 => _167(node.id)]);
5914
+ const existing = _optionalChain([this, 'access', _166 => _166.a11yElements, 'optionalAccess', _167 => _167.get, 'call', _168 => _168(node.id)]);
5639
5915
  if (existing && typeof document !== "undefined" && document.activeElement === existing) {
5640
5916
  return true;
5641
5917
  }
@@ -5649,8 +5925,8 @@ var Scene = (_class15 = class _Scene {
5649
5925
  * selection.
5650
5926
  */
5651
5927
  projectsSelectableText(node) {
5652
- const projection = _optionalChain([node, 'access', _168 => _168.getContentProjection, 'optionalCall', _169 => _169()]);
5653
- return !!_optionalChain([projection, 'optionalAccess', _170 => _170.text]) && projection.selectable !== false;
5928
+ const projection = _optionalChain([node, 'access', _169 => _169.getContentProjection, 'optionalCall', _170 => _170()]);
5929
+ return !!_optionalChain([projection, 'optionalAccess', _171 => _171.text]) && projection.selectable !== false;
5654
5930
  }
5655
5931
  /**
5656
5932
  * Keep `entity`'s a11y shadow node projected while it has
@@ -5745,20 +6021,20 @@ var Scene = (_class15 = class _Scene {
5745
6021
  el.style.background = "transparent";
5746
6022
  }
5747
6023
  el.addEventListener("click", (e) => {
5748
- node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("click", node, e));
6024
+ node.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)("click", node, e));
5749
6025
  });
5750
6026
  el.addEventListener("dblclick", (e) => {
5751
- node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("dblclick", node, e));
6027
+ node.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)("dblclick", node, e));
5752
6028
  });
5753
6029
  el.addEventListener("mouseenter", (e) => {
5754
6030
  if (this.debugA11y) el.style.backgroundColor = "rgba(56, 189, 248, 0.2)";
5755
6031
  this.hoveredA11yElements.add(el);
5756
- node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("hover", node, e, false));
6032
+ node.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)("hover", node, e, false));
5757
6033
  });
5758
6034
  el.addEventListener("mouseleave", (e) => {
5759
6035
  if (this.debugA11y) el.style.backgroundColor = "rgba(56, 189, 248, 0.05)";
5760
6036
  this.hoveredA11yElements.delete(el);
5761
- node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("pointerleave", node, e, false));
6037
+ node.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)("pointerleave", node, e, false));
5762
6038
  });
5763
6039
  const capEl = el;
5764
6040
  const releasePointer = (event) => {
@@ -5776,24 +6052,24 @@ var Scene = (_class15 = class _Scene {
5776
6052
  if (e.target === capEl && typeof capEl.setPointerCapture === "function") {
5777
6053
  capEl.setPointerCapture(e.pointerId);
5778
6054
  }
5779
- node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("pointerdown", node, e));
6055
+ node.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)("pointerdown", node, e));
5780
6056
  });
5781
6057
  el.addEventListener("pointerup", (e) => {
5782
6058
  releasePointer(e);
5783
- node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("pointerup", node, e));
6059
+ node.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)("pointerup", node, e));
5784
6060
  });
5785
6061
  el.addEventListener("pointercancel", (e) => {
5786
6062
  releasePointer(e);
5787
- node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("pointercancel", node, e));
6063
+ node.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)("pointercancel", node, e));
5788
6064
  });
5789
6065
  el.addEventListener(
5790
6066
  "pointermove",
5791
- (e) => node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("pointermove", node, e))
6067
+ (e) => node.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)("pointermove", node, e))
5792
6068
  );
5793
6069
  el.addEventListener(
5794
6070
  "wheel",
5795
6071
  (e) => {
5796
- node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("wheel", node, e));
6072
+ node.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)("wheel", node, e));
5797
6073
  },
5798
6074
  { passive: false }
5799
6075
  );
@@ -5818,10 +6094,10 @@ var Scene = (_class15 = class _Scene {
5818
6094
  );
5819
6095
  emitInitialScroll = emitScroll;
5820
6096
  el.addEventListener("keydown", (e) => {
5821
- node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("keydown", node, e));
6097
+ node.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)("keydown", node, e));
5822
6098
  });
5823
6099
  el.addEventListener("keyup", (e) => {
5824
- node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("keyup", node, e));
6100
+ node.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)("keyup", node, e));
5825
6101
  });
5826
6102
  if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {
5827
6103
  const input = el;
@@ -5852,7 +6128,7 @@ var Scene = (_class15 = class _Scene {
5852
6128
  el.addEventListener("compositionupdate", (e) => {
5853
6129
  const data = _nullishCoalesce(e.data, () => ( ""));
5854
6130
  composition = {
5855
- start: _nullishCoalesce(_optionalChain([composition, 'optionalAccess', _171 => _171.start]), () => ( 0)),
6131
+ start: _nullishCoalesce(_optionalChain([composition, 'optionalAccess', _172 => _172.start]), () => ( 0)),
5856
6132
  length: data.length
5857
6133
  };
5858
6134
  forward();
@@ -5883,14 +6159,6 @@ var Scene = (_class15 = class _Scene {
5883
6159
  }
5884
6160
  node.emit("blur", {});
5885
6161
  });
5886
- if (!isNativelyFocusable(el) && attrs.role && INTERACTIVE_A11Y_ROLES.has(attrs.role)) {
5887
- el.addEventListener("keydown", (e) => {
5888
- if (e.key === "Enter" || e.key === " ") {
5889
- e.preventDefault();
5890
- node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("click", node, e));
5891
- }
5892
- });
5893
- }
5894
6162
  if (node.a11yFullViewport) {
5895
6163
  this.a11yRoot.insertBefore(el, this.a11yRoot.firstChild);
5896
6164
  } else {
@@ -5909,7 +6177,24 @@ var Scene = (_class15 = class _Scene {
5909
6177
  if (renderOrder !== void 0 && el.style.zIndex !== String(renderOrder)) {
5910
6178
  el.style.zIndex = String(renderOrder);
5911
6179
  }
5912
- const implicitTabIndex = !isNativelyFocusable(el) && attrs.role && INTERACTIVE_A11Y_ROLES.has(attrs.role) ? 0 : null;
6180
+ const interactiveRole = !isNativelyFocusable(el) && !!attrs.role && INTERACTIVE_A11Y_ROLES.has(attrs.role);
6181
+ if (interactiveRole && !this.keyboardActivatedA11y.has(el)) {
6182
+ el.addEventListener("keydown", (e) => {
6183
+ if (e.key === "Enter") {
6184
+ e.preventDefault();
6185
+ node.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)("click", node, e));
6186
+ } else if (e.key === " ") {
6187
+ e.preventDefault();
6188
+ }
6189
+ });
6190
+ el.addEventListener("keyup", (e) => {
6191
+ if (e.key === " ") {
6192
+ node.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)("click", node, e));
6193
+ }
6194
+ });
6195
+ this.keyboardActivatedA11y.add(el);
6196
+ }
6197
+ const implicitTabIndex = interactiveRole ? 0 : null;
5913
6198
  const desiredTabIndex = _nullishCoalesce(attrs.tabIndex, () => ( implicitTabIndex));
5914
6199
  if (desiredTabIndex === null) {
5915
6200
  if (el.hasAttribute("tabindex")) el.removeAttribute("tabindex");
@@ -5925,7 +6210,7 @@ var Scene = (_class15 = class _Scene {
5925
6210
  this.syncOptionalAttribute(
5926
6211
  el,
5927
6212
  "href",
5928
- attrs.href === void 0 ? void 0 : _chunkZYYP2M5Djs.sanitizeUrl.call(void 0, attrs.href)
6213
+ attrs.href === void 0 ? void 0 : _chunkKRUDTGNRjs.sanitizeUrl.call(void 0, attrs.href)
5929
6214
  );
5930
6215
  this.syncOptionalAttribute(el, "target", attrs.target);
5931
6216
  }
@@ -6094,7 +6379,7 @@ var Scene = (_class15 = class _Scene {
6094
6379
  const display = visible ? "" : "none";
6095
6380
  if (el.style.display !== display) el.style.display = display;
6096
6381
  }
6097
- _optionalChain([emitInitialScroll, 'optionalCall', _172 => _172()]);
6382
+ _optionalChain([emitInitialScroll, 'optionalCall', _173 => _173()]);
6098
6383
  }
6099
6384
  if (this.phases.enabled) {
6100
6385
  const projectionStart = performance.now();
@@ -6324,7 +6609,7 @@ var Scene = (_class15 = class _Scene {
6324
6609
  el.addEventListener(
6325
6610
  "wheel",
6326
6611
  (e2) => {
6327
- node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("wheel", node, e2));
6612
+ node.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)("wheel", node, e2));
6328
6613
  },
6329
6614
  { passive: false }
6330
6615
  );
@@ -6555,8 +6840,8 @@ var Scene = (_class15 = class _Scene {
6555
6840
  next.f = f2;
6556
6841
  next.tier = tier;
6557
6842
  next.hasBand = lineBand !== null;
6558
- next.bandMin = _nullishCoalesce(_optionalChain([lineBand, 'optionalAccess', _173 => _173.minY]), () => ( 0));
6559
- next.bandMax = _nullishCoalesce(_optionalChain([lineBand, 'optionalAccess', _174 => _174.maxY]), () => ( 0));
6843
+ next.bandMin = _nullishCoalesce(_optionalChain([lineBand, 'optionalAccess', _174 => _174.minY]), () => ( 0));
6844
+ next.bandMax = _nullishCoalesce(_optionalChain([lineBand, 'optionalAccess', _175 => _175.maxY]), () => ( 0));
6560
6845
  next.visible = visible;
6561
6846
  next.width = node.width;
6562
6847
  next.height = node.height;
@@ -6742,7 +7027,7 @@ var Scene = (_class15 = class _Scene {
6742
7027
  * (and {@link respectReducedMotion} is on). `0` means uncapped.
6743
7028
  */
6744
7029
  effectiveMaxFPS() {
6745
- const reduced = this.respectReducedMotion && !!_optionalChain([this, 'access', _175 => _175.reducedMotionQuery, 'optionalAccess', _176 => _176.matches]);
7030
+ const reduced = this.respectReducedMotion && !!_optionalChain([this, 'access', _176 => _176.reducedMotionQuery, 'optionalAccess', _177 => _177.matches]);
6746
7031
  if (reduced)
6747
7032
  return this.maxFPS > 0 ? Math.min(this.maxFPS, REDUCED_MOTION_FPS) : REDUCED_MOTION_FPS;
6748
7033
  return this.maxFPS;
@@ -6837,7 +7122,7 @@ var Scene = (_class15 = class _Scene {
6837
7122
  * @param time - Current absolute time in milliseconds (default 0).
6838
7123
  */
6839
7124
  render(renderer, dt = 0, time = 0) {
6840
- if (_optionalChain([renderer, 'access', _177 => _177.isContextLost, 'optionalCall', _178 => _178()])) return;
7125
+ if (_optionalChain([renderer, 'access', _178 => _178.isContextLost, 'optionalCall', _179 => _179()])) return;
6841
7126
  const isMainRenderer = renderer === this.renderer;
6842
7127
  if (isMainRenderer && this.a11yRoot && this.canvas.parentElement) {
6843
7128
  const parentStyle = this.canvas.parentElement.style;
@@ -6851,6 +7136,7 @@ var Scene = (_class15 = class _Scene {
6851
7136
  this.renderOrderCounter = 0;
6852
7137
  this.a11yRenderOrders.clear();
6853
7138
  this.activePortalsThisFrame.clear();
7139
+ this._updateWalkDt = dt;
6854
7140
  this._tickBatchedDrivers(dt);
6855
7141
  }
6856
7142
  const computeEntities = this._computeEntitiesFor(this._structureVersion);
@@ -6986,14 +7272,14 @@ var Scene = (_class15 = class _Scene {
6986
7272
  }
6987
7273
  renderer.clear();
6988
7274
  if (isMainRenderer) {
6989
- _optionalChain([this, 'access', _179 => _179.pointRenderer, 'optionalAccess', _180 => _180.begin, 'call', _181 => _181()]);
7275
+ _optionalChain([this, 'access', _180 => _180.pointRenderer, 'optionalAccess', _181 => _181.begin, 'call', _182 => _182()]);
6990
7276
  }
6991
7277
  const vw = this.width;
6992
7278
  const vh = this.height;
6993
7279
  let walkHadAnimation = false;
6994
7280
  let walkHadInteractive = false;
6995
7281
  const runUpdate = (node) => {
6996
- const overridesUpdate = node.update !== _chunkSRXU3ZXKjs.Entity.prototype.update;
7282
+ const overridesUpdate = node.update !== _chunkPOPXUPKRjs.Entity.prototype.update;
6997
7283
  let pending = node.hasPendingAnimations();
6998
7284
  if (pending || overridesUpdate) {
6999
7285
  node.update(dt, time);
@@ -7002,7 +7288,7 @@ var Scene = (_class15 = class _Scene {
7002
7288
  if (pending) walkHadAnimation = true;
7003
7289
  if (!walkHadInteractive && node.interactive) walkHadInteractive = true;
7004
7290
  if (this._devActive && this._devFrameCount % 120 === 0) {
7005
- if (overridesUpdate && node.hasPendingAnimations === _chunkSRXU3ZXKjs.Entity.prototype.hasPendingAnimations) {
7291
+ if (overridesUpdate && node.hasPendingAnimations === _chunkPOPXUPKRjs.Entity.prototype.hasPendingAnimations) {
7006
7292
  this._devWarn(
7007
7293
  `Entity "${node.id}" overrides update() but not hasPendingAnimations(). Custom motion in update() without overriding hasPendingAnimations() causes the idle throttle to slow the animation. Override hasPendingAnimations() to return true while motion is in flight.`
7008
7294
  );
@@ -7021,6 +7307,7 @@ var Scene = (_class15 = class _Scene {
7021
7307
  };
7022
7308
  updateWalk(this.root);
7023
7309
  for (const overlay of this.overlayRoot.children) updateWalk(overlay);
7310
+ this._updateWalkDt = null;
7024
7311
  }
7025
7312
  const transformTiming = this.phases.userTiming ? beginVectoUserTiming(VECTO_USER_TIMING.scene.transform) : null;
7026
7313
  const wasmT0 = this.phases.enabled ? performance.now() : 0;
@@ -7221,6 +7508,7 @@ var Scene = (_class15 = class _Scene {
7221
7508
  for (const overlay of this.overlayRoot.children) {
7222
7509
  renderNode(overlay, 1, 0, 0, 1, 0, 0, 1);
7223
7510
  }
7511
+ if (isMainRenderer) this._updateWalkDt = null;
7224
7512
  if (this.phases.enabled) this.phases.record("drawWalk", performance.now() - drawT0);
7225
7513
  if (drawTiming) endVectoUserTiming(drawTiming);
7226
7514
  if (this.phases.userTiming) {
@@ -7235,9 +7523,9 @@ var Scene = (_class15 = class _Scene {
7235
7523
  const flushT0 = this.phases.enabled ? performance.now() : 0;
7236
7524
  renderer.flush();
7237
7525
  if (isMainRenderer) {
7238
- _optionalChain([this, 'access', _182 => _182.pointRenderer, 'optionalAccess', _183 => _183.flush, 'call', _184 => _184()]);
7526
+ _optionalChain([this, 'access', _183 => _183.pointRenderer, 'optionalAccess', _184 => _184.flush, 'call', _185 => _185()]);
7239
7527
  }
7240
- _optionalChain([renderer, 'access', _185 => _185.present, 'optionalCall', _186 => _186()]);
7528
+ _optionalChain([renderer, 'access', _186 => _186.present, 'optionalCall', _187 => _187()]);
7241
7529
  if (this.phases.enabled) this.phases.record("flush", performance.now() - flushT0);
7242
7530
  if (flushTiming) endVectoUserTiming(flushTiming);
7243
7531
  if (this._devActive) {
@@ -7250,7 +7538,7 @@ var Scene = (_class15 = class _Scene {
7250
7538
  * Export the current scene state to a lightweight, flat SVG XML string.
7251
7539
  */
7252
7540
  toSVG() {
7253
- const renderer = new (0, _chunkZYYP2M5Djs.SVGRenderer)(this.width, this.height);
7541
+ const renderer = new (0, _chunkKRUDTGNRjs.SVGRenderer)(this.width, this.height);
7254
7542
  this.render(renderer, 0, 0);
7255
7543
  return renderer.toXMLString();
7256
7544
  }
@@ -7463,20 +7751,20 @@ function defaultMeasurer() {
7463
7751
  sharedMeasurer ??= _layout.resolveGlyphMeasurer.call(void 0, "sans-serif");
7464
7752
  return sharedMeasurer;
7465
7753
  }
7466
- var TextEntity = (_class16 = class extends _chunkSRXU3ZXKjs.Entity {
7754
+ var TextEntity = (_class16 = class extends _chunkPOPXUPKRjs.Entity {
7467
7755
 
7468
7756
 
7469
7757
 
7470
7758
 
7471
- __init177() {this.nodes = []}
7759
+ __init189() {this.nodes = []}
7472
7760
 
7473
- __init178() {this.fillStyle = "#94a3b8"}
7474
- __init179() {this.strokeStyle = null}
7475
- __init180() {this.hoveredFillStyle = "#ffffff"}
7476
- __init181() {this.lineWidth = 1}
7477
- __init182() {this.isHovered = false}
7761
+ __init190() {this.fillStyle = "#94a3b8"}
7762
+ __init191() {this.strokeStyle = null}
7763
+ __init192() {this.hoveredFillStyle = "#ffffff"}
7764
+ __init193() {this.lineWidth = 1}
7765
+ __init194() {this.isHovered = false}
7478
7766
  /** Bumped by {@link applyLayout}; read by `Scene` to skip an unchanged sync. */
7479
- __init183() {this.contentEpoch = 0}
7767
+ __init195() {this.contentEpoch = 0}
7480
7768
  /**
7481
7769
  * Visual lines with real canvas geometry, rebuilt by {@link applyLayout}
7482
7770
  * exactly when `this.nodes` changes.
@@ -7488,9 +7776,9 @@ var TextEntity = (_class16 = class extends _chunkSRXU3ZXKjs.Entity {
7488
7776
  * ~0.35 em per line for a 24 px font, Firefox). Per-line carriers pin the DOM
7489
7777
  * baseline to the canvas baseline by construction, same as `ui/Text`.
7490
7778
  */
7491
- __init184() {this.projectionLines = []}
7779
+ __init196() {this.projectionLines = []}
7492
7780
  constructor(text, atlas, maxWidth, fontSize = 24) {
7493
- super();_class16.prototype.__init177.call(this);_class16.prototype.__init178.call(this);_class16.prototype.__init179.call(this);_class16.prototype.__init180.call(this);_class16.prototype.__init181.call(this);_class16.prototype.__init182.call(this);_class16.prototype.__init183.call(this);_class16.prototype.__init184.call(this);;
7781
+ super();_class16.prototype.__init189.call(this);_class16.prototype.__init190.call(this);_class16.prototype.__init191.call(this);_class16.prototype.__init192.call(this);_class16.prototype.__init193.call(this);_class16.prototype.__init194.call(this);_class16.prototype.__init195.call(this);_class16.prototype.__init196.call(this);;
7494
7782
  this.text = text;
7495
7783
  this.atlas = atlas;
7496
7784
  this.fontSize = fontSize;
@@ -7667,17 +7955,17 @@ var TextEntity = (_class16 = class extends _chunkSRXU3ZXKjs.Entity {
7667
7955
  }, _class16);
7668
7956
 
7669
7957
  // src/components/GridTextEntity.ts
7670
- var GridTextEntity = (_class17 = class extends _chunkSRXU3ZXKjs.Entity {
7958
+ var GridTextEntity = (_class17 = class extends _chunkPOPXUPKRjs.Entity {
7671
7959
 
7672
- __init185() {this.fillStyle = "#ffffff"}
7673
- __init186() {this.grid = []}
7960
+ __init197() {this.fillStyle = "#ffffff"}
7961
+ __init198() {this.grid = []}
7674
7962
  // Array of rows
7675
- __init187() {this.cols = 0}
7676
- __init188() {this.rows = 0}
7963
+ __init199() {this.cols = 0}
7964
+ __init200() {this.rows = 0}
7677
7965
 
7678
7966
 
7679
7967
  constructor(_atlas, fontSize = 10) {
7680
- super();_class17.prototype.__init185.call(this);_class17.prototype.__init186.call(this);_class17.prototype.__init187.call(this);_class17.prototype.__init188.call(this);;
7968
+ super();_class17.prototype.__init197.call(this);_class17.prototype.__init198.call(this);_class17.prototype.__init199.call(this);_class17.prototype.__init200.call(this);;
7681
7969
  this.fontSize = fontSize;
7682
7970
  this.charWidth = fontSize * 1;
7683
7971
  this.charHeight = fontSize * 1.1;
@@ -7686,7 +7974,7 @@ var GridTextEntity = (_class17 = class extends _chunkSRXU3ZXKjs.Entity {
7686
7974
  updateGrid(ascii) {
7687
7975
  this.grid = ascii;
7688
7976
  this.rows = ascii.length;
7689
- this.cols = ascii.reduce((widest, row) => Math.max(widest, _nullishCoalesce(_optionalChain([row, 'optionalAccess', _187 => _187.length]), () => ( 0))), 0);
7977
+ this.cols = ascii.reduce((widest, row) => Math.max(widest, _nullishCoalesce(_optionalChain([row, 'optionalAccess', _188 => _188.length]), () => ( 0))), 0);
7690
7978
  }
7691
7979
  isPointInside(_globalX, _globalY) {
7692
7980
  return false;
@@ -7761,7 +8049,7 @@ function distSqToSegment(px, py, x1, y1, x2, y2) {
7761
8049
  const ey = py - cy;
7762
8050
  return ex * ex + ey * ey;
7763
8051
  }
7764
- var SplineEntity = (_class18 = class extends _chunkSRXU3ZXKjs.Entity {
8052
+ var SplineEntity = (_class18 = class extends _chunkPOPXUPKRjs.Entity {
7765
8053
 
7766
8054
 
7767
8055
 
@@ -7769,17 +8057,17 @@ var SplineEntity = (_class18 = class extends _chunkSRXU3ZXKjs.Entity {
7769
8057
 
7770
8058
 
7771
8059
 
7772
- __init189() {this.offscreen = null}
7773
- __init190() {this.baked = false}
8060
+ __init201() {this.offscreen = null}
8061
+ __init202() {this.baked = false}
7774
8062
  /** Logical (CSS-pixel) size of the baked bitmap — the blit destination size. */
7775
- __init191() {this.bakedWidth = 0}
7776
- __init192() {this.bakedHeight = 0}
8063
+ __init203() {this.bakedWidth = 0}
8064
+ __init204() {this.bakedHeight = 0}
7777
8065
  /**
7778
8066
  * Device-pixel ratio the bitmap was rasterized at. Tracked so a DPR change
7779
8067
  * (browser zoom, a monitor move, a renderer whose clamp changed) re-bakes
7780
8068
  * instead of blitting a bitmap at the wrong device density forever.
7781
8069
  */
7782
- __init193() {this.bakedDPR = 0}
8070
+ __init205() {this.bakedDPR = 0}
7783
8071
  /**
7784
8072
  * Gradient strokes can't be baked to a solid-color bitmap; they render
7785
8073
  * per-frame. Derived from `_doc`, so it is NOT readonly — assigning a new
@@ -7787,7 +8075,7 @@ var SplineEntity = (_class18 = class extends _chunkSRXU3ZXKjs.Entity {
7787
8075
  */
7788
8076
 
7789
8077
  /** Lazily-flattened polylines (one Float32Array of [x,y,...] per segment) for hit-testing. */
7790
- __init194() {this.polylines = null}
8078
+ __init206() {this.polylines = null}
7791
8079
  /**
7792
8080
  * The spline document to render. Assigning a new document invalidates all
7793
8081
  * caches (baked canvas, flattened polylines, bounds).
@@ -7821,9 +8109,9 @@ var SplineEntity = (_class18 = class extends _chunkSRXU3ZXKjs.Entity {
7821
8109
  * local bounds after painting the curves. Useful for drag feedback and
7822
8110
  * debugging hit areas. Defaults to `false`.
7823
8111
  */
7824
- __init195() {this.showBounds = false}
8112
+ __init207() {this.showBounds = false}
7825
8113
  constructor(doc, opts = {}) {
7826
- super();_class18.prototype.__init189.call(this);_class18.prototype.__init190.call(this);_class18.prototype.__init191.call(this);_class18.prototype.__init192.call(this);_class18.prototype.__init193.call(this);_class18.prototype.__init194.call(this);_class18.prototype.__init195.call(this);;
8114
+ super();_class18.prototype.__init201.call(this);_class18.prototype.__init202.call(this);_class18.prototype.__init203.call(this);_class18.prototype.__init204.call(this);_class18.prototype.__init205.call(this);_class18.prototype.__init206.call(this);_class18.prototype.__init207.call(this);;
7827
8115
  this._doc = doc;
7828
8116
  this._lineWidth = _nullishCoalesce(opts.lineWidth, () => ( 2));
7829
8117
  this.cache = _nullishCoalesce(opts.cache, () => ( true));
@@ -7843,7 +8131,7 @@ var SplineEntity = (_class18 = class extends _chunkSRXU3ZXKjs.Entity {
7843
8131
  */
7844
8132
  computeContainsGradient() {
7845
8133
  const isGradient = (c) => c !== null && !Array.isArray(c);
7846
- return (_nullishCoalesce(_optionalChain([this, 'access', _188 => _188._doc, 'access', _189 => _189.equations, 'optionalAccess', _190 => _190.some, 'call', _191 => _191((eq) => isGradient(eq.color_rgb))]), () => ( false))) || (_nullishCoalesce(_optionalChain([this, 'access', _192 => _192._doc, 'access', _193 => _193.paths, 'optionalAccess', _194 => _194.some, 'call', _195 => _195((p) => isGradient(p.color_rgb))]), () => ( false)));
8134
+ return (_nullishCoalesce(_optionalChain([this, 'access', _189 => _189._doc, 'access', _190 => _190.equations, 'optionalAccess', _191 => _191.some, 'call', _192 => _192((eq) => isGradient(eq.color_rgb))]), () => ( false))) || (_nullishCoalesce(_optionalChain([this, 'access', _193 => _193._doc, 'access', _194 => _194.paths, 'optionalAccess', _195 => _195.some, 'call', _196 => _196((p) => isGradient(p.color_rgb))]), () => ( false)));
7847
8135
  }
7848
8136
  /** Clear cached baked canvas and hit-test polylines. */
7849
8137
  invalidateCache() {
@@ -8089,7 +8377,7 @@ async function loadSpline(url) {
8089
8377
  }
8090
8378
 
8091
8379
  // src/components/Rect.ts
8092
- var Rect = class extends _chunkSRXU3ZXKjs.Entity {
8380
+ var Rect = class extends _chunkPOPXUPKRjs.Entity {
8093
8381
 
8094
8382
 
8095
8383
 
@@ -8145,7 +8433,7 @@ var Rect = class extends _chunkSRXU3ZXKjs.Entity {
8145
8433
  };
8146
8434
 
8147
8435
  // src/components/Circle.ts
8148
- var Circle = class extends _chunkSRXU3ZXKjs.Entity {
8436
+ var Circle = class extends _chunkPOPXUPKRjs.Entity {
8149
8437
 
8150
8438
 
8151
8439
 
@@ -8209,7 +8497,7 @@ var Circle = class extends _chunkSRXU3ZXKjs.Entity {
8209
8497
  };
8210
8498
 
8211
8499
  // src/components/Group.ts
8212
- var Group = class extends _chunkSRXU3ZXKjs.Entity {
8500
+ var Group = class extends _chunkPOPXUPKRjs.Entity {
8213
8501
  constructor(...children) {
8214
8502
  super();
8215
8503
  if (children.length > 0) this.add(...children);
@@ -8228,21 +8516,21 @@ var _math = require('@vectojs/math'); _createStarExport(_math);
8228
8516
 
8229
8517
 
8230
8518
  // src/tree/DOMPortalEntity.ts
8231
- var DOMPortalEntity = (_class19 = class extends _chunkSRXU3ZXKjs.Entity {
8519
+ var DOMPortalEntity = (_class19 = class extends _chunkPOPXUPKRjs.Entity {
8232
8520
 
8233
- __init196() {this.isDOMPortal = true}
8234
- __init197() {this.domListeners = []}
8235
- __init198() {this.resizeObserver = null}
8236
- __init199() {this.domBound = false}
8237
- __init200() {this.cachedWidth = 100}
8238
- __init201() {this.cachedHeight = 100}
8239
- __init202() {this.lastWidth = ""}
8240
- __init203() {this.lastHeight = ""}
8241
- __init204() {this.lastTransform = ""}
8242
- __init205() {this.lastZIndex = ""}
8243
- __init206() {this.lastOpacity = ""}
8521
+ __init208() {this.isDOMPortal = true}
8522
+ __init209() {this.domListeners = []}
8523
+ __init210() {this.resizeObserver = null}
8524
+ __init211() {this.domBound = false}
8525
+ __init212() {this.cachedWidth = 100}
8526
+ __init213() {this.cachedHeight = 100}
8527
+ __init214() {this.lastWidth = ""}
8528
+ __init215() {this.lastHeight = ""}
8529
+ __init216() {this.lastTransform = ""}
8530
+ __init217() {this.lastZIndex = ""}
8531
+ __init218() {this.lastOpacity = ""}
8244
8532
  constructor(domElement, width, height, id) {
8245
- super(id);_class19.prototype.__init196.call(this);_class19.prototype.__init197.call(this);_class19.prototype.__init198.call(this);_class19.prototype.__init199.call(this);_class19.prototype.__init200.call(this);_class19.prototype.__init201.call(this);_class19.prototype.__init202.call(this);_class19.prototype.__init203.call(this);_class19.prototype.__init204.call(this);_class19.prototype.__init205.call(this);_class19.prototype.__init206.call(this);;
8533
+ super(id);_class19.prototype.__init208.call(this);_class19.prototype.__init209.call(this);_class19.prototype.__init210.call(this);_class19.prototype.__init211.call(this);_class19.prototype.__init212.call(this);_class19.prototype.__init213.call(this);_class19.prototype.__init214.call(this);_class19.prototype.__init215.call(this);_class19.prototype.__init216.call(this);_class19.prototype.__init217.call(this);_class19.prototype.__init218.call(this);;
8246
8534
  this.domElement = domElement;
8247
8535
  this.width = _nullishCoalesce(width, () => ( 0));
8248
8536
  this.height = _nullishCoalesce(height, () => ( 0));
@@ -8284,7 +8572,7 @@ var DOMPortalEntity = (_class19 = class extends _chunkSRXU3ZXKjs.Entity {
8284
8572
  ];
8285
8573
  for (const type of events) {
8286
8574
  const handler = (e) => {
8287
- this.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)(type, this, e));
8575
+ this.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)(type, this, e));
8288
8576
  };
8289
8577
  this.domElement.addEventListener(type, handler);
8290
8578
  this.domListeners.push({ type, handler, capture: false });
@@ -8295,7 +8583,7 @@ var DOMPortalEntity = (_class19 = class extends _chunkSRXU3ZXKjs.Entity {
8295
8583
  ];
8296
8584
  for (const { native, vecto } of hoverEvents) {
8297
8585
  const handler = (e) => {
8298
- this.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)(vecto, this, e, false));
8586
+ this.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)(vecto, this, e, false));
8299
8587
  };
8300
8588
  this.domElement.addEventListener(native, handler);
8301
8589
  this.domListeners.push({ type: native, handler, capture: false });
@@ -8303,7 +8591,7 @@ var DOMPortalEntity = (_class19 = class extends _chunkSRXU3ZXKjs.Entity {
8303
8591
  const focusEvents = ["focus", "blur"];
8304
8592
  for (const type of focusEvents) {
8305
8593
  const handler = (e) => {
8306
- this.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)(type, this, e, true));
8594
+ this.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)(type, this, e, true));
8307
8595
  };
8308
8596
  this.domElement.addEventListener(type, handler, true);
8309
8597
  this.domListeners.push({ type, handler, capture: true });
@@ -8354,8 +8642,11 @@ var DOMPortalEntity = (_class19 = class extends _chunkSRXU3ZXKjs.Entity {
8354
8642
  }, _class19);
8355
8643
 
8356
8644
  // src/index.ts
8357
- Scene.registerWebGLPointRendererCreator(_chunkZYYP2M5Djs.createWebGLPointRenderer);
8358
- Scene.registerWebGPUParticleSystemManager(_chunkZYYP2M5Djs.WebGPUParticleSystemManager);
8645
+ Scene.registerWebGLPointRendererCreator(_chunkKRUDTGNRjs.createWebGLPointRenderer);
8646
+ Scene.registerWebGPUParticleSystemManager(_chunkKRUDTGNRjs.WebGPUParticleSystemManager);
8647
+
8648
+
8649
+
8359
8650
 
8360
8651
 
8361
8652
 
@@ -8401,4 +8692,4 @@ Scene.registerWebGPUParticleSystemManager(_chunkZYYP2M5Djs.WebGPUParticleSystemM
8401
8692
 
8402
8693
 
8403
8694
 
8404
- exports.CanvasRenderer = _chunkZYYP2M5Djs.CanvasRenderer; exports.Circle = Circle; exports.ComputeParticleEntity = ComputeParticleEntity; exports.DEFAULT_CONTENT_SEMANTIC_BUDGET = DEFAULT_CONTENT_SEMANTIC_BUDGET; exports.DOMPortalEntity = DOMPortalEntity; exports.Entity = _chunkSRXU3ZXKjs.Entity; exports.GlyphRasterAtlas = _chunkZYYP2M5Djs.GlyphRasterAtlas; exports.GridTextEntity = GridTextEntity; exports.Group = Group; exports.MSDFTextEntity = _chunkSRXU3ZXKjs.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.SCENE_OPTION_KEYS = SCENE_OPTION_KEYS; exports.SVGEntity = _chunkSRXU3ZXKjs.SVGEntity; exports.SVGRenderer = _chunkZYYP2M5Djs.SVGRenderer; exports.Scene = Scene; exports.SplineEntity = SplineEntity; exports.TextEntity = TextEntity; exports.TextRasterCache = _chunkZYYP2M5Djs.TextRasterCache; exports.VECTO_USER_TIMING = VECTO_USER_TIMING; exports.VectoJSEvent = _chunkSRXU3ZXKjs.VectoJSEvent; exports.WebGPUParticleSystemManager = _chunkZYYP2M5Djs.WebGPUParticleSystemManager; exports.beginVectoUserTiming = beginVectoUserTiming; exports.contentLineInHint = _chunkSRXU3ZXKjs.contentLineInHint; exports.createWebGLPointRenderer = _chunkZYYP2M5Djs.createWebGLPointRenderer; exports.endVectoUserTiming = endVectoUserTiming; exports.installRendererDevTraps = _chunkZYYP2M5Djs.installRendererDevTraps; exports.isRendererDevMode = _chunkZYYP2M5Djs.isRendererDevMode; exports.isSafeUrl = _chunkZYYP2M5Djs.isSafeUrl; exports.loadSpline = loadSpline; exports.measureVectoUserTiming = measureVectoUserTiming; exports.parseColorToRGBA = _chunkZYYP2M5Djs.parseColorToRGBA; exports.polySegmentToBezier = polySegmentToBezier; exports.sanitizeUrl = _chunkZYYP2M5Djs.sanitizeUrl; exports.setRendererDevMode = _chunkZYYP2M5Djs.setRendererDevMode;
8695
+ exports.CanvasRenderer = _chunkKRUDTGNRjs.CanvasRenderer; exports.Circle = Circle; exports.ComputeParticleEntity = ComputeParticleEntity; exports.DEFAULT_CONTENT_SEMANTIC_BUDGET = DEFAULT_CONTENT_SEMANTIC_BUDGET; exports.DOMPortalEntity = DOMPortalEntity; exports.Entity = _chunkPOPXUPKRjs.Entity; exports.GlyphRasterAtlas = _chunkKRUDTGNRjs.GlyphRasterAtlas; exports.GridTextEntity = GridTextEntity; exports.Group = Group; exports.KEYBOARD_OWNING_ROLES = KEYBOARD_OWNING_ROLES; exports.MSDFTextEntity = _chunkPOPXUPKRjs.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.SCENE_OPTION_KEYS = SCENE_OPTION_KEYS; exports.SVGEntity = _chunkPOPXUPKRjs.SVGEntity; exports.SVGRenderer = _chunkKRUDTGNRjs.SVGRenderer; exports.Scene = Scene; exports.SplineEntity = SplineEntity; exports.TextEntity = TextEntity; exports.TextRasterCache = _chunkKRUDTGNRjs.TextRasterCache; exports.VECTO_USER_TIMING = VECTO_USER_TIMING; exports.VectoJSEvent = _chunkPOPXUPKRjs.VectoJSEvent; exports.WebGPUParticleSystemManager = _chunkKRUDTGNRjs.WebGPUParticleSystemManager; exports.beginVectoUserTiming = beginVectoUserTiming; exports.contentLineInHint = _chunkPOPXUPKRjs.contentLineInHint; exports.createWebGLPointRenderer = _chunkKRUDTGNRjs.createWebGLPointRenderer; exports.endVectoUserTiming = endVectoUserTiming; exports.installRendererDevTraps = _chunkKRUDTGNRjs.installRendererDevTraps; exports.isRendererDevMode = _chunkKRUDTGNRjs.isRendererDevMode; exports.isSafeUrl = _chunkKRUDTGNRjs.isSafeUrl; exports.loadSpline = loadSpline; exports.measureVectoUserTiming = measureVectoUserTiming; exports.normalizeChord = normalizeChord; exports.ownsKeyboard = ownsKeyboard; exports.parseColorToRGBA = _chunkKRUDTGNRjs.parseColorToRGBA; exports.polySegmentToBezier = polySegmentToBezier; exports.sanitizeUrl = _chunkKRUDTGNRjs.sanitizeUrl; exports.setRendererDevMode = _chunkKRUDTGNRjs.setRendererDevMode;