@vectojs/core 1.25.0 → 1.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -94,7 +94,7 @@ var ComputeParticleEntity = (_class = class extends _chunkZ3HFY75Rjs.Entity {
94
94
  this.particleData[idx + PARTICLE_OFFSET_LIFE] = -1;
95
95
  }
96
96
  this.needsInit = true;
97
- _optionalChain([this, 'access', _ => _.scene, 'optionalAccess', _2 => _2.markDirty, 'call', _3 => _3()]);
97
+ _optionalChain([this, 'access', _2 => _2.scene, 'optionalAccess', _3 => _3.markDirty, 'call', _4 => _4()]);
98
98
  }
99
99
  /**
100
100
  * Sets the origins (ox, oy) for a subset or all particles.
@@ -120,7 +120,7 @@ var ComputeParticleEntity = (_class = class extends _chunkZ3HFY75Rjs.Entity {
120
120
  }
121
121
  }
122
122
  this.needsInit = true;
123
- _optionalChain([this, 'access', _4 => _4.scene, 'optionalAccess', _5 => _5.markDirty, 'call', _6 => _6()]);
123
+ _optionalChain([this, 'access', _5 => _5.scene, 'optionalAccess', _6 => _6.markDirty, 'call', _7 => _7()]);
124
124
  }
125
125
  /**
126
126
  * Sets the current positions (x, y) for a subset or all particles.
@@ -136,7 +136,7 @@ var ComputeParticleEntity = (_class = class extends _chunkZ3HFY75Rjs.Entity {
136
136
  this.particleData[idx + PARTICLE_OFFSET_POSITION_Y] = positions[ptIdx + 1];
137
137
  }
138
138
  this.needsInit = true;
139
- _optionalChain([this, 'access', _7 => _7.scene, 'optionalAccess', _8 => _8.markDirty, 'call', _9 => _9()]);
139
+ _optionalChain([this, 'access', _8 => _8.scene, 'optionalAccess', _9 => _9.markDirty, 'call', _10 => _10()]);
140
140
  }
141
141
  /**
142
142
  * Sets the current velocities (vx, vy) for a subset or all particles.
@@ -152,7 +152,7 @@ var ComputeParticleEntity = (_class = class extends _chunkZ3HFY75Rjs.Entity {
152
152
  this.particleData[idx + PARTICLE_OFFSET_VELOCITY_Y] = velocities[ptIdx + 1];
153
153
  }
154
154
  this.needsInit = true;
155
- _optionalChain([this, 'access', _10 => _10.scene, 'optionalAccess', _11 => _11.markDirty, 'call', _12 => _12()]);
155
+ _optionalChain([this, 'access', _11 => _11.scene, 'optionalAccess', _12 => _12.markDirty, 'call', _13 => _13()]);
156
156
  }
157
157
  /**
158
158
  * Triggers an explosion force center.
@@ -1259,7 +1259,7 @@ var VECTO_USER_TIMING = {
1259
1259
  var nextSpanId = 0;
1260
1260
  function beginVectoUserTiming(name) {
1261
1261
  const candidate = globalThis.performance;
1262
- if (typeof _optionalChain([candidate, 'optionalAccess', _13 => _13.mark]) !== "function" || typeof candidate.measure !== "function") {
1262
+ if (typeof _optionalChain([candidate, 'optionalAccess', _14 => _14.mark]) !== "function" || typeof candidate.measure !== "function") {
1263
1263
  return null;
1264
1264
  }
1265
1265
  const id = nextSpanId++;
@@ -1286,15 +1286,15 @@ function endVectoUserTiming(span) {
1286
1286
  } catch (e8) {
1287
1287
  } finally {
1288
1288
  try {
1289
- _optionalChain([timing, 'access', _14 => _14.clearMarks, 'optionalCall', _15 => _15(span.startMark)]);
1290
- _optionalChain([timing, 'access', _16 => _16.clearMarks, 'optionalCall', _17 => _17(span.endMark)]);
1289
+ _optionalChain([timing, 'access', _15 => _15.clearMarks, 'optionalCall', _16 => _16(span.startMark)]);
1290
+ _optionalChain([timing, 'access', _17 => _17.clearMarks, 'optionalCall', _18 => _18(span.endMark)]);
1291
1291
  } catch (e9) {
1292
1292
  }
1293
1293
  }
1294
1294
  }
1295
1295
  function measureVectoUserTiming(name, durationMs) {
1296
1296
  const candidate = globalThis.performance;
1297
- if (typeof _optionalChain([candidate, 'optionalAccess', _18 => _18.now]) !== "function" || typeof candidate.mark !== "function" || typeof candidate.measure !== "function") {
1297
+ if (typeof _optionalChain([candidate, 'optionalAccess', _19 => _19.now]) !== "function" || typeof candidate.mark !== "function" || typeof candidate.measure !== "function") {
1298
1298
  return;
1299
1299
  }
1300
1300
  const id = nextSpanId++;
@@ -1309,8 +1309,8 @@ function measureVectoUserTiming(name, durationMs) {
1309
1309
  } catch (e10) {
1310
1310
  } finally {
1311
1311
  try {
1312
- _optionalChain([candidate, 'access', _19 => _19.clearMarks, 'optionalCall', _20 => _20(startMark)]);
1313
- _optionalChain([candidate, 'access', _21 => _21.clearMarks, 'optionalCall', _22 => _22(endMark)]);
1312
+ _optionalChain([candidate, 'access', _20 => _20.clearMarks, 'optionalCall', _21 => _21(startMark)]);
1313
+ _optionalChain([candidate, 'access', _22 => _22.clearMarks, 'optionalCall', _23 => _23(endMark)]);
1314
1314
  } catch (e11) {
1315
1315
  }
1316
1316
  }
@@ -1371,6 +1371,57 @@ function rebaseChildBox(parent, parentOriginX, parentOriginY, child, childOrigin
1371
1371
  REBASED_BOX.matrix = `matrix(${a}, ${b}, ${c}, ${d}, 0, 0)`;
1372
1372
  return REBASED_BOX;
1373
1373
  }
1374
+ var SCENE_OPTION_KEYS = [
1375
+ "a11ySyncInterval",
1376
+ "autoThrottle",
1377
+ "contentProjection",
1378
+ "contentProjectionMargin",
1379
+ "debugA11y",
1380
+ "disableWindowResize",
1381
+ "maxDPR",
1382
+ "maxFPS",
1383
+ "particleBackend",
1384
+ "pointBackend",
1385
+ "readingDirection",
1386
+ "renderer",
1387
+ "renderMode",
1388
+ "respectReducedMotion",
1389
+ "userTiming"
1390
+ ];
1391
+ var SCENE_FIELD_NOT_OPTION = {
1392
+ devMode: "set the static `Scene.devMode = true` before constructing"
1393
+ };
1394
+ function editDistance(a, b) {
1395
+ const m = a.length;
1396
+ const n = b.length;
1397
+ if (m === 0 || n === 0) return Math.max(m, n);
1398
+ let prev = Array.from({ length: n + 1 }, (_, i) => i);
1399
+ const curr = Array.from({ length: n + 1 });
1400
+ for (let i = 1; i <= m; i++) {
1401
+ curr[0] = i;
1402
+ for (let j = 1; j <= n; j++) {
1403
+ const cost = a.charCodeAt(i - 1) === b.charCodeAt(j - 1) ? 0 : 1;
1404
+ curr[j] = Math.min(curr[j - 1] + 1, prev[j] + 1, prev[j - 1] + cost);
1405
+ }
1406
+ prev = curr.slice();
1407
+ }
1408
+ return prev[n];
1409
+ }
1410
+ function closestOptionKey(key) {
1411
+ const lower = key.toLowerCase();
1412
+ let best;
1413
+ let bestScore = Infinity;
1414
+ for (const candidate of SCENE_OPTION_KEYS) {
1415
+ if (candidate.toLowerCase() === lower) return candidate;
1416
+ const d = editDistance(lower, candidate.toLowerCase());
1417
+ if (d < bestScore) {
1418
+ bestScore = d;
1419
+ best = candidate;
1420
+ }
1421
+ }
1422
+ const limit = Math.max(2, Math.floor(key.length / 3));
1423
+ return bestScore <= limit ? best : void 0;
1424
+ }
1374
1425
  var REDUCED_MOTION_FPS = 30;
1375
1426
  function parseInlinePx(value) {
1376
1427
  if (!value || !value.endsWith("px")) return null;
@@ -1456,9 +1507,9 @@ function parseCssMatrix(transform) {
1456
1507
  }
1457
1508
  function clientToGridLocal(contentEl, canvas, clientX, clientY) {
1458
1509
  const line = contentEl.querySelector("[data-vecto-grid-line]");
1459
- const originMarker = _optionalChain([line, 'optionalAccess', _23 => _23.querySelector, 'call', _24 => _24('[data-vecto-grid-basis="origin"]')]);
1460
- const xMarker = _optionalChain([line, 'optionalAccess', _25 => _25.querySelector, 'call', _26 => _26('[data-vecto-grid-basis="x"]')]);
1461
- const yMarker = _optionalChain([line, 'optionalAccess', _27 => _27.querySelector, 'call', _28 => _28('[data-vecto-grid-basis="y"]')]);
1510
+ const originMarker = _optionalChain([line, 'optionalAccess', _24 => _24.querySelector, 'call', _25 => _25('[data-vecto-grid-basis="origin"]')]);
1511
+ const xMarker = _optionalChain([line, 'optionalAccess', _26 => _26.querySelector, 'call', _27 => _27('[data-vecto-grid-basis="x"]')]);
1512
+ const yMarker = _optionalChain([line, 'optionalAccess', _28 => _28.querySelector, 'call', _29 => _29('[data-vecto-grid-basis="y"]')]);
1462
1513
  if (line && originMarker && xMarker && yMarker) {
1463
1514
  const origin = originMarker.getBoundingClientRect();
1464
1515
  const xPoint = xMarker.getBoundingClientRect();
@@ -1542,7 +1593,7 @@ function nearestTextPositionInLine(line, x, y) {
1542
1593
  nearest = { position: { node, offset }, distance: candidate.distance };
1543
1594
  }
1544
1595
  }
1545
- return _nullishCoalesce(_optionalChain([nearest, 'optionalAccess', _29 => _29.position]), () => ( null));
1596
+ return _nullishCoalesce(_optionalChain([nearest, 'optionalAccess', _30 => _30.position]), () => ( null));
1546
1597
  }
1547
1598
  function nearestTextPositionInProjection(contentEl, canvas, x, y, eventTarget) {
1548
1599
  if (contentEl.dataset.vectoContentGrid !== void 0) {
@@ -1556,7 +1607,7 @@ function nearestTextPositionInProjection(contentEl, canvas, x, y, eventTarget) {
1556
1607
  }
1557
1608
  targetLine = targetLine.parentElement;
1558
1609
  }
1559
- if (_optionalChain([targetLine, 'optionalAccess', _30 => _30.parentElement]) === contentEl) {
1610
+ if (_optionalChain([targetLine, 'optionalAccess', _31 => _31.parentElement]) === contentEl) {
1560
1611
  return nearestTextPositionInLine(targetLine, x, y);
1561
1612
  }
1562
1613
  let bestLine = null;
@@ -1834,7 +1885,7 @@ var Scene = (_class7 = class _Scene {
1834
1885
  __init50() {this.forcedColorsChangeHandler = null}
1835
1886
  /** True when the OS asks for reduced motion and we respect it. Read by the animation drivers. */
1836
1887
  get prefersReducedMotion() {
1837
- return this.respectReducedMotion && !!_optionalChain([this, 'access', _31 => _31.reducedMotionQuery, 'optionalAccess', _32 => _32.matches]);
1888
+ return this.respectReducedMotion && !!_optionalChain([this, 'access', _32 => _32.reducedMotionQuery, 'optionalAccess', _33 => _33.matches]);
1838
1889
  }
1839
1890
  /**
1840
1891
  * True when the OS is in a forced-colors mode (Windows High Contrast, and the
@@ -1845,7 +1896,7 @@ var Scene = (_class7 = class _Scene {
1845
1896
  * when the setting toggles.
1846
1897
  */
1847
1898
  get forcedColors() {
1848
- return !!_optionalChain([this, 'access', _33 => _33.forcedColorsQuery, 'optionalAccess', _34 => _34.matches]);
1899
+ return !!_optionalChain([this, 'access', _34 => _34.forcedColorsQuery, 'optionalAccess', _35 => _35.matches]);
1849
1900
  }
1850
1901
  /**
1851
1902
  * Throttle interval (ms) for the a11y/automation shadow sync. `0` = every
@@ -2281,7 +2332,7 @@ var Scene = (_class7 = class _Scene {
2281
2332
  const idx = cell[k];
2282
2333
  if (x < minx[idx] || x > maxx[idx] || y < miny[idx] || y > maxy[idx]) continue;
2283
2334
  const entity = this._hitSlotEntity[idx];
2284
- if (_optionalChain([entity, 'optionalAccess', _35 => _35.isPointInside, 'call', _36 => _36(x, y)]) && this.isHitEligible(entity, x, y)) {
2335
+ if (_optionalChain([entity, 'optionalAccess', _36 => _36.isPointInside, 'call', _37 => _37(x, y)]) && this.isHitEligible(entity, x, y)) {
2285
2336
  bestIndex = idx;
2286
2337
  bestEntity = entity;
2287
2338
  break;
@@ -2752,7 +2803,7 @@ var Scene = (_class7 = class _Scene {
2752
2803
  * active.
2753
2804
  */
2754
2805
  get webglDrawStats() {
2755
- return _nullishCoalesce(_optionalChain([this, 'access', _37 => _37.pointRenderer, 'optionalAccess', _38 => _38.stats, 'optionalCall', _39 => _39()]), () => ( null));
2806
+ return _nullishCoalesce(_optionalChain([this, 'access', _38 => _38.pointRenderer, 'optionalAccess', _39 => _39.stats, 'optionalCall', _40 => _40()]), () => ( null));
2756
2807
  }
2757
2808
  /**
2758
2809
  * Whether a WebGPU device is currently live for particle compute.
@@ -2794,8 +2845,8 @@ var Scene = (_class7 = class _Scene {
2794
2845
  static _devModeDetected() {
2795
2846
  if (_Scene.devMode) return true;
2796
2847
  const gp = typeof globalThis !== "undefined" ? globalThis : void 0;
2797
- if (_optionalChain([gp, 'optionalAccess', _40 => _40.__DEV__])) return true;
2798
- if (_optionalChain([gp, 'optionalAccess', _41 => _41.process, 'optionalAccess', _42 => _42.env, 'optionalAccess', _43 => _43.NODE_ENV]) === "development") return true;
2848
+ if (_optionalChain([gp, 'optionalAccess', _41 => _41.__DEV__])) return true;
2849
+ if (_optionalChain([gp, 'optionalAccess', _42 => _42.process, 'optionalAccess', _43 => _43.env, 'optionalAccess', _44 => _44.NODE_ENV]) === "development") return true;
2799
2850
  return false;
2800
2851
  }
2801
2852
 
@@ -2804,6 +2855,37 @@ var Scene = (_class7 = class _Scene {
2804
2855
  if (!this._devActive) return;
2805
2856
  console.warn(`[vectojs/dev] ${message}`);
2806
2857
  }
2858
+ /**
2859
+ * Warn (dev mode only) about `SceneOptions` keys this version does not read.
2860
+ *
2861
+ * A structural type makes an unrecognized key a silent no-op, and TypeScript
2862
+ * only rejects one when the object literal sits inline at the call site — not
2863
+ * when options are built dynamically, and never in plain JS. Since the
2864
+ * failure mode is "the option appears to work", a runtime check is the only
2865
+ * thing that surfaces it.
2866
+ *
2867
+ * Dev-mode only on purpose: the loop is O(keys × known keys) with an edit
2868
+ * distance per pair, which is nothing at construction but is still pure
2869
+ * overhead in production, where the value has already been shipped.
2870
+ */
2871
+ _warnUnknownOptions(options) {
2872
+ if (!this._devActive) return;
2873
+ const known = new Set(SCENE_OPTION_KEYS);
2874
+ for (const key of Object.keys(options)) {
2875
+ if (known.has(key)) continue;
2876
+ const fieldHint = SCENE_FIELD_NOT_OPTION[key];
2877
+ if (fieldHint) {
2878
+ this._devWarn(
2879
+ `SceneOptions: \`${key}\` is not a constructor option \u2014 ${fieldHint}. Passing it here has no effect.`
2880
+ );
2881
+ continue;
2882
+ }
2883
+ const suggestion = closestOptionKey(key);
2884
+ this._devWarn(
2885
+ `SceneOptions: unknown option \`${key}\` is ignored.` + (suggestion ? ` Did you mean \`${suggestion}\`?` : "")
2886
+ );
2887
+ }
2888
+ }
2807
2889
  /** @internal Periodic dev checks — called once per frame in dev mode. */
2808
2890
  _devRunChecks() {
2809
2891
  this._devFrameCount++;
@@ -2826,9 +2908,9 @@ var Scene = (_class7 = class _Scene {
2826
2908
  let checked = 0;
2827
2909
  const walkProjections = (node) => {
2828
2910
  if (checked > 10) return;
2829
- const proj = _optionalChain([node, 'access', _44 => _44.getContentProjection, 'optionalCall', _45 => _45()]);
2830
- if (_optionalChain([proj, 'optionalAccess', _46 => _46.text]) && proj.selectable !== false) {
2831
- const el = _optionalChain([this, 'access', _47 => _47.contentElements, 'optionalAccess', _48 => _48.get, 'call', _49 => _49(node.id)]);
2911
+ const proj = _optionalChain([node, 'access', _45 => _45.getContentProjection, 'optionalCall', _46 => _46()]);
2912
+ if (_optionalChain([proj, 'optionalAccess', _47 => _47.text]) && proj.selectable !== false) {
2913
+ const el = _optionalChain([this, 'access', _48 => _48.contentElements, 'optionalAccess', _49 => _49.get, 'call', _50 => _50(node.id)]);
2832
2914
  if (el) {
2833
2915
  const projectedText = el.textContent || "";
2834
2916
  if (projectedText !== "" && projectedText !== proj.text) {
@@ -2849,8 +2931,8 @@ var Scene = (_class7 = class _Scene {
2849
2931
  this.disableWindowResize = _nullishCoalesce(options.disableWindowResize, () => ( false));
2850
2932
  this.maxDPR = options.maxDPR;
2851
2933
  if (this.disableWindowResize) {
2852
- const styleWidth = parseInlinePx(_optionalChain([canvas, 'access', _50 => _50.style, 'optionalAccess', _51 => _51.width]));
2853
- const styleHeight = parseInlinePx(_optionalChain([canvas, 'access', _52 => _52.style, 'optionalAccess', _53 => _53.height]));
2934
+ const styleWidth = parseInlinePx(_optionalChain([canvas, 'access', _51 => _51.style, 'optionalAccess', _52 => _52.width]));
2935
+ const styleHeight = parseInlinePx(_optionalChain([canvas, 'access', _53 => _53.style, 'optionalAccess', _54 => _54.height]));
2854
2936
  this.width = _nullishCoalesce(styleWidth, () => ( (canvas.width || canvas.clientWidth || 0)));
2855
2937
  this.height = _nullishCoalesce(styleHeight, () => ( (canvas.height || canvas.clientHeight || 0)));
2856
2938
  } else {
@@ -2858,7 +2940,7 @@ var Scene = (_class7 = class _Scene {
2858
2940
  this.height = typeof window !== "undefined" ? window.innerHeight : canvas.clientHeight || canvas.height || 600;
2859
2941
  }
2860
2942
  const globalProcess = typeof globalThis !== "undefined" ? globalThis.process : void 0;
2861
- const isTest = globalProcess && (_optionalChain([globalProcess, 'access', _54 => _54.env, 'optionalAccess', _55 => _55.NODE_ENV]) === "test" || _optionalChain([globalProcess, 'access', _56 => _56.env, 'optionalAccess', _57 => _57.VITEST]) === "true");
2943
+ const isTest = globalProcess && (_optionalChain([globalProcess, 'access', _55 => _55.env, 'optionalAccess', _56 => _56.NODE_ENV]) === "test" || _optionalChain([globalProcess, 'access', _57 => _57.env, 'optionalAccess', _58 => _58.VITEST]) === "true");
2862
2944
  this.maxFPS = _nullishCoalesce(options.maxFPS, () => ( (isTest ? 0 : 60)));
2863
2945
  this.respectReducedMotion = _nullishCoalesce(options.respectReducedMotion, () => ( true));
2864
2946
  this.autoThrottle = _nullishCoalesce(options.autoThrottle, () => ( true));
@@ -2868,12 +2950,14 @@ var Scene = (_class7 = class _Scene {
2868
2950
  this.contentProjectionEnabled = _nullishCoalesce(options.contentProjection, () => ( true));
2869
2951
  this.contentProjectionMargin = options.contentProjectionMargin;
2870
2952
  this.readingDirection = _nullishCoalesce(options.readingDirection, () => ( "ltr"));
2953
+ this.renderMode = _nullishCoalesce(options.renderMode, () => ( "always"));
2871
2954
  this._devActive = _Scene._devModeDetected();
2955
+ this._warnUnknownOptions(options);
2872
2956
  this.reducedMotionQuery = typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia("(prefers-reduced-motion: reduce)") : null;
2873
2957
  if (typeof window !== "undefined" && typeof window.matchMedia === "function") {
2874
2958
  this.forcedColorsQuery = window.matchMedia("(forced-colors: active)");
2875
2959
  this.forcedColorsChangeHandler = () => this.markDirty();
2876
- _optionalChain([this, 'access', _58 => _58.forcedColorsQuery, 'access', _59 => _59.addEventListener, 'optionalCall', _60 => _60("change", this.forcedColorsChangeHandler)]);
2960
+ _optionalChain([this, 'access', _59 => _59.forcedColorsQuery, 'access', _60 => _60.addEventListener, 'optionalCall', _61 => _61("change", this.forcedColorsChangeHandler)]);
2877
2961
  }
2878
2962
  this.root = new class RootEntity extends _chunkZ3HFY75Rjs.Entity {
2879
2963
  isPointInside() {
@@ -2901,9 +2985,9 @@ var Scene = (_class7 = class _Scene {
2901
2985
  this.maxDPR
2902
2986
  );
2903
2987
  }
2904
- _optionalChain([this, 'access', _61 => _61.renderer, 'access', _62 => _62.onContextRestored, 'optionalCall', _63 => _63(() => {
2988
+ _optionalChain([this, 'access', _62 => _62.renderer, 'access', _63 => _63.onContextRestored, 'optionalCall', _64 => _64(() => {
2905
2989
  this.markDirty();
2906
- if (_optionalChain([this, 'access', _64 => _64.renderer, 'access', _65 => _65.isContextLost, 'optionalCall', _66 => _66()]) !== true) this.render(this.renderer);
2990
+ if (_optionalChain([this, 'access', _65 => _65.renderer, 'access', _66 => _66.isContextLost, 'optionalCall', _67 => _67()]) !== true) this.render(this.renderer);
2907
2991
  })]);
2908
2992
  if (typeof document !== "undefined") {
2909
2993
  this.a11yRoot = document.createElement("div");
@@ -3073,7 +3157,7 @@ var Scene = (_class7 = class _Scene {
3073
3157
  watchDevicePixelRatio() {
3074
3158
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
3075
3159
  if (this.dprMediaQuery && this.dprChangeHandler) {
3076
- _optionalChain([this, 'access', _67 => _67.dprMediaQuery, 'access', _68 => _68.removeEventListener, 'optionalCall', _69 => _69("change", this.dprChangeHandler)]);
3160
+ _optionalChain([this, 'access', _68 => _68.dprMediaQuery, 'access', _69 => _69.removeEventListener, 'optionalCall', _70 => _70("change", this.dprChangeHandler)]);
3077
3161
  }
3078
3162
  const dpr = window.devicePixelRatio || 1;
3079
3163
  const query = window.matchMedia(`(resolution: ${dpr}dppx)`);
@@ -3081,7 +3165,7 @@ var Scene = (_class7 = class _Scene {
3081
3165
  this.resize(this.width, this.height);
3082
3166
  this.watchDevicePixelRatio();
3083
3167
  };
3084
- _optionalChain([query, 'access', _70 => _70.addEventListener, 'optionalCall', _71 => _71("change", handler)]);
3168
+ _optionalChain([query, 'access', _71 => _71.addEventListener, 'optionalCall', _72 => _72("change", handler)]);
3085
3169
  this.dprMediaQuery = query;
3086
3170
  this.dprChangeHandler = handler;
3087
3171
  }
@@ -3101,7 +3185,7 @@ var Scene = (_class7 = class _Scene {
3101
3185
  if (typeof gl.addEventListener !== "function") return;
3102
3186
  this.glContextLostHandler = (e) => {
3103
3187
  e.preventDefault();
3104
- _optionalChain([this, 'access', _72 => _72.pointRenderer, 'optionalAccess', _73 => _73.destroy, 'call', _74 => _74()]);
3188
+ _optionalChain([this, 'access', _73 => _73.pointRenderer, 'optionalAccess', _74 => _74.destroy, 'call', _75 => _75()]);
3105
3189
  this.pointRenderer = null;
3106
3190
  };
3107
3191
  this.glContextRestoredHandler = () => {
@@ -3131,26 +3215,26 @@ var Scene = (_class7 = class _Scene {
3131
3215
  * to the live DOM selection.
3132
3216
  */
3133
3217
  contentGridSelectionLine(el) {
3134
- const candidates = [_optionalChain([this, 'access', _75 => _75.contentSelectionAnchor, 'optionalAccess', _76 => _76.node])];
3218
+ const candidates = [_optionalChain([this, 'access', _76 => _76.contentSelectionAnchor, 'optionalAccess', _77 => _77.node])];
3135
3219
  if (typeof window !== "undefined" && typeof window.getSelection === "function") {
3136
3220
  const selection = window.getSelection();
3137
- candidates.push(_optionalChain([selection, 'optionalAccess', _77 => _77.anchorNode]), _optionalChain([selection, 'optionalAccess', _78 => _78.focusNode]));
3221
+ candidates.push(_optionalChain([selection, 'optionalAccess', _78 => _78.anchorNode]), _optionalChain([selection, 'optionalAccess', _79 => _79.focusNode]));
3138
3222
  }
3139
3223
  for (const candidate of candidates) {
3140
3224
  if (!candidate || !el.contains(candidate)) continue;
3141
3225
  let cursor = candidate;
3142
3226
  while (cursor && cursor.parentNode !== el) cursor = cursor.parentNode;
3143
- const lineIndex = _optionalChain([cursor, 'optionalAccess', _79 => _79.dataset, 'optionalAccess', _80 => _80.vectoGridLine]);
3227
+ const lineIndex = _optionalChain([cursor, 'optionalAccess', _80 => _80.dataset, 'optionalAccess', _81 => _81.vectoGridLine]);
3144
3228
  if (lineIndex !== void 0) return Number(lineIndex);
3145
3229
  }
3146
3230
  return null;
3147
3231
  }
3148
3232
  releaseContentSelectionForRebuild(el) {
3149
3233
  const selection = typeof window !== "undefined" && typeof window.getSelection === "function" ? window.getSelection() : null;
3150
- const ownsSelection = this.contentSelectionAnchor && el.contains(this.contentSelectionAnchor.node) || (_optionalChain([selection, 'optionalAccess', _81 => _81.anchorNode]) ? el.contains(selection.anchorNode) : false) || (_optionalChain([selection, 'optionalAccess', _82 => _82.focusNode]) ? el.contains(selection.focusNode) : false);
3234
+ const ownsSelection = this.contentSelectionAnchor && el.contains(this.contentSelectionAnchor.node) || (_optionalChain([selection, 'optionalAccess', _82 => _82.anchorNode]) ? el.contains(selection.anchorNode) : false) || (_optionalChain([selection, 'optionalAccess', _83 => _83.focusNode]) ? el.contains(selection.focusNode) : false);
3151
3235
  if (!ownsSelection) return;
3152
3236
  this.endContentSelectionDrag();
3153
- _optionalChain([selection, 'optionalAccess', _83 => _83.removeAllRanges, 'call', _84 => _84()]);
3237
+ _optionalChain([selection, 'optionalAccess', _84 => _84.removeAllRanges, 'call', _85 => _85()]);
3154
3238
  }
3155
3239
  /**
3156
3240
  * Rebuild a content-projection element's DOM (`rebuild`) while preserving a
@@ -3209,7 +3293,7 @@ var Scene = (_class7 = class _Scene {
3209
3293
  }
3210
3294
  /** Convert browser viewport coordinates into this Scene's logical coordinates. */
3211
3295
  clientToScene(clientX, clientY) {
3212
- const rect = _optionalChain([this, 'access', _85 => _85.canvas, 'access', _86 => _86.getBoundingClientRect, 'optionalCall', _87 => _87()]);
3296
+ const rect = _optionalChain([this, 'access', _86 => _86.canvas, 'access', _87 => _87.getBoundingClientRect, 'optionalCall', _88 => _88()]);
3213
3297
  if (!rect) return { x: clientX, y: clientY };
3214
3298
  const cssWidth = rect.width || this.canvas.clientWidth || this.width;
3215
3299
  const cssHeight = rect.height || this.canvas.clientHeight || this.height;
@@ -3247,7 +3331,7 @@ var Scene = (_class7 = class _Scene {
3247
3331
  cancelAnimationFrame(calibrationFrame);
3248
3332
  }
3249
3333
  this.contentGridCalibrationFrames.delete(entityId);
3250
- _optionalChain([this, 'access', _88 => _88.contentGridCalibrationProbes, 'access', _89 => _89.get, 'call', _90 => _90(entityId), 'optionalAccess', _91 => _91.remove, 'call', _92 => _92()]);
3334
+ _optionalChain([this, 'access', _89 => _89.contentGridCalibrationProbes, 'access', _90 => _90.get, 'call', _91 => _91(entityId), 'optionalAccess', _92 => _92.remove, 'call', _93 => _93()]);
3251
3335
  this.contentGridCalibrationProbes.delete(entityId);
3252
3336
  delete el.dataset.vectoGridCalibrationPending;
3253
3337
  delete el.dataset.vectoGridCalibration;
@@ -3401,12 +3485,12 @@ var Scene = (_class7 = class _Scene {
3401
3485
  this.canvasResizeObserver = null;
3402
3486
  }
3403
3487
  if (this.dprMediaQuery && this.dprChangeHandler) {
3404
- _optionalChain([this, 'access', _93 => _93.dprMediaQuery, 'access', _94 => _94.removeEventListener, 'optionalCall', _95 => _95("change", this.dprChangeHandler)]);
3488
+ _optionalChain([this, 'access', _94 => _94.dprMediaQuery, 'access', _95 => _95.removeEventListener, 'optionalCall', _96 => _96("change", this.dprChangeHandler)]);
3405
3489
  this.dprMediaQuery = null;
3406
3490
  this.dprChangeHandler = null;
3407
3491
  }
3408
3492
  if (this.forcedColorsQuery && this.forcedColorsChangeHandler) {
3409
- _optionalChain([this, 'access', _96 => _96.forcedColorsQuery, 'access', _97 => _97.removeEventListener, 'optionalCall', _98 => _98("change", this.forcedColorsChangeHandler)]);
3493
+ _optionalChain([this, 'access', _97 => _97.forcedColorsQuery, 'access', _98 => _98.removeEventListener, 'optionalCall', _99 => _99("change", this.forcedColorsChangeHandler)]);
3410
3494
  this.forcedColorsQuery = null;
3411
3495
  this.forcedColorsChangeHandler = null;
3412
3496
  }
@@ -3424,9 +3508,9 @@ var Scene = (_class7 = class _Scene {
3424
3508
  }
3425
3509
  this.pointerEventTarget = null;
3426
3510
  }
3427
- _optionalChain([this, 'access', _99 => _99.a11yRoot, 'optionalAccess', _100 => _100.remove, 'call', _101 => _101()]);
3511
+ _optionalChain([this, 'access', _100 => _100.a11yRoot, 'optionalAccess', _101 => _101.remove, 'call', _102 => _102()]);
3428
3512
  this.focusSentinel = null;
3429
- _optionalChain([this, 'access', _102 => _102.portalRoot, 'optionalAccess', _103 => _103.remove, 'call', _104 => _104()]);
3513
+ _optionalChain([this, 'access', _103 => _103.portalRoot, 'optionalAccess', _104 => _104.remove, 'call', _105 => _105()]);
3430
3514
  this.a11yElements.clear();
3431
3515
  for (const el of this.contentElements.values()) el.remove();
3432
3516
  this.contentElements.clear();
@@ -3449,10 +3533,10 @@ var Scene = (_class7 = class _Scene {
3449
3533
  }
3450
3534
  this.glContextLostHandler = null;
3451
3535
  this.glContextRestoredHandler = null;
3452
- _optionalChain([this, 'access', _105 => _105.pointRenderer, 'optionalAccess', _106 => _106.destroy, 'call', _107 => _107()]);
3453
- _optionalChain([this, 'access', _108 => _108.renderer, 'access', _109 => _109.dispose, 'optionalCall', _110 => _110()]);
3454
- _optionalChain([this, 'access', _111 => _111.glCanvas, 'optionalAccess', _112 => _112.remove, 'call', _113 => _113()]);
3455
- _optionalChain([this, 'access', _114 => _114.gpuCanvas, 'optionalAccess', _115 => _115.remove, 'call', _116 => _116()]);
3536
+ _optionalChain([this, 'access', _106 => _106.pointRenderer, 'optionalAccess', _107 => _107.destroy, 'call', _108 => _108()]);
3537
+ _optionalChain([this, 'access', _109 => _109.renderer, 'access', _110 => _110.dispose, 'optionalCall', _111 => _111()]);
3538
+ _optionalChain([this, 'access', _112 => _112.glCanvas, 'optionalAccess', _113 => _113.remove, 'call', _114 => _114()]);
3539
+ _optionalChain([this, 'access', _115 => _115.gpuCanvas, 'optionalAccess', _116 => _116.remove, 'call', _117 => _117()]);
3456
3540
  this.gpuCanvas = null;
3457
3541
  this.gpuContext = null;
3458
3542
  if (this.recoveryTimerId) {
@@ -3464,7 +3548,7 @@ var Scene = (_class7 = class _Scene {
3464
3548
  this.manager = null;
3465
3549
  }
3466
3550
  if (this.device) {
3467
- _optionalChain([this, 'access', _117 => _117.device, 'access', _118 => _118.destroy, 'optionalCall', _119 => _119()]);
3551
+ _optionalChain([this, 'access', _118 => _118.device, 'access', _119 => _119.destroy, 'optionalCall', _120 => _120()]);
3468
3552
  this.device = null;
3469
3553
  }
3470
3554
  }
@@ -3897,7 +3981,7 @@ var Scene = (_class7 = class _Scene {
3897
3981
  el.addEventListener("compositionupdate", (e) => {
3898
3982
  const data = _nullishCoalesce(e.data, () => ( ""));
3899
3983
  composition = {
3900
- start: _nullishCoalesce(_optionalChain([composition, 'optionalAccess', _120 => _120.start]), () => ( 0)),
3984
+ start: _nullishCoalesce(_optionalChain([composition, 'optionalAccess', _121 => _121.start]), () => ( 0)),
3901
3985
  length: data.length
3902
3986
  };
3903
3987
  forward();
@@ -4376,9 +4460,9 @@ var Scene = (_class7 = class _Scene {
4376
4460
  for (let lineIndex = 0; lineIndex < grid.lines.length; lineIndex++) {
4377
4461
  const gridLine = grid.lines[lineIndex];
4378
4462
  const projectedLine = projectionLines[lineIndex];
4379
- const lineHeight = _nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _121 => _121.lineHeight]), () => ( grid.lineHeight));
4380
- const baseline = _nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _122 => _122.baseline]), () => ( grid.baseline));
4381
- const lineFont = _nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _123 => _123.font]), () => ( grid.font));
4463
+ const lineHeight = _nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _122 => _122.lineHeight]), () => ( grid.lineHeight));
4464
+ const baseline = _nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _123 => _123.baseline]), () => ( grid.baseline));
4465
+ const lineFont = _nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _124 => _124.font]), () => ( grid.font));
4382
4466
  const lineSignature = contentGridLineSignature(
4383
4467
  grid,
4384
4468
  gridLine,
@@ -4398,8 +4482,8 @@ var Scene = (_class7 = class _Scene {
4398
4482
  lineElement.dir = "ltr";
4399
4483
  lineElement.dataset.vectoGridLine = `${lineIndex}`;
4400
4484
  lineElement.style.position = "absolute";
4401
- lineElement.style.left = `${_nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _124 => _124.x]), () => ( 0))}px`;
4402
- lineElement.style.top = `${(_nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _125 => _125.y]), () => ( lineIndex * grid.lineHeight))) + baseline - _text.cssLineBoxBaseline.call(void 0, lineFont, lineHeight)}px`;
4485
+ lineElement.style.left = `${_nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _125 => _125.x]), () => ( 0))}px`;
4486
+ lineElement.style.top = `${(_nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _126 => _126.y]), () => ( lineIndex * grid.lineHeight))) + baseline - _text.cssLineBoxBaseline.call(void 0, lineFont, lineHeight)}px`;
4403
4487
  lineElement.style.width = `${gridLine.width}px`;
4404
4488
  lineElement.style.height = `${lineHeight}px`;
4405
4489
  lineElement.style.whiteSpace = "pre";
@@ -4469,7 +4553,7 @@ var Scene = (_class7 = class _Scene {
4469
4553
  if (selectionLine !== null && selectionLine >= grid.lines.length) {
4470
4554
  rebuiltSelectionLine = true;
4471
4555
  }
4472
- _optionalChain([el, 'access', _126 => _126.lastElementChild, 'optionalAccess', _127 => _127.remove, 'call', _128 => _128()]);
4556
+ _optionalChain([el, 'access', _127 => _127.lastElementChild, 'optionalAccess', _128 => _128.remove, 'call', _129 => _129()]);
4473
4557
  }
4474
4558
  if (rebuiltSelectionLine) this.releaseContentSelectionForRebuild(el);
4475
4559
  el.dataset.vectoProjectionLines = signature;
@@ -4533,7 +4617,7 @@ var Scene = (_class7 = class _Scene {
4533
4617
  if (previous !== void 0 && typeof cancelAnimationFrame === "function") {
4534
4618
  cancelAnimationFrame(previous);
4535
4619
  }
4536
- _optionalChain([this, 'access', _129 => _129.contentGridCalibrationProbes, 'access', _130 => _130.get, 'call', _131 => _131(entityId), 'optionalAccess', _132 => _132.remove, 'call', _133 => _133()]);
4620
+ _optionalChain([this, 'access', _130 => _130.contentGridCalibrationProbes, 'access', _131 => _131.get, 'call', _132 => _132(entityId), 'optionalAccess', _133 => _133.remove, 'call', _134 => _134()]);
4537
4621
  this.contentGridCalibrationProbes.delete(entityId);
4538
4622
  const calibrationStart = typeof performance !== "undefined" ? performance.now() : 0;
4539
4623
  const probe = document.createElement("div");
@@ -4567,7 +4651,7 @@ var Scene = (_class7 = class _Scene {
4567
4651
  target.dataset.vectoGridCalib = generation;
4568
4652
  continue;
4569
4653
  }
4570
- const sourceText = _nullishCoalesce(_optionalChain([target, 'access', _134 => _134.textContent, 'optionalAccess', _135 => _135.slice, 'call', _136 => _136(0, sourceLength)]), () => ( ""));
4654
+ const sourceText = _nullishCoalesce(_optionalChain([target, 'access', _135 => _135.textContent, 'optionalAccess', _136 => _136.slice, 'call', _137 => _137(0, sourceLength)]), () => ( ""));
4571
4655
  if (!sourceText) {
4572
4656
  target.dataset.vectoGridCalib = generation;
4573
4657
  continue;
@@ -4794,12 +4878,12 @@ var Scene = (_class7 = class _Scene {
4794
4878
  syncOverlayGeometry() {
4795
4879
  const parent = this.canvas.parentElement;
4796
4880
  if (!parent) return;
4797
- const canvasRect = _optionalChain([this, 'access', _137 => _137.canvas, 'access', _138 => _138.getBoundingClientRect, 'optionalCall', _139 => _139()]);
4798
- const parentRect = _optionalChain([parent, 'access', _140 => _140.getBoundingClientRect, 'optionalCall', _141 => _141()]);
4799
- const cssWidth = _optionalChain([canvasRect, 'optionalAccess', _142 => _142.width]) || this.canvas.clientWidth || this.width;
4800
- const cssHeight = _optionalChain([canvasRect, 'optionalAccess', _143 => _143.height]) || this.canvas.clientHeight || this.height;
4801
- const left = (_nullishCoalesce(_optionalChain([canvasRect, 'optionalAccess', _144 => _144.left]), () => ( 0))) - (_nullishCoalesce(_optionalChain([parentRect, 'optionalAccess', _145 => _145.left]), () => ( 0))) - (parent.clientLeft || 0) + parent.scrollLeft;
4802
- const top = (_nullishCoalesce(_optionalChain([canvasRect, 'optionalAccess', _146 => _146.top]), () => ( 0))) - (_nullishCoalesce(_optionalChain([parentRect, 'optionalAccess', _147 => _147.top]), () => ( 0))) - (parent.clientTop || 0) + parent.scrollTop;
4881
+ const canvasRect = _optionalChain([this, 'access', _138 => _138.canvas, 'access', _139 => _139.getBoundingClientRect, 'optionalCall', _140 => _140()]);
4882
+ const parentRect = _optionalChain([parent, 'access', _141 => _141.getBoundingClientRect, 'optionalCall', _142 => _142()]);
4883
+ const cssWidth = _optionalChain([canvasRect, 'optionalAccess', _143 => _143.width]) || this.canvas.clientWidth || this.width;
4884
+ const cssHeight = _optionalChain([canvasRect, 'optionalAccess', _144 => _144.height]) || this.canvas.clientHeight || this.height;
4885
+ const left = (_nullishCoalesce(_optionalChain([canvasRect, 'optionalAccess', _145 => _145.left]), () => ( 0))) - (_nullishCoalesce(_optionalChain([parentRect, 'optionalAccess', _146 => _146.left]), () => ( 0))) - (parent.clientLeft || 0) + parent.scrollLeft;
4886
+ const top = (_nullishCoalesce(_optionalChain([canvasRect, 'optionalAccess', _147 => _147.top]), () => ( 0))) - (_nullishCoalesce(_optionalChain([parentRect, 'optionalAccess', _148 => _148.top]), () => ( 0))) - (parent.clientTop || 0) + parent.scrollTop;
4803
4887
  const scaleX = this.width > 0 ? cssWidth / this.width : 1;
4804
4888
  const scaleY = this.height > 0 ? cssHeight / this.height : 1;
4805
4889
  const prev = this._overlayGeometry;
@@ -4938,7 +5022,7 @@ var Scene = (_class7 = class _Scene {
4938
5022
  * (and {@link respectReducedMotion} is on). `0` means uncapped.
4939
5023
  */
4940
5024
  effectiveMaxFPS() {
4941
- const reduced = this.respectReducedMotion && !!_optionalChain([this, 'access', _148 => _148.reducedMotionQuery, 'optionalAccess', _149 => _149.matches]);
5025
+ const reduced = this.respectReducedMotion && !!_optionalChain([this, 'access', _149 => _149.reducedMotionQuery, 'optionalAccess', _150 => _150.matches]);
4942
5026
  if (reduced)
4943
5027
  return this.maxFPS > 0 ? Math.min(this.maxFPS, REDUCED_MOTION_FPS) : REDUCED_MOTION_FPS;
4944
5028
  return this.maxFPS;
@@ -5033,7 +5117,7 @@ var Scene = (_class7 = class _Scene {
5033
5117
  * @param time - Current absolute time in milliseconds (default 0).
5034
5118
  */
5035
5119
  render(renderer, dt = 0, time = 0) {
5036
- if (_optionalChain([renderer, 'access', _150 => _150.isContextLost, 'optionalCall', _151 => _151()])) return;
5120
+ if (_optionalChain([renderer, 'access', _151 => _151.isContextLost, 'optionalCall', _152 => _152()])) return;
5037
5121
  const isMainRenderer = renderer === this.renderer;
5038
5122
  if (isMainRenderer && this.a11yRoot && this.canvas.parentElement) {
5039
5123
  const parentStyle = this.canvas.parentElement.style;
@@ -5177,7 +5261,7 @@ var Scene = (_class7 = class _Scene {
5177
5261
  }
5178
5262
  renderer.clear();
5179
5263
  if (isMainRenderer) {
5180
- _optionalChain([this, 'access', _152 => _152.pointRenderer, 'optionalAccess', _153 => _153.begin, 'call', _154 => _154()]);
5264
+ _optionalChain([this, 'access', _153 => _153.pointRenderer, 'optionalAccess', _154 => _154.begin, 'call', _155 => _155()]);
5181
5265
  }
5182
5266
  const vw = this.width;
5183
5267
  const vh = this.height;
@@ -5391,9 +5475,9 @@ var Scene = (_class7 = class _Scene {
5391
5475
  const flushT0 = this._phaseTiming ? performance.now() : 0;
5392
5476
  renderer.flush();
5393
5477
  if (isMainRenderer) {
5394
- _optionalChain([this, 'access', _155 => _155.pointRenderer, 'optionalAccess', _156 => _156.flush, 'call', _157 => _157()]);
5478
+ _optionalChain([this, 'access', _156 => _156.pointRenderer, 'optionalAccess', _157 => _157.flush, 'call', _158 => _158()]);
5395
5479
  }
5396
- _optionalChain([renderer, 'access', _158 => _158.present, 'optionalCall', _159 => _159()]);
5480
+ _optionalChain([renderer, 'access', _159 => _159.present, 'optionalCall', _160 => _160()]);
5397
5481
  if (this._phaseTiming) this._recordPhase("flush", performance.now() - flushT0);
5398
5482
  if (flushTiming) endVectoUserTiming(flushTiming);
5399
5483
  if (this._devActive) {
@@ -5674,8 +5758,8 @@ function pointInBounds(b, x, y) {
5674
5758
  function contentGridLineSignature(grid, line, projected, lineHeight, baseline, font, isFirstLine) {
5675
5759
  const parts = [
5676
5760
  // Line box: position, size, and the font that resolves its baseline.
5677
- `${_nullishCoalesce(_optionalChain([projected, 'optionalAccess', _160 => _160.x]), () => ( 0))}`,
5678
- `${_nullishCoalesce(_optionalChain([projected, 'optionalAccess', _161 => _161.y]), () => ( ""))}`,
5761
+ `${_nullishCoalesce(_optionalChain([projected, 'optionalAccess', _161 => _161.x]), () => ( 0))}`,
5762
+ `${_nullishCoalesce(_optionalChain([projected, 'optionalAccess', _162 => _162.y]), () => ( ""))}`,
5679
5763
  `${lineHeight}`,
5680
5764
  `${baseline}`,
5681
5765
  font,
@@ -5862,7 +5946,7 @@ var GridTextEntity = (_class9 = class extends _chunkZ3HFY75Rjs.Entity {
5862
5946
  updateGrid(ascii) {
5863
5947
  this.grid = ascii;
5864
5948
  this.rows = ascii.length;
5865
- this.cols = _optionalChain([ascii, 'access', _162 => _162[0], 'optionalAccess', _163 => _163.length]) || 0;
5949
+ this.cols = _optionalChain([ascii, 'access', _163 => _163[0], 'optionalAccess', _164 => _164.length]) || 0;
5866
5950
  }
5867
5951
  isPointInside(_globalX, _globalY) {
5868
5952
  return false;
@@ -5972,7 +6056,7 @@ var SplineEntity = (_class10 = class extends _chunkZ3HFY75Rjs.Entity {
5972
6056
  this.width = this.bounds.width;
5973
6057
  this.height = this.bounds.height;
5974
6058
  const isGradient = (c) => c !== null && !Array.isArray(c);
5975
- this.containsGradient = (_nullishCoalesce(_optionalChain([this, 'access', _164 => _164.doc, 'access', _165 => _165.equations, 'optionalAccess', _166 => _166.some, 'call', _167 => _167((eq) => isGradient(eq.color_rgb))]), () => ( false))) || (_nullishCoalesce(_optionalChain([this, 'access', _168 => _168.doc, 'access', _169 => _169.paths, 'optionalAccess', _170 => _170.some, 'call', _171 => _171((p) => isGradient(p.color_rgb))]), () => ( false)));
6059
+ this.containsGradient = (_nullishCoalesce(_optionalChain([this, 'access', _165 => _165.doc, 'access', _166 => _166.equations, 'optionalAccess', _167 => _167.some, 'call', _168 => _168((eq) => isGradient(eq.color_rgb))]), () => ( false))) || (_nullishCoalesce(_optionalChain([this, 'access', _169 => _169.doc, 'access', _170 => _170.paths, 'optionalAccess', _171 => _171.some, 'call', _172 => _172((p) => isGradient(p.color_rgb))]), () => ( false)));
5976
6060
  this.interactive = true;
5977
6061
  }
5978
6062
  computeBounds() {
@@ -6507,4 +6591,5 @@ Scene.registerWebGPUParticleSystemManager(_chunkKHGHP2J3js.WebGPUParticleSystemM
6507
6591
 
6508
6592
 
6509
6593
 
6510
- exports.CanvasRenderer = _chunkKHGHP2J3js.CanvasRenderer; exports.Circle = Circle; exports.ComputeParticleEntity = ComputeParticleEntity; exports.DOMPortalEntity = DOMPortalEntity; exports.Entity = _chunkZ3HFY75Rjs.Entity; exports.GlyphRasterAtlas = _chunkKHGHP2J3js.GlyphRasterAtlas; exports.GridTextEntity = GridTextEntity; exports.Group = Group; exports.MSDFTextEntity = _chunkZ3HFY75Rjs.MSDFTextEntity; exports.PARTICLE_OFFSET_LIFE = PARTICLE_OFFSET_LIFE; exports.PARTICLE_OFFSET_ORIGIN_X = PARTICLE_OFFSET_ORIGIN_X; exports.PARTICLE_OFFSET_ORIGIN_Y = PARTICLE_OFFSET_ORIGIN_Y; exports.PARTICLE_OFFSET_POSITION_X = PARTICLE_OFFSET_POSITION_X; exports.PARTICLE_OFFSET_POSITION_Y = PARTICLE_OFFSET_POSITION_Y; exports.PARTICLE_OFFSET_SIZE = PARTICLE_OFFSET_SIZE; exports.PARTICLE_OFFSET_VELOCITY_X = PARTICLE_OFFSET_VELOCITY_X; exports.PARTICLE_OFFSET_VELOCITY_Y = PARTICLE_OFFSET_VELOCITY_Y; exports.PARTICLE_STRIDE_FLOATS = PARTICLE_STRIDE_FLOATS; exports.REDUCED_MOTION_FPS = REDUCED_MOTION_FPS; exports.Rect = Rect; exports.SVGEntity = _chunkZ3HFY75Rjs.SVGEntity; exports.SVGRenderer = _chunkKHGHP2J3js.SVGRenderer; exports.Scene = Scene; exports.SplineEntity = SplineEntity; exports.TextEntity = TextEntity; exports.TextRasterCache = _chunkKHGHP2J3js.TextRasterCache; exports.VECTO_USER_TIMING = VECTO_USER_TIMING; exports.VectoJSEvent = _chunkZ3HFY75Rjs.VectoJSEvent; exports.WebGPUParticleSystemManager = _chunkKHGHP2J3js.WebGPUParticleSystemManager; exports.beginVectoUserTiming = beginVectoUserTiming; exports.createWebGLPointRenderer = _chunkKHGHP2J3js.createWebGLPointRenderer; exports.endVectoUserTiming = endVectoUserTiming; exports.isSafeUrl = _chunkKHGHP2J3js.isSafeUrl; exports.loadSpline = loadSpline; exports.measureVectoUserTiming = measureVectoUserTiming; exports.parseColorToRGBA = _chunkKHGHP2J3js.parseColorToRGBA; exports.polySegmentToBezier = polySegmentToBezier; exports.sanitizeUrl = _chunkKHGHP2J3js.sanitizeUrl;
6594
+
6595
+ exports.CanvasRenderer = _chunkKHGHP2J3js.CanvasRenderer; exports.Circle = Circle; exports.ComputeParticleEntity = ComputeParticleEntity; exports.DOMPortalEntity = DOMPortalEntity; exports.Entity = _chunkZ3HFY75Rjs.Entity; exports.GlyphRasterAtlas = _chunkKHGHP2J3js.GlyphRasterAtlas; exports.GridTextEntity = GridTextEntity; exports.Group = Group; exports.MSDFTextEntity = _chunkZ3HFY75Rjs.MSDFTextEntity; exports.PARTICLE_OFFSET_LIFE = PARTICLE_OFFSET_LIFE; exports.PARTICLE_OFFSET_ORIGIN_X = PARTICLE_OFFSET_ORIGIN_X; exports.PARTICLE_OFFSET_ORIGIN_Y = PARTICLE_OFFSET_ORIGIN_Y; exports.PARTICLE_OFFSET_POSITION_X = PARTICLE_OFFSET_POSITION_X; exports.PARTICLE_OFFSET_POSITION_Y = PARTICLE_OFFSET_POSITION_Y; exports.PARTICLE_OFFSET_SIZE = PARTICLE_OFFSET_SIZE; exports.PARTICLE_OFFSET_VELOCITY_X = PARTICLE_OFFSET_VELOCITY_X; exports.PARTICLE_OFFSET_VELOCITY_Y = PARTICLE_OFFSET_VELOCITY_Y; exports.PARTICLE_STRIDE_FLOATS = PARTICLE_STRIDE_FLOATS; exports.REDUCED_MOTION_FPS = REDUCED_MOTION_FPS; exports.Rect = Rect; exports.SCENE_OPTION_KEYS = SCENE_OPTION_KEYS; exports.SVGEntity = _chunkZ3HFY75Rjs.SVGEntity; exports.SVGRenderer = _chunkKHGHP2J3js.SVGRenderer; exports.Scene = Scene; exports.SplineEntity = SplineEntity; exports.TextEntity = TextEntity; exports.TextRasterCache = _chunkKHGHP2J3js.TextRasterCache; exports.VECTO_USER_TIMING = VECTO_USER_TIMING; exports.VectoJSEvent = _chunkZ3HFY75Rjs.VectoJSEvent; exports.WebGPUParticleSystemManager = _chunkKHGHP2J3js.WebGPUParticleSystemManager; exports.beginVectoUserTiming = beginVectoUserTiming; exports.createWebGLPointRenderer = _chunkKHGHP2J3js.createWebGLPointRenderer; exports.endVectoUserTiming = endVectoUserTiming; exports.isSafeUrl = _chunkKHGHP2J3js.isSafeUrl; exports.loadSpline = loadSpline; exports.measureVectoUserTiming = measureVectoUserTiming; exports.parseColorToRGBA = _chunkKHGHP2J3js.parseColorToRGBA; exports.polySegmentToBezier = polySegmentToBezier; exports.sanitizeUrl = _chunkKHGHP2J3js.sanitizeUrl;
package/dist/index.mjs CHANGED
@@ -1370,6 +1370,57 @@ function rebaseChildBox(parent, parentOriginX, parentOriginY, child, childOrigin
1370
1370
  REBASED_BOX.matrix = `matrix(${a}, ${b}, ${c}, ${d}, 0, 0)`;
1371
1371
  return REBASED_BOX;
1372
1372
  }
1373
+ var SCENE_OPTION_KEYS = [
1374
+ "a11ySyncInterval",
1375
+ "autoThrottle",
1376
+ "contentProjection",
1377
+ "contentProjectionMargin",
1378
+ "debugA11y",
1379
+ "disableWindowResize",
1380
+ "maxDPR",
1381
+ "maxFPS",
1382
+ "particleBackend",
1383
+ "pointBackend",
1384
+ "readingDirection",
1385
+ "renderer",
1386
+ "renderMode",
1387
+ "respectReducedMotion",
1388
+ "userTiming"
1389
+ ];
1390
+ var SCENE_FIELD_NOT_OPTION = {
1391
+ devMode: "set the static `Scene.devMode = true` before constructing"
1392
+ };
1393
+ function editDistance(a, b) {
1394
+ const m = a.length;
1395
+ const n = b.length;
1396
+ if (m === 0 || n === 0) return Math.max(m, n);
1397
+ let prev = Array.from({ length: n + 1 }, (_, i) => i);
1398
+ const curr = Array.from({ length: n + 1 });
1399
+ for (let i = 1; i <= m; i++) {
1400
+ curr[0] = i;
1401
+ for (let j = 1; j <= n; j++) {
1402
+ const cost = a.charCodeAt(i - 1) === b.charCodeAt(j - 1) ? 0 : 1;
1403
+ curr[j] = Math.min(curr[j - 1] + 1, prev[j] + 1, prev[j - 1] + cost);
1404
+ }
1405
+ prev = curr.slice();
1406
+ }
1407
+ return prev[n];
1408
+ }
1409
+ function closestOptionKey(key) {
1410
+ const lower = key.toLowerCase();
1411
+ let best;
1412
+ let bestScore = Infinity;
1413
+ for (const candidate of SCENE_OPTION_KEYS) {
1414
+ if (candidate.toLowerCase() === lower) return candidate;
1415
+ const d = editDistance(lower, candidate.toLowerCase());
1416
+ if (d < bestScore) {
1417
+ bestScore = d;
1418
+ best = candidate;
1419
+ }
1420
+ }
1421
+ const limit = Math.max(2, Math.floor(key.length / 3));
1422
+ return bestScore <= limit ? best : void 0;
1423
+ }
1373
1424
  var REDUCED_MOTION_FPS = 30;
1374
1425
  function parseInlinePx(value) {
1375
1426
  if (!value || !value.endsWith("px")) return null;
@@ -2803,6 +2854,37 @@ var Scene = class _Scene {
2803
2854
  if (!this._devActive) return;
2804
2855
  console.warn(`[vectojs/dev] ${message}`);
2805
2856
  }
2857
+ /**
2858
+ * Warn (dev mode only) about `SceneOptions` keys this version does not read.
2859
+ *
2860
+ * A structural type makes an unrecognized key a silent no-op, and TypeScript
2861
+ * only rejects one when the object literal sits inline at the call site — not
2862
+ * when options are built dynamically, and never in plain JS. Since the
2863
+ * failure mode is "the option appears to work", a runtime check is the only
2864
+ * thing that surfaces it.
2865
+ *
2866
+ * Dev-mode only on purpose: the loop is O(keys × known keys) with an edit
2867
+ * distance per pair, which is nothing at construction but is still pure
2868
+ * overhead in production, where the value has already been shipped.
2869
+ */
2870
+ _warnUnknownOptions(options) {
2871
+ if (!this._devActive) return;
2872
+ const known = new Set(SCENE_OPTION_KEYS);
2873
+ for (const key of Object.keys(options)) {
2874
+ if (known.has(key)) continue;
2875
+ const fieldHint = SCENE_FIELD_NOT_OPTION[key];
2876
+ if (fieldHint) {
2877
+ this._devWarn(
2878
+ `SceneOptions: \`${key}\` is not a constructor option \u2014 ${fieldHint}. Passing it here has no effect.`
2879
+ );
2880
+ continue;
2881
+ }
2882
+ const suggestion = closestOptionKey(key);
2883
+ this._devWarn(
2884
+ `SceneOptions: unknown option \`${key}\` is ignored.` + (suggestion ? ` Did you mean \`${suggestion}\`?` : "")
2885
+ );
2886
+ }
2887
+ }
2806
2888
  /** @internal Periodic dev checks — called once per frame in dev mode. */
2807
2889
  _devRunChecks() {
2808
2890
  this._devFrameCount++;
@@ -2867,7 +2949,9 @@ var Scene = class _Scene {
2867
2949
  this.contentProjectionEnabled = options.contentProjection ?? true;
2868
2950
  this.contentProjectionMargin = options.contentProjectionMargin;
2869
2951
  this.readingDirection = options.readingDirection ?? "ltr";
2952
+ this.renderMode = options.renderMode ?? "always";
2870
2953
  this._devActive = _Scene._devModeDetected();
2954
+ this._warnUnknownOptions(options);
2871
2955
  this.reducedMotionQuery = typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia("(prefers-reduced-motion: reduce)") : null;
2872
2956
  if (typeof window !== "undefined" && typeof window.matchMedia === "function") {
2873
2957
  this.forcedColorsQuery = window.matchMedia("(forced-colors: active)");
@@ -6488,6 +6572,7 @@ export {
6488
6572
  PARTICLE_STRIDE_FLOATS,
6489
6573
  REDUCED_MOTION_FPS,
6490
6574
  Rect,
6575
+ SCENE_OPTION_KEYS,
6491
6576
  SVGEntity,
6492
6577
  SVGRenderer,
6493
6578
  Scene,
@@ -210,7 +210,37 @@ export interface SceneOptions {
210
210
  * (`'rtl'`). Also settable later via {@link Scene.readingDirection}.
211
211
  */
212
212
  readingDirection?: 'ltr' | 'rtl';
213
+ /**
214
+ * When to repaint:
215
+ * - `'always'` (default): drive a continuous rAF loop, throttling to 2 FPS
216
+ * while the scene is idle if {@link SceneOptions.autoThrottle} is on.
217
+ * - `'onDemand'`: paint only after {@link Scene.markDirty} (or an active
218
+ * transition), so a genuinely static scene costs zero frames.
219
+ *
220
+ * Also settable later via {@link Scene.renderMode}. Prefer this option when
221
+ * the mode is known at construction: it applies before the first frame, so an
222
+ * `onDemand` scene never pays for the initial always-on frames.
223
+ */
224
+ renderMode?: 'always' | 'onDemand';
213
225
  }
226
+ /**
227
+ * Every recognized {@link SceneOptions} key, used only to warn about unknown
228
+ * ones in dev mode.
229
+ *
230
+ * This exists because `SceneOptions` is structural: passing a key it does not
231
+ * declare is a **silent** no-op, and TypeScript only catches it when the object
232
+ * is written inline at the call site. Code that builds options dynamically, or
233
+ * plain untranspiled JS, gets no diagnostic at all. `renderMode` was a public
234
+ * field with no matching option for several releases, and four `@vectojs` demos
235
+ * shipped `new Scene(canvas, { renderMode: 'onDemand' })` — reading correctly,
236
+ * doing nothing, and sitting on the 2 FPS idle floor.
237
+ *
238
+ * Kept as a literal rather than derived from a type: `keyof SceneOptions` does
239
+ * not survive to runtime, so this list is the only form a constructor can check
240
+ * against. A new option must be added here too — the test suite asserts the two
241
+ * stay in sync.
242
+ */
243
+ export declare const SCENE_OPTION_KEYS: readonly ['a11ySyncInterval', 'autoThrottle', 'contentProjection', 'contentProjectionMargin', 'debugA11y', 'disableWindowResize', 'maxDPR', 'maxFPS', 'particleBackend', 'pointBackend', 'readingDirection', 'renderer', 'renderMode', 'respectReducedMotion', 'userTiming'];
214
244
  /** Frame-rate the loop is capped to when the OS requests reduced motion. */
215
245
  export declare const REDUCED_MOTION_FPS = 30;
216
246
  /**
@@ -955,6 +985,20 @@ export declare class Scene {
955
985
  private _devActive;
956
986
  private _devFrameCount;
957
987
  private _devWarn;
988
+ /**
989
+ * Warn (dev mode only) about `SceneOptions` keys this version does not read.
990
+ *
991
+ * A structural type makes an unrecognized key a silent no-op, and TypeScript
992
+ * only rejects one when the object literal sits inline at the call site — not
993
+ * when options are built dynamically, and never in plain JS. Since the
994
+ * failure mode is "the option appears to work", a runtime check is the only
995
+ * thing that surfaces it.
996
+ *
997
+ * Dev-mode only on purpose: the loop is O(keys × known keys) with an edit
998
+ * distance per pair, which is nothing at construction but is still pure
999
+ * overhead in production, where the value has already been shipped.
1000
+ */
1001
+ private _warnUnknownOptions;
958
1002
  /** @internal Periodic dev checks — called once per frame in dev mode. */
959
1003
  private _devRunChecks;
960
1004
  constructor(canvas: HTMLCanvasElement, options?: SceneOptions);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vectojs/core",
3
- "version": "1.25.0",
3
+ "version": "1.26.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },