@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.
@@ -1,7 +1,7 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2;
2
2
 
3
3
 
4
- var _chunk76NMTLYLjs = require('./chunk-76NMTLYL.js');
4
+ var _chunkCTZQOM5Zjs = require('./chunk-CTZQOM5Z.js');
5
5
 
6
6
  // src/layout/LayoutEngine.ts
7
7
  function computeLineSegments(top, bottom, maxWidth, exclusions) {
@@ -148,8 +148,8 @@ var LayoutEngine = (_class = class {
148
148
  offset += paragraph.length + 1;
149
149
  continue;
150
150
  }
151
- const { shapedText, indexMap } = _chunk76NMTLYLjs.ArabicShaper.shapeArabic(paragraph);
152
- const levels = _chunk76NMTLYLjs.BidiResolver.resolveLevels(shapedText);
151
+ const { shapedText, indexMap } = _chunkCTZQOM5Zjs.ArabicShaper.shapeArabic(paragraph);
152
+ const levels = _chunkCTZQOM5Zjs.BidiResolver.resolveLevels(shapedText);
153
153
  const words = [];
154
154
  let shapedCharIdx = 0;
155
155
  let pFallback = false;
@@ -193,7 +193,7 @@ var LayoutEngine = (_class = class {
193
193
  words,
194
194
  isEmpty: false,
195
195
  fallbackToCanvas: pFallback || void 0,
196
- baseLevel: _chunk76NMTLYLjs.BidiResolver.getBaseLevel(shapedText)
196
+ baseLevel: _chunkCTZQOM5Zjs.BidiResolver.getBaseLevel(shapedText)
197
197
  };
198
198
  if (this.paragraphCache.size > 1e3) this.paragraphCache.clear();
199
199
  this.paragraphCache.set(key, prepared);
@@ -265,8 +265,8 @@ var LayoutEngine = (_class = class {
265
265
  offset += paragraph.length + 1;
266
266
  continue;
267
267
  }
268
- const { shapedText, indexMap } = _chunk76NMTLYLjs.ArabicShaper.shapeArabic(paragraph);
269
- const levels = _chunk76NMTLYLjs.BidiResolver.resolveLevels(shapedText);
268
+ const { shapedText, indexMap } = _chunkCTZQOM5Zjs.ArabicShaper.shapeArabic(paragraph);
269
+ const levels = _chunkCTZQOM5Zjs.BidiResolver.resolveLevels(shapedText);
270
270
  const words = [];
271
271
  let shapedCharIdx = 0;
272
272
  let pFallback = false;
@@ -313,7 +313,7 @@ var LayoutEngine = (_class = class {
313
313
  words,
314
314
  isEmpty: false,
315
315
  fallbackToCanvas: pFallback || void 0,
316
- baseLevel: _chunk76NMTLYLjs.BidiResolver.getBaseLevel(shapedText)
316
+ baseLevel: _chunkCTZQOM5Zjs.BidiResolver.getBaseLevel(shapedText)
317
317
  };
318
318
  if (this.richParagraphCache.size > 1e3) this.richParagraphCache.clear();
319
319
  this.richParagraphCache.set(key, prepared);
@@ -368,7 +368,7 @@ var LayoutEngine = (_class = class {
368
368
  }
369
369
  for (const run of runs) {
370
370
  const runStartX = run[0].x;
371
- _chunk76NMTLYLjs.BidiResolver.reorderVisual(run, paragraphBaseLevel);
371
+ _chunkCTZQOM5Zjs.BidiResolver.reorderVisual(run, paragraphBaseLevel);
372
372
  let x = runStartX;
373
373
  for (const node of run) {
374
374
  node.x = x;
@@ -296,7 +296,7 @@ var BidiResolver = class _BidiResolver {
296
296
  };
297
297
 
298
298
  // src/layout/LayoutWorkerSource.ts
299
- var WORKER_SOURCE_STRING = '"use strict";(()=>{var k=new Map;function n(t){return typeof t=="number"&&Number.isFinite(t)}function H(t){return t.origin?t.origin===self.location.origin:!0}function M(t){if(!t||typeof t!="object")return!1;let e=t;return typeof e.id=="string"&&n(e.seqId)&&typeof e.text=="string"&&typeof e.fontId=="string"&&(e.fontData===void 0||typeof e.fontData=="object")&&n(e.maxWidth)&&n(e.maxHeight)&&n(e.fontSize)&&(e.lineHeight===void 0||n(e.lineHeight))&&(e.letterSpacing===void 0||n(e.letterSpacing))}self.onmessage=t=>{if(!H(t)||!M(t.data))return;let{id:e,seqId:F,text:q,fontId:d,fontData:f,maxWidth:y,maxHeight:I,fontSize:i,lineHeight:A,letterSpacing:D}=t.data;f&&k.set(d,f);let s=k.get(d);if(!s)return;let g=[],l=[],p=[],m=[],o=0,a=0,h=s.metrics?.ascender??.8,W=s.metrics?.descender??-.2,b=A??i*(h-W),x=Array.from(q);for(let c=0;c<x.length;c++){let u=x[c].codePointAt(0),S=(s.glyphs?.find(C=>C.unicode===u)?.advance??1)*i;o+S>y&&u===32&&(o=0,a++),g.push(u),l.push(o);let w=a*b+h*i;p.push(w),m.push(-256),o+=S+(D??0)}let r={id:e,seqId:F,width:Math.min(o,y),height:(a+1)*b,codePoints:new Uint32Array(g),xCoords:new Float32Array(l),yCoords:new Float32Array(p),packedStyles:new Uint32Array(m)};self.postMessage(r,[r.codePoints.buffer,r.xCoords.buffer,r.yCoords.buffer,r.packedStyles.buffer])};})();\n';
299
+ var WORKER_SOURCE_STRING = '"use strict";(()=>{var C=new Map;function f(t){return typeof t=="number"&&Number.isFinite(t)}function U(t){return t.origin?t.origin===self.location.origin:!0}function j(t){if(!t||typeof t!="object")return!1;let e=t;return typeof e.id=="string"&&f(e.seqId)&&typeof e.text=="string"&&typeof e.fontId=="string"&&(e.fontData===void 0||typeof e.fontData=="object")&&f(e.maxWidth)&&f(e.maxHeight)&&f(e.fontSize)&&(e.lineHeight===void 0||f(e.lineHeight))&&(e.letterSpacing===void 0||f(e.letterSpacing))}self.onmessage=t=>{if(!U(t)||!j(t.data))return;let{id:e,seqId:D,text:L,fontId:k,fontData:S,maxWidth:H,maxHeight:z,fontSize:d,lineHeight:I,letterSpacing:M}=t.data;S&&C.set(k,S);let g=C.get(k);if(!g)return;let m=[],i=[],h=[],w=[],n=0,c=0,r=0,o=-1,p=g.metrics?.ascender??.8,R=g.metrics?.descender??-.2,b=I??d*(p-R),P=M??0,F=new Map;for(let s of g.glyphs??[])F.set(s.unicode,s.advance);let x=()=>{n>r&&(r=n),n=0,c++,o=-1},A=Array.from(L);for(let s=0;s<A.length;s++){let a=A[s].codePointAt(0);if(a===10){x();continue}let W=(F.get(a)??1)*d,q=a>=11904;if(q&&(o=-1),n+W>H&&n>0){if(a===32){x();continue}if(o>=0&&i[o]>0){let l=i[o];l>r&&(r=l),c++;let v=c*b+p*d;for(let y=o;y<i.length;y++)i[y]-=l,h[y]=v;n-=l}else x()}a===32?o=-1:o===-1&&!q&&(o=m.length),m.push(a),i.push(n),h.push(c*b+p*d),w.push(-256),n+=W+P}n>r&&(r=n);let u={id:e,seqId:D,width:r,height:(c+1)*b,codePoints:new Uint32Array(m),xCoords:new Float32Array(i),yCoords:new Float32Array(h),packedStyles:new Uint32Array(w)};self.postMessage(u,[u.codePoints.buffer,u.xCoords.buffer,u.yCoords.buffer,u.packedStyles.buffer])};})();\n';
300
300
 
301
301
  // src/layout/LayoutWorkerManager.ts
302
302
  var LayoutWorkerManager = (_class2 = class _LayoutWorkerManager {
@@ -1,8 +1,10 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4; var _class5; var _class6; var _class7;
2
2
 
3
- var _chunk76NMTLYLjs = require('./chunk-76NMTLYL.js');
3
+ var _chunkCTZQOM5Zjs = require('./chunk-CTZQOM5Z.js');
4
4
 
5
5
  // src/math/SpringPhysics.ts
6
+ var MAX_FRAME_DT = 0.25;
7
+ var MAX_STEP_DT = 1 / 120;
6
8
  var SpringPhysics = (_class = class {
7
9
 
8
10
 
@@ -22,11 +24,22 @@ var SpringPhysics = (_class = class {
22
24
  this.velocity = 0;
23
25
  return;
24
26
  }
25
- const forceSpring = -this.stiffness * (this.value - this.target);
26
- const forceDamping = -this.damping * this.velocity;
27
- const acceleration = (forceSpring + forceDamping) / this.mass;
28
- this.velocity += acceleration * dt;
29
- this.value += this.velocity * dt;
27
+ if (!(dt > 0)) return;
28
+ let remaining = dt < MAX_FRAME_DT ? dt : MAX_FRAME_DT;
29
+ while (remaining > 0) {
30
+ const step = remaining < MAX_STEP_DT ? remaining : MAX_STEP_DT;
31
+ const forceSpring = -this.stiffness * (this.value - this.target);
32
+ const forceDamping = -this.damping * this.velocity;
33
+ const acceleration = (forceSpring + forceDamping) / this.mass;
34
+ this.velocity += acceleration * step;
35
+ this.value += this.velocity * step;
36
+ remaining -= step;
37
+ if (this.isAtRest()) {
38
+ this.value = this.target;
39
+ this.velocity = 0;
40
+ return;
41
+ }
42
+ }
30
43
  }
31
44
  isAtRest() {
32
45
  return Math.abs(this.value - this.target) < this.valEpsilon && Math.abs(this.velocity) < this.velEpsilon;
@@ -117,6 +130,14 @@ var SpringDriver = class {
117
130
  };
118
131
 
119
132
  // src/tree/Entity.ts
133
+ var ANIMATABLE_PROPS = /* @__PURE__ */ new Set([
134
+ "x",
135
+ "y",
136
+ "scaleX",
137
+ "scaleY",
138
+ "rotation",
139
+ "opacity"
140
+ ]);
120
141
  var VectoJSEvent = (_class3 = class {
121
142
  /** The event name. */
122
143
 
@@ -568,7 +589,12 @@ var Entity = (_class4 = class {
568
589
  const end = anim.target[key];
569
590
  if (typeof start === "number" && typeof end === "number") {
570
591
  const easeOut = progress * (2 - progress);
571
- this[key] = start + (end - start) * easeOut;
592
+ const value = start + (end - start) * easeOut;
593
+ if (ANIMATABLE_PROPS.has(key)) {
594
+ this._applyAnimated(key, value);
595
+ } else {
596
+ this[key] = value;
597
+ }
572
598
  }
573
599
  }
574
600
  if (progress >= 1) {
@@ -619,6 +645,10 @@ var Entity = (_class4 = class {
619
645
  */
620
646
  destroy() {
621
647
  this.animations = [];
648
+ for (const driver of this._drivers.values()) {
649
+ this._settleDriver(driver);
650
+ }
651
+ this._drivers.clear();
622
652
  this.listeners.clear();
623
653
  this.captureListeners.clear();
624
654
  if (this.parent) {
@@ -862,6 +892,19 @@ var Entity = (_class4 = class {
862
892
  getBatchRect() {
863
893
  return null;
864
894
  }
895
+ /**
896
+ * Opt into DOM content projection for entities that render static text.
897
+ * The {@link Scene} mirrors the returned text as a transparent DOM node
898
+ * positioned over the drawn glyphs, making canvas text findable (Ctrl+F),
899
+ * readable by screen readers and crawlers, translatable, and — when
900
+ * `selectable` is set — natively selectable. Returns `null` by default.
901
+ * Read on the a11y sync cadence, so text changes propagate automatically.
902
+ *
903
+ * @returns The projection descriptor, or `null` to project nothing.
904
+ */
905
+ getContentProjection() {
906
+ return null;
907
+ }
865
908
  /**
866
909
  * Whether this entity still has a queued/running tween animation, or an
867
910
  * active {@link setTransition}/{@link animateTo}/{@link springTo} property
@@ -989,6 +1032,8 @@ var MSDFTextEntity = (_class6 = class extends Entity {
989
1032
 
990
1033
 
991
1034
 
1035
+
1036
+
992
1037
  __init35() {this.text = ""}
993
1038
  __init36() {this.lastRenderedSeqId = 0}
994
1039
  __init37() {this.rgbColorCache = /* @__PURE__ */ new Map()}
@@ -1003,17 +1048,27 @@ var MSDFTextEntity = (_class6 = class extends Entity {
1003
1048
  this.color = _nullishCoalesce(options.color, () => ( "#ffffff"));
1004
1049
  this.letterSpacing = _nullishCoalesce(options.letterSpacing, () => ( 0));
1005
1050
  this.lineHeight = options.lineHeight;
1051
+ this.maxWidth = _nullishCoalesce(options.maxWidth, () => ( 1e3));
1052
+ this.maxHeight = _nullishCoalesce(options.maxHeight, () => ( 1e3));
1006
1053
  this.setText(text);
1007
1054
  }
1055
+ /** Change the wrap boundary and re-run layout for the current text. */
1056
+ setMaxWidth(maxWidth) {
1057
+ if (this.maxWidth === maxWidth) return;
1058
+ this.maxWidth = maxWidth;
1059
+ this.queueLayout();
1060
+ }
1008
1061
  setText(text) {
1009
1062
  if (this.text === text && this.layoutResult) return;
1010
1063
  this.text = text;
1011
- _chunk76NMTLYLjs.LayoutWorkerManager.getInstance().queueLayout(this.id, this.text, {
1064
+ this.queueLayout();
1065
+ }
1066
+ queueLayout() {
1067
+ _chunkCTZQOM5Zjs.LayoutWorkerManager.getInstance().queueLayout(this.id, this.text, {
1012
1068
  fontId: this.font.id,
1013
1069
  fontSize: this.fontSize,
1014
- maxWidth: 1e3,
1015
- // standard wrap boundary
1016
- maxHeight: 1e3,
1070
+ maxWidth: this.maxWidth,
1071
+ maxHeight: this.maxHeight,
1017
1072
  fontData: this.font.data,
1018
1073
  letterSpacing: this.letterSpacing,
1019
1074
  lineHeight: this.lineHeight,
@@ -1025,6 +1080,18 @@ var MSDFTextEntity = (_class6 = class extends Entity {
1025
1080
  }
1026
1081
  });
1027
1082
  }
1083
+ /**
1084
+ * Mirror the rendered text into the DOM content layer: find-in-page, screen
1085
+ * readers, crawlers, and translation see the same string the canvas draws.
1086
+ */
1087
+ getContentProjection() {
1088
+ if (!this.text) return null;
1089
+ return {
1090
+ text: this.text,
1091
+ font: `${this.fontSize}px ${this.fallbackFont}`,
1092
+ lineHeight: this.lineHeight
1093
+ };
1094
+ }
1028
1095
  isPointInside(globalX, globalY) {
1029
1096
  if (!this.layoutResult) return false;
1030
1097
  const local = this.worldToLocal(globalX, globalY);
@@ -1042,6 +1109,8 @@ var MSDFTextEntity = (_class6 = class extends Entity {
1042
1109
  if (scene && scene.pointRenderer && scene.glCanvas && canUsePointGlyphs) {
1043
1110
  scene.pointRenderer.setMSDFTexture(this.texture, this.font.distanceRange);
1044
1111
  const worldRot = Math.atan2(world.b, world.a);
1112
+ let worldOpacity = this.opacity;
1113
+ for (let p = this.parent; p; p = p.parent) worldOpacity *= p.opacity;
1045
1114
  const len2 = this.layoutResult.codePoints.length;
1046
1115
  for (let i = 0; i < len2; i++) {
1047
1116
  const code = this.layoutResult.codePoints[i];
@@ -1080,7 +1149,7 @@ var MSDFTextEntity = (_class6 = class extends Entity {
1080
1149
  ab.right / aw,
1081
1150
  v1,
1082
1151
  runColor,
1083
- this.opacity,
1152
+ worldOpacity,
1084
1153
  worldRot
1085
1154
  );
1086
1155
  }
@@ -1112,7 +1181,7 @@ var MSDFTextEntity = (_class6 = class extends Entity {
1112
1181
  }
1113
1182
  }
1114
1183
  destroy() {
1115
- _chunk76NMTLYLjs.LayoutWorkerManager.getInstance().cancelLayout(this.id);
1184
+ _chunkCTZQOM5Zjs.LayoutWorkerManager.getInstance().cancelLayout(this.id);
1116
1185
  super.destroy();
1117
1186
  }
1118
1187
  }, _class6);
@@ -20,12 +20,24 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
20
20
  __init2() {this.batchColor = ""}
21
21
  __init3() {this.batchAlpha = 1}
22
22
  __init4() {this.batchCount = 0}
23
- constructor(canvas) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);
23
+ /**
24
+ * @param canvas - The target canvas. Its backing store is resized to the
25
+ * logical size × devicePixelRatio.
26
+ * @param size - Explicit logical size. Without it the renderer assumes a
27
+ * fullscreen canvas and sizes to the window — pass this for embedded /
28
+ * custom-container canvases (the Scene does when `disableWindowResize` is
29
+ * set) so the canvas's own dimensions aren't clobbered by the window's.
30
+ */
31
+ constructor(canvas, size) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);
24
32
  const dpr = getDevicePixelRatio();
25
- this.width = typeof window !== "undefined" ? window.innerWidth : canvas.width || 0;
26
- this.height = typeof window !== "undefined" ? window.innerHeight : canvas.height || 0;
33
+ this.width = _nullishCoalesce(_optionalChain([size, 'optionalAccess', _ => _.width]), () => ( (typeof window !== "undefined" ? window.innerWidth : canvas.width || 0)));
34
+ this.height = _nullishCoalesce(_optionalChain([size, 'optionalAccess', _2 => _2.height]), () => ( (typeof window !== "undefined" ? window.innerHeight : canvas.height || 0)));
27
35
  canvas.width = this.width * dpr;
28
36
  canvas.height = this.height * dpr;
37
+ if (canvas.style) {
38
+ canvas.style.width = `${this.width}px`;
39
+ canvas.style.height = `${this.height}px`;
40
+ }
29
41
  const ctx = canvas.getContext("2d");
30
42
  this.ctx = ctx;
31
43
  if (ctx) ctx.scale(dpr, dpr);
@@ -414,18 +426,19 @@ var SVGRenderer = (_class2 = class {
414
426
  } else {
415
427
  this.currentPath.push(`L ${xs} ${ys}`);
416
428
  }
417
- const deltaAngle = Math.abs(endAngle - startAngle);
418
- if (deltaAngle >= Math.PI * 2 - 1e-4) {
429
+ const TWO_PI2 = Math.PI * 2;
430
+ const directedDelta = ccw ? startAngle - endAngle : endAngle - startAngle;
431
+ const sweepAngle = directedDelta >= TWO_PI2 ? TWO_PI2 : (directedDelta % TWO_PI2 + TWO_PI2) % TWO_PI2;
432
+ const sweep = ccw ? 0 : 1;
433
+ if (sweepAngle >= TWO_PI2 - 1e-4) {
419
434
  const xm = x - r * Math.cos(startAngle);
420
435
  const ym = y - r * Math.sin(startAngle);
421
- const sweep = ccw ? 0 : 1;
422
436
  this.currentPath.push(`A ${r} ${r} 0 0 ${sweep} ${xm} ${ym}`);
423
437
  this.currentPath.push(`A ${r} ${r} 0 0 ${sweep} ${xs} ${ys}`);
424
438
  } else {
425
439
  const xe = x + r * Math.cos(endAngle);
426
440
  const ye = y + r * Math.sin(endAngle);
427
- const largeArc = deltaAngle > Math.PI ? 1 : 0;
428
- const sweep = ccw ? 0 : 1;
441
+ const largeArc = sweepAngle > Math.PI ? 1 : 0;
429
442
  this.currentPath.push(`A ${r} ${r} 0 ${largeArc} ${sweep} ${xe} ${ye}`);
430
443
  }
431
444
  }
@@ -537,8 +550,8 @@ var SVGRenderer = (_class2 = class {
537
550
  }
538
551
  drawImage(source, dx, dy, dw, dh) {
539
552
  this.flush();
540
- const fromCanvas2 = typeof _optionalChain([source, 'optionalAccess', _ => _.toDataURL]) === "function";
541
- const rawHref = fromCanvas2 ? source.toDataURL() : _optionalChain([source, 'optionalAccess', _2 => _2.src]) || "";
553
+ const fromCanvas2 = typeof _optionalChain([source, 'optionalAccess', _3 => _3.toDataURL]) === "function";
554
+ const rawHref = fromCanvas2 ? source.toDataURL() : _optionalChain([source, 'optionalAccess', _4 => _4.src]) || "";
542
555
  const href = fromCanvas2 && this.isSafeRasterDataUrl(rawHref) ? rawHref : sanitizeUrl(String(rawHref));
543
556
  const transformStr = `matrix(${this.ma},${this.mb},${this.mc},${this.md},${this.me},${this.mf})`;
544
557
  if (href) {
@@ -723,6 +736,7 @@ function fromCanvas(css) {
723
736
  if (typeof document === "undefined") return null;
724
737
  if (!fallbackCtx) fallbackCtx = document.createElement("canvas").getContext("2d");
725
738
  if (!fallbackCtx) return null;
739
+ fallbackCtx.clearRect(0, 0, 1, 1);
726
740
  fallbackCtx.fillStyle = css;
727
741
  fallbackCtx.fillRect(0, 0, 1, 1);
728
742
  const d = fallbackCtx.getImageData(0, 0, 1, 1).data;
@@ -869,7 +883,9 @@ function grow(data, needed) {
869
883
  return grown;
870
884
  }
871
885
  function createWebGLPointRenderer(canvas) {
872
- const gl = canvas.getContext("webgl2");
886
+ const gl = canvas.getContext("webgl2", {
887
+ premultipliedAlpha: false
888
+ });
873
889
  if (!gl) return null;
874
890
  const pointProgram = link(gl, POINT_VERT, POINT_FRAG);
875
891
  const rectProgram = link(gl, RECT_VERT, RECT_FRAG);
@@ -937,7 +953,9 @@ function createWebGLPointRenderer(canvas) {
937
953
  gl.vertexAttribPointer(gATint, 4, gl.FLOAT, false, SPRITE_VERT_STRIDE, 16);
938
954
  gl.bindVertexArray(null);
939
955
  let texture = null;
956
+ let textureSource = null;
940
957
  let msdfTexture = null;
958
+ let msdfSource = null;
941
959
  let distanceRange = 4;
942
960
  let pointData = new Float32Array(FLOATS_PER_POINT * 1024);
943
961
  let pointCount = 0;
@@ -951,6 +969,22 @@ function createWebGLPointRenderer(canvas) {
951
969
  let logicalH = 0;
952
970
  let dpr = 1;
953
971
  let destroyed = false;
972
+ const drawGlyphs = () => {
973
+ if (glyphCount === 0 || !msdfTexture) return;
974
+ const floats = glyphCount * VERTS_PER_SPRITE * FLOATS_PER_SPRITE_VERT;
975
+ gl.useProgram(msdfProgram);
976
+ gl.bindVertexArray(glyphVAO);
977
+ gl.bindBuffer(gl.ARRAY_BUFFER, glyphBuffer);
978
+ gl.bufferData(gl.ARRAY_BUFFER, glyphData.subarray(0, floats), gl.DYNAMIC_DRAW);
979
+ gl.activeTexture(gl.TEXTURE0);
980
+ gl.bindTexture(gl.TEXTURE_2D, msdfTexture);
981
+ gl.uniform1i(gUTex, 0);
982
+ gl.uniform2f(gURes, logicalW, logicalH);
983
+ gl.uniform1f(gURange, distanceRange);
984
+ gl.drawArrays(gl.TRIANGLES, 0, glyphCount * VERTS_PER_SPRITE);
985
+ gl.bindVertexArray(null);
986
+ glyphCount = 0;
987
+ };
954
988
  return {
955
989
  resize(width, height) {
956
990
  logicalW = width;
@@ -967,8 +1001,11 @@ function createWebGLPointRenderer(canvas) {
967
1001
  rectCount = 0;
968
1002
  spriteCount = 0;
969
1003
  glyphCount = 0;
1004
+ gl.clearColor(0, 0, 0, 0);
1005
+ gl.clear(gl.COLOR_BUFFER_BIT);
970
1006
  },
971
1007
  setTexture(source) {
1008
+ if (source === textureSource && texture) return;
972
1009
  if (!texture) {
973
1010
  texture = gl.createTexture();
974
1011
  gl.bindTexture(gl.TEXTURE_2D, texture);
@@ -980,6 +1017,7 @@ function createWebGLPointRenderer(canvas) {
980
1017
  gl.bindTexture(gl.TEXTURE_2D, texture);
981
1018
  }
982
1019
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
1020
+ textureSource = source;
983
1021
  },
984
1022
  addSprite(x, y, width, height, u0, v0, u1, v1, color = "#ffffff", alpha = 1, rotation = 0) {
985
1023
  if (!texture) return;
@@ -1016,6 +1054,11 @@ function createWebGLPointRenderer(canvas) {
1016
1054
  spriteCount++;
1017
1055
  },
1018
1056
  setMSDFTexture(source, range) {
1057
+ if (source === msdfSource && msdfTexture) {
1058
+ distanceRange = range;
1059
+ return;
1060
+ }
1061
+ drawGlyphs();
1019
1062
  distanceRange = range;
1020
1063
  if (!msdfTexture) {
1021
1064
  msdfTexture = gl.createTexture();
@@ -1028,6 +1071,7 @@ function createWebGLPointRenderer(canvas) {
1028
1071
  gl.bindTexture(gl.TEXTURE_2D, msdfTexture);
1029
1072
  }
1030
1073
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
1074
+ msdfSource = source;
1031
1075
  },
1032
1076
  addGlyph(x, y, width, height, u0, v0, u1, v1, color = "#ffffff", alpha = 1, rotation = 0) {
1033
1077
  if (!msdfTexture) return;
@@ -1105,8 +1149,6 @@ function createWebGLPointRenderer(canvas) {
1105
1149
  rectCount++;
1106
1150
  },
1107
1151
  flush() {
1108
- gl.clearColor(0, 0, 0, 0);
1109
- gl.clear(gl.COLOR_BUFFER_BIT);
1110
1152
  if (rectCount > 0) {
1111
1153
  const floats = rectCount * VERTS_PER_RECT * FLOATS_PER_RECT_VERT;
1112
1154
  gl.useProgram(rectProgram);
@@ -1141,20 +1183,8 @@ function createWebGLPointRenderer(canvas) {
1141
1183
  gl.uniform2f(sURes, logicalW, logicalH);
1142
1184
  gl.drawArrays(gl.TRIANGLES, 0, spriteCount * VERTS_PER_SPRITE);
1143
1185
  }
1144
- if (glyphCount > 0 && msdfTexture) {
1145
- const floats = glyphCount * VERTS_PER_SPRITE * FLOATS_PER_SPRITE_VERT;
1146
- gl.useProgram(msdfProgram);
1147
- gl.bindVertexArray(glyphVAO);
1148
- gl.bindBuffer(gl.ARRAY_BUFFER, glyphBuffer);
1149
- gl.bufferData(gl.ARRAY_BUFFER, glyphData.subarray(0, floats), gl.DYNAMIC_DRAW);
1150
- gl.activeTexture(gl.TEXTURE0);
1151
- gl.bindTexture(gl.TEXTURE_2D, msdfTexture);
1152
- gl.uniform1i(gUTex, 0);
1153
- gl.uniform2f(gURes, logicalW, logicalH);
1154
- gl.uniform1f(gURange, distanceRange);
1155
- gl.drawArrays(gl.TRIANGLES, 0, glyphCount * VERTS_PER_SPRITE);
1156
- }
1157
1186
  gl.bindVertexArray(null);
1187
+ drawGlyphs();
1158
1188
  },
1159
1189
  destroy() {
1160
1190
  if (destroyed) return;
@@ -20,12 +20,24 @@ var CanvasRenderer = class _CanvasRenderer {
20
20
  batchColor = "";
21
21
  batchAlpha = 1;
22
22
  batchCount = 0;
23
- constructor(canvas) {
23
+ /**
24
+ * @param canvas - The target canvas. Its backing store is resized to the
25
+ * logical size × devicePixelRatio.
26
+ * @param size - Explicit logical size. Without it the renderer assumes a
27
+ * fullscreen canvas and sizes to the window — pass this for embedded /
28
+ * custom-container canvases (the Scene does when `disableWindowResize` is
29
+ * set) so the canvas's own dimensions aren't clobbered by the window's.
30
+ */
31
+ constructor(canvas, size) {
24
32
  const dpr = getDevicePixelRatio();
25
- this.width = typeof window !== "undefined" ? window.innerWidth : canvas.width || 0;
26
- this.height = typeof window !== "undefined" ? window.innerHeight : canvas.height || 0;
33
+ this.width = size?.width ?? (typeof window !== "undefined" ? window.innerWidth : canvas.width || 0);
34
+ this.height = size?.height ?? (typeof window !== "undefined" ? window.innerHeight : canvas.height || 0);
27
35
  canvas.width = this.width * dpr;
28
36
  canvas.height = this.height * dpr;
37
+ if (canvas.style) {
38
+ canvas.style.width = `${this.width}px`;
39
+ canvas.style.height = `${this.height}px`;
40
+ }
29
41
  const ctx = canvas.getContext("2d");
30
42
  this.ctx = ctx;
31
43
  if (ctx) ctx.scale(dpr, dpr);
@@ -414,18 +426,19 @@ var SVGRenderer = class {
414
426
  } else {
415
427
  this.currentPath.push(`L ${xs} ${ys}`);
416
428
  }
417
- const deltaAngle = Math.abs(endAngle - startAngle);
418
- if (deltaAngle >= Math.PI * 2 - 1e-4) {
429
+ const TWO_PI2 = Math.PI * 2;
430
+ const directedDelta = ccw ? startAngle - endAngle : endAngle - startAngle;
431
+ const sweepAngle = directedDelta >= TWO_PI2 ? TWO_PI2 : (directedDelta % TWO_PI2 + TWO_PI2) % TWO_PI2;
432
+ const sweep = ccw ? 0 : 1;
433
+ if (sweepAngle >= TWO_PI2 - 1e-4) {
419
434
  const xm = x - r * Math.cos(startAngle);
420
435
  const ym = y - r * Math.sin(startAngle);
421
- const sweep = ccw ? 0 : 1;
422
436
  this.currentPath.push(`A ${r} ${r} 0 0 ${sweep} ${xm} ${ym}`);
423
437
  this.currentPath.push(`A ${r} ${r} 0 0 ${sweep} ${xs} ${ys}`);
424
438
  } else {
425
439
  const xe = x + r * Math.cos(endAngle);
426
440
  const ye = y + r * Math.sin(endAngle);
427
- const largeArc = deltaAngle > Math.PI ? 1 : 0;
428
- const sweep = ccw ? 0 : 1;
441
+ const largeArc = sweepAngle > Math.PI ? 1 : 0;
429
442
  this.currentPath.push(`A ${r} ${r} 0 ${largeArc} ${sweep} ${xe} ${ye}`);
430
443
  }
431
444
  }
@@ -723,6 +736,7 @@ function fromCanvas(css) {
723
736
  if (typeof document === "undefined") return null;
724
737
  if (!fallbackCtx) fallbackCtx = document.createElement("canvas").getContext("2d");
725
738
  if (!fallbackCtx) return null;
739
+ fallbackCtx.clearRect(0, 0, 1, 1);
726
740
  fallbackCtx.fillStyle = css;
727
741
  fallbackCtx.fillRect(0, 0, 1, 1);
728
742
  const d = fallbackCtx.getImageData(0, 0, 1, 1).data;
@@ -869,7 +883,9 @@ function grow(data, needed) {
869
883
  return grown;
870
884
  }
871
885
  function createWebGLPointRenderer(canvas) {
872
- const gl = canvas.getContext("webgl2");
886
+ const gl = canvas.getContext("webgl2", {
887
+ premultipliedAlpha: false
888
+ });
873
889
  if (!gl) return null;
874
890
  const pointProgram = link(gl, POINT_VERT, POINT_FRAG);
875
891
  const rectProgram = link(gl, RECT_VERT, RECT_FRAG);
@@ -937,7 +953,9 @@ function createWebGLPointRenderer(canvas) {
937
953
  gl.vertexAttribPointer(gATint, 4, gl.FLOAT, false, SPRITE_VERT_STRIDE, 16);
938
954
  gl.bindVertexArray(null);
939
955
  let texture = null;
956
+ let textureSource = null;
940
957
  let msdfTexture = null;
958
+ let msdfSource = null;
941
959
  let distanceRange = 4;
942
960
  let pointData = new Float32Array(FLOATS_PER_POINT * 1024);
943
961
  let pointCount = 0;
@@ -951,6 +969,22 @@ function createWebGLPointRenderer(canvas) {
951
969
  let logicalH = 0;
952
970
  let dpr = 1;
953
971
  let destroyed = false;
972
+ const drawGlyphs = () => {
973
+ if (glyphCount === 0 || !msdfTexture) return;
974
+ const floats = glyphCount * VERTS_PER_SPRITE * FLOATS_PER_SPRITE_VERT;
975
+ gl.useProgram(msdfProgram);
976
+ gl.bindVertexArray(glyphVAO);
977
+ gl.bindBuffer(gl.ARRAY_BUFFER, glyphBuffer);
978
+ gl.bufferData(gl.ARRAY_BUFFER, glyphData.subarray(0, floats), gl.DYNAMIC_DRAW);
979
+ gl.activeTexture(gl.TEXTURE0);
980
+ gl.bindTexture(gl.TEXTURE_2D, msdfTexture);
981
+ gl.uniform1i(gUTex, 0);
982
+ gl.uniform2f(gURes, logicalW, logicalH);
983
+ gl.uniform1f(gURange, distanceRange);
984
+ gl.drawArrays(gl.TRIANGLES, 0, glyphCount * VERTS_PER_SPRITE);
985
+ gl.bindVertexArray(null);
986
+ glyphCount = 0;
987
+ };
954
988
  return {
955
989
  resize(width, height) {
956
990
  logicalW = width;
@@ -967,8 +1001,11 @@ function createWebGLPointRenderer(canvas) {
967
1001
  rectCount = 0;
968
1002
  spriteCount = 0;
969
1003
  glyphCount = 0;
1004
+ gl.clearColor(0, 0, 0, 0);
1005
+ gl.clear(gl.COLOR_BUFFER_BIT);
970
1006
  },
971
1007
  setTexture(source) {
1008
+ if (source === textureSource && texture) return;
972
1009
  if (!texture) {
973
1010
  texture = gl.createTexture();
974
1011
  gl.bindTexture(gl.TEXTURE_2D, texture);
@@ -980,6 +1017,7 @@ function createWebGLPointRenderer(canvas) {
980
1017
  gl.bindTexture(gl.TEXTURE_2D, texture);
981
1018
  }
982
1019
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
1020
+ textureSource = source;
983
1021
  },
984
1022
  addSprite(x, y, width, height, u0, v0, u1, v1, color = "#ffffff", alpha = 1, rotation = 0) {
985
1023
  if (!texture) return;
@@ -1016,6 +1054,11 @@ function createWebGLPointRenderer(canvas) {
1016
1054
  spriteCount++;
1017
1055
  },
1018
1056
  setMSDFTexture(source, range) {
1057
+ if (source === msdfSource && msdfTexture) {
1058
+ distanceRange = range;
1059
+ return;
1060
+ }
1061
+ drawGlyphs();
1019
1062
  distanceRange = range;
1020
1063
  if (!msdfTexture) {
1021
1064
  msdfTexture = gl.createTexture();
@@ -1028,6 +1071,7 @@ function createWebGLPointRenderer(canvas) {
1028
1071
  gl.bindTexture(gl.TEXTURE_2D, msdfTexture);
1029
1072
  }
1030
1073
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
1074
+ msdfSource = source;
1031
1075
  },
1032
1076
  addGlyph(x, y, width, height, u0, v0, u1, v1, color = "#ffffff", alpha = 1, rotation = 0) {
1033
1077
  if (!msdfTexture) return;
@@ -1105,8 +1149,6 @@ function createWebGLPointRenderer(canvas) {
1105
1149
  rectCount++;
1106
1150
  },
1107
1151
  flush() {
1108
- gl.clearColor(0, 0, 0, 0);
1109
- gl.clear(gl.COLOR_BUFFER_BIT);
1110
1152
  if (rectCount > 0) {
1111
1153
  const floats = rectCount * VERTS_PER_RECT * FLOATS_PER_RECT_VERT;
1112
1154
  gl.useProgram(rectProgram);
@@ -1141,20 +1183,8 @@ function createWebGLPointRenderer(canvas) {
1141
1183
  gl.uniform2f(sURes, logicalW, logicalH);
1142
1184
  gl.drawArrays(gl.TRIANGLES, 0, spriteCount * VERTS_PER_SPRITE);
1143
1185
  }
1144
- if (glyphCount > 0 && msdfTexture) {
1145
- const floats = glyphCount * VERTS_PER_SPRITE * FLOATS_PER_SPRITE_VERT;
1146
- gl.useProgram(msdfProgram);
1147
- gl.bindVertexArray(glyphVAO);
1148
- gl.bindBuffer(gl.ARRAY_BUFFER, glyphBuffer);
1149
- gl.bufferData(gl.ARRAY_BUFFER, glyphData.subarray(0, floats), gl.DYNAMIC_DRAW);
1150
- gl.activeTexture(gl.TEXTURE0);
1151
- gl.bindTexture(gl.TEXTURE_2D, msdfTexture);
1152
- gl.uniform1i(gUTex, 0);
1153
- gl.uniform2f(gURes, logicalW, logicalH);
1154
- gl.uniform1f(gURange, distanceRange);
1155
- gl.drawArrays(gl.TRIANGLES, 0, glyphCount * VERTS_PER_SPRITE);
1156
- }
1157
1186
  gl.bindVertexArray(null);
1187
+ drawGlyphs();
1158
1188
  },
1159
1189
  destroy() {
1160
1190
  if (destroyed) return;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ArabicShaper,
3
3
  BidiResolver
4
- } from "./chunk-B3Z3JEJH.mjs";
4
+ } from "./chunk-STWPTWO4.mjs";
5
5
 
6
6
  // src/layout/LayoutEngine.ts
7
7
  function computeLineSegments(top, bottom, maxWidth, exclusions) {
@@ -296,7 +296,7 @@ var BidiResolver = class _BidiResolver {
296
296
  };
297
297
 
298
298
  // src/layout/LayoutWorkerSource.ts
299
- var WORKER_SOURCE_STRING = '"use strict";(()=>{var k=new Map;function n(t){return typeof t=="number"&&Number.isFinite(t)}function H(t){return t.origin?t.origin===self.location.origin:!0}function M(t){if(!t||typeof t!="object")return!1;let e=t;return typeof e.id=="string"&&n(e.seqId)&&typeof e.text=="string"&&typeof e.fontId=="string"&&(e.fontData===void 0||typeof e.fontData=="object")&&n(e.maxWidth)&&n(e.maxHeight)&&n(e.fontSize)&&(e.lineHeight===void 0||n(e.lineHeight))&&(e.letterSpacing===void 0||n(e.letterSpacing))}self.onmessage=t=>{if(!H(t)||!M(t.data))return;let{id:e,seqId:F,text:q,fontId:d,fontData:f,maxWidth:y,maxHeight:I,fontSize:i,lineHeight:A,letterSpacing:D}=t.data;f&&k.set(d,f);let s=k.get(d);if(!s)return;let g=[],l=[],p=[],m=[],o=0,a=0,h=s.metrics?.ascender??.8,W=s.metrics?.descender??-.2,b=A??i*(h-W),x=Array.from(q);for(let c=0;c<x.length;c++){let u=x[c].codePointAt(0),S=(s.glyphs?.find(C=>C.unicode===u)?.advance??1)*i;o+S>y&&u===32&&(o=0,a++),g.push(u),l.push(o);let w=a*b+h*i;p.push(w),m.push(-256),o+=S+(D??0)}let r={id:e,seqId:F,width:Math.min(o,y),height:(a+1)*b,codePoints:new Uint32Array(g),xCoords:new Float32Array(l),yCoords:new Float32Array(p),packedStyles:new Uint32Array(m)};self.postMessage(r,[r.codePoints.buffer,r.xCoords.buffer,r.yCoords.buffer,r.packedStyles.buffer])};})();\n';
299
+ var WORKER_SOURCE_STRING = '"use strict";(()=>{var C=new Map;function f(t){return typeof t=="number"&&Number.isFinite(t)}function U(t){return t.origin?t.origin===self.location.origin:!0}function j(t){if(!t||typeof t!="object")return!1;let e=t;return typeof e.id=="string"&&f(e.seqId)&&typeof e.text=="string"&&typeof e.fontId=="string"&&(e.fontData===void 0||typeof e.fontData=="object")&&f(e.maxWidth)&&f(e.maxHeight)&&f(e.fontSize)&&(e.lineHeight===void 0||f(e.lineHeight))&&(e.letterSpacing===void 0||f(e.letterSpacing))}self.onmessage=t=>{if(!U(t)||!j(t.data))return;let{id:e,seqId:D,text:L,fontId:k,fontData:S,maxWidth:H,maxHeight:z,fontSize:d,lineHeight:I,letterSpacing:M}=t.data;S&&C.set(k,S);let g=C.get(k);if(!g)return;let m=[],i=[],h=[],w=[],n=0,c=0,r=0,o=-1,p=g.metrics?.ascender??.8,R=g.metrics?.descender??-.2,b=I??d*(p-R),P=M??0,F=new Map;for(let s of g.glyphs??[])F.set(s.unicode,s.advance);let x=()=>{n>r&&(r=n),n=0,c++,o=-1},A=Array.from(L);for(let s=0;s<A.length;s++){let a=A[s].codePointAt(0);if(a===10){x();continue}let W=(F.get(a)??1)*d,q=a>=11904;if(q&&(o=-1),n+W>H&&n>0){if(a===32){x();continue}if(o>=0&&i[o]>0){let l=i[o];l>r&&(r=l),c++;let v=c*b+p*d;for(let y=o;y<i.length;y++)i[y]-=l,h[y]=v;n-=l}else x()}a===32?o=-1:o===-1&&!q&&(o=m.length),m.push(a),i.push(n),h.push(c*b+p*d),w.push(-256),n+=W+P}n>r&&(r=n);let u={id:e,seqId:D,width:r,height:(c+1)*b,codePoints:new Uint32Array(m),xCoords:new Float32Array(i),yCoords:new Float32Array(h),packedStyles:new Uint32Array(w)};self.postMessage(u,[u.codePoints.buffer,u.xCoords.buffer,u.yCoords.buffer,u.packedStyles.buffer])};})();\n';
300
300
 
301
301
  // src/layout/LayoutWorkerManager.ts
302
302
  var LayoutWorkerManager = class _LayoutWorkerManager {