@fieldnotes/core 0.4.0 → 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.cjs CHANGED
@@ -69,6 +69,7 @@ __export(index_exports, {
69
69
  isBindable: () => isBindable,
70
70
  isNearBezier: () => isNearBezier,
71
71
  parseState: () => parseState,
72
+ snapPoint: () => snapPoint,
72
73
  unbindArrow: () => unbindArrow,
73
74
  updateBoundArrow: () => updateBoundArrow
74
75
  });
@@ -190,6 +191,17 @@ function migrateElement(obj) {
190
191
  if (obj["type"] === "shape" && typeof obj["shape"] !== "string") {
191
192
  obj["shape"] = "rectangle";
192
193
  }
194
+ if (obj["type"] === "note" && typeof obj["textColor"] !== "string") {
195
+ obj["textColor"] = "#000000";
196
+ }
197
+ }
198
+
199
+ // src/core/snap.ts
200
+ function snapPoint(point, gridSize) {
201
+ return {
202
+ x: Math.round(point.x / gridSize) * gridSize || 0,
203
+ y: Math.round(point.y / gridSize) * gridSize || 0
204
+ };
193
205
  }
194
206
 
195
207
  // src/core/auto-save.ts
@@ -1497,7 +1509,8 @@ function createNote(input) {
1497
1509
  locked: input.locked ?? false,
1498
1510
  size: input.size ?? { w: 200, h: 100 },
1499
1511
  text: input.text ?? "",
1500
- backgroundColor: input.backgroundColor ?? "#ffeb3b"
1512
+ backgroundColor: input.backgroundColor ?? "#ffeb3b",
1513
+ textColor: input.textColor ?? "#000000"
1501
1514
  };
1502
1515
  }
1503
1516
  function createArrow(input) {
@@ -1573,6 +1586,7 @@ var Viewport = class {
1573
1586
  this.container = container;
1574
1587
  this.camera = new Camera(options.camera);
1575
1588
  this.background = new Background(options.background);
1589
+ this._gridSize = options.background?.spacing ?? 24;
1576
1590
  this.store = new ElementStore();
1577
1591
  this.toolManager = new ToolManager();
1578
1592
  this.renderer = new ElementRenderer();
@@ -1595,7 +1609,9 @@ var Viewport = class {
1595
1609
  editElement: (id) => this.startEditingElement(id),
1596
1610
  setCursor: (cursor) => {
1597
1611
  this.wrapper.style.cursor = cursor;
1598
- }
1612
+ },
1613
+ snapToGrid: false,
1614
+ gridSize: this._gridSize
1599
1615
  };
1600
1616
  this.inputHandler = new InputHandler(this.wrapper, this.camera, {
1601
1617
  toolManager: this.toolManager,
@@ -1640,6 +1656,8 @@ var Viewport = class {
1640
1656
  toolContext;
1641
1657
  resizeObserver = null;
1642
1658
  animFrameId = 0;
1659
+ _snapToGrid = false;
1660
+ _gridSize;
1643
1661
  needsRender = true;
1644
1662
  domNodes = /* @__PURE__ */ new Map();
1645
1663
  htmlContent = /* @__PURE__ */ new Map();
@@ -1647,6 +1665,13 @@ var Viewport = class {
1647
1665
  get ctx() {
1648
1666
  return this.canvasEl.getContext("2d");
1649
1667
  }
1668
+ get snapToGrid() {
1669
+ return this._snapToGrid;
1670
+ }
1671
+ setSnapToGrid(enabled) {
1672
+ this._snapToGrid = enabled;
1673
+ this.toolContext.snapToGrid = enabled;
1674
+ }
1650
1675
  requestRender() {
1651
1676
  this.needsRender = true;
1652
1677
  }
@@ -1896,6 +1921,7 @@ var Viewport = class {
1896
1921
  node.dataset["initialized"] = "true";
1897
1922
  Object.assign(node.style, {
1898
1923
  backgroundColor: element.backgroundColor,
1924
+ color: element.textColor,
1899
1925
  padding: "8px",
1900
1926
  borderRadius: "4px",
1901
1927
  boxShadow: "0 2px 8px rgba(0,0,0,0.15)",
@@ -1917,6 +1943,7 @@ var Viewport = class {
1917
1943
  node.textContent = element.text || "";
1918
1944
  }
1919
1945
  node.style.backgroundColor = element.backgroundColor;
1946
+ node.style.color = element.textColor;
1920
1947
  }
1921
1948
  }
1922
1949
  if (element.type === "image") {
@@ -2393,10 +2420,13 @@ var SelectTool = class {
2393
2420
  this.mode = { type: "idle" };
2394
2421
  ctx.setCursor?.("default");
2395
2422
  }
2423
+ snap(point, ctx) {
2424
+ return ctx.snapToGrid && ctx.gridSize ? snapPoint(point, ctx.gridSize) : point;
2425
+ }
2396
2426
  onPointerDown(state, ctx) {
2397
2427
  this.ctx = ctx;
2398
2428
  const world = ctx.camera.screenToWorld({ x: state.x, y: state.y });
2399
- this.lastWorld = world;
2429
+ this.lastWorld = this.snap(world, ctx);
2400
2430
  this.currentWorld = world;
2401
2431
  const arrowHit = hitTestArrowHandles(world, this._selectedIds, ctx);
2402
2432
  if (arrowHit) {
@@ -2449,9 +2479,10 @@ var SelectTool = class {
2449
2479
  }
2450
2480
  if (this.mode.type === "dragging" && this._selectedIds.length > 0) {
2451
2481
  ctx.setCursor?.("move");
2452
- const dx = world.x - this.lastWorld.x;
2453
- const dy = world.y - this.lastWorld.y;
2454
- this.lastWorld = world;
2482
+ const snapped = this.snap(world, ctx);
2483
+ const dx = snapped.x - this.lastWorld.x;
2484
+ const dy = snapped.y - this.lastWorld.y;
2485
+ this.lastWorld = snapped;
2455
2486
  for (const id of this._selectedIds) {
2456
2487
  const el = ctx.store.getById(id);
2457
2488
  if (!el || el.locked) continue;
@@ -2800,7 +2831,7 @@ var ArrowTool = class {
2800
2831
  this.fromBinding = { elementId: target.id };
2801
2832
  this.fromTarget = target;
2802
2833
  } else {
2803
- this.start = world;
2834
+ this.start = ctx.snapToGrid && ctx.gridSize ? snapPoint(world, ctx.gridSize) : world;
2804
2835
  this.fromBinding = void 0;
2805
2836
  this.fromTarget = null;
2806
2837
  }
@@ -2817,7 +2848,7 @@ var ArrowTool = class {
2817
2848
  this.end = getElementCenter(target);
2818
2849
  this.toTarget = target;
2819
2850
  } else {
2820
- this.end = world;
2851
+ this.end = ctx.snapToGrid && ctx.gridSize ? snapPoint(world, ctx.gridSize) : world;
2821
2852
  this.toTarget = null;
2822
2853
  }
2823
2854
  ctx.requestRender();
@@ -2895,13 +2926,16 @@ var ArrowTool = class {
2895
2926
  var NoteTool = class {
2896
2927
  name = "note";
2897
2928
  backgroundColor;
2929
+ textColor;
2898
2930
  size;
2899
2931
  constructor(options = {}) {
2900
2932
  this.backgroundColor = options.backgroundColor ?? "#ffeb3b";
2933
+ this.textColor = options.textColor ?? "#000000";
2901
2934
  this.size = options.size ?? { w: 200, h: 100 };
2902
2935
  }
2903
2936
  setOptions(options) {
2904
2937
  if (options.backgroundColor !== void 0) this.backgroundColor = options.backgroundColor;
2938
+ if (options.textColor !== void 0) this.textColor = options.textColor;
2905
2939
  if (options.size !== void 0) this.size = options.size;
2906
2940
  }
2907
2941
  onPointerDown(_state, _ctx) {
@@ -2909,11 +2943,15 @@ var NoteTool = class {
2909
2943
  onPointerMove(_state, _ctx) {
2910
2944
  }
2911
2945
  onPointerUp(state, ctx) {
2912
- const world = ctx.camera.screenToWorld({ x: state.x, y: state.y });
2946
+ let world = ctx.camera.screenToWorld({ x: state.x, y: state.y });
2947
+ if (ctx.snapToGrid && ctx.gridSize) {
2948
+ world = snapPoint(world, ctx.gridSize);
2949
+ }
2913
2950
  const note = createNote({
2914
2951
  position: world,
2915
2952
  size: { ...this.size },
2916
- backgroundColor: this.backgroundColor
2953
+ backgroundColor: this.backgroundColor,
2954
+ textColor: this.textColor
2917
2955
  });
2918
2956
  ctx.store.add(note);
2919
2957
  ctx.requestRender();
@@ -2949,7 +2987,10 @@ var TextTool = class {
2949
2987
  onPointerMove(_state, _ctx) {
2950
2988
  }
2951
2989
  onPointerUp(state, ctx) {
2952
- const world = ctx.camera.screenToWorld({ x: state.x, y: state.y });
2990
+ let world = ctx.camera.screenToWorld({ x: state.x, y: state.y });
2991
+ if (ctx.snapToGrid && ctx.gridSize) {
2992
+ world = snapPoint(world, ctx.gridSize);
2993
+ }
2953
2994
  const textEl = createText({
2954
2995
  position: world,
2955
2996
  fontSize: this.fontSize,
@@ -3031,12 +3072,12 @@ var ShapeTool = class {
3031
3072
  }
3032
3073
  onPointerDown(state, ctx) {
3033
3074
  this.drawing = true;
3034
- this.start = ctx.camera.screenToWorld({ x: state.x, y: state.y });
3075
+ this.start = this.snap(ctx.camera.screenToWorld({ x: state.x, y: state.y }), ctx);
3035
3076
  this.end = { ...this.start };
3036
3077
  }
3037
3078
  onPointerMove(state, ctx) {
3038
3079
  if (!this.drawing) return;
3039
- this.end = ctx.camera.screenToWorld({ x: state.x, y: state.y });
3080
+ this.end = this.snap(ctx.camera.screenToWorld({ x: state.x, y: state.y }), ctx);
3040
3081
  ctx.requestRender();
3041
3082
  }
3042
3083
  onPointerUp(_state, ctx) {
@@ -3100,6 +3141,9 @@ var ShapeTool = class {
3100
3141
  }
3101
3142
  return { position: { x, y }, size: { w, h } };
3102
3143
  }
3144
+ snap(point, ctx) {
3145
+ return ctx.snapToGrid && ctx.gridSize ? snapPoint(point, ctx.gridSize) : point;
3146
+ }
3103
3147
  onKeyDown = (e) => {
3104
3148
  if (e.key === "Shift") this.shiftHeld = true;
3105
3149
  };
@@ -3109,7 +3153,7 @@ var ShapeTool = class {
3109
3153
  };
3110
3154
 
3111
3155
  // src/index.ts
3112
- var VERSION = "0.4.0";
3156
+ var VERSION = "0.5.0";
3113
3157
  // Annotate the CommonJS export names for ESM import in node:
3114
3158
  0 && (module.exports = {
3115
3159
  AddElementCommand,
@@ -3161,6 +3205,7 @@ var VERSION = "0.4.0";
3161
3205
  isBindable,
3162
3206
  isNearBezier,
3163
3207
  parseState,
3208
+ snapPoint,
3164
3209
  unbindArrow,
3165
3210
  updateBoundArrow
3166
3211
  });