@vectojs/core 1.32.5 → 1.32.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -18,7 +18,7 @@ import {
18
18
  SVGEntity,
19
19
  VectoJSEvent,
20
20
  contentLineInHint
21
- } from "./chunk-RWN3ITBD.mjs";
21
+ } from "./chunk-YTGGL4I4.mjs";
22
22
 
23
23
  // src/tree/Scene.ts
24
24
  import { SpringDriver, TweenDriver } from "@vectojs/animation";
@@ -1434,6 +1434,7 @@ function parseInlinePx(value) {
1434
1434
  return Number.isFinite(n) && n > 0 ? n : null;
1435
1435
  }
1436
1436
  var DEFAULT_CONTENT_SEMANTIC_BUDGET = 256;
1437
+ var WASM_UPLOAD_REJECT_LIMIT = 3;
1437
1438
  function projectionGridLineWindow(grid, projectionLines, band) {
1438
1439
  const count = grid.lines.length;
1439
1440
  const all = { start: 0, end: count, gated: false };
@@ -2204,6 +2205,14 @@ var Scene = class _Scene {
2204
2205
  _wasmWorld = null;
2205
2206
  _structureVersion = 0;
2206
2207
  _storeStructureVersion = -1;
2208
+ /**
2209
+ * Consecutive `uploadRuns` rejections. Reset by any success and by
2210
+ * {@link setTransformBackend}; at {@link WASM_UPLOAD_REJECT_LIMIT} the backend
2211
+ * mode flips to `'js'` permanently.
2212
+ */
2213
+ _wasmUploadRejections = 0;
2214
+ /** Latch for the permanent-fallback warning, which fires from a per-frame path. */
2215
+ hasWarnedWasmUploadFallback = false;
2207
2216
  // Cached list of ComputeParticleEntity instances in the tree, keyed by the
2208
2217
  // structure version it was gathered at. Rebuilt only on a topology change.
2209
2218
  _computeEntities = [];
@@ -2241,6 +2250,7 @@ var Scene = class _Scene {
2241
2250
  setTransformBackend(backend) {
2242
2251
  this._wasm = backend;
2243
2252
  this._transformBackend = backend ? "wasm" : "js";
2253
+ this._wasmUploadRejections = 0;
2244
2254
  }
2245
2255
  /**
2246
2256
  * Asynchronously instantiate the WASM transform core and, on success, hot-swap
@@ -2872,8 +2882,19 @@ var Scene = class _Scene {
2872
2882
  }
2873
2883
  if (!backend.uploadRuns(built.store)) {
2874
2884
  this._transformReason = "rejected";
2885
+ this._wasmUploadRejections++;
2886
+ if (this._wasmUploadRejections >= WASM_UPLOAD_REJECT_LIMIT) {
2887
+ this._transformBackend = "js";
2888
+ if (!this.hasWarnedWasmUploadFallback) {
2889
+ console.warn(
2890
+ `[VectoJS] WASM transform backend disabled after ${this._wasmUploadRejections} consecutive run-table rejections (${built.store.runCount} runs for ${built.store.count} entities); the crate refused this topology. Falling back to JS transforms for this scene. Call scene.setTransformBackend(backend) to re-enable after reducing the tree.`
2891
+ );
2892
+ this.hasWarnedWasmUploadFallback = true;
2893
+ }
2894
+ }
2875
2895
  return null;
2876
2896
  }
2897
+ this._wasmUploadRejections = 0;
2877
2898
  this._treeStore = built.store;
2878
2899
  this._slotEntity = slotEntity2;
2879
2900
  this._wasmInputs = backend.inputView();
@@ -3003,6 +3024,16 @@ var Scene = class _Scene {
3003
3024
  * else the canvas). Stored so `destroy()` detaches from the same element. */
3004
3025
  pointerEventTarget = null;
3005
3026
  hasWarnedZeroSize = false;
3027
+ /**
3028
+ * Latch for {@link Scene.resize}'s invalid-dimension warning.
3029
+ *
3030
+ * Separate from `hasWarnedZeroSize`: that one fires for a tolerated zero-size
3031
+ * scene at `start()`, this one for a rejected resize, and sharing the flag
3032
+ * would let either suppress the other's first (and only) report. Latched
3033
+ * because `resize()` is commonly driven from a `ResizeObserver`, which would
3034
+ * otherwise warn on every frame of a drag.
3035
+ */
3036
+ hasWarnedInvalidResize = false;
3006
3037
  fontLoadHandler = null;
3007
3038
  // ── Dev-mode warning infrastructure ──────────────────────────────
3008
3039
  //
@@ -3804,9 +3835,11 @@ var Scene = class _Scene {
3804
3835
  /**
3805
3836
  * Begin the `requestAnimationFrame` render loop.
3806
3837
  *
3807
- * Idempotent — calling `start()` on an already-running scene is a no-op.
3838
+ * Idempotent — calling `start()` on an already-running scene is a no-op, and so
3839
+ * is calling it on a destroyed one.
3808
3840
  */
3809
3841
  start() {
3842
+ if (this.destroyed) return;
3810
3843
  if (this.isRunning) return;
3811
3844
  if ((this.width === 0 || this.height === 0) && !this.hasWarnedZeroSize) {
3812
3845
  console.warn(
@@ -4975,9 +5008,27 @@ var Scene = class _Scene {
4975
5008
  if (el.dataset.vectoContentGrid !== signature) {
4976
5009
  const materializeStart = typeof performance !== "undefined" ? performance.now() : 0;
4977
5010
  this.clearContentGridState(node.id, el, false);
5011
+ if (el.firstChild !== null) {
5012
+ let scan = el.firstChild;
5013
+ let strippedSelection = false;
5014
+ while (scan !== null) {
5015
+ const next = scan.nextSibling;
5016
+ if (scan.nodeType === 3) {
5017
+ if (!strippedSelection) {
5018
+ strippedSelection = true;
5019
+ this.releaseContentSelectionForRebuild(el);
5020
+ }
5021
+ scan.remove();
5022
+ }
5023
+ scan = next;
5024
+ }
5025
+ }
4978
5026
  const projectionLines = projection.lines ?? [];
4979
5027
  const selectionLine = this.contentGridSelectionLine(el);
4980
5028
  let rebuiltSelectionLine = false;
5029
+ if (selectionLine !== null && (selectionLine < gridWindow.start || selectionLine >= gridWindow.end)) {
5030
+ rebuiltSelectionLine = true;
5031
+ }
4981
5032
  const existingLines = el.children;
4982
5033
  for (let lineIndex = gridWindow.start; lineIndex < gridWindow.end; lineIndex++) {
4983
5034
  const domIndex = lineIndex - gridWindow.start;
@@ -5074,9 +5125,6 @@ var Scene = class _Scene {
5074
5125
  }
5075
5126
  const windowLength = gridWindow.end - gridWindow.start;
5076
5127
  while (el.children.length > windowLength) {
5077
- if (selectionLine !== null && selectionLine >= gridWindow.end) {
5078
- rebuiltSelectionLine = true;
5079
- }
5080
5128
  el.lastElementChild?.remove();
5081
5129
  }
5082
5130
  if (rebuiltSelectionLine) this.releaseContentSelectionForRebuild(el);
@@ -5339,6 +5387,13 @@ var Scene = class _Scene {
5339
5387
  const fullLen = this.fullViewportElements.length;
5340
5388
  const normalLen = this.normalElements.length;
5341
5389
  const totalLen = fullLen + normalLen;
5390
+ let selection = null;
5391
+ let selAnchorNode = null;
5392
+ let selFocusNode = null;
5393
+ let selAnchorOffset = 0;
5394
+ let selFocusOffset = 0;
5395
+ let selectionSnapshotTaken = false;
5396
+ let selectionMoved = false;
5342
5397
  this.a11yOrderCursors.clear();
5343
5398
  for (let i = 0; i < totalLen; i++) {
5344
5399
  const expected = i < fullLen ? this.fullViewportElements[i] : this.normalElements[i - fullLen];
@@ -5349,10 +5404,30 @@ var Scene = class _Scene {
5349
5404
  const current = parent.childNodes[at];
5350
5405
  if (current !== expected) {
5351
5406
  const refocus = document.activeElement === expected;
5407
+ if (!selectionSnapshotTaken) {
5408
+ selectionSnapshotTaken = true;
5409
+ const live = typeof window !== "undefined" && typeof window.getSelection === "function" ? window.getSelection() : null;
5410
+ if (live?.anchorNode && live.focusNode) {
5411
+ selection = live;
5412
+ selAnchorNode = live.anchorNode;
5413
+ selFocusNode = live.focusNode;
5414
+ selAnchorOffset = live.anchorOffset;
5415
+ selFocusOffset = live.focusOffset;
5416
+ }
5417
+ }
5418
+ if (!selectionMoved && (selAnchorNode !== null && expected.contains(selAnchorNode) || selFocusNode !== null && expected.contains(selFocusNode))) {
5419
+ selectionMoved = true;
5420
+ }
5352
5421
  parent.insertBefore(expected, current || null);
5353
5422
  if (refocus) expected.focus({ preventScroll: true });
5354
5423
  }
5355
5424
  }
5425
+ if (selectionMoved && selection && selAnchorNode && selFocusNode) {
5426
+ try {
5427
+ selection.setBaseAndExtent(selAnchorNode, selAnchorOffset, selFocusNode, selFocusOffset);
5428
+ } catch {
5429
+ }
5430
+ }
5356
5431
  this.a11yNeedsReorder = false;
5357
5432
  }
5358
5433
  /**
@@ -5718,6 +5793,11 @@ var Scene = class _Scene {
5718
5793
  if (isMainRenderPath && !this.device && !this.webgpuDisabled && !this.initializingWebGPU && !this.deviceLost) {
5719
5794
  this.initializingWebGPU = true;
5720
5795
  this.initWebGPUContext(computeEntities).then((newDevice) => {
5796
+ if (this.destroyed) {
5797
+ newDevice.destroy();
5798
+ this.initializingWebGPU = false;
5799
+ return;
5800
+ }
5721
5801
  this.device = newDevice;
5722
5802
  this.initializingWebGPU = false;
5723
5803
  const format = navigator.gpu ? navigator.gpu.getPreferredCanvasFormat() : "rgba8unorm";
@@ -6070,8 +6150,21 @@ var Scene = class _Scene {
6070
6150
  }
6071
6151
  /**
6072
6152
  * Manually resize the Scene's viewport.
6153
+ *
6154
+ * Rejects a negative or non-finite dimension, keeping the last known-good
6155
+ * size. `0` is accepted: a zero-size scene is tolerated and warned about at
6156
+ * {@link start} instead, and rejecting it here would contradict that.
6073
6157
  */
6074
6158
  resize(width, height) {
6159
+ if (!Number.isFinite(width) || !Number.isFinite(height) || width < 0 || height < 0) {
6160
+ if (!this.hasWarnedInvalidResize) {
6161
+ console.warn(
6162
+ `[VectoJS] Scene.resize() ignored invalid dimensions (width: ${width}, height: ${height}). Width and height must be finite and non-negative; the previous viewport size was kept.`
6163
+ );
6164
+ this.hasWarnedInvalidResize = true;
6165
+ }
6166
+ return;
6167
+ }
6075
6168
  this.width = width;
6076
6169
  this.height = height;
6077
6170
  this.contentFontEpoch++;
@@ -6604,8 +6697,8 @@ function distSqToSegment(px, py, x1, y1, x2, y2) {
6604
6697
  return ex * ex + ey * ey;
6605
6698
  }
6606
6699
  var SplineEntity = class extends Entity {
6607
- doc;
6608
- lineWidth;
6700
+ _doc;
6701
+ _lineWidth;
6609
6702
  defaultColor;
6610
6703
  hitTolerance;
6611
6704
  cache;
@@ -6616,10 +6709,42 @@ var SplineEntity = class extends Entity {
6616
6709
  /** Logical (CSS-pixel) size of the baked bitmap — the blit destination size. */
6617
6710
  bakedWidth = 0;
6618
6711
  bakedHeight = 0;
6619
- /** Gradient strokes can't be baked to a solid-color bitmap; they render per-frame. */
6712
+ /**
6713
+ * Gradient strokes can't be baked to a solid-color bitmap; they render
6714
+ * per-frame. Derived from `_doc`, so it is NOT readonly — assigning a new
6715
+ * document has to recompute it (see the `doc` setter).
6716
+ */
6620
6717
  containsGradient;
6621
6718
  /** Lazily-flattened polylines (one Float32Array of [x,y,...] per segment) for hit-testing. */
6622
6719
  polylines = null;
6720
+ /**
6721
+ * The spline document to render. Assigning a new document invalidates all
6722
+ * caches (baked canvas, flattened polylines, bounds).
6723
+ */
6724
+ get doc() {
6725
+ return this._doc;
6726
+ }
6727
+ set doc(value) {
6728
+ if (this._doc === value) return;
6729
+ this._doc = value;
6730
+ this.invalidateCache();
6731
+ this.bounds = this.computeBounds();
6732
+ this.width = this.bounds.width;
6733
+ this.height = this.bounds.height;
6734
+ this.containsGradient = this.computeContainsGradient();
6735
+ }
6736
+ /**
6737
+ * Stroke width in local units. Assigning a new value invalidates the baked
6738
+ * canvas and hit-test polylines so visual and hit geometry stay consistent.
6739
+ */
6740
+ get lineWidth() {
6741
+ return this._lineWidth;
6742
+ }
6743
+ set lineWidth(value) {
6744
+ if (this._lineWidth === value) return;
6745
+ this._lineWidth = value;
6746
+ this.invalidateCache();
6747
+ }
6623
6748
  /**
6624
6749
  * When `true`, the renderer draws a rounded-rect outline of the entity's
6625
6750
  * local bounds after painting the curves. Useful for drag feedback and
@@ -6628,8 +6753,8 @@ var SplineEntity = class extends Entity {
6628
6753
  showBounds = false;
6629
6754
  constructor(doc, opts = {}) {
6630
6755
  super();
6631
- this.doc = doc;
6632
- this.lineWidth = opts.lineWidth ?? 2;
6756
+ this._doc = doc;
6757
+ this._lineWidth = opts.lineWidth ?? 2;
6633
6758
  this.cache = opts.cache ?? true;
6634
6759
  this.defaultColor = opts.defaultColor ?? "#e2e8f0";
6635
6760
  this.hitMode = opts.hitTest ?? "curve";
@@ -6637,21 +6762,35 @@ var SplineEntity = class extends Entity {
6637
6762
  this.bounds = this.computeBounds();
6638
6763
  this.width = this.bounds.width;
6639
6764
  this.height = this.bounds.height;
6640
- const isGradient = (c) => c !== null && !Array.isArray(c);
6641
- this.containsGradient = (this.doc.equations?.some((eq) => isGradient(eq.color_rgb)) ?? false) || (this.doc.paths?.some((p) => isGradient(p.color_rgb)) ?? false);
6765
+ this.containsGradient = this.computeContainsGradient();
6642
6766
  this.interactive = true;
6643
6767
  }
6768
+ /**
6769
+ * Whether any stroke in the current document is a gradient. Shared by the
6770
+ * constructor and the `doc` setter so both stay in agreement — the flag picks
6771
+ * the render path, so a stale value renders a gradient as a flat color.
6772
+ */
6773
+ computeContainsGradient() {
6774
+ const isGradient = (c) => c !== null && !Array.isArray(c);
6775
+ return (this._doc.equations?.some((eq) => isGradient(eq.color_rgb)) ?? false) || (this._doc.paths?.some((p) => isGradient(p.color_rgb)) ?? false);
6776
+ }
6777
+ /** Clear cached baked canvas and hit-test polylines. */
6778
+ invalidateCache() {
6779
+ this.baked = false;
6780
+ this.offscreen = null;
6781
+ this.polylines = null;
6782
+ }
6644
6783
  computeBounds() {
6645
- if (this.doc.bounding_box) {
6646
- const [minX2, minY2, maxX2, maxY2] = this.doc.bounding_box;
6784
+ if (this._doc.bounding_box) {
6785
+ const [minX2, minY2, maxX2, maxY2] = this._doc.bounding_box;
6647
6786
  return { x: minX2, y: minY2, width: maxX2 - minX2, height: maxY2 - minY2 };
6648
6787
  }
6649
6788
  let minX = Infinity;
6650
6789
  let minY = Infinity;
6651
6790
  let maxX = -Infinity;
6652
6791
  let maxY = -Infinity;
6653
- if (this.doc.equations) {
6654
- for (const eq of this.doc.equations) {
6792
+ if (this._doc.equations) {
6793
+ for (const eq of this._doc.equations) {
6655
6794
  for (const seg of eq.data) {
6656
6795
  const b = polySegmentToBezier(seg);
6657
6796
  for (const [x, y] of [
@@ -6668,8 +6807,8 @@ var SplineEntity = class extends Entity {
6668
6807
  }
6669
6808
  }
6670
6809
  }
6671
- if (this.doc.paths) {
6672
- for (const path of this.doc.paths) {
6810
+ if (this._doc.paths) {
6811
+ for (const path of this._doc.paths) {
6673
6812
  for (const pt of path.data) {
6674
6813
  if (pt.x < minX) minX = pt.x;
6675
6814
  if (pt.x > maxX) maxX = pt.x;
@@ -6729,15 +6868,15 @@ var SplineEntity = class extends Entity {
6729
6868
  getPolylines() {
6730
6869
  if (this.polylines) return this.polylines;
6731
6870
  const out = [];
6732
- if (this.doc.equations) {
6733
- for (const eq of this.doc.equations) {
6871
+ if (this._doc.equations) {
6872
+ for (const eq of this._doc.equations) {
6734
6873
  for (const seg of eq.data) {
6735
6874
  out.push(flattenBezier(polySegmentToBezier(seg), HIT_SAMPLES));
6736
6875
  }
6737
6876
  }
6738
6877
  }
6739
- if (this.doc.paths) {
6740
- for (const path of this.doc.paths) {
6878
+ if (this._doc.paths) {
6879
+ for (const path of this._doc.paths) {
6741
6880
  const pts = new Float32Array(path.data.length * 2);
6742
6881
  for (let i = 0; i < path.data.length; i++) {
6743
6882
  pts[i * 2] = path.data[i].x;
@@ -6766,8 +6905,8 @@ var SplineEntity = class extends Entity {
6766
6905
  );
6767
6906
  }
6768
6907
  strokeEquations(r) {
6769
- if (this.doc.equations) {
6770
- for (const eq of this.doc.equations) {
6908
+ if (this._doc.equations) {
6909
+ for (const eq of this._doc.equations) {
6771
6910
  const stroke = this.resolveColor(eq.color_rgb, r);
6772
6911
  r.beginPath();
6773
6912
  for (const seg of eq.data) {
@@ -6778,8 +6917,8 @@ var SplineEntity = class extends Entity {
6778
6917
  r.stroke(stroke, this.lineWidth);
6779
6918
  }
6780
6919
  }
6781
- if (this.doc.paths) {
6782
- for (const path of this.doc.paths) {
6920
+ if (this._doc.paths) {
6921
+ for (const path of this._doc.paths) {
6783
6922
  const stroke = this.resolveColor(path.color_rgb, r);
6784
6923
  r.beginPath();
6785
6924
  if (path.data.length > 0) {
@@ -6818,8 +6957,8 @@ var SplineEntity = class extends Entity {
6818
6957
  ctx.lineWidth = this.lineWidth;
6819
6958
  ctx.lineCap = "round";
6820
6959
  ctx.lineJoin = "round";
6821
- if (this.doc.equations) {
6822
- for (const eq of this.doc.equations) {
6960
+ if (this._doc.equations) {
6961
+ for (const eq of this._doc.equations) {
6823
6962
  ctx.strokeStyle = eq.color_rgb === null ? this.defaultColor : Array.isArray(eq.color_rgb) ? rgbToCss(eq.color_rgb) : this.defaultColor;
6824
6963
  ctx.beginPath();
6825
6964
  for (const seg of eq.data) {
@@ -6830,8 +6969,8 @@ var SplineEntity = class extends Entity {
6830
6969
  ctx.stroke();
6831
6970
  }
6832
6971
  }
6833
- if (this.doc.paths) {
6834
- for (const path of this.doc.paths) {
6972
+ if (this._doc.paths) {
6973
+ for (const path of this._doc.paths) {
6835
6974
  ctx.strokeStyle = path.color_rgb === null ? this.defaultColor : Array.isArray(path.color_rgb) ? rgbToCss(path.color_rgb) : this.defaultColor;
6836
6975
  ctx.beginPath();
6837
6976
  if (path.data.length > 0) {
@@ -6893,7 +7032,14 @@ var Rect = class extends Entity {
6893
7032
  this.radius = opts.radius ?? 0;
6894
7033
  }
6895
7034
  getBounds() {
6896
- return { x: 0, y: 0, width: this.width, height: this.height };
7035
+ const inflation = this.stroke !== null ? this.strokeWidth / 2 : 0;
7036
+ const origin = inflation > 0 ? -inflation : 0;
7037
+ return {
7038
+ x: origin,
7039
+ y: origin,
7040
+ width: this.width + inflation * 2,
7041
+ height: this.height + inflation * 2
7042
+ };
6897
7043
  }
6898
7044
  /**
6899
7045
  * Solid-fill, square-cornered, unstroked rectangles opt into the GPU
@@ -6955,11 +7101,15 @@ var Circle = class extends Entity {
6955
7101
  this.a11yOffsetY = -this._radius;
6956
7102
  }
6957
7103
  getBounds() {
7104
+ const r = this._radius;
7105
+ const inflation = this.stroke !== null ? this.strokeWidth / 2 : 0;
7106
+ const fullRadius = r + inflation;
7107
+ const origin = fullRadius > 0 ? -fullRadius : 0;
6958
7108
  return {
6959
- x: -this._radius,
6960
- y: -this._radius,
6961
- width: this._radius * 2,
6962
- height: this._radius * 2
7109
+ x: origin,
7110
+ y: origin,
7111
+ width: fullRadius * 2,
7112
+ height: fullRadius * 2
6963
7113
  };
6964
7114
  }
6965
7115
  /**
package/dist/text.js CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
 
4
4
 
5
- var _chunkTRT2TRCWjs = require('./chunk-TRT2TRCW.js');
5
+ var _chunkFQ2565ITjs = require('./chunk-FQ2565IT.js');
6
6
 
7
7
  // src/text/index.ts
8
8
  var _text = require('@vectojs/text'); _createStarExport(_text);
9
9
 
10
10
 
11
11
 
12
- exports.MSDFTextEntity = _chunkTRT2TRCWjs.MSDFTextEntity; exports.SVGEntity = _chunkTRT2TRCWjs.SVGEntity;
12
+ exports.MSDFTextEntity = _chunkFQ2565ITjs.MSDFTextEntity; exports.SVGEntity = _chunkFQ2565ITjs.SVGEntity;
package/dist/text.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  MSDFTextEntity,
3
3
  SVGEntity
4
- } from "./chunk-RWN3ITBD.mjs";
4
+ } from "./chunk-YTGGL4I4.mjs";
5
5
 
6
6
  // src/text/index.ts
7
7
  export * from "@vectojs/text";
@@ -481,6 +481,8 @@ export declare class VectoJSEvent<N = unknown> {
481
481
  get deltaX(): number | undefined;
482
482
  /** Native vertical wheel delta, if this wraps a `WheelEvent`. */
483
483
  get deltaY(): number | undefined;
484
+ /** Native wheel delta mode (0=pixels, 1=lines, 2=pages), if this wraps a `WheelEvent`. */
485
+ get deltaMode(): number | undefined;
484
486
  /** Native pointer X, if this wraps a pointer/mouse event. */
485
487
  get clientX(): number | undefined;
486
488
  /** Native pointer Y, if this wraps a pointer/mouse event. */
@@ -799,6 +799,14 @@ export declare class Scene {
799
799
  private _wasmWorld;
800
800
  private _structureVersion;
801
801
  private _storeStructureVersion;
802
+ /**
803
+ * Consecutive `uploadRuns` rejections. Reset by any success and by
804
+ * {@link setTransformBackend}; at {@link WASM_UPLOAD_REJECT_LIMIT} the backend
805
+ * mode flips to `'js'` permanently.
806
+ */
807
+ private _wasmUploadRejections;
808
+ /** Latch for the permanent-fallback warning, which fires from a per-frame path. */
809
+ private hasWarnedWasmUploadFallback;
802
810
  private _computeEntities;
803
811
  private _computeEntitiesVersion;
804
812
  /** Invalidate the resident WASM store layout; the next wasm-mode frame rebuilds
@@ -1188,6 +1196,16 @@ export declare class Scene {
1188
1196
  * else the canvas). Stored so `destroy()` detaches from the same element. */
1189
1197
  private pointerEventTarget;
1190
1198
  private hasWarnedZeroSize;
1199
+ /**
1200
+ * Latch for {@link Scene.resize}'s invalid-dimension warning.
1201
+ *
1202
+ * Separate from `hasWarnedZeroSize`: that one fires for a tolerated zero-size
1203
+ * scene at `start()`, this one for a rejected resize, and sharing the flag
1204
+ * would let either suppress the other's first (and only) report. Latched
1205
+ * because `resize()` is commonly driven from a `ResizeObserver`, which would
1206
+ * otherwise warn on every frame of a drag.
1207
+ */
1208
+ private hasWarnedInvalidResize;
1191
1209
  private fontLoadHandler;
1192
1210
  private static _devMode;
1193
1211
  /**
@@ -1376,7 +1394,8 @@ export declare class Scene {
1376
1394
  /**
1377
1395
  * Begin the `requestAnimationFrame` render loop.
1378
1396
  *
1379
- * Idempotent — calling `start()` on an already-running scene is a no-op.
1397
+ * Idempotent — calling `start()` on an already-running scene is a no-op, and so
1398
+ * is calling it on a destroyed one.
1380
1399
  */
1381
1400
  start(): void;
1382
1401
  /** Schedule the next frame, or no-op where `requestAnimationFrame` is absent (SSR). */
@@ -1737,6 +1756,10 @@ export declare class Scene {
1737
1756
  toSVG(): string;
1738
1757
  /**
1739
1758
  * Manually resize the Scene's viewport.
1759
+ *
1760
+ * Rejects a negative or non-finite dimension, keeping the last known-good
1761
+ * size. `0` is accepted: a zero-size scene is tolerated and warned about at
1762
+ * {@link start} instead, and rejecting it here would contradict that.
1740
1763
  */
1741
1764
  resize(width: number, height: number): void;
1742
1765
  /** Effective device pixel ratio, matching CanvasRenderer: real DPR clamped to
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vectojs/core",
3
- "version": "1.32.5",
3
+ "version": "1.32.7",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },