@fieldnotes/core 0.4.1 → 0.5.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
@@ -101,6 +101,8 @@ declare function exportState(elements: CanvasElement[], camera: {
101
101
  }): CanvasState;
102
102
  declare function parseState(json: string): CanvasState;
103
103
 
104
+ declare function snapPoint(point: Point, gridSize: number): Point;
105
+
104
106
  interface ElementUpdateEvent {
105
107
  previous: CanvasElement;
106
108
  current: CanvasElement;
@@ -203,6 +205,8 @@ interface ToolContext {
203
205
  switchTool?: (name: string) => void;
204
206
  editElement?: (id: string) => void;
205
207
  setCursor?: (cursor: string) => void;
208
+ snapToGrid?: boolean;
209
+ gridSize?: number;
206
210
  }
207
211
  interface PointerState {
208
212
  x: number;
@@ -353,12 +357,16 @@ declare class Viewport {
353
357
  readonly toolContext: ToolContext;
354
358
  private resizeObserver;
355
359
  private animFrameId;
360
+ private _snapToGrid;
361
+ private readonly _gridSize;
356
362
  private needsRender;
357
363
  private domNodes;
358
364
  private htmlContent;
359
365
  private interactingElementId;
360
366
  constructor(container: HTMLElement, options?: ViewportOptions);
361
367
  get ctx(): CanvasRenderingContext2D | null;
368
+ get snapToGrid(): boolean;
369
+ setSnapToGrid(enabled: boolean): void;
362
370
  requestRender(): void;
363
371
  exportState(): CanvasState;
364
372
  exportJSON(): string;
@@ -612,6 +620,7 @@ declare class SelectTool implements Tool {
612
620
  get isMarqueeActive(): boolean;
613
621
  onActivate(ctx: ToolContext): void;
614
622
  onDeactivate(ctx: ToolContext): void;
623
+ private snap;
615
624
  onPointerDown(state: PointerState, ctx: ToolContext): void;
616
625
  onPointerMove(state: PointerState, ctx: ToolContext): void;
617
626
  onPointerUp(_state: PointerState, ctx: ToolContext): void;
@@ -729,10 +738,11 @@ declare class ShapeTool implements Tool {
729
738
  onPointerUp(_state: PointerState, ctx: ToolContext): void;
730
739
  renderOverlay(ctx: CanvasRenderingContext2D): void;
731
740
  private computeRect;
741
+ private snap;
732
742
  private onKeyDown;
733
743
  private onKeyUp;
734
744
  }
735
745
 
736
- declare const VERSION = "0.4.1";
746
+ declare const VERSION = "0.5.0";
737
747
 
738
- export { AddElementCommand, type ArrowElement, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, Background, type BackgroundOptions, type BackgroundPattern, BatchCommand, type Binding, type Bounds, Camera, type CameraOptions, type CanvasElement, type CanvasState, type Command, ElementRenderer, ElementStore, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, EventBus, HandTool, HistoryRecorder, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, InputHandler, NoteEditor, type NoteElement, NoteTool, type NoteToolOptions, PencilTool, type PencilToolOptions, type Point, type PointerState, RemoveElementCommand, SelectTool, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type Size, type StrokeElement, type StrokePoint, type TextElement, TextTool, type TextToolOptions, type Tool, type ToolContext, ToolManager, type ToolName, UpdateElementCommand, VERSION, Viewport, type ViewportOptions, clearStaleBindings, createArrow, createHtmlElement, createId, createImage, createNote, createShape, createStroke, createText, exportState, findBindTarget, findBoundArrows, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getEdgeIntersection, getElementBounds, getElementCenter, isBindable, isNearBezier, parseState, unbindArrow, updateBoundArrow };
748
+ export { AddElementCommand, type ArrowElement, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, Background, type BackgroundOptions, type BackgroundPattern, BatchCommand, type Binding, type Bounds, Camera, type CameraOptions, type CanvasElement, type CanvasState, type Command, ElementRenderer, ElementStore, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, EventBus, HandTool, HistoryRecorder, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, InputHandler, NoteEditor, type NoteElement, NoteTool, type NoteToolOptions, PencilTool, type PencilToolOptions, type Point, type PointerState, RemoveElementCommand, SelectTool, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type Size, type StrokeElement, type StrokePoint, type TextElement, TextTool, type TextToolOptions, type Tool, type ToolContext, ToolManager, type ToolName, UpdateElementCommand, VERSION, Viewport, type ViewportOptions, clearStaleBindings, createArrow, createHtmlElement, createId, createImage, createNote, createShape, createStroke, createText, exportState, findBindTarget, findBoundArrows, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getEdgeIntersection, getElementBounds, getElementCenter, isBindable, isNearBezier, parseState, snapPoint, unbindArrow, updateBoundArrow };
package/dist/index.d.ts CHANGED
@@ -101,6 +101,8 @@ declare function exportState(elements: CanvasElement[], camera: {
101
101
  }): CanvasState;
102
102
  declare function parseState(json: string): CanvasState;
103
103
 
104
+ declare function snapPoint(point: Point, gridSize: number): Point;
105
+
104
106
  interface ElementUpdateEvent {
105
107
  previous: CanvasElement;
106
108
  current: CanvasElement;
@@ -203,6 +205,8 @@ interface ToolContext {
203
205
  switchTool?: (name: string) => void;
204
206
  editElement?: (id: string) => void;
205
207
  setCursor?: (cursor: string) => void;
208
+ snapToGrid?: boolean;
209
+ gridSize?: number;
206
210
  }
207
211
  interface PointerState {
208
212
  x: number;
@@ -353,12 +357,16 @@ declare class Viewport {
353
357
  readonly toolContext: ToolContext;
354
358
  private resizeObserver;
355
359
  private animFrameId;
360
+ private _snapToGrid;
361
+ private readonly _gridSize;
356
362
  private needsRender;
357
363
  private domNodes;
358
364
  private htmlContent;
359
365
  private interactingElementId;
360
366
  constructor(container: HTMLElement, options?: ViewportOptions);
361
367
  get ctx(): CanvasRenderingContext2D | null;
368
+ get snapToGrid(): boolean;
369
+ setSnapToGrid(enabled: boolean): void;
362
370
  requestRender(): void;
363
371
  exportState(): CanvasState;
364
372
  exportJSON(): string;
@@ -612,6 +620,7 @@ declare class SelectTool implements Tool {
612
620
  get isMarqueeActive(): boolean;
613
621
  onActivate(ctx: ToolContext): void;
614
622
  onDeactivate(ctx: ToolContext): void;
623
+ private snap;
615
624
  onPointerDown(state: PointerState, ctx: ToolContext): void;
616
625
  onPointerMove(state: PointerState, ctx: ToolContext): void;
617
626
  onPointerUp(_state: PointerState, ctx: ToolContext): void;
@@ -729,10 +738,11 @@ declare class ShapeTool implements Tool {
729
738
  onPointerUp(_state: PointerState, ctx: ToolContext): void;
730
739
  renderOverlay(ctx: CanvasRenderingContext2D): void;
731
740
  private computeRect;
741
+ private snap;
732
742
  private onKeyDown;
733
743
  private onKeyUp;
734
744
  }
735
745
 
736
- declare const VERSION = "0.4.1";
746
+ declare const VERSION = "0.5.0";
737
747
 
738
- export { AddElementCommand, type ArrowElement, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, Background, type BackgroundOptions, type BackgroundPattern, BatchCommand, type Binding, type Bounds, Camera, type CameraOptions, type CanvasElement, type CanvasState, type Command, ElementRenderer, ElementStore, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, EventBus, HandTool, HistoryRecorder, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, InputHandler, NoteEditor, type NoteElement, NoteTool, type NoteToolOptions, PencilTool, type PencilToolOptions, type Point, type PointerState, RemoveElementCommand, SelectTool, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type Size, type StrokeElement, type StrokePoint, type TextElement, TextTool, type TextToolOptions, type Tool, type ToolContext, ToolManager, type ToolName, UpdateElementCommand, VERSION, Viewport, type ViewportOptions, clearStaleBindings, createArrow, createHtmlElement, createId, createImage, createNote, createShape, createStroke, createText, exportState, findBindTarget, findBoundArrows, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getEdgeIntersection, getElementBounds, getElementCenter, isBindable, isNearBezier, parseState, unbindArrow, updateBoundArrow };
748
+ export { AddElementCommand, type ArrowElement, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, Background, type BackgroundOptions, type BackgroundPattern, BatchCommand, type Binding, type Bounds, Camera, type CameraOptions, type CanvasElement, type CanvasState, type Command, ElementRenderer, ElementStore, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, EventBus, HandTool, HistoryRecorder, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, InputHandler, NoteEditor, type NoteElement, NoteTool, type NoteToolOptions, PencilTool, type PencilToolOptions, type Point, type PointerState, RemoveElementCommand, SelectTool, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type Size, type StrokeElement, type StrokePoint, type TextElement, TextTool, type TextToolOptions, type Tool, type ToolContext, ToolManager, type ToolName, UpdateElementCommand, VERSION, Viewport, type ViewportOptions, clearStaleBindings, createArrow, createHtmlElement, createId, createImage, createNote, createShape, createStroke, createText, exportState, findBindTarget, findBoundArrows, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getEdgeIntersection, getElementBounds, getElementCenter, isBindable, isNearBezier, parseState, snapPoint, unbindArrow, updateBoundArrow };
package/dist/index.js CHANGED
@@ -119,6 +119,14 @@ function migrateElement(obj) {
119
119
  }
120
120
  }
121
121
 
122
+ // src/core/snap.ts
123
+ function snapPoint(point, gridSize) {
124
+ return {
125
+ x: Math.round(point.x / gridSize) * gridSize || 0,
126
+ y: Math.round(point.y / gridSize) * gridSize || 0
127
+ };
128
+ }
129
+
122
130
  // src/core/auto-save.ts
123
131
  var DEFAULT_KEY = "fieldnotes-autosave";
124
132
  var DEFAULT_DEBOUNCE_MS = 1e3;
@@ -1501,6 +1509,7 @@ var Viewport = class {
1501
1509
  this.container = container;
1502
1510
  this.camera = new Camera(options.camera);
1503
1511
  this.background = new Background(options.background);
1512
+ this._gridSize = options.background?.spacing ?? 24;
1504
1513
  this.store = new ElementStore();
1505
1514
  this.toolManager = new ToolManager();
1506
1515
  this.renderer = new ElementRenderer();
@@ -1523,7 +1532,9 @@ var Viewport = class {
1523
1532
  editElement: (id) => this.startEditingElement(id),
1524
1533
  setCursor: (cursor) => {
1525
1534
  this.wrapper.style.cursor = cursor;
1526
- }
1535
+ },
1536
+ snapToGrid: false,
1537
+ gridSize: this._gridSize
1527
1538
  };
1528
1539
  this.inputHandler = new InputHandler(this.wrapper, this.camera, {
1529
1540
  toolManager: this.toolManager,
@@ -1568,6 +1579,8 @@ var Viewport = class {
1568
1579
  toolContext;
1569
1580
  resizeObserver = null;
1570
1581
  animFrameId = 0;
1582
+ _snapToGrid = false;
1583
+ _gridSize;
1571
1584
  needsRender = true;
1572
1585
  domNodes = /* @__PURE__ */ new Map();
1573
1586
  htmlContent = /* @__PURE__ */ new Map();
@@ -1575,6 +1588,13 @@ var Viewport = class {
1575
1588
  get ctx() {
1576
1589
  return this.canvasEl.getContext("2d");
1577
1590
  }
1591
+ get snapToGrid() {
1592
+ return this._snapToGrid;
1593
+ }
1594
+ setSnapToGrid(enabled) {
1595
+ this._snapToGrid = enabled;
1596
+ this.toolContext.snapToGrid = enabled;
1597
+ }
1578
1598
  requestRender() {
1579
1599
  this.needsRender = true;
1580
1600
  }
@@ -2323,10 +2343,13 @@ var SelectTool = class {
2323
2343
  this.mode = { type: "idle" };
2324
2344
  ctx.setCursor?.("default");
2325
2345
  }
2346
+ snap(point, ctx) {
2347
+ return ctx.snapToGrid && ctx.gridSize ? snapPoint(point, ctx.gridSize) : point;
2348
+ }
2326
2349
  onPointerDown(state, ctx) {
2327
2350
  this.ctx = ctx;
2328
2351
  const world = ctx.camera.screenToWorld({ x: state.x, y: state.y });
2329
- this.lastWorld = world;
2352
+ this.lastWorld = this.snap(world, ctx);
2330
2353
  this.currentWorld = world;
2331
2354
  const arrowHit = hitTestArrowHandles(world, this._selectedIds, ctx);
2332
2355
  if (arrowHit) {
@@ -2379,9 +2402,10 @@ var SelectTool = class {
2379
2402
  }
2380
2403
  if (this.mode.type === "dragging" && this._selectedIds.length > 0) {
2381
2404
  ctx.setCursor?.("move");
2382
- const dx = world.x - this.lastWorld.x;
2383
- const dy = world.y - this.lastWorld.y;
2384
- this.lastWorld = world;
2405
+ const snapped = this.snap(world, ctx);
2406
+ const dx = snapped.x - this.lastWorld.x;
2407
+ const dy = snapped.y - this.lastWorld.y;
2408
+ this.lastWorld = snapped;
2385
2409
  for (const id of this._selectedIds) {
2386
2410
  const el = ctx.store.getById(id);
2387
2411
  if (!el || el.locked) continue;
@@ -2730,7 +2754,7 @@ var ArrowTool = class {
2730
2754
  this.fromBinding = { elementId: target.id };
2731
2755
  this.fromTarget = target;
2732
2756
  } else {
2733
- this.start = world;
2757
+ this.start = ctx.snapToGrid && ctx.gridSize ? snapPoint(world, ctx.gridSize) : world;
2734
2758
  this.fromBinding = void 0;
2735
2759
  this.fromTarget = null;
2736
2760
  }
@@ -2747,7 +2771,7 @@ var ArrowTool = class {
2747
2771
  this.end = getElementCenter(target);
2748
2772
  this.toTarget = target;
2749
2773
  } else {
2750
- this.end = world;
2774
+ this.end = ctx.snapToGrid && ctx.gridSize ? snapPoint(world, ctx.gridSize) : world;
2751
2775
  this.toTarget = null;
2752
2776
  }
2753
2777
  ctx.requestRender();
@@ -2842,7 +2866,10 @@ var NoteTool = class {
2842
2866
  onPointerMove(_state, _ctx) {
2843
2867
  }
2844
2868
  onPointerUp(state, ctx) {
2845
- const world = ctx.camera.screenToWorld({ x: state.x, y: state.y });
2869
+ let world = ctx.camera.screenToWorld({ x: state.x, y: state.y });
2870
+ if (ctx.snapToGrid && ctx.gridSize) {
2871
+ world = snapPoint(world, ctx.gridSize);
2872
+ }
2846
2873
  const note = createNote({
2847
2874
  position: world,
2848
2875
  size: { ...this.size },
@@ -2883,7 +2910,10 @@ var TextTool = class {
2883
2910
  onPointerMove(_state, _ctx) {
2884
2911
  }
2885
2912
  onPointerUp(state, ctx) {
2886
- const world = ctx.camera.screenToWorld({ x: state.x, y: state.y });
2913
+ let world = ctx.camera.screenToWorld({ x: state.x, y: state.y });
2914
+ if (ctx.snapToGrid && ctx.gridSize) {
2915
+ world = snapPoint(world, ctx.gridSize);
2916
+ }
2887
2917
  const textEl = createText({
2888
2918
  position: world,
2889
2919
  fontSize: this.fontSize,
@@ -2965,12 +2995,12 @@ var ShapeTool = class {
2965
2995
  }
2966
2996
  onPointerDown(state, ctx) {
2967
2997
  this.drawing = true;
2968
- this.start = ctx.camera.screenToWorld({ x: state.x, y: state.y });
2998
+ this.start = this.snap(ctx.camera.screenToWorld({ x: state.x, y: state.y }), ctx);
2969
2999
  this.end = { ...this.start };
2970
3000
  }
2971
3001
  onPointerMove(state, ctx) {
2972
3002
  if (!this.drawing) return;
2973
- this.end = ctx.camera.screenToWorld({ x: state.x, y: state.y });
3003
+ this.end = this.snap(ctx.camera.screenToWorld({ x: state.x, y: state.y }), ctx);
2974
3004
  ctx.requestRender();
2975
3005
  }
2976
3006
  onPointerUp(_state, ctx) {
@@ -3034,6 +3064,9 @@ var ShapeTool = class {
3034
3064
  }
3035
3065
  return { position: { x, y }, size: { w, h } };
3036
3066
  }
3067
+ snap(point, ctx) {
3068
+ return ctx.snapToGrid && ctx.gridSize ? snapPoint(point, ctx.gridSize) : point;
3069
+ }
3037
3070
  onKeyDown = (e) => {
3038
3071
  if (e.key === "Shift") this.shiftHeld = true;
3039
3072
  };
@@ -3043,7 +3076,7 @@ var ShapeTool = class {
3043
3076
  };
3044
3077
 
3045
3078
  // src/index.ts
3046
- var VERSION = "0.4.1";
3079
+ var VERSION = "0.5.0";
3047
3080
  export {
3048
3081
  AddElementCommand,
3049
3082
  ArrowTool,
@@ -3094,6 +3127,7 @@ export {
3094
3127
  isBindable,
3095
3128
  isNearBezier,
3096
3129
  parseState,
3130
+ snapPoint,
3097
3131
  unbindArrow,
3098
3132
  updateBoundArrow
3099
3133
  };