@vectojs/core 1.8.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -372,10 +372,18 @@ var Entity = (_class4 = class {
372
372
  * `child.parent` is only ever `null` or `this`'s ultimate owner, so this
373
373
  * check is O(1) for the overwhelming common case (a brand-new entity).
374
374
  *
375
- * @param child - The entity to add as a child.
375
+ * Accepts multiple children in one call (`parent.add(a, b, c)`); each is
376
+ * attached in argument order with the same detach-then-attach semantics.
377
+ *
378
+ * @param children - One or more entities to add as children.
376
379
  * @returns `this` for method chaining.
377
380
  */
378
- add(child) {
381
+ add(...children) {
382
+ for (const child of children) this._addOne(child);
383
+ return this;
384
+ }
385
+ /** Attach a single child (the O(1) common path). See {@link add}. */
386
+ _addOne(child) {
379
387
  if (child.parent) child.parent.remove(child);
380
388
  child.parent = this;
381
389
  this.children.push(child);
@@ -385,7 +393,6 @@ var Entity = (_class4 = class {
385
393
  s.markDirty();
386
394
  child._notifyMounted();
387
395
  }
388
- return this;
389
396
  }
390
397
  /** Called once when this entity becomes attached to a live Scene. Override to react. */
391
398
  onMounted() {
@@ -417,6 +424,24 @@ var Entity = (_class4 = class {
417
424
  }
418
425
  return this;
419
426
  }
427
+ /**
428
+ * Assign several own properties in one call, each through its normal setter
429
+ * (so a property with a configured {@link setTransition} still animates, and
430
+ * `interactive` still flags the scene for a11y reorder). A construction-time
431
+ * ergonomic only — it is a plain `for…in` over the given object and touches
432
+ * no per-frame path.
433
+ *
434
+ * @param props - Partial set of this entity's own properties to assign.
435
+ * @returns `this` for method chaining.
436
+ * @example rect.set({ x: 40, y: 40, width: 120, fill: '#38bdf8' });
437
+ */
438
+ set(props) {
439
+ for (const key in props) {
440
+ const value = props[key];
441
+ if (value !== void 0) this[key] = value;
442
+ }
443
+ return this;
444
+ }
420
445
  /**
421
446
  * Set the local position of this entity.
422
447
  *
@@ -372,10 +372,18 @@ var Entity = class {
372
372
  * `child.parent` is only ever `null` or `this`'s ultimate owner, so this
373
373
  * check is O(1) for the overwhelming common case (a brand-new entity).
374
374
  *
375
- * @param child - The entity to add as a child.
375
+ * Accepts multiple children in one call (`parent.add(a, b, c)`); each is
376
+ * attached in argument order with the same detach-then-attach semantics.
377
+ *
378
+ * @param children - One or more entities to add as children.
376
379
  * @returns `this` for method chaining.
377
380
  */
378
- add(child) {
381
+ add(...children) {
382
+ for (const child of children) this._addOne(child);
383
+ return this;
384
+ }
385
+ /** Attach a single child (the O(1) common path). See {@link add}. */
386
+ _addOne(child) {
379
387
  if (child.parent) child.parent.remove(child);
380
388
  child.parent = this;
381
389
  this.children.push(child);
@@ -385,7 +393,6 @@ var Entity = class {
385
393
  s.markDirty();
386
394
  child._notifyMounted();
387
395
  }
388
- return this;
389
396
  }
390
397
  /** Called once when this entity becomes attached to a live Scene. Override to react. */
391
398
  onMounted() {
@@ -417,6 +424,24 @@ var Entity = class {
417
424
  }
418
425
  return this;
419
426
  }
427
+ /**
428
+ * Assign several own properties in one call, each through its normal setter
429
+ * (so a property with a configured {@link setTransition} still animates, and
430
+ * `interactive` still flags the scene for a11y reorder). A construction-time
431
+ * ergonomic only — it is a plain `for…in` over the given object and touches
432
+ * no per-frame path.
433
+ *
434
+ * @param props - Partial set of this entity's own properties to assign.
435
+ * @returns `this` for method chaining.
436
+ * @example rect.set({ x: 40, y: 40, width: 120, fill: '#38bdf8' });
437
+ */
438
+ set(props) {
439
+ for (const key in props) {
440
+ const value = props[key];
441
+ if (value !== void 0) this[key] = value;
442
+ }
443
+ return this;
444
+ }
420
445
  /**
421
446
  * Set the local position of this entity.
422
447
  *
@@ -0,0 +1,48 @@
1
+ import { Entity, type Bounds, type BatchCircle } from '../tree/Entity';
2
+ import type { IRenderer } from '../renderer/IRenderer';
3
+ /** Construction options for {@link Circle}. */
4
+ export interface CircleOptions {
5
+ /** Radius in local units. Default `0`. */
6
+ radius?: number;
7
+ /** CSS fill color, or `null` for no fill. Default `'#38bdf8'`. */
8
+ fill?: string | null;
9
+ /** CSS stroke color, or `null` for no stroke. Default `null`. */
10
+ stroke?: string | null;
11
+ /** Stroke width in local units. Default `1`. */
12
+ strokeWidth?: number;
13
+ }
14
+ /**
15
+ * A concrete circle primitive centered on its local origin `(0, 0)`.
16
+ * Instantiate directly — no subclassing:
17
+ *
18
+ * @example
19
+ * const dot = new Circle({ radius: 24, fill: '#f97316' });
20
+ * dot.set({ x: 100, y: 100 });
21
+ * scene.add(dot);
22
+ *
23
+ * The accessibility shadow box is sized to the circle's bounding square and
24
+ * offset by `-radius` so it covers the drawn disc (whose center is the entity
25
+ * origin). A solid-fill (unstroked) Circle opts into the point-batch fast path
26
+ * via {@link getBatchCircle}; a stroked circle renders through the normal path.
27
+ */
28
+ export declare class Circle extends Entity {
29
+ fill: string | null;
30
+ stroke: string | null;
31
+ strokeWidth: number;
32
+ private _radius;
33
+ constructor(opts?: CircleOptions);
34
+ get radius(): number;
35
+ set radius(v: number);
36
+ /** Keep the a11y box (a square around the centered disc) in sync with radius. */
37
+ private syncBox;
38
+ getBounds(): Bounds;
39
+ /**
40
+ * A solid-fill, unstroked circle opts into the renderer's circle batch
41
+ * (center = entity origin, radius scaled by world scale). A stroke needs the
42
+ * exact Canvas path, so return `null` there. Read each frame, so an animated
43
+ * `fill`/`radius` still batches.
44
+ */
45
+ getBatchCircle(): BatchCircle | null;
46
+ isPointInside(globalX: number, globalY: number): boolean;
47
+ render(renderer: IRenderer): void;
48
+ }
@@ -0,0 +1,24 @@
1
+ import { Entity } from '../tree/Entity';
2
+ import type { IRenderer } from '../renderer/IRenderer';
3
+ /**
4
+ * A transform-only container: it draws nothing itself and is invisible to
5
+ * hit-testing, existing solely to compose one transform (`x`/`y`/`scale`/
6
+ * `rotation`/`opacity`) onto a set of children. Instantiate directly and pass
7
+ * children inline:
8
+ *
9
+ * @example
10
+ * const toolbar = new Group(saveBtn, undoBtn, redoBtn);
11
+ * toolbar.set({ x: 20, y: 20 });
12
+ * scene.add(toolbar);
13
+ *
14
+ * `isPointInside` returns `false` so the group never becomes the pick target;
15
+ * `Scene`'s hit-test recurses into children before testing a parent, so the
16
+ * children remain independently interactive. `render` is a no-op — children are
17
+ * drawn by the scene's normal tree walk under this group's accumulated
18
+ * transform.
19
+ */
20
+ export declare class Group extends Entity {
21
+ constructor(...children: Entity[]);
22
+ isPointInside(): boolean;
23
+ render(_renderer: IRenderer): void;
24
+ }
@@ -0,0 +1,48 @@
1
+ import { Entity, type Bounds, type BatchRect } from '../tree/Entity';
2
+ import type { IRenderer } from '../renderer/IRenderer';
3
+ /** Construction options for {@link Rect}. */
4
+ export interface RectOptions {
5
+ /** Width in local units. Default `0`. */
6
+ width?: number;
7
+ /** Height in local units. Default `0`. */
8
+ height?: number;
9
+ /** CSS fill color, or `null` for no fill. Default `'#38bdf8'`. */
10
+ fill?: string | null;
11
+ /** CSS stroke color, or `null` for no stroke. Default `null`. */
12
+ stroke?: string | null;
13
+ /** Stroke width in local units. Default `1`. */
14
+ strokeWidth?: number;
15
+ /** Corner radius in local units (uniform). Default `0` (sharp corners). */
16
+ radius?: number;
17
+ }
18
+ /**
19
+ * A concrete axis-aligned rectangle primitive drawn from its local origin
20
+ * `(0, 0)` to `(width, height)`. Instantiate directly — no subclassing:
21
+ *
22
+ * @example
23
+ * const box = new Rect({ width: 120, height: 64, fill: '#38bdf8', radius: 8 });
24
+ * box.set({ x: 40, y: 40 });
25
+ * scene.add(box);
26
+ *
27
+ * The box matches the entity's `width`/`height`, so its accessibility shadow
28
+ * node lines up with what's drawn. A solid-fill, non-rounded, unstroked Rect
29
+ * opts into the WebGL instanced-rectangle fast path via {@link getBatchRect};
30
+ * rounded or stroked rectangles render through the normal Canvas path.
31
+ */
32
+ export declare class Rect extends Entity {
33
+ fill: string | null;
34
+ stroke: string | null;
35
+ strokeWidth: number;
36
+ radius: number;
37
+ constructor(opts?: RectOptions);
38
+ getBounds(): Bounds;
39
+ /**
40
+ * Solid-fill, square-cornered, unstroked rectangles opt into the GPU
41
+ * instanced-rect batch (WebGL `pointBackend` only). Any stroke or corner
42
+ * radius needs the exact Canvas path, so return `null` to fall back to
43
+ * {@link render}. Read each frame, so an animated `fill` still batches.
44
+ */
45
+ getBatchRect(): BatchRect | null;
46
+ isPointInside(globalX: number, globalY: number): boolean;
47
+ render(renderer: IRenderer): void;
48
+ }
package/dist/index.d.ts CHANGED
@@ -10,6 +10,9 @@ export * from './tree/Scene';
10
10
  export * from './components/TextEntity';
11
11
  export * from './components/GridTextEntity';
12
12
  export * from './components/SplineEntity';
13
+ export * from './components/Rect';
14
+ export * from './components/Circle';
15
+ export * from './components/Group';
13
16
  export * from './layout/LayoutEngine';
14
17
  export * from './layout/measure';
15
18
  export * from './text/MSDFFont';
package/dist/index.js CHANGED
@@ -27,7 +27,7 @@ var _chunkBPMNCGU7js = require('./chunk-BPMNCGU7.js');
27
27
 
28
28
 
29
29
 
30
- var _chunkFIQAIF55js = require('./chunk-FIQAIF55.js');
30
+ var _chunkGKLTTGBRjs = require('./chunk-GKLTTGBR.js');
31
31
 
32
32
 
33
33
 
@@ -44,7 +44,7 @@ var PARTICLE_OFFSET_ORIGIN_X = 4;
44
44
  var PARTICLE_OFFSET_ORIGIN_Y = 5;
45
45
  var PARTICLE_OFFSET_SIZE = 6;
46
46
  var PARTICLE_OFFSET_LIFE = 7;
47
- var ComputeParticleEntity = (_class = class extends _chunkFIQAIF55js.Entity {
47
+ var ComputeParticleEntity = (_class = class extends _chunkGKLTTGBRjs.Entity {
48
48
 
49
49
 
50
50
 
@@ -755,7 +755,7 @@ var Scene = (_class2 = class _Scene {
755
755
  this.a11ySyncInterval = _nullishCoalesce(options.a11ySyncInterval, () => ( 0));
756
756
  this.contentProjectionEnabled = _nullishCoalesce(options.contentProjection, () => ( true));
757
757
  this.reducedMotionQuery = typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia("(prefers-reduced-motion: reduce)") : null;
758
- this.root = new class RootEntity extends _chunkFIQAIF55js.Entity {
758
+ this.root = new class RootEntity extends _chunkGKLTTGBRjs.Entity {
759
759
  isPointInside() {
760
760
  return false;
761
761
  }
@@ -764,7 +764,7 @@ var Scene = (_class2 = class _Scene {
764
764
  }
765
765
  }("root");
766
766
  this.root._scene = this;
767
- this.overlayRoot = new class OverlayRoot extends _chunkFIQAIF55js.Entity {
767
+ this.overlayRoot = new class OverlayRoot extends _chunkGKLTTGBRjs.Entity {
768
768
  isPointInside() {
769
769
  return false;
770
770
  }
@@ -915,7 +915,7 @@ var Scene = (_class2 = class _Scene {
915
915
  };
916
916
  if (typeof document !== "undefined" && document.fonts) {
917
917
  this.fontLoadHandler = () => {
918
- _chunkFIQAIF55js.clearCssLineBoxMetrics.call(void 0, );
918
+ _chunkGKLTTGBRjs.clearCssLineBoxMetrics.call(void 0, );
919
919
  this.contentFontEpoch++;
920
920
  this.markDirty();
921
921
  };
@@ -1259,15 +1259,15 @@ var Scene = (_class2 = class _Scene {
1259
1259
  el.style.background = "transparent";
1260
1260
  }
1261
1261
  el.addEventListener("click", (e) => {
1262
- node.dispatchEvent(new (0, _chunkFIQAIF55js.VectoJSEvent)("click", node, e));
1262
+ node.dispatchEvent(new (0, _chunkGKLTTGBRjs.VectoJSEvent)("click", node, e));
1263
1263
  });
1264
1264
  el.addEventListener("mouseenter", (e) => {
1265
1265
  if (this.debugA11y) el.style.backgroundColor = "rgba(56, 189, 248, 0.2)";
1266
- node.dispatchEvent(new (0, _chunkFIQAIF55js.VectoJSEvent)("hover", node, e, false));
1266
+ node.dispatchEvent(new (0, _chunkGKLTTGBRjs.VectoJSEvent)("hover", node, e, false));
1267
1267
  });
1268
1268
  el.addEventListener("mouseleave", (e) => {
1269
1269
  if (this.debugA11y) el.style.backgroundColor = "rgba(56, 189, 248, 0.05)";
1270
- node.dispatchEvent(new (0, _chunkFIQAIF55js.VectoJSEvent)("pointerleave", node, e, false));
1270
+ node.dispatchEvent(new (0, _chunkGKLTTGBRjs.VectoJSEvent)("pointerleave", node, e, false));
1271
1271
  });
1272
1272
  const capEl = el;
1273
1273
  const releasePointer = (event) => {
@@ -1283,32 +1283,32 @@ var Scene = (_class2 = class _Scene {
1283
1283
  };
1284
1284
  el.addEventListener("pointerdown", (e) => {
1285
1285
  if (typeof capEl.setPointerCapture === "function") capEl.setPointerCapture(e.pointerId);
1286
- node.dispatchEvent(new (0, _chunkFIQAIF55js.VectoJSEvent)("pointerdown", node, e));
1286
+ node.dispatchEvent(new (0, _chunkGKLTTGBRjs.VectoJSEvent)("pointerdown", node, e));
1287
1287
  });
1288
1288
  el.addEventListener("pointerup", (e) => {
1289
1289
  releasePointer(e);
1290
- node.dispatchEvent(new (0, _chunkFIQAIF55js.VectoJSEvent)("pointerup", node, e));
1290
+ node.dispatchEvent(new (0, _chunkGKLTTGBRjs.VectoJSEvent)("pointerup", node, e));
1291
1291
  });
1292
1292
  el.addEventListener("pointercancel", (e) => {
1293
1293
  releasePointer(e);
1294
- node.dispatchEvent(new (0, _chunkFIQAIF55js.VectoJSEvent)("pointercancel", node, e));
1294
+ node.dispatchEvent(new (0, _chunkGKLTTGBRjs.VectoJSEvent)("pointercancel", node, e));
1295
1295
  });
1296
1296
  el.addEventListener(
1297
1297
  "pointermove",
1298
- (e) => node.dispatchEvent(new (0, _chunkFIQAIF55js.VectoJSEvent)("pointermove", node, e))
1298
+ (e) => node.dispatchEvent(new (0, _chunkGKLTTGBRjs.VectoJSEvent)("pointermove", node, e))
1299
1299
  );
1300
1300
  el.addEventListener(
1301
1301
  "wheel",
1302
1302
  (e) => {
1303
- node.dispatchEvent(new (0, _chunkFIQAIF55js.VectoJSEvent)("wheel", node, e));
1303
+ node.dispatchEvent(new (0, _chunkGKLTTGBRjs.VectoJSEvent)("wheel", node, e));
1304
1304
  },
1305
1305
  { passive: false }
1306
1306
  );
1307
1307
  el.addEventListener("keydown", (e) => {
1308
- node.dispatchEvent(new (0, _chunkFIQAIF55js.VectoJSEvent)("keydown", node, e));
1308
+ node.dispatchEvent(new (0, _chunkGKLTTGBRjs.VectoJSEvent)("keydown", node, e));
1309
1309
  });
1310
1310
  el.addEventListener("keyup", (e) => {
1311
- node.dispatchEvent(new (0, _chunkFIQAIF55js.VectoJSEvent)("keyup", node, e));
1311
+ node.dispatchEvent(new (0, _chunkGKLTTGBRjs.VectoJSEvent)("keyup", node, e));
1312
1312
  });
1313
1313
  if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {
1314
1314
  const input = el;
@@ -1368,7 +1368,7 @@ var Scene = (_class2 = class _Scene {
1368
1368
  el.addEventListener("keydown", (e) => {
1369
1369
  if (e.key === "Enter" || e.key === " ") {
1370
1370
  e.preventDefault();
1371
- node.dispatchEvent(new (0, _chunkFIQAIF55js.VectoJSEvent)("click", node, e));
1371
+ node.dispatchEvent(new (0, _chunkGKLTTGBRjs.VectoJSEvent)("click", node, e));
1372
1372
  }
1373
1373
  });
1374
1374
  }
@@ -1529,7 +1529,7 @@ var Scene = (_class2 = class _Scene {
1529
1529
  el.addEventListener(
1530
1530
  "wheel",
1531
1531
  (e2) => {
1532
- node.dispatchEvent(new (0, _chunkFIQAIF55js.VectoJSEvent)("wheel", node, e2));
1532
+ node.dispatchEvent(new (0, _chunkGKLTTGBRjs.VectoJSEvent)("wheel", node, e2));
1533
1533
  },
1534
1534
  { passive: false }
1535
1535
  );
@@ -1560,7 +1560,7 @@ var Scene = (_class2 = class _Scene {
1560
1560
  lineElement.style.position = "absolute";
1561
1561
  lineElement.dir = "auto";
1562
1562
  lineElement.style.left = `${line.x}px`;
1563
- lineElement.style.top = `${line.y + line.baseline - _chunkFIQAIF55js.cssLineBoxBaseline.call(void 0, lineFont, lineHeight2)}px`;
1563
+ lineElement.style.top = `${line.y + line.baseline - _chunkGKLTTGBRjs.cssLineBoxBaseline.call(void 0, lineFont, lineHeight2)}px`;
1564
1564
  lineElement.style.whiteSpace = "pre";
1565
1565
  if (lineFont) lineElement.style.font = lineFont;
1566
1566
  lineElement.style.lineHeight = `${lineHeight2}px`;
@@ -1612,7 +1612,7 @@ var Scene = (_class2 = class _Scene {
1612
1612
  const { a, b, c, d, e, f } = node.getWorldTransform();
1613
1613
  const contentX = _nullishCoalesce(projection.contentX, () => ( 0));
1614
1614
  const contentY = _nullishCoalesce(projection.contentY, () => ( 0));
1615
- const baselineOffset = lines && lines.length > 0 ? 0 : projection.baseline === void 0 ? 0 : projection.baseline - _chunkFIQAIF55js.cssLineBoxBaseline.call(void 0, font, _nullishCoalesce(projection.lineHeight, () => ( 16)));
1615
+ const baselineOffset = lines && lines.length > 0 ? 0 : projection.baseline === void 0 ? 0 : projection.baseline - _chunkGKLTTGBRjs.cssLineBoxBaseline.call(void 0, font, _nullishCoalesce(projection.lineHeight, () => ( 16)));
1616
1616
  const localY = contentY + baselineOffset;
1617
1617
  el.style.left = `${e + a * contentX + c * localY}px`;
1618
1618
  el.style.top = `${f + b * contentX + d * localY}px`;
@@ -1684,7 +1684,7 @@ var Scene = (_class2 = class _Scene {
1684
1684
  lineElement.dataset.vectoGridLine = `${lineIndex}`;
1685
1685
  lineElement.style.position = "absolute";
1686
1686
  lineElement.style.left = `${_nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _68 => _68.x]), () => ( 0))}px`;
1687
- lineElement.style.top = `${(_nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _69 => _69.y]), () => ( lineIndex * grid.lineHeight))) + baseline - _chunkFIQAIF55js.cssLineBoxBaseline.call(void 0, lineFont, lineHeight)}px`;
1687
+ lineElement.style.top = `${(_nullishCoalesce(_optionalChain([projectedLine, 'optionalAccess', _69 => _69.y]), () => ( lineIndex * grid.lineHeight))) + baseline - _chunkGKLTTGBRjs.cssLineBoxBaseline.call(void 0, lineFont, lineHeight)}px`;
1688
1688
  lineElement.style.width = `${gridLine.width}px`;
1689
1689
  lineElement.style.height = `${lineHeight}px`;
1690
1690
  lineElement.style.whiteSpace = "pre";
@@ -2616,7 +2616,7 @@ function defaultMeasurer() {
2616
2616
  if (sharedMeasurer === void 0) sharedMeasurer = _chunkBA5HUUDFjs.createCanvasMeasurer.call(void 0, "sans-serif");
2617
2617
  return sharedMeasurer;
2618
2618
  }
2619
- var TextEntity = (_class3 = class extends _chunkFIQAIF55js.Entity {
2619
+ var TextEntity = (_class3 = class extends _chunkGKLTTGBRjs.Entity {
2620
2620
 
2621
2621
 
2622
2622
 
@@ -2742,7 +2742,7 @@ var TextEntity = (_class3 = class extends _chunkFIQAIF55js.Entity {
2742
2742
  }, _class3);
2743
2743
 
2744
2744
  // src/components/GridTextEntity.ts
2745
- var GridTextEntity = (_class4 = class extends _chunkFIQAIF55js.Entity {
2745
+ var GridTextEntity = (_class4 = class extends _chunkGKLTTGBRjs.Entity {
2746
2746
 
2747
2747
  __init68() {this.fillStyle = "#ffffff"}
2748
2748
  __init69() {this.grid = []}
@@ -2836,7 +2836,7 @@ function distSqToSegment(px, py, x1, y1, x2, y2) {
2836
2836
  const ey = py - cy;
2837
2837
  return ex * ex + ey * ey;
2838
2838
  }
2839
- var SplineEntity = (_class5 = class extends _chunkFIQAIF55js.Entity {
2839
+ var SplineEntity = (_class5 = class extends _chunkGKLTTGBRjs.Entity {
2840
2840
 
2841
2841
 
2842
2842
 
@@ -3110,6 +3110,128 @@ async function loadSpline(url) {
3110
3110
  return await res.json();
3111
3111
  }
3112
3112
 
3113
+ // src/components/Rect.ts
3114
+ var Rect = class extends _chunkGKLTTGBRjs.Entity {
3115
+
3116
+
3117
+
3118
+
3119
+ constructor(opts = {}) {
3120
+ super();
3121
+ this.width = _nullishCoalesce(opts.width, () => ( 0));
3122
+ this.height = _nullishCoalesce(opts.height, () => ( 0));
3123
+ this.fill = opts.fill === void 0 ? "#38bdf8" : opts.fill;
3124
+ this.stroke = _nullishCoalesce(opts.stroke, () => ( null));
3125
+ this.strokeWidth = _nullishCoalesce(opts.strokeWidth, () => ( 1));
3126
+ this.radius = _nullishCoalesce(opts.radius, () => ( 0));
3127
+ }
3128
+ getBounds() {
3129
+ return { x: 0, y: 0, width: this.width, height: this.height };
3130
+ }
3131
+ /**
3132
+ * Solid-fill, square-cornered, unstroked rectangles opt into the GPU
3133
+ * instanced-rect batch (WebGL `pointBackend` only). Any stroke or corner
3134
+ * radius needs the exact Canvas path, so return `null` to fall back to
3135
+ * {@link render}. Read each frame, so an animated `fill` still batches.
3136
+ */
3137
+ getBatchRect() {
3138
+ if (!this.fill || this.stroke || this.radius > 0) return null;
3139
+ return { width: this.width, height: this.height, color: this.fill };
3140
+ }
3141
+ isPointInside(globalX, globalY) {
3142
+ const local = this.worldToLocal(globalX, globalY);
3143
+ if (!local) return false;
3144
+ return local.x >= 0 && local.x <= this.width && local.y >= 0 && local.y <= this.height;
3145
+ }
3146
+ render(renderer) {
3147
+ renderer.beginPath();
3148
+ if (this.radius > 0) {
3149
+ renderer.roundRect(0, 0, this.width, this.height, this.radius);
3150
+ } else {
3151
+ renderer.moveTo(0, 0);
3152
+ renderer.lineTo(this.width, 0);
3153
+ renderer.lineTo(this.width, this.height);
3154
+ renderer.lineTo(0, this.height);
3155
+ renderer.closePath();
3156
+ }
3157
+ if (this.fill) renderer.fill(this.fill);
3158
+ if (this.stroke) renderer.stroke(this.stroke, this.strokeWidth);
3159
+ }
3160
+ };
3161
+
3162
+ // src/components/Circle.ts
3163
+ var Circle = class extends _chunkGKLTTGBRjs.Entity {
3164
+
3165
+
3166
+
3167
+
3168
+ constructor(opts = {}) {
3169
+ super();
3170
+ this._radius = _nullishCoalesce(opts.radius, () => ( 0));
3171
+ this.fill = opts.fill === void 0 ? "#38bdf8" : opts.fill;
3172
+ this.stroke = _nullishCoalesce(opts.stroke, () => ( null));
3173
+ this.strokeWidth = _nullishCoalesce(opts.strokeWidth, () => ( 1));
3174
+ this.syncBox();
3175
+ }
3176
+ get radius() {
3177
+ return this._radius;
3178
+ }
3179
+ set radius(v) {
3180
+ this._radius = v;
3181
+ this.syncBox();
3182
+ }
3183
+ /** Keep the a11y box (a square around the centered disc) in sync with radius. */
3184
+ syncBox() {
3185
+ this.width = this._radius * 2;
3186
+ this.height = this._radius * 2;
3187
+ this.a11yOffsetX = -this._radius;
3188
+ this.a11yOffsetY = -this._radius;
3189
+ }
3190
+ getBounds() {
3191
+ return {
3192
+ x: -this._radius,
3193
+ y: -this._radius,
3194
+ width: this._radius * 2,
3195
+ height: this._radius * 2
3196
+ };
3197
+ }
3198
+ /**
3199
+ * A solid-fill, unstroked circle opts into the renderer's circle batch
3200
+ * (center = entity origin, radius scaled by world scale). A stroke needs the
3201
+ * exact Canvas path, so return `null` there. Read each frame, so an animated
3202
+ * `fill`/`radius` still batches.
3203
+ */
3204
+ getBatchCircle() {
3205
+ if (!this.fill || this.stroke) return null;
3206
+ return { radius: this._radius, color: this.fill };
3207
+ }
3208
+ isPointInside(globalX, globalY) {
3209
+ const local = this.worldToLocal(globalX, globalY);
3210
+ if (!local) return false;
3211
+ return local.x * local.x + local.y * local.y <= this._radius * this._radius;
3212
+ }
3213
+ render(renderer) {
3214
+ renderer.beginPath();
3215
+ renderer.arc(0, 0, this._radius, 0, Math.PI * 2);
3216
+ renderer.closePath();
3217
+ if (this.fill) renderer.fill(this.fill);
3218
+ if (this.stroke) renderer.stroke(this.stroke, this.strokeWidth);
3219
+ }
3220
+ };
3221
+
3222
+ // src/components/Group.ts
3223
+ var Group = class extends _chunkGKLTTGBRjs.Entity {
3224
+ constructor(...children) {
3225
+ super();
3226
+ if (children.length > 0) this.add(...children);
3227
+ }
3228
+ isPointInside() {
3229
+ return false;
3230
+ }
3231
+ render(_renderer) {
3232
+ }
3233
+ };
3234
+
3113
3235
  // src/math/SpatialHashGrid.ts
3114
3236
  var SpatialHashGrid = (_class6 = class {
3115
3237
 
@@ -3205,7 +3327,7 @@ var SpatialHashGrid = (_class6 = class {
3205
3327
  }, _class6);
3206
3328
 
3207
3329
  // src/tree/DOMPortalEntity.ts
3208
- var DOMPortalEntity = (_class7 = class extends _chunkFIQAIF55js.Entity {
3330
+ var DOMPortalEntity = (_class7 = class extends _chunkGKLTTGBRjs.Entity {
3209
3331
 
3210
3332
  __init80() {this.isDOMPortal = true}
3211
3333
  __init81() {this.domListeners = []}
@@ -3247,7 +3369,7 @@ var DOMPortalEntity = (_class7 = class extends _chunkFIQAIF55js.Entity {
3247
3369
  ];
3248
3370
  for (const type of events) {
3249
3371
  const handler = (e) => {
3250
- this.dispatchEvent(new (0, _chunkFIQAIF55js.VectoJSEvent)(type, this, e));
3372
+ this.dispatchEvent(new (0, _chunkGKLTTGBRjs.VectoJSEvent)(type, this, e));
3251
3373
  };
3252
3374
  this.domElement.addEventListener(type, handler);
3253
3375
  this.domListeners.push({ type, handler, capture: false });
@@ -3258,7 +3380,7 @@ var DOMPortalEntity = (_class7 = class extends _chunkFIQAIF55js.Entity {
3258
3380
  ];
3259
3381
  for (const { native, vecto } of hoverEvents) {
3260
3382
  const handler = (e) => {
3261
- this.dispatchEvent(new (0, _chunkFIQAIF55js.VectoJSEvent)(vecto, this, e, false));
3383
+ this.dispatchEvent(new (0, _chunkGKLTTGBRjs.VectoJSEvent)(vecto, this, e, false));
3262
3384
  };
3263
3385
  this.domElement.addEventListener(native, handler);
3264
3386
  this.domListeners.push({ type: native, handler, capture: false });
@@ -3266,7 +3388,7 @@ var DOMPortalEntity = (_class7 = class extends _chunkFIQAIF55js.Entity {
3266
3388
  const focusEvents = ["focus", "blur"];
3267
3389
  for (const type of focusEvents) {
3268
3390
  const handler = (e) => {
3269
- this.dispatchEvent(new (0, _chunkFIQAIF55js.VectoJSEvent)(type, this, e, true));
3391
+ this.dispatchEvent(new (0, _chunkGKLTTGBRjs.VectoJSEvent)(type, this, e, true));
3270
3392
  };
3271
3393
  this.domElement.addEventListener(type, handler, true);
3272
3394
  this.domListeners.push({ type, handler, capture: true });
@@ -3354,4 +3476,7 @@ Scene.registerWebGPUParticleSystemManager(_chunkBPMNCGU7js.WebGPUParticleSystemM
3354
3476
 
3355
3477
 
3356
3478
 
3357
- exports.ArabicShaper = _chunk4AR425ARjs.ArabicShaper; exports.BidiResolver = _chunk4AR425ARjs.BidiResolver; exports.CanvasRenderer = _chunkBPMNCGU7js.CanvasRenderer; exports.ComputeParticleEntity = ComputeParticleEntity; exports.DOMPortalEntity = DOMPortalEntity; exports.Easing = _chunkFIQAIF55js.Easing; exports.Entity = _chunkFIQAIF55js.Entity; exports.GridTextEntity = GridTextEntity; exports.LayoutEngine = _chunkBA5HUUDFjs.LayoutEngine; exports.LayoutResultBuffer = _chunkBA5HUUDFjs.LayoutResultBuffer; exports.LayoutWorkerManager = _chunk4AR425ARjs.LayoutWorkerManager; exports.MSDFFont = _chunkFIQAIF55js.MSDFFont; exports.MSDFTextEntity = _chunkFIQAIF55js.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 = _chunkFIQAIF55js.SVGEntity; exports.SVGRenderer = _chunkBPMNCGU7js.SVGRenderer; exports.Scene = Scene; exports.SpatialHashGrid = SpatialHashGrid; exports.SplineEntity = SplineEntity; exports.SpringDriver = _chunkFIQAIF55js.SpringDriver; exports.SpringPhysics = _chunkFIQAIF55js.SpringPhysics; exports.TextEntity = TextEntity; exports.TweenDriver = _chunkFIQAIF55js.TweenDriver; exports.VectoJSEvent = _chunkFIQAIF55js.VectoJSEvent; exports.WebGPUParticleSystemManager = _chunkBPMNCGU7js.WebGPUParticleSystemManager; exports.clearCssLineBoxMetrics = _chunkFIQAIF55js.clearCssLineBoxMetrics; exports.computeLineSegments = _chunkBA5HUUDFjs.computeLineSegments; exports.createCanvasMeasurer = _chunkBA5HUUDFjs.createCanvasMeasurer; exports.createWebGLPointRenderer = _chunkBPMNCGU7js.createWebGLPointRenderer; exports.cssLineBoxBaseline = _chunkFIQAIF55js.cssLineBoxBaseline; exports.isSafeUrl = _chunkBPMNCGU7js.isSafeUrl; exports.isTweenConfig = _chunkFIQAIF55js.isTweenConfig; exports.loadSpline = loadSpline; exports.parseColorToRGBA = _chunkBPMNCGU7js.parseColorToRGBA; exports.polySegmentToBezier = polySegmentToBezier; exports.prepareContentGrid = _chunkFIQAIF55js.prepareContentGrid; exports.sanitizeUrl = _chunkBPMNCGU7js.sanitizeUrl;
3479
+
3480
+
3481
+
3482
+ exports.ArabicShaper = _chunk4AR425ARjs.ArabicShaper; exports.BidiResolver = _chunk4AR425ARjs.BidiResolver; exports.CanvasRenderer = _chunkBPMNCGU7js.CanvasRenderer; exports.Circle = Circle; exports.ComputeParticleEntity = ComputeParticleEntity; exports.DOMPortalEntity = DOMPortalEntity; exports.Easing = _chunkGKLTTGBRjs.Easing; exports.Entity = _chunkGKLTTGBRjs.Entity; exports.GridTextEntity = GridTextEntity; exports.Group = Group; exports.LayoutEngine = _chunkBA5HUUDFjs.LayoutEngine; exports.LayoutResultBuffer = _chunkBA5HUUDFjs.LayoutResultBuffer; exports.LayoutWorkerManager = _chunk4AR425ARjs.LayoutWorkerManager; exports.MSDFFont = _chunkGKLTTGBRjs.MSDFFont; exports.MSDFTextEntity = _chunkGKLTTGBRjs.MSDFTextEntity; exports.PARTICLE_OFFSET_LIFE = PARTICLE_OFFSET_LIFE; exports.PARTICLE_OFFSET_ORIGIN_X = PARTICLE_OFFSET_ORIGIN_X; exports.PARTICLE_OFFSET_ORIGIN_Y = PARTICLE_OFFSET_ORIGIN_Y; exports.PARTICLE_OFFSET_POSITION_X = PARTICLE_OFFSET_POSITION_X; exports.PARTICLE_OFFSET_POSITION_Y = PARTICLE_OFFSET_POSITION_Y; exports.PARTICLE_OFFSET_SIZE = PARTICLE_OFFSET_SIZE; exports.PARTICLE_OFFSET_VELOCITY_X = PARTICLE_OFFSET_VELOCITY_X; exports.PARTICLE_OFFSET_VELOCITY_Y = PARTICLE_OFFSET_VELOCITY_Y; exports.PARTICLE_STRIDE_FLOATS = PARTICLE_STRIDE_FLOATS; exports.REDUCED_MOTION_FPS = REDUCED_MOTION_FPS; exports.Rect = Rect; exports.SVGEntity = _chunkGKLTTGBRjs.SVGEntity; exports.SVGRenderer = _chunkBPMNCGU7js.SVGRenderer; exports.Scene = Scene; exports.SpatialHashGrid = SpatialHashGrid; exports.SplineEntity = SplineEntity; exports.SpringDriver = _chunkGKLTTGBRjs.SpringDriver; exports.SpringPhysics = _chunkGKLTTGBRjs.SpringPhysics; exports.TextEntity = TextEntity; exports.TweenDriver = _chunkGKLTTGBRjs.TweenDriver; exports.VectoJSEvent = _chunkGKLTTGBRjs.VectoJSEvent; exports.WebGPUParticleSystemManager = _chunkBPMNCGU7js.WebGPUParticleSystemManager; exports.clearCssLineBoxMetrics = _chunkGKLTTGBRjs.clearCssLineBoxMetrics; exports.computeLineSegments = _chunkBA5HUUDFjs.computeLineSegments; exports.createCanvasMeasurer = _chunkBA5HUUDFjs.createCanvasMeasurer; exports.createWebGLPointRenderer = _chunkBPMNCGU7js.createWebGLPointRenderer; exports.cssLineBoxBaseline = _chunkGKLTTGBRjs.cssLineBoxBaseline; exports.isSafeUrl = _chunkBPMNCGU7js.isSafeUrl; exports.isTweenConfig = _chunkGKLTTGBRjs.isTweenConfig; exports.loadSpline = loadSpline; exports.parseColorToRGBA = _chunkBPMNCGU7js.parseColorToRGBA; exports.polySegmentToBezier = polySegmentToBezier; exports.prepareContentGrid = _chunkGKLTTGBRjs.prepareContentGrid; exports.sanitizeUrl = _chunkBPMNCGU7js.sanitizeUrl;
package/dist/index.mjs CHANGED
@@ -27,7 +27,7 @@ import {
27
27
  cssLineBoxBaseline,
28
28
  isTweenConfig,
29
29
  prepareContentGrid
30
- } from "./chunk-RQ2ETTBN.mjs";
30
+ } from "./chunk-J2P6QZJV.mjs";
31
31
  import {
32
32
  ArabicShaper,
33
33
  BidiResolver,
@@ -3110,6 +3110,128 @@ async function loadSpline(url) {
3110
3110
  return await res.json();
3111
3111
  }
3112
3112
 
3113
+ // src/components/Rect.ts
3114
+ var Rect = class extends Entity {
3115
+ fill;
3116
+ stroke;
3117
+ strokeWidth;
3118
+ radius;
3119
+ constructor(opts = {}) {
3120
+ super();
3121
+ this.width = opts.width ?? 0;
3122
+ this.height = opts.height ?? 0;
3123
+ this.fill = opts.fill === void 0 ? "#38bdf8" : opts.fill;
3124
+ this.stroke = opts.stroke ?? null;
3125
+ this.strokeWidth = opts.strokeWidth ?? 1;
3126
+ this.radius = opts.radius ?? 0;
3127
+ }
3128
+ getBounds() {
3129
+ return { x: 0, y: 0, width: this.width, height: this.height };
3130
+ }
3131
+ /**
3132
+ * Solid-fill, square-cornered, unstroked rectangles opt into the GPU
3133
+ * instanced-rect batch (WebGL `pointBackend` only). Any stroke or corner
3134
+ * radius needs the exact Canvas path, so return `null` to fall back to
3135
+ * {@link render}. Read each frame, so an animated `fill` still batches.
3136
+ */
3137
+ getBatchRect() {
3138
+ if (!this.fill || this.stroke || this.radius > 0) return null;
3139
+ return { width: this.width, height: this.height, color: this.fill };
3140
+ }
3141
+ isPointInside(globalX, globalY) {
3142
+ const local = this.worldToLocal(globalX, globalY);
3143
+ if (!local) return false;
3144
+ return local.x >= 0 && local.x <= this.width && local.y >= 0 && local.y <= this.height;
3145
+ }
3146
+ render(renderer) {
3147
+ renderer.beginPath();
3148
+ if (this.radius > 0) {
3149
+ renderer.roundRect(0, 0, this.width, this.height, this.radius);
3150
+ } else {
3151
+ renderer.moveTo(0, 0);
3152
+ renderer.lineTo(this.width, 0);
3153
+ renderer.lineTo(this.width, this.height);
3154
+ renderer.lineTo(0, this.height);
3155
+ renderer.closePath();
3156
+ }
3157
+ if (this.fill) renderer.fill(this.fill);
3158
+ if (this.stroke) renderer.stroke(this.stroke, this.strokeWidth);
3159
+ }
3160
+ };
3161
+
3162
+ // src/components/Circle.ts
3163
+ var Circle = class extends Entity {
3164
+ fill;
3165
+ stroke;
3166
+ strokeWidth;
3167
+ _radius;
3168
+ constructor(opts = {}) {
3169
+ super();
3170
+ this._radius = opts.radius ?? 0;
3171
+ this.fill = opts.fill === void 0 ? "#38bdf8" : opts.fill;
3172
+ this.stroke = opts.stroke ?? null;
3173
+ this.strokeWidth = opts.strokeWidth ?? 1;
3174
+ this.syncBox();
3175
+ }
3176
+ get radius() {
3177
+ return this._radius;
3178
+ }
3179
+ set radius(v) {
3180
+ this._radius = v;
3181
+ this.syncBox();
3182
+ }
3183
+ /** Keep the a11y box (a square around the centered disc) in sync with radius. */
3184
+ syncBox() {
3185
+ this.width = this._radius * 2;
3186
+ this.height = this._radius * 2;
3187
+ this.a11yOffsetX = -this._radius;
3188
+ this.a11yOffsetY = -this._radius;
3189
+ }
3190
+ getBounds() {
3191
+ return {
3192
+ x: -this._radius,
3193
+ y: -this._radius,
3194
+ width: this._radius * 2,
3195
+ height: this._radius * 2
3196
+ };
3197
+ }
3198
+ /**
3199
+ * A solid-fill, unstroked circle opts into the renderer's circle batch
3200
+ * (center = entity origin, radius scaled by world scale). A stroke needs the
3201
+ * exact Canvas path, so return `null` there. Read each frame, so an animated
3202
+ * `fill`/`radius` still batches.
3203
+ */
3204
+ getBatchCircle() {
3205
+ if (!this.fill || this.stroke) return null;
3206
+ return { radius: this._radius, color: this.fill };
3207
+ }
3208
+ isPointInside(globalX, globalY) {
3209
+ const local = this.worldToLocal(globalX, globalY);
3210
+ if (!local) return false;
3211
+ return local.x * local.x + local.y * local.y <= this._radius * this._radius;
3212
+ }
3213
+ render(renderer) {
3214
+ renderer.beginPath();
3215
+ renderer.arc(0, 0, this._radius, 0, Math.PI * 2);
3216
+ renderer.closePath();
3217
+ if (this.fill) renderer.fill(this.fill);
3218
+ if (this.stroke) renderer.stroke(this.stroke, this.strokeWidth);
3219
+ }
3220
+ };
3221
+
3222
+ // src/components/Group.ts
3223
+ var Group = class extends Entity {
3224
+ constructor(...children) {
3225
+ super();
3226
+ if (children.length > 0) this.add(...children);
3227
+ }
3228
+ isPointInside() {
3229
+ return false;
3230
+ }
3231
+ render(_renderer) {
3232
+ }
3233
+ };
3234
+
3113
3235
  // src/math/SpatialHashGrid.ts
3114
3236
  var SpatialHashGrid = class {
3115
3237
  cellSize;
@@ -3311,11 +3433,13 @@ export {
3311
3433
  ArabicShaper,
3312
3434
  BidiResolver,
3313
3435
  CanvasRenderer,
3436
+ Circle,
3314
3437
  ComputeParticleEntity,
3315
3438
  DOMPortalEntity,
3316
3439
  Easing,
3317
3440
  Entity,
3318
3441
  GridTextEntity,
3442
+ Group,
3319
3443
  LayoutEngine,
3320
3444
  LayoutResultBuffer,
3321
3445
  LayoutWorkerManager,
@@ -3331,6 +3455,7 @@ export {
3331
3455
  PARTICLE_OFFSET_VELOCITY_Y,
3332
3456
  PARTICLE_STRIDE_FLOATS,
3333
3457
  REDUCED_MOTION_FPS,
3458
+ Rect,
3334
3459
  SVGEntity,
3335
3460
  SVGRenderer,
3336
3461
  Scene,
package/dist/text.js CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
 
7
7
 
8
- var _chunkFIQAIF55js = require('./chunk-FIQAIF55.js');
8
+ var _chunkGKLTTGBRjs = require('./chunk-GKLTTGBR.js');
9
9
 
10
10
 
11
11
 
@@ -19,4 +19,4 @@ var _chunk4AR425ARjs = require('./chunk-4AR425AR.js');
19
19
 
20
20
 
21
21
 
22
- exports.ArabicShaper = _chunk4AR425ARjs.ArabicShaper; exports.BidiResolver = _chunk4AR425ARjs.BidiResolver; exports.MSDFFont = _chunkFIQAIF55js.MSDFFont; exports.MSDFTextEntity = _chunkFIQAIF55js.MSDFTextEntity; exports.SVGEntity = _chunkFIQAIF55js.SVGEntity; exports.clearCssLineBoxMetrics = _chunkFIQAIF55js.clearCssLineBoxMetrics; exports.cssLineBoxBaseline = _chunkFIQAIF55js.cssLineBoxBaseline; exports.prepareContentGrid = _chunkFIQAIF55js.prepareContentGrid;
22
+ exports.ArabicShaper = _chunk4AR425ARjs.ArabicShaper; exports.BidiResolver = _chunk4AR425ARjs.BidiResolver; exports.MSDFFont = _chunkGKLTTGBRjs.MSDFFont; exports.MSDFTextEntity = _chunkGKLTTGBRjs.MSDFTextEntity; exports.SVGEntity = _chunkGKLTTGBRjs.SVGEntity; exports.clearCssLineBoxMetrics = _chunkGKLTTGBRjs.clearCssLineBoxMetrics; exports.cssLineBoxBaseline = _chunkGKLTTGBRjs.cssLineBoxBaseline; exports.prepareContentGrid = _chunkGKLTTGBRjs.prepareContentGrid;
package/dist/text.mjs CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  clearCssLineBoxMetrics,
6
6
  cssLineBoxBaseline,
7
7
  prepareContentGrid
8
- } from "./chunk-RQ2ETTBN.mjs";
8
+ } from "./chunk-J2P6QZJV.mjs";
9
9
  import {
10
10
  ArabicShaper,
11
11
  BidiResolver
@@ -348,10 +348,15 @@ export declare abstract class Entity {
348
348
  * `child.parent` is only ever `null` or `this`'s ultimate owner, so this
349
349
  * check is O(1) for the overwhelming common case (a brand-new entity).
350
350
  *
351
- * @param child - The entity to add as a child.
351
+ * Accepts multiple children in one call (`parent.add(a, b, c)`); each is
352
+ * attached in argument order with the same detach-then-attach semantics.
353
+ *
354
+ * @param children - One or more entities to add as children.
352
355
  * @returns `this` for method chaining.
353
356
  */
354
- add(child: Entity): this;
357
+ add(...children: Entity[]): this;
358
+ /** Attach a single child (the O(1) common path). See {@link add}. */
359
+ private _addOne;
355
360
  /** Called once when this entity becomes attached to a live Scene. Override to react. */
356
361
  protected onMounted(): void;
357
362
  /** Fire onMounted for this node and its descendants, guarded against double-fire. */
@@ -363,6 +368,18 @@ export declare abstract class Entity {
363
368
  * @returns `this` for method chaining.
364
369
  */
365
370
  remove(child: Entity): this;
371
+ /**
372
+ * Assign several own properties in one call, each through its normal setter
373
+ * (so a property with a configured {@link setTransition} still animates, and
374
+ * `interactive` still flags the scene for a11y reorder). A construction-time
375
+ * ergonomic only — it is a plain `for…in` over the given object and touches
376
+ * no per-frame path.
377
+ *
378
+ * @param props - Partial set of this entity's own properties to assign.
379
+ * @returns `this` for method chaining.
380
+ * @example rect.set({ x: 40, y: 40, width: 120, fill: '#38bdf8' });
381
+ */
382
+ set(props: Partial<this>): this;
366
383
  /**
367
384
  * Set the local position of this entity.
368
385
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vectojs/core",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },