@fieldnotes/core 0.19.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/README.md +9 -0
- package/dist/index.cjs +169 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -2
- package/dist/index.d.ts +16 -2
- package/dist/index.js +168 -19
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -588,12 +588,17 @@ interface ViewportOptions {
|
|
|
588
588
|
background?: BackgroundOptions;
|
|
589
589
|
fontSizePresets?: FontSizePreset[];
|
|
590
590
|
toolbar?: boolean;
|
|
591
|
+
placeholder?: string;
|
|
591
592
|
shortcuts?: ShortcutOptions;
|
|
592
593
|
onHtmlElementMount?: (elementId: string, domId: string | undefined, container: HTMLDivElement) => void;
|
|
593
594
|
onDrop?: (event: DragEvent, worldPosition: {
|
|
594
595
|
x: number;
|
|
595
596
|
y: number;
|
|
596
597
|
}) => void;
|
|
598
|
+
onImageError?: (info: {
|
|
599
|
+
src: string;
|
|
600
|
+
elementIds: string[];
|
|
601
|
+
}) => void;
|
|
597
602
|
}
|
|
598
603
|
declare class Viewport {
|
|
599
604
|
private readonly container;
|
|
@@ -694,12 +699,14 @@ declare class ElementRenderer {
|
|
|
694
699
|
private store;
|
|
695
700
|
private imageCache;
|
|
696
701
|
private onImageLoad;
|
|
702
|
+
private onImageError;
|
|
697
703
|
private camera;
|
|
698
704
|
private canvasSize;
|
|
699
705
|
private hexTileCache;
|
|
700
706
|
private hexTileCacheKey;
|
|
701
707
|
setStore(store: ElementStore): void;
|
|
702
708
|
setOnImageLoad(callback: () => void): void;
|
|
709
|
+
setOnImageError(callback: (src: string) => void): void;
|
|
703
710
|
setCamera(camera: Camera): void;
|
|
704
711
|
setCanvasSize(w: number, h: number): void;
|
|
705
712
|
isDomElement(element: CanvasElement): boolean;
|
|
@@ -717,6 +724,7 @@ declare class ElementRenderer {
|
|
|
717
724
|
private renderHexTemplate;
|
|
718
725
|
private renderRadiusMarker;
|
|
719
726
|
private renderImage;
|
|
727
|
+
private renderImagePlaceholder;
|
|
720
728
|
private getHexTile;
|
|
721
729
|
private getImage;
|
|
722
730
|
}
|
|
@@ -724,6 +732,7 @@ declare class ElementRenderer {
|
|
|
724
732
|
interface NoteEditorOptions {
|
|
725
733
|
fontSizePresets?: FontSizePreset[];
|
|
726
734
|
toolbar?: boolean;
|
|
735
|
+
placeholder?: string;
|
|
727
736
|
}
|
|
728
737
|
declare class NoteEditor {
|
|
729
738
|
private editingId;
|
|
@@ -731,9 +740,11 @@ declare class NoteEditor {
|
|
|
731
740
|
private blurHandler;
|
|
732
741
|
private keyHandler;
|
|
733
742
|
private pointerHandler;
|
|
743
|
+
private inputHandler;
|
|
734
744
|
private pendingEditId;
|
|
735
745
|
private onStopCallback;
|
|
736
746
|
private toolbar;
|
|
747
|
+
private readonly placeholder;
|
|
737
748
|
constructor(options?: NoteEditorOptions);
|
|
738
749
|
get isEditing(): boolean;
|
|
739
750
|
get editingElementId(): string | null;
|
|
@@ -756,6 +767,7 @@ interface StyledRun extends RunStyle {
|
|
|
756
767
|
text: string;
|
|
757
768
|
}
|
|
758
769
|
declare function sanitizeNoteHtml(html: string): string;
|
|
770
|
+
declare function isNoteContentEmpty(html: string): boolean;
|
|
759
771
|
|
|
760
772
|
interface ActiveFormats {
|
|
761
773
|
bold: boolean;
|
|
@@ -985,6 +997,7 @@ declare class SelectTool implements Tool {
|
|
|
985
997
|
private pendingSingleSelectId;
|
|
986
998
|
private hasDragged;
|
|
987
999
|
private resizeAspectRatio;
|
|
1000
|
+
private hoveredId;
|
|
988
1001
|
get selectedIds(): string[];
|
|
989
1002
|
setSelection(ids: string[]): void;
|
|
990
1003
|
get isMarqueeActive(): boolean;
|
|
@@ -999,6 +1012,7 @@ declare class SelectTool implements Tool {
|
|
|
999
1012
|
private updateArrowsBoundTo;
|
|
1000
1013
|
nudgeSelection(dx: number, dy: number, ctx: ToolContext): boolean;
|
|
1001
1014
|
private updateHoverCursor;
|
|
1015
|
+
private setHovered;
|
|
1002
1016
|
private handleResize;
|
|
1003
1017
|
private hitTestResizeHandle;
|
|
1004
1018
|
private getHandlePositions;
|
|
@@ -1233,6 +1247,6 @@ declare class UpdateLayerCommand implements Command {
|
|
|
1233
1247
|
undo(_store: ElementStore): void;
|
|
1234
1248
|
}
|
|
1235
1249
|
|
|
1236
|
-
declare const VERSION = "0.
|
|
1250
|
+
declare const VERSION = "0.21.0";
|
|
1237
1251
|
|
|
1238
|
-
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, parseState, sanitizeNoteHtml, setFontSize, smartSnap, snapPoint, snapToHexCenter, toggleBold, toggleItalic, toggleStrikethrough, toggleUnderline, unbindArrow, updateBoundArrow };
|
|
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
|
@@ -588,12 +588,17 @@ interface ViewportOptions {
|
|
|
588
588
|
background?: BackgroundOptions;
|
|
589
589
|
fontSizePresets?: FontSizePreset[];
|
|
590
590
|
toolbar?: boolean;
|
|
591
|
+
placeholder?: string;
|
|
591
592
|
shortcuts?: ShortcutOptions;
|
|
592
593
|
onHtmlElementMount?: (elementId: string, domId: string | undefined, container: HTMLDivElement) => void;
|
|
593
594
|
onDrop?: (event: DragEvent, worldPosition: {
|
|
594
595
|
x: number;
|
|
595
596
|
y: number;
|
|
596
597
|
}) => void;
|
|
598
|
+
onImageError?: (info: {
|
|
599
|
+
src: string;
|
|
600
|
+
elementIds: string[];
|
|
601
|
+
}) => void;
|
|
597
602
|
}
|
|
598
603
|
declare class Viewport {
|
|
599
604
|
private readonly container;
|
|
@@ -694,12 +699,14 @@ declare class ElementRenderer {
|
|
|
694
699
|
private store;
|
|
695
700
|
private imageCache;
|
|
696
701
|
private onImageLoad;
|
|
702
|
+
private onImageError;
|
|
697
703
|
private camera;
|
|
698
704
|
private canvasSize;
|
|
699
705
|
private hexTileCache;
|
|
700
706
|
private hexTileCacheKey;
|
|
701
707
|
setStore(store: ElementStore): void;
|
|
702
708
|
setOnImageLoad(callback: () => void): void;
|
|
709
|
+
setOnImageError(callback: (src: string) => void): void;
|
|
703
710
|
setCamera(camera: Camera): void;
|
|
704
711
|
setCanvasSize(w: number, h: number): void;
|
|
705
712
|
isDomElement(element: CanvasElement): boolean;
|
|
@@ -717,6 +724,7 @@ declare class ElementRenderer {
|
|
|
717
724
|
private renderHexTemplate;
|
|
718
725
|
private renderRadiusMarker;
|
|
719
726
|
private renderImage;
|
|
727
|
+
private renderImagePlaceholder;
|
|
720
728
|
private getHexTile;
|
|
721
729
|
private getImage;
|
|
722
730
|
}
|
|
@@ -724,6 +732,7 @@ declare class ElementRenderer {
|
|
|
724
732
|
interface NoteEditorOptions {
|
|
725
733
|
fontSizePresets?: FontSizePreset[];
|
|
726
734
|
toolbar?: boolean;
|
|
735
|
+
placeholder?: string;
|
|
727
736
|
}
|
|
728
737
|
declare class NoteEditor {
|
|
729
738
|
private editingId;
|
|
@@ -731,9 +740,11 @@ declare class NoteEditor {
|
|
|
731
740
|
private blurHandler;
|
|
732
741
|
private keyHandler;
|
|
733
742
|
private pointerHandler;
|
|
743
|
+
private inputHandler;
|
|
734
744
|
private pendingEditId;
|
|
735
745
|
private onStopCallback;
|
|
736
746
|
private toolbar;
|
|
747
|
+
private readonly placeholder;
|
|
737
748
|
constructor(options?: NoteEditorOptions);
|
|
738
749
|
get isEditing(): boolean;
|
|
739
750
|
get editingElementId(): string | null;
|
|
@@ -756,6 +767,7 @@ interface StyledRun extends RunStyle {
|
|
|
756
767
|
text: string;
|
|
757
768
|
}
|
|
758
769
|
declare function sanitizeNoteHtml(html: string): string;
|
|
770
|
+
declare function isNoteContentEmpty(html: string): boolean;
|
|
759
771
|
|
|
760
772
|
interface ActiveFormats {
|
|
761
773
|
bold: boolean;
|
|
@@ -985,6 +997,7 @@ declare class SelectTool implements Tool {
|
|
|
985
997
|
private pendingSingleSelectId;
|
|
986
998
|
private hasDragged;
|
|
987
999
|
private resizeAspectRatio;
|
|
1000
|
+
private hoveredId;
|
|
988
1001
|
get selectedIds(): string[];
|
|
989
1002
|
setSelection(ids: string[]): void;
|
|
990
1003
|
get isMarqueeActive(): boolean;
|
|
@@ -999,6 +1012,7 @@ declare class SelectTool implements Tool {
|
|
|
999
1012
|
private updateArrowsBoundTo;
|
|
1000
1013
|
nudgeSelection(dx: number, dy: number, ctx: ToolContext): boolean;
|
|
1001
1014
|
private updateHoverCursor;
|
|
1015
|
+
private setHovered;
|
|
1002
1016
|
private handleResize;
|
|
1003
1017
|
private hitTestResizeHandle;
|
|
1004
1018
|
private getHandlePositions;
|
|
@@ -1233,6 +1247,6 @@ declare class UpdateLayerCommand implements Command {
|
|
|
1233
1247
|
undo(_store: ElementStore): void;
|
|
1234
1248
|
}
|
|
1235
1249
|
|
|
1236
|
-
declare const VERSION = "0.
|
|
1250
|
+
declare const VERSION = "0.21.0";
|
|
1237
1251
|
|
|
1238
|
-
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, parseState, sanitizeNoteHtml, setFontSize, smartSnap, snapPoint, snapToHexCenter, toggleBold, toggleItalic, toggleStrikethrough, toggleUnderline, unbindArrow, updateBoundArrow };
|
|
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
|
@@ -273,6 +273,12 @@ function sanitizeNode(node) {
|
|
|
273
273
|
sanitizeNode(el);
|
|
274
274
|
}
|
|
275
275
|
}
|
|
276
|
+
function isNoteContentEmpty(html) {
|
|
277
|
+
if (!html) return true;
|
|
278
|
+
const doc = new DOMParser().parseFromString(html, "text/html");
|
|
279
|
+
const text = doc.body.textContent ?? "";
|
|
280
|
+
return text.replace(/\u00a0/g, " ").trim().length === 0;
|
|
281
|
+
}
|
|
276
282
|
function sanitizeAttributes(el, tag) {
|
|
277
283
|
const attrs = Array.from(el.attributes);
|
|
278
284
|
for (const attr of attrs) {
|
|
@@ -2603,6 +2609,7 @@ var ElementRenderer = class {
|
|
|
2603
2609
|
store = null;
|
|
2604
2610
|
imageCache = /* @__PURE__ */ new Map();
|
|
2605
2611
|
onImageLoad = null;
|
|
2612
|
+
onImageError = null;
|
|
2606
2613
|
camera = null;
|
|
2607
2614
|
canvasSize = null;
|
|
2608
2615
|
hexTileCache = null;
|
|
@@ -2613,6 +2620,9 @@ var ElementRenderer = class {
|
|
|
2613
2620
|
setOnImageLoad(callback) {
|
|
2614
2621
|
this.onImageLoad = callback;
|
|
2615
2622
|
}
|
|
2623
|
+
setOnImageError(callback) {
|
|
2624
|
+
this.onImageError = callback;
|
|
2625
|
+
}
|
|
2616
2626
|
setCamera(camera) {
|
|
2617
2627
|
this.camera = camera;
|
|
2618
2628
|
}
|
|
@@ -2971,6 +2981,10 @@ var ElementRenderer = class {
|
|
|
2971
2981
|
ctx.restore();
|
|
2972
2982
|
}
|
|
2973
2983
|
renderImage(ctx, image) {
|
|
2984
|
+
if (this.imageCache.get(image.src) === "failed") {
|
|
2985
|
+
this.renderImagePlaceholder(ctx, image);
|
|
2986
|
+
return;
|
|
2987
|
+
}
|
|
2974
2988
|
const img = this.getImage(image.src);
|
|
2975
2989
|
if (!img) return;
|
|
2976
2990
|
ctx.drawImage(
|
|
@@ -2981,6 +2995,27 @@ var ElementRenderer = class {
|
|
|
2981
2995
|
image.size.h
|
|
2982
2996
|
);
|
|
2983
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
|
+
}
|
|
2984
3019
|
getHexTile(cellSize, orientation, strokeColor, strokeWidth, opacity, scale) {
|
|
2985
3020
|
const key = `${cellSize}:${orientation}:${strokeColor}:${strokeWidth}:${opacity}:${scale}`;
|
|
2986
3021
|
if (this.hexTileCacheKey === key && this.hexTileCache) {
|
|
@@ -2996,6 +3031,7 @@ var ElementRenderer = class {
|
|
|
2996
3031
|
getImage(src) {
|
|
2997
3032
|
const cached = this.imageCache.get(src);
|
|
2998
3033
|
if (cached) {
|
|
3034
|
+
if (cached === "failed") return null;
|
|
2999
3035
|
if (cached instanceof HTMLImageElement) return cached.complete ? cached : null;
|
|
3000
3036
|
return cached;
|
|
3001
3037
|
}
|
|
@@ -3012,6 +3048,11 @@ var ElementRenderer = class {
|
|
|
3012
3048
|
});
|
|
3013
3049
|
}
|
|
3014
3050
|
};
|
|
3051
|
+
img.onerror = () => {
|
|
3052
|
+
this.imageCache.set(src, "failed");
|
|
3053
|
+
this.onImageError?.(src);
|
|
3054
|
+
this.onImageLoad?.();
|
|
3055
|
+
};
|
|
3015
3056
|
return null;
|
|
3016
3057
|
}
|
|
3017
3058
|
};
|
|
@@ -3364,17 +3405,36 @@ var FORMAT_SHORTCUTS = {
|
|
|
3364
3405
|
i: toggleItalic,
|
|
3365
3406
|
u: toggleUnderline
|
|
3366
3407
|
};
|
|
3408
|
+
function ensureEditorStyles() {
|
|
3409
|
+
if (document.querySelector("style[data-fieldnotes-editor]")) return;
|
|
3410
|
+
const style = document.createElement("style");
|
|
3411
|
+
style.setAttribute("data-fieldnotes-editor", "");
|
|
3412
|
+
style.textContent = `[data-fn-placeholder][data-fn-empty='true']::before {
|
|
3413
|
+
content: attr(data-fn-placeholder);
|
|
3414
|
+
color: #9e9e9e;
|
|
3415
|
+
position: absolute;
|
|
3416
|
+
pointer-events: none;
|
|
3417
|
+
}`;
|
|
3418
|
+
document.head.appendChild(style);
|
|
3419
|
+
}
|
|
3420
|
+
function isNodeEmpty(node) {
|
|
3421
|
+
const text = node.textContent ?? "";
|
|
3422
|
+
return text.replace(/\u00a0/g, " ").trim().length === 0;
|
|
3423
|
+
}
|
|
3367
3424
|
var NoteEditor = class {
|
|
3368
3425
|
editingId = null;
|
|
3369
3426
|
editingNode = null;
|
|
3370
3427
|
blurHandler = null;
|
|
3371
3428
|
keyHandler = null;
|
|
3372
3429
|
pointerHandler = null;
|
|
3430
|
+
inputHandler = null;
|
|
3373
3431
|
pendingEditId = null;
|
|
3374
3432
|
onStopCallback = null;
|
|
3375
3433
|
toolbar;
|
|
3434
|
+
placeholder;
|
|
3376
3435
|
constructor(options) {
|
|
3377
3436
|
this.toolbar = options?.toolbar === false ? null : new NoteToolbar(options?.fontSizePresets);
|
|
3437
|
+
this.placeholder = options?.placeholder ?? "Type\u2026";
|
|
3378
3438
|
}
|
|
3379
3439
|
get isEditing() {
|
|
3380
3440
|
return this.editingId !== null;
|
|
@@ -3409,6 +3469,11 @@ var NoteEditor = class {
|
|
|
3409
3469
|
if (this.pointerHandler) {
|
|
3410
3470
|
this.editingNode.removeEventListener("pointerdown", this.pointerHandler);
|
|
3411
3471
|
}
|
|
3472
|
+
if (this.inputHandler) {
|
|
3473
|
+
this.editingNode.removeEventListener("input", this.inputHandler);
|
|
3474
|
+
}
|
|
3475
|
+
this.editingNode.removeAttribute("data-fn-placeholder");
|
|
3476
|
+
this.editingNode.removeAttribute("data-fn-empty");
|
|
3412
3477
|
const text = sanitizeNoteHtml(this.editingNode.innerHTML);
|
|
3413
3478
|
store.update(this.editingId, { text });
|
|
3414
3479
|
this.editingNode.contentEditable = "false";
|
|
@@ -3425,6 +3490,7 @@ var NoteEditor = class {
|
|
|
3425
3490
|
this.blurHandler = null;
|
|
3426
3491
|
this.keyHandler = null;
|
|
3427
3492
|
this.pointerHandler = null;
|
|
3493
|
+
this.inputHandler = null;
|
|
3428
3494
|
}
|
|
3429
3495
|
destroy(store) {
|
|
3430
3496
|
this.pendingEditId = null;
|
|
@@ -3455,6 +3521,13 @@ var NoteEditor = class {
|
|
|
3455
3521
|
selection.removeAllRanges();
|
|
3456
3522
|
selection.addRange(range);
|
|
3457
3523
|
}
|
|
3524
|
+
ensureEditorStyles();
|
|
3525
|
+
node.setAttribute("data-fn-placeholder", this.placeholder);
|
|
3526
|
+
node.setAttribute("data-fn-empty", String(isNodeEmpty(node)));
|
|
3527
|
+
this.inputHandler = () => {
|
|
3528
|
+
node.setAttribute("data-fn-empty", String(isNodeEmpty(node)));
|
|
3529
|
+
};
|
|
3530
|
+
node.addEventListener("input", this.inputHandler);
|
|
3458
3531
|
this.toolbar?.show(node);
|
|
3459
3532
|
this.blurHandler = (e) => {
|
|
3460
3533
|
const related = e.relatedTarget;
|
|
@@ -4891,9 +4964,21 @@ var Viewport = class {
|
|
|
4891
4964
|
this.renderLoop.markAllLayersDirty();
|
|
4892
4965
|
this.requestRender();
|
|
4893
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
|
+
});
|
|
4894
4978
|
this.noteEditor = new NoteEditor({
|
|
4895
4979
|
fontSizePresets: options.fontSizePresets,
|
|
4896
|
-
toolbar: options.toolbar
|
|
4980
|
+
toolbar: options.toolbar,
|
|
4981
|
+
placeholder: options.placeholder
|
|
4897
4982
|
});
|
|
4898
4983
|
this.noteEditor.setOnStop((id) => this.onTextEditStop(id));
|
|
4899
4984
|
this.onHtmlElementMount = options.onHtmlElementMount;
|
|
@@ -5239,7 +5324,16 @@ var Viewport = class {
|
|
|
5239
5324
|
}
|
|
5240
5325
|
onTextEditStop(elementId) {
|
|
5241
5326
|
const element = this.store.getById(elementId);
|
|
5242
|
-
if (!element
|
|
5327
|
+
if (!element) return;
|
|
5328
|
+
if (element.type === "note") {
|
|
5329
|
+
if (isNoteContentEmpty(element.text)) {
|
|
5330
|
+
this.historyRecorder.begin();
|
|
5331
|
+
this.store.remove(elementId);
|
|
5332
|
+
this.historyRecorder.commit();
|
|
5333
|
+
}
|
|
5334
|
+
return;
|
|
5335
|
+
}
|
|
5336
|
+
if (element.type !== "text") return;
|
|
5243
5337
|
if (!element.text || element.text.trim() === "") {
|
|
5244
5338
|
this.historyRecorder.begin();
|
|
5245
5339
|
this.store.remove(elementId);
|
|
@@ -5585,6 +5679,43 @@ var PencilTool = class {
|
|
|
5585
5679
|
}
|
|
5586
5680
|
};
|
|
5587
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
|
+
|
|
5588
5719
|
// src/tools/eraser-tool.ts
|
|
5589
5720
|
var DEFAULT_RADIUS = 20;
|
|
5590
5721
|
function makeEraserCursor(radius) {
|
|
@@ -5643,12 +5774,7 @@ var EraserTool = class {
|
|
|
5643
5774
|
if (erased) ctx.requestRender();
|
|
5644
5775
|
}
|
|
5645
5776
|
strokeIntersects(stroke, point) {
|
|
5646
|
-
|
|
5647
|
-
return stroke.points.some((p) => {
|
|
5648
|
-
const dx = p.x + stroke.position.x - point.x;
|
|
5649
|
-
const dy = p.y + stroke.position.y - point.y;
|
|
5650
|
-
return dx * dx + dy * dy <= radiusSq;
|
|
5651
|
-
});
|
|
5777
|
+
return hitTestStroke(stroke, point, this.radius);
|
|
5652
5778
|
}
|
|
5653
5779
|
};
|
|
5654
5780
|
|
|
@@ -5788,6 +5914,7 @@ var SelectTool = class {
|
|
|
5788
5914
|
pendingSingleSelectId = null;
|
|
5789
5915
|
hasDragged = false;
|
|
5790
5916
|
resizeAspectRatio = 0;
|
|
5917
|
+
hoveredId = null;
|
|
5791
5918
|
get selectedIds() {
|
|
5792
5919
|
return [...this._selectedIds];
|
|
5793
5920
|
}
|
|
@@ -5804,6 +5931,7 @@ var SelectTool = class {
|
|
|
5804
5931
|
onDeactivate(ctx) {
|
|
5805
5932
|
this._selectedIds = [];
|
|
5806
5933
|
this.mode = { type: "idle" };
|
|
5934
|
+
this.hoveredId = null;
|
|
5807
5935
|
ctx.setCursor?.("default");
|
|
5808
5936
|
}
|
|
5809
5937
|
snap(point, ctx) {
|
|
@@ -5811,6 +5939,7 @@ var SelectTool = class {
|
|
|
5811
5939
|
}
|
|
5812
5940
|
onPointerDown(state, ctx) {
|
|
5813
5941
|
this.ctx = ctx;
|
|
5942
|
+
this.setHovered(null, ctx);
|
|
5814
5943
|
const world = ctx.camera.screenToWorld({ x: state.x, y: state.y });
|
|
5815
5944
|
this.lastWorld = this.snap(world, ctx);
|
|
5816
5945
|
this.currentWorld = world;
|
|
@@ -5953,7 +6082,8 @@ var SelectTool = class {
|
|
|
5953
6082
|
}
|
|
5954
6083
|
onHover(state, ctx) {
|
|
5955
6084
|
const world = ctx.camera.screenToWorld({ x: state.x, y: state.y });
|
|
5956
|
-
this.updateHoverCursor(world, ctx);
|
|
6085
|
+
const hoverId = this.updateHoverCursor(world, ctx);
|
|
6086
|
+
this.setHovered(hoverId, ctx);
|
|
5957
6087
|
}
|
|
5958
6088
|
renderOverlay(canvasCtx) {
|
|
5959
6089
|
this.renderMarquee(canvasCtx);
|
|
@@ -5974,6 +6104,23 @@ var SelectTool = class {
|
|
|
5974
6104
|
canvasCtx.restore();
|
|
5975
6105
|
}
|
|
5976
6106
|
}
|
|
6107
|
+
if (this.hoveredId && this.ctx && this.mode.type === "idle") {
|
|
6108
|
+
if (!this._selectedIds.includes(this.hoveredId)) {
|
|
6109
|
+
const el = this.ctx.store.getById(this.hoveredId);
|
|
6110
|
+
if (el) {
|
|
6111
|
+
const b = getElementBounds(el);
|
|
6112
|
+
if (b) {
|
|
6113
|
+
canvasCtx.save();
|
|
6114
|
+
canvasCtx.strokeStyle = "#2196F3";
|
|
6115
|
+
canvasCtx.globalAlpha = 0.35;
|
|
6116
|
+
canvasCtx.lineWidth = 1.5 / this.ctx.camera.zoom;
|
|
6117
|
+
canvasCtx.setLineDash([]);
|
|
6118
|
+
canvasCtx.strokeRect(b.x, b.y, b.w, b.h);
|
|
6119
|
+
canvasCtx.restore();
|
|
6120
|
+
}
|
|
6121
|
+
}
|
|
6122
|
+
}
|
|
6123
|
+
}
|
|
5977
6124
|
}
|
|
5978
6125
|
updateArrowsBoundTo(ids, ctx) {
|
|
5979
6126
|
const movedNonArrowIds = /* @__PURE__ */ new Set();
|
|
@@ -6022,20 +6169,26 @@ var SelectTool = class {
|
|
|
6022
6169
|
const arrowHit = hitTestArrowHandles(world, this._selectedIds, ctx);
|
|
6023
6170
|
if (arrowHit) {
|
|
6024
6171
|
ctx.setCursor?.(getArrowHandleCursor(arrowHit.handle, false));
|
|
6025
|
-
return;
|
|
6172
|
+
return null;
|
|
6026
6173
|
}
|
|
6027
6174
|
const templateResizeHit = this.hitTestTemplateResizeHandle(world, ctx);
|
|
6028
6175
|
if (templateResizeHit) {
|
|
6029
6176
|
ctx.setCursor?.("nwse-resize");
|
|
6030
|
-
return;
|
|
6177
|
+
return null;
|
|
6031
6178
|
}
|
|
6032
6179
|
const resizeHit = this.hitTestResizeHandle(world, ctx);
|
|
6033
6180
|
if (resizeHit) {
|
|
6034
6181
|
ctx.setCursor?.(HANDLE_CURSORS[resizeHit.handle]);
|
|
6035
|
-
return;
|
|
6182
|
+
return null;
|
|
6036
6183
|
}
|
|
6037
6184
|
const hit = this.hitTest(world, ctx);
|
|
6038
6185
|
ctx.setCursor?.(hit ? "move" : "default");
|
|
6186
|
+
return hit ? hit.id : null;
|
|
6187
|
+
}
|
|
6188
|
+
setHovered(id, ctx) {
|
|
6189
|
+
if (this.hoveredId === id) return;
|
|
6190
|
+
this.hoveredId = id;
|
|
6191
|
+
ctx.requestRender();
|
|
6039
6192
|
}
|
|
6040
6193
|
handleResize(world, ctx, shiftKey = false) {
|
|
6041
6194
|
if (this.mode.type !== "resizing") return;
|
|
@@ -6301,12 +6454,7 @@ var SelectTool = class {
|
|
|
6301
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;
|
|
6302
6455
|
}
|
|
6303
6456
|
if (el.type === "stroke") {
|
|
6304
|
-
|
|
6305
|
-
return el.points.some((p) => {
|
|
6306
|
-
const dx = p.x + el.position.x - point.x;
|
|
6307
|
-
const dy = p.y + el.position.y - point.y;
|
|
6308
|
-
return dx * dx + dy * dy <= HIT_RADIUS * HIT_RADIUS;
|
|
6309
|
-
});
|
|
6457
|
+
return hitTestStroke(el, point, 10);
|
|
6310
6458
|
}
|
|
6311
6459
|
if (el.type === "arrow") {
|
|
6312
6460
|
return isNearBezier(point, el.from, el.to, el.bend, 10);
|
|
@@ -7142,7 +7290,7 @@ var TemplateTool = class {
|
|
|
7142
7290
|
};
|
|
7143
7291
|
|
|
7144
7292
|
// src/index.ts
|
|
7145
|
-
var VERSION = "0.
|
|
7293
|
+
var VERSION = "0.21.0";
|
|
7146
7294
|
export {
|
|
7147
7295
|
AddElementCommand,
|
|
7148
7296
|
ArrowTool,
|
|
@@ -7216,6 +7364,7 @@ export {
|
|
|
7216
7364
|
getHexDistance,
|
|
7217
7365
|
isBindable,
|
|
7218
7366
|
isNearBezier,
|
|
7367
|
+
isNoteContentEmpty,
|
|
7219
7368
|
parseState,
|
|
7220
7369
|
sanitizeNoteHtml,
|
|
7221
7370
|
setFontSize,
|