@fieldnotes/core 0.20.0 → 0.21.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
@@ -595,6 +595,10 @@ interface ViewportOptions {
595
595
  x: number;
596
596
  y: number;
597
597
  }) => void;
598
+ onImageError?: (info: {
599
+ src: string;
600
+ elementIds: string[];
601
+ }) => void;
598
602
  }
599
603
  declare class Viewport {
600
604
  private readonly container;
@@ -695,12 +699,14 @@ declare class ElementRenderer {
695
699
  private store;
696
700
  private imageCache;
697
701
  private onImageLoad;
702
+ private onImageError;
698
703
  private camera;
699
704
  private canvasSize;
700
705
  private hexTileCache;
701
706
  private hexTileCacheKey;
702
707
  setStore(store: ElementStore): void;
703
708
  setOnImageLoad(callback: () => void): void;
709
+ setOnImageError(callback: (src: string) => void): void;
704
710
  setCamera(camera: Camera): void;
705
711
  setCanvasSize(w: number, h: number): void;
706
712
  isDomElement(element: CanvasElement): boolean;
@@ -718,6 +724,7 @@ declare class ElementRenderer {
718
724
  private renderHexTemplate;
719
725
  private renderRadiusMarker;
720
726
  private renderImage;
727
+ private renderImagePlaceholder;
721
728
  private getHexTile;
722
729
  private getImage;
723
730
  }
@@ -1240,6 +1247,6 @@ declare class UpdateLayerCommand implements Command {
1240
1247
  undo(_store: ElementStore): void;
1241
1248
  }
1242
1249
 
1243
- declare const VERSION = "0.20.0";
1250
+ declare const VERSION = "0.21.0";
1244
1251
 
1245
1252
  export { type ActiveFormats, AddElementCommand, type ArrowElement, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, Background, type BackgroundOptions, type BackgroundPattern, BatchCommand, type Binding, type Bounds, Camera, type CameraChangeInfo, type CameraOptions, type CanvasElement, type CanvasState, type Command, CreateLayerCommand, DEFAULT_FONT_SIZE_PRESETS, DEFAULT_NOTE_FONT_SIZE, DoubleTapDetector, type DoubleTapDetectorOptions, ElementRenderer, ElementStore, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, EventBus, type ExportImageOptions, type FilterAction, type FilteredEvent, type FilteredUpEvent, type FontSizePreset, type GridElement, type GridInfo, HandTool, type HexOrientation, HistoryRecorder, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, InputFilter, InputHandler, type InputHandlerOptions, type Layer, LayerManager, MeasureTool, type MeasureToolOptions, type Measurement, NoteEditor, type NoteEditorOptions, type NoteElement, NoteTool, type NoteToolOptions, NoteToolbar, PencilTool, type PencilToolOptions, type Point, type PointerState, Quadtree, RemoveElementCommand, RemoveLayerCommand, type RenderStatsSnapshot, SelectTool, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type ShortcutBindings, type ShortcutOptions, type ShortcutsApi, type Size, type StrokeElement, type StrokePoint, type StyledRun, type TemplateElement, type TemplateShape, TemplateTool, type TemplateToolOptions, type TextElement, TextTool, type TextToolOptions, type Tool, type ToolContext, ToolManager, type ToolName, UpdateElementCommand, UpdateLayerCommand, VERSION, Viewport, type ViewportOptions, boundsIntersect, clearStaleBindings, createArrow, createGrid, createHtmlElement, createId, createImage, createNote, createShape, createStroke, createTemplate, createText, drawHexPath, exportImage, exportState, findBindTarget, findBoundArrows, getActiveFormats, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getEdgeIntersection, getElementBounds, getElementCenter, getElementsBoundingBox, getHexCellsInCone, getHexCellsInLine, getHexCellsInRadius, getHexCellsInSquare, getHexDistance, isBindable, isNearBezier, isNoteContentEmpty, parseState, sanitizeNoteHtml, setFontSize, smartSnap, snapPoint, snapToHexCenter, toggleBold, toggleItalic, toggleStrikethrough, toggleUnderline, unbindArrow, updateBoundArrow };
package/dist/index.d.ts CHANGED
@@ -595,6 +595,10 @@ interface ViewportOptions {
595
595
  x: number;
596
596
  y: number;
597
597
  }) => void;
598
+ onImageError?: (info: {
599
+ src: string;
600
+ elementIds: string[];
601
+ }) => void;
598
602
  }
599
603
  declare class Viewport {
600
604
  private readonly container;
@@ -695,12 +699,14 @@ declare class ElementRenderer {
695
699
  private store;
696
700
  private imageCache;
697
701
  private onImageLoad;
702
+ private onImageError;
698
703
  private camera;
699
704
  private canvasSize;
700
705
  private hexTileCache;
701
706
  private hexTileCacheKey;
702
707
  setStore(store: ElementStore): void;
703
708
  setOnImageLoad(callback: () => void): void;
709
+ setOnImageError(callback: (src: string) => void): void;
704
710
  setCamera(camera: Camera): void;
705
711
  setCanvasSize(w: number, h: number): void;
706
712
  isDomElement(element: CanvasElement): boolean;
@@ -718,6 +724,7 @@ declare class ElementRenderer {
718
724
  private renderHexTemplate;
719
725
  private renderRadiusMarker;
720
726
  private renderImage;
727
+ private renderImagePlaceholder;
721
728
  private getHexTile;
722
729
  private getImage;
723
730
  }
@@ -1240,6 +1247,6 @@ declare class UpdateLayerCommand implements Command {
1240
1247
  undo(_store: ElementStore): void;
1241
1248
  }
1242
1249
 
1243
- declare const VERSION = "0.20.0";
1250
+ declare const VERSION = "0.21.0";
1244
1251
 
1245
1252
  export { type ActiveFormats, AddElementCommand, type ArrowElement, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, Background, type BackgroundOptions, type BackgroundPattern, BatchCommand, type Binding, type Bounds, Camera, type CameraChangeInfo, type CameraOptions, type CanvasElement, type CanvasState, type Command, CreateLayerCommand, DEFAULT_FONT_SIZE_PRESETS, DEFAULT_NOTE_FONT_SIZE, DoubleTapDetector, type DoubleTapDetectorOptions, ElementRenderer, ElementStore, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, EventBus, type ExportImageOptions, type FilterAction, type FilteredEvent, type FilteredUpEvent, type FontSizePreset, type GridElement, type GridInfo, HandTool, type HexOrientation, HistoryRecorder, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, InputFilter, InputHandler, type InputHandlerOptions, type Layer, LayerManager, MeasureTool, type MeasureToolOptions, type Measurement, NoteEditor, type NoteEditorOptions, type NoteElement, NoteTool, type NoteToolOptions, NoteToolbar, PencilTool, type PencilToolOptions, type Point, type PointerState, Quadtree, RemoveElementCommand, RemoveLayerCommand, type RenderStatsSnapshot, SelectTool, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type ShortcutBindings, type ShortcutOptions, type ShortcutsApi, type Size, type StrokeElement, type StrokePoint, type StyledRun, type TemplateElement, type TemplateShape, TemplateTool, type TemplateToolOptions, type TextElement, TextTool, type TextToolOptions, type Tool, type ToolContext, ToolManager, type ToolName, UpdateElementCommand, UpdateLayerCommand, VERSION, Viewport, type ViewportOptions, boundsIntersect, clearStaleBindings, createArrow, createGrid, createHtmlElement, createId, createImage, createNote, createShape, createStroke, createTemplate, createText, drawHexPath, exportImage, exportState, findBindTarget, findBoundArrows, getActiveFormats, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getEdgeIntersection, getElementBounds, getElementCenter, getElementsBoundingBox, getHexCellsInCone, getHexCellsInLine, getHexCellsInRadius, getHexCellsInSquare, getHexDistance, isBindable, isNearBezier, isNoteContentEmpty, parseState, sanitizeNoteHtml, setFontSize, smartSnap, snapPoint, snapToHexCenter, toggleBold, toggleItalic, toggleStrikethrough, toggleUnderline, unbindArrow, updateBoundArrow };
package/dist/index.js CHANGED
@@ -2609,6 +2609,7 @@ var ElementRenderer = class {
2609
2609
  store = null;
2610
2610
  imageCache = /* @__PURE__ */ new Map();
2611
2611
  onImageLoad = null;
2612
+ onImageError = null;
2612
2613
  camera = null;
2613
2614
  canvasSize = null;
2614
2615
  hexTileCache = null;
@@ -2619,6 +2620,9 @@ var ElementRenderer = class {
2619
2620
  setOnImageLoad(callback) {
2620
2621
  this.onImageLoad = callback;
2621
2622
  }
2623
+ setOnImageError(callback) {
2624
+ this.onImageError = callback;
2625
+ }
2622
2626
  setCamera(camera) {
2623
2627
  this.camera = camera;
2624
2628
  }
@@ -2977,6 +2981,10 @@ var ElementRenderer = class {
2977
2981
  ctx.restore();
2978
2982
  }
2979
2983
  renderImage(ctx, image) {
2984
+ if (this.imageCache.get(image.src) === "failed") {
2985
+ this.renderImagePlaceholder(ctx, image);
2986
+ return;
2987
+ }
2980
2988
  const img = this.getImage(image.src);
2981
2989
  if (!img) return;
2982
2990
  ctx.drawImage(
@@ -2987,6 +2995,27 @@ var ElementRenderer = class {
2987
2995
  image.size.h
2988
2996
  );
2989
2997
  }
2998
+ renderImagePlaceholder(ctx, image) {
2999
+ const { x, y } = image.position;
3000
+ const { w, h } = image.size;
3001
+ ctx.save();
3002
+ ctx.fillStyle = "#eeeeee";
3003
+ ctx.fillRect(x, y, w, h);
3004
+ ctx.strokeStyle = "#bdbdbd";
3005
+ ctx.lineWidth = 1;
3006
+ ctx.strokeRect(x, y, w, h);
3007
+ const glyph = Math.min(24, w / 2, h / 2);
3008
+ const cx = x + w / 2;
3009
+ const cy = y + h / 2;
3010
+ ctx.strokeStyle = "#9e9e9e";
3011
+ ctx.lineWidth = 2;
3012
+ ctx.beginPath();
3013
+ ctx.arc(cx, cy, glyph / 2, 0, Math.PI * 2);
3014
+ ctx.moveTo(cx - glyph / 2, cy + glyph / 2);
3015
+ ctx.lineTo(cx + glyph / 2, cy - glyph / 2);
3016
+ ctx.stroke();
3017
+ ctx.restore();
3018
+ }
2990
3019
  getHexTile(cellSize, orientation, strokeColor, strokeWidth, opacity, scale) {
2991
3020
  const key = `${cellSize}:${orientation}:${strokeColor}:${strokeWidth}:${opacity}:${scale}`;
2992
3021
  if (this.hexTileCacheKey === key && this.hexTileCache) {
@@ -3002,6 +3031,7 @@ var ElementRenderer = class {
3002
3031
  getImage(src) {
3003
3032
  const cached = this.imageCache.get(src);
3004
3033
  if (cached) {
3034
+ if (cached === "failed") return null;
3005
3035
  if (cached instanceof HTMLImageElement) return cached.complete ? cached : null;
3006
3036
  return cached;
3007
3037
  }
@@ -3018,6 +3048,11 @@ var ElementRenderer = class {
3018
3048
  });
3019
3049
  }
3020
3050
  };
3051
+ img.onerror = () => {
3052
+ this.imageCache.set(src, "failed");
3053
+ this.onImageError?.(src);
3054
+ this.onImageLoad?.();
3055
+ };
3021
3056
  return null;
3022
3057
  }
3023
3058
  };
@@ -4929,6 +4964,17 @@ var Viewport = class {
4929
4964
  this.renderLoop.markAllLayersDirty();
4930
4965
  this.requestRender();
4931
4966
  });
4967
+ this.renderer.setOnImageError((src) => {
4968
+ const elementIds = [];
4969
+ for (const el of this.store.getAll()) {
4970
+ if (el.type === "image" && el.src === src) elementIds.push(el.id);
4971
+ }
4972
+ if (options.onImageError) {
4973
+ options.onImageError({ src, elementIds });
4974
+ } else {
4975
+ console.warn(`[fieldnotes] image failed to load: ${src}`);
4976
+ }
4977
+ });
4932
4978
  this.noteEditor = new NoteEditor({
4933
4979
  fontSizePresets: options.fontSizePresets,
4934
4980
  toolbar: options.toolbar,
@@ -5633,6 +5679,43 @@ var PencilTool = class {
5633
5679
  }
5634
5680
  };
5635
5681
 
5682
+ // src/elements/stroke-hit.ts
5683
+ function distSqToSegment(p, a, b) {
5684
+ const abx = b.x - a.x;
5685
+ const aby = b.y - a.y;
5686
+ const apx = p.x - a.x;
5687
+ const apy = p.y - a.y;
5688
+ const lenSq = abx * abx + aby * aby;
5689
+ if (lenSq === 0) {
5690
+ return apx * apx + apy * apy;
5691
+ }
5692
+ const t = Math.max(0, Math.min(1, (apx * abx + apy * aby) / lenSq));
5693
+ const dx = p.x - (a.x + t * abx);
5694
+ const dy = p.y - (a.y + t * aby);
5695
+ return dx * dx + dy * dy;
5696
+ }
5697
+ function hitTestStroke(stroke, point, radius) {
5698
+ const bounds = getElementBounds(stroke);
5699
+ if (!bounds) return false;
5700
+ if (point.x < bounds.x - radius || point.x > bounds.x + bounds.w + radius || point.y < bounds.y - radius || point.y > bounds.y + bounds.h + radius) {
5701
+ return false;
5702
+ }
5703
+ const radiusSq = radius * radius;
5704
+ const local = { x: point.x - stroke.position.x, y: point.y - stroke.position.y };
5705
+ const { segments } = getStrokeRenderData(stroke);
5706
+ if (segments.length === 0) {
5707
+ const p = stroke.points[0];
5708
+ if (!p) return false;
5709
+ const dx = p.x - local.x;
5710
+ const dy = p.y - local.y;
5711
+ return dx * dx + dy * dy <= radiusSq;
5712
+ }
5713
+ for (const seg of segments) {
5714
+ if (distSqToSegment(local, seg.start, seg.end) <= radiusSq) return true;
5715
+ }
5716
+ return false;
5717
+ }
5718
+
5636
5719
  // src/tools/eraser-tool.ts
5637
5720
  var DEFAULT_RADIUS = 20;
5638
5721
  function makeEraserCursor(radius) {
@@ -5691,12 +5774,7 @@ var EraserTool = class {
5691
5774
  if (erased) ctx.requestRender();
5692
5775
  }
5693
5776
  strokeIntersects(stroke, point) {
5694
- const radiusSq = this.radius * this.radius;
5695
- return stroke.points.some((p) => {
5696
- const dx = p.x + stroke.position.x - point.x;
5697
- const dy = p.y + stroke.position.y - point.y;
5698
- return dx * dx + dy * dy <= radiusSq;
5699
- });
5777
+ return hitTestStroke(stroke, point, this.radius);
5700
5778
  }
5701
5779
  };
5702
5780
 
@@ -6376,12 +6454,7 @@ var SelectTool = class {
6376
6454
  return point.x >= el.position.x && point.x <= el.position.x + s.w && point.y >= el.position.y && point.y <= el.position.y + s.h;
6377
6455
  }
6378
6456
  if (el.type === "stroke") {
6379
- const HIT_RADIUS = 10;
6380
- return el.points.some((p) => {
6381
- const dx = p.x + el.position.x - point.x;
6382
- const dy = p.y + el.position.y - point.y;
6383
- return dx * dx + dy * dy <= HIT_RADIUS * HIT_RADIUS;
6384
- });
6457
+ return hitTestStroke(el, point, 10);
6385
6458
  }
6386
6459
  if (el.type === "arrow") {
6387
6460
  return isNearBezier(point, el.from, el.to, el.bend, 10);
@@ -7217,7 +7290,7 @@ var TemplateTool = class {
7217
7290
  };
7218
7291
 
7219
7292
  // src/index.ts
7220
- var VERSION = "0.20.0";
7293
+ var VERSION = "0.21.0";
7221
7294
  export {
7222
7295
  AddElementCommand,
7223
7296
  ArrowTool,