@fieldnotes/core 0.42.0 → 0.43.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.d.cts CHANGED
@@ -471,6 +471,8 @@ interface ViewportOptions {
471
471
  contextMenu?: boolean;
472
472
  /** Coast (inertial glide) after a pan flick. Default `true`. */
473
473
  panInertia?: boolean;
474
+ /** Show an overview minimap (bottom-right) with tap/drag-to-navigate. Default `false`. */
475
+ minimap?: boolean;
474
476
  }
475
477
  declare class Viewport {
476
478
  private readonly container;
@@ -506,6 +508,7 @@ declare class Viewport {
506
508
  private readonly gridController;
507
509
  private readonly interactions;
508
510
  private contextMenu;
511
+ private minimap;
509
512
  private readonly htmlRenderers;
510
513
  constructor(container: HTMLElement, options?: ViewportOptions);
511
514
  get ctx(): CanvasRenderingContext2D | null;
@@ -1057,6 +1060,6 @@ declare class LaserTool implements Tool {
1057
1060
  private notifyOptionsChange;
1058
1061
  }
1059
1062
 
1060
- declare const VERSION = "0.42.0";
1063
+ declare const VERSION = "0.43.0";
1061
1064
 
1062
1065
  export { type ActiveFormats, type AlignEdge, type ArrowElement, type ArrowStrokeStyle, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, type BackgroundOptions, type BackgroundPattern, type Binding, type Bounds, Camera, type CameraChangeInfo, type CameraOptions, type CanvasElement, type CanvasState, type Command, DEFAULT_NOTE_FONT_SIZE, type DistributeAxis, ElementStore, type ElementStyle, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, type ExportImageOptions, type ExportSvgOptions, type FontSizePreset, type GridElement, type GridInfo, HandTool, type HexOrientation, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, LaserTool, type LaserToolOptions, type Layer, LayerManager, MeasureTool, type MeasureToolOptions, type Measurement, type NoteElement, NoteTool, type NoteToolOptions, PencilTool, type PencilToolOptions, type Point, type PointerState, type RenderStatsSnapshot, SelectTool, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type ShortcutBindings, type ShortcutOptions, type ShortcutsApi, type Size, type StrokeElement, type StrokePoint, type TemplateElement, type TemplateShape, TemplateTool, type TemplateToolOptions, type TextElement, TextTool, type TextToolOptions, type Tool, type ToolContext, ToolManager, type ToolName, VERSION, Viewport, type ViewportOptions, boundsIntersect, createArrow, createGrid, createHtmlElement, createImage, createNote, createShape, createStroke, createTemplate, createText, drawHexPath, exportImage, exportSvg, getActiveFormats, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getElementBounds, getElementStyle, getElementsBoundingBox, getHexCellsInCone, getHexCellsInLine, getHexCellsInRadius, getHexCellsInSquare, getHexDistance, isNearBezier, setFontSize, smartSnap, snapPoint, snapToHexCenter, styleToPatch, toggleBold, toggleItalic, toggleStrikethrough, toggleUnderline };
package/dist/index.d.ts CHANGED
@@ -471,6 +471,8 @@ interface ViewportOptions {
471
471
  contextMenu?: boolean;
472
472
  /** Coast (inertial glide) after a pan flick. Default `true`. */
473
473
  panInertia?: boolean;
474
+ /** Show an overview minimap (bottom-right) with tap/drag-to-navigate. Default `false`. */
475
+ minimap?: boolean;
474
476
  }
475
477
  declare class Viewport {
476
478
  private readonly container;
@@ -506,6 +508,7 @@ declare class Viewport {
506
508
  private readonly gridController;
507
509
  private readonly interactions;
508
510
  private contextMenu;
511
+ private minimap;
509
512
  private readonly htmlRenderers;
510
513
  constructor(container: HTMLElement, options?: ViewportOptions);
511
514
  get ctx(): CanvasRenderingContext2D | null;
@@ -1057,6 +1060,6 @@ declare class LaserTool implements Tool {
1057
1060
  private notifyOptionsChange;
1058
1061
  }
1059
1062
 
1060
- declare const VERSION = "0.42.0";
1063
+ declare const VERSION = "0.43.0";
1061
1064
 
1062
1065
  export { type ActiveFormats, type AlignEdge, type ArrowElement, type ArrowStrokeStyle, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, type BackgroundOptions, type BackgroundPattern, type Binding, type Bounds, Camera, type CameraChangeInfo, type CameraOptions, type CanvasElement, type CanvasState, type Command, DEFAULT_NOTE_FONT_SIZE, type DistributeAxis, ElementStore, type ElementStyle, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, type ExportImageOptions, type ExportSvgOptions, type FontSizePreset, type GridElement, type GridInfo, HandTool, type HexOrientation, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, LaserTool, type LaserToolOptions, type Layer, LayerManager, MeasureTool, type MeasureToolOptions, type Measurement, type NoteElement, NoteTool, type NoteToolOptions, PencilTool, type PencilToolOptions, type Point, type PointerState, type RenderStatsSnapshot, SelectTool, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type ShortcutBindings, type ShortcutOptions, type ShortcutsApi, type Size, type StrokeElement, type StrokePoint, type TemplateElement, type TemplateShape, TemplateTool, type TemplateToolOptions, type TextElement, TextTool, type TextToolOptions, type Tool, type ToolContext, ToolManager, type ToolName, VERSION, Viewport, type ViewportOptions, boundsIntersect, createArrow, createGrid, createHtmlElement, createImage, createNote, createShape, createStroke, createTemplate, createText, drawHexPath, exportImage, exportSvg, getActiveFormats, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getElementBounds, getElementStyle, getElementsBoundingBox, getHexCellsInCone, getHexCellsInLine, getHexCellsInRadius, getHexCellsInSquare, getHexDistance, isNearBezier, setFontSize, smartSnap, snapPoint, snapToHexCenter, styleToPatch, toggleBold, toggleItalic, toggleStrikethrough, toggleUnderline };
package/dist/index.js CHANGED
@@ -4277,6 +4277,135 @@ var ContextMenu = class {
4277
4277
  }
4278
4278
  };
4279
4279
 
4280
+ // src/canvas/minimap-transform.ts
4281
+ function unionBounds(a, b) {
4282
+ const minX = Math.min(a.x, b.x);
4283
+ const minY = Math.min(a.y, b.y);
4284
+ const maxX = Math.max(a.x + a.w, b.x + b.w);
4285
+ const maxY = Math.max(a.y + a.h, b.y + b.h);
4286
+ return { x: minX, y: minY, w: maxX - minX, h: maxY - minY };
4287
+ }
4288
+ function computeMinimapTransform(mapping, miniW, miniH, padding) {
4289
+ const availW = Math.max(1, miniW - 2 * padding);
4290
+ const availH = Math.max(1, miniH - 2 * padding);
4291
+ const w = Math.max(mapping.w, 1);
4292
+ const h = Math.max(mapping.h, 1);
4293
+ const scale = Math.min(availW / w, availH / h);
4294
+ const offsetX = (miniW - mapping.w * scale) / 2 - mapping.x * scale;
4295
+ const offsetY = (miniH - mapping.h * scale) / 2 - mapping.y * scale;
4296
+ return { scale, offsetX, offsetY };
4297
+ }
4298
+ function worldToMini(t, p) {
4299
+ return { x: p.x * t.scale + t.offsetX, y: p.y * t.scale + t.offsetY };
4300
+ }
4301
+ function miniToWorld(t, p) {
4302
+ return { x: (p.x - t.offsetX) / t.scale, y: (p.y - t.offsetY) / t.scale };
4303
+ }
4304
+
4305
+ // src/canvas/minimap.ts
4306
+ var WIDTH = 200;
4307
+ var HEIGHT = 140;
4308
+ var MARGIN = 16;
4309
+ var PADDING = 8;
4310
+ var NEUTRAL = "rgba(100,116,139,0.6)";
4311
+ var VIEWPORT_STROKE = "#3b82f6";
4312
+ function elementColor(el) {
4313
+ return "color" in el && typeof el.color === "string" ? el.color : NEUTRAL;
4314
+ }
4315
+ var Minimap = class {
4316
+ constructor(deps) {
4317
+ this.deps = deps;
4318
+ const canvas = document.createElement("canvas");
4319
+ canvas.width = WIDTH;
4320
+ canvas.height = HEIGHT;
4321
+ Object.assign(canvas.style, {
4322
+ position: "absolute",
4323
+ right: `${MARGIN}px`,
4324
+ bottom: `${MARGIN}px`,
4325
+ width: `${WIDTH}px`,
4326
+ height: `${HEIGHT}px`,
4327
+ background: "rgba(255,255,255,0.85)",
4328
+ border: "1px solid rgba(0,0,0,0.15)",
4329
+ borderRadius: "4px",
4330
+ touchAction: "none",
4331
+ cursor: "pointer",
4332
+ zIndex: "10"
4333
+ });
4334
+ canvas.addEventListener("pointerdown", this.onPointerDown);
4335
+ canvas.addEventListener("pointermove", this.onPointerMove);
4336
+ canvas.addEventListener("pointerup", this.onPointerUp);
4337
+ this.deps.container.appendChild(canvas);
4338
+ this.canvas = canvas;
4339
+ }
4340
+ canvas;
4341
+ rafId = null;
4342
+ dragging = false;
4343
+ scheduleDraw() {
4344
+ if (this.rafId !== null) return;
4345
+ this.rafId = this.deps.requestFrame(this.draw);
4346
+ }
4347
+ destroy() {
4348
+ if (this.rafId !== null) {
4349
+ this.deps.cancelFrame(this.rafId);
4350
+ this.rafId = null;
4351
+ }
4352
+ this.canvas.removeEventListener("pointerdown", this.onPointerDown);
4353
+ this.canvas.removeEventListener("pointermove", this.onPointerMove);
4354
+ this.canvas.removeEventListener("pointerup", this.onPointerUp);
4355
+ this.canvas.remove();
4356
+ }
4357
+ currentTransform() {
4358
+ const viewport = this.deps.getViewportRect();
4359
+ const content = this.deps.getContentBounds();
4360
+ const mapping = content ? unionBounds(content, viewport) : viewport;
4361
+ return computeMinimapTransform(mapping, WIDTH, HEIGHT, PADDING);
4362
+ }
4363
+ draw = () => {
4364
+ this.rafId = null;
4365
+ const ctx = this.canvas.getContext("2d");
4366
+ if (!ctx) return;
4367
+ const t = this.currentTransform();
4368
+ const viewport = this.deps.getViewportRect();
4369
+ ctx.clearRect(0, 0, WIDTH, HEIGHT);
4370
+ for (const el of this.deps.getElements()) {
4371
+ const b = getElementBounds(el);
4372
+ if (!b) continue;
4373
+ const tl = worldToMini(t, { x: b.x, y: b.y });
4374
+ ctx.fillStyle = elementColor(el);
4375
+ ctx.fillRect(tl.x, tl.y, Math.max(1, b.w * t.scale), Math.max(1, b.h * t.scale));
4376
+ }
4377
+ const vtl = worldToMini(t, { x: viewport.x, y: viewport.y });
4378
+ ctx.strokeStyle = VIEWPORT_STROKE;
4379
+ ctx.lineWidth = 1.5;
4380
+ ctx.strokeRect(vtl.x, vtl.y, viewport.w * t.scale, viewport.h * t.scale);
4381
+ };
4382
+ navigateFromEvent(e) {
4383
+ const rect = this.canvas.getBoundingClientRect();
4384
+ const point = { x: e.clientX - rect.left, y: e.clientY - rect.top };
4385
+ const world = miniToWorld(this.currentTransform(), point);
4386
+ this.deps.navigateTo(world);
4387
+ }
4388
+ onPointerDown = (e) => {
4389
+ e.stopPropagation();
4390
+ e.preventDefault();
4391
+ this.dragging = true;
4392
+ this.canvas.setPointerCapture?.(e.pointerId);
4393
+ this.navigateFromEvent(e);
4394
+ };
4395
+ onPointerMove = (e) => {
4396
+ if (!this.dragging) return;
4397
+ e.stopPropagation();
4398
+ this.navigateFromEvent(e);
4399
+ };
4400
+ onPointerUp = (e) => {
4401
+ this.dragging = false;
4402
+ try {
4403
+ this.canvas.releasePointerCapture(e.pointerId);
4404
+ } catch {
4405
+ }
4406
+ };
4407
+ };
4408
+
4280
4409
  // src/canvas/viewport-dom.ts
4281
4410
  function createWrapper() {
4282
4411
  const el = document.createElement("div");
@@ -6402,7 +6531,7 @@ function getElementStyle(element) {
6402
6531
  }
6403
6532
 
6404
6533
  // src/canvas/selection-ops.ts
6405
- function unionBounds(list) {
6534
+ function unionBounds2(list) {
6406
6535
  let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
6407
6536
  for (const b of list) {
6408
6537
  minX = Math.min(minX, b.x);
@@ -6497,7 +6626,7 @@ var SelectionOps = class {
6497
6626
  align(edge) {
6498
6627
  const bounded = this.boundedSelection();
6499
6628
  if (bounded.length < 2) return;
6500
- const B = unionBounds(bounded.map((e) => e.bounds));
6629
+ const B = unionBounds2(bounded.map((e) => e.bounds));
6501
6630
  this.deps.recorder.begin();
6502
6631
  const moved = [];
6503
6632
  for (const { id, el, bounds: b } of bounded) {
@@ -6904,6 +7033,27 @@ var Viewport = class {
6904
7033
  });
6905
7034
  }
6906
7035
  this.unsubToolChange = this.toolManager.onChange(() => this.contextMenu?.close());
7036
+ if (options.minimap) {
7037
+ const visibleEls = () => this.store.getAll().filter((el) => this.layerManager.isLayerVisible(el.layerId));
7038
+ this.minimap = new Minimap({
7039
+ container: this.wrapper,
7040
+ getElements: visibleEls,
7041
+ getContentBounds: () => getElementsBoundingBox(visibleEls()),
7042
+ getViewportRect: () => this.camera.getVisibleRect(this.canvasEl.clientWidth, this.canvasEl.clientHeight),
7043
+ navigateTo: (w) => {
7044
+ const z = this.camera.zoom;
7045
+ this.camera.moveTo(
7046
+ this.canvasEl.clientWidth / 2 - w.x * z,
7047
+ this.canvasEl.clientHeight / 2 - w.y * z
7048
+ );
7049
+ },
7050
+ requestFrame: (cb) => typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame(cb) : 0,
7051
+ cancelFrame: (id) => {
7052
+ if (typeof cancelAnimationFrame !== "undefined") cancelAnimationFrame(id);
7053
+ }
7054
+ });
7055
+ this.minimap.scheduleDraw();
7056
+ }
6907
7057
  this.domNodeManager = new DomNodeManager({
6908
7058
  domLayer: this.domLayer,
6909
7059
  onEditRequest: (id) => this.interactions.startEditingElement(id),
@@ -6936,6 +7086,7 @@ var Viewport = class {
6936
7086
  this.applyCameraTransform();
6937
7087
  this.noteEditor.updateToolbarPosition();
6938
7088
  this.contextMenu?.close();
7089
+ this.minimap?.scheduleDraw();
6939
7090
  this.requestRender();
6940
7091
  });
6941
7092
  this.gridController = new GridController({
@@ -6950,6 +7101,7 @@ var Viewport = class {
6950
7101
  this.store.on("add", (el) => {
6951
7102
  if (el.type === "grid") this.gridController.syncContext();
6952
7103
  this.renderLoop.markLayerDirty(el.layerId);
7104
+ this.minimap?.scheduleDraw();
6953
7105
  this.requestRender();
6954
7106
  }),
6955
7107
  this.store.on("remove", (el) => {
@@ -6957,6 +7109,7 @@ var Viewport = class {
6957
7109
  this.unbindArrowsFrom(el);
6958
7110
  this.domNodeManager.removeDomNode(el.id);
6959
7111
  this.renderLoop.markLayerDirty(el.layerId);
7112
+ this.minimap?.scheduleDraw();
6960
7113
  this.requestRender();
6961
7114
  }),
6962
7115
  this.store.on("update", ({ previous, current }) => {
@@ -6965,17 +7118,20 @@ var Viewport = class {
6965
7118
  if (previous.layerId !== current.layerId) {
6966
7119
  this.renderLoop.markLayerDirty(previous.layerId);
6967
7120
  }
7121
+ this.minimap?.scheduleDraw();
6968
7122
  this.requestRender();
6969
7123
  }),
6970
7124
  this.store.on("clear", () => {
6971
7125
  this.domNodeManager.clearDomNodes();
6972
7126
  this.renderLoop.markAllLayersDirty();
6973
7127
  this.gridController.syncContext();
7128
+ this.minimap?.scheduleDraw();
6974
7129
  this.requestRender();
6975
7130
  })
6976
7131
  ];
6977
7132
  this.layerManager.on("change", () => {
6978
7133
  this.toolContext.activeLayerId = this.layerManager.activeLayerId;
7134
+ this.minimap?.scheduleDraw();
6979
7135
  this.requestRender();
6980
7136
  });
6981
7137
  this.interactions = new ViewportInteractions({
@@ -7035,6 +7191,7 @@ var Viewport = class {
7035
7191
  gridController;
7036
7192
  interactions;
7037
7193
  contextMenu = null;
7194
+ minimap = null;
7038
7195
  htmlRenderers = /* @__PURE__ */ new Map();
7039
7196
  get ctx() {
7040
7197
  return this.canvasEl.getContext("2d");
@@ -7308,6 +7465,7 @@ var Viewport = class {
7308
7465
  this.arrowLabelEditor.cancel();
7309
7466
  this.historyRecorder.destroy();
7310
7467
  this.contextMenu?.dispose();
7468
+ this.minimap?.destroy();
7311
7469
  this.wrapper.removeEventListener("pointerdown", this.interactions.onTapDown);
7312
7470
  this.wrapper.removeEventListener("pointerup", this.interactions.onDoubleTap);
7313
7471
  this.wrapper.removeEventListener("dragover", this.interactions.onDragOver);
@@ -9810,7 +9968,7 @@ var LaserTool = class {
9810
9968
  };
9811
9969
 
9812
9970
  // src/index.ts
9813
- var VERSION = "0.42.0";
9971
+ var VERSION = "0.43.0";
9814
9972
  export {
9815
9973
  ArrowTool,
9816
9974
  AutoSave,