@vectojs/core 1.32.6 → 1.32.7

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
@@ -19,7 +19,7 @@ var _chunkM73XB4ZKjs = require('./chunk-M73XB4ZK.js');
19
19
 
20
20
 
21
21
 
22
- var _chunkTRT2TRCWjs = require('./chunk-TRT2TRCW.js');
22
+ var _chunkFQ2565ITjs = require('./chunk-FQ2565IT.js');
23
23
 
24
24
  // src/tree/Scene.ts
25
25
  var _animation = require('@vectojs/animation'); _createStarExport(_animation);
@@ -34,7 +34,7 @@ var PARTICLE_OFFSET_ORIGIN_X = 4;
34
34
  var PARTICLE_OFFSET_ORIGIN_Y = 5;
35
35
  var PARTICLE_OFFSET_SIZE = 6;
36
36
  var PARTICLE_OFFSET_LIFE = 7;
37
- var ComputeParticleEntity = (_class = class extends _chunkTRT2TRCWjs.Entity {
37
+ var ComputeParticleEntity = (_class = class extends _chunkFQ2565ITjs.Entity {
38
38
 
39
39
 
40
40
 
@@ -1435,6 +1435,7 @@ function parseInlinePx(value) {
1435
1435
  return Number.isFinite(n) && n > 0 ? n : null;
1436
1436
  }
1437
1437
  var DEFAULT_CONTENT_SEMANTIC_BUDGET = 256;
1438
+ var WASM_UPLOAD_REJECT_LIMIT = 3;
1438
1439
  function projectionGridLineWindow(grid, projectionLines, band) {
1439
1440
  const count = grid.lines.length;
1440
1441
  const all = { start: 0, end: count, gated: false };
@@ -2205,10 +2206,18 @@ var Scene = (_class7 = class _Scene {
2205
2206
  __init115() {this._wasmWorld = null}
2206
2207
  __init116() {this._structureVersion = 0}
2207
2208
  __init117() {this._storeStructureVersion = -1}
2209
+ /**
2210
+ * Consecutive `uploadRuns` rejections. Reset by any success and by
2211
+ * {@link setTransformBackend}; at {@link WASM_UPLOAD_REJECT_LIMIT} the backend
2212
+ * mode flips to `'js'` permanently.
2213
+ */
2214
+ __init118() {this._wasmUploadRejections = 0}
2215
+ /** Latch for the permanent-fallback warning, which fires from a per-frame path. */
2216
+ __init119() {this.hasWarnedWasmUploadFallback = false}
2208
2217
  // Cached list of ComputeParticleEntity instances in the tree, keyed by the
2209
2218
  // structure version it was gathered at. Rebuilt only on a topology change.
2210
- __init118() {this._computeEntities = []}
2211
- __init119() {this._computeEntitiesVersion = -1}
2219
+ __init120() {this._computeEntities = []}
2220
+ __init121() {this._computeEntitiesVersion = -1}
2212
2221
  /** Invalidate the resident WASM store layout; the next wasm-mode frame rebuilds
2213
2222
  * it. Called by `Entity.add`/`remove` (topology changes only). */
2214
2223
  markStructureChanged() {
@@ -2242,6 +2251,7 @@ var Scene = (_class7 = class _Scene {
2242
2251
  setTransformBackend(backend) {
2243
2252
  this._wasm = backend;
2244
2253
  this._transformBackend = backend ? "wasm" : "js";
2254
+ this._wasmUploadRejections = 0;
2245
2255
  }
2246
2256
  /**
2247
2257
  * Asynchronously instantiate the WASM transform core and, on success, hot-swap
@@ -2293,7 +2303,7 @@ var Scene = (_class7 = class _Scene {
2293
2303
  * cached globally; the instance is per-Scene, which is the isolation that
2294
2304
  * actually matters.
2295
2305
  */
2296
- __init120() {this._wasmRuntime = null}
2306
+ __init122() {this._wasmRuntime = null}
2297
2307
  /**
2298
2308
  * Load (or reuse) this Scene's shared WASM runtime.
2299
2309
  *
@@ -2321,30 +2331,30 @@ var Scene = (_class7 = class _Scene {
2321
2331
  get wasmRuntime() {
2322
2332
  return this._wasmRuntime;
2323
2333
  }
2324
- __init121() {this._hitWasm = null}
2334
+ __init123() {this._hitWasm = null}
2325
2335
  // Cache key: which frame + structure version the grid was last (successfully,
2326
2336
  // non-overflowing) built for. findEntityAt is called ad-hoc (pointer
2327
2337
  // hover/click), not every frame, so the grid is refreshed lazily on demand
2328
2338
  // rather than proactively every render() — unlike the transform store, which
2329
2339
  // every frame's draw depends on.
2330
- __init122() {this._hitGridFrame = -1}
2331
- __init123() {this._hitGridOk = false}
2332
- __init124() {this._hitSlotEntity = []}
2333
- __init125() {this._hitBoundless = []}
2340
+ __init124() {this._hitGridFrame = -1}
2341
+ __init125() {this._hitGridOk = false}
2342
+ __init126() {this._hitSlotEntity = []}
2343
+ __init127() {this._hitBoundless = []}
2334
2344
  /** Reused buffer for the fused gather, so a pointer query allocates nothing. */
2335
- __init126() {this._hitGatherBuffer = null}
2345
+ __init128() {this._hitGatherBuffer = null}
2336
2346
  /**
2337
2347
  * Whether the last grid build sourced its AABBs from the WASM transform store
2338
2348
  * rather than recomputing them in JS. Diagnostic only — both paths must
2339
2349
  * produce the same entity for a given point.
2340
2350
  */
2341
- __init127() {this._hitFusedGather = false}
2351
+ __init129() {this._hitFusedGather = false}
2342
2352
  /**
2343
2353
  * Whether `compute_aabbs` has run against the current frame's world matrices.
2344
2354
  * The AABB pass is only meaningful after a `compose_*`, so the fused gather
2345
2355
  * must not read the views before then.
2346
2356
  */
2347
- __init128() {this._wasmAabbsFresh = false}
2357
+ __init130() {this._wasmAabbsFresh = false}
2348
2358
  /** Did the last hit-grid build use the fused (WASM-store) gather? */
2349
2359
  get hitGatherPath() {
2350
2360
  return this._hitFusedGather ? "fused" : "js";
@@ -2353,9 +2363,9 @@ var Scene = (_class7 = class _Scene {
2353
2363
  * Why the transform accelerator did or did not run on the most recent frame.
2354
2364
  * Written by the render walk and `_syncWasmStore`.
2355
2365
  */
2356
- __init129() {this._transformReason = "not-installed"}
2366
+ __init131() {this._transformReason = "not-installed"}
2357
2367
  /** Why the batched-driver accelerator did or did not run. */
2358
- __init130() {this._animReason = "not-installed"}
2368
+ __init132() {this._animReason = "not-installed"}
2359
2369
  /**
2360
2370
  * Why the hit-test accelerator did or did not serve the last pointer query.
2361
2371
  * The grid is built lazily on demand, not every frame, so this describes the
@@ -2363,11 +2373,11 @@ var Scene = (_class7 = class _Scene {
2363
2373
  * before a backend exists; `_ensureHitGrid` moves it to `'not-applicable'`
2364
2374
  * once one is installed but nothing has queried yet.
2365
2375
  */
2366
- __init131() {this._hitReason = "not-installed"}
2376
+ __init133() {this._hitReason = "not-installed"}
2367
2377
  /** Why the particle accelerator did or did not run. */
2368
- __init132() {this._particleReason = "not-applicable"}
2378
+ __init134() {this._particleReason = "not-applicable"}
2369
2379
  /** Which particle implementation actually simulated the most recent frame. */
2370
- __init133() {this._particlePath = "none"}
2380
+ __init135() {this._particlePath = "none"}
2371
2381
  /**
2372
2382
  * Per-frame status of every invisible accelerator: whether each is installed,
2373
2383
  * whether it actually ran on the most recent frame, and why.
@@ -2527,25 +2537,25 @@ var Scene = (_class7 = class _Scene {
2527
2537
  // EasingFn (which cannot cross into WASM) all fall through to it — WASM can
2528
2538
  // only ever change *how* a driver is advanced, never *what* value it lands
2529
2539
  // on.
2530
- __init134() {this._animWasm = null}
2540
+ __init136() {this._animWasm = null}
2531
2541
  // Entities with at least one active driver, added by Entity._spawnDriver.
2532
2542
  // Self-pruning: _tickBatchedDrivers drops an entry the first time it visits
2533
2543
  // an entity whose drivers have since all completed or been removed. This is
2534
2544
  // what lets the batch pass find its candidates in O(active drivers), not
2535
2545
  // O(tree size) — the exact mistake G3's first integrated benchmark made.
2536
- __init135() {this._activeDriverEntities = /* @__PURE__ */ new Set()}
2546
+ __init137() {this._activeDriverEntities = /* @__PURE__ */ new Set()}
2537
2547
  // Reused across frames instead of allocating a fresh array + N {entity,prop,
2538
2548
  // driver} objects every call — the integrated benchmark
2539
2549
  // (benchmarks/anim-wasm-scene) found that allocation churn was the
2540
2550
  // dominant integrated cost, not the wasm kernel itself. Parallel arrays,
2541
2551
  // truncated to the live count after each use so a stale tail slot never
2542
2552
  // pins a no-longer-active entity/driver in memory.
2543
- __init136() {this._springEntities = []}
2544
- __init137() {this._springProps = []}
2545
- __init138() {this._springDrivers = []}
2546
- __init139() {this._tweenEntities = []}
2547
- __init140() {this._tweenProps = []}
2548
- __init141() {this._tweenDrivers = []}
2553
+ __init138() {this._springEntities = []}
2554
+ __init139() {this._springProps = []}
2555
+ __init140() {this._springDrivers = []}
2556
+ __init141() {this._tweenEntities = []}
2557
+ __init142() {this._tweenProps = []}
2558
+ __init143() {this._tweenDrivers = []}
2549
2559
  /**
2550
2560
  * Minimum number of batchable (spring, or named-easing tween) active drivers
2551
2561
  * before a frame engages the WASM batch path at all; below it, every driver
@@ -2617,7 +2627,7 @@ var Scene = (_class7 = class _Scene {
2617
2627
  * Setting {@link animDriverGateCount} overwrites all three, so existing code
2618
2628
  * that tuned the single knob keeps working unchanged.
2619
2629
  */
2620
- __init142() {this._animBatchedLastFrame = false}
2630
+ __init144() {this._animBatchedLastFrame = false}
2621
2631
  /**
2622
2632
  * Whether the WASM batch path actually ran on the most recent frame.
2623
2633
  *
@@ -2629,7 +2639,7 @@ var Scene = (_class7 = class _Scene {
2629
2639
  get animBatchedLastFrame() {
2630
2640
  return this._animBatchedLastFrame;
2631
2641
  }
2632
- __init143() {this.animGate = {
2642
+ __init145() {this.animGate = {
2633
2643
  spring: 128,
2634
2644
  tween: 256,
2635
2645
  mixed: 128
@@ -2667,7 +2677,7 @@ var Scene = (_class7 = class _Scene {
2667
2677
  // (benchmarks/particle-wasm). f32 (matches the WGSL shader), bit-identical to
2668
2678
  // a JS f32 reference oracle; updateCPU (f64) stays the permanent fallback when
2669
2679
  // no backend is installed or a scene runs on WebGPU.
2670
- __init144() {this._particleWasm = null}
2680
+ __init146() {this._particleWasm = null}
2671
2681
  /** Which backend runs the CPU particle simulation. Reflects only whether a
2672
2682
  * backend is installed (the WebGPU compute path, when active, is used first
2673
2683
  * regardless). */
@@ -2873,8 +2883,19 @@ var Scene = (_class7 = class _Scene {
2873
2883
  }
2874
2884
  if (!backend.uploadRuns(built.store)) {
2875
2885
  this._transformReason = "rejected";
2886
+ this._wasmUploadRejections++;
2887
+ if (this._wasmUploadRejections >= WASM_UPLOAD_REJECT_LIMIT) {
2888
+ this._transformBackend = "js";
2889
+ if (!this.hasWarnedWasmUploadFallback) {
2890
+ console.warn(
2891
+ `[VectoJS] WASM transform backend disabled after ${this._wasmUploadRejections} consecutive run-table rejections (${built.store.runCount} runs for ${built.store.count} entities); the crate refused this topology. Falling back to JS transforms for this scene. Call scene.setTransformBackend(backend) to re-enable after reducing the tree.`
2892
+ );
2893
+ this.hasWarnedWasmUploadFallback = true;
2894
+ }
2895
+ }
2876
2896
  return null;
2877
2897
  }
2898
+ this._wasmUploadRejections = 0;
2878
2899
  this._treeStore = built.store;
2879
2900
  this._slotEntity = slotEntity2;
2880
2901
  this._wasmInputs = backend.inputView();
@@ -2944,24 +2965,24 @@ var Scene = (_class7 = class _Scene {
2944
2965
  * sync, so retaining the order prevents a newly opened overlay from spending
2945
2966
  * its first frame below previously projected controls.
2946
2967
  */
2947
- __init145() {this.a11yRenderOrders = /* @__PURE__ */ new Map()}
2968
+ __init147() {this.a11yRenderOrders = /* @__PURE__ */ new Map()}
2948
2969
  // Optional WebGL point-cloud layer (see SceneOptions.pointBackend).
2949
- __init146() {this.pointRenderer = null}
2950
- __init147() {this.glCanvas = null}
2951
- __init148() {this.glContextLostHandler = null}
2952
- __init149() {this.glContextRestoredHandler = null}
2970
+ __init148() {this.pointRenderer = null}
2971
+ __init149() {this.glCanvas = null}
2972
+ __init150() {this.glContextLostHandler = null}
2973
+ __init151() {this.glContextRestoredHandler = null}
2953
2974
 
2954
2975
 
2955
2976
 
2956
- __init150() {this.disableWindowResize = false}
2977
+ __init152() {this.disableWindowResize = false}
2957
2978
  /** See {@link SceneOptions.maxDPR}. `undefined` = uncapped (real DPR). */
2958
2979
 
2959
2980
  // WebGPU properties
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}
2981
+ __init153() {this.destroyed = false}
2982
+ __init154() {this.device = null}
2983
+ __init155() {this.deviceLost = false}
2984
+ __init156() {this.particleBackend = "auto"}
2985
+ __init157() {this._webgpuDisabled = false}
2965
2986
  get webgpuDisabled() {
2966
2987
  return this._webgpuDisabled || this.particleBackend === "cpu";
2967
2988
  }
@@ -2989,22 +3010,32 @@ var Scene = (_class7 = class _Scene {
2989
3010
  set webgpuDisabled(value) {
2990
3011
  this._webgpuDisabled = value;
2991
3012
  }
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}
3013
+ __init158() {this.recoveryTimerId = null}
3014
+ __init159() {this.manager = null}
3015
+ __init160() {this.initializingWebGPU = false}
3016
+ __init161() {this.gpuCanvas = null}
3017
+ __init162() {this.gpuContext = null}
2997
3018
  /** True while the GPU canvas holds a presented particle frame (needs clearing when they leave). */
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}
3019
+ __init163() {this.gpuHasContent = false}
3020
+ __init164() {this.mouseX = -9999}
3021
+ __init165() {this.mouseY = -9999}
3022
+ __init166() {this.pointerMoveListener = null}
3023
+ __init167() {this.pointerLeaveListener = null}
3003
3024
  /** Element the pointer listeners are bound to (parent container if present,
3004
3025
  * else the canvas). Stored so `destroy()` detaches from the same element. */
3005
- __init166() {this.pointerEventTarget = null}
3006
- __init167() {this.hasWarnedZeroSize = false}
3007
- __init168() {this.fontLoadHandler = null}
3026
+ __init168() {this.pointerEventTarget = null}
3027
+ __init169() {this.hasWarnedZeroSize = false}
3028
+ /**
3029
+ * Latch for {@link Scene.resize}'s invalid-dimension warning.
3030
+ *
3031
+ * Separate from `hasWarnedZeroSize`: that one fires for a tolerated zero-size
3032
+ * scene at `start()`, this one for a rejected resize, and sharing the flag
3033
+ * would let either suppress the other's first (and only) report. Latched
3034
+ * because `resize()` is commonly driven from a `ResizeObserver`, which would
3035
+ * otherwise warn on every frame of a drag.
3036
+ */
3037
+ __init170() {this.hasWarnedInvalidResize = false}
3038
+ __init171() {this.fontLoadHandler = null}
3008
3039
  // ── Dev-mode warning infrastructure ──────────────────────────────
3009
3040
  //
3010
3041
  // Enable with `Scene.devMode = true` or by setting `globalThis.__DEV__`.
@@ -3038,7 +3069,7 @@ var Scene = (_class7 = class _Scene {
3038
3069
  return false;
3039
3070
  }
3040
3071
 
3041
- __init169() {this._devFrameCount = 0}
3072
+ __init172() {this._devFrameCount = 0}
3042
3073
  _devWarn(message) {
3043
3074
  if (!this._devActive) return;
3044
3075
  console.warn(`[vectojs/dev] ${message}`);
@@ -3115,7 +3146,7 @@ var Scene = (_class7 = class _Scene {
3115
3146
  };
3116
3147
  walkProjections(this.root);
3117
3148
  }
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);
3149
+ 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);_class7.prototype.__init170.call(this);_class7.prototype.__init171.call(this);_class7.prototype.__init172.call(this);
3119
3150
  this.canvas = canvas;
3120
3151
  this.debugA11y = _nullishCoalesce(options.debugA11y, () => ( false));
3121
3152
  this.disableWindowResize = _nullishCoalesce(options.disableWindowResize, () => ( false));
@@ -3152,7 +3183,7 @@ var Scene = (_class7 = class _Scene {
3152
3183
  this.forcedColorsChangeHandler = () => this.markDirty();
3153
3184
  _optionalChain([this, 'access', _62 => _62.forcedColorsQuery, 'access', _63 => _63.addEventListener, 'optionalCall', _64 => _64("change", this.forcedColorsChangeHandler)]);
3154
3185
  }
3155
- this.root = new class RootEntity extends _chunkTRT2TRCWjs.Entity {
3186
+ this.root = new class RootEntity extends _chunkFQ2565ITjs.Entity {
3156
3187
  isPointInside() {
3157
3188
  return false;
3158
3189
  }
@@ -3161,7 +3192,7 @@ var Scene = (_class7 = class _Scene {
3161
3192
  }
3162
3193
  }("root");
3163
3194
  this.root._scene = this;
3164
- this.overlayRoot = new class OverlayRoot extends _chunkTRT2TRCWjs.Entity {
3195
+ this.overlayRoot = new class OverlayRoot extends _chunkFQ2565ITjs.Entity {
3165
3196
  isPointInside() {
3166
3197
  return false;
3167
3198
  }
@@ -3600,7 +3631,7 @@ var Scene = (_class7 = class _Scene {
3600
3631
  }
3601
3632
  if (this.hoveredA11yElements.has(el)) {
3602
3633
  this.hoveredA11yElements.delete(el);
3603
- node.dispatchEvent(new (0, _chunkTRT2TRCWjs.VectoJSEvent)("pointerleave", node, void 0, false));
3634
+ node.dispatchEvent(new (0, _chunkFQ2565ITjs.VectoJSEvent)("pointerleave", node, void 0, false));
3604
3635
  }
3605
3636
  this.preserveFocusOnRemoval(el);
3606
3637
  el.remove();
@@ -3805,9 +3836,11 @@ var Scene = (_class7 = class _Scene {
3805
3836
  /**
3806
3837
  * Begin the `requestAnimationFrame` render loop.
3807
3838
  *
3808
- * Idempotent — calling `start()` on an already-running scene is a no-op.
3839
+ * Idempotent — calling `start()` on an already-running scene is a no-op, and so
3840
+ * is calling it on a destroyed one.
3809
3841
  */
3810
3842
  start() {
3843
+ if (this.destroyed) return;
3811
3844
  if (this.isRunning) return;
3812
3845
  if ((this.width === 0 || this.height === 0) && !this.hasWarnedZeroSize) {
3813
3846
  console.warn(
@@ -4220,20 +4253,20 @@ var Scene = (_class7 = class _Scene {
4220
4253
  el.style.background = "transparent";
4221
4254
  }
4222
4255
  el.addEventListener("click", (e) => {
4223
- node.dispatchEvent(new (0, _chunkTRT2TRCWjs.VectoJSEvent)("click", node, e));
4256
+ node.dispatchEvent(new (0, _chunkFQ2565ITjs.VectoJSEvent)("click", node, e));
4224
4257
  });
4225
4258
  el.addEventListener("dblclick", (e) => {
4226
- node.dispatchEvent(new (0, _chunkTRT2TRCWjs.VectoJSEvent)("dblclick", node, e));
4259
+ node.dispatchEvent(new (0, _chunkFQ2565ITjs.VectoJSEvent)("dblclick", node, e));
4227
4260
  });
4228
4261
  el.addEventListener("mouseenter", (e) => {
4229
4262
  if (this.debugA11y) el.style.backgroundColor = "rgba(56, 189, 248, 0.2)";
4230
4263
  this.hoveredA11yElements.add(el);
4231
- node.dispatchEvent(new (0, _chunkTRT2TRCWjs.VectoJSEvent)("hover", node, e, false));
4264
+ node.dispatchEvent(new (0, _chunkFQ2565ITjs.VectoJSEvent)("hover", node, e, false));
4232
4265
  });
4233
4266
  el.addEventListener("mouseleave", (e) => {
4234
4267
  if (this.debugA11y) el.style.backgroundColor = "rgba(56, 189, 248, 0.05)";
4235
4268
  this.hoveredA11yElements.delete(el);
4236
- node.dispatchEvent(new (0, _chunkTRT2TRCWjs.VectoJSEvent)("pointerleave", node, e, false));
4269
+ node.dispatchEvent(new (0, _chunkFQ2565ITjs.VectoJSEvent)("pointerleave", node, e, false));
4237
4270
  });
4238
4271
  const capEl = el;
4239
4272
  const releasePointer = (event) => {
@@ -4249,24 +4282,24 @@ var Scene = (_class7 = class _Scene {
4249
4282
  };
4250
4283
  el.addEventListener("pointerdown", (e) => {
4251
4284
  if (typeof capEl.setPointerCapture === "function") capEl.setPointerCapture(e.pointerId);
4252
- node.dispatchEvent(new (0, _chunkTRT2TRCWjs.VectoJSEvent)("pointerdown", node, e));
4285
+ node.dispatchEvent(new (0, _chunkFQ2565ITjs.VectoJSEvent)("pointerdown", node, e));
4253
4286
  });
4254
4287
  el.addEventListener("pointerup", (e) => {
4255
4288
  releasePointer(e);
4256
- node.dispatchEvent(new (0, _chunkTRT2TRCWjs.VectoJSEvent)("pointerup", node, e));
4289
+ node.dispatchEvent(new (0, _chunkFQ2565ITjs.VectoJSEvent)("pointerup", node, e));
4257
4290
  });
4258
4291
  el.addEventListener("pointercancel", (e) => {
4259
4292
  releasePointer(e);
4260
- node.dispatchEvent(new (0, _chunkTRT2TRCWjs.VectoJSEvent)("pointercancel", node, e));
4293
+ node.dispatchEvent(new (0, _chunkFQ2565ITjs.VectoJSEvent)("pointercancel", node, e));
4261
4294
  });
4262
4295
  el.addEventListener(
4263
4296
  "pointermove",
4264
- (e) => node.dispatchEvent(new (0, _chunkTRT2TRCWjs.VectoJSEvent)("pointermove", node, e))
4297
+ (e) => node.dispatchEvent(new (0, _chunkFQ2565ITjs.VectoJSEvent)("pointermove", node, e))
4265
4298
  );
4266
4299
  el.addEventListener(
4267
4300
  "wheel",
4268
4301
  (e) => {
4269
- node.dispatchEvent(new (0, _chunkTRT2TRCWjs.VectoJSEvent)("wheel", node, e));
4302
+ node.dispatchEvent(new (0, _chunkFQ2565ITjs.VectoJSEvent)("wheel", node, e));
4270
4303
  },
4271
4304
  { passive: false }
4272
4305
  );
@@ -4291,10 +4324,10 @@ var Scene = (_class7 = class _Scene {
4291
4324
  );
4292
4325
  emitInitialScroll = emitScroll;
4293
4326
  el.addEventListener("keydown", (e) => {
4294
- node.dispatchEvent(new (0, _chunkTRT2TRCWjs.VectoJSEvent)("keydown", node, e));
4327
+ node.dispatchEvent(new (0, _chunkFQ2565ITjs.VectoJSEvent)("keydown", node, e));
4295
4328
  });
4296
4329
  el.addEventListener("keyup", (e) => {
4297
- node.dispatchEvent(new (0, _chunkTRT2TRCWjs.VectoJSEvent)("keyup", node, e));
4330
+ node.dispatchEvent(new (0, _chunkFQ2565ITjs.VectoJSEvent)("keyup", node, e));
4298
4331
  });
4299
4332
  if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {
4300
4333
  const input = el;
@@ -4360,7 +4393,7 @@ var Scene = (_class7 = class _Scene {
4360
4393
  el.addEventListener("keydown", (e) => {
4361
4394
  if (e.key === "Enter" || e.key === " ") {
4362
4395
  e.preventDefault();
4363
- node.dispatchEvent(new (0, _chunkTRT2TRCWjs.VectoJSEvent)("click", node, e));
4396
+ node.dispatchEvent(new (0, _chunkFQ2565ITjs.VectoJSEvent)("click", node, e));
4364
4397
  }
4365
4398
  });
4366
4399
  }
@@ -4796,7 +4829,7 @@ var Scene = (_class7 = class _Scene {
4796
4829
  el.addEventListener(
4797
4830
  "wheel",
4798
4831
  (e2) => {
4799
- node.dispatchEvent(new (0, _chunkTRT2TRCWjs.VectoJSEvent)("wheel", node, e2));
4832
+ node.dispatchEvent(new (0, _chunkFQ2565ITjs.VectoJSEvent)("wheel", node, e2));
4800
4833
  },
4801
4834
  { passive: false }
4802
4835
  );
@@ -4976,9 +5009,27 @@ var Scene = (_class7 = class _Scene {
4976
5009
  if (el.dataset.vectoContentGrid !== signature) {
4977
5010
  const materializeStart = typeof performance !== "undefined" ? performance.now() : 0;
4978
5011
  this.clearContentGridState(node.id, el, false);
5012
+ if (el.firstChild !== null) {
5013
+ let scan = el.firstChild;
5014
+ let strippedSelection = false;
5015
+ while (scan !== null) {
5016
+ const next = scan.nextSibling;
5017
+ if (scan.nodeType === 3) {
5018
+ if (!strippedSelection) {
5019
+ strippedSelection = true;
5020
+ this.releaseContentSelectionForRebuild(el);
5021
+ }
5022
+ scan.remove();
5023
+ }
5024
+ scan = next;
5025
+ }
5026
+ }
4979
5027
  const projectionLines = _nullishCoalesce(projection.lines, () => ( []));
4980
5028
  const selectionLine = this.contentGridSelectionLine(el);
4981
5029
  let rebuiltSelectionLine = false;
5030
+ if (selectionLine !== null && (selectionLine < gridWindow.start || selectionLine >= gridWindow.end)) {
5031
+ rebuiltSelectionLine = true;
5032
+ }
4982
5033
  const existingLines = el.children;
4983
5034
  for (let lineIndex = gridWindow.start; lineIndex < gridWindow.end; lineIndex++) {
4984
5035
  const domIndex = lineIndex - gridWindow.start;
@@ -5075,9 +5126,6 @@ var Scene = (_class7 = class _Scene {
5075
5126
  }
5076
5127
  const windowLength = gridWindow.end - gridWindow.start;
5077
5128
  while (el.children.length > windowLength) {
5078
- if (selectionLine !== null && selectionLine >= gridWindow.end) {
5079
- rebuiltSelectionLine = true;
5080
- }
5081
5129
  _optionalChain([el, 'access', _139 => _139.lastElementChild, 'optionalAccess', _140 => _140.remove, 'call', _141 => _141()]);
5082
5130
  }
5083
5131
  if (rebuiltSelectionLine) this.releaseContentSelectionForRebuild(el);
@@ -5746,6 +5794,11 @@ var Scene = (_class7 = class _Scene {
5746
5794
  if (isMainRenderPath && !this.device && !this.webgpuDisabled && !this.initializingWebGPU && !this.deviceLost) {
5747
5795
  this.initializingWebGPU = true;
5748
5796
  this.initWebGPUContext(computeEntities).then((newDevice) => {
5797
+ if (this.destroyed) {
5798
+ newDevice.destroy();
5799
+ this.initializingWebGPU = false;
5800
+ return;
5801
+ }
5749
5802
  this.device = newDevice;
5750
5803
  this.initializingWebGPU = false;
5751
5804
  const format = navigator.gpu ? navigator.gpu.getPreferredCanvasFormat() : "rgba8unorm";
@@ -5871,7 +5924,7 @@ var Scene = (_class7 = class _Scene {
5871
5924
  let walkHadAnimation = false;
5872
5925
  let walkHadInteractive = false;
5873
5926
  const runUpdate = (node) => {
5874
- const overridesUpdate = node.update !== _chunkTRT2TRCWjs.Entity.prototype.update;
5927
+ const overridesUpdate = node.update !== _chunkFQ2565ITjs.Entity.prototype.update;
5875
5928
  let pending = node.hasPendingAnimations();
5876
5929
  if (pending || overridesUpdate) {
5877
5930
  node.update(dt, time);
@@ -5880,7 +5933,7 @@ var Scene = (_class7 = class _Scene {
5880
5933
  if (pending) walkHadAnimation = true;
5881
5934
  if (!walkHadInteractive && node.interactive) walkHadInteractive = true;
5882
5935
  if (this._devActive && this._devFrameCount % 120 === 0) {
5883
- if (overridesUpdate && node.hasPendingAnimations === _chunkTRT2TRCWjs.Entity.prototype.hasPendingAnimations) {
5936
+ if (overridesUpdate && node.hasPendingAnimations === _chunkFQ2565ITjs.Entity.prototype.hasPendingAnimations) {
5884
5937
  this._devWarn(
5885
5938
  `Entity "${node.id}" overrides update() but not hasPendingAnimations(). Custom motion in update() without overriding hasPendingAnimations() causes the idle throttle to drop the animation to ~2fps. Override hasPendingAnimations() to return true while motion is in flight.`
5886
5939
  );
@@ -6098,8 +6151,21 @@ var Scene = (_class7 = class _Scene {
6098
6151
  }
6099
6152
  /**
6100
6153
  * Manually resize the Scene's viewport.
6154
+ *
6155
+ * Rejects a negative or non-finite dimension, keeping the last known-good
6156
+ * size. `0` is accepted: a zero-size scene is tolerated and warned about at
6157
+ * {@link start} instead, and rejecting it here would contradict that.
6101
6158
  */
6102
6159
  resize(width, height) {
6160
+ if (!Number.isFinite(width) || !Number.isFinite(height) || width < 0 || height < 0) {
6161
+ if (!this.hasWarnedInvalidResize) {
6162
+ console.warn(
6163
+ `[VectoJS] Scene.resize() ignored invalid dimensions (width: ${width}, height: ${height}). Width and height must be finite and non-negative; the previous viewport size was kept.`
6164
+ );
6165
+ this.hasWarnedInvalidResize = true;
6166
+ }
6167
+ return;
6168
+ }
6103
6169
  this.width = width;
6104
6170
  this.height = height;
6105
6171
  this.contentFontEpoch++;
@@ -6405,22 +6471,22 @@ function defaultMeasurer() {
6405
6471
  sharedMeasurer ??= _layout.resolveGlyphMeasurer.call(void 0, "sans-serif");
6406
6472
  return sharedMeasurer;
6407
6473
  }
6408
- var TextEntity = (_class8 = class extends _chunkTRT2TRCWjs.Entity {
6474
+ var TextEntity = (_class8 = class extends _chunkFQ2565ITjs.Entity {
6409
6475
 
6410
6476
 
6411
6477
 
6412
6478
 
6413
- __init170() {this.nodes = []}
6479
+ __init173() {this.nodes = []}
6414
6480
 
6415
- __init171() {this.fillStyle = "#94a3b8"}
6416
- __init172() {this.strokeStyle = null}
6417
- __init173() {this.hoveredFillStyle = "#ffffff"}
6418
- __init174() {this.lineWidth = 1}
6419
- __init175() {this.isHovered = false}
6481
+ __init174() {this.fillStyle = "#94a3b8"}
6482
+ __init175() {this.strokeStyle = null}
6483
+ __init176() {this.hoveredFillStyle = "#ffffff"}
6484
+ __init177() {this.lineWidth = 1}
6485
+ __init178() {this.isHovered = false}
6420
6486
  /** Bumped by {@link applyLayout}; read by `Scene` to skip an unchanged sync. */
6421
- __init176() {this.contentEpoch = 0}
6487
+ __init179() {this.contentEpoch = 0}
6422
6488
  constructor(text, atlas, maxWidth, fontSize = 24) {
6423
- 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);;
6489
+ super();_class8.prototype.__init173.call(this);_class8.prototype.__init174.call(this);_class8.prototype.__init175.call(this);_class8.prototype.__init176.call(this);_class8.prototype.__init177.call(this);_class8.prototype.__init178.call(this);_class8.prototype.__init179.call(this);;
6424
6490
  this.text = text;
6425
6491
  this.atlas = atlas;
6426
6492
  this.fontSize = fontSize;
@@ -6537,17 +6603,17 @@ var TextEntity = (_class8 = class extends _chunkTRT2TRCWjs.Entity {
6537
6603
  }, _class8);
6538
6604
 
6539
6605
  // src/components/GridTextEntity.ts
6540
- var GridTextEntity = (_class9 = class extends _chunkTRT2TRCWjs.Entity {
6606
+ var GridTextEntity = (_class9 = class extends _chunkFQ2565ITjs.Entity {
6541
6607
 
6542
- __init177() {this.fillStyle = "#ffffff"}
6543
- __init178() {this.grid = []}
6608
+ __init180() {this.fillStyle = "#ffffff"}
6609
+ __init181() {this.grid = []}
6544
6610
  // Array of rows
6545
- __init179() {this.cols = 0}
6546
- __init180() {this.rows = 0}
6611
+ __init182() {this.cols = 0}
6612
+ __init183() {this.rows = 0}
6547
6613
 
6548
6614
 
6549
6615
  constructor(_atlas, fontSize = 10) {
6550
- super();_class9.prototype.__init177.call(this);_class9.prototype.__init178.call(this);_class9.prototype.__init179.call(this);_class9.prototype.__init180.call(this);;
6616
+ super();_class9.prototype.__init180.call(this);_class9.prototype.__init181.call(this);_class9.prototype.__init182.call(this);_class9.prototype.__init183.call(this);;
6551
6617
  this.fontSize = fontSize;
6552
6618
  this.charWidth = fontSize * 1;
6553
6619
  this.charHeight = fontSize * 1.1;
@@ -6631,7 +6697,7 @@ function distSqToSegment(px, py, x1, y1, x2, y2) {
6631
6697
  const ey = py - cy;
6632
6698
  return ex * ex + ey * ey;
6633
6699
  }
6634
- var SplineEntity = (_class10 = class extends _chunkTRT2TRCWjs.Entity {
6700
+ var SplineEntity = (_class10 = class extends _chunkFQ2565ITjs.Entity {
6635
6701
 
6636
6702
 
6637
6703
 
@@ -6639,25 +6705,57 @@ var SplineEntity = (_class10 = class extends _chunkTRT2TRCWjs.Entity {
6639
6705
 
6640
6706
 
6641
6707
 
6642
- __init181() {this.offscreen = null}
6643
- __init182() {this.baked = false}
6708
+ __init184() {this.offscreen = null}
6709
+ __init185() {this.baked = false}
6644
6710
  /** Logical (CSS-pixel) size of the baked bitmap — the blit destination size. */
6645
- __init183() {this.bakedWidth = 0}
6646
- __init184() {this.bakedHeight = 0}
6647
- /** Gradient strokes can't be baked to a solid-color bitmap; they render per-frame. */
6711
+ __init186() {this.bakedWidth = 0}
6712
+ __init187() {this.bakedHeight = 0}
6713
+ /**
6714
+ * Gradient strokes can't be baked to a solid-color bitmap; they render
6715
+ * per-frame. Derived from `_doc`, so it is NOT readonly — assigning a new
6716
+ * document has to recompute it (see the `doc` setter).
6717
+ */
6648
6718
 
6649
6719
  /** Lazily-flattened polylines (one Float32Array of [x,y,...] per segment) for hit-testing. */
6650
- __init185() {this.polylines = null}
6720
+ __init188() {this.polylines = null}
6721
+ /**
6722
+ * The spline document to render. Assigning a new document invalidates all
6723
+ * caches (baked canvas, flattened polylines, bounds).
6724
+ */
6725
+ get doc() {
6726
+ return this._doc;
6727
+ }
6728
+ set doc(value) {
6729
+ if (this._doc === value) return;
6730
+ this._doc = value;
6731
+ this.invalidateCache();
6732
+ this.bounds = this.computeBounds();
6733
+ this.width = this.bounds.width;
6734
+ this.height = this.bounds.height;
6735
+ this.containsGradient = this.computeContainsGradient();
6736
+ }
6737
+ /**
6738
+ * Stroke width in local units. Assigning a new value invalidates the baked
6739
+ * canvas and hit-test polylines so visual and hit geometry stay consistent.
6740
+ */
6741
+ get lineWidth() {
6742
+ return this._lineWidth;
6743
+ }
6744
+ set lineWidth(value) {
6745
+ if (this._lineWidth === value) return;
6746
+ this._lineWidth = value;
6747
+ this.invalidateCache();
6748
+ }
6651
6749
  /**
6652
6750
  * When `true`, the renderer draws a rounded-rect outline of the entity's
6653
6751
  * local bounds after painting the curves. Useful for drag feedback and
6654
6752
  * debugging hit areas. Defaults to `false`.
6655
6753
  */
6656
- __init186() {this.showBounds = false}
6754
+ __init189() {this.showBounds = false}
6657
6755
  constructor(doc, opts = {}) {
6658
- 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);;
6659
- this.doc = doc;
6660
- this.lineWidth = _nullishCoalesce(opts.lineWidth, () => ( 2));
6756
+ super();_class10.prototype.__init184.call(this);_class10.prototype.__init185.call(this);_class10.prototype.__init186.call(this);_class10.prototype.__init187.call(this);_class10.prototype.__init188.call(this);_class10.prototype.__init189.call(this);;
6757
+ this._doc = doc;
6758
+ this._lineWidth = _nullishCoalesce(opts.lineWidth, () => ( 2));
6661
6759
  this.cache = _nullishCoalesce(opts.cache, () => ( true));
6662
6760
  this.defaultColor = _nullishCoalesce(opts.defaultColor, () => ( "#e2e8f0"));
6663
6761
  this.hitMode = _nullishCoalesce(opts.hitTest, () => ( "curve"));
@@ -6665,21 +6763,35 @@ var SplineEntity = (_class10 = class extends _chunkTRT2TRCWjs.Entity {
6665
6763
  this.bounds = this.computeBounds();
6666
6764
  this.width = this.bounds.width;
6667
6765
  this.height = this.bounds.height;
6668
- const isGradient = (c) => c !== null && !Array.isArray(c);
6669
- this.containsGradient = (_nullishCoalesce(_optionalChain([this, 'access', _178 => _178.doc, 'access', _179 => _179.equations, 'optionalAccess', _180 => _180.some, 'call', _181 => _181((eq) => isGradient(eq.color_rgb))]), () => ( false))) || (_nullishCoalesce(_optionalChain([this, 'access', _182 => _182.doc, 'access', _183 => _183.paths, 'optionalAccess', _184 => _184.some, 'call', _185 => _185((p) => isGradient(p.color_rgb))]), () => ( false)));
6766
+ this.containsGradient = this.computeContainsGradient();
6670
6767
  this.interactive = true;
6671
6768
  }
6769
+ /**
6770
+ * Whether any stroke in the current document is a gradient. Shared by the
6771
+ * constructor and the `doc` setter so both stay in agreement — the flag picks
6772
+ * the render path, so a stale value renders a gradient as a flat color.
6773
+ */
6774
+ computeContainsGradient() {
6775
+ const isGradient = (c) => c !== null && !Array.isArray(c);
6776
+ return (_nullishCoalesce(_optionalChain([this, 'access', _178 => _178._doc, 'access', _179 => _179.equations, 'optionalAccess', _180 => _180.some, 'call', _181 => _181((eq) => isGradient(eq.color_rgb))]), () => ( false))) || (_nullishCoalesce(_optionalChain([this, 'access', _182 => _182._doc, 'access', _183 => _183.paths, 'optionalAccess', _184 => _184.some, 'call', _185 => _185((p) => isGradient(p.color_rgb))]), () => ( false)));
6777
+ }
6778
+ /** Clear cached baked canvas and hit-test polylines. */
6779
+ invalidateCache() {
6780
+ this.baked = false;
6781
+ this.offscreen = null;
6782
+ this.polylines = null;
6783
+ }
6672
6784
  computeBounds() {
6673
- if (this.doc.bounding_box) {
6674
- const [minX2, minY2, maxX2, maxY2] = this.doc.bounding_box;
6785
+ if (this._doc.bounding_box) {
6786
+ const [minX2, minY2, maxX2, maxY2] = this._doc.bounding_box;
6675
6787
  return { x: minX2, y: minY2, width: maxX2 - minX2, height: maxY2 - minY2 };
6676
6788
  }
6677
6789
  let minX = Infinity;
6678
6790
  let minY = Infinity;
6679
6791
  let maxX = -Infinity;
6680
6792
  let maxY = -Infinity;
6681
- if (this.doc.equations) {
6682
- for (const eq of this.doc.equations) {
6793
+ if (this._doc.equations) {
6794
+ for (const eq of this._doc.equations) {
6683
6795
  for (const seg of eq.data) {
6684
6796
  const b = polySegmentToBezier(seg);
6685
6797
  for (const [x, y] of [
@@ -6696,8 +6808,8 @@ var SplineEntity = (_class10 = class extends _chunkTRT2TRCWjs.Entity {
6696
6808
  }
6697
6809
  }
6698
6810
  }
6699
- if (this.doc.paths) {
6700
- for (const path of this.doc.paths) {
6811
+ if (this._doc.paths) {
6812
+ for (const path of this._doc.paths) {
6701
6813
  for (const pt of path.data) {
6702
6814
  if (pt.x < minX) minX = pt.x;
6703
6815
  if (pt.x > maxX) maxX = pt.x;
@@ -6757,15 +6869,15 @@ var SplineEntity = (_class10 = class extends _chunkTRT2TRCWjs.Entity {
6757
6869
  getPolylines() {
6758
6870
  if (this.polylines) return this.polylines;
6759
6871
  const out = [];
6760
- if (this.doc.equations) {
6761
- for (const eq of this.doc.equations) {
6872
+ if (this._doc.equations) {
6873
+ for (const eq of this._doc.equations) {
6762
6874
  for (const seg of eq.data) {
6763
6875
  out.push(flattenBezier(polySegmentToBezier(seg), HIT_SAMPLES));
6764
6876
  }
6765
6877
  }
6766
6878
  }
6767
- if (this.doc.paths) {
6768
- for (const path of this.doc.paths) {
6879
+ if (this._doc.paths) {
6880
+ for (const path of this._doc.paths) {
6769
6881
  const pts = new Float32Array(path.data.length * 2);
6770
6882
  for (let i = 0; i < path.data.length; i++) {
6771
6883
  pts[i * 2] = path.data[i].x;
@@ -6794,8 +6906,8 @@ var SplineEntity = (_class10 = class extends _chunkTRT2TRCWjs.Entity {
6794
6906
  );
6795
6907
  }
6796
6908
  strokeEquations(r) {
6797
- if (this.doc.equations) {
6798
- for (const eq of this.doc.equations) {
6909
+ if (this._doc.equations) {
6910
+ for (const eq of this._doc.equations) {
6799
6911
  const stroke = this.resolveColor(eq.color_rgb, r);
6800
6912
  r.beginPath();
6801
6913
  for (const seg of eq.data) {
@@ -6806,8 +6918,8 @@ var SplineEntity = (_class10 = class extends _chunkTRT2TRCWjs.Entity {
6806
6918
  r.stroke(stroke, this.lineWidth);
6807
6919
  }
6808
6920
  }
6809
- if (this.doc.paths) {
6810
- for (const path of this.doc.paths) {
6921
+ if (this._doc.paths) {
6922
+ for (const path of this._doc.paths) {
6811
6923
  const stroke = this.resolveColor(path.color_rgb, r);
6812
6924
  r.beginPath();
6813
6925
  if (path.data.length > 0) {
@@ -6846,8 +6958,8 @@ var SplineEntity = (_class10 = class extends _chunkTRT2TRCWjs.Entity {
6846
6958
  ctx.lineWidth = this.lineWidth;
6847
6959
  ctx.lineCap = "round";
6848
6960
  ctx.lineJoin = "round";
6849
- if (this.doc.equations) {
6850
- for (const eq of this.doc.equations) {
6961
+ if (this._doc.equations) {
6962
+ for (const eq of this._doc.equations) {
6851
6963
  ctx.strokeStyle = eq.color_rgb === null ? this.defaultColor : Array.isArray(eq.color_rgb) ? rgbToCss(eq.color_rgb) : this.defaultColor;
6852
6964
  ctx.beginPath();
6853
6965
  for (const seg of eq.data) {
@@ -6858,8 +6970,8 @@ var SplineEntity = (_class10 = class extends _chunkTRT2TRCWjs.Entity {
6858
6970
  ctx.stroke();
6859
6971
  }
6860
6972
  }
6861
- if (this.doc.paths) {
6862
- for (const path of this.doc.paths) {
6973
+ if (this._doc.paths) {
6974
+ for (const path of this._doc.paths) {
6863
6975
  ctx.strokeStyle = path.color_rgb === null ? this.defaultColor : Array.isArray(path.color_rgb) ? rgbToCss(path.color_rgb) : this.defaultColor;
6864
6976
  ctx.beginPath();
6865
6977
  if (path.data.length > 0) {
@@ -6906,7 +7018,7 @@ async function loadSpline(url) {
6906
7018
  }
6907
7019
 
6908
7020
  // src/components/Rect.ts
6909
- var Rect = class extends _chunkTRT2TRCWjs.Entity {
7021
+ var Rect = class extends _chunkFQ2565ITjs.Entity {
6910
7022
 
6911
7023
 
6912
7024
 
@@ -6921,7 +7033,14 @@ var Rect = class extends _chunkTRT2TRCWjs.Entity {
6921
7033
  this.radius = _nullishCoalesce(opts.radius, () => ( 0));
6922
7034
  }
6923
7035
  getBounds() {
6924
- return { x: 0, y: 0, width: this.width, height: this.height };
7036
+ const inflation = this.stroke !== null ? this.strokeWidth / 2 : 0;
7037
+ const origin = inflation > 0 ? -inflation : 0;
7038
+ return {
7039
+ x: origin,
7040
+ y: origin,
7041
+ width: this.width + inflation * 2,
7042
+ height: this.height + inflation * 2
7043
+ };
6925
7044
  }
6926
7045
  /**
6927
7046
  * Solid-fill, square-cornered, unstroked rectangles opt into the GPU
@@ -6955,7 +7074,7 @@ var Rect = class extends _chunkTRT2TRCWjs.Entity {
6955
7074
  };
6956
7075
 
6957
7076
  // src/components/Circle.ts
6958
- var Circle = class extends _chunkTRT2TRCWjs.Entity {
7077
+ var Circle = class extends _chunkFQ2565ITjs.Entity {
6959
7078
 
6960
7079
 
6961
7080
 
@@ -6983,11 +7102,15 @@ var Circle = class extends _chunkTRT2TRCWjs.Entity {
6983
7102
  this.a11yOffsetY = -this._radius;
6984
7103
  }
6985
7104
  getBounds() {
7105
+ const r = this._radius;
7106
+ const inflation = this.stroke !== null ? this.strokeWidth / 2 : 0;
7107
+ const fullRadius = r + inflation;
7108
+ const origin = fullRadius > 0 ? -fullRadius : 0;
6986
7109
  return {
6987
- x: -this._radius,
6988
- y: -this._radius,
6989
- width: this._radius * 2,
6990
- height: this._radius * 2
7110
+ x: origin,
7111
+ y: origin,
7112
+ width: fullRadius * 2,
7113
+ height: fullRadius * 2
6991
7114
  };
6992
7115
  }
6993
7116
  /**
@@ -7015,7 +7138,7 @@ var Circle = class extends _chunkTRT2TRCWjs.Entity {
7015
7138
  };
7016
7139
 
7017
7140
  // src/components/Group.ts
7018
- var Group = class extends _chunkTRT2TRCWjs.Entity {
7141
+ var Group = class extends _chunkFQ2565ITjs.Entity {
7019
7142
  constructor(...children) {
7020
7143
  super();
7021
7144
  if (children.length > 0) this.add(...children);
@@ -7034,21 +7157,21 @@ var _math = require('@vectojs/math'); _createStarExport(_math);
7034
7157
 
7035
7158
 
7036
7159
  // src/tree/DOMPortalEntity.ts
7037
- var DOMPortalEntity = (_class11 = class extends _chunkTRT2TRCWjs.Entity {
7160
+ var DOMPortalEntity = (_class11 = class extends _chunkFQ2565ITjs.Entity {
7038
7161
 
7039
- __init187() {this.isDOMPortal = true}
7040
- __init188() {this.domListeners = []}
7041
- __init189() {this.resizeObserver = null}
7042
- __init190() {this.domBound = false}
7043
- __init191() {this.cachedWidth = 100}
7044
- __init192() {this.cachedHeight = 100}
7045
- __init193() {this.lastWidth = ""}
7046
- __init194() {this.lastHeight = ""}
7047
- __init195() {this.lastTransform = ""}
7048
- __init196() {this.lastZIndex = ""}
7049
- __init197() {this.lastOpacity = ""}
7162
+ __init190() {this.isDOMPortal = true}
7163
+ __init191() {this.domListeners = []}
7164
+ __init192() {this.resizeObserver = null}
7165
+ __init193() {this.domBound = false}
7166
+ __init194() {this.cachedWidth = 100}
7167
+ __init195() {this.cachedHeight = 100}
7168
+ __init196() {this.lastWidth = ""}
7169
+ __init197() {this.lastHeight = ""}
7170
+ __init198() {this.lastTransform = ""}
7171
+ __init199() {this.lastZIndex = ""}
7172
+ __init200() {this.lastOpacity = ""}
7050
7173
  constructor(domElement, width, height, id) {
7051
- 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);;
7174
+ super(id);_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);_class11.prototype.__init198.call(this);_class11.prototype.__init199.call(this);_class11.prototype.__init200.call(this);;
7052
7175
  this.domElement = domElement;
7053
7176
  this.width = _nullishCoalesce(width, () => ( 0));
7054
7177
  this.height = _nullishCoalesce(height, () => ( 0));
@@ -7090,7 +7213,7 @@ var DOMPortalEntity = (_class11 = class extends _chunkTRT2TRCWjs.Entity {
7090
7213
  ];
7091
7214
  for (const type of events) {
7092
7215
  const handler = (e) => {
7093
- this.dispatchEvent(new (0, _chunkTRT2TRCWjs.VectoJSEvent)(type, this, e));
7216
+ this.dispatchEvent(new (0, _chunkFQ2565ITjs.VectoJSEvent)(type, this, e));
7094
7217
  };
7095
7218
  this.domElement.addEventListener(type, handler);
7096
7219
  this.domListeners.push({ type, handler, capture: false });
@@ -7101,7 +7224,7 @@ var DOMPortalEntity = (_class11 = class extends _chunkTRT2TRCWjs.Entity {
7101
7224
  ];
7102
7225
  for (const { native, vecto } of hoverEvents) {
7103
7226
  const handler = (e) => {
7104
- this.dispatchEvent(new (0, _chunkTRT2TRCWjs.VectoJSEvent)(vecto, this, e, false));
7227
+ this.dispatchEvent(new (0, _chunkFQ2565ITjs.VectoJSEvent)(vecto, this, e, false));
7105
7228
  };
7106
7229
  this.domElement.addEventListener(native, handler);
7107
7230
  this.domListeners.push({ type: native, handler, capture: false });
@@ -7109,7 +7232,7 @@ var DOMPortalEntity = (_class11 = class extends _chunkTRT2TRCWjs.Entity {
7109
7232
  const focusEvents = ["focus", "blur"];
7110
7233
  for (const type of focusEvents) {
7111
7234
  const handler = (e) => {
7112
- this.dispatchEvent(new (0, _chunkTRT2TRCWjs.VectoJSEvent)(type, this, e, true));
7235
+ this.dispatchEvent(new (0, _chunkFQ2565ITjs.VectoJSEvent)(type, this, e, true));
7113
7236
  };
7114
7237
  this.domElement.addEventListener(type, handler, true);
7115
7238
  this.domListeners.push({ type, handler, capture: true });
@@ -7207,4 +7330,4 @@ Scene.registerWebGPUParticleSystemManager(_chunkM73XB4ZKjs.WebGPUParticleSystemM
7207
7330
 
7208
7331
 
7209
7332
 
7210
- 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;
7333
+ exports.CanvasRenderer = _chunkM73XB4ZKjs.CanvasRenderer; exports.Circle = Circle; exports.ComputeParticleEntity = ComputeParticleEntity; exports.DEFAULT_CONTENT_SEMANTIC_BUDGET = DEFAULT_CONTENT_SEMANTIC_BUDGET; exports.DOMPortalEntity = DOMPortalEntity; exports.Entity = _chunkFQ2565ITjs.Entity; exports.GlyphRasterAtlas = _chunkM73XB4ZKjs.GlyphRasterAtlas; exports.GridTextEntity = GridTextEntity; exports.Group = Group; exports.MSDFTextEntity = _chunkFQ2565ITjs.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 = _chunkFQ2565ITjs.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 = _chunkFQ2565ITjs.VectoJSEvent; exports.WebGPUParticleSystemManager = _chunkM73XB4ZKjs.WebGPUParticleSystemManager; exports.beginVectoUserTiming = beginVectoUserTiming; exports.contentLineInHint = _chunkFQ2565ITjs.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;