@vectojs/core 1.29.0 → 1.30.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 _chunk7PI7LVWWjs = require('./chunk-7PI7LVWW.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 _chunk7PI7LVWWjs.Entity {
37
38
 
38
39
 
39
40
 
@@ -1431,6 +1432,45 @@ function parseInlinePx(value) {
1431
1432
  const n = parseFloat(value);
1432
1433
  return Number.isFinite(n) && n > 0 ? n : null;
1433
1434
  }
1435
+ function projectionGridLineWindow(grid, projectionLines, band) {
1436
+ const count = grid.lines.length;
1437
+ const all = { start: 0, end: count, gated: false };
1438
+ if (!band || count === 0) return all;
1439
+ const lines = [];
1440
+ for (let i = 0; i < count; i++) {
1441
+ const projected = _optionalChain([projectionLines, 'optionalAccess', _24 => _24[i]]);
1442
+ lines.push({
1443
+ y: _nullishCoalesce(_optionalChain([projected, 'optionalAccess', _25 => _25.y]), () => ( i * grid.lineHeight)),
1444
+ lineHeight: _nullishCoalesce(_optionalChain([projected, 'optionalAccess', _26 => _26.lineHeight]), () => ( grid.lineHeight))
1445
+ });
1446
+ }
1447
+ return projectionLineWindow(lines, band, grid.lineHeight);
1448
+ }
1449
+ function projectionLineWindow(lines, band, fallbackLineHeight) {
1450
+ const all = {
1451
+ start: 0,
1452
+ end: lines.length,
1453
+ gated: false
1454
+ };
1455
+ if (!band || lines.length === 0) return all;
1456
+ let start = -1;
1457
+ let end = -1;
1458
+ for (let i = 0; i < lines.length; i++) {
1459
+ const line = lines[i];
1460
+ const h = _nullishCoalesce(line.lineHeight, () => ( fallbackLineHeight));
1461
+ if (line.y + h >= band.minY && line.y <= band.maxY) {
1462
+ if (start === -1) start = i;
1463
+ end = i + 1;
1464
+ } else if (start !== -1 && line.y > band.maxY) {
1465
+ break;
1466
+ }
1467
+ }
1468
+ if (start === -1) {
1469
+ return { start: 0, end: Math.min(1, lines.length), gated: true };
1470
+ }
1471
+ if (start === 0 && end === lines.length) return all;
1472
+ return { start, end, gated: true };
1473
+ }
1434
1474
  function collectTextNodes(root) {
1435
1475
  const out = [];
1436
1476
  const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
@@ -1510,9 +1550,9 @@ function parseCssMatrix(transform) {
1510
1550
  }
1511
1551
  function clientToGridLocal(contentEl, canvas, clientX, clientY) {
1512
1552
  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"]')]);
1553
+ const originMarker = _optionalChain([line, 'optionalAccess', _27 => _27.querySelector, 'call', _28 => _28('[data-vecto-grid-basis="origin"]')]);
1554
+ const xMarker = _optionalChain([line, 'optionalAccess', _29 => _29.querySelector, 'call', _30 => _30('[data-vecto-grid-basis="x"]')]);
1555
+ const yMarker = _optionalChain([line, 'optionalAccess', _31 => _31.querySelector, 'call', _32 => _32('[data-vecto-grid-basis="y"]')]);
1516
1556
  if (line && originMarker && xMarker && yMarker) {
1517
1557
  const origin = originMarker.getBoundingClientRect();
1518
1558
  const xPoint = xMarker.getBoundingClientRect();
@@ -1596,7 +1636,7 @@ function nearestTextPositionInLine(line, x, y) {
1596
1636
  nearest = { position: { node, offset }, distance: candidate.distance };
1597
1637
  }
1598
1638
  }
1599
- return _nullishCoalesce(_optionalChain([nearest, 'optionalAccess', _30 => _30.position]), () => ( null));
1639
+ return _nullishCoalesce(_optionalChain([nearest, 'optionalAccess', _33 => _33.position]), () => ( null));
1600
1640
  }
1601
1641
  function nearestTextPositionInProjection(contentEl, canvas, x, y, eventTarget) {
1602
1642
  if (contentEl.dataset.vectoContentGrid !== void 0) {
@@ -1610,7 +1650,7 @@ function nearestTextPositionInProjection(contentEl, canvas, x, y, eventTarget) {
1610
1650
  }
1611
1651
  targetLine = targetLine.parentElement;
1612
1652
  }
1613
- if (_optionalChain([targetLine, 'optionalAccess', _31 => _31.parentElement]) === contentEl) {
1653
+ if (_optionalChain([targetLine, 'optionalAccess', _34 => _34.parentElement]) === contentEl) {
1614
1654
  return nearestTextPositionInLine(targetLine, x, y);
1615
1655
  }
1616
1656
  let bestLine = null;
@@ -1888,7 +1928,7 @@ var Scene = (_class7 = class _Scene {
1888
1928
  __init50() {this.forcedColorsChangeHandler = null}
1889
1929
  /** True when the OS asks for reduced motion and we respect it. Read by the animation drivers. */
1890
1930
  get prefersReducedMotion() {
1891
- return this.respectReducedMotion && !!_optionalChain([this, 'access', _32 => _32.reducedMotionQuery, 'optionalAccess', _33 => _33.matches]);
1931
+ return this.respectReducedMotion && !!_optionalChain([this, 'access', _35 => _35.reducedMotionQuery, 'optionalAccess', _36 => _36.matches]);
1892
1932
  }
1893
1933
  /**
1894
1934
  * True when the OS is in a forced-colors mode (Windows High Contrast, and the
@@ -1899,7 +1939,7 @@ var Scene = (_class7 = class _Scene {
1899
1939
  * when the setting toggles.
1900
1940
  */
1901
1941
  get forcedColors() {
1902
- return !!_optionalChain([this, 'access', _34 => _34.forcedColorsQuery, 'optionalAccess', _35 => _35.matches]);
1942
+ return !!_optionalChain([this, 'access', _37 => _37.forcedColorsQuery, 'optionalAccess', _38 => _38.matches]);
1903
1943
  }
1904
1944
  /**
1905
1945
  * Throttle interval (ms) for the a11y/automation shadow sync. `0` = every
@@ -1989,21 +2029,29 @@ var Scene = (_class7 = class _Scene {
1989
2029
  * mid-hover synthesize the `pointerleave` the browser never sends for a
1990
2030
  * detached element, so the entity doesn't keep its hover state. */
1991
2031
  __init75() {this.hoveredA11yElements = /* @__PURE__ */ new WeakSet()}
2032
+ /**
2033
+ * Entity ids the application has pinned via {@link requestA11yProjection}.
2034
+ *
2035
+ * Ids rather than entities so a removed entity cannot be retained by this set;
2036
+ * a stale id simply never matches. Cleared per-entity by
2037
+ * {@link releaseA11yProjection}.
2038
+ */
2039
+ __init76() {this.a11yProjectionRequests = /* @__PURE__ */ new Set()}
1992
2040
  /** Persistent tabindex=-1 element in a11yRoot. When the focused a11y mirror is
1993
2041
  * pruned (virtualization/streaming/removal) while it holds focus, we move
1994
2042
  * focus here instead of letting the browser drop it to <body> — keeping the
1995
2043
  * 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()}
2044
+ __init77() {this.focusSentinel = null}
2045
+ __init78() {this.caretBlinkTimer = null}
2046
+ __init79() {this.a11yNeedsReorder = true}
2047
+ __init80() {this.portalRoot = null}
2048
+ __init81() {this.fullViewportElements = []}
2049
+ __init82() {this.normalElements = []}
2050
+ __init83() {this.activeIds = /* @__PURE__ */ new Set()}
2003
2051
  /** Per-parent insertion cursor, reused by `enforceA11yDomOrder`. */
2004
- __init83() {this.a11yOrderCursors = /* @__PURE__ */ new Map()}
2052
+ __init84() {this.a11yOrderCursors = /* @__PURE__ */ new Map()}
2005
2053
  /** Membership set for the elements being ordered, reused per reorder pass. */
2006
- __init84() {this.a11yOrderMembers = /* @__PURE__ */ new Set()}
2054
+ __init85() {this.a11yOrderMembers = /* @__PURE__ */ new Set()}
2007
2055
  /**
2008
2056
  * Elements that are an *ancestor* of another ordered element, reused per pass.
2009
2057
  *
@@ -2011,11 +2059,11 @@ var Scene = (_class7 = class _Scene {
2011
2059
  * items) spans every descendant row, so it must not extend a visual row band —
2012
2060
  * see {@link sortNormalElementsVisually}.
2013
2061
  */
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}
2062
+ __init86() {this.a11yOrderContainers = /* @__PURE__ */ new Set()}
2063
+ __init87() {this.activePortalsThisFrame = /* @__PURE__ */ new Set()}
2064
+ __init88() {this.activePortalsPrevFrame = /* @__PURE__ */ new Set()}
2065
+ __init89() {this.portalEntities = /* @__PURE__ */ new Map()}
2066
+ __init90() {this.renderOrderCounter = 0}
2019
2067
  /**
2020
2068
  * Monotonic render-frame counter, bumped once per authoritative `render()`
2021
2069
  * pass. Entities stamp their per-frame world-matrix cache with this value and
@@ -2024,7 +2072,7 @@ var Scene = (_class7 = class _Scene {
2024
2072
  * back to the ancestor walk. Public for the same reason `Entity._getTrig`/
2025
2073
  * `_setWorldCache` are: it is a cross-class render-internal contract.
2026
2074
  */
2027
- __init90() {this.currentFrame = 0}
2075
+ __init91() {this.currentFrame = 0}
2028
2076
  // ── WASM transform backend (invisible accelerator) ──────────────────────────
2029
2077
  // When `_transformBackend === 'wasm'`, the main render walk sources each
2030
2078
  // entity's world matrix from an SoA store composed by `_wasm` (see
@@ -2032,24 +2080,24 @@ var Scene = (_class7 = class _Scene {
2032
2080
  // fallback and the default: a null backend, a non-main renderer, or any entity
2033
2081
  // absent from the store all fall back to the JS composition, so WASM can only
2034
2082
  // ever change *how fast* a world matrix is produced, never *what* it is.
2035
- __init91() {this._wasm = null}
2036
- __init92() {this._transformBackend = "js"}
2083
+ __init92() {this._wasm = null}
2084
+ __init93() {this._transformBackend = "js"}
2037
2085
  // Resident store state (Stage 3). The store layout — slot assignment + sibling
2038
2086
  // runs — depends only on tree TOPOLOGY, so it is rebuilt only when the
2039
2087
  // structure changes (add/remove/reparent bump `_structureVersion`). Between
2040
2088
  // rebuilds the per-frame cost is: gather each entity's transform into the
2041
2089
  // resident wasm input view + run the kernel — no reallocation, no readback.
2042
- __init93() {this._treeStore = null}
2043
- __init94() {this._slotEntity = []}
2090
+ __init94() {this._treeStore = null}
2091
+ __init95() {this._slotEntity = []}
2044
2092
  // 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}
2093
+ __init96() {this._wasmInputs = null}
2094
+ __init97() {this._wasmWorld = null}
2095
+ __init98() {this._structureVersion = 0}
2096
+ __init99() {this._storeStructureVersion = -1}
2049
2097
  // Cached list of ComputeParticleEntity instances in the tree, keyed by the
2050
2098
  // structure version it was gathered at. Rebuilt only on a topology change.
2051
- __init99() {this._computeEntities = []}
2052
- __init100() {this._computeEntitiesVersion = -1}
2099
+ __init100() {this._computeEntities = []}
2100
+ __init101() {this._computeEntitiesVersion = -1}
2053
2101
  /** Invalidate the resident WASM store layout; the next wasm-mode frame rebuilds
2054
2102
  * it. Called by `Entity.add`/`remove` (topology changes only). */
2055
2103
  markStructureChanged() {
@@ -2134,7 +2182,7 @@ var Scene = (_class7 = class _Scene {
2134
2182
  * cached globally; the instance is per-Scene, which is the isolation that
2135
2183
  * actually matters.
2136
2184
  */
2137
- __init101() {this._wasmRuntime = null}
2185
+ __init102() {this._wasmRuntime = null}
2138
2186
  /**
2139
2187
  * Load (or reuse) this Scene's shared WASM runtime.
2140
2188
  *
@@ -2162,30 +2210,30 @@ var Scene = (_class7 = class _Scene {
2162
2210
  get wasmRuntime() {
2163
2211
  return this._wasmRuntime;
2164
2212
  }
2165
- __init102() {this._hitWasm = null}
2213
+ __init103() {this._hitWasm = null}
2166
2214
  // Cache key: which frame + structure version the grid was last (successfully,
2167
2215
  // non-overflowing) built for. findEntityAt is called ad-hoc (pointer
2168
2216
  // hover/click), not every frame, so the grid is refreshed lazily on demand
2169
2217
  // rather than proactively every render() — unlike the transform store, which
2170
2218
  // every frame's draw depends on.
2171
- __init103() {this._hitGridFrame = -1}
2172
- __init104() {this._hitGridOk = false}
2173
- __init105() {this._hitSlotEntity = []}
2174
- __init106() {this._hitBoundless = []}
2219
+ __init104() {this._hitGridFrame = -1}
2220
+ __init105() {this._hitGridOk = false}
2221
+ __init106() {this._hitSlotEntity = []}
2222
+ __init107() {this._hitBoundless = []}
2175
2223
  /** Reused buffer for the fused gather, so a pointer query allocates nothing. */
2176
- __init107() {this._hitGatherBuffer = null}
2224
+ __init108() {this._hitGatherBuffer = null}
2177
2225
  /**
2178
2226
  * Whether the last grid build sourced its AABBs from the WASM transform store
2179
2227
  * rather than recomputing them in JS. Diagnostic only — both paths must
2180
2228
  * produce the same entity for a given point.
2181
2229
  */
2182
- __init108() {this._hitFusedGather = false}
2230
+ __init109() {this._hitFusedGather = false}
2183
2231
  /**
2184
2232
  * Whether `compute_aabbs` has run against the current frame's world matrices.
2185
2233
  * The AABB pass is only meaningful after a `compose_*`, so the fused gather
2186
2234
  * must not read the views before then.
2187
2235
  */
2188
- __init109() {this._wasmAabbsFresh = false}
2236
+ __init110() {this._wasmAabbsFresh = false}
2189
2237
  /** Did the last hit-grid build use the fused (WASM-store) gather? */
2190
2238
  get hitGatherPath() {
2191
2239
  return this._hitFusedGather ? "fused" : "js";
@@ -2194,9 +2242,9 @@ var Scene = (_class7 = class _Scene {
2194
2242
  * Why the transform accelerator did or did not run on the most recent frame.
2195
2243
  * Written by the render walk and `_syncWasmStore`.
2196
2244
  */
2197
- __init110() {this._transformReason = "not-installed"}
2245
+ __init111() {this._transformReason = "not-installed"}
2198
2246
  /** Why the batched-driver accelerator did or did not run. */
2199
- __init111() {this._animReason = "not-installed"}
2247
+ __init112() {this._animReason = "not-installed"}
2200
2248
  /**
2201
2249
  * Why the hit-test accelerator did or did not serve the last pointer query.
2202
2250
  * The grid is built lazily on demand, not every frame, so this describes the
@@ -2204,11 +2252,11 @@ var Scene = (_class7 = class _Scene {
2204
2252
  * before a backend exists; `_ensureHitGrid` moves it to `'not-applicable'`
2205
2253
  * once one is installed but nothing has queried yet.
2206
2254
  */
2207
- __init112() {this._hitReason = "not-installed"}
2255
+ __init113() {this._hitReason = "not-installed"}
2208
2256
  /** Why the particle accelerator did or did not run. */
2209
- __init113() {this._particleReason = "not-applicable"}
2257
+ __init114() {this._particleReason = "not-applicable"}
2210
2258
  /** Which particle implementation actually simulated the most recent frame. */
2211
- __init114() {this._particlePath = "none"}
2259
+ __init115() {this._particlePath = "none"}
2212
2260
  /**
2213
2261
  * Per-frame status of every invisible accelerator: whether each is installed,
2214
2262
  * whether it actually ran on the most recent frame, and why.
@@ -2345,7 +2393,7 @@ var Scene = (_class7 = class _Scene {
2345
2393
  const idx = cell[k];
2346
2394
  if (x < minx[idx] || x > maxx[idx] || y < miny[idx] || y > maxy[idx]) continue;
2347
2395
  const entity = this._hitSlotEntity[idx];
2348
- if (_optionalChain([entity, 'optionalAccess', _36 => _36.isPointInside, 'call', _37 => _37(x, y)]) && this.isHitEligible(entity, x, y)) {
2396
+ if (_optionalChain([entity, 'optionalAccess', _39 => _39.isPointInside, 'call', _40 => _40(x, y)]) && this.isHitEligible(entity, x, y)) {
2349
2397
  bestIndex = idx;
2350
2398
  bestEntity = entity;
2351
2399
  break;
@@ -2368,25 +2416,25 @@ var Scene = (_class7 = class _Scene {
2368
2416
  // EasingFn (which cannot cross into WASM) all fall through to it — WASM can
2369
2417
  // only ever change *how* a driver is advanced, never *what* value it lands
2370
2418
  // on.
2371
- __init115() {this._animWasm = null}
2419
+ __init116() {this._animWasm = null}
2372
2420
  // Entities with at least one active driver, added by Entity._spawnDriver.
2373
2421
  // Self-pruning: _tickBatchedDrivers drops an entry the first time it visits
2374
2422
  // an entity whose drivers have since all completed or been removed. This is
2375
2423
  // what lets the batch pass find its candidates in O(active drivers), not
2376
2424
  // O(tree size) — the exact mistake G3's first integrated benchmark made.
2377
- __init116() {this._activeDriverEntities = /* @__PURE__ */ new Set()}
2425
+ __init117() {this._activeDriverEntities = /* @__PURE__ */ new Set()}
2378
2426
  // Reused across frames instead of allocating a fresh array + N {entity,prop,
2379
2427
  // driver} objects every call — the integrated benchmark
2380
2428
  // (benchmarks/anim-wasm-scene) found that allocation churn was the
2381
2429
  // dominant integrated cost, not the wasm kernel itself. Parallel arrays,
2382
2430
  // truncated to the live count after each use so a stale tail slot never
2383
2431
  // 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 = []}
2432
+ __init118() {this._springEntities = []}
2433
+ __init119() {this._springProps = []}
2434
+ __init120() {this._springDrivers = []}
2435
+ __init121() {this._tweenEntities = []}
2436
+ __init122() {this._tweenProps = []}
2437
+ __init123() {this._tweenDrivers = []}
2390
2438
  /**
2391
2439
  * Minimum number of batchable (spring, or named-easing tween) active drivers
2392
2440
  * before a frame engages the WASM batch path at all; below it, every driver
@@ -2458,7 +2506,7 @@ var Scene = (_class7 = class _Scene {
2458
2506
  * Setting {@link animDriverGateCount} overwrites all three, so existing code
2459
2507
  * that tuned the single knob keeps working unchanged.
2460
2508
  */
2461
- __init123() {this._animBatchedLastFrame = false}
2509
+ __init124() {this._animBatchedLastFrame = false}
2462
2510
  /**
2463
2511
  * Whether the WASM batch path actually ran on the most recent frame.
2464
2512
  *
@@ -2470,7 +2518,7 @@ var Scene = (_class7 = class _Scene {
2470
2518
  get animBatchedLastFrame() {
2471
2519
  return this._animBatchedLastFrame;
2472
2520
  }
2473
- __init124() {this.animGate = {
2521
+ __init125() {this.animGate = {
2474
2522
  spring: 128,
2475
2523
  tween: 256,
2476
2524
  mixed: 128
@@ -2508,7 +2556,7 @@ var Scene = (_class7 = class _Scene {
2508
2556
  // (benchmarks/particle-wasm). f32 (matches the WGSL shader), bit-identical to
2509
2557
  // a JS f32 reference oracle; updateCPU (f64) stays the permanent fallback when
2510
2558
  // no backend is installed or a scene runs on WebGPU.
2511
- __init125() {this._particleWasm = null}
2559
+ __init126() {this._particleWasm = null}
2512
2560
  /** Which backend runs the CPU particle simulation. Reflects only whether a
2513
2561
  * backend is installed (the WebGPU compute path, when active, is used first
2514
2562
  * regardless). */
@@ -2785,24 +2833,24 @@ var Scene = (_class7 = class _Scene {
2785
2833
  * sync, so retaining the order prevents a newly opened overlay from spending
2786
2834
  * its first frame below previously projected controls.
2787
2835
  */
2788
- __init126() {this.a11yRenderOrders = /* @__PURE__ */ new Map()}
2836
+ __init127() {this.a11yRenderOrders = /* @__PURE__ */ new Map()}
2789
2837
  // 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}
2838
+ __init128() {this.pointRenderer = null}
2839
+ __init129() {this.glCanvas = null}
2840
+ __init130() {this.glContextLostHandler = null}
2841
+ __init131() {this.glContextRestoredHandler = null}
2794
2842
 
2795
2843
 
2796
2844
 
2797
- __init131() {this.disableWindowResize = false}
2845
+ __init132() {this.disableWindowResize = false}
2798
2846
  /** See {@link SceneOptions.maxDPR}. `undefined` = uncapped (real DPR). */
2799
2847
 
2800
2848
  // 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}
2849
+ __init133() {this.destroyed = false}
2850
+ __init134() {this.device = null}
2851
+ __init135() {this.deviceLost = false}
2852
+ __init136() {this.particleBackend = "auto"}
2853
+ __init137() {this._webgpuDisabled = false}
2806
2854
  get webgpuDisabled() {
2807
2855
  return this._webgpuDisabled || this.particleBackend === "cpu";
2808
2856
  }
@@ -2816,7 +2864,7 @@ var Scene = (_class7 = class _Scene {
2816
2864
  * active.
2817
2865
  */
2818
2866
  get webglDrawStats() {
2819
- return _nullishCoalesce(_optionalChain([this, 'access', _38 => _38.pointRenderer, 'optionalAccess', _39 => _39.stats, 'optionalCall', _40 => _40()]), () => ( null));
2867
+ return _nullishCoalesce(_optionalChain([this, 'access', _41 => _41.pointRenderer, 'optionalAccess', _42 => _42.stats, 'optionalCall', _43 => _43()]), () => ( null));
2820
2868
  }
2821
2869
  /**
2822
2870
  * Whether a WebGPU device is currently live for particle compute.
@@ -2830,22 +2878,22 @@ var Scene = (_class7 = class _Scene {
2830
2878
  set webgpuDisabled(value) {
2831
2879
  this._webgpuDisabled = value;
2832
2880
  }
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}
2881
+ __init138() {this.recoveryTimerId = null}
2882
+ __init139() {this.manager = null}
2883
+ __init140() {this.initializingWebGPU = false}
2884
+ __init141() {this.gpuCanvas = null}
2885
+ __init142() {this.gpuContext = null}
2838
2886
  /** 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}
2887
+ __init143() {this.gpuHasContent = false}
2888
+ __init144() {this.mouseX = -9999}
2889
+ __init145() {this.mouseY = -9999}
2890
+ __init146() {this.pointerMoveListener = null}
2891
+ __init147() {this.pointerLeaveListener = null}
2844
2892
  /** Element the pointer listeners are bound to (parent container if present,
2845
2893
  * 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}
2894
+ __init148() {this.pointerEventTarget = null}
2895
+ __init149() {this.hasWarnedZeroSize = false}
2896
+ __init150() {this.fontLoadHandler = null}
2849
2897
  // ── Dev-mode warning infrastructure ──────────────────────────────
2850
2898
  //
2851
2899
  // Enable with `Scene.devMode = true` or by setting `globalThis.__DEV__`.
@@ -2874,12 +2922,12 @@ var Scene = (_class7 = class _Scene {
2874
2922
  static _devModeDetected() {
2875
2923
  if (_Scene._devMode) return true;
2876
2924
  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;
2925
+ if (_optionalChain([gp, 'optionalAccess', _44 => _44.__DEV__])) return true;
2926
+ if (_optionalChain([gp, 'optionalAccess', _45 => _45.process, 'optionalAccess', _46 => _46.env, 'optionalAccess', _47 => _47.NODE_ENV]) === "development") return true;
2879
2927
  return false;
2880
2928
  }
2881
2929
 
2882
- __init150() {this._devFrameCount = 0}
2930
+ __init151() {this._devFrameCount = 0}
2883
2931
  _devWarn(message) {
2884
2932
  if (!this._devActive) return;
2885
2933
  console.warn(`[vectojs/dev] ${message}`);
@@ -2937,12 +2985,14 @@ var Scene = (_class7 = class _Scene {
2937
2985
  let checked = 0;
2938
2986
  const walkProjections = (node) => {
2939
2987
  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)]);
2988
+ const proj = _optionalChain([node, 'access', _48 => _48.getContentProjection, 'optionalCall', _49 => _49()]);
2989
+ if (_optionalChain([proj, 'optionalAccess', _50 => _50.text]) && proj.selectable !== false) {
2990
+ const el = _optionalChain([this, 'access', _51 => _51.contentElements, 'optionalAccess', _52 => _52.get, 'call', _53 => _53(node.id)]);
2943
2991
  if (el) {
2944
2992
  const projectedText = el.textContent || "";
2945
- if (projectedText !== "" && projectedText !== proj.text) {
2993
+ const windowed = el.dataset.vectoProjectionWindow !== void 0;
2994
+ const mismatched = windowed ? !proj.text.includes(projectedText) : projectedText !== proj.text;
2995
+ if (projectedText !== "" && mismatched) {
2946
2996
  this._devWarn(
2947
2997
  `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
2998
  );
@@ -2954,14 +3004,14 @@ var Scene = (_class7 = class _Scene {
2954
3004
  };
2955
3005
  walkProjections(this.root);
2956
3006
  }
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);
3007
+ 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);
2958
3008
  this.canvas = canvas;
2959
3009
  this.debugA11y = _nullishCoalesce(options.debugA11y, () => ( false));
2960
3010
  this.disableWindowResize = _nullishCoalesce(options.disableWindowResize, () => ( false));
2961
3011
  this.maxDPR = options.maxDPR;
2962
3012
  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]));
3013
+ const styleWidth = parseInlinePx(_optionalChain([canvas, 'access', _54 => _54.style, 'optionalAccess', _55 => _55.width]));
3014
+ const styleHeight = parseInlinePx(_optionalChain([canvas, 'access', _56 => _56.style, 'optionalAccess', _57 => _57.height]));
2965
3015
  this.width = _nullishCoalesce(styleWidth, () => ( (canvas.width || canvas.clientWidth || 0)));
2966
3016
  this.height = _nullishCoalesce(styleHeight, () => ( (canvas.height || canvas.clientHeight || 0)));
2967
3017
  } else {
@@ -2969,7 +3019,7 @@ var Scene = (_class7 = class _Scene {
2969
3019
  this.height = typeof window !== "undefined" ? window.innerHeight : canvas.clientHeight || canvas.height || 600;
2970
3020
  }
2971
3021
  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");
3022
+ 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
3023
  this.maxFPS = _nullishCoalesce(options.maxFPS, () => ( (isTest ? 0 : 60)));
2974
3024
  this.respectReducedMotion = _nullishCoalesce(options.respectReducedMotion, () => ( true));
2975
3025
  this.autoThrottle = _nullishCoalesce(options.autoThrottle, () => ( true));
@@ -2987,9 +3037,9 @@ var Scene = (_class7 = class _Scene {
2987
3037
  if (typeof window !== "undefined" && typeof window.matchMedia === "function") {
2988
3038
  this.forcedColorsQuery = window.matchMedia("(forced-colors: active)");
2989
3039
  this.forcedColorsChangeHandler = () => this.markDirty();
2990
- _optionalChain([this, 'access', _59 => _59.forcedColorsQuery, 'access', _60 => _60.addEventListener, 'optionalCall', _61 => _61("change", this.forcedColorsChangeHandler)]);
3040
+ _optionalChain([this, 'access', _62 => _62.forcedColorsQuery, 'access', _63 => _63.addEventListener, 'optionalCall', _64 => _64("change", this.forcedColorsChangeHandler)]);
2991
3041
  }
2992
- this.root = new class RootEntity extends _chunkZ3HFY75Rjs.Entity {
3042
+ this.root = new class RootEntity extends _chunk7PI7LVWWjs.Entity {
2993
3043
  isPointInside() {
2994
3044
  return false;
2995
3045
  }
@@ -2998,7 +3048,7 @@ var Scene = (_class7 = class _Scene {
2998
3048
  }
2999
3049
  }("root");
3000
3050
  this.root._scene = this;
3001
- this.overlayRoot = new class OverlayRoot extends _chunkZ3HFY75Rjs.Entity {
3051
+ this.overlayRoot = new class OverlayRoot extends _chunk7PI7LVWWjs.Entity {
3002
3052
  isPointInside() {
3003
3053
  return false;
3004
3054
  }
@@ -3015,9 +3065,9 @@ var Scene = (_class7 = class _Scene {
3015
3065
  this.maxDPR
3016
3066
  );
3017
3067
  }
3018
- _optionalChain([this, 'access', _62 => _62.renderer, 'access', _63 => _63.onContextRestored, 'optionalCall', _64 => _64(() => {
3068
+ _optionalChain([this, 'access', _65 => _65.renderer, 'access', _66 => _66.onContextRestored, 'optionalCall', _67 => _67(() => {
3019
3069
  this.markDirty();
3020
- if (_optionalChain([this, 'access', _65 => _65.renderer, 'access', _66 => _66.isContextLost, 'optionalCall', _67 => _67()]) !== true) this.render(this.renderer);
3070
+ if (_optionalChain([this, 'access', _68 => _68.renderer, 'access', _69 => _69.isContextLost, 'optionalCall', _70 => _70()]) !== true) this.render(this.renderer);
3021
3071
  })]);
3022
3072
  if (typeof document !== "undefined") {
3023
3073
  this.a11yRoot = document.createElement("div");
@@ -3187,7 +3237,7 @@ var Scene = (_class7 = class _Scene {
3187
3237
  watchDevicePixelRatio() {
3188
3238
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
3189
3239
  if (this.dprMediaQuery && this.dprChangeHandler) {
3190
- _optionalChain([this, 'access', _68 => _68.dprMediaQuery, 'access', _69 => _69.removeEventListener, 'optionalCall', _70 => _70("change", this.dprChangeHandler)]);
3240
+ _optionalChain([this, 'access', _71 => _71.dprMediaQuery, 'access', _72 => _72.removeEventListener, 'optionalCall', _73 => _73("change", this.dprChangeHandler)]);
3191
3241
  }
3192
3242
  const dpr = window.devicePixelRatio || 1;
3193
3243
  const query = window.matchMedia(`(resolution: ${dpr}dppx)`);
@@ -3195,7 +3245,7 @@ var Scene = (_class7 = class _Scene {
3195
3245
  this.resize(this.width, this.height);
3196
3246
  this.watchDevicePixelRatio();
3197
3247
  };
3198
- _optionalChain([query, 'access', _71 => _71.addEventListener, 'optionalCall', _72 => _72("change", handler)]);
3248
+ _optionalChain([query, 'access', _74 => _74.addEventListener, 'optionalCall', _75 => _75("change", handler)]);
3199
3249
  this.dprMediaQuery = query;
3200
3250
  this.dprChangeHandler = handler;
3201
3251
  }
@@ -3215,7 +3265,7 @@ var Scene = (_class7 = class _Scene {
3215
3265
  if (typeof gl.addEventListener !== "function") return;
3216
3266
  this.glContextLostHandler = (e) => {
3217
3267
  e.preventDefault();
3218
- _optionalChain([this, 'access', _73 => _73.pointRenderer, 'optionalAccess', _74 => _74.destroy, 'call', _75 => _75()]);
3268
+ _optionalChain([this, 'access', _76 => _76.pointRenderer, 'optionalAccess', _77 => _77.destroy, 'call', _78 => _78()]);
3219
3269
  this.pointRenderer = null;
3220
3270
  };
3221
3271
  this.glContextRestoredHandler = () => {
@@ -3245,26 +3295,26 @@ var Scene = (_class7 = class _Scene {
3245
3295
  * to the live DOM selection.
3246
3296
  */
3247
3297
  contentGridSelectionLine(el) {
3248
- const candidates = [_optionalChain([this, 'access', _76 => _76.contentSelectionAnchor, 'optionalAccess', _77 => _77.node])];
3298
+ const candidates = [_optionalChain([this, 'access', _79 => _79.contentSelectionAnchor, 'optionalAccess', _80 => _80.node])];
3249
3299
  if (typeof window !== "undefined" && typeof window.getSelection === "function") {
3250
3300
  const selection = window.getSelection();
3251
- candidates.push(_optionalChain([selection, 'optionalAccess', _78 => _78.anchorNode]), _optionalChain([selection, 'optionalAccess', _79 => _79.focusNode]));
3301
+ candidates.push(_optionalChain([selection, 'optionalAccess', _81 => _81.anchorNode]), _optionalChain([selection, 'optionalAccess', _82 => _82.focusNode]));
3252
3302
  }
3253
3303
  for (const candidate of candidates) {
3254
3304
  if (!candidate || !el.contains(candidate)) continue;
3255
3305
  let cursor = candidate;
3256
3306
  while (cursor && cursor.parentNode !== el) cursor = cursor.parentNode;
3257
- const lineIndex = _optionalChain([cursor, 'optionalAccess', _80 => _80.dataset, 'optionalAccess', _81 => _81.vectoGridLine]);
3307
+ const lineIndex = _optionalChain([cursor, 'optionalAccess', _83 => _83.dataset, 'optionalAccess', _84 => _84.vectoGridLine]);
3258
3308
  if (lineIndex !== void 0) return Number(lineIndex);
3259
3309
  }
3260
3310
  return null;
3261
3311
  }
3262
3312
  releaseContentSelectionForRebuild(el) {
3263
3313
  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);
3314
+ 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
3315
  if (!ownsSelection) return;
3266
3316
  this.endContentSelectionDrag();
3267
- _optionalChain([selection, 'optionalAccess', _84 => _84.removeAllRanges, 'call', _85 => _85()]);
3317
+ _optionalChain([selection, 'optionalAccess', _87 => _87.removeAllRanges, 'call', _88 => _88()]);
3268
3318
  }
3269
3319
  /**
3270
3320
  * Rebuild a content-projection element's DOM (`rebuild`) while preserving a
@@ -3323,7 +3373,7 @@ var Scene = (_class7 = class _Scene {
3323
3373
  }
3324
3374
  /** Convert browser viewport coordinates into this Scene's logical coordinates. */
3325
3375
  clientToScene(clientX, clientY) {
3326
- const rect = _optionalChain([this, 'access', _86 => _86.canvas, 'access', _87 => _87.getBoundingClientRect, 'optionalCall', _88 => _88()]);
3376
+ const rect = _optionalChain([this, 'access', _89 => _89.canvas, 'access', _90 => _90.getBoundingClientRect, 'optionalCall', _91 => _91()]);
3327
3377
  if (!rect) return { x: clientX, y: clientY };
3328
3378
  const cssWidth = rect.width || this.canvas.clientWidth || this.width;
3329
3379
  const cssHeight = rect.height || this.canvas.clientHeight || this.height;
@@ -3361,7 +3411,7 @@ var Scene = (_class7 = class _Scene {
3361
3411
  cancelAnimationFrame(calibrationFrame);
3362
3412
  }
3363
3413
  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()]);
3414
+ _optionalChain([this, 'access', _92 => _92.contentGridCalibrationProbes, 'access', _93 => _93.get, 'call', _94 => _94(entityId), 'optionalAccess', _95 => _95.remove, 'call', _96 => _96()]);
3365
3415
  this.contentGridCalibrationProbes.delete(entityId);
3366
3416
  delete el.dataset.vectoGridCalibrationPending;
3367
3417
  delete el.dataset.vectoGridCalibration;
@@ -3413,7 +3463,7 @@ var Scene = (_class7 = class _Scene {
3413
3463
  }
3414
3464
  if (this.hoveredA11yElements.has(el)) {
3415
3465
  this.hoveredA11yElements.delete(el);
3416
- node.dispatchEvent(new (0, _chunkZ3HFY75Rjs.VectoJSEvent)("pointerleave", node, void 0, false));
3466
+ node.dispatchEvent(new (0, _chunk7PI7LVWWjs.VectoJSEvent)("pointerleave", node, void 0, false));
3417
3467
  }
3418
3468
  this.preserveFocusOnRemoval(el);
3419
3469
  el.remove();
@@ -3515,12 +3565,12 @@ var Scene = (_class7 = class _Scene {
3515
3565
  this.canvasResizeObserver = null;
3516
3566
  }
3517
3567
  if (this.dprMediaQuery && this.dprChangeHandler) {
3518
- _optionalChain([this, 'access', _94 => _94.dprMediaQuery, 'access', _95 => _95.removeEventListener, 'optionalCall', _96 => _96("change", this.dprChangeHandler)]);
3568
+ _optionalChain([this, 'access', _97 => _97.dprMediaQuery, 'access', _98 => _98.removeEventListener, 'optionalCall', _99 => _99("change", this.dprChangeHandler)]);
3519
3569
  this.dprMediaQuery = null;
3520
3570
  this.dprChangeHandler = null;
3521
3571
  }
3522
3572
  if (this.forcedColorsQuery && this.forcedColorsChangeHandler) {
3523
- _optionalChain([this, 'access', _97 => _97.forcedColorsQuery, 'access', _98 => _98.removeEventListener, 'optionalCall', _99 => _99("change", this.forcedColorsChangeHandler)]);
3573
+ _optionalChain([this, 'access', _100 => _100.forcedColorsQuery, 'access', _101 => _101.removeEventListener, 'optionalCall', _102 => _102("change", this.forcedColorsChangeHandler)]);
3524
3574
  this.forcedColorsQuery = null;
3525
3575
  this.forcedColorsChangeHandler = null;
3526
3576
  }
@@ -3538,9 +3588,9 @@ var Scene = (_class7 = class _Scene {
3538
3588
  }
3539
3589
  this.pointerEventTarget = null;
3540
3590
  }
3541
- _optionalChain([this, 'access', _100 => _100.a11yRoot, 'optionalAccess', _101 => _101.remove, 'call', _102 => _102()]);
3591
+ _optionalChain([this, 'access', _103 => _103.a11yRoot, 'optionalAccess', _104 => _104.remove, 'call', _105 => _105()]);
3542
3592
  this.focusSentinel = null;
3543
- _optionalChain([this, 'access', _103 => _103.portalRoot, 'optionalAccess', _104 => _104.remove, 'call', _105 => _105()]);
3593
+ _optionalChain([this, 'access', _106 => _106.portalRoot, 'optionalAccess', _107 => _107.remove, 'call', _108 => _108()]);
3544
3594
  this.a11yElements.clear();
3545
3595
  for (const el of this.contentElements.values()) el.remove();
3546
3596
  this.contentElements.clear();
@@ -3563,10 +3613,10 @@ var Scene = (_class7 = class _Scene {
3563
3613
  }
3564
3614
  this.glContextLostHandler = null;
3565
3615
  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()]);
3616
+ _optionalChain([this, 'access', _109 => _109.pointRenderer, 'optionalAccess', _110 => _110.destroy, 'call', _111 => _111()]);
3617
+ _optionalChain([this, 'access', _112 => _112.renderer, 'access', _113 => _113.dispose, 'optionalCall', _114 => _114()]);
3618
+ _optionalChain([this, 'access', _115 => _115.glCanvas, 'optionalAccess', _116 => _116.remove, 'call', _117 => _117()]);
3619
+ _optionalChain([this, 'access', _118 => _118.gpuCanvas, 'optionalAccess', _119 => _119.remove, 'call', _120 => _120()]);
3570
3620
  this.gpuCanvas = null;
3571
3621
  this.gpuContext = null;
3572
3622
  if (this.recoveryTimerId) {
@@ -3578,7 +3628,7 @@ var Scene = (_class7 = class _Scene {
3578
3628
  this.manager = null;
3579
3629
  }
3580
3630
  if (this.device) {
3581
- _optionalChain([this, 'access', _118 => _118.device, 'access', _119 => _119.destroy, 'optionalCall', _120 => _120()]);
3631
+ _optionalChain([this, 'access', _121 => _121.device, 'access', _122 => _122.destroy, 'optionalCall', _123 => _123()]);
3582
3632
  this.device = null;
3583
3633
  }
3584
3634
  }
@@ -3886,7 +3936,92 @@ var Scene = (_class7 = class _Scene {
3886
3936
  * predicate, which is only tractable while it has one home.
3887
3937
  */
3888
3938
  shouldProjectA11y(node) {
3889
- return node.interactive && (node.width > 0 || node.a11yFullViewport);
3939
+ if (!node.interactive) return false;
3940
+ if (!(node.width > 0 || node.a11yFullViewport)) return false;
3941
+ switch (node.a11yProjection) {
3942
+ case "never":
3943
+ return false;
3944
+ case "onDemand":
3945
+ return this.a11yEngaged(node);
3946
+ default:
3947
+ return true;
3948
+ }
3949
+ }
3950
+ /**
3951
+ * Whether an `a11yProjection: 'onDemand'` entity is currently engaged enough to
3952
+ * deserve a shadow node.
3953
+ *
3954
+ * Deliberately **not** hover alone. A keyboard or assistive-technology user
3955
+ * generates no pointer events, so a hover-only trigger would withhold the
3956
+ * semantic node from precisely the users it exists for. Three signals, any of
3957
+ * which counts:
3958
+ *
3959
+ * - **Focus.** Covers keyboard traversal and AT-driven focus. Checked against
3960
+ * the live element so a node keeps its own focus rather than being pruned out
3961
+ * from under the user mid-interaction.
3962
+ * - **Pointer target.** The entity under the pointer, so a mouse user gets the
3963
+ * same node a hover-gated design would have given them.
3964
+ * - **Explicit request.** {@link Scene.requestA11yProjection}, for anything the
3965
+ * app knows is significant — the selected item, a search hit, a
3966
+ * just-announced element. This is the escape hatch that keeps the mode usable
3967
+ * when neither focus nor pointer applies.
3968
+ *
3969
+ * The entity stays hit-testable on canvas regardless, so a click always reaches
3970
+ * it and promotes it on the next sync.
3971
+ */
3972
+ a11yEngaged(node) {
3973
+ if (this.a11yProjectionRequests.has(node.id)) return true;
3974
+ if (this.mouseX > -9e3 && !this.projectsSelectableText(node)) {
3975
+ if (node.isPointInside(this.mouseX, this.mouseY)) return true;
3976
+ }
3977
+ const existing = _optionalChain([this, 'access', _124 => _124.a11yElements, 'optionalAccess', _125 => _125.get, 'call', _126 => _126(node.id)]);
3978
+ if (existing && typeof document !== "undefined" && document.activeElement === existing) {
3979
+ return true;
3980
+ }
3981
+ return false;
3982
+ }
3983
+ /**
3984
+ * Whether `node` mirrors selectable text of its own.
3985
+ *
3986
+ * Such an entity must not be promoted by the pointer: its interactive a11y node
3987
+ * would sit above the text mirror and eat the mousedown that starts a native
3988
+ * selection.
3989
+ */
3990
+ projectsSelectableText(node) {
3991
+ const projection = _optionalChain([node, 'access', _127 => _127.getContentProjection, 'optionalCall', _128 => _128()]);
3992
+ return !!_optionalChain([projection, 'optionalAccess', _129 => _129.text]) && projection.selectable !== false;
3993
+ }
3994
+ /**
3995
+ * Keep `entity`'s a11y shadow node projected while it has
3996
+ * `a11yProjection: 'onDemand'`.
3997
+ *
3998
+ * For anything the application knows matters but the engine cannot infer — the
3999
+ * selected danmaku, a search hit, a node just announced in a live region.
4000
+ * Without this, `'onDemand'` would be reachable only by focus or pointer, and
4001
+ * an app-driven selection change would leave the selected entity semantically
4002
+ * invisible.
4003
+ *
4004
+ * Idempotent. Has no effect on an `'eager'` entity, which is always projected.
4005
+ */
4006
+ requestA11yProjection(entity) {
4007
+ const id = typeof entity === "string" ? entity : entity.id;
4008
+ if (this.a11yProjectionRequests.has(id)) return;
4009
+ this.a11yProjectionRequests.add(id);
4010
+ this.a11yNeedsReorder = true;
4011
+ this.markDirty({ entity: id, reason: "a11y-reorder" });
4012
+ }
4013
+ /**
4014
+ * Drop a projection request made by {@link requestA11yProjection}.
4015
+ *
4016
+ * The node is not removed immediately: it survives while it is focused or under
4017
+ * the pointer, and is pruned on the next sync that finds it unengaged. Releasing
4018
+ * a request the scene does not hold is a no-op.
4019
+ */
4020
+ releaseA11yProjection(entity) {
4021
+ const id = typeof entity === "string" ? entity : entity.id;
4022
+ if (!this.a11yProjectionRequests.delete(id)) return;
4023
+ this.a11yNeedsReorder = true;
4024
+ this.markDirty({ entity: id, reason: "a11y-reorder" });
3890
4025
  }
3891
4026
  syncA11y(node, container = null) {
3892
4027
  if (!this.a11yRoot) return;
@@ -3941,20 +4076,20 @@ var Scene = (_class7 = class _Scene {
3941
4076
  el.style.background = "transparent";
3942
4077
  }
3943
4078
  el.addEventListener("click", (e) => {
3944
- node.dispatchEvent(new (0, _chunkZ3HFY75Rjs.VectoJSEvent)("click", node, e));
4079
+ node.dispatchEvent(new (0, _chunk7PI7LVWWjs.VectoJSEvent)("click", node, e));
3945
4080
  });
3946
4081
  el.addEventListener("dblclick", (e) => {
3947
- node.dispatchEvent(new (0, _chunkZ3HFY75Rjs.VectoJSEvent)("dblclick", node, e));
4082
+ node.dispatchEvent(new (0, _chunk7PI7LVWWjs.VectoJSEvent)("dblclick", node, e));
3948
4083
  });
3949
4084
  el.addEventListener("mouseenter", (e) => {
3950
4085
  if (this.debugA11y) el.style.backgroundColor = "rgba(56, 189, 248, 0.2)";
3951
4086
  this.hoveredA11yElements.add(el);
3952
- node.dispatchEvent(new (0, _chunkZ3HFY75Rjs.VectoJSEvent)("hover", node, e, false));
4087
+ node.dispatchEvent(new (0, _chunk7PI7LVWWjs.VectoJSEvent)("hover", node, e, false));
3953
4088
  });
3954
4089
  el.addEventListener("mouseleave", (e) => {
3955
4090
  if (this.debugA11y) el.style.backgroundColor = "rgba(56, 189, 248, 0.05)";
3956
4091
  this.hoveredA11yElements.delete(el);
3957
- node.dispatchEvent(new (0, _chunkZ3HFY75Rjs.VectoJSEvent)("pointerleave", node, e, false));
4092
+ node.dispatchEvent(new (0, _chunk7PI7LVWWjs.VectoJSEvent)("pointerleave", node, e, false));
3958
4093
  });
3959
4094
  const capEl = el;
3960
4095
  const releasePointer = (event) => {
@@ -3970,24 +4105,24 @@ var Scene = (_class7 = class _Scene {
3970
4105
  };
3971
4106
  el.addEventListener("pointerdown", (e) => {
3972
4107
  if (typeof capEl.setPointerCapture === "function") capEl.setPointerCapture(e.pointerId);
3973
- node.dispatchEvent(new (0, _chunkZ3HFY75Rjs.VectoJSEvent)("pointerdown", node, e));
4108
+ node.dispatchEvent(new (0, _chunk7PI7LVWWjs.VectoJSEvent)("pointerdown", node, e));
3974
4109
  });
3975
4110
  el.addEventListener("pointerup", (e) => {
3976
4111
  releasePointer(e);
3977
- node.dispatchEvent(new (0, _chunkZ3HFY75Rjs.VectoJSEvent)("pointerup", node, e));
4112
+ node.dispatchEvent(new (0, _chunk7PI7LVWWjs.VectoJSEvent)("pointerup", node, e));
3978
4113
  });
3979
4114
  el.addEventListener("pointercancel", (e) => {
3980
4115
  releasePointer(e);
3981
- node.dispatchEvent(new (0, _chunkZ3HFY75Rjs.VectoJSEvent)("pointercancel", node, e));
4116
+ node.dispatchEvent(new (0, _chunk7PI7LVWWjs.VectoJSEvent)("pointercancel", node, e));
3982
4117
  });
3983
4118
  el.addEventListener(
3984
4119
  "pointermove",
3985
- (e) => node.dispatchEvent(new (0, _chunkZ3HFY75Rjs.VectoJSEvent)("pointermove", node, e))
4120
+ (e) => node.dispatchEvent(new (0, _chunk7PI7LVWWjs.VectoJSEvent)("pointermove", node, e))
3986
4121
  );
3987
4122
  el.addEventListener(
3988
4123
  "wheel",
3989
4124
  (e) => {
3990
- node.dispatchEvent(new (0, _chunkZ3HFY75Rjs.VectoJSEvent)("wheel", node, e));
4125
+ node.dispatchEvent(new (0, _chunk7PI7LVWWjs.VectoJSEvent)("wheel", node, e));
3991
4126
  },
3992
4127
  { passive: false }
3993
4128
  );
@@ -4012,10 +4147,10 @@ var Scene = (_class7 = class _Scene {
4012
4147
  );
4013
4148
  emitInitialScroll = emitScroll;
4014
4149
  el.addEventListener("keydown", (e) => {
4015
- node.dispatchEvent(new (0, _chunkZ3HFY75Rjs.VectoJSEvent)("keydown", node, e));
4150
+ node.dispatchEvent(new (0, _chunk7PI7LVWWjs.VectoJSEvent)("keydown", node, e));
4016
4151
  });
4017
4152
  el.addEventListener("keyup", (e) => {
4018
- node.dispatchEvent(new (0, _chunkZ3HFY75Rjs.VectoJSEvent)("keyup", node, e));
4153
+ node.dispatchEvent(new (0, _chunk7PI7LVWWjs.VectoJSEvent)("keyup", node, e));
4019
4154
  });
4020
4155
  if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {
4021
4156
  const input = el;
@@ -4046,7 +4181,7 @@ var Scene = (_class7 = class _Scene {
4046
4181
  el.addEventListener("compositionupdate", (e) => {
4047
4182
  const data = _nullishCoalesce(e.data, () => ( ""));
4048
4183
  composition = {
4049
- start: _nullishCoalesce(_optionalChain([composition, 'optionalAccess', _121 => _121.start]), () => ( 0)),
4184
+ start: _nullishCoalesce(_optionalChain([composition, 'optionalAccess', _130 => _130.start]), () => ( 0)),
4050
4185
  length: data.length
4051
4186
  };
4052
4187
  forward();
@@ -4081,7 +4216,7 @@ var Scene = (_class7 = class _Scene {
4081
4216
  el.addEventListener("keydown", (e) => {
4082
4217
  if (e.key === "Enter" || e.key === " ") {
4083
4218
  e.preventDefault();
4084
- node.dispatchEvent(new (0, _chunkZ3HFY75Rjs.VectoJSEvent)("click", node, e));
4219
+ node.dispatchEvent(new (0, _chunk7PI7LVWWjs.VectoJSEvent)("click", node, e));
4085
4220
  }
4086
4221
  });
4087
4222
  }
@@ -4288,7 +4423,7 @@ var Scene = (_class7 = class _Scene {
4288
4423
  const display = visible ? "" : "none";
4289
4424
  if (el.style.display !== display) el.style.display = display;
4290
4425
  }
4291
- _optionalChain([emitInitialScroll, 'optionalCall', _122 => _122()]);
4426
+ _optionalChain([emitInitialScroll, 'optionalCall', _131 => _131()]);
4292
4427
  }
4293
4428
  if (this._phaseTiming) {
4294
4429
  const projectionStart = performance.now();
@@ -4360,6 +4495,57 @@ var Scene = (_class7 = class _Scene {
4360
4495
  }
4361
4496
  return true;
4362
4497
  }
4498
+ /**
4499
+ * The band of an entity's own y coordinates that is worth projecting, or
4500
+ * `null` to project everything.
4501
+ *
4502
+ * {@link projectionBoxVisible} answers "is this entity near the viewport",
4503
+ * which frees whole blocks that scroll away. It cannot help a single entity
4504
+ * *taller* than the viewport: that entity's box always intersects, so every
4505
+ * one of its visual lines was materialized — a `<span>` per line and, on the
4506
+ * grid path, a `<span>` per glyph cluster. That is where "14.8k elements for a
4507
+ * 346KB Markdown doc" comes from, and it is O(document) rather than
4508
+ * O(viewport) in both element count and per-frame walk cost.
4509
+ *
4510
+ * Measured on one entity scrolled to its middle, real headed browsers
4511
+ * (`benchmarks/projection-per-line/`): at 4000 lines, materializing every line
4512
+ * costs 6.28 ms/frame on Chrome and 6.51 ms on Firefox with 36,000 child
4513
+ * elements, against 0.28/0.16 ms and 963 elements when only the visible band
4514
+ * is emitted. The gated cost is *flat* across a 20x document-size range, so
4515
+ * this converts an asymptote rather than shaving a constant.
4516
+ *
4517
+ * Returns local-y bounds in the entity's own coordinate space, already
4518
+ * expanded by `margin` and intersected with every `clipChildren` ancestor, so
4519
+ * a line inside a scrolled container is measured against the container rather
4520
+ * than the window. `null` means "no useful bound" — a degenerate transform, a
4521
+ * rotation/skew that makes a y-band meaningless, or a boundless entity — and
4522
+ * the caller must then project every line, because emitting nothing would
4523
+ * silently drop text from selection, find-in-page and screen readers.
4524
+ */
4525
+ projectionVisibleLocalYBand(node, tf, margin) {
4526
+ const { b, d, f } = tf;
4527
+ if (b !== 0 || d === 0 || !Number.isFinite(d) || !Number.isFinite(f)) return null;
4528
+ const top = (-margin - f) / d;
4529
+ const bottom = (this.height + margin - f) / d;
4530
+ let minY = Math.min(top, bottom);
4531
+ let maxY = Math.max(top, bottom);
4532
+ for (let ancestor = node.parent; ancestor; ancestor = ancestor.parent) {
4533
+ if (!ancestor.clipChildren || ancestor.width <= 0 || ancestor.height <= 0) continue;
4534
+ const originWorldY = f;
4535
+ const unitWorldY = d + f;
4536
+ const originLocal = ancestor.worldToLocal(tf.e, originWorldY);
4537
+ const unitLocal = ancestor.worldToLocal(tf.e + tf.c, unitWorldY);
4538
+ if (!originLocal || !unitLocal) return null;
4539
+ const slope = unitLocal.y - originLocal.y;
4540
+ if (slope === 0 || !Number.isFinite(slope)) return null;
4541
+ const a1 = (-margin - originLocal.y) / slope;
4542
+ const a2 = (ancestor.height + margin - originLocal.y) / slope;
4543
+ minY = Math.max(minY, Math.min(a1, a2));
4544
+ maxY = Math.min(maxY, Math.max(a1, a2));
4545
+ }
4546
+ if (!Number.isFinite(minY) || !Number.isFinite(maxY) || maxY < minY) return null;
4547
+ return { minY, maxY };
4548
+ }
4363
4549
  syncContentProjection(node) {
4364
4550
  if (!this.contentProjectionEnabled || !this.a11yRoot) return;
4365
4551
  let el = this.contentElements.get(node.id);
@@ -4377,7 +4563,10 @@ var Scene = (_class7 = class _Scene {
4377
4563
  releaseProjectionEl();
4378
4564
  return;
4379
4565
  }
4380
- const projection = node.getContentProjection();
4566
+ const lineBand = Number.isFinite(margin) ? this.projectionVisibleLocalYBand(node, worldTf, margin) : null;
4567
+ const projection = node.getContentProjection(
4568
+ lineBand ? { minY: lineBand.minY, maxY: lineBand.maxY } : void 0
4569
+ );
4381
4570
  if (!projection || !projection.text) {
4382
4571
  releaseProjectionEl();
4383
4572
  return;
@@ -4398,7 +4587,7 @@ var Scene = (_class7 = class _Scene {
4398
4587
  el.addEventListener(
4399
4588
  "wheel",
4400
4589
  (e2) => {
4401
- node.dispatchEvent(new (0, _chunkZ3HFY75Rjs.VectoJSEvent)("wheel", node, e2));
4590
+ node.dispatchEvent(new (0, _chunk7PI7LVWWjs.VectoJSEvent)("wheel", node, e2));
4402
4591
  },
4403
4592
  { passive: false }
4404
4593
  );
@@ -4412,18 +4601,24 @@ var Scene = (_class7 = class _Scene {
4412
4601
  }
4413
4602
  if (projection.grid) {
4414
4603
  const gridSyncStart = this._phaseTiming ? performance.now() : 0;
4415
- this.syncContentGridProjection(node, el, projection, projection.grid);
4604
+ this.syncContentGridProjection(node, el, projection, projection.grid, lineBand);
4416
4605
  if (this._phaseTiming) this._recordPhase("gridSync", performance.now() - gridSyncStart);
4417
4606
  } else if (lines && lines.length > 0) {
4607
+ const lineWindow = projectionLineWindow(lines, lineBand, _nullishCoalesce(projection.lineHeight, () => ( 16)));
4418
4608
  const signature = JSON.stringify({
4419
4609
  lines,
4420
4610
  fallbackFont: _nullishCoalesce(projection.font, () => ( "")),
4421
- fallbackLineHeight: _nullishCoalesce(projection.lineHeight, () => ( 16))
4611
+ fallbackLineHeight: _nullishCoalesce(projection.lineHeight, () => ( 16)),
4612
+ // Part of the signature, or scrolling would not rebuild the carriers and
4613
+ // the window would stay frozen where it was first built. Quantized to
4614
+ // whole line indices by construction, so a sub-pixel scroll inside one
4615
+ // line does not churn the DOM.
4616
+ window: lineWindow.gated ? `${lineWindow.start}-${lineWindow.end}` : "all"
4422
4617
  });
4423
4618
  if (el.dataset.vectoProjectionLines !== signature) {
4424
4619
  this.preserveContentSelectionAcrossRebuild(el, () => {
4425
4620
  el.replaceChildren();
4426
- for (let index = 0; index < lines.length; index++) {
4621
+ for (let index = lineWindow.start; index < lineWindow.end; index++) {
4427
4622
  const line = lines[index];
4428
4623
  const lineElement = document.createElement("span");
4429
4624
  const lineFont = _nullishCoalesce(_nullishCoalesce(line.font, () => ( projection.font)), () => ( ""));
@@ -4464,6 +4659,11 @@ var Scene = (_class7 = class _Scene {
4464
4659
  }
4465
4660
  });
4466
4661
  el.dataset.vectoProjectionLines = signature;
4662
+ if (lineWindow.gated) {
4663
+ el.dataset.vectoProjectionWindow = `${lineWindow.start}-${lineWindow.end}/${lines.length}`;
4664
+ } else {
4665
+ delete el.dataset.vectoProjectionWindow;
4666
+ }
4467
4667
  }
4468
4668
  } else {
4469
4669
  if (el.textContent !== projection.text) {
@@ -4512,11 +4712,12 @@ var Scene = (_class7 = class _Scene {
4512
4712
  * carrier from the shared canvas geometry. Browser font measurement happens
4513
4713
  * later in one cold read/write batch, never inside projection synchronization.
4514
4714
  */
4515
- syncContentGridProjection(node, el, projection, grid) {
4715
+ syncContentGridProjection(node, el, projection, grid, lineBand) {
4516
4716
  if (grid.source !== projection.text) {
4517
4717
  throw new Error("ContentProjection.grid.source must equal ContentProjection.text");
4518
4718
  }
4519
- const signature = `${grid.revision}`;
4719
+ const gridWindow = projectionGridLineWindow(grid, projection.lines, lineBand);
4720
+ const signature = gridWindow.gated ? `${grid.revision}:${gridWindow.start}-${gridWindow.end}` : `${grid.revision}`;
4520
4721
  if (el.dataset.vectoContentGrid !== signature) {
4521
4722
  const materializeStart = typeof performance !== "undefined" ? performance.now() : 0;
4522
4723
  this.clearContentGridState(node.id, el, false);
@@ -4524,12 +4725,13 @@ var Scene = (_class7 = class _Scene {
4524
4725
  const selectionLine = this.contentGridSelectionLine(el);
4525
4726
  let rebuiltSelectionLine = false;
4526
4727
  const existingLines = el.children;
4527
- for (let lineIndex = 0; lineIndex < grid.lines.length; lineIndex++) {
4728
+ for (let lineIndex = gridWindow.start; lineIndex < gridWindow.end; lineIndex++) {
4729
+ const domIndex = lineIndex - gridWindow.start;
4528
4730
  const gridLine = grid.lines[lineIndex];
4529
4731
  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));
4732
+ const lineHeight = _nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _132 => _132.lineHeight]), () => ( grid.lineHeight));
4733
+ const baseline = _nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _133 => _133.baseline]), () => ( grid.baseline));
4734
+ const lineFont = _nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _134 => _134.font]), () => ( grid.font));
4533
4735
  const lineSignature = contentGridLineSignature(
4534
4736
  grid,
4535
4737
  gridLine,
@@ -4539,7 +4741,7 @@ var Scene = (_class7 = class _Scene {
4539
4741
  lineFont,
4540
4742
  lineIndex === 0
4541
4743
  );
4542
- const reusable = existingLines[lineIndex];
4744
+ const reusable = existingLines[domIndex];
4543
4745
  if (reusable !== void 0 && reusable.dataset.vectoGridLineSig === lineSignature && reusable.dataset.vectoGridLine === `${lineIndex}`) {
4544
4746
  continue;
4545
4747
  }
@@ -4549,8 +4751,8 @@ var Scene = (_class7 = class _Scene {
4549
4751
  lineElement.dir = "ltr";
4550
4752
  lineElement.dataset.vectoGridLine = `${lineIndex}`;
4551
4753
  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`;
4754
+ lineElement.style.left = `${_nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _135 => _135.x]), () => ( 0))}px`;
4755
+ lineElement.style.top = `${(_nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _136 => _136.y]), () => ( lineIndex * grid.lineHeight))) + baseline - _text.cssLineBoxBaseline.call(void 0, lineFont, lineHeight)}px`;
4554
4756
  lineElement.style.width = `${gridLine.width}px`;
4555
4757
  lineElement.style.height = `${lineHeight}px`;
4556
4758
  lineElement.style.whiteSpace = "pre";
@@ -4612,19 +4814,25 @@ var Scene = (_class7 = class _Scene {
4612
4814
  lineElement.appendChild(marker);
4613
4815
  }
4614
4816
  }
4615
- const occupant = el.children[lineIndex];
4817
+ const occupant = el.children[domIndex];
4616
4818
  if (occupant) el.replaceChild(lineElement, occupant);
4617
4819
  else el.appendChild(lineElement);
4618
4820
  }
4619
- while (el.children.length > grid.lines.length) {
4620
- if (selectionLine !== null && selectionLine >= grid.lines.length) {
4821
+ const windowLength = gridWindow.end - gridWindow.start;
4822
+ while (el.children.length > windowLength) {
4823
+ if (selectionLine !== null && selectionLine >= gridWindow.end) {
4621
4824
  rebuiltSelectionLine = true;
4622
4825
  }
4623
- _optionalChain([el, 'access', _128 => _128.lastElementChild, 'optionalAccess', _129 => _129.remove, 'call', _130 => _130()]);
4826
+ _optionalChain([el, 'access', _137 => _137.lastElementChild, 'optionalAccess', _138 => _138.remove, 'call', _139 => _139()]);
4624
4827
  }
4625
4828
  if (rebuiltSelectionLine) this.releaseContentSelectionForRebuild(el);
4626
4829
  el.dataset.vectoProjectionLines = signature;
4627
4830
  el.dataset.vectoContentGrid = signature;
4831
+ if (gridWindow.gated) {
4832
+ el.dataset.vectoProjectionWindow = `${gridWindow.start}-${gridWindow.end}/${grid.lines.length}`;
4833
+ } else {
4834
+ delete el.dataset.vectoProjectionWindow;
4835
+ }
4628
4836
  el.dataset.vectoGridCarriers = `${el.querySelectorAll("[data-vecto-grid-cell]").length}`;
4629
4837
  if (typeof performance !== "undefined") {
4630
4838
  const materializeMs = performance.now() - materializeStart;
@@ -4684,7 +4892,7 @@ var Scene = (_class7 = class _Scene {
4684
4892
  if (previous !== void 0 && typeof cancelAnimationFrame === "function") {
4685
4893
  cancelAnimationFrame(previous);
4686
4894
  }
4687
- _optionalChain([this, 'access', _131 => _131.contentGridCalibrationProbes, 'access', _132 => _132.get, 'call', _133 => _133(entityId), 'optionalAccess', _134 => _134.remove, 'call', _135 => _135()]);
4895
+ _optionalChain([this, 'access', _140 => _140.contentGridCalibrationProbes, 'access', _141 => _141.get, 'call', _142 => _142(entityId), 'optionalAccess', _143 => _143.remove, 'call', _144 => _144()]);
4688
4896
  this.contentGridCalibrationProbes.delete(entityId);
4689
4897
  const calibrationStart = typeof performance !== "undefined" ? performance.now() : 0;
4690
4898
  const probe = document.createElement("div");
@@ -4718,7 +4926,7 @@ var Scene = (_class7 = class _Scene {
4718
4926
  target.dataset.vectoGridCalib = generation;
4719
4927
  continue;
4720
4928
  }
4721
- const sourceText = _nullishCoalesce(_optionalChain([target, 'access', _136 => _136.textContent, 'optionalAccess', _137 => _137.slice, 'call', _138 => _138(0, sourceLength)]), () => ( ""));
4929
+ const sourceText = _nullishCoalesce(_optionalChain([target, 'access', _145 => _145.textContent, 'optionalAccess', _146 => _146.slice, 'call', _147 => _147(0, sourceLength)]), () => ( ""));
4722
4930
  if (!sourceText) {
4723
4931
  target.dataset.vectoGridCalib = generation;
4724
4932
  continue;
@@ -4965,12 +5173,12 @@ var Scene = (_class7 = class _Scene {
4965
5173
  syncOverlayGeometry() {
4966
5174
  const parent = this.canvas.parentElement;
4967
5175
  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;
5176
+ const canvasRect = _optionalChain([this, 'access', _148 => _148.canvas, 'access', _149 => _149.getBoundingClientRect, 'optionalCall', _150 => _150()]);
5177
+ const parentRect = _optionalChain([parent, 'access', _151 => _151.getBoundingClientRect, 'optionalCall', _152 => _152()]);
5178
+ const cssWidth = _optionalChain([canvasRect, 'optionalAccess', _153 => _153.width]) || this.canvas.clientWidth || this.width;
5179
+ const cssHeight = _optionalChain([canvasRect, 'optionalAccess', _154 => _154.height]) || this.canvas.clientHeight || this.height;
5180
+ const left = (_nullishCoalesce(_optionalChain([canvasRect, 'optionalAccess', _155 => _155.left]), () => ( 0))) - (_nullishCoalesce(_optionalChain([parentRect, 'optionalAccess', _156 => _156.left]), () => ( 0))) - (parent.clientLeft || 0) + parent.scrollLeft;
5181
+ const top = (_nullishCoalesce(_optionalChain([canvasRect, 'optionalAccess', _157 => _157.top]), () => ( 0))) - (_nullishCoalesce(_optionalChain([parentRect, 'optionalAccess', _158 => _158.top]), () => ( 0))) - (parent.clientTop || 0) + parent.scrollTop;
4974
5182
  const scaleX = this.width > 0 ? cssWidth / this.width : 1;
4975
5183
  const scaleY = this.height > 0 ? cssHeight / this.height : 1;
4976
5184
  const prev = this._overlayGeometry;
@@ -5109,7 +5317,7 @@ var Scene = (_class7 = class _Scene {
5109
5317
  * (and {@link respectReducedMotion} is on). `0` means uncapped.
5110
5318
  */
5111
5319
  effectiveMaxFPS() {
5112
- const reduced = this.respectReducedMotion && !!_optionalChain([this, 'access', _150 => _150.reducedMotionQuery, 'optionalAccess', _151 => _151.matches]);
5320
+ const reduced = this.respectReducedMotion && !!_optionalChain([this, 'access', _159 => _159.reducedMotionQuery, 'optionalAccess', _160 => _160.matches]);
5113
5321
  if (reduced)
5114
5322
  return this.maxFPS > 0 ? Math.min(this.maxFPS, REDUCED_MOTION_FPS) : REDUCED_MOTION_FPS;
5115
5323
  return this.maxFPS;
@@ -5204,7 +5412,7 @@ var Scene = (_class7 = class _Scene {
5204
5412
  * @param time - Current absolute time in milliseconds (default 0).
5205
5413
  */
5206
5414
  render(renderer, dt = 0, time = 0) {
5207
- if (_optionalChain([renderer, 'access', _152 => _152.isContextLost, 'optionalCall', _153 => _153()])) return;
5415
+ if (_optionalChain([renderer, 'access', _161 => _161.isContextLost, 'optionalCall', _162 => _162()])) return;
5208
5416
  const isMainRenderer = renderer === this.renderer;
5209
5417
  if (isMainRenderer && this.a11yRoot && this.canvas.parentElement) {
5210
5418
  const parentStyle = this.canvas.parentElement.style;
@@ -5348,14 +5556,14 @@ var Scene = (_class7 = class _Scene {
5348
5556
  }
5349
5557
  renderer.clear();
5350
5558
  if (isMainRenderer) {
5351
- _optionalChain([this, 'access', _154 => _154.pointRenderer, 'optionalAccess', _155 => _155.begin, 'call', _156 => _156()]);
5559
+ _optionalChain([this, 'access', _163 => _163.pointRenderer, 'optionalAccess', _164 => _164.begin, 'call', _165 => _165()]);
5352
5560
  }
5353
5561
  const vw = this.width;
5354
5562
  const vh = this.height;
5355
5563
  let walkHadAnimation = false;
5356
5564
  let walkHadInteractive = false;
5357
5565
  const runUpdate = (node) => {
5358
- const overridesUpdate = node.update !== _chunkZ3HFY75Rjs.Entity.prototype.update;
5566
+ const overridesUpdate = node.update !== _chunk7PI7LVWWjs.Entity.prototype.update;
5359
5567
  let pending = node.hasPendingAnimations();
5360
5568
  if (pending || overridesUpdate) {
5361
5569
  node.update(dt, time);
@@ -5364,7 +5572,7 @@ var Scene = (_class7 = class _Scene {
5364
5572
  if (pending) walkHadAnimation = true;
5365
5573
  if (!walkHadInteractive && node.interactive) walkHadInteractive = true;
5366
5574
  if (this._devActive && this._devFrameCount % 120 === 0) {
5367
- if (overridesUpdate && node.hasPendingAnimations === _chunkZ3HFY75Rjs.Entity.prototype.hasPendingAnimations) {
5575
+ if (overridesUpdate && node.hasPendingAnimations === _chunk7PI7LVWWjs.Entity.prototype.hasPendingAnimations) {
5368
5576
  this._devWarn(
5369
5577
  `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
5578
  );
@@ -5562,9 +5770,9 @@ var Scene = (_class7 = class _Scene {
5562
5770
  const flushT0 = this._phaseTiming ? performance.now() : 0;
5563
5771
  renderer.flush();
5564
5772
  if (isMainRenderer) {
5565
- _optionalChain([this, 'access', _157 => _157.pointRenderer, 'optionalAccess', _158 => _158.flush, 'call', _159 => _159()]);
5773
+ _optionalChain([this, 'access', _166 => _166.pointRenderer, 'optionalAccess', _167 => _167.flush, 'call', _168 => _168()]);
5566
5774
  }
5567
- _optionalChain([renderer, 'access', _160 => _160.present, 'optionalCall', _161 => _161()]);
5775
+ _optionalChain([renderer, 'access', _169 => _169.present, 'optionalCall', _170 => _170()]);
5568
5776
  if (this._phaseTiming) this._recordPhase("flush", performance.now() - flushT0);
5569
5777
  if (flushTiming) endVectoUserTiming(flushTiming);
5570
5778
  if (this._devActive) {
@@ -5845,8 +6053,8 @@ function pointInBounds(b, x, y) {
5845
6053
  function contentGridLineSignature(grid, line, projected, lineHeight, baseline, font, isFirstLine) {
5846
6054
  const parts = [
5847
6055
  // 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]), () => ( ""))}`,
6056
+ `${_nullishCoalesce(_optionalChain([projected, 'optionalAccess', _171 => _171.x]), () => ( 0))}`,
6057
+ `${_nullishCoalesce(_optionalChain([projected, 'optionalAccess', _172 => _172.y]), () => ( ""))}`,
5850
6058
  `${lineHeight}`,
5851
6059
  `${baseline}`,
5852
6060
  font,
@@ -5888,20 +6096,20 @@ function defaultMeasurer() {
5888
6096
  sharedMeasurer ??= _layout.resolveGlyphMeasurer.call(void 0, "sans-serif");
5889
6097
  return sharedMeasurer;
5890
6098
  }
5891
- var TextEntity = (_class8 = class extends _chunkZ3HFY75Rjs.Entity {
6099
+ var TextEntity = (_class8 = class extends _chunk7PI7LVWWjs.Entity {
5892
6100
 
5893
6101
 
5894
6102
 
5895
6103
 
5896
- __init151() {this.nodes = []}
6104
+ __init152() {this.nodes = []}
5897
6105
 
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}
6106
+ __init153() {this.fillStyle = "#94a3b8"}
6107
+ __init154() {this.strokeStyle = null}
6108
+ __init155() {this.hoveredFillStyle = "#ffffff"}
6109
+ __init156() {this.lineWidth = 1}
6110
+ __init157() {this.isHovered = false}
5903
6111
  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);;
6112
+ super();_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);_class8.prototype.__init157.call(this);;
5905
6113
  this.text = text;
5906
6114
  this.atlas = atlas;
5907
6115
  this.fontSize = fontSize;
@@ -6014,17 +6222,17 @@ var TextEntity = (_class8 = class extends _chunkZ3HFY75Rjs.Entity {
6014
6222
  }, _class8);
6015
6223
 
6016
6224
  // src/components/GridTextEntity.ts
6017
- var GridTextEntity = (_class9 = class extends _chunkZ3HFY75Rjs.Entity {
6225
+ var GridTextEntity = (_class9 = class extends _chunk7PI7LVWWjs.Entity {
6018
6226
 
6019
- __init157() {this.fillStyle = "#ffffff"}
6020
- __init158() {this.grid = []}
6227
+ __init158() {this.fillStyle = "#ffffff"}
6228
+ __init159() {this.grid = []}
6021
6229
  // Array of rows
6022
- __init159() {this.cols = 0}
6023
- __init160() {this.rows = 0}
6230
+ __init160() {this.cols = 0}
6231
+ __init161() {this.rows = 0}
6024
6232
 
6025
6233
 
6026
6234
  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);;
6235
+ super();_class9.prototype.__init158.call(this);_class9.prototype.__init159.call(this);_class9.prototype.__init160.call(this);_class9.prototype.__init161.call(this);;
6028
6236
  this.fontSize = fontSize;
6029
6237
  this.charWidth = fontSize * 1;
6030
6238
  this.charHeight = fontSize * 1.1;
@@ -6033,7 +6241,7 @@ var GridTextEntity = (_class9 = class extends _chunkZ3HFY75Rjs.Entity {
6033
6241
  updateGrid(ascii) {
6034
6242
  this.grid = ascii;
6035
6243
  this.rows = ascii.length;
6036
- this.cols = _optionalChain([ascii, 'access', _164 => _164[0], 'optionalAccess', _165 => _165.length]) || 0;
6244
+ this.cols = _optionalChain([ascii, 'access', _173 => _173[0], 'optionalAccess', _174 => _174.length]) || 0;
6037
6245
  }
6038
6246
  isPointInside(_globalX, _globalY) {
6039
6247
  return false;
@@ -6108,7 +6316,7 @@ function distSqToSegment(px, py, x1, y1, x2, y2) {
6108
6316
  const ey = py - cy;
6109
6317
  return ex * ex + ey * ey;
6110
6318
  }
6111
- var SplineEntity = (_class10 = class extends _chunkZ3HFY75Rjs.Entity {
6319
+ var SplineEntity = (_class10 = class extends _chunk7PI7LVWWjs.Entity {
6112
6320
 
6113
6321
 
6114
6322
 
@@ -6116,23 +6324,23 @@ var SplineEntity = (_class10 = class extends _chunkZ3HFY75Rjs.Entity {
6116
6324
 
6117
6325
 
6118
6326
 
6119
- __init161() {this.offscreen = null}
6120
- __init162() {this.baked = false}
6327
+ __init162() {this.offscreen = null}
6328
+ __init163() {this.baked = false}
6121
6329
  /** Logical (CSS-pixel) size of the baked bitmap — the blit destination size. */
6122
- __init163() {this.bakedWidth = 0}
6123
- __init164() {this.bakedHeight = 0}
6330
+ __init164() {this.bakedWidth = 0}
6331
+ __init165() {this.bakedHeight = 0}
6124
6332
  /** Gradient strokes can't be baked to a solid-color bitmap; they render per-frame. */
6125
6333
 
6126
6334
  /** Lazily-flattened polylines (one Float32Array of [x,y,...] per segment) for hit-testing. */
6127
- __init165() {this.polylines = null}
6335
+ __init166() {this.polylines = null}
6128
6336
  /**
6129
6337
  * When `true`, the renderer draws a rounded-rect outline of the entity's
6130
6338
  * local bounds after painting the curves. Useful for drag feedback and
6131
6339
  * debugging hit areas. Defaults to `false`.
6132
6340
  */
6133
- __init166() {this.showBounds = false}
6341
+ __init167() {this.showBounds = false}
6134
6342
  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);;
6343
+ super();_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);_class10.prototype.__init167.call(this);;
6136
6344
  this.doc = doc;
6137
6345
  this.lineWidth = _nullishCoalesce(opts.lineWidth, () => ( 2));
6138
6346
  this.cache = _nullishCoalesce(opts.cache, () => ( true));
@@ -6143,7 +6351,7 @@ var SplineEntity = (_class10 = class extends _chunkZ3HFY75Rjs.Entity {
6143
6351
  this.width = this.bounds.width;
6144
6352
  this.height = this.bounds.height;
6145
6353
  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)));
6354
+ this.containsGradient = (_nullishCoalesce(_optionalChain([this, 'access', _175 => _175.doc, 'access', _176 => _176.equations, 'optionalAccess', _177 => _177.some, 'call', _178 => _178((eq) => isGradient(eq.color_rgb))]), () => ( false))) || (_nullishCoalesce(_optionalChain([this, 'access', _179 => _179.doc, 'access', _180 => _180.paths, 'optionalAccess', _181 => _181.some, 'call', _182 => _182((p) => isGradient(p.color_rgb))]), () => ( false)));
6147
6355
  this.interactive = true;
6148
6356
  }
6149
6357
  computeBounds() {
@@ -6383,7 +6591,7 @@ async function loadSpline(url) {
6383
6591
  }
6384
6592
 
6385
6593
  // src/components/Rect.ts
6386
- var Rect = class extends _chunkZ3HFY75Rjs.Entity {
6594
+ var Rect = class extends _chunk7PI7LVWWjs.Entity {
6387
6595
 
6388
6596
 
6389
6597
 
@@ -6432,7 +6640,7 @@ var Rect = class extends _chunkZ3HFY75Rjs.Entity {
6432
6640
  };
6433
6641
 
6434
6642
  // src/components/Circle.ts
6435
- var Circle = class extends _chunkZ3HFY75Rjs.Entity {
6643
+ var Circle = class extends _chunk7PI7LVWWjs.Entity {
6436
6644
 
6437
6645
 
6438
6646
 
@@ -6492,7 +6700,7 @@ var Circle = class extends _chunkZ3HFY75Rjs.Entity {
6492
6700
  };
6493
6701
 
6494
6702
  // src/components/Group.ts
6495
- var Group = class extends _chunkZ3HFY75Rjs.Entity {
6703
+ var Group = class extends _chunk7PI7LVWWjs.Entity {
6496
6704
  constructor(...children) {
6497
6705
  super();
6498
6706
  if (children.length > 0) this.add(...children);
@@ -6511,21 +6719,21 @@ var _math = require('@vectojs/math'); _createStarExport(_math);
6511
6719
 
6512
6720
 
6513
6721
  // src/tree/DOMPortalEntity.ts
6514
- var DOMPortalEntity = (_class11 = class extends _chunkZ3HFY75Rjs.Entity {
6722
+ var DOMPortalEntity = (_class11 = class extends _chunk7PI7LVWWjs.Entity {
6515
6723
 
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 = ""}
6724
+ __init168() {this.isDOMPortal = true}
6725
+ __init169() {this.domListeners = []}
6726
+ __init170() {this.resizeObserver = null}
6727
+ __init171() {this.domBound = false}
6728
+ __init172() {this.cachedWidth = 100}
6729
+ __init173() {this.cachedHeight = 100}
6730
+ __init174() {this.lastWidth = ""}
6731
+ __init175() {this.lastHeight = ""}
6732
+ __init176() {this.lastTransform = ""}
6733
+ __init177() {this.lastZIndex = ""}
6734
+ __init178() {this.lastOpacity = ""}
6527
6735
  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);;
6736
+ super(id);_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);_class11.prototype.__init178.call(this);;
6529
6737
  this.domElement = domElement;
6530
6738
  this.width = _nullishCoalesce(width, () => ( 0));
6531
6739
  this.height = _nullishCoalesce(height, () => ( 0));
@@ -6567,7 +6775,7 @@ var DOMPortalEntity = (_class11 = class extends _chunkZ3HFY75Rjs.Entity {
6567
6775
  ];
6568
6776
  for (const type of events) {
6569
6777
  const handler = (e) => {
6570
- this.dispatchEvent(new (0, _chunkZ3HFY75Rjs.VectoJSEvent)(type, this, e));
6778
+ this.dispatchEvent(new (0, _chunk7PI7LVWWjs.VectoJSEvent)(type, this, e));
6571
6779
  };
6572
6780
  this.domElement.addEventListener(type, handler);
6573
6781
  this.domListeners.push({ type, handler, capture: false });
@@ -6578,7 +6786,7 @@ var DOMPortalEntity = (_class11 = class extends _chunkZ3HFY75Rjs.Entity {
6578
6786
  ];
6579
6787
  for (const { native, vecto } of hoverEvents) {
6580
6788
  const handler = (e) => {
6581
- this.dispatchEvent(new (0, _chunkZ3HFY75Rjs.VectoJSEvent)(vecto, this, e, false));
6789
+ this.dispatchEvent(new (0, _chunk7PI7LVWWjs.VectoJSEvent)(vecto, this, e, false));
6582
6790
  };
6583
6791
  this.domElement.addEventListener(native, handler);
6584
6792
  this.domListeners.push({ type: native, handler, capture: false });
@@ -6586,7 +6794,7 @@ var DOMPortalEntity = (_class11 = class extends _chunkZ3HFY75Rjs.Entity {
6586
6794
  const focusEvents = ["focus", "blur"];
6587
6795
  for (const type of focusEvents) {
6588
6796
  const handler = (e) => {
6589
- this.dispatchEvent(new (0, _chunkZ3HFY75Rjs.VectoJSEvent)(type, this, e, true));
6797
+ this.dispatchEvent(new (0, _chunk7PI7LVWWjs.VectoJSEvent)(type, this, e, true));
6590
6798
  };
6591
6799
  this.domElement.addEventListener(type, handler, true);
6592
6800
  this.domListeners.push({ type, handler, capture: true });
@@ -6682,4 +6890,5 @@ Scene.registerWebGPUParticleSystemManager(_chunkM73XB4ZKjs.WebGPUParticleSystemM
6682
6890
 
6683
6891
 
6684
6892
 
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;
6893
+
6894
+ exports.CanvasRenderer = _chunkM73XB4ZKjs.CanvasRenderer; exports.Circle = Circle; exports.ComputeParticleEntity = ComputeParticleEntity; exports.DOMPortalEntity = DOMPortalEntity; exports.Entity = _chunk7PI7LVWWjs.Entity; exports.GlyphRasterAtlas = _chunkM73XB4ZKjs.GlyphRasterAtlas; exports.GridTextEntity = GridTextEntity; exports.Group = Group; exports.MSDFTextEntity = _chunk7PI7LVWWjs.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 = _chunk7PI7LVWWjs.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 = _chunk7PI7LVWWjs.VectoJSEvent; exports.WebGPUParticleSystemManager = _chunkM73XB4ZKjs.WebGPUParticleSystemManager; exports.beginVectoUserTiming = beginVectoUserTiming; exports.contentLineInHint = _chunk7PI7LVWWjs.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;