@vectojs/core 1.31.0 → 1.32.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1380,6 +1380,7 @@ var SCENE_OPTION_KEYS = [
1380
1380
  "autoThrottle",
1381
1381
  "contentProjection",
1382
1382
  "contentProjectionMargin",
1383
+ "contentSemanticBudget",
1383
1384
  "contentSemanticMargin",
1384
1385
  "debugA11y",
1385
1386
  "disableWindowResize",
@@ -1433,6 +1434,7 @@ function parseInlinePx(value) {
1433
1434
  const n = parseFloat(value);
1434
1435
  return Number.isFinite(n) && n > 0 ? n : null;
1435
1436
  }
1437
+ var DEFAULT_CONTENT_SEMANTIC_BUDGET = 256;
1436
1438
  function projectionGridLineWindow(grid, projectionLines, band) {
1437
1439
  const count = grid.lines.length;
1438
1440
  const all = { start: 0, end: count, gated: false };
@@ -2008,42 +2010,77 @@ var Scene = (_class7 = class _Scene {
2008
2010
  // any projected DOM. `undefined` → falls back to contentProjectionMargin, so
2009
2011
  // the default keeps one gate. `Infinity` = every block keeps resident text.
2010
2012
  __init66() {this.contentSemanticMargin = void 0}
2013
+ // How many coarse-tier blocks may be MATERIALIZED per sync, spreading the
2014
+ // resident tier's document-open cost across frames. `Infinity` = one
2015
+ // synchronous pass.
2016
+ __init67() {this.contentSemanticBudget = DEFAULT_CONTENT_SEMANTIC_BUDGET}
2017
+ // Remaining materializations in the CURRENT sync. Reset at the start of each
2018
+ // a11y walk; decremented per coarse block that creates its element.
2019
+ __init68() {this.contentSemanticBudgetLeft = 0}
2020
+ // Set when a sync deferred at least one block, so the scene knows to keep
2021
+ // drawing frames until the resident tier is complete. Without it a static
2022
+ // scene in `onDemand` mode would stop rendering with the document half
2023
+ // materialized and never finish.
2024
+ __init69() {this.contentSemanticDeferred = false}
2025
+ /**
2026
+ * Per-sync memo of "does the document hold a selection at all".
2027
+ *
2028
+ * Reading ANY property of a `Selection` (`anchorNode`, `rangeCount`, `type`,
2029
+ * `isCollapsed`) forces a synchronous layout, because Blink validates the
2030
+ * selection against current box geometry before answering. Measured in real
2031
+ * Chrome against a 1000-carrier subtree with layout dirtied between reads:
2032
+ * `anchorNode` 0.5ms, `rangeCount` 0.4ms, `type` 0.5ms, `isCollapsed` 0.5ms —
2033
+ * all indistinguishable from `offsetHeight` (0.5ms), against a 0ms floor for
2034
+ * mutating without reading. So there is no cheap property to probe with; the
2035
+ * only way to avoid the layout is to not touch the object at all.
2036
+ *
2037
+ * Materializing a block rebuilds its carriers, which asks whether the rebuild
2038
+ * would destroy a selection. Once per block, that read cost a forced layout
2039
+ * over the whole (and growing) projection subtree, which is what made
2040
+ * per-block cost rise with resident count: profiled at 1973 forced layouts
2041
+ * totalling 633ms of an 847ms 1000-block drain (75%).
2042
+ *
2043
+ * A selection is a single document-wide object and a sync walk cannot yield to
2044
+ * the user, so its presence cannot change mid-walk. Resolving it once per walk
2045
+ * turns O(blocks) forced layouts into O(1). `null` = not yet resolved.
2046
+ */
2047
+ __init70() {this.contentSelectionPresentThisSync = null}
2011
2048
  /**
2012
2049
  * True while a text-selection drag that started on a projection's blank
2013
2050
  * region (no text node under the press) is being driven manually — the
2014
2051
  * browser has no native anchor for it, so mousemove extends the Selection
2015
2052
  * from the position we resolved ourselves.
2016
2053
  */
2017
- __init67() {this.blankRegionSelectionDrag = false}
2018
- __init68() {this.contentSelectionAnchor = null}
2019
- __init69() {this.contentSelectionEndListener = null}
2054
+ __init71() {this.blankRegionSelectionDrag = false}
2055
+ __init72() {this.contentSelectionAnchor = null}
2056
+ __init73() {this.contentSelectionEndListener = null}
2020
2057
  // Animation/interactive flags collected during the render walk (tree-walk
2021
2058
  // fusion): the loop reads last frame's answers instead of re-walking the
2022
2059
  // tree up to 4× per tick. Start true so the first tick stays conservative.
2023
- __init70() {this.frameHadAnimation = true}
2024
- __init71() {this.frameHadInteractive = true}
2060
+ __init74() {this.frameHadAnimation = true}
2061
+ __init75() {this.frameHadInteractive = true}
2025
2062
 
2026
2063
  /** Active `(resolution: Ndppx)` media query watching for a runtime DPR change
2027
2064
  * (window moved between monitors, browser zoom) so the canvas backing store
2028
2065
  * can be re-scaled — otherwise it stays rasterized at the old DPR and blurs.
2029
2066
  * A resolution media query only fires when leaving its exact value, so the
2030
2067
  * handler re-arms a fresh query for the new DPR each time. */
2031
- __init72() {this.dprMediaQuery = null}
2068
+ __init76() {this.dprMediaQuery = null}
2032
2069
  /** For embedded (`disableWindowResize`) scenes: observes the canvas element so
2033
2070
  * a CSS/layout-driven size change re-runs `resize()`. A window `resize`
2034
2071
  * listener never fires for these (the window isn't what changed), so without
2035
2072
  * this an embedded canvas stayed at its initial size forever. */
2036
- __init73() {this.canvasResizeObserver = null}
2037
- __init74() {this.dprChangeHandler = null}
2038
- __init75() {this.focusedA11yElement = null}
2073
+ __init77() {this.canvasResizeObserver = null}
2074
+ __init78() {this.dprChangeHandler = null}
2075
+ __init79() {this.focusedA11yElement = null}
2039
2076
  /** Last geometry `syncOverlayGeometry` wrote, so an unchanged frame can skip the
2040
2077
  * style writes entirely. Reset to `null` to force the next sync (a new overlay
2041
2078
  * layer was created and has never been positioned). */
2042
- __init76() {this._overlayGeometry = null}
2079
+ __init80() {this._overlayGeometry = null}
2043
2080
  /** Shadow elements the pointer is currently inside. Lets a removal that happens
2044
2081
  * mid-hover synthesize the `pointerleave` the browser never sends for a
2045
2082
  * detached element, so the entity doesn't keep its hover state. */
2046
- __init77() {this.hoveredA11yElements = /* @__PURE__ */ new WeakSet()}
2083
+ __init81() {this.hoveredA11yElements = /* @__PURE__ */ new WeakSet()}
2047
2084
  /**
2048
2085
  * Entity ids the application has pinned via {@link requestA11yProjection}.
2049
2086
  *
@@ -2051,22 +2088,22 @@ var Scene = (_class7 = class _Scene {
2051
2088
  * a stale id simply never matches. Cleared per-entity by
2052
2089
  * {@link releaseA11yProjection}.
2053
2090
  */
2054
- __init78() {this.a11yProjectionRequests = /* @__PURE__ */ new Set()}
2091
+ __init82() {this.a11yProjectionRequests = /* @__PURE__ */ new Set()}
2055
2092
  /** Persistent tabindex=-1 element in a11yRoot. When the focused a11y mirror is
2056
2093
  * pruned (virtualization/streaming/removal) while it holds focus, we move
2057
2094
  * focus here instead of letting the browser drop it to <body> — keeping the
2058
2095
  * screen-reader virtual cursor inside the scene's a11y region. */
2059
- __init79() {this.focusSentinel = null}
2060
- __init80() {this.caretBlinkTimer = null}
2061
- __init81() {this.a11yNeedsReorder = true}
2062
- __init82() {this.portalRoot = null}
2063
- __init83() {this.fullViewportElements = []}
2064
- __init84() {this.normalElements = []}
2065
- __init85() {this.activeIds = /* @__PURE__ */ new Set()}
2096
+ __init83() {this.focusSentinel = null}
2097
+ __init84() {this.caretBlinkTimer = null}
2098
+ __init85() {this.a11yNeedsReorder = true}
2099
+ __init86() {this.portalRoot = null}
2100
+ __init87() {this.fullViewportElements = []}
2101
+ __init88() {this.normalElements = []}
2102
+ __init89() {this.activeIds = /* @__PURE__ */ new Set()}
2066
2103
  /** Per-parent insertion cursor, reused by `enforceA11yDomOrder`. */
2067
- __init86() {this.a11yOrderCursors = /* @__PURE__ */ new Map()}
2104
+ __init90() {this.a11yOrderCursors = /* @__PURE__ */ new Map()}
2068
2105
  /** Membership set for the elements being ordered, reused per reorder pass. */
2069
- __init87() {this.a11yOrderMembers = /* @__PURE__ */ new Set()}
2106
+ __init91() {this.a11yOrderMembers = /* @__PURE__ */ new Set()}
2070
2107
  /**
2071
2108
  * Elements that are an *ancestor* of another ordered element, reused per pass.
2072
2109
  *
@@ -2074,11 +2111,21 @@ var Scene = (_class7 = class _Scene {
2074
2111
  * items) spans every descendant row, so it must not extend a visual row band —
2075
2112
  * see {@link sortNormalElementsVisually}.
2076
2113
  */
2077
- __init88() {this.a11yOrderContainers = /* @__PURE__ */ new Set()}
2078
- __init89() {this.activePortalsThisFrame = /* @__PURE__ */ new Set()}
2079
- __init90() {this.activePortalsPrevFrame = /* @__PURE__ */ new Set()}
2080
- __init91() {this.portalEntities = /* @__PURE__ */ new Map()}
2081
- __init92() {this.renderOrderCounter = 0}
2114
+ __init92() {this.a11yOrderContainers = /* @__PURE__ */ new Set()}
2115
+ /**
2116
+ * Nearest `clipChildren` ancestor per ordered element — its *region* — reused
2117
+ * per pass. Written by `enforceA11yDomOrder`'s collect walk, which already has
2118
+ * the entity in hand, so a region costs one comparison per node rather than an
2119
+ * ancestor walk per element.
2120
+ *
2121
+ * Absent means the element sits under no clipping ancestor and belongs to the
2122
+ * implicit root region. See {@link sortNormalElementsVisually}.
2123
+ */
2124
+ __init93() {this.a11yOrderRegions = /* @__PURE__ */ new Map()}
2125
+ __init94() {this.activePortalsThisFrame = /* @__PURE__ */ new Set()}
2126
+ __init95() {this.activePortalsPrevFrame = /* @__PURE__ */ new Set()}
2127
+ __init96() {this.portalEntities = /* @__PURE__ */ new Map()}
2128
+ __init97() {this.renderOrderCounter = 0}
2082
2129
  /**
2083
2130
  * Monotonic render-frame counter, bumped once per authoritative `render()`
2084
2131
  * pass. Entities stamp their per-frame world-matrix cache with this value and
@@ -2087,7 +2134,7 @@ var Scene = (_class7 = class _Scene {
2087
2134
  * back to the ancestor walk. Public for the same reason `Entity._getTrig`/
2088
2135
  * `_setWorldCache` are: it is a cross-class render-internal contract.
2089
2136
  */
2090
- __init93() {this.currentFrame = 0}
2137
+ __init98() {this.currentFrame = 0}
2091
2138
  // ── WASM transform backend (invisible accelerator) ──────────────────────────
2092
2139
  // When `_transformBackend === 'wasm'`, the main render walk sources each
2093
2140
  // entity's world matrix from an SoA store composed by `_wasm` (see
@@ -2095,24 +2142,24 @@ var Scene = (_class7 = class _Scene {
2095
2142
  // fallback and the default: a null backend, a non-main renderer, or any entity
2096
2143
  // absent from the store all fall back to the JS composition, so WASM can only
2097
2144
  // ever change *how fast* a world matrix is produced, never *what* it is.
2098
- __init94() {this._wasm = null}
2099
- __init95() {this._transformBackend = "js"}
2145
+ __init99() {this._wasm = null}
2146
+ __init100() {this._transformBackend = "js"}
2100
2147
  // Resident store state (Stage 3). The store layout — slot assignment + sibling
2101
2148
  // runs — depends only on tree TOPOLOGY, so it is rebuilt only when the
2102
2149
  // structure changes (add/remove/reparent bump `_structureVersion`). Between
2103
2150
  // rebuilds the per-frame cost is: gather each entity's transform into the
2104
2151
  // resident wasm input view + run the kernel — no reallocation, no readback.
2105
- __init96() {this._treeStore = null}
2106
- __init97() {this._slotEntity = []}
2152
+ __init101() {this._treeStore = null}
2153
+ __init102() {this._slotEntity = []}
2107
2154
  // store slot -> entity (also validates slots)
2108
- __init98() {this._wasmInputs = null}
2109
- __init99() {this._wasmWorld = null}
2110
- __init100() {this._structureVersion = 0}
2111
- __init101() {this._storeStructureVersion = -1}
2155
+ __init103() {this._wasmInputs = null}
2156
+ __init104() {this._wasmWorld = null}
2157
+ __init105() {this._structureVersion = 0}
2158
+ __init106() {this._storeStructureVersion = -1}
2112
2159
  // Cached list of ComputeParticleEntity instances in the tree, keyed by the
2113
2160
  // structure version it was gathered at. Rebuilt only on a topology change.
2114
- __init102() {this._computeEntities = []}
2115
- __init103() {this._computeEntitiesVersion = -1}
2161
+ __init107() {this._computeEntities = []}
2162
+ __init108() {this._computeEntitiesVersion = -1}
2116
2163
  /** Invalidate the resident WASM store layout; the next wasm-mode frame rebuilds
2117
2164
  * it. Called by `Entity.add`/`remove` (topology changes only). */
2118
2165
  markStructureChanged() {
@@ -2197,7 +2244,7 @@ var Scene = (_class7 = class _Scene {
2197
2244
  * cached globally; the instance is per-Scene, which is the isolation that
2198
2245
  * actually matters.
2199
2246
  */
2200
- __init104() {this._wasmRuntime = null}
2247
+ __init109() {this._wasmRuntime = null}
2201
2248
  /**
2202
2249
  * Load (or reuse) this Scene's shared WASM runtime.
2203
2250
  *
@@ -2225,30 +2272,30 @@ var Scene = (_class7 = class _Scene {
2225
2272
  get wasmRuntime() {
2226
2273
  return this._wasmRuntime;
2227
2274
  }
2228
- __init105() {this._hitWasm = null}
2275
+ __init110() {this._hitWasm = null}
2229
2276
  // Cache key: which frame + structure version the grid was last (successfully,
2230
2277
  // non-overflowing) built for. findEntityAt is called ad-hoc (pointer
2231
2278
  // hover/click), not every frame, so the grid is refreshed lazily on demand
2232
2279
  // rather than proactively every render() — unlike the transform store, which
2233
2280
  // every frame's draw depends on.
2234
- __init106() {this._hitGridFrame = -1}
2235
- __init107() {this._hitGridOk = false}
2236
- __init108() {this._hitSlotEntity = []}
2237
- __init109() {this._hitBoundless = []}
2281
+ __init111() {this._hitGridFrame = -1}
2282
+ __init112() {this._hitGridOk = false}
2283
+ __init113() {this._hitSlotEntity = []}
2284
+ __init114() {this._hitBoundless = []}
2238
2285
  /** Reused buffer for the fused gather, so a pointer query allocates nothing. */
2239
- __init110() {this._hitGatherBuffer = null}
2286
+ __init115() {this._hitGatherBuffer = null}
2240
2287
  /**
2241
2288
  * Whether the last grid build sourced its AABBs from the WASM transform store
2242
2289
  * rather than recomputing them in JS. Diagnostic only — both paths must
2243
2290
  * produce the same entity for a given point.
2244
2291
  */
2245
- __init111() {this._hitFusedGather = false}
2292
+ __init116() {this._hitFusedGather = false}
2246
2293
  /**
2247
2294
  * Whether `compute_aabbs` has run against the current frame's world matrices.
2248
2295
  * The AABB pass is only meaningful after a `compose_*`, so the fused gather
2249
2296
  * must not read the views before then.
2250
2297
  */
2251
- __init112() {this._wasmAabbsFresh = false}
2298
+ __init117() {this._wasmAabbsFresh = false}
2252
2299
  /** Did the last hit-grid build use the fused (WASM-store) gather? */
2253
2300
  get hitGatherPath() {
2254
2301
  return this._hitFusedGather ? "fused" : "js";
@@ -2257,9 +2304,9 @@ var Scene = (_class7 = class _Scene {
2257
2304
  * Why the transform accelerator did or did not run on the most recent frame.
2258
2305
  * Written by the render walk and `_syncWasmStore`.
2259
2306
  */
2260
- __init113() {this._transformReason = "not-installed"}
2307
+ __init118() {this._transformReason = "not-installed"}
2261
2308
  /** Why the batched-driver accelerator did or did not run. */
2262
- __init114() {this._animReason = "not-installed"}
2309
+ __init119() {this._animReason = "not-installed"}
2263
2310
  /**
2264
2311
  * Why the hit-test accelerator did or did not serve the last pointer query.
2265
2312
  * The grid is built lazily on demand, not every frame, so this describes the
@@ -2267,11 +2314,11 @@ var Scene = (_class7 = class _Scene {
2267
2314
  * before a backend exists; `_ensureHitGrid` moves it to `'not-applicable'`
2268
2315
  * once one is installed but nothing has queried yet.
2269
2316
  */
2270
- __init115() {this._hitReason = "not-installed"}
2317
+ __init120() {this._hitReason = "not-installed"}
2271
2318
  /** Why the particle accelerator did or did not run. */
2272
- __init116() {this._particleReason = "not-applicable"}
2319
+ __init121() {this._particleReason = "not-applicable"}
2273
2320
  /** Which particle implementation actually simulated the most recent frame. */
2274
- __init117() {this._particlePath = "none"}
2321
+ __init122() {this._particlePath = "none"}
2275
2322
  /**
2276
2323
  * Per-frame status of every invisible accelerator: whether each is installed,
2277
2324
  * whether it actually ran on the most recent frame, and why.
@@ -2431,25 +2478,25 @@ var Scene = (_class7 = class _Scene {
2431
2478
  // EasingFn (which cannot cross into WASM) all fall through to it — WASM can
2432
2479
  // only ever change *how* a driver is advanced, never *what* value it lands
2433
2480
  // on.
2434
- __init118() {this._animWasm = null}
2481
+ __init123() {this._animWasm = null}
2435
2482
  // Entities with at least one active driver, added by Entity._spawnDriver.
2436
2483
  // Self-pruning: _tickBatchedDrivers drops an entry the first time it visits
2437
2484
  // an entity whose drivers have since all completed or been removed. This is
2438
2485
  // what lets the batch pass find its candidates in O(active drivers), not
2439
2486
  // O(tree size) — the exact mistake G3's first integrated benchmark made.
2440
- __init119() {this._activeDriverEntities = /* @__PURE__ */ new Set()}
2487
+ __init124() {this._activeDriverEntities = /* @__PURE__ */ new Set()}
2441
2488
  // Reused across frames instead of allocating a fresh array + N {entity,prop,
2442
2489
  // driver} objects every call — the integrated benchmark
2443
2490
  // (benchmarks/anim-wasm-scene) found that allocation churn was the
2444
2491
  // dominant integrated cost, not the wasm kernel itself. Parallel arrays,
2445
2492
  // truncated to the live count after each use so a stale tail slot never
2446
2493
  // pins a no-longer-active entity/driver in memory.
2447
- __init120() {this._springEntities = []}
2448
- __init121() {this._springProps = []}
2449
- __init122() {this._springDrivers = []}
2450
- __init123() {this._tweenEntities = []}
2451
- __init124() {this._tweenProps = []}
2452
- __init125() {this._tweenDrivers = []}
2494
+ __init125() {this._springEntities = []}
2495
+ __init126() {this._springProps = []}
2496
+ __init127() {this._springDrivers = []}
2497
+ __init128() {this._tweenEntities = []}
2498
+ __init129() {this._tweenProps = []}
2499
+ __init130() {this._tweenDrivers = []}
2453
2500
  /**
2454
2501
  * Minimum number of batchable (spring, or named-easing tween) active drivers
2455
2502
  * before a frame engages the WASM batch path at all; below it, every driver
@@ -2521,7 +2568,7 @@ var Scene = (_class7 = class _Scene {
2521
2568
  * Setting {@link animDriverGateCount} overwrites all three, so existing code
2522
2569
  * that tuned the single knob keeps working unchanged.
2523
2570
  */
2524
- __init126() {this._animBatchedLastFrame = false}
2571
+ __init131() {this._animBatchedLastFrame = false}
2525
2572
  /**
2526
2573
  * Whether the WASM batch path actually ran on the most recent frame.
2527
2574
  *
@@ -2533,7 +2580,7 @@ var Scene = (_class7 = class _Scene {
2533
2580
  get animBatchedLastFrame() {
2534
2581
  return this._animBatchedLastFrame;
2535
2582
  }
2536
- __init127() {this.animGate = {
2583
+ __init132() {this.animGate = {
2537
2584
  spring: 128,
2538
2585
  tween: 256,
2539
2586
  mixed: 128
@@ -2571,7 +2618,7 @@ var Scene = (_class7 = class _Scene {
2571
2618
  // (benchmarks/particle-wasm). f32 (matches the WGSL shader), bit-identical to
2572
2619
  // a JS f32 reference oracle; updateCPU (f64) stays the permanent fallback when
2573
2620
  // no backend is installed or a scene runs on WebGPU.
2574
- __init128() {this._particleWasm = null}
2621
+ __init133() {this._particleWasm = null}
2575
2622
  /** Which backend runs the CPU particle simulation. Reflects only whether a
2576
2623
  * backend is installed (the WebGPU compute path, when active, is used first
2577
2624
  * regardless). */
@@ -2848,24 +2895,24 @@ var Scene = (_class7 = class _Scene {
2848
2895
  * sync, so retaining the order prevents a newly opened overlay from spending
2849
2896
  * its first frame below previously projected controls.
2850
2897
  */
2851
- __init129() {this.a11yRenderOrders = /* @__PURE__ */ new Map()}
2898
+ __init134() {this.a11yRenderOrders = /* @__PURE__ */ new Map()}
2852
2899
  // Optional WebGL point-cloud layer (see SceneOptions.pointBackend).
2853
- __init130() {this.pointRenderer = null}
2854
- __init131() {this.glCanvas = null}
2855
- __init132() {this.glContextLostHandler = null}
2856
- __init133() {this.glContextRestoredHandler = null}
2900
+ __init135() {this.pointRenderer = null}
2901
+ __init136() {this.glCanvas = null}
2902
+ __init137() {this.glContextLostHandler = null}
2903
+ __init138() {this.glContextRestoredHandler = null}
2857
2904
 
2858
2905
 
2859
2906
 
2860
- __init134() {this.disableWindowResize = false}
2907
+ __init139() {this.disableWindowResize = false}
2861
2908
  /** See {@link SceneOptions.maxDPR}. `undefined` = uncapped (real DPR). */
2862
2909
 
2863
2910
  // WebGPU properties
2864
- __init135() {this.destroyed = false}
2865
- __init136() {this.device = null}
2866
- __init137() {this.deviceLost = false}
2867
- __init138() {this.particleBackend = "auto"}
2868
- __init139() {this._webgpuDisabled = false}
2911
+ __init140() {this.destroyed = false}
2912
+ __init141() {this.device = null}
2913
+ __init142() {this.deviceLost = false}
2914
+ __init143() {this.particleBackend = "auto"}
2915
+ __init144() {this._webgpuDisabled = false}
2869
2916
  get webgpuDisabled() {
2870
2917
  return this._webgpuDisabled || this.particleBackend === "cpu";
2871
2918
  }
@@ -2893,22 +2940,22 @@ var Scene = (_class7 = class _Scene {
2893
2940
  set webgpuDisabled(value) {
2894
2941
  this._webgpuDisabled = value;
2895
2942
  }
2896
- __init140() {this.recoveryTimerId = null}
2897
- __init141() {this.manager = null}
2898
- __init142() {this.initializingWebGPU = false}
2899
- __init143() {this.gpuCanvas = null}
2900
- __init144() {this.gpuContext = null}
2943
+ __init145() {this.recoveryTimerId = null}
2944
+ __init146() {this.manager = null}
2945
+ __init147() {this.initializingWebGPU = false}
2946
+ __init148() {this.gpuCanvas = null}
2947
+ __init149() {this.gpuContext = null}
2901
2948
  /** True while the GPU canvas holds a presented particle frame (needs clearing when they leave). */
2902
- __init145() {this.gpuHasContent = false}
2903
- __init146() {this.mouseX = -9999}
2904
- __init147() {this.mouseY = -9999}
2905
- __init148() {this.pointerMoveListener = null}
2906
- __init149() {this.pointerLeaveListener = null}
2949
+ __init150() {this.gpuHasContent = false}
2950
+ __init151() {this.mouseX = -9999}
2951
+ __init152() {this.mouseY = -9999}
2952
+ __init153() {this.pointerMoveListener = null}
2953
+ __init154() {this.pointerLeaveListener = null}
2907
2954
  /** Element the pointer listeners are bound to (parent container if present,
2908
2955
  * else the canvas). Stored so `destroy()` detaches from the same element. */
2909
- __init150() {this.pointerEventTarget = null}
2910
- __init151() {this.hasWarnedZeroSize = false}
2911
- __init152() {this.fontLoadHandler = null}
2956
+ __init155() {this.pointerEventTarget = null}
2957
+ __init156() {this.hasWarnedZeroSize = false}
2958
+ __init157() {this.fontLoadHandler = null}
2912
2959
  // ── Dev-mode warning infrastructure ──────────────────────────────
2913
2960
  //
2914
2961
  // Enable with `Scene.devMode = true` or by setting `globalThis.__DEV__`.
@@ -2942,7 +2989,7 @@ var Scene = (_class7 = class _Scene {
2942
2989
  return false;
2943
2990
  }
2944
2991
 
2945
- __init153() {this._devFrameCount = 0}
2992
+ __init158() {this._devFrameCount = 0}
2946
2993
  _devWarn(message) {
2947
2994
  if (!this._devActive) return;
2948
2995
  console.warn(`[vectojs/dev] ${message}`);
@@ -3019,7 +3066,7 @@ var Scene = (_class7 = class _Scene {
3019
3066
  };
3020
3067
  walkProjections(this.root);
3021
3068
  }
3022
- constructor(canvas, options = {}) {;_class7.prototype.__init27.call(this);_class7.prototype.__init28.call(this);_class7.prototype.__init29.call(this);_class7.prototype.__init30.call(this);_class7.prototype.__init31.call(this);_class7.prototype.__init32.call(this);_class7.prototype.__init33.call(this);_class7.prototype.__init34.call(this);_class7.prototype.__init35.call(this);_class7.prototype.__init36.call(this);_class7.prototype.__init37.call(this);_class7.prototype.__init38.call(this);_class7.prototype.__init39.call(this);_class7.prototype.__init40.call(this);_class7.prototype.__init41.call(this);_class7.prototype.__init42.call(this);_class7.prototype.__init43.call(this);_class7.prototype.__init44.call(this);_class7.prototype.__init45.call(this);_class7.prototype.__init46.call(this);_class7.prototype.__init47.call(this);_class7.prototype.__init48.call(this);_class7.prototype.__init49.call(this);_class7.prototype.__init50.call(this);_class7.prototype.__init51.call(this);_class7.prototype.__init52.call(this);_class7.prototype.__init53.call(this);_class7.prototype.__init54.call(this);_class7.prototype.__init55.call(this);_class7.prototype.__init56.call(this);_class7.prototype.__init57.call(this);_class7.prototype.__init58.call(this);_class7.prototype.__init59.call(this);_class7.prototype.__init60.call(this);_class7.prototype.__init61.call(this);_class7.prototype.__init62.call(this);_class7.prototype.__init63.call(this);_class7.prototype.__init64.call(this);_class7.prototype.__init65.call(this);_class7.prototype.__init66.call(this);_class7.prototype.__init67.call(this);_class7.prototype.__init68.call(this);_class7.prototype.__init69.call(this);_class7.prototype.__init70.call(this);_class7.prototype.__init71.call(this);_class7.prototype.__init72.call(this);_class7.prototype.__init73.call(this);_class7.prototype.__init74.call(this);_class7.prototype.__init75.call(this);_class7.prototype.__init76.call(this);_class7.prototype.__init77.call(this);_class7.prototype.__init78.call(this);_class7.prototype.__init79.call(this);_class7.prototype.__init80.call(this);_class7.prototype.__init81.call(this);_class7.prototype.__init82.call(this);_class7.prototype.__init83.call(this);_class7.prototype.__init84.call(this);_class7.prototype.__init85.call(this);_class7.prototype.__init86.call(this);_class7.prototype.__init87.call(this);_class7.prototype.__init88.call(this);_class7.prototype.__init89.call(this);_class7.prototype.__init90.call(this);_class7.prototype.__init91.call(this);_class7.prototype.__init92.call(this);_class7.prototype.__init93.call(this);_class7.prototype.__init94.call(this);_class7.prototype.__init95.call(this);_class7.prototype.__init96.call(this);_class7.prototype.__init97.call(this);_class7.prototype.__init98.call(this);_class7.prototype.__init99.call(this);_class7.prototype.__init100.call(this);_class7.prototype.__init101.call(this);_class7.prototype.__init102.call(this);_class7.prototype.__init103.call(this);_class7.prototype.__init104.call(this);_class7.prototype.__init105.call(this);_class7.prototype.__init106.call(this);_class7.prototype.__init107.call(this);_class7.prototype.__init108.call(this);_class7.prototype.__init109.call(this);_class7.prototype.__init110.call(this);_class7.prototype.__init111.call(this);_class7.prototype.__init112.call(this);_class7.prototype.__init113.call(this);_class7.prototype.__init114.call(this);_class7.prototype.__init115.call(this);_class7.prototype.__init116.call(this);_class7.prototype.__init117.call(this);_class7.prototype.__init118.call(this);_class7.prototype.__init119.call(this);_class7.prototype.__init120.call(this);_class7.prototype.__init121.call(this);_class7.prototype.__init122.call(this);_class7.prototype.__init123.call(this);_class7.prototype.__init124.call(this);_class7.prototype.__init125.call(this);_class7.prototype.__init126.call(this);_class7.prototype.__init127.call(this);_class7.prototype.__init128.call(this);_class7.prototype.__init129.call(this);_class7.prototype.__init130.call(this);_class7.prototype.__init131.call(this);_class7.prototype.__init132.call(this);_class7.prototype.__init133.call(this);_class7.prototype.__init134.call(this);_class7.prototype.__init135.call(this);_class7.prototype.__init136.call(this);_class7.prototype.__init137.call(this);_class7.prototype.__init138.call(this);_class7.prototype.__init139.call(this);_class7.prototype.__init140.call(this);_class7.prototype.__init141.call(this);_class7.prototype.__init142.call(this);_class7.prototype.__init143.call(this);_class7.prototype.__init144.call(this);_class7.prototype.__init145.call(this);_class7.prototype.__init146.call(this);_class7.prototype.__init147.call(this);_class7.prototype.__init148.call(this);_class7.prototype.__init149.call(this);_class7.prototype.__init150.call(this);_class7.prototype.__init151.call(this);_class7.prototype.__init152.call(this);_class7.prototype.__init153.call(this);
3069
+ constructor(canvas, options = {}) {;_class7.prototype.__init27.call(this);_class7.prototype.__init28.call(this);_class7.prototype.__init29.call(this);_class7.prototype.__init30.call(this);_class7.prototype.__init31.call(this);_class7.prototype.__init32.call(this);_class7.prototype.__init33.call(this);_class7.prototype.__init34.call(this);_class7.prototype.__init35.call(this);_class7.prototype.__init36.call(this);_class7.prototype.__init37.call(this);_class7.prototype.__init38.call(this);_class7.prototype.__init39.call(this);_class7.prototype.__init40.call(this);_class7.prototype.__init41.call(this);_class7.prototype.__init42.call(this);_class7.prototype.__init43.call(this);_class7.prototype.__init44.call(this);_class7.prototype.__init45.call(this);_class7.prototype.__init46.call(this);_class7.prototype.__init47.call(this);_class7.prototype.__init48.call(this);_class7.prototype.__init49.call(this);_class7.prototype.__init50.call(this);_class7.prototype.__init51.call(this);_class7.prototype.__init52.call(this);_class7.prototype.__init53.call(this);_class7.prototype.__init54.call(this);_class7.prototype.__init55.call(this);_class7.prototype.__init56.call(this);_class7.prototype.__init57.call(this);_class7.prototype.__init58.call(this);_class7.prototype.__init59.call(this);_class7.prototype.__init60.call(this);_class7.prototype.__init61.call(this);_class7.prototype.__init62.call(this);_class7.prototype.__init63.call(this);_class7.prototype.__init64.call(this);_class7.prototype.__init65.call(this);_class7.prototype.__init66.call(this);_class7.prototype.__init67.call(this);_class7.prototype.__init68.call(this);_class7.prototype.__init69.call(this);_class7.prototype.__init70.call(this);_class7.prototype.__init71.call(this);_class7.prototype.__init72.call(this);_class7.prototype.__init73.call(this);_class7.prototype.__init74.call(this);_class7.prototype.__init75.call(this);_class7.prototype.__init76.call(this);_class7.prototype.__init77.call(this);_class7.prototype.__init78.call(this);_class7.prototype.__init79.call(this);_class7.prototype.__init80.call(this);_class7.prototype.__init81.call(this);_class7.prototype.__init82.call(this);_class7.prototype.__init83.call(this);_class7.prototype.__init84.call(this);_class7.prototype.__init85.call(this);_class7.prototype.__init86.call(this);_class7.prototype.__init87.call(this);_class7.prototype.__init88.call(this);_class7.prototype.__init89.call(this);_class7.prototype.__init90.call(this);_class7.prototype.__init91.call(this);_class7.prototype.__init92.call(this);_class7.prototype.__init93.call(this);_class7.prototype.__init94.call(this);_class7.prototype.__init95.call(this);_class7.prototype.__init96.call(this);_class7.prototype.__init97.call(this);_class7.prototype.__init98.call(this);_class7.prototype.__init99.call(this);_class7.prototype.__init100.call(this);_class7.prototype.__init101.call(this);_class7.prototype.__init102.call(this);_class7.prototype.__init103.call(this);_class7.prototype.__init104.call(this);_class7.prototype.__init105.call(this);_class7.prototype.__init106.call(this);_class7.prototype.__init107.call(this);_class7.prototype.__init108.call(this);_class7.prototype.__init109.call(this);_class7.prototype.__init110.call(this);_class7.prototype.__init111.call(this);_class7.prototype.__init112.call(this);_class7.prototype.__init113.call(this);_class7.prototype.__init114.call(this);_class7.prototype.__init115.call(this);_class7.prototype.__init116.call(this);_class7.prototype.__init117.call(this);_class7.prototype.__init118.call(this);_class7.prototype.__init119.call(this);_class7.prototype.__init120.call(this);_class7.prototype.__init121.call(this);_class7.prototype.__init122.call(this);_class7.prototype.__init123.call(this);_class7.prototype.__init124.call(this);_class7.prototype.__init125.call(this);_class7.prototype.__init126.call(this);_class7.prototype.__init127.call(this);_class7.prototype.__init128.call(this);_class7.prototype.__init129.call(this);_class7.prototype.__init130.call(this);_class7.prototype.__init131.call(this);_class7.prototype.__init132.call(this);_class7.prototype.__init133.call(this);_class7.prototype.__init134.call(this);_class7.prototype.__init135.call(this);_class7.prototype.__init136.call(this);_class7.prototype.__init137.call(this);_class7.prototype.__init138.call(this);_class7.prototype.__init139.call(this);_class7.prototype.__init140.call(this);_class7.prototype.__init141.call(this);_class7.prototype.__init142.call(this);_class7.prototype.__init143.call(this);_class7.prototype.__init144.call(this);_class7.prototype.__init145.call(this);_class7.prototype.__init146.call(this);_class7.prototype.__init147.call(this);_class7.prototype.__init148.call(this);_class7.prototype.__init149.call(this);_class7.prototype.__init150.call(this);_class7.prototype.__init151.call(this);_class7.prototype.__init152.call(this);_class7.prototype.__init153.call(this);_class7.prototype.__init154.call(this);_class7.prototype.__init155.call(this);_class7.prototype.__init156.call(this);_class7.prototype.__init157.call(this);_class7.prototype.__init158.call(this);
3023
3070
  this.canvas = canvas;
3024
3071
  this.debugA11y = _nullishCoalesce(options.debugA11y, () => ( false));
3025
3072
  this.disableWindowResize = _nullishCoalesce(options.disableWindowResize, () => ( false));
@@ -3044,6 +3091,7 @@ var Scene = (_class7 = class _Scene {
3044
3091
  this.contentProjectionEnabled = _nullishCoalesce(options.contentProjection, () => ( true));
3045
3092
  this.contentProjectionMargin = options.contentProjectionMargin;
3046
3093
  this.contentSemanticMargin = options.contentSemanticMargin;
3094
+ this.contentSemanticBudget = _nullishCoalesce(options.contentSemanticBudget, () => ( DEFAULT_CONTENT_SEMANTIC_BUDGET));
3047
3095
  this.readingDirection = _nullishCoalesce(options.readingDirection, () => ( "ltr"));
3048
3096
  this.renderMode = _nullishCoalesce(options.renderMode, () => ( "always"));
3049
3097
  this._devActive = _Scene._devModeDetected();
@@ -3325,12 +3373,31 @@ var Scene = (_class7 = class _Scene {
3325
3373
  }
3326
3374
  return null;
3327
3375
  }
3376
+ /**
3377
+ * Does the document hold a selection right now, memoized for this sync walk?
3378
+ *
3379
+ * Pays one forced layout per walk instead of one per rebuilt element — see
3380
+ * {@link Scene.contentSelectionPresentThisSync} for the measurements. When the
3381
+ * answer is `false` no element can own a selection, so every per-element
3382
+ * ownership test can be skipped without touching the object.
3383
+ */
3384
+ contentSelectionPresent() {
3385
+ if (this.contentSelectionPresentThisSync !== null) {
3386
+ return this.contentSelectionPresentThisSync;
3387
+ }
3388
+ const selection = typeof window !== "undefined" && typeof window.getSelection === "function" ? window.getSelection() : null;
3389
+ const present = !!selection && (!!selection.anchorNode || !!selection.focusNode);
3390
+ this.contentSelectionPresentThisSync = present;
3391
+ return present;
3392
+ }
3328
3393
  releaseContentSelectionForRebuild(el) {
3394
+ if (!this.contentSelectionAnchor && !this.contentSelectionPresent()) return;
3329
3395
  const selection = typeof window !== "undefined" && typeof window.getSelection === "function" ? window.getSelection() : null;
3330
3396
  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);
3331
3397
  if (!ownsSelection) return;
3332
3398
  this.endContentSelectionDrag();
3333
3399
  _optionalChain([selection, 'optionalAccess', _87 => _87.removeAllRanges, 'call', _88 => _88()]);
3400
+ this.contentSelectionPresentThisSync = null;
3334
3401
  }
3335
3402
  /**
3336
3403
  * Rebuild a content-projection element's DOM (`rebuild`) while preserving a
@@ -3348,6 +3415,10 @@ var Scene = (_class7 = class _Scene {
3348
3415
  * restore against).
3349
3416
  */
3350
3417
  preserveContentSelectionAcrossRebuild(el, rebuild) {
3418
+ if (!this.contentSelectionAnchor && !this.contentSelectionPresent()) {
3419
+ rebuild();
3420
+ return;
3421
+ }
3351
3422
  const selection = typeof window !== "undefined" && typeof window.getSelection === "function" ? window.getSelection() : null;
3352
3423
  const owns = !!selection && !this.blankRegionSelectionDrag && ((selection.anchorNode ? el.contains(selection.anchorNode) : false) || (selection.focusNode ? el.contains(selection.focusNode) : false));
3353
3424
  if (!owns || !selection.anchorNode || !selection.focusNode) {
@@ -4043,6 +4114,11 @@ var Scene = (_class7 = class _Scene {
4043
4114
  }
4044
4115
  syncA11y(node, container = null) {
4045
4116
  if (!this.a11yRoot) return;
4117
+ if (node === this.root) {
4118
+ this.contentSemanticBudgetLeft = this.contentSemanticBudget;
4119
+ this.contentSemanticDeferred = false;
4120
+ this.contentSelectionPresentThisSync = null;
4121
+ }
4046
4122
  if (node.isDOMPortal) {
4047
4123
  return;
4048
4124
  }
@@ -4461,7 +4537,9 @@ var Scene = (_class7 = class _Scene {
4461
4537
  * transparent DOM node positioned over the drawn glyphs. Runs on the a11y
4462
4538
  * sync cadence; all writes are dirty-checked. Off-viewport projections are
4463
4539
  * hidden (`display: none`) so text-heavy scenes only materialize what is
4464
- * visible to the browser's text machinery anyway.
4540
+ * visible to the browser's text machinery anyway — except in the coarse
4541
+ * (resident) tier, which stays displayed because hiding it would make its text
4542
+ * unfindable and remove it from the accessibility tree, defeating the tier.
4465
4543
  */
4466
4544
  /**
4467
4545
  * Whether `node`'s world-space box, expanded by `margin` px on every side,
@@ -4470,8 +4548,15 @@ var Scene = (_class7 = class _Scene {
4470
4548
  * at `margin = contentProjectionMargin`) and for the exact `display:none`
4471
4549
  * visibility test (`margin = 0`). Boundless nodes (width/height 0) opt out of
4472
4550
  * culling and always count as visible, matching the legacy behavior.
4551
+ *
4552
+ * `viewportOnly` skips the `clipChildren` ancestor walk, answering the narrower
4553
+ * question "does this box overlap the viewport at all". The coarse content tier
4554
+ * needs the two apart: text that is merely off-viewport is clipped by
4555
+ * `a11yRoot`'s own `overflow: hidden` and can safely stay displayed, while text
4556
+ * rejected by an ancestor clip box that itself overlaps the viewport would sit
4557
+ * transparently on top of whatever is really drawn there.
4473
4558
  */
4474
- projectionBoxVisible(node, tf, margin) {
4559
+ projectionBoxVisible(node, tf, margin, viewportOnly = false) {
4475
4560
  if (!(node.width > 0 && node.height > 0)) return true;
4476
4561
  const { a, b, c, d, e, f } = tf;
4477
4562
  const worldCorners = [];
@@ -4493,6 +4578,7 @@ var Scene = (_class7 = class _Scene {
4493
4578
  if (!(maxX >= -margin && minX <= this.width + margin && maxY >= -margin && minY <= this.height + margin)) {
4494
4579
  return false;
4495
4580
  }
4581
+ if (viewportOnly) return true;
4496
4582
  for (let ancestor = node.parent; ancestor; ancestor = ancestor.parent) {
4497
4583
  if (!ancestor.clipChildren || ancestor.width <= 0 || ancestor.height <= 0) continue;
4498
4584
  let localMinX = Infinity;
@@ -4595,6 +4681,10 @@ var Scene = (_class7 = class _Scene {
4595
4681
  return;
4596
4682
  }
4597
4683
  }
4684
+ if (tier === "coarse" && !el && this.contentSemanticBudgetLeft <= 0) {
4685
+ this.contentSemanticDeferred = true;
4686
+ return;
4687
+ }
4598
4688
  const projection = node.getContentProjection(
4599
4689
  lineBand ? { minY: lineBand.minY, maxY: lineBand.maxY } : void 0
4600
4690
  );
@@ -4603,6 +4693,7 @@ var Scene = (_class7 = class _Scene {
4603
4693
  return;
4604
4694
  }
4605
4695
  if (!el) {
4696
+ if (tier === "coarse") this.contentSemanticBudgetLeft--;
4606
4697
  el = document.createElement("div");
4607
4698
  el.setAttribute("data-vecto-content", node.id);
4608
4699
  const s = el.style;
@@ -4737,7 +4828,8 @@ var Scene = (_class7 = class _Scene {
4737
4828
  if (node.width > 0) el.style.width = `${node.width}px`;
4738
4829
  if (node.height > 0) el.style.height = `${node.height}px`;
4739
4830
  el.style.transform = `matrix(${a}, ${b}, ${c}, ${d}, 0, 0)`;
4740
- const display = visible ? "" : "none";
4831
+ const residentTier = semanticMargin > interactionMargin;
4832
+ const display = visible || residentTier && !this.projectionBoxVisible(node, worldTf, 0, true) ? "" : "none";
4741
4833
  if (el.style.display !== display) el.style.display = display;
4742
4834
  if (epoch !== null) {
4743
4835
  const { a: a2, b: b2, c: c2, d: d2, e: e2, f: f2 } = worldTf;
@@ -5088,12 +5180,14 @@ var Scene = (_class7 = class _Scene {
5088
5180
  this.fullViewportElements.length = 0;
5089
5181
  this.normalElements.length = 0;
5090
5182
  this.activeIds.clear();
5091
- const collect = (node) => {
5183
+ this.a11yOrderRegions.clear();
5184
+ const collect = (node, region) => {
5092
5185
  if (node.isDOMPortal) return;
5093
5186
  const contentEl = this.contentElements.get(node.id);
5094
5187
  if (contentEl) {
5095
5188
  if (node.a11yFullViewport) this.fullViewportElements.push(contentEl);
5096
5189
  else this.normalElements.push(contentEl);
5190
+ if (region) this.a11yOrderRegions.set(contentEl, region);
5097
5191
  }
5098
5192
  if (this.shouldProjectA11y(node)) {
5099
5193
  const el = this.a11yElements.get(node.id);
@@ -5101,14 +5195,16 @@ var Scene = (_class7 = class _Scene {
5101
5195
  this.activeIds.add(node.id);
5102
5196
  if (node.a11yFullViewport) this.fullViewportElements.push(el);
5103
5197
  else this.normalElements.push(el);
5198
+ if (region) this.a11yOrderRegions.set(el, region);
5104
5199
  }
5105
5200
  }
5106
- for (const child of node.children) collect(child);
5201
+ const childRegion = node.clipChildren && node.width > 0 && node.height > 0 ? node : region;
5202
+ for (const child of node.children) collect(child, childRegion);
5107
5203
  if (node === this.root) {
5108
- for (const overlay of this.overlayRoot.children) collect(overlay);
5204
+ for (const overlay of this.overlayRoot.children) collect(overlay, null);
5109
5205
  }
5110
5206
  };
5111
- collect(this.root);
5207
+ collect(this.root, null);
5112
5208
  let elementsPruned = false;
5113
5209
  for (const [id, el] of this.a11yElements.entries()) {
5114
5210
  if (!this.activeIds.has(id)) {
@@ -5170,6 +5266,18 @@ var Scene = (_class7 = class _Scene {
5170
5266
  * parents, which no `insertBefore` ever acts on. Normalizing everything back
5171
5267
  * to world coordinates here would cost a transform per element per frame to
5172
5268
  * change nothing observable.
5269
+ *
5270
+ * Banding runs **per region** — per nearest `clipChildren` ancestor, recorded
5271
+ * by {@link enforceA11yDomOrder}'s collect walk — rather than once over the
5272
+ * whole scene. Purely visual banding is right for a screen reader but wrong
5273
+ * for selection: a DOM `Selection` covers everything between anchor and focus
5274
+ * in DOM order, so under one global banding a vertical drag through a
5275
+ * transcript also swallowed a sidebar whose headings happened to fall in the
5276
+ * same rows. Regions are laid out side by side, so ordering region-major keeps
5277
+ * each one a contiguous DOM run and a drag stays inside it, while reading
5278
+ * order *within* a region is unchanged. Regions are emitted in the order their
5279
+ * clipper is first reached by the depth-first walk, so a screen reader still
5280
+ * meets them in the author's declared order.
5173
5281
  */
5174
5282
  sortNormalElementsVisually() {
5175
5283
  const els = this.normalElements;
@@ -5197,30 +5305,40 @@ var Scene = (_class7 = class _Scene {
5197
5305
  }
5198
5306
  return { top, left };
5199
5307
  };
5200
- const order = els.map((el, i) => {
5308
+ const decorated = els.map((el, i) => {
5201
5309
  const { top, left } = absolute(el);
5202
5310
  return { el, i, top, left, container: containers.has(el) };
5203
5311
  });
5204
- order.sort((p, q) => p.top - q.top || p.i - q.i);
5312
+ const regions = this.a11yOrderRegions;
5313
+ const buckets = /* @__PURE__ */ new Map();
5314
+ for (const d of decorated) {
5315
+ const key = _nullishCoalesce(regions.get(d.el), () => ( null));
5316
+ const bucket = buckets.get(key);
5317
+ if (bucket) bucket.push(d);
5318
+ else buckets.set(key, [d]);
5319
+ }
5205
5320
  const bandBottom = (r) => r.top + (r.container ? 4 : heightOf(r.el));
5206
5321
  const sorted = [];
5207
- let rowStart = 0;
5208
- let rowBottom = order.length ? bandBottom(order[0]) : 0;
5209
- const flushRow = (end) => {
5210
- const row = order.slice(rowStart, end);
5211
- row.sort((p, q) => (rtl ? q.left - p.left : p.left - q.left) || p.i - q.i);
5212
- for (const r of row) sorted.push(r.el);
5213
- };
5214
- for (let k = 1; k < order.length; k++) {
5215
- if (order[k].top < rowBottom) {
5216
- rowBottom = Math.max(rowBottom, bandBottom(order[k]));
5217
- } else {
5218
- flushRow(k);
5219
- rowStart = k;
5220
- rowBottom = bandBottom(order[k]);
5322
+ for (const order of buckets.values()) {
5323
+ order.sort((p, q) => p.top - q.top || p.i - q.i);
5324
+ let rowStart = 0;
5325
+ let rowBottom = order.length ? bandBottom(order[0]) : 0;
5326
+ const flushRow = (end) => {
5327
+ const row = order.slice(rowStart, end);
5328
+ row.sort((p, q) => (rtl ? q.left - p.left : p.left - q.left) || p.i - q.i);
5329
+ for (const r of row) sorted.push(r.el);
5330
+ };
5331
+ for (let k = 1; k < order.length; k++) {
5332
+ if (order[k].top < rowBottom) {
5333
+ rowBottom = Math.max(rowBottom, bandBottom(order[k]));
5334
+ } else {
5335
+ flushRow(k);
5336
+ rowStart = k;
5337
+ rowBottom = bandBottom(order[k]);
5338
+ }
5221
5339
  }
5340
+ flushRow(order.length);
5222
5341
  }
5223
- flushRow(order.length);
5224
5342
  for (let i = 0; i < sorted.length; i++) els[i] = sorted[i];
5225
5343
  }
5226
5344
  /** Keep DOM/WebGL overlay layers aligned with the canvas's CSS box. */
@@ -5380,7 +5498,7 @@ var Scene = (_class7 = class _Scene {
5380
5498
  if (!this.isRunning) return;
5381
5499
  if (!this._canvasOnScreen) return;
5382
5500
  let cap = this.effectiveMaxFPS();
5383
- const isIdle = !this.dirty && !this.frameHadAnimation;
5501
+ const isIdle = !this.dirty && !this.frameHadAnimation && !this.contentSemanticDeferred;
5384
5502
  if (isIdle && this.autoThrottle && this.renderMode === "always" && this.maxFPS > 0) {
5385
5503
  cap = Math.min(cap, 2);
5386
5504
  }
@@ -6155,17 +6273,17 @@ var TextEntity = (_class8 = class extends _chunkTRT2TRCWjs.Entity {
6155
6273
 
6156
6274
 
6157
6275
 
6158
- __init154() {this.nodes = []}
6276
+ __init159() {this.nodes = []}
6159
6277
 
6160
- __init155() {this.fillStyle = "#94a3b8"}
6161
- __init156() {this.strokeStyle = null}
6162
- __init157() {this.hoveredFillStyle = "#ffffff"}
6163
- __init158() {this.lineWidth = 1}
6164
- __init159() {this.isHovered = false}
6278
+ __init160() {this.fillStyle = "#94a3b8"}
6279
+ __init161() {this.strokeStyle = null}
6280
+ __init162() {this.hoveredFillStyle = "#ffffff"}
6281
+ __init163() {this.lineWidth = 1}
6282
+ __init164() {this.isHovered = false}
6165
6283
  /** Bumped by {@link applyLayout}; read by `Scene` to skip an unchanged sync. */
6166
- __init160() {this.contentEpoch = 0}
6284
+ __init165() {this.contentEpoch = 0}
6167
6285
  constructor(text, atlas, maxWidth, fontSize = 24) {
6168
- super();_class8.prototype.__init154.call(this);_class8.prototype.__init155.call(this);_class8.prototype.__init156.call(this);_class8.prototype.__init157.call(this);_class8.prototype.__init158.call(this);_class8.prototype.__init159.call(this);_class8.prototype.__init160.call(this);;
6286
+ super();_class8.prototype.__init159.call(this);_class8.prototype.__init160.call(this);_class8.prototype.__init161.call(this);_class8.prototype.__init162.call(this);_class8.prototype.__init163.call(this);_class8.prototype.__init164.call(this);_class8.prototype.__init165.call(this);;
6169
6287
  this.text = text;
6170
6288
  this.atlas = atlas;
6171
6289
  this.fontSize = fontSize;
@@ -6284,15 +6402,15 @@ var TextEntity = (_class8 = class extends _chunkTRT2TRCWjs.Entity {
6284
6402
  // src/components/GridTextEntity.ts
6285
6403
  var GridTextEntity = (_class9 = class extends _chunkTRT2TRCWjs.Entity {
6286
6404
 
6287
- __init161() {this.fillStyle = "#ffffff"}
6288
- __init162() {this.grid = []}
6405
+ __init166() {this.fillStyle = "#ffffff"}
6406
+ __init167() {this.grid = []}
6289
6407
  // Array of rows
6290
- __init163() {this.cols = 0}
6291
- __init164() {this.rows = 0}
6408
+ __init168() {this.cols = 0}
6409
+ __init169() {this.rows = 0}
6292
6410
 
6293
6411
 
6294
6412
  constructor(_atlas, fontSize = 10) {
6295
- super();_class9.prototype.__init161.call(this);_class9.prototype.__init162.call(this);_class9.prototype.__init163.call(this);_class9.prototype.__init164.call(this);;
6413
+ super();_class9.prototype.__init166.call(this);_class9.prototype.__init167.call(this);_class9.prototype.__init168.call(this);_class9.prototype.__init169.call(this);;
6296
6414
  this.fontSize = fontSize;
6297
6415
  this.charWidth = fontSize * 1;
6298
6416
  this.charHeight = fontSize * 1.1;
@@ -6384,23 +6502,23 @@ var SplineEntity = (_class10 = class extends _chunkTRT2TRCWjs.Entity {
6384
6502
 
6385
6503
 
6386
6504
 
6387
- __init165() {this.offscreen = null}
6388
- __init166() {this.baked = false}
6505
+ __init170() {this.offscreen = null}
6506
+ __init171() {this.baked = false}
6389
6507
  /** Logical (CSS-pixel) size of the baked bitmap — the blit destination size. */
6390
- __init167() {this.bakedWidth = 0}
6391
- __init168() {this.bakedHeight = 0}
6508
+ __init172() {this.bakedWidth = 0}
6509
+ __init173() {this.bakedHeight = 0}
6392
6510
  /** Gradient strokes can't be baked to a solid-color bitmap; they render per-frame. */
6393
6511
 
6394
6512
  /** Lazily-flattened polylines (one Float32Array of [x,y,...] per segment) for hit-testing. */
6395
- __init169() {this.polylines = null}
6513
+ __init174() {this.polylines = null}
6396
6514
  /**
6397
6515
  * When `true`, the renderer draws a rounded-rect outline of the entity's
6398
6516
  * local bounds after painting the curves. Useful for drag feedback and
6399
6517
  * debugging hit areas. Defaults to `false`.
6400
6518
  */
6401
- __init170() {this.showBounds = false}
6519
+ __init175() {this.showBounds = false}
6402
6520
  constructor(doc, opts = {}) {
6403
- super();_class10.prototype.__init165.call(this);_class10.prototype.__init166.call(this);_class10.prototype.__init167.call(this);_class10.prototype.__init168.call(this);_class10.prototype.__init169.call(this);_class10.prototype.__init170.call(this);;
6521
+ super();_class10.prototype.__init170.call(this);_class10.prototype.__init171.call(this);_class10.prototype.__init172.call(this);_class10.prototype.__init173.call(this);_class10.prototype.__init174.call(this);_class10.prototype.__init175.call(this);;
6404
6522
  this.doc = doc;
6405
6523
  this.lineWidth = _nullishCoalesce(opts.lineWidth, () => ( 2));
6406
6524
  this.cache = _nullishCoalesce(opts.cache, () => ( true));
@@ -6781,19 +6899,19 @@ var _math = require('@vectojs/math'); _createStarExport(_math);
6781
6899
  // src/tree/DOMPortalEntity.ts
6782
6900
  var DOMPortalEntity = (_class11 = class extends _chunkTRT2TRCWjs.Entity {
6783
6901
 
6784
- __init171() {this.isDOMPortal = true}
6785
- __init172() {this.domListeners = []}
6786
- __init173() {this.resizeObserver = null}
6787
- __init174() {this.domBound = false}
6788
- __init175() {this.cachedWidth = 100}
6789
- __init176() {this.cachedHeight = 100}
6790
- __init177() {this.lastWidth = ""}
6791
- __init178() {this.lastHeight = ""}
6792
- __init179() {this.lastTransform = ""}
6793
- __init180() {this.lastZIndex = ""}
6794
- __init181() {this.lastOpacity = ""}
6902
+ __init176() {this.isDOMPortal = true}
6903
+ __init177() {this.domListeners = []}
6904
+ __init178() {this.resizeObserver = null}
6905
+ __init179() {this.domBound = false}
6906
+ __init180() {this.cachedWidth = 100}
6907
+ __init181() {this.cachedHeight = 100}
6908
+ __init182() {this.lastWidth = ""}
6909
+ __init183() {this.lastHeight = ""}
6910
+ __init184() {this.lastTransform = ""}
6911
+ __init185() {this.lastZIndex = ""}
6912
+ __init186() {this.lastOpacity = ""}
6795
6913
  constructor(domElement, width, height, id) {
6796
- super(id);_class11.prototype.__init171.call(this);_class11.prototype.__init172.call(this);_class11.prototype.__init173.call(this);_class11.prototype.__init174.call(this);_class11.prototype.__init175.call(this);_class11.prototype.__init176.call(this);_class11.prototype.__init177.call(this);_class11.prototype.__init178.call(this);_class11.prototype.__init179.call(this);_class11.prototype.__init180.call(this);_class11.prototype.__init181.call(this);;
6914
+ super(id);_class11.prototype.__init176.call(this);_class11.prototype.__init177.call(this);_class11.prototype.__init178.call(this);_class11.prototype.__init179.call(this);_class11.prototype.__init180.call(this);_class11.prototype.__init181.call(this);_class11.prototype.__init182.call(this);_class11.prototype.__init183.call(this);_class11.prototype.__init184.call(this);_class11.prototype.__init185.call(this);_class11.prototype.__init186.call(this);;
6797
6915
  this.domElement = domElement;
6798
6916
  this.width = _nullishCoalesce(width, () => ( 0));
6799
6917
  this.height = _nullishCoalesce(height, () => ( 0));
@@ -6951,4 +7069,5 @@ Scene.registerWebGPUParticleSystemManager(_chunkM73XB4ZKjs.WebGPUParticleSystemM
6951
7069
 
6952
7070
 
6953
7071
 
6954
- exports.CanvasRenderer = _chunkM73XB4ZKjs.CanvasRenderer; exports.Circle = Circle; exports.ComputeParticleEntity = ComputeParticleEntity; exports.DOMPortalEntity = DOMPortalEntity; exports.Entity = _chunkTRT2TRCWjs.Entity; exports.GlyphRasterAtlas = _chunkM73XB4ZKjs.GlyphRasterAtlas; exports.GridTextEntity = GridTextEntity; exports.Group = Group; exports.MSDFTextEntity = _chunkTRT2TRCWjs.MSDFTextEntity; exports.PARTICLE_OFFSET_LIFE = PARTICLE_OFFSET_LIFE; exports.PARTICLE_OFFSET_ORIGIN_X = PARTICLE_OFFSET_ORIGIN_X; exports.PARTICLE_OFFSET_ORIGIN_Y = PARTICLE_OFFSET_ORIGIN_Y; exports.PARTICLE_OFFSET_POSITION_X = PARTICLE_OFFSET_POSITION_X; exports.PARTICLE_OFFSET_POSITION_Y = PARTICLE_OFFSET_POSITION_Y; exports.PARTICLE_OFFSET_SIZE = PARTICLE_OFFSET_SIZE; exports.PARTICLE_OFFSET_VELOCITY_X = PARTICLE_OFFSET_VELOCITY_X; exports.PARTICLE_OFFSET_VELOCITY_Y = PARTICLE_OFFSET_VELOCITY_Y; exports.PARTICLE_STRIDE_FLOATS = PARTICLE_STRIDE_FLOATS; exports.REDUCED_MOTION_FPS = REDUCED_MOTION_FPS; exports.Rect = Rect; exports.SCENE_OPTION_KEYS = SCENE_OPTION_KEYS; exports.SVGEntity = _chunkTRT2TRCWjs.SVGEntity; exports.SVGRenderer = _chunkM73XB4ZKjs.SVGRenderer; exports.Scene = Scene; exports.SplineEntity = SplineEntity; exports.TextEntity = TextEntity; exports.TextRasterCache = _chunkM73XB4ZKjs.TextRasterCache; exports.VECTO_USER_TIMING = VECTO_USER_TIMING; exports.VectoJSEvent = _chunkTRT2TRCWjs.VectoJSEvent; exports.WebGPUParticleSystemManager = _chunkM73XB4ZKjs.WebGPUParticleSystemManager; exports.beginVectoUserTiming = beginVectoUserTiming; exports.contentLineInHint = _chunkTRT2TRCWjs.contentLineInHint; exports.createWebGLPointRenderer = _chunkM73XB4ZKjs.createWebGLPointRenderer; exports.endVectoUserTiming = endVectoUserTiming; exports.installRendererDevTraps = _chunkM73XB4ZKjs.installRendererDevTraps; exports.isRendererDevMode = _chunkM73XB4ZKjs.isRendererDevMode; exports.isSafeUrl = _chunkM73XB4ZKjs.isSafeUrl; exports.loadSpline = loadSpline; exports.measureVectoUserTiming = measureVectoUserTiming; exports.parseColorToRGBA = _chunkM73XB4ZKjs.parseColorToRGBA; exports.polySegmentToBezier = polySegmentToBezier; exports.sanitizeUrl = _chunkM73XB4ZKjs.sanitizeUrl; exports.setRendererDevMode = _chunkM73XB4ZKjs.setRendererDevMode;
7072
+
7073
+ exports.CanvasRenderer = _chunkM73XB4ZKjs.CanvasRenderer; exports.Circle = Circle; exports.ComputeParticleEntity = ComputeParticleEntity; exports.DEFAULT_CONTENT_SEMANTIC_BUDGET = DEFAULT_CONTENT_SEMANTIC_BUDGET; exports.DOMPortalEntity = DOMPortalEntity; exports.Entity = _chunkTRT2TRCWjs.Entity; exports.GlyphRasterAtlas = _chunkM73XB4ZKjs.GlyphRasterAtlas; exports.GridTextEntity = GridTextEntity; exports.Group = Group; exports.MSDFTextEntity = _chunkTRT2TRCWjs.MSDFTextEntity; exports.PARTICLE_OFFSET_LIFE = PARTICLE_OFFSET_LIFE; exports.PARTICLE_OFFSET_ORIGIN_X = PARTICLE_OFFSET_ORIGIN_X; exports.PARTICLE_OFFSET_ORIGIN_Y = PARTICLE_OFFSET_ORIGIN_Y; exports.PARTICLE_OFFSET_POSITION_X = PARTICLE_OFFSET_POSITION_X; exports.PARTICLE_OFFSET_POSITION_Y = PARTICLE_OFFSET_POSITION_Y; exports.PARTICLE_OFFSET_SIZE = PARTICLE_OFFSET_SIZE; exports.PARTICLE_OFFSET_VELOCITY_X = PARTICLE_OFFSET_VELOCITY_X; exports.PARTICLE_OFFSET_VELOCITY_Y = PARTICLE_OFFSET_VELOCITY_Y; exports.PARTICLE_STRIDE_FLOATS = PARTICLE_STRIDE_FLOATS; exports.REDUCED_MOTION_FPS = REDUCED_MOTION_FPS; exports.Rect = Rect; exports.SCENE_OPTION_KEYS = SCENE_OPTION_KEYS; exports.SVGEntity = _chunkTRT2TRCWjs.SVGEntity; exports.SVGRenderer = _chunkM73XB4ZKjs.SVGRenderer; exports.Scene = Scene; exports.SplineEntity = SplineEntity; exports.TextEntity = TextEntity; exports.TextRasterCache = _chunkM73XB4ZKjs.TextRasterCache; exports.VECTO_USER_TIMING = VECTO_USER_TIMING; exports.VectoJSEvent = _chunkTRT2TRCWjs.VectoJSEvent; exports.WebGPUParticleSystemManager = _chunkM73XB4ZKjs.WebGPUParticleSystemManager; exports.beginVectoUserTiming = beginVectoUserTiming; exports.contentLineInHint = _chunkTRT2TRCWjs.contentLineInHint; exports.createWebGLPointRenderer = _chunkM73XB4ZKjs.createWebGLPointRenderer; exports.endVectoUserTiming = endVectoUserTiming; exports.installRendererDevTraps = _chunkM73XB4ZKjs.installRendererDevTraps; exports.isRendererDevMode = _chunkM73XB4ZKjs.isRendererDevMode; exports.isSafeUrl = _chunkM73XB4ZKjs.isSafeUrl; exports.loadSpline = loadSpline; exports.measureVectoUserTiming = measureVectoUserTiming; exports.parseColorToRGBA = _chunkM73XB4ZKjs.parseColorToRGBA; exports.polySegmentToBezier = polySegmentToBezier; exports.sanitizeUrl = _chunkM73XB4ZKjs.sanitizeUrl; exports.setRendererDevMode = _chunkM73XB4ZKjs.setRendererDevMode;