@fieldnotes/core 0.17.0 → 0.19.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/LICENSE +21 -0
- package/README.md +472 -436
- package/dist/index.cjs +282 -58
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +26 -2
- package/dist/index.d.ts +26 -2
- package/dist/index.js +282 -58
- package/dist/index.js.map +1 -0
- package/package.json +4 -2
package/dist/index.d.cts
CHANGED
|
@@ -411,12 +411,14 @@ declare class HistoryRecorder {
|
|
|
411
411
|
private readonly layerManager?;
|
|
412
412
|
private recording;
|
|
413
413
|
private transaction;
|
|
414
|
+
private generation;
|
|
414
415
|
private updateSnapshots;
|
|
415
416
|
private unsubscribers;
|
|
416
417
|
constructor(store: ElementStore, stack: HistoryStack, layerManager?: LayerManager | undefined);
|
|
417
418
|
pause(): void;
|
|
418
419
|
resume(): void;
|
|
419
420
|
begin(): void;
|
|
421
|
+
get currentTransactionId(): number | null;
|
|
420
422
|
commit(): void;
|
|
421
423
|
rollback(): void;
|
|
422
424
|
destroy(): void;
|
|
@@ -430,12 +432,25 @@ declare class HistoryRecorder {
|
|
|
430
432
|
private flushUpdateSnapshots;
|
|
431
433
|
}
|
|
432
434
|
|
|
435
|
+
type ShortcutBindings = Record<string, string | string[] | null>;
|
|
436
|
+
interface ShortcutOptions {
|
|
437
|
+
scope?: 'focus' | 'window';
|
|
438
|
+
bindings?: ShortcutBindings;
|
|
439
|
+
}
|
|
440
|
+
interface ShortcutsApi {
|
|
441
|
+
rebind(action: string, bindings: string | string[] | null): void;
|
|
442
|
+
disable(action: string): void;
|
|
443
|
+
reset(action?: string): void;
|
|
444
|
+
getBindings(): Record<string, string[]>;
|
|
445
|
+
}
|
|
446
|
+
|
|
433
447
|
interface InputHandlerOptions {
|
|
434
448
|
toolManager?: ToolManager;
|
|
435
449
|
toolContext?: ToolContext;
|
|
436
450
|
historyRecorder?: HistoryRecorder;
|
|
437
451
|
historyStack?: HistoryStack;
|
|
438
452
|
fitToContent?: () => void;
|
|
453
|
+
shortcuts?: ShortcutOptions;
|
|
439
454
|
}
|
|
440
455
|
declare class InputHandler {
|
|
441
456
|
private readonly element;
|
|
@@ -456,9 +471,12 @@ declare class InputHandler {
|
|
|
456
471
|
private deferredDown;
|
|
457
472
|
private readonly abortController;
|
|
458
473
|
private readonly actions;
|
|
474
|
+
private readonly shortcutMap;
|
|
475
|
+
private readonly scope;
|
|
459
476
|
constructor(element: HTMLElement, camera: Camera, options?: InputHandlerOptions);
|
|
460
477
|
setToolManager(toolManager: ToolManager, toolContext: ToolContext): void;
|
|
461
478
|
flushPendingHistory(): void;
|
|
479
|
+
get shortcuts(): ShortcutsApi;
|
|
462
480
|
destroy(): void;
|
|
463
481
|
private bind;
|
|
464
482
|
private onWheel;
|
|
@@ -467,6 +485,7 @@ declare class InputHandler {
|
|
|
467
485
|
private onPointerUp;
|
|
468
486
|
private onKeyDown;
|
|
469
487
|
private onKeyUp;
|
|
488
|
+
private runAction;
|
|
470
489
|
private startPinch;
|
|
471
490
|
private handlePinchMove;
|
|
472
491
|
private getPinchPoints;
|
|
@@ -477,6 +496,8 @@ declare class InputHandler {
|
|
|
477
496
|
private dispatchToolMove;
|
|
478
497
|
private dispatchToolHover;
|
|
479
498
|
private dispatchToolUp;
|
|
499
|
+
private isInScope;
|
|
500
|
+
private focusSelf;
|
|
480
501
|
private cancelToolIfActive;
|
|
481
502
|
}
|
|
482
503
|
|
|
@@ -567,6 +588,7 @@ interface ViewportOptions {
|
|
|
567
588
|
background?: BackgroundOptions;
|
|
568
589
|
fontSizePresets?: FontSizePreset[];
|
|
569
590
|
toolbar?: boolean;
|
|
591
|
+
shortcuts?: ShortcutOptions;
|
|
570
592
|
onHtmlElementMount?: (elementId: string, domId: string | undefined, container: HTMLDivElement) => void;
|
|
571
593
|
onDrop?: (event: DragEvent, worldPosition: {
|
|
572
594
|
x: number;
|
|
@@ -614,6 +636,8 @@ declare class Viewport {
|
|
|
614
636
|
exportImage(options?: ExportImageOptions): Promise<Blob | null>;
|
|
615
637
|
loadState(state: CanvasState): void;
|
|
616
638
|
loadJSON(json: string): void;
|
|
639
|
+
setTool(name: string): void;
|
|
640
|
+
get shortcuts(): ShortcutsApi;
|
|
617
641
|
undo(): boolean;
|
|
618
642
|
redo(): boolean;
|
|
619
643
|
addImage(src: string, position: {
|
|
@@ -1209,6 +1233,6 @@ declare class UpdateLayerCommand implements Command {
|
|
|
1209
1233
|
undo(_store: ElementStore): void;
|
|
1210
1234
|
}
|
|
1211
1235
|
|
|
1212
|
-
declare const VERSION = "0.
|
|
1236
|
+
declare const VERSION = "0.19.0";
|
|
1213
1237
|
|
|
1214
|
-
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 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 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 };
|
|
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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -411,12 +411,14 @@ declare class HistoryRecorder {
|
|
|
411
411
|
private readonly layerManager?;
|
|
412
412
|
private recording;
|
|
413
413
|
private transaction;
|
|
414
|
+
private generation;
|
|
414
415
|
private updateSnapshots;
|
|
415
416
|
private unsubscribers;
|
|
416
417
|
constructor(store: ElementStore, stack: HistoryStack, layerManager?: LayerManager | undefined);
|
|
417
418
|
pause(): void;
|
|
418
419
|
resume(): void;
|
|
419
420
|
begin(): void;
|
|
421
|
+
get currentTransactionId(): number | null;
|
|
420
422
|
commit(): void;
|
|
421
423
|
rollback(): void;
|
|
422
424
|
destroy(): void;
|
|
@@ -430,12 +432,25 @@ declare class HistoryRecorder {
|
|
|
430
432
|
private flushUpdateSnapshots;
|
|
431
433
|
}
|
|
432
434
|
|
|
435
|
+
type ShortcutBindings = Record<string, string | string[] | null>;
|
|
436
|
+
interface ShortcutOptions {
|
|
437
|
+
scope?: 'focus' | 'window';
|
|
438
|
+
bindings?: ShortcutBindings;
|
|
439
|
+
}
|
|
440
|
+
interface ShortcutsApi {
|
|
441
|
+
rebind(action: string, bindings: string | string[] | null): void;
|
|
442
|
+
disable(action: string): void;
|
|
443
|
+
reset(action?: string): void;
|
|
444
|
+
getBindings(): Record<string, string[]>;
|
|
445
|
+
}
|
|
446
|
+
|
|
433
447
|
interface InputHandlerOptions {
|
|
434
448
|
toolManager?: ToolManager;
|
|
435
449
|
toolContext?: ToolContext;
|
|
436
450
|
historyRecorder?: HistoryRecorder;
|
|
437
451
|
historyStack?: HistoryStack;
|
|
438
452
|
fitToContent?: () => void;
|
|
453
|
+
shortcuts?: ShortcutOptions;
|
|
439
454
|
}
|
|
440
455
|
declare class InputHandler {
|
|
441
456
|
private readonly element;
|
|
@@ -456,9 +471,12 @@ declare class InputHandler {
|
|
|
456
471
|
private deferredDown;
|
|
457
472
|
private readonly abortController;
|
|
458
473
|
private readonly actions;
|
|
474
|
+
private readonly shortcutMap;
|
|
475
|
+
private readonly scope;
|
|
459
476
|
constructor(element: HTMLElement, camera: Camera, options?: InputHandlerOptions);
|
|
460
477
|
setToolManager(toolManager: ToolManager, toolContext: ToolContext): void;
|
|
461
478
|
flushPendingHistory(): void;
|
|
479
|
+
get shortcuts(): ShortcutsApi;
|
|
462
480
|
destroy(): void;
|
|
463
481
|
private bind;
|
|
464
482
|
private onWheel;
|
|
@@ -467,6 +485,7 @@ declare class InputHandler {
|
|
|
467
485
|
private onPointerUp;
|
|
468
486
|
private onKeyDown;
|
|
469
487
|
private onKeyUp;
|
|
488
|
+
private runAction;
|
|
470
489
|
private startPinch;
|
|
471
490
|
private handlePinchMove;
|
|
472
491
|
private getPinchPoints;
|
|
@@ -477,6 +496,8 @@ declare class InputHandler {
|
|
|
477
496
|
private dispatchToolMove;
|
|
478
497
|
private dispatchToolHover;
|
|
479
498
|
private dispatchToolUp;
|
|
499
|
+
private isInScope;
|
|
500
|
+
private focusSelf;
|
|
480
501
|
private cancelToolIfActive;
|
|
481
502
|
}
|
|
482
503
|
|
|
@@ -567,6 +588,7 @@ interface ViewportOptions {
|
|
|
567
588
|
background?: BackgroundOptions;
|
|
568
589
|
fontSizePresets?: FontSizePreset[];
|
|
569
590
|
toolbar?: boolean;
|
|
591
|
+
shortcuts?: ShortcutOptions;
|
|
570
592
|
onHtmlElementMount?: (elementId: string, domId: string | undefined, container: HTMLDivElement) => void;
|
|
571
593
|
onDrop?: (event: DragEvent, worldPosition: {
|
|
572
594
|
x: number;
|
|
@@ -614,6 +636,8 @@ declare class Viewport {
|
|
|
614
636
|
exportImage(options?: ExportImageOptions): Promise<Blob | null>;
|
|
615
637
|
loadState(state: CanvasState): void;
|
|
616
638
|
loadJSON(json: string): void;
|
|
639
|
+
setTool(name: string): void;
|
|
640
|
+
get shortcuts(): ShortcutsApi;
|
|
617
641
|
undo(): boolean;
|
|
618
642
|
redo(): boolean;
|
|
619
643
|
addImage(src: string, position: {
|
|
@@ -1209,6 +1233,6 @@ declare class UpdateLayerCommand implements Command {
|
|
|
1209
1233
|
undo(_store: ElementStore): void;
|
|
1210
1234
|
}
|
|
1211
1235
|
|
|
1212
|
-
declare const VERSION = "0.
|
|
1236
|
+
declare const VERSION = "0.19.0";
|
|
1213
1237
|
|
|
1214
|
-
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 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 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 };
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -15,7 +15,13 @@ var EventBus = class {
|
|
|
15
15
|
this.listeners.get(event)?.delete(listener);
|
|
16
16
|
}
|
|
17
17
|
emit(event, data) {
|
|
18
|
-
this.listeners.get(event)?.forEach((listener) =>
|
|
18
|
+
this.listeners.get(event)?.forEach((listener) => {
|
|
19
|
+
try {
|
|
20
|
+
listener(data);
|
|
21
|
+
} catch (err) {
|
|
22
|
+
console.error(`[fieldnotes] listener error for "${String(event)}"`, err);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
19
25
|
}
|
|
20
26
|
clear() {
|
|
21
27
|
this.listeners.clear();
|
|
@@ -819,6 +825,7 @@ var KeyboardActions = class {
|
|
|
819
825
|
clipboard = [];
|
|
820
826
|
pasteCount = 0;
|
|
821
827
|
nudgeTimer = null;
|
|
828
|
+
nudgeTxId = null;
|
|
822
829
|
dispose() {
|
|
823
830
|
this.flushPendingNudge();
|
|
824
831
|
}
|
|
@@ -837,7 +844,9 @@ var KeyboardActions = class {
|
|
|
837
844
|
if (sel.tool.selectedIds.length === 0) return false;
|
|
838
845
|
const step = byCell ? sel.ctx.gridSize ?? 10 : 1;
|
|
839
846
|
if (this.nudgeTimer === null) {
|
|
840
|
-
this.deps.getHistoryRecorder()
|
|
847
|
+
const recorder = this.deps.getHistoryRecorder();
|
|
848
|
+
recorder?.begin();
|
|
849
|
+
this.nudgeTxId = recorder?.currentTransactionId ?? null;
|
|
841
850
|
} else {
|
|
842
851
|
clearTimeout(this.nudgeTimer);
|
|
843
852
|
}
|
|
@@ -849,9 +858,14 @@ var KeyboardActions = class {
|
|
|
849
858
|
if (this.nudgeTimer === null) return;
|
|
850
859
|
clearTimeout(this.nudgeTimer);
|
|
851
860
|
this.nudgeTimer = null;
|
|
852
|
-
this.deps.getHistoryRecorder()
|
|
861
|
+
const recorder = this.deps.getHistoryRecorder();
|
|
862
|
+
if (this.nudgeTxId === null || recorder?.currentTransactionId === this.nudgeTxId) {
|
|
863
|
+
recorder?.commit();
|
|
864
|
+
}
|
|
865
|
+
this.nudgeTxId = null;
|
|
853
866
|
}
|
|
854
867
|
deleteSelected() {
|
|
868
|
+
if (this.deps.isToolActive()) return;
|
|
855
869
|
this.flushPendingNudge();
|
|
856
870
|
const sel = this.selectTool();
|
|
857
871
|
if (!sel) return;
|
|
@@ -866,6 +880,7 @@ var KeyboardActions = class {
|
|
|
866
880
|
sel.ctx.requestRender();
|
|
867
881
|
}
|
|
868
882
|
undo() {
|
|
883
|
+
if (this.deps.isToolActive()) return;
|
|
869
884
|
this.flushPendingNudge();
|
|
870
885
|
const ctx = this.deps.getToolContext();
|
|
871
886
|
const stack = this.deps.getHistoryStack();
|
|
@@ -877,6 +892,7 @@ var KeyboardActions = class {
|
|
|
877
892
|
ctx.requestRender();
|
|
878
893
|
}
|
|
879
894
|
redo() {
|
|
895
|
+
if (this.deps.isToolActive()) return;
|
|
880
896
|
this.flushPendingNudge();
|
|
881
897
|
const ctx = this.deps.getToolContext();
|
|
882
898
|
const stack = this.deps.getHistoryStack();
|
|
@@ -950,6 +966,7 @@ var KeyboardActions = class {
|
|
|
950
966
|
this.deps.fitToContent?.();
|
|
951
967
|
}
|
|
952
968
|
zOrder(operation) {
|
|
969
|
+
if (this.deps.isToolActive()) return;
|
|
953
970
|
this.flushPendingNudge();
|
|
954
971
|
const sel = this.selectTool();
|
|
955
972
|
if (!sel) return;
|
|
@@ -1024,14 +1041,158 @@ var KeyboardActions = class {
|
|
|
1024
1041
|
}
|
|
1025
1042
|
};
|
|
1026
1043
|
|
|
1044
|
+
// src/canvas/shortcut-map.ts
|
|
1045
|
+
var DEFAULT_BINDINGS = [
|
|
1046
|
+
["delete", ["delete", "backspace"]],
|
|
1047
|
+
["deselect", ["escape"]],
|
|
1048
|
+
["undo", ["mod+z"]],
|
|
1049
|
+
["redo", ["mod+y", "mod+shift+z"]],
|
|
1050
|
+
["select-all", ["mod+a"]],
|
|
1051
|
+
["copy", ["mod+c"]],
|
|
1052
|
+
["paste", ["mod+v"]],
|
|
1053
|
+
["duplicate", ["mod+d"]],
|
|
1054
|
+
["z-forward", ["]"]],
|
|
1055
|
+
["z-backward", ["["]],
|
|
1056
|
+
["z-front", ["mod+]"]],
|
|
1057
|
+
["z-back", ["mod+["]],
|
|
1058
|
+
["zoom-fit", ["shift+1"]],
|
|
1059
|
+
["nudge-left", ["arrowleft"]],
|
|
1060
|
+
["nudge-right", ["arrowright"]],
|
|
1061
|
+
["nudge-up", ["arrowup"]],
|
|
1062
|
+
["nudge-down", ["arrowdown"]],
|
|
1063
|
+
["tool:select", ["v"]],
|
|
1064
|
+
["tool:hand", ["h"]],
|
|
1065
|
+
["tool:pencil", ["p"]],
|
|
1066
|
+
["tool:eraser", ["e"]],
|
|
1067
|
+
["tool:arrow", ["a"]],
|
|
1068
|
+
["tool:note", ["n"]],
|
|
1069
|
+
["tool:text", ["t"]],
|
|
1070
|
+
["tool:shape", ["s"]],
|
|
1071
|
+
["tool:measure", ["m"]],
|
|
1072
|
+
["tool:template", ["g"]]
|
|
1073
|
+
];
|
|
1074
|
+
var ALLOW_SHIFT = /* @__PURE__ */ new Set(["nudge-left", "nudge-right", "nudge-up", "nudge-down"]);
|
|
1075
|
+
var MODIFIERS = /* @__PURE__ */ new Set(["mod", "ctrl", "meta", "shift", "alt"]);
|
|
1076
|
+
function parseBinding(binding) {
|
|
1077
|
+
const parts = binding.toLowerCase().split("+");
|
|
1078
|
+
const key = parts.pop();
|
|
1079
|
+
if (key === void 0 || key.length === 0 || MODIFIERS.has(key)) {
|
|
1080
|
+
throw new Error(`Invalid shortcut binding "${binding}": missing key`);
|
|
1081
|
+
}
|
|
1082
|
+
const normalizedKey = key === "space" ? " " : key;
|
|
1083
|
+
const parsed = {
|
|
1084
|
+
mod: false,
|
|
1085
|
+
ctrl: false,
|
|
1086
|
+
meta: false,
|
|
1087
|
+
shift: false,
|
|
1088
|
+
alt: false,
|
|
1089
|
+
key: normalizedKey,
|
|
1090
|
+
digit: /^[0-9]$/.test(normalizedKey)
|
|
1091
|
+
};
|
|
1092
|
+
for (const part of parts) {
|
|
1093
|
+
switch (part) {
|
|
1094
|
+
case "mod":
|
|
1095
|
+
parsed.mod = true;
|
|
1096
|
+
break;
|
|
1097
|
+
case "ctrl":
|
|
1098
|
+
parsed.ctrl = true;
|
|
1099
|
+
break;
|
|
1100
|
+
case "meta":
|
|
1101
|
+
parsed.meta = true;
|
|
1102
|
+
break;
|
|
1103
|
+
case "shift":
|
|
1104
|
+
parsed.shift = true;
|
|
1105
|
+
break;
|
|
1106
|
+
case "alt":
|
|
1107
|
+
parsed.alt = true;
|
|
1108
|
+
break;
|
|
1109
|
+
default:
|
|
1110
|
+
throw new Error(`Invalid shortcut binding "${binding}": unknown modifier "${part}"`);
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
return parsed;
|
|
1114
|
+
}
|
|
1115
|
+
function bindingMatches(p, e, allowShift) {
|
|
1116
|
+
if (p.mod) {
|
|
1117
|
+
if (!e.ctrlKey && !e.metaKey) return false;
|
|
1118
|
+
} else if (e.ctrlKey !== p.ctrl || e.metaKey !== p.meta) {
|
|
1119
|
+
return false;
|
|
1120
|
+
}
|
|
1121
|
+
if (!allowShift && e.shiftKey !== p.shift) return false;
|
|
1122
|
+
if (e.altKey !== p.alt) return false;
|
|
1123
|
+
return p.digit ? e.code === `Digit${p.key}` : e.key.toLowerCase() === p.key;
|
|
1124
|
+
}
|
|
1125
|
+
function toArray(bindings) {
|
|
1126
|
+
if (bindings === null) return [];
|
|
1127
|
+
return Array.isArray(bindings) ? [...bindings] : [bindings];
|
|
1128
|
+
}
|
|
1129
|
+
var ShortcutMap = class {
|
|
1130
|
+
raw = /* @__PURE__ */ new Map();
|
|
1131
|
+
parsed = /* @__PURE__ */ new Map();
|
|
1132
|
+
constructor(overrides) {
|
|
1133
|
+
this.applyDefaults();
|
|
1134
|
+
if (overrides) {
|
|
1135
|
+
for (const [action, bindings] of Object.entries(overrides)) {
|
|
1136
|
+
this.rebind(action, bindings);
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
/** First matching action in registration order wins when bindings conflict. */
|
|
1141
|
+
match(e) {
|
|
1142
|
+
for (const [action, parsedList] of this.parsed) {
|
|
1143
|
+
const allowShift = ALLOW_SHIFT.has(action);
|
|
1144
|
+
for (const p of parsedList) {
|
|
1145
|
+
if (bindingMatches(p, e, allowShift)) return action;
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
return null;
|
|
1149
|
+
}
|
|
1150
|
+
rebind(action, bindings) {
|
|
1151
|
+
const list = toArray(bindings);
|
|
1152
|
+
const parsedList = list.map(parseBinding);
|
|
1153
|
+
this.raw.set(action, list);
|
|
1154
|
+
this.parsed.set(action, parsedList);
|
|
1155
|
+
}
|
|
1156
|
+
disable(action) {
|
|
1157
|
+
this.rebind(action, null);
|
|
1158
|
+
}
|
|
1159
|
+
reset(action) {
|
|
1160
|
+
if (action === void 0) {
|
|
1161
|
+
this.raw.clear();
|
|
1162
|
+
this.parsed.clear();
|
|
1163
|
+
this.applyDefaults();
|
|
1164
|
+
return;
|
|
1165
|
+
}
|
|
1166
|
+
const def = DEFAULT_BINDINGS.find(([name]) => name === action);
|
|
1167
|
+
if (def) {
|
|
1168
|
+
this.rebind(action, [...def[1]]);
|
|
1169
|
+
} else if (this.raw.has(action)) {
|
|
1170
|
+
this.raw.delete(action);
|
|
1171
|
+
this.parsed.delete(action);
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
getBindings() {
|
|
1175
|
+
const out = {};
|
|
1176
|
+
for (const [action, list] of this.raw) {
|
|
1177
|
+
out[action] = [...list];
|
|
1178
|
+
}
|
|
1179
|
+
return out;
|
|
1180
|
+
}
|
|
1181
|
+
applyDefaults() {
|
|
1182
|
+
for (const [action, bindings] of DEFAULT_BINDINGS) {
|
|
1183
|
+
this.rebind(action, [...bindings]);
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
};
|
|
1187
|
+
|
|
1027
1188
|
// src/canvas/input-handler.ts
|
|
1028
1189
|
var ZOOM_SENSITIVITY = 1e-3;
|
|
1029
1190
|
var MIDDLE_BUTTON = 1;
|
|
1030
|
-
var
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1191
|
+
var NUDGE_DELTAS = {
|
|
1192
|
+
"nudge-left": [-1, 0],
|
|
1193
|
+
"nudge-right": [1, 0],
|
|
1194
|
+
"nudge-up": [0, -1],
|
|
1195
|
+
"nudge-down": [0, 1]
|
|
1035
1196
|
};
|
|
1036
1197
|
var InputHandler = class {
|
|
1037
1198
|
constructor(element, camera, options = {}) {
|
|
@@ -1049,7 +1210,13 @@ var InputHandler = class {
|
|
|
1049
1210
|
isToolActive: () => this.isToolActive,
|
|
1050
1211
|
fitToContent: options.fitToContent
|
|
1051
1212
|
});
|
|
1213
|
+
this.shortcutMap = new ShortcutMap(options.shortcuts?.bindings);
|
|
1214
|
+
this.scope = options.shortcuts?.scope ?? "focus";
|
|
1052
1215
|
this.element.style.touchAction = "none";
|
|
1216
|
+
if (this.scope === "focus") {
|
|
1217
|
+
this.element.tabIndex = 0;
|
|
1218
|
+
this.element.style.outline = "none";
|
|
1219
|
+
}
|
|
1053
1220
|
this.bind();
|
|
1054
1221
|
}
|
|
1055
1222
|
isPanning = false;
|
|
@@ -1068,6 +1235,8 @@ var InputHandler = class {
|
|
|
1068
1235
|
deferredDown = null;
|
|
1069
1236
|
abortController = new AbortController();
|
|
1070
1237
|
actions;
|
|
1238
|
+
shortcutMap;
|
|
1239
|
+
scope;
|
|
1071
1240
|
setToolManager(toolManager, toolContext) {
|
|
1072
1241
|
this.toolManager = toolManager;
|
|
1073
1242
|
this.toolContext = toolContext;
|
|
@@ -1075,6 +1244,9 @@ var InputHandler = class {
|
|
|
1075
1244
|
flushPendingHistory() {
|
|
1076
1245
|
this.actions.flushPendingNudge();
|
|
1077
1246
|
}
|
|
1247
|
+
get shortcuts() {
|
|
1248
|
+
return this.shortcutMap;
|
|
1249
|
+
}
|
|
1078
1250
|
destroy() {
|
|
1079
1251
|
this.actions.dispose();
|
|
1080
1252
|
this.abortController.abort();
|
|
@@ -1104,6 +1276,7 @@ var InputHandler = class {
|
|
|
1104
1276
|
});
|
|
1105
1277
|
};
|
|
1106
1278
|
onPointerDown = (e) => {
|
|
1279
|
+
this.focusSelf();
|
|
1107
1280
|
this.activePointers.set(e.pointerId, { x: e.clientX, y: e.clientY });
|
|
1108
1281
|
this.element.setPointerCapture?.(e.pointerId);
|
|
1109
1282
|
if (this.activePointers.size === 2) {
|
|
@@ -1186,57 +1359,13 @@ var InputHandler = class {
|
|
|
1186
1359
|
if (target?.isContentEditable) return;
|
|
1187
1360
|
const tag = target?.tagName;
|
|
1188
1361
|
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return;
|
|
1362
|
+
if (!this.isInScope()) return;
|
|
1189
1363
|
if (e.key === " ") {
|
|
1190
1364
|
this.spaceHeld = true;
|
|
1191
1365
|
}
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
if (e.key === "Escape") {
|
|
1196
|
-
this.actions.deselect();
|
|
1197
|
-
}
|
|
1198
|
-
if ((e.ctrlKey || e.metaKey) && e.key === "z" && !e.shiftKey) {
|
|
1199
|
-
e.preventDefault();
|
|
1200
|
-
this.actions.undo();
|
|
1201
|
-
}
|
|
1202
|
-
if ((e.ctrlKey || e.metaKey) && (e.key === "y" || e.key === "z" && e.shiftKey)) {
|
|
1203
|
-
e.preventDefault();
|
|
1204
|
-
this.actions.redo();
|
|
1205
|
-
}
|
|
1206
|
-
if ((e.ctrlKey || e.metaKey) && e.key === "a") {
|
|
1207
|
-
e.preventDefault();
|
|
1208
|
-
this.actions.selectAll();
|
|
1209
|
-
}
|
|
1210
|
-
if ((e.ctrlKey || e.metaKey) && e.key === "c") {
|
|
1211
|
-
e.preventDefault();
|
|
1212
|
-
this.actions.copy();
|
|
1213
|
-
}
|
|
1214
|
-
if ((e.ctrlKey || e.metaKey) && e.key === "v") {
|
|
1215
|
-
e.preventDefault();
|
|
1216
|
-
this.actions.paste();
|
|
1217
|
-
}
|
|
1218
|
-
if ((e.ctrlKey || e.metaKey) && e.key === "d") {
|
|
1219
|
-
e.preventDefault();
|
|
1220
|
-
this.actions.duplicate();
|
|
1221
|
-
}
|
|
1222
|
-
if (e.key === "]") {
|
|
1223
|
-
e.preventDefault();
|
|
1224
|
-
this.actions.zOrder(e.ctrlKey || e.metaKey ? "front" : "forward");
|
|
1225
|
-
}
|
|
1226
|
-
if (e.key === "[") {
|
|
1227
|
-
e.preventDefault();
|
|
1228
|
-
this.actions.zOrder(e.ctrlKey || e.metaKey ? "back" : "backward");
|
|
1229
|
-
}
|
|
1230
|
-
if (e.shiftKey && e.code === "Digit1" && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
|
1231
|
-
e.preventDefault();
|
|
1232
|
-
this.actions.zoomToFit();
|
|
1233
|
-
}
|
|
1234
|
-
const nudgeDelta = NUDGE_KEYS[e.key];
|
|
1235
|
-
if (nudgeDelta) {
|
|
1236
|
-
const [dx, dy] = nudgeDelta;
|
|
1237
|
-
if (this.actions.nudge(dx, dy, e.shiftKey)) {
|
|
1238
|
-
e.preventDefault();
|
|
1239
|
-
}
|
|
1366
|
+
const action = this.shortcutMap.match(e);
|
|
1367
|
+
if (action !== null) {
|
|
1368
|
+
this.runAction(action, e);
|
|
1240
1369
|
}
|
|
1241
1370
|
};
|
|
1242
1371
|
onKeyUp = (e) => {
|
|
@@ -1251,6 +1380,79 @@ var InputHandler = class {
|
|
|
1251
1380
|
}
|
|
1252
1381
|
}
|
|
1253
1382
|
};
|
|
1383
|
+
runAction(action, e) {
|
|
1384
|
+
switch (action) {
|
|
1385
|
+
case "delete":
|
|
1386
|
+
e.preventDefault();
|
|
1387
|
+
this.actions.deleteSelected();
|
|
1388
|
+
return;
|
|
1389
|
+
case "deselect":
|
|
1390
|
+
this.actions.deselect();
|
|
1391
|
+
return;
|
|
1392
|
+
case "undo":
|
|
1393
|
+
e.preventDefault();
|
|
1394
|
+
this.actions.undo();
|
|
1395
|
+
return;
|
|
1396
|
+
case "redo":
|
|
1397
|
+
e.preventDefault();
|
|
1398
|
+
this.actions.redo();
|
|
1399
|
+
return;
|
|
1400
|
+
case "select-all":
|
|
1401
|
+
e.preventDefault();
|
|
1402
|
+
this.actions.selectAll();
|
|
1403
|
+
return;
|
|
1404
|
+
case "copy":
|
|
1405
|
+
e.preventDefault();
|
|
1406
|
+
this.actions.copy();
|
|
1407
|
+
return;
|
|
1408
|
+
case "paste":
|
|
1409
|
+
e.preventDefault();
|
|
1410
|
+
this.actions.paste();
|
|
1411
|
+
return;
|
|
1412
|
+
case "duplicate":
|
|
1413
|
+
e.preventDefault();
|
|
1414
|
+
this.actions.duplicate();
|
|
1415
|
+
return;
|
|
1416
|
+
case "z-forward":
|
|
1417
|
+
e.preventDefault();
|
|
1418
|
+
this.actions.zOrder("forward");
|
|
1419
|
+
return;
|
|
1420
|
+
case "z-backward":
|
|
1421
|
+
e.preventDefault();
|
|
1422
|
+
this.actions.zOrder("backward");
|
|
1423
|
+
return;
|
|
1424
|
+
case "z-front":
|
|
1425
|
+
e.preventDefault();
|
|
1426
|
+
this.actions.zOrder("front");
|
|
1427
|
+
return;
|
|
1428
|
+
case "z-back":
|
|
1429
|
+
e.preventDefault();
|
|
1430
|
+
this.actions.zOrder("back");
|
|
1431
|
+
return;
|
|
1432
|
+
case "zoom-fit":
|
|
1433
|
+
e.preventDefault();
|
|
1434
|
+
this.actions.zoomToFit();
|
|
1435
|
+
return;
|
|
1436
|
+
case "nudge-left":
|
|
1437
|
+
case "nudge-right":
|
|
1438
|
+
case "nudge-up":
|
|
1439
|
+
case "nudge-down": {
|
|
1440
|
+
const delta = NUDGE_DELTAS[action];
|
|
1441
|
+
if (delta && this.actions.nudge(delta[0], delta[1], e.shiftKey)) {
|
|
1442
|
+
e.preventDefault();
|
|
1443
|
+
}
|
|
1444
|
+
return;
|
|
1445
|
+
}
|
|
1446
|
+
default:
|
|
1447
|
+
if (action.startsWith("tool:")) {
|
|
1448
|
+
if (this.isToolActive) return;
|
|
1449
|
+
e.preventDefault();
|
|
1450
|
+
this.toolContext?.switchTool?.(action.slice("tool:".length));
|
|
1451
|
+
return;
|
|
1452
|
+
}
|
|
1453
|
+
console.warn(`[fieldnotes] unknown shortcut action "${action}"`);
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1254
1456
|
startPinch() {
|
|
1255
1457
|
this.inputFilter.reset();
|
|
1256
1458
|
this.deferredDown = null;
|
|
@@ -1321,6 +1523,15 @@ var InputHandler = class {
|
|
|
1321
1523
|
this.toolManager.handlePointerUp(this.toPointerState(e), this.toolContext);
|
|
1322
1524
|
this.historyRecorder?.commit();
|
|
1323
1525
|
}
|
|
1526
|
+
isInScope() {
|
|
1527
|
+
if (this.scope === "window") return true;
|
|
1528
|
+
const active = document.activeElement;
|
|
1529
|
+
return active === this.element || this.element.contains(active);
|
|
1530
|
+
}
|
|
1531
|
+
focusSelf() {
|
|
1532
|
+
if (this.scope !== "focus" || this.isInScope()) return;
|
|
1533
|
+
this.element.focus({ preventScroll: true });
|
|
1534
|
+
}
|
|
1324
1535
|
cancelToolIfActive(e) {
|
|
1325
1536
|
if (this.isToolActive) {
|
|
1326
1537
|
this.dispatchToolUp(e);
|
|
@@ -3509,6 +3720,7 @@ var HistoryRecorder = class {
|
|
|
3509
3720
|
}
|
|
3510
3721
|
recording = true;
|
|
3511
3722
|
transaction = null;
|
|
3723
|
+
generation = 0;
|
|
3512
3724
|
updateSnapshots = /* @__PURE__ */ new Map();
|
|
3513
3725
|
unsubscribers;
|
|
3514
3726
|
pause() {
|
|
@@ -3523,6 +3735,10 @@ var HistoryRecorder = class {
|
|
|
3523
3735
|
}
|
|
3524
3736
|
this.transaction = [];
|
|
3525
3737
|
this.updateSnapshots.clear();
|
|
3738
|
+
this.generation += 1;
|
|
3739
|
+
}
|
|
3740
|
+
get currentTransactionId() {
|
|
3741
|
+
return this.transaction !== null ? this.generation : null;
|
|
3526
3742
|
}
|
|
3527
3743
|
commit() {
|
|
3528
3744
|
if (!this.transaction) return;
|
|
@@ -4710,7 +4926,8 @@ var Viewport = class {
|
|
|
4710
4926
|
toolContext: this.toolContext,
|
|
4711
4927
|
historyRecorder: this.historyRecorder,
|
|
4712
4928
|
historyStack: this.history,
|
|
4713
|
-
fitToContent: () => this.fitToContent()
|
|
4929
|
+
fitToContent: () => this.fitToContent(),
|
|
4930
|
+
shortcuts: options.shortcuts
|
|
4714
4931
|
});
|
|
4715
4932
|
this.domNodeManager = new DomNodeManager({
|
|
4716
4933
|
domLayer: this.domLayer,
|
|
@@ -4881,6 +5098,12 @@ var Viewport = class {
|
|
|
4881
5098
|
loadJSON(json) {
|
|
4882
5099
|
this.loadState(parseState(json));
|
|
4883
5100
|
}
|
|
5101
|
+
setTool(name) {
|
|
5102
|
+
this.toolManager.setTool(name, this.toolContext);
|
|
5103
|
+
}
|
|
5104
|
+
get shortcuts() {
|
|
5105
|
+
return this.inputHandler.shortcuts;
|
|
5106
|
+
}
|
|
4884
5107
|
undo() {
|
|
4885
5108
|
this.inputHandler.flushPendingHistory();
|
|
4886
5109
|
this.historyRecorder.pause();
|
|
@@ -6919,7 +7142,7 @@ var TemplateTool = class {
|
|
|
6919
7142
|
};
|
|
6920
7143
|
|
|
6921
7144
|
// src/index.ts
|
|
6922
|
-
var VERSION = "0.
|
|
7145
|
+
var VERSION = "0.19.0";
|
|
6923
7146
|
export {
|
|
6924
7147
|
AddElementCommand,
|
|
6925
7148
|
ArrowTool,
|
|
@@ -7006,3 +7229,4 @@ export {
|
|
|
7006
7229
|
unbindArrow,
|
|
7007
7230
|
updateBoundArrow
|
|
7008
7231
|
};
|
|
7232
|
+
//# sourceMappingURL=index.js.map
|