@vectojs/core 1.32.0 → 1.32.2
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 +296 -150
- package/dist/index.mjs +167 -21
- package/dist/tree/Scene.d.ts +80 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1998,30 +1998,79 @@ var Scene = (_class7 = class _Scene {
|
|
|
1998
1998
|
__init60() {this.contentGridCalibrationStamp = ""}
|
|
1999
1999
|
/** Invalidates grid font calibration after browser font availability changes. */
|
|
2000
2000
|
__init61() {this.contentFontEpoch = 0}
|
|
2001
|
+
/**
|
|
2002
|
+
* Bumped whenever the viewport itself changes shape, which re-tiers blocks
|
|
2003
|
+
* without moving any of them.
|
|
2004
|
+
*
|
|
2005
|
+
* The settled-walk fast path in {@link syncContentProjection} decides a block
|
|
2006
|
+
* is unchanged from its own local transform plus its parent's world transform.
|
|
2007
|
+
* Both are viewport-independent, so a resize alone would slip past it and leave
|
|
2008
|
+
* every block holding DOM built for the old viewport — a block that should have
|
|
2009
|
+
* been promoted to the fine tier keeping no carriers, or a demoted one keeping
|
|
2010
|
+
* carriers it no longer needs. Scrolling needs no such epoch: it moves the root,
|
|
2011
|
+
* so every block's parent world transform changes and the check fails honestly.
|
|
2012
|
+
*/
|
|
2013
|
+
__init62() {this.contentViewportEpoch = 0}
|
|
2014
|
+
/**
|
|
2015
|
+
* One-entry memo for a parent's world transform, held as scalars.
|
|
2016
|
+
*
|
|
2017
|
+
* The a11y walk visits all of one parent's children consecutively, so a
|
|
2018
|
+
* one-entry memo hits for every child after the first — turning an
|
|
2019
|
+
* O(children) sequence of `getWorldTransform()` calls, each of which
|
|
2020
|
+
* allocates a fresh object on its cache-hit path
|
|
2021
|
+
* ({@link Entity.getWorldTransform}), into one call per parent. Flattened to
|
|
2022
|
+
* scalars rather than a cached object so the memo itself never allocates.
|
|
2023
|
+
*
|
|
2024
|
+
* Keyed on `(entity, syncSerial)`: the serial is bumped once per top-level
|
|
2025
|
+
* sync, so a memo can never survive into a frame in which the parent has
|
|
2026
|
+
* moved.
|
|
2027
|
+
*/
|
|
2028
|
+
__init63() {this._pwNode = null}
|
|
2029
|
+
__init64() {this._pwSerial = -1}
|
|
2030
|
+
__init65() {this._pwa = 1}
|
|
2031
|
+
__init66() {this._pwb = 0}
|
|
2032
|
+
__init67() {this._pwc = 0}
|
|
2033
|
+
__init68() {this._pwd = 1}
|
|
2034
|
+
__init69() {this._pwe = 0}
|
|
2035
|
+
__init70() {this._pwf = 0}
|
|
2036
|
+
/** Incremented once per top-level `syncA11y`, invalidating `_pwNode`. */
|
|
2037
|
+
__init71() {this._syncSerial = 0}
|
|
2038
|
+
/**
|
|
2039
|
+
* Disables the settled-walk fast path in {@link syncContentProjection}.
|
|
2040
|
+
*
|
|
2041
|
+
* Exists so a benchmark can measure both arms in ONE run on ONE commit, rather
|
|
2042
|
+
* than comparing two builds and inheriting every difference between them. Also
|
|
2043
|
+
* lets a test assert that a behaviour is genuinely unchanged by the fast path
|
|
2044
|
+
* rather than merely unobserved.
|
|
2045
|
+
*
|
|
2046
|
+
* Not part of the public API and not documented as an option: turning it on only
|
|
2047
|
+
* makes a settled document slower, never more correct.
|
|
2048
|
+
*/
|
|
2049
|
+
__init72() {this.disableSettledFastPath = false}
|
|
2001
2050
|
/** Cached Canvas-to-client scale for the current font/viewport epoch. */
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2051
|
+
__init73() {this.contentMetricScaleEpoch = -1}
|
|
2052
|
+
__init74() {this.contentMetricScaleX = 1}
|
|
2053
|
+
__init75() {this.contentProjectionEnabled = true}
|
|
2005
2054
|
// Virtualization margin (px) for the content-projection CARRIER band;
|
|
2006
2055
|
// `undefined` → one viewport height, resolved at sync time. `Infinity` is
|
|
2007
2056
|
// unsupported here: it unwindows every carrier, which is O(total glyphs).
|
|
2008
|
-
|
|
2057
|
+
__init76() {this.contentProjectionMargin = void 0}
|
|
2009
2058
|
// Virtualization margin (px) for the SEMANTIC tier — whether a block has
|
|
2010
2059
|
// any projected DOM. `undefined` → falls back to contentProjectionMargin, so
|
|
2011
2060
|
// the default keeps one gate. `Infinity` = every block keeps resident text.
|
|
2012
|
-
|
|
2061
|
+
__init77() {this.contentSemanticMargin = void 0}
|
|
2013
2062
|
// How many coarse-tier blocks may be MATERIALIZED per sync, spreading the
|
|
2014
2063
|
// resident tier's document-open cost across frames. `Infinity` = one
|
|
2015
2064
|
// synchronous pass.
|
|
2016
|
-
|
|
2065
|
+
__init78() {this.contentSemanticBudget = DEFAULT_CONTENT_SEMANTIC_BUDGET}
|
|
2017
2066
|
// Remaining materializations in the CURRENT sync. Reset at the start of each
|
|
2018
2067
|
// a11y walk; decremented per coarse block that creates its element.
|
|
2019
|
-
|
|
2068
|
+
__init79() {this.contentSemanticBudgetLeft = 0}
|
|
2020
2069
|
// Set when a sync deferred at least one block, so the scene knows to keep
|
|
2021
2070
|
// drawing frames until the resident tier is complete. Without it a static
|
|
2022
2071
|
// scene in `onDemand` mode would stop rendering with the document half
|
|
2023
2072
|
// materialized and never finish.
|
|
2024
|
-
|
|
2073
|
+
__init80() {this.contentSemanticDeferred = false}
|
|
2025
2074
|
/**
|
|
2026
2075
|
* Per-sync memo of "does the document hold a selection at all".
|
|
2027
2076
|
*
|
|
@@ -2044,43 +2093,43 @@ var Scene = (_class7 = class _Scene {
|
|
|
2044
2093
|
* the user, so its presence cannot change mid-walk. Resolving it once per walk
|
|
2045
2094
|
* turns O(blocks) forced layouts into O(1). `null` = not yet resolved.
|
|
2046
2095
|
*/
|
|
2047
|
-
|
|
2096
|
+
__init81() {this.contentSelectionPresentThisSync = null}
|
|
2048
2097
|
/**
|
|
2049
2098
|
* True while a text-selection drag that started on a projection's blank
|
|
2050
2099
|
* region (no text node under the press) is being driven manually — the
|
|
2051
2100
|
* browser has no native anchor for it, so mousemove extends the Selection
|
|
2052
2101
|
* from the position we resolved ourselves.
|
|
2053
2102
|
*/
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2103
|
+
__init82() {this.blankRegionSelectionDrag = false}
|
|
2104
|
+
__init83() {this.contentSelectionAnchor = null}
|
|
2105
|
+
__init84() {this.contentSelectionEndListener = null}
|
|
2057
2106
|
// Animation/interactive flags collected during the render walk (tree-walk
|
|
2058
2107
|
// fusion): the loop reads last frame's answers instead of re-walking the
|
|
2059
2108
|
// tree up to 4× per tick. Start true so the first tick stays conservative.
|
|
2060
|
-
|
|
2061
|
-
|
|
2109
|
+
__init85() {this.frameHadAnimation = true}
|
|
2110
|
+
__init86() {this.frameHadInteractive = true}
|
|
2062
2111
|
|
|
2063
2112
|
/** Active `(resolution: Ndppx)` media query watching for a runtime DPR change
|
|
2064
2113
|
* (window moved between monitors, browser zoom) so the canvas backing store
|
|
2065
2114
|
* can be re-scaled — otherwise it stays rasterized at the old DPR and blurs.
|
|
2066
2115
|
* A resolution media query only fires when leaving its exact value, so the
|
|
2067
2116
|
* handler re-arms a fresh query for the new DPR each time. */
|
|
2068
|
-
|
|
2117
|
+
__init87() {this.dprMediaQuery = null}
|
|
2069
2118
|
/** For embedded (`disableWindowResize`) scenes: observes the canvas element so
|
|
2070
2119
|
* a CSS/layout-driven size change re-runs `resize()`. A window `resize`
|
|
2071
2120
|
* listener never fires for these (the window isn't what changed), so without
|
|
2072
2121
|
* this an embedded canvas stayed at its initial size forever. */
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2122
|
+
__init88() {this.canvasResizeObserver = null}
|
|
2123
|
+
__init89() {this.dprChangeHandler = null}
|
|
2124
|
+
__init90() {this.focusedA11yElement = null}
|
|
2076
2125
|
/** Last geometry `syncOverlayGeometry` wrote, so an unchanged frame can skip the
|
|
2077
2126
|
* style writes entirely. Reset to `null` to force the next sync (a new overlay
|
|
2078
2127
|
* layer was created and has never been positioned). */
|
|
2079
|
-
|
|
2128
|
+
__init91() {this._overlayGeometry = null}
|
|
2080
2129
|
/** Shadow elements the pointer is currently inside. Lets a removal that happens
|
|
2081
2130
|
* mid-hover synthesize the `pointerleave` the browser never sends for a
|
|
2082
2131
|
* detached element, so the entity doesn't keep its hover state. */
|
|
2083
|
-
|
|
2132
|
+
__init92() {this.hoveredA11yElements = /* @__PURE__ */ new WeakSet()}
|
|
2084
2133
|
/**
|
|
2085
2134
|
* Entity ids the application has pinned via {@link requestA11yProjection}.
|
|
2086
2135
|
*
|
|
@@ -2088,22 +2137,22 @@ var Scene = (_class7 = class _Scene {
|
|
|
2088
2137
|
* a stale id simply never matches. Cleared per-entity by
|
|
2089
2138
|
* {@link releaseA11yProjection}.
|
|
2090
2139
|
*/
|
|
2091
|
-
|
|
2140
|
+
__init93() {this.a11yProjectionRequests = /* @__PURE__ */ new Set()}
|
|
2092
2141
|
/** Persistent tabindex=-1 element in a11yRoot. When the focused a11y mirror is
|
|
2093
2142
|
* pruned (virtualization/streaming/removal) while it holds focus, we move
|
|
2094
2143
|
* focus here instead of letting the browser drop it to <body> — keeping the
|
|
2095
2144
|
* screen-reader virtual cursor inside the scene's a11y region. */
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2145
|
+
__init94() {this.focusSentinel = null}
|
|
2146
|
+
__init95() {this.caretBlinkTimer = null}
|
|
2147
|
+
__init96() {this.a11yNeedsReorder = true}
|
|
2148
|
+
__init97() {this.portalRoot = null}
|
|
2149
|
+
__init98() {this.fullViewportElements = []}
|
|
2150
|
+
__init99() {this.normalElements = []}
|
|
2151
|
+
__init100() {this.activeIds = /* @__PURE__ */ new Set()}
|
|
2103
2152
|
/** Per-parent insertion cursor, reused by `enforceA11yDomOrder`. */
|
|
2104
|
-
|
|
2153
|
+
__init101() {this.a11yOrderCursors = /* @__PURE__ */ new Map()}
|
|
2105
2154
|
/** Membership set for the elements being ordered, reused per reorder pass. */
|
|
2106
|
-
|
|
2155
|
+
__init102() {this.a11yOrderMembers = /* @__PURE__ */ new Set()}
|
|
2107
2156
|
/**
|
|
2108
2157
|
* Elements that are an *ancestor* of another ordered element, reused per pass.
|
|
2109
2158
|
*
|
|
@@ -2111,11 +2160,21 @@ var Scene = (_class7 = class _Scene {
|
|
|
2111
2160
|
* items) spans every descendant row, so it must not extend a visual row band —
|
|
2112
2161
|
* see {@link sortNormalElementsVisually}.
|
|
2113
2162
|
*/
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2163
|
+
__init103() {this.a11yOrderContainers = /* @__PURE__ */ new Set()}
|
|
2164
|
+
/**
|
|
2165
|
+
* Nearest `clipChildren` ancestor per ordered element — its *region* — reused
|
|
2166
|
+
* per pass. Written by `enforceA11yDomOrder`'s collect walk, which already has
|
|
2167
|
+
* the entity in hand, so a region costs one comparison per node rather than an
|
|
2168
|
+
* ancestor walk per element.
|
|
2169
|
+
*
|
|
2170
|
+
* Absent means the element sits under no clipping ancestor and belongs to the
|
|
2171
|
+
* implicit root region. See {@link sortNormalElementsVisually}.
|
|
2172
|
+
*/
|
|
2173
|
+
__init104() {this.a11yOrderRegions = /* @__PURE__ */ new Map()}
|
|
2174
|
+
__init105() {this.activePortalsThisFrame = /* @__PURE__ */ new Set()}
|
|
2175
|
+
__init106() {this.activePortalsPrevFrame = /* @__PURE__ */ new Set()}
|
|
2176
|
+
__init107() {this.portalEntities = /* @__PURE__ */ new Map()}
|
|
2177
|
+
__init108() {this.renderOrderCounter = 0}
|
|
2119
2178
|
/**
|
|
2120
2179
|
* Monotonic render-frame counter, bumped once per authoritative `render()`
|
|
2121
2180
|
* pass. Entities stamp their per-frame world-matrix cache with this value and
|
|
@@ -2124,7 +2183,7 @@ var Scene = (_class7 = class _Scene {
|
|
|
2124
2183
|
* back to the ancestor walk. Public for the same reason `Entity._getTrig`/
|
|
2125
2184
|
* `_setWorldCache` are: it is a cross-class render-internal contract.
|
|
2126
2185
|
*/
|
|
2127
|
-
|
|
2186
|
+
__init109() {this.currentFrame = 0}
|
|
2128
2187
|
// ── WASM transform backend (invisible accelerator) ──────────────────────────
|
|
2129
2188
|
// When `_transformBackend === 'wasm'`, the main render walk sources each
|
|
2130
2189
|
// entity's world matrix from an SoA store composed by `_wasm` (see
|
|
@@ -2132,24 +2191,24 @@ var Scene = (_class7 = class _Scene {
|
|
|
2132
2191
|
// fallback and the default: a null backend, a non-main renderer, or any entity
|
|
2133
2192
|
// absent from the store all fall back to the JS composition, so WASM can only
|
|
2134
2193
|
// ever change *how fast* a world matrix is produced, never *what* it is.
|
|
2135
|
-
|
|
2136
|
-
|
|
2194
|
+
__init110() {this._wasm = null}
|
|
2195
|
+
__init111() {this._transformBackend = "js"}
|
|
2137
2196
|
// Resident store state (Stage 3). The store layout — slot assignment + sibling
|
|
2138
2197
|
// runs — depends only on tree TOPOLOGY, so it is rebuilt only when the
|
|
2139
2198
|
// structure changes (add/remove/reparent bump `_structureVersion`). Between
|
|
2140
2199
|
// rebuilds the per-frame cost is: gather each entity's transform into the
|
|
2141
2200
|
// resident wasm input view + run the kernel — no reallocation, no readback.
|
|
2142
|
-
|
|
2143
|
-
|
|
2201
|
+
__init112() {this._treeStore = null}
|
|
2202
|
+
__init113() {this._slotEntity = []}
|
|
2144
2203
|
// store slot -> entity (also validates slots)
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2204
|
+
__init114() {this._wasmInputs = null}
|
|
2205
|
+
__init115() {this._wasmWorld = null}
|
|
2206
|
+
__init116() {this._structureVersion = 0}
|
|
2207
|
+
__init117() {this._storeStructureVersion = -1}
|
|
2149
2208
|
// Cached list of ComputeParticleEntity instances in the tree, keyed by the
|
|
2150
2209
|
// structure version it was gathered at. Rebuilt only on a topology change.
|
|
2151
|
-
|
|
2152
|
-
|
|
2210
|
+
__init118() {this._computeEntities = []}
|
|
2211
|
+
__init119() {this._computeEntitiesVersion = -1}
|
|
2153
2212
|
/** Invalidate the resident WASM store layout; the next wasm-mode frame rebuilds
|
|
2154
2213
|
* it. Called by `Entity.add`/`remove` (topology changes only). */
|
|
2155
2214
|
markStructureChanged() {
|
|
@@ -2234,7 +2293,7 @@ var Scene = (_class7 = class _Scene {
|
|
|
2234
2293
|
* cached globally; the instance is per-Scene, which is the isolation that
|
|
2235
2294
|
* actually matters.
|
|
2236
2295
|
*/
|
|
2237
|
-
|
|
2296
|
+
__init120() {this._wasmRuntime = null}
|
|
2238
2297
|
/**
|
|
2239
2298
|
* Load (or reuse) this Scene's shared WASM runtime.
|
|
2240
2299
|
*
|
|
@@ -2262,30 +2321,30 @@ var Scene = (_class7 = class _Scene {
|
|
|
2262
2321
|
get wasmRuntime() {
|
|
2263
2322
|
return this._wasmRuntime;
|
|
2264
2323
|
}
|
|
2265
|
-
|
|
2324
|
+
__init121() {this._hitWasm = null}
|
|
2266
2325
|
// Cache key: which frame + structure version the grid was last (successfully,
|
|
2267
2326
|
// non-overflowing) built for. findEntityAt is called ad-hoc (pointer
|
|
2268
2327
|
// hover/click), not every frame, so the grid is refreshed lazily on demand
|
|
2269
2328
|
// rather than proactively every render() — unlike the transform store, which
|
|
2270
2329
|
// every frame's draw depends on.
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2330
|
+
__init122() {this._hitGridFrame = -1}
|
|
2331
|
+
__init123() {this._hitGridOk = false}
|
|
2332
|
+
__init124() {this._hitSlotEntity = []}
|
|
2333
|
+
__init125() {this._hitBoundless = []}
|
|
2275
2334
|
/** Reused buffer for the fused gather, so a pointer query allocates nothing. */
|
|
2276
|
-
|
|
2335
|
+
__init126() {this._hitGatherBuffer = null}
|
|
2277
2336
|
/**
|
|
2278
2337
|
* Whether the last grid build sourced its AABBs from the WASM transform store
|
|
2279
2338
|
* rather than recomputing them in JS. Diagnostic only — both paths must
|
|
2280
2339
|
* produce the same entity for a given point.
|
|
2281
2340
|
*/
|
|
2282
|
-
|
|
2341
|
+
__init127() {this._hitFusedGather = false}
|
|
2283
2342
|
/**
|
|
2284
2343
|
* Whether `compute_aabbs` has run against the current frame's world matrices.
|
|
2285
2344
|
* The AABB pass is only meaningful after a `compose_*`, so the fused gather
|
|
2286
2345
|
* must not read the views before then.
|
|
2287
2346
|
*/
|
|
2288
|
-
|
|
2347
|
+
__init128() {this._wasmAabbsFresh = false}
|
|
2289
2348
|
/** Did the last hit-grid build use the fused (WASM-store) gather? */
|
|
2290
2349
|
get hitGatherPath() {
|
|
2291
2350
|
return this._hitFusedGather ? "fused" : "js";
|
|
@@ -2294,9 +2353,9 @@ var Scene = (_class7 = class _Scene {
|
|
|
2294
2353
|
* Why the transform accelerator did or did not run on the most recent frame.
|
|
2295
2354
|
* Written by the render walk and `_syncWasmStore`.
|
|
2296
2355
|
*/
|
|
2297
|
-
|
|
2356
|
+
__init129() {this._transformReason = "not-installed"}
|
|
2298
2357
|
/** Why the batched-driver accelerator did or did not run. */
|
|
2299
|
-
|
|
2358
|
+
__init130() {this._animReason = "not-installed"}
|
|
2300
2359
|
/**
|
|
2301
2360
|
* Why the hit-test accelerator did or did not serve the last pointer query.
|
|
2302
2361
|
* The grid is built lazily on demand, not every frame, so this describes the
|
|
@@ -2304,11 +2363,11 @@ var Scene = (_class7 = class _Scene {
|
|
|
2304
2363
|
* before a backend exists; `_ensureHitGrid` moves it to `'not-applicable'`
|
|
2305
2364
|
* once one is installed but nothing has queried yet.
|
|
2306
2365
|
*/
|
|
2307
|
-
|
|
2366
|
+
__init131() {this._hitReason = "not-installed"}
|
|
2308
2367
|
/** Why the particle accelerator did or did not run. */
|
|
2309
|
-
|
|
2368
|
+
__init132() {this._particleReason = "not-applicable"}
|
|
2310
2369
|
/** Which particle implementation actually simulated the most recent frame. */
|
|
2311
|
-
|
|
2370
|
+
__init133() {this._particlePath = "none"}
|
|
2312
2371
|
/**
|
|
2313
2372
|
* Per-frame status of every invisible accelerator: whether each is installed,
|
|
2314
2373
|
* whether it actually ran on the most recent frame, and why.
|
|
@@ -2468,25 +2527,25 @@ var Scene = (_class7 = class _Scene {
|
|
|
2468
2527
|
// EasingFn (which cannot cross into WASM) all fall through to it — WASM can
|
|
2469
2528
|
// only ever change *how* a driver is advanced, never *what* value it lands
|
|
2470
2529
|
// on.
|
|
2471
|
-
|
|
2530
|
+
__init134() {this._animWasm = null}
|
|
2472
2531
|
// Entities with at least one active driver, added by Entity._spawnDriver.
|
|
2473
2532
|
// Self-pruning: _tickBatchedDrivers drops an entry the first time it visits
|
|
2474
2533
|
// an entity whose drivers have since all completed or been removed. This is
|
|
2475
2534
|
// what lets the batch pass find its candidates in O(active drivers), not
|
|
2476
2535
|
// O(tree size) — the exact mistake G3's first integrated benchmark made.
|
|
2477
|
-
|
|
2536
|
+
__init135() {this._activeDriverEntities = /* @__PURE__ */ new Set()}
|
|
2478
2537
|
// Reused across frames instead of allocating a fresh array + N {entity,prop,
|
|
2479
2538
|
// driver} objects every call — the integrated benchmark
|
|
2480
2539
|
// (benchmarks/anim-wasm-scene) found that allocation churn was the
|
|
2481
2540
|
// dominant integrated cost, not the wasm kernel itself. Parallel arrays,
|
|
2482
2541
|
// truncated to the live count after each use so a stale tail slot never
|
|
2483
2542
|
// pins a no-longer-active entity/driver in memory.
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2543
|
+
__init136() {this._springEntities = []}
|
|
2544
|
+
__init137() {this._springProps = []}
|
|
2545
|
+
__init138() {this._springDrivers = []}
|
|
2546
|
+
__init139() {this._tweenEntities = []}
|
|
2547
|
+
__init140() {this._tweenProps = []}
|
|
2548
|
+
__init141() {this._tweenDrivers = []}
|
|
2490
2549
|
/**
|
|
2491
2550
|
* Minimum number of batchable (spring, or named-easing tween) active drivers
|
|
2492
2551
|
* before a frame engages the WASM batch path at all; below it, every driver
|
|
@@ -2558,7 +2617,7 @@ var Scene = (_class7 = class _Scene {
|
|
|
2558
2617
|
* Setting {@link animDriverGateCount} overwrites all three, so existing code
|
|
2559
2618
|
* that tuned the single knob keeps working unchanged.
|
|
2560
2619
|
*/
|
|
2561
|
-
|
|
2620
|
+
__init142() {this._animBatchedLastFrame = false}
|
|
2562
2621
|
/**
|
|
2563
2622
|
* Whether the WASM batch path actually ran on the most recent frame.
|
|
2564
2623
|
*
|
|
@@ -2570,7 +2629,7 @@ var Scene = (_class7 = class _Scene {
|
|
|
2570
2629
|
get animBatchedLastFrame() {
|
|
2571
2630
|
return this._animBatchedLastFrame;
|
|
2572
2631
|
}
|
|
2573
|
-
|
|
2632
|
+
__init143() {this.animGate = {
|
|
2574
2633
|
spring: 128,
|
|
2575
2634
|
tween: 256,
|
|
2576
2635
|
mixed: 128
|
|
@@ -2608,7 +2667,7 @@ var Scene = (_class7 = class _Scene {
|
|
|
2608
2667
|
// (benchmarks/particle-wasm). f32 (matches the WGSL shader), bit-identical to
|
|
2609
2668
|
// a JS f32 reference oracle; updateCPU (f64) stays the permanent fallback when
|
|
2610
2669
|
// no backend is installed or a scene runs on WebGPU.
|
|
2611
|
-
|
|
2670
|
+
__init144() {this._particleWasm = null}
|
|
2612
2671
|
/** Which backend runs the CPU particle simulation. Reflects only whether a
|
|
2613
2672
|
* backend is installed (the WebGPU compute path, when active, is used first
|
|
2614
2673
|
* regardless). */
|
|
@@ -2885,24 +2944,24 @@ var Scene = (_class7 = class _Scene {
|
|
|
2885
2944
|
* sync, so retaining the order prevents a newly opened overlay from spending
|
|
2886
2945
|
* its first frame below previously projected controls.
|
|
2887
2946
|
*/
|
|
2888
|
-
|
|
2947
|
+
__init145() {this.a11yRenderOrders = /* @__PURE__ */ new Map()}
|
|
2889
2948
|
// Optional WebGL point-cloud layer (see SceneOptions.pointBackend).
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2949
|
+
__init146() {this.pointRenderer = null}
|
|
2950
|
+
__init147() {this.glCanvas = null}
|
|
2951
|
+
__init148() {this.glContextLostHandler = null}
|
|
2952
|
+
__init149() {this.glContextRestoredHandler = null}
|
|
2894
2953
|
|
|
2895
2954
|
|
|
2896
2955
|
|
|
2897
|
-
|
|
2956
|
+
__init150() {this.disableWindowResize = false}
|
|
2898
2957
|
/** See {@link SceneOptions.maxDPR}. `undefined` = uncapped (real DPR). */
|
|
2899
2958
|
|
|
2900
2959
|
// WebGPU properties
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2960
|
+
__init151() {this.destroyed = false}
|
|
2961
|
+
__init152() {this.device = null}
|
|
2962
|
+
__init153() {this.deviceLost = false}
|
|
2963
|
+
__init154() {this.particleBackend = "auto"}
|
|
2964
|
+
__init155() {this._webgpuDisabled = false}
|
|
2906
2965
|
get webgpuDisabled() {
|
|
2907
2966
|
return this._webgpuDisabled || this.particleBackend === "cpu";
|
|
2908
2967
|
}
|
|
@@ -2930,22 +2989,22 @@ var Scene = (_class7 = class _Scene {
|
|
|
2930
2989
|
set webgpuDisabled(value) {
|
|
2931
2990
|
this._webgpuDisabled = value;
|
|
2932
2991
|
}
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2992
|
+
__init156() {this.recoveryTimerId = null}
|
|
2993
|
+
__init157() {this.manager = null}
|
|
2994
|
+
__init158() {this.initializingWebGPU = false}
|
|
2995
|
+
__init159() {this.gpuCanvas = null}
|
|
2996
|
+
__init160() {this.gpuContext = null}
|
|
2938
2997
|
/** True while the GPU canvas holds a presented particle frame (needs clearing when they leave). */
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2998
|
+
__init161() {this.gpuHasContent = false}
|
|
2999
|
+
__init162() {this.mouseX = -9999}
|
|
3000
|
+
__init163() {this.mouseY = -9999}
|
|
3001
|
+
__init164() {this.pointerMoveListener = null}
|
|
3002
|
+
__init165() {this.pointerLeaveListener = null}
|
|
2944
3003
|
/** Element the pointer listeners are bound to (parent container if present,
|
|
2945
3004
|
* else the canvas). Stored so `destroy()` detaches from the same element. */
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
3005
|
+
__init166() {this.pointerEventTarget = null}
|
|
3006
|
+
__init167() {this.hasWarnedZeroSize = false}
|
|
3007
|
+
__init168() {this.fontLoadHandler = null}
|
|
2949
3008
|
// ── Dev-mode warning infrastructure ──────────────────────────────
|
|
2950
3009
|
//
|
|
2951
3010
|
// Enable with `Scene.devMode = true` or by setting `globalThis.__DEV__`.
|
|
@@ -2979,7 +3038,7 @@ var Scene = (_class7 = class _Scene {
|
|
|
2979
3038
|
return false;
|
|
2980
3039
|
}
|
|
2981
3040
|
|
|
2982
|
-
|
|
3041
|
+
__init169() {this._devFrameCount = 0}
|
|
2983
3042
|
_devWarn(message) {
|
|
2984
3043
|
if (!this._devActive) return;
|
|
2985
3044
|
console.warn(`[vectojs/dev] ${message}`);
|
|
@@ -3056,7 +3115,7 @@ var Scene = (_class7 = class _Scene {
|
|
|
3056
3115
|
};
|
|
3057
3116
|
walkProjections(this.root);
|
|
3058
3117
|
}
|
|
3059
|
-
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);
|
|
3118
|
+
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);_class7.prototype.__init159.call(this);_class7.prototype.__init160.call(this);_class7.prototype.__init161.call(this);_class7.prototype.__init162.call(this);_class7.prototype.__init163.call(this);_class7.prototype.__init164.call(this);_class7.prototype.__init165.call(this);_class7.prototype.__init166.call(this);_class7.prototype.__init167.call(this);_class7.prototype.__init168.call(this);_class7.prototype.__init169.call(this);
|
|
3060
3119
|
this.canvas = canvas;
|
|
3061
3120
|
this.debugA11y = _nullishCoalesce(options.debugA11y, () => ( false));
|
|
3062
3121
|
this.disableWindowResize = _nullishCoalesce(options.disableWindowResize, () => ( false));
|
|
@@ -4107,6 +4166,7 @@ var Scene = (_class7 = class _Scene {
|
|
|
4107
4166
|
if (node === this.root) {
|
|
4108
4167
|
this.contentSemanticBudgetLeft = this.contentSemanticBudget;
|
|
4109
4168
|
this.contentSemanticDeferred = false;
|
|
4169
|
+
this._syncSerial++;
|
|
4110
4170
|
this.contentSelectionPresentThisSync = null;
|
|
4111
4171
|
}
|
|
4112
4172
|
if (node.isDOMPortal) {
|
|
@@ -4546,6 +4606,26 @@ var Scene = (_class7 = class _Scene {
|
|
|
4546
4606
|
* rejected by an ancestor clip box that itself overlaps the viewport would sit
|
|
4547
4607
|
* transparently on top of whatever is really drawn there.
|
|
4548
4608
|
*/
|
|
4609
|
+
/**
|
|
4610
|
+
* Load `parent`'s world transform into the `_pw*` scalar memo.
|
|
4611
|
+
*
|
|
4612
|
+
* Exists so the settled-walk fast path costs no allocation. The walk visits a
|
|
4613
|
+
* parent's children consecutively, so this recomputes at most once per parent
|
|
4614
|
+
* per sync and is a serial + identity comparison for every child after the
|
|
4615
|
+
* first. See the `_pwNode` field for why it is keyed the way it is.
|
|
4616
|
+
*/
|
|
4617
|
+
readParentWorld(parent) {
|
|
4618
|
+
if (this._pwNode === parent && this._pwSerial === this._syncSerial) return;
|
|
4619
|
+
const tf = parent.getWorldTransform();
|
|
4620
|
+
this._pwa = tf.a;
|
|
4621
|
+
this._pwb = tf.b;
|
|
4622
|
+
this._pwc = tf.c;
|
|
4623
|
+
this._pwd = tf.d;
|
|
4624
|
+
this._pwe = tf.e;
|
|
4625
|
+
this._pwf = tf.f;
|
|
4626
|
+
this._pwNode = parent;
|
|
4627
|
+
this._pwSerial = this._syncSerial;
|
|
4628
|
+
}
|
|
4549
4629
|
projectionBoxVisible(node, tf, margin, viewportOnly = false) {
|
|
4550
4630
|
if (!(node.width > 0 && node.height > 0)) return true;
|
|
4551
4631
|
const { a, b, c, d, e, f } = tf;
|
|
@@ -4643,6 +4723,23 @@ var Scene = (_class7 = class _Scene {
|
|
|
4643
4723
|
syncContentProjection(node) {
|
|
4644
4724
|
if (!this.contentProjectionEnabled || !this.a11yRoot) return;
|
|
4645
4725
|
let el = this.contentElements.get(node.id);
|
|
4726
|
+
const prior0 = this.disableSettledFastPath ? void 0 : this.contentSyncState.get(node.id);
|
|
4727
|
+
if (prior0 !== void 0 && el !== void 0) {
|
|
4728
|
+
const epoch0 = node.getContentEpoch();
|
|
4729
|
+
if (epoch0 !== null && prior0.epoch === epoch0 && prior0.fontEpoch === this.contentFontEpoch && prior0.viewportEpoch === this.contentViewportEpoch && prior0.lx === node.x && prior0.ly === node.y && prior0.lScaleX === node.scaleX && prior0.lScaleY === node.scaleY && prior0.lRotation === node.rotation && prior0.width === node.width && prior0.height === node.height && prior0.interactive === node.interactive) {
|
|
4730
|
+
const parent = node.parent;
|
|
4731
|
+
if (parent === null) {
|
|
4732
|
+
if (prior0.pa === 1 && prior0.pb === 0 && prior0.pc === 0 && prior0.pd === 1 && prior0.pe === 0 && prior0.pf === 0) {
|
|
4733
|
+
return;
|
|
4734
|
+
}
|
|
4735
|
+
} else {
|
|
4736
|
+
this.readParentWorld(parent);
|
|
4737
|
+
if (prior0.pa === this._pwa && prior0.pb === this._pwb && prior0.pc === this._pwc && prior0.pd === this._pwd && prior0.pe === this._pwe && prior0.pf === this._pwf) {
|
|
4738
|
+
return;
|
|
4739
|
+
}
|
|
4740
|
+
}
|
|
4741
|
+
}
|
|
4742
|
+
}
|
|
4646
4743
|
const releaseProjectionEl = () => {
|
|
4647
4744
|
if (el) {
|
|
4648
4745
|
this.clearContentGridState(node.id, el);
|
|
@@ -4826,6 +4923,28 @@ var Scene = (_class7 = class _Scene {
|
|
|
4826
4923
|
const next = _nullishCoalesce(prior, () => ( {}));
|
|
4827
4924
|
next.epoch = epoch;
|
|
4828
4925
|
next.fontEpoch = this.contentFontEpoch;
|
|
4926
|
+
next.viewportEpoch = this.contentViewportEpoch;
|
|
4927
|
+
next.lx = node.x;
|
|
4928
|
+
next.ly = node.y;
|
|
4929
|
+
next.lScaleX = node.scaleX;
|
|
4930
|
+
next.lScaleY = node.scaleY;
|
|
4931
|
+
next.lRotation = node.rotation;
|
|
4932
|
+
if (node.parent === null) {
|
|
4933
|
+
next.pa = 1;
|
|
4934
|
+
next.pb = 0;
|
|
4935
|
+
next.pc = 0;
|
|
4936
|
+
next.pd = 1;
|
|
4937
|
+
next.pe = 0;
|
|
4938
|
+
next.pf = 0;
|
|
4939
|
+
} else {
|
|
4940
|
+
this.readParentWorld(node.parent);
|
|
4941
|
+
next.pa = this._pwa;
|
|
4942
|
+
next.pb = this._pwb;
|
|
4943
|
+
next.pc = this._pwc;
|
|
4944
|
+
next.pd = this._pwd;
|
|
4945
|
+
next.pe = this._pwe;
|
|
4946
|
+
next.pf = this._pwf;
|
|
4947
|
+
}
|
|
4829
4948
|
next.a = a2;
|
|
4830
4949
|
next.b = b2;
|
|
4831
4950
|
next.c = c2;
|
|
@@ -5170,12 +5289,14 @@ var Scene = (_class7 = class _Scene {
|
|
|
5170
5289
|
this.fullViewportElements.length = 0;
|
|
5171
5290
|
this.normalElements.length = 0;
|
|
5172
5291
|
this.activeIds.clear();
|
|
5173
|
-
|
|
5292
|
+
this.a11yOrderRegions.clear();
|
|
5293
|
+
const collect = (node, region) => {
|
|
5174
5294
|
if (node.isDOMPortal) return;
|
|
5175
5295
|
const contentEl = this.contentElements.get(node.id);
|
|
5176
5296
|
if (contentEl) {
|
|
5177
5297
|
if (node.a11yFullViewport) this.fullViewportElements.push(contentEl);
|
|
5178
5298
|
else this.normalElements.push(contentEl);
|
|
5299
|
+
if (region) this.a11yOrderRegions.set(contentEl, region);
|
|
5179
5300
|
}
|
|
5180
5301
|
if (this.shouldProjectA11y(node)) {
|
|
5181
5302
|
const el = this.a11yElements.get(node.id);
|
|
@@ -5183,14 +5304,16 @@ var Scene = (_class7 = class _Scene {
|
|
|
5183
5304
|
this.activeIds.add(node.id);
|
|
5184
5305
|
if (node.a11yFullViewport) this.fullViewportElements.push(el);
|
|
5185
5306
|
else this.normalElements.push(el);
|
|
5307
|
+
if (region) this.a11yOrderRegions.set(el, region);
|
|
5186
5308
|
}
|
|
5187
5309
|
}
|
|
5188
|
-
|
|
5310
|
+
const childRegion = node.clipChildren && node.width > 0 && node.height > 0 ? node : region;
|
|
5311
|
+
for (const child of node.children) collect(child, childRegion);
|
|
5189
5312
|
if (node === this.root) {
|
|
5190
|
-
for (const overlay of this.overlayRoot.children) collect(overlay);
|
|
5313
|
+
for (const overlay of this.overlayRoot.children) collect(overlay, null);
|
|
5191
5314
|
}
|
|
5192
5315
|
};
|
|
5193
|
-
collect(this.root);
|
|
5316
|
+
collect(this.root, null);
|
|
5194
5317
|
let elementsPruned = false;
|
|
5195
5318
|
for (const [id, el] of this.a11yElements.entries()) {
|
|
5196
5319
|
if (!this.activeIds.has(id)) {
|
|
@@ -5252,6 +5375,18 @@ var Scene = (_class7 = class _Scene {
|
|
|
5252
5375
|
* parents, which no `insertBefore` ever acts on. Normalizing everything back
|
|
5253
5376
|
* to world coordinates here would cost a transform per element per frame to
|
|
5254
5377
|
* change nothing observable.
|
|
5378
|
+
*
|
|
5379
|
+
* Banding runs **per region** — per nearest `clipChildren` ancestor, recorded
|
|
5380
|
+
* by {@link enforceA11yDomOrder}'s collect walk — rather than once over the
|
|
5381
|
+
* whole scene. Purely visual banding is right for a screen reader but wrong
|
|
5382
|
+
* for selection: a DOM `Selection` covers everything between anchor and focus
|
|
5383
|
+
* in DOM order, so under one global banding a vertical drag through a
|
|
5384
|
+
* transcript also swallowed a sidebar whose headings happened to fall in the
|
|
5385
|
+
* same rows. Regions are laid out side by side, so ordering region-major keeps
|
|
5386
|
+
* each one a contiguous DOM run and a drag stays inside it, while reading
|
|
5387
|
+
* order *within* a region is unchanged. Regions are emitted in the order their
|
|
5388
|
+
* clipper is first reached by the depth-first walk, so a screen reader still
|
|
5389
|
+
* meets them in the author's declared order.
|
|
5255
5390
|
*/
|
|
5256
5391
|
sortNormalElementsVisually() {
|
|
5257
5392
|
const els = this.normalElements;
|
|
@@ -5279,30 +5414,40 @@ var Scene = (_class7 = class _Scene {
|
|
|
5279
5414
|
}
|
|
5280
5415
|
return { top, left };
|
|
5281
5416
|
};
|
|
5282
|
-
const
|
|
5417
|
+
const decorated = els.map((el, i) => {
|
|
5283
5418
|
const { top, left } = absolute(el);
|
|
5284
5419
|
return { el, i, top, left, container: containers.has(el) };
|
|
5285
5420
|
});
|
|
5286
|
-
|
|
5421
|
+
const regions = this.a11yOrderRegions;
|
|
5422
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
5423
|
+
for (const d of decorated) {
|
|
5424
|
+
const key = _nullishCoalesce(regions.get(d.el), () => ( null));
|
|
5425
|
+
const bucket = buckets.get(key);
|
|
5426
|
+
if (bucket) bucket.push(d);
|
|
5427
|
+
else buckets.set(key, [d]);
|
|
5428
|
+
}
|
|
5287
5429
|
const bandBottom = (r) => r.top + (r.container ? 4 : heightOf(r.el));
|
|
5288
5430
|
const sorted = [];
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5431
|
+
for (const order of buckets.values()) {
|
|
5432
|
+
order.sort((p, q) => p.top - q.top || p.i - q.i);
|
|
5433
|
+
let rowStart = 0;
|
|
5434
|
+
let rowBottom = order.length ? bandBottom(order[0]) : 0;
|
|
5435
|
+
const flushRow = (end) => {
|
|
5436
|
+
const row = order.slice(rowStart, end);
|
|
5437
|
+
row.sort((p, q) => (rtl ? q.left - p.left : p.left - q.left) || p.i - q.i);
|
|
5438
|
+
for (const r of row) sorted.push(r.el);
|
|
5439
|
+
};
|
|
5440
|
+
for (let k = 1; k < order.length; k++) {
|
|
5441
|
+
if (order[k].top < rowBottom) {
|
|
5442
|
+
rowBottom = Math.max(rowBottom, bandBottom(order[k]));
|
|
5443
|
+
} else {
|
|
5444
|
+
flushRow(k);
|
|
5445
|
+
rowStart = k;
|
|
5446
|
+
rowBottom = bandBottom(order[k]);
|
|
5447
|
+
}
|
|
5303
5448
|
}
|
|
5449
|
+
flushRow(order.length);
|
|
5304
5450
|
}
|
|
5305
|
-
flushRow(order.length);
|
|
5306
5451
|
for (let i = 0; i < sorted.length; i++) els[i] = sorted[i];
|
|
5307
5452
|
}
|
|
5308
5453
|
/** Keep DOM/WebGL overlay layers aligned with the canvas's CSS box. */
|
|
@@ -5931,6 +6076,7 @@ var Scene = (_class7 = class _Scene {
|
|
|
5931
6076
|
this.width = width;
|
|
5932
6077
|
this.height = height;
|
|
5933
6078
|
this.contentFontEpoch++;
|
|
6079
|
+
this.contentViewportEpoch++;
|
|
5934
6080
|
if (typeof this.renderer.resize === "function") {
|
|
5935
6081
|
if ("maxDPR" in this.renderer) this.renderer.maxDPR = this.maxDPR;
|
|
5936
6082
|
this.renderer.resize(width, height);
|
|
@@ -6237,17 +6383,17 @@ var TextEntity = (_class8 = class extends _chunkTRT2TRCWjs.Entity {
|
|
|
6237
6383
|
|
|
6238
6384
|
|
|
6239
6385
|
|
|
6240
|
-
|
|
6386
|
+
__init170() {this.nodes = []}
|
|
6241
6387
|
|
|
6242
|
-
|
|
6243
|
-
|
|
6244
|
-
|
|
6245
|
-
|
|
6246
|
-
|
|
6388
|
+
__init171() {this.fillStyle = "#94a3b8"}
|
|
6389
|
+
__init172() {this.strokeStyle = null}
|
|
6390
|
+
__init173() {this.hoveredFillStyle = "#ffffff"}
|
|
6391
|
+
__init174() {this.lineWidth = 1}
|
|
6392
|
+
__init175() {this.isHovered = false}
|
|
6247
6393
|
/** Bumped by {@link applyLayout}; read by `Scene` to skip an unchanged sync. */
|
|
6248
|
-
|
|
6394
|
+
__init176() {this.contentEpoch = 0}
|
|
6249
6395
|
constructor(text, atlas, maxWidth, fontSize = 24) {
|
|
6250
|
-
super();_class8.prototype.
|
|
6396
|
+
super();_class8.prototype.__init170.call(this);_class8.prototype.__init171.call(this);_class8.prototype.__init172.call(this);_class8.prototype.__init173.call(this);_class8.prototype.__init174.call(this);_class8.prototype.__init175.call(this);_class8.prototype.__init176.call(this);;
|
|
6251
6397
|
this.text = text;
|
|
6252
6398
|
this.atlas = atlas;
|
|
6253
6399
|
this.fontSize = fontSize;
|
|
@@ -6366,15 +6512,15 @@ var TextEntity = (_class8 = class extends _chunkTRT2TRCWjs.Entity {
|
|
|
6366
6512
|
// src/components/GridTextEntity.ts
|
|
6367
6513
|
var GridTextEntity = (_class9 = class extends _chunkTRT2TRCWjs.Entity {
|
|
6368
6514
|
|
|
6369
|
-
|
|
6370
|
-
|
|
6515
|
+
__init177() {this.fillStyle = "#ffffff"}
|
|
6516
|
+
__init178() {this.grid = []}
|
|
6371
6517
|
// Array of rows
|
|
6372
|
-
|
|
6373
|
-
|
|
6518
|
+
__init179() {this.cols = 0}
|
|
6519
|
+
__init180() {this.rows = 0}
|
|
6374
6520
|
|
|
6375
6521
|
|
|
6376
6522
|
constructor(_atlas, fontSize = 10) {
|
|
6377
|
-
super();_class9.prototype.
|
|
6523
|
+
super();_class9.prototype.__init177.call(this);_class9.prototype.__init178.call(this);_class9.prototype.__init179.call(this);_class9.prototype.__init180.call(this);;
|
|
6378
6524
|
this.fontSize = fontSize;
|
|
6379
6525
|
this.charWidth = fontSize * 1;
|
|
6380
6526
|
this.charHeight = fontSize * 1.1;
|
|
@@ -6466,23 +6612,23 @@ var SplineEntity = (_class10 = class extends _chunkTRT2TRCWjs.Entity {
|
|
|
6466
6612
|
|
|
6467
6613
|
|
|
6468
6614
|
|
|
6469
|
-
|
|
6470
|
-
|
|
6615
|
+
__init181() {this.offscreen = null}
|
|
6616
|
+
__init182() {this.baked = false}
|
|
6471
6617
|
/** Logical (CSS-pixel) size of the baked bitmap — the blit destination size. */
|
|
6472
|
-
|
|
6473
|
-
|
|
6618
|
+
__init183() {this.bakedWidth = 0}
|
|
6619
|
+
__init184() {this.bakedHeight = 0}
|
|
6474
6620
|
/** Gradient strokes can't be baked to a solid-color bitmap; they render per-frame. */
|
|
6475
6621
|
|
|
6476
6622
|
/** Lazily-flattened polylines (one Float32Array of [x,y,...] per segment) for hit-testing. */
|
|
6477
|
-
|
|
6623
|
+
__init185() {this.polylines = null}
|
|
6478
6624
|
/**
|
|
6479
6625
|
* When `true`, the renderer draws a rounded-rect outline of the entity's
|
|
6480
6626
|
* local bounds after painting the curves. Useful for drag feedback and
|
|
6481
6627
|
* debugging hit areas. Defaults to `false`.
|
|
6482
6628
|
*/
|
|
6483
|
-
|
|
6629
|
+
__init186() {this.showBounds = false}
|
|
6484
6630
|
constructor(doc, opts = {}) {
|
|
6485
|
-
super();_class10.prototype.
|
|
6631
|
+
super();_class10.prototype.__init181.call(this);_class10.prototype.__init182.call(this);_class10.prototype.__init183.call(this);_class10.prototype.__init184.call(this);_class10.prototype.__init185.call(this);_class10.prototype.__init186.call(this);;
|
|
6486
6632
|
this.doc = doc;
|
|
6487
6633
|
this.lineWidth = _nullishCoalesce(opts.lineWidth, () => ( 2));
|
|
6488
6634
|
this.cache = _nullishCoalesce(opts.cache, () => ( true));
|
|
@@ -6863,19 +7009,19 @@ var _math = require('@vectojs/math'); _createStarExport(_math);
|
|
|
6863
7009
|
// src/tree/DOMPortalEntity.ts
|
|
6864
7010
|
var DOMPortalEntity = (_class11 = class extends _chunkTRT2TRCWjs.Entity {
|
|
6865
7011
|
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
|
|
6875
|
-
|
|
6876
|
-
|
|
7012
|
+
__init187() {this.isDOMPortal = true}
|
|
7013
|
+
__init188() {this.domListeners = []}
|
|
7014
|
+
__init189() {this.resizeObserver = null}
|
|
7015
|
+
__init190() {this.domBound = false}
|
|
7016
|
+
__init191() {this.cachedWidth = 100}
|
|
7017
|
+
__init192() {this.cachedHeight = 100}
|
|
7018
|
+
__init193() {this.lastWidth = ""}
|
|
7019
|
+
__init194() {this.lastHeight = ""}
|
|
7020
|
+
__init195() {this.lastTransform = ""}
|
|
7021
|
+
__init196() {this.lastZIndex = ""}
|
|
7022
|
+
__init197() {this.lastOpacity = ""}
|
|
6877
7023
|
constructor(domElement, width, height, id) {
|
|
6878
|
-
super(id);_class11.prototype.
|
|
7024
|
+
super(id);_class11.prototype.__init187.call(this);_class11.prototype.__init188.call(this);_class11.prototype.__init189.call(this);_class11.prototype.__init190.call(this);_class11.prototype.__init191.call(this);_class11.prototype.__init192.call(this);_class11.prototype.__init193.call(this);_class11.prototype.__init194.call(this);_class11.prototype.__init195.call(this);_class11.prototype.__init196.call(this);_class11.prototype.__init197.call(this);;
|
|
6879
7025
|
this.domElement = domElement;
|
|
6880
7026
|
this.width = _nullishCoalesce(width, () => ( 0));
|
|
6881
7027
|
this.height = _nullishCoalesce(height, () => ( 0));
|
package/dist/index.mjs
CHANGED
|
@@ -1997,6 +1997,55 @@ var Scene = class _Scene {
|
|
|
1997
1997
|
contentGridCalibrationStamp = "";
|
|
1998
1998
|
/** Invalidates grid font calibration after browser font availability changes. */
|
|
1999
1999
|
contentFontEpoch = 0;
|
|
2000
|
+
/**
|
|
2001
|
+
* Bumped whenever the viewport itself changes shape, which re-tiers blocks
|
|
2002
|
+
* without moving any of them.
|
|
2003
|
+
*
|
|
2004
|
+
* The settled-walk fast path in {@link syncContentProjection} decides a block
|
|
2005
|
+
* is unchanged from its own local transform plus its parent's world transform.
|
|
2006
|
+
* Both are viewport-independent, so a resize alone would slip past it and leave
|
|
2007
|
+
* every block holding DOM built for the old viewport — a block that should have
|
|
2008
|
+
* been promoted to the fine tier keeping no carriers, or a demoted one keeping
|
|
2009
|
+
* carriers it no longer needs. Scrolling needs no such epoch: it moves the root,
|
|
2010
|
+
* so every block's parent world transform changes and the check fails honestly.
|
|
2011
|
+
*/
|
|
2012
|
+
contentViewportEpoch = 0;
|
|
2013
|
+
/**
|
|
2014
|
+
* One-entry memo for a parent's world transform, held as scalars.
|
|
2015
|
+
*
|
|
2016
|
+
* The a11y walk visits all of one parent's children consecutively, so a
|
|
2017
|
+
* one-entry memo hits for every child after the first — turning an
|
|
2018
|
+
* O(children) sequence of `getWorldTransform()` calls, each of which
|
|
2019
|
+
* allocates a fresh object on its cache-hit path
|
|
2020
|
+
* ({@link Entity.getWorldTransform}), into one call per parent. Flattened to
|
|
2021
|
+
* scalars rather than a cached object so the memo itself never allocates.
|
|
2022
|
+
*
|
|
2023
|
+
* Keyed on `(entity, syncSerial)`: the serial is bumped once per top-level
|
|
2024
|
+
* sync, so a memo can never survive into a frame in which the parent has
|
|
2025
|
+
* moved.
|
|
2026
|
+
*/
|
|
2027
|
+
_pwNode = null;
|
|
2028
|
+
_pwSerial = -1;
|
|
2029
|
+
_pwa = 1;
|
|
2030
|
+
_pwb = 0;
|
|
2031
|
+
_pwc = 0;
|
|
2032
|
+
_pwd = 1;
|
|
2033
|
+
_pwe = 0;
|
|
2034
|
+
_pwf = 0;
|
|
2035
|
+
/** Incremented once per top-level `syncA11y`, invalidating `_pwNode`. */
|
|
2036
|
+
_syncSerial = 0;
|
|
2037
|
+
/**
|
|
2038
|
+
* Disables the settled-walk fast path in {@link syncContentProjection}.
|
|
2039
|
+
*
|
|
2040
|
+
* Exists so a benchmark can measure both arms in ONE run on ONE commit, rather
|
|
2041
|
+
* than comparing two builds and inheriting every difference between them. Also
|
|
2042
|
+
* lets a test assert that a behaviour is genuinely unchanged by the fast path
|
|
2043
|
+
* rather than merely unobserved.
|
|
2044
|
+
*
|
|
2045
|
+
* Not part of the public API and not documented as an option: turning it on only
|
|
2046
|
+
* makes a settled document slower, never more correct.
|
|
2047
|
+
*/
|
|
2048
|
+
disableSettledFastPath = false;
|
|
2000
2049
|
/** Cached Canvas-to-client scale for the current font/viewport epoch. */
|
|
2001
2050
|
contentMetricScaleEpoch = -1;
|
|
2002
2051
|
contentMetricScaleX = 1;
|
|
@@ -2111,6 +2160,16 @@ var Scene = class _Scene {
|
|
|
2111
2160
|
* see {@link sortNormalElementsVisually}.
|
|
2112
2161
|
*/
|
|
2113
2162
|
a11yOrderContainers = /* @__PURE__ */ new Set();
|
|
2163
|
+
/**
|
|
2164
|
+
* Nearest `clipChildren` ancestor per ordered element — its *region* — reused
|
|
2165
|
+
* per pass. Written by `enforceA11yDomOrder`'s collect walk, which already has
|
|
2166
|
+
* the entity in hand, so a region costs one comparison per node rather than an
|
|
2167
|
+
* ancestor walk per element.
|
|
2168
|
+
*
|
|
2169
|
+
* Absent means the element sits under no clipping ancestor and belongs to the
|
|
2170
|
+
* implicit root region. See {@link sortNormalElementsVisually}.
|
|
2171
|
+
*/
|
|
2172
|
+
a11yOrderRegions = /* @__PURE__ */ new Map();
|
|
2114
2173
|
activePortalsThisFrame = /* @__PURE__ */ new Set();
|
|
2115
2174
|
activePortalsPrevFrame = /* @__PURE__ */ new Set();
|
|
2116
2175
|
portalEntities = /* @__PURE__ */ new Map();
|
|
@@ -4106,6 +4165,7 @@ var Scene = class _Scene {
|
|
|
4106
4165
|
if (node === this.root) {
|
|
4107
4166
|
this.contentSemanticBudgetLeft = this.contentSemanticBudget;
|
|
4108
4167
|
this.contentSemanticDeferred = false;
|
|
4168
|
+
this._syncSerial++;
|
|
4109
4169
|
this.contentSelectionPresentThisSync = null;
|
|
4110
4170
|
}
|
|
4111
4171
|
if (node.isDOMPortal) {
|
|
@@ -4545,6 +4605,26 @@ var Scene = class _Scene {
|
|
|
4545
4605
|
* rejected by an ancestor clip box that itself overlaps the viewport would sit
|
|
4546
4606
|
* transparently on top of whatever is really drawn there.
|
|
4547
4607
|
*/
|
|
4608
|
+
/**
|
|
4609
|
+
* Load `parent`'s world transform into the `_pw*` scalar memo.
|
|
4610
|
+
*
|
|
4611
|
+
* Exists so the settled-walk fast path costs no allocation. The walk visits a
|
|
4612
|
+
* parent's children consecutively, so this recomputes at most once per parent
|
|
4613
|
+
* per sync and is a serial + identity comparison for every child after the
|
|
4614
|
+
* first. See the `_pwNode` field for why it is keyed the way it is.
|
|
4615
|
+
*/
|
|
4616
|
+
readParentWorld(parent) {
|
|
4617
|
+
if (this._pwNode === parent && this._pwSerial === this._syncSerial) return;
|
|
4618
|
+
const tf = parent.getWorldTransform();
|
|
4619
|
+
this._pwa = tf.a;
|
|
4620
|
+
this._pwb = tf.b;
|
|
4621
|
+
this._pwc = tf.c;
|
|
4622
|
+
this._pwd = tf.d;
|
|
4623
|
+
this._pwe = tf.e;
|
|
4624
|
+
this._pwf = tf.f;
|
|
4625
|
+
this._pwNode = parent;
|
|
4626
|
+
this._pwSerial = this._syncSerial;
|
|
4627
|
+
}
|
|
4548
4628
|
projectionBoxVisible(node, tf, margin, viewportOnly = false) {
|
|
4549
4629
|
if (!(node.width > 0 && node.height > 0)) return true;
|
|
4550
4630
|
const { a, b, c, d, e, f } = tf;
|
|
@@ -4642,6 +4722,23 @@ var Scene = class _Scene {
|
|
|
4642
4722
|
syncContentProjection(node) {
|
|
4643
4723
|
if (!this.contentProjectionEnabled || !this.a11yRoot) return;
|
|
4644
4724
|
let el = this.contentElements.get(node.id);
|
|
4725
|
+
const prior0 = this.disableSettledFastPath ? void 0 : this.contentSyncState.get(node.id);
|
|
4726
|
+
if (prior0 !== void 0 && el !== void 0) {
|
|
4727
|
+
const epoch0 = node.getContentEpoch();
|
|
4728
|
+
if (epoch0 !== null && prior0.epoch === epoch0 && prior0.fontEpoch === this.contentFontEpoch && prior0.viewportEpoch === this.contentViewportEpoch && prior0.lx === node.x && prior0.ly === node.y && prior0.lScaleX === node.scaleX && prior0.lScaleY === node.scaleY && prior0.lRotation === node.rotation && prior0.width === node.width && prior0.height === node.height && prior0.interactive === node.interactive) {
|
|
4729
|
+
const parent = node.parent;
|
|
4730
|
+
if (parent === null) {
|
|
4731
|
+
if (prior0.pa === 1 && prior0.pb === 0 && prior0.pc === 0 && prior0.pd === 1 && prior0.pe === 0 && prior0.pf === 0) {
|
|
4732
|
+
return;
|
|
4733
|
+
}
|
|
4734
|
+
} else {
|
|
4735
|
+
this.readParentWorld(parent);
|
|
4736
|
+
if (prior0.pa === this._pwa && prior0.pb === this._pwb && prior0.pc === this._pwc && prior0.pd === this._pwd && prior0.pe === this._pwe && prior0.pf === this._pwf) {
|
|
4737
|
+
return;
|
|
4738
|
+
}
|
|
4739
|
+
}
|
|
4740
|
+
}
|
|
4741
|
+
}
|
|
4645
4742
|
const releaseProjectionEl = () => {
|
|
4646
4743
|
if (el) {
|
|
4647
4744
|
this.clearContentGridState(node.id, el);
|
|
@@ -4825,6 +4922,28 @@ var Scene = class _Scene {
|
|
|
4825
4922
|
const next = prior ?? {};
|
|
4826
4923
|
next.epoch = epoch;
|
|
4827
4924
|
next.fontEpoch = this.contentFontEpoch;
|
|
4925
|
+
next.viewportEpoch = this.contentViewportEpoch;
|
|
4926
|
+
next.lx = node.x;
|
|
4927
|
+
next.ly = node.y;
|
|
4928
|
+
next.lScaleX = node.scaleX;
|
|
4929
|
+
next.lScaleY = node.scaleY;
|
|
4930
|
+
next.lRotation = node.rotation;
|
|
4931
|
+
if (node.parent === null) {
|
|
4932
|
+
next.pa = 1;
|
|
4933
|
+
next.pb = 0;
|
|
4934
|
+
next.pc = 0;
|
|
4935
|
+
next.pd = 1;
|
|
4936
|
+
next.pe = 0;
|
|
4937
|
+
next.pf = 0;
|
|
4938
|
+
} else {
|
|
4939
|
+
this.readParentWorld(node.parent);
|
|
4940
|
+
next.pa = this._pwa;
|
|
4941
|
+
next.pb = this._pwb;
|
|
4942
|
+
next.pc = this._pwc;
|
|
4943
|
+
next.pd = this._pwd;
|
|
4944
|
+
next.pe = this._pwe;
|
|
4945
|
+
next.pf = this._pwf;
|
|
4946
|
+
}
|
|
4828
4947
|
next.a = a2;
|
|
4829
4948
|
next.b = b2;
|
|
4830
4949
|
next.c = c2;
|
|
@@ -5169,12 +5288,14 @@ var Scene = class _Scene {
|
|
|
5169
5288
|
this.fullViewportElements.length = 0;
|
|
5170
5289
|
this.normalElements.length = 0;
|
|
5171
5290
|
this.activeIds.clear();
|
|
5172
|
-
|
|
5291
|
+
this.a11yOrderRegions.clear();
|
|
5292
|
+
const collect = (node, region) => {
|
|
5173
5293
|
if (node.isDOMPortal) return;
|
|
5174
5294
|
const contentEl = this.contentElements.get(node.id);
|
|
5175
5295
|
if (contentEl) {
|
|
5176
5296
|
if (node.a11yFullViewport) this.fullViewportElements.push(contentEl);
|
|
5177
5297
|
else this.normalElements.push(contentEl);
|
|
5298
|
+
if (region) this.a11yOrderRegions.set(contentEl, region);
|
|
5178
5299
|
}
|
|
5179
5300
|
if (this.shouldProjectA11y(node)) {
|
|
5180
5301
|
const el = this.a11yElements.get(node.id);
|
|
@@ -5182,14 +5303,16 @@ var Scene = class _Scene {
|
|
|
5182
5303
|
this.activeIds.add(node.id);
|
|
5183
5304
|
if (node.a11yFullViewport) this.fullViewportElements.push(el);
|
|
5184
5305
|
else this.normalElements.push(el);
|
|
5306
|
+
if (region) this.a11yOrderRegions.set(el, region);
|
|
5185
5307
|
}
|
|
5186
5308
|
}
|
|
5187
|
-
|
|
5309
|
+
const childRegion = node.clipChildren && node.width > 0 && node.height > 0 ? node : region;
|
|
5310
|
+
for (const child of node.children) collect(child, childRegion);
|
|
5188
5311
|
if (node === this.root) {
|
|
5189
|
-
for (const overlay of this.overlayRoot.children) collect(overlay);
|
|
5312
|
+
for (const overlay of this.overlayRoot.children) collect(overlay, null);
|
|
5190
5313
|
}
|
|
5191
5314
|
};
|
|
5192
|
-
collect(this.root);
|
|
5315
|
+
collect(this.root, null);
|
|
5193
5316
|
let elementsPruned = false;
|
|
5194
5317
|
for (const [id, el] of this.a11yElements.entries()) {
|
|
5195
5318
|
if (!this.activeIds.has(id)) {
|
|
@@ -5251,6 +5374,18 @@ var Scene = class _Scene {
|
|
|
5251
5374
|
* parents, which no `insertBefore` ever acts on. Normalizing everything back
|
|
5252
5375
|
* to world coordinates here would cost a transform per element per frame to
|
|
5253
5376
|
* change nothing observable.
|
|
5377
|
+
*
|
|
5378
|
+
* Banding runs **per region** — per nearest `clipChildren` ancestor, recorded
|
|
5379
|
+
* by {@link enforceA11yDomOrder}'s collect walk — rather than once over the
|
|
5380
|
+
* whole scene. Purely visual banding is right for a screen reader but wrong
|
|
5381
|
+
* for selection: a DOM `Selection` covers everything between anchor and focus
|
|
5382
|
+
* in DOM order, so under one global banding a vertical drag through a
|
|
5383
|
+
* transcript also swallowed a sidebar whose headings happened to fall in the
|
|
5384
|
+
* same rows. Regions are laid out side by side, so ordering region-major keeps
|
|
5385
|
+
* each one a contiguous DOM run and a drag stays inside it, while reading
|
|
5386
|
+
* order *within* a region is unchanged. Regions are emitted in the order their
|
|
5387
|
+
* clipper is first reached by the depth-first walk, so a screen reader still
|
|
5388
|
+
* meets them in the author's declared order.
|
|
5254
5389
|
*/
|
|
5255
5390
|
sortNormalElementsVisually() {
|
|
5256
5391
|
const els = this.normalElements;
|
|
@@ -5278,30 +5413,40 @@ var Scene = class _Scene {
|
|
|
5278
5413
|
}
|
|
5279
5414
|
return { top, left };
|
|
5280
5415
|
};
|
|
5281
|
-
const
|
|
5416
|
+
const decorated = els.map((el, i) => {
|
|
5282
5417
|
const { top, left } = absolute(el);
|
|
5283
5418
|
return { el, i, top, left, container: containers.has(el) };
|
|
5284
5419
|
});
|
|
5285
|
-
|
|
5420
|
+
const regions = this.a11yOrderRegions;
|
|
5421
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
5422
|
+
for (const d of decorated) {
|
|
5423
|
+
const key = regions.get(d.el) ?? null;
|
|
5424
|
+
const bucket = buckets.get(key);
|
|
5425
|
+
if (bucket) bucket.push(d);
|
|
5426
|
+
else buckets.set(key, [d]);
|
|
5427
|
+
}
|
|
5286
5428
|
const bandBottom = (r) => r.top + (r.container ? 4 : heightOf(r.el));
|
|
5287
5429
|
const sorted = [];
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5430
|
+
for (const order of buckets.values()) {
|
|
5431
|
+
order.sort((p, q) => p.top - q.top || p.i - q.i);
|
|
5432
|
+
let rowStart = 0;
|
|
5433
|
+
let rowBottom = order.length ? bandBottom(order[0]) : 0;
|
|
5434
|
+
const flushRow = (end) => {
|
|
5435
|
+
const row = order.slice(rowStart, end);
|
|
5436
|
+
row.sort((p, q) => (rtl ? q.left - p.left : p.left - q.left) || p.i - q.i);
|
|
5437
|
+
for (const r of row) sorted.push(r.el);
|
|
5438
|
+
};
|
|
5439
|
+
for (let k = 1; k < order.length; k++) {
|
|
5440
|
+
if (order[k].top < rowBottom) {
|
|
5441
|
+
rowBottom = Math.max(rowBottom, bandBottom(order[k]));
|
|
5442
|
+
} else {
|
|
5443
|
+
flushRow(k);
|
|
5444
|
+
rowStart = k;
|
|
5445
|
+
rowBottom = bandBottom(order[k]);
|
|
5446
|
+
}
|
|
5302
5447
|
}
|
|
5448
|
+
flushRow(order.length);
|
|
5303
5449
|
}
|
|
5304
|
-
flushRow(order.length);
|
|
5305
5450
|
for (let i = 0; i < sorted.length; i++) els[i] = sorted[i];
|
|
5306
5451
|
}
|
|
5307
5452
|
/** Keep DOM/WebGL overlay layers aligned with the canvas's CSS box. */
|
|
@@ -5930,6 +6075,7 @@ var Scene = class _Scene {
|
|
|
5930
6075
|
this.width = width;
|
|
5931
6076
|
this.height = height;
|
|
5932
6077
|
this.contentFontEpoch++;
|
|
6078
|
+
this.contentViewportEpoch++;
|
|
5933
6079
|
if (typeof this.renderer.resize === "function") {
|
|
5934
6080
|
if ("maxDPR" in this.renderer) this.renderer.maxDPR = this.maxDPR;
|
|
5935
6081
|
this.renderer.resize(width, height);
|
package/dist/tree/Scene.d.ts
CHANGED
|
@@ -623,6 +623,55 @@ export declare class Scene {
|
|
|
623
623
|
private contentGridCalibrationStamp;
|
|
624
624
|
/** Invalidates grid font calibration after browser font availability changes. */
|
|
625
625
|
private contentFontEpoch;
|
|
626
|
+
/**
|
|
627
|
+
* Bumped whenever the viewport itself changes shape, which re-tiers blocks
|
|
628
|
+
* without moving any of them.
|
|
629
|
+
*
|
|
630
|
+
* The settled-walk fast path in {@link syncContentProjection} decides a block
|
|
631
|
+
* is unchanged from its own local transform plus its parent's world transform.
|
|
632
|
+
* Both are viewport-independent, so a resize alone would slip past it and leave
|
|
633
|
+
* every block holding DOM built for the old viewport — a block that should have
|
|
634
|
+
* been promoted to the fine tier keeping no carriers, or a demoted one keeping
|
|
635
|
+
* carriers it no longer needs. Scrolling needs no such epoch: it moves the root,
|
|
636
|
+
* so every block's parent world transform changes and the check fails honestly.
|
|
637
|
+
*/
|
|
638
|
+
private contentViewportEpoch;
|
|
639
|
+
/**
|
|
640
|
+
* One-entry memo for a parent's world transform, held as scalars.
|
|
641
|
+
*
|
|
642
|
+
* The a11y walk visits all of one parent's children consecutively, so a
|
|
643
|
+
* one-entry memo hits for every child after the first — turning an
|
|
644
|
+
* O(children) sequence of `getWorldTransform()` calls, each of which
|
|
645
|
+
* allocates a fresh object on its cache-hit path
|
|
646
|
+
* ({@link Entity.getWorldTransform}), into one call per parent. Flattened to
|
|
647
|
+
* scalars rather than a cached object so the memo itself never allocates.
|
|
648
|
+
*
|
|
649
|
+
* Keyed on `(entity, syncSerial)`: the serial is bumped once per top-level
|
|
650
|
+
* sync, so a memo can never survive into a frame in which the parent has
|
|
651
|
+
* moved.
|
|
652
|
+
*/
|
|
653
|
+
private _pwNode;
|
|
654
|
+
private _pwSerial;
|
|
655
|
+
private _pwa;
|
|
656
|
+
private _pwb;
|
|
657
|
+
private _pwc;
|
|
658
|
+
private _pwd;
|
|
659
|
+
private _pwe;
|
|
660
|
+
private _pwf;
|
|
661
|
+
/** Incremented once per top-level `syncA11y`, invalidating `_pwNode`. */
|
|
662
|
+
private _syncSerial;
|
|
663
|
+
/**
|
|
664
|
+
* Disables the settled-walk fast path in {@link syncContentProjection}.
|
|
665
|
+
*
|
|
666
|
+
* Exists so a benchmark can measure both arms in ONE run on ONE commit, rather
|
|
667
|
+
* than comparing two builds and inheriting every difference between them. Also
|
|
668
|
+
* lets a test assert that a behaviour is genuinely unchanged by the fast path
|
|
669
|
+
* rather than merely unobserved.
|
|
670
|
+
*
|
|
671
|
+
* Not part of the public API and not documented as an option: turning it on only
|
|
672
|
+
* makes a settled document slower, never more correct.
|
|
673
|
+
*/
|
|
674
|
+
disableSettledFastPath: boolean;
|
|
626
675
|
/** Cached Canvas-to-client scale for the current font/viewport epoch. */
|
|
627
676
|
private contentMetricScaleEpoch;
|
|
628
677
|
private contentMetricScaleX;
|
|
@@ -719,6 +768,16 @@ export declare class Scene {
|
|
|
719
768
|
* see {@link sortNormalElementsVisually}.
|
|
720
769
|
*/
|
|
721
770
|
private a11yOrderContainers;
|
|
771
|
+
/**
|
|
772
|
+
* Nearest `clipChildren` ancestor per ordered element — its *region* — reused
|
|
773
|
+
* per pass. Written by `enforceA11yDomOrder`'s collect walk, which already has
|
|
774
|
+
* the entity in hand, so a region costs one comparison per node rather than an
|
|
775
|
+
* ancestor walk per element.
|
|
776
|
+
*
|
|
777
|
+
* Absent means the element sits under no clipping ancestor and belongs to the
|
|
778
|
+
* implicit root region. See {@link sortNormalElementsVisually}.
|
|
779
|
+
*/
|
|
780
|
+
private a11yOrderRegions;
|
|
722
781
|
private activePortalsThisFrame;
|
|
723
782
|
private activePortalsPrevFrame;
|
|
724
783
|
private portalEntities;
|
|
@@ -1551,6 +1610,15 @@ export declare class Scene {
|
|
|
1551
1610
|
* rejected by an ancestor clip box that itself overlaps the viewport would sit
|
|
1552
1611
|
* transparently on top of whatever is really drawn there.
|
|
1553
1612
|
*/
|
|
1613
|
+
/**
|
|
1614
|
+
* Load `parent`'s world transform into the `_pw*` scalar memo.
|
|
1615
|
+
*
|
|
1616
|
+
* Exists so the settled-walk fast path costs no allocation. The walk visits a
|
|
1617
|
+
* parent's children consecutively, so this recomputes at most once per parent
|
|
1618
|
+
* per sync and is a serial + identity comparison for every child after the
|
|
1619
|
+
* first. See the `_pwNode` field for why it is keyed the way it is.
|
|
1620
|
+
*/
|
|
1621
|
+
private readParentWorld;
|
|
1554
1622
|
private projectionBoxVisible;
|
|
1555
1623
|
/**
|
|
1556
1624
|
* The band of an entity's own y coordinates that is worth projecting, or
|
|
@@ -1609,6 +1677,18 @@ export declare class Scene {
|
|
|
1609
1677
|
* parents, which no `insertBefore` ever acts on. Normalizing everything back
|
|
1610
1678
|
* to world coordinates here would cost a transform per element per frame to
|
|
1611
1679
|
* change nothing observable.
|
|
1680
|
+
*
|
|
1681
|
+
* Banding runs **per region** — per nearest `clipChildren` ancestor, recorded
|
|
1682
|
+
* by {@link enforceA11yDomOrder}'s collect walk — rather than once over the
|
|
1683
|
+
* whole scene. Purely visual banding is right for a screen reader but wrong
|
|
1684
|
+
* for selection: a DOM `Selection` covers everything between anchor and focus
|
|
1685
|
+
* in DOM order, so under one global banding a vertical drag through a
|
|
1686
|
+
* transcript also swallowed a sidebar whose headings happened to fall in the
|
|
1687
|
+
* same rows. Regions are laid out side by side, so ordering region-major keeps
|
|
1688
|
+
* each one a contiguous DOM run and a drag stays inside it, while reading
|
|
1689
|
+
* order *within* a region is unchanged. Regions are emitted in the order their
|
|
1690
|
+
* clipper is first reached by the depth-first walk, so a screen reader still
|
|
1691
|
+
* meets them in the author's declared order.
|
|
1612
1692
|
*/
|
|
1613
1693
|
private sortNormalElementsVisually;
|
|
1614
1694
|
/** Keep DOM/WebGL overlay layers aligned with the canvas's CSS box. */
|