@vectojs/core 1.38.1 → 1.39.1

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 _chunkBKXERWADjs = require('./chunk-BKXERWAD.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;
@@ -1029,8 +1070,11 @@ var CanvasGeometry = (_class3 = class {
1029
1070
  /** Effective device pixel ratio, matching CanvasRenderer: real DPR clamped to
1030
1071
  * `maxDPR` when set. */
1031
1072
  effectiveDPR(maxDPR) {
1032
- const real = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
1033
- return maxDPR !== void 0 ? Math.min(real, maxDPR) : real;
1073
+ const raw = typeof window !== "undefined" ? window.devicePixelRatio : 1;
1074
+ const real = Number.isFinite(raw) && raw > 0 ? raw : 1;
1075
+ if (maxDPR === void 0) return real;
1076
+ if (!Number.isFinite(maxDPR) || maxDPR <= 0) return real;
1077
+ return Math.min(real, maxDPR);
1034
1078
  }
1035
1079
  /** Size the WebGPU particle canvas: backing store at logical × DPR, CSS box at
1036
1080
  * the logical size. Sizing the backing store in logical px (the old
@@ -1055,14 +1099,14 @@ var CanvasGeometry = (_class3 = class {
1055
1099
  syncOverlay(width, height, glCanvas, gpuCanvas) {
1056
1100
  const parent = this.canvas.parentElement;
1057
1101
  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;
1102
+ const canvasRect = _optionalChain([this, 'access', _46 => _46.canvas, 'access', _47 => _47.getBoundingClientRect, 'optionalCall', _48 => _48()]);
1103
+ const parentRect = _optionalChain([parent, 'access', _49 => _49.getBoundingClientRect, 'optionalCall', _50 => _50()]);
1104
+ const cssWidth = _optionalChain([canvasRect, 'optionalAccess', _51 => _51.width]) || this.canvas.clientWidth || width;
1105
+ const cssHeight = _optionalChain([canvasRect, 'optionalAccess', _52 => _52.height]) || this.canvas.clientHeight || height;
1062
1106
  const canvasPosition = typeof getComputedStyle === "function" ? getComputedStyle(this.canvas).position : "";
1063
1107
  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;
1108
+ 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;
1109
+ 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
1110
  const scaleX = width > 0 ? cssWidth / width : 1;
1067
1111
  const scaleY = height > 0 ? cssHeight / height : 1;
1068
1112
  const prev = this.overlayGeometry;
@@ -1100,12 +1144,12 @@ var CanvasGeometry = (_class3 = class {
1100
1144
  }, _class3);
1101
1145
 
1102
1146
  // 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); }
1147
+ var DirtyTracker = (_class4 = class _DirtyTracker {constructor() { _class4.prototype.__init21.call(this);_class4.prototype.__init22.call(this);_class4.prototype.__init23.call(this); }
1104
1148
  /** Cap on distinct recorded dirty reasons (see {@link record}). */
1105
1149
  static __initStatic() {this.MAX_DIRTY_REASONS = 200}
1106
- __init17() {this.tracking = false}
1107
- __init18() {this.reasons = /* @__PURE__ */ new Map()}
1108
- __init19() {this.isDirty = true}
1150
+ __init21() {this.tracking = false}
1151
+ __init22() {this.reasons = /* @__PURE__ */ new Map()}
1152
+ __init23() {this.isDirty = true}
1109
1153
  /** Whether the next frame must redraw. */
1110
1154
  get dirty() {
1111
1155
  return this.isDirty;
@@ -1187,20 +1231,20 @@ var DriverTicker = (_class5 = class {
1187
1231
  // an entity whose drivers have since all completed or been removed. This is
1188
1232
  // what lets the batch pass find its candidates in O(active drivers), not
1189
1233
  // O(tree size) — the exact mistake G3's first integrated benchmark made.
1190
- __init20() {this.activeEntities = /* @__PURE__ */ new Set()}
1234
+ __init24() {this.activeEntities = /* @__PURE__ */ new Set()}
1191
1235
  // Reused across frames instead of allocating a fresh array + N {entity,prop,
1192
1236
  // driver} objects every call — the integrated benchmark
1193
1237
  // (benchmarks/anim-wasm-scene) found that allocation churn was the
1194
1238
  // dominant integrated cost, not the wasm kernel itself. Parallel arrays,
1195
1239
  // truncated to the live count after each use so a stale tail slot never
1196
1240
  // 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);
1241
+ __init25() {this.springEntities = []}
1242
+ __init26() {this.springProps = []}
1243
+ __init27() {this.springDrivers = []}
1244
+ __init28() {this.tweenEntities = []}
1245
+ __init29() {this.tweenProps = []}
1246
+ __init30() {this.tweenDrivers = []}
1247
+ 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
1248
  this.backends = backends;
1205
1249
  }
1206
1250
  /** The candidate set, for the delegating accessor `Scene` keeps for its tests. */
@@ -1328,6 +1372,8 @@ var DriverTicker = (_class5 = class {
1328
1372
  tD.length = tweenCount;
1329
1373
  backend.ensure(springCount, tweenCount);
1330
1374
  backend.revalidateViews();
1375
+ let springsRejected = false;
1376
+ let tweensRejected = false;
1331
1377
  if (springCount > 0) {
1332
1378
  const sv = backend.springView();
1333
1379
  for (let i = 0; i < springCount; i++) {
@@ -1343,8 +1389,7 @@ var DriverTicker = (_class5 = class {
1343
1389
  for (let i = 0; i < springCount; i++) sD[i].syncExternal(sv.val[i], sv.vel[i]);
1344
1390
  } else {
1345
1391
  for (let i = 0; i < springCount; i++) sD[i].tick(dt);
1346
- this.backends.animReason = "rejected";
1347
- this.backends.animBatchedLastFrame = false;
1392
+ springsRejected = true;
1348
1393
  }
1349
1394
  }
1350
1395
  if (tweenCount > 0) {
@@ -1362,10 +1407,13 @@ var DriverTicker = (_class5 = class {
1362
1407
  for (let i = 0; i < tweenCount; i++) tD[i].syncExternal(tv.val[i], tv.elapsed[i]);
1363
1408
  } else {
1364
1409
  for (let i = 0; i < tweenCount; i++) tD[i].tick(dt);
1365
- this.backends.animReason = "rejected";
1366
- this.backends.animBatchedLastFrame = false;
1410
+ tweensRejected = true;
1367
1411
  }
1368
1412
  }
1413
+ if (springsRejected || tweensRejected) {
1414
+ this.backends.animReason = springsRejected && tweensRejected ? "rejected" : springsRejected ? "springs-rejected" : "tweens-rejected";
1415
+ this.backends.animBatchedLastFrame = springCount > 0 && !springsRejected || tweenCount > 0 && !tweensRejected;
1416
+ }
1369
1417
  for (let i = 0; i < springCount; i++) sE[i]._applyDriverTick(sP[i], sD[i]);
1370
1418
  for (let i = 0; i < tweenCount; i++) tE[i]._applyDriverTick(tP[i], tD[i]);
1371
1419
  }
@@ -1526,20 +1574,17 @@ function intersectBounds(a, b) {
1526
1574
  height: Math.max(0, bottom - y)
1527
1575
  };
1528
1576
  }
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
1577
  var HitTester = (_class6 = class {
1533
1578
 
1534
1579
 
1535
1580
 
1536
1581
  /** Slot index → entity, as built by the most recent grid build. */
1537
- __init27() {this.slotEntity = []}
1582
+ __init31() {this.slotEntity = []}
1538
1583
  /** Entities with no `getBounds()`, which the grid cannot index. */
1539
- __init28() {this.boundless = []}
1584
+ __init32() {this.boundless = []}
1540
1585
  /** 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);
1586
+ __init33() {this.gatherBuffer = null}
1587
+ constructor(root, overlayRoot, backends) {;_class6.prototype.__init31.call(this);_class6.prototype.__init32.call(this);_class6.prototype.__init33.call(this);
1543
1588
  this.root = root;
1544
1589
  this.overlayRoot = overlayRoot;
1545
1590
  this.backends = backends;
@@ -1575,7 +1620,7 @@ var HitTester = (_class6 = class {
1575
1620
  this.backends.hitReason = "not-installed";
1576
1621
  return false;
1577
1622
  }
1578
- if (this.backends.hitGridFrame === frame) {
1623
+ if (this.backends.hitGridFrame === frame && this.backends.hitGridStructureVersion === this.backends.structureVersion) {
1579
1624
  return this.backends.hitGridOk;
1580
1625
  }
1581
1626
  let gathered = null;
@@ -1606,6 +1651,7 @@ var HitTester = (_class6 = class {
1606
1651
  this.slotEntity = gathered.slotEntity;
1607
1652
  this.boundless = gathered.boundless;
1608
1653
  this.backends.hitGridFrame = frame;
1654
+ this.backends.hitGridStructureVersion = this.backends.structureVersion;
1609
1655
  this.backends.hitGridOk = ok;
1610
1656
  this.backends.hitReason = ok ? "active" : "rejected";
1611
1657
  return ok;
@@ -1630,7 +1676,7 @@ var HitTester = (_class6 = class {
1630
1676
  const idx = cell[k];
1631
1677
  if (x < minx[idx] || x > maxx[idx] || y < miny[idx] || y > maxy[idx]) continue;
1632
1678
  const entity = this.slotEntity[idx];
1633
- if (_optionalChain([entity, 'optionalAccess', _58 => _58.isPointInside, 'call', _59 => _59(x, y)]) && this.isHitEligible(entity, x, y)) {
1679
+ if (_optionalChain([entity, 'optionalAccess', _59 => _59.isPointInside, 'call', _60 => _60(x, y)]) && this.isHitEligible(entity, x, y)) {
1634
1680
  bestIndex = idx;
1635
1681
  bestEntity = entity;
1636
1682
  break;
@@ -1656,7 +1702,7 @@ var HitTester = (_class6 = class {
1656
1702
  const hit = this.findHitRecursively(node.children[i], x, y, childClip);
1657
1703
  if (hit) return hit;
1658
1704
  }
1659
- if (node.isPointInside && node.isPointInside(x, y) && (!clip || pointInBounds(clip, x, y)) && !this.isPointerTransparent(node)) {
1705
+ if (node.isPointInside && node.isPointInside(x, y) && this.isInsideAllClippers(node, x, y) && !this.isPointerTransparent(node)) {
1660
1706
  return node;
1661
1707
  }
1662
1708
  return null;
@@ -1668,6 +1714,25 @@ var HitTester = (_class6 = class {
1668
1714
  const attrs = node.getA11yAttributes();
1669
1715
  return attrs.disabled === true || attrs.pointerEvents === "none";
1670
1716
  }
1717
+ /**
1718
+ * Exact rotation-aware `clipChildren` test shared by BOTH hit paths: the
1719
+ * point must lie inside every clipChildren ancestor's local rect — the same
1720
+ * shape rendering clips to — not merely inside the ancestor's world AABB.
1721
+ * For a rotated clipper those disagree, and until both paths used the exact
1722
+ * rect a query's answer depended on which backend was active (#680). The
1723
+ * clip-stack AABB intersection in {@link findHitRecursively} remains purely
1724
+ * as a subtree-pruning pre-filter; this is the authoritative gate.
1725
+ */
1726
+ isInsideAllClippers(node, x, y) {
1727
+ for (let ancestor = node.parent; ancestor; ancestor = ancestor.parent) {
1728
+ if (!ancestor.clipChildren) continue;
1729
+ const local = ancestor.worldToLocal(x, y);
1730
+ if (!local || local.x < 0 || local.y < 0 || local.x > ancestor.width || local.y > ancestor.height) {
1731
+ return false;
1732
+ }
1733
+ }
1734
+ return true;
1735
+ }
1671
1736
  /**
1672
1737
  * Whether a confirmed geometric hit on `node` at world `(x, y)` is a REAL hit,
1673
1738
  * applying the same visibility/input gating as {@link findHitRecursively} but
@@ -1682,14 +1747,8 @@ var HitTester = (_class6 = class {
1682
1747
  if (node.opacity <= 0) return false;
1683
1748
  for (let ancestor = node.parent; ancestor; ancestor = ancestor.parent) {
1684
1749
  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
1750
  }
1692
- return true;
1751
+ return this.isInsideAllClippers(node, x, y);
1693
1752
  }
1694
1753
  }, _class6);
1695
1754
 
@@ -1709,9 +1768,9 @@ var ContentProjectionManager = (_class7 = class {
1709
1768
  * Mid-drag the browser is authoritative, so a rebuild must not try to preserve
1710
1769
  * anything.
1711
1770
  */
1712
- __init30() {this.blankRegionDrag = false}
1771
+ __init34() {this.blankRegionDrag = false}
1713
1772
  /** Tracked drag anchor. Survives a drag, unlike the live DOM selection. */
1714
- __init31() {this.anchor = null}
1773
+ __init35() {this.anchor = null}
1715
1774
  /**
1716
1775
  * Memo for {@link selectionPresent}, valid for one sync walk.
1717
1776
  *
@@ -1719,20 +1778,20 @@ var ContentProjectionManager = (_class7 = class {
1719
1778
  * is resolved once per walk rather than once per rebuilt element. `null` means
1720
1779
  * "not yet asked this walk".
1721
1780
  */
1722
- __init32() {this.presentThisSync = null}
1781
+ __init36() {this.presentThisSync = null}
1723
1782
  // --- grid calibration ------------------------------------------------------
1724
1783
  /** Pending calibration rAF handle per projected grid entity. */
1725
- __init33() {this.calibrationFrameHandles = /* @__PURE__ */ new Map()}
1784
+ __init37() {this.calibrationFrameHandles = /* @__PURE__ */ new Map()}
1726
1785
  /** Detached, untransformed font probes used by the cold calibration pass. */
1727
- __init34() {this.calibrationProbes = /* @__PURE__ */ new Map()}
1786
+ __init38() {this.calibrationProbes = /* @__PURE__ */ new Map()}
1728
1787
  /**
1729
1788
  * Bumped when the conditions a measurement depends on change, which
1730
1789
  * invalidates every existing per-cell `scaleX` at once without touching them.
1731
1790
  */
1732
- __init35() {this.calibrationGeneration = 0}
1791
+ __init39() {this.calibrationGeneration = 0}
1733
1792
  /** 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);
1793
+ __init40() {this.calibrationStamp = ""}
1794
+ 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
1795
  this.a11yRoot = a11yRoot;
1737
1796
  this.phases = phases;
1738
1797
  }
@@ -1786,16 +1845,16 @@ var ContentProjectionManager = (_class7 = class {
1786
1845
  * to the live DOM selection.
1787
1846
  */
1788
1847
  gridSelectionLine(el) {
1789
- const candidates = [_optionalChain([this, 'access', _60 => _60.anchor, 'optionalAccess', _61 => _61.node])];
1848
+ const candidates = [_optionalChain([this, 'access', _61 => _61.anchor, 'optionalAccess', _62 => _62.node])];
1790
1849
  if (typeof window !== "undefined" && typeof window.getSelection === "function") {
1791
1850
  const selection = window.getSelection();
1792
- candidates.push(_optionalChain([selection, 'optionalAccess', _62 => _62.anchorNode]), _optionalChain([selection, 'optionalAccess', _63 => _63.focusNode]));
1851
+ candidates.push(_optionalChain([selection, 'optionalAccess', _63 => _63.anchorNode]), _optionalChain([selection, 'optionalAccess', _64 => _64.focusNode]));
1793
1852
  }
1794
1853
  for (const candidate of candidates) {
1795
1854
  if (!candidate || !el.contains(candidate)) continue;
1796
1855
  let cursor = candidate;
1797
1856
  while (cursor && cursor.parentNode !== el) cursor = cursor.parentNode;
1798
- const lineIndex = _optionalChain([cursor, 'optionalAccess', _64 => _64.dataset, 'optionalAccess', _65 => _65.vectoGridLine]);
1857
+ const lineIndex = _optionalChain([cursor, 'optionalAccess', _65 => _65.dataset, 'optionalAccess', _66 => _66.vectoGridLine]);
1799
1858
  if (lineIndex !== void 0) return Number(lineIndex);
1800
1859
  }
1801
1860
  return null;
@@ -1820,10 +1879,10 @@ var ContentProjectionManager = (_class7 = class {
1820
1879
  releaseSelectionForRebuild(el) {
1821
1880
  if (!this.anchor && !this.selectionPresent()) return;
1822
1881
  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);
1882
+ 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
1883
  if (!ownsSelection) return;
1825
1884
  this.endDrag();
1826
- _optionalChain([selection, 'optionalAccess', _68 => _68.removeAllRanges, 'call', _69 => _69()]);
1885
+ _optionalChain([selection, 'optionalAccess', _69 => _69.removeAllRanges, 'call', _70 => _70()]);
1827
1886
  this.presentThisSync = null;
1828
1887
  }
1829
1888
  /**
@@ -1898,7 +1957,7 @@ var ContentProjectionManager = (_class7 = class {
1898
1957
  let cursor = node;
1899
1958
  while (cursor && cursor !== el) {
1900
1959
  const cell = cursor;
1901
- const start = _optionalChain([cell, 'access', _70 => _70.dataset, 'optionalAccess', _71 => _71.vectoGridSourceStart]);
1960
+ const start = _optionalChain([cell, 'access', _71 => _71.dataset, 'optionalAccess', _72 => _72.vectoGridSourceStart]);
1902
1961
  if (start !== void 0) {
1903
1962
  const sourceStart = Number(start);
1904
1963
  const sourceLength = Number(_nullishCoalesce(cell.dataset.vectoGridSourceLength, () => ( 0)));
@@ -1992,7 +2051,7 @@ var ContentProjectionManager = (_class7 = class {
1992
2051
  cancelAnimationFrame(calibrationFrame);
1993
2052
  }
1994
2053
  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()]);
2054
+ _optionalChain([this, 'access', _73 => _73.calibrationProbes, 'access', _74 => _74.get, 'call', _75 => _75(entityId), 'optionalAccess', _76 => _76.remove, 'call', _77 => _77()]);
1996
2055
  this.calibrationProbes.delete(entityId);
1997
2056
  delete el.dataset.vectoGridCalibrationPending;
1998
2057
  delete el.dataset.vectoGridCalibration;
@@ -2056,7 +2115,7 @@ var ContentProjectionManager = (_class7 = class {
2056
2115
  if (previous !== void 0 && typeof cancelAnimationFrame === "function") {
2057
2116
  cancelAnimationFrame(previous);
2058
2117
  }
2059
- _optionalChain([this, 'access', _77 => _77.calibrationProbes, 'access', _78 => _78.get, 'call', _79 => _79(entityId), 'optionalAccess', _80 => _80.remove, 'call', _81 => _81()]);
2118
+ _optionalChain([this, 'access', _78 => _78.calibrationProbes, 'access', _79 => _79.get, 'call', _80 => _80(entityId), 'optionalAccess', _81 => _81.remove, 'call', _82 => _82()]);
2060
2119
  this.calibrationProbes.delete(entityId);
2061
2120
  const calibrationStart = typeof performance !== "undefined" ? performance.now() : 0;
2062
2121
  const probe = document.createElement("div");
@@ -2090,7 +2149,7 @@ var ContentProjectionManager = (_class7 = class {
2090
2149
  target.dataset.vectoGridCalib = generation;
2091
2150
  continue;
2092
2151
  }
2093
- const sourceText = _nullishCoalesce(_optionalChain([target, 'access', _82 => _82.textContent, 'optionalAccess', _83 => _83.slice, 'call', _84 => _84(0, sourceLength)]), () => ( ""));
2152
+ const sourceText = _nullishCoalesce(_optionalChain([target, 'access', _83 => _83.textContent, 'optionalAccess', _84 => _84.slice, 'call', _85 => _85(0, sourceLength)]), () => ( ""));
2094
2153
  if (!sourceText) {
2095
2154
  target.dataset.vectoGridCalib = generation;
2096
2155
  continue;
@@ -2201,17 +2260,17 @@ var ContentProjectionManager = (_class7 = class {
2201
2260
  }, _class7);
2202
2261
 
2203
2262
  // 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); }
2263
+ var PhaseTimer = (_class8 = class {constructor() { _class8.prototype.__init41.call(this);_class8.prototype.__init42.call(this);_class8.prototype.__init43.call(this); }
2205
2264
  /**
2206
2265
  * Whether per-phase timing is being recorded.
2207
2266
  *
2208
2267
  * A field rather than an accessor: it is tested on the frame path once per
2209
2268
  * probe, and the disabled cost has to be a single boolean read.
2210
2269
  */
2211
- __init37() {this.enabled = false}
2270
+ __init41() {this.enabled = false}
2212
2271
  /** Whether browser User Timing instrumentation is enabled. */
2213
- __init38() {this.userTiming = false}
2214
- __init39() {this.totals = /* @__PURE__ */ new Map()}
2272
+ __init42() {this.userTiming = false}
2273
+ __init43() {this.totals = /* @__PURE__ */ new Map()}
2215
2274
  /** Start or stop recording. Disabling also drops what was collected. */
2216
2275
  setEnabled(enabled) {
2217
2276
  this.enabled = enabled;
@@ -2389,10 +2448,10 @@ function viewsStale(cap, probe, memory) {
2389
2448
  }
2390
2449
  var PAD2 = 8;
2391
2450
  var WasmTransformBackend = (_class9 = class {
2392
- __init40() {this.available = true}
2451
+ __init44() {this.available = true}
2393
2452
 
2394
- __init41() {this.cap = 0}
2395
- __init42() {this.runCap = 0}
2453
+ __init45() {this.cap = 0}
2454
+ __init46() {this.runCap = 0}
2396
2455
  // Views over wasm linear memory, valid until the next init().
2397
2456
 
2398
2457
 
@@ -2419,7 +2478,7 @@ var WasmTransformBackend = (_class9 = class {
2419
2478
 
2420
2479
 
2421
2480
 
2422
- constructor(instance) {;_class9.prototype.__init40.call(this);_class9.prototype.__init41.call(this);_class9.prototype.__init42.call(this);_class9.prototype.__init43.call(this);
2481
+ constructor(instance) {;_class9.prototype.__init44.call(this);_class9.prototype.__init45.call(this);_class9.prototype.__init46.call(this);_class9.prototype.__init47.call(this);
2423
2482
  this.ex = instance.exports;
2424
2483
  }
2425
2484
  /** Compose world matrices for `store` in WASM, writing back into its
@@ -2442,7 +2501,8 @@ var WasmTransformBackend = (_class9 = class {
2442
2501
  this.lastStatus = WASM_STATUS.CAPACITY;
2443
2502
  return;
2444
2503
  }
2445
- const status = kernel === "scalar" ? this.ex.compose_scalar() : this.ex.compose_simd();
2504
+ const simd = kernel === "simd" && typeof this.ex.compose_simd === "function";
2505
+ const status = simd ? this.ex.compose_simd() : this.ex.compose_scalar();
2446
2506
  this.lastStatus = status;
2447
2507
  if (status !== WASM_STATUS.OK) return;
2448
2508
  store.wa.set(this.vwa.subarray(0, n));
@@ -2464,7 +2524,8 @@ var WasmTransformBackend = (_class9 = class {
2464
2524
  * {@link uploadRuns} after a topology change.
2465
2525
  */
2466
2526
  runKernel(kernel = "simd") {
2467
- const status = kernel === "scalar" ? this.ex.compose_scalar() : this.ex.compose_simd();
2527
+ const simd = kernel === "simd" && typeof this.ex.compose_simd === "function";
2528
+ const status = simd ? this.ex.compose_simd() : this.ex.compose_scalar();
2468
2529
  this.lastStatus = status;
2469
2530
  return status;
2470
2531
  }
@@ -2486,7 +2547,7 @@ var WasmTransformBackend = (_class9 = class {
2486
2547
  * Status of the most recent kernel or run-table call. `WASM_STATUS.OK` unless
2487
2548
  * the crate rejected its arguments, in which case that call was a no-op.
2488
2549
  */
2489
- __init43() {this.lastStatus = WASM_STATUS.OK}
2550
+ __init47() {this.lastStatus = WASM_STATUS.OK}
2490
2551
  /** The resident wasm input views (`x,y,sx,sy,cos,sin,opacity`), valid until
2491
2552
  * the next capacity growth. Writing here is what makes uploads unnecessary. */
2492
2553
  inputView() {
@@ -2516,21 +2577,21 @@ var WasmTransformBackend = (_class9 = class {
2516
2577
  /**
2517
2578
  * Compute world-space AABBs for `store` in WASM (G1+), writing back into its
2518
2579
  * `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.
2580
+ * pass, reads results back. Result is bit-identical to `computeAabbsJS(store)`
2581
+ * for both kernels. `compose` (or `runKernel`) must have populated the world
2582
+ * matrices first — this pass reads them. For the resident (no-copy)
2583
+ * integration, write bounds via {@link boundsView} and read via
2584
+ * {@link aabbView} + call {@link runAabbs} instead.
2524
2585
  */
2525
- computeAabbs(store) {
2586
+ computeAabbs(store, kernel = "simd") {
2526
2587
  this.ensure(store.count, store.runCount);
2527
2588
  const n = store.count;
2528
2589
  this.vbx.set(store.bx.subarray(0, n));
2529
2590
  this.vby.set(store.by.subarray(0, n));
2530
2591
  this.vbw.set(store.bw.subarray(0, n));
2531
2592
  this.vbh.set(store.bh.subarray(0, n));
2532
- this.lastStatus = this.ex.compute_aabbs(n);
2533
- if (this.lastStatus !== WASM_STATUS.OK) return;
2593
+ const ok = this.runAabbs(n, kernel);
2594
+ if (!ok) return;
2534
2595
  store.aminx.set(this.vaminx.subarray(0, n));
2535
2596
  store.aminy.set(this.vaminy.subarray(0, n));
2536
2597
  store.amaxx.set(this.vamaxx.subarray(0, n));
@@ -2541,8 +2602,9 @@ var WasmTransformBackend = (_class9 = class {
2541
2602
  * composed). No upload/readback — the per-frame resident path. Returns
2542
2603
  * `false` if the kernel rejected `count` (beyond capacity, or uninitialized),
2543
2604
  * in which case {@link aabbView} still holds the previous frame's bounds. */
2544
- runAabbs(count) {
2545
- this.lastStatus = this.ex.compute_aabbs(count);
2605
+ runAabbs(count, kernel = "simd") {
2606
+ const simd = kernel === "simd" && typeof this.ex.compute_aabbs_simd === "function";
2607
+ this.lastStatus = simd ? this.ex.compute_aabbs_simd(count) : this.ex.compute_aabbs(count);
2546
2608
  return this.lastStatus === WASM_STATUS.OK;
2547
2609
  }
2548
2610
  /** Resident wasm local-bounds input views (`bx,by,bw,bh`) for the AABB pass. */
@@ -2627,11 +2689,11 @@ var WasmTransformBackend = (_class9 = class {
2627
2689
  var PAD3 = 8;
2628
2690
  var AnimBackend = (_class10 = class {
2629
2691
 
2630
- __init44() {this.springCap = 0}
2631
- __init45() {this.tweenCap = 0}
2692
+ __init48() {this.springCap = 0}
2693
+ __init49() {this.tweenCap = 0}
2632
2694
 
2633
2695
 
2634
- constructor(instance) {;_class10.prototype.__init44.call(this);_class10.prototype.__init45.call(this);_class10.prototype.__init46.call(this);
2696
+ constructor(instance) {;_class10.prototype.__init48.call(this);_class10.prototype.__init49.call(this);_class10.prototype.__init50.call(this);
2635
2697
  this.ex = instance.exports;
2636
2698
  }
2637
2699
  /** The resident spring SoA input/output views, valid until the next capacity
@@ -2698,7 +2760,7 @@ var AnimBackend = (_class10 = class {
2698
2760
  * Status of the most recent kernel call — `WASM_STATUS.OK` unless the kernel
2699
2761
  * declined it. Mirrors {@link TransformBackend.lastStatus}.
2700
2762
  */
2701
- __init46() {this.lastStatus = WASM_STATUS.OK}
2763
+ __init50() {this.lastStatus = WASM_STATUS.OK}
2702
2764
  refreshViews() {
2703
2765
  const buf = this.ex.memory.buffer;
2704
2766
  const sCap = this.springCap;
@@ -2728,9 +2790,9 @@ var PAD4 = 8;
2728
2790
  var CELLS_PER_ENTITY_HINT = 4;
2729
2791
  var HitTestBackend = (_class11 = class {
2730
2792
 
2731
- __init47() {this.entityCap = 0}
2732
- __init48() {this.cellCap = 0}
2733
- __init49() {this.itemCap = 0}
2793
+ __init51() {this.entityCap = 0}
2794
+ __init52() {this.cellCap = 0}
2795
+ __init53() {this.itemCap = 0}
2734
2796
 
2735
2797
 
2736
2798
 
@@ -2739,10 +2801,10 @@ var HitTestBackend = (_class11 = class {
2739
2801
 
2740
2802
 
2741
2803
  /** 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);
2804
+ __init54() {this.gridW = 0}
2805
+ __init55() {this.gridH = 0}
2806
+ __init56() {this.cellSize = 64}
2807
+ 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
2808
  this.ex = instance.exports;
2747
2809
  }
2748
2810
  /** The resident AABB input views (`minx/miny/maxx/maxy`), valid until the
@@ -2843,9 +2905,9 @@ var HitTestBackend = (_class11 = class {
2843
2905
  var PAD5 = 8;
2844
2906
  var ParticleBackend = (_class12 = class {
2845
2907
 
2846
- __init53() {this.cap = 0}
2908
+ __init57() {this.cap = 0}
2847
2909
 
2848
- constructor(instance) {;_class12.prototype.__init53.call(this);_class12.prototype.__init54.call(this);
2910
+ constructor(instance) {;_class12.prototype.__init57.call(this);_class12.prototype.__init58.call(this);
2849
2911
  this.ex = instance.exports;
2850
2912
  }
2851
2913
  /** The resident SoA views, valid until the next capacity growth. */
@@ -2916,7 +2978,7 @@ var ParticleBackend = (_class12 = class {
2916
2978
  * Status of the most recent {@link step} — `WASM_STATUS.OK` unless the kernel
2917
2979
  * declined it. Mirrors {@link TransformBackend.lastStatus}.
2918
2980
  */
2919
- __init54() {this.lastStatus = WASM_STATUS.OK}
2981
+ __init58() {this.lastStatus = WASM_STATUS.OK}
2920
2982
  /** Transpose the AoS stride-8 buffer into the SoA views (position/velocity/
2921
2983
  * life every frame; origin upload-once when `withOrigin`). */
2922
2984
  gather(data, count, withOrigin) {
@@ -3007,11 +3069,11 @@ async function loadCoreWasmModule(source) {
3007
3069
  }
3008
3070
  var CoreWasmRuntime = (_class13 = class {
3009
3071
 
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);
3072
+ __init59() {this.transformBackend = null}
3073
+ __init60() {this.animBackend = null}
3074
+ __init61() {this.hitBackend = null}
3075
+ __init62() {this.particleBackendInstance = null}
3076
+ constructor(instance) {;_class13.prototype.__init59.call(this);_class13.prototype.__init60.call(this);_class13.prototype.__init61.call(this);_class13.prototype.__init62.call(this);
3015
3077
  this.instance = instance;
3016
3078
  }
3017
3079
  /**
@@ -3063,7 +3125,7 @@ var WasmBackendFacade = (_class14 = class {
3063
3125
  * path, so the overlay root is not needed here.
3064
3126
  */
3065
3127
 
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);
3128
+ 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
3129
  this.root = root;
3068
3130
  }
3069
3131
  // ── WASM transform backend (invisible accelerator) ──────────────────────────
@@ -3073,34 +3135,34 @@ var WasmBackendFacade = (_class14 = class {
3073
3135
  // default: a null backend, a non-main renderer, or any entity absent from the
3074
3136
  // store all fall back to the JS composition, so WASM can only ever change
3075
3137
  // *how fast* a world matrix is produced, never *what* it is.
3076
- __init59() {this._transform = null}
3077
- __init60() {this._mode = "js"}
3138
+ __init63() {this._transform = null}
3139
+ __init64() {this._mode = "js"}
3078
3140
  // Resident store state (Stage 3). The store layout — slot assignment + sibling
3079
3141
  // runs — depends only on tree TOPOLOGY, so it is rebuilt only when the
3080
3142
  // structure changes (add/remove/reparent bump `structureVersion`). Between
3081
3143
  // rebuilds the per-frame cost is: gather each entity's transform into the
3082
3144
  // resident wasm input view + run the kernel — no reallocation, no readback.
3083
- __init61() {this._treeStore = null}
3084
- __init62() {this._slotEntity = []}
3145
+ __init65() {this._treeStore = null}
3146
+ __init66() {this._slotEntity = []}
3085
3147
  // 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}
3148
+ __init67() {this._inputs = null}
3149
+ __init68() {this._world = null}
3150
+ __init69() {this._structureVersion = 0}
3151
+ __init70() {this._storeStructureVersion = -1}
3090
3152
  /**
3091
3153
  * Consecutive `uploadRuns` rejections. Reset by any success and by
3092
3154
  * {@link setTransform}; at {@link WASM_UPLOAD_REJECT_LIMIT} the backend mode
3093
3155
  * flips to `'js'` permanently.
3094
3156
  */
3095
- __init67() {this._uploadRejections = 0}
3157
+ __init71() {this._uploadRejections = 0}
3096
3158
  /** Latch for the permanent-fallback warning, which fires from a per-frame path. */
3097
- __init68() {this.hasWarnedUploadFallback = false}
3159
+ __init72() {this.hasWarnedUploadFallback = false}
3098
3160
  /**
3099
3161
  * Whether `compute_aabbs` has run against the current frame's world matrices.
3100
3162
  * The AABB pass is only meaningful after a `compose_*`, so the fused gather
3101
3163
  * must not read the views before then.
3102
3164
  */
3103
- __init69() {this._aabbsFresh = false}
3165
+ __init73() {this._aabbsFresh = false}
3104
3166
  /** The installed transform backend, or `null` on the JS path. */
3105
3167
  get transform() {
3106
3168
  return this._transform;
@@ -3165,7 +3227,7 @@ var WasmBackendFacade = (_class14 = class {
3165
3227
  * cached globally; the instance is per-Scene, which is the isolation that
3166
3228
  * actually matters.
3167
3229
  */
3168
- __init70() {this._runtime = null}
3230
+ __init74() {this._runtime = null}
3169
3231
  /** The shared WASM runtime, if one has been loaded. */
3170
3232
  get runtime() {
3171
3233
  return this._runtime;
@@ -3194,9 +3256,9 @@ var WasmBackendFacade = (_class14 = class {
3194
3256
  return runtime;
3195
3257
  }
3196
3258
  // ── The other three backend handles ─────────────────────────────────────────
3197
- __init71() {this._hit = null}
3198
- __init72() {this._anim = null}
3199
- __init73() {this._particle = null}
3259
+ __init75() {this._hit = null}
3260
+ __init76() {this._anim = null}
3261
+ __init77() {this._particle = null}
3200
3262
  /** The installed hit-test backend, or `null` for the JS depth-first walk. */
3201
3263
  get hit() {
3202
3264
  return this._hit;
@@ -3205,6 +3267,7 @@ var WasmBackendFacade = (_class14 = class {
3205
3267
  setHit(backend) {
3206
3268
  this._hit = backend;
3207
3269
  this.hitGridFrame = -1;
3270
+ this.hitGridStructureVersion = -1;
3208
3271
  this.hitReason = backend ? "not-applicable" : "not-installed";
3209
3272
  }
3210
3273
  /** The installed batched-animation backend, or `null` for the JS tick. */
@@ -3231,9 +3294,9 @@ var WasmBackendFacade = (_class14 = class {
3231
3294
  * Why the transform accelerator did or did not run on the most recent frame.
3232
3295
  * Written by the render walk and {@link syncStore}.
3233
3296
  */
3234
- __init74() {this.transformReason = "not-installed"}
3297
+ __init78() {this.transformReason = "not-installed"}
3235
3298
  /** Why the batched-driver accelerator did or did not run. */
3236
- __init75() {this.animReason = "not-installed"}
3299
+ __init79() {this.animReason = "not-installed"}
3237
3300
  /**
3238
3301
  * Why the hit-test accelerator did or did not serve the last pointer query.
3239
3302
  * The grid is built lazily on demand, not every frame, so this describes the
@@ -3241,28 +3304,35 @@ var WasmBackendFacade = (_class14 = class {
3241
3304
  * before a backend exists; the grid build moves it to `'not-applicable'` once
3242
3305
  * one is installed but nothing has queried yet.
3243
3306
  */
3244
- __init76() {this.hitReason = "not-installed"}
3307
+ __init80() {this.hitReason = "not-installed"}
3245
3308
  /** Why the particle accelerator did or did not run. */
3246
- __init77() {this.particleReason = "not-applicable"}
3309
+ __init81() {this.particleReason = "not-applicable"}
3247
3310
  /** Which particle implementation actually simulated the most recent frame. */
3248
- __init78() {this.particlePath = "none"}
3311
+ __init82() {this.particlePath = "none"}
3249
3312
  /**
3250
3313
  * Whether the last grid build sourced its AABBs from the WASM transform store
3251
3314
  * rather than recomputing them in JS. Diagnostic only — both paths must
3252
3315
  * produce the same entity for a given point.
3253
3316
  */
3254
- __init79() {this.hitFusedGather = false}
3317
+ __init83() {this.hitFusedGather = false}
3255
3318
  /** Whether the WASM batch path actually ran on the most recent frame. */
3256
- __init80() {this.animBatchedLastFrame = false}
3319
+ __init84() {this.animBatchedLastFrame = false}
3257
3320
  // Hit-grid cache key: which frame + structure version the grid was last
3258
3321
  // (successfully, non-overflowing) built for. Owned here rather than by
3259
3322
  // `HitTester` because {@link setHit} has to invalidate it, and a backend swap
3260
3323
  // is this facade's job. The grid contents themselves stay with the hit-test
3261
3324
  // domain.
3262
3325
  /** Frame the hit grid was last built for; `-1` forces a rebuild. */
3263
- __init81() {this.hitGridFrame = -1}
3326
+ __init85() {this.hitGridFrame = -1}
3327
+ /**
3328
+ * Structure version the hit grid was built for; `-1` forces a rebuild.
3329
+ * Half of the cache key the comment above promises: without it, a pointer
3330
+ * query in the same frame as a structural mutation would resolve against
3331
+ * pre-mutation geometry while the JS walk sees live state.
3332
+ */
3333
+ __init86() {this.hitGridStructureVersion = -1}
3264
3334
  /** Whether that build succeeded (did not overflow its item budget). */
3265
- __init82() {this.hitGridOk = false}
3335
+ __init87() {this.hitGridOk = false}
3266
3336
  /**
3267
3337
  * Per-frame status of every invisible accelerator: whether each is installed,
3268
3338
  * whether it actually ran on the most recent frame, and why.
@@ -3400,6 +3470,38 @@ var WasmBackendFacade = (_class14 = class {
3400
3470
  }
3401
3471
  }, _class14);
3402
3472
 
3473
+ // src/tree/scene/keyboard.ts
3474
+ function normalizeChord(input) {
3475
+ if (typeof input === "string") {
3476
+ const parts = input.split("+").map((p) => p.trim()).filter(Boolean);
3477
+ const mods = /* @__PURE__ */ new Set();
3478
+ let key2 = "";
3479
+ for (const p of parts) {
3480
+ const low = p.toLowerCase();
3481
+ if (low === "ctrl" || low === "control") mods.add("Control");
3482
+ else if (low === "alt") mods.add("Alt");
3483
+ else if (low === "shift") mods.add("Shift");
3484
+ else if (low === "meta" || low === "cmd" || low === "super") mods.add("Meta");
3485
+ else key2 = p.length === 1 ? p.toUpperCase() : p;
3486
+ }
3487
+ const ordered2 = ["Control", "Alt", "Shift", "Meta"].filter((m) => mods.has(m));
3488
+ return key2 ? [...ordered2, key2].join("+") : ordered2.join("+");
3489
+ }
3490
+ const ordered = [];
3491
+ if (input.ctrlKey) ordered.push("Control");
3492
+ if (input.altKey) ordered.push("Alt");
3493
+ if (input.shiftKey) ordered.push("Shift");
3494
+ if (input.metaKey) ordered.push("Meta");
3495
+ let key = input.key;
3496
+ if (key === " ") key = "Space";
3497
+ else if (key.length === 1) key = key.toUpperCase();
3498
+ if (["Control", "Alt", "Shift", "Meta"].includes(key)) {
3499
+ return ordered.join("+");
3500
+ }
3501
+ ordered.push(key);
3502
+ return ordered.join("+");
3503
+ }
3504
+
3403
3505
  // src/tree/Scene.ts
3404
3506
  var RANGE_VALUE_ROLES = /* @__PURE__ */ new Set(["slider", "spinbutton", "progressbar", "scrollbar", "meter"]);
3405
3507
  var INTERACTIVE_A11Y_ROLES = /* @__PURE__ */ new Set([
@@ -3413,6 +3515,24 @@ var INTERACTIVE_A11Y_ROLES = /* @__PURE__ */ new Set([
3413
3515
  "slider",
3414
3516
  "combobox"
3415
3517
  ]);
3518
+ var KEYBOARD_OWNING_ROLES = /* @__PURE__ */ new Set([
3519
+ ...INTERACTIVE_A11Y_ROLES,
3520
+ "option",
3521
+ "listbox",
3522
+ "textbox",
3523
+ "searchbox",
3524
+ "spinbutton"
3525
+ ]);
3526
+ function ownsKeyboard(el) {
3527
+ if (!el) return false;
3528
+ if (el === document.body || el === document.documentElement) return false;
3529
+ if (el.hasAttribute("data-vecto-a11y-root")) return false;
3530
+ const tag = el.tagName;
3531
+ if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
3532
+ if (el.isContentEditable) return true;
3533
+ const role = el.getAttribute("role");
3534
+ return role !== null && KEYBOARD_OWNING_ROLES.has(role);
3535
+ }
3416
3536
  var A11Y_REQUIRED_OWNED = /* @__PURE__ */ new Map([
3417
3537
  ["grid", /* @__PURE__ */ new Set(["row", "rowgroup"])],
3418
3538
  ["table", /* @__PURE__ */ new Set(["row", "rowgroup"])],
@@ -3561,9 +3681,9 @@ function parseCssMatrix(transform) {
3561
3681
  }
3562
3682
  function clientToGridLocal(contentEl, canvas, clientX, clientY) {
3563
3683
  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"]')]);
3684
+ const originMarker = _optionalChain([line, 'optionalAccess', _86 => _86.querySelector, 'call', _87 => _87('[data-vecto-grid-basis="origin"]')]);
3685
+ const xMarker = _optionalChain([line, 'optionalAccess', _88 => _88.querySelector, 'call', _89 => _89('[data-vecto-grid-basis="x"]')]);
3686
+ const yMarker = _optionalChain([line, 'optionalAccess', _90 => _90.querySelector, 'call', _91 => _91('[data-vecto-grid-basis="y"]')]);
3567
3687
  if (line && originMarker && xMarker && yMarker) {
3568
3688
  const origin = originMarker.getBoundingClientRect();
3569
3689
  const xPoint = xMarker.getBoundingClientRect();
@@ -3647,7 +3767,7 @@ function nearestTextPositionInLine(line, x, y) {
3647
3767
  nearest = { position: { node, offset }, distance: candidate.distance };
3648
3768
  }
3649
3769
  }
3650
- return _nullishCoalesce(_optionalChain([nearest, 'optionalAccess', _91 => _91.position]), () => ( null));
3770
+ return _nullishCoalesce(_optionalChain([nearest, 'optionalAccess', _92 => _92.position]), () => ( null));
3651
3771
  }
3652
3772
  function nearestTextPositionInProjection(contentEl, canvas, x, y, eventTarget) {
3653
3773
  if (contentEl.dataset.vectoContentGrid !== void 0) {
@@ -3661,7 +3781,7 @@ function nearestTextPositionInProjection(contentEl, canvas, x, y, eventTarget) {
3661
3781
  }
3662
3782
  targetLine = targetLine.parentElement;
3663
3783
  }
3664
- if (_optionalChain([targetLine, 'optionalAccess', _92 => _92.parentElement]) === contentEl) {
3784
+ if (_optionalChain([targetLine, 'optionalAccess', _93 => _93.parentElement]) === contentEl) {
3665
3785
  return nearestTextPositionInLine(targetLine, x, y);
3666
3786
  }
3667
3787
  let bestLine = null;
@@ -3766,15 +3886,15 @@ var Scene = (_class15 = class _Scene {
3766
3886
 
3767
3887
 
3768
3888
 
3769
- __init83() {this.isRunning = false}
3889
+ __init88() {this.isRunning = false}
3770
3890
  /** Whether the canvas is at least partially in the viewport. When it scrolls
3771
3891
  * fully off-screen the rAF loop pauses (stops rescheduling) instead of
3772
3892
  * burning frames on a scene nobody can see; an IntersectionObserver resumes
3773
3893
  * it on re-entry. Defaults true (and stays true where IntersectionObserver
3774
3894
  * 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}
3895
+ __init89() {this._canvasOnScreen = true}
3896
+ __init90() {this._canvasObserver = null}
3897
+ __init91() {this.lastTime = 0}
3778
3898
 
3779
3899
  // --- domain: render-scheduler — mode, phase timing, frame telemetry, FPS caps ---
3780
3900
  /**
@@ -3784,7 +3904,7 @@ var Scene = (_class15 = class _Scene {
3784
3904
  * {@link markDirty}) or while an animation is pending. Ideal for static /
3785
3905
  * event-driven UIs where idle frames should cost ~0.
3786
3906
  */
3787
- __init87() {this.renderMode = "always"}
3907
+ __init92() {this.renderMode = "always"}
3788
3908
  /**
3789
3909
  * Per-phase frame timing and the browser User Timing flag.
3790
3910
  *
@@ -3794,7 +3914,7 @@ var Scene = (_class15 = class _Scene {
3794
3914
  * domains write phases, so this is a shared leaf rather than any one domain's
3795
3915
  * property — see `DEC-0021`.
3796
3916
  */
3797
- __init88() {this.phases = new PhaseTimer()}
3917
+ __init93() {this.phases = new PhaseTimer()}
3798
3918
  /**
3799
3919
  * Start or stop per-phase render timing.
3800
3920
  *
@@ -3849,7 +3969,7 @@ var Scene = (_class15 = class _Scene {
3849
3969
  * injected input, so it follows {@link phases} and {@link a11yOrder} rather
3850
3970
  * than the definite-assignment collaborators.
3851
3971
  */
3852
- __init89() {this._dirty = new DirtyTracker()}
3972
+ __init94() {this._dirty = new DirtyTracker()}
3853
3973
  /**
3854
3974
  * The redraw-pending flag, under its original name.
3855
3975
  *
@@ -3869,34 +3989,34 @@ var Scene = (_class15 = class _Scene {
3869
3989
  * Whether to throttle the `'always'` loop when the scene is static to save
3870
3990
  * power. The idle floor is {@link Scene.idleFPS} (default 60).
3871
3991
  */
3872
- __init90() {this.autoThrottle = true}
3992
+ __init95() {this.autoThrottle = true}
3873
3993
  /**
3874
3994
  * Frame-rate floor for an idle `'always'` scene when {@link Scene.autoThrottle}
3875
3995
  * is on. Default `60`. Set `2` for the legacy aggressive idle sleep, `0` to
3876
3996
  * keep the scene's `maxFPS` cadence while idle.
3877
3997
  */
3878
- __init91() {this.idleFPS = 60}
3998
+ __init96() {this.idleFPS = 60}
3879
3999
  // --- Frame telemetry (read via `frameStats`) ---------------------------
3880
4000
  /** Wall-clock ms spent inside the last `render()` call. */
3881
- __init92() {this._lastFrameMs = 0}
4001
+ __init97() {this._lastFrameMs = 0}
3882
4002
  /** Rolling exponential average of rendered-frame intervals, in ms. */
3883
- __init93() {this._avgFrameIntervalMs = 0}
4003
+ __init98() {this._avgFrameIntervalMs = 0}
3884
4004
  /** dt (ms) handed to the last rendered frame. */
3885
- __init94() {this._lastDt = 0}
4005
+ __init99() {this._lastDt = 0}
3886
4006
  /** Count of frames actually rendered since the loop started. */
3887
- __init95() {this._renderedFrames = 0}
4007
+ __init100() {this._renderedFrames = 0}
3888
4008
  /** Count of rAF ticks skipped (idle / capped) since the loop started. */
3889
- __init96() {this._skippedFrames = 0}
4009
+ __init101() {this._skippedFrames = 0}
3890
4010
  /** `time` of the previous *rendered* frame, for interval measurement. */
3891
- __init97() {this._lastRenderTick = 0}
4011
+ __init102() {this._lastRenderTick = 0}
3892
4012
  /**
3893
4013
  * Frame-rate cap (power saving). `0` = uncapped (native refresh). When set,
3894
4014
  * the loop renders at most `maxFPS` times per second; animations still run,
3895
4015
  * just less often. See {@link SceneOptions.maxFPS}.
3896
4016
  */
3897
- __init98() {this.maxFPS = 60}
4017
+ __init103() {this.maxFPS = 60}
3898
4018
  /** Whether the OS prefers-reduced-motion setting auto-caps the loop. */
3899
- __init99() {this.respectReducedMotion = true}
4019
+ __init104() {this.respectReducedMotion = true}
3900
4020
  /**
3901
4021
  * Reading direction for accessibility tab/traversal order (`'ltr'` default,
3902
4022
  * `'rtl'`). Controls the inline sort within a visual row in
@@ -3912,18 +4032,18 @@ var Scene = (_class15 = class _Scene {
3912
4032
  this.a11yOrder.markNeedsReorder();
3913
4033
  }
3914
4034
  }
3915
- __init100() {this._readingDirection = "ltr"}
4035
+ __init105() {this._readingDirection = "ltr"}
3916
4036
  /** Cached media-query list; `.matches` is read live each frame. */
3917
- __init101() {this.reducedMotionQuery = null}
4037
+ __init106() {this.reducedMotionQuery = null}
3918
4038
  /** Cached `(forced-colors: active)` query (Windows High Contrast etc.). A
3919
4039
  * canvas gets NO automatic forced-colors treatment from the browser (it's
3920
4040
  * opaque pixels), so components must read {@link forcedColors} and repaint
3921
4041
  * with system colors themselves; a change listener repaints idle scenes. */
3922
- __init102() {this.forcedColorsQuery = null}
3923
- __init103() {this.forcedColorsChangeHandler = null}
4042
+ __init107() {this.forcedColorsQuery = null}
4043
+ __init108() {this.forcedColorsChangeHandler = null}
3924
4044
  /** True when the OS asks for reduced motion and we respect it. Read by the animation drivers. */
3925
4045
  get prefersReducedMotion() {
3926
- return this.respectReducedMotion && !!_optionalChain([this, 'access', _93 => _93.reducedMotionQuery, 'optionalAccess', _94 => _94.matches]);
4046
+ return this.respectReducedMotion && !!_optionalChain([this, 'access', _94 => _94.reducedMotionQuery, 'optionalAccess', _95 => _95.matches]);
3927
4047
  }
3928
4048
  /**
3929
4049
  * True when the OS is in a forced-colors mode (Windows High Contrast, and the
@@ -3934,26 +4054,29 @@ var Scene = (_class15 = class _Scene {
3934
4054
  * when the setting toggles.
3935
4055
  */
3936
4056
  get forcedColors() {
3937
- return !!_optionalChain([this, 'access', _95 => _95.forcedColorsQuery, 'optionalAccess', _96 => _96.matches]);
4057
+ return !!_optionalChain([this, 'access', _96 => _96.forcedColorsQuery, 'optionalAccess', _97 => _97.matches]);
3938
4058
  }
3939
4059
  // --- domain: a11y-projection — sync throttle and shadow-DOM state ---
3940
4060
  /**
3941
4061
  * Throttle interval (ms) for the a11y/automation shadow sync. `0` = every
3942
4062
  * frame. See {@link SceneOptions.a11ySyncInterval}.
3943
4063
  */
3944
- __init104() {this.a11ySyncInterval = 0}
4064
+ __init109() {this.a11ySyncInterval = 0}
3945
4065
  /** Timestamp of the last a11y sync, for throttling. */
3946
- __init105() {this.lastA11ySync = -Infinity}
4066
+ __init110() {this.lastA11ySync = -Infinity}
3947
4067
  /** True if we skipped an a11y sync during animation and need to sync when at rest. */
3948
- __init106() {this.a11yPendingSyncAfterAnimation = false}
4068
+ __init111() {this.a11yPendingSyncAfterAnimation = false}
3949
4069
  // A11y / Automation Layer. `null` in non-DOM (SSR/Node) environments — the
3950
4070
  // whole projection degrades to a no-op so the engine's logic stays usable
3951
4071
  // server-side (e.g. headless layout / vector export) without jsdom.
3952
4072
 
3953
- __init107() {this.a11yElements = /* @__PURE__ */ new Map()}
4073
+ __init112() {this.a11yElements = /* @__PURE__ */ new Map()}
4074
+ /** Elements that already have the synthetic Enter/Space activation handler
4075
+ * (see the syncA11y refresh pass) — install-once bookkeeping (#694). */
4076
+ __init113() {this.keyboardActivatedA11y = /* @__PURE__ */ new WeakSet()}
3954
4077
  // --- domain: content-projection — projected DOM, sync state, calibration, selection ---
3955
4078
  /** DOM nodes mirroring static text content, keyed by entity id. */
3956
- __init108() {this.contentElements = /* @__PURE__ */ new Map()}
4079
+ __init114() {this.contentElements = /* @__PURE__ */ new Map()}
3957
4080
  /**
3958
4081
  * What the last completed content-projection sync was built from, per entity.
3959
4082
  *
@@ -3962,9 +4085,9 @@ var Scene = (_class15 = class _Scene {
3962
4085
  * build. Only populated for entities that opt in via
3963
4086
  * {@link Entity.getContentEpoch}. (carryctx CTX-0199)
3964
4087
  */
3965
- __init109() {this.contentSyncState = /* @__PURE__ */ new Map()}
4088
+ __init115() {this.contentSyncState = /* @__PURE__ */ new Map()}
3966
4089
  /** Invalidates grid font calibration after browser font availability changes. */
3967
- __init110() {this.contentFontEpoch = 0}
4090
+ __init116() {this.contentFontEpoch = 0}
3968
4091
  /**
3969
4092
  * Bumped whenever the viewport itself changes shape, which re-tiers blocks
3970
4093
  * without moving any of them.
@@ -3977,7 +4100,7 @@ var Scene = (_class15 = class _Scene {
3977
4100
  * carriers it no longer needs. Scrolling needs no such epoch: it moves the root,
3978
4101
  * so every block's parent world transform changes and the check fails honestly.
3979
4102
  */
3980
- __init111() {this.contentViewportEpoch = 0}
4103
+ __init117() {this.contentViewportEpoch = 0}
3981
4104
  /**
3982
4105
  * One-entry memo for a parent's world transform, held as scalars.
3983
4106
  *
@@ -3992,16 +4115,16 @@ var Scene = (_class15 = class _Scene {
3992
4115
  * sync, so a memo can never survive into a frame in which the parent has
3993
4116
  * moved.
3994
4117
  */
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}
4118
+ __init118() {this._pwNode = null}
4119
+ __init119() {this._pwSerial = -1}
4120
+ __init120() {this._pwa = 1}
4121
+ __init121() {this._pwb = 0}
4122
+ __init122() {this._pwc = 0}
4123
+ __init123() {this._pwd = 1}
4124
+ __init124() {this._pwe = 0}
4125
+ __init125() {this._pwf = 0}
4003
4126
  /** Incremented once per top-level `syncA11y`, invalidating `_pwNode`. */
4004
- __init120() {this._syncSerial = 0}
4127
+ __init126() {this._syncSerial = 0}
4005
4128
  /**
4006
4129
  * Disables the settled-walk fast path in {@link syncContentProjection}.
4007
4130
  *
@@ -4013,38 +4136,38 @@ var Scene = (_class15 = class _Scene {
4013
4136
  * Not part of the public API and not documented as an option: turning it on only
4014
4137
  * makes a settled document slower, never more correct.
4015
4138
  */
4016
- __init121() {this.disableSettledFastPath = false}
4139
+ __init127() {this.disableSettledFastPath = false}
4017
4140
  /** 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}
4141
+ __init128() {this.contentMetricScaleEpoch = -1}
4142
+ __init129() {this.contentMetricScaleX = 1}
4143
+ __init130() {this.contentProjectionEnabled = true}
4021
4144
  // Virtualization margin (px) for the content-projection CARRIER band;
4022
4145
  // `undefined` → one viewport height, resolved at sync time. `Infinity` is
4023
4146
  // unsupported here: it unwindows every carrier, which is O(total glyphs).
4024
- __init125() {this.contentProjectionMargin = void 0}
4147
+ __init131() {this.contentProjectionMargin = void 0}
4025
4148
  // Virtualization margin (px) for the SEMANTIC tier — whether a block has
4026
4149
  // any projected DOM. `undefined` → falls back to contentProjectionMargin, so
4027
4150
  // the default keeps one gate. `Infinity` = every block keeps resident text.
4028
- __init126() {this.contentSemanticMargin = void 0}
4151
+ __init132() {this.contentSemanticMargin = void 0}
4029
4152
  // How many coarse-tier blocks may be MATERIALIZED per sync, spreading the
4030
4153
  // resident tier's document-open cost across frames. `Infinity` = one
4031
4154
  // synchronous pass.
4032
- __init127() {this.contentSemanticBudget = DEFAULT_CONTENT_SEMANTIC_BUDGET}
4155
+ __init133() {this.contentSemanticBudget = DEFAULT_CONTENT_SEMANTIC_BUDGET}
4033
4156
  // Remaining materializations in the CURRENT sync. Reset at the start of each
4034
4157
  // a11y walk; decremented per coarse block that creates its element.
4035
- __init128() {this.contentSemanticBudgetLeft = 0}
4158
+ __init134() {this.contentSemanticBudgetLeft = 0}
4036
4159
  // Set when a sync deferred at least one block, so the scene knows to keep
4037
4160
  // drawing frames until the resident tier is complete. Without it a static
4038
4161
  // scene in `onDemand` mode would stop rendering with the document half
4039
4162
  // materialized and never finish.
4040
- __init129() {this.contentSemanticDeferred = false}
4041
- __init130() {this.contentSelectionEndListener = null}
4163
+ __init135() {this.contentSemanticDeferred = false}
4164
+ __init136() {this.contentSelectionEndListener = null}
4042
4165
  // --- domain: render-scheduler — per-frame walk flags ---
4043
4166
  // Animation/interactive flags collected during the render walk (tree-walk
4044
4167
  // fusion): the loop reads last frame's answers instead of re-walking the
4045
4168
  // tree up to 4× per tick. Start true so the first tick stays conservative.
4046
- __init131() {this.frameHadAnimation = true}
4047
- __init132() {this.frameHadInteractive = true}
4169
+ __init137() {this.frameHadAnimation = true}
4170
+ __init138() {this.frameHadInteractive = true}
4048
4171
  // --- domain: context-and-resize — resize handler, DPR query, canvas observer ---
4049
4172
 
4050
4173
  /** Active `(resolution: Ndppx)` media query watching for a runtime DPR change
@@ -4052,15 +4175,17 @@ var Scene = (_class15 = class _Scene {
4052
4175
  * can be re-scaled — otherwise it stays rasterized at the old DPR and blurs.
4053
4176
  * A resolution media query only fires when leaving its exact value, so the
4054
4177
  * handler re-arms a fresh query for the new DPR each time. */
4055
- __init133() {this.dprMediaQuery = null}
4178
+ __init139() {this.dprMediaQuery = null}
4056
4179
  /** For embedded (`disableWindowResize`) scenes: observes the canvas element so
4057
4180
  * a CSS/layout-driven size change re-runs `resize()`. A window `resize`
4058
4181
  * listener never fires for these (the window isn't what changed), so without
4059
4182
  * this an embedded canvas stayed at its initial size forever. */
4060
- __init134() {this.canvasResizeObserver = null}
4061
- __init135() {this.dprChangeHandler = null}
4183
+ __init140() {this.canvasResizeObserver = null}
4184
+ __init141() {this.dprChangeHandler = null}
4185
+ __init142() {this.dprPollInterval = null}
4186
+ __init143() {this.lastDpr = 1}
4062
4187
  // --- domain: a11y-projection — focus, overlay geometry, DOM ordering, portals ---
4063
- __init136() {this.focusedA11yElement = null}
4188
+ __init144() {this.focusedA11yElement = null}
4064
4189
  /**
4065
4190
  * Canvas box geometry: the CSS↔logical mapping, overlay layer alignment, and
4066
4191
  * the DPR math (extraction 5, `DEC-0024`).
@@ -4087,7 +4212,7 @@ var Scene = (_class15 = class _Scene {
4087
4212
  /** Shadow elements the pointer is currently inside. Lets a removal that happens
4088
4213
  * mid-hover synthesize the `pointerleave` the browser never sends for a
4089
4214
  * detached element, so the entity doesn't keep its hover state. */
4090
- __init137() {this.hoveredA11yElements = /* @__PURE__ */ new WeakSet()}
4215
+ __init145() {this.hoveredA11yElements = /* @__PURE__ */ new WeakSet()}
4091
4216
  /**
4092
4217
  * Entity ids the application has pinned via {@link requestA11yProjection}.
4093
4218
  *
@@ -4095,13 +4220,13 @@ var Scene = (_class15 = class _Scene {
4095
4220
  * a stale id simply never matches. Cleared per-entity by
4096
4221
  * {@link releaseA11yProjection}.
4097
4222
  */
4098
- __init138() {this.a11yProjectionRequests = /* @__PURE__ */ new Set()}
4223
+ __init146() {this.a11yProjectionRequests = /* @__PURE__ */ new Set()}
4099
4224
  /** Persistent tabindex=-1 element in a11yRoot. When the focused a11y mirror is
4100
4225
  * pruned (virtualization/streaming/removal) while it holds focus, we move
4101
4226
  * focus here instead of letting the browser drop it to <body> — keeping the
4102
4227
  * screen-reader virtual cursor inside the scene's a11y region. */
4103
- __init139() {this.focusSentinel = null}
4104
- __init140() {this.caretBlinkTimer = null}
4228
+ __init147() {this.focusSentinel = null}
4229
+ __init148() {this.caretBlinkTimer = null}
4105
4230
  /**
4106
4231
  * The projected DOM's ordering engine — the reorder flag, the per-pass scratch
4107
4232
  * collections, the visual reading-order sort and the cursor-based
@@ -4111,7 +4236,7 @@ var Scene = (_class15 = class _Scene {
4111
4236
  * delegate here; the collect-and-prune walk stays behind because it needs
4112
4237
  * {@link shouldProjectA11y} and the focus/caret state.
4113
4238
  */
4114
- __init141() {this.a11yOrder = new A11yProjectionManager()}
4239
+ __init149() {this.a11yOrder = new A11yProjectionManager()}
4115
4240
  /**
4116
4241
  * The content projection's selection preservation and grid calibration
4117
4242
  * (extraction 3, `DEC-0022`).
@@ -4163,11 +4288,11 @@ var Scene = (_class15 = class _Scene {
4163
4288
  set a11yNeedsReorder(value) {
4164
4289
  this.a11yOrder.needsReorder = value;
4165
4290
  }
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}
4291
+ __init150() {this.portalRoot = null}
4292
+ __init151() {this.activePortalsThisFrame = /* @__PURE__ */ new Set()}
4293
+ __init152() {this.activePortalsPrevFrame = /* @__PURE__ */ new Set()}
4294
+ __init153() {this.portalEntities = /* @__PURE__ */ new Map()}
4295
+ __init154() {this.renderOrderCounter = 0}
4171
4296
  // --- domain: render-scheduler — authoritative frame counter ---
4172
4297
  /**
4173
4298
  * Monotonic render-frame counter, bumped once per authoritative `render()`
@@ -4177,7 +4302,7 @@ var Scene = (_class15 = class _Scene {
4177
4302
  * back to the ancestor walk. Public for the same reason `Entity._getTrig`/
4178
4303
  * `_setWorldCache` are: it is a cross-class render-internal contract.
4179
4304
  */
4180
- __init147() {this.currentFrame = 0}
4305
+ __init155() {this.currentFrame = 0}
4181
4306
  // --- domain: wasm-backend-facade — EXTRACTED (see scene/WasmBackendFacade.ts) ---
4182
4307
  /**
4183
4308
  * The four invisible WASM accelerators and the resident transform store.
@@ -4247,8 +4372,8 @@ var Scene = (_class15 = class _Scene {
4247
4372
  // Stays on `Scene`: it walks `root` *and* `overlayRoot` for entities the
4248
4373
  // render walk consumes, so only its cache key is store state. The facade owns
4249
4374
  // the key; the walk stays here.
4250
- __init148() {this._computeEntities = []}
4251
- __init149() {this._computeEntitiesVersion = -1}
4375
+ __init156() {this._computeEntities = []}
4376
+ __init157() {this._computeEntitiesVersion = -1}
4252
4377
  /** Invalidate the resident WASM store layout; the next wasm-mode frame rebuilds
4253
4378
  * it. Called by `Entity.add`/`remove` (topology changes only). */
4254
4379
  markStructureChanged() {
@@ -4487,7 +4612,7 @@ var Scene = (_class15 = class _Scene {
4487
4612
  get animBatchedLastFrame() {
4488
4613
  return this._wasmBackend.animBatchedLastFrame;
4489
4614
  }
4490
- __init150() {this.animGate = {
4615
+ __init158() {this.animGate = {
4491
4616
  spring: 128,
4492
4617
  tween: 256,
4493
4618
  mixed: 128
@@ -4575,6 +4700,15 @@ var Scene = (_class15 = class _Scene {
4575
4700
  _registerActiveDriverSubtree(entity) {
4576
4701
  this._driverTicker.registerSubtree(entity);
4577
4702
  }
4703
+ /**
4704
+ * The `dt` of the frame currently executing its update walk (the WASM-mode
4705
+ * pre-pass or the JS-mode interleaved render walk), or `null` outside it.
4706
+ * Read by `Entity._spawnDriver`: a driver spawned mid-walk onto an entity the
4707
+ * batched pass already claimed this frame must be advanced once here, or
4708
+ * `tickDrivers()`'s per-frame stamp would skip it a full frame — a one-frame
4709
+ * JS/WASM divergence. Written in {@link render} next to `_tickBatchedDrivers`.
4710
+ */
4711
+ __init159() {this._updateWalkDt = null}
4578
4712
  // --- domain: render-scheduler — batched driver tick (extraction 6), reads the anim backend ---
4579
4713
  /**
4580
4714
  * Advance every registered entity's active drivers for this frame, batching
@@ -4602,25 +4736,25 @@ var Scene = (_class15 = class _Scene {
4602
4736
  * sync, so retaining the order prevents a newly opened overlay from spending
4603
4737
  * its first frame below previously projected controls.
4604
4738
  */
4605
- __init151() {this.a11yRenderOrders = /* @__PURE__ */ new Map()}
4739
+ __init160() {this.a11yRenderOrders = /* @__PURE__ */ new Map()}
4606
4740
  // --- domain: context-and-resize — GL/GPU canvases, device state, pointer, size warnings ---
4607
4741
  // 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}
4742
+ __init161() {this.pointRenderer = null}
4743
+ __init162() {this.glCanvas = null}
4744
+ __init163() {this.glContextLostHandler = null}
4745
+ __init164() {this.glContextRestoredHandler = null}
4612
4746
 
4613
4747
 
4614
4748
 
4615
- __init156() {this.disableWindowResize = false}
4749
+ __init165() {this.disableWindowResize = false}
4616
4750
  /** See {@link SceneOptions.maxDPR}. `undefined` = uncapped (real DPR). */
4617
4751
 
4618
4752
  // 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}
4753
+ __init166() {this.destroyed = false}
4754
+ __init167() {this.device = null}
4755
+ __init168() {this.deviceLost = false}
4756
+ __init169() {this.particleBackend = "auto"}
4757
+ __init170() {this._webgpuDisabled = false}
4624
4758
  get webgpuDisabled() {
4625
4759
  return this._webgpuDisabled || this.particleBackend === "cpu";
4626
4760
  }
@@ -4634,7 +4768,7 @@ var Scene = (_class15 = class _Scene {
4634
4768
  * active.
4635
4769
  */
4636
4770
  get webglDrawStats() {
4637
- return _nullishCoalesce(_optionalChain([this, 'access', _97 => _97.pointRenderer, 'optionalAccess', _98 => _98.stats, 'optionalCall', _99 => _99()]), () => ( null));
4771
+ return _nullishCoalesce(_optionalChain([this, 'access', _98 => _98.pointRenderer, 'optionalAccess', _99 => _99.stats, 'optionalCall', _100 => _100()]), () => ( null));
4638
4772
  }
4639
4773
  /**
4640
4774
  * Whether a WebGPU device is currently live for particle compute.
@@ -4648,21 +4782,28 @@ var Scene = (_class15 = class _Scene {
4648
4782
  set webgpuDisabled(value) {
4649
4783
  this._webgpuDisabled = value;
4650
4784
  }
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}
4785
+ __init171() {this.recoveryTimerId = null}
4786
+ __init172() {this.manager = null}
4787
+ __init173() {this.initializingWebGPU = false}
4788
+ __init174() {this.gpuCanvas = null}
4789
+ __init175() {this.gpuContext = null}
4656
4790
  /** 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}
4791
+ __init176() {this.gpuHasContent = false}
4792
+ __init177() {this.mouseX = -9999}
4793
+ __init178() {this.mouseY = -9999}
4794
+ __init179() {this.pointerMoveListener = null}
4795
+ __init180() {this.pointerLeaveListener = null}
4662
4796
  /** Element the pointer listeners are bound to (parent container if present,
4663
4797
  * else the canvas). Stored so `destroy()` detaches from the same element. */
4664
- __init172() {this.pointerEventTarget = null}
4665
- __init173() {this.hasWarnedZeroSize = false}
4798
+ __init181() {this.pointerEventTarget = null}
4799
+ // --- scene-level keyboard channel state (see on/off/registerShortcut) ---
4800
+ __init182() {this.keydownHandlers = []}
4801
+ __init183() {this.keyupHandlers = []}
4802
+ /** Chord-normalized shortcut table, matched on keydown only. */
4803
+ __init184() {this.shortcuts = []}
4804
+ __init185() {this.windowKeyDownHandler = null}
4805
+ __init186() {this.windowKeyUpHandler = null}
4806
+ __init187() {this.hasWarnedZeroSize = false}
4666
4807
  /**
4667
4808
  * Latch for {@link Scene.resize}'s invalid-dimension warning.
4668
4809
  *
@@ -4672,8 +4813,8 @@ var Scene = (_class15 = class _Scene {
4672
4813
  * because `resize()` is commonly driven from a `ResizeObserver`, which would
4673
4814
  * otherwise warn on every frame of a drag.
4674
4815
  */
4675
- __init174() {this.hasWarnedInvalidResize = false}
4676
- __init175() {this.fontLoadHandler = null}
4816
+ __init188() {this.hasWarnedInvalidResize = false}
4817
+ __init189() {this.fontLoadHandler = null}
4677
4818
  // --- domain: scene-facade — dev-mode diagnostics ---
4678
4819
  // ── Dev-mode warning infrastructure ──────────────────────────────
4679
4820
  //
@@ -4698,17 +4839,17 @@ var Scene = (_class15 = class _Scene {
4698
4839
  }
4699
4840
  static set devMode(active) {
4700
4841
  _Scene._devMode = active;
4701
- _chunkZYYP2M5Djs.setRendererDevMode.call(void 0, active);
4842
+ _chunkBKXERWADjs.setRendererDevMode.call(void 0, active);
4702
4843
  }
4703
4844
  static _devModeDetected() {
4704
4845
  if (_Scene._devMode) return true;
4705
4846
  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;
4847
+ if (_optionalChain([gp, 'optionalAccess', _101 => _101.__DEV__])) return true;
4848
+ if (_optionalChain([gp, 'optionalAccess', _102 => _102.process, 'optionalAccess', _103 => _103.env, 'optionalAccess', _104 => _104.NODE_ENV]) === "development") return true;
4708
4849
  return false;
4709
4850
  }
4710
4851
 
4711
- __init176() {this._devFrameCount = 0}
4852
+ __init190() {this._devFrameCount = 0}
4712
4853
  _devWarn(message) {
4713
4854
  if (!this._devActive) return;
4714
4855
  console.warn(`[vectojs/dev] ${message}`);
@@ -4766,9 +4907,9 @@ var Scene = (_class15 = class _Scene {
4766
4907
  let checked = 0;
4767
4908
  const walkProjections = (node) => {
4768
4909
  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)]);
4910
+ const proj = _optionalChain([node, 'access', _105 => _105.getContentProjection, 'optionalCall', _106 => _106()]);
4911
+ if (_optionalChain([proj, 'optionalAccess', _107 => _107.text]) && proj.selectable !== false) {
4912
+ const el = _optionalChain([this, 'access', _108 => _108.contentElements, 'optionalAccess', _109 => _109.get, 'call', _110 => _110(node.id)]);
4772
4913
  if (el) {
4773
4914
  const projectedText = el.textContent || "";
4774
4915
  const windowed = el.dataset.vectoProjectionWindow !== void 0;
@@ -4786,22 +4927,27 @@ var Scene = (_class15 = class _Scene {
4786
4927
  walkProjections(this.root);
4787
4928
  }
4788
4929
  // --- 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);
4930
+ 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);_class15.prototype.__init189.call(this);_class15.prototype.__init190.call(this);
4790
4931
  this.canvas = canvas;
4791
4932
  this.debugA11y = _nullishCoalesce(options.debugA11y, () => ( false));
4792
4933
  this.disableWindowResize = _nullishCoalesce(options.disableWindowResize, () => ( false));
4793
4934
  this.maxDPR = options.maxDPR;
4794
4935
  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]));
4936
+ const styleWidth = parseInlinePx(_optionalChain([canvas, 'access', _111 => _111.style, 'optionalAccess', _112 => _112.width]));
4937
+ const styleHeight = parseInlinePx(_optionalChain([canvas, 'access', _113 => _113.style, 'optionalAccess', _114 => _114.height]));
4797
4938
  this.width = _nullishCoalesce(styleWidth, () => ( (canvas.width || canvas.clientWidth || 0)));
4798
4939
  this.height = _nullishCoalesce(styleHeight, () => ( (canvas.height || canvas.clientHeight || 0)));
4799
4940
  } else {
4800
- this.width = typeof window !== "undefined" ? window.innerWidth : canvas.clientWidth || canvas.width || 800;
4801
- this.height = typeof window !== "undefined" ? window.innerHeight : canvas.clientHeight || canvas.height || 600;
4941
+ if (typeof canvas.isConnected === "boolean" && !canvas.isConnected) {
4942
+ this.width = 0;
4943
+ this.height = 0;
4944
+ } else {
4945
+ this.width = typeof window !== "undefined" ? window.innerWidth : canvas.clientWidth || canvas.width || 800;
4946
+ this.height = typeof window !== "undefined" ? window.innerHeight : canvas.clientHeight || canvas.height || 600;
4947
+ }
4802
4948
  }
4803
4949
  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");
4950
+ 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
4951
  this.maxFPS = _nullishCoalesce(options.maxFPS, () => ( (isTest ? 0 : 60)));
4806
4952
  this.respectReducedMotion = _nullishCoalesce(options.respectReducedMotion, () => ( true));
4807
4953
  this.autoThrottle = _nullishCoalesce(options.autoThrottle, () => ( true));
@@ -4816,15 +4962,15 @@ var Scene = (_class15 = class _Scene {
4816
4962
  this.readingDirection = _nullishCoalesce(options.readingDirection, () => ( "ltr"));
4817
4963
  this.renderMode = _nullishCoalesce(options.renderMode, () => ( "always"));
4818
4964
  this._devActive = _Scene._devModeDetected();
4819
- _chunkZYYP2M5Djs.setRendererDevMode.call(void 0, this._devActive);
4965
+ _chunkBKXERWADjs.setRendererDevMode.call(void 0, this._devActive);
4820
4966
  this._warnUnknownOptions(options);
4821
4967
  this.reducedMotionQuery = typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia("(prefers-reduced-motion: reduce)") : null;
4822
4968
  if (typeof window !== "undefined" && typeof window.matchMedia === "function") {
4823
4969
  this.forcedColorsQuery = window.matchMedia("(forced-colors: active)");
4824
4970
  this.forcedColorsChangeHandler = () => this.markDirty();
4825
- _optionalChain([this, 'access', _118 => _118.forcedColorsQuery, 'access', _119 => _119.addEventListener, 'optionalCall', _120 => _120("change", this.forcedColorsChangeHandler)]);
4971
+ _optionalChain([this, 'access', _119 => _119.forcedColorsQuery, 'access', _120 => _120.addEventListener, 'optionalCall', _121 => _121("change", this.forcedColorsChangeHandler)]);
4826
4972
  }
4827
- this.root = new class RootEntity extends _chunkSRXU3ZXKjs.Entity {
4973
+ this.root = new class RootEntity extends _chunkPOPXUPKRjs.Entity {
4828
4974
  isPointInside() {
4829
4975
  return false;
4830
4976
  }
@@ -4834,7 +4980,7 @@ var Scene = (_class15 = class _Scene {
4834
4980
  }("root");
4835
4981
  this.root._scene = this;
4836
4982
  this._wasmBackend = new WasmBackendFacade(this.root);
4837
- this.overlayRoot = new class OverlayRoot extends _chunkSRXU3ZXKjs.Entity {
4983
+ this.overlayRoot = new class OverlayRoot extends _chunkPOPXUPKRjs.Entity {
4838
4984
  isPointInside() {
4839
4985
  return false;
4840
4986
  }
@@ -4847,15 +4993,15 @@ var Scene = (_class15 = class _Scene {
4847
4993
  if (options.renderer) {
4848
4994
  this.renderer = options.renderer;
4849
4995
  } else {
4850
- this.renderer = new (0, _chunkZYYP2M5Djs.CanvasRenderer)(
4996
+ this.renderer = new (0, _chunkBKXERWADjs.CanvasRenderer)(
4851
4997
  canvas,
4852
4998
  this.disableWindowResize ? { width: this.width, height: this.height } : void 0,
4853
4999
  this.maxDPR
4854
5000
  );
4855
5001
  }
4856
- _optionalChain([this, 'access', _121 => _121.renderer, 'access', _122 => _122.onContextRestored, 'optionalCall', _123 => _123(() => {
5002
+ _optionalChain([this, 'access', _122 => _122.renderer, 'access', _123 => _123.onContextRestored, 'optionalCall', _124 => _124(() => {
4857
5003
  this.markDirty();
4858
- if (_optionalChain([this, 'access', _124 => _124.renderer, 'access', _125 => _125.isContextLost, 'optionalCall', _126 => _126()]) !== true) this.render(this.renderer);
5004
+ if (_optionalChain([this, 'access', _125 => _125.renderer, 'access', _126 => _126.isContextLost, 'optionalCall', _127 => _127()]) !== true) this.render(this.renderer);
4859
5005
  })]);
4860
5006
  if (typeof document !== "undefined") {
4861
5007
  this.a11yRoot = document.createElement("div");
@@ -5028,18 +5174,54 @@ var Scene = (_class15 = class _Scene {
5028
5174
  watchDevicePixelRatio() {
5029
5175
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
5030
5176
  if (this.dprMediaQuery && this.dprChangeHandler) {
5031
- _optionalChain([this, 'access', _127 => _127.dprMediaQuery, 'access', _128 => _128.removeEventListener, 'optionalCall', _129 => _129("change", this.dprChangeHandler)]);
5177
+ _optionalChain([this, 'access', _128 => _128.dprMediaQuery, 'access', _129 => _129.removeEventListener, 'optionalCall', _130 => _130("change", this.dprChangeHandler)]);
5032
5178
  }
5033
- const dpr = window.devicePixelRatio || 1;
5179
+ const raw = window.devicePixelRatio;
5180
+ const dpr = Number.isFinite(raw) && raw > 0 ? raw : 1;
5034
5181
  const query = window.matchMedia(`(resolution: ${dpr}dppx)`);
5035
5182
  const handler = () => {
5036
- 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);
5183
+ const curRaw = window.devicePixelRatio;
5184
+ const cur = Number.isFinite(curRaw) && curRaw > 0 ? curRaw : 1;
5185
+ if (Math.abs(cur - dpr) <= 1e-3) {
5186
+ this.watchDevicePixelRatio();
5187
+ return;
5188
+ }
5189
+ this.lastDpr = cur;
5190
+ try {
5191
+ this.resize(this.width, this.height);
5192
+ } catch (err) {
5193
+ console.warn("[VectoJS] DPR resize failed", err);
5194
+ }
5195
+ try {
5196
+ if (_optionalChain([this, 'access', _131 => _131.renderer, 'access', _132 => _132.isContextLost, 'optionalCall', _133 => _133()]) !== true) this.render(this.renderer);
5197
+ } catch (err) {
5198
+ console.warn("[VectoJS] DPR repaint failed", err);
5199
+ }
5038
5200
  this.watchDevicePixelRatio();
5039
5201
  };
5040
- _optionalChain([query, 'access', _133 => _133.addEventListener, 'optionalCall', _134 => _134("change", handler)]);
5202
+ _optionalChain([query, 'access', _134 => _134.addEventListener, 'optionalCall', _135 => _135("change", handler)]);
5041
5203
  this.dprMediaQuery = query;
5042
5204
  this.dprChangeHandler = handler;
5205
+ this.lastDpr = dpr;
5206
+ if (this.dprPollInterval === null && typeof setInterval === "function") {
5207
+ this.dprPollInterval = setInterval(() => {
5208
+ const rawPoll = window.devicePixelRatio;
5209
+ const curPoll = Number.isFinite(rawPoll) && rawPoll > 0 ? rawPoll : 1;
5210
+ if (Math.abs(curPoll - this.lastDpr) <= 1e-3) return;
5211
+ this.lastDpr = curPoll;
5212
+ try {
5213
+ this.resize(this.width, this.height);
5214
+ } catch (err) {
5215
+ console.warn("[VectoJS] DPR poll resize failed", err);
5216
+ }
5217
+ try {
5218
+ if (_optionalChain([this, 'access', _136 => _136.renderer, 'access', _137 => _137.isContextLost, 'optionalCall', _138 => _138()]) !== true) this.render(this.renderer);
5219
+ } catch (err) {
5220
+ console.warn("[VectoJS] DPR poll repaint failed", err);
5221
+ }
5222
+ this.watchDevicePixelRatio();
5223
+ }, 1e3);
5224
+ }
5043
5225
  }
5044
5226
  /**
5045
5227
  * Recover the WebGL point layer from a GPU context loss (driver TDR reset,
@@ -5057,7 +5239,7 @@ var Scene = (_class15 = class _Scene {
5057
5239
  if (typeof gl.addEventListener !== "function") return;
5058
5240
  this.glContextLostHandler = (e) => {
5059
5241
  e.preventDefault();
5060
- _optionalChain([this, 'access', _135 => _135.pointRenderer, 'optionalAccess', _136 => _136.destroy, 'call', _137 => _137()]);
5242
+ _optionalChain([this, 'access', _139 => _139.pointRenderer, 'optionalAccess', _140 => _140.destroy, 'call', _141 => _141()]);
5061
5243
  this.pointRenderer = null;
5062
5244
  };
5063
5245
  this.glContextRestoredHandler = () => {
@@ -5073,6 +5255,35 @@ var Scene = (_class15 = class _Scene {
5073
5255
  gl.addEventListener("webglcontextlost", this.glContextLostHandler);
5074
5256
  gl.addEventListener("webglcontextrestored", this.glContextRestoredHandler);
5075
5257
  }
5258
+ /**
5259
+ * One-shot adoption of the window viewport once an initially-detached canvas
5260
+ * gains layout (#817). A full-window scene constructed before its canvas is
5261
+ * attached starts at 0×0, and no window `resize` fires on attachment — so
5262
+ * without this latch the scene would stay unsized forever. The first nonzero
5263
+ * layout box adopts `window.innerWidth/innerHeight` (the same sizing the
5264
+ * window resize handler applies, keeping the full-window contract), then the
5265
+ * observer disconnects: steady-state sizing stays exactly as if the canvas
5266
+ * had been attached before construction. An explicit user `resize()` between
5267
+ * construction and attachment is respected and skips adoption.
5268
+ *
5269
+ * No-op when the scene already has a size (attached at construction) or when
5270
+ * `ResizeObserver` is unavailable (the caller then drives sizing explicitly).
5271
+ */
5272
+ armAttachmentViewportLatch() {
5273
+ if (this.width > 0 && this.height > 0) return;
5274
+ if (typeof ResizeObserver === "undefined") return;
5275
+ const observer = new ResizeObserver((entries) => {
5276
+ const box = _optionalChain([entries, 'access', _142 => _142[0], 'optionalAccess', _143 => _143.contentRect]);
5277
+ if (!box || !(box.width > 0) || !(box.height > 0)) return;
5278
+ observer.disconnect();
5279
+ this.canvasResizeObserver = null;
5280
+ if (this.width === 0 || this.height === 0) {
5281
+ this.resize(window.innerWidth, window.innerHeight);
5282
+ }
5283
+ });
5284
+ this.canvasResizeObserver = observer;
5285
+ observer.observe(this.canvas);
5286
+ }
5076
5287
  // --- domain: scene-facade — renderer accessor ---
5077
5288
  /**
5078
5289
  * Expose the underlying {@link IRenderer} for advanced direct-draw operations.
@@ -5154,7 +5365,7 @@ var Scene = (_class15 = class _Scene {
5154
5365
  }
5155
5366
  if (this.hoveredA11yElements.has(el)) {
5156
5367
  this.hoveredA11yElements.delete(el);
5157
- node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("pointerleave", node, void 0, false));
5368
+ node.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)("pointerleave", node, void 0, false));
5158
5369
  }
5159
5370
  this.preserveFocusOnRemoval(el);
5160
5371
  el.remove();
@@ -5254,12 +5465,16 @@ var Scene = (_class15 = class _Scene {
5254
5465
  this.canvasResizeObserver = null;
5255
5466
  }
5256
5467
  if (this.dprMediaQuery && this.dprChangeHandler) {
5257
- _optionalChain([this, 'access', _138 => _138.dprMediaQuery, 'access', _139 => _139.removeEventListener, 'optionalCall', _140 => _140("change", this.dprChangeHandler)]);
5468
+ _optionalChain([this, 'access', _144 => _144.dprMediaQuery, 'access', _145 => _145.removeEventListener, 'optionalCall', _146 => _146("change", this.dprChangeHandler)]);
5258
5469
  this.dprMediaQuery = null;
5259
5470
  this.dprChangeHandler = null;
5260
5471
  }
5472
+ if (this.dprPollInterval !== null) {
5473
+ clearInterval(this.dprPollInterval);
5474
+ this.dprPollInterval = null;
5475
+ }
5261
5476
  if (this.forcedColorsQuery && this.forcedColorsChangeHandler) {
5262
- _optionalChain([this, 'access', _141 => _141.forcedColorsQuery, 'access', _142 => _142.removeEventListener, 'optionalCall', _143 => _143("change", this.forcedColorsChangeHandler)]);
5477
+ _optionalChain([this, 'access', _147 => _147.forcedColorsQuery, 'access', _148 => _148.removeEventListener, 'optionalCall', _149 => _149("change", this.forcedColorsChangeHandler)]);
5263
5478
  this.forcedColorsQuery = null;
5264
5479
  this.forcedColorsChangeHandler = null;
5265
5480
  }
@@ -5268,6 +5483,19 @@ var Scene = (_class15 = class _Scene {
5268
5483
  window.removeEventListener("blur", this.contentSelectionEndListener);
5269
5484
  this.contentSelectionEndListener = null;
5270
5485
  }
5486
+ if (typeof window !== "undefined") {
5487
+ if (this.windowKeyDownHandler) {
5488
+ window.removeEventListener("keydown", this.windowKeyDownHandler);
5489
+ }
5490
+ if (this.windowKeyUpHandler) {
5491
+ window.removeEventListener("keyup", this.windowKeyUpHandler);
5492
+ }
5493
+ }
5494
+ this.windowKeyDownHandler = null;
5495
+ this.windowKeyUpHandler = null;
5496
+ this.keydownHandlers.length = 0;
5497
+ this.keyupHandlers.length = 0;
5498
+ this.shortcuts.length = 0;
5271
5499
  if (typeof window !== "undefined" && this.pointerEventTarget && typeof this.pointerEventTarget.removeEventListener === "function") {
5272
5500
  if (this.pointerMoveListener) {
5273
5501
  this.pointerEventTarget.removeEventListener("pointermove", this.pointerMoveListener);
@@ -5277,9 +5505,9 @@ var Scene = (_class15 = class _Scene {
5277
5505
  }
5278
5506
  this.pointerEventTarget = null;
5279
5507
  }
5280
- _optionalChain([this, 'access', _144 => _144.a11yRoot, 'optionalAccess', _145 => _145.remove, 'call', _146 => _146()]);
5508
+ _optionalChain([this, 'access', _150 => _150.a11yRoot, 'optionalAccess', _151 => _151.remove, 'call', _152 => _152()]);
5281
5509
  this.focusSentinel = null;
5282
- _optionalChain([this, 'access', _147 => _147.portalRoot, 'optionalAccess', _148 => _148.remove, 'call', _149 => _149()]);
5510
+ _optionalChain([this, 'access', _153 => _153.portalRoot, 'optionalAccess', _154 => _154.remove, 'call', _155 => _155()]);
5283
5511
  this.a11yElements.clear();
5284
5512
  for (const el of this.contentElements.values()) el.remove();
5285
5513
  this.contentElements.clear();
@@ -5295,10 +5523,10 @@ var Scene = (_class15 = class _Scene {
5295
5523
  }
5296
5524
  this.glContextLostHandler = null;
5297
5525
  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()]);
5526
+ _optionalChain([this, 'access', _156 => _156.pointRenderer, 'optionalAccess', _157 => _157.destroy, 'call', _158 => _158()]);
5527
+ _optionalChain([this, 'access', _159 => _159.renderer, 'access', _160 => _160.dispose, 'optionalCall', _161 => _161()]);
5528
+ _optionalChain([this, 'access', _162 => _162.glCanvas, 'optionalAccess', _163 => _163.remove, 'call', _164 => _164()]);
5529
+ _optionalChain([this, 'access', _165 => _165.gpuCanvas, 'optionalAccess', _166 => _166.remove, 'call', _167 => _167()]);
5302
5530
  this.gpuCanvas = null;
5303
5531
  this.gpuContext = null;
5304
5532
  if (this.recoveryTimerId) {
@@ -5310,7 +5538,7 @@ var Scene = (_class15 = class _Scene {
5310
5538
  this.manager = null;
5311
5539
  }
5312
5540
  if (this.device) {
5313
- _optionalChain([this, 'access', _162 => _162.device, 'access', _163 => _163.destroy, 'optionalCall', _164 => _164()]);
5541
+ _optionalChain([this, 'access', _168 => _168.device, 'access', _169 => _169.destroy, 'optionalCall', _170 => _170()]);
5314
5542
  this.device = null;
5315
5543
  }
5316
5544
  }
@@ -5318,6 +5546,7 @@ var Scene = (_class15 = class _Scene {
5318
5546
  setupEvents() {
5319
5547
  if (typeof window !== "undefined" && !this.disableWindowResize) {
5320
5548
  window.addEventListener("resize", this.resizeHandler);
5549
+ this.armAttachmentViewportLatch();
5321
5550
  } else if (this.disableWindowResize && typeof ResizeObserver !== "undefined" && this.canvas && typeof this.canvas.getBoundingClientRect === "function") {
5322
5551
  this.canvasResizeObserver = new ResizeObserver((entries) => {
5323
5552
  const entry = entries[0];
@@ -5367,6 +5596,7 @@ var Scene = (_class15 = class _Scene {
5367
5596
  this.lastTime = typeof performance !== "undefined" ? performance.now() : 0;
5368
5597
  this.watchCanvasVisibility();
5369
5598
  this.scheduleFrame();
5599
+ this.attachWindowKeyListeners();
5370
5600
  const isTextFocused = this.focusedA11yElement instanceof HTMLInputElement || this.focusedA11yElement instanceof HTMLTextAreaElement;
5371
5601
  if (isTextFocused && this.renderMode === "onDemand" && !this.caretBlinkTimer) {
5372
5602
  this.caretBlinkTimer = setInterval(() => {
@@ -5434,6 +5664,132 @@ var Scene = (_class15 = class _Scene {
5434
5664
  }
5435
5665
  this._canvasOnScreen = true;
5436
5666
  }
5667
+ // --- domain: input — scene-level keyboard channel ---
5668
+ /**
5669
+ * Register a listener on the scene-level keyboard channel.
5670
+ *
5671
+ * Mirrors {@link Entity.on} minus the options parameter (bubble-only, and
5672
+ * there is no capture phase for a single window tap). Handlers fire only
5673
+ * when ALL of these hold:
5674
+ *
5675
+ * 1. the native event was not already `defaultPrevented`,
5676
+ * 2. it is not an auto-repeat (`e.repeat`),
5677
+ * 3. `document.activeElement` does not own the keyboard
5678
+ * (see {@link ownsKeyboard}) — typing in an `<input>` or focusing a
5679
+ * slider-role element suppresses the scene channel, unconditionally,
5680
+ * including modifier chords.
5681
+ *
5682
+ * The listeners are window-level BUBBLE-phase taps, so an entity handler
5683
+ * that calls `nativeEvent.stopPropagation()` keeps the key from ever
5684
+ * reaching this channel. Deliberately not registered in the capture phase:
5685
+ * a window-capture listener would preempt Modal's document-capture trap.
5686
+ *
5687
+ * Listeners attach at `start()` and survive `stop()`; only `destroy()`
5688
+ * removes them.
5689
+ *
5690
+ * @param event - `'keydown'` or `'keyup'`; anything else is rejected with a
5691
+ * dev warning (guards JS callers — TypeScript already narrows this).
5692
+ * @param callback - Handler invoked with a {@link SceneKeyEvent}.
5693
+ * @returns `this` for method chaining.
5694
+ */
5695
+ on(event, callback) {
5696
+ if (event !== "keydown" && event !== "keyup") {
5697
+ console.warn(
5698
+ `[VectoJS] Scene.on: unsupported event '${String(event)}'. Only 'keydown' and 'keyup' are supported.`
5699
+ );
5700
+ return this;
5701
+ }
5702
+ (event === "keydown" ? this.keydownHandlers : this.keyupHandlers).push(callback);
5703
+ return this;
5704
+ }
5705
+ /**
5706
+ * Remove a listener previously registered with {@link Scene.on}. Silent
5707
+ * no-op for unknown handlers, unsupported event names, or after
5708
+ * {@link Scene.destroy}.
5709
+ *
5710
+ * @returns `this` for method chaining.
5711
+ */
5712
+ off(event, callback) {
5713
+ if (event !== "keydown" && event !== "keyup") {
5714
+ console.warn(
5715
+ `[VectoJS] Scene.off: unsupported event '${String(event)}'. Only 'keydown' and 'keyup' are supported.`
5716
+ );
5717
+ return this;
5718
+ }
5719
+ const handlers = event === "keydown" ? this.keydownHandlers : this.keyupHandlers;
5720
+ const idx = handlers.indexOf(callback);
5721
+ if (idx !== -1) handlers.splice(idx, 1);
5722
+ return this;
5723
+ }
5724
+ /**
5725
+ * Sugar over the keyboard channel: invoke `spec.handler` whenever a
5726
+ * non-repeat `keydown` whose normalized chord matches `spec.chord`
5727
+ * (via {@link normalizeChord}) passes the same gates as {@link Scene.on} —
5728
+ * including the unconditional `ownsKeyboard(activeElement)` suppression.
5729
+ *
5730
+ * @param spec - `{ chord, handler }`; the chord is normalized once at
5731
+ * registration, so `'ctrl+n'`, `'Control+n'` and `'Ctrl+N'` are aliases.
5732
+ * @returns `this` for method chaining.
5733
+ */
5734
+ registerShortcut(spec) {
5735
+ this.shortcuts.push({ chord: normalizeChord(spec.chord), handler: spec.handler });
5736
+ return this;
5737
+ }
5738
+ /**
5739
+ * Remove a shortcut previously registered with {@link Scene.registerShortcut}.
5740
+ * Matches on normalized chord + exact handler reference; silent no-op when
5741
+ * nothing matches.
5742
+ *
5743
+ * @param spec - The same spec object (chord spelling may differ; it is
5744
+ * re-normalized before matching).
5745
+ * @returns `this` for method chaining.
5746
+ */
5747
+ unregisterShortcut(spec) {
5748
+ const chord = normalizeChord(spec.chord);
5749
+ const idx = this.shortcuts.findIndex((s) => s.chord === chord && s.handler === spec.handler);
5750
+ if (idx !== -1) this.shortcuts.splice(idx, 1);
5751
+ return this;
5752
+ }
5753
+ /** Attach the window bubble-phase keyboard listeners exactly once. */
5754
+ attachWindowKeyListeners() {
5755
+ if (this.windowKeyDownHandler || typeof window === "undefined") return;
5756
+ this.windowKeyDownHandler = (e) => this.dispatchKeyboard(e, "keydown");
5757
+ this.windowKeyUpHandler = (e) => this.dispatchKeyboard(e, "keyup");
5758
+ window.addEventListener("keydown", this.windowKeyDownHandler);
5759
+ window.addEventListener("keyup", this.windowKeyUpHandler);
5760
+ }
5761
+ /**
5762
+ * Gate a native keyboard event and fan it out to scene handlers +
5763
+ * chord-matched shortcuts. See {@link Scene.on} for the gate contract.
5764
+ */
5765
+ dispatchKeyboard(e, type) {
5766
+ if (e.defaultPrevented) return;
5767
+ if (e.repeat) return;
5768
+ const active = typeof document !== "undefined" ? document.activeElement : null;
5769
+ if (ownsKeyboard(active)) return;
5770
+ const event = {
5771
+ type,
5772
+ key: e.key,
5773
+ code: e.code,
5774
+ repeat: e.repeat,
5775
+ ctrlKey: e.ctrlKey,
5776
+ altKey: e.altKey,
5777
+ shiftKey: e.shiftKey,
5778
+ metaKey: e.metaKey,
5779
+ target: e.target,
5780
+ nativeEvent: e,
5781
+ stopPropagation: () => e.stopPropagation(),
5782
+ preventDefault: () => e.preventDefault()
5783
+ };
5784
+ const handlers = type === "keydown" ? this.keydownHandlers : this.keyupHandlers;
5785
+ for (const handler of [...handlers]) handler(event);
5786
+ if (type === "keydown") {
5787
+ const chord = normalizeChord(e);
5788
+ for (const shortcut of [...this.shortcuts]) {
5789
+ if (shortcut.chord === chord) shortcut.handler(event);
5790
+ }
5791
+ }
5792
+ }
5437
5793
  // --- domain: scene-facade — tree accessors ---
5438
5794
  /**
5439
5795
  * Manually advance the scene clock by `dt` milliseconds and render synchronously.
@@ -5635,7 +5991,7 @@ var Scene = (_class15 = class _Scene {
5635
5991
  if (this.mouseX > -9e3 && !this.projectsSelectableText(node)) {
5636
5992
  if (node.isPointInside(this.mouseX, this.mouseY)) return true;
5637
5993
  }
5638
- const existing = _optionalChain([this, 'access', _165 => _165.a11yElements, 'optionalAccess', _166 => _166.get, 'call', _167 => _167(node.id)]);
5994
+ const existing = _optionalChain([this, 'access', _171 => _171.a11yElements, 'optionalAccess', _172 => _172.get, 'call', _173 => _173(node.id)]);
5639
5995
  if (existing && typeof document !== "undefined" && document.activeElement === existing) {
5640
5996
  return true;
5641
5997
  }
@@ -5649,8 +6005,8 @@ var Scene = (_class15 = class _Scene {
5649
6005
  * selection.
5650
6006
  */
5651
6007
  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;
6008
+ const projection = _optionalChain([node, 'access', _174 => _174.getContentProjection, 'optionalCall', _175 => _175()]);
6009
+ return !!_optionalChain([projection, 'optionalAccess', _176 => _176.text]) && projection.selectable !== false;
5654
6010
  }
5655
6011
  /**
5656
6012
  * Keep `entity`'s a11y shadow node projected while it has
@@ -5745,20 +6101,20 @@ var Scene = (_class15 = class _Scene {
5745
6101
  el.style.background = "transparent";
5746
6102
  }
5747
6103
  el.addEventListener("click", (e) => {
5748
- node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("click", node, e));
6104
+ node.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)("click", node, e));
5749
6105
  });
5750
6106
  el.addEventListener("dblclick", (e) => {
5751
- node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("dblclick", node, e));
6107
+ node.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)("dblclick", node, e));
5752
6108
  });
5753
6109
  el.addEventListener("mouseenter", (e) => {
5754
6110
  if (this.debugA11y) el.style.backgroundColor = "rgba(56, 189, 248, 0.2)";
5755
6111
  this.hoveredA11yElements.add(el);
5756
- node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("hover", node, e, false));
6112
+ node.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)("hover", node, e, false));
5757
6113
  });
5758
6114
  el.addEventListener("mouseleave", (e) => {
5759
6115
  if (this.debugA11y) el.style.backgroundColor = "rgba(56, 189, 248, 0.05)";
5760
6116
  this.hoveredA11yElements.delete(el);
5761
- node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("pointerleave", node, e, false));
6117
+ node.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)("pointerleave", node, e, false));
5762
6118
  });
5763
6119
  const capEl = el;
5764
6120
  const releasePointer = (event) => {
@@ -5776,24 +6132,24 @@ var Scene = (_class15 = class _Scene {
5776
6132
  if (e.target === capEl && typeof capEl.setPointerCapture === "function") {
5777
6133
  capEl.setPointerCapture(e.pointerId);
5778
6134
  }
5779
- node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("pointerdown", node, e));
6135
+ node.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)("pointerdown", node, e));
5780
6136
  });
5781
6137
  el.addEventListener("pointerup", (e) => {
5782
6138
  releasePointer(e);
5783
- node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("pointerup", node, e));
6139
+ node.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)("pointerup", node, e));
5784
6140
  });
5785
6141
  el.addEventListener("pointercancel", (e) => {
5786
6142
  releasePointer(e);
5787
- node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("pointercancel", node, e));
6143
+ node.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)("pointercancel", node, e));
5788
6144
  });
5789
6145
  el.addEventListener(
5790
6146
  "pointermove",
5791
- (e) => node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("pointermove", node, e))
6147
+ (e) => node.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)("pointermove", node, e))
5792
6148
  );
5793
6149
  el.addEventListener(
5794
6150
  "wheel",
5795
6151
  (e) => {
5796
- node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("wheel", node, e));
6152
+ node.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)("wheel", node, e));
5797
6153
  },
5798
6154
  { passive: false }
5799
6155
  );
@@ -5818,10 +6174,10 @@ var Scene = (_class15 = class _Scene {
5818
6174
  );
5819
6175
  emitInitialScroll = emitScroll;
5820
6176
  el.addEventListener("keydown", (e) => {
5821
- node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("keydown", node, e));
6177
+ node.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)("keydown", node, e));
5822
6178
  });
5823
6179
  el.addEventListener("keyup", (e) => {
5824
- node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("keyup", node, e));
6180
+ node.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)("keyup", node, e));
5825
6181
  });
5826
6182
  if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {
5827
6183
  const input = el;
@@ -5852,7 +6208,7 @@ var Scene = (_class15 = class _Scene {
5852
6208
  el.addEventListener("compositionupdate", (e) => {
5853
6209
  const data = _nullishCoalesce(e.data, () => ( ""));
5854
6210
  composition = {
5855
- start: _nullishCoalesce(_optionalChain([composition, 'optionalAccess', _171 => _171.start]), () => ( 0)),
6211
+ start: _nullishCoalesce(_optionalChain([composition, 'optionalAccess', _177 => _177.start]), () => ( 0)),
5856
6212
  length: data.length
5857
6213
  };
5858
6214
  forward();
@@ -5883,14 +6239,6 @@ var Scene = (_class15 = class _Scene {
5883
6239
  }
5884
6240
  node.emit("blur", {});
5885
6241
  });
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
6242
  if (node.a11yFullViewport) {
5895
6243
  this.a11yRoot.insertBefore(el, this.a11yRoot.firstChild);
5896
6244
  } else {
@@ -5909,7 +6257,24 @@ var Scene = (_class15 = class _Scene {
5909
6257
  if (renderOrder !== void 0 && el.style.zIndex !== String(renderOrder)) {
5910
6258
  el.style.zIndex = String(renderOrder);
5911
6259
  }
5912
- const implicitTabIndex = !isNativelyFocusable(el) && attrs.role && INTERACTIVE_A11Y_ROLES.has(attrs.role) ? 0 : null;
6260
+ const interactiveRole = !isNativelyFocusable(el) && !!attrs.role && INTERACTIVE_A11Y_ROLES.has(attrs.role);
6261
+ if (interactiveRole && !this.keyboardActivatedA11y.has(el)) {
6262
+ el.addEventListener("keydown", (e) => {
6263
+ if (e.key === "Enter") {
6264
+ e.preventDefault();
6265
+ node.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)("click", node, e));
6266
+ } else if (e.key === " ") {
6267
+ e.preventDefault();
6268
+ }
6269
+ });
6270
+ el.addEventListener("keyup", (e) => {
6271
+ if (e.key === " ") {
6272
+ node.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)("click", node, e));
6273
+ }
6274
+ });
6275
+ this.keyboardActivatedA11y.add(el);
6276
+ }
6277
+ const implicitTabIndex = interactiveRole ? 0 : null;
5913
6278
  const desiredTabIndex = _nullishCoalesce(attrs.tabIndex, () => ( implicitTabIndex));
5914
6279
  if (desiredTabIndex === null) {
5915
6280
  if (el.hasAttribute("tabindex")) el.removeAttribute("tabindex");
@@ -5925,7 +6290,7 @@ var Scene = (_class15 = class _Scene {
5925
6290
  this.syncOptionalAttribute(
5926
6291
  el,
5927
6292
  "href",
5928
- attrs.href === void 0 ? void 0 : _chunkZYYP2M5Djs.sanitizeUrl.call(void 0, attrs.href)
6293
+ attrs.href === void 0 ? void 0 : _chunkBKXERWADjs.sanitizeUrl.call(void 0, attrs.href)
5929
6294
  );
5930
6295
  this.syncOptionalAttribute(el, "target", attrs.target);
5931
6296
  }
@@ -6094,7 +6459,7 @@ var Scene = (_class15 = class _Scene {
6094
6459
  const display = visible ? "" : "none";
6095
6460
  if (el.style.display !== display) el.style.display = display;
6096
6461
  }
6097
- _optionalChain([emitInitialScroll, 'optionalCall', _172 => _172()]);
6462
+ _optionalChain([emitInitialScroll, 'optionalCall', _178 => _178()]);
6098
6463
  }
6099
6464
  if (this.phases.enabled) {
6100
6465
  const projectionStart = performance.now();
@@ -6324,7 +6689,7 @@ var Scene = (_class15 = class _Scene {
6324
6689
  el.addEventListener(
6325
6690
  "wheel",
6326
6691
  (e2) => {
6327
- node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("wheel", node, e2));
6692
+ node.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)("wheel", node, e2));
6328
6693
  },
6329
6694
  { passive: false }
6330
6695
  );
@@ -6555,8 +6920,8 @@ var Scene = (_class15 = class _Scene {
6555
6920
  next.f = f2;
6556
6921
  next.tier = tier;
6557
6922
  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));
6923
+ next.bandMin = _nullishCoalesce(_optionalChain([lineBand, 'optionalAccess', _179 => _179.minY]), () => ( 0));
6924
+ next.bandMax = _nullishCoalesce(_optionalChain([lineBand, 'optionalAccess', _180 => _180.maxY]), () => ( 0));
6560
6925
  next.visible = visible;
6561
6926
  next.width = node.width;
6562
6927
  next.height = node.height;
@@ -6742,7 +7107,7 @@ var Scene = (_class15 = class _Scene {
6742
7107
  * (and {@link respectReducedMotion} is on). `0` means uncapped.
6743
7108
  */
6744
7109
  effectiveMaxFPS() {
6745
- const reduced = this.respectReducedMotion && !!_optionalChain([this, 'access', _175 => _175.reducedMotionQuery, 'optionalAccess', _176 => _176.matches]);
7110
+ const reduced = this.respectReducedMotion && !!_optionalChain([this, 'access', _181 => _181.reducedMotionQuery, 'optionalAccess', _182 => _182.matches]);
6746
7111
  if (reduced)
6747
7112
  return this.maxFPS > 0 ? Math.min(this.maxFPS, REDUCED_MOTION_FPS) : REDUCED_MOTION_FPS;
6748
7113
  return this.maxFPS;
@@ -6837,7 +7202,7 @@ var Scene = (_class15 = class _Scene {
6837
7202
  * @param time - Current absolute time in milliseconds (default 0).
6838
7203
  */
6839
7204
  render(renderer, dt = 0, time = 0) {
6840
- if (_optionalChain([renderer, 'access', _177 => _177.isContextLost, 'optionalCall', _178 => _178()])) return;
7205
+ if (_optionalChain([renderer, 'access', _183 => _183.isContextLost, 'optionalCall', _184 => _184()])) return;
6841
7206
  const isMainRenderer = renderer === this.renderer;
6842
7207
  if (isMainRenderer && this.a11yRoot && this.canvas.parentElement) {
6843
7208
  const parentStyle = this.canvas.parentElement.style;
@@ -6851,6 +7216,7 @@ var Scene = (_class15 = class _Scene {
6851
7216
  this.renderOrderCounter = 0;
6852
7217
  this.a11yRenderOrders.clear();
6853
7218
  this.activePortalsThisFrame.clear();
7219
+ this._updateWalkDt = dt;
6854
7220
  this._tickBatchedDrivers(dt);
6855
7221
  }
6856
7222
  const computeEntities = this._computeEntitiesFor(this._structureVersion);
@@ -6986,14 +7352,14 @@ var Scene = (_class15 = class _Scene {
6986
7352
  }
6987
7353
  renderer.clear();
6988
7354
  if (isMainRenderer) {
6989
- _optionalChain([this, 'access', _179 => _179.pointRenderer, 'optionalAccess', _180 => _180.begin, 'call', _181 => _181()]);
7355
+ _optionalChain([this, 'access', _185 => _185.pointRenderer, 'optionalAccess', _186 => _186.begin, 'call', _187 => _187()]);
6990
7356
  }
6991
7357
  const vw = this.width;
6992
7358
  const vh = this.height;
6993
7359
  let walkHadAnimation = false;
6994
7360
  let walkHadInteractive = false;
6995
7361
  const runUpdate = (node) => {
6996
- const overridesUpdate = node.update !== _chunkSRXU3ZXKjs.Entity.prototype.update;
7362
+ const overridesUpdate = node.update !== _chunkPOPXUPKRjs.Entity.prototype.update;
6997
7363
  let pending = node.hasPendingAnimations();
6998
7364
  if (pending || overridesUpdate) {
6999
7365
  node.update(dt, time);
@@ -7002,7 +7368,7 @@ var Scene = (_class15 = class _Scene {
7002
7368
  if (pending) walkHadAnimation = true;
7003
7369
  if (!walkHadInteractive && node.interactive) walkHadInteractive = true;
7004
7370
  if (this._devActive && this._devFrameCount % 120 === 0) {
7005
- if (overridesUpdate && node.hasPendingAnimations === _chunkSRXU3ZXKjs.Entity.prototype.hasPendingAnimations) {
7371
+ if (overridesUpdate && node.hasPendingAnimations === _chunkPOPXUPKRjs.Entity.prototype.hasPendingAnimations) {
7006
7372
  this._devWarn(
7007
7373
  `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
7374
  );
@@ -7021,6 +7387,7 @@ var Scene = (_class15 = class _Scene {
7021
7387
  };
7022
7388
  updateWalk(this.root);
7023
7389
  for (const overlay of this.overlayRoot.children) updateWalk(overlay);
7390
+ this._updateWalkDt = null;
7024
7391
  }
7025
7392
  const transformTiming = this.phases.userTiming ? beginVectoUserTiming(VECTO_USER_TIMING.scene.transform) : null;
7026
7393
  const wasmT0 = this.phases.enabled ? performance.now() : 0;
@@ -7221,6 +7588,7 @@ var Scene = (_class15 = class _Scene {
7221
7588
  for (const overlay of this.overlayRoot.children) {
7222
7589
  renderNode(overlay, 1, 0, 0, 1, 0, 0, 1);
7223
7590
  }
7591
+ if (isMainRenderer) this._updateWalkDt = null;
7224
7592
  if (this.phases.enabled) this.phases.record("drawWalk", performance.now() - drawT0);
7225
7593
  if (drawTiming) endVectoUserTiming(drawTiming);
7226
7594
  if (this.phases.userTiming) {
@@ -7235,9 +7603,9 @@ var Scene = (_class15 = class _Scene {
7235
7603
  const flushT0 = this.phases.enabled ? performance.now() : 0;
7236
7604
  renderer.flush();
7237
7605
  if (isMainRenderer) {
7238
- _optionalChain([this, 'access', _182 => _182.pointRenderer, 'optionalAccess', _183 => _183.flush, 'call', _184 => _184()]);
7606
+ _optionalChain([this, 'access', _188 => _188.pointRenderer, 'optionalAccess', _189 => _189.flush, 'call', _190 => _190()]);
7239
7607
  }
7240
- _optionalChain([renderer, 'access', _185 => _185.present, 'optionalCall', _186 => _186()]);
7608
+ _optionalChain([renderer, 'access', _191 => _191.present, 'optionalCall', _192 => _192()]);
7241
7609
  if (this.phases.enabled) this.phases.record("flush", performance.now() - flushT0);
7242
7610
  if (flushTiming) endVectoUserTiming(flushTiming);
7243
7611
  if (this._devActive) {
@@ -7250,7 +7618,7 @@ var Scene = (_class15 = class _Scene {
7250
7618
  * Export the current scene state to a lightweight, flat SVG XML string.
7251
7619
  */
7252
7620
  toSVG() {
7253
- const renderer = new (0, _chunkZYYP2M5Djs.SVGRenderer)(this.width, this.height);
7621
+ const renderer = new (0, _chunkBKXERWADjs.SVGRenderer)(this.width, this.height);
7254
7622
  this.render(renderer, 0, 0);
7255
7623
  return renderer.toXMLString();
7256
7624
  }
@@ -7463,20 +7831,20 @@ function defaultMeasurer() {
7463
7831
  sharedMeasurer ??= _layout.resolveGlyphMeasurer.call(void 0, "sans-serif");
7464
7832
  return sharedMeasurer;
7465
7833
  }
7466
- var TextEntity = (_class16 = class extends _chunkSRXU3ZXKjs.Entity {
7834
+ var TextEntity = (_class16 = class extends _chunkPOPXUPKRjs.Entity {
7467
7835
 
7468
7836
 
7469
7837
 
7470
7838
 
7471
- __init177() {this.nodes = []}
7839
+ __init191() {this.nodes = []}
7472
7840
 
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}
7841
+ __init192() {this.fillStyle = "#94a3b8"}
7842
+ __init193() {this.strokeStyle = null}
7843
+ __init194() {this.hoveredFillStyle = "#ffffff"}
7844
+ __init195() {this.lineWidth = 1}
7845
+ __init196() {this.isHovered = false}
7478
7846
  /** Bumped by {@link applyLayout}; read by `Scene` to skip an unchanged sync. */
7479
- __init183() {this.contentEpoch = 0}
7847
+ __init197() {this.contentEpoch = 0}
7480
7848
  /**
7481
7849
  * Visual lines with real canvas geometry, rebuilt by {@link applyLayout}
7482
7850
  * exactly when `this.nodes` changes.
@@ -7488,9 +7856,9 @@ var TextEntity = (_class16 = class extends _chunkSRXU3ZXKjs.Entity {
7488
7856
  * ~0.35 em per line for a 24 px font, Firefox). Per-line carriers pin the DOM
7489
7857
  * baseline to the canvas baseline by construction, same as `ui/Text`.
7490
7858
  */
7491
- __init184() {this.projectionLines = []}
7859
+ __init198() {this.projectionLines = []}
7492
7860
  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);;
7861
+ super();_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);_class16.prototype.__init197.call(this);_class16.prototype.__init198.call(this);;
7494
7862
  this.text = text;
7495
7863
  this.atlas = atlas;
7496
7864
  this.fontSize = fontSize;
@@ -7667,17 +8035,17 @@ var TextEntity = (_class16 = class extends _chunkSRXU3ZXKjs.Entity {
7667
8035
  }, _class16);
7668
8036
 
7669
8037
  // src/components/GridTextEntity.ts
7670
- var GridTextEntity = (_class17 = class extends _chunkSRXU3ZXKjs.Entity {
8038
+ var GridTextEntity = (_class17 = class extends _chunkPOPXUPKRjs.Entity {
7671
8039
 
7672
- __init185() {this.fillStyle = "#ffffff"}
7673
- __init186() {this.grid = []}
8040
+ __init199() {this.fillStyle = "#ffffff"}
8041
+ __init200() {this.grid = []}
7674
8042
  // Array of rows
7675
- __init187() {this.cols = 0}
7676
- __init188() {this.rows = 0}
8043
+ __init201() {this.cols = 0}
8044
+ __init202() {this.rows = 0}
7677
8045
 
7678
8046
 
7679
8047
  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);;
8048
+ super();_class17.prototype.__init199.call(this);_class17.prototype.__init200.call(this);_class17.prototype.__init201.call(this);_class17.prototype.__init202.call(this);;
7681
8049
  this.fontSize = fontSize;
7682
8050
  this.charWidth = fontSize * 1;
7683
8051
  this.charHeight = fontSize * 1.1;
@@ -7686,7 +8054,7 @@ var GridTextEntity = (_class17 = class extends _chunkSRXU3ZXKjs.Entity {
7686
8054
  updateGrid(ascii) {
7687
8055
  this.grid = ascii;
7688
8056
  this.rows = ascii.length;
7689
- this.cols = ascii.reduce((widest, row) => Math.max(widest, _nullishCoalesce(_optionalChain([row, 'optionalAccess', _187 => _187.length]), () => ( 0))), 0);
8057
+ this.cols = ascii.reduce((widest, row) => Math.max(widest, _nullishCoalesce(_optionalChain([row, 'optionalAccess', _193 => _193.length]), () => ( 0))), 0);
7690
8058
  }
7691
8059
  isPointInside(_globalX, _globalY) {
7692
8060
  return false;
@@ -7761,7 +8129,7 @@ function distSqToSegment(px, py, x1, y1, x2, y2) {
7761
8129
  const ey = py - cy;
7762
8130
  return ex * ex + ey * ey;
7763
8131
  }
7764
- var SplineEntity = (_class18 = class extends _chunkSRXU3ZXKjs.Entity {
8132
+ var SplineEntity = (_class18 = class extends _chunkPOPXUPKRjs.Entity {
7765
8133
 
7766
8134
 
7767
8135
 
@@ -7769,17 +8137,17 @@ var SplineEntity = (_class18 = class extends _chunkSRXU3ZXKjs.Entity {
7769
8137
 
7770
8138
 
7771
8139
 
7772
- __init189() {this.offscreen = null}
7773
- __init190() {this.baked = false}
8140
+ __init203() {this.offscreen = null}
8141
+ __init204() {this.baked = false}
7774
8142
  /** Logical (CSS-pixel) size of the baked bitmap — the blit destination size. */
7775
- __init191() {this.bakedWidth = 0}
7776
- __init192() {this.bakedHeight = 0}
8143
+ __init205() {this.bakedWidth = 0}
8144
+ __init206() {this.bakedHeight = 0}
7777
8145
  /**
7778
8146
  * Device-pixel ratio the bitmap was rasterized at. Tracked so a DPR change
7779
8147
  * (browser zoom, a monitor move, a renderer whose clamp changed) re-bakes
7780
8148
  * instead of blitting a bitmap at the wrong device density forever.
7781
8149
  */
7782
- __init193() {this.bakedDPR = 0}
8150
+ __init207() {this.bakedDPR = 0}
7783
8151
  /**
7784
8152
  * Gradient strokes can't be baked to a solid-color bitmap; they render
7785
8153
  * per-frame. Derived from `_doc`, so it is NOT readonly — assigning a new
@@ -7787,7 +8155,7 @@ var SplineEntity = (_class18 = class extends _chunkSRXU3ZXKjs.Entity {
7787
8155
  */
7788
8156
 
7789
8157
  /** Lazily-flattened polylines (one Float32Array of [x,y,...] per segment) for hit-testing. */
7790
- __init194() {this.polylines = null}
8158
+ __init208() {this.polylines = null}
7791
8159
  /**
7792
8160
  * The spline document to render. Assigning a new document invalidates all
7793
8161
  * caches (baked canvas, flattened polylines, bounds).
@@ -7821,9 +8189,9 @@ var SplineEntity = (_class18 = class extends _chunkSRXU3ZXKjs.Entity {
7821
8189
  * local bounds after painting the curves. Useful for drag feedback and
7822
8190
  * debugging hit areas. Defaults to `false`.
7823
8191
  */
7824
- __init195() {this.showBounds = false}
8192
+ __init209() {this.showBounds = false}
7825
8193
  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);;
8194
+ super();_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);_class18.prototype.__init208.call(this);_class18.prototype.__init209.call(this);;
7827
8195
  this._doc = doc;
7828
8196
  this._lineWidth = _nullishCoalesce(opts.lineWidth, () => ( 2));
7829
8197
  this.cache = _nullishCoalesce(opts.cache, () => ( true));
@@ -7843,7 +8211,7 @@ var SplineEntity = (_class18 = class extends _chunkSRXU3ZXKjs.Entity {
7843
8211
  */
7844
8212
  computeContainsGradient() {
7845
8213
  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)));
8214
+ return (_nullishCoalesce(_optionalChain([this, 'access', _194 => _194._doc, 'access', _195 => _195.equations, 'optionalAccess', _196 => _196.some, 'call', _197 => _197((eq) => isGradient(eq.color_rgb))]), () => ( false))) || (_nullishCoalesce(_optionalChain([this, 'access', _198 => _198._doc, 'access', _199 => _199.paths, 'optionalAccess', _200 => _200.some, 'call', _201 => _201((p) => isGradient(p.color_rgb))]), () => ( false)));
7847
8215
  }
7848
8216
  /** Clear cached baked canvas and hit-test polylines. */
7849
8217
  invalidateCache() {
@@ -8089,7 +8457,7 @@ async function loadSpline(url) {
8089
8457
  }
8090
8458
 
8091
8459
  // src/components/Rect.ts
8092
- var Rect = class extends _chunkSRXU3ZXKjs.Entity {
8460
+ var Rect = class extends _chunkPOPXUPKRjs.Entity {
8093
8461
 
8094
8462
 
8095
8463
 
@@ -8145,7 +8513,7 @@ var Rect = class extends _chunkSRXU3ZXKjs.Entity {
8145
8513
  };
8146
8514
 
8147
8515
  // src/components/Circle.ts
8148
- var Circle = class extends _chunkSRXU3ZXKjs.Entity {
8516
+ var Circle = class extends _chunkPOPXUPKRjs.Entity {
8149
8517
 
8150
8518
 
8151
8519
 
@@ -8209,7 +8577,7 @@ var Circle = class extends _chunkSRXU3ZXKjs.Entity {
8209
8577
  };
8210
8578
 
8211
8579
  // src/components/Group.ts
8212
- var Group = class extends _chunkSRXU3ZXKjs.Entity {
8580
+ var Group = class extends _chunkPOPXUPKRjs.Entity {
8213
8581
  constructor(...children) {
8214
8582
  super();
8215
8583
  if (children.length > 0) this.add(...children);
@@ -8228,21 +8596,21 @@ var _math = require('@vectojs/math'); _createStarExport(_math);
8228
8596
 
8229
8597
 
8230
8598
  // src/tree/DOMPortalEntity.ts
8231
- var DOMPortalEntity = (_class19 = class extends _chunkSRXU3ZXKjs.Entity {
8599
+ var DOMPortalEntity = (_class19 = class extends _chunkPOPXUPKRjs.Entity {
8232
8600
 
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 = ""}
8601
+ __init210() {this.isDOMPortal = true}
8602
+ __init211() {this.domListeners = []}
8603
+ __init212() {this.resizeObserver = null}
8604
+ __init213() {this.domBound = false}
8605
+ __init214() {this.cachedWidth = 100}
8606
+ __init215() {this.cachedHeight = 100}
8607
+ __init216() {this.lastWidth = ""}
8608
+ __init217() {this.lastHeight = ""}
8609
+ __init218() {this.lastTransform = ""}
8610
+ __init219() {this.lastZIndex = ""}
8611
+ __init220() {this.lastOpacity = ""}
8244
8612
  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);;
8613
+ super(id);_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);_class19.prototype.__init219.call(this);_class19.prototype.__init220.call(this);;
8246
8614
  this.domElement = domElement;
8247
8615
  this.width = _nullishCoalesce(width, () => ( 0));
8248
8616
  this.height = _nullishCoalesce(height, () => ( 0));
@@ -8284,7 +8652,7 @@ var DOMPortalEntity = (_class19 = class extends _chunkSRXU3ZXKjs.Entity {
8284
8652
  ];
8285
8653
  for (const type of events) {
8286
8654
  const handler = (e) => {
8287
- this.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)(type, this, e));
8655
+ this.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)(type, this, e));
8288
8656
  };
8289
8657
  this.domElement.addEventListener(type, handler);
8290
8658
  this.domListeners.push({ type, handler, capture: false });
@@ -8295,7 +8663,7 @@ var DOMPortalEntity = (_class19 = class extends _chunkSRXU3ZXKjs.Entity {
8295
8663
  ];
8296
8664
  for (const { native, vecto } of hoverEvents) {
8297
8665
  const handler = (e) => {
8298
- this.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)(vecto, this, e, false));
8666
+ this.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)(vecto, this, e, false));
8299
8667
  };
8300
8668
  this.domElement.addEventListener(native, handler);
8301
8669
  this.domListeners.push({ type: native, handler, capture: false });
@@ -8303,7 +8671,7 @@ var DOMPortalEntity = (_class19 = class extends _chunkSRXU3ZXKjs.Entity {
8303
8671
  const focusEvents = ["focus", "blur"];
8304
8672
  for (const type of focusEvents) {
8305
8673
  const handler = (e) => {
8306
- this.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)(type, this, e, true));
8674
+ this.dispatchEvent(new (0, _chunkPOPXUPKRjs.VectoJSEvent)(type, this, e, true));
8307
8675
  };
8308
8676
  this.domElement.addEventListener(type, handler, true);
8309
8677
  this.domListeners.push({ type, handler, capture: true });
@@ -8354,8 +8722,11 @@ var DOMPortalEntity = (_class19 = class extends _chunkSRXU3ZXKjs.Entity {
8354
8722
  }, _class19);
8355
8723
 
8356
8724
  // src/index.ts
8357
- Scene.registerWebGLPointRendererCreator(_chunkZYYP2M5Djs.createWebGLPointRenderer);
8358
- Scene.registerWebGPUParticleSystemManager(_chunkZYYP2M5Djs.WebGPUParticleSystemManager);
8725
+ Scene.registerWebGLPointRendererCreator(_chunkBKXERWADjs.createWebGLPointRenderer);
8726
+ Scene.registerWebGPUParticleSystemManager(_chunkBKXERWADjs.WebGPUParticleSystemManager);
8727
+
8728
+
8729
+
8359
8730
 
8360
8731
 
8361
8732
 
@@ -8401,4 +8772,4 @@ Scene.registerWebGPUParticleSystemManager(_chunkZYYP2M5Djs.WebGPUParticleSystemM
8401
8772
 
8402
8773
 
8403
8774
 
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;
8775
+ exports.CanvasRenderer = _chunkBKXERWADjs.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 = _chunkBKXERWADjs.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 = _chunkBKXERWADjs.SVGRenderer; exports.Scene = Scene; exports.SplineEntity = SplineEntity; exports.TextEntity = TextEntity; exports.TextRasterCache = _chunkBKXERWADjs.TextRasterCache; exports.VECTO_USER_TIMING = VECTO_USER_TIMING; exports.VectoJSEvent = _chunkPOPXUPKRjs.VectoJSEvent; exports.WebGPUParticleSystemManager = _chunkBKXERWADjs.WebGPUParticleSystemManager; exports.beginVectoUserTiming = beginVectoUserTiming; exports.contentLineInHint = _chunkPOPXUPKRjs.contentLineInHint; exports.createWebGLPointRenderer = _chunkBKXERWADjs.createWebGLPointRenderer; exports.endVectoUserTiming = endVectoUserTiming; exports.installRendererDevTraps = _chunkBKXERWADjs.installRendererDevTraps; exports.isRendererDevMode = _chunkBKXERWADjs.isRendererDevMode; exports.isSafeUrl = _chunkBKXERWADjs.isSafeUrl; exports.loadSpline = loadSpline; exports.measureVectoUserTiming = measureVectoUserTiming; exports.normalizeChord = normalizeChord; exports.ownsKeyboard = ownsKeyboard; exports.parseColorToRGBA = _chunkBKXERWADjs.parseColorToRGBA; exports.polySegmentToBezier = polySegmentToBezier; exports.sanitizeUrl = _chunkBKXERWADjs.sanitizeUrl; exports.setRendererDevMode = _chunkBKXERWADjs.setRendererDevMode;