@vectojs/core 0.2.5 → 0.2.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
@@ -3,7 +3,7 @@
3
3
 
4
4
 
5
5
 
6
- var _chunk5KLB6BEZjs = require('./chunk-5KLB6BEZ.js');
6
+ var _chunkBWLNEJJWjs = require('./chunk-BWLNEJJW.js');
7
7
 
8
8
 
9
9
 
@@ -12,7 +12,7 @@ var _chunk5KLB6BEZjs = require('./chunk-5KLB6BEZ.js');
12
12
 
13
13
 
14
14
 
15
- var _chunkTPNADFNNjs = require('./chunk-TPNADFNN.js');
15
+ var _chunkGTQRCY6Wjs = require('./chunk-GTQRCY6W.js');
16
16
 
17
17
 
18
18
 
@@ -24,12 +24,12 @@ var _chunkTPNADFNNjs = require('./chunk-TPNADFNN.js');
24
24
 
25
25
 
26
26
 
27
- var _chunkTQ4H357Qjs = require('./chunk-TQ4H357Q.js');
27
+ var _chunkDWMQYHNCjs = require('./chunk-DWMQYHNC.js');
28
28
 
29
29
 
30
30
 
31
31
 
32
- var _chunk76NMTLYLjs = require('./chunk-76NMTLYL.js');
32
+ var _chunkCTZQOM5Zjs = require('./chunk-CTZQOM5Z.js');
33
33
 
34
34
  // src/tree/ComputeParticleEntity.ts
35
35
  var PARTICLE_STRIDE_FLOATS = 8;
@@ -41,7 +41,7 @@ var PARTICLE_OFFSET_ORIGIN_X = 4;
41
41
  var PARTICLE_OFFSET_ORIGIN_Y = 5;
42
42
  var PARTICLE_OFFSET_SIZE = 6;
43
43
  var PARTICLE_OFFSET_LIFE = 7;
44
- var ComputeParticleEntity = (_class = class extends _chunkTQ4H357Qjs.Entity {
44
+ var ComputeParticleEntity = (_class = class extends _chunkDWMQYHNCjs.Entity {
45
45
 
46
46
 
47
47
 
@@ -296,6 +296,11 @@ var ComputeParticleEntity = (_class = class extends _chunkTQ4H357Qjs.Entity {
296
296
 
297
297
  // src/tree/Scene.ts
298
298
  var REDUCED_MOTION_FPS = 30;
299
+ function parseInlinePx(value) {
300
+ if (!value || !value.endsWith("px")) return null;
301
+ const n = parseFloat(value);
302
+ return Number.isFinite(n) && n > 0 ? n : null;
303
+ }
299
304
  var Scene = (_class2 = class _Scene {
300
305
  static __initStatic() {this.webglCreator = null}
301
306
  static __initStatic2() {this.webgpuManagerClass = null}
@@ -350,67 +355,75 @@ var Scene = (_class2 = class _Scene {
350
355
  // server-side (e.g. headless layout / vector export) without jsdom.
351
356
 
352
357
  __init18() {this.a11yElements = /* @__PURE__ */ new Map()}
358
+ /** DOM nodes mirroring static text content, keyed by entity id. */
359
+ __init19() {this.contentElements = /* @__PURE__ */ new Map()}
360
+ __init20() {this.contentProjectionEnabled = true}
353
361
 
354
- __init19() {this.focusedA11yElement = null}
355
- __init20() {this.caretBlinkTimer = null}
356
- __init21() {this.a11yNeedsReorder = true}
357
- __init22() {this.portalRoot = null}
358
- __init23() {this.fullViewportElements = []}
359
- __init24() {this.normalElements = []}
360
- __init25() {this.activeIds = /* @__PURE__ */ new Set()}
361
- __init26() {this.activePortalsThisFrame = /* @__PURE__ */ new Set()}
362
- __init27() {this.activePortalsPrevFrame = /* @__PURE__ */ new Set()}
363
- __init28() {this.portalEntities = /* @__PURE__ */ new Map()}
364
- __init29() {this.renderOrderCounter = 0}
362
+ __init21() {this.focusedA11yElement = null}
363
+ __init22() {this.caretBlinkTimer = null}
364
+ __init23() {this.a11yNeedsReorder = true}
365
+ __init24() {this.portalRoot = null}
366
+ __init25() {this.fullViewportElements = []}
367
+ __init26() {this.normalElements = []}
368
+ __init27() {this.activeIds = /* @__PURE__ */ new Set()}
369
+ __init28() {this.activePortalsThisFrame = /* @__PURE__ */ new Set()}
370
+ __init29() {this.activePortalsPrevFrame = /* @__PURE__ */ new Set()}
371
+ __init30() {this.portalEntities = /* @__PURE__ */ new Map()}
372
+ __init31() {this.renderOrderCounter = 0}
365
373
  // Optional WebGL point-cloud layer (see SceneOptions.pointBackend).
366
- __init30() {this.pointRenderer = null}
367
- __init31() {this.glCanvas = null}
374
+ __init32() {this.pointRenderer = null}
375
+ __init33() {this.glCanvas = null}
368
376
 
369
377
 
370
378
 
371
- __init32() {this.disableWindowResize = false}
379
+ __init34() {this.disableWindowResize = false}
372
380
  // WebGPU properties
373
- __init33() {this.destroyed = false}
374
- __init34() {this.device = null}
375
- __init35() {this.deviceLost = false}
376
- __init36() {this.particleBackend = "auto"}
377
- __init37() {this._webgpuDisabled = false}
381
+ __init35() {this.destroyed = false}
382
+ __init36() {this.device = null}
383
+ __init37() {this.deviceLost = false}
384
+ __init38() {this.particleBackend = "auto"}
385
+ __init39() {this._webgpuDisabled = false}
378
386
  get webgpuDisabled() {
379
387
  return this._webgpuDisabled || this.particleBackend === "cpu";
380
388
  }
381
389
  set webgpuDisabled(value) {
382
390
  this._webgpuDisabled = value;
383
391
  }
384
- __init38() {this.recoveryTimerId = null}
385
- __init39() {this.manager = null}
386
- __init40() {this.initializingWebGPU = false}
387
- __init41() {this.gpuCanvas = null}
388
- __init42() {this.gpuContext = null}
389
- __init43() {this.mouseX = -9999}
390
- __init44() {this.mouseY = -9999}
391
- __init45() {this.pointerMoveListener = null}
392
- __init46() {this.pointerLeaveListener = null}
393
- __init47() {this.hasWarnedZeroSize = false}
394
- constructor(canvas, options = {}) {;_class2.prototype.__init7.call(this);_class2.prototype.__init8.call(this);_class2.prototype.__init9.call(this);_class2.prototype.__init10.call(this);_class2.prototype.__init11.call(this);_class2.prototype.__init12.call(this);_class2.prototype.__init13.call(this);_class2.prototype.__init14.call(this);_class2.prototype.__init15.call(this);_class2.prototype.__init16.call(this);_class2.prototype.__init17.call(this);_class2.prototype.__init18.call(this);_class2.prototype.__init19.call(this);_class2.prototype.__init20.call(this);_class2.prototype.__init21.call(this);_class2.prototype.__init22.call(this);_class2.prototype.__init23.call(this);_class2.prototype.__init24.call(this);_class2.prototype.__init25.call(this);_class2.prototype.__init26.call(this);_class2.prototype.__init27.call(this);_class2.prototype.__init28.call(this);_class2.prototype.__init29.call(this);_class2.prototype.__init30.call(this);_class2.prototype.__init31.call(this);_class2.prototype.__init32.call(this);_class2.prototype.__init33.call(this);_class2.prototype.__init34.call(this);_class2.prototype.__init35.call(this);_class2.prototype.__init36.call(this);_class2.prototype.__init37.call(this);_class2.prototype.__init38.call(this);_class2.prototype.__init39.call(this);_class2.prototype.__init40.call(this);_class2.prototype.__init41.call(this);_class2.prototype.__init42.call(this);_class2.prototype.__init43.call(this);_class2.prototype.__init44.call(this);_class2.prototype.__init45.call(this);_class2.prototype.__init46.call(this);_class2.prototype.__init47.call(this);
392
+ __init40() {this.recoveryTimerId = null}
393
+ __init41() {this.manager = null}
394
+ __init42() {this.initializingWebGPU = false}
395
+ __init43() {this.gpuCanvas = null}
396
+ __init44() {this.gpuContext = null}
397
+ /** True while the GPU canvas holds a presented particle frame (needs clearing when they leave). */
398
+ __init45() {this.gpuHasContent = false}
399
+ __init46() {this.mouseX = -9999}
400
+ __init47() {this.mouseY = -9999}
401
+ __init48() {this.pointerMoveListener = null}
402
+ __init49() {this.pointerLeaveListener = null}
403
+ __init50() {this.hasWarnedZeroSize = false}
404
+ constructor(canvas, options = {}) {;_class2.prototype.__init7.call(this);_class2.prototype.__init8.call(this);_class2.prototype.__init9.call(this);_class2.prototype.__init10.call(this);_class2.prototype.__init11.call(this);_class2.prototype.__init12.call(this);_class2.prototype.__init13.call(this);_class2.prototype.__init14.call(this);_class2.prototype.__init15.call(this);_class2.prototype.__init16.call(this);_class2.prototype.__init17.call(this);_class2.prototype.__init18.call(this);_class2.prototype.__init19.call(this);_class2.prototype.__init20.call(this);_class2.prototype.__init21.call(this);_class2.prototype.__init22.call(this);_class2.prototype.__init23.call(this);_class2.prototype.__init24.call(this);_class2.prototype.__init25.call(this);_class2.prototype.__init26.call(this);_class2.prototype.__init27.call(this);_class2.prototype.__init28.call(this);_class2.prototype.__init29.call(this);_class2.prototype.__init30.call(this);_class2.prototype.__init31.call(this);_class2.prototype.__init32.call(this);_class2.prototype.__init33.call(this);_class2.prototype.__init34.call(this);_class2.prototype.__init35.call(this);_class2.prototype.__init36.call(this);_class2.prototype.__init37.call(this);_class2.prototype.__init38.call(this);_class2.prototype.__init39.call(this);_class2.prototype.__init40.call(this);_class2.prototype.__init41.call(this);_class2.prototype.__init42.call(this);_class2.prototype.__init43.call(this);_class2.prototype.__init44.call(this);_class2.prototype.__init45.call(this);_class2.prototype.__init46.call(this);_class2.prototype.__init47.call(this);_class2.prototype.__init48.call(this);_class2.prototype.__init49.call(this);_class2.prototype.__init50.call(this);
395
405
  this.canvas = canvas;
396
406
  this.debugA11y = _nullishCoalesce(options.debugA11y, () => ( false));
397
407
  this.disableWindowResize = _nullishCoalesce(options.disableWindowResize, () => ( false));
398
408
  if (this.disableWindowResize) {
399
- this.width = canvas.width || canvas.clientWidth || 0;
400
- this.height = canvas.height || canvas.clientHeight || 0;
409
+ const styleWidth = parseInlinePx(_optionalChain([canvas, 'access', _15 => _15.style, 'optionalAccess', _16 => _16.width]));
410
+ const styleHeight = parseInlinePx(_optionalChain([canvas, 'access', _17 => _17.style, 'optionalAccess', _18 => _18.height]));
411
+ this.width = _nullishCoalesce(styleWidth, () => ( (canvas.width || canvas.clientWidth || 0)));
412
+ this.height = _nullishCoalesce(styleHeight, () => ( (canvas.height || canvas.clientHeight || 0)));
401
413
  } else {
402
414
  this.width = typeof window !== "undefined" ? window.innerWidth : canvas.clientWidth || canvas.width || 800;
403
415
  this.height = typeof window !== "undefined" ? window.innerHeight : canvas.clientHeight || canvas.height || 600;
404
416
  }
405
417
  const globalProcess = typeof globalThis !== "undefined" ? globalThis.process : void 0;
406
- const isTest = globalProcess && (_optionalChain([globalProcess, 'access', _15 => _15.env, 'optionalAccess', _16 => _16.NODE_ENV]) === "test" || _optionalChain([globalProcess, 'access', _17 => _17.env, 'optionalAccess', _18 => _18.VITEST]) === "true");
418
+ const isTest = globalProcess && (_optionalChain([globalProcess, 'access', _19 => _19.env, 'optionalAccess', _20 => _20.NODE_ENV]) === "test" || _optionalChain([globalProcess, 'access', _21 => _21.env, 'optionalAccess', _22 => _22.VITEST]) === "true");
407
419
  this.maxFPS = _nullishCoalesce(options.maxFPS, () => ( (isTest ? 0 : 60)));
408
420
  this.respectReducedMotion = _nullishCoalesce(options.respectReducedMotion, () => ( true));
409
421
  this.autoThrottle = _nullishCoalesce(options.autoThrottle, () => ( true));
410
422
  this.particleBackend = _nullishCoalesce(options.particleBackend, () => ( "auto"));
411
423
  this.a11ySyncInterval = _nullishCoalesce(options.a11ySyncInterval, () => ( 0));
424
+ this.contentProjectionEnabled = _nullishCoalesce(options.contentProjection, () => ( true));
412
425
  this.reducedMotionQuery = typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia("(prefers-reduced-motion: reduce)") : null;
413
- this.root = new class RootEntity extends _chunkTQ4H357Qjs.Entity {
426
+ this.root = new class RootEntity extends _chunkDWMQYHNCjs.Entity {
414
427
  isPointInside() {
415
428
  return false;
416
429
  }
@@ -419,7 +432,7 @@ var Scene = (_class2 = class _Scene {
419
432
  }
420
433
  }("root");
421
434
  this.root._scene = this;
422
- this.overlayRoot = new class OverlayRoot extends _chunkTQ4H357Qjs.Entity {
435
+ this.overlayRoot = new class OverlayRoot extends _chunkDWMQYHNCjs.Entity {
423
436
  isPointInside() {
424
437
  return false;
425
438
  }
@@ -430,7 +443,10 @@ var Scene = (_class2 = class _Scene {
430
443
  if (options.renderer) {
431
444
  this.renderer = options.renderer;
432
445
  } else {
433
- this.renderer = new (0, _chunkTPNADFNNjs.CanvasRenderer)(canvas);
446
+ this.renderer = new (0, _chunkGTQRCY6Wjs.CanvasRenderer)(
447
+ canvas,
448
+ this.disableWindowResize ? { width: this.width, height: this.height } : void 0
449
+ );
434
450
  }
435
451
  if (typeof document !== "undefined") {
436
452
  this.a11yRoot = document.createElement("div");
@@ -493,7 +509,7 @@ var Scene = (_class2 = class _Scene {
493
509
  }
494
510
  /** Convert browser viewport coordinates into this Scene's logical coordinates. */
495
511
  clientToScene(clientX, clientY) {
496
- const rect = _optionalChain([this, 'access', _19 => _19.canvas, 'access', _20 => _20.getBoundingClientRect, 'optionalCall', _21 => _21()]);
512
+ const rect = _optionalChain([this, 'access', _23 => _23.canvas, 'access', _24 => _24.getBoundingClientRect, 'optionalCall', _25 => _25()]);
497
513
  if (!rect) return { x: clientX, y: clientY };
498
514
  const cssWidth = rect.width || this.canvas.clientWidth || this.width;
499
515
  const cssHeight = rect.height || this.canvas.clientHeight || this.height;
@@ -559,6 +575,11 @@ var Scene = (_class2 = class _Scene {
559
575
  * @param entity - The subtree whose shadow nodes should be removed.
560
576
  */
561
577
  detachA11y(entity) {
578
+ const contentEl = this.contentElements.get(entity.id);
579
+ if (contentEl) {
580
+ contentEl.remove();
581
+ this.contentElements.delete(entity.id);
582
+ }
562
583
  this.removeA11yRecursively(entity);
563
584
  }
564
585
  /**
@@ -602,13 +623,15 @@ var Scene = (_class2 = class _Scene {
602
623
  this.canvas.removeEventListener("pointerleave", this.pointerLeaveListener);
603
624
  }
604
625
  }
605
- _optionalChain([this, 'access', _22 => _22.a11yRoot, 'optionalAccess', _23 => _23.remove, 'call', _24 => _24()]);
606
- _optionalChain([this, 'access', _25 => _25.portalRoot, 'optionalAccess', _26 => _26.remove, 'call', _27 => _27()]);
626
+ _optionalChain([this, 'access', _26 => _26.a11yRoot, 'optionalAccess', _27 => _27.remove, 'call', _28 => _28()]);
627
+ _optionalChain([this, 'access', _29 => _29.portalRoot, 'optionalAccess', _30 => _30.remove, 'call', _31 => _31()]);
607
628
  this.a11yElements.clear();
608
- _optionalChain([this, 'access', _28 => _28.pointRenderer, 'optionalAccess', _29 => _29.destroy, 'call', _30 => _30()]);
609
- _optionalChain([this, 'access', _31 => _31.renderer, 'access', _32 => _32.dispose, 'optionalCall', _33 => _33()]);
610
- _optionalChain([this, 'access', _34 => _34.glCanvas, 'optionalAccess', _35 => _35.remove, 'call', _36 => _36()]);
611
- _optionalChain([this, 'access', _37 => _37.gpuCanvas, 'optionalAccess', _38 => _38.remove, 'call', _39 => _39()]);
629
+ for (const el of this.contentElements.values()) el.remove();
630
+ this.contentElements.clear();
631
+ _optionalChain([this, 'access', _32 => _32.pointRenderer, 'optionalAccess', _33 => _33.destroy, 'call', _34 => _34()]);
632
+ _optionalChain([this, 'access', _35 => _35.renderer, 'access', _36 => _36.dispose, 'optionalCall', _37 => _37()]);
633
+ _optionalChain([this, 'access', _38 => _38.glCanvas, 'optionalAccess', _39 => _39.remove, 'call', _40 => _40()]);
634
+ _optionalChain([this, 'access', _41 => _41.gpuCanvas, 'optionalAccess', _42 => _42.remove, 'call', _43 => _43()]);
612
635
  this.gpuCanvas = null;
613
636
  this.gpuContext = null;
614
637
  if (this.recoveryTimerId) {
@@ -619,6 +642,10 @@ var Scene = (_class2 = class _Scene {
619
642
  this.manager.destroy();
620
643
  this.manager = null;
621
644
  }
645
+ if (this.device) {
646
+ _optionalChain([this, 'access', _44 => _44.device, 'access', _45 => _45.destroy, 'optionalCall', _46 => _46()]);
647
+ this.device = null;
648
+ }
622
649
  }
623
650
  setupEvents() {
624
651
  if (typeof window !== "undefined" && !this.disableWindowResize) {
@@ -760,42 +787,42 @@ var Scene = (_class2 = class _Scene {
760
787
  el.style.background = "transparent";
761
788
  }
762
789
  el.addEventListener("click", (e) => {
763
- node.dispatchEvent(new (0, _chunkTQ4H357Qjs.VectoJSEvent)("click", node, e));
790
+ node.dispatchEvent(new (0, _chunkDWMQYHNCjs.VectoJSEvent)("click", node, e));
764
791
  });
765
792
  el.addEventListener("mouseenter", (e) => {
766
793
  if (this.debugA11y) el.style.backgroundColor = "rgba(56, 189, 248, 0.2)";
767
- node.dispatchEvent(new (0, _chunkTQ4H357Qjs.VectoJSEvent)("hover", node, e, false));
794
+ node.dispatchEvent(new (0, _chunkDWMQYHNCjs.VectoJSEvent)("hover", node, e, false));
768
795
  });
769
796
  el.addEventListener("mouseleave", (e) => {
770
797
  if (this.debugA11y) el.style.backgroundColor = "rgba(56, 189, 248, 0.05)";
771
- node.dispatchEvent(new (0, _chunkTQ4H357Qjs.VectoJSEvent)("pointerleave", node, e, false));
798
+ node.dispatchEvent(new (0, _chunkDWMQYHNCjs.VectoJSEvent)("pointerleave", node, e, false));
772
799
  });
773
800
  const capEl = el;
774
801
  el.addEventListener("pointerdown", (e) => {
775
802
  if (typeof capEl.setPointerCapture === "function") capEl.setPointerCapture(e.pointerId);
776
- node.dispatchEvent(new (0, _chunkTQ4H357Qjs.VectoJSEvent)("pointerdown", node, e));
803
+ node.dispatchEvent(new (0, _chunkDWMQYHNCjs.VectoJSEvent)("pointerdown", node, e));
777
804
  });
778
805
  el.addEventListener("pointerup", (e) => {
779
806
  if (typeof capEl.releasePointerCapture === "function")
780
807
  capEl.releasePointerCapture(e.pointerId);
781
- node.dispatchEvent(new (0, _chunkTQ4H357Qjs.VectoJSEvent)("pointerup", node, e));
808
+ node.dispatchEvent(new (0, _chunkDWMQYHNCjs.VectoJSEvent)("pointerup", node, e));
782
809
  });
783
810
  el.addEventListener(
784
811
  "pointermove",
785
- (e) => node.dispatchEvent(new (0, _chunkTQ4H357Qjs.VectoJSEvent)("pointermove", node, e))
812
+ (e) => node.dispatchEvent(new (0, _chunkDWMQYHNCjs.VectoJSEvent)("pointermove", node, e))
786
813
  );
787
814
  el.addEventListener(
788
815
  "wheel",
789
816
  (e) => {
790
- node.dispatchEvent(new (0, _chunkTQ4H357Qjs.VectoJSEvent)("wheel", node, e));
817
+ node.dispatchEvent(new (0, _chunkDWMQYHNCjs.VectoJSEvent)("wheel", node, e));
791
818
  },
792
819
  { passive: false }
793
820
  );
794
821
  el.addEventListener("keydown", (e) => {
795
- node.dispatchEvent(new (0, _chunkTQ4H357Qjs.VectoJSEvent)("keydown", node, e));
822
+ node.dispatchEvent(new (0, _chunkDWMQYHNCjs.VectoJSEvent)("keydown", node, e));
796
823
  });
797
824
  el.addEventListener("keyup", (e) => {
798
- node.dispatchEvent(new (0, _chunkTQ4H357Qjs.VectoJSEvent)("keyup", node, e));
825
+ node.dispatchEvent(new (0, _chunkDWMQYHNCjs.VectoJSEvent)("keyup", node, e));
799
826
  });
800
827
  if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {
801
828
  const input = el;
@@ -822,7 +849,7 @@ var Scene = (_class2 = class _Scene {
822
849
  });
823
850
  el.addEventListener("compositionupdate", (e) => {
824
851
  const data = _nullishCoalesce(e.data, () => ( ""));
825
- composition = { start: _nullishCoalesce(_optionalChain([composition, 'optionalAccess', _40 => _40.start]), () => ( 0)), length: data.length };
852
+ composition = { start: _nullishCoalesce(_optionalChain([composition, 'optionalAccess', _47 => _47.start]), () => ( 0)), length: data.length };
826
853
  forward();
827
854
  });
828
855
  el.addEventListener("compositionend", () => {
@@ -868,7 +895,7 @@ var Scene = (_class2 = class _Scene {
868
895
  el.addEventListener("keydown", (e) => {
869
896
  if (e.key === "Enter" || e.key === " ") {
870
897
  e.preventDefault();
871
- node.dispatchEvent(new (0, _chunkTQ4H357Qjs.VectoJSEvent)("click", node, e));
898
+ node.dispatchEvent(new (0, _chunkDWMQYHNCjs.VectoJSEvent)("click", node, e));
872
899
  }
873
900
  });
874
901
  }
@@ -893,7 +920,7 @@ var Scene = (_class2 = class _Scene {
893
920
  if (el.placeholder !== attrs.placeholder) el.placeholder = attrs.placeholder;
894
921
  }
895
922
  if (attrs.href !== void 0 && el instanceof HTMLAnchorElement) {
896
- const safeHref = _chunkTPNADFNNjs.sanitizeUrl.call(void 0, attrs.href);
923
+ const safeHref = _chunkGTQRCY6Wjs.sanitizeUrl.call(void 0, attrs.href);
897
924
  if (el.getAttribute("href") !== safeHref) el.setAttribute("href", safeHref);
898
925
  }
899
926
  if (el instanceof HTMLImageElement) {
@@ -963,11 +990,96 @@ var Scene = (_class2 = class _Scene {
963
990
  el.style.transform = `matrix(${a}, ${b}, ${c}, ${d}, 0, 0)`;
964
991
  }
965
992
  }
993
+ this.syncContentProjection(node);
966
994
  for (const child of node.children) this.syncA11y(child);
967
995
  if (node === this.root) {
968
996
  for (const overlay of this.overlayRoot.children) this.syncA11y(overlay);
969
997
  }
970
998
  }
999
+ /**
1000
+ * Mirror one entity's static text ({@link Entity.getContentProjection}) as a
1001
+ * transparent DOM node positioned over the drawn glyphs. Runs on the a11y
1002
+ * sync cadence; all writes are dirty-checked. Off-viewport projections are
1003
+ * hidden (`display: none`) so text-heavy scenes only materialize what is
1004
+ * visible to the browser's text machinery anyway.
1005
+ */
1006
+ syncContentProjection(node) {
1007
+ if (!this.contentProjectionEnabled || !this.a11yRoot) return;
1008
+ const projection = node.getContentProjection();
1009
+ let el = this.contentElements.get(node.id);
1010
+ if (!projection || !projection.text) {
1011
+ if (el) {
1012
+ el.remove();
1013
+ this.contentElements.delete(node.id);
1014
+ }
1015
+ return;
1016
+ }
1017
+ if (!el) {
1018
+ el = document.createElement("div");
1019
+ el.setAttribute("data-vecto-content", node.id);
1020
+ const s = el.style;
1021
+ s.position = "absolute";
1022
+ s.transformOrigin = "0 0";
1023
+ s.margin = "0";
1024
+ s.padding = "0";
1025
+ s.color = "transparent";
1026
+ s.whiteSpace = "pre-wrap";
1027
+ s.overflow = "hidden";
1028
+ s.zIndex = "0";
1029
+ el.addEventListener(
1030
+ "wheel",
1031
+ (e2) => {
1032
+ node.dispatchEvent(new (0, _chunkDWMQYHNCjs.VectoJSEvent)("wheel", node, e2));
1033
+ },
1034
+ { passive: false }
1035
+ );
1036
+ this.a11yRoot.appendChild(el);
1037
+ this.contentElements.set(node.id, el);
1038
+ }
1039
+ if (el.textContent !== projection.text) el.textContent = projection.text;
1040
+ const font = _nullishCoalesce(projection.font, () => ( ""));
1041
+ if (el.style.font !== font) el.style.font = font;
1042
+ const lineHeight = projection.lineHeight !== void 0 ? `${projection.lineHeight}px` : "";
1043
+ if (el.style.lineHeight !== lineHeight) el.style.lineHeight = lineHeight;
1044
+ const hidden = node.interactive ? "true" : null;
1045
+ if (el.getAttribute("aria-hidden") !== hidden) {
1046
+ if (hidden) el.setAttribute("aria-hidden", hidden);
1047
+ else el.removeAttribute("aria-hidden");
1048
+ }
1049
+ const selectable = projection.selectable === true;
1050
+ const pointerEvents = selectable ? "auto" : "none";
1051
+ if (el.style.pointerEvents !== pointerEvents) {
1052
+ el.style.pointerEvents = pointerEvents;
1053
+ el.style.userSelect = selectable ? "text" : "none";
1054
+ el.style.cursor = selectable ? "text" : "";
1055
+ }
1056
+ const { a, b, c, d, e, f } = node.getWorldTransform();
1057
+ el.style.left = `${e}px`;
1058
+ el.style.top = `${f}px`;
1059
+ if (node.width > 0) el.style.width = `${node.width}px`;
1060
+ if (node.height > 0) el.style.height = `${node.height}px`;
1061
+ el.style.transform = `matrix(${a}, ${b}, ${c}, ${d}, 0, 0)`;
1062
+ let visible = true;
1063
+ if (node.width > 0 && node.height > 0) {
1064
+ let minX = Infinity;
1065
+ let minY = Infinity;
1066
+ let maxX = -Infinity;
1067
+ let maxY = -Infinity;
1068
+ for (let i = 0; i < 4; i++) {
1069
+ const lx = i & 1 ? node.width : 0;
1070
+ const ly = i & 2 ? node.height : 0;
1071
+ const wx = a * lx + c * ly + e;
1072
+ const wy = b * lx + d * ly + f;
1073
+ if (wx < minX) minX = wx;
1074
+ if (wx > maxX) maxX = wx;
1075
+ if (wy < minY) minY = wy;
1076
+ if (wy > maxY) maxY = wy;
1077
+ }
1078
+ visible = maxX >= 0 && minX <= this.width && maxY >= 0 && minY <= this.height;
1079
+ }
1080
+ const display = visible ? "" : "none";
1081
+ if (el.style.display !== display) el.style.display = display;
1082
+ }
971
1083
  enforceA11yDomOrder() {
972
1084
  if (!this.a11yRoot) return;
973
1085
  this.fullViewportElements.length = 0;
@@ -1026,12 +1138,12 @@ var Scene = (_class2 = class _Scene {
1026
1138
  syncOverlayGeometry() {
1027
1139
  const parent = this.canvas.parentElement;
1028
1140
  if (!parent) return;
1029
- const canvasRect = _optionalChain([this, 'access', _41 => _41.canvas, 'access', _42 => _42.getBoundingClientRect, 'optionalCall', _43 => _43()]);
1030
- const parentRect = _optionalChain([parent, 'access', _44 => _44.getBoundingClientRect, 'optionalCall', _45 => _45()]);
1031
- const cssWidth = _optionalChain([canvasRect, 'optionalAccess', _46 => _46.width]) || this.canvas.clientWidth || this.width;
1032
- const cssHeight = _optionalChain([canvasRect, 'optionalAccess', _47 => _47.height]) || this.canvas.clientHeight || this.height;
1033
- const left = (_nullishCoalesce(_optionalChain([canvasRect, 'optionalAccess', _48 => _48.left]), () => ( 0))) - (_nullishCoalesce(_optionalChain([parentRect, 'optionalAccess', _49 => _49.left]), () => ( 0))) - (parent.clientLeft || 0) + parent.scrollLeft;
1034
- const top = (_nullishCoalesce(_optionalChain([canvasRect, 'optionalAccess', _50 => _50.top]), () => ( 0))) - (_nullishCoalesce(_optionalChain([parentRect, 'optionalAccess', _51 => _51.top]), () => ( 0))) - (parent.clientTop || 0) + parent.scrollTop;
1141
+ const canvasRect = _optionalChain([this, 'access', _48 => _48.canvas, 'access', _49 => _49.getBoundingClientRect, 'optionalCall', _50 => _50()]);
1142
+ const parentRect = _optionalChain([parent, 'access', _51 => _51.getBoundingClientRect, 'optionalCall', _52 => _52()]);
1143
+ const cssWidth = _optionalChain([canvasRect, 'optionalAccess', _53 => _53.width]) || this.canvas.clientWidth || this.width;
1144
+ const cssHeight = _optionalChain([canvasRect, 'optionalAccess', _54 => _54.height]) || this.canvas.clientHeight || this.height;
1145
+ const left = (_nullishCoalesce(_optionalChain([canvasRect, 'optionalAccess', _55 => _55.left]), () => ( 0))) - (_nullishCoalesce(_optionalChain([parentRect, 'optionalAccess', _56 => _56.left]), () => ( 0))) - (parent.clientLeft || 0) + parent.scrollLeft;
1146
+ const top = (_nullishCoalesce(_optionalChain([canvasRect, 'optionalAccess', _57 => _57.top]), () => ( 0))) - (_nullishCoalesce(_optionalChain([parentRect, 'optionalAccess', _58 => _58.top]), () => ( 0))) - (parent.clientTop || 0) + parent.scrollTop;
1035
1147
  const scaleX = this.width > 0 ? cssWidth / this.width : 1;
1036
1148
  const scaleY = this.height > 0 ? cssHeight / this.height : 1;
1037
1149
  for (const root of [this.a11yRoot, this.portalRoot]) {
@@ -1156,7 +1268,7 @@ var Scene = (_class2 = class _Scene {
1156
1268
  * (and {@link respectReducedMotion} is on). `0` means uncapped.
1157
1269
  */
1158
1270
  effectiveMaxFPS() {
1159
- const reduced = this.respectReducedMotion && !!_optionalChain([this, 'access', _52 => _52.reducedMotionQuery, 'optionalAccess', _53 => _53.matches]);
1271
+ const reduced = this.respectReducedMotion && !!_optionalChain([this, 'access', _59 => _59.reducedMotionQuery, 'optionalAccess', _60 => _60.matches]);
1160
1272
  if (reduced)
1161
1273
  return this.maxFPS > 0 ? Math.min(this.maxFPS, REDUCED_MOTION_FPS) : REDUCED_MOTION_FPS;
1162
1274
  return this.maxFPS;
@@ -1164,8 +1276,8 @@ var Scene = (_class2 = class _Scene {
1164
1276
  loop(time) {
1165
1277
  if (!this.isRunning) return;
1166
1278
  let cap = this.effectiveMaxFPS();
1167
- const isStatic = this.autoThrottle && !this.dirty && !this.hasAnyPendingAnimation(this.root) && !this.hasAnyPendingAnimation(this.overlayRoot);
1168
- if (isStatic && this.renderMode === "always" && this.maxFPS > 0) {
1279
+ const isIdle = !this.dirty && !this.hasAnyPendingAnimation(this.root) && !this.hasAnyPendingAnimation(this.overlayRoot);
1280
+ if (isIdle && this.autoThrottle && this.renderMode === "always" && this.maxFPS > 0) {
1169
1281
  cap = Math.min(cap, 2);
1170
1282
  }
1171
1283
  if (cap > 0 && time - this.lastTime < 1e3 / cap - 1) {
@@ -1174,17 +1286,19 @@ var Scene = (_class2 = class _Scene {
1174
1286
  }
1175
1287
  const dt = time - this.lastTime;
1176
1288
  this.lastTime = time;
1177
- if (this.renderMode === "onDemand" && isStatic) {
1289
+ if (this.renderMode === "onDemand" && isIdle) {
1178
1290
  this.scheduleFrame();
1179
1291
  return;
1180
1292
  }
1293
+ this.dirty = false;
1181
1294
  this.render(this.renderer, dt, time);
1182
1295
  const hasActiveAnimation = this.hasAnyPendingAnimation(this.root) || this.hasAnyPendingAnimation(this.overlayRoot);
1183
1296
  const hasInteractive = this.hasAnyInteractive(this.root) || this.hasAnyInteractive(this.overlayRoot);
1297
+ const wantsContentSync = this.contentProjectionEnabled;
1184
1298
  const shouldSyncInterval = this.a11ySyncInterval <= 0 || time - this.lastA11ySync >= this.a11ySyncInterval;
1185
- if ((hasInteractive || this.a11yElements.size > 0) && (shouldSyncInterval || this.a11yPendingSyncAfterAnimation)) {
1299
+ if ((hasInteractive || this.a11yElements.size > 0 || wantsContentSync) && (shouldSyncInterval || this.a11yPendingSyncAfterAnimation)) {
1186
1300
  this.lastA11ySync = time;
1187
- if (hasInteractive) {
1301
+ if (hasInteractive || wantsContentSync) {
1188
1302
  this.syncA11y(this.root);
1189
1303
  }
1190
1304
  this.enforceA11yDomOrder();
@@ -1192,7 +1306,6 @@ var Scene = (_class2 = class _Scene {
1192
1306
  } else if (hasActiveAnimation) {
1193
1307
  this.a11yPendingSyncAfterAnimation = true;
1194
1308
  }
1195
- this.dirty = false;
1196
1309
  this.scheduleFrame();
1197
1310
  }
1198
1311
  /**
@@ -1304,6 +1417,7 @@ var Scene = (_class2 = class _Scene {
1304
1417
  renderPass.end();
1305
1418
  }
1306
1419
  this.device.queue.submit([commandEncoder.finish()]);
1420
+ if (this.gpuContext) this.gpuHasContent = true;
1307
1421
  } catch (e) {
1308
1422
  console.error("WebGPU frame execution failed. Falling back.", e);
1309
1423
  this.deviceLost = true;
@@ -1312,13 +1426,27 @@ var Scene = (_class2 = class _Scene {
1312
1426
  }
1313
1427
  } else if (isMainRenderPath) {
1314
1428
  for (const entity of computeEntities) {
1315
- entity.updateCPU(dt / 1e3, this.mouseX, this.mouseY, this.width, this.height);
1429
+ let mx = this.mouseX;
1430
+ let my = this.mouseY;
1431
+ if (mx > -9e3 && my > -9e3) {
1432
+ const local = entity.worldToLocal(mx, my);
1433
+ if (local) {
1434
+ mx = local.x;
1435
+ my = local.y;
1436
+ } else {
1437
+ mx = -9999;
1438
+ my = -9999;
1439
+ }
1440
+ }
1441
+ entity.updateCPU(dt / 1e3, mx, my, this.width, this.height);
1316
1442
  }
1317
1443
  }
1444
+ } else if (isMainRenderer) {
1445
+ this.clearGPUCanvasIfStale();
1318
1446
  }
1319
1447
  renderer.clear();
1320
1448
  if (isMainRenderer) {
1321
- _optionalChain([this, 'access', _54 => _54.pointRenderer, 'optionalAccess', _55 => _55.begin, 'call', _56 => _56()]);
1449
+ _optionalChain([this, 'access', _61 => _61.pointRenderer, 'optionalAccess', _62 => _62.begin, 'call', _63 => _63()]);
1322
1450
  }
1323
1451
  const vw = this.width;
1324
1452
  const vh = this.height;
@@ -1412,7 +1540,19 @@ var Scene = (_class2 = class _Scene {
1412
1540
  if (visible) {
1413
1541
  if (node instanceof ComputeParticleEntity) {
1414
1542
  if (this.deviceLost || this.webgpuDisabled || !this.device || !this.manager) {
1415
- this.renderCPUParticles(renderer, node, worldOpacity);
1543
+ this.renderCPUParticles(
1544
+ renderer,
1545
+ node,
1546
+ worldOpacity,
1547
+ a,
1548
+ b,
1549
+ c,
1550
+ d,
1551
+ te,
1552
+ tf,
1553
+ worldScaleX,
1554
+ isSimilarityTransform
1555
+ );
1416
1556
  }
1417
1557
  } else {
1418
1558
  node.render(renderer);
@@ -1434,14 +1574,15 @@ var Scene = (_class2 = class _Scene {
1434
1574
  if (isMainRenderer) this.reconcilePortals();
1435
1575
  renderer.flush();
1436
1576
  if (isMainRenderer) {
1437
- _optionalChain([this, 'access', _57 => _57.pointRenderer, 'optionalAccess', _58 => _58.flush, 'call', _59 => _59()]);
1577
+ _optionalChain([this, 'access', _64 => _64.pointRenderer, 'optionalAccess', _65 => _65.flush, 'call', _66 => _66()]);
1438
1578
  }
1579
+ _optionalChain([renderer, 'access', _67 => _67.present, 'optionalCall', _68 => _68()]);
1439
1580
  }
1440
1581
  /**
1441
1582
  * Export the current scene state to a lightweight, flat SVG XML string.
1442
1583
  */
1443
1584
  toSVG() {
1444
- const renderer = new (0, _chunkTPNADFNNjs.SVGRenderer)(this.width, this.height);
1585
+ const renderer = new (0, _chunkGTQRCY6Wjs.SVGRenderer)(this.width, this.height);
1445
1586
  this.render(renderer, 0, 0);
1446
1587
  return renderer.toXMLString();
1447
1588
  }
@@ -1454,7 +1595,11 @@ var Scene = (_class2 = class _Scene {
1454
1595
  if (typeof this.renderer.resize === "function") {
1455
1596
  this.renderer.resize(width, height);
1456
1597
  }
1457
- _optionalChain([this, 'access', _60 => _60.pointRenderer, 'optionalAccess', _61 => _61.resize, 'call', _62 => _62(width, height)]);
1598
+ _optionalChain([this, 'access', _69 => _69.pointRenderer, 'optionalAccess', _70 => _70.resize, 'call', _71 => _71(width, height)]);
1599
+ if (this.gpuCanvas) {
1600
+ this.gpuCanvas.width = width;
1601
+ this.gpuCanvas.height = height;
1602
+ }
1458
1603
  this.markDirty();
1459
1604
  }
1460
1605
  /**
@@ -1477,6 +1622,27 @@ var Scene = (_class2 = class _Scene {
1477
1622
  if (overlayHit) return overlayHit;
1478
1623
  return this.findHitRecursively(this.root, x, y);
1479
1624
  }
1625
+ /** Submit one transparent clear pass when particle content lingers on the GPU canvas. */
1626
+ clearGPUCanvasIfStale() {
1627
+ if (!this.gpuHasContent || !this.device || !this.gpuContext || this.deviceLost) return;
1628
+ try {
1629
+ const encoder = this.device.createCommandEncoder();
1630
+ const pass = encoder.beginRenderPass({
1631
+ colorAttachments: [
1632
+ {
1633
+ view: this.gpuContext.getCurrentTexture().createView(),
1634
+ clearValue: { r: 0, g: 0, b: 0, a: 0 },
1635
+ loadOp: "clear",
1636
+ storeOp: "store"
1637
+ }
1638
+ ]
1639
+ });
1640
+ pass.end();
1641
+ this.device.queue.submit([encoder.finish()]);
1642
+ } catch (e3) {
1643
+ }
1644
+ this.gpuHasContent = false;
1645
+ }
1480
1646
  async initWebGPUContext(entities) {
1481
1647
  if (!navigator.gpu) {
1482
1648
  throw new Error("WebGPU not supported on this platform.");
@@ -1569,10 +1735,10 @@ var Scene = (_class2 = class _Scene {
1569
1735
  }).catch(() => this.recreateWebGPUDeviceWithRetry(entities, attempt + 1));
1570
1736
  }, backoff);
1571
1737
  }
1572
- renderCPUParticles(renderer, entity, worldOpacity) {
1738
+ renderCPUParticles(renderer, entity, worldOpacity, a, b, c, d, e, f, worldScale, isSimilarityTransform) {
1573
1739
  const data = entity.particleData;
1574
1740
  const size = entity.maxParticles;
1575
- const isMain = renderer === this.renderer;
1741
+ const useGL = renderer === this.renderer && !!this.pointRenderer && isSimilarityTransform;
1576
1742
  for (let i = 0; i < size; i++) {
1577
1743
  const idx = i * 8;
1578
1744
  const x = data[idx];
@@ -1582,8 +1748,14 @@ var Scene = (_class2 = class _Scene {
1582
1748
  if (life === 0) continue;
1583
1749
  const opacity = life < 0 ? worldOpacity : worldOpacity * Math.min(1, life);
1584
1750
  const scale = life >= 0 ? Math.min(1, life) : 1;
1585
- if (isMain && this.pointRenderer) {
1586
- this.pointRenderer.addCircle(x, y, pSize * scale, entity.baseColor, opacity);
1751
+ if (useGL) {
1752
+ this.pointRenderer.addCircle(
1753
+ a * x + c * y + e,
1754
+ b * x + d * y + f,
1755
+ pSize * scale * worldScale,
1756
+ entity.baseColor,
1757
+ opacity
1758
+ );
1587
1759
  } else {
1588
1760
  renderer.fillCircle(x, y, pSize * scale, entity.baseColor, opacity);
1589
1761
  }
@@ -1604,33 +1776,41 @@ var Scene = (_class2 = class _Scene {
1604
1776
  // src/components/TextEntity.ts
1605
1777
  var sharedMeasurer;
1606
1778
  function defaultMeasurer() {
1607
- if (sharedMeasurer === void 0) sharedMeasurer = _chunk5KLB6BEZjs.createCanvasMeasurer.call(void 0, "sans-serif");
1779
+ if (sharedMeasurer === void 0) sharedMeasurer = _chunkBWLNEJJWjs.createCanvasMeasurer.call(void 0, "sans-serif");
1608
1780
  return sharedMeasurer;
1609
1781
  }
1610
- var TextEntity = (_class3 = class extends _chunkTQ4H357Qjs.Entity {
1782
+ var TextEntity = (_class3 = class extends _chunkDWMQYHNCjs.Entity {
1611
1783
 
1612
1784
 
1613
1785
 
1614
1786
 
1615
- __init48() {this.nodes = []}
1787
+ __init51() {this.nodes = []}
1616
1788
 
1617
- __init49() {this.fillStyle = "#94a3b8"}
1618
- __init50() {this.strokeStyle = null}
1619
- __init51() {this.hoveredFillStyle = "#ffffff"}
1620
- __init52() {this.lineWidth = 1}
1621
- __init53() {this.isHovered = false}
1789
+ __init52() {this.fillStyle = "#94a3b8"}
1790
+ __init53() {this.strokeStyle = null}
1791
+ __init54() {this.hoveredFillStyle = "#ffffff"}
1792
+ __init55() {this.lineWidth = 1}
1793
+ __init56() {this.isHovered = false}
1622
1794
  constructor(text, atlas, maxWidth, fontSize = 24) {
1623
- super();_class3.prototype.__init48.call(this);_class3.prototype.__init49.call(this);_class3.prototype.__init50.call(this);_class3.prototype.__init51.call(this);_class3.prototype.__init52.call(this);_class3.prototype.__init53.call(this);;
1795
+ super();_class3.prototype.__init51.call(this);_class3.prototype.__init52.call(this);_class3.prototype.__init53.call(this);_class3.prototype.__init54.call(this);_class3.prototype.__init55.call(this);_class3.prototype.__init56.call(this);;
1624
1796
  this.text = text;
1625
1797
  this.atlas = atlas;
1626
1798
  this.fontSize = fontSize;
1627
- this.layout = new (0, _chunk5KLB6BEZjs.LayoutEngine)(maxWidth, 1e4, defaultMeasurer());
1799
+ this.layout = new (0, _chunkBWLNEJJWjs.LayoutEngine)(maxWidth, 1e4, defaultMeasurer());
1628
1800
  this.prepared = this.layout.prepare(this.text, this.atlas, this.fontSize);
1629
1801
  this.applyLayout();
1630
1802
  this.interactive = true;
1631
1803
  this.on("hover", () => this.isHovered = true);
1632
1804
  this.on("pointerleave", () => this.isHovered = false);
1633
1805
  }
1806
+ /**
1807
+ * Mirror the rendered text into the DOM content layer: find-in-page, screen
1808
+ * readers, crawlers, and translation see the same string the canvas draws.
1809
+ */
1810
+ getContentProjection() {
1811
+ if (!this.text) return null;
1812
+ return { text: this.text, font: `${this.fontSize}px sans-serif` };
1813
+ }
1634
1814
  /**
1635
1815
  * Replace the text content. Runs the **cold** measurement pass (re-segment +
1636
1816
  * re-measure) since the glyphs changed, then re-lays out.
@@ -1705,17 +1885,17 @@ var TextEntity = (_class3 = class extends _chunkTQ4H357Qjs.Entity {
1705
1885
  }, _class3);
1706
1886
 
1707
1887
  // src/components/GridTextEntity.ts
1708
- var GridTextEntity = (_class4 = class extends _chunkTQ4H357Qjs.Entity {
1888
+ var GridTextEntity = (_class4 = class extends _chunkDWMQYHNCjs.Entity {
1709
1889
 
1710
- __init54() {this.fillStyle = "#ffffff"}
1711
- __init55() {this.grid = []}
1890
+ __init57() {this.fillStyle = "#ffffff"}
1891
+ __init58() {this.grid = []}
1712
1892
  // Array of rows
1713
- __init56() {this.cols = 0}
1714
- __init57() {this.rows = 0}
1893
+ __init59() {this.cols = 0}
1894
+ __init60() {this.rows = 0}
1715
1895
 
1716
1896
 
1717
1897
  constructor(_atlas, fontSize = 10) {
1718
- super();_class4.prototype.__init54.call(this);_class4.prototype.__init55.call(this);_class4.prototype.__init56.call(this);_class4.prototype.__init57.call(this);;
1898
+ super();_class4.prototype.__init57.call(this);_class4.prototype.__init58.call(this);_class4.prototype.__init59.call(this);_class4.prototype.__init60.call(this);;
1719
1899
  this.fontSize = fontSize;
1720
1900
  this.charWidth = fontSize * 1;
1721
1901
  this.charHeight = fontSize * 1.1;
@@ -1724,7 +1904,7 @@ var GridTextEntity = (_class4 = class extends _chunkTQ4H357Qjs.Entity {
1724
1904
  updateGrid(ascii) {
1725
1905
  this.grid = ascii;
1726
1906
  this.rows = ascii.length;
1727
- this.cols = _optionalChain([ascii, 'access', _63 => _63[0], 'optionalAccess', _64 => _64.length]) || 0;
1907
+ this.cols = _optionalChain([ascii, 'access', _72 => _72[0], 'optionalAccess', _73 => _73.length]) || 0;
1728
1908
  }
1729
1909
  isPointInside(_globalX, _globalY) {
1730
1910
  return false;
@@ -1799,26 +1979,31 @@ function distSqToSegment(px, py, x1, y1, x2, y2) {
1799
1979
  const ey = py - cy;
1800
1980
  return ex * ex + ey * ey;
1801
1981
  }
1802
- var SplineEntity = (_class5 = class extends _chunkTQ4H357Qjs.Entity {
1982
+ var SplineEntity = (_class5 = class extends _chunkDWMQYHNCjs.Entity {
1983
+
1803
1984
 
1804
1985
 
1805
1986
 
1806
1987
 
1807
1988
 
1808
1989
 
1990
+ __init61() {this.offscreen = null}
1991
+ __init62() {this.baked = false}
1992
+ /** Logical (CSS-pixel) size of the baked bitmap — the blit destination size. */
1993
+ __init63() {this.bakedWidth = 0}
1994
+ __init64() {this.bakedHeight = 0}
1995
+ /** Gradient strokes can't be baked to a solid-color bitmap; they render per-frame. */
1809
1996
 
1810
- __init58() {this.offscreen = null}
1811
- __init59() {this.baked = false}
1812
1997
  /** Lazily-flattened polylines (one Float32Array of [x,y,...] per segment) for hit-testing. */
1813
- __init60() {this.polylines = null}
1998
+ __init65() {this.polylines = null}
1814
1999
  /**
1815
2000
  * When `true`, the renderer draws a rounded-rect outline of the entity's
1816
2001
  * local bounds after painting the curves. Useful for drag feedback and
1817
2002
  * debugging hit areas. Defaults to `false`.
1818
2003
  */
1819
- __init61() {this.showBounds = false}
2004
+ __init66() {this.showBounds = false}
1820
2005
  constructor(doc, opts = {}) {
1821
- super();_class5.prototype.__init58.call(this);_class5.prototype.__init59.call(this);_class5.prototype.__init60.call(this);_class5.prototype.__init61.call(this);;
2006
+ super();_class5.prototype.__init61.call(this);_class5.prototype.__init62.call(this);_class5.prototype.__init63.call(this);_class5.prototype.__init64.call(this);_class5.prototype.__init65.call(this);_class5.prototype.__init66.call(this);;
1822
2007
  this.doc = doc;
1823
2008
  this.lineWidth = _nullishCoalesce(opts.lineWidth, () => ( 2));
1824
2009
  this.cache = _nullishCoalesce(opts.cache, () => ( true));
@@ -1828,6 +2013,8 @@ var SplineEntity = (_class5 = class extends _chunkTQ4H357Qjs.Entity {
1828
2013
  this.bounds = this.computeBounds();
1829
2014
  this.width = this.bounds.width;
1830
2015
  this.height = this.bounds.height;
2016
+ const isGradient = (c) => c !== null && !Array.isArray(c);
2017
+ this.containsGradient = (_nullishCoalesce(_optionalChain([this, 'access', _74 => _74.doc, 'access', _75 => _75.equations, 'optionalAccess', _76 => _76.some, 'call', _77 => _77((eq) => isGradient(eq.color_rgb))]), () => ( false))) || (_nullishCoalesce(_optionalChain([this, 'access', _78 => _78.doc, 'access', _79 => _79.paths, 'optionalAccess', _80 => _80.some, 'call', _81 => _81((p) => isGradient(p.color_rgb))]), () => ( false)));
1831
2018
  this.interactive = true;
1832
2019
  }
1833
2020
  computeBounds() {
@@ -1987,18 +2174,22 @@ var SplineEntity = (_class5 = class extends _chunkTQ4H357Qjs.Entity {
1987
2174
  const pad = this.lineWidth + 2;
1988
2175
  const w = Math.max(1, Math.ceil(this.bounds.width) + pad * 2);
1989
2176
  const h = Math.max(1, Math.ceil(this.bounds.height) + pad * 2);
2177
+ this.bakedWidth = w;
2178
+ this.bakedHeight = h;
2179
+ const dpr = typeof window !== "undefined" && typeof window.devicePixelRatio === "number" ? window.devicePixelRatio || 1 : 1;
1990
2180
  let canvas;
1991
2181
  if (typeof OffscreenCanvas !== "undefined") {
1992
- canvas = new OffscreenCanvas(w, h);
2182
+ canvas = new OffscreenCanvas(w * dpr, h * dpr);
1993
2183
  } else if (typeof document !== "undefined") {
1994
2184
  canvas = document.createElement("canvas");
1995
- canvas.width = w;
1996
- canvas.height = h;
2185
+ canvas.width = w * dpr;
2186
+ canvas.height = h * dpr;
1997
2187
  } else {
1998
2188
  return;
1999
2189
  }
2000
2190
  const ctx = canvas.getContext("2d");
2001
2191
  if (!ctx) return;
2192
+ ctx.scale(dpr, dpr);
2002
2193
  ctx.translate(pad - this.bounds.x, pad - this.bounds.y);
2003
2194
  ctx.lineWidth = this.lineWidth;
2004
2195
  ctx.lineCap = "round";
@@ -2032,7 +2223,7 @@ var SplineEntity = (_class5 = class extends _chunkTQ4H357Qjs.Entity {
2032
2223
  }
2033
2224
  render(r) {
2034
2225
  let rendered = false;
2035
- if (this.cache) {
2226
+ if (this.cache && !this.containsGradient) {
2036
2227
  if (!this.baked) this.bake();
2037
2228
  if (this.offscreen) {
2038
2229
  const pad = this.lineWidth + 2;
@@ -2040,8 +2231,9 @@ var SplineEntity = (_class5 = class extends _chunkTQ4H357Qjs.Entity {
2040
2231
  this.offscreen,
2041
2232
  this.bounds.x - pad,
2042
2233
  this.bounds.y - pad,
2043
- this.offscreen.width,
2044
- this.offscreen.height
2234
+ // Logical size, not the (DPR-scaled) bitmap size.
2235
+ this.bakedWidth,
2236
+ this.bakedHeight
2045
2237
  );
2046
2238
  rendered = true;
2047
2239
  }
@@ -2064,9 +2256,9 @@ async function loadSpline(url) {
2064
2256
  // src/math/SpatialHashGrid.ts
2065
2257
  var SpatialHashGrid = (_class6 = class {
2066
2258
 
2067
- __init62() {this.grid = /* @__PURE__ */ new Map()}
2068
- __init63() {this.entityCells = /* @__PURE__ */ new Map()}
2069
- constructor(cellSize = 64) {;_class6.prototype.__init62.call(this);_class6.prototype.__init63.call(this);
2259
+ __init67() {this.grid = /* @__PURE__ */ new Map()}
2260
+ __init68() {this.entityCells = /* @__PURE__ */ new Map()}
2261
+ constructor(cellSize = 64) {;_class6.prototype.__init67.call(this);_class6.prototype.__init68.call(this);
2070
2262
  this.cellSize = cellSize;
2071
2263
  }
2072
2264
  hash(cx, cy) {
@@ -2120,7 +2312,7 @@ var SpatialHashGrid = (_class6 = class {
2120
2312
  const keys = this.entityCells.get(id);
2121
2313
  if (!keys) return;
2122
2314
  for (const key of keys) {
2123
- _optionalChain([this, 'access', _65 => _65.grid, 'access', _66 => _66.get, 'call', _67 => _67(key), 'optionalAccess', _68 => _68.delete, 'call', _69 => _69(id)]);
2315
+ _optionalChain([this, 'access', _82 => _82.grid, 'access', _83 => _83.get, 'call', _84 => _84(key), 'optionalAccess', _85 => _85.delete, 'call', _86 => _86(id)]);
2124
2316
  }
2125
2317
  this.entityCells.delete(id);
2126
2318
  }
@@ -2156,20 +2348,20 @@ var SpatialHashGrid = (_class6 = class {
2156
2348
  }, _class6);
2157
2349
 
2158
2350
  // src/tree/DOMPortalEntity.ts
2159
- var DOMPortalEntity = (_class7 = class extends _chunkTQ4H357Qjs.Entity {
2351
+ var DOMPortalEntity = (_class7 = class extends _chunkDWMQYHNCjs.Entity {
2160
2352
 
2161
- __init64() {this.isDOMPortal = true}
2162
- __init65() {this.domListeners = []}
2163
- __init66() {this.resizeObserver = null}
2164
- __init67() {this.cachedWidth = 100}
2165
- __init68() {this.cachedHeight = 100}
2166
- __init69() {this.lastWidth = ""}
2167
- __init70() {this.lastHeight = ""}
2168
- __init71() {this.lastTransform = ""}
2169
- __init72() {this.lastZIndex = ""}
2170
- __init73() {this.lastOpacity = ""}
2353
+ __init69() {this.isDOMPortal = true}
2354
+ __init70() {this.domListeners = []}
2355
+ __init71() {this.resizeObserver = null}
2356
+ __init72() {this.cachedWidth = 100}
2357
+ __init73() {this.cachedHeight = 100}
2358
+ __init74() {this.lastWidth = ""}
2359
+ __init75() {this.lastHeight = ""}
2360
+ __init76() {this.lastTransform = ""}
2361
+ __init77() {this.lastZIndex = ""}
2362
+ __init78() {this.lastOpacity = ""}
2171
2363
  constructor(domElement, width, height, id) {
2172
- super(id);_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);;
2364
+ super(id);_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);;
2173
2365
  this.domElement = domElement;
2174
2366
  this.width = _nullishCoalesce(width, () => ( 0));
2175
2367
  this.height = _nullishCoalesce(height, () => ( 0));
@@ -2191,7 +2383,7 @@ var DOMPortalEntity = (_class7 = class extends _chunkTQ4H357Qjs.Entity {
2191
2383
  const events = ["click", "pointerdown", "pointerup", "pointermove", "wheel"];
2192
2384
  for (const type of events) {
2193
2385
  const handler = (e) => {
2194
- this.dispatchEvent(new (0, _chunkTQ4H357Qjs.VectoJSEvent)(type, this, e));
2386
+ this.dispatchEvent(new (0, _chunkDWMQYHNCjs.VectoJSEvent)(type, this, e));
2195
2387
  };
2196
2388
  this.domElement.addEventListener(type, handler);
2197
2389
  this.domListeners.push({ type, handler, capture: false });
@@ -2202,7 +2394,7 @@ var DOMPortalEntity = (_class7 = class extends _chunkTQ4H357Qjs.Entity {
2202
2394
  ];
2203
2395
  for (const { native, vecto } of hoverEvents) {
2204
2396
  const handler = (e) => {
2205
- this.dispatchEvent(new (0, _chunkTQ4H357Qjs.VectoJSEvent)(vecto, this, e, false));
2397
+ this.dispatchEvent(new (0, _chunkDWMQYHNCjs.VectoJSEvent)(vecto, this, e, false));
2206
2398
  };
2207
2399
  this.domElement.addEventListener(native, handler);
2208
2400
  this.domListeners.push({ type: native, handler, capture: false });
@@ -2210,7 +2402,7 @@ var DOMPortalEntity = (_class7 = class extends _chunkTQ4H357Qjs.Entity {
2210
2402
  const focusEvents = ["focus", "blur"];
2211
2403
  for (const type of focusEvents) {
2212
2404
  const handler = (e) => {
2213
- this.dispatchEvent(new (0, _chunkTQ4H357Qjs.VectoJSEvent)(type, this, e, true));
2405
+ this.dispatchEvent(new (0, _chunkDWMQYHNCjs.VectoJSEvent)(type, this, e, true));
2214
2406
  };
2215
2407
  this.domElement.addEventListener(type, handler, true);
2216
2408
  this.domListeners.push({ type, handler, capture: true });
@@ -2249,8 +2441,8 @@ var DOMPortalEntity = (_class7 = class extends _chunkTQ4H357Qjs.Entity {
2249
2441
  }, _class7);
2250
2442
 
2251
2443
  // src/index.ts
2252
- Scene.registerWebGLPointRendererCreator(_chunkTPNADFNNjs.createWebGLPointRenderer);
2253
- Scene.registerWebGPUParticleSystemManager(_chunkTPNADFNNjs.WebGPUParticleSystemManager);
2444
+ Scene.registerWebGLPointRendererCreator(_chunkGTQRCY6Wjs.createWebGLPointRenderer);
2445
+ Scene.registerWebGPUParticleSystemManager(_chunkGTQRCY6Wjs.WebGPUParticleSystemManager);
2254
2446
 
2255
2447
 
2256
2448
 
@@ -2295,4 +2487,4 @@ Scene.registerWebGPUParticleSystemManager(_chunkTPNADFNNjs.WebGPUParticleSystemM
2295
2487
 
2296
2488
 
2297
2489
 
2298
- exports.ArabicShaper = _chunk76NMTLYLjs.ArabicShaper; exports.BidiResolver = _chunk76NMTLYLjs.BidiResolver; exports.CanvasRenderer = _chunkTPNADFNNjs.CanvasRenderer; exports.ComputeParticleEntity = ComputeParticleEntity; exports.DOMPortalEntity = DOMPortalEntity; exports.Easing = _chunkTQ4H357Qjs.Easing; exports.Entity = _chunkTQ4H357Qjs.Entity; exports.GridTextEntity = GridTextEntity; exports.LayoutEngine = _chunk5KLB6BEZjs.LayoutEngine; exports.LayoutResultBuffer = _chunk5KLB6BEZjs.LayoutResultBuffer; exports.LayoutWorkerManager = _chunk76NMTLYLjs.LayoutWorkerManager; exports.MSDFFont = _chunkTQ4H357Qjs.MSDFFont; exports.MSDFTextEntity = _chunkTQ4H357Qjs.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.SVGEntity = _chunkTQ4H357Qjs.SVGEntity; exports.SVGRenderer = _chunkTPNADFNNjs.SVGRenderer; exports.Scene = Scene; exports.SpatialHashGrid = SpatialHashGrid; exports.SplineEntity = SplineEntity; exports.SpringDriver = _chunkTQ4H357Qjs.SpringDriver; exports.SpringPhysics = _chunkTQ4H357Qjs.SpringPhysics; exports.TextEntity = TextEntity; exports.TweenDriver = _chunkTQ4H357Qjs.TweenDriver; exports.VectoJSEvent = _chunkTQ4H357Qjs.VectoJSEvent; exports.WebGPUParticleSystemManager = _chunkTPNADFNNjs.WebGPUParticleSystemManager; exports.computeLineSegments = _chunk5KLB6BEZjs.computeLineSegments; exports.createCanvasMeasurer = _chunk5KLB6BEZjs.createCanvasMeasurer; exports.createWebGLPointRenderer = _chunkTPNADFNNjs.createWebGLPointRenderer; exports.isSafeUrl = _chunkTPNADFNNjs.isSafeUrl; exports.isTweenConfig = _chunkTQ4H357Qjs.isTweenConfig; exports.loadSpline = loadSpline; exports.parseColorToRGBA = _chunkTPNADFNNjs.parseColorToRGBA; exports.polySegmentToBezier = polySegmentToBezier; exports.sanitizeUrl = _chunkTPNADFNNjs.sanitizeUrl;
2490
+ exports.ArabicShaper = _chunkCTZQOM5Zjs.ArabicShaper; exports.BidiResolver = _chunkCTZQOM5Zjs.BidiResolver; exports.CanvasRenderer = _chunkGTQRCY6Wjs.CanvasRenderer; exports.ComputeParticleEntity = ComputeParticleEntity; exports.DOMPortalEntity = DOMPortalEntity; exports.Easing = _chunkDWMQYHNCjs.Easing; exports.Entity = _chunkDWMQYHNCjs.Entity; exports.GridTextEntity = GridTextEntity; exports.LayoutEngine = _chunkBWLNEJJWjs.LayoutEngine; exports.LayoutResultBuffer = _chunkBWLNEJJWjs.LayoutResultBuffer; exports.LayoutWorkerManager = _chunkCTZQOM5Zjs.LayoutWorkerManager; exports.MSDFFont = _chunkDWMQYHNCjs.MSDFFont; exports.MSDFTextEntity = _chunkDWMQYHNCjs.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.SVGEntity = _chunkDWMQYHNCjs.SVGEntity; exports.SVGRenderer = _chunkGTQRCY6Wjs.SVGRenderer; exports.Scene = Scene; exports.SpatialHashGrid = SpatialHashGrid; exports.SplineEntity = SplineEntity; exports.SpringDriver = _chunkDWMQYHNCjs.SpringDriver; exports.SpringPhysics = _chunkDWMQYHNCjs.SpringPhysics; exports.TextEntity = TextEntity; exports.TweenDriver = _chunkDWMQYHNCjs.TweenDriver; exports.VectoJSEvent = _chunkDWMQYHNCjs.VectoJSEvent; exports.WebGPUParticleSystemManager = _chunkGTQRCY6Wjs.WebGPUParticleSystemManager; exports.computeLineSegments = _chunkBWLNEJJWjs.computeLineSegments; exports.createCanvasMeasurer = _chunkBWLNEJJWjs.createCanvasMeasurer; exports.createWebGLPointRenderer = _chunkGTQRCY6Wjs.createWebGLPointRenderer; exports.isSafeUrl = _chunkGTQRCY6Wjs.isSafeUrl; exports.isTweenConfig = _chunkDWMQYHNCjs.isTweenConfig; exports.loadSpline = loadSpline; exports.parseColorToRGBA = _chunkGTQRCY6Wjs.parseColorToRGBA; exports.polySegmentToBezier = polySegmentToBezier; exports.sanitizeUrl = _chunkGTQRCY6Wjs.sanitizeUrl;