@vectojs/core 0.2.3 → 0.2.4

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/README.md CHANGED
@@ -1,55 +1,132 @@
1
1
  # @vectojs/core
2
2
 
3
- > The Canvas-native rendering engine behind **VectoJS** an entity scene graph and Virtual Math
4
- > Tree with an accessibility/automation projection layer.
3
+ > Scene, layout, interaction, text, rendering, and semantic projection for canvas-native interfaces.
5
4
 
6
- Part of the [VectoJS](https://github.com/vectojs/vectojs) ecosystem.
5
+ [![npm](https://img.shields.io/npm/v/@vectojs/core?color=22d3ee)](https://www.npmjs.com/package/@vectojs/core)
6
+ [![CI](https://github.com/vectojs/vectojs/actions/workflows/ci.yml/badge.svg)](https://github.com/vectojs/vectojs/actions/workflows/ci.yml)
7
+ [![MIT](https://img.shields.io/badge/license-MIT-6366f1.svg)](https://github.com/vectojs/vectojs/blob/main/LICENSE)
7
8
 
8
- ## What it does
9
+ `@vectojs/core` is the runtime beneath VectoJS. It owns the retained `Scene`/`Entity` tree, affine
10
+ transforms, render scheduling, layout, text flow, spatial hit-testing, event propagation, renderer
11
+ backends, and the accessibility/automation projection layer.
9
12
 
10
- `@vectojs/core` renders a whole UI onto one `<canvas>`: layout, hit-testing, animation and
11
- physics are pure math on a Virtual Math Tree, dispatched to a Canvas 2D (or WebGL2) renderer —
12
- **no per-element DOM, no reflow, no style recalc**. Interactive entities project a real,
13
- transparent DOM node through the **`a11yRoot`** shadow layer, so a pure-canvas page stays
14
- accessible and drivable by assistive tech and AI agents.
13
+ [Core guide](https://vectojs.xuepoo.xyz/learn/core-scene/) ·
14
+ [API reference](https://vectojs.xuepoo.xyz/reference/core-api/) ·
15
+ [Main repository](https://github.com/vectojs/vectojs)
15
16
 
16
- Includes: `Scene` (render loop + a11y sync), `Entity` (scene-graph base), `LayoutEngine` (Intl.Segmenter
17
- with a cold/hot `prepare`/`layoutPrepared` split), `SpatialHashGrid`, `LayoutResultBuffer`
18
- (reusable typed storage), `SplineEntity` (native vectomancy math-curve rendering + curve-accurate hit-testing),
19
- `CanvasRenderer`, and a `WebGLPointRenderer` point/rect batch layer.
17
+ ## Install
20
18
 
21
- ## Performance
19
+ ```bash
20
+ bun add @vectojs/core
21
+ ```
22
+
23
+ ## Minimal scene
22
24
 
23
- See the [main README](https://github.com/vectojs/vectojs#testing--quality) for reproducible
24
- workloads (`bun run benchmark` / `bun run compare:dom`). Headline levers include viewport culling,
25
- on-demand redraw, draw-call batching, a WebGL2 point layer, and a cold/hot text layout split.
26
- Results are machine- and workload-dependent.
25
+ ```ts
26
+ import { Entity, type IRenderer, Scene } from '@vectojs/core';
27
27
 
28
- ## Quick Start
28
+ class Dot extends Entity {
29
+ constructor() {
30
+ super();
31
+ this.width = 48;
32
+ this.height = 48;
33
+ this.interactive = true;
34
+ this.on('click', () => this.animate({ scaleX: 1.25, scaleY: 1.25 }, 120));
35
+ }
29
36
 
30
- ```typescript
31
- import { Scene, Entity, IRenderer } from '@vectojs/core';
37
+ isPointInside(globalX: number, globalY: number): boolean {
38
+ const local = this.worldToLocal(globalX, globalY);
39
+ return !!local && Math.hypot(local.x - 24, local.y - 24) <= 24;
40
+ }
32
41
 
33
- class CircleEntity extends Entity {
34
- isPointInside(x: number, y: number) {
35
- return Math.hypot(x - this.x, y - this.y) < 50;
42
+ getA11yAttributes() {
43
+ return { tag: 'button' as const, role: 'button', label: 'Animated dot' };
36
44
  }
37
- render(r: IRenderer) {
38
- r.beginPath();
39
- r.arc(0, 0, 50, 0, Math.PI * 2);
40
- r.fill('#38bdf8');
45
+
46
+ render(renderer: IRenderer): void {
47
+ renderer.beginPath();
48
+ renderer.arc(24, 24, 24, 0, Math.PI * 2);
49
+ renderer.fill('#22d3ee');
41
50
  }
42
51
  }
43
52
 
44
- const canvas = document.querySelector('canvas')!;
53
+ const canvas = document.querySelector<HTMLCanvasElement>('canvas')!;
45
54
  const scene = new Scene(canvas);
46
- scene.add(new CircleEntity().setPosition(100, 100));
55
+ scene.renderMode = 'onDemand';
56
+ scene.add(new Dot().setPosition(80, 80));
57
+ scene.start();
58
+ ```
59
+
60
+ ## Runtime building blocks
61
+
62
+ | Area | Main APIs | Purpose |
63
+ | ----------- | ----------------------------------------------- | ---------------------------------------------------------------------- |
64
+ | Scene graph | `Scene`, `Entity` | ownership, transforms, lifecycle, update/render traversal |
65
+ | Layout | `LayoutEngine`, layout subpath | prepared text/rich-text layout, wrapping, exclusions, reusable buffers |
66
+ | Interaction | entity events, `SpatialHashGrid` | hit-testing and DOM-like capture/bubble dispatch |
67
+ | Text | `TextEntity`, `MSDFTextEntity`, `SplineEntity` | Canvas text, GPU text, and mathematical curves |
68
+ | Rendering | `IRenderer`, `CanvasRenderer`, renderer subpath | backend-neutral drawing contract and concrete renderers |
69
+ | GPU paths | WebGL point batching, WebGPU particles | high-volume points/rects and compute-driven particles |
70
+ | Semantics | `A11yAttributes`, Scene projection | role/name/state, native inputs, screen readers, Playwright/agents |
71
+ | Animation | `animate`, springs, transitions, `Scene.step()` | real-time or deterministic fixed-step motion |
72
+
73
+ ## Scene lifecycle
74
+
75
+ ```ts
76
+ const scene = new Scene(canvas, {
77
+ maxFPS: 60,
78
+ pointBackend: 'canvas', // or 'webgl'
79
+ particleBackend: 'cpu', // or 'webgpu' when supported
80
+ });
81
+ scene.renderMode = 'onDemand'; // redraw only when dirty
82
+
83
+ scene.resize(width, height); // logical CSS pixels
84
+ scene.markDirty();
47
85
  scene.start();
86
+ scene.stop();
87
+ scene.step(1000 / 60); // deterministic single step
88
+ scene.destroy(); // release renderers, workers, observers, and semantic DOM
48
89
  ```
49
90
 
50
- For high-level accessible components (Button, Input, Card…), see
51
- [`@vectojs/ui`](https://www.npmjs.com/package/@vectojs/ui).
91
+ Always call `destroy()` when a framework component unmounts. A `Scene` owns browser observers,
92
+ renderer resources, layout work, and projected DOM nodes.
93
+
94
+ ## Package entry points
95
+
96
+ ```ts
97
+ import { Scene, Entity } from '@vectojs/core';
98
+ import { LayoutEngine } from '@vectojs/core/layout';
99
+ import type { IRenderer } from '@vectojs/core/renderer';
100
+ import { TextEntity } from '@vectojs/core/text';
101
+ ```
102
+
103
+ Both ESM and CommonJS outputs are published with TypeScript declarations.
104
+
105
+ ## Accessibility and automation
106
+
107
+ Canvas pixels have no semantics. Interactive entities can implement `getA11yAttributes()`; the
108
+ Scene projects transparent DOM nodes over their world-space bounds and forwards native input back
109
+ into the VectoJS event system.
110
+
111
+ This projection is intentionally thin. Applications still own accessible names, keyboard behavior,
112
+ focus order, contrast, and correct control semantics. See the
113
+ [accessibility guide](https://vectojs.xuepoo.xyz/learn/accessibility/).
114
+
115
+ ## Performance model
116
+
117
+ Useful levers include on-demand rendering, viewport culling, spatial hashing, prepared text layout,
118
+ typed reusable buffers, batched WebGL points, and optional WebGPU particle compute. None makes every
119
+ workload allocation-free or GPU-bound; profile the renderer and entity types used by your app.
120
+
121
+ Run the repository benchmarks with `bun run benchmark`, `bun run compare:dom`, and
122
+ `bun run compare`.
123
+
124
+ ## Related packages
125
+
126
+ - [`@vectojs/ui`](https://github.com/vectojs/vectojs/tree/main/packages/ui) — high-level accessible components
127
+ - [`@vectojs/three`](https://github.com/vectojs/vectojs/tree/main/packages/three) — Three.js/WebXR projection and raycast routing
128
+ - [`@vectojs/video-exporter`](https://github.com/vectojs/vectojs/tree/main/packages/video-exporter) — deterministic H.264 capture
52
129
 
53
130
  ## License
54
131
 
55
- MIT © 2026 Xuepoo
132
+ [MIT](https://github.com/vectojs/vectojs/blob/main/LICENSE) © 2026 Xuepoo
@@ -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 b=new Map;self.onmessage=x=>{let{id:S,seqId:k,text:A,fontId:i,fontData:c,maxWidth:d,maxHeight:H,fontSize:o,lineHeight:F,letterSpacing:C}=x.data;c&&b.set(i,c);let n=b.get(i);if(!n)return;let f=[],u=[],y=[],l=[],e=0,r=0,h=n.metrics?.ascender??.8,D=n.metrics?.descender??-.2,m=F??o*(h-D),p=Array.from(A);for(let s=0;s<p.length;s++){let a=p[s].codePointAt(0),g=(n.glyphs?.find(w=>w.unicode===a)?.advance??1)*o;e+g>d&&a===32&&(e=0,r++),f.push(a),u.push(e);let M=r*m+h*o;y.push(M),l.push(-256),e+=g+(C??0)}let t={id:S,seqId:k,width:Math.min(e,d),height:(r+1)*m,codePoints:new Uint32Array(f),xCoords:new Float32Array(u),yCoords:new Float32Array(y),packedStyles:new Uint32Array(l)};self.postMessage(t,[t.codePoints.buffer,t.xCoords.buffer,t.yCoords.buffer,t.packedStyles.buffer])};})();\n';
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';
300
300
 
301
301
  // src/layout/LayoutWorkerManager.ts
302
302
  var LayoutWorkerManager = (_class2 = class _LayoutWorkerManager {
@@ -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 b=new Map;self.onmessage=x=>{let{id:S,seqId:k,text:A,fontId:i,fontData:c,maxWidth:d,maxHeight:H,fontSize:o,lineHeight:F,letterSpacing:C}=x.data;c&&b.set(i,c);let n=b.get(i);if(!n)return;let f=[],u=[],y=[],l=[],e=0,r=0,h=n.metrics?.ascender??.8,D=n.metrics?.descender??-.2,m=F??o*(h-D),p=Array.from(A);for(let s=0;s<p.length;s++){let a=p[s].codePointAt(0),g=(n.glyphs?.find(w=>w.unicode===a)?.advance??1)*o;e+g>d&&a===32&&(e=0,r++),f.push(a),u.push(e);let M=r*m+h*o;y.push(M),l.push(-256),e+=g+(C??0)}let t={id:S,seqId:k,width:Math.min(e,d),height:(r+1)*m,codePoints:new Uint32Array(f),xCoords:new Float32Array(u),yCoords:new Float32Array(y),packedStyles:new Uint32Array(l)};self.postMessage(t,[t.codePoints.buffer,t.xCoords.buffer,t.yCoords.buffer,t.packedStyles.buffer])};})();\n';
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';
300
300
 
301
301
  // src/layout/LayoutWorkerManager.ts
302
302
  var LayoutWorkerManager = class _LayoutWorkerManager {
@@ -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 _chunk6I53LI3Zjs = require('./chunk-6I53LI3Z.js');
4
+ var _chunk76NMTLYLjs = require('./chunk-76NMTLYL.js');
5
5
 
6
6
  // src/layout/LayoutEngine.ts
7
7
  function computeLineSegments(top, bottom, maxWidth, exclusions) {
@@ -142,8 +142,8 @@ var LayoutEngine = (_class = class {
142
142
  offset += paragraph.length + 1;
143
143
  continue;
144
144
  }
145
- const { shapedText, indexMap } = _chunk6I53LI3Zjs.ArabicShaper.shapeArabic(paragraph);
146
- const levels = _chunk6I53LI3Zjs.BidiResolver.resolveLevels(shapedText);
145
+ const { shapedText, indexMap } = _chunk76NMTLYLjs.ArabicShaper.shapeArabic(paragraph);
146
+ const levels = _chunk76NMTLYLjs.BidiResolver.resolveLevels(shapedText);
147
147
  const words = [];
148
148
  let shapedCharIdx = 0;
149
149
  let pFallback = false;
@@ -192,7 +192,7 @@ var LayoutEngine = (_class = class {
192
192
  words,
193
193
  isEmpty: false,
194
194
  fallbackToCanvas: pFallback || void 0,
195
- baseLevel: _chunk6I53LI3Zjs.BidiResolver.getBaseLevel(shapedText)
195
+ baseLevel: _chunk76NMTLYLjs.BidiResolver.getBaseLevel(shapedText)
196
196
  };
197
197
  if (this.paragraphCache.size > 1e3) this.paragraphCache.clear();
198
198
  this.paragraphCache.set(key, prepared);
@@ -264,8 +264,8 @@ var LayoutEngine = (_class = class {
264
264
  offset += paragraph.length + 1;
265
265
  continue;
266
266
  }
267
- const { shapedText, indexMap } = _chunk6I53LI3Zjs.ArabicShaper.shapeArabic(paragraph);
268
- const levels = _chunk6I53LI3Zjs.BidiResolver.resolveLevels(shapedText);
267
+ const { shapedText, indexMap } = _chunk76NMTLYLjs.ArabicShaper.shapeArabic(paragraph);
268
+ const levels = _chunk76NMTLYLjs.BidiResolver.resolveLevels(shapedText);
269
269
  const words = [];
270
270
  let shapedCharIdx = 0;
271
271
  let pFallback = false;
@@ -317,7 +317,7 @@ var LayoutEngine = (_class = class {
317
317
  words,
318
318
  isEmpty: false,
319
319
  fallbackToCanvas: pFallback || void 0,
320
- baseLevel: _chunk6I53LI3Zjs.BidiResolver.getBaseLevel(shapedText)
320
+ baseLevel: _chunk76NMTLYLjs.BidiResolver.getBaseLevel(shapedText)
321
321
  };
322
322
  if (this.richParagraphCache.size > 1e3) this.richParagraphCache.clear();
323
323
  this.richParagraphCache.set(key, prepared);
@@ -372,7 +372,7 @@ var LayoutEngine = (_class = class {
372
372
  }
373
373
  for (const run of runs) {
374
374
  const runStartX = run[0].x;
375
- _chunk6I53LI3Zjs.BidiResolver.reorderVisual(run, paragraphBaseLevel);
375
+ _chunk76NMTLYLjs.BidiResolver.reorderVisual(run, paragraphBaseLevel);
376
376
  let x = runStartX;
377
377
  for (const node of run) {
378
378
  node.x = x;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ArabicShaper,
3
3
  BidiResolver
4
- } from "./chunk-YSS44ADQ.mjs";
4
+ } from "./chunk-B3Z3JEJH.mjs";
5
5
 
6
6
  // src/layout/LayoutEngine.ts
7
7
  function computeLineSegments(top, bottom, maxWidth, exclusions) {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  LayoutWorkerManager
3
- } from "./chunk-YSS44ADQ.mjs";
3
+ } from "./chunk-B3Z3JEJH.mjs";
4
4
 
5
5
  // src/math/SpringPhysics.ts
6
6
  var SpringPhysics = class {
@@ -1118,6 +1118,36 @@ var MSDFTextEntity = class extends Entity {
1118
1118
  };
1119
1119
 
1120
1120
  // src/text/SVGEntity.ts
1121
+ function isSvgWhitespace(ch) {
1122
+ return ch === " " || ch === " " || ch === "\n" || ch === "\r";
1123
+ }
1124
+ function readSvgAttribute(source, name) {
1125
+ const lowerSource = source.toLowerCase();
1126
+ const svgStart = lowerSource.indexOf("<svg");
1127
+ if (svgStart < 0) return null;
1128
+ const tagEnd = source.indexOf(">", svgStart + 4);
1129
+ if (tagEnd < 0) return null;
1130
+ const tag = source.slice(svgStart + 4, tagEnd);
1131
+ const lowerTag = tag.toLowerCase();
1132
+ const lowerName = name.toLowerCase();
1133
+ for (let i = 0; i < tag.length; i++) {
1134
+ const before = i === 0 ? " " : tag[i - 1];
1135
+ if (!isSvgWhitespace(before)) continue;
1136
+ if (!lowerTag.startsWith(lowerName, i)) continue;
1137
+ let cursor = i + lowerName.length;
1138
+ while (cursor < tag.length && isSvgWhitespace(tag[cursor])) cursor++;
1139
+ if (tag[cursor] !== "=") continue;
1140
+ cursor++;
1141
+ while (cursor < tag.length && isSvgWhitespace(tag[cursor])) cursor++;
1142
+ const quote = tag[cursor];
1143
+ if (quote !== '"' && quote !== "'") continue;
1144
+ const valueStart = cursor + 1;
1145
+ const valueEnd = tag.indexOf(quote, valueStart);
1146
+ if (valueEnd < 0) return null;
1147
+ return tag.slice(valueStart, valueEnd);
1148
+ }
1149
+ return null;
1150
+ }
1121
1151
  var SVGEntity = class extends Entity {
1122
1152
  svgSource = "";
1123
1153
  imageBitmap = null;
@@ -1169,17 +1199,17 @@ var SVGEntity = class extends Entity {
1169
1199
  }
1170
1200
  }
1171
1201
  } catch (e) {
1172
- console.error("Failed parsing SVG via DOMParser, falling back to regex:", e);
1202
+ console.error("Failed parsing SVG via DOMParser, falling back to attribute scan:", e);
1173
1203
  }
1174
1204
  } else {
1175
- const wMatch = /<svg[^>]*\bwidth\s*=\s*["']([^"']+)["']/i.exec(this.svgSource);
1176
- const hMatch = /<svg[^>]*\bheight\s*=\s*["']([^"']+)["']/i.exec(this.svgSource);
1177
- const vbMatch = /<svg[^>]*\bviewBox\s*=\s*["']([^"']+)["']/i.exec(this.svgSource);
1178
- if (wMatch && hMatch) {
1179
- width = parseFloat(wMatch[1]) || 100;
1180
- height = parseFloat(hMatch[1]) || 100;
1181
- } else if (vbMatch) {
1182
- const parts = vbMatch[1].split(/[\s,]+/).map(parseFloat);
1205
+ const wAttr = readSvgAttribute(this.svgSource, "width");
1206
+ const hAttr = readSvgAttribute(this.svgSource, "height");
1207
+ const vbAttr = readSvgAttribute(this.svgSource, "viewBox");
1208
+ if (wAttr && hAttr) {
1209
+ width = parseFloat(wAttr) || 100;
1210
+ height = parseFloat(hAttr) || 100;
1211
+ } else if (vbAttr) {
1212
+ const parts = vbAttr.split(/[\s,]+/).map(parseFloat);
1183
1213
  if (parts.length === 4) {
1184
1214
  width = parts[2];
1185
1215
  height = parts[3];
@@ -230,6 +230,55 @@ function isSafeUrl(urlStr) {
230
230
  }
231
231
 
232
232
  // src/renderer/SVGRenderer.ts
233
+ function parseFontSizeToken(font) {
234
+ for (let i = 0; i < font.length; i++) {
235
+ const ch = font[i];
236
+ if (!(ch >= "0" && ch <= "9" || ch === ".")) continue;
237
+ let j = i + 1;
238
+ while (j < font.length) {
239
+ const next = font[j];
240
+ if (next >= "0" && next <= "9" || next === ".") {
241
+ j++;
242
+ } else {
243
+ break;
244
+ }
245
+ }
246
+ const unit = font.startsWith("rem", j) ? "rem" : font.startsWith("em", j) ? "em" : font.startsWith("px", j) ? "px" : null;
247
+ if (!unit) {
248
+ i = j;
249
+ continue;
250
+ }
251
+ let end = j + unit.length;
252
+ if (font[end] === "/") {
253
+ end++;
254
+ while (end < font.length && font[end] !== " " && font[end] !== " ") end++;
255
+ }
256
+ const value = Number.parseFloat(font.slice(i, j));
257
+ return Number.isFinite(value) ? { value, unit, start: i, end } : null;
258
+ }
259
+ return null;
260
+ }
261
+ function fontFamilyFromShorthand(font, sizeToken) {
262
+ const withoutSize = sizeToken ? `${font.slice(0, sizeToken.start)} ${font.slice(sizeToken.end)}`.trim() : font.trim();
263
+ const family = withoutSize.split(" ").map((part) => part.trim()).filter(
264
+ (part) => part && ![
265
+ "bold",
266
+ "italic",
267
+ "oblique",
268
+ "normal",
269
+ "900",
270
+ "800",
271
+ "700",
272
+ "600",
273
+ "500",
274
+ "400",
275
+ "300",
276
+ "200",
277
+ "100"
278
+ ].includes(part.toLowerCase())
279
+ ).join(" ");
280
+ return family || "sans-serif";
281
+ }
233
282
  var SVGRenderer = (_class2 = class {
234
283
 
235
284
 
@@ -455,17 +504,17 @@ var SVGRenderer = (_class2 = class {
455
504
  }
456
505
  fillText(text, x, y, font, color) {
457
506
  this.flush();
458
- const sizeMatch = font.match(/(\d+(?:\.\d+)?)(px|em|rem)/);
459
- let fontSize = sizeMatch ? parseFloat(sizeMatch[1]) : 16;
460
- if (sizeMatch && sizeMatch[2] !== "px") {
507
+ const sizeToken = parseFontSizeToken(font);
508
+ let fontSize = sizeToken ? sizeToken.value : 16;
509
+ if (sizeToken && sizeToken.unit !== "px") {
461
510
  fontSize = fontSize * 16;
462
511
  }
463
- const styleMatch = font.match(/(italic|oblique)/i);
464
- const fontWeightMatch = font.match(/(bold|[1-9]00)/i);
465
- const fontStyle = styleMatch ? styleMatch[1].toLowerCase() : "normal";
466
- const fontWeight = fontWeightMatch ? fontWeightMatch[1].toLowerCase() : "normal";
467
- const cleanFont = font.replace(/\d+(?:\.\d+)?(px|em|rem)(?:\/\d+(?:\.\d+)?(?:px|em|rem|%)?)?/, "").trim();
468
- const fontFamily = cleanFont.replace(/(bold|italic|normal|600|500|400|300|100)\s+/gi, "").trim() || "sans-serif";
512
+ const lowerFont = font.toLowerCase();
513
+ const fontStyle = lowerFont.includes("italic") ? "italic" : lowerFont.includes("oblique") ? "oblique" : "normal";
514
+ const fontWeight = lowerFont.includes("bold") ? "bold" : _nullishCoalesce(["900", "800", "700", "600", "500", "400", "300", "200", "100"].find(
515
+ (weight) => lowerFont.includes(weight)
516
+ ), () => ( "normal"));
517
+ const fontFamily = fontFamilyFromShorthand(font, sizeToken);
469
518
  const fillVal = this.escapeXML(this.resolveGradient(color));
470
519
  const transformStr = `matrix(${this.ma},${this.mb},${this.mc},${this.md},${this.me},${this.mf})`;
471
520
  this.buffer.push(
@@ -1,6 +1,6 @@
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 _chunk6I53LI3Zjs = require('./chunk-6I53LI3Z.js');
3
+ var _chunk76NMTLYLjs = require('./chunk-76NMTLYL.js');
4
4
 
5
5
  // src/math/SpringPhysics.ts
6
6
  var SpringPhysics = (_class = class {
@@ -1008,7 +1008,7 @@ var MSDFTextEntity = (_class6 = class extends Entity {
1008
1008
  setText(text) {
1009
1009
  if (this.text === text && this.layoutResult) return;
1010
1010
  this.text = text;
1011
- _chunk6I53LI3Zjs.LayoutWorkerManager.getInstance().queueLayout(this.id, this.text, {
1011
+ _chunk76NMTLYLjs.LayoutWorkerManager.getInstance().queueLayout(this.id, this.text, {
1012
1012
  fontId: this.font.id,
1013
1013
  fontSize: this.fontSize,
1014
1014
  maxWidth: 1e3,
@@ -1112,12 +1112,42 @@ var MSDFTextEntity = (_class6 = class extends Entity {
1112
1112
  }
1113
1113
  }
1114
1114
  destroy() {
1115
- _chunk6I53LI3Zjs.LayoutWorkerManager.getInstance().cancelLayout(this.id);
1115
+ _chunk76NMTLYLjs.LayoutWorkerManager.getInstance().cancelLayout(this.id);
1116
1116
  super.destroy();
1117
1117
  }
1118
1118
  }, _class6);
1119
1119
 
1120
1120
  // src/text/SVGEntity.ts
1121
+ function isSvgWhitespace(ch) {
1122
+ return ch === " " || ch === " " || ch === "\n" || ch === "\r";
1123
+ }
1124
+ function readSvgAttribute(source, name) {
1125
+ const lowerSource = source.toLowerCase();
1126
+ const svgStart = lowerSource.indexOf("<svg");
1127
+ if (svgStart < 0) return null;
1128
+ const tagEnd = source.indexOf(">", svgStart + 4);
1129
+ if (tagEnd < 0) return null;
1130
+ const tag = source.slice(svgStart + 4, tagEnd);
1131
+ const lowerTag = tag.toLowerCase();
1132
+ const lowerName = name.toLowerCase();
1133
+ for (let i = 0; i < tag.length; i++) {
1134
+ const before = i === 0 ? " " : tag[i - 1];
1135
+ if (!isSvgWhitespace(before)) continue;
1136
+ if (!lowerTag.startsWith(lowerName, i)) continue;
1137
+ let cursor = i + lowerName.length;
1138
+ while (cursor < tag.length && isSvgWhitespace(tag[cursor])) cursor++;
1139
+ if (tag[cursor] !== "=") continue;
1140
+ cursor++;
1141
+ while (cursor < tag.length && isSvgWhitespace(tag[cursor])) cursor++;
1142
+ const quote = tag[cursor];
1143
+ if (quote !== '"' && quote !== "'") continue;
1144
+ const valueStart = cursor + 1;
1145
+ const valueEnd = tag.indexOf(quote, valueStart);
1146
+ if (valueEnd < 0) return null;
1147
+ return tag.slice(valueStart, valueEnd);
1148
+ }
1149
+ return null;
1150
+ }
1121
1151
  var SVGEntity = (_class7 = class extends Entity {
1122
1152
  __init40() {this.svgSource = ""}
1123
1153
  __init41() {this.imageBitmap = null}
@@ -1169,17 +1199,17 @@ var SVGEntity = (_class7 = class extends Entity {
1169
1199
  }
1170
1200
  }
1171
1201
  } catch (e) {
1172
- console.error("Failed parsing SVG via DOMParser, falling back to regex:", e);
1202
+ console.error("Failed parsing SVG via DOMParser, falling back to attribute scan:", e);
1173
1203
  }
1174
1204
  } else {
1175
- const wMatch = /<svg[^>]*\bwidth\s*=\s*["']([^"']+)["']/i.exec(this.svgSource);
1176
- const hMatch = /<svg[^>]*\bheight\s*=\s*["']([^"']+)["']/i.exec(this.svgSource);
1177
- const vbMatch = /<svg[^>]*\bviewBox\s*=\s*["']([^"']+)["']/i.exec(this.svgSource);
1178
- if (wMatch && hMatch) {
1179
- width = parseFloat(wMatch[1]) || 100;
1180
- height = parseFloat(hMatch[1]) || 100;
1181
- } else if (vbMatch) {
1182
- const parts = vbMatch[1].split(/[\s,]+/).map(parseFloat);
1205
+ const wAttr = readSvgAttribute(this.svgSource, "width");
1206
+ const hAttr = readSvgAttribute(this.svgSource, "height");
1207
+ const vbAttr = readSvgAttribute(this.svgSource, "viewBox");
1208
+ if (wAttr && hAttr) {
1209
+ width = parseFloat(wAttr) || 100;
1210
+ height = parseFloat(hAttr) || 100;
1211
+ } else if (vbAttr) {
1212
+ const parts = vbAttr.split(/[\s,]+/).map(parseFloat);
1183
1213
  if (parts.length === 4) {
1184
1214
  width = parts[2];
1185
1215
  height = parts[3];
@@ -230,6 +230,55 @@ function isSafeUrl(urlStr) {
230
230
  }
231
231
 
232
232
  // src/renderer/SVGRenderer.ts
233
+ function parseFontSizeToken(font) {
234
+ for (let i = 0; i < font.length; i++) {
235
+ const ch = font[i];
236
+ if (!(ch >= "0" && ch <= "9" || ch === ".")) continue;
237
+ let j = i + 1;
238
+ while (j < font.length) {
239
+ const next = font[j];
240
+ if (next >= "0" && next <= "9" || next === ".") {
241
+ j++;
242
+ } else {
243
+ break;
244
+ }
245
+ }
246
+ const unit = font.startsWith("rem", j) ? "rem" : font.startsWith("em", j) ? "em" : font.startsWith("px", j) ? "px" : null;
247
+ if (!unit) {
248
+ i = j;
249
+ continue;
250
+ }
251
+ let end = j + unit.length;
252
+ if (font[end] === "/") {
253
+ end++;
254
+ while (end < font.length && font[end] !== " " && font[end] !== " ") end++;
255
+ }
256
+ const value = Number.parseFloat(font.slice(i, j));
257
+ return Number.isFinite(value) ? { value, unit, start: i, end } : null;
258
+ }
259
+ return null;
260
+ }
261
+ function fontFamilyFromShorthand(font, sizeToken) {
262
+ const withoutSize = sizeToken ? `${font.slice(0, sizeToken.start)} ${font.slice(sizeToken.end)}`.trim() : font.trim();
263
+ const family = withoutSize.split(" ").map((part) => part.trim()).filter(
264
+ (part) => part && ![
265
+ "bold",
266
+ "italic",
267
+ "oblique",
268
+ "normal",
269
+ "900",
270
+ "800",
271
+ "700",
272
+ "600",
273
+ "500",
274
+ "400",
275
+ "300",
276
+ "200",
277
+ "100"
278
+ ].includes(part.toLowerCase())
279
+ ).join(" ");
280
+ return family || "sans-serif";
281
+ }
233
282
  var SVGRenderer = class {
234
283
  width;
235
284
  height;
@@ -455,17 +504,17 @@ var SVGRenderer = class {
455
504
  }
456
505
  fillText(text, x, y, font, color) {
457
506
  this.flush();
458
- const sizeMatch = font.match(/(\d+(?:\.\d+)?)(px|em|rem)/);
459
- let fontSize = sizeMatch ? parseFloat(sizeMatch[1]) : 16;
460
- if (sizeMatch && sizeMatch[2] !== "px") {
507
+ const sizeToken = parseFontSizeToken(font);
508
+ let fontSize = sizeToken ? sizeToken.value : 16;
509
+ if (sizeToken && sizeToken.unit !== "px") {
461
510
  fontSize = fontSize * 16;
462
511
  }
463
- const styleMatch = font.match(/(italic|oblique)/i);
464
- const fontWeightMatch = font.match(/(bold|[1-9]00)/i);
465
- const fontStyle = styleMatch ? styleMatch[1].toLowerCase() : "normal";
466
- const fontWeight = fontWeightMatch ? fontWeightMatch[1].toLowerCase() : "normal";
467
- const cleanFont = font.replace(/\d+(?:\.\d+)?(px|em|rem)(?:\/\d+(?:\.\d+)?(?:px|em|rem|%)?)?/, "").trim();
468
- const fontFamily = cleanFont.replace(/(bold|italic|normal|600|500|400|300|100)\s+/gi, "").trim() || "sans-serif";
512
+ const lowerFont = font.toLowerCase();
513
+ const fontStyle = lowerFont.includes("italic") ? "italic" : lowerFont.includes("oblique") ? "oblique" : "normal";
514
+ const fontWeight = lowerFont.includes("bold") ? "bold" : ["900", "800", "700", "600", "500", "400", "300", "200", "100"].find(
515
+ (weight) => lowerFont.includes(weight)
516
+ ) ?? "normal";
517
+ const fontFamily = fontFamilyFromShorthand(font, sizeToken);
469
518
  const fillVal = this.escapeXML(this.resolveGradient(color));
470
519
  const transformStr = `matrix(${this.ma},${this.mb},${this.mc},${this.md},${this.me},${this.mf})`;
471
520
  this.buffer.push(
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
 
5
5
 
6
- var _chunkVVOQNUBKjs = require('./chunk-VVOQNUBK.js');
6
+ var _chunkIKLYTHALjs = require('./chunk-IKLYTHAL.js');
7
7
 
8
8
 
9
9
 
@@ -12,7 +12,7 @@ var _chunkVVOQNUBKjs = require('./chunk-VVOQNUBK.js');
12
12
 
13
13
 
14
14
 
15
- var _chunkTXA3LZDMjs = require('./chunk-TXA3LZDM.js');
15
+ var _chunkTPNADFNNjs = require('./chunk-TPNADFNN.js');
16
16
 
17
17
 
18
18
 
@@ -24,12 +24,12 @@ var _chunkTXA3LZDMjs = require('./chunk-TXA3LZDM.js');
24
24
 
25
25
 
26
26
 
27
- var _chunkKPWVPGHRjs = require('./chunk-KPWVPGHR.js');
27
+ var _chunkTQ4H357Qjs = require('./chunk-TQ4H357Q.js');
28
28
 
29
29
 
30
30
 
31
31
 
32
- var _chunk6I53LI3Zjs = require('./chunk-6I53LI3Z.js');
32
+ var _chunk76NMTLYLjs = require('./chunk-76NMTLYL.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 _chunkKPWVPGHRjs.Entity {
44
+ var ComputeParticleEntity = (_class = class extends _chunkTQ4H357Qjs.Entity {
45
45
 
46
46
 
47
47
 
@@ -410,7 +410,7 @@ var Scene = (_class2 = class _Scene {
410
410
  this.particleBackend = _nullishCoalesce(options.particleBackend, () => ( "auto"));
411
411
  this.a11ySyncInterval = _nullishCoalesce(options.a11ySyncInterval, () => ( 0));
412
412
  this.reducedMotionQuery = typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia("(prefers-reduced-motion: reduce)") : null;
413
- this.root = new class RootEntity extends _chunkKPWVPGHRjs.Entity {
413
+ this.root = new class RootEntity extends _chunkTQ4H357Qjs.Entity {
414
414
  isPointInside() {
415
415
  return false;
416
416
  }
@@ -419,7 +419,7 @@ var Scene = (_class2 = class _Scene {
419
419
  }
420
420
  }("root");
421
421
  this.root._scene = this;
422
- this.overlayRoot = new class OverlayRoot extends _chunkKPWVPGHRjs.Entity {
422
+ this.overlayRoot = new class OverlayRoot extends _chunkTQ4H357Qjs.Entity {
423
423
  isPointInside() {
424
424
  return false;
425
425
  }
@@ -430,7 +430,7 @@ var Scene = (_class2 = class _Scene {
430
430
  if (options.renderer) {
431
431
  this.renderer = options.renderer;
432
432
  } else {
433
- this.renderer = new (0, _chunkTXA3LZDMjs.CanvasRenderer)(canvas);
433
+ this.renderer = new (0, _chunkTPNADFNNjs.CanvasRenderer)(canvas);
434
434
  }
435
435
  if (typeof document !== "undefined") {
436
436
  this.a11yRoot = document.createElement("div");
@@ -760,42 +760,42 @@ var Scene = (_class2 = class _Scene {
760
760
  el.style.background = "transparent";
761
761
  }
762
762
  el.addEventListener("click", (e) => {
763
- node.dispatchEvent(new (0, _chunkKPWVPGHRjs.VectoJSEvent)("click", node, e));
763
+ node.dispatchEvent(new (0, _chunkTQ4H357Qjs.VectoJSEvent)("click", node, e));
764
764
  });
765
765
  el.addEventListener("mouseenter", (e) => {
766
766
  if (this.debugA11y) el.style.backgroundColor = "rgba(56, 189, 248, 0.2)";
767
- node.dispatchEvent(new (0, _chunkKPWVPGHRjs.VectoJSEvent)("hover", node, e, false));
767
+ node.dispatchEvent(new (0, _chunkTQ4H357Qjs.VectoJSEvent)("hover", node, e, false));
768
768
  });
769
769
  el.addEventListener("mouseleave", (e) => {
770
770
  if (this.debugA11y) el.style.backgroundColor = "rgba(56, 189, 248, 0.05)";
771
- node.dispatchEvent(new (0, _chunkKPWVPGHRjs.VectoJSEvent)("pointerleave", node, e, false));
771
+ node.dispatchEvent(new (0, _chunkTQ4H357Qjs.VectoJSEvent)("pointerleave", node, e, false));
772
772
  });
773
773
  const capEl = el;
774
774
  el.addEventListener("pointerdown", (e) => {
775
775
  if (typeof capEl.setPointerCapture === "function") capEl.setPointerCapture(e.pointerId);
776
- node.dispatchEvent(new (0, _chunkKPWVPGHRjs.VectoJSEvent)("pointerdown", node, e));
776
+ node.dispatchEvent(new (0, _chunkTQ4H357Qjs.VectoJSEvent)("pointerdown", node, e));
777
777
  });
778
778
  el.addEventListener("pointerup", (e) => {
779
779
  if (typeof capEl.releasePointerCapture === "function")
780
780
  capEl.releasePointerCapture(e.pointerId);
781
- node.dispatchEvent(new (0, _chunkKPWVPGHRjs.VectoJSEvent)("pointerup", node, e));
781
+ node.dispatchEvent(new (0, _chunkTQ4H357Qjs.VectoJSEvent)("pointerup", node, e));
782
782
  });
783
783
  el.addEventListener(
784
784
  "pointermove",
785
- (e) => node.dispatchEvent(new (0, _chunkKPWVPGHRjs.VectoJSEvent)("pointermove", node, e))
785
+ (e) => node.dispatchEvent(new (0, _chunkTQ4H357Qjs.VectoJSEvent)("pointermove", node, e))
786
786
  );
787
787
  el.addEventListener(
788
788
  "wheel",
789
789
  (e) => {
790
- node.dispatchEvent(new (0, _chunkKPWVPGHRjs.VectoJSEvent)("wheel", node, e));
790
+ node.dispatchEvent(new (0, _chunkTQ4H357Qjs.VectoJSEvent)("wheel", node, e));
791
791
  },
792
792
  { passive: false }
793
793
  );
794
794
  el.addEventListener("keydown", (e) => {
795
- node.dispatchEvent(new (0, _chunkKPWVPGHRjs.VectoJSEvent)("keydown", node, e));
795
+ node.dispatchEvent(new (0, _chunkTQ4H357Qjs.VectoJSEvent)("keydown", node, e));
796
796
  });
797
797
  el.addEventListener("keyup", (e) => {
798
- node.dispatchEvent(new (0, _chunkKPWVPGHRjs.VectoJSEvent)("keyup", node, e));
798
+ node.dispatchEvent(new (0, _chunkTQ4H357Qjs.VectoJSEvent)("keyup", node, e));
799
799
  });
800
800
  if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {
801
801
  const input = el;
@@ -809,6 +809,7 @@ var Scene = (_class2 = class _Scene {
809
809
  selectionEnd: _nullishCoalesce(input.selectionEnd, () => ( input.value.length)),
810
810
  composition
811
811
  });
812
+ this.markDirty();
812
813
  };
813
814
  el.addEventListener("input", forward);
814
815
  el.addEventListener("change", forward);
@@ -867,7 +868,7 @@ var Scene = (_class2 = class _Scene {
867
868
  el.addEventListener("keydown", (e) => {
868
869
  if (e.key === "Enter" || e.key === " ") {
869
870
  e.preventDefault();
870
- node.dispatchEvent(new (0, _chunkKPWVPGHRjs.VectoJSEvent)("click", node, e));
871
+ node.dispatchEvent(new (0, _chunkTQ4H357Qjs.VectoJSEvent)("click", node, e));
871
872
  }
872
873
  });
873
874
  }
@@ -892,7 +893,7 @@ var Scene = (_class2 = class _Scene {
892
893
  if (el.placeholder !== attrs.placeholder) el.placeholder = attrs.placeholder;
893
894
  }
894
895
  if (attrs.href !== void 0 && el instanceof HTMLAnchorElement) {
895
- const safeHref = _chunkTXA3LZDMjs.sanitizeUrl.call(void 0, attrs.href);
896
+ const safeHref = _chunkTPNADFNNjs.sanitizeUrl.call(void 0, attrs.href);
896
897
  if (el.getAttribute("href") !== safeHref) el.setAttribute("href", safeHref);
897
898
  }
898
899
  if (el instanceof HTMLImageElement) {
@@ -1440,7 +1441,7 @@ var Scene = (_class2 = class _Scene {
1440
1441
  * Export the current scene state to a lightweight, flat SVG XML string.
1441
1442
  */
1442
1443
  toSVG() {
1443
- const renderer = new (0, _chunkTXA3LZDMjs.SVGRenderer)(this.width, this.height);
1444
+ const renderer = new (0, _chunkTPNADFNNjs.SVGRenderer)(this.width, this.height);
1444
1445
  this.render(renderer, 0, 0);
1445
1446
  return renderer.toXMLString();
1446
1447
  }
@@ -1603,10 +1604,10 @@ var Scene = (_class2 = class _Scene {
1603
1604
  // src/components/TextEntity.ts
1604
1605
  var sharedMeasurer;
1605
1606
  function defaultMeasurer() {
1606
- if (sharedMeasurer === void 0) sharedMeasurer = _chunkVVOQNUBKjs.createCanvasMeasurer.call(void 0, "sans-serif");
1607
+ if (sharedMeasurer === void 0) sharedMeasurer = _chunkIKLYTHALjs.createCanvasMeasurer.call(void 0, "sans-serif");
1607
1608
  return sharedMeasurer;
1608
1609
  }
1609
- var TextEntity = (_class3 = class extends _chunkKPWVPGHRjs.Entity {
1610
+ var TextEntity = (_class3 = class extends _chunkTQ4H357Qjs.Entity {
1610
1611
 
1611
1612
 
1612
1613
 
@@ -1623,7 +1624,7 @@ var TextEntity = (_class3 = class extends _chunkKPWVPGHRjs.Entity {
1623
1624
  this.text = text;
1624
1625
  this.atlas = atlas;
1625
1626
  this.fontSize = fontSize;
1626
- this.layout = new (0, _chunkVVOQNUBKjs.LayoutEngine)(maxWidth, 1e4, defaultMeasurer());
1627
+ this.layout = new (0, _chunkIKLYTHALjs.LayoutEngine)(maxWidth, 1e4, defaultMeasurer());
1627
1628
  this.prepared = this.layout.prepare(this.text, this.atlas, this.fontSize);
1628
1629
  this.applyLayout();
1629
1630
  this.interactive = true;
@@ -1704,7 +1705,7 @@ var TextEntity = (_class3 = class extends _chunkKPWVPGHRjs.Entity {
1704
1705
  }, _class3);
1705
1706
 
1706
1707
  // src/components/GridTextEntity.ts
1707
- var GridTextEntity = (_class4 = class extends _chunkKPWVPGHRjs.Entity {
1708
+ var GridTextEntity = (_class4 = class extends _chunkTQ4H357Qjs.Entity {
1708
1709
 
1709
1710
  __init54() {this.fillStyle = "#ffffff"}
1710
1711
  __init55() {this.grid = []}
@@ -1798,7 +1799,7 @@ function distSqToSegment(px, py, x1, y1, x2, y2) {
1798
1799
  const ey = py - cy;
1799
1800
  return ex * ex + ey * ey;
1800
1801
  }
1801
- var SplineEntity = (_class5 = class extends _chunkKPWVPGHRjs.Entity {
1802
+ var SplineEntity = (_class5 = class extends _chunkTQ4H357Qjs.Entity {
1802
1803
 
1803
1804
 
1804
1805
 
@@ -2155,7 +2156,7 @@ var SpatialHashGrid = (_class6 = class {
2155
2156
  }, _class6);
2156
2157
 
2157
2158
  // src/tree/DOMPortalEntity.ts
2158
- var DOMPortalEntity = (_class7 = class extends _chunkKPWVPGHRjs.Entity {
2159
+ var DOMPortalEntity = (_class7 = class extends _chunkTQ4H357Qjs.Entity {
2159
2160
 
2160
2161
  __init64() {this.isDOMPortal = true}
2161
2162
  __init65() {this.domListeners = []}
@@ -2190,7 +2191,7 @@ var DOMPortalEntity = (_class7 = class extends _chunkKPWVPGHRjs.Entity {
2190
2191
  const events = ["click", "pointerdown", "pointerup", "pointermove", "wheel"];
2191
2192
  for (const type of events) {
2192
2193
  const handler = (e) => {
2193
- this.dispatchEvent(new (0, _chunkKPWVPGHRjs.VectoJSEvent)(type, this, e));
2194
+ this.dispatchEvent(new (0, _chunkTQ4H357Qjs.VectoJSEvent)(type, this, e));
2194
2195
  };
2195
2196
  this.domElement.addEventListener(type, handler);
2196
2197
  this.domListeners.push({ type, handler, capture: false });
@@ -2201,7 +2202,7 @@ var DOMPortalEntity = (_class7 = class extends _chunkKPWVPGHRjs.Entity {
2201
2202
  ];
2202
2203
  for (const { native, vecto } of hoverEvents) {
2203
2204
  const handler = (e) => {
2204
- this.dispatchEvent(new (0, _chunkKPWVPGHRjs.VectoJSEvent)(vecto, this, e, false));
2205
+ this.dispatchEvent(new (0, _chunkTQ4H357Qjs.VectoJSEvent)(vecto, this, e, false));
2205
2206
  };
2206
2207
  this.domElement.addEventListener(native, handler);
2207
2208
  this.domListeners.push({ type: native, handler, capture: false });
@@ -2209,7 +2210,7 @@ var DOMPortalEntity = (_class7 = class extends _chunkKPWVPGHRjs.Entity {
2209
2210
  const focusEvents = ["focus", "blur"];
2210
2211
  for (const type of focusEvents) {
2211
2212
  const handler = (e) => {
2212
- this.dispatchEvent(new (0, _chunkKPWVPGHRjs.VectoJSEvent)(type, this, e, true));
2213
+ this.dispatchEvent(new (0, _chunkTQ4H357Qjs.VectoJSEvent)(type, this, e, true));
2213
2214
  };
2214
2215
  this.domElement.addEventListener(type, handler, true);
2215
2216
  this.domListeners.push({ type, handler, capture: true });
@@ -2248,8 +2249,8 @@ var DOMPortalEntity = (_class7 = class extends _chunkKPWVPGHRjs.Entity {
2248
2249
  }, _class7);
2249
2250
 
2250
2251
  // src/index.ts
2251
- Scene.registerWebGLPointRendererCreator(_chunkTXA3LZDMjs.createWebGLPointRenderer);
2252
- Scene.registerWebGPUParticleSystemManager(_chunkTXA3LZDMjs.WebGPUParticleSystemManager);
2252
+ Scene.registerWebGLPointRendererCreator(_chunkTPNADFNNjs.createWebGLPointRenderer);
2253
+ Scene.registerWebGPUParticleSystemManager(_chunkTPNADFNNjs.WebGPUParticleSystemManager);
2253
2254
 
2254
2255
 
2255
2256
 
@@ -2294,4 +2295,4 @@ Scene.registerWebGPUParticleSystemManager(_chunkTXA3LZDMjs.WebGPUParticleSystemM
2294
2295
 
2295
2296
 
2296
2297
 
2297
- exports.ArabicShaper = _chunk6I53LI3Zjs.ArabicShaper; exports.BidiResolver = _chunk6I53LI3Zjs.BidiResolver; exports.CanvasRenderer = _chunkTXA3LZDMjs.CanvasRenderer; exports.ComputeParticleEntity = ComputeParticleEntity; exports.DOMPortalEntity = DOMPortalEntity; exports.Easing = _chunkKPWVPGHRjs.Easing; exports.Entity = _chunkKPWVPGHRjs.Entity; exports.GridTextEntity = GridTextEntity; exports.LayoutEngine = _chunkVVOQNUBKjs.LayoutEngine; exports.LayoutResultBuffer = _chunkVVOQNUBKjs.LayoutResultBuffer; exports.LayoutWorkerManager = _chunk6I53LI3Zjs.LayoutWorkerManager; exports.MSDFFont = _chunkKPWVPGHRjs.MSDFFont; exports.MSDFTextEntity = _chunkKPWVPGHRjs.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 = _chunkKPWVPGHRjs.SVGEntity; exports.SVGRenderer = _chunkTXA3LZDMjs.SVGRenderer; exports.Scene = Scene; exports.SpatialHashGrid = SpatialHashGrid; exports.SplineEntity = SplineEntity; exports.SpringDriver = _chunkKPWVPGHRjs.SpringDriver; exports.SpringPhysics = _chunkKPWVPGHRjs.SpringPhysics; exports.TextEntity = TextEntity; exports.TweenDriver = _chunkKPWVPGHRjs.TweenDriver; exports.VectoJSEvent = _chunkKPWVPGHRjs.VectoJSEvent; exports.WebGPUParticleSystemManager = _chunkTXA3LZDMjs.WebGPUParticleSystemManager; exports.computeLineSegments = _chunkVVOQNUBKjs.computeLineSegments; exports.createCanvasMeasurer = _chunkVVOQNUBKjs.createCanvasMeasurer; exports.createWebGLPointRenderer = _chunkTXA3LZDMjs.createWebGLPointRenderer; exports.isSafeUrl = _chunkTXA3LZDMjs.isSafeUrl; exports.isTweenConfig = _chunkKPWVPGHRjs.isTweenConfig; exports.loadSpline = loadSpline; exports.parseColorToRGBA = _chunkTXA3LZDMjs.parseColorToRGBA; exports.polySegmentToBezier = polySegmentToBezier; exports.sanitizeUrl = _chunkTXA3LZDMjs.sanitizeUrl;
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 = _chunkIKLYTHALjs.LayoutEngine; exports.LayoutResultBuffer = _chunkIKLYTHALjs.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 = _chunkIKLYTHALjs.computeLineSegments; exports.createCanvasMeasurer = _chunkIKLYTHALjs.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;
package/dist/index.mjs CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  LayoutResultBuffer,
4
4
  computeLineSegments,
5
5
  createCanvasMeasurer
6
- } from "./chunk-LIOJ37MH.mjs";
6
+ } from "./chunk-MCULB5VC.mjs";
7
7
  import {
8
8
  CanvasRenderer,
9
9
  SVGRenderer,
@@ -12,7 +12,7 @@ import {
12
12
  isSafeUrl,
13
13
  parseColorToRGBA,
14
14
  sanitizeUrl
15
- } from "./chunk-NKOQV3RM.mjs";
15
+ } from "./chunk-WEQRBXT2.mjs";
16
16
  import {
17
17
  Easing,
18
18
  Entity,
@@ -24,12 +24,12 @@ import {
24
24
  TweenDriver,
25
25
  VectoJSEvent,
26
26
  isTweenConfig
27
- } from "./chunk-ISGOYXPF.mjs";
27
+ } from "./chunk-P6MDWIEX.mjs";
28
28
  import {
29
29
  ArabicShaper,
30
30
  BidiResolver,
31
31
  LayoutWorkerManager
32
- } from "./chunk-YSS44ADQ.mjs";
32
+ } from "./chunk-B3Z3JEJH.mjs";
33
33
 
34
34
  // src/tree/ComputeParticleEntity.ts
35
35
  var PARTICLE_STRIDE_FLOATS = 8;
@@ -809,6 +809,7 @@ var Scene = class _Scene {
809
809
  selectionEnd: input.selectionEnd ?? input.value.length,
810
810
  composition
811
811
  });
812
+ this.markDirty();
812
813
  };
813
814
  el.addEventListener("input", forward);
814
815
  el.addEventListener("change", forward);
@@ -1 +1 @@
1
- export declare const WORKER_SOURCE_STRING = "\"use strict\";(()=>{var b=new Map;self.onmessage=x=>{let{id:S,seqId:k,text:A,fontId:i,fontData:c,maxWidth:d,maxHeight:H,fontSize:o,lineHeight:F,letterSpacing:C}=x.data;c&&b.set(i,c);let n=b.get(i);if(!n)return;let f=[],u=[],y=[],l=[],e=0,r=0,h=n.metrics?.ascender??.8,D=n.metrics?.descender??-.2,m=F??o*(h-D),p=Array.from(A);for(let s=0;s<p.length;s++){let a=p[s].codePointAt(0),g=(n.glyphs?.find(w=>w.unicode===a)?.advance??1)*o;e+g>d&&a===32&&(e=0,r++),f.push(a),u.push(e);let M=r*m+h*o;y.push(M),l.push(-256),e+=g+(C??0)}let t={id:S,seqId:k,width:Math.min(e,d),height:(r+1)*m,codePoints:new Uint32Array(f),xCoords:new Float32Array(u),yCoords:new Float32Array(y),packedStyles:new Uint32Array(l)};self.postMessage(t,[t.codePoints.buffer,t.xCoords.buffer,t.yCoords.buffer,t.packedStyles.buffer])};})();\n";
1
+ export declare const 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";
package/dist/layout.js CHANGED
@@ -3,14 +3,14 @@
3
3
 
4
4
 
5
5
 
6
- var _chunkVVOQNUBKjs = require('./chunk-VVOQNUBK.js');
6
+ var _chunkIKLYTHALjs = require('./chunk-IKLYTHAL.js');
7
7
 
8
8
 
9
- var _chunk6I53LI3Zjs = require('./chunk-6I53LI3Z.js');
9
+ var _chunk76NMTLYLjs = require('./chunk-76NMTLYL.js');
10
10
 
11
11
 
12
12
 
13
13
 
14
14
 
15
15
 
16
- exports.LayoutEngine = _chunkVVOQNUBKjs.LayoutEngine; exports.LayoutResultBuffer = _chunkVVOQNUBKjs.LayoutResultBuffer; exports.LayoutWorkerManager = _chunk6I53LI3Zjs.LayoutWorkerManager; exports.computeLineSegments = _chunkVVOQNUBKjs.computeLineSegments; exports.createCanvasMeasurer = _chunkVVOQNUBKjs.createCanvasMeasurer;
16
+ exports.LayoutEngine = _chunkIKLYTHALjs.LayoutEngine; exports.LayoutResultBuffer = _chunkIKLYTHALjs.LayoutResultBuffer; exports.LayoutWorkerManager = _chunk76NMTLYLjs.LayoutWorkerManager; exports.computeLineSegments = _chunkIKLYTHALjs.computeLineSegments; exports.createCanvasMeasurer = _chunkIKLYTHALjs.createCanvasMeasurer;
package/dist/layout.mjs CHANGED
@@ -3,10 +3,10 @@ import {
3
3
  LayoutResultBuffer,
4
4
  computeLineSegments,
5
5
  createCanvasMeasurer
6
- } from "./chunk-LIOJ37MH.mjs";
6
+ } from "./chunk-MCULB5VC.mjs";
7
7
  import {
8
8
  LayoutWorkerManager
9
- } from "./chunk-YSS44ADQ.mjs";
9
+ } from "./chunk-B3Z3JEJH.mjs";
10
10
  export {
11
11
  LayoutEngine,
12
12
  LayoutResultBuffer,
package/dist/renderer.js CHANGED
@@ -4,11 +4,11 @@
4
4
 
5
5
 
6
6
 
7
- var _chunkTXA3LZDMjs = require('./chunk-TXA3LZDM.js');
7
+ var _chunkTPNADFNNjs = require('./chunk-TPNADFNN.js');
8
8
 
9
9
 
10
10
 
11
11
 
12
12
 
13
13
 
14
- exports.CanvasRenderer = _chunkTXA3LZDMjs.CanvasRenderer; exports.SVGRenderer = _chunkTXA3LZDMjs.SVGRenderer; exports.WebGPUParticleSystemManager = _chunkTXA3LZDMjs.WebGPUParticleSystemManager; exports.createWebGLPointRenderer = _chunkTXA3LZDMjs.createWebGLPointRenderer; exports.parseColorToRGBA = _chunkTXA3LZDMjs.parseColorToRGBA;
14
+ exports.CanvasRenderer = _chunkTPNADFNNjs.CanvasRenderer; exports.SVGRenderer = _chunkTPNADFNNjs.SVGRenderer; exports.WebGPUParticleSystemManager = _chunkTPNADFNNjs.WebGPUParticleSystemManager; exports.createWebGLPointRenderer = _chunkTPNADFNNjs.createWebGLPointRenderer; exports.parseColorToRGBA = _chunkTPNADFNNjs.parseColorToRGBA;
package/dist/renderer.mjs CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  WebGPUParticleSystemManager,
5
5
  createWebGLPointRenderer,
6
6
  parseColorToRGBA
7
- } from "./chunk-NKOQV3RM.mjs";
7
+ } from "./chunk-WEQRBXT2.mjs";
8
8
  export {
9
9
  CanvasRenderer,
10
10
  SVGRenderer,
package/dist/text.js CHANGED
@@ -2,15 +2,15 @@
2
2
 
3
3
 
4
4
 
5
- var _chunkKPWVPGHRjs = require('./chunk-KPWVPGHR.js');
5
+ var _chunkTQ4H357Qjs = require('./chunk-TQ4H357Q.js');
6
6
 
7
7
 
8
8
 
9
- var _chunk6I53LI3Zjs = require('./chunk-6I53LI3Z.js');
9
+ var _chunk76NMTLYLjs = require('./chunk-76NMTLYL.js');
10
10
 
11
11
 
12
12
 
13
13
 
14
14
 
15
15
 
16
- exports.ArabicShaper = _chunk6I53LI3Zjs.ArabicShaper; exports.BidiResolver = _chunk6I53LI3Zjs.BidiResolver; exports.MSDFFont = _chunkKPWVPGHRjs.MSDFFont; exports.MSDFTextEntity = _chunkKPWVPGHRjs.MSDFTextEntity; exports.SVGEntity = _chunkKPWVPGHRjs.SVGEntity;
16
+ exports.ArabicShaper = _chunk76NMTLYLjs.ArabicShaper; exports.BidiResolver = _chunk76NMTLYLjs.BidiResolver; exports.MSDFFont = _chunkTQ4H357Qjs.MSDFFont; exports.MSDFTextEntity = _chunkTQ4H357Qjs.MSDFTextEntity; exports.SVGEntity = _chunkTQ4H357Qjs.SVGEntity;
package/dist/text.mjs CHANGED
@@ -2,11 +2,11 @@ import {
2
2
  MSDFFont,
3
3
  MSDFTextEntity,
4
4
  SVGEntity
5
- } from "./chunk-ISGOYXPF.mjs";
5
+ } from "./chunk-P6MDWIEX.mjs";
6
6
  import {
7
7
  ArabicShaper,
8
8
  BidiResolver
9
- } from "./chunk-YSS44ADQ.mjs";
9
+ } from "./chunk-B3Z3JEJH.mjs";
10
10
  export {
11
11
  ArabicShaper,
12
12
  BidiResolver,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vectojs/core",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },