@vectojs/core 1.28.1 → 1.29.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.
@@ -86,6 +86,12 @@ var CanvasRenderer = class _CanvasRenderer {
86
86
  * change at runtime (e.g. a window dragged between displays).
87
87
  */
88
88
  maxDPR;
89
+ /**
90
+ * The ratio the context is actually scaled by, i.e. the last value the
91
+ * constructor or {@link resize} applied. Backs {@link pixelRatio}; see there
92
+ * for why this is recorded rather than recomputed on read.
93
+ */
94
+ appliedDPR = 1;
89
95
  /**
90
96
  * Max circles per batched `fill()`. A single Canvas 2D `fill()` over a path is
91
97
  * superlinear in sub-path count, so an unbounded batch is *slower* than many
@@ -124,6 +130,7 @@ var CanvasRenderer = class _CanvasRenderer {
124
130
  constructor(canvas, size, maxDPR) {
125
131
  this.maxDPR = maxDPR;
126
132
  const dpr = this.effectiveDPR();
133
+ this.appliedDPR = dpr;
127
134
  this.width = size?.width ?? (typeof window !== "undefined" ? window.innerWidth : canvas.width || 0);
128
135
  this.height = size?.height ?? (typeof window !== "undefined" ? window.innerHeight : canvas.height || 0);
129
136
  canvas.width = this.width * dpr;
@@ -162,7 +169,9 @@ var CanvasRenderer = class _CanvasRenderer {
162
169
  if (!ctx) return;
163
170
  this.ctx = ctx;
164
171
  ctx.setTransform(1, 0, 0, 1, 0, 0);
165
- ctx.scale(this.effectiveDPR(), this.effectiveDPR());
172
+ const restoredDPR = this.effectiveDPR();
173
+ this.appliedDPR = restoredDPR;
174
+ ctx.scale(restoredDPR, restoredDPR);
166
175
  this._cachedFont = "";
167
176
  this._cachedFill = "";
168
177
  this.batchActive = false;
@@ -185,6 +194,25 @@ var CanvasRenderer = class _CanvasRenderer {
185
194
  const real = getDevicePixelRatio();
186
195
  return this.maxDPR !== void 0 ? Math.min(real, this.maxDPR) : real;
187
196
  }
197
+ /**
198
+ * @inheritdoc
199
+ *
200
+ * The ratio the context is **currently scaled by**, recorded by the constructor
201
+ * and {@link resize} — deliberately not a live `effectiveDPR()` call.
202
+ *
203
+ * The distinction is load-bearing rather than pedantic. `devicePixelRatio`
204
+ * changes the instant a zoom lands, but the backing store is only reallocated
205
+ * when something calls {@link resize} (in a `Scene`, the `(resolution: Ndppx)`
206
+ * media query). A live getter therefore reports the *future* ratio during that
207
+ * window, and a caller rasterizing pixels from it produces a texture that the
208
+ * still-old context scale resamples — the same defect this property exists to
209
+ * let callers avoid, merely inverted. Reporting the applied ratio means a
210
+ * cache keyed on it is always consistent with the pixels it is blitted into,
211
+ * and it simply re-keys on the next `resize`.
212
+ */
213
+ get pixelRatio() {
214
+ return this.appliedDPR;
215
+ }
188
216
  /**
189
217
  * Resize the backing canvas buffer and re-apply DPR scaling.
190
218
  *
@@ -195,6 +223,7 @@ var CanvasRenderer = class _CanvasRenderer {
195
223
  */
196
224
  resize(width, height) {
197
225
  const dpr = this.effectiveDPR();
226
+ this.appliedDPR = dpr;
198
227
  this.width = width;
199
228
  this.height = height;
200
229
  this.ctx.canvas.width = width * dpr;
@@ -2075,6 +2104,20 @@ var GlyphRasterAtlas = class {
2075
2104
  this.dpr = Math.max(1, options.dpr ?? 1);
2076
2105
  this.maxSize = Math.min(HARD_MAX_SIZE, Math.max(256, options.maxSize ?? 2048));
2077
2106
  }
2107
+ /**
2108
+ * The device-pixel-ratio these slots were rasterized at.
2109
+ *
2110
+ * Immutable by design. Slot `sx`/`sy`/`sw`/`sh` are device pixels at *this*
2111
+ * ratio while `w`/`h` are CSS pixels, so changing it would invalidate every
2112
+ * resident slot — an atlas is therefore keyed by DPR and replaced on a change,
2113
+ * not mutated (see `Markdown`'s code atlas pool). Exposed so a caller can
2114
+ * compare it against {@link IRenderer.pixelRatio} and assert the blit is
2115
+ * 1:1 rather than resampled: `blitScale = renderer.pixelRatio / atlas.dpr`,
2116
+ * which must be 1 for the pixels to land crisp.
2117
+ */
2118
+ get pixelRatio() {
2119
+ return this.dpr;
2120
+ }
2078
2121
  /** Live instrumentation snapshot. */
2079
2122
  get stats() {
2080
2123
  return {
@@ -88,6 +88,12 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
88
88
  * change at runtime (e.g. a window dragged between displays).
89
89
  */
90
90
 
91
+ /**
92
+ * The ratio the context is actually scaled by, i.e. the last value the
93
+ * constructor or {@link resize} applied. Backs {@link pixelRatio}; see there
94
+ * for why this is recorded rather than recomputed on read.
95
+ */
96
+ __init3() {this.appliedDPR = 1}
91
97
  /**
92
98
  * Max circles per batched `fill()`. A single Canvas 2D `fill()` over a path is
93
99
  * superlinear in sub-path count, so an unbounded batch is *slower* than many
@@ -97,23 +103,23 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
97
103
  static __initStatic() {this.MAX_BATCH = 64}
98
104
  // Order-preserving batch state for fillCircle(): a run of same-style circles
99
105
  // accumulates into one path and is committed by a single fill() on flush().
100
- __init3() {this.batchActive = false}
101
- __init4() {this.batchColor = ""}
102
- __init5() {this.batchAlpha = 1}
103
- __init6() {this.batchCount = 0}
104
- __init7() {this._cachedFont = ""}
105
- __init8() {this._cachedFill = ""}
106
+ __init4() {this.batchActive = false}
107
+ __init5() {this.batchColor = ""}
108
+ __init6() {this.batchAlpha = 1}
109
+ __init7() {this.batchCount = 0}
110
+ __init8() {this._cachedFont = ""}
111
+ __init9() {this._cachedFill = ""}
106
112
  /** Backend discriminator; see {@link IRenderer.kind}. */
107
- __init9() {this.kind = "canvas2d"}
113
+ __init10() {this.kind = "canvas2d"}
108
114
  /**
109
115
  * Draw counters, allocated only once counting is enabled.
110
116
  *
111
117
  * Null when off, so the guard on every op is a single null test and an inactive
112
118
  * renderer carries no counter object at all.
113
119
  */
114
- __init10() {this.counters = null}
120
+ __init11() {this.counters = null}
115
121
  /** Accumulated primitive area, kept separately so the ratio is derived on read. */
116
- __init11() {this.drawnArea = 0}
122
+ __init12() {this.drawnArea = 0}
117
123
  /**
118
124
  * @param canvas - The target canvas. Its backing store is resized to the
119
125
  * logical size × devicePixelRatio.
@@ -123,9 +129,10 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
123
129
  * set) so the canvas's own dimensions aren't clobbered by the window's.
124
130
  * @param maxDPR - See {@link maxDPR}.
125
131
  */
126
- constructor(canvas, size, maxDPR) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);_class.prototype.__init5.call(this);_class.prototype.__init6.call(this);_class.prototype.__init7.call(this);_class.prototype.__init8.call(this);_class.prototype.__init9.call(this);_class.prototype.__init10.call(this);_class.prototype.__init11.call(this);
132
+ constructor(canvas, size, maxDPR) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);_class.prototype.__init5.call(this);_class.prototype.__init6.call(this);_class.prototype.__init7.call(this);_class.prototype.__init8.call(this);_class.prototype.__init9.call(this);_class.prototype.__init10.call(this);_class.prototype.__init11.call(this);_class.prototype.__init12.call(this);
127
133
  this.maxDPR = maxDPR;
128
134
  const dpr = this.effectiveDPR();
135
+ this.appliedDPR = dpr;
129
136
  this.width = _nullishCoalesce(_optionalChain([size, 'optionalAccess', _5 => _5.width]), () => ( (typeof window !== "undefined" ? window.innerWidth : canvas.width || 0)));
130
137
  this.height = _nullishCoalesce(_optionalChain([size, 'optionalAccess', _6 => _6.height]), () => ( (typeof window !== "undefined" ? window.innerHeight : canvas.height || 0)));
131
138
  canvas.width = this.width * dpr;
@@ -164,7 +171,9 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
164
171
  if (!ctx) return;
165
172
  this.ctx = ctx;
166
173
  ctx.setTransform(1, 0, 0, 1, 0, 0);
167
- ctx.scale(this.effectiveDPR(), this.effectiveDPR());
174
+ const restoredDPR = this.effectiveDPR();
175
+ this.appliedDPR = restoredDPR;
176
+ ctx.scale(restoredDPR, restoredDPR);
168
177
  this._cachedFont = "";
169
178
  this._cachedFill = "";
170
179
  this.batchActive = false;
@@ -187,6 +196,25 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
187
196
  const real = getDevicePixelRatio();
188
197
  return this.maxDPR !== void 0 ? Math.min(real, this.maxDPR) : real;
189
198
  }
199
+ /**
200
+ * @inheritdoc
201
+ *
202
+ * The ratio the context is **currently scaled by**, recorded by the constructor
203
+ * and {@link resize} — deliberately not a live `effectiveDPR()` call.
204
+ *
205
+ * The distinction is load-bearing rather than pedantic. `devicePixelRatio`
206
+ * changes the instant a zoom lands, but the backing store is only reallocated
207
+ * when something calls {@link resize} (in a `Scene`, the `(resolution: Ndppx)`
208
+ * media query). A live getter therefore reports the *future* ratio during that
209
+ * window, and a caller rasterizing pixels from it produces a texture that the
210
+ * still-old context scale resamples — the same defect this property exists to
211
+ * let callers avoid, merely inverted. Reporting the applied ratio means a
212
+ * cache keyed on it is always consistent with the pixels it is blitted into,
213
+ * and it simply re-keys on the next `resize`.
214
+ */
215
+ get pixelRatio() {
216
+ return this.appliedDPR;
217
+ }
190
218
  /**
191
219
  * Resize the backing canvas buffer and re-apply DPR scaling.
192
220
  *
@@ -197,6 +225,7 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
197
225
  */
198
226
  resize(width, height) {
199
227
  const dpr = this.effectiveDPR();
228
+ this.appliedDPR = dpr;
200
229
  this.width = width;
201
230
  this.height = height;
202
231
  this.ctx.canvas.width = width * dpr;
@@ -501,42 +530,42 @@ function fontFamilyFromShorthand(font, sizeToken) {
501
530
  }
502
531
  var SVGRenderer = (_class2 = class {
503
532
  /** Backend discriminator; see {@link IRenderer.kind}. */
504
- __init12() {this.kind = "svg"}
533
+ __init13() {this.kind = "svg"}
505
534
 
506
535
 
507
- __init13() {this.buffer = []}
508
- __init14() {this.defsBuffer = []}
509
- __init15() {this.currentPath = []}
536
+ __init14() {this.buffer = []}
537
+ __init15() {this.defsBuffer = []}
538
+ __init16() {this.currentPath = []}
510
539
  // Inline matrix states (3x3 representation)
511
540
  // [a, b, c, d, e, f] corresponding to:
512
541
  // | a c e |
513
542
  // | b d f |
514
543
  // | 0 0 1 |
515
- __init16() {this.mStack = []}
516
- __init17() {this.ma = 1}
517
- __init18() {this.mb = 0}
518
- __init19() {this.mc = 0}
519
- __init20() {this.md = 1}
520
- __init21() {this.me = 0}
521
- __init22() {this.mf = 0}
544
+ __init17() {this.mStack = []}
545
+ __init18() {this.ma = 1}
546
+ __init19() {this.mb = 0}
547
+ __init20() {this.mc = 0}
548
+ __init21() {this.md = 1}
549
+ __init22() {this.me = 0}
550
+ __init23() {this.mf = 0}
522
551
  // Alpha stack
523
- __init23() {this.alphaStack = []}
524
- __init24() {this.globalAlpha = 1}
552
+ __init24() {this.alphaStack = []}
553
+ __init25() {this.globalAlpha = 1}
525
554
  // Clipping stack
526
- __init25() {this.clipDepthStack = []}
527
- __init26() {this.clipDepth = 0}
528
- __init27() {this.clipCounter = 0}
555
+ __init26() {this.clipDepthStack = []}
556
+ __init27() {this.clipDepth = 0}
557
+ __init28() {this.clipCounter = 0}
529
558
  // uniquely identifies clipPaths
530
559
  // Batch circles states (local coordinates)
531
- __init28() {this.batchCircles = []}
532
- __init29() {this.batchMatrix = [1, 0, 0, 1, 0, 0]}
533
- __init30() {this.batchColor = ""}
534
- __init31() {this.batchAlpha = 1}
535
- __init32() {this.batchActive = false}
560
+ __init29() {this.batchCircles = []}
561
+ __init30() {this.batchMatrix = [1, 0, 0, 1, 0, 0]}
562
+ __init31() {this.batchColor = ""}
563
+ __init32() {this.batchAlpha = 1}
564
+ __init33() {this.batchActive = false}
536
565
  // Cache for generated gradient defs
537
- __init33() {this.gradientCounter = 0}
538
- __init34() {this.gradientCache = /* @__PURE__ */ new Map()}
539
- constructor(width, height) {;_class2.prototype.__init12.call(this);_class2.prototype.__init13.call(this);_class2.prototype.__init14.call(this);_class2.prototype.__init15.call(this);_class2.prototype.__init16.call(this);_class2.prototype.__init17.call(this);_class2.prototype.__init18.call(this);_class2.prototype.__init19.call(this);_class2.prototype.__init20.call(this);_class2.prototype.__init21.call(this);_class2.prototype.__init22.call(this);_class2.prototype.__init23.call(this);_class2.prototype.__init24.call(this);_class2.prototype.__init25.call(this);_class2.prototype.__init26.call(this);_class2.prototype.__init27.call(this);_class2.prototype.__init28.call(this);_class2.prototype.__init29.call(this);_class2.prototype.__init30.call(this);_class2.prototype.__init31.call(this);_class2.prototype.__init32.call(this);_class2.prototype.__init33.call(this);_class2.prototype.__init34.call(this);
566
+ __init34() {this.gradientCounter = 0}
567
+ __init35() {this.gradientCache = /* @__PURE__ */ new Map()}
568
+ constructor(width, height) {;_class2.prototype.__init13.call(this);_class2.prototype.__init14.call(this);_class2.prototype.__init15.call(this);_class2.prototype.__init16.call(this);_class2.prototype.__init17.call(this);_class2.prototype.__init18.call(this);_class2.prototype.__init19.call(this);_class2.prototype.__init20.call(this);_class2.prototype.__init21.call(this);_class2.prototype.__init22.call(this);_class2.prototype.__init23.call(this);_class2.prototype.__init24.call(this);_class2.prototype.__init25.call(this);_class2.prototype.__init26.call(this);_class2.prototype.__init27.call(this);_class2.prototype.__init28.call(this);_class2.prototype.__init29.call(this);_class2.prototype.__init30.call(this);_class2.prototype.__init31.call(this);_class2.prototype.__init32.call(this);_class2.prototype.__init33.call(this);_class2.prototype.__init34.call(this);_class2.prototype.__init35.call(this);
540
569
  this.width = width;
541
570
  this.height = height;
542
571
  installRendererDevTraps(this, "SVGRenderer");
@@ -1908,11 +1937,11 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
1908
1937
  `;
1909
1938
  var WebGPUParticleSystemManager = (_class3 = class {
1910
1939
 
1911
- __init35() {this.computePipeline = null}
1912
- __init36() {this.renderPipeline = null}
1913
- __init37() {this.computeBindGroupLayout = null}
1914
- __init38() {this.renderBindGroupLayout = null}
1915
- constructor(device) {;_class3.prototype.__init35.call(this);_class3.prototype.__init36.call(this);_class3.prototype.__init37.call(this);_class3.prototype.__init38.call(this);
1940
+ __init36() {this.computePipeline = null}
1941
+ __init37() {this.renderPipeline = null}
1942
+ __init38() {this.computeBindGroupLayout = null}
1943
+ __init39() {this.renderBindGroupLayout = null}
1944
+ constructor(device) {;_class3.prototype.__init36.call(this);_class3.prototype.__init37.call(this);_class3.prototype.__init38.call(this);_class3.prototype.__init39.call(this);
1916
1945
  this.device = device;
1917
1946
  }
1918
1947
  initPipelines(format) {
@@ -2057,26 +2086,40 @@ var WebGPUParticleSystemManager = (_class3 = class {
2057
2086
  var HARD_MAX_SIZE = 8192;
2058
2087
  var PAD = 2;
2059
2088
  var GlyphRasterAtlas = (_class4 = class {
2060
- __init39() {this.slots = /* @__PURE__ */ new Map()}
2089
+ __init40() {this.slots = /* @__PURE__ */ new Map()}
2061
2090
 
2062
2091
 
2063
- __init40() {this.canvas = null}
2064
- __init41() {this.ctx = null}
2092
+ __init41() {this.canvas = null}
2093
+ __init42() {this.ctx = null}
2065
2094
  /**
2066
2095
  * Shelf packing: glyphs land left-to-right on a row, then a new row starts.
2067
2096
  * A monospace grid produces near-uniform widths, so shelves waste very little
2068
2097
  * and cost one comparison per insert — a real 2D packer would buy nothing here.
2069
2098
  */
2070
- __init42() {this.penX = PAD}
2071
- __init43() {this.penY = PAD}
2072
- __init44() {this.rowHeight = 0}
2073
- __init45() {this._hits = 0}
2074
- __init46() {this._misses = 0}
2075
- __init47() {this._resets = 0}
2076
- constructor(options = {}) {;_class4.prototype.__init39.call(this);_class4.prototype.__init40.call(this);_class4.prototype.__init41.call(this);_class4.prototype.__init42.call(this);_class4.prototype.__init43.call(this);_class4.prototype.__init44.call(this);_class4.prototype.__init45.call(this);_class4.prototype.__init46.call(this);_class4.prototype.__init47.call(this);
2099
+ __init43() {this.penX = PAD}
2100
+ __init44() {this.penY = PAD}
2101
+ __init45() {this.rowHeight = 0}
2102
+ __init46() {this._hits = 0}
2103
+ __init47() {this._misses = 0}
2104
+ __init48() {this._resets = 0}
2105
+ constructor(options = {}) {;_class4.prototype.__init40.call(this);_class4.prototype.__init41.call(this);_class4.prototype.__init42.call(this);_class4.prototype.__init43.call(this);_class4.prototype.__init44.call(this);_class4.prototype.__init45.call(this);_class4.prototype.__init46.call(this);_class4.prototype.__init47.call(this);_class4.prototype.__init48.call(this);
2077
2106
  this.dpr = Math.max(1, _nullishCoalesce(options.dpr, () => ( 1)));
2078
2107
  this.maxSize = Math.min(HARD_MAX_SIZE, Math.max(256, _nullishCoalesce(options.maxSize, () => ( 2048))));
2079
2108
  }
2109
+ /**
2110
+ * The device-pixel-ratio these slots were rasterized at.
2111
+ *
2112
+ * Immutable by design. Slot `sx`/`sy`/`sw`/`sh` are device pixels at *this*
2113
+ * ratio while `w`/`h` are CSS pixels, so changing it would invalidate every
2114
+ * resident slot — an atlas is therefore keyed by DPR and replaced on a change,
2115
+ * not mutated (see `Markdown`'s code atlas pool). Exposed so a caller can
2116
+ * compare it against {@link IRenderer.pixelRatio} and assert the blit is
2117
+ * 1:1 rather than resampled: `blitScale = renderer.pixelRatio / atlas.dpr`,
2118
+ * which must be 1 for the pixels to land crisp.
2119
+ */
2120
+ get pixelRatio() {
2121
+ return this.dpr;
2122
+ }
2080
2123
  /** Live instrumentation snapshot. */
2081
2124
  get stats() {
2082
2125
  return {
@@ -2241,13 +2284,13 @@ var GlyphRasterAtlas = (_class4 = class {
2241
2284
 
2242
2285
  // src/renderer/TextRasterCache.ts
2243
2286
  var TextRasterCache = (_class5 = class {
2244
- __init48() {this.cache = /* @__PURE__ */ new Map()}
2287
+ __init49() {this.cache = /* @__PURE__ */ new Map()}
2245
2288
 
2246
2289
 
2247
- __init49() {this.scratchCtx = null}
2248
- __init50() {this._hits = 0}
2249
- __init51() {this._misses = 0}
2250
- constructor(options = {}) {;_class5.prototype.__init48.call(this);_class5.prototype.__init49.call(this);_class5.prototype.__init50.call(this);_class5.prototype.__init51.call(this);
2290
+ __init50() {this.scratchCtx = null}
2291
+ __init51() {this._hits = 0}
2292
+ __init52() {this._misses = 0}
2293
+ constructor(options = {}) {;_class5.prototype.__init49.call(this);_class5.prototype.__init50.call(this);_class5.prototype.__init51.call(this);_class5.prototype.__init52.call(this);
2251
2294
  this.maxEntries = Math.max(1, _nullishCoalesce(options.maxEntries, () => ( 4096)));
2252
2295
  this.dpr = Math.max(1, _nullishCoalesce(options.dpr, () => ( 1)));
2253
2296
  }
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@
12
12
 
13
13
 
14
14
 
15
- var _chunkK2UGEIT6js = require('./chunk-K2UGEIT6.js');
15
+ var _chunkM73XB4ZKjs = require('./chunk-M73XB4ZK.js');
16
16
 
17
17
 
18
18
 
@@ -2869,7 +2869,7 @@ var Scene = (_class7 = class _Scene {
2869
2869
  }
2870
2870
  static set devMode(active) {
2871
2871
  _Scene._devMode = active;
2872
- _chunkK2UGEIT6js.setRendererDevMode.call(void 0, active);
2872
+ _chunkM73XB4ZKjs.setRendererDevMode.call(void 0, active);
2873
2873
  }
2874
2874
  static _devModeDetected() {
2875
2875
  if (_Scene._devMode) return true;
@@ -2981,7 +2981,7 @@ var Scene = (_class7 = class _Scene {
2981
2981
  this.readingDirection = _nullishCoalesce(options.readingDirection, () => ( "ltr"));
2982
2982
  this.renderMode = _nullishCoalesce(options.renderMode, () => ( "always"));
2983
2983
  this._devActive = _Scene._devModeDetected();
2984
- _chunkK2UGEIT6js.setRendererDevMode.call(void 0, this._devActive);
2984
+ _chunkM73XB4ZKjs.setRendererDevMode.call(void 0, this._devActive);
2985
2985
  this._warnUnknownOptions(options);
2986
2986
  this.reducedMotionQuery = typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia("(prefers-reduced-motion: reduce)") : null;
2987
2987
  if (typeof window !== "undefined" && typeof window.matchMedia === "function") {
@@ -3009,7 +3009,7 @@ var Scene = (_class7 = class _Scene {
3009
3009
  if (options.renderer) {
3010
3010
  this.renderer = options.renderer;
3011
3011
  } else {
3012
- this.renderer = new (0, _chunkK2UGEIT6js.CanvasRenderer)(
3012
+ this.renderer = new (0, _chunkM73XB4ZKjs.CanvasRenderer)(
3013
3013
  canvas,
3014
3014
  this.disableWindowResize ? { width: this.width, height: this.height } : void 0,
3015
3015
  this.maxDPR
@@ -4119,7 +4119,7 @@ var Scene = (_class7 = class _Scene {
4119
4119
  this.syncOptionalAttribute(
4120
4120
  el,
4121
4121
  "href",
4122
- attrs.href === void 0 ? void 0 : _chunkK2UGEIT6js.sanitizeUrl.call(void 0, attrs.href)
4122
+ attrs.href === void 0 ? void 0 : _chunkM73XB4ZKjs.sanitizeUrl.call(void 0, attrs.href)
4123
4123
  );
4124
4124
  this.syncOptionalAttribute(el, "target", attrs.target);
4125
4125
  }
@@ -5576,7 +5576,7 @@ var Scene = (_class7 = class _Scene {
5576
5576
  * Export the current scene state to a lightweight, flat SVG XML string.
5577
5577
  */
5578
5578
  toSVG() {
5579
- const renderer = new (0, _chunkK2UGEIT6js.SVGRenderer)(this.width, this.height);
5579
+ const renderer = new (0, _chunkM73XB4ZKjs.SVGRenderer)(this.width, this.height);
5580
5580
  this.render(renderer, 0, 0);
5581
5581
  return renderer.toXMLString();
5582
5582
  }
@@ -6637,8 +6637,8 @@ var DOMPortalEntity = (_class11 = class extends _chunkZ3HFY75Rjs.Entity {
6637
6637
  }, _class11);
6638
6638
 
6639
6639
  // src/index.ts
6640
- Scene.registerWebGLPointRendererCreator(_chunkK2UGEIT6js.createWebGLPointRenderer);
6641
- Scene.registerWebGPUParticleSystemManager(_chunkK2UGEIT6js.WebGPUParticleSystemManager);
6640
+ Scene.registerWebGLPointRendererCreator(_chunkM73XB4ZKjs.createWebGLPointRenderer);
6641
+ Scene.registerWebGPUParticleSystemManager(_chunkM73XB4ZKjs.WebGPUParticleSystemManager);
6642
6642
 
6643
6643
 
6644
6644
 
@@ -6682,4 +6682,4 @@ Scene.registerWebGPUParticleSystemManager(_chunkK2UGEIT6js.WebGPUParticleSystemM
6682
6682
 
6683
6683
 
6684
6684
 
6685
- exports.CanvasRenderer = _chunkK2UGEIT6js.CanvasRenderer; exports.Circle = Circle; exports.ComputeParticleEntity = ComputeParticleEntity; exports.DOMPortalEntity = DOMPortalEntity; exports.Entity = _chunkZ3HFY75Rjs.Entity; exports.GlyphRasterAtlas = _chunkK2UGEIT6js.GlyphRasterAtlas; exports.GridTextEntity = GridTextEntity; exports.Group = Group; exports.MSDFTextEntity = _chunkZ3HFY75Rjs.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.SCENE_OPTION_KEYS = SCENE_OPTION_KEYS; exports.SVGEntity = _chunkZ3HFY75Rjs.SVGEntity; exports.SVGRenderer = _chunkK2UGEIT6js.SVGRenderer; exports.Scene = Scene; exports.SplineEntity = SplineEntity; exports.TextEntity = TextEntity; exports.TextRasterCache = _chunkK2UGEIT6js.TextRasterCache; exports.VECTO_USER_TIMING = VECTO_USER_TIMING; exports.VectoJSEvent = _chunkZ3HFY75Rjs.VectoJSEvent; exports.WebGPUParticleSystemManager = _chunkK2UGEIT6js.WebGPUParticleSystemManager; exports.beginVectoUserTiming = beginVectoUserTiming; exports.createWebGLPointRenderer = _chunkK2UGEIT6js.createWebGLPointRenderer; exports.endVectoUserTiming = endVectoUserTiming; exports.installRendererDevTraps = _chunkK2UGEIT6js.installRendererDevTraps; exports.isRendererDevMode = _chunkK2UGEIT6js.isRendererDevMode; exports.isSafeUrl = _chunkK2UGEIT6js.isSafeUrl; exports.loadSpline = loadSpline; exports.measureVectoUserTiming = measureVectoUserTiming; exports.parseColorToRGBA = _chunkK2UGEIT6js.parseColorToRGBA; exports.polySegmentToBezier = polySegmentToBezier; exports.sanitizeUrl = _chunkK2UGEIT6js.sanitizeUrl; exports.setRendererDevMode = _chunkK2UGEIT6js.setRendererDevMode;
6685
+ exports.CanvasRenderer = _chunkM73XB4ZKjs.CanvasRenderer; exports.Circle = Circle; exports.ComputeParticleEntity = ComputeParticleEntity; exports.DOMPortalEntity = DOMPortalEntity; exports.Entity = _chunkZ3HFY75Rjs.Entity; exports.GlyphRasterAtlas = _chunkM73XB4ZKjs.GlyphRasterAtlas; exports.GridTextEntity = GridTextEntity; exports.Group = Group; exports.MSDFTextEntity = _chunkZ3HFY75Rjs.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.SCENE_OPTION_KEYS = SCENE_OPTION_KEYS; exports.SVGEntity = _chunkZ3HFY75Rjs.SVGEntity; exports.SVGRenderer = _chunkM73XB4ZKjs.SVGRenderer; exports.Scene = Scene; exports.SplineEntity = SplineEntity; exports.TextEntity = TextEntity; exports.TextRasterCache = _chunkM73XB4ZKjs.TextRasterCache; exports.VECTO_USER_TIMING = VECTO_USER_TIMING; exports.VectoJSEvent = _chunkZ3HFY75Rjs.VectoJSEvent; exports.WebGPUParticleSystemManager = _chunkM73XB4ZKjs.WebGPUParticleSystemManager; exports.beginVectoUserTiming = beginVectoUserTiming; exports.createWebGLPointRenderer = _chunkM73XB4ZKjs.createWebGLPointRenderer; exports.endVectoUserTiming = endVectoUserTiming; exports.installRendererDevTraps = _chunkM73XB4ZKjs.installRendererDevTraps; exports.isRendererDevMode = _chunkM73XB4ZKjs.isRendererDevMode; exports.isSafeUrl = _chunkM73XB4ZKjs.isSafeUrl; exports.loadSpline = loadSpline; exports.measureVectoUserTiming = measureVectoUserTiming; exports.parseColorToRGBA = _chunkM73XB4ZKjs.parseColorToRGBA; exports.polySegmentToBezier = polySegmentToBezier; exports.sanitizeUrl = _chunkM73XB4ZKjs.sanitizeUrl; exports.setRendererDevMode = _chunkM73XB4ZKjs.setRendererDevMode;
package/dist/index.mjs CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  parseColorToRGBA,
12
12
  sanitizeUrl,
13
13
  setRendererDevMode
14
- } from "./chunk-TI5EMJGL.mjs";
14
+ } from "./chunk-IFSFHYF7.mjs";
15
15
  import {
16
16
  Entity,
17
17
  MSDFTextEntity,
@@ -20,6 +20,12 @@ export declare class CanvasRenderer implements IRenderer {
20
20
  * change at runtime (e.g. a window dragged between displays).
21
21
  */
22
22
  maxDPR?: number;
23
+ /**
24
+ * The ratio the context is actually scaled by, i.e. the last value the
25
+ * constructor or {@link resize} applied. Backs {@link pixelRatio}; see there
26
+ * for why this is recorded rather than recomputed on read.
27
+ */
28
+ private appliedDPR;
23
29
  /**
24
30
  * Max circles per batched `fill()`. A single Canvas 2D `fill()` over a path is
25
31
  * superlinear in sub-path count, so an unbounded batch is *slower* than many
@@ -77,6 +83,23 @@ export declare class CanvasRenderer implements IRenderer {
77
83
  getContext(): CanvasRenderingContext2D;
78
84
  /** Real `devicePixelRatio`, clamped to {@link maxDPR} when set. */
79
85
  private effectiveDPR;
86
+ /**
87
+ * @inheritdoc
88
+ *
89
+ * The ratio the context is **currently scaled by**, recorded by the constructor
90
+ * and {@link resize} — deliberately not a live `effectiveDPR()` call.
91
+ *
92
+ * The distinction is load-bearing rather than pedantic. `devicePixelRatio`
93
+ * changes the instant a zoom lands, but the backing store is only reallocated
94
+ * when something calls {@link resize} (in a `Scene`, the `(resolution: Ndppx)`
95
+ * media query). A live getter therefore reports the *future* ratio during that
96
+ * window, and a caller rasterizing pixels from it produces a texture that the
97
+ * still-old context scale resamples — the same defect this property exists to
98
+ * let callers avoid, merely inverted. Reporting the applied ratio means a
99
+ * cache keyed on it is always consistent with the pixels it is blitted into,
100
+ * and it simply re-keys on the next `resize`.
101
+ */
102
+ get pixelRatio(): number;
80
103
  /**
81
104
  * Resize the backing canvas buffer and re-apply DPR scaling.
82
105
  *
@@ -143,6 +143,18 @@ export declare class GlyphRasterAtlas {
143
143
  private _misses;
144
144
  private _resets;
145
145
  constructor(options?: GlyphRasterAtlasOptions);
146
+ /**
147
+ * The device-pixel-ratio these slots were rasterized at.
148
+ *
149
+ * Immutable by design. Slot `sx`/`sy`/`sw`/`sh` are device pixels at *this*
150
+ * ratio while `w`/`h` are CSS pixels, so changing it would invalidate every
151
+ * resident slot — an atlas is therefore keyed by DPR and replaced on a change,
152
+ * not mutated (see `Markdown`'s code atlas pool). Exposed so a caller can
153
+ * compare it against {@link IRenderer.pixelRatio} and assert the blit is
154
+ * 1:1 rather than resampled: `blitScale = renderer.pixelRatio / atlas.dpr`,
155
+ * which must be 1 for the pixels to land crisp.
156
+ */
157
+ get pixelRatio(): number;
146
158
  /** Live instrumentation snapshot. */
147
159
  get stats(): GlyphRasterAtlasStats;
148
160
  /**
@@ -67,6 +67,23 @@ export interface IRenderer {
67
67
  * backend in the build where it matters is not much of a debug tool.
68
68
  */
69
69
  readonly kind?: 'canvas2d' | 'svg' | 'three' | string;
70
+ /**
71
+ * Device pixels per CSS pixel of this renderer's backing store, i.e. the scale
72
+ * already applied to the drawing context.
73
+ *
74
+ * Read this rather than `window.devicePixelRatio` when rasterizing pixels that
75
+ * will be blitted into the renderer: the two differ whenever a backend clamps
76
+ * (`CanvasRenderer.maxDPR`, `SceneOptions.maxDPR`), and rasterizing at the
77
+ * window's ratio while the context is scaled to a clamped one blits a texture
78
+ * the destination then resamples. A cache keyed on this value also stays
79
+ * correct across a zoom or a monitor move, which a value captured once at
80
+ * module scope cannot — that is the defect this exists to make fixable
81
+ * (`GlyphRasterAtlas` consumers; see `Markdown`'s code atlas pool).
82
+ *
83
+ * Optional and a *live* read, not a snapshot: a backend that has no backing
84
+ * store of its own omits it, and a caller treats the absence as `1`.
85
+ */
86
+ readonly pixelRatio?: number;
70
87
  /**
71
88
  * Enable or disable draw counting. Optional; a backend that cannot count omits
72
89
  * it and a reader treats the absence as "not available".
package/dist/renderer.js CHANGED
@@ -10,7 +10,7 @@
10
10
 
11
11
 
12
12
 
13
- var _chunkK2UGEIT6js = require('./chunk-K2UGEIT6.js');
13
+ var _chunkM73XB4ZKjs = require('./chunk-M73XB4ZK.js');
14
14
 
15
15
 
16
16
 
@@ -22,4 +22,4 @@ var _chunkK2UGEIT6js = require('./chunk-K2UGEIT6.js');
22
22
 
23
23
 
24
24
 
25
- exports.CanvasRenderer = _chunkK2UGEIT6js.CanvasRenderer; exports.GlyphRasterAtlas = _chunkK2UGEIT6js.GlyphRasterAtlas; exports.SVGRenderer = _chunkK2UGEIT6js.SVGRenderer; exports.TextRasterCache = _chunkK2UGEIT6js.TextRasterCache; exports.WebGPUParticleSystemManager = _chunkK2UGEIT6js.WebGPUParticleSystemManager; exports.createWebGLPointRenderer = _chunkK2UGEIT6js.createWebGLPointRenderer; exports.installRendererDevTraps = _chunkK2UGEIT6js.installRendererDevTraps; exports.isRendererDevMode = _chunkK2UGEIT6js.isRendererDevMode; exports.parseColorToRGBA = _chunkK2UGEIT6js.parseColorToRGBA; exports.setRendererDevMode = _chunkK2UGEIT6js.setRendererDevMode;
25
+ exports.CanvasRenderer = _chunkM73XB4ZKjs.CanvasRenderer; exports.GlyphRasterAtlas = _chunkM73XB4ZKjs.GlyphRasterAtlas; exports.SVGRenderer = _chunkM73XB4ZKjs.SVGRenderer; exports.TextRasterCache = _chunkM73XB4ZKjs.TextRasterCache; exports.WebGPUParticleSystemManager = _chunkM73XB4ZKjs.WebGPUParticleSystemManager; exports.createWebGLPointRenderer = _chunkM73XB4ZKjs.createWebGLPointRenderer; exports.installRendererDevTraps = _chunkM73XB4ZKjs.installRendererDevTraps; exports.isRendererDevMode = _chunkM73XB4ZKjs.isRendererDevMode; exports.parseColorToRGBA = _chunkM73XB4ZKjs.parseColorToRGBA; exports.setRendererDevMode = _chunkM73XB4ZKjs.setRendererDevMode;
package/dist/renderer.mjs CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  isRendererDevMode,
10
10
  parseColorToRGBA,
11
11
  setRendererDevMode
12
- } from "./chunk-TI5EMJGL.mjs";
12
+ } from "./chunk-IFSFHYF7.mjs";
13
13
  export {
14
14
  CanvasRenderer,
15
15
  GlyphRasterAtlas,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vectojs/core",
3
- "version": "1.28.1",
3
+ "version": "1.29.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },