@fieldnotes/core 0.66.0 → 0.67.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.
package/dist/index.cjs CHANGED
@@ -126,6 +126,7 @@ __export(index_exports, {
126
126
  isPingPresence: () => isPingPresence,
127
127
  pathDistanceCells: () => pathDistanceCells,
128
128
  recommendedFogCellSize: () => recommendedFogCellSize,
129
+ resolveFogStyle: () => resolveFogStyle,
129
130
  resolveHtmlRouting: () => resolveHtmlRouting,
130
131
  setFontSize: () => setFontSize,
131
132
  smartSnap: () => smartSnap,
@@ -7325,19 +7326,181 @@ async function renderHtmlElements(elements, options) {
7325
7326
  return sources;
7326
7327
  }
7327
7328
 
7329
+ // src/fog/fog-style.ts
7330
+ var DEFAULT_PROCEDURAL_OPACITY = 0.6;
7331
+ var DEFAULT_PROCEDURAL_SCALE = 256;
7332
+ var DEFAULT_PROCEDURAL_SEED = 0;
7333
+ var DEFAULT_PROCEDURAL_DETAIL = 2;
7334
+ var DEFAULT_PROCEDURAL_TINT = "#ffffff";
7335
+ var MIN_SCALE = 64;
7336
+ var MAX_SCALE = 1024;
7337
+ var MIN_DETAIL = 1;
7338
+ var MAX_DETAIL = 4;
7339
+ var MAX_SEED = 65535;
7340
+ function clamp(value, min, max) {
7341
+ return Math.max(min, Math.min(max, value));
7342
+ }
7343
+ function finiteOrDefault(value, defaultValue) {
7344
+ return value !== void 0 && Number.isFinite(value) ? value : defaultValue;
7345
+ }
7346
+ function resolveFogStyle(style, legacyColor, defaultColor) {
7347
+ if (style && style.kind === "procedural") {
7348
+ const opacity = clamp(finiteOrDefault(style.opacity, DEFAULT_PROCEDURAL_OPACITY), 0, 1);
7349
+ const scale = clamp(
7350
+ finiteOrDefault(style.scale, DEFAULT_PROCEDURAL_SCALE),
7351
+ MIN_SCALE,
7352
+ MAX_SCALE
7353
+ );
7354
+ const seed = clamp(
7355
+ Math.floor(finiteOrDefault(style.seed, DEFAULT_PROCEDURAL_SEED)),
7356
+ 0,
7357
+ MAX_SEED
7358
+ );
7359
+ const detail = clamp(
7360
+ Math.floor(finiteOrDefault(style.detail, DEFAULT_PROCEDURAL_DETAIL)),
7361
+ MIN_DETAIL,
7362
+ MAX_DETAIL
7363
+ );
7364
+ return {
7365
+ kind: "procedural",
7366
+ backdrop: style.backdrop,
7367
+ // `tint` is required for typed callers. Keep a visible runtime fallback for
7368
+ // untyped/older JavaScript hosts instead of producing a flat same-color overlay.
7369
+ tint: style.tint || DEFAULT_PROCEDURAL_TINT,
7370
+ opacity,
7371
+ scale,
7372
+ seed,
7373
+ detail
7374
+ };
7375
+ }
7376
+ if (style && (!style.kind || style.kind === "solid")) {
7377
+ return { kind: "solid", color: style.color };
7378
+ }
7379
+ return { kind: "solid", color: legacyColor ?? defaultColor };
7380
+ }
7381
+
7382
+ // src/fog/fog-procedural-tile.ts
7383
+ var TILE_PX = 128;
7384
+ function xorshift32(state) {
7385
+ state ^= state << 13;
7386
+ state ^= state >>> 17;
7387
+ state ^= state << 5;
7388
+ return state >>> 0;
7389
+ }
7390
+ function seedState(seed) {
7391
+ return seed * 2654435761 + 1 >>> 0 || 1;
7392
+ }
7393
+ function smoothstep(t) {
7394
+ return t * t * (3 - 2 * t);
7395
+ }
7396
+ function lerp(a, b, t) {
7397
+ return a + (b - a) * t;
7398
+ }
7399
+ function generateGradients(size, prngState) {
7400
+ const count = size * size;
7401
+ const gx = new Float32Array(count);
7402
+ const gy = new Float32Array(count);
7403
+ let s = prngState;
7404
+ for (let i = 0; i < count; i++) {
7405
+ s = xorshift32(s);
7406
+ const angle = (s >>> 0) / 4294967296 * Math.PI * 2;
7407
+ gx[i] = Math.cos(angle);
7408
+ gy[i] = Math.sin(angle);
7409
+ }
7410
+ return { gx, gy, state: s };
7411
+ }
7412
+ function perlinNoise(px, py, gridSize, gx, gy) {
7413
+ const gx0 = Math.floor(px) % gridSize;
7414
+ const gy0 = Math.floor(py) % gridSize;
7415
+ const gx1 = (gx0 + 1) % gridSize;
7416
+ const gy1 = (gy0 + 1) % gridSize;
7417
+ const fx = px - Math.floor(px);
7418
+ const fy = py - Math.floor(py);
7419
+ const sx = smoothstep(fx);
7420
+ const sy = smoothstep(fy);
7421
+ const dot = (ix, iy, dx, dy) => {
7422
+ const idx = iy * gridSize + ix;
7423
+ return gx[idx] * dx + gy[idx] * dy;
7424
+ };
7425
+ const n00 = dot(gx0, gy0, fx, fy);
7426
+ const n10 = dot(gx1, gy0, fx - 1, fy);
7427
+ const n01 = dot(gx0, gy1, fx, fy - 1);
7428
+ const n11 = dot(gx1, gy1, fx - 1, fy - 1);
7429
+ return lerp(lerp(n00, n10, sx), lerp(n01, n11, sx), sy);
7430
+ }
7431
+ function layeredNoise(x, y, octaves, gridSize, gx, gy) {
7432
+ let value = 0;
7433
+ let amplitude = 1;
7434
+ let frequency = 1;
7435
+ let maxAmplitude = 0;
7436
+ for (let o = 0; o < octaves; o++) {
7437
+ value += perlinNoise(x * frequency, y * frequency, gridSize * frequency, gx, gy) * amplitude;
7438
+ maxAmplitude += amplitude;
7439
+ amplitude *= 0.5;
7440
+ frequency *= 2;
7441
+ }
7442
+ return (value / maxAmplitude + 1) * 0.5;
7443
+ }
7444
+ function generateProceduralTile(style) {
7445
+ const gridSize = 8;
7446
+ const maxFreq = gridSize * (1 << style.detail - 1);
7447
+ const { gx, gy } = generateGradients(maxFreq, seedState(style.seed));
7448
+ const data = new Uint8ClampedArray(TILE_PX * TILE_PX * 4);
7449
+ for (let py = 0; py < TILE_PX; py++) {
7450
+ for (let px = 0; px < TILE_PX; px++) {
7451
+ const nx = px / TILE_PX * gridSize;
7452
+ const ny = py / TILE_PX * gridSize;
7453
+ const n2 = layeredNoise(nx, ny, style.detail, gridSize, gx, gy);
7454
+ const alpha = Math.round(n2 * style.opacity * 255);
7455
+ const idx = (py * TILE_PX + px) * 4;
7456
+ data[idx] = 255;
7457
+ data[idx + 1] = 255;
7458
+ data[idx + 2] = 255;
7459
+ data[idx + 3] = alpha;
7460
+ }
7461
+ }
7462
+ return { data, width: TILE_PX, height: TILE_PX };
7463
+ }
7464
+ var tileCache = /* @__PURE__ */ new Map();
7465
+ var MAX_CACHED_TILES = 16;
7466
+ function getCachedProceduralTile(style) {
7467
+ const key = `${style.opacity}\0${style.seed}\0${style.detail}`;
7468
+ const cached = tileCache.get(key);
7469
+ if (cached) return cached;
7470
+ const tile = generateProceduralTile(style);
7471
+ if (tileCache.size >= MAX_CACHED_TILES) {
7472
+ const oldest = tileCache.keys().next().value;
7473
+ if (oldest !== void 0) tileCache.delete(oldest);
7474
+ }
7475
+ tileCache.set(key, tile);
7476
+ return tile;
7477
+ }
7478
+ function clearProceduralTileCache() {
7479
+ tileCache.clear();
7480
+ }
7481
+
7328
7482
  // src/fog/fog-renderer.ts
7329
7483
  var DEFAULT_EDITOR_COLOR = "rgba(30, 40, 60, 0.45)";
7330
7484
  var DEFAULT_PLAYER_COLOR = "#0b1020";
7331
7485
  var FogRenderer = class {
7332
7486
  tileCache = /* @__PURE__ */ new Map();
7487
+ patternCache = /* @__PURE__ */ new Map();
7333
7488
  state = null;
7334
7489
  viewMode = "off";
7335
7490
  dirty = true;
7336
- editorColor;
7337
- playerColor;
7491
+ editorStyle;
7492
+ playerStyle;
7338
7493
  constructor(options = {}) {
7339
- this.editorColor = options.editorColor ?? DEFAULT_EDITOR_COLOR;
7340
- this.playerColor = options.playerColor ?? DEFAULT_PLAYER_COLOR;
7494
+ this.editorStyle = resolveFogStyle(
7495
+ options.editorStyle,
7496
+ options.editorColor,
7497
+ DEFAULT_EDITOR_COLOR
7498
+ );
7499
+ this.playerStyle = resolveFogStyle(
7500
+ options.playerStyle,
7501
+ options.playerColor,
7502
+ DEFAULT_PLAYER_COLOR
7503
+ );
7341
7504
  }
7342
7505
  setState(state) {
7343
7506
  this.state = state;
@@ -7363,12 +7526,23 @@ var FogRenderer = class {
7363
7526
  isVisible() {
7364
7527
  return this.viewMode !== "off" && this.state !== null;
7365
7528
  }
7529
+ getResolvedStyle(mode) {
7530
+ return mode === "editor" ? this.editorStyle : this.playerStyle;
7531
+ }
7366
7532
  render(ctx, camera, viewportWidth, viewportHeight, _dpr) {
7367
7533
  if (!this.state || this.viewMode === "off") return;
7368
7534
  const def = this.state.definition;
7369
7535
  const cellSize = def.cellSize;
7370
7536
  const tileWorldSize = FOG_TILE_CELLS * cellSize;
7371
- const color = this.viewMode === "editor" ? this.editorColor : this.playerColor;
7537
+ const mode = this.viewMode === "editor" ? "editor" : "player";
7538
+ const style = this.getResolvedStyle(mode);
7539
+ const proceduralStyle = style.kind === "procedural" ? style : null;
7540
+ const color = style.kind === "procedural" ? normalizeCanvasColor(
7541
+ ctx,
7542
+ style.backdrop,
7543
+ mode === "player" ? DEFAULT_PLAYER_COLOR : DEFAULT_EDITOR_COLOR
7544
+ ) : style.color;
7545
+ const safetyColor = proceduralStyle && mode === "player" ? DEFAULT_PLAYER_COLOR : null;
7372
7546
  const worldBounds = getVisibleWorld(camera, viewportWidth, viewportHeight);
7373
7547
  const minTX = Math.floor(Math.max(def.bounds.x, worldBounds.x) / tileWorldSize);
7374
7548
  const minTY = Math.floor(Math.max(def.bounds.y, worldBounds.y) / tileWorldSize);
@@ -7399,7 +7573,22 @@ var FogRenderer = class {
7399
7573
  const clipR = Math.min(tileWorldX + tileWorldSize, def.bounds.x + def.bounds.w);
7400
7574
  const clipB = Math.min(tileWorldY + tileWorldSize, def.bounds.y + def.bounds.h);
7401
7575
  if (clipR > clipX && clipB > clipY) {
7576
+ if (safetyColor) {
7577
+ ctx.fillStyle = safetyColor;
7578
+ ctx.fillRect(clipX, clipY, clipR - clipX, clipB - clipY);
7579
+ }
7580
+ ctx.fillStyle = color;
7402
7581
  ctx.fillRect(clipX, clipY, clipR - clipX, clipB - clipY);
7582
+ if (proceduralStyle) {
7583
+ this.paintProceduralOverlay(
7584
+ ctx,
7585
+ proceduralStyle,
7586
+ clipX,
7587
+ clipY,
7588
+ clipR - clipX,
7589
+ clipB - clipY
7590
+ );
7591
+ }
7403
7592
  }
7404
7593
  continue;
7405
7594
  }
@@ -7407,18 +7596,33 @@ var FogRenderer = class {
7407
7596
  continue;
7408
7597
  }
7409
7598
  if (data) {
7599
+ if (safetyColor) this.renderTile(ctx, data, tx, ty, def, safetyColor);
7410
7600
  this.renderTile(ctx, data, tx, ty, def, color);
7601
+ if (proceduralStyle) {
7602
+ this.renderTileProceduralOverlay(ctx, data, tx, ty, def, proceduralStyle);
7603
+ }
7411
7604
  }
7412
7605
  }
7413
7606
  }
7414
7607
  ctx.restore();
7415
7608
  this.dirty = false;
7416
7609
  }
7417
- renderForExport(ctx, state, mode, color) {
7610
+ renderForExport(ctx, state, mode, color, style) {
7418
7611
  const def = state.definition;
7419
7612
  const cellSize = def.cellSize;
7420
7613
  const tileWorldSize = FOG_TILE_CELLS * cellSize;
7421
- const fogColor = color ?? (mode === "editor" ? this.editorColor : this.playerColor);
7614
+ const resolved = style ? resolveFogStyle(
7615
+ style,
7616
+ void 0,
7617
+ mode === "editor" ? DEFAULT_EDITOR_COLOR : DEFAULT_PLAYER_COLOR
7618
+ ) : this.getResolvedStyle(mode);
7619
+ const proceduralStyle = !color && resolved.kind === "procedural" ? resolved : null;
7620
+ const fogColor = color ?? (proceduralStyle ? normalizeCanvasColor(
7621
+ ctx,
7622
+ proceduralStyle.backdrop,
7623
+ mode === "player" ? DEFAULT_PLAYER_COLOR : DEFAULT_EDITOR_COLOR
7624
+ ) : resolved.kind === "solid" ? resolved.color : resolved.backdrop);
7625
+ const safetyColor = proceduralStyle && mode === "player" ? DEFAULT_PLAYER_COLOR : null;
7422
7626
  const baseCovered = def.base === "covered";
7423
7627
  const tileMap = /* @__PURE__ */ new Map();
7424
7628
  for (const tile of state.tiles) {
@@ -7441,20 +7645,129 @@ var FogRenderer = class {
7441
7645
  const clipR = Math.min(tileWorldX + tileWorldSize, def.bounds.x + def.bounds.w);
7442
7646
  const clipB = Math.min(tileWorldY + tileWorldSize, def.bounds.y + def.bounds.h);
7443
7647
  if (clipR > clipX && clipB > clipY) {
7648
+ if (safetyColor) {
7649
+ ctx.fillStyle = safetyColor;
7650
+ ctx.fillRect(clipX, clipY, clipR - clipX, clipB - clipY);
7651
+ }
7652
+ ctx.fillStyle = fogColor;
7444
7653
  ctx.fillRect(clipX, clipY, clipR - clipX, clipB - clipY);
7654
+ if (proceduralStyle) {
7655
+ this.paintProceduralOverlay(
7656
+ ctx,
7657
+ proceduralStyle,
7658
+ clipX,
7659
+ clipY,
7660
+ clipR - clipX,
7661
+ clipB - clipY
7662
+ );
7663
+ }
7445
7664
  }
7446
7665
  continue;
7447
7666
  }
7448
7667
  if (data) {
7668
+ if (safetyColor) this.renderTileForExport(ctx, data, tx, ty, def, safetyColor);
7449
7669
  this.renderTileForExport(ctx, data, tx, ty, def, fogColor);
7670
+ if (proceduralStyle) {
7671
+ this.renderTileProceduralOverlay(ctx, data, tx, ty, def, proceduralStyle);
7672
+ }
7450
7673
  }
7451
7674
  }
7452
7675
  }
7453
7676
  }
7454
7677
  dispose() {
7455
7678
  this.tileCache.clear();
7679
+ this.patternCache.clear();
7680
+ clearProceduralTileCache();
7456
7681
  this.state = null;
7457
7682
  }
7683
+ getOrCreatePattern(ctx, style, worldScale) {
7684
+ const key = `${style.backdrop}\0${style.tint}\0${style.opacity}\0${style.scale}\0${style.seed}\0${style.detail}\0${worldScale}`;
7685
+ const cached = this.patternCache.get(key);
7686
+ if (cached !== void 0) return cached;
7687
+ let pattern = null;
7688
+ try {
7689
+ const tileData = getCachedProceduralTile(style);
7690
+ pattern = this.createPatternFromTileData(ctx, tileData, style, worldScale);
7691
+ } catch {
7692
+ }
7693
+ if (this.patternCache.size >= 32) {
7694
+ const oldest = this.patternCache.keys().next().value;
7695
+ if (oldest !== void 0) this.patternCache.delete(oldest);
7696
+ }
7697
+ this.patternCache.set(key, pattern);
7698
+ return pattern;
7699
+ }
7700
+ createPatternFromTileData(ctx, tileData, style, worldScale) {
7701
+ if (typeof document === "undefined") return null;
7702
+ const sourceCanvas = document.createElement("canvas");
7703
+ sourceCanvas.width = tileData.width;
7704
+ sourceCanvas.height = tileData.height;
7705
+ const sourceCtx = sourceCanvas.getContext("2d");
7706
+ if (!sourceCtx) return null;
7707
+ const imageData = new ImageData(
7708
+ new Uint8ClampedArray(tileData.data),
7709
+ tileData.width,
7710
+ tileData.height
7711
+ );
7712
+ sourceCtx.putImageData(imageData, 0, 0);
7713
+ sourceCtx.globalCompositeOperation = "source-in";
7714
+ sourceCtx.fillStyle = normalizeCanvasColor(sourceCtx, style.tint, "#ffffff");
7715
+ sourceCtx.fillRect(0, 0, tileData.width, tileData.height);
7716
+ sourceCtx.globalCompositeOperation = "source-over";
7717
+ const patternScale = style.scale * worldScale / tileData.width;
7718
+ const pattern = ctx.createPattern(sourceCanvas, "repeat");
7719
+ if (pattern && typeof pattern.setTransform === "function" && typeof DOMMatrix !== "undefined") {
7720
+ try {
7721
+ pattern.setTransform(new DOMMatrix([patternScale, 0, 0, patternScale, 0, 0]));
7722
+ return pattern;
7723
+ } catch {
7724
+ }
7725
+ }
7726
+ const patternPx = Math.round(style.scale * worldScale);
7727
+ if (patternPx < 1) return null;
7728
+ const patternCanvas = document.createElement("canvas");
7729
+ patternCanvas.width = patternPx;
7730
+ patternCanvas.height = patternPx;
7731
+ const patternCtx = patternCanvas.getContext("2d");
7732
+ if (!patternCtx) return null;
7733
+ patternCtx.drawImage(sourceCanvas, 0, 0, patternPx, patternPx);
7734
+ return ctx.createPattern(patternCanvas, "repeat");
7735
+ }
7736
+ paintProceduralOverlay(ctx, style, x, y, w, h) {
7737
+ const pattern = this.getOrCreatePattern(ctx, style, 1);
7738
+ if (!pattern) return;
7739
+ ctx.save();
7740
+ ctx.fillStyle = pattern;
7741
+ ctx.fillRect(x, y, w, h);
7742
+ ctx.restore();
7743
+ }
7744
+ renderTileProceduralOverlay(ctx, data, tx, ty, def, style) {
7745
+ const cellSize = def.cellSize;
7746
+ const tileWorldX = tx * FOG_TILE_CELLS * cellSize;
7747
+ const tileWorldY = ty * FOG_TILE_CELLS * cellSize;
7748
+ const pattern = this.getOrCreatePattern(ctx, style, 1);
7749
+ if (!pattern) return;
7750
+ const bytes = decodeBase64(data);
7751
+ ctx.save();
7752
+ ctx.fillStyle = pattern;
7753
+ for (let row = 0; row < FOG_TILE_CELLS; row++) {
7754
+ for (let col = 0; col < FOG_TILE_CELLS; col++) {
7755
+ const cellWorldX = tileWorldX + col * cellSize;
7756
+ const cellWorldY = tileWorldY + row * cellSize;
7757
+ if (cellWorldX < def.bounds.x || cellWorldY < def.bounds.y || cellWorldX >= def.bounds.x + def.bounds.w || cellWorldY >= def.bounds.y + def.bounds.h) {
7758
+ continue;
7759
+ }
7760
+ const index = row * FOG_TILE_CELLS + col;
7761
+ const byteIndex = index >> 3;
7762
+ const bitIndex = 7 - (index & 7);
7763
+ const revealed = (bytes[byteIndex] >> bitIndex & 1) === 1;
7764
+ if (!revealed) {
7765
+ ctx.fillRect(cellWorldX, cellWorldY, cellSize, cellSize);
7766
+ }
7767
+ }
7768
+ }
7769
+ ctx.restore();
7770
+ }
7458
7771
  tileRaster(data, color) {
7459
7772
  const key = `${color}\0${data}`;
7460
7773
  const cached = this.tileCache.get(key);
@@ -7538,6 +7851,22 @@ function getVisibleWorld(camera, viewportWidth, viewportHeight) {
7538
7851
  h: bottomRight.y - topLeft.y
7539
7852
  };
7540
7853
  }
7854
+ function normalizeCanvasColor(ctx, value, fallback) {
7855
+ const previous = ctx.fillStyle;
7856
+ try {
7857
+ ctx.fillStyle = "#010203";
7858
+ ctx.fillStyle = value;
7859
+ const first = ctx.fillStyle;
7860
+ ctx.fillStyle = "#040506";
7861
+ ctx.fillStyle = value;
7862
+ const second = ctx.fillStyle;
7863
+ return typeof first === "string" && first === second ? first : fallback;
7864
+ } catch {
7865
+ return fallback;
7866
+ } finally {
7867
+ ctx.fillStyle = previous;
7868
+ }
7869
+ }
7541
7870
 
7542
7871
  // src/canvas/export-image.ts
7543
7872
  var DEFAULT_IMAGE_TIMEOUT_MS = 1e4;
@@ -7953,7 +8282,13 @@ async function exportImage(store, options = {}, layerManager) {
7953
8282
  }
7954
8283
  if (options.fog) {
7955
8284
  const fogRenderer = new FogRenderer();
7956
- fogRenderer.renderForExport(ctx, options.fog.state, options.fog.mode, options.fog.color);
8285
+ fogRenderer.renderForExport(
8286
+ ctx,
8287
+ options.fog.state,
8288
+ options.fog.mode,
8289
+ options.fog.color,
8290
+ options.fog.style
8291
+ );
7957
8292
  }
7958
8293
  const mimeType = format === "jpeg" ? "image/jpeg" : "image/png";
7959
8294
  return new Promise((resolve) => {
@@ -8347,7 +8682,13 @@ async function exportSvg(store, options = {}, layerManager) {
8347
8682
  if (fogCtx) {
8348
8683
  fogCtx.translate(-bounds.x, -bounds.y);
8349
8684
  const fogRenderer = new FogRenderer();
8350
- fogRenderer.renderForExport(fogCtx, fogState, options.fog.mode, options.fog.color);
8685
+ fogRenderer.renderForExport(
8686
+ fogCtx,
8687
+ fogState,
8688
+ options.fog.mode,
8689
+ options.fog.color,
8690
+ options.fog.style
8691
+ );
8351
8692
  try {
8352
8693
  const fogDataUri = fogCanvas.toDataURL("image/png");
8353
8694
  if (fogDataUri.startsWith("data:")) {
@@ -11342,26 +11683,35 @@ var Viewport = class _Viewport {
11342
11683
  const expected = base.expectedCanvasTypes ? /* @__PURE__ */ new Set([...declared, ...base.expectedCanvasTypes]) : declared;
11343
11684
  return { ...base, htmlPainters: registry, expectedCanvasTypes: expected };
11344
11685
  }
11345
- async exportImage(options) {
11346
- const opts = this.withHtmlDefaults(options);
11347
- if (opts.fog === void 0 && this.fogRenderer.isVisible()) {
11348
- const state = this.fogManager.getState();
11349
- if (state) {
11350
- const mode = this.fogRenderer.getViewMode();
11351
- opts.fog = { state, mode };
11352
- }
11686
+ /**
11687
+ * Carry constructor-configured fog presentation into both implicit exports and
11688
+ * explicit state/mode exports. Explicit style and legacy color overrides win.
11689
+ */
11690
+ withFogDefaults(options) {
11691
+ const fog = options.fog;
11692
+ if (fog === false) return options;
11693
+ if (fog !== void 0) {
11694
+ if (fog.style !== void 0 || fog.color !== void 0) return options;
11695
+ return {
11696
+ ...options,
11697
+ fog: { ...fog, style: this.fogRenderer.getResolvedStyle(fog.mode) }
11698
+ };
11353
11699
  }
11700
+ if (!this.fogRenderer.isVisible()) return options;
11701
+ const state = this.fogManager.getState();
11702
+ if (!state) return options;
11703
+ const mode = this.fogRenderer.getViewMode();
11704
+ return {
11705
+ ...options,
11706
+ fog: { state, mode, style: this.fogRenderer.getResolvedStyle(mode) }
11707
+ };
11708
+ }
11709
+ async exportImage(options) {
11710
+ const opts = this.withFogDefaults(this.withHtmlDefaults(options));
11354
11711
  return exportImage(this.store, opts, this.layerManager);
11355
11712
  }
11356
11713
  async exportSVG(options) {
11357
- const opts = this.withHtmlDefaults(options);
11358
- if (opts.fog === void 0 && this.fogRenderer.isVisible()) {
11359
- const state = this.fogManager.getState();
11360
- if (state) {
11361
- const mode = this.fogRenderer.getViewMode();
11362
- opts.fog = { state, mode };
11363
- }
11364
- }
11714
+ const opts = this.withFogDefaults(this.withHtmlDefaults(options));
11365
11715
  return exportSvg(this.store, opts, this.layerManager);
11366
11716
  }
11367
11717
  loadState(state) {
@@ -12995,15 +13345,15 @@ function applyCameraView(camera, view, canvasW, canvasH) {
12995
13345
  var DEFAULT_DURATION_MS3 = 400;
12996
13346
  var FRAMED_EPSILON = 1e-6;
12997
13347
  var easeOutCubic2 = (t) => 1 - Math.pow(1 - t, 3);
12998
- function lerp(a, b, k) {
13348
+ function lerp2(a, b, k) {
12999
13349
  return a + (b - a) * k;
13000
13350
  }
13001
13351
  function lerpView(from, to, k) {
13002
13352
  return {
13003
- x: lerp(from.x, to.x, k),
13004
- y: lerp(from.y, to.y, k),
13005
- w: lerp(from.w, to.w, k),
13006
- h: lerp(from.h, to.h, k)
13353
+ x: lerp2(from.x, to.x, k),
13354
+ y: lerp2(from.y, to.y, k),
13355
+ w: lerp2(from.w, to.w, k),
13356
+ h: lerp2(from.h, to.h, k)
13007
13357
  };
13008
13358
  }
13009
13359
  function viewsClose(a, b) {
@@ -14257,7 +14607,7 @@ var PencilTool = class {
14257
14607
  };
14258
14608
 
14259
14609
  // src/elements/stroke-erase.ts
14260
- function lerp2(a, b, t) {
14610
+ function lerp3(a, b, t) {
14261
14611
  return {
14262
14612
  x: a.x + (b.x - a.x) * t,
14263
14613
  y: a.y + (b.y - a.y) * t,
@@ -14316,13 +14666,13 @@ function erasePoints(points, eraser, radius) {
14316
14666
  erased = true;
14317
14667
  if (tLo > 0) {
14318
14668
  if (current.length === 0) current.push(a);
14319
- current.push(lerp2(a, b, tLo));
14669
+ current.push(lerp3(a, b, tLo));
14320
14670
  flush();
14321
14671
  } else {
14322
14672
  flush();
14323
14673
  }
14324
14674
  if (tHi < 1) {
14325
- current = [lerp2(a, b, tHi), b];
14675
+ current = [lerp3(a, b, tHi), b];
14326
14676
  }
14327
14677
  }
14328
14678
  flush();
@@ -16838,7 +17188,7 @@ var FogTool = class {
16838
17188
  };
16839
17189
 
16840
17190
  // src/index.ts
16841
- var VERSION = "0.66.0";
17191
+ var VERSION = "0.67.0";
16842
17192
  // Annotate the CommonJS export names for ESM import in node:
16843
17193
  0 && (module.exports = {
16844
17194
  AWARENESS_MAX_SELECTION,
@@ -16947,6 +17297,7 @@ var VERSION = "0.66.0";
16947
17297
  isPingPresence,
16948
17298
  pathDistanceCells,
16949
17299
  recommendedFogCellSize,
17300
+ resolveFogStyle,
16950
17301
  resolveHtmlRouting,
16951
17302
  setFontSize,
16952
17303
  smartSnap,