@fieldnotes/core 0.41.0 → 0.42.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
@@ -469,6 +469,8 @@ interface ViewportOptions {
469
469
  panBufferMargin?: number;
470
470
  /** Enable the built-in context menu. Default `true`. */
471
471
  contextMenu?: boolean;
472
+ /** Coast (inertial glide) after a pan flick. Default `true`. */
473
+ panInertia?: boolean;
472
474
  }
473
475
  declare class Viewport {
474
476
  private readonly container;
@@ -1055,6 +1057,6 @@ declare class LaserTool implements Tool {
1055
1057
  private notifyOptionsChange;
1056
1058
  }
1057
1059
 
1058
- declare const VERSION = "0.41.0";
1060
+ declare const VERSION = "0.42.0";
1059
1061
 
1060
1062
  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
@@ -469,6 +469,8 @@ interface ViewportOptions {
469
469
  panBufferMargin?: number;
470
470
  /** Enable the built-in context menu. Default `true`. */
471
471
  contextMenu?: boolean;
472
+ /** Coast (inertial glide) after a pan flick. Default `true`. */
473
+ panInertia?: boolean;
472
474
  }
473
475
  declare class Viewport {
474
476
  private readonly container;
@@ -1055,6 +1057,6 @@ declare class LaserTool implements Tool {
1055
1057
  private notifyOptionsChange;
1056
1058
  }
1057
1059
 
1058
- declare const VERSION = "0.41.0";
1060
+ declare const VERSION = "0.42.0";
1059
1061
 
1060
1062
  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
@@ -1471,6 +1471,81 @@ var KeyboardHandler = class {
1471
1471
  }
1472
1472
  };
1473
1473
 
1474
+ // src/canvas/pan-inertia.ts
1475
+ var VELOCITY_WINDOW_MS = 60;
1476
+ var FRICTION = 0.92;
1477
+ var MIN_START_SPEED = 2;
1478
+ var MIN_STOP_SPEED = 0.3;
1479
+ var PanInertia = class {
1480
+ constructor(deps) {
1481
+ this.deps = deps;
1482
+ }
1483
+ samples = [];
1484
+ vx = 0;
1485
+ vy = 0;
1486
+ rafId = null;
1487
+ sample(dx, dy) {
1488
+ const t = this.deps.now();
1489
+ this.samples.push({ dx, dy, t });
1490
+ this.prune(t);
1491
+ }
1492
+ release() {
1493
+ if (!this.deps.enabled()) {
1494
+ this.reset();
1495
+ return;
1496
+ }
1497
+ const t = this.deps.now();
1498
+ this.prune(t);
1499
+ const recent = this.samples;
1500
+ if (recent.length === 0) {
1501
+ this.reset();
1502
+ return;
1503
+ }
1504
+ let sx = 0;
1505
+ let sy = 0;
1506
+ for (const s of recent) {
1507
+ sx += s.dx;
1508
+ sy += s.dy;
1509
+ }
1510
+ this.vx = sx / recent.length;
1511
+ this.vy = sy / recent.length;
1512
+ this.samples = [];
1513
+ if (Math.hypot(this.vx, this.vy) < MIN_START_SPEED) {
1514
+ this.vx = 0;
1515
+ this.vy = 0;
1516
+ return;
1517
+ }
1518
+ this.rafId = this.deps.requestFrame(this.step);
1519
+ }
1520
+ cancel() {
1521
+ this.reset();
1522
+ }
1523
+ step = () => {
1524
+ if (this.rafId === null) return;
1525
+ this.deps.pan(this.vx, this.vy);
1526
+ this.vx *= FRICTION;
1527
+ this.vy *= FRICTION;
1528
+ if (Math.hypot(this.vx, this.vy) >= MIN_STOP_SPEED) {
1529
+ this.rafId = this.deps.requestFrame(this.step);
1530
+ } else {
1531
+ this.reset();
1532
+ }
1533
+ };
1534
+ prune(now) {
1535
+ const cutoff = now - VELOCITY_WINDOW_MS;
1536
+ this.samples = this.samples.filter((s) => s.t >= cutoff);
1537
+ }
1538
+ reset() {
1539
+ if (this.rafId !== null) {
1540
+ this.deps.cancelFrame(this.rafId);
1541
+ this.rafId = null;
1542
+ }
1543
+ this.samples = [];
1544
+ this.vx = 0;
1545
+ this.vy = 0;
1546
+ }
1547
+ };
1548
+
1474
1549
  // src/canvas/input-handler.ts
1475
1550
  var ZOOM_SENSITIVITY = 1e-3;
1476
1551
  var MIDDLE_BUTTON = 1;
@@ -1496,6 +1571,16 @@ var InputHandler = class {
1496
1571
  getLastPointerWorld: () => this.lastPointerWorld()
1497
1572
  });
1498
1573
  this.openContextMenu = options.openContextMenu;
1574
+ this.panInertiaEnabled = options.panInertia ?? true;
1575
+ this.panInertia = new PanInertia({
1576
+ pan: (dx, dy) => this.camera.pan(dx, dy),
1577
+ now: () => typeof performance !== "undefined" ? performance.now() : Date.now(),
1578
+ requestFrame: (cb) => typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame(cb) : 0,
1579
+ cancelFrame: (id) => {
1580
+ if (typeof cancelAnimationFrame !== "undefined") cancelAnimationFrame(id);
1581
+ },
1582
+ enabled: () => this.panInertiaEnabled
1583
+ });
1499
1584
  this.scope = options.shortcuts?.scope ?? "focus";
1500
1585
  this.keyboard = new KeyboardHandler({
1501
1586
  element: this.element,
@@ -1545,6 +1630,8 @@ var InputHandler = class {
1545
1630
  keyboard;
1546
1631
  scope;
1547
1632
  openContextMenu;
1633
+ panInertia;
1634
+ panInertiaEnabled;
1548
1635
  setToolManager(toolManager, toolContext) {
1549
1636
  this.toolManager = toolManager;
1550
1637
  this.toolContext = toolContext;
@@ -1556,6 +1643,7 @@ var InputHandler = class {
1556
1643
  return this.keyboard.shortcuts;
1557
1644
  }
1558
1645
  destroy() {
1646
+ this.panInertia.cancel();
1559
1647
  this.actions.dispose();
1560
1648
  this.abortController.abort();
1561
1649
  this.inputFilter.reset();
@@ -1579,6 +1667,7 @@ var InputHandler = class {
1579
1667
  }
1580
1668
  onWheel = (e) => {
1581
1669
  e.preventDefault();
1670
+ this.panInertia.cancel();
1582
1671
  const rect = this.element.getBoundingClientRect();
1583
1672
  const zoomFactor = 1 - e.deltaY * ZOOM_SENSITIVITY;
1584
1673
  const newZoom = this.camera.zoom * zoomFactor;
@@ -1588,6 +1677,7 @@ var InputHandler = class {
1588
1677
  });
1589
1678
  };
1590
1679
  onPointerDown = (e) => {
1680
+ this.panInertia.cancel();
1591
1681
  this.focusSelf();
1592
1682
  this.activePointers.set(e.pointerId, { x: e.clientX, y: e.clientY });
1593
1683
  this.element.setPointerCapture?.(e.pointerId);
@@ -1628,6 +1718,7 @@ var InputHandler = class {
1628
1718
  const dy = e.clientY - this.lastPointer.y;
1629
1719
  this.lastPointer = { x: e.clientX, y: e.clientY };
1630
1720
  this.camera.pan(dx, dy);
1721
+ this.panInertia.sample(dx, dy);
1631
1722
  return;
1632
1723
  }
1633
1724
  if (e.pointerType === "pen" && !this.activePointers.has(e.pointerId)) {
@@ -1658,6 +1749,7 @@ var InputHandler = class {
1658
1749
  }
1659
1750
  if (this.isPanning && this.activePointers.size === 0) {
1660
1751
  this.isPanning = false;
1752
+ this.panInertia.release();
1661
1753
  }
1662
1754
  const upResult = this.inputFilter.filterUp(e);
1663
1755
  if (this.isToolActive) {
@@ -1678,6 +1770,7 @@ var InputHandler = class {
1678
1770
  return this.actions.hasClipboard();
1679
1771
  }
1680
1772
  startPinch() {
1773
+ this.panInertia.cancel();
1681
1774
  this.cancelLongPress();
1682
1775
  this.inputFilter.reset();
1683
1776
  this.deferredDown = null;
@@ -1699,6 +1792,7 @@ var InputHandler = class {
1699
1792
  const dx = center2.x - this.lastPointer.x;
1700
1793
  const dy = center2.y - this.lastPointer.y;
1701
1794
  this.camera.pan(dx, dy);
1795
+ this.panInertia.sample(dx, dy);
1702
1796
  this.lastPinchDistance = dist;
1703
1797
  this.lastPinchCenter = center2;
1704
1798
  this.lastPointer = { ...center2 };
@@ -6800,7 +6894,8 @@ var Viewport = class {
6800
6894
  shortcuts: options.shortcuts,
6801
6895
  addImage: (src, world) => this.addImage(src, world),
6802
6896
  getCenteredWorld: () => this.centeredPosition({ w: 300, h: 200 }),
6803
- onPaste: options.onPaste
6897
+ onPaste: options.onPaste,
6898
+ panInertia: options.panInertia
6804
6899
  });
6805
6900
  if (options.contextMenu !== false) {
6806
6901
  this.contextMenu = new ContextMenu({
@@ -9715,7 +9810,7 @@ var LaserTool = class {
9715
9810
  };
9716
9811
 
9717
9812
  // src/index.ts
9718
- var VERSION = "0.41.0";
9813
+ var VERSION = "0.42.0";
9719
9814
  export {
9720
9815
  ArrowTool,
9721
9816
  AutoSave,