@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.d.cts CHANGED
@@ -487,6 +487,8 @@ interface ElementStyle {
487
487
  declare function styleToPatch(element: CanvasElement, style: ElementStyle): Partial<CanvasElement>;
488
488
  declare function getElementStyle(element: CanvasElement): ElementStyle;
489
489
 
490
+ type RotateDirection = 'cw' | 'ccw';
491
+
490
492
  type AlignEdge = 'left' | 'center-x' | 'right' | 'top' | 'middle' | 'bottom';
491
493
  type DistributeAxis = 'horizontal' | 'vertical';
492
494
 
@@ -641,6 +643,7 @@ declare class Viewport {
641
643
  toggleLockSelection(): void;
642
644
  alignSelection(edge: AlignEdge): void;
643
645
  distributeSelection(axis: DistributeAxis): void;
646
+ rotateSelection(direction: RotateDirection): void;
644
647
  getRenderStats(): RenderStatsSnapshot;
645
648
  logPerformance(intervalMs?: number): () => void;
646
649
  destroy(): void;
@@ -1120,6 +1123,6 @@ declare class LaserTool implements Tool {
1120
1123
  private notifyOptionsChange;
1121
1124
  }
1122
1125
 
1123
- declare const VERSION = "0.48.0";
1126
+ declare const VERSION = "0.50.0";
1124
1127
 
1125
- 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, type ElementChangeMeta, 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, IndexedDBAdapter, type IndexedDBAdapterOptions, LaserTool, type LaserToolOptions, type Layer, LayerManager, LocalStorageAdapter, MeasureTool, type MeasureToolOptions, type Measurement, MemoryAdapter, 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 StorageAdapter, type StrokeElement, type StrokePoint, type TemplateElement, type TemplateRenderStyle, 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, getHexCellsInRectangle, getHexCellsInSquare, getHexDistance, isNearBezier, setFontSize, smartSnap, snapPoint, snapToHexCenter, styleToPatch, toggleBold, toggleItalic, toggleStrikethrough, toggleUnderline };
1128
+ 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, type ElementChangeMeta, 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, IndexedDBAdapter, type IndexedDBAdapterOptions, LaserTool, type LaserToolOptions, type Layer, LayerManager, LocalStorageAdapter, MeasureTool, type MeasureToolOptions, type Measurement, MemoryAdapter, type NoteElement, NoteTool, type NoteToolOptions, PencilTool, type PencilToolOptions, type Point, type PointerState, type RenderStatsSnapshot, type RotateDirection, SelectTool, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type ShortcutBindings, type ShortcutOptions, type ShortcutsApi, type Size, type StorageAdapter, type StrokeElement, type StrokePoint, type TemplateElement, type TemplateRenderStyle, 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, getHexCellsInRectangle, getHexCellsInSquare, getHexDistance, isNearBezier, setFontSize, smartSnap, snapPoint, snapToHexCenter, styleToPatch, toggleBold, toggleItalic, toggleStrikethrough, toggleUnderline };
package/dist/index.d.ts CHANGED
@@ -487,6 +487,8 @@ interface ElementStyle {
487
487
  declare function styleToPatch(element: CanvasElement, style: ElementStyle): Partial<CanvasElement>;
488
488
  declare function getElementStyle(element: CanvasElement): ElementStyle;
489
489
 
490
+ type RotateDirection = 'cw' | 'ccw';
491
+
490
492
  type AlignEdge = 'left' | 'center-x' | 'right' | 'top' | 'middle' | 'bottom';
491
493
  type DistributeAxis = 'horizontal' | 'vertical';
492
494
 
@@ -641,6 +643,7 @@ declare class Viewport {
641
643
  toggleLockSelection(): void;
642
644
  alignSelection(edge: AlignEdge): void;
643
645
  distributeSelection(axis: DistributeAxis): void;
646
+ rotateSelection(direction: RotateDirection): void;
644
647
  getRenderStats(): RenderStatsSnapshot;
645
648
  logPerformance(intervalMs?: number): () => void;
646
649
  destroy(): void;
@@ -1120,6 +1123,6 @@ declare class LaserTool implements Tool {
1120
1123
  private notifyOptionsChange;
1121
1124
  }
1122
1125
 
1123
- declare const VERSION = "0.48.0";
1126
+ declare const VERSION = "0.50.0";
1124
1127
 
1125
- 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, type ElementChangeMeta, 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, IndexedDBAdapter, type IndexedDBAdapterOptions, LaserTool, type LaserToolOptions, type Layer, LayerManager, LocalStorageAdapter, MeasureTool, type MeasureToolOptions, type Measurement, MemoryAdapter, 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 StorageAdapter, type StrokeElement, type StrokePoint, type TemplateElement, type TemplateRenderStyle, 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, getHexCellsInRectangle, getHexCellsInSquare, getHexDistance, isNearBezier, setFontSize, smartSnap, snapPoint, snapToHexCenter, styleToPatch, toggleBold, toggleItalic, toggleStrikethrough, toggleUnderline };
1128
+ 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, type ElementChangeMeta, 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, IndexedDBAdapter, type IndexedDBAdapterOptions, LaserTool, type LaserToolOptions, type Layer, LayerManager, LocalStorageAdapter, MeasureTool, type MeasureToolOptions, type Measurement, MemoryAdapter, type NoteElement, NoteTool, type NoteToolOptions, PencilTool, type PencilToolOptions, type Point, type PointerState, type RenderStatsSnapshot, type RotateDirection, SelectTool, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type ShortcutBindings, type ShortcutOptions, type ShortcutsApi, type Size, type StorageAdapter, type StrokeElement, type StrokePoint, type TemplateElement, type TemplateRenderStyle, 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, getHexCellsInRectangle, getHexCellsInSquare, getHexDistance, isNearBezier, setFontSize, smartSnap, snapPoint, snapToHexCenter, styleToPatch, toggleBold, toggleItalic, toggleStrikethrough, toggleUnderline };
package/dist/index.js CHANGED
@@ -1155,6 +1155,14 @@ var KeyboardActions = class {
1155
1155
  if (this.deps.isToolActive()) return;
1156
1156
  this.deps.toggleLock?.();
1157
1157
  }
1158
+ rotate(direction) {
1159
+ if (this.deps.isToolActive()) return;
1160
+ this.flushPendingNudge();
1161
+ const sel = this.selectTool();
1162
+ if (!sel) return;
1163
+ if (sel.tool.selectedIds.length === 0) return;
1164
+ this.deps.rotate?.(direction);
1165
+ }
1158
1166
  zOrder(operation) {
1159
1167
  if (this.deps.isToolActive()) return;
1160
1168
  this.flushPendingNudge();
@@ -1259,6 +1267,8 @@ var DEFAULT_BINDINGS = [
1259
1267
  ["ungroup", ["mod+shift+g"]],
1260
1268
  ["cut", ["mod+x"]],
1261
1269
  ["toggle-lock", ["mod+shift+l"]],
1270
+ ["rotate-cw", ["r"]],
1271
+ ["rotate-ccw", ["shift+r"]],
1262
1272
  ["nudge-left", ["arrowleft"]],
1263
1273
  ["nudge-right", ["arrowright"]],
1264
1274
  ["nudge-up", ["arrowup"]],
@@ -1571,6 +1581,14 @@ var KeyboardHandler = class {
1571
1581
  e?.preventDefault();
1572
1582
  this.deps.actions.toggleLock();
1573
1583
  return;
1584
+ case "rotate-cw":
1585
+ e?.preventDefault();
1586
+ this.deps.actions.rotate("cw");
1587
+ return;
1588
+ case "rotate-ccw":
1589
+ e?.preventDefault();
1590
+ this.deps.actions.rotate("ccw");
1591
+ return;
1574
1592
  case "zoom-in":
1575
1593
  e?.preventDefault();
1576
1594
  this.zoomByFactor(ZOOM_STEP);
@@ -1707,6 +1725,7 @@ var InputHandler = class {
1707
1725
  group: options.group,
1708
1726
  ungroup: options.ungroup,
1709
1727
  toggleLock: options.toggleLock,
1728
+ rotate: options.rotate,
1710
1729
  getLastPointerWorld: () => this.lastPointerWorld()
1711
1730
  });
1712
1731
  this.openContextMenu = options.openContextMenu;
@@ -6795,7 +6814,7 @@ function getElementStyle(element) {
6795
6814
  }
6796
6815
  }
6797
6816
 
6798
- // src/canvas/selection-ops.ts
6817
+ // src/canvas/selection-rotate.ts
6799
6818
  function unionBounds2(list) {
6800
6819
  let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
6801
6820
  for (const b of list) {
@@ -6806,6 +6825,39 @@ function unionBounds2(list) {
6806
6825
  }
6807
6826
  return { x: minX, y: minY, w: maxX - minX, h: maxY - minY };
6808
6827
  }
6828
+ function rotationPivot(items) {
6829
+ const first = items[0];
6830
+ if (items.length === 1 && first) {
6831
+ return { x: first.bounds.x + first.bounds.w / 2, y: first.bounds.y + first.bounds.h / 2 };
6832
+ }
6833
+ const u = unionBounds2(items.map((it) => rotatedAABB(it.bounds, it.el.rotation ?? 0)));
6834
+ return { x: u.x + u.w / 2, y: u.y + u.h / 2 };
6835
+ }
6836
+ function rotateElementPatch(el, bounds, pivot, delta) {
6837
+ if (el.type === "arrow") {
6838
+ const center3 = { x: bounds.x + bounds.w / 2, y: bounds.y + bounds.h / 2 };
6839
+ const moved2 = rotatePoint(center3, pivot, delta);
6840
+ return {
6841
+ position: { x: el.position.x + moved2.x - center3.x, y: el.position.y + moved2.y - center3.y },
6842
+ from: rotatePoint(el.from, pivot, delta),
6843
+ to: rotatePoint(el.to, pivot, delta)
6844
+ };
6845
+ }
6846
+ if (el.type === "template") {
6847
+ return {
6848
+ position: rotatePoint(el.position, pivot, delta),
6849
+ angle: normalizeAngle(el.angle + delta)
6850
+ };
6851
+ }
6852
+ const center2 = { x: bounds.x + bounds.w / 2, y: bounds.y + bounds.h / 2 };
6853
+ const moved = rotatePoint(center2, pivot, delta);
6854
+ return {
6855
+ position: { x: el.position.x + moved.x - center2.x, y: el.position.y + moved.y - center2.y },
6856
+ rotation: normalizeAngle((el.rotation ?? 0) + delta)
6857
+ };
6858
+ }
6859
+
6860
+ // src/canvas/selection-ops.ts
6809
6861
  function sharedValue(values) {
6810
6862
  const present = values.filter((v) => v !== void 0);
6811
6863
  if (present.length === 0) return void 0;
@@ -6953,6 +7005,21 @@ var SelectionOps = class {
6953
7005
  this.deps.recorder.commit();
6954
7006
  this.deps.requestRender();
6955
7007
  }
7008
+ rotateSelection(direction) {
7009
+ const eligible = this.boundedSelection().filter(({ el }) => this.isMovable(el));
7010
+ if (eligible.length === 0) return;
7011
+ const delta = direction === "cw" ? Math.PI / 2 : -Math.PI / 2;
7012
+ const pivot = rotationPivot(eligible);
7013
+ this.deps.recorder.begin();
7014
+ const moved = [];
7015
+ for (const { id, el, bounds } of eligible) {
7016
+ this.deps.store.update(id, rotateElementPatch(el, bounds, pivot, delta));
7017
+ moved.push(id);
7018
+ }
7019
+ updateArrowsBoundToElements(moved, this.deps.store);
7020
+ this.deps.recorder.commit();
7021
+ this.deps.requestRender();
7022
+ }
6956
7023
  boundedSelection() {
6957
7024
  const out = [];
6958
7025
  for (const id of this.deps.getSelectedIds()) {
@@ -7281,6 +7348,7 @@ var Viewport = class {
7281
7348
  group: () => this.groupSelection(),
7282
7349
  ungroup: () => this.ungroupSelection(),
7283
7350
  toggleLock: () => this.toggleLockSelection(),
7351
+ rotate: (direction) => this.rotateSelection(direction),
7284
7352
  openContextMenu: (screenPos, world) => {
7285
7353
  this.getSelectTool()?.selectAtPoint(world, this.toolContext);
7286
7354
  this.openContextMenu(screenPos);
@@ -7678,6 +7746,8 @@ var Viewport = class {
7678
7746
  items.push({ label: "Bring Forward", action: "z-forward" });
7679
7747
  items.push({ label: "Send Backward", action: "z-backward" });
7680
7748
  items.push({ label: "Send to Back", action: "z-back" });
7749
+ items.push({ label: "Rotate 90\xB0 CW", action: "rotate-cw" });
7750
+ items.push({ label: "Rotate 90\xB0 CCW", action: "rotate-ccw" });
7681
7751
  const allLocked = ids.every((id) => this.store.getById(id)?.locked);
7682
7752
  items.push({ label: allLocked ? "Unlock" : "Lock", action: "toggle-lock" });
7683
7753
  } else if (this.canPaste()) {
@@ -7711,6 +7781,9 @@ var Viewport = class {
7711
7781
  distributeSelection(axis) {
7712
7782
  this.selectionOps.distribute(axis);
7713
7783
  }
7784
+ rotateSelection(direction) {
7785
+ this.selectionOps.rotateSelection(direction);
7786
+ }
7714
7787
  getRenderStats() {
7715
7788
  return this.renderLoop.getStats();
7716
7789
  }
@@ -8805,7 +8878,7 @@ function anchorOffset(handle, w, h) {
8805
8878
  return { x: 0, y: 0 };
8806
8879
  }
8807
8880
  }
8808
- function computeResize(el, handle, world, lastWorld, aspectRatio, shiftKey) {
8881
+ function computeResize(el, handle, world, lastWorld, aspectRatio, lockAspect) {
8809
8882
  const dx = world.x - lastWorld.x;
8810
8883
  const dy = world.y - lastWorld.y;
8811
8884
  let { x, y, w, h } = { x: el.position.x, y: el.position.y, w: el.size.w, h: el.size.h };
@@ -8831,7 +8904,7 @@ function computeResize(el, handle, world, lastWorld, aspectRatio, shiftKey) {
8831
8904
  h -= dy;
8832
8905
  break;
8833
8906
  }
8834
- if (shiftKey && aspectRatio > 0) {
8907
+ if (lockAspect && aspectRatio > 0) {
8835
8908
  const absDw = Math.abs(w - el.size.w);
8836
8909
  const absDh = Math.abs(h - el.size.h);
8837
8910
  if (absDw >= absDh) {
@@ -8856,7 +8929,7 @@ function computeResize(el, handle, world, lastWorld, aspectRatio, shiftKey) {
8856
8929
  }
8857
8930
  return { position: { x, y }, size: { w, h } };
8858
8931
  }
8859
- function computeRotatedResize(el, handle, angle, world, lastWorld, aspectRatio, shiftKey) {
8932
+ function computeRotatedResize(el, handle, angle, world, lastWorld, aspectRatio, lockAspect) {
8860
8933
  const wdx = world.x - lastWorld.x;
8861
8934
  const wdy = world.y - lastWorld.y;
8862
8935
  const cosN = Math.cos(-angle);
@@ -8883,7 +8956,7 @@ function computeRotatedResize(el, handle, angle, world, lastWorld, aspectRatio,
8883
8956
  h -= ldy;
8884
8957
  break;
8885
8958
  }
8886
- if (shiftKey && aspectRatio > 0) {
8959
+ if (lockAspect && aspectRatio > 0) {
8887
8960
  const absDw = Math.abs(w - el.size.w);
8888
8961
  const absDh = Math.abs(h - el.size.h);
8889
8962
  if (absDw >= absDh) h = w / aspectRatio;
@@ -9413,6 +9486,7 @@ var SelectTool = class {
9413
9486
  if (this.mode.type !== "resizing") return;
9414
9487
  const el = ctx.store.getById(this.mode.elementId);
9415
9488
  if (!el || !("size" in el) || el.locked) return;
9489
+ const lockAspect = shiftKey !== (el.type === "image");
9416
9490
  const angle = el.rotation ?? 0;
9417
9491
  const patch = angle !== 0 ? computeRotatedResize(
9418
9492
  el,
@@ -9421,14 +9495,14 @@ var SelectTool = class {
9421
9495
  world,
9422
9496
  this.lastWorld,
9423
9497
  this.resizeAspectRatio,
9424
- shiftKey
9498
+ lockAspect
9425
9499
  ) : computeResize(
9426
9500
  el,
9427
9501
  this.mode.handle,
9428
9502
  world,
9429
9503
  this.lastWorld,
9430
9504
  this.resizeAspectRatio,
9431
- shiftKey
9505
+ lockAspect
9432
9506
  );
9433
9507
  this.lastWorld = world;
9434
9508
  ctx.store.update(this.mode.elementId, patch);
@@ -10453,7 +10527,7 @@ var LaserTool = class {
10453
10527
  };
10454
10528
 
10455
10529
  // src/index.ts
10456
- var VERSION = "0.48.0";
10530
+ var VERSION = "0.50.0";
10457
10531
  export {
10458
10532
  ArrowTool,
10459
10533
  AutoSave,