@vectojs/core 1.29.0 → 1.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -18,7 +18,8 @@ var _chunkM73XB4ZKjs = require('./chunk-M73XB4ZK.js');
18
18
 
19
19
 
20
20
 
21
- var _chunkZ3HFY75Rjs = require('./chunk-Z3HFY75R.js');
21
+
22
+ var _chunkTRT2TRCWjs = require('./chunk-TRT2TRCW.js');
22
23
 
23
24
  // src/tree/Scene.ts
24
25
  var _animation = require('@vectojs/animation'); _createStarExport(_animation);
@@ -33,7 +34,7 @@ var PARTICLE_OFFSET_ORIGIN_X = 4;
33
34
  var PARTICLE_OFFSET_ORIGIN_Y = 5;
34
35
  var PARTICLE_OFFSET_SIZE = 6;
35
36
  var PARTICLE_OFFSET_LIFE = 7;
36
- var ComputeParticleEntity = (_class = class extends _chunkZ3HFY75Rjs.Entity {
37
+ var ComputeParticleEntity = (_class = class extends _chunkTRT2TRCWjs.Entity {
37
38
 
38
39
 
39
40
 
@@ -1379,6 +1380,7 @@ var SCENE_OPTION_KEYS = [
1379
1380
  "autoThrottle",
1380
1381
  "contentProjection",
1381
1382
  "contentProjectionMargin",
1383
+ "contentSemanticMargin",
1382
1384
  "debugA11y",
1383
1385
  "disableWindowResize",
1384
1386
  "maxDPR",
@@ -1431,6 +1433,45 @@ function parseInlinePx(value) {
1431
1433
  const n = parseFloat(value);
1432
1434
  return Number.isFinite(n) && n > 0 ? n : null;
1433
1435
  }
1436
+ function projectionGridLineWindow(grid, projectionLines, band) {
1437
+ const count = grid.lines.length;
1438
+ const all = { start: 0, end: count, gated: false };
1439
+ if (!band || count === 0) return all;
1440
+ const lines = [];
1441
+ for (let i = 0; i < count; i++) {
1442
+ const projected = _optionalChain([projectionLines, 'optionalAccess', _24 => _24[i]]);
1443
+ lines.push({
1444
+ y: _nullishCoalesce(_optionalChain([projected, 'optionalAccess', _25 => _25.y]), () => ( i * grid.lineHeight)),
1445
+ lineHeight: _nullishCoalesce(_optionalChain([projected, 'optionalAccess', _26 => _26.lineHeight]), () => ( grid.lineHeight))
1446
+ });
1447
+ }
1448
+ return projectionLineWindow(lines, band, grid.lineHeight);
1449
+ }
1450
+ function projectionLineWindow(lines, band, fallbackLineHeight) {
1451
+ const all = {
1452
+ start: 0,
1453
+ end: lines.length,
1454
+ gated: false
1455
+ };
1456
+ if (!band || lines.length === 0) return all;
1457
+ let start = -1;
1458
+ let end = -1;
1459
+ for (let i = 0; i < lines.length; i++) {
1460
+ const line = lines[i];
1461
+ const h = _nullishCoalesce(line.lineHeight, () => ( fallbackLineHeight));
1462
+ if (line.y + h >= band.minY && line.y <= band.maxY) {
1463
+ if (start === -1) start = i;
1464
+ end = i + 1;
1465
+ } else if (start !== -1 && line.y > band.maxY) {
1466
+ break;
1467
+ }
1468
+ }
1469
+ if (start === -1) {
1470
+ return { start: 0, end: Math.min(1, lines.length), gated: true };
1471
+ }
1472
+ if (start === 0 && end === lines.length) return all;
1473
+ return { start, end, gated: true };
1474
+ }
1434
1475
  function collectTextNodes(root) {
1435
1476
  const out = [];
1436
1477
  const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
@@ -1510,9 +1551,9 @@ function parseCssMatrix(transform) {
1510
1551
  }
1511
1552
  function clientToGridLocal(contentEl, canvas, clientX, clientY) {
1512
1553
  const line = contentEl.querySelector("[data-vecto-grid-line]");
1513
- const originMarker = _optionalChain([line, 'optionalAccess', _24 => _24.querySelector, 'call', _25 => _25('[data-vecto-grid-basis="origin"]')]);
1514
- const xMarker = _optionalChain([line, 'optionalAccess', _26 => _26.querySelector, 'call', _27 => _27('[data-vecto-grid-basis="x"]')]);
1515
- const yMarker = _optionalChain([line, 'optionalAccess', _28 => _28.querySelector, 'call', _29 => _29('[data-vecto-grid-basis="y"]')]);
1554
+ const originMarker = _optionalChain([line, 'optionalAccess', _27 => _27.querySelector, 'call', _28 => _28('[data-vecto-grid-basis="origin"]')]);
1555
+ const xMarker = _optionalChain([line, 'optionalAccess', _29 => _29.querySelector, 'call', _30 => _30('[data-vecto-grid-basis="x"]')]);
1556
+ const yMarker = _optionalChain([line, 'optionalAccess', _31 => _31.querySelector, 'call', _32 => _32('[data-vecto-grid-basis="y"]')]);
1516
1557
  if (line && originMarker && xMarker && yMarker) {
1517
1558
  const origin = originMarker.getBoundingClientRect();
1518
1559
  const xPoint = xMarker.getBoundingClientRect();
@@ -1596,7 +1637,7 @@ function nearestTextPositionInLine(line, x, y) {
1596
1637
  nearest = { position: { node, offset }, distance: candidate.distance };
1597
1638
  }
1598
1639
  }
1599
- return _nullishCoalesce(_optionalChain([nearest, 'optionalAccess', _30 => _30.position]), () => ( null));
1640
+ return _nullishCoalesce(_optionalChain([nearest, 'optionalAccess', _33 => _33.position]), () => ( null));
1600
1641
  }
1601
1642
  function nearestTextPositionInProjection(contentEl, canvas, x, y, eventTarget) {
1602
1643
  if (contentEl.dataset.vectoContentGrid !== void 0) {
@@ -1610,7 +1651,7 @@ function nearestTextPositionInProjection(contentEl, canvas, x, y, eventTarget) {
1610
1651
  }
1611
1652
  targetLine = targetLine.parentElement;
1612
1653
  }
1613
- if (_optionalChain([targetLine, 'optionalAccess', _31 => _31.parentElement]) === contentEl) {
1654
+ if (_optionalChain([targetLine, 'optionalAccess', _34 => _34.parentElement]) === contentEl) {
1614
1655
  return nearestTextPositionInLine(targetLine, x, y);
1615
1656
  }
1616
1657
  let bestLine = null;
@@ -1888,7 +1929,7 @@ var Scene = (_class7 = class _Scene {
1888
1929
  __init50() {this.forcedColorsChangeHandler = null}
1889
1930
  /** True when the OS asks for reduced motion and we respect it. Read by the animation drivers. */
1890
1931
  get prefersReducedMotion() {
1891
- return this.respectReducedMotion && !!_optionalChain([this, 'access', _32 => _32.reducedMotionQuery, 'optionalAccess', _33 => _33.matches]);
1932
+ return this.respectReducedMotion && !!_optionalChain([this, 'access', _35 => _35.reducedMotionQuery, 'optionalAccess', _36 => _36.matches]);
1892
1933
  }
1893
1934
  /**
1894
1935
  * True when the OS is in a forced-colors mode (Windows High Contrast, and the
@@ -1899,7 +1940,7 @@ var Scene = (_class7 = class _Scene {
1899
1940
  * when the setting toggles.
1900
1941
  */
1901
1942
  get forcedColors() {
1902
- return !!_optionalChain([this, 'access', _34 => _34.forcedColorsQuery, 'optionalAccess', _35 => _35.matches]);
1943
+ return !!_optionalChain([this, 'access', _37 => _37.forcedColorsQuery, 'optionalAccess', _38 => _38.matches]);
1903
1944
  }
1904
1945
  /**
1905
1946
  * Throttle interval (ms) for the a11y/automation shadow sync. `0` = every
@@ -1917,10 +1958,19 @@ var Scene = (_class7 = class _Scene {
1917
1958
  __init54() {this.a11yElements = /* @__PURE__ */ new Map()}
1918
1959
  /** DOM nodes mirroring static text content, keyed by entity id. */
1919
1960
  __init55() {this.contentElements = /* @__PURE__ */ new Map()}
1961
+ /**
1962
+ * What the last completed content-projection sync was built from, per entity.
1963
+ *
1964
+ * Compared at the top of {@link syncContentProjection} to skip a block whose
1965
+ * content AND geometry are both unchanged, before the O(glyphs) projection
1966
+ * build. Only populated for entities that opt in via
1967
+ * {@link Entity.getContentEpoch}. (carryctx CTX-0199)
1968
+ */
1969
+ __init56() {this.contentSyncState = /* @__PURE__ */ new Map()}
1920
1970
  /** Pending cold font-calibration frame per projected grid entity. */
1921
- __init56() {this.contentGridCalibrationFrames = /* @__PURE__ */ new Map()}
1971
+ __init57() {this.contentGridCalibrationFrames = /* @__PURE__ */ new Map()}
1922
1972
  /** Detached, untransformed font probes used by the cold calibration pass. */
1923
- __init57() {this.contentGridCalibrationProbes = /* @__PURE__ */ new Map()}
1973
+ __init58() {this.contentGridCalibrationProbes = /* @__PURE__ */ new Map()}
1924
1974
  /**
1925
1975
  * Monotonic stamp identifying the conditions grid cells were calibrated under.
1926
1976
  *
@@ -1941,69 +1991,82 @@ var Scene = (_class7 = class _Scene {
1941
1991
  * A plain incrementing integer rather than the descriptive calibration key,
1942
1992
  * because it goes into an attribute selector and must not need escaping.
1943
1993
  */
1944
- __init58() {this.contentGridCalibrationGeneration = 0}
1994
+ __init59() {this.contentGridCalibrationGeneration = 0}
1945
1995
  /** The `(fontEpoch, pageScale)` pair the current generation corresponds to. */
1946
- __init59() {this.contentGridCalibrationStamp = ""}
1996
+ __init60() {this.contentGridCalibrationStamp = ""}
1947
1997
  /** Invalidates grid font calibration after browser font availability changes. */
1948
- __init60() {this.contentFontEpoch = 0}
1998
+ __init61() {this.contentFontEpoch = 0}
1949
1999
  /** Cached Canvas-to-client scale for the current font/viewport epoch. */
1950
- __init61() {this.contentMetricScaleEpoch = -1}
1951
- __init62() {this.contentMetricScaleX = 1}
1952
- __init63() {this.contentProjectionEnabled = true}
1953
- // Virtualization margin (px) for content projection; `undefined` → one
1954
- // viewport height, resolved at sync time. `Infinity` = materialize everything.
1955
- __init64() {this.contentProjectionMargin = void 0}
2000
+ __init62() {this.contentMetricScaleEpoch = -1}
2001
+ __init63() {this.contentMetricScaleX = 1}
2002
+ __init64() {this.contentProjectionEnabled = true}
2003
+ // Virtualization margin (px) for the content-projection CARRIER band;
2004
+ // `undefined` → one viewport height, resolved at sync time. `Infinity` is
2005
+ // unsupported here: it unwindows every carrier, which is O(total glyphs).
2006
+ __init65() {this.contentProjectionMargin = void 0}
2007
+ // Virtualization margin (px) for the SEMANTIC tier — whether a block has
2008
+ // any projected DOM. `undefined` → falls back to contentProjectionMargin, so
2009
+ // the default keeps one gate. `Infinity` = every block keeps resident text.
2010
+ __init66() {this.contentSemanticMargin = void 0}
1956
2011
  /**
1957
2012
  * True while a text-selection drag that started on a projection's blank
1958
2013
  * region (no text node under the press) is being driven manually — the
1959
2014
  * browser has no native anchor for it, so mousemove extends the Selection
1960
2015
  * from the position we resolved ourselves.
1961
2016
  */
1962
- __init65() {this.blankRegionSelectionDrag = false}
1963
- __init66() {this.contentSelectionAnchor = null}
1964
- __init67() {this.contentSelectionEndListener = null}
2017
+ __init67() {this.blankRegionSelectionDrag = false}
2018
+ __init68() {this.contentSelectionAnchor = null}
2019
+ __init69() {this.contentSelectionEndListener = null}
1965
2020
  // Animation/interactive flags collected during the render walk (tree-walk
1966
2021
  // fusion): the loop reads last frame's answers instead of re-walking the
1967
2022
  // tree up to 4× per tick. Start true so the first tick stays conservative.
1968
- __init68() {this.frameHadAnimation = true}
1969
- __init69() {this.frameHadInteractive = true}
2023
+ __init70() {this.frameHadAnimation = true}
2024
+ __init71() {this.frameHadInteractive = true}
1970
2025
 
1971
2026
  /** Active `(resolution: Ndppx)` media query watching for a runtime DPR change
1972
2027
  * (window moved between monitors, browser zoom) so the canvas backing store
1973
2028
  * can be re-scaled — otherwise it stays rasterized at the old DPR and blurs.
1974
2029
  * A resolution media query only fires when leaving its exact value, so the
1975
2030
  * handler re-arms a fresh query for the new DPR each time. */
1976
- __init70() {this.dprMediaQuery = null}
2031
+ __init72() {this.dprMediaQuery = null}
1977
2032
  /** For embedded (`disableWindowResize`) scenes: observes the canvas element so
1978
2033
  * a CSS/layout-driven size change re-runs `resize()`. A window `resize`
1979
2034
  * listener never fires for these (the window isn't what changed), so without
1980
2035
  * this an embedded canvas stayed at its initial size forever. */
1981
- __init71() {this.canvasResizeObserver = null}
1982
- __init72() {this.dprChangeHandler = null}
1983
- __init73() {this.focusedA11yElement = null}
2036
+ __init73() {this.canvasResizeObserver = null}
2037
+ __init74() {this.dprChangeHandler = null}
2038
+ __init75() {this.focusedA11yElement = null}
1984
2039
  /** Last geometry `syncOverlayGeometry` wrote, so an unchanged frame can skip the
1985
2040
  * style writes entirely. Reset to `null` to force the next sync (a new overlay
1986
2041
  * layer was created and has never been positioned). */
1987
- __init74() {this._overlayGeometry = null}
2042
+ __init76() {this._overlayGeometry = null}
1988
2043
  /** Shadow elements the pointer is currently inside. Lets a removal that happens
1989
2044
  * mid-hover synthesize the `pointerleave` the browser never sends for a
1990
2045
  * detached element, so the entity doesn't keep its hover state. */
1991
- __init75() {this.hoveredA11yElements = /* @__PURE__ */ new WeakSet()}
2046
+ __init77() {this.hoveredA11yElements = /* @__PURE__ */ new WeakSet()}
2047
+ /**
2048
+ * Entity ids the application has pinned via {@link requestA11yProjection}.
2049
+ *
2050
+ * Ids rather than entities so a removed entity cannot be retained by this set;
2051
+ * a stale id simply never matches. Cleared per-entity by
2052
+ * {@link releaseA11yProjection}.
2053
+ */
2054
+ __init78() {this.a11yProjectionRequests = /* @__PURE__ */ new Set()}
1992
2055
  /** Persistent tabindex=-1 element in a11yRoot. When the focused a11y mirror is
1993
2056
  * pruned (virtualization/streaming/removal) while it holds focus, we move
1994
2057
  * focus here instead of letting the browser drop it to <body> — keeping the
1995
2058
  * screen-reader virtual cursor inside the scene's a11y region. */
1996
- __init76() {this.focusSentinel = null}
1997
- __init77() {this.caretBlinkTimer = null}
1998
- __init78() {this.a11yNeedsReorder = true}
1999
- __init79() {this.portalRoot = null}
2000
- __init80() {this.fullViewportElements = []}
2001
- __init81() {this.normalElements = []}
2002
- __init82() {this.activeIds = /* @__PURE__ */ new Set()}
2059
+ __init79() {this.focusSentinel = null}
2060
+ __init80() {this.caretBlinkTimer = null}
2061
+ __init81() {this.a11yNeedsReorder = true}
2062
+ __init82() {this.portalRoot = null}
2063
+ __init83() {this.fullViewportElements = []}
2064
+ __init84() {this.normalElements = []}
2065
+ __init85() {this.activeIds = /* @__PURE__ */ new Set()}
2003
2066
  /** Per-parent insertion cursor, reused by `enforceA11yDomOrder`. */
2004
- __init83() {this.a11yOrderCursors = /* @__PURE__ */ new Map()}
2067
+ __init86() {this.a11yOrderCursors = /* @__PURE__ */ new Map()}
2005
2068
  /** Membership set for the elements being ordered, reused per reorder pass. */
2006
- __init84() {this.a11yOrderMembers = /* @__PURE__ */ new Set()}
2069
+ __init87() {this.a11yOrderMembers = /* @__PURE__ */ new Set()}
2007
2070
  /**
2008
2071
  * Elements that are an *ancestor* of another ordered element, reused per pass.
2009
2072
  *
@@ -2011,11 +2074,11 @@ var Scene = (_class7 = class _Scene {
2011
2074
  * items) spans every descendant row, so it must not extend a visual row band —
2012
2075
  * see {@link sortNormalElementsVisually}.
2013
2076
  */
2014
- __init85() {this.a11yOrderContainers = /* @__PURE__ */ new Set()}
2015
- __init86() {this.activePortalsThisFrame = /* @__PURE__ */ new Set()}
2016
- __init87() {this.activePortalsPrevFrame = /* @__PURE__ */ new Set()}
2017
- __init88() {this.portalEntities = /* @__PURE__ */ new Map()}
2018
- __init89() {this.renderOrderCounter = 0}
2077
+ __init88() {this.a11yOrderContainers = /* @__PURE__ */ new Set()}
2078
+ __init89() {this.activePortalsThisFrame = /* @__PURE__ */ new Set()}
2079
+ __init90() {this.activePortalsPrevFrame = /* @__PURE__ */ new Set()}
2080
+ __init91() {this.portalEntities = /* @__PURE__ */ new Map()}
2081
+ __init92() {this.renderOrderCounter = 0}
2019
2082
  /**
2020
2083
  * Monotonic render-frame counter, bumped once per authoritative `render()`
2021
2084
  * pass. Entities stamp their per-frame world-matrix cache with this value and
@@ -2024,7 +2087,7 @@ var Scene = (_class7 = class _Scene {
2024
2087
  * back to the ancestor walk. Public for the same reason `Entity._getTrig`/
2025
2088
  * `_setWorldCache` are: it is a cross-class render-internal contract.
2026
2089
  */
2027
- __init90() {this.currentFrame = 0}
2090
+ __init93() {this.currentFrame = 0}
2028
2091
  // ── WASM transform backend (invisible accelerator) ──────────────────────────
2029
2092
  // When `_transformBackend === 'wasm'`, the main render walk sources each
2030
2093
  // entity's world matrix from an SoA store composed by `_wasm` (see
@@ -2032,24 +2095,24 @@ var Scene = (_class7 = class _Scene {
2032
2095
  // fallback and the default: a null backend, a non-main renderer, or any entity
2033
2096
  // absent from the store all fall back to the JS composition, so WASM can only
2034
2097
  // ever change *how fast* a world matrix is produced, never *what* it is.
2035
- __init91() {this._wasm = null}
2036
- __init92() {this._transformBackend = "js"}
2098
+ __init94() {this._wasm = null}
2099
+ __init95() {this._transformBackend = "js"}
2037
2100
  // Resident store state (Stage 3). The store layout — slot assignment + sibling
2038
2101
  // runs — depends only on tree TOPOLOGY, so it is rebuilt only when the
2039
2102
  // structure changes (add/remove/reparent bump `_structureVersion`). Between
2040
2103
  // rebuilds the per-frame cost is: gather each entity's transform into the
2041
2104
  // resident wasm input view + run the kernel — no reallocation, no readback.
2042
- __init93() {this._treeStore = null}
2043
- __init94() {this._slotEntity = []}
2105
+ __init96() {this._treeStore = null}
2106
+ __init97() {this._slotEntity = []}
2044
2107
  // store slot -> entity (also validates slots)
2045
- __init95() {this._wasmInputs = null}
2046
- __init96() {this._wasmWorld = null}
2047
- __init97() {this._structureVersion = 0}
2048
- __init98() {this._storeStructureVersion = -1}
2108
+ __init98() {this._wasmInputs = null}
2109
+ __init99() {this._wasmWorld = null}
2110
+ __init100() {this._structureVersion = 0}
2111
+ __init101() {this._storeStructureVersion = -1}
2049
2112
  // Cached list of ComputeParticleEntity instances in the tree, keyed by the
2050
2113
  // structure version it was gathered at. Rebuilt only on a topology change.
2051
- __init99() {this._computeEntities = []}
2052
- __init100() {this._computeEntitiesVersion = -1}
2114
+ __init102() {this._computeEntities = []}
2115
+ __init103() {this._computeEntitiesVersion = -1}
2053
2116
  /** Invalidate the resident WASM store layout; the next wasm-mode frame rebuilds
2054
2117
  * it. Called by `Entity.add`/`remove` (topology changes only). */
2055
2118
  markStructureChanged() {
@@ -2134,7 +2197,7 @@ var Scene = (_class7 = class _Scene {
2134
2197
  * cached globally; the instance is per-Scene, which is the isolation that
2135
2198
  * actually matters.
2136
2199
  */
2137
- __init101() {this._wasmRuntime = null}
2200
+ __init104() {this._wasmRuntime = null}
2138
2201
  /**
2139
2202
  * Load (or reuse) this Scene's shared WASM runtime.
2140
2203
  *
@@ -2162,30 +2225,30 @@ var Scene = (_class7 = class _Scene {
2162
2225
  get wasmRuntime() {
2163
2226
  return this._wasmRuntime;
2164
2227
  }
2165
- __init102() {this._hitWasm = null}
2228
+ __init105() {this._hitWasm = null}
2166
2229
  // Cache key: which frame + structure version the grid was last (successfully,
2167
2230
  // non-overflowing) built for. findEntityAt is called ad-hoc (pointer
2168
2231
  // hover/click), not every frame, so the grid is refreshed lazily on demand
2169
2232
  // rather than proactively every render() — unlike the transform store, which
2170
2233
  // every frame's draw depends on.
2171
- __init103() {this._hitGridFrame = -1}
2172
- __init104() {this._hitGridOk = false}
2173
- __init105() {this._hitSlotEntity = []}
2174
- __init106() {this._hitBoundless = []}
2234
+ __init106() {this._hitGridFrame = -1}
2235
+ __init107() {this._hitGridOk = false}
2236
+ __init108() {this._hitSlotEntity = []}
2237
+ __init109() {this._hitBoundless = []}
2175
2238
  /** Reused buffer for the fused gather, so a pointer query allocates nothing. */
2176
- __init107() {this._hitGatherBuffer = null}
2239
+ __init110() {this._hitGatherBuffer = null}
2177
2240
  /**
2178
2241
  * Whether the last grid build sourced its AABBs from the WASM transform store
2179
2242
  * rather than recomputing them in JS. Diagnostic only — both paths must
2180
2243
  * produce the same entity for a given point.
2181
2244
  */
2182
- __init108() {this._hitFusedGather = false}
2245
+ __init111() {this._hitFusedGather = false}
2183
2246
  /**
2184
2247
  * Whether `compute_aabbs` has run against the current frame's world matrices.
2185
2248
  * The AABB pass is only meaningful after a `compose_*`, so the fused gather
2186
2249
  * must not read the views before then.
2187
2250
  */
2188
- __init109() {this._wasmAabbsFresh = false}
2251
+ __init112() {this._wasmAabbsFresh = false}
2189
2252
  /** Did the last hit-grid build use the fused (WASM-store) gather? */
2190
2253
  get hitGatherPath() {
2191
2254
  return this._hitFusedGather ? "fused" : "js";
@@ -2194,9 +2257,9 @@ var Scene = (_class7 = class _Scene {
2194
2257
  * Why the transform accelerator did or did not run on the most recent frame.
2195
2258
  * Written by the render walk and `_syncWasmStore`.
2196
2259
  */
2197
- __init110() {this._transformReason = "not-installed"}
2260
+ __init113() {this._transformReason = "not-installed"}
2198
2261
  /** Why the batched-driver accelerator did or did not run. */
2199
- __init111() {this._animReason = "not-installed"}
2262
+ __init114() {this._animReason = "not-installed"}
2200
2263
  /**
2201
2264
  * Why the hit-test accelerator did or did not serve the last pointer query.
2202
2265
  * The grid is built lazily on demand, not every frame, so this describes the
@@ -2204,11 +2267,11 @@ var Scene = (_class7 = class _Scene {
2204
2267
  * before a backend exists; `_ensureHitGrid` moves it to `'not-applicable'`
2205
2268
  * once one is installed but nothing has queried yet.
2206
2269
  */
2207
- __init112() {this._hitReason = "not-installed"}
2270
+ __init115() {this._hitReason = "not-installed"}
2208
2271
  /** Why the particle accelerator did or did not run. */
2209
- __init113() {this._particleReason = "not-applicable"}
2272
+ __init116() {this._particleReason = "not-applicable"}
2210
2273
  /** Which particle implementation actually simulated the most recent frame. */
2211
- __init114() {this._particlePath = "none"}
2274
+ __init117() {this._particlePath = "none"}
2212
2275
  /**
2213
2276
  * Per-frame status of every invisible accelerator: whether each is installed,
2214
2277
  * whether it actually ran on the most recent frame, and why.
@@ -2345,7 +2408,7 @@ var Scene = (_class7 = class _Scene {
2345
2408
  const idx = cell[k];
2346
2409
  if (x < minx[idx] || x > maxx[idx] || y < miny[idx] || y > maxy[idx]) continue;
2347
2410
  const entity = this._hitSlotEntity[idx];
2348
- if (_optionalChain([entity, 'optionalAccess', _36 => _36.isPointInside, 'call', _37 => _37(x, y)]) && this.isHitEligible(entity, x, y)) {
2411
+ if (_optionalChain([entity, 'optionalAccess', _39 => _39.isPointInside, 'call', _40 => _40(x, y)]) && this.isHitEligible(entity, x, y)) {
2349
2412
  bestIndex = idx;
2350
2413
  bestEntity = entity;
2351
2414
  break;
@@ -2368,25 +2431,25 @@ var Scene = (_class7 = class _Scene {
2368
2431
  // EasingFn (which cannot cross into WASM) all fall through to it — WASM can
2369
2432
  // only ever change *how* a driver is advanced, never *what* value it lands
2370
2433
  // on.
2371
- __init115() {this._animWasm = null}
2434
+ __init118() {this._animWasm = null}
2372
2435
  // Entities with at least one active driver, added by Entity._spawnDriver.
2373
2436
  // Self-pruning: _tickBatchedDrivers drops an entry the first time it visits
2374
2437
  // an entity whose drivers have since all completed or been removed. This is
2375
2438
  // what lets the batch pass find its candidates in O(active drivers), not
2376
2439
  // O(tree size) — the exact mistake G3's first integrated benchmark made.
2377
- __init116() {this._activeDriverEntities = /* @__PURE__ */ new Set()}
2440
+ __init119() {this._activeDriverEntities = /* @__PURE__ */ new Set()}
2378
2441
  // Reused across frames instead of allocating a fresh array + N {entity,prop,
2379
2442
  // driver} objects every call — the integrated benchmark
2380
2443
  // (benchmarks/anim-wasm-scene) found that allocation churn was the
2381
2444
  // dominant integrated cost, not the wasm kernel itself. Parallel arrays,
2382
2445
  // truncated to the live count after each use so a stale tail slot never
2383
2446
  // pins a no-longer-active entity/driver in memory.
2384
- __init117() {this._springEntities = []}
2385
- __init118() {this._springProps = []}
2386
- __init119() {this._springDrivers = []}
2387
- __init120() {this._tweenEntities = []}
2388
- __init121() {this._tweenProps = []}
2389
- __init122() {this._tweenDrivers = []}
2447
+ __init120() {this._springEntities = []}
2448
+ __init121() {this._springProps = []}
2449
+ __init122() {this._springDrivers = []}
2450
+ __init123() {this._tweenEntities = []}
2451
+ __init124() {this._tweenProps = []}
2452
+ __init125() {this._tweenDrivers = []}
2390
2453
  /**
2391
2454
  * Minimum number of batchable (spring, or named-easing tween) active drivers
2392
2455
  * before a frame engages the WASM batch path at all; below it, every driver
@@ -2458,7 +2521,7 @@ var Scene = (_class7 = class _Scene {
2458
2521
  * Setting {@link animDriverGateCount} overwrites all three, so existing code
2459
2522
  * that tuned the single knob keeps working unchanged.
2460
2523
  */
2461
- __init123() {this._animBatchedLastFrame = false}
2524
+ __init126() {this._animBatchedLastFrame = false}
2462
2525
  /**
2463
2526
  * Whether the WASM batch path actually ran on the most recent frame.
2464
2527
  *
@@ -2470,7 +2533,7 @@ var Scene = (_class7 = class _Scene {
2470
2533
  get animBatchedLastFrame() {
2471
2534
  return this._animBatchedLastFrame;
2472
2535
  }
2473
- __init124() {this.animGate = {
2536
+ __init127() {this.animGate = {
2474
2537
  spring: 128,
2475
2538
  tween: 256,
2476
2539
  mixed: 128
@@ -2508,7 +2571,7 @@ var Scene = (_class7 = class _Scene {
2508
2571
  // (benchmarks/particle-wasm). f32 (matches the WGSL shader), bit-identical to
2509
2572
  // a JS f32 reference oracle; updateCPU (f64) stays the permanent fallback when
2510
2573
  // no backend is installed or a scene runs on WebGPU.
2511
- __init125() {this._particleWasm = null}
2574
+ __init128() {this._particleWasm = null}
2512
2575
  /** Which backend runs the CPU particle simulation. Reflects only whether a
2513
2576
  * backend is installed (the WebGPU compute path, when active, is used first
2514
2577
  * regardless). */
@@ -2785,24 +2848,24 @@ var Scene = (_class7 = class _Scene {
2785
2848
  * sync, so retaining the order prevents a newly opened overlay from spending
2786
2849
  * its first frame below previously projected controls.
2787
2850
  */
2788
- __init126() {this.a11yRenderOrders = /* @__PURE__ */ new Map()}
2851
+ __init129() {this.a11yRenderOrders = /* @__PURE__ */ new Map()}
2789
2852
  // Optional WebGL point-cloud layer (see SceneOptions.pointBackend).
2790
- __init127() {this.pointRenderer = null}
2791
- __init128() {this.glCanvas = null}
2792
- __init129() {this.glContextLostHandler = null}
2793
- __init130() {this.glContextRestoredHandler = null}
2853
+ __init130() {this.pointRenderer = null}
2854
+ __init131() {this.glCanvas = null}
2855
+ __init132() {this.glContextLostHandler = null}
2856
+ __init133() {this.glContextRestoredHandler = null}
2794
2857
 
2795
2858
 
2796
2859
 
2797
- __init131() {this.disableWindowResize = false}
2860
+ __init134() {this.disableWindowResize = false}
2798
2861
  /** See {@link SceneOptions.maxDPR}. `undefined` = uncapped (real DPR). */
2799
2862
 
2800
2863
  // WebGPU properties
2801
- __init132() {this.destroyed = false}
2802
- __init133() {this.device = null}
2803
- __init134() {this.deviceLost = false}
2804
- __init135() {this.particleBackend = "auto"}
2805
- __init136() {this._webgpuDisabled = false}
2864
+ __init135() {this.destroyed = false}
2865
+ __init136() {this.device = null}
2866
+ __init137() {this.deviceLost = false}
2867
+ __init138() {this.particleBackend = "auto"}
2868
+ __init139() {this._webgpuDisabled = false}
2806
2869
  get webgpuDisabled() {
2807
2870
  return this._webgpuDisabled || this.particleBackend === "cpu";
2808
2871
  }
@@ -2816,7 +2879,7 @@ var Scene = (_class7 = class _Scene {
2816
2879
  * active.
2817
2880
  */
2818
2881
  get webglDrawStats() {
2819
- return _nullishCoalesce(_optionalChain([this, 'access', _38 => _38.pointRenderer, 'optionalAccess', _39 => _39.stats, 'optionalCall', _40 => _40()]), () => ( null));
2882
+ return _nullishCoalesce(_optionalChain([this, 'access', _41 => _41.pointRenderer, 'optionalAccess', _42 => _42.stats, 'optionalCall', _43 => _43()]), () => ( null));
2820
2883
  }
2821
2884
  /**
2822
2885
  * Whether a WebGPU device is currently live for particle compute.
@@ -2830,22 +2893,22 @@ var Scene = (_class7 = class _Scene {
2830
2893
  set webgpuDisabled(value) {
2831
2894
  this._webgpuDisabled = value;
2832
2895
  }
2833
- __init137() {this.recoveryTimerId = null}
2834
- __init138() {this.manager = null}
2835
- __init139() {this.initializingWebGPU = false}
2836
- __init140() {this.gpuCanvas = null}
2837
- __init141() {this.gpuContext = null}
2896
+ __init140() {this.recoveryTimerId = null}
2897
+ __init141() {this.manager = null}
2898
+ __init142() {this.initializingWebGPU = false}
2899
+ __init143() {this.gpuCanvas = null}
2900
+ __init144() {this.gpuContext = null}
2838
2901
  /** True while the GPU canvas holds a presented particle frame (needs clearing when they leave). */
2839
- __init142() {this.gpuHasContent = false}
2840
- __init143() {this.mouseX = -9999}
2841
- __init144() {this.mouseY = -9999}
2842
- __init145() {this.pointerMoveListener = null}
2843
- __init146() {this.pointerLeaveListener = null}
2902
+ __init145() {this.gpuHasContent = false}
2903
+ __init146() {this.mouseX = -9999}
2904
+ __init147() {this.mouseY = -9999}
2905
+ __init148() {this.pointerMoveListener = null}
2906
+ __init149() {this.pointerLeaveListener = null}
2844
2907
  /** Element the pointer listeners are bound to (parent container if present,
2845
2908
  * else the canvas). Stored so `destroy()` detaches from the same element. */
2846
- __init147() {this.pointerEventTarget = null}
2847
- __init148() {this.hasWarnedZeroSize = false}
2848
- __init149() {this.fontLoadHandler = null}
2909
+ __init150() {this.pointerEventTarget = null}
2910
+ __init151() {this.hasWarnedZeroSize = false}
2911
+ __init152() {this.fontLoadHandler = null}
2849
2912
  // ── Dev-mode warning infrastructure ──────────────────────────────
2850
2913
  //
2851
2914
  // Enable with `Scene.devMode = true` or by setting `globalThis.__DEV__`.
@@ -2874,12 +2937,12 @@ var Scene = (_class7 = class _Scene {
2874
2937
  static _devModeDetected() {
2875
2938
  if (_Scene._devMode) return true;
2876
2939
  const gp = typeof globalThis !== "undefined" ? globalThis : void 0;
2877
- if (_optionalChain([gp, 'optionalAccess', _41 => _41.__DEV__])) return true;
2878
- if (_optionalChain([gp, 'optionalAccess', _42 => _42.process, 'optionalAccess', _43 => _43.env, 'optionalAccess', _44 => _44.NODE_ENV]) === "development") return true;
2940
+ if (_optionalChain([gp, 'optionalAccess', _44 => _44.__DEV__])) return true;
2941
+ if (_optionalChain([gp, 'optionalAccess', _45 => _45.process, 'optionalAccess', _46 => _46.env, 'optionalAccess', _47 => _47.NODE_ENV]) === "development") return true;
2879
2942
  return false;
2880
2943
  }
2881
2944
 
2882
- __init150() {this._devFrameCount = 0}
2945
+ __init153() {this._devFrameCount = 0}
2883
2946
  _devWarn(message) {
2884
2947
  if (!this._devActive) return;
2885
2948
  console.warn(`[vectojs/dev] ${message}`);
@@ -2937,12 +3000,14 @@ var Scene = (_class7 = class _Scene {
2937
3000
  let checked = 0;
2938
3001
  const walkProjections = (node) => {
2939
3002
  if (checked > 10) return;
2940
- const proj = _optionalChain([node, 'access', _45 => _45.getContentProjection, 'optionalCall', _46 => _46()]);
2941
- if (_optionalChain([proj, 'optionalAccess', _47 => _47.text]) && proj.selectable !== false) {
2942
- const el = _optionalChain([this, 'access', _48 => _48.contentElements, 'optionalAccess', _49 => _49.get, 'call', _50 => _50(node.id)]);
3003
+ const proj = _optionalChain([node, 'access', _48 => _48.getContentProjection, 'optionalCall', _49 => _49()]);
3004
+ if (_optionalChain([proj, 'optionalAccess', _50 => _50.text]) && proj.selectable !== false) {
3005
+ const el = _optionalChain([this, 'access', _51 => _51.contentElements, 'optionalAccess', _52 => _52.get, 'call', _53 => _53(node.id)]);
2943
3006
  if (el) {
2944
3007
  const projectedText = el.textContent || "";
2945
- if (projectedText !== "" && projectedText !== proj.text) {
3008
+ const windowed = el.dataset.vectoProjectionWindow !== void 0;
3009
+ const mismatched = windowed ? !proj.text.includes(projectedText) : projectedText !== proj.text;
3010
+ if (projectedText !== "" && mismatched) {
2946
3011
  this._devWarn(
2947
3012
  `Content projection mismatch for entity "${node.id}": projection says "${proj.text.slice(0, 60)}" but DOM shows "${projectedText.slice(0, 60)}". Ensure getContentProjection() output matches what drawSelf renders.`
2948
3013
  );
@@ -2954,14 +3019,14 @@ var Scene = (_class7 = class _Scene {
2954
3019
  };
2955
3020
  walkProjections(this.root);
2956
3021
  }
2957
- constructor(canvas, options = {}) {;_class7.prototype.__init27.call(this);_class7.prototype.__init28.call(this);_class7.prototype.__init29.call(this);_class7.prototype.__init30.call(this);_class7.prototype.__init31.call(this);_class7.prototype.__init32.call(this);_class7.prototype.__init33.call(this);_class7.prototype.__init34.call(this);_class7.prototype.__init35.call(this);_class7.prototype.__init36.call(this);_class7.prototype.__init37.call(this);_class7.prototype.__init38.call(this);_class7.prototype.__init39.call(this);_class7.prototype.__init40.call(this);_class7.prototype.__init41.call(this);_class7.prototype.__init42.call(this);_class7.prototype.__init43.call(this);_class7.prototype.__init44.call(this);_class7.prototype.__init45.call(this);_class7.prototype.__init46.call(this);_class7.prototype.__init47.call(this);_class7.prototype.__init48.call(this);_class7.prototype.__init49.call(this);_class7.prototype.__init50.call(this);_class7.prototype.__init51.call(this);_class7.prototype.__init52.call(this);_class7.prototype.__init53.call(this);_class7.prototype.__init54.call(this);_class7.prototype.__init55.call(this);_class7.prototype.__init56.call(this);_class7.prototype.__init57.call(this);_class7.prototype.__init58.call(this);_class7.prototype.__init59.call(this);_class7.prototype.__init60.call(this);_class7.prototype.__init61.call(this);_class7.prototype.__init62.call(this);_class7.prototype.__init63.call(this);_class7.prototype.__init64.call(this);_class7.prototype.__init65.call(this);_class7.prototype.__init66.call(this);_class7.prototype.__init67.call(this);_class7.prototype.__init68.call(this);_class7.prototype.__init69.call(this);_class7.prototype.__init70.call(this);_class7.prototype.__init71.call(this);_class7.prototype.__init72.call(this);_class7.prototype.__init73.call(this);_class7.prototype.__init74.call(this);_class7.prototype.__init75.call(this);_class7.prototype.__init76.call(this);_class7.prototype.__init77.call(this);_class7.prototype.__init78.call(this);_class7.prototype.__init79.call(this);_class7.prototype.__init80.call(this);_class7.prototype.__init81.call(this);_class7.prototype.__init82.call(this);_class7.prototype.__init83.call(this);_class7.prototype.__init84.call(this);_class7.prototype.__init85.call(this);_class7.prototype.__init86.call(this);_class7.prototype.__init87.call(this);_class7.prototype.__init88.call(this);_class7.prototype.__init89.call(this);_class7.prototype.__init90.call(this);_class7.prototype.__init91.call(this);_class7.prototype.__init92.call(this);_class7.prototype.__init93.call(this);_class7.prototype.__init94.call(this);_class7.prototype.__init95.call(this);_class7.prototype.__init96.call(this);_class7.prototype.__init97.call(this);_class7.prototype.__init98.call(this);_class7.prototype.__init99.call(this);_class7.prototype.__init100.call(this);_class7.prototype.__init101.call(this);_class7.prototype.__init102.call(this);_class7.prototype.__init103.call(this);_class7.prototype.__init104.call(this);_class7.prototype.__init105.call(this);_class7.prototype.__init106.call(this);_class7.prototype.__init107.call(this);_class7.prototype.__init108.call(this);_class7.prototype.__init109.call(this);_class7.prototype.__init110.call(this);_class7.prototype.__init111.call(this);_class7.prototype.__init112.call(this);_class7.prototype.__init113.call(this);_class7.prototype.__init114.call(this);_class7.prototype.__init115.call(this);_class7.prototype.__init116.call(this);_class7.prototype.__init117.call(this);_class7.prototype.__init118.call(this);_class7.prototype.__init119.call(this);_class7.prototype.__init120.call(this);_class7.prototype.__init121.call(this);_class7.prototype.__init122.call(this);_class7.prototype.__init123.call(this);_class7.prototype.__init124.call(this);_class7.prototype.__init125.call(this);_class7.prototype.__init126.call(this);_class7.prototype.__init127.call(this);_class7.prototype.__init128.call(this);_class7.prototype.__init129.call(this);_class7.prototype.__init130.call(this);_class7.prototype.__init131.call(this);_class7.prototype.__init132.call(this);_class7.prototype.__init133.call(this);_class7.prototype.__init134.call(this);_class7.prototype.__init135.call(this);_class7.prototype.__init136.call(this);_class7.prototype.__init137.call(this);_class7.prototype.__init138.call(this);_class7.prototype.__init139.call(this);_class7.prototype.__init140.call(this);_class7.prototype.__init141.call(this);_class7.prototype.__init142.call(this);_class7.prototype.__init143.call(this);_class7.prototype.__init144.call(this);_class7.prototype.__init145.call(this);_class7.prototype.__init146.call(this);_class7.prototype.__init147.call(this);_class7.prototype.__init148.call(this);_class7.prototype.__init149.call(this);_class7.prototype.__init150.call(this);
3022
+ constructor(canvas, options = {}) {;_class7.prototype.__init27.call(this);_class7.prototype.__init28.call(this);_class7.prototype.__init29.call(this);_class7.prototype.__init30.call(this);_class7.prototype.__init31.call(this);_class7.prototype.__init32.call(this);_class7.prototype.__init33.call(this);_class7.prototype.__init34.call(this);_class7.prototype.__init35.call(this);_class7.prototype.__init36.call(this);_class7.prototype.__init37.call(this);_class7.prototype.__init38.call(this);_class7.prototype.__init39.call(this);_class7.prototype.__init40.call(this);_class7.prototype.__init41.call(this);_class7.prototype.__init42.call(this);_class7.prototype.__init43.call(this);_class7.prototype.__init44.call(this);_class7.prototype.__init45.call(this);_class7.prototype.__init46.call(this);_class7.prototype.__init47.call(this);_class7.prototype.__init48.call(this);_class7.prototype.__init49.call(this);_class7.prototype.__init50.call(this);_class7.prototype.__init51.call(this);_class7.prototype.__init52.call(this);_class7.prototype.__init53.call(this);_class7.prototype.__init54.call(this);_class7.prototype.__init55.call(this);_class7.prototype.__init56.call(this);_class7.prototype.__init57.call(this);_class7.prototype.__init58.call(this);_class7.prototype.__init59.call(this);_class7.prototype.__init60.call(this);_class7.prototype.__init61.call(this);_class7.prototype.__init62.call(this);_class7.prototype.__init63.call(this);_class7.prototype.__init64.call(this);_class7.prototype.__init65.call(this);_class7.prototype.__init66.call(this);_class7.prototype.__init67.call(this);_class7.prototype.__init68.call(this);_class7.prototype.__init69.call(this);_class7.prototype.__init70.call(this);_class7.prototype.__init71.call(this);_class7.prototype.__init72.call(this);_class7.prototype.__init73.call(this);_class7.prototype.__init74.call(this);_class7.prototype.__init75.call(this);_class7.prototype.__init76.call(this);_class7.prototype.__init77.call(this);_class7.prototype.__init78.call(this);_class7.prototype.__init79.call(this);_class7.prototype.__init80.call(this);_class7.prototype.__init81.call(this);_class7.prototype.__init82.call(this);_class7.prototype.__init83.call(this);_class7.prototype.__init84.call(this);_class7.prototype.__init85.call(this);_class7.prototype.__init86.call(this);_class7.prototype.__init87.call(this);_class7.prototype.__init88.call(this);_class7.prototype.__init89.call(this);_class7.prototype.__init90.call(this);_class7.prototype.__init91.call(this);_class7.prototype.__init92.call(this);_class7.prototype.__init93.call(this);_class7.prototype.__init94.call(this);_class7.prototype.__init95.call(this);_class7.prototype.__init96.call(this);_class7.prototype.__init97.call(this);_class7.prototype.__init98.call(this);_class7.prototype.__init99.call(this);_class7.prototype.__init100.call(this);_class7.prototype.__init101.call(this);_class7.prototype.__init102.call(this);_class7.prototype.__init103.call(this);_class7.prototype.__init104.call(this);_class7.prototype.__init105.call(this);_class7.prototype.__init106.call(this);_class7.prototype.__init107.call(this);_class7.prototype.__init108.call(this);_class7.prototype.__init109.call(this);_class7.prototype.__init110.call(this);_class7.prototype.__init111.call(this);_class7.prototype.__init112.call(this);_class7.prototype.__init113.call(this);_class7.prototype.__init114.call(this);_class7.prototype.__init115.call(this);_class7.prototype.__init116.call(this);_class7.prototype.__init117.call(this);_class7.prototype.__init118.call(this);_class7.prototype.__init119.call(this);_class7.prototype.__init120.call(this);_class7.prototype.__init121.call(this);_class7.prototype.__init122.call(this);_class7.prototype.__init123.call(this);_class7.prototype.__init124.call(this);_class7.prototype.__init125.call(this);_class7.prototype.__init126.call(this);_class7.prototype.__init127.call(this);_class7.prototype.__init128.call(this);_class7.prototype.__init129.call(this);_class7.prototype.__init130.call(this);_class7.prototype.__init131.call(this);_class7.prototype.__init132.call(this);_class7.prototype.__init133.call(this);_class7.prototype.__init134.call(this);_class7.prototype.__init135.call(this);_class7.prototype.__init136.call(this);_class7.prototype.__init137.call(this);_class7.prototype.__init138.call(this);_class7.prototype.__init139.call(this);_class7.prototype.__init140.call(this);_class7.prototype.__init141.call(this);_class7.prototype.__init142.call(this);_class7.prototype.__init143.call(this);_class7.prototype.__init144.call(this);_class7.prototype.__init145.call(this);_class7.prototype.__init146.call(this);_class7.prototype.__init147.call(this);_class7.prototype.__init148.call(this);_class7.prototype.__init149.call(this);_class7.prototype.__init150.call(this);_class7.prototype.__init151.call(this);_class7.prototype.__init152.call(this);_class7.prototype.__init153.call(this);
2958
3023
  this.canvas = canvas;
2959
3024
  this.debugA11y = _nullishCoalesce(options.debugA11y, () => ( false));
2960
3025
  this.disableWindowResize = _nullishCoalesce(options.disableWindowResize, () => ( false));
2961
3026
  this.maxDPR = options.maxDPR;
2962
3027
  if (this.disableWindowResize) {
2963
- const styleWidth = parseInlinePx(_optionalChain([canvas, 'access', _51 => _51.style, 'optionalAccess', _52 => _52.width]));
2964
- const styleHeight = parseInlinePx(_optionalChain([canvas, 'access', _53 => _53.style, 'optionalAccess', _54 => _54.height]));
3028
+ const styleWidth = parseInlinePx(_optionalChain([canvas, 'access', _54 => _54.style, 'optionalAccess', _55 => _55.width]));
3029
+ const styleHeight = parseInlinePx(_optionalChain([canvas, 'access', _56 => _56.style, 'optionalAccess', _57 => _57.height]));
2965
3030
  this.width = _nullishCoalesce(styleWidth, () => ( (canvas.width || canvas.clientWidth || 0)));
2966
3031
  this.height = _nullishCoalesce(styleHeight, () => ( (canvas.height || canvas.clientHeight || 0)));
2967
3032
  } else {
@@ -2969,7 +3034,7 @@ var Scene = (_class7 = class _Scene {
2969
3034
  this.height = typeof window !== "undefined" ? window.innerHeight : canvas.clientHeight || canvas.height || 600;
2970
3035
  }
2971
3036
  const globalProcess = typeof globalThis !== "undefined" ? globalThis.process : void 0;
2972
- const isTest = globalProcess && (_optionalChain([globalProcess, 'access', _55 => _55.env, 'optionalAccess', _56 => _56.NODE_ENV]) === "test" || _optionalChain([globalProcess, 'access', _57 => _57.env, 'optionalAccess', _58 => _58.VITEST]) === "true");
3037
+ const isTest = globalProcess && (_optionalChain([globalProcess, 'access', _58 => _58.env, 'optionalAccess', _59 => _59.NODE_ENV]) === "test" || _optionalChain([globalProcess, 'access', _60 => _60.env, 'optionalAccess', _61 => _61.VITEST]) === "true");
2973
3038
  this.maxFPS = _nullishCoalesce(options.maxFPS, () => ( (isTest ? 0 : 60)));
2974
3039
  this.respectReducedMotion = _nullishCoalesce(options.respectReducedMotion, () => ( true));
2975
3040
  this.autoThrottle = _nullishCoalesce(options.autoThrottle, () => ( true));
@@ -2978,6 +3043,7 @@ var Scene = (_class7 = class _Scene {
2978
3043
  this.a11ySyncInterval = _nullishCoalesce(options.a11ySyncInterval, () => ( 0));
2979
3044
  this.contentProjectionEnabled = _nullishCoalesce(options.contentProjection, () => ( true));
2980
3045
  this.contentProjectionMargin = options.contentProjectionMargin;
3046
+ this.contentSemanticMargin = options.contentSemanticMargin;
2981
3047
  this.readingDirection = _nullishCoalesce(options.readingDirection, () => ( "ltr"));
2982
3048
  this.renderMode = _nullishCoalesce(options.renderMode, () => ( "always"));
2983
3049
  this._devActive = _Scene._devModeDetected();
@@ -2987,9 +3053,9 @@ var Scene = (_class7 = class _Scene {
2987
3053
  if (typeof window !== "undefined" && typeof window.matchMedia === "function") {
2988
3054
  this.forcedColorsQuery = window.matchMedia("(forced-colors: active)");
2989
3055
  this.forcedColorsChangeHandler = () => this.markDirty();
2990
- _optionalChain([this, 'access', _59 => _59.forcedColorsQuery, 'access', _60 => _60.addEventListener, 'optionalCall', _61 => _61("change", this.forcedColorsChangeHandler)]);
3056
+ _optionalChain([this, 'access', _62 => _62.forcedColorsQuery, 'access', _63 => _63.addEventListener, 'optionalCall', _64 => _64("change", this.forcedColorsChangeHandler)]);
2991
3057
  }
2992
- this.root = new class RootEntity extends _chunkZ3HFY75Rjs.Entity {
3058
+ this.root = new class RootEntity extends _chunkTRT2TRCWjs.Entity {
2993
3059
  isPointInside() {
2994
3060
  return false;
2995
3061
  }
@@ -2998,7 +3064,7 @@ var Scene = (_class7 = class _Scene {
2998
3064
  }
2999
3065
  }("root");
3000
3066
  this.root._scene = this;
3001
- this.overlayRoot = new class OverlayRoot extends _chunkZ3HFY75Rjs.Entity {
3067
+ this.overlayRoot = new class OverlayRoot extends _chunkTRT2TRCWjs.Entity {
3002
3068
  isPointInside() {
3003
3069
  return false;
3004
3070
  }
@@ -3015,9 +3081,9 @@ var Scene = (_class7 = class _Scene {
3015
3081
  this.maxDPR
3016
3082
  );
3017
3083
  }
3018
- _optionalChain([this, 'access', _62 => _62.renderer, 'access', _63 => _63.onContextRestored, 'optionalCall', _64 => _64(() => {
3084
+ _optionalChain([this, 'access', _65 => _65.renderer, 'access', _66 => _66.onContextRestored, 'optionalCall', _67 => _67(() => {
3019
3085
  this.markDirty();
3020
- if (_optionalChain([this, 'access', _65 => _65.renderer, 'access', _66 => _66.isContextLost, 'optionalCall', _67 => _67()]) !== true) this.render(this.renderer);
3086
+ if (_optionalChain([this, 'access', _68 => _68.renderer, 'access', _69 => _69.isContextLost, 'optionalCall', _70 => _70()]) !== true) this.render(this.renderer);
3021
3087
  })]);
3022
3088
  if (typeof document !== "undefined") {
3023
3089
  this.a11yRoot = document.createElement("div");
@@ -3187,7 +3253,7 @@ var Scene = (_class7 = class _Scene {
3187
3253
  watchDevicePixelRatio() {
3188
3254
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
3189
3255
  if (this.dprMediaQuery && this.dprChangeHandler) {
3190
- _optionalChain([this, 'access', _68 => _68.dprMediaQuery, 'access', _69 => _69.removeEventListener, 'optionalCall', _70 => _70("change", this.dprChangeHandler)]);
3256
+ _optionalChain([this, 'access', _71 => _71.dprMediaQuery, 'access', _72 => _72.removeEventListener, 'optionalCall', _73 => _73("change", this.dprChangeHandler)]);
3191
3257
  }
3192
3258
  const dpr = window.devicePixelRatio || 1;
3193
3259
  const query = window.matchMedia(`(resolution: ${dpr}dppx)`);
@@ -3195,7 +3261,7 @@ var Scene = (_class7 = class _Scene {
3195
3261
  this.resize(this.width, this.height);
3196
3262
  this.watchDevicePixelRatio();
3197
3263
  };
3198
- _optionalChain([query, 'access', _71 => _71.addEventListener, 'optionalCall', _72 => _72("change", handler)]);
3264
+ _optionalChain([query, 'access', _74 => _74.addEventListener, 'optionalCall', _75 => _75("change", handler)]);
3199
3265
  this.dprMediaQuery = query;
3200
3266
  this.dprChangeHandler = handler;
3201
3267
  }
@@ -3215,7 +3281,7 @@ var Scene = (_class7 = class _Scene {
3215
3281
  if (typeof gl.addEventListener !== "function") return;
3216
3282
  this.glContextLostHandler = (e) => {
3217
3283
  e.preventDefault();
3218
- _optionalChain([this, 'access', _73 => _73.pointRenderer, 'optionalAccess', _74 => _74.destroy, 'call', _75 => _75()]);
3284
+ _optionalChain([this, 'access', _76 => _76.pointRenderer, 'optionalAccess', _77 => _77.destroy, 'call', _78 => _78()]);
3219
3285
  this.pointRenderer = null;
3220
3286
  };
3221
3287
  this.glContextRestoredHandler = () => {
@@ -3245,26 +3311,26 @@ var Scene = (_class7 = class _Scene {
3245
3311
  * to the live DOM selection.
3246
3312
  */
3247
3313
  contentGridSelectionLine(el) {
3248
- const candidates = [_optionalChain([this, 'access', _76 => _76.contentSelectionAnchor, 'optionalAccess', _77 => _77.node])];
3314
+ const candidates = [_optionalChain([this, 'access', _79 => _79.contentSelectionAnchor, 'optionalAccess', _80 => _80.node])];
3249
3315
  if (typeof window !== "undefined" && typeof window.getSelection === "function") {
3250
3316
  const selection = window.getSelection();
3251
- candidates.push(_optionalChain([selection, 'optionalAccess', _78 => _78.anchorNode]), _optionalChain([selection, 'optionalAccess', _79 => _79.focusNode]));
3317
+ candidates.push(_optionalChain([selection, 'optionalAccess', _81 => _81.anchorNode]), _optionalChain([selection, 'optionalAccess', _82 => _82.focusNode]));
3252
3318
  }
3253
3319
  for (const candidate of candidates) {
3254
3320
  if (!candidate || !el.contains(candidate)) continue;
3255
3321
  let cursor = candidate;
3256
3322
  while (cursor && cursor.parentNode !== el) cursor = cursor.parentNode;
3257
- const lineIndex = _optionalChain([cursor, 'optionalAccess', _80 => _80.dataset, 'optionalAccess', _81 => _81.vectoGridLine]);
3323
+ const lineIndex = _optionalChain([cursor, 'optionalAccess', _83 => _83.dataset, 'optionalAccess', _84 => _84.vectoGridLine]);
3258
3324
  if (lineIndex !== void 0) return Number(lineIndex);
3259
3325
  }
3260
3326
  return null;
3261
3327
  }
3262
3328
  releaseContentSelectionForRebuild(el) {
3263
3329
  const selection = typeof window !== "undefined" && typeof window.getSelection === "function" ? window.getSelection() : null;
3264
- const ownsSelection = this.contentSelectionAnchor && el.contains(this.contentSelectionAnchor.node) || (_optionalChain([selection, 'optionalAccess', _82 => _82.anchorNode]) ? el.contains(selection.anchorNode) : false) || (_optionalChain([selection, 'optionalAccess', _83 => _83.focusNode]) ? el.contains(selection.focusNode) : false);
3330
+ const ownsSelection = this.contentSelectionAnchor && el.contains(this.contentSelectionAnchor.node) || (_optionalChain([selection, 'optionalAccess', _85 => _85.anchorNode]) ? el.contains(selection.anchorNode) : false) || (_optionalChain([selection, 'optionalAccess', _86 => _86.focusNode]) ? el.contains(selection.focusNode) : false);
3265
3331
  if (!ownsSelection) return;
3266
3332
  this.endContentSelectionDrag();
3267
- _optionalChain([selection, 'optionalAccess', _84 => _84.removeAllRanges, 'call', _85 => _85()]);
3333
+ _optionalChain([selection, 'optionalAccess', _87 => _87.removeAllRanges, 'call', _88 => _88()]);
3268
3334
  }
3269
3335
  /**
3270
3336
  * Rebuild a content-projection element's DOM (`rebuild`) while preserving a
@@ -3323,7 +3389,7 @@ var Scene = (_class7 = class _Scene {
3323
3389
  }
3324
3390
  /** Convert browser viewport coordinates into this Scene's logical coordinates. */
3325
3391
  clientToScene(clientX, clientY) {
3326
- const rect = _optionalChain([this, 'access', _86 => _86.canvas, 'access', _87 => _87.getBoundingClientRect, 'optionalCall', _88 => _88()]);
3392
+ const rect = _optionalChain([this, 'access', _89 => _89.canvas, 'access', _90 => _90.getBoundingClientRect, 'optionalCall', _91 => _91()]);
3327
3393
  if (!rect) return { x: clientX, y: clientY };
3328
3394
  const cssWidth = rect.width || this.canvas.clientWidth || this.width;
3329
3395
  const cssHeight = rect.height || this.canvas.clientHeight || this.height;
@@ -3361,7 +3427,7 @@ var Scene = (_class7 = class _Scene {
3361
3427
  cancelAnimationFrame(calibrationFrame);
3362
3428
  }
3363
3429
  this.contentGridCalibrationFrames.delete(entityId);
3364
- _optionalChain([this, 'access', _89 => _89.contentGridCalibrationProbes, 'access', _90 => _90.get, 'call', _91 => _91(entityId), 'optionalAccess', _92 => _92.remove, 'call', _93 => _93()]);
3430
+ _optionalChain([this, 'access', _92 => _92.contentGridCalibrationProbes, 'access', _93 => _93.get, 'call', _94 => _94(entityId), 'optionalAccess', _95 => _95.remove, 'call', _96 => _96()]);
3365
3431
  this.contentGridCalibrationProbes.delete(entityId);
3366
3432
  delete el.dataset.vectoGridCalibrationPending;
3367
3433
  delete el.dataset.vectoGridCalibration;
@@ -3400,6 +3466,7 @@ var Scene = (_class7 = class _Scene {
3400
3466
  this.clearContentGridState(node.id, contentEl);
3401
3467
  contentEl.remove();
3402
3468
  this.contentElements.delete(node.id);
3469
+ this.contentSyncState.delete(node.id);
3403
3470
  this.a11yNeedsReorder = true;
3404
3471
  }
3405
3472
  const el = this.a11yElements.get(node.id);
@@ -3413,7 +3480,7 @@ var Scene = (_class7 = class _Scene {
3413
3480
  }
3414
3481
  if (this.hoveredA11yElements.has(el)) {
3415
3482
  this.hoveredA11yElements.delete(el);
3416
- node.dispatchEvent(new (0, _chunkZ3HFY75Rjs.VectoJSEvent)("pointerleave", node, void 0, false));
3483
+ node.dispatchEvent(new (0, _chunkTRT2TRCWjs.VectoJSEvent)("pointerleave", node, void 0, false));
3417
3484
  }
3418
3485
  this.preserveFocusOnRemoval(el);
3419
3486
  el.remove();
@@ -3515,12 +3582,12 @@ var Scene = (_class7 = class _Scene {
3515
3582
  this.canvasResizeObserver = null;
3516
3583
  }
3517
3584
  if (this.dprMediaQuery && this.dprChangeHandler) {
3518
- _optionalChain([this, 'access', _94 => _94.dprMediaQuery, 'access', _95 => _95.removeEventListener, 'optionalCall', _96 => _96("change", this.dprChangeHandler)]);
3585
+ _optionalChain([this, 'access', _97 => _97.dprMediaQuery, 'access', _98 => _98.removeEventListener, 'optionalCall', _99 => _99("change", this.dprChangeHandler)]);
3519
3586
  this.dprMediaQuery = null;
3520
3587
  this.dprChangeHandler = null;
3521
3588
  }
3522
3589
  if (this.forcedColorsQuery && this.forcedColorsChangeHandler) {
3523
- _optionalChain([this, 'access', _97 => _97.forcedColorsQuery, 'access', _98 => _98.removeEventListener, 'optionalCall', _99 => _99("change", this.forcedColorsChangeHandler)]);
3590
+ _optionalChain([this, 'access', _100 => _100.forcedColorsQuery, 'access', _101 => _101.removeEventListener, 'optionalCall', _102 => _102("change", this.forcedColorsChangeHandler)]);
3524
3591
  this.forcedColorsQuery = null;
3525
3592
  this.forcedColorsChangeHandler = null;
3526
3593
  }
@@ -3538,12 +3605,13 @@ var Scene = (_class7 = class _Scene {
3538
3605
  }
3539
3606
  this.pointerEventTarget = null;
3540
3607
  }
3541
- _optionalChain([this, 'access', _100 => _100.a11yRoot, 'optionalAccess', _101 => _101.remove, 'call', _102 => _102()]);
3608
+ _optionalChain([this, 'access', _103 => _103.a11yRoot, 'optionalAccess', _104 => _104.remove, 'call', _105 => _105()]);
3542
3609
  this.focusSentinel = null;
3543
- _optionalChain([this, 'access', _103 => _103.portalRoot, 'optionalAccess', _104 => _104.remove, 'call', _105 => _105()]);
3610
+ _optionalChain([this, 'access', _106 => _106.portalRoot, 'optionalAccess', _107 => _107.remove, 'call', _108 => _108()]);
3544
3611
  this.a11yElements.clear();
3545
3612
  for (const el of this.contentElements.values()) el.remove();
3546
3613
  this.contentElements.clear();
3614
+ this.contentSyncState.clear();
3547
3615
  if (typeof cancelAnimationFrame === "function") {
3548
3616
  for (const frame of this.contentGridCalibrationFrames.values()) {
3549
3617
  cancelAnimationFrame(frame);
@@ -3563,10 +3631,10 @@ var Scene = (_class7 = class _Scene {
3563
3631
  }
3564
3632
  this.glContextLostHandler = null;
3565
3633
  this.glContextRestoredHandler = null;
3566
- _optionalChain([this, 'access', _106 => _106.pointRenderer, 'optionalAccess', _107 => _107.destroy, 'call', _108 => _108()]);
3567
- _optionalChain([this, 'access', _109 => _109.renderer, 'access', _110 => _110.dispose, 'optionalCall', _111 => _111()]);
3568
- _optionalChain([this, 'access', _112 => _112.glCanvas, 'optionalAccess', _113 => _113.remove, 'call', _114 => _114()]);
3569
- _optionalChain([this, 'access', _115 => _115.gpuCanvas, 'optionalAccess', _116 => _116.remove, 'call', _117 => _117()]);
3634
+ _optionalChain([this, 'access', _109 => _109.pointRenderer, 'optionalAccess', _110 => _110.destroy, 'call', _111 => _111()]);
3635
+ _optionalChain([this, 'access', _112 => _112.renderer, 'access', _113 => _113.dispose, 'optionalCall', _114 => _114()]);
3636
+ _optionalChain([this, 'access', _115 => _115.glCanvas, 'optionalAccess', _116 => _116.remove, 'call', _117 => _117()]);
3637
+ _optionalChain([this, 'access', _118 => _118.gpuCanvas, 'optionalAccess', _119 => _119.remove, 'call', _120 => _120()]);
3570
3638
  this.gpuCanvas = null;
3571
3639
  this.gpuContext = null;
3572
3640
  if (this.recoveryTimerId) {
@@ -3578,7 +3646,7 @@ var Scene = (_class7 = class _Scene {
3578
3646
  this.manager = null;
3579
3647
  }
3580
3648
  if (this.device) {
3581
- _optionalChain([this, 'access', _118 => _118.device, 'access', _119 => _119.destroy, 'optionalCall', _120 => _120()]);
3649
+ _optionalChain([this, 'access', _121 => _121.device, 'access', _122 => _122.destroy, 'optionalCall', _123 => _123()]);
3582
3650
  this.device = null;
3583
3651
  }
3584
3652
  }
@@ -3886,7 +3954,92 @@ var Scene = (_class7 = class _Scene {
3886
3954
  * predicate, which is only tractable while it has one home.
3887
3955
  */
3888
3956
  shouldProjectA11y(node) {
3889
- return node.interactive && (node.width > 0 || node.a11yFullViewport);
3957
+ if (!node.interactive) return false;
3958
+ if (!(node.width > 0 || node.a11yFullViewport)) return false;
3959
+ switch (node.a11yProjection) {
3960
+ case "never":
3961
+ return false;
3962
+ case "onDemand":
3963
+ return this.a11yEngaged(node);
3964
+ default:
3965
+ return true;
3966
+ }
3967
+ }
3968
+ /**
3969
+ * Whether an `a11yProjection: 'onDemand'` entity is currently engaged enough to
3970
+ * deserve a shadow node.
3971
+ *
3972
+ * Deliberately **not** hover alone. A keyboard or assistive-technology user
3973
+ * generates no pointer events, so a hover-only trigger would withhold the
3974
+ * semantic node from precisely the users it exists for. Three signals, any of
3975
+ * which counts:
3976
+ *
3977
+ * - **Focus.** Covers keyboard traversal and AT-driven focus. Checked against
3978
+ * the live element so a node keeps its own focus rather than being pruned out
3979
+ * from under the user mid-interaction.
3980
+ * - **Pointer target.** The entity under the pointer, so a mouse user gets the
3981
+ * same node a hover-gated design would have given them.
3982
+ * - **Explicit request.** {@link Scene.requestA11yProjection}, for anything the
3983
+ * app knows is significant — the selected item, a search hit, a
3984
+ * just-announced element. This is the escape hatch that keeps the mode usable
3985
+ * when neither focus nor pointer applies.
3986
+ *
3987
+ * The entity stays hit-testable on canvas regardless, so a click always reaches
3988
+ * it and promotes it on the next sync.
3989
+ */
3990
+ a11yEngaged(node) {
3991
+ if (this.a11yProjectionRequests.has(node.id)) return true;
3992
+ if (this.mouseX > -9e3 && !this.projectsSelectableText(node)) {
3993
+ if (node.isPointInside(this.mouseX, this.mouseY)) return true;
3994
+ }
3995
+ const existing = _optionalChain([this, 'access', _124 => _124.a11yElements, 'optionalAccess', _125 => _125.get, 'call', _126 => _126(node.id)]);
3996
+ if (existing && typeof document !== "undefined" && document.activeElement === existing) {
3997
+ return true;
3998
+ }
3999
+ return false;
4000
+ }
4001
+ /**
4002
+ * Whether `node` mirrors selectable text of its own.
4003
+ *
4004
+ * Such an entity must not be promoted by the pointer: its interactive a11y node
4005
+ * would sit above the text mirror and eat the mousedown that starts a native
4006
+ * selection.
4007
+ */
4008
+ projectsSelectableText(node) {
4009
+ const projection = _optionalChain([node, 'access', _127 => _127.getContentProjection, 'optionalCall', _128 => _128()]);
4010
+ return !!_optionalChain([projection, 'optionalAccess', _129 => _129.text]) && projection.selectable !== false;
4011
+ }
4012
+ /**
4013
+ * Keep `entity`'s a11y shadow node projected while it has
4014
+ * `a11yProjection: 'onDemand'`.
4015
+ *
4016
+ * For anything the application knows matters but the engine cannot infer — the
4017
+ * selected danmaku, a search hit, a node just announced in a live region.
4018
+ * Without this, `'onDemand'` would be reachable only by focus or pointer, and
4019
+ * an app-driven selection change would leave the selected entity semantically
4020
+ * invisible.
4021
+ *
4022
+ * Idempotent. Has no effect on an `'eager'` entity, which is always projected.
4023
+ */
4024
+ requestA11yProjection(entity) {
4025
+ const id = typeof entity === "string" ? entity : entity.id;
4026
+ if (this.a11yProjectionRequests.has(id)) return;
4027
+ this.a11yProjectionRequests.add(id);
4028
+ this.a11yNeedsReorder = true;
4029
+ this.markDirty({ entity: id, reason: "a11y-reorder" });
4030
+ }
4031
+ /**
4032
+ * Drop a projection request made by {@link requestA11yProjection}.
4033
+ *
4034
+ * The node is not removed immediately: it survives while it is focused or under
4035
+ * the pointer, and is pruned on the next sync that finds it unengaged. Releasing
4036
+ * a request the scene does not hold is a no-op.
4037
+ */
4038
+ releaseA11yProjection(entity) {
4039
+ const id = typeof entity === "string" ? entity : entity.id;
4040
+ if (!this.a11yProjectionRequests.delete(id)) return;
4041
+ this.a11yNeedsReorder = true;
4042
+ this.markDirty({ entity: id, reason: "a11y-reorder" });
3890
4043
  }
3891
4044
  syncA11y(node, container = null) {
3892
4045
  if (!this.a11yRoot) return;
@@ -3941,20 +4094,20 @@ var Scene = (_class7 = class _Scene {
3941
4094
  el.style.background = "transparent";
3942
4095
  }
3943
4096
  el.addEventListener("click", (e) => {
3944
- node.dispatchEvent(new (0, _chunkZ3HFY75Rjs.VectoJSEvent)("click", node, e));
4097
+ node.dispatchEvent(new (0, _chunkTRT2TRCWjs.VectoJSEvent)("click", node, e));
3945
4098
  });
3946
4099
  el.addEventListener("dblclick", (e) => {
3947
- node.dispatchEvent(new (0, _chunkZ3HFY75Rjs.VectoJSEvent)("dblclick", node, e));
4100
+ node.dispatchEvent(new (0, _chunkTRT2TRCWjs.VectoJSEvent)("dblclick", node, e));
3948
4101
  });
3949
4102
  el.addEventListener("mouseenter", (e) => {
3950
4103
  if (this.debugA11y) el.style.backgroundColor = "rgba(56, 189, 248, 0.2)";
3951
4104
  this.hoveredA11yElements.add(el);
3952
- node.dispatchEvent(new (0, _chunkZ3HFY75Rjs.VectoJSEvent)("hover", node, e, false));
4105
+ node.dispatchEvent(new (0, _chunkTRT2TRCWjs.VectoJSEvent)("hover", node, e, false));
3953
4106
  });
3954
4107
  el.addEventListener("mouseleave", (e) => {
3955
4108
  if (this.debugA11y) el.style.backgroundColor = "rgba(56, 189, 248, 0.05)";
3956
4109
  this.hoveredA11yElements.delete(el);
3957
- node.dispatchEvent(new (0, _chunkZ3HFY75Rjs.VectoJSEvent)("pointerleave", node, e, false));
4110
+ node.dispatchEvent(new (0, _chunkTRT2TRCWjs.VectoJSEvent)("pointerleave", node, e, false));
3958
4111
  });
3959
4112
  const capEl = el;
3960
4113
  const releasePointer = (event) => {
@@ -3970,24 +4123,24 @@ var Scene = (_class7 = class _Scene {
3970
4123
  };
3971
4124
  el.addEventListener("pointerdown", (e) => {
3972
4125
  if (typeof capEl.setPointerCapture === "function") capEl.setPointerCapture(e.pointerId);
3973
- node.dispatchEvent(new (0, _chunkZ3HFY75Rjs.VectoJSEvent)("pointerdown", node, e));
4126
+ node.dispatchEvent(new (0, _chunkTRT2TRCWjs.VectoJSEvent)("pointerdown", node, e));
3974
4127
  });
3975
4128
  el.addEventListener("pointerup", (e) => {
3976
4129
  releasePointer(e);
3977
- node.dispatchEvent(new (0, _chunkZ3HFY75Rjs.VectoJSEvent)("pointerup", node, e));
4130
+ node.dispatchEvent(new (0, _chunkTRT2TRCWjs.VectoJSEvent)("pointerup", node, e));
3978
4131
  });
3979
4132
  el.addEventListener("pointercancel", (e) => {
3980
4133
  releasePointer(e);
3981
- node.dispatchEvent(new (0, _chunkZ3HFY75Rjs.VectoJSEvent)("pointercancel", node, e));
4134
+ node.dispatchEvent(new (0, _chunkTRT2TRCWjs.VectoJSEvent)("pointercancel", node, e));
3982
4135
  });
3983
4136
  el.addEventListener(
3984
4137
  "pointermove",
3985
- (e) => node.dispatchEvent(new (0, _chunkZ3HFY75Rjs.VectoJSEvent)("pointermove", node, e))
4138
+ (e) => node.dispatchEvent(new (0, _chunkTRT2TRCWjs.VectoJSEvent)("pointermove", node, e))
3986
4139
  );
3987
4140
  el.addEventListener(
3988
4141
  "wheel",
3989
4142
  (e) => {
3990
- node.dispatchEvent(new (0, _chunkZ3HFY75Rjs.VectoJSEvent)("wheel", node, e));
4143
+ node.dispatchEvent(new (0, _chunkTRT2TRCWjs.VectoJSEvent)("wheel", node, e));
3991
4144
  },
3992
4145
  { passive: false }
3993
4146
  );
@@ -4012,10 +4165,10 @@ var Scene = (_class7 = class _Scene {
4012
4165
  );
4013
4166
  emitInitialScroll = emitScroll;
4014
4167
  el.addEventListener("keydown", (e) => {
4015
- node.dispatchEvent(new (0, _chunkZ3HFY75Rjs.VectoJSEvent)("keydown", node, e));
4168
+ node.dispatchEvent(new (0, _chunkTRT2TRCWjs.VectoJSEvent)("keydown", node, e));
4016
4169
  });
4017
4170
  el.addEventListener("keyup", (e) => {
4018
- node.dispatchEvent(new (0, _chunkZ3HFY75Rjs.VectoJSEvent)("keyup", node, e));
4171
+ node.dispatchEvent(new (0, _chunkTRT2TRCWjs.VectoJSEvent)("keyup", node, e));
4019
4172
  });
4020
4173
  if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {
4021
4174
  const input = el;
@@ -4046,7 +4199,7 @@ var Scene = (_class7 = class _Scene {
4046
4199
  el.addEventListener("compositionupdate", (e) => {
4047
4200
  const data = _nullishCoalesce(e.data, () => ( ""));
4048
4201
  composition = {
4049
- start: _nullishCoalesce(_optionalChain([composition, 'optionalAccess', _121 => _121.start]), () => ( 0)),
4202
+ start: _nullishCoalesce(_optionalChain([composition, 'optionalAccess', _130 => _130.start]), () => ( 0)),
4050
4203
  length: data.length
4051
4204
  };
4052
4205
  forward();
@@ -4081,7 +4234,7 @@ var Scene = (_class7 = class _Scene {
4081
4234
  el.addEventListener("keydown", (e) => {
4082
4235
  if (e.key === "Enter" || e.key === " ") {
4083
4236
  e.preventDefault();
4084
- node.dispatchEvent(new (0, _chunkZ3HFY75Rjs.VectoJSEvent)("click", node, e));
4237
+ node.dispatchEvent(new (0, _chunkTRT2TRCWjs.VectoJSEvent)("click", node, e));
4085
4238
  }
4086
4239
  });
4087
4240
  }
@@ -4288,7 +4441,7 @@ var Scene = (_class7 = class _Scene {
4288
4441
  const display = visible ? "" : "none";
4289
4442
  if (el.style.display !== display) el.style.display = display;
4290
4443
  }
4291
- _optionalChain([emitInitialScroll, 'optionalCall', _122 => _122()]);
4444
+ _optionalChain([emitInitialScroll, 'optionalCall', _131 => _131()]);
4292
4445
  }
4293
4446
  if (this._phaseTiming) {
4294
4447
  const projectionStart = performance.now();
@@ -4360,6 +4513,57 @@ var Scene = (_class7 = class _Scene {
4360
4513
  }
4361
4514
  return true;
4362
4515
  }
4516
+ /**
4517
+ * The band of an entity's own y coordinates that is worth projecting, or
4518
+ * `null` to project everything.
4519
+ *
4520
+ * {@link projectionBoxVisible} answers "is this entity near the viewport",
4521
+ * which frees whole blocks that scroll away. It cannot help a single entity
4522
+ * *taller* than the viewport: that entity's box always intersects, so every
4523
+ * one of its visual lines was materialized — a `<span>` per line and, on the
4524
+ * grid path, a `<span>` per glyph cluster. That is where "14.8k elements for a
4525
+ * 346KB Markdown doc" comes from, and it is O(document) rather than
4526
+ * O(viewport) in both element count and per-frame walk cost.
4527
+ *
4528
+ * Measured on one entity scrolled to its middle, real headed browsers
4529
+ * (`benchmarks/projection-per-line/`): at 4000 lines, materializing every line
4530
+ * costs 6.28 ms/frame on Chrome and 6.51 ms on Firefox with 36,000 child
4531
+ * elements, against 0.28/0.16 ms and 963 elements when only the visible band
4532
+ * is emitted. The gated cost is *flat* across a 20x document-size range, so
4533
+ * this converts an asymptote rather than shaving a constant.
4534
+ *
4535
+ * Returns local-y bounds in the entity's own coordinate space, already
4536
+ * expanded by `margin` and intersected with every `clipChildren` ancestor, so
4537
+ * a line inside a scrolled container is measured against the container rather
4538
+ * than the window. `null` means "no useful bound" — a degenerate transform, a
4539
+ * rotation/skew that makes a y-band meaningless, or a boundless entity — and
4540
+ * the caller must then project every line, because emitting nothing would
4541
+ * silently drop text from selection, find-in-page and screen readers.
4542
+ */
4543
+ projectionVisibleLocalYBand(node, tf, margin) {
4544
+ const { b, d, f } = tf;
4545
+ if (b !== 0 || d === 0 || !Number.isFinite(d) || !Number.isFinite(f)) return null;
4546
+ const top = (-margin - f) / d;
4547
+ const bottom = (this.height + margin - f) / d;
4548
+ let minY = Math.min(top, bottom);
4549
+ let maxY = Math.max(top, bottom);
4550
+ for (let ancestor = node.parent; ancestor; ancestor = ancestor.parent) {
4551
+ if (!ancestor.clipChildren || ancestor.width <= 0 || ancestor.height <= 0) continue;
4552
+ const originWorldY = f;
4553
+ const unitWorldY = d + f;
4554
+ const originLocal = ancestor.worldToLocal(tf.e, originWorldY);
4555
+ const unitLocal = ancestor.worldToLocal(tf.e + tf.c, unitWorldY);
4556
+ if (!originLocal || !unitLocal) return null;
4557
+ const slope = unitLocal.y - originLocal.y;
4558
+ if (slope === 0 || !Number.isFinite(slope)) return null;
4559
+ const a1 = (-margin - originLocal.y) / slope;
4560
+ const a2 = (ancestor.height + margin - originLocal.y) / slope;
4561
+ minY = Math.max(minY, Math.min(a1, a2));
4562
+ maxY = Math.min(maxY, Math.max(a1, a2));
4563
+ }
4564
+ if (!Number.isFinite(minY) || !Number.isFinite(maxY) || maxY < minY) return null;
4565
+ return { minY, maxY };
4566
+ }
4363
4567
  syncContentProjection(node) {
4364
4568
  if (!this.contentProjectionEnabled || !this.a11yRoot) return;
4365
4569
  let el = this.contentElements.get(node.id);
@@ -4370,14 +4574,30 @@ var Scene = (_class7 = class _Scene {
4370
4574
  this.contentElements.delete(node.id);
4371
4575
  this.a11yNeedsReorder = true;
4372
4576
  }
4577
+ this.contentSyncState.delete(node.id);
4373
4578
  };
4374
4579
  const worldTf = node.getWorldTransform();
4375
- const margin = _nullishCoalesce(this.contentProjectionMargin, () => ( this.height));
4376
- if (Number.isFinite(margin) && !this.projectionBoxVisible(node, worldTf, margin)) {
4580
+ const interactionMargin = _nullishCoalesce(this.contentProjectionMargin, () => ( this.height));
4581
+ const semanticMargin = _nullishCoalesce(this.contentSemanticMargin, () => ( interactionMargin));
4582
+ if (Number.isFinite(semanticMargin) && !this.projectionBoxVisible(node, worldTf, semanticMargin)) {
4377
4583
  releaseProjectionEl();
4378
4584
  return;
4379
4585
  }
4380
- const projection = node.getContentProjection();
4586
+ const inInteractionBand = !Number.isFinite(interactionMargin) || this.projectionBoxVisible(node, worldTf, interactionMargin);
4587
+ const tier = inInteractionBand ? "fine" : "coarse";
4588
+ const lineBand = tier === "coarse" ? null : Number.isFinite(interactionMargin) ? this.projectionVisibleLocalYBand(node, worldTf, interactionMargin) : null;
4589
+ const visible = this.projectionBoxVisible(node, worldTf, 0);
4590
+ const epoch = node.getContentEpoch();
4591
+ const prior = this.contentSyncState.get(node.id);
4592
+ if (epoch !== null && el && prior !== void 0) {
4593
+ const { a: a2, b: b2, c: c2, d: d2, e: e2, f: f2 } = worldTf;
4594
+ if (prior.epoch === epoch && prior.fontEpoch === this.contentFontEpoch && prior.a === a2 && prior.b === b2 && prior.c === c2 && prior.d === d2 && prior.e === e2 && prior.f === f2 && prior.tier === tier && prior.hasBand === (lineBand !== null) && (lineBand === null || prior.bandMin === lineBand.minY && prior.bandMax === lineBand.maxY) && prior.visible === visible && prior.width === node.width && prior.height === node.height && prior.interactive === node.interactive) {
4595
+ return;
4596
+ }
4597
+ }
4598
+ const projection = node.getContentProjection(
4599
+ lineBand ? { minY: lineBand.minY, maxY: lineBand.maxY } : void 0
4600
+ );
4381
4601
  if (!projection || !projection.text) {
4382
4602
  releaseProjectionEl();
4383
4603
  return;
@@ -4398,7 +4618,7 @@ var Scene = (_class7 = class _Scene {
4398
4618
  el.addEventListener(
4399
4619
  "wheel",
4400
4620
  (e2) => {
4401
- node.dispatchEvent(new (0, _chunkZ3HFY75Rjs.VectoJSEvent)("wheel", node, e2));
4621
+ node.dispatchEvent(new (0, _chunkTRT2TRCWjs.VectoJSEvent)("wheel", node, e2));
4402
4622
  },
4403
4623
  { passive: false }
4404
4624
  );
@@ -4407,23 +4627,30 @@ var Scene = (_class7 = class _Scene {
4407
4627
  this.a11yNeedsReorder = true;
4408
4628
  }
4409
4629
  const lines = projection.lines;
4410
- if (!projection.grid && el.dataset.vectoContentGrid !== void 0) {
4630
+ const useCarriers = tier === "fine";
4631
+ if ((!projection.grid || !useCarriers) && el.dataset.vectoContentGrid !== void 0) {
4411
4632
  this.clearContentGridState(node.id, el);
4412
4633
  }
4413
- if (projection.grid) {
4634
+ if (projection.grid && useCarriers) {
4414
4635
  const gridSyncStart = this._phaseTiming ? performance.now() : 0;
4415
- this.syncContentGridProjection(node, el, projection, projection.grid);
4636
+ this.syncContentGridProjection(node, el, projection, projection.grid, lineBand);
4416
4637
  if (this._phaseTiming) this._recordPhase("gridSync", performance.now() - gridSyncStart);
4417
- } else if (lines && lines.length > 0) {
4638
+ } else if (useCarriers && lines && lines.length > 0) {
4639
+ const lineWindow = projectionLineWindow(lines, lineBand, _nullishCoalesce(projection.lineHeight, () => ( 16)));
4418
4640
  const signature = JSON.stringify({
4419
4641
  lines,
4420
4642
  fallbackFont: _nullishCoalesce(projection.font, () => ( "")),
4421
- fallbackLineHeight: _nullishCoalesce(projection.lineHeight, () => ( 16))
4643
+ fallbackLineHeight: _nullishCoalesce(projection.lineHeight, () => ( 16)),
4644
+ // Part of the signature, or scrolling would not rebuild the carriers and
4645
+ // the window would stay frozen where it was first built. Quantized to
4646
+ // whole line indices by construction, so a sub-pixel scroll inside one
4647
+ // line does not churn the DOM.
4648
+ window: lineWindow.gated ? `${lineWindow.start}-${lineWindow.end}` : "all"
4422
4649
  });
4423
4650
  if (el.dataset.vectoProjectionLines !== signature) {
4424
4651
  this.preserveContentSelectionAcrossRebuild(el, () => {
4425
4652
  el.replaceChildren();
4426
- for (let index = 0; index < lines.length; index++) {
4653
+ for (let index = lineWindow.start; index < lineWindow.end; index++) {
4427
4654
  const line = lines[index];
4428
4655
  const lineElement = document.createElement("span");
4429
4656
  const lineFont = _nullishCoalesce(_nullishCoalesce(line.font, () => ( projection.font)), () => ( ""));
@@ -4464,13 +4691,20 @@ var Scene = (_class7 = class _Scene {
4464
4691
  }
4465
4692
  });
4466
4693
  el.dataset.vectoProjectionLines = signature;
4694
+ if (lineWindow.gated) {
4695
+ el.dataset.vectoProjectionWindow = `${lineWindow.start}-${lineWindow.end}/${lines.length}`;
4696
+ } else {
4697
+ delete el.dataset.vectoProjectionWindow;
4698
+ }
4467
4699
  }
4468
4700
  } else {
4469
- if (el.textContent !== projection.text) {
4701
+ const demoted = tier === "coarse" && el.children.length > 0;
4702
+ if (el.textContent !== projection.text || demoted) {
4470
4703
  this.releaseContentSelectionForRebuild(el);
4471
4704
  el.textContent = projection.text;
4472
4705
  }
4473
4706
  delete el.dataset.vectoProjectionLines;
4707
+ if (tier === "coarse") delete el.dataset.vectoProjectionWindow;
4474
4708
  }
4475
4709
  const font = _nullishCoalesce(projection.font, () => ( ""));
4476
4710
  if (el.style.font !== font) el.style.font = font;
@@ -4503,20 +4737,41 @@ var Scene = (_class7 = class _Scene {
4503
4737
  if (node.width > 0) el.style.width = `${node.width}px`;
4504
4738
  if (node.height > 0) el.style.height = `${node.height}px`;
4505
4739
  el.style.transform = `matrix(${a}, ${b}, ${c}, ${d}, 0, 0)`;
4506
- const visible = this.projectionBoxVisible(node, worldTf, 0);
4507
4740
  const display = visible ? "" : "none";
4508
4741
  if (el.style.display !== display) el.style.display = display;
4742
+ if (epoch !== null) {
4743
+ const { a: a2, b: b2, c: c2, d: d2, e: e2, f: f2 } = worldTf;
4744
+ const next = _nullishCoalesce(prior, () => ( {}));
4745
+ next.epoch = epoch;
4746
+ next.fontEpoch = this.contentFontEpoch;
4747
+ next.a = a2;
4748
+ next.b = b2;
4749
+ next.c = c2;
4750
+ next.d = d2;
4751
+ next.e = e2;
4752
+ next.f = f2;
4753
+ next.tier = tier;
4754
+ next.hasBand = lineBand !== null;
4755
+ next.bandMin = _nullishCoalesce(_optionalChain([lineBand, 'optionalAccess', _132 => _132.minY]), () => ( 0));
4756
+ next.bandMax = _nullishCoalesce(_optionalChain([lineBand, 'optionalAccess', _133 => _133.maxY]), () => ( 0));
4757
+ next.visible = visible;
4758
+ next.width = node.width;
4759
+ next.height = node.height;
4760
+ next.interactive = node.interactive;
4761
+ if (prior === void 0) this.contentSyncState.set(node.id, next);
4762
+ }
4509
4763
  }
4510
4764
  /**
4511
4765
  * Materialize a prepared grid in logical source order while positioning each
4512
4766
  * carrier from the shared canvas geometry. Browser font measurement happens
4513
4767
  * later in one cold read/write batch, never inside projection synchronization.
4514
4768
  */
4515
- syncContentGridProjection(node, el, projection, grid) {
4769
+ syncContentGridProjection(node, el, projection, grid, lineBand) {
4516
4770
  if (grid.source !== projection.text) {
4517
4771
  throw new Error("ContentProjection.grid.source must equal ContentProjection.text");
4518
4772
  }
4519
- const signature = `${grid.revision}`;
4773
+ const gridWindow = projectionGridLineWindow(grid, projection.lines, lineBand);
4774
+ const signature = gridWindow.gated ? `${grid.revision}:${gridWindow.start}-${gridWindow.end}` : `${grid.revision}`;
4520
4775
  if (el.dataset.vectoContentGrid !== signature) {
4521
4776
  const materializeStart = typeof performance !== "undefined" ? performance.now() : 0;
4522
4777
  this.clearContentGridState(node.id, el, false);
@@ -4524,12 +4779,13 @@ var Scene = (_class7 = class _Scene {
4524
4779
  const selectionLine = this.contentGridSelectionLine(el);
4525
4780
  let rebuiltSelectionLine = false;
4526
4781
  const existingLines = el.children;
4527
- for (let lineIndex = 0; lineIndex < grid.lines.length; lineIndex++) {
4782
+ for (let lineIndex = gridWindow.start; lineIndex < gridWindow.end; lineIndex++) {
4783
+ const domIndex = lineIndex - gridWindow.start;
4528
4784
  const gridLine = grid.lines[lineIndex];
4529
4785
  const projectedLine = projectionLines[lineIndex];
4530
- const lineHeight = _nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _123 => _123.lineHeight]), () => ( grid.lineHeight));
4531
- const baseline = _nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _124 => _124.baseline]), () => ( grid.baseline));
4532
- const lineFont = _nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _125 => _125.font]), () => ( grid.font));
4786
+ const lineHeight = _nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _134 => _134.lineHeight]), () => ( grid.lineHeight));
4787
+ const baseline = _nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _135 => _135.baseline]), () => ( grid.baseline));
4788
+ const lineFont = _nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _136 => _136.font]), () => ( grid.font));
4533
4789
  const lineSignature = contentGridLineSignature(
4534
4790
  grid,
4535
4791
  gridLine,
@@ -4539,7 +4795,7 @@ var Scene = (_class7 = class _Scene {
4539
4795
  lineFont,
4540
4796
  lineIndex === 0
4541
4797
  );
4542
- const reusable = existingLines[lineIndex];
4798
+ const reusable = existingLines[domIndex];
4543
4799
  if (reusable !== void 0 && reusable.dataset.vectoGridLineSig === lineSignature && reusable.dataset.vectoGridLine === `${lineIndex}`) {
4544
4800
  continue;
4545
4801
  }
@@ -4549,8 +4805,8 @@ var Scene = (_class7 = class _Scene {
4549
4805
  lineElement.dir = "ltr";
4550
4806
  lineElement.dataset.vectoGridLine = `${lineIndex}`;
4551
4807
  lineElement.style.position = "absolute";
4552
- lineElement.style.left = `${_nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _126 => _126.x]), () => ( 0))}px`;
4553
- lineElement.style.top = `${(_nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _127 => _127.y]), () => ( lineIndex * grid.lineHeight))) + baseline - _text.cssLineBoxBaseline.call(void 0, lineFont, lineHeight)}px`;
4808
+ lineElement.style.left = `${_nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _137 => _137.x]), () => ( 0))}px`;
4809
+ lineElement.style.top = `${(_nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _138 => _138.y]), () => ( lineIndex * grid.lineHeight))) + baseline - _text.cssLineBoxBaseline.call(void 0, lineFont, lineHeight)}px`;
4554
4810
  lineElement.style.width = `${gridLine.width}px`;
4555
4811
  lineElement.style.height = `${lineHeight}px`;
4556
4812
  lineElement.style.whiteSpace = "pre";
@@ -4612,19 +4868,25 @@ var Scene = (_class7 = class _Scene {
4612
4868
  lineElement.appendChild(marker);
4613
4869
  }
4614
4870
  }
4615
- const occupant = el.children[lineIndex];
4871
+ const occupant = el.children[domIndex];
4616
4872
  if (occupant) el.replaceChild(lineElement, occupant);
4617
4873
  else el.appendChild(lineElement);
4618
4874
  }
4619
- while (el.children.length > grid.lines.length) {
4620
- if (selectionLine !== null && selectionLine >= grid.lines.length) {
4875
+ const windowLength = gridWindow.end - gridWindow.start;
4876
+ while (el.children.length > windowLength) {
4877
+ if (selectionLine !== null && selectionLine >= gridWindow.end) {
4621
4878
  rebuiltSelectionLine = true;
4622
4879
  }
4623
- _optionalChain([el, 'access', _128 => _128.lastElementChild, 'optionalAccess', _129 => _129.remove, 'call', _130 => _130()]);
4880
+ _optionalChain([el, 'access', _139 => _139.lastElementChild, 'optionalAccess', _140 => _140.remove, 'call', _141 => _141()]);
4624
4881
  }
4625
4882
  if (rebuiltSelectionLine) this.releaseContentSelectionForRebuild(el);
4626
4883
  el.dataset.vectoProjectionLines = signature;
4627
4884
  el.dataset.vectoContentGrid = signature;
4885
+ if (gridWindow.gated) {
4886
+ el.dataset.vectoProjectionWindow = `${gridWindow.start}-${gridWindow.end}/${grid.lines.length}`;
4887
+ } else {
4888
+ delete el.dataset.vectoProjectionWindow;
4889
+ }
4628
4890
  el.dataset.vectoGridCarriers = `${el.querySelectorAll("[data-vecto-grid-cell]").length}`;
4629
4891
  if (typeof performance !== "undefined") {
4630
4892
  const materializeMs = performance.now() - materializeStart;
@@ -4684,7 +4946,7 @@ var Scene = (_class7 = class _Scene {
4684
4946
  if (previous !== void 0 && typeof cancelAnimationFrame === "function") {
4685
4947
  cancelAnimationFrame(previous);
4686
4948
  }
4687
- _optionalChain([this, 'access', _131 => _131.contentGridCalibrationProbes, 'access', _132 => _132.get, 'call', _133 => _133(entityId), 'optionalAccess', _134 => _134.remove, 'call', _135 => _135()]);
4949
+ _optionalChain([this, 'access', _142 => _142.contentGridCalibrationProbes, 'access', _143 => _143.get, 'call', _144 => _144(entityId), 'optionalAccess', _145 => _145.remove, 'call', _146 => _146()]);
4688
4950
  this.contentGridCalibrationProbes.delete(entityId);
4689
4951
  const calibrationStart = typeof performance !== "undefined" ? performance.now() : 0;
4690
4952
  const probe = document.createElement("div");
@@ -4718,7 +4980,7 @@ var Scene = (_class7 = class _Scene {
4718
4980
  target.dataset.vectoGridCalib = generation;
4719
4981
  continue;
4720
4982
  }
4721
- const sourceText = _nullishCoalesce(_optionalChain([target, 'access', _136 => _136.textContent, 'optionalAccess', _137 => _137.slice, 'call', _138 => _138(0, sourceLength)]), () => ( ""));
4983
+ const sourceText = _nullishCoalesce(_optionalChain([target, 'access', _147 => _147.textContent, 'optionalAccess', _148 => _148.slice, 'call', _149 => _149(0, sourceLength)]), () => ( ""));
4722
4984
  if (!sourceText) {
4723
4985
  target.dataset.vectoGridCalib = generation;
4724
4986
  continue;
@@ -4965,12 +5227,12 @@ var Scene = (_class7 = class _Scene {
4965
5227
  syncOverlayGeometry() {
4966
5228
  const parent = this.canvas.parentElement;
4967
5229
  if (!parent) return;
4968
- const canvasRect = _optionalChain([this, 'access', _139 => _139.canvas, 'access', _140 => _140.getBoundingClientRect, 'optionalCall', _141 => _141()]);
4969
- const parentRect = _optionalChain([parent, 'access', _142 => _142.getBoundingClientRect, 'optionalCall', _143 => _143()]);
4970
- const cssWidth = _optionalChain([canvasRect, 'optionalAccess', _144 => _144.width]) || this.canvas.clientWidth || this.width;
4971
- const cssHeight = _optionalChain([canvasRect, 'optionalAccess', _145 => _145.height]) || this.canvas.clientHeight || this.height;
4972
- const left = (_nullishCoalesce(_optionalChain([canvasRect, 'optionalAccess', _146 => _146.left]), () => ( 0))) - (_nullishCoalesce(_optionalChain([parentRect, 'optionalAccess', _147 => _147.left]), () => ( 0))) - (parent.clientLeft || 0) + parent.scrollLeft;
4973
- const top = (_nullishCoalesce(_optionalChain([canvasRect, 'optionalAccess', _148 => _148.top]), () => ( 0))) - (_nullishCoalesce(_optionalChain([parentRect, 'optionalAccess', _149 => _149.top]), () => ( 0))) - (parent.clientTop || 0) + parent.scrollTop;
5230
+ const canvasRect = _optionalChain([this, 'access', _150 => _150.canvas, 'access', _151 => _151.getBoundingClientRect, 'optionalCall', _152 => _152()]);
5231
+ const parentRect = _optionalChain([parent, 'access', _153 => _153.getBoundingClientRect, 'optionalCall', _154 => _154()]);
5232
+ const cssWidth = _optionalChain([canvasRect, 'optionalAccess', _155 => _155.width]) || this.canvas.clientWidth || this.width;
5233
+ const cssHeight = _optionalChain([canvasRect, 'optionalAccess', _156 => _156.height]) || this.canvas.clientHeight || this.height;
5234
+ const left = (_nullishCoalesce(_optionalChain([canvasRect, 'optionalAccess', _157 => _157.left]), () => ( 0))) - (_nullishCoalesce(_optionalChain([parentRect, 'optionalAccess', _158 => _158.left]), () => ( 0))) - (parent.clientLeft || 0) + parent.scrollLeft;
5235
+ const top = (_nullishCoalesce(_optionalChain([canvasRect, 'optionalAccess', _159 => _159.top]), () => ( 0))) - (_nullishCoalesce(_optionalChain([parentRect, 'optionalAccess', _160 => _160.top]), () => ( 0))) - (parent.clientTop || 0) + parent.scrollTop;
4974
5236
  const scaleX = this.width > 0 ? cssWidth / this.width : 1;
4975
5237
  const scaleY = this.height > 0 ? cssHeight / this.height : 1;
4976
5238
  const prev = this._overlayGeometry;
@@ -5109,7 +5371,7 @@ var Scene = (_class7 = class _Scene {
5109
5371
  * (and {@link respectReducedMotion} is on). `0` means uncapped.
5110
5372
  */
5111
5373
  effectiveMaxFPS() {
5112
- const reduced = this.respectReducedMotion && !!_optionalChain([this, 'access', _150 => _150.reducedMotionQuery, 'optionalAccess', _151 => _151.matches]);
5374
+ const reduced = this.respectReducedMotion && !!_optionalChain([this, 'access', _161 => _161.reducedMotionQuery, 'optionalAccess', _162 => _162.matches]);
5113
5375
  if (reduced)
5114
5376
  return this.maxFPS > 0 ? Math.min(this.maxFPS, REDUCED_MOTION_FPS) : REDUCED_MOTION_FPS;
5115
5377
  return this.maxFPS;
@@ -5204,7 +5466,7 @@ var Scene = (_class7 = class _Scene {
5204
5466
  * @param time - Current absolute time in milliseconds (default 0).
5205
5467
  */
5206
5468
  render(renderer, dt = 0, time = 0) {
5207
- if (_optionalChain([renderer, 'access', _152 => _152.isContextLost, 'optionalCall', _153 => _153()])) return;
5469
+ if (_optionalChain([renderer, 'access', _163 => _163.isContextLost, 'optionalCall', _164 => _164()])) return;
5208
5470
  const isMainRenderer = renderer === this.renderer;
5209
5471
  if (isMainRenderer && this.a11yRoot && this.canvas.parentElement) {
5210
5472
  const parentStyle = this.canvas.parentElement.style;
@@ -5348,14 +5610,14 @@ var Scene = (_class7 = class _Scene {
5348
5610
  }
5349
5611
  renderer.clear();
5350
5612
  if (isMainRenderer) {
5351
- _optionalChain([this, 'access', _154 => _154.pointRenderer, 'optionalAccess', _155 => _155.begin, 'call', _156 => _156()]);
5613
+ _optionalChain([this, 'access', _165 => _165.pointRenderer, 'optionalAccess', _166 => _166.begin, 'call', _167 => _167()]);
5352
5614
  }
5353
5615
  const vw = this.width;
5354
5616
  const vh = this.height;
5355
5617
  let walkHadAnimation = false;
5356
5618
  let walkHadInteractive = false;
5357
5619
  const runUpdate = (node) => {
5358
- const overridesUpdate = node.update !== _chunkZ3HFY75Rjs.Entity.prototype.update;
5620
+ const overridesUpdate = node.update !== _chunkTRT2TRCWjs.Entity.prototype.update;
5359
5621
  let pending = node.hasPendingAnimations();
5360
5622
  if (pending || overridesUpdate) {
5361
5623
  node.update(dt, time);
@@ -5364,7 +5626,7 @@ var Scene = (_class7 = class _Scene {
5364
5626
  if (pending) walkHadAnimation = true;
5365
5627
  if (!walkHadInteractive && node.interactive) walkHadInteractive = true;
5366
5628
  if (this._devActive && this._devFrameCount % 120 === 0) {
5367
- if (overridesUpdate && node.hasPendingAnimations === _chunkZ3HFY75Rjs.Entity.prototype.hasPendingAnimations) {
5629
+ if (overridesUpdate && node.hasPendingAnimations === _chunkTRT2TRCWjs.Entity.prototype.hasPendingAnimations) {
5368
5630
  this._devWarn(
5369
5631
  `Entity "${node.id}" overrides update() but not hasPendingAnimations(). Custom motion in update() without overriding hasPendingAnimations() causes the idle throttle to drop the animation to ~2fps. Override hasPendingAnimations() to return true while motion is in flight.`
5370
5632
  );
@@ -5562,9 +5824,9 @@ var Scene = (_class7 = class _Scene {
5562
5824
  const flushT0 = this._phaseTiming ? performance.now() : 0;
5563
5825
  renderer.flush();
5564
5826
  if (isMainRenderer) {
5565
- _optionalChain([this, 'access', _157 => _157.pointRenderer, 'optionalAccess', _158 => _158.flush, 'call', _159 => _159()]);
5827
+ _optionalChain([this, 'access', _168 => _168.pointRenderer, 'optionalAccess', _169 => _169.flush, 'call', _170 => _170()]);
5566
5828
  }
5567
- _optionalChain([renderer, 'access', _160 => _160.present, 'optionalCall', _161 => _161()]);
5829
+ _optionalChain([renderer, 'access', _171 => _171.present, 'optionalCall', _172 => _172()]);
5568
5830
  if (this._phaseTiming) this._recordPhase("flush", performance.now() - flushT0);
5569
5831
  if (flushTiming) endVectoUserTiming(flushTiming);
5570
5832
  if (this._devActive) {
@@ -5845,8 +6107,8 @@ function pointInBounds(b, x, y) {
5845
6107
  function contentGridLineSignature(grid, line, projected, lineHeight, baseline, font, isFirstLine) {
5846
6108
  const parts = [
5847
6109
  // Line box: position, size, and the font that resolves its baseline.
5848
- `${_nullishCoalesce(_optionalChain([projected, 'optionalAccess', _162 => _162.x]), () => ( 0))}`,
5849
- `${_nullishCoalesce(_optionalChain([projected, 'optionalAccess', _163 => _163.y]), () => ( ""))}`,
6110
+ `${_nullishCoalesce(_optionalChain([projected, 'optionalAccess', _173 => _173.x]), () => ( 0))}`,
6111
+ `${_nullishCoalesce(_optionalChain([projected, 'optionalAccess', _174 => _174.y]), () => ( ""))}`,
5850
6112
  `${lineHeight}`,
5851
6113
  `${baseline}`,
5852
6114
  font,
@@ -5888,20 +6150,22 @@ function defaultMeasurer() {
5888
6150
  sharedMeasurer ??= _layout.resolveGlyphMeasurer.call(void 0, "sans-serif");
5889
6151
  return sharedMeasurer;
5890
6152
  }
5891
- var TextEntity = (_class8 = class extends _chunkZ3HFY75Rjs.Entity {
6153
+ var TextEntity = (_class8 = class extends _chunkTRT2TRCWjs.Entity {
5892
6154
 
5893
6155
 
5894
6156
 
5895
6157
 
5896
- __init151() {this.nodes = []}
6158
+ __init154() {this.nodes = []}
5897
6159
 
5898
- __init152() {this.fillStyle = "#94a3b8"}
5899
- __init153() {this.strokeStyle = null}
5900
- __init154() {this.hoveredFillStyle = "#ffffff"}
5901
- __init155() {this.lineWidth = 1}
5902
- __init156() {this.isHovered = false}
6160
+ __init155() {this.fillStyle = "#94a3b8"}
6161
+ __init156() {this.strokeStyle = null}
6162
+ __init157() {this.hoveredFillStyle = "#ffffff"}
6163
+ __init158() {this.lineWidth = 1}
6164
+ __init159() {this.isHovered = false}
6165
+ /** Bumped by {@link applyLayout}; read by `Scene` to skip an unchanged sync. */
6166
+ __init160() {this.contentEpoch = 0}
5903
6167
  constructor(text, atlas, maxWidth, fontSize = 24) {
5904
- super();_class8.prototype.__init151.call(this);_class8.prototype.__init152.call(this);_class8.prototype.__init153.call(this);_class8.prototype.__init154.call(this);_class8.prototype.__init155.call(this);_class8.prototype.__init156.call(this);;
6168
+ super();_class8.prototype.__init154.call(this);_class8.prototype.__init155.call(this);_class8.prototype.__init156.call(this);_class8.prototype.__init157.call(this);_class8.prototype.__init158.call(this);_class8.prototype.__init159.call(this);_class8.prototype.__init160.call(this);;
5905
6169
  this.text = text;
5906
6170
  this.atlas = atlas;
5907
6171
  this.fontSize = fontSize;
@@ -5920,6 +6184,9 @@ var TextEntity = (_class8 = class extends _chunkZ3HFY75Rjs.Entity {
5920
6184
  if (!this.text) return null;
5921
6185
  return { text: this.text, font: `${this.fontSize}px sans-serif` };
5922
6186
  }
6187
+ getContentEpoch() {
6188
+ return this.contentEpoch;
6189
+ }
5923
6190
  /**
5924
6191
  * Replace the text content. Runs the **cold** measurement pass (re-segment +
5925
6192
  * re-measure) since the glyphs changed, then re-lays out.
@@ -5966,6 +6233,7 @@ var TextEntity = (_class8 = class extends _chunkZ3HFY75Rjs.Entity {
5966
6233
  }
5967
6234
  /** Hot pass: place the cached {@link PreparedText} and refresh the a11y box. */
5968
6235
  applyLayout() {
6236
+ this.contentEpoch++;
5969
6237
  const result = this.layout.layoutPrepared(this.prepared);
5970
6238
  this.nodes = result.nodes;
5971
6239
  this.width = result.totalWidth;
@@ -6014,17 +6282,17 @@ var TextEntity = (_class8 = class extends _chunkZ3HFY75Rjs.Entity {
6014
6282
  }, _class8);
6015
6283
 
6016
6284
  // src/components/GridTextEntity.ts
6017
- var GridTextEntity = (_class9 = class extends _chunkZ3HFY75Rjs.Entity {
6285
+ var GridTextEntity = (_class9 = class extends _chunkTRT2TRCWjs.Entity {
6018
6286
 
6019
- __init157() {this.fillStyle = "#ffffff"}
6020
- __init158() {this.grid = []}
6287
+ __init161() {this.fillStyle = "#ffffff"}
6288
+ __init162() {this.grid = []}
6021
6289
  // Array of rows
6022
- __init159() {this.cols = 0}
6023
- __init160() {this.rows = 0}
6290
+ __init163() {this.cols = 0}
6291
+ __init164() {this.rows = 0}
6024
6292
 
6025
6293
 
6026
6294
  constructor(_atlas, fontSize = 10) {
6027
- super();_class9.prototype.__init157.call(this);_class9.prototype.__init158.call(this);_class9.prototype.__init159.call(this);_class9.prototype.__init160.call(this);;
6295
+ super();_class9.prototype.__init161.call(this);_class9.prototype.__init162.call(this);_class9.prototype.__init163.call(this);_class9.prototype.__init164.call(this);;
6028
6296
  this.fontSize = fontSize;
6029
6297
  this.charWidth = fontSize * 1;
6030
6298
  this.charHeight = fontSize * 1.1;
@@ -6033,7 +6301,7 @@ var GridTextEntity = (_class9 = class extends _chunkZ3HFY75Rjs.Entity {
6033
6301
  updateGrid(ascii) {
6034
6302
  this.grid = ascii;
6035
6303
  this.rows = ascii.length;
6036
- this.cols = _optionalChain([ascii, 'access', _164 => _164[0], 'optionalAccess', _165 => _165.length]) || 0;
6304
+ this.cols = _optionalChain([ascii, 'access', _175 => _175[0], 'optionalAccess', _176 => _176.length]) || 0;
6037
6305
  }
6038
6306
  isPointInside(_globalX, _globalY) {
6039
6307
  return false;
@@ -6108,7 +6376,7 @@ function distSqToSegment(px, py, x1, y1, x2, y2) {
6108
6376
  const ey = py - cy;
6109
6377
  return ex * ex + ey * ey;
6110
6378
  }
6111
- var SplineEntity = (_class10 = class extends _chunkZ3HFY75Rjs.Entity {
6379
+ var SplineEntity = (_class10 = class extends _chunkTRT2TRCWjs.Entity {
6112
6380
 
6113
6381
 
6114
6382
 
@@ -6116,23 +6384,23 @@ var SplineEntity = (_class10 = class extends _chunkZ3HFY75Rjs.Entity {
6116
6384
 
6117
6385
 
6118
6386
 
6119
- __init161() {this.offscreen = null}
6120
- __init162() {this.baked = false}
6387
+ __init165() {this.offscreen = null}
6388
+ __init166() {this.baked = false}
6121
6389
  /** Logical (CSS-pixel) size of the baked bitmap — the blit destination size. */
6122
- __init163() {this.bakedWidth = 0}
6123
- __init164() {this.bakedHeight = 0}
6390
+ __init167() {this.bakedWidth = 0}
6391
+ __init168() {this.bakedHeight = 0}
6124
6392
  /** Gradient strokes can't be baked to a solid-color bitmap; they render per-frame. */
6125
6393
 
6126
6394
  /** Lazily-flattened polylines (one Float32Array of [x,y,...] per segment) for hit-testing. */
6127
- __init165() {this.polylines = null}
6395
+ __init169() {this.polylines = null}
6128
6396
  /**
6129
6397
  * When `true`, the renderer draws a rounded-rect outline of the entity's
6130
6398
  * local bounds after painting the curves. Useful for drag feedback and
6131
6399
  * debugging hit areas. Defaults to `false`.
6132
6400
  */
6133
- __init166() {this.showBounds = false}
6401
+ __init170() {this.showBounds = false}
6134
6402
  constructor(doc, opts = {}) {
6135
- super();_class10.prototype.__init161.call(this);_class10.prototype.__init162.call(this);_class10.prototype.__init163.call(this);_class10.prototype.__init164.call(this);_class10.prototype.__init165.call(this);_class10.prototype.__init166.call(this);;
6403
+ super();_class10.prototype.__init165.call(this);_class10.prototype.__init166.call(this);_class10.prototype.__init167.call(this);_class10.prototype.__init168.call(this);_class10.prototype.__init169.call(this);_class10.prototype.__init170.call(this);;
6136
6404
  this.doc = doc;
6137
6405
  this.lineWidth = _nullishCoalesce(opts.lineWidth, () => ( 2));
6138
6406
  this.cache = _nullishCoalesce(opts.cache, () => ( true));
@@ -6143,7 +6411,7 @@ var SplineEntity = (_class10 = class extends _chunkZ3HFY75Rjs.Entity {
6143
6411
  this.width = this.bounds.width;
6144
6412
  this.height = this.bounds.height;
6145
6413
  const isGradient = (c) => c !== null && !Array.isArray(c);
6146
- this.containsGradient = (_nullishCoalesce(_optionalChain([this, 'access', _166 => _166.doc, 'access', _167 => _167.equations, 'optionalAccess', _168 => _168.some, 'call', _169 => _169((eq) => isGradient(eq.color_rgb))]), () => ( false))) || (_nullishCoalesce(_optionalChain([this, 'access', _170 => _170.doc, 'access', _171 => _171.paths, 'optionalAccess', _172 => _172.some, 'call', _173 => _173((p) => isGradient(p.color_rgb))]), () => ( false)));
6414
+ this.containsGradient = (_nullishCoalesce(_optionalChain([this, 'access', _177 => _177.doc, 'access', _178 => _178.equations, 'optionalAccess', _179 => _179.some, 'call', _180 => _180((eq) => isGradient(eq.color_rgb))]), () => ( false))) || (_nullishCoalesce(_optionalChain([this, 'access', _181 => _181.doc, 'access', _182 => _182.paths, 'optionalAccess', _183 => _183.some, 'call', _184 => _184((p) => isGradient(p.color_rgb))]), () => ( false)));
6147
6415
  this.interactive = true;
6148
6416
  }
6149
6417
  computeBounds() {
@@ -6383,7 +6651,7 @@ async function loadSpline(url) {
6383
6651
  }
6384
6652
 
6385
6653
  // src/components/Rect.ts
6386
- var Rect = class extends _chunkZ3HFY75Rjs.Entity {
6654
+ var Rect = class extends _chunkTRT2TRCWjs.Entity {
6387
6655
 
6388
6656
 
6389
6657
 
@@ -6432,7 +6700,7 @@ var Rect = class extends _chunkZ3HFY75Rjs.Entity {
6432
6700
  };
6433
6701
 
6434
6702
  // src/components/Circle.ts
6435
- var Circle = class extends _chunkZ3HFY75Rjs.Entity {
6703
+ var Circle = class extends _chunkTRT2TRCWjs.Entity {
6436
6704
 
6437
6705
 
6438
6706
 
@@ -6492,7 +6760,7 @@ var Circle = class extends _chunkZ3HFY75Rjs.Entity {
6492
6760
  };
6493
6761
 
6494
6762
  // src/components/Group.ts
6495
- var Group = class extends _chunkZ3HFY75Rjs.Entity {
6763
+ var Group = class extends _chunkTRT2TRCWjs.Entity {
6496
6764
  constructor(...children) {
6497
6765
  super();
6498
6766
  if (children.length > 0) this.add(...children);
@@ -6511,21 +6779,21 @@ var _math = require('@vectojs/math'); _createStarExport(_math);
6511
6779
 
6512
6780
 
6513
6781
  // src/tree/DOMPortalEntity.ts
6514
- var DOMPortalEntity = (_class11 = class extends _chunkZ3HFY75Rjs.Entity {
6782
+ var DOMPortalEntity = (_class11 = class extends _chunkTRT2TRCWjs.Entity {
6515
6783
 
6516
- __init167() {this.isDOMPortal = true}
6517
- __init168() {this.domListeners = []}
6518
- __init169() {this.resizeObserver = null}
6519
- __init170() {this.domBound = false}
6520
- __init171() {this.cachedWidth = 100}
6521
- __init172() {this.cachedHeight = 100}
6522
- __init173() {this.lastWidth = ""}
6523
- __init174() {this.lastHeight = ""}
6524
- __init175() {this.lastTransform = ""}
6525
- __init176() {this.lastZIndex = ""}
6526
- __init177() {this.lastOpacity = ""}
6784
+ __init171() {this.isDOMPortal = true}
6785
+ __init172() {this.domListeners = []}
6786
+ __init173() {this.resizeObserver = null}
6787
+ __init174() {this.domBound = false}
6788
+ __init175() {this.cachedWidth = 100}
6789
+ __init176() {this.cachedHeight = 100}
6790
+ __init177() {this.lastWidth = ""}
6791
+ __init178() {this.lastHeight = ""}
6792
+ __init179() {this.lastTransform = ""}
6793
+ __init180() {this.lastZIndex = ""}
6794
+ __init181() {this.lastOpacity = ""}
6527
6795
  constructor(domElement, width, height, id) {
6528
- super(id);_class11.prototype.__init167.call(this);_class11.prototype.__init168.call(this);_class11.prototype.__init169.call(this);_class11.prototype.__init170.call(this);_class11.prototype.__init171.call(this);_class11.prototype.__init172.call(this);_class11.prototype.__init173.call(this);_class11.prototype.__init174.call(this);_class11.prototype.__init175.call(this);_class11.prototype.__init176.call(this);_class11.prototype.__init177.call(this);;
6796
+ super(id);_class11.prototype.__init171.call(this);_class11.prototype.__init172.call(this);_class11.prototype.__init173.call(this);_class11.prototype.__init174.call(this);_class11.prototype.__init175.call(this);_class11.prototype.__init176.call(this);_class11.prototype.__init177.call(this);_class11.prototype.__init178.call(this);_class11.prototype.__init179.call(this);_class11.prototype.__init180.call(this);_class11.prototype.__init181.call(this);;
6529
6797
  this.domElement = domElement;
6530
6798
  this.width = _nullishCoalesce(width, () => ( 0));
6531
6799
  this.height = _nullishCoalesce(height, () => ( 0));
@@ -6567,7 +6835,7 @@ var DOMPortalEntity = (_class11 = class extends _chunkZ3HFY75Rjs.Entity {
6567
6835
  ];
6568
6836
  for (const type of events) {
6569
6837
  const handler = (e) => {
6570
- this.dispatchEvent(new (0, _chunkZ3HFY75Rjs.VectoJSEvent)(type, this, e));
6838
+ this.dispatchEvent(new (0, _chunkTRT2TRCWjs.VectoJSEvent)(type, this, e));
6571
6839
  };
6572
6840
  this.domElement.addEventListener(type, handler);
6573
6841
  this.domListeners.push({ type, handler, capture: false });
@@ -6578,7 +6846,7 @@ var DOMPortalEntity = (_class11 = class extends _chunkZ3HFY75Rjs.Entity {
6578
6846
  ];
6579
6847
  for (const { native, vecto } of hoverEvents) {
6580
6848
  const handler = (e) => {
6581
- this.dispatchEvent(new (0, _chunkZ3HFY75Rjs.VectoJSEvent)(vecto, this, e, false));
6849
+ this.dispatchEvent(new (0, _chunkTRT2TRCWjs.VectoJSEvent)(vecto, this, e, false));
6582
6850
  };
6583
6851
  this.domElement.addEventListener(native, handler);
6584
6852
  this.domListeners.push({ type: native, handler, capture: false });
@@ -6586,7 +6854,7 @@ var DOMPortalEntity = (_class11 = class extends _chunkZ3HFY75Rjs.Entity {
6586
6854
  const focusEvents = ["focus", "blur"];
6587
6855
  for (const type of focusEvents) {
6588
6856
  const handler = (e) => {
6589
- this.dispatchEvent(new (0, _chunkZ3HFY75Rjs.VectoJSEvent)(type, this, e, true));
6857
+ this.dispatchEvent(new (0, _chunkTRT2TRCWjs.VectoJSEvent)(type, this, e, true));
6590
6858
  };
6591
6859
  this.domElement.addEventListener(type, handler, true);
6592
6860
  this.domListeners.push({ type, handler, capture: true });
@@ -6682,4 +6950,5 @@ Scene.registerWebGPUParticleSystemManager(_chunkM73XB4ZKjs.WebGPUParticleSystemM
6682
6950
 
6683
6951
 
6684
6952
 
6685
- exports.CanvasRenderer = _chunkM73XB4ZKjs.CanvasRenderer; exports.Circle = Circle; exports.ComputeParticleEntity = ComputeParticleEntity; exports.DOMPortalEntity = DOMPortalEntity; exports.Entity = _chunkZ3HFY75Rjs.Entity; exports.GlyphRasterAtlas = _chunkM73XB4ZKjs.GlyphRasterAtlas; exports.GridTextEntity = GridTextEntity; exports.Group = Group; exports.MSDFTextEntity = _chunkZ3HFY75Rjs.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 = _chunkZ3HFY75Rjs.SVGEntity; exports.SVGRenderer = _chunkM73XB4ZKjs.SVGRenderer; exports.Scene = Scene; exports.SplineEntity = SplineEntity; exports.TextEntity = TextEntity; exports.TextRasterCache = _chunkM73XB4ZKjs.TextRasterCache; exports.VECTO_USER_TIMING = VECTO_USER_TIMING; exports.VectoJSEvent = _chunkZ3HFY75Rjs.VectoJSEvent; exports.WebGPUParticleSystemManager = _chunkM73XB4ZKjs.WebGPUParticleSystemManager; exports.beginVectoUserTiming = beginVectoUserTiming; exports.createWebGLPointRenderer = _chunkM73XB4ZKjs.createWebGLPointRenderer; exports.endVectoUserTiming = endVectoUserTiming; exports.installRendererDevTraps = _chunkM73XB4ZKjs.installRendererDevTraps; exports.isRendererDevMode = _chunkM73XB4ZKjs.isRendererDevMode; exports.isSafeUrl = _chunkM73XB4ZKjs.isSafeUrl; exports.loadSpline = loadSpline; exports.measureVectoUserTiming = measureVectoUserTiming; exports.parseColorToRGBA = _chunkM73XB4ZKjs.parseColorToRGBA; exports.polySegmentToBezier = polySegmentToBezier; exports.sanitizeUrl = _chunkM73XB4ZKjs.sanitizeUrl; exports.setRendererDevMode = _chunkM73XB4ZKjs.setRendererDevMode;
6953
+
6954
+ exports.CanvasRenderer = _chunkM73XB4ZKjs.CanvasRenderer; exports.Circle = Circle; exports.ComputeParticleEntity = ComputeParticleEntity; exports.DOMPortalEntity = DOMPortalEntity; exports.Entity = _chunkTRT2TRCWjs.Entity; exports.GlyphRasterAtlas = _chunkM73XB4ZKjs.GlyphRasterAtlas; exports.GridTextEntity = GridTextEntity; exports.Group = Group; exports.MSDFTextEntity = _chunkTRT2TRCWjs.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 = _chunkTRT2TRCWjs.SVGEntity; exports.SVGRenderer = _chunkM73XB4ZKjs.SVGRenderer; exports.Scene = Scene; exports.SplineEntity = SplineEntity; exports.TextEntity = TextEntity; exports.TextRasterCache = _chunkM73XB4ZKjs.TextRasterCache; exports.VECTO_USER_TIMING = VECTO_USER_TIMING; exports.VectoJSEvent = _chunkTRT2TRCWjs.VectoJSEvent; exports.WebGPUParticleSystemManager = _chunkM73XB4ZKjs.WebGPUParticleSystemManager; exports.beginVectoUserTiming = beginVectoUserTiming; exports.contentLineInHint = _chunkTRT2TRCWjs.contentLineInHint; exports.createWebGLPointRenderer = _chunkM73XB4ZKjs.createWebGLPointRenderer; exports.endVectoUserTiming = endVectoUserTiming; exports.installRendererDevTraps = _chunkM73XB4ZKjs.installRendererDevTraps; exports.isRendererDevMode = _chunkM73XB4ZKjs.isRendererDevMode; exports.isSafeUrl = _chunkM73XB4ZKjs.isSafeUrl; exports.loadSpline = loadSpline; exports.measureVectoUserTiming = measureVectoUserTiming; exports.parseColorToRGBA = _chunkM73XB4ZKjs.parseColorToRGBA; exports.polySegmentToBezier = polySegmentToBezier; exports.sanitizeUrl = _chunkM73XB4ZKjs.sanitizeUrl; exports.setRendererDevMode = _chunkM73XB4ZKjs.setRendererDevMode;