@fieldnotes/core 0.66.0 → 0.68.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.js CHANGED
@@ -7174,19 +7174,196 @@ async function renderHtmlElements(elements, options) {
7174
7174
  return sources;
7175
7175
  }
7176
7176
 
7177
+ // src/fog/fog-style.ts
7178
+ var DEFAULT_PROCEDURAL_OPACITY = 0.6;
7179
+ var DEFAULT_PROCEDURAL_SCALE = 256;
7180
+ var DEFAULT_PROCEDURAL_SEED = 0;
7181
+ var DEFAULT_PROCEDURAL_DETAIL = 2;
7182
+ var DEFAULT_PROCEDURAL_TINT = "#ffffff";
7183
+ var MIN_SCALE = 64;
7184
+ var MAX_SCALE = 1024;
7185
+ var MIN_DETAIL = 1;
7186
+ var MAX_DETAIL = 4;
7187
+ var MAX_SEED = 65535;
7188
+ function clamp(value, min, max) {
7189
+ return Math.max(min, Math.min(max, value));
7190
+ }
7191
+ function finiteOrDefault(value, defaultValue) {
7192
+ return value !== void 0 && Number.isFinite(value) ? value : defaultValue;
7193
+ }
7194
+ function resolveFogStyle(style, legacyColor, defaultColor) {
7195
+ if (style && style.kind === "procedural") {
7196
+ const opacity = clamp(finiteOrDefault(style.opacity, DEFAULT_PROCEDURAL_OPACITY), 0, 1);
7197
+ const scale = clamp(
7198
+ finiteOrDefault(style.scale, DEFAULT_PROCEDURAL_SCALE),
7199
+ MIN_SCALE,
7200
+ MAX_SCALE
7201
+ );
7202
+ const seed = clamp(
7203
+ Math.floor(finiteOrDefault(style.seed, DEFAULT_PROCEDURAL_SEED)),
7204
+ 0,
7205
+ MAX_SEED
7206
+ );
7207
+ const detail = clamp(
7208
+ Math.floor(finiteOrDefault(style.detail, DEFAULT_PROCEDURAL_DETAIL)),
7209
+ MIN_DETAIL,
7210
+ MAX_DETAIL
7211
+ );
7212
+ return {
7213
+ kind: "procedural",
7214
+ backdrop: style.backdrop,
7215
+ // `tint` is required for typed callers. Keep a visible runtime fallback for
7216
+ // untyped/older JavaScript hosts instead of producing a flat same-color overlay.
7217
+ tint: style.tint || DEFAULT_PROCEDURAL_TINT,
7218
+ opacity,
7219
+ scale,
7220
+ seed,
7221
+ detail
7222
+ };
7223
+ }
7224
+ if (style && (!style.kind || style.kind === "solid")) {
7225
+ return { kind: "solid", color: style.color };
7226
+ }
7227
+ return { kind: "solid", color: legacyColor ?? defaultColor };
7228
+ }
7229
+
7230
+ // src/fog/fog-procedural-tile.ts
7231
+ var TILE_PX = 128;
7232
+ function xorshift32(state) {
7233
+ state ^= state << 13;
7234
+ state ^= state >>> 17;
7235
+ state ^= state << 5;
7236
+ return state >>> 0;
7237
+ }
7238
+ function seedState(seed) {
7239
+ return seed * 2654435761 + 1 >>> 0 || 1;
7240
+ }
7241
+ function smoothstep(t) {
7242
+ return t * t * (3 - 2 * t);
7243
+ }
7244
+ function lerp(a, b, t) {
7245
+ return a + (b - a) * t;
7246
+ }
7247
+ function generateGradients(size, prngState) {
7248
+ const count = size * size;
7249
+ const gx = new Float32Array(count);
7250
+ const gy = new Float32Array(count);
7251
+ let s = prngState;
7252
+ for (let i = 0; i < count; i++) {
7253
+ s = xorshift32(s);
7254
+ const angle = (s >>> 0) / 4294967296 * Math.PI * 2;
7255
+ gx[i] = Math.cos(angle);
7256
+ gy[i] = Math.sin(angle);
7257
+ }
7258
+ return { gx, gy, state: s };
7259
+ }
7260
+ function perlinNoise(px, py, gridSize, gx, gy) {
7261
+ const gx0 = Math.floor(px) % gridSize;
7262
+ const gy0 = Math.floor(py) % gridSize;
7263
+ const gx1 = (gx0 + 1) % gridSize;
7264
+ const gy1 = (gy0 + 1) % gridSize;
7265
+ const fx = px - Math.floor(px);
7266
+ const fy = py - Math.floor(py);
7267
+ const sx = smoothstep(fx);
7268
+ const sy = smoothstep(fy);
7269
+ const dot = (ix, iy, dx, dy) => {
7270
+ const idx = iy * gridSize + ix;
7271
+ return gx[idx] * dx + gy[idx] * dy;
7272
+ };
7273
+ const n00 = dot(gx0, gy0, fx, fy);
7274
+ const n10 = dot(gx1, gy0, fx - 1, fy);
7275
+ const n01 = dot(gx0, gy1, fx, fy - 1);
7276
+ const n11 = dot(gx1, gy1, fx - 1, fy - 1);
7277
+ return lerp(lerp(n00, n10, sx), lerp(n01, n11, sx), sy);
7278
+ }
7279
+ function layeredNoise(x, y, octaves, gridSize, gx, gy) {
7280
+ let value = 0;
7281
+ let amplitude = 1;
7282
+ let frequency = 1;
7283
+ let maxAmplitude = 0;
7284
+ for (let o = 0; o < octaves; o++) {
7285
+ value += perlinNoise(x * frequency, y * frequency, gridSize * frequency, gx, gy) * amplitude;
7286
+ maxAmplitude += amplitude;
7287
+ amplitude *= 0.5;
7288
+ frequency *= 2;
7289
+ }
7290
+ return (value / maxAmplitude + 1) * 0.5;
7291
+ }
7292
+ function generateProceduralTile(style) {
7293
+ const gridSize = 8;
7294
+ const maxFreq = gridSize * (1 << style.detail - 1);
7295
+ const { gx, gy } = generateGradients(maxFreq, seedState(style.seed));
7296
+ const data = new Uint8ClampedArray(TILE_PX * TILE_PX * 4);
7297
+ for (let py = 0; py < TILE_PX; py++) {
7298
+ for (let px = 0; px < TILE_PX; px++) {
7299
+ const nx = px / TILE_PX * gridSize;
7300
+ const ny = py / TILE_PX * gridSize;
7301
+ const n2 = layeredNoise(nx, ny, style.detail, gridSize, gx, gy);
7302
+ const alpha = Math.round(n2 * style.opacity * 255);
7303
+ const idx = (py * TILE_PX + px) * 4;
7304
+ data[idx] = 255;
7305
+ data[idx + 1] = 255;
7306
+ data[idx + 2] = 255;
7307
+ data[idx + 3] = alpha;
7308
+ }
7309
+ }
7310
+ return { data, width: TILE_PX, height: TILE_PX };
7311
+ }
7312
+ var tileCache = /* @__PURE__ */ new Map();
7313
+ var MAX_CACHED_TILES = 16;
7314
+ function getCachedProceduralTile(style) {
7315
+ const key = `${style.opacity}\0${style.seed}\0${style.detail}`;
7316
+ const cached = tileCache.get(key);
7317
+ if (cached) return cached;
7318
+ const tile = generateProceduralTile(style);
7319
+ if (tileCache.size >= MAX_CACHED_TILES) {
7320
+ const oldest = tileCache.keys().next().value;
7321
+ if (oldest !== void 0) tileCache.delete(oldest);
7322
+ }
7323
+ tileCache.set(key, tile);
7324
+ return tile;
7325
+ }
7326
+ function clearProceduralTileCache() {
7327
+ tileCache.clear();
7328
+ }
7329
+
7177
7330
  // src/fog/fog-renderer.ts
7178
7331
  var DEFAULT_EDITOR_COLOR = "rgba(30, 40, 60, 0.45)";
7179
7332
  var DEFAULT_PLAYER_COLOR = "#0b1020";
7180
7333
  var FogRenderer = class {
7181
7334
  tileCache = /* @__PURE__ */ new Map();
7335
+ patternCache = /* @__PURE__ */ new Map();
7182
7336
  state = null;
7183
7337
  viewMode = "off";
7184
7338
  dirty = true;
7185
- editorColor;
7186
- playerColor;
7339
+ editorStyle;
7340
+ playerStyle;
7187
7341
  constructor(options = {}) {
7188
- this.editorColor = options.editorColor ?? DEFAULT_EDITOR_COLOR;
7189
- this.playerColor = options.playerColor ?? DEFAULT_PLAYER_COLOR;
7342
+ this.editorStyle = resolveFogStyle(
7343
+ options.editorStyle,
7344
+ options.editorColor,
7345
+ DEFAULT_EDITOR_COLOR
7346
+ );
7347
+ this.playerStyle = resolveFogStyle(
7348
+ options.playerStyle,
7349
+ options.playerColor,
7350
+ DEFAULT_PLAYER_COLOR
7351
+ );
7352
+ }
7353
+ setOptions(options) {
7354
+ this.editorStyle = resolveFogStyle(
7355
+ options.editorStyle,
7356
+ options.editorColor,
7357
+ DEFAULT_EDITOR_COLOR
7358
+ );
7359
+ this.playerStyle = resolveFogStyle(
7360
+ options.playerStyle,
7361
+ options.playerColor,
7362
+ DEFAULT_PLAYER_COLOR
7363
+ );
7364
+ this.tileCache.clear();
7365
+ this.patternCache.clear();
7366
+ this.dirty = true;
7190
7367
  }
7191
7368
  setState(state) {
7192
7369
  this.state = state;
@@ -7212,12 +7389,23 @@ var FogRenderer = class {
7212
7389
  isVisible() {
7213
7390
  return this.viewMode !== "off" && this.state !== null;
7214
7391
  }
7392
+ getResolvedStyle(mode) {
7393
+ return mode === "editor" ? this.editorStyle : this.playerStyle;
7394
+ }
7215
7395
  render(ctx, camera, viewportWidth, viewportHeight, _dpr) {
7216
7396
  if (!this.state || this.viewMode === "off") return;
7217
7397
  const def = this.state.definition;
7218
7398
  const cellSize = def.cellSize;
7219
7399
  const tileWorldSize = FOG_TILE_CELLS * cellSize;
7220
- const color = this.viewMode === "editor" ? this.editorColor : this.playerColor;
7400
+ const mode = this.viewMode === "editor" ? "editor" : "player";
7401
+ const style = this.getResolvedStyle(mode);
7402
+ const proceduralStyle = style.kind === "procedural" ? style : null;
7403
+ const color = style.kind === "procedural" ? normalizeCanvasColor(
7404
+ ctx,
7405
+ style.backdrop,
7406
+ mode === "player" ? DEFAULT_PLAYER_COLOR : DEFAULT_EDITOR_COLOR
7407
+ ) : style.color;
7408
+ const safetyColor = proceduralStyle && mode === "player" ? DEFAULT_PLAYER_COLOR : null;
7221
7409
  const worldBounds = getVisibleWorld(camera, viewportWidth, viewportHeight);
7222
7410
  const minTX = Math.floor(Math.max(def.bounds.x, worldBounds.x) / tileWorldSize);
7223
7411
  const minTY = Math.floor(Math.max(def.bounds.y, worldBounds.y) / tileWorldSize);
@@ -7248,7 +7436,22 @@ var FogRenderer = class {
7248
7436
  const clipR = Math.min(tileWorldX + tileWorldSize, def.bounds.x + def.bounds.w);
7249
7437
  const clipB = Math.min(tileWorldY + tileWorldSize, def.bounds.y + def.bounds.h);
7250
7438
  if (clipR > clipX && clipB > clipY) {
7439
+ if (safetyColor) {
7440
+ ctx.fillStyle = safetyColor;
7441
+ ctx.fillRect(clipX, clipY, clipR - clipX, clipB - clipY);
7442
+ }
7443
+ ctx.fillStyle = color;
7251
7444
  ctx.fillRect(clipX, clipY, clipR - clipX, clipB - clipY);
7445
+ if (proceduralStyle) {
7446
+ this.paintProceduralOverlay(
7447
+ ctx,
7448
+ proceduralStyle,
7449
+ clipX,
7450
+ clipY,
7451
+ clipR - clipX,
7452
+ clipB - clipY
7453
+ );
7454
+ }
7252
7455
  }
7253
7456
  continue;
7254
7457
  }
@@ -7256,18 +7459,33 @@ var FogRenderer = class {
7256
7459
  continue;
7257
7460
  }
7258
7461
  if (data) {
7462
+ if (safetyColor) this.renderTile(ctx, data, tx, ty, def, safetyColor);
7259
7463
  this.renderTile(ctx, data, tx, ty, def, color);
7464
+ if (proceduralStyle) {
7465
+ this.renderTileProceduralOverlay(ctx, data, tx, ty, def, proceduralStyle);
7466
+ }
7260
7467
  }
7261
7468
  }
7262
7469
  }
7263
7470
  ctx.restore();
7264
7471
  this.dirty = false;
7265
7472
  }
7266
- renderForExport(ctx, state, mode, color) {
7473
+ renderForExport(ctx, state, mode, color, style) {
7267
7474
  const def = state.definition;
7268
7475
  const cellSize = def.cellSize;
7269
7476
  const tileWorldSize = FOG_TILE_CELLS * cellSize;
7270
- const fogColor = color ?? (mode === "editor" ? this.editorColor : this.playerColor);
7477
+ const resolved = style ? resolveFogStyle(
7478
+ style,
7479
+ void 0,
7480
+ mode === "editor" ? DEFAULT_EDITOR_COLOR : DEFAULT_PLAYER_COLOR
7481
+ ) : this.getResolvedStyle(mode);
7482
+ const proceduralStyle = !color && resolved.kind === "procedural" ? resolved : null;
7483
+ const fogColor = color ?? (proceduralStyle ? normalizeCanvasColor(
7484
+ ctx,
7485
+ proceduralStyle.backdrop,
7486
+ mode === "player" ? DEFAULT_PLAYER_COLOR : DEFAULT_EDITOR_COLOR
7487
+ ) : resolved.kind === "solid" ? resolved.color : resolved.backdrop);
7488
+ const safetyColor = proceduralStyle && mode === "player" ? DEFAULT_PLAYER_COLOR : null;
7271
7489
  const baseCovered = def.base === "covered";
7272
7490
  const tileMap = /* @__PURE__ */ new Map();
7273
7491
  for (const tile of state.tiles) {
@@ -7290,20 +7508,129 @@ var FogRenderer = class {
7290
7508
  const clipR = Math.min(tileWorldX + tileWorldSize, def.bounds.x + def.bounds.w);
7291
7509
  const clipB = Math.min(tileWorldY + tileWorldSize, def.bounds.y + def.bounds.h);
7292
7510
  if (clipR > clipX && clipB > clipY) {
7511
+ if (safetyColor) {
7512
+ ctx.fillStyle = safetyColor;
7513
+ ctx.fillRect(clipX, clipY, clipR - clipX, clipB - clipY);
7514
+ }
7515
+ ctx.fillStyle = fogColor;
7293
7516
  ctx.fillRect(clipX, clipY, clipR - clipX, clipB - clipY);
7517
+ if (proceduralStyle) {
7518
+ this.paintProceduralOverlay(
7519
+ ctx,
7520
+ proceduralStyle,
7521
+ clipX,
7522
+ clipY,
7523
+ clipR - clipX,
7524
+ clipB - clipY
7525
+ );
7526
+ }
7294
7527
  }
7295
7528
  continue;
7296
7529
  }
7297
7530
  if (data) {
7531
+ if (safetyColor) this.renderTileForExport(ctx, data, tx, ty, def, safetyColor);
7298
7532
  this.renderTileForExport(ctx, data, tx, ty, def, fogColor);
7533
+ if (proceduralStyle) {
7534
+ this.renderTileProceduralOverlay(ctx, data, tx, ty, def, proceduralStyle);
7535
+ }
7299
7536
  }
7300
7537
  }
7301
7538
  }
7302
7539
  }
7303
7540
  dispose() {
7304
7541
  this.tileCache.clear();
7542
+ this.patternCache.clear();
7543
+ clearProceduralTileCache();
7305
7544
  this.state = null;
7306
7545
  }
7546
+ getOrCreatePattern(ctx, style, worldScale) {
7547
+ const key = `${style.backdrop}\0${style.tint}\0${style.opacity}\0${style.scale}\0${style.seed}\0${style.detail}\0${worldScale}`;
7548
+ const cached = this.patternCache.get(key);
7549
+ if (cached !== void 0) return cached;
7550
+ let pattern = null;
7551
+ try {
7552
+ const tileData = getCachedProceduralTile(style);
7553
+ pattern = this.createPatternFromTileData(ctx, tileData, style, worldScale);
7554
+ } catch {
7555
+ }
7556
+ if (this.patternCache.size >= 32) {
7557
+ const oldest = this.patternCache.keys().next().value;
7558
+ if (oldest !== void 0) this.patternCache.delete(oldest);
7559
+ }
7560
+ this.patternCache.set(key, pattern);
7561
+ return pattern;
7562
+ }
7563
+ createPatternFromTileData(ctx, tileData, style, worldScale) {
7564
+ if (typeof document === "undefined") return null;
7565
+ const sourceCanvas = document.createElement("canvas");
7566
+ sourceCanvas.width = tileData.width;
7567
+ sourceCanvas.height = tileData.height;
7568
+ const sourceCtx = sourceCanvas.getContext("2d");
7569
+ if (!sourceCtx) return null;
7570
+ const imageData = new ImageData(
7571
+ new Uint8ClampedArray(tileData.data),
7572
+ tileData.width,
7573
+ tileData.height
7574
+ );
7575
+ sourceCtx.putImageData(imageData, 0, 0);
7576
+ sourceCtx.globalCompositeOperation = "source-in";
7577
+ sourceCtx.fillStyle = normalizeCanvasColor(sourceCtx, style.tint, "#ffffff");
7578
+ sourceCtx.fillRect(0, 0, tileData.width, tileData.height);
7579
+ sourceCtx.globalCompositeOperation = "source-over";
7580
+ const patternScale = style.scale * worldScale / tileData.width;
7581
+ const pattern = ctx.createPattern(sourceCanvas, "repeat");
7582
+ if (pattern && typeof pattern.setTransform === "function" && typeof DOMMatrix !== "undefined") {
7583
+ try {
7584
+ pattern.setTransform(new DOMMatrix([patternScale, 0, 0, patternScale, 0, 0]));
7585
+ return pattern;
7586
+ } catch {
7587
+ }
7588
+ }
7589
+ const patternPx = Math.round(style.scale * worldScale);
7590
+ if (patternPx < 1) return null;
7591
+ const patternCanvas = document.createElement("canvas");
7592
+ patternCanvas.width = patternPx;
7593
+ patternCanvas.height = patternPx;
7594
+ const patternCtx = patternCanvas.getContext("2d");
7595
+ if (!patternCtx) return null;
7596
+ patternCtx.drawImage(sourceCanvas, 0, 0, patternPx, patternPx);
7597
+ return ctx.createPattern(patternCanvas, "repeat");
7598
+ }
7599
+ paintProceduralOverlay(ctx, style, x, y, w, h) {
7600
+ const pattern = this.getOrCreatePattern(ctx, style, 1);
7601
+ if (!pattern) return;
7602
+ ctx.save();
7603
+ ctx.fillStyle = pattern;
7604
+ ctx.fillRect(x, y, w, h);
7605
+ ctx.restore();
7606
+ }
7607
+ renderTileProceduralOverlay(ctx, data, tx, ty, def, style) {
7608
+ const cellSize = def.cellSize;
7609
+ const tileWorldX = tx * FOG_TILE_CELLS * cellSize;
7610
+ const tileWorldY = ty * FOG_TILE_CELLS * cellSize;
7611
+ const pattern = this.getOrCreatePattern(ctx, style, 1);
7612
+ if (!pattern) return;
7613
+ const bytes = decodeBase64(data);
7614
+ ctx.save();
7615
+ ctx.fillStyle = pattern;
7616
+ for (let row = 0; row < FOG_TILE_CELLS; row++) {
7617
+ for (let col = 0; col < FOG_TILE_CELLS; col++) {
7618
+ const cellWorldX = tileWorldX + col * cellSize;
7619
+ const cellWorldY = tileWorldY + row * cellSize;
7620
+ if (cellWorldX < def.bounds.x || cellWorldY < def.bounds.y || cellWorldX >= def.bounds.x + def.bounds.w || cellWorldY >= def.bounds.y + def.bounds.h) {
7621
+ continue;
7622
+ }
7623
+ const index = row * FOG_TILE_CELLS + col;
7624
+ const byteIndex = index >> 3;
7625
+ const bitIndex = 7 - (index & 7);
7626
+ const revealed = (bytes[byteIndex] >> bitIndex & 1) === 1;
7627
+ if (!revealed) {
7628
+ ctx.fillRect(cellWorldX, cellWorldY, cellSize, cellSize);
7629
+ }
7630
+ }
7631
+ }
7632
+ ctx.restore();
7633
+ }
7307
7634
  tileRaster(data, color) {
7308
7635
  const key = `${color}\0${data}`;
7309
7636
  const cached = this.tileCache.get(key);
@@ -7387,6 +7714,22 @@ function getVisibleWorld(camera, viewportWidth, viewportHeight) {
7387
7714
  h: bottomRight.y - topLeft.y
7388
7715
  };
7389
7716
  }
7717
+ function normalizeCanvasColor(ctx, value, fallback) {
7718
+ const previous = ctx.fillStyle;
7719
+ try {
7720
+ ctx.fillStyle = "#010203";
7721
+ ctx.fillStyle = value;
7722
+ const first = ctx.fillStyle;
7723
+ ctx.fillStyle = "#040506";
7724
+ ctx.fillStyle = value;
7725
+ const second = ctx.fillStyle;
7726
+ return typeof first === "string" && first === second ? first : fallback;
7727
+ } catch {
7728
+ return fallback;
7729
+ } finally {
7730
+ ctx.fillStyle = previous;
7731
+ }
7732
+ }
7390
7733
 
7391
7734
  // src/canvas/export-image.ts
7392
7735
  var DEFAULT_IMAGE_TIMEOUT_MS = 1e4;
@@ -7802,7 +8145,13 @@ async function exportImage(store, options = {}, layerManager) {
7802
8145
  }
7803
8146
  if (options.fog) {
7804
8147
  const fogRenderer = new FogRenderer();
7805
- fogRenderer.renderForExport(ctx, options.fog.state, options.fog.mode, options.fog.color);
8148
+ fogRenderer.renderForExport(
8149
+ ctx,
8150
+ options.fog.state,
8151
+ options.fog.mode,
8152
+ options.fog.color,
8153
+ options.fog.style
8154
+ );
7806
8155
  }
7807
8156
  const mimeType = format === "jpeg" ? "image/jpeg" : "image/png";
7808
8157
  return new Promise((resolve) => {
@@ -8196,7 +8545,13 @@ async function exportSvg(store, options = {}, layerManager) {
8196
8545
  if (fogCtx) {
8197
8546
  fogCtx.translate(-bounds.x, -bounds.y);
8198
8547
  const fogRenderer = new FogRenderer();
8199
- fogRenderer.renderForExport(fogCtx, fogState, options.fog.mode, options.fog.color);
8548
+ fogRenderer.renderForExport(
8549
+ fogCtx,
8550
+ fogState,
8551
+ options.fog.mode,
8552
+ options.fog.color,
8553
+ options.fog.style
8554
+ );
8200
8555
  try {
8201
8556
  const fogDataUri = fogCanvas.toDataURL("image/png");
8202
8557
  if (fogDataUri.startsWith("data:")) {
@@ -11085,6 +11440,11 @@ var Viewport = class _Viewport {
11085
11440
  get fog() {
11086
11441
  return this.fogManager;
11087
11442
  }
11443
+ setFogStyle(options) {
11444
+ this.fogRenderer.setOptions(options);
11445
+ this.renderLoop.requestRender();
11446
+ this.minimap?.invalidateScene();
11447
+ }
11088
11448
  get snapToGrid() {
11089
11449
  return this._snapToGrid;
11090
11450
  }
@@ -11191,26 +11551,35 @@ var Viewport = class _Viewport {
11191
11551
  const expected = base.expectedCanvasTypes ? /* @__PURE__ */ new Set([...declared, ...base.expectedCanvasTypes]) : declared;
11192
11552
  return { ...base, htmlPainters: registry, expectedCanvasTypes: expected };
11193
11553
  }
11194
- async exportImage(options) {
11195
- const opts = this.withHtmlDefaults(options);
11196
- if (opts.fog === void 0 && this.fogRenderer.isVisible()) {
11197
- const state = this.fogManager.getState();
11198
- if (state) {
11199
- const mode = this.fogRenderer.getViewMode();
11200
- opts.fog = { state, mode };
11201
- }
11554
+ /**
11555
+ * Carry constructor-configured fog presentation into both implicit exports and
11556
+ * explicit state/mode exports. Explicit style and legacy color overrides win.
11557
+ */
11558
+ withFogDefaults(options) {
11559
+ const fog = options.fog;
11560
+ if (fog === false) return options;
11561
+ if (fog !== void 0) {
11562
+ if (fog.style !== void 0 || fog.color !== void 0) return options;
11563
+ return {
11564
+ ...options,
11565
+ fog: { ...fog, style: this.fogRenderer.getResolvedStyle(fog.mode) }
11566
+ };
11202
11567
  }
11568
+ if (!this.fogRenderer.isVisible()) return options;
11569
+ const state = this.fogManager.getState();
11570
+ if (!state) return options;
11571
+ const mode = this.fogRenderer.getViewMode();
11572
+ return {
11573
+ ...options,
11574
+ fog: { state, mode, style: this.fogRenderer.getResolvedStyle(mode) }
11575
+ };
11576
+ }
11577
+ async exportImage(options) {
11578
+ const opts = this.withFogDefaults(this.withHtmlDefaults(options));
11203
11579
  return exportImage(this.store, opts, this.layerManager);
11204
11580
  }
11205
11581
  async exportSVG(options) {
11206
- const opts = this.withHtmlDefaults(options);
11207
- if (opts.fog === void 0 && this.fogRenderer.isVisible()) {
11208
- const state = this.fogManager.getState();
11209
- if (state) {
11210
- const mode = this.fogRenderer.getViewMode();
11211
- opts.fog = { state, mode };
11212
- }
11213
- }
11582
+ const opts = this.withFogDefaults(this.withHtmlDefaults(options));
11214
11583
  return exportSvg(this.store, opts, this.layerManager);
11215
11584
  }
11216
11585
  loadState(state) {
@@ -12844,15 +13213,15 @@ function applyCameraView(camera, view, canvasW, canvasH) {
12844
13213
  var DEFAULT_DURATION_MS3 = 400;
12845
13214
  var FRAMED_EPSILON = 1e-6;
12846
13215
  var easeOutCubic2 = (t) => 1 - Math.pow(1 - t, 3);
12847
- function lerp(a, b, k) {
13216
+ function lerp2(a, b, k) {
12848
13217
  return a + (b - a) * k;
12849
13218
  }
12850
13219
  function lerpView(from, to, k) {
12851
13220
  return {
12852
- x: lerp(from.x, to.x, k),
12853
- y: lerp(from.y, to.y, k),
12854
- w: lerp(from.w, to.w, k),
12855
- h: lerp(from.h, to.h, k)
13221
+ x: lerp2(from.x, to.x, k),
13222
+ y: lerp2(from.y, to.y, k),
13223
+ w: lerp2(from.w, to.w, k),
13224
+ h: lerp2(from.h, to.h, k)
12856
13225
  };
12857
13226
  }
12858
13227
  function viewsClose(a, b) {
@@ -14106,7 +14475,7 @@ var PencilTool = class {
14106
14475
  };
14107
14476
 
14108
14477
  // src/elements/stroke-erase.ts
14109
- function lerp2(a, b, t) {
14478
+ function lerp3(a, b, t) {
14110
14479
  return {
14111
14480
  x: a.x + (b.x - a.x) * t,
14112
14481
  y: a.y + (b.y - a.y) * t,
@@ -14165,13 +14534,13 @@ function erasePoints(points, eraser, radius) {
14165
14534
  erased = true;
14166
14535
  if (tLo > 0) {
14167
14536
  if (current.length === 0) current.push(a);
14168
- current.push(lerp2(a, b, tLo));
14537
+ current.push(lerp3(a, b, tLo));
14169
14538
  flush();
14170
14539
  } else {
14171
14540
  flush();
14172
14541
  }
14173
14542
  if (tHi < 1) {
14174
- current = [lerp2(a, b, tHi), b];
14543
+ current = [lerp3(a, b, tHi), b];
14175
14544
  }
14176
14545
  }
14177
14546
  flush();
@@ -16687,7 +17056,7 @@ var FogTool = class {
16687
17056
  };
16688
17057
 
16689
17058
  // src/index.ts
16690
- var VERSION = "0.66.0";
17059
+ var VERSION = "0.68.0";
16691
17060
  export {
16692
17061
  AWARENESS_MAX_SELECTION,
16693
17062
  AWARENESS_PRESENCE_KIND,
@@ -16795,6 +17164,7 @@ export {
16795
17164
  isPingPresence,
16796
17165
  pathDistanceCells,
16797
17166
  recommendedFogCellSize,
17167
+ resolveFogStyle,
16798
17168
  resolveHtmlRouting,
16799
17169
  setFontSize,
16800
17170
  smartSnap,