@fieldnotes/core 0.48.0 → 0.50.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
@@ -1242,6 +1242,14 @@ var KeyboardActions = class {
1242
1242
  if (this.deps.isToolActive()) return;
1243
1243
  this.deps.toggleLock?.();
1244
1244
  }
1245
+ rotate(direction) {
1246
+ if (this.deps.isToolActive()) return;
1247
+ this.flushPendingNudge();
1248
+ const sel = this.selectTool();
1249
+ if (!sel) return;
1250
+ if (sel.tool.selectedIds.length === 0) return;
1251
+ this.deps.rotate?.(direction);
1252
+ }
1245
1253
  zOrder(operation) {
1246
1254
  if (this.deps.isToolActive()) return;
1247
1255
  this.flushPendingNudge();
@@ -1346,6 +1354,8 @@ var DEFAULT_BINDINGS = [
1346
1354
  ["ungroup", ["mod+shift+g"]],
1347
1355
  ["cut", ["mod+x"]],
1348
1356
  ["toggle-lock", ["mod+shift+l"]],
1357
+ ["rotate-cw", ["r"]],
1358
+ ["rotate-ccw", ["shift+r"]],
1349
1359
  ["nudge-left", ["arrowleft"]],
1350
1360
  ["nudge-right", ["arrowright"]],
1351
1361
  ["nudge-up", ["arrowup"]],
@@ -1658,6 +1668,14 @@ var KeyboardHandler = class {
1658
1668
  e?.preventDefault();
1659
1669
  this.deps.actions.toggleLock();
1660
1670
  return;
1671
+ case "rotate-cw":
1672
+ e?.preventDefault();
1673
+ this.deps.actions.rotate("cw");
1674
+ return;
1675
+ case "rotate-ccw":
1676
+ e?.preventDefault();
1677
+ this.deps.actions.rotate("ccw");
1678
+ return;
1661
1679
  case "zoom-in":
1662
1680
  e?.preventDefault();
1663
1681
  this.zoomByFactor(ZOOM_STEP);
@@ -1794,6 +1812,7 @@ var InputHandler = class {
1794
1812
  group: options.group,
1795
1813
  ungroup: options.ungroup,
1796
1814
  toggleLock: options.toggleLock,
1815
+ rotate: options.rotate,
1797
1816
  getLastPointerWorld: () => this.lastPointerWorld()
1798
1817
  });
1799
1818
  this.openContextMenu = options.openContextMenu;
@@ -6882,7 +6901,7 @@ function getElementStyle(element) {
6882
6901
  }
6883
6902
  }
6884
6903
 
6885
- // src/canvas/selection-ops.ts
6904
+ // src/canvas/selection-rotate.ts
6886
6905
  function unionBounds2(list) {
6887
6906
  let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
6888
6907
  for (const b of list) {
@@ -6893,6 +6912,39 @@ function unionBounds2(list) {
6893
6912
  }
6894
6913
  return { x: minX, y: minY, w: maxX - minX, h: maxY - minY };
6895
6914
  }
6915
+ function rotationPivot(items) {
6916
+ const first = items[0];
6917
+ if (items.length === 1 && first) {
6918
+ return { x: first.bounds.x + first.bounds.w / 2, y: first.bounds.y + first.bounds.h / 2 };
6919
+ }
6920
+ const u = unionBounds2(items.map((it) => rotatedAABB(it.bounds, it.el.rotation ?? 0)));
6921
+ return { x: u.x + u.w / 2, y: u.y + u.h / 2 };
6922
+ }
6923
+ function rotateElementPatch(el, bounds, pivot, delta) {
6924
+ if (el.type === "arrow") {
6925
+ const center3 = { x: bounds.x + bounds.w / 2, y: bounds.y + bounds.h / 2 };
6926
+ const moved2 = rotatePoint(center3, pivot, delta);
6927
+ return {
6928
+ position: { x: el.position.x + moved2.x - center3.x, y: el.position.y + moved2.y - center3.y },
6929
+ from: rotatePoint(el.from, pivot, delta),
6930
+ to: rotatePoint(el.to, pivot, delta)
6931
+ };
6932
+ }
6933
+ if (el.type === "template") {
6934
+ return {
6935
+ position: rotatePoint(el.position, pivot, delta),
6936
+ angle: normalizeAngle(el.angle + delta)
6937
+ };
6938
+ }
6939
+ const center2 = { x: bounds.x + bounds.w / 2, y: bounds.y + bounds.h / 2 };
6940
+ const moved = rotatePoint(center2, pivot, delta);
6941
+ return {
6942
+ position: { x: el.position.x + moved.x - center2.x, y: el.position.y + moved.y - center2.y },
6943
+ rotation: normalizeAngle((el.rotation ?? 0) + delta)
6944
+ };
6945
+ }
6946
+
6947
+ // src/canvas/selection-ops.ts
6896
6948
  function sharedValue(values) {
6897
6949
  const present = values.filter((v) => v !== void 0);
6898
6950
  if (present.length === 0) return void 0;
@@ -7040,6 +7092,21 @@ var SelectionOps = class {
7040
7092
  this.deps.recorder.commit();
7041
7093
  this.deps.requestRender();
7042
7094
  }
7095
+ rotateSelection(direction) {
7096
+ const eligible = this.boundedSelection().filter(({ el }) => this.isMovable(el));
7097
+ if (eligible.length === 0) return;
7098
+ const delta = direction === "cw" ? Math.PI / 2 : -Math.PI / 2;
7099
+ const pivot = rotationPivot(eligible);
7100
+ this.deps.recorder.begin();
7101
+ const moved = [];
7102
+ for (const { id, el, bounds } of eligible) {
7103
+ this.deps.store.update(id, rotateElementPatch(el, bounds, pivot, delta));
7104
+ moved.push(id);
7105
+ }
7106
+ updateArrowsBoundToElements(moved, this.deps.store);
7107
+ this.deps.recorder.commit();
7108
+ this.deps.requestRender();
7109
+ }
7043
7110
  boundedSelection() {
7044
7111
  const out = [];
7045
7112
  for (const id of this.deps.getSelectedIds()) {
@@ -7368,6 +7435,7 @@ var Viewport = class {
7368
7435
  group: () => this.groupSelection(),
7369
7436
  ungroup: () => this.ungroupSelection(),
7370
7437
  toggleLock: () => this.toggleLockSelection(),
7438
+ rotate: (direction) => this.rotateSelection(direction),
7371
7439
  openContextMenu: (screenPos, world) => {
7372
7440
  this.getSelectTool()?.selectAtPoint(world, this.toolContext);
7373
7441
  this.openContextMenu(screenPos);
@@ -7765,6 +7833,8 @@ var Viewport = class {
7765
7833
  items.push({ label: "Bring Forward", action: "z-forward" });
7766
7834
  items.push({ label: "Send Backward", action: "z-backward" });
7767
7835
  items.push({ label: "Send to Back", action: "z-back" });
7836
+ items.push({ label: "Rotate 90\xB0 CW", action: "rotate-cw" });
7837
+ items.push({ label: "Rotate 90\xB0 CCW", action: "rotate-ccw" });
7768
7838
  const allLocked = ids.every((id) => this.store.getById(id)?.locked);
7769
7839
  items.push({ label: allLocked ? "Unlock" : "Lock", action: "toggle-lock" });
7770
7840
  } else if (this.canPaste()) {
@@ -7798,6 +7868,9 @@ var Viewport = class {
7798
7868
  distributeSelection(axis) {
7799
7869
  this.selectionOps.distribute(axis);
7800
7870
  }
7871
+ rotateSelection(direction) {
7872
+ this.selectionOps.rotateSelection(direction);
7873
+ }
7801
7874
  getRenderStats() {
7802
7875
  return this.renderLoop.getStats();
7803
7876
  }
@@ -8892,7 +8965,7 @@ function anchorOffset(handle, w, h) {
8892
8965
  return { x: 0, y: 0 };
8893
8966
  }
8894
8967
  }
8895
- function computeResize(el, handle, world, lastWorld, aspectRatio, shiftKey) {
8968
+ function computeResize(el, handle, world, lastWorld, aspectRatio, lockAspect) {
8896
8969
  const dx = world.x - lastWorld.x;
8897
8970
  const dy = world.y - lastWorld.y;
8898
8971
  let { x, y, w, h } = { x: el.position.x, y: el.position.y, w: el.size.w, h: el.size.h };
@@ -8918,7 +8991,7 @@ function computeResize(el, handle, world, lastWorld, aspectRatio, shiftKey) {
8918
8991
  h -= dy;
8919
8992
  break;
8920
8993
  }
8921
- if (shiftKey && aspectRatio > 0) {
8994
+ if (lockAspect && aspectRatio > 0) {
8922
8995
  const absDw = Math.abs(w - el.size.w);
8923
8996
  const absDh = Math.abs(h - el.size.h);
8924
8997
  if (absDw >= absDh) {
@@ -8943,7 +9016,7 @@ function computeResize(el, handle, world, lastWorld, aspectRatio, shiftKey) {
8943
9016
  }
8944
9017
  return { position: { x, y }, size: { w, h } };
8945
9018
  }
8946
- function computeRotatedResize(el, handle, angle, world, lastWorld, aspectRatio, shiftKey) {
9019
+ function computeRotatedResize(el, handle, angle, world, lastWorld, aspectRatio, lockAspect) {
8947
9020
  const wdx = world.x - lastWorld.x;
8948
9021
  const wdy = world.y - lastWorld.y;
8949
9022
  const cosN = Math.cos(-angle);
@@ -8970,7 +9043,7 @@ function computeRotatedResize(el, handle, angle, world, lastWorld, aspectRatio,
8970
9043
  h -= ldy;
8971
9044
  break;
8972
9045
  }
8973
- if (shiftKey && aspectRatio > 0) {
9046
+ if (lockAspect && aspectRatio > 0) {
8974
9047
  const absDw = Math.abs(w - el.size.w);
8975
9048
  const absDh = Math.abs(h - el.size.h);
8976
9049
  if (absDw >= absDh) h = w / aspectRatio;
@@ -9500,6 +9573,7 @@ var SelectTool = class {
9500
9573
  if (this.mode.type !== "resizing") return;
9501
9574
  const el = ctx.store.getById(this.mode.elementId);
9502
9575
  if (!el || !("size" in el) || el.locked) return;
9576
+ const lockAspect = shiftKey !== (el.type === "image");
9503
9577
  const angle = el.rotation ?? 0;
9504
9578
  const patch = angle !== 0 ? computeRotatedResize(
9505
9579
  el,
@@ -9508,14 +9582,14 @@ var SelectTool = class {
9508
9582
  world,
9509
9583
  this.lastWorld,
9510
9584
  this.resizeAspectRatio,
9511
- shiftKey
9585
+ lockAspect
9512
9586
  ) : computeResize(
9513
9587
  el,
9514
9588
  this.mode.handle,
9515
9589
  world,
9516
9590
  this.lastWorld,
9517
9591
  this.resizeAspectRatio,
9518
- shiftKey
9592
+ lockAspect
9519
9593
  );
9520
9594
  this.lastWorld = world;
9521
9595
  ctx.store.update(this.mode.elementId, patch);
@@ -10540,7 +10614,7 @@ var LaserTool = class {
10540
10614
  };
10541
10615
 
10542
10616
  // src/index.ts
10543
- var VERSION = "0.48.0";
10617
+ var VERSION = "0.50.0";
10544
10618
  // Annotate the CommonJS export names for ESM import in node:
10545
10619
  0 && (module.exports = {
10546
10620
  ArrowTool,