@fieldnotes/core 0.26.0 → 0.28.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 +583 -503
- package/dist/index.cjs +306 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +26 -2
- package/dist/index.d.ts +26 -2
- package/dist/index.js +304 -10
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -50,6 +50,8 @@ interface ArrowElement extends BaseElement {
|
|
|
50
50
|
toBinding?: Binding;
|
|
51
51
|
/** Derived from from/to/bend. Redundant in serialized state — safe to omit. */
|
|
52
52
|
cachedControlPoint?: Point;
|
|
53
|
+
/** Optional text rendered at the curve midpoint. */
|
|
54
|
+
label?: string;
|
|
53
55
|
}
|
|
54
56
|
interface ImageElement extends BaseElement {
|
|
55
57
|
type: 'image';
|
|
@@ -394,6 +396,16 @@ interface RenderStatsSnapshot {
|
|
|
394
396
|
frameCount: number;
|
|
395
397
|
}
|
|
396
398
|
|
|
399
|
+
interface ElementStyle {
|
|
400
|
+
color?: string;
|
|
401
|
+
fillColor?: string;
|
|
402
|
+
strokeWidth?: number;
|
|
403
|
+
opacity?: number;
|
|
404
|
+
fontSize?: number;
|
|
405
|
+
}
|
|
406
|
+
declare function styleToPatch(element: CanvasElement, style: ElementStyle): Partial<CanvasElement>;
|
|
407
|
+
declare function getElementStyle(element: CanvasElement): ElementStyle;
|
|
408
|
+
|
|
397
409
|
interface GridInfo {
|
|
398
410
|
gridType: 'square' | 'hex';
|
|
399
411
|
hexOrientation: 'pointy' | 'flat';
|
|
@@ -436,6 +448,7 @@ declare class Viewport {
|
|
|
436
448
|
private readonly background;
|
|
437
449
|
private readonly renderer;
|
|
438
450
|
private readonly noteEditor;
|
|
451
|
+
private readonly arrowLabelEditor;
|
|
439
452
|
private readonly historyRecorder;
|
|
440
453
|
readonly toolContext: ToolContext;
|
|
441
454
|
private readonly marginViewport;
|
|
@@ -494,6 +507,11 @@ declare class Viewport {
|
|
|
494
507
|
removeGrid(): void;
|
|
495
508
|
getGridInfo(): GridInfo | null;
|
|
496
509
|
onGridChange(listener: (info: GridInfo | null) => void): () => void;
|
|
510
|
+
private getSelectTool;
|
|
511
|
+
getSelectedIds(): string[];
|
|
512
|
+
onSelectionChange(listener: () => void): () => void;
|
|
513
|
+
getSelectionStyle(): ElementStyle | null;
|
|
514
|
+
applyStyleToSelection(style: ElementStyle): void;
|
|
497
515
|
getRenderStats(): RenderStatsSnapshot;
|
|
498
516
|
logPerformance(intervalMs?: number): () => void;
|
|
499
517
|
destroy(): void;
|
|
@@ -502,6 +520,8 @@ declare class Viewport {
|
|
|
502
520
|
private onTextEditStop;
|
|
503
521
|
private onTapDown;
|
|
504
522
|
private onDoubleTap;
|
|
523
|
+
private findArrowAt;
|
|
524
|
+
private startArrowLabelEdit;
|
|
505
525
|
private hitTestWorld;
|
|
506
526
|
stopInteracting(): void;
|
|
507
527
|
private onDragOver;
|
|
@@ -559,6 +579,7 @@ interface ArrowInput extends BaseDefaults {
|
|
|
559
579
|
width?: number;
|
|
560
580
|
fromBinding?: Binding;
|
|
561
581
|
toBinding?: Binding;
|
|
582
|
+
label?: string;
|
|
562
583
|
}
|
|
563
584
|
interface ImageInput extends BaseDefaults {
|
|
564
585
|
position: Point;
|
|
@@ -700,6 +721,7 @@ declare class EraserTool implements Tool {
|
|
|
700
721
|
declare class SelectTool implements Tool {
|
|
701
722
|
readonly name = "select";
|
|
702
723
|
private _selectedIds;
|
|
724
|
+
private selectionListeners;
|
|
703
725
|
private mode;
|
|
704
726
|
private lastWorld;
|
|
705
727
|
private currentWorld;
|
|
@@ -709,6 +731,8 @@ declare class SelectTool implements Tool {
|
|
|
709
731
|
private resizeAspectRatio;
|
|
710
732
|
private hoveredId;
|
|
711
733
|
get selectedIds(): string[];
|
|
734
|
+
onSelectionChange(listener: () => void): () => void;
|
|
735
|
+
private setSelectedIds;
|
|
712
736
|
setSelection(ids: string[]): void;
|
|
713
737
|
get isMarqueeActive(): boolean;
|
|
714
738
|
onActivate(ctx: ToolContext): void;
|
|
@@ -933,6 +957,6 @@ declare class TemplateTool implements Tool {
|
|
|
933
957
|
private notifyOptionsChange;
|
|
934
958
|
}
|
|
935
959
|
|
|
936
|
-
declare const VERSION = "0.
|
|
960
|
+
declare const VERSION = "0.28.0";
|
|
937
961
|
|
|
938
|
-
export { type ActiveFormats, type ArrowElement, 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, ElementStore, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, type ExportImageOptions, type FontSizePreset, type GridElement, type GridInfo, HandTool, type HexOrientation, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, 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, getActiveFormats, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getElementBounds, getElementsBoundingBox, getHexCellsInCone, getHexCellsInLine, getHexCellsInRadius, getHexCellsInSquare, getHexDistance, isNearBezier, setFontSize, smartSnap, snapPoint, snapToHexCenter, toggleBold, toggleItalic, toggleStrikethrough, toggleUnderline };
|
|
962
|
+
export { type ActiveFormats, type ArrowElement, 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, ElementStore, type ElementStyle, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, type ExportImageOptions, type FontSizePreset, type GridElement, type GridInfo, HandTool, type HexOrientation, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, 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, 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
|
@@ -50,6 +50,8 @@ interface ArrowElement extends BaseElement {
|
|
|
50
50
|
toBinding?: Binding;
|
|
51
51
|
/** Derived from from/to/bend. Redundant in serialized state — safe to omit. */
|
|
52
52
|
cachedControlPoint?: Point;
|
|
53
|
+
/** Optional text rendered at the curve midpoint. */
|
|
54
|
+
label?: string;
|
|
53
55
|
}
|
|
54
56
|
interface ImageElement extends BaseElement {
|
|
55
57
|
type: 'image';
|
|
@@ -394,6 +396,16 @@ interface RenderStatsSnapshot {
|
|
|
394
396
|
frameCount: number;
|
|
395
397
|
}
|
|
396
398
|
|
|
399
|
+
interface ElementStyle {
|
|
400
|
+
color?: string;
|
|
401
|
+
fillColor?: string;
|
|
402
|
+
strokeWidth?: number;
|
|
403
|
+
opacity?: number;
|
|
404
|
+
fontSize?: number;
|
|
405
|
+
}
|
|
406
|
+
declare function styleToPatch(element: CanvasElement, style: ElementStyle): Partial<CanvasElement>;
|
|
407
|
+
declare function getElementStyle(element: CanvasElement): ElementStyle;
|
|
408
|
+
|
|
397
409
|
interface GridInfo {
|
|
398
410
|
gridType: 'square' | 'hex';
|
|
399
411
|
hexOrientation: 'pointy' | 'flat';
|
|
@@ -436,6 +448,7 @@ declare class Viewport {
|
|
|
436
448
|
private readonly background;
|
|
437
449
|
private readonly renderer;
|
|
438
450
|
private readonly noteEditor;
|
|
451
|
+
private readonly arrowLabelEditor;
|
|
439
452
|
private readonly historyRecorder;
|
|
440
453
|
readonly toolContext: ToolContext;
|
|
441
454
|
private readonly marginViewport;
|
|
@@ -494,6 +507,11 @@ declare class Viewport {
|
|
|
494
507
|
removeGrid(): void;
|
|
495
508
|
getGridInfo(): GridInfo | null;
|
|
496
509
|
onGridChange(listener: (info: GridInfo | null) => void): () => void;
|
|
510
|
+
private getSelectTool;
|
|
511
|
+
getSelectedIds(): string[];
|
|
512
|
+
onSelectionChange(listener: () => void): () => void;
|
|
513
|
+
getSelectionStyle(): ElementStyle | null;
|
|
514
|
+
applyStyleToSelection(style: ElementStyle): void;
|
|
497
515
|
getRenderStats(): RenderStatsSnapshot;
|
|
498
516
|
logPerformance(intervalMs?: number): () => void;
|
|
499
517
|
destroy(): void;
|
|
@@ -502,6 +520,8 @@ declare class Viewport {
|
|
|
502
520
|
private onTextEditStop;
|
|
503
521
|
private onTapDown;
|
|
504
522
|
private onDoubleTap;
|
|
523
|
+
private findArrowAt;
|
|
524
|
+
private startArrowLabelEdit;
|
|
505
525
|
private hitTestWorld;
|
|
506
526
|
stopInteracting(): void;
|
|
507
527
|
private onDragOver;
|
|
@@ -559,6 +579,7 @@ interface ArrowInput extends BaseDefaults {
|
|
|
559
579
|
width?: number;
|
|
560
580
|
fromBinding?: Binding;
|
|
561
581
|
toBinding?: Binding;
|
|
582
|
+
label?: string;
|
|
562
583
|
}
|
|
563
584
|
interface ImageInput extends BaseDefaults {
|
|
564
585
|
position: Point;
|
|
@@ -700,6 +721,7 @@ declare class EraserTool implements Tool {
|
|
|
700
721
|
declare class SelectTool implements Tool {
|
|
701
722
|
readonly name = "select";
|
|
702
723
|
private _selectedIds;
|
|
724
|
+
private selectionListeners;
|
|
703
725
|
private mode;
|
|
704
726
|
private lastWorld;
|
|
705
727
|
private currentWorld;
|
|
@@ -709,6 +731,8 @@ declare class SelectTool implements Tool {
|
|
|
709
731
|
private resizeAspectRatio;
|
|
710
732
|
private hoveredId;
|
|
711
733
|
get selectedIds(): string[];
|
|
734
|
+
onSelectionChange(listener: () => void): () => void;
|
|
735
|
+
private setSelectedIds;
|
|
712
736
|
setSelection(ids: string[]): void;
|
|
713
737
|
get isMarqueeActive(): boolean;
|
|
714
738
|
onActivate(ctx: ToolContext): void;
|
|
@@ -933,6 +957,6 @@ declare class TemplateTool implements Tool {
|
|
|
933
957
|
private notifyOptionsChange;
|
|
934
958
|
}
|
|
935
959
|
|
|
936
|
-
declare const VERSION = "0.
|
|
960
|
+
declare const VERSION = "0.28.0";
|
|
937
961
|
|
|
938
|
-
export { type ActiveFormats, type ArrowElement, 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, ElementStore, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, type ExportImageOptions, type FontSizePreset, type GridElement, type GridInfo, HandTool, type HexOrientation, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, 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, getActiveFormats, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getElementBounds, getElementsBoundingBox, getHexCellsInCone, getHexCellsInLine, getHexCellsInRadius, getHexCellsInSquare, getHexDistance, isNearBezier, setFontSize, smartSnap, snapPoint, snapToHexCenter, toggleBold, toggleItalic, toggleStrikethrough, toggleUnderline };
|
|
962
|
+
export { type ActiveFormats, type ArrowElement, 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, ElementStore, type ElementStyle, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, type ExportImageOptions, type FontSizePreset, type GridElement, type GridInfo, HandTool, type HexOrientation, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, 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, 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
|
@@ -2658,6 +2658,7 @@ function drawHexPath(ctx, cx, cy, cellSize, orientation) {
|
|
|
2658
2658
|
var DOM_ELEMENT_TYPES = /* @__PURE__ */ new Set(["note", "html", "text"]);
|
|
2659
2659
|
var ARROWHEAD_LENGTH = 12;
|
|
2660
2660
|
var ARROWHEAD_ANGLE = Math.PI / 6;
|
|
2661
|
+
var ARROW_LABEL_FONT_SIZE = 14;
|
|
2661
2662
|
var ElementRenderer = class {
|
|
2662
2663
|
store = null;
|
|
2663
2664
|
imageCache = /* @__PURE__ */ new Map();
|
|
@@ -2668,6 +2669,7 @@ var ElementRenderer = class {
|
|
|
2668
2669
|
hexTileCache = null;
|
|
2669
2670
|
hexTileCacheKey = "";
|
|
2670
2671
|
gridBoundsOverride = null;
|
|
2672
|
+
labelEditingId = null;
|
|
2671
2673
|
setStore(store) {
|
|
2672
2674
|
this.store = store;
|
|
2673
2675
|
}
|
|
@@ -2686,6 +2688,9 @@ var ElementRenderer = class {
|
|
|
2686
2688
|
setGridBoundsOverride(bounds) {
|
|
2687
2689
|
this.gridBoundsOverride = bounds;
|
|
2688
2690
|
}
|
|
2691
|
+
setLabelEditingId(id) {
|
|
2692
|
+
this.labelEditingId = id;
|
|
2693
|
+
}
|
|
2689
2694
|
isDomElement(element) {
|
|
2690
2695
|
return DOM_ELEMENT_TYPES.has(element.type);
|
|
2691
2696
|
}
|
|
@@ -2762,6 +2767,28 @@ var ElementRenderer = class {
|
|
|
2762
2767
|
ctx.stroke();
|
|
2763
2768
|
this.renderArrowhead(ctx, arrow, visualTo, geometry.tangentEnd);
|
|
2764
2769
|
ctx.restore();
|
|
2770
|
+
this.renderArrowLabel(ctx, arrow);
|
|
2771
|
+
}
|
|
2772
|
+
renderArrowLabel(ctx, arrow) {
|
|
2773
|
+
if (!arrow.label || arrow.label.length === 0) return;
|
|
2774
|
+
if (arrow.id === this.labelEditingId) return;
|
|
2775
|
+
const mid = getArrowMidpoint(arrow.from, arrow.to, arrow.bend);
|
|
2776
|
+
ctx.save();
|
|
2777
|
+
ctx.font = `${ARROW_LABEL_FONT_SIZE}px system-ui, sans-serif`;
|
|
2778
|
+
const metrics = ctx.measureText(arrow.label);
|
|
2779
|
+
const padX = 6;
|
|
2780
|
+
const padY = 4;
|
|
2781
|
+
const w = metrics.width + padX * 2;
|
|
2782
|
+
const h = ARROW_LABEL_FONT_SIZE + padY * 2;
|
|
2783
|
+
ctx.fillStyle = "rgba(255, 255, 255, 0.9)";
|
|
2784
|
+
ctx.beginPath();
|
|
2785
|
+
ctx.roundRect(mid.x - w / 2, mid.y - h / 2, w, h, 4);
|
|
2786
|
+
ctx.fill();
|
|
2787
|
+
ctx.fillStyle = "#1a1a1a";
|
|
2788
|
+
ctx.textAlign = "center";
|
|
2789
|
+
ctx.textBaseline = "middle";
|
|
2790
|
+
ctx.fillText(arrow.label, mid.x, mid.y);
|
|
2791
|
+
ctx.restore();
|
|
2765
2792
|
}
|
|
2766
2793
|
renderArrowhead(ctx, arrow, tip, angle) {
|
|
2767
2794
|
ctx.beginPath();
|
|
@@ -3172,6 +3199,7 @@ function createArrow(input) {
|
|
|
3172
3199
|
};
|
|
3173
3200
|
if (input.fromBinding) result.fromBinding = input.fromBinding;
|
|
3174
3201
|
if (input.toBinding) result.toBinding = input.toBinding;
|
|
3202
|
+
if (input.label !== void 0) result.label = input.label;
|
|
3175
3203
|
return result;
|
|
3176
3204
|
}
|
|
3177
3205
|
function createImage(input) {
|
|
@@ -3635,6 +3663,84 @@ var NoteEditor = class {
|
|
|
3635
3663
|
}
|
|
3636
3664
|
};
|
|
3637
3665
|
|
|
3666
|
+
// src/elements/arrow-label-editor.ts
|
|
3667
|
+
var ArrowLabelEditor = class {
|
|
3668
|
+
input = null;
|
|
3669
|
+
done = false;
|
|
3670
|
+
get isEditing() {
|
|
3671
|
+
return this.input !== null;
|
|
3672
|
+
}
|
|
3673
|
+
startEditing(start) {
|
|
3674
|
+
if (this.input) this.cleanup();
|
|
3675
|
+
const { arrow, layer, store, recorder, onDone } = start;
|
|
3676
|
+
const mid = getArrowMidpoint(arrow.from, arrow.to, arrow.bend);
|
|
3677
|
+
const input = document.createElement("input");
|
|
3678
|
+
input.type = "text";
|
|
3679
|
+
input.value = arrow.label ?? "";
|
|
3680
|
+
Object.assign(input.style, {
|
|
3681
|
+
position: "absolute",
|
|
3682
|
+
left: `${mid.x}px`,
|
|
3683
|
+
top: `${mid.y}px`,
|
|
3684
|
+
transform: "translate(-50%, -50%)",
|
|
3685
|
+
// domLayer is pointer-events:none; the input must opt back in to receive taps/clicks.
|
|
3686
|
+
pointerEvents: "auto",
|
|
3687
|
+
font: "14px system-ui, sans-serif",
|
|
3688
|
+
padding: "2px 6px",
|
|
3689
|
+
border: "1px solid #2196F3",
|
|
3690
|
+
borderRadius: "4px",
|
|
3691
|
+
background: "#ffffff",
|
|
3692
|
+
color: "#1a1a1a",
|
|
3693
|
+
outline: "none",
|
|
3694
|
+
minWidth: "40px"
|
|
3695
|
+
});
|
|
3696
|
+
this.done = false;
|
|
3697
|
+
const commit = () => {
|
|
3698
|
+
if (this.done) return;
|
|
3699
|
+
this.done = true;
|
|
3700
|
+
const next = input.value.trim() || void 0;
|
|
3701
|
+
if (next !== arrow.label) {
|
|
3702
|
+
recorder.begin();
|
|
3703
|
+
store.update(arrow.id, { label: next });
|
|
3704
|
+
recorder.commit();
|
|
3705
|
+
}
|
|
3706
|
+
this.cleanup();
|
|
3707
|
+
onDone();
|
|
3708
|
+
};
|
|
3709
|
+
const cancel = () => {
|
|
3710
|
+
if (this.done) return;
|
|
3711
|
+
this.done = true;
|
|
3712
|
+
this.cleanup();
|
|
3713
|
+
onDone();
|
|
3714
|
+
};
|
|
3715
|
+
input.addEventListener("keydown", (e) => {
|
|
3716
|
+
if (e.key === "Enter") {
|
|
3717
|
+
e.preventDefault();
|
|
3718
|
+
commit();
|
|
3719
|
+
} else if (e.key === "Escape") {
|
|
3720
|
+
e.preventDefault();
|
|
3721
|
+
cancel();
|
|
3722
|
+
}
|
|
3723
|
+
e.stopPropagation();
|
|
3724
|
+
});
|
|
3725
|
+
input.addEventListener("blur", commit);
|
|
3726
|
+
layer.appendChild(input);
|
|
3727
|
+
this.input = input;
|
|
3728
|
+
input.focus();
|
|
3729
|
+
input.select();
|
|
3730
|
+
}
|
|
3731
|
+
/** Abort any in-progress edit without committing (e.g. on viewport teardown). */
|
|
3732
|
+
cancel() {
|
|
3733
|
+
this.done = true;
|
|
3734
|
+
this.cleanup();
|
|
3735
|
+
}
|
|
3736
|
+
cleanup() {
|
|
3737
|
+
if (this.input) {
|
|
3738
|
+
this.input.remove();
|
|
3739
|
+
this.input = null;
|
|
3740
|
+
}
|
|
3741
|
+
}
|
|
3742
|
+
};
|
|
3743
|
+
|
|
3638
3744
|
// src/tools/tool-manager.ts
|
|
3639
3745
|
var ToolManager = class {
|
|
3640
3746
|
tools = /* @__PURE__ */ new Map();
|
|
@@ -5155,7 +5261,104 @@ var MarginViewport = class {
|
|
|
5155
5261
|
}
|
|
5156
5262
|
};
|
|
5157
5263
|
|
|
5264
|
+
// src/elements/element-style.ts
|
|
5265
|
+
function styleToPatch(element, style) {
|
|
5266
|
+
const { color, fillColor, strokeWidth, opacity, fontSize } = style;
|
|
5267
|
+
switch (element.type) {
|
|
5268
|
+
case "stroke":
|
|
5269
|
+
return {
|
|
5270
|
+
...color !== void 0 ? { color } : {},
|
|
5271
|
+
...strokeWidth !== void 0 ? { width: strokeWidth } : {},
|
|
5272
|
+
...opacity !== void 0 ? { opacity } : {}
|
|
5273
|
+
};
|
|
5274
|
+
case "arrow":
|
|
5275
|
+
return {
|
|
5276
|
+
...color !== void 0 ? { color } : {},
|
|
5277
|
+
...strokeWidth !== void 0 ? { width: strokeWidth } : {}
|
|
5278
|
+
};
|
|
5279
|
+
case "shape":
|
|
5280
|
+
return {
|
|
5281
|
+
...color !== void 0 ? { strokeColor: color } : {},
|
|
5282
|
+
...fillColor !== void 0 ? { fillColor } : {},
|
|
5283
|
+
...strokeWidth !== void 0 ? { strokeWidth } : {}
|
|
5284
|
+
};
|
|
5285
|
+
case "text":
|
|
5286
|
+
return {
|
|
5287
|
+
...color !== void 0 ? { color } : {},
|
|
5288
|
+
...fontSize !== void 0 ? { fontSize } : {}
|
|
5289
|
+
};
|
|
5290
|
+
case "note":
|
|
5291
|
+
return {
|
|
5292
|
+
...color !== void 0 ? { textColor: color } : {},
|
|
5293
|
+
...fillColor !== void 0 ? { backgroundColor: fillColor } : {},
|
|
5294
|
+
...fontSize !== void 0 ? { fontSize } : {}
|
|
5295
|
+
};
|
|
5296
|
+
case "grid":
|
|
5297
|
+
return {
|
|
5298
|
+
...color !== void 0 ? { strokeColor: color } : {},
|
|
5299
|
+
...strokeWidth !== void 0 ? { strokeWidth } : {},
|
|
5300
|
+
...opacity !== void 0 ? { opacity } : {}
|
|
5301
|
+
};
|
|
5302
|
+
case "template":
|
|
5303
|
+
return {
|
|
5304
|
+
...color !== void 0 ? { strokeColor: color } : {},
|
|
5305
|
+
...fillColor !== void 0 ? { fillColor } : {},
|
|
5306
|
+
...strokeWidth !== void 0 ? { strokeWidth } : {},
|
|
5307
|
+
...opacity !== void 0 ? { opacity } : {}
|
|
5308
|
+
};
|
|
5309
|
+
default:
|
|
5310
|
+
return {};
|
|
5311
|
+
}
|
|
5312
|
+
}
|
|
5313
|
+
function getElementStyle(element) {
|
|
5314
|
+
switch (element.type) {
|
|
5315
|
+
case "stroke":
|
|
5316
|
+
return { color: element.color, strokeWidth: element.width, opacity: element.opacity };
|
|
5317
|
+
case "arrow":
|
|
5318
|
+
return { color: element.color, strokeWidth: element.width };
|
|
5319
|
+
case "shape":
|
|
5320
|
+
return {
|
|
5321
|
+
color: element.strokeColor,
|
|
5322
|
+
fillColor: element.fillColor,
|
|
5323
|
+
strokeWidth: element.strokeWidth
|
|
5324
|
+
};
|
|
5325
|
+
case "text":
|
|
5326
|
+
return { color: element.color, fontSize: element.fontSize };
|
|
5327
|
+
case "note":
|
|
5328
|
+
return {
|
|
5329
|
+
color: element.textColor,
|
|
5330
|
+
fillColor: element.backgroundColor,
|
|
5331
|
+
...element.fontSize !== void 0 ? { fontSize: element.fontSize } : {}
|
|
5332
|
+
};
|
|
5333
|
+
case "grid":
|
|
5334
|
+
return {
|
|
5335
|
+
color: element.strokeColor,
|
|
5336
|
+
strokeWidth: element.strokeWidth,
|
|
5337
|
+
opacity: element.opacity
|
|
5338
|
+
};
|
|
5339
|
+
case "template":
|
|
5340
|
+
return {
|
|
5341
|
+
color: element.strokeColor,
|
|
5342
|
+
fillColor: element.fillColor,
|
|
5343
|
+
strokeWidth: element.strokeWidth,
|
|
5344
|
+
opacity: element.opacity
|
|
5345
|
+
};
|
|
5346
|
+
default:
|
|
5347
|
+
return {};
|
|
5348
|
+
}
|
|
5349
|
+
}
|
|
5350
|
+
|
|
5158
5351
|
// src/canvas/viewport.ts
|
|
5352
|
+
var EMPTY_IDS = [];
|
|
5353
|
+
var ARROW_HIT_THRESHOLD = 10;
|
|
5354
|
+
function noop() {
|
|
5355
|
+
}
|
|
5356
|
+
function sharedValue(values) {
|
|
5357
|
+
const present = values.filter((v) => v !== void 0);
|
|
5358
|
+
if (present.length === 0) return void 0;
|
|
5359
|
+
const first = present[0];
|
|
5360
|
+
return present.every((v) => v === first) ? first : void 0;
|
|
5361
|
+
}
|
|
5159
5362
|
var Viewport = class {
|
|
5160
5363
|
constructor(container, options = {}) {
|
|
5161
5364
|
this.container = container;
|
|
@@ -5189,6 +5392,7 @@ var Viewport = class {
|
|
|
5189
5392
|
placeholder: options.placeholder
|
|
5190
5393
|
});
|
|
5191
5394
|
this.noteEditor.setOnStop((id) => this.onTextEditStop(id));
|
|
5395
|
+
this.arrowLabelEditor = new ArrowLabelEditor();
|
|
5192
5396
|
this.noteEditor.setHistoryHooks(
|
|
5193
5397
|
() => this.historyRecorder.begin(),
|
|
5194
5398
|
() => this.historyRecorder.commit()
|
|
@@ -5315,6 +5519,7 @@ var Viewport = class {
|
|
|
5315
5519
|
background;
|
|
5316
5520
|
renderer;
|
|
5317
5521
|
noteEditor;
|
|
5522
|
+
arrowLabelEditor;
|
|
5318
5523
|
historyRecorder;
|
|
5319
5524
|
toolContext;
|
|
5320
5525
|
marginViewport;
|
|
@@ -5507,6 +5712,52 @@ var Viewport = class {
|
|
|
5507
5712
|
this.gridChangeListeners.delete(listener);
|
|
5508
5713
|
};
|
|
5509
5714
|
}
|
|
5715
|
+
getSelectTool() {
|
|
5716
|
+
return this.toolManager.getTool("select");
|
|
5717
|
+
}
|
|
5718
|
+
getSelectedIds() {
|
|
5719
|
+
return this.getSelectTool()?.selectedIds ?? EMPTY_IDS;
|
|
5720
|
+
}
|
|
5721
|
+
onSelectionChange(listener) {
|
|
5722
|
+
const tool = this.getSelectTool();
|
|
5723
|
+
return tool ? tool.onSelectionChange(listener) : noop;
|
|
5724
|
+
}
|
|
5725
|
+
getSelectionStyle() {
|
|
5726
|
+
const ids = this.getSelectedIds();
|
|
5727
|
+
if (ids.length === 0) return null;
|
|
5728
|
+
const styles = [];
|
|
5729
|
+
for (const id of ids) {
|
|
5730
|
+
const el = this.store.getById(id);
|
|
5731
|
+
if (el) styles.push(getElementStyle(el));
|
|
5732
|
+
}
|
|
5733
|
+
if (styles.length === 0) return null;
|
|
5734
|
+
const result = {};
|
|
5735
|
+
const color = sharedValue(styles.map((s) => s.color));
|
|
5736
|
+
if (color !== void 0) result.color = color;
|
|
5737
|
+
const fillColor = sharedValue(styles.map((s) => s.fillColor));
|
|
5738
|
+
if (fillColor !== void 0) result.fillColor = fillColor;
|
|
5739
|
+
const strokeWidth = sharedValue(styles.map((s) => s.strokeWidth));
|
|
5740
|
+
if (strokeWidth !== void 0) result.strokeWidth = strokeWidth;
|
|
5741
|
+
const opacity = sharedValue(styles.map((s) => s.opacity));
|
|
5742
|
+
if (opacity !== void 0) result.opacity = opacity;
|
|
5743
|
+
const fontSize = sharedValue(styles.map((s) => s.fontSize));
|
|
5744
|
+
if (fontSize !== void 0) result.fontSize = fontSize;
|
|
5745
|
+
return result;
|
|
5746
|
+
}
|
|
5747
|
+
applyStyleToSelection(style) {
|
|
5748
|
+
const ids = this.getSelectedIds();
|
|
5749
|
+
if (ids.length === 0) return;
|
|
5750
|
+
this.historyRecorder.begin();
|
|
5751
|
+
for (const id of ids) {
|
|
5752
|
+
const el = this.store.getById(id);
|
|
5753
|
+
if (!el) continue;
|
|
5754
|
+
const patch = styleToPatch(el, style);
|
|
5755
|
+
if (Object.keys(patch).length > 0) {
|
|
5756
|
+
this.store.update(id, patch);
|
|
5757
|
+
}
|
|
5758
|
+
}
|
|
5759
|
+
this.historyRecorder.commit();
|
|
5760
|
+
}
|
|
5510
5761
|
getRenderStats() {
|
|
5511
5762
|
return this.renderLoop.getStats();
|
|
5512
5763
|
}
|
|
@@ -5523,6 +5774,7 @@ var Viewport = class {
|
|
|
5523
5774
|
this.renderLoop.stop();
|
|
5524
5775
|
this.interactMode.destroy();
|
|
5525
5776
|
this.noteEditor.destroy(this.store);
|
|
5777
|
+
this.arrowLabelEditor.cancel();
|
|
5526
5778
|
this.historyRecorder.destroy();
|
|
5527
5779
|
this.wrapper.removeEventListener("pointerdown", this.onTapDown);
|
|
5528
5780
|
this.wrapper.removeEventListener("pointerup", this.onDoubleTap);
|
|
@@ -5608,8 +5860,35 @@ var Viewport = class {
|
|
|
5608
5860
|
const hit = this.hitTestWorld(world);
|
|
5609
5861
|
if (hit?.type === "html") {
|
|
5610
5862
|
this.interactMode.startInteracting(hit.id);
|
|
5863
|
+
return;
|
|
5864
|
+
}
|
|
5865
|
+
const arrow = this.findArrowAt(world);
|
|
5866
|
+
if (arrow) {
|
|
5867
|
+
this.startArrowLabelEdit(arrow);
|
|
5611
5868
|
}
|
|
5612
5869
|
};
|
|
5870
|
+
findArrowAt(world) {
|
|
5871
|
+
const candidates = this.store.queryPoint(world).reverse();
|
|
5872
|
+
for (const el of candidates) {
|
|
5873
|
+
if (el.type === "arrow" && isNearBezier(world, el.from, el.to, el.bend, ARROW_HIT_THRESHOLD)) {
|
|
5874
|
+
return el;
|
|
5875
|
+
}
|
|
5876
|
+
}
|
|
5877
|
+
return void 0;
|
|
5878
|
+
}
|
|
5879
|
+
startArrowLabelEdit(arrow) {
|
|
5880
|
+
this.arrowLabelEditor.startEditing({
|
|
5881
|
+
arrow,
|
|
5882
|
+
layer: this.domLayer,
|
|
5883
|
+
store: this.store,
|
|
5884
|
+
recorder: this.historyRecorder,
|
|
5885
|
+
onDone: () => {
|
|
5886
|
+
this.renderer.setLabelEditingId(null);
|
|
5887
|
+
this.requestRender();
|
|
5888
|
+
}
|
|
5889
|
+
});
|
|
5890
|
+
this.renderer.setLabelEditingId(arrow.id);
|
|
5891
|
+
}
|
|
5613
5892
|
hitTestWorld(world) {
|
|
5614
5893
|
const candidates = this.store.queryPoint(world).reverse();
|
|
5615
5894
|
for (const el of candidates) {
|
|
@@ -6122,6 +6401,7 @@ var HANDLE_CURSORS = {
|
|
|
6122
6401
|
var SelectTool = class {
|
|
6123
6402
|
name = "select";
|
|
6124
6403
|
_selectedIds = [];
|
|
6404
|
+
selectionListeners = /* @__PURE__ */ new Set();
|
|
6125
6405
|
mode = { type: "idle" };
|
|
6126
6406
|
lastWorld = { x: 0, y: 0 };
|
|
6127
6407
|
currentWorld = { x: 0, y: 0 };
|
|
@@ -6131,10 +6411,22 @@ var SelectTool = class {
|
|
|
6131
6411
|
resizeAspectRatio = 0;
|
|
6132
6412
|
hoveredId = null;
|
|
6133
6413
|
get selectedIds() {
|
|
6134
|
-
return
|
|
6414
|
+
return this._selectedIds;
|
|
6135
6415
|
}
|
|
6136
|
-
|
|
6416
|
+
onSelectionChange(listener) {
|
|
6417
|
+
this.selectionListeners.add(listener);
|
|
6418
|
+
return () => {
|
|
6419
|
+
this.selectionListeners.delete(listener);
|
|
6420
|
+
};
|
|
6421
|
+
}
|
|
6422
|
+
setSelectedIds(ids) {
|
|
6423
|
+
const prev = this._selectedIds;
|
|
6424
|
+
if (prev.length === ids.length && prev.every((id, i) => id === ids[i])) return;
|
|
6137
6425
|
this._selectedIds = ids;
|
|
6426
|
+
for (const listener of this.selectionListeners) listener();
|
|
6427
|
+
}
|
|
6428
|
+
setSelection(ids) {
|
|
6429
|
+
this.setSelectedIds(ids);
|
|
6138
6430
|
this.ctx?.requestRender();
|
|
6139
6431
|
}
|
|
6140
6432
|
get isMarqueeActive() {
|
|
@@ -6144,7 +6436,7 @@ var SelectTool = class {
|
|
|
6144
6436
|
this.ctx = ctx;
|
|
6145
6437
|
}
|
|
6146
6438
|
onDeactivate(ctx) {
|
|
6147
|
-
this.
|
|
6439
|
+
this.setSelectedIds([]);
|
|
6148
6440
|
this.mode = { type: "idle" };
|
|
6149
6441
|
this.hoveredId = null;
|
|
6150
6442
|
ctx.setCursor?.("default");
|
|
@@ -6195,22 +6487,22 @@ var SelectTool = class {
|
|
|
6195
6487
|
const alreadySelected = this._selectedIds.includes(hit.id);
|
|
6196
6488
|
if (state.shiftKey) {
|
|
6197
6489
|
if (alreadySelected) {
|
|
6198
|
-
this.
|
|
6490
|
+
this.setSelectedIds(this._selectedIds.filter((id) => id !== hit.id));
|
|
6199
6491
|
this.mode = { type: "idle" };
|
|
6200
6492
|
} else {
|
|
6201
|
-
this.
|
|
6493
|
+
this.setSelectedIds([...this._selectedIds, hit.id]);
|
|
6202
6494
|
this.mode = hit.locked ? { type: "idle" } : { type: "dragging" };
|
|
6203
6495
|
}
|
|
6204
6496
|
} else {
|
|
6205
6497
|
if (!alreadySelected) {
|
|
6206
|
-
this.
|
|
6498
|
+
this.setSelectedIds([hit.id]);
|
|
6207
6499
|
} else if (this._selectedIds.length > 1) {
|
|
6208
6500
|
this.pendingSingleSelectId = hit.id;
|
|
6209
6501
|
}
|
|
6210
6502
|
this.mode = hit.locked ? { type: "idle" } : { type: "dragging" };
|
|
6211
6503
|
}
|
|
6212
6504
|
} else {
|
|
6213
|
-
this.
|
|
6505
|
+
this.setSelectedIds([]);
|
|
6214
6506
|
this.mode = { type: "marquee", start: world };
|
|
6215
6507
|
}
|
|
6216
6508
|
ctx.requestRender();
|
|
@@ -6283,12 +6575,12 @@ var SelectTool = class {
|
|
|
6283
6575
|
if (this.mode.type === "marquee") {
|
|
6284
6576
|
const rect = this.getMarqueeRect();
|
|
6285
6577
|
if (rect) {
|
|
6286
|
-
this.
|
|
6578
|
+
this.setSelectedIds(this.findElementsInRect(rect, ctx));
|
|
6287
6579
|
}
|
|
6288
6580
|
ctx.requestRender();
|
|
6289
6581
|
}
|
|
6290
6582
|
if (!this.hasDragged && this.pendingSingleSelectId !== null) {
|
|
6291
|
-
this.
|
|
6583
|
+
this.setSelectedIds([this.pendingSingleSelectId]);
|
|
6292
6584
|
}
|
|
6293
6585
|
this.pendingSingleSelectId = null;
|
|
6294
6586
|
this.hasDragged = false;
|
|
@@ -7510,7 +7802,7 @@ var TemplateTool = class {
|
|
|
7510
7802
|
};
|
|
7511
7803
|
|
|
7512
7804
|
// src/index.ts
|
|
7513
|
-
var VERSION = "0.
|
|
7805
|
+
var VERSION = "0.28.0";
|
|
7514
7806
|
export {
|
|
7515
7807
|
ArrowTool,
|
|
7516
7808
|
AutoSave,
|
|
@@ -7551,6 +7843,7 @@ export {
|
|
|
7551
7843
|
getArrowTangentAngle,
|
|
7552
7844
|
getBendFromPoint,
|
|
7553
7845
|
getElementBounds,
|
|
7846
|
+
getElementStyle,
|
|
7554
7847
|
getElementsBoundingBox,
|
|
7555
7848
|
getHexCellsInCone,
|
|
7556
7849
|
getHexCellsInLine,
|
|
@@ -7562,6 +7855,7 @@ export {
|
|
|
7562
7855
|
smartSnap,
|
|
7563
7856
|
snapPoint,
|
|
7564
7857
|
snapToHexCenter,
|
|
7858
|
+
styleToPatch,
|
|
7565
7859
|
toggleBold,
|
|
7566
7860
|
toggleItalic,
|
|
7567
7861
|
toggleStrikethrough,
|