@vectojs/core 1.35.1 → 1.35.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -231,6 +231,10 @@ var CanvasRenderer = class _CanvasRenderer {
231
231
  this.ctx.canvas.style.width = `${width}px`;
232
232
  this.ctx.canvas.style.height = `${height}px`;
233
233
  this.ctx.scale(dpr, dpr);
234
+ this._cachedFont = "";
235
+ this._cachedFill = "";
236
+ this.batchActive = false;
237
+ this.batchCount = 0;
234
238
  }
235
239
  /** Whether the 2D context is currently lost (drawing is a no-op until it is
236
240
  * restored). The owner skips its render pass while this is true. */
@@ -378,10 +382,12 @@ var CanvasRenderer = class _CanvasRenderer {
378
382
  flush() {
379
383
  if (!this.batchActive) return;
380
384
  if (this.counters) this.counters.flushes++;
385
+ const prevAlpha = this.ctx.globalAlpha;
381
386
  this.ctx.globalAlpha = this.batchAlpha;
382
387
  this.ctx.fillStyle = this.batchColor;
383
388
  this.ctx.fill();
384
- this.ctx.globalAlpha = 1;
389
+ this.ctx.globalAlpha = prevAlpha;
390
+ this._cachedFill = this.batchColor;
385
391
  this.batchActive = false;
386
392
  this.batchCount = 0;
387
393
  }
@@ -463,17 +469,22 @@ function hasSafeSchemeOrIsRelative(url) {
463
469
  if (!SCHEME_PATTERN.test(candidate)) return true;
464
470
  return SAFE_SCHEMES.has(`${candidate.toLowerCase()}:`);
465
471
  }
472
+ var CHAR_REF = /&(?:#\d+|#x[\da-f]+|colon|tab|newline);/i;
473
+ function decodeCharacterReferences(value) {
474
+ if (value.indexOf("&") < 0 || !CHAR_REF.test(value)) return value;
475
+ return value.replace(/&#x([\da-f]+);/gi, (_, hex) => String.fromCodePoint(parseInt(hex, 16))).replace(/&#(\d+);/g, (_, dec) => String.fromCodePoint(parseInt(dec, 10))).replace(/&colon;/gi, ":").replace(/&tab;/gi, " ").replace(/&newline;/gi, "\n");
476
+ }
466
477
  function sanitizeUrl(href) {
467
478
  if (typeof href !== "string") return "";
468
479
  const trimmed = href.trim();
469
480
  if (trimmed === "") return "";
470
- return hasSafeSchemeOrIsRelative(trimmed) ? trimmed : "#";
481
+ return hasSafeSchemeOrIsRelative(decodeCharacterReferences(trimmed)) ? trimmed : "#";
471
482
  }
472
483
  function isSafeUrl(urlStr) {
473
484
  if (typeof urlStr !== "string") return false;
474
485
  const trimmed = urlStr.trim();
475
486
  if (trimmed === "") return false;
476
- return hasSafeSchemeOrIsRelative(trimmed);
487
+ return hasSafeSchemeOrIsRelative(decodeCharacterReferences(trimmed));
477
488
  }
478
489
 
479
490
  // src/renderer/SVGRenderer.ts
@@ -942,6 +953,7 @@ var SVGRenderer = class {
942
953
  var CACHE_MAX = 1e3;
943
954
  var cache = /* @__PURE__ */ new Map();
944
955
  var fallbackCtx;
956
+ var SENTINEL = "#010203";
945
957
  function fromHex(hex) {
946
958
  const h = hex.slice(1);
947
959
  const n = h.length;
@@ -976,7 +988,9 @@ function fromCanvas(css) {
976
988
  if (!fallbackCtx) fallbackCtx = document.createElement("canvas").getContext("2d");
977
989
  if (!fallbackCtx) return null;
978
990
  fallbackCtx.clearRect(0, 0, 1, 1);
991
+ fallbackCtx.fillStyle = SENTINEL;
979
992
  fallbackCtx.fillStyle = css;
993
+ if (fallbackCtx.fillStyle === SENTINEL) return null;
980
994
  fallbackCtx.fillRect(0, 0, 1, 1);
981
995
  const d = fallbackCtx.getImageData(0, 0, 1, 1).data;
982
996
  return [d[0] / 255, d[1] / 255, d[2] / 255, d[3] / 255];
@@ -1475,6 +1489,22 @@ function createWebGLPointRenderer(canvas) {
1475
1489
  let destroyed = false;
1476
1490
  const pointSizeRange = typeof gl.getParameter === "function" ? gl.getParameter(gl.ALIASED_POINT_SIZE_RANGE) : null;
1477
1491
  const maxPointSize = pointSizeRange ? pointSizeRange[1] : Infinity;
1492
+ const drawSprites = () => {
1493
+ if (spriteCount === 0 || !texture) return;
1494
+ const bytes = spriteCount * VERTS_PER_QUAD * PACKED_QUAD_VERT_STRIDE;
1495
+ gl.useProgram(spriteProgram);
1496
+ gl.bindVertexArray(spriteVAO);
1497
+ gl.bindBuffer(gl.ARRAY_BUFFER, spriteBuffer);
1498
+ gl.bufferData(gl.ARRAY_BUFFER, spriteData.u8.subarray(0, bytes), gl.DYNAMIC_DRAW);
1499
+ gl.activeTexture(gl.TEXTURE0);
1500
+ gl.bindTexture(gl.TEXTURE_2D, texture);
1501
+ gl.uniform1i(sUTex, 0);
1502
+ gl.uniform2f(sURes, logicalW, logicalH);
1503
+ ensureQuadIndices(spriteCount);
1504
+ gl.drawElements(gl.TRIANGLES, spriteCount * INDICES_PER_QUAD, gl.UNSIGNED_INT, 0);
1505
+ frameDrawCalls++;
1506
+ spriteCount = 0;
1507
+ };
1478
1508
  const drawGlyphs = () => {
1479
1509
  if (glyphCount === 0 || !msdfTexture) return;
1480
1510
  const bytes = glyphCount * VERTS_PER_QUAD * PACKED_QUAD_VERT_STRIDE;
@@ -1534,6 +1564,7 @@ function createWebGLPointRenderer(canvas) {
1534
1564
  setTexture(source) {
1535
1565
  if (source === textureSource && texture) return;
1536
1566
  if (!isSourceReady(source)) return;
1567
+ drawSprites();
1537
1568
  if (!texture) {
1538
1569
  texture = gl.createTexture();
1539
1570
  gl.bindTexture(gl.TEXTURE_2D, texture);
@@ -1718,18 +1749,7 @@ function createWebGLPointRenderer(canvas) {
1718
1749
  frameDrawCalls++;
1719
1750
  }
1720
1751
  if (spriteCount > 0 && texture) {
1721
- const bytes = spriteCount * VERTS_PER_QUAD * PACKED_QUAD_VERT_STRIDE;
1722
- gl.useProgram(spriteProgram);
1723
- gl.bindVertexArray(spriteVAO);
1724
- gl.bindBuffer(gl.ARRAY_BUFFER, spriteBuffer);
1725
- gl.bufferData(gl.ARRAY_BUFFER, spriteData.u8.subarray(0, bytes), gl.DYNAMIC_DRAW);
1726
- gl.activeTexture(gl.TEXTURE0);
1727
- gl.bindTexture(gl.TEXTURE_2D, texture);
1728
- gl.uniform1i(sUTex, 0);
1729
- gl.uniform2f(sURes, logicalW, logicalH);
1730
- ensureQuadIndices(spriteCount);
1731
- gl.drawElements(gl.TRIANGLES, spriteCount * INDICES_PER_QUAD, gl.UNSIGNED_INT, 0);
1732
- frameDrawCalls++;
1752
+ drawSprites();
1733
1753
  }
1734
1754
  gl.bindVertexArray(null);
1735
1755
  drawGlyphs();
@@ -390,6 +390,7 @@ var Entity = class {
390
390
  s.markStructureChanged?.();
391
391
  s.markDirty({ entity: this.id, reason: "child-added" });
392
392
  child._notifyMounted();
393
+ s._registerActiveDriverSubtree?.(child);
393
394
  }
394
395
  }
395
396
  /** Called once when this entity becomes attached to a live Scene. Override to react. */
@@ -419,6 +420,7 @@ var Entity = class {
419
420
  s.a11yNeedsReorder = true;
420
421
  s.markStructureChanged?.();
421
422
  s.markDirty({ entity: this.id, reason: "child-removed" });
423
+ s._unregisterActiveDriverSubtree?.(child);
422
424
  }
423
425
  }
424
426
  return this;
@@ -22,8 +22,8 @@ function setRendererDevMode(active) {
22
22
  function isRendererDevMode() {
23
23
  if (rendererDevMode) return true;
24
24
  const g = typeof globalThis !== "undefined" ? globalThis : void 0;
25
- if (_optionalChain([g, 'optionalAccess', _ => _.__DEV__])) return true;
26
- return _optionalChain([g, 'optionalAccess', _2 => _2.process, 'optionalAccess', _3 => _3.env, 'optionalAccess', _4 => _4.NODE_ENV]) === "development";
25
+ if (_optionalChain([g, 'optionalAccess', _2 => _2.__DEV__])) return true;
26
+ return _optionalChain([g, 'optionalAccess', _3 => _3.process, 'optionalAccess', _4 => _4.env, 'optionalAccess', _5 => _5.NODE_ENV]) === "development";
27
27
  }
28
28
  function installRendererDevTraps(renderer, label) {
29
29
  if (!isRendererDevMode()) return;
@@ -133,8 +133,8 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
133
133
  this.maxDPR = maxDPR;
134
134
  const dpr = this.effectiveDPR();
135
135
  this.appliedDPR = dpr;
136
- this.width = _nullishCoalesce(_optionalChain([size, 'optionalAccess', _5 => _5.width]), () => ( (typeof window !== "undefined" ? window.innerWidth : canvas.width || 0)));
137
- this.height = _nullishCoalesce(_optionalChain([size, 'optionalAccess', _6 => _6.height]), () => ( (typeof window !== "undefined" ? window.innerHeight : canvas.height || 0)));
136
+ this.width = _nullishCoalesce(_optionalChain([size, 'optionalAccess', _6 => _6.width]), () => ( (typeof window !== "undefined" ? window.innerWidth : canvas.width || 0)));
137
+ this.height = _nullishCoalesce(_optionalChain([size, 'optionalAccess', _7 => _7.height]), () => ( (typeof window !== "undefined" ? window.innerHeight : canvas.height || 0)));
138
138
  canvas.width = this.width * dpr;
139
139
  canvas.height = this.height * dpr;
140
140
  if (canvas.style) {
@@ -179,7 +179,7 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
179
179
  this.batchActive = false;
180
180
  this.batchCount = 0;
181
181
  this.contextLost = false;
182
- _optionalChain([this, 'access', _7 => _7.contextRestoredCb, 'optionalCall', _8 => _8()]);
182
+ _optionalChain([this, 'access', _8 => _8.contextRestoredCb, 'optionalCall', _9 => _9()]);
183
183
  });
184
184
  }
185
185
  /**
@@ -233,6 +233,10 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
233
233
  this.ctx.canvas.style.width = `${width}px`;
234
234
  this.ctx.canvas.style.height = `${height}px`;
235
235
  this.ctx.scale(dpr, dpr);
236
+ this._cachedFont = "";
237
+ this._cachedFill = "";
238
+ this.batchActive = false;
239
+ this.batchCount = 0;
236
240
  }
237
241
  /** Whether the 2D context is currently lost (drawing is a no-op until it is
238
242
  * restored). The owner skips its render pass while this is true. */
@@ -380,10 +384,12 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
380
384
  flush() {
381
385
  if (!this.batchActive) return;
382
386
  if (this.counters) this.counters.flushes++;
387
+ const prevAlpha = this.ctx.globalAlpha;
383
388
  this.ctx.globalAlpha = this.batchAlpha;
384
389
  this.ctx.fillStyle = this.batchColor;
385
390
  this.ctx.fill();
386
- this.ctx.globalAlpha = 1;
391
+ this.ctx.globalAlpha = prevAlpha;
392
+ this._cachedFill = this.batchColor;
387
393
  this.batchActive = false;
388
394
  this.batchCount = 0;
389
395
  }
@@ -465,17 +471,22 @@ function hasSafeSchemeOrIsRelative(url) {
465
471
  if (!SCHEME_PATTERN.test(candidate)) return true;
466
472
  return SAFE_SCHEMES.has(`${candidate.toLowerCase()}:`);
467
473
  }
474
+ var CHAR_REF = /&(?:#\d+|#x[\da-f]+|colon|tab|newline);/i;
475
+ function decodeCharacterReferences(value) {
476
+ if (value.indexOf("&") < 0 || !CHAR_REF.test(value)) return value;
477
+ return value.replace(/&#x([\da-f]+);/gi, (_, hex) => String.fromCodePoint(parseInt(hex, 16))).replace(/&#(\d+);/g, (_, dec) => String.fromCodePoint(parseInt(dec, 10))).replace(/&colon;/gi, ":").replace(/&tab;/gi, " ").replace(/&newline;/gi, "\n");
478
+ }
468
479
  function sanitizeUrl(href) {
469
480
  if (typeof href !== "string") return "";
470
481
  const trimmed = href.trim();
471
482
  if (trimmed === "") return "";
472
- return hasSafeSchemeOrIsRelative(trimmed) ? trimmed : "#";
483
+ return hasSafeSchemeOrIsRelative(decodeCharacterReferences(trimmed)) ? trimmed : "#";
473
484
  }
474
485
  function isSafeUrl(urlStr) {
475
486
  if (typeof urlStr !== "string") return false;
476
487
  const trimmed = urlStr.trim();
477
488
  if (trimmed === "") return false;
478
- return hasSafeSchemeOrIsRelative(trimmed);
489
+ return hasSafeSchemeOrIsRelative(decodeCharacterReferences(trimmed));
479
490
  }
480
491
 
481
492
  // src/renderer/SVGRenderer.ts
@@ -790,8 +801,8 @@ var SVGRenderer = (_class2 = class {
790
801
  }
791
802
  drawImage(source, dx, dy, dw, dh) {
792
803
  this.flush();
793
- const fromCanvas2 = typeof _optionalChain([source, 'optionalAccess', _9 => _9.toDataURL]) === "function";
794
- const rawHref = fromCanvas2 ? source.toDataURL() : _optionalChain([source, 'optionalAccess', _10 => _10.src]) || "";
804
+ const fromCanvas2 = typeof _optionalChain([source, 'optionalAccess', _10 => _10.toDataURL]) === "function";
805
+ const rawHref = fromCanvas2 ? source.toDataURL() : _optionalChain([source, 'optionalAccess', _11 => _11.src]) || "";
795
806
  const href = fromCanvas2 && this.isSafeRasterDataUrl(rawHref) ? rawHref : sanitizeUrl(String(rawHref));
796
807
  const transformStr = `matrix(${this.ma},${this.mb},${this.mc},${this.md},${this.me},${this.mf})`;
797
808
  if (href) {
@@ -944,6 +955,7 @@ var SVGRenderer = (_class2 = class {
944
955
  var CACHE_MAX = 1e3;
945
956
  var cache = /* @__PURE__ */ new Map();
946
957
  var fallbackCtx;
958
+ var SENTINEL = "#010203";
947
959
  function fromHex(hex) {
948
960
  const h = hex.slice(1);
949
961
  const n = h.length;
@@ -978,7 +990,9 @@ function fromCanvas(css) {
978
990
  if (!fallbackCtx) fallbackCtx = document.createElement("canvas").getContext("2d");
979
991
  if (!fallbackCtx) return null;
980
992
  fallbackCtx.clearRect(0, 0, 1, 1);
993
+ fallbackCtx.fillStyle = SENTINEL;
981
994
  fallbackCtx.fillStyle = css;
995
+ if (fallbackCtx.fillStyle === SENTINEL) return null;
982
996
  fallbackCtx.fillRect(0, 0, 1, 1);
983
997
  const d = fallbackCtx.getImageData(0, 0, 1, 1).data;
984
998
  return [d[0] / 255, d[1] / 255, d[2] / 255, d[3] / 255];
@@ -1477,6 +1491,22 @@ function createWebGLPointRenderer(canvas) {
1477
1491
  let destroyed = false;
1478
1492
  const pointSizeRange = typeof gl.getParameter === "function" ? gl.getParameter(gl.ALIASED_POINT_SIZE_RANGE) : null;
1479
1493
  const maxPointSize = pointSizeRange ? pointSizeRange[1] : Infinity;
1494
+ const drawSprites = () => {
1495
+ if (spriteCount === 0 || !texture) return;
1496
+ const bytes = spriteCount * VERTS_PER_QUAD * PACKED_QUAD_VERT_STRIDE;
1497
+ gl.useProgram(spriteProgram);
1498
+ gl.bindVertexArray(spriteVAO);
1499
+ gl.bindBuffer(gl.ARRAY_BUFFER, spriteBuffer);
1500
+ gl.bufferData(gl.ARRAY_BUFFER, spriteData.u8.subarray(0, bytes), gl.DYNAMIC_DRAW);
1501
+ gl.activeTexture(gl.TEXTURE0);
1502
+ gl.bindTexture(gl.TEXTURE_2D, texture);
1503
+ gl.uniform1i(sUTex, 0);
1504
+ gl.uniform2f(sURes, logicalW, logicalH);
1505
+ ensureQuadIndices(spriteCount);
1506
+ gl.drawElements(gl.TRIANGLES, spriteCount * INDICES_PER_QUAD, gl.UNSIGNED_INT, 0);
1507
+ frameDrawCalls++;
1508
+ spriteCount = 0;
1509
+ };
1480
1510
  const drawGlyphs = () => {
1481
1511
  if (glyphCount === 0 || !msdfTexture) return;
1482
1512
  const bytes = glyphCount * VERTS_PER_QUAD * PACKED_QUAD_VERT_STRIDE;
@@ -1536,6 +1566,7 @@ function createWebGLPointRenderer(canvas) {
1536
1566
  setTexture(source) {
1537
1567
  if (source === textureSource && texture) return;
1538
1568
  if (!isSourceReady(source)) return;
1569
+ drawSprites();
1539
1570
  if (!texture) {
1540
1571
  texture = gl.createTexture();
1541
1572
  gl.bindTexture(gl.TEXTURE_2D, texture);
@@ -1720,18 +1751,7 @@ function createWebGLPointRenderer(canvas) {
1720
1751
  frameDrawCalls++;
1721
1752
  }
1722
1753
  if (spriteCount > 0 && texture) {
1723
- const bytes = spriteCount * VERTS_PER_QUAD * PACKED_QUAD_VERT_STRIDE;
1724
- gl.useProgram(spriteProgram);
1725
- gl.bindVertexArray(spriteVAO);
1726
- gl.bindBuffer(gl.ARRAY_BUFFER, spriteBuffer);
1727
- gl.bufferData(gl.ARRAY_BUFFER, spriteData.u8.subarray(0, bytes), gl.DYNAMIC_DRAW);
1728
- gl.activeTexture(gl.TEXTURE0);
1729
- gl.bindTexture(gl.TEXTURE_2D, texture);
1730
- gl.uniform1i(sUTex, 0);
1731
- gl.uniform2f(sURes, logicalW, logicalH);
1732
- ensureQuadIndices(spriteCount);
1733
- gl.drawElements(gl.TRIANGLES, spriteCount * INDICES_PER_QUAD, gl.UNSIGNED_INT, 0);
1734
- frameDrawCalls++;
1754
+ drawSprites();
1735
1755
  }
1736
1756
  gl.bindVertexArray(null);
1737
1757
  drawGlyphs();
@@ -392,6 +392,7 @@ var Entity = (_class2 = class {
392
392
  _optionalChain([s, 'access', _43 => _43.markStructureChanged, 'optionalCall', _44 => _44()]);
393
393
  s.markDirty({ entity: this.id, reason: "child-added" });
394
394
  child._notifyMounted();
395
+ _optionalChain([s, 'access', _45 => _45._registerActiveDriverSubtree, 'optionalCall', _46 => _46(child)]);
395
396
  }
396
397
  }
397
398
  /** Called once when this entity becomes attached to a live Scene. Override to react. */
@@ -419,8 +420,9 @@ var Entity = (_class2 = class {
419
420
  if (s) {
420
421
  s.detachA11y(child);
421
422
  s.a11yNeedsReorder = true;
422
- _optionalChain([s, 'access', _45 => _45.markStructureChanged, 'optionalCall', _46 => _46()]);
423
+ _optionalChain([s, 'access', _47 => _47.markStructureChanged, 'optionalCall', _48 => _48()]);
423
424
  s.markDirty({ entity: this.id, reason: "child-removed" });
425
+ _optionalChain([s, 'access', _49 => _49._unregisterActiveDriverSubtree, 'optionalCall', _50 => _50(child)]);
424
426
  }
425
427
  }
426
428
  return this;
@@ -474,7 +476,7 @@ var Entity = (_class2 = class {
474
476
  startTime: -1,
475
477
  startProps: {}
476
478
  });
477
- _optionalChain([this, 'access', _47 => _47.scene, 'optionalAccess', _48 => _48.markDirty, 'call', _49 => _49({ entity: this.id, reason: "animation-start" })]);
479
+ _optionalChain([this, 'access', _51 => _51.scene, 'optionalAccess', _52 => _52.markDirty, 'call', _53 => _53({ entity: this.id, reason: "animation-start" })]);
478
480
  return this;
479
481
  }
480
482
  /** Write a driver-computed value to a backing field without re-triggering the setter. */
@@ -521,26 +523,26 @@ var Entity = (_class2 = class {
521
523
  * that need to seed a starting state (e.g. the presence helper's enter `from`).
522
524
  */
523
525
  setImmediate(prop, v) {
524
- const existing = _optionalChain([this, 'access', _50 => _50._drivers, 'optionalAccess', _51 => _51.get, 'call', _52 => _52(prop)]);
526
+ const existing = _optionalChain([this, 'access', _54 => _54._drivers, 'optionalAccess', _55 => _55.get, 'call', _56 => _56(prop)]);
525
527
  if (existing) this._settleDriver(existing);
526
- _optionalChain([this, 'access', _53 => _53._drivers, 'optionalAccess', _54 => _54.delete, 'call', _55 => _55(prop)]);
528
+ _optionalChain([this, 'access', _57 => _57._drivers, 'optionalAccess', _58 => _58.delete, 'call', _59 => _59(prop)]);
527
529
  this._applyAnimated(prop, v);
528
530
  }
529
531
  _settleDriver(driver) {
530
532
  const active = driver;
531
533
  const onDone = active.onDone;
532
534
  active.onDone = void 0;
533
- _optionalChain([onDone, 'optionalCall', _56 => _56()]);
535
+ _optionalChain([onDone, 'optionalCall', _60 => _60()]);
534
536
  }
535
537
  _spawnDriver(prop, to, cfg) {
536
- if (prop !== "opacity" && _optionalChain([this, 'access', _57 => _57.scene, 'optionalAccess', _58 => _58.prefersReducedMotion])) {
537
- const existing2 = _optionalChain([this, 'access', _59 => _59._drivers, 'optionalAccess', _60 => _60.get, 'call', _61 => _61(prop)]);
538
+ if (prop !== "opacity" && _optionalChain([this, 'access', _61 => _61.scene, 'optionalAccess', _62 => _62.prefersReducedMotion])) {
539
+ const existing2 = _optionalChain([this, 'access', _63 => _63._drivers, 'optionalAccess', _64 => _64.get, 'call', _65 => _65(prop)]);
538
540
  if (existing2) this._settleDriver(existing2);
539
- _optionalChain([this, 'access', _62 => _62._drivers, 'optionalAccess', _63 => _63.delete, 'call', _64 => _64(prop)]);
541
+ _optionalChain([this, 'access', _66 => _66._drivers, 'optionalAccess', _67 => _67.delete, 'call', _68 => _68(prop)]);
540
542
  this._applyAnimated(prop, to);
541
543
  return;
542
544
  }
543
- const existing = _optionalChain([this, 'access', _65 => _65._drivers, 'optionalAccess', _66 => _66.get, 'call', _67 => _67(prop)]);
545
+ const existing = _optionalChain([this, 'access', _69 => _69._drivers, 'optionalAccess', _70 => _70.get, 'call', _71 => _71(prop)]);
544
546
  if (existing) {
545
547
  this._settleDriver(existing);
546
548
  existing.retarget(to);
@@ -549,12 +551,12 @@ var Entity = (_class2 = class {
549
551
  const from = this._currentOf(prop);
550
552
  const driver = _animation.isTweenConfig.call(void 0, cfg) ? new (0, _animation.TweenDriver)(from, to, cfg) : new (0, _animation.SpringDriver)(from, to, cfg === "spring" ? {} : cfg);
551
553
  (this._drivers ??= /* @__PURE__ */ new Map()).set(prop, driver);
552
- _optionalChain([this, 'access', _68 => _68.scene, 'optionalAccess', _69 => _69.markDirty, 'call', _70 => _70({ entity: this.id, reason: "driver-added" })]);
553
- _optionalChain([this, 'access', _71 => _71.scene, 'optionalAccess', _72 => _72._registerActiveDriverEntity, 'call', _73 => _73(this)]);
554
+ _optionalChain([this, 'access', _72 => _72.scene, 'optionalAccess', _73 => _73.markDirty, 'call', _74 => _74({ entity: this.id, reason: "driver-added" })]);
555
+ _optionalChain([this, 'access', _75 => _75.scene, 'optionalAccess', _76 => _76._registerActiveDriverEntity, 'call', _77 => _77(this)]);
554
556
  }
555
557
  /** Assignment path when a declarative transition is configured for `prop`. */
556
558
  _animateProp(prop, to) {
557
- const cfg = _optionalChain([this, 'access', _74 => _74._transitions, 'optionalAccess', _75 => _75.get, 'call', _76 => _76(prop)]);
559
+ const cfg = _optionalChain([this, 'access', _78 => _78._transitions, 'optionalAccess', _79 => _79.get, 'call', _80 => _80(prop)]);
558
560
  if (!cfg) {
559
561
  this._applyAnimated(prop, to);
560
562
  return;
@@ -583,7 +585,7 @@ var Entity = (_class2 = class {
583
585
  entries.map(
584
586
  (e) => new Promise((resolve) => {
585
587
  this._spawnDriver(e[0], e[1], cfg);
586
- const d = _optionalChain([this, 'access', _77 => _77._drivers, 'optionalAccess', _78 => _78.get, 'call', _79 => _79(e[0])]);
588
+ const d = _optionalChain([this, 'access', _81 => _81._drivers, 'optionalAccess', _82 => _82.get, 'call', _83 => _83(e[0])]);
587
589
  if (!d)
588
590
  resolve();
589
591
  else d.onDone = resolve;
@@ -605,7 +607,7 @@ var Entity = (_class2 = class {
605
607
  this._applyAnimated(prop, driver.value);
606
608
  }
607
609
  }
608
- _optionalChain([this, 'access', _80 => _80.scene, 'optionalAccess', _81 => _81.markDirty, 'call', _82 => _82({ entity: this.id, reason: "driver-tick" })]);
610
+ _optionalChain([this, 'access', _84 => _84.scene, 'optionalAccess', _85 => _85.markDirty, 'call', _86 => _86({ entity: this.id, reason: "driver-tick" })]);
609
611
  }
610
612
  /**
611
613
  * Internal: this entity's active-driver map (read-only view), or `null` if
@@ -630,7 +632,7 @@ var Entity = (_class2 = class {
630
632
  if (driver.isDone()) {
631
633
  this._applyAnimated(prop, driver.target);
632
634
  this._settleDriver(driver);
633
- _optionalChain([this, 'access', _83 => _83._drivers, 'optionalAccess', _84 => _84.delete, 'call', _85 => _85(prop)]);
635
+ _optionalChain([this, 'access', _87 => _87._drivers, 'optionalAccess', _88 => _88.delete, 'call', _89 => _89(prop)]);
634
636
  } else {
635
637
  this._applyAnimated(prop, driver.value);
636
638
  }
@@ -691,7 +693,7 @@ var Entity = (_class2 = class {
691
693
  * @example entity.on('click', (e) => console.log('clicked', e));
692
694
  */
693
695
  on(event, callback, options) {
694
- const map = _optionalChain([options, 'optionalAccess', _86 => _86.capture]) ? this.captureListeners ??= /* @__PURE__ */ new Map() : this.listeners ??= /* @__PURE__ */ new Map();
696
+ const map = _optionalChain([options, 'optionalAccess', _90 => _90.capture]) ? this.captureListeners ??= /* @__PURE__ */ new Map() : this.listeners ??= /* @__PURE__ */ new Map();
695
697
  if (!map.has(event)) {
696
698
  map.set(event, []);
697
699
  }
@@ -707,7 +709,7 @@ var Entity = (_class2 = class {
707
709
  * @returns `this` for method chaining.
708
710
  */
709
711
  off(event, callback, options) {
710
- const handlers = _optionalChain([(_optionalChain([options, 'optionalAccess', _87 => _87.capture]) ? this.captureListeners : this.listeners), 'optionalAccess', _88 => _88.get, 'call', _89 => _89(event)]);
712
+ const handlers = _optionalChain([(_optionalChain([options, 'optionalAccess', _91 => _91.capture]) ? this.captureListeners : this.listeners), 'optionalAccess', _92 => _92.get, 'call', _93 => _93(event)]);
711
713
  if (handlers) {
712
714
  const idx = handlers.indexOf(callback);
713
715
  if (idx !== -1) handlers.splice(idx, 1);
@@ -744,8 +746,8 @@ var Entity = (_class2 = class {
744
746
  }
745
747
  this._drivers.clear();
746
748
  }
747
- _optionalChain([this, 'access', _90 => _90.listeners, 'optionalAccess', _91 => _91.clear, 'call', _92 => _92()]);
748
- _optionalChain([this, 'access', _93 => _93.captureListeners, 'optionalAccess', _94 => _94.clear, 'call', _95 => _95()]);
749
+ _optionalChain([this, 'access', _94 => _94.listeners, 'optionalAccess', _95 => _95.clear, 'call', _96 => _96()]);
750
+ _optionalChain([this, 'access', _97 => _97.captureListeners, 'optionalAccess', _98 => _98.clear, 'call', _99 => _99()]);
749
751
  if (this.parent) {
750
752
  this.parent.remove(this);
751
753
  }
@@ -760,7 +762,7 @@ var Entity = (_class2 = class {
760
762
  * @param payload - Arbitrary data forwarded to each listener.
761
763
  */
762
764
  emit(event, payload) {
763
- const handlers = _optionalChain([this, 'access', _96 => _96.listeners, 'optionalAccess', _97 => _97.get, 'call', _98 => _98(event)]);
765
+ const handlers = _optionalChain([this, 'access', _100 => _100.listeners, 'optionalAccess', _101 => _101.get, 'call', _102 => _102(event)]);
764
766
  if (handlers) {
765
767
  handlers.forEach((h) => h(payload));
766
768
  }
@@ -774,19 +776,19 @@ var Entity = (_class2 = class {
774
776
  * with `requestAnimationFrame(() => document.getElementById(id)?.focus())`.
775
777
  */
776
778
  focus() {
777
- const el = _optionalChain([this, 'access', _99 => _99.scene, 'optionalAccess', _100 => _100.getA11yElement, 'call', _101 => _101(this.id)]);
779
+ const el = _optionalChain([this, 'access', _103 => _103.scene, 'optionalAccess', _104 => _104.getA11yElement, 'call', _105 => _105(this.id)]);
778
780
  if (el) {
779
781
  el.focus();
780
782
  return;
781
783
  }
782
784
  requestAnimationFrame(() => {
783
- const retry = _optionalChain([this, 'access', _102 => _102.scene, 'optionalAccess', _103 => _103.getA11yElement, 'call', _104 => _104(this.id)]);
785
+ const retry = _optionalChain([this, 'access', _106 => _106.scene, 'optionalAccess', _107 => _107.getA11yElement, 'call', _108 => _108(this.id)]);
784
786
  if (retry) retry.focus();
785
787
  });
786
788
  }
787
789
  /** Run one node's listeners for the event, honoring stopImmediatePropagation. */
788
790
  fireListeners(node, map, event) {
789
- const handlers = _optionalChain([map, 'optionalAccess', _105 => _105.get, 'call', _106 => _106(event.type)]);
791
+ const handlers = _optionalChain([map, 'optionalAccess', _109 => _109.get, 'call', _110 => _110(event.type)]);
790
792
  if (!handlers) return;
791
793
  event.currentTarget = node;
792
794
  for (const h of handlers.slice()) {
@@ -1173,7 +1175,7 @@ var Entity = (_class2 = class {
1173
1175
  * @returns `true` if at least one animation or property driver remains.
1174
1176
  */
1175
1177
  hasPendingAnimations() {
1176
- return (_nullishCoalesce(_optionalChain([this, 'access', _107 => _107.animations, 'optionalAccess', _108 => _108.length]), () => ( 0))) > 0 || (_nullishCoalesce(_optionalChain([this, 'access', _109 => _109._drivers, 'optionalAccess', _110 => _110.size]), () => ( 0))) > 0;
1178
+ return (_nullishCoalesce(_optionalChain([this, 'access', _111 => _111.animations, 'optionalAccess', _112 => _112.length]), () => ( 0))) > 0 || (_nullishCoalesce(_optionalChain([this, 'access', _113 => _113._drivers, 'optionalAccess', _114 => _114.size]), () => ( 0))) > 0;
1177
1179
  }
1178
1180
  }, _class2);
1179
1181
 
@@ -1261,7 +1263,7 @@ var MSDFTextEntity = (_class3 = class extends Entity {
1261
1263
  const target = this.texture;
1262
1264
  this.atlasDecodeHandler = () => {
1263
1265
  this.detachAtlasDecodeListener();
1264
- _optionalChain([this, 'access', _111 => _111.scene, 'optionalAccess', _112 => _112.markDirty, 'call', _113 => _113()]);
1266
+ _optionalChain([this, 'access', _115 => _115.scene, 'optionalAccess', _116 => _116.markDirty, 'call', _117 => _117()]);
1265
1267
  };
1266
1268
  target.addEventListener("load", this.atlasDecodeHandler);
1267
1269
  target.addEventListener("error", this.atlasDecodeHandler);
@@ -1343,7 +1345,7 @@ var MSDFTextEntity = (_class3 = class extends Entity {
1343
1345
  this.layoutResult = res;
1344
1346
  this.contentEpoch++;
1345
1347
  this.rebuildProjectionLines();
1346
- _optionalChain([this, 'access', _114 => _114.scene, 'optionalAccess', _115 => _115.markDirty, 'call', _116 => _116()]);
1348
+ _optionalChain([this, 'access', _118 => _118.scene, 'optionalAccess', _119 => _119.markDirty, 'call', _120 => _120()]);
1347
1349
  }
1348
1350
  });
1349
1351
  }
@@ -1362,8 +1364,8 @@ var MSDFTextEntity = (_class3 = class extends Entity {
1362
1364
  getContentProjection() {
1363
1365
  if (!this.text) return null;
1364
1366
  const metrics = this.font.data.metrics;
1365
- const asc = _nullishCoalesce(_optionalChain([metrics, 'optionalAccess', _117 => _117.ascender]), () => ( 0.8));
1366
- const desc = _nullishCoalesce(_optionalChain([metrics, 'optionalAccess', _118 => _118.descender]), () => ( -0.2));
1367
+ const asc = _nullishCoalesce(_optionalChain([metrics, 'optionalAccess', _121 => _121.ascender]), () => ( 0.8));
1368
+ const desc = _nullishCoalesce(_optionalChain([metrics, 'optionalAccess', _122 => _122.descender]), () => ( -0.2));
1367
1369
  const actualLineHeight = _nullishCoalesce(this.lineHeight, () => ( this.fontSize * (asc - desc)));
1368
1370
  return {
1369
1371
  text: this.text,
@@ -1398,8 +1400,8 @@ var MSDFTextEntity = (_class3 = class extends Entity {
1398
1400
  return;
1399
1401
  }
1400
1402
  const metrics = this.font.data.metrics;
1401
- const asc = _nullishCoalesce(_optionalChain([metrics, 'optionalAccess', _119 => _119.ascender]), () => ( 0.8));
1402
- const desc = _nullishCoalesce(_optionalChain([metrics, 'optionalAccess', _120 => _120.descender]), () => ( -0.2));
1403
+ const asc = _nullishCoalesce(_optionalChain([metrics, 'optionalAccess', _123 => _123.ascender]), () => ( 0.8));
1404
+ const desc = _nullishCoalesce(_optionalChain([metrics, 'optionalAccess', _124 => _124.descender]), () => ( -0.2));
1403
1405
  const actualLineHeight = _nullishCoalesce(this.lineHeight, () => ( this.fontSize * (asc - desc)));
1404
1406
  const baseline = asc * this.fontSize;
1405
1407
  const srcIdx = [];
@@ -99,6 +99,12 @@ export declare class SplineEntity extends Entity {
99
99
  /** Logical (CSS-pixel) size of the baked bitmap — the blit destination size. */
100
100
  private bakedWidth;
101
101
  private bakedHeight;
102
+ /**
103
+ * Device-pixel ratio the bitmap was rasterized at. Tracked so a DPR change
104
+ * (browser zoom, a monitor move, a renderer whose clamp changed) re-bakes
105
+ * instead of blitting a bitmap at the wrong device density forever.
106
+ */
107
+ private bakedDPR;
102
108
  /**
103
109
  * Gradient strokes can't be baked to a solid-color bitmap; they render
104
110
  * per-frame. Derived from `_doc`, so it is NOT readonly — assigning a new