@fieldnotes/core 0.42.0 → 0.44.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
@@ -3204,7 +3204,7 @@ function drawHexPath(ctx, cx, cy, cellSize, orientation) {
3204
3204
  // src/elements/renderers/template-renderer.ts
3205
3205
  function renderTemplate(ctx, template, store) {
3206
3206
  const grid = store?.getElementsByType("grid")[0];
3207
- if (grid && grid.gridType === "hex") {
3207
+ if (grid && grid.gridType === "hex" && template.renderStyle !== "geometric") {
3208
3208
  renderHexTemplate(ctx, template, grid.cellSize, grid.hexOrientation);
3209
3209
  return;
3210
3210
  }
@@ -3880,7 +3880,8 @@ function createTemplate(input) {
3880
3880
  strokeWidth: input.strokeWidth ?? 2,
3881
3881
  opacity: input.opacity ?? 0.6,
3882
3882
  feetPerCell: input.feetPerCell,
3883
- radiusFeet: input.radiusFeet
3883
+ radiusFeet: input.radiusFeet,
3884
+ ...input.renderStyle !== void 0 ? { renderStyle: input.renderStyle } : {}
3884
3885
  };
3885
3886
  }
3886
3887
 
@@ -4360,6 +4361,135 @@ var ContextMenu = class {
4360
4361
  }
4361
4362
  };
4362
4363
 
4364
+ // src/canvas/minimap-transform.ts
4365
+ function unionBounds(a, b) {
4366
+ const minX = Math.min(a.x, b.x);
4367
+ const minY = Math.min(a.y, b.y);
4368
+ const maxX = Math.max(a.x + a.w, b.x + b.w);
4369
+ const maxY = Math.max(a.y + a.h, b.y + b.h);
4370
+ return { x: minX, y: minY, w: maxX - minX, h: maxY - minY };
4371
+ }
4372
+ function computeMinimapTransform(mapping, miniW, miniH, padding) {
4373
+ const availW = Math.max(1, miniW - 2 * padding);
4374
+ const availH = Math.max(1, miniH - 2 * padding);
4375
+ const w = Math.max(mapping.w, 1);
4376
+ const h = Math.max(mapping.h, 1);
4377
+ const scale = Math.min(availW / w, availH / h);
4378
+ const offsetX = (miniW - mapping.w * scale) / 2 - mapping.x * scale;
4379
+ const offsetY = (miniH - mapping.h * scale) / 2 - mapping.y * scale;
4380
+ return { scale, offsetX, offsetY };
4381
+ }
4382
+ function worldToMini(t, p) {
4383
+ return { x: p.x * t.scale + t.offsetX, y: p.y * t.scale + t.offsetY };
4384
+ }
4385
+ function miniToWorld(t, p) {
4386
+ return { x: (p.x - t.offsetX) / t.scale, y: (p.y - t.offsetY) / t.scale };
4387
+ }
4388
+
4389
+ // src/canvas/minimap.ts
4390
+ var WIDTH = 200;
4391
+ var HEIGHT = 140;
4392
+ var MARGIN = 16;
4393
+ var PADDING = 8;
4394
+ var NEUTRAL = "rgba(100,116,139,0.6)";
4395
+ var VIEWPORT_STROKE = "#3b82f6";
4396
+ function elementColor(el) {
4397
+ return "color" in el && typeof el.color === "string" ? el.color : NEUTRAL;
4398
+ }
4399
+ var Minimap = class {
4400
+ constructor(deps) {
4401
+ this.deps = deps;
4402
+ const canvas = document.createElement("canvas");
4403
+ canvas.width = WIDTH;
4404
+ canvas.height = HEIGHT;
4405
+ Object.assign(canvas.style, {
4406
+ position: "absolute",
4407
+ right: `${MARGIN}px`,
4408
+ bottom: `${MARGIN}px`,
4409
+ width: `${WIDTH}px`,
4410
+ height: `${HEIGHT}px`,
4411
+ background: "rgba(255,255,255,0.85)",
4412
+ border: "1px solid rgba(0,0,0,0.15)",
4413
+ borderRadius: "4px",
4414
+ touchAction: "none",
4415
+ cursor: "pointer",
4416
+ zIndex: "10"
4417
+ });
4418
+ canvas.addEventListener("pointerdown", this.onPointerDown);
4419
+ canvas.addEventListener("pointermove", this.onPointerMove);
4420
+ canvas.addEventListener("pointerup", this.onPointerUp);
4421
+ this.deps.container.appendChild(canvas);
4422
+ this.canvas = canvas;
4423
+ }
4424
+ canvas;
4425
+ rafId = null;
4426
+ dragging = false;
4427
+ scheduleDraw() {
4428
+ if (this.rafId !== null) return;
4429
+ this.rafId = this.deps.requestFrame(this.draw);
4430
+ }
4431
+ destroy() {
4432
+ if (this.rafId !== null) {
4433
+ this.deps.cancelFrame(this.rafId);
4434
+ this.rafId = null;
4435
+ }
4436
+ this.canvas.removeEventListener("pointerdown", this.onPointerDown);
4437
+ this.canvas.removeEventListener("pointermove", this.onPointerMove);
4438
+ this.canvas.removeEventListener("pointerup", this.onPointerUp);
4439
+ this.canvas.remove();
4440
+ }
4441
+ currentTransform() {
4442
+ const viewport = this.deps.getViewportRect();
4443
+ const content = this.deps.getContentBounds();
4444
+ const mapping = content ? unionBounds(content, viewport) : viewport;
4445
+ return computeMinimapTransform(mapping, WIDTH, HEIGHT, PADDING);
4446
+ }
4447
+ draw = () => {
4448
+ this.rafId = null;
4449
+ const ctx = this.canvas.getContext("2d");
4450
+ if (!ctx) return;
4451
+ const t = this.currentTransform();
4452
+ const viewport = this.deps.getViewportRect();
4453
+ ctx.clearRect(0, 0, WIDTH, HEIGHT);
4454
+ for (const el of this.deps.getElements()) {
4455
+ const b = getElementBounds(el);
4456
+ if (!b) continue;
4457
+ const tl = worldToMini(t, { x: b.x, y: b.y });
4458
+ ctx.fillStyle = elementColor(el);
4459
+ ctx.fillRect(tl.x, tl.y, Math.max(1, b.w * t.scale), Math.max(1, b.h * t.scale));
4460
+ }
4461
+ const vtl = worldToMini(t, { x: viewport.x, y: viewport.y });
4462
+ ctx.strokeStyle = VIEWPORT_STROKE;
4463
+ ctx.lineWidth = 1.5;
4464
+ ctx.strokeRect(vtl.x, vtl.y, viewport.w * t.scale, viewport.h * t.scale);
4465
+ };
4466
+ navigateFromEvent(e) {
4467
+ const rect = this.canvas.getBoundingClientRect();
4468
+ const point = { x: e.clientX - rect.left, y: e.clientY - rect.top };
4469
+ const world = miniToWorld(this.currentTransform(), point);
4470
+ this.deps.navigateTo(world);
4471
+ }
4472
+ onPointerDown = (e) => {
4473
+ e.stopPropagation();
4474
+ e.preventDefault();
4475
+ this.dragging = true;
4476
+ this.canvas.setPointerCapture?.(e.pointerId);
4477
+ this.navigateFromEvent(e);
4478
+ };
4479
+ onPointerMove = (e) => {
4480
+ if (!this.dragging) return;
4481
+ e.stopPropagation();
4482
+ this.navigateFromEvent(e);
4483
+ };
4484
+ onPointerUp = (e) => {
4485
+ this.dragging = false;
4486
+ try {
4487
+ this.canvas.releasePointerCapture(e.pointerId);
4488
+ } catch {
4489
+ }
4490
+ };
4491
+ };
4492
+
4363
4493
  // src/canvas/viewport-dom.ts
4364
4494
  function createWrapper() {
4365
4495
  const el = document.createElement("div");
@@ -6485,7 +6615,7 @@ function getElementStyle(element) {
6485
6615
  }
6486
6616
 
6487
6617
  // src/canvas/selection-ops.ts
6488
- function unionBounds(list) {
6618
+ function unionBounds2(list) {
6489
6619
  let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
6490
6620
  for (const b of list) {
6491
6621
  minX = Math.min(minX, b.x);
@@ -6580,7 +6710,7 @@ var SelectionOps = class {
6580
6710
  align(edge) {
6581
6711
  const bounded = this.boundedSelection();
6582
6712
  if (bounded.length < 2) return;
6583
- const B = unionBounds(bounded.map((e) => e.bounds));
6713
+ const B = unionBounds2(bounded.map((e) => e.bounds));
6584
6714
  this.deps.recorder.begin();
6585
6715
  const moved = [];
6586
6716
  for (const { id, el, bounds: b } of bounded) {
@@ -6987,6 +7117,27 @@ var Viewport = class {
6987
7117
  });
6988
7118
  }
6989
7119
  this.unsubToolChange = this.toolManager.onChange(() => this.contextMenu?.close());
7120
+ if (options.minimap) {
7121
+ const visibleEls = () => this.store.getAll().filter((el) => this.layerManager.isLayerVisible(el.layerId));
7122
+ this.minimap = new Minimap({
7123
+ container: this.wrapper,
7124
+ getElements: visibleEls,
7125
+ getContentBounds: () => getElementsBoundingBox(visibleEls()),
7126
+ getViewportRect: () => this.camera.getVisibleRect(this.canvasEl.clientWidth, this.canvasEl.clientHeight),
7127
+ navigateTo: (w) => {
7128
+ const z = this.camera.zoom;
7129
+ this.camera.moveTo(
7130
+ this.canvasEl.clientWidth / 2 - w.x * z,
7131
+ this.canvasEl.clientHeight / 2 - w.y * z
7132
+ );
7133
+ },
7134
+ requestFrame: (cb) => typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame(cb) : 0,
7135
+ cancelFrame: (id) => {
7136
+ if (typeof cancelAnimationFrame !== "undefined") cancelAnimationFrame(id);
7137
+ }
7138
+ });
7139
+ this.minimap.scheduleDraw();
7140
+ }
6990
7141
  this.domNodeManager = new DomNodeManager({
6991
7142
  domLayer: this.domLayer,
6992
7143
  onEditRequest: (id) => this.interactions.startEditingElement(id),
@@ -7019,6 +7170,7 @@ var Viewport = class {
7019
7170
  this.applyCameraTransform();
7020
7171
  this.noteEditor.updateToolbarPosition();
7021
7172
  this.contextMenu?.close();
7173
+ this.minimap?.scheduleDraw();
7022
7174
  this.requestRender();
7023
7175
  });
7024
7176
  this.gridController = new GridController({
@@ -7033,6 +7185,7 @@ var Viewport = class {
7033
7185
  this.store.on("add", (el) => {
7034
7186
  if (el.type === "grid") this.gridController.syncContext();
7035
7187
  this.renderLoop.markLayerDirty(el.layerId);
7188
+ this.minimap?.scheduleDraw();
7036
7189
  this.requestRender();
7037
7190
  }),
7038
7191
  this.store.on("remove", (el) => {
@@ -7040,6 +7193,7 @@ var Viewport = class {
7040
7193
  this.unbindArrowsFrom(el);
7041
7194
  this.domNodeManager.removeDomNode(el.id);
7042
7195
  this.renderLoop.markLayerDirty(el.layerId);
7196
+ this.minimap?.scheduleDraw();
7043
7197
  this.requestRender();
7044
7198
  }),
7045
7199
  this.store.on("update", ({ previous, current }) => {
@@ -7048,17 +7202,20 @@ var Viewport = class {
7048
7202
  if (previous.layerId !== current.layerId) {
7049
7203
  this.renderLoop.markLayerDirty(previous.layerId);
7050
7204
  }
7205
+ this.minimap?.scheduleDraw();
7051
7206
  this.requestRender();
7052
7207
  }),
7053
7208
  this.store.on("clear", () => {
7054
7209
  this.domNodeManager.clearDomNodes();
7055
7210
  this.renderLoop.markAllLayersDirty();
7056
7211
  this.gridController.syncContext();
7212
+ this.minimap?.scheduleDraw();
7057
7213
  this.requestRender();
7058
7214
  })
7059
7215
  ];
7060
7216
  this.layerManager.on("change", () => {
7061
7217
  this.toolContext.activeLayerId = this.layerManager.activeLayerId;
7218
+ this.minimap?.scheduleDraw();
7062
7219
  this.requestRender();
7063
7220
  });
7064
7221
  this.interactions = new ViewportInteractions({
@@ -7118,6 +7275,7 @@ var Viewport = class {
7118
7275
  gridController;
7119
7276
  interactions;
7120
7277
  contextMenu = null;
7278
+ minimap = null;
7121
7279
  htmlRenderers = /* @__PURE__ */ new Map();
7122
7280
  get ctx() {
7123
7281
  return this.canvasEl.getContext("2d");
@@ -7391,6 +7549,7 @@ var Viewport = class {
7391
7549
  this.arrowLabelEditor.cancel();
7392
7550
  this.historyRecorder.destroy();
7393
7551
  this.contextMenu?.dispose();
7552
+ this.minimap?.destroy();
7394
7553
  this.wrapper.removeEventListener("pointerdown", this.interactions.onTapDown);
7395
7554
  this.wrapper.removeEventListener("pointerup", this.interactions.onDoubleTap);
7396
7555
  this.wrapper.removeEventListener("dragover", this.interactions.onDragOver);
@@ -9537,6 +9696,7 @@ var TemplateTool = class {
9537
9696
  strokeWidth;
9538
9697
  opacity;
9539
9698
  feetPerCell;
9699
+ renderStyle;
9540
9700
  optionListeners = /* @__PURE__ */ new Set();
9541
9701
  constructor(options = {}) {
9542
9702
  this.templateShape = options.templateShape ?? "circle";
@@ -9545,6 +9705,7 @@ var TemplateTool = class {
9545
9705
  this.strokeWidth = options.strokeWidth ?? 2;
9546
9706
  this.opacity = options.opacity ?? 0.6;
9547
9707
  this.feetPerCell = options.feetPerCell ?? 5;
9708
+ this.renderStyle = options.renderStyle ?? "cells";
9548
9709
  }
9549
9710
  getOptions() {
9550
9711
  return {
@@ -9553,7 +9714,8 @@ var TemplateTool = class {
9553
9714
  strokeColor: this.strokeColor,
9554
9715
  strokeWidth: this.strokeWidth,
9555
9716
  opacity: this.opacity,
9556
- feetPerCell: this.feetPerCell
9717
+ feetPerCell: this.feetPerCell,
9718
+ renderStyle: this.renderStyle
9557
9719
  };
9558
9720
  }
9559
9721
  setOptions(options) {
@@ -9563,6 +9725,7 @@ var TemplateTool = class {
9563
9725
  if (options.strokeWidth !== void 0) this.strokeWidth = options.strokeWidth;
9564
9726
  if (options.opacity !== void 0) this.opacity = options.opacity;
9565
9727
  if (options.feetPerCell !== void 0) this.feetPerCell = options.feetPerCell;
9728
+ if (options.renderStyle !== void 0) this.renderStyle = options.renderStyle;
9566
9729
  this.notifyOptionsChange();
9567
9730
  }
9568
9731
  onOptionsChange(listener) {
@@ -9605,6 +9768,7 @@ var TemplateTool = class {
9605
9768
  opacity: this.opacity,
9606
9769
  feetPerCell: this.feetPerCell,
9607
9770
  radiusFeet: radiusFeet > 0 ? radiusFeet : void 0,
9771
+ renderStyle: this.renderStyle,
9608
9772
  layerId: ctx.activeLayerId ?? ""
9609
9773
  });
9610
9774
  ctx.store.add(element);
@@ -9620,7 +9784,7 @@ var TemplateTool = class {
9620
9784
  if (!this.drawing) return;
9621
9785
  const radius = this.computeRadius();
9622
9786
  if (radius <= 0) return;
9623
- if (this.gridType === "hex" && this.hexOrientation) {
9787
+ if (this.gridType === "hex" && this.hexOrientation && this.renderStyle !== "geometric") {
9624
9788
  this.renderHexOverlay(ctx, radius);
9625
9789
  return;
9626
9790
  }
@@ -9893,7 +10057,7 @@ var LaserTool = class {
9893
10057
  };
9894
10058
 
9895
10059
  // src/index.ts
9896
- var VERSION = "0.42.0";
10060
+ var VERSION = "0.44.0";
9897
10061
  // Annotate the CommonJS export names for ESM import in node:
9898
10062
  0 && (module.exports = {
9899
10063
  ArrowTool,