@fieldnotes/core 0.29.0 → 0.31.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 +595 -583
- package/dist/index.cjs +129 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -3
- package/dist/index.d.ts +15 -3
- package/dist/index.js +129 -10
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -27,6 +27,8 @@ interface StrokeElement extends BaseElement {
|
|
|
27
27
|
color: string;
|
|
28
28
|
width: number;
|
|
29
29
|
opacity: number;
|
|
30
|
+
/** Optional canvas blend mode (e.g. highlighter uses 'multiply'). */
|
|
31
|
+
blendMode?: 'multiply';
|
|
30
32
|
}
|
|
31
33
|
interface NoteElement extends BaseElement {
|
|
32
34
|
type: 'note';
|
|
@@ -72,7 +74,7 @@ interface TextElement extends BaseElement {
|
|
|
72
74
|
color: string;
|
|
73
75
|
textAlign: 'left' | 'center' | 'right';
|
|
74
76
|
}
|
|
75
|
-
type ShapeKind = 'rectangle' | 'ellipse';
|
|
77
|
+
type ShapeKind = 'rectangle' | 'ellipse' | 'line';
|
|
76
78
|
interface ShapeElement extends BaseElement {
|
|
77
79
|
type: 'shape';
|
|
78
80
|
shape: ShapeKind;
|
|
@@ -80,6 +82,8 @@ interface ShapeElement extends BaseElement {
|
|
|
80
82
|
strokeColor: string;
|
|
81
83
|
strokeWidth: number;
|
|
82
84
|
fillColor: string;
|
|
85
|
+
/** Line-only: which bbox diagonal the segment runs along. Absent/false = main diagonal. */
|
|
86
|
+
flip?: boolean;
|
|
83
87
|
}
|
|
84
88
|
type HexOrientation = 'pointy' | 'flat';
|
|
85
89
|
interface GridElement extends BaseElement {
|
|
@@ -562,6 +566,7 @@ interface StrokeInput extends BaseDefaults {
|
|
|
562
566
|
color?: string;
|
|
563
567
|
width?: number;
|
|
564
568
|
opacity?: number;
|
|
569
|
+
blendMode?: 'multiply';
|
|
565
570
|
}
|
|
566
571
|
interface NoteInput extends BaseDefaults {
|
|
567
572
|
position: Point;
|
|
@@ -612,6 +617,7 @@ interface ShapeInput extends BaseDefaults {
|
|
|
612
617
|
strokeColor?: string;
|
|
613
618
|
strokeWidth?: number;
|
|
614
619
|
fillColor?: string;
|
|
620
|
+
flip?: boolean;
|
|
615
621
|
}
|
|
616
622
|
declare function createShape(input: ShapeInput): ShapeElement;
|
|
617
623
|
interface GridInput extends BaseDefaults {
|
|
@@ -669,14 +675,17 @@ declare class HandTool implements Tool {
|
|
|
669
675
|
}
|
|
670
676
|
|
|
671
677
|
interface PencilToolOptions {
|
|
678
|
+
name?: string;
|
|
672
679
|
color?: string;
|
|
673
680
|
width?: number;
|
|
674
681
|
smoothing?: number;
|
|
675
682
|
minPointDistance?: number;
|
|
676
683
|
progressiveSimplifyThreshold?: number;
|
|
684
|
+
opacity?: number;
|
|
685
|
+
blendMode?: 'multiply';
|
|
677
686
|
}
|
|
678
687
|
declare class PencilTool implements Tool {
|
|
679
|
-
readonly name
|
|
688
|
+
readonly name: string;
|
|
680
689
|
private drawing;
|
|
681
690
|
private points;
|
|
682
691
|
private color;
|
|
@@ -685,6 +694,8 @@ declare class PencilTool implements Tool {
|
|
|
685
694
|
private minPointDistance;
|
|
686
695
|
private progressiveThreshold;
|
|
687
696
|
private nextSimplifyAt;
|
|
697
|
+
private opacity;
|
|
698
|
+
private blendMode;
|
|
688
699
|
private optionListeners;
|
|
689
700
|
constructor(options?: PencilToolOptions);
|
|
690
701
|
onActivate(ctx: ToolContext): void;
|
|
@@ -752,6 +763,7 @@ declare class SelectTool implements Tool {
|
|
|
752
763
|
private setHovered;
|
|
753
764
|
private handleResize;
|
|
754
765
|
private hitTestResizeHandle;
|
|
766
|
+
private hitTestLineHandles;
|
|
755
767
|
private getHandlePositions;
|
|
756
768
|
private renderMarquee;
|
|
757
769
|
private renderSelectionBoxes;
|
|
@@ -960,6 +972,6 @@ declare class TemplateTool implements Tool {
|
|
|
960
972
|
private notifyOptionsChange;
|
|
961
973
|
}
|
|
962
974
|
|
|
963
|
-
declare const VERSION = "0.
|
|
975
|
+
declare const VERSION = "0.31.0";
|
|
964
976
|
|
|
965
977
|
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
|
@@ -27,6 +27,8 @@ interface StrokeElement extends BaseElement {
|
|
|
27
27
|
color: string;
|
|
28
28
|
width: number;
|
|
29
29
|
opacity: number;
|
|
30
|
+
/** Optional canvas blend mode (e.g. highlighter uses 'multiply'). */
|
|
31
|
+
blendMode?: 'multiply';
|
|
30
32
|
}
|
|
31
33
|
interface NoteElement extends BaseElement {
|
|
32
34
|
type: 'note';
|
|
@@ -72,7 +74,7 @@ interface TextElement extends BaseElement {
|
|
|
72
74
|
color: string;
|
|
73
75
|
textAlign: 'left' | 'center' | 'right';
|
|
74
76
|
}
|
|
75
|
-
type ShapeKind = 'rectangle' | 'ellipse';
|
|
77
|
+
type ShapeKind = 'rectangle' | 'ellipse' | 'line';
|
|
76
78
|
interface ShapeElement extends BaseElement {
|
|
77
79
|
type: 'shape';
|
|
78
80
|
shape: ShapeKind;
|
|
@@ -80,6 +82,8 @@ interface ShapeElement extends BaseElement {
|
|
|
80
82
|
strokeColor: string;
|
|
81
83
|
strokeWidth: number;
|
|
82
84
|
fillColor: string;
|
|
85
|
+
/** Line-only: which bbox diagonal the segment runs along. Absent/false = main diagonal. */
|
|
86
|
+
flip?: boolean;
|
|
83
87
|
}
|
|
84
88
|
type HexOrientation = 'pointy' | 'flat';
|
|
85
89
|
interface GridElement extends BaseElement {
|
|
@@ -562,6 +566,7 @@ interface StrokeInput extends BaseDefaults {
|
|
|
562
566
|
color?: string;
|
|
563
567
|
width?: number;
|
|
564
568
|
opacity?: number;
|
|
569
|
+
blendMode?: 'multiply';
|
|
565
570
|
}
|
|
566
571
|
interface NoteInput extends BaseDefaults {
|
|
567
572
|
position: Point;
|
|
@@ -612,6 +617,7 @@ interface ShapeInput extends BaseDefaults {
|
|
|
612
617
|
strokeColor?: string;
|
|
613
618
|
strokeWidth?: number;
|
|
614
619
|
fillColor?: string;
|
|
620
|
+
flip?: boolean;
|
|
615
621
|
}
|
|
616
622
|
declare function createShape(input: ShapeInput): ShapeElement;
|
|
617
623
|
interface GridInput extends BaseDefaults {
|
|
@@ -669,14 +675,17 @@ declare class HandTool implements Tool {
|
|
|
669
675
|
}
|
|
670
676
|
|
|
671
677
|
interface PencilToolOptions {
|
|
678
|
+
name?: string;
|
|
672
679
|
color?: string;
|
|
673
680
|
width?: number;
|
|
674
681
|
smoothing?: number;
|
|
675
682
|
minPointDistance?: number;
|
|
676
683
|
progressiveSimplifyThreshold?: number;
|
|
684
|
+
opacity?: number;
|
|
685
|
+
blendMode?: 'multiply';
|
|
677
686
|
}
|
|
678
687
|
declare class PencilTool implements Tool {
|
|
679
|
-
readonly name
|
|
688
|
+
readonly name: string;
|
|
680
689
|
private drawing;
|
|
681
690
|
private points;
|
|
682
691
|
private color;
|
|
@@ -685,6 +694,8 @@ declare class PencilTool implements Tool {
|
|
|
685
694
|
private minPointDistance;
|
|
686
695
|
private progressiveThreshold;
|
|
687
696
|
private nextSimplifyAt;
|
|
697
|
+
private opacity;
|
|
698
|
+
private blendMode;
|
|
688
699
|
private optionListeners;
|
|
689
700
|
constructor(options?: PencilToolOptions);
|
|
690
701
|
onActivate(ctx: ToolContext): void;
|
|
@@ -752,6 +763,7 @@ declare class SelectTool implements Tool {
|
|
|
752
763
|
private setHovered;
|
|
753
764
|
private handleResize;
|
|
754
765
|
private hitTestResizeHandle;
|
|
766
|
+
private hitTestLineHandles;
|
|
755
767
|
private getHandlePositions;
|
|
756
768
|
private renderMarquee;
|
|
757
769
|
private renderSelectionBoxes;
|
|
@@ -960,6 +972,6 @@ declare class TemplateTool implements Tool {
|
|
|
960
972
|
private notifyOptionsChange;
|
|
961
973
|
}
|
|
962
974
|
|
|
963
|
-
declare const VERSION = "0.
|
|
975
|
+
declare const VERSION = "0.31.0";
|
|
964
976
|
|
|
965
977
|
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
|
@@ -2195,6 +2195,26 @@ function getArrowRenderGeometry(arrow) {
|
|
|
2195
2195
|
return geometry;
|
|
2196
2196
|
}
|
|
2197
2197
|
|
|
2198
|
+
// src/elements/shape-geometry.ts
|
|
2199
|
+
function lineFromEndpoints(a, b) {
|
|
2200
|
+
return {
|
|
2201
|
+
position: { x: Math.min(a.x, b.x), y: Math.min(a.y, b.y) },
|
|
2202
|
+
size: { w: Math.abs(b.x - a.x), h: Math.abs(b.y - a.y) },
|
|
2203
|
+
flip: b.x > a.x !== b.y > a.y
|
|
2204
|
+
};
|
|
2205
|
+
}
|
|
2206
|
+
function lineEndpoints(shape) {
|
|
2207
|
+
const { x, y } = shape.position;
|
|
2208
|
+
const { w, h } = shape.size;
|
|
2209
|
+
return shape.flip ? [
|
|
2210
|
+
{ x, y: y + h },
|
|
2211
|
+
{ x: x + w, y }
|
|
2212
|
+
] : [
|
|
2213
|
+
{ x, y },
|
|
2214
|
+
{ x: x + w, y: y + h }
|
|
2215
|
+
];
|
|
2216
|
+
}
|
|
2217
|
+
|
|
2198
2218
|
// src/elements/arrow-binding.ts
|
|
2199
2219
|
var BINDABLE_TYPES = /* @__PURE__ */ new Set(["note", "text", "image", "html", "shape"]);
|
|
2200
2220
|
function isBindable(element) {
|
|
@@ -2719,6 +2739,7 @@ var ElementRenderer = class {
|
|
|
2719
2739
|
renderStroke(ctx, stroke) {
|
|
2720
2740
|
if (stroke.points.length < 2) return;
|
|
2721
2741
|
ctx.save();
|
|
2742
|
+
if (stroke.blendMode) ctx.globalCompositeOperation = stroke.blendMode;
|
|
2722
2743
|
ctx.translate(stroke.position.x, stroke.position.y);
|
|
2723
2744
|
ctx.strokeStyle = stroke.color;
|
|
2724
2745
|
ctx.lineCap = "round";
|
|
@@ -2841,7 +2862,7 @@ var ElementRenderer = class {
|
|
|
2841
2862
|
}
|
|
2842
2863
|
renderShape(ctx, shape) {
|
|
2843
2864
|
ctx.save();
|
|
2844
|
-
if (shape.fillColor !== "none") {
|
|
2865
|
+
if (shape.fillColor !== "none" && shape.shape !== "line") {
|
|
2845
2866
|
ctx.fillStyle = shape.fillColor;
|
|
2846
2867
|
this.fillShapePath(ctx, shape);
|
|
2847
2868
|
}
|
|
@@ -2880,6 +2901,15 @@ var ElementRenderer = class {
|
|
|
2880
2901
|
ctx.stroke();
|
|
2881
2902
|
break;
|
|
2882
2903
|
}
|
|
2904
|
+
case "line": {
|
|
2905
|
+
const [a, b] = lineEndpoints(shape);
|
|
2906
|
+
ctx.lineCap = "round";
|
|
2907
|
+
ctx.beginPath();
|
|
2908
|
+
ctx.moveTo(a.x, a.y);
|
|
2909
|
+
ctx.lineTo(b.x, b.y);
|
|
2910
|
+
ctx.stroke();
|
|
2911
|
+
break;
|
|
2912
|
+
}
|
|
2883
2913
|
}
|
|
2884
2914
|
}
|
|
2885
2915
|
renderGrid(ctx, grid) {
|
|
@@ -3153,7 +3183,7 @@ var ElementRenderer = class {
|
|
|
3153
3183
|
// src/elements/element-factory.ts
|
|
3154
3184
|
var DEFAULT_NOTE_FONT_SIZE = 18;
|
|
3155
3185
|
function createStroke(input) {
|
|
3156
|
-
|
|
3186
|
+
const result = {
|
|
3157
3187
|
id: createId("stroke"),
|
|
3158
3188
|
type: "stroke",
|
|
3159
3189
|
position: input.position ?? { x: 0, y: 0 },
|
|
@@ -3165,6 +3195,8 @@ function createStroke(input) {
|
|
|
3165
3195
|
width: input.width ?? 2,
|
|
3166
3196
|
opacity: input.opacity ?? 1
|
|
3167
3197
|
};
|
|
3198
|
+
if (input.blendMode) result.blendMode = input.blendMode;
|
|
3199
|
+
return result;
|
|
3168
3200
|
}
|
|
3169
3201
|
function createNote(input) {
|
|
3170
3202
|
return {
|
|
@@ -3229,7 +3261,7 @@ function createHtmlElement(input) {
|
|
|
3229
3261
|
return el;
|
|
3230
3262
|
}
|
|
3231
3263
|
function createShape(input) {
|
|
3232
|
-
|
|
3264
|
+
const result = {
|
|
3233
3265
|
id: createId("shape"),
|
|
3234
3266
|
type: "shape",
|
|
3235
3267
|
position: input.position,
|
|
@@ -3242,6 +3274,8 @@ function createShape(input) {
|
|
|
3242
3274
|
strokeWidth: input.strokeWidth ?? 2,
|
|
3243
3275
|
fillColor: input.fillColor ?? "none"
|
|
3244
3276
|
};
|
|
3277
|
+
if (input.flip) result.flip = input.flip;
|
|
3278
|
+
return result;
|
|
3245
3279
|
}
|
|
3246
3280
|
function createGrid(input) {
|
|
3247
3281
|
return {
|
|
@@ -6072,7 +6106,7 @@ var DEFAULT_MIN_POINT_DISTANCE = 3;
|
|
|
6072
6106
|
var DEFAULT_PROGRESSIVE_THRESHOLD = 200;
|
|
6073
6107
|
var PROGRESSIVE_HOT_ZONE = 30;
|
|
6074
6108
|
var PencilTool = class {
|
|
6075
|
-
name
|
|
6109
|
+
name;
|
|
6076
6110
|
drawing = false;
|
|
6077
6111
|
points = [];
|
|
6078
6112
|
color;
|
|
@@ -6081,14 +6115,19 @@ var PencilTool = class {
|
|
|
6081
6115
|
minPointDistance;
|
|
6082
6116
|
progressiveThreshold;
|
|
6083
6117
|
nextSimplifyAt;
|
|
6118
|
+
opacity;
|
|
6119
|
+
blendMode;
|
|
6084
6120
|
optionListeners = /* @__PURE__ */ new Set();
|
|
6085
6121
|
constructor(options = {}) {
|
|
6122
|
+
this.name = options.name ?? "pencil";
|
|
6086
6123
|
this.color = options.color ?? "#000000";
|
|
6087
6124
|
this.width = options.width ?? 2;
|
|
6088
6125
|
this.smoothing = options.smoothing ?? DEFAULT_SMOOTHING;
|
|
6089
6126
|
this.minPointDistance = options.minPointDistance ?? DEFAULT_MIN_POINT_DISTANCE;
|
|
6090
6127
|
this.progressiveThreshold = options.progressiveSimplifyThreshold ?? DEFAULT_PROGRESSIVE_THRESHOLD;
|
|
6091
6128
|
this.nextSimplifyAt = this.progressiveThreshold;
|
|
6129
|
+
this.opacity = options.opacity ?? 1;
|
|
6130
|
+
this.blendMode = options.blendMode;
|
|
6092
6131
|
}
|
|
6093
6132
|
onActivate(ctx) {
|
|
6094
6133
|
ctx.setCursor?.("crosshair");
|
|
@@ -6102,7 +6141,9 @@ var PencilTool = class {
|
|
|
6102
6141
|
width: this.width,
|
|
6103
6142
|
smoothing: this.smoothing,
|
|
6104
6143
|
minPointDistance: this.minPointDistance,
|
|
6105
|
-
progressiveSimplifyThreshold: this.progressiveThreshold
|
|
6144
|
+
progressiveSimplifyThreshold: this.progressiveThreshold,
|
|
6145
|
+
opacity: this.opacity,
|
|
6146
|
+
blendMode: this.blendMode
|
|
6106
6147
|
};
|
|
6107
6148
|
}
|
|
6108
6149
|
onOptionsChange(listener) {
|
|
@@ -6116,6 +6157,8 @@ var PencilTool = class {
|
|
|
6116
6157
|
if (options.minPointDistance !== void 0) this.minPointDistance = options.minPointDistance;
|
|
6117
6158
|
if (options.progressiveSimplifyThreshold !== void 0)
|
|
6118
6159
|
this.progressiveThreshold = options.progressiveSimplifyThreshold;
|
|
6160
|
+
if (options.opacity !== void 0) this.opacity = options.opacity;
|
|
6161
|
+
if (options.blendMode !== void 0) this.blendMode = options.blendMode;
|
|
6119
6162
|
this.notifyOptionsChange();
|
|
6120
6163
|
}
|
|
6121
6164
|
onPointerDown(state, ctx) {
|
|
@@ -6157,7 +6200,9 @@ var PencilTool = class {
|
|
|
6157
6200
|
points: simplified,
|
|
6158
6201
|
color: this.color,
|
|
6159
6202
|
width: this.width,
|
|
6160
|
-
layerId: ctx.activeLayerId ?? ""
|
|
6203
|
+
layerId: ctx.activeLayerId ?? "",
|
|
6204
|
+
opacity: this.opacity,
|
|
6205
|
+
blendMode: this.blendMode
|
|
6161
6206
|
});
|
|
6162
6207
|
ctx.store.add(stroke);
|
|
6163
6208
|
computeStrokeSegments(stroke);
|
|
@@ -6173,7 +6218,8 @@ var PencilTool = class {
|
|
|
6173
6218
|
ctx.strokeStyle = this.color;
|
|
6174
6219
|
ctx.lineCap = "round";
|
|
6175
6220
|
ctx.lineJoin = "round";
|
|
6176
|
-
ctx.globalAlpha = 0.8;
|
|
6221
|
+
ctx.globalAlpha = this.blendMode ? this.opacity : 0.8;
|
|
6222
|
+
if (this.blendMode) ctx.globalCompositeOperation = this.blendMode;
|
|
6177
6223
|
const segments = smoothToSegments(this.points);
|
|
6178
6224
|
for (const seg of segments) {
|
|
6179
6225
|
const w = (pressureToWidth(seg.start.pressure, this.width) + pressureToWidth(seg.end.pressure, this.width)) / 2;
|
|
@@ -6562,6 +6608,12 @@ var SelectTool = class {
|
|
|
6562
6608
|
ctx.requestRender();
|
|
6563
6609
|
return;
|
|
6564
6610
|
}
|
|
6611
|
+
const lineHit = this.hitTestLineHandles(world, ctx);
|
|
6612
|
+
if (lineHit) {
|
|
6613
|
+
this.mode = { type: "line-handle", elementId: lineHit.elementId, fixed: lineHit.fixed };
|
|
6614
|
+
ctx.requestRender();
|
|
6615
|
+
return;
|
|
6616
|
+
}
|
|
6565
6617
|
const templateResizeHit = this.hitTestTemplateResizeHandle(world, ctx);
|
|
6566
6618
|
if (templateResizeHit) {
|
|
6567
6619
|
this.mode = { type: "resizing-template", elementId: templateResizeHit };
|
|
@@ -6617,6 +6669,15 @@ var SelectTool = class {
|
|
|
6617
6669
|
applyArrowHandleDrag(this.mode.handle, this.mode.elementId, world, ctx);
|
|
6618
6670
|
return;
|
|
6619
6671
|
}
|
|
6672
|
+
if (this.mode.type === "line-handle") {
|
|
6673
|
+
ctx.setCursor?.("grabbing");
|
|
6674
|
+
const el = ctx.store.getById(this.mode.elementId);
|
|
6675
|
+
if (el && el.type === "shape") {
|
|
6676
|
+
ctx.store.update(el.id, lineFromEndpoints(this.mode.fixed, world));
|
|
6677
|
+
}
|
|
6678
|
+
ctx.requestRender();
|
|
6679
|
+
return;
|
|
6680
|
+
}
|
|
6620
6681
|
if (this.mode.type === "resizing-template") {
|
|
6621
6682
|
ctx.setCursor?.("nwse-resize");
|
|
6622
6683
|
this.handleTemplateResize(world, ctx);
|
|
@@ -6785,6 +6846,10 @@ var SelectTool = class {
|
|
|
6785
6846
|
ctx.setCursor?.(getArrowHandleCursor(arrowHit.handle, false));
|
|
6786
6847
|
return null;
|
|
6787
6848
|
}
|
|
6849
|
+
if (this.hitTestLineHandles(world, ctx)) {
|
|
6850
|
+
ctx.setCursor?.("grab");
|
|
6851
|
+
return null;
|
|
6852
|
+
}
|
|
6788
6853
|
const templateResizeHit = this.hitTestTemplateResizeHandle(world, ctx);
|
|
6789
6854
|
if (templateResizeHit) {
|
|
6790
6855
|
ctx.setCursor?.("nwse-resize");
|
|
@@ -6872,6 +6937,7 @@ var SelectTool = class {
|
|
|
6872
6937
|
for (const id of this._selectedIds) {
|
|
6873
6938
|
const el = ctx.store.getById(id);
|
|
6874
6939
|
if (!el || !("size" in el)) continue;
|
|
6940
|
+
if (el.type === "shape" && el.shape === "line") continue;
|
|
6875
6941
|
const bounds = getElementBounds(el);
|
|
6876
6942
|
if (!bounds) continue;
|
|
6877
6943
|
const corners = this.getHandlePositions(bounds);
|
|
@@ -6883,6 +6949,20 @@ var SelectTool = class {
|
|
|
6883
6949
|
}
|
|
6884
6950
|
return null;
|
|
6885
6951
|
}
|
|
6952
|
+
hitTestLineHandles(world, ctx) {
|
|
6953
|
+
if (this._selectedIds.length === 0) return null;
|
|
6954
|
+
const zoom = ctx.camera.zoom;
|
|
6955
|
+
const r = (HANDLE_SIZE / 2 + HANDLE_HIT_PADDING2) / zoom;
|
|
6956
|
+
const r2 = r * r;
|
|
6957
|
+
for (const id of this._selectedIds) {
|
|
6958
|
+
const el = ctx.store.getById(id);
|
|
6959
|
+
if (!el || el.type !== "shape" || el.shape !== "line") continue;
|
|
6960
|
+
const [a, b] = lineEndpoints(el);
|
|
6961
|
+
if ((world.x - a.x) ** 2 + (world.y - a.y) ** 2 <= r2) return { elementId: id, fixed: b };
|
|
6962
|
+
if ((world.x - b.x) ** 2 + (world.y - b.y) ** 2 <= r2) return { elementId: id, fixed: a };
|
|
6963
|
+
}
|
|
6964
|
+
return null;
|
|
6965
|
+
}
|
|
6886
6966
|
getHandlePositions(bounds) {
|
|
6887
6967
|
return [
|
|
6888
6968
|
["nw", { x: bounds.x, y: bounds.y }],
|
|
@@ -6920,6 +7000,19 @@ var SelectTool = class {
|
|
|
6920
7000
|
this.renderBindingHighlights(canvasCtx, el, zoom);
|
|
6921
7001
|
continue;
|
|
6922
7002
|
}
|
|
7003
|
+
if (el.type === "shape" && el.shape === "line") {
|
|
7004
|
+
canvasCtx.setLineDash([]);
|
|
7005
|
+
canvasCtx.fillStyle = "#ffffff";
|
|
7006
|
+
const r = handleWorldSize / 2;
|
|
7007
|
+
for (const p of lineEndpoints(el)) {
|
|
7008
|
+
canvasCtx.beginPath();
|
|
7009
|
+
canvasCtx.arc(p.x, p.y, r, 0, Math.PI * 2);
|
|
7010
|
+
canvasCtx.fill();
|
|
7011
|
+
canvasCtx.stroke();
|
|
7012
|
+
}
|
|
7013
|
+
canvasCtx.setLineDash([4 / zoom, 4 / zoom]);
|
|
7014
|
+
continue;
|
|
7015
|
+
}
|
|
6923
7016
|
const bounds = getElementBounds(el);
|
|
6924
7017
|
if (!bounds) continue;
|
|
6925
7018
|
const pad = SELECTION_PAD / zoom;
|
|
@@ -7063,6 +7156,11 @@ var SelectTool = class {
|
|
|
7063
7156
|
}
|
|
7064
7157
|
isInsideBounds(point, el) {
|
|
7065
7158
|
if (el.type === "grid") return false;
|
|
7159
|
+
if (el.type === "shape" && el.shape === "line") {
|
|
7160
|
+
const [a, b] = lineEndpoints(el);
|
|
7161
|
+
const threshold = Math.max(el.strokeWidth / 2, 6);
|
|
7162
|
+
return distSqToSegment(point, a, b) <= threshold * threshold;
|
|
7163
|
+
}
|
|
7066
7164
|
if ("size" in el) {
|
|
7067
7165
|
const s = el.size;
|
|
7068
7166
|
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;
|
|
@@ -7376,6 +7474,15 @@ var ImageTool = class {
|
|
|
7376
7474
|
};
|
|
7377
7475
|
|
|
7378
7476
|
// src/tools/shape-tool.ts
|
|
7477
|
+
function snapTo45(start, end) {
|
|
7478
|
+
const dx = end.x - start.x;
|
|
7479
|
+
const dy = end.y - start.y;
|
|
7480
|
+
const len = Math.hypot(dx, dy);
|
|
7481
|
+
if (len === 0) return { ...end };
|
|
7482
|
+
const step = Math.PI / 4;
|
|
7483
|
+
const angle = Math.round(Math.atan2(dy, dx) / step) * step;
|
|
7484
|
+
return { x: start.x + Math.cos(angle) * len, y: start.y + Math.sin(angle) * len };
|
|
7485
|
+
}
|
|
7379
7486
|
var ShapeTool = class {
|
|
7380
7487
|
name = "shape";
|
|
7381
7488
|
drawing = false;
|
|
@@ -7433,13 +7540,17 @@ var ShapeTool = class {
|
|
|
7433
7540
|
onPointerMove(state, ctx) {
|
|
7434
7541
|
if (!this.drawing) return;
|
|
7435
7542
|
this.end = this.snap(ctx.camera.screenToWorld({ x: state.x, y: state.y }), ctx);
|
|
7543
|
+
if (this.shape === "line" && this.shiftHeld) {
|
|
7544
|
+
this.end = snapTo45(this.start, this.end);
|
|
7545
|
+
}
|
|
7436
7546
|
ctx.requestRender();
|
|
7437
7547
|
}
|
|
7438
7548
|
onPointerUp(_state, ctx) {
|
|
7439
7549
|
if (!this.drawing) return;
|
|
7440
7550
|
this.drawing = false;
|
|
7441
7551
|
const { position, size } = this.computeRect();
|
|
7442
|
-
|
|
7552
|
+
const isLine = this.shape === "line";
|
|
7553
|
+
if (isLine ? size.w === 0 && size.h === 0 : size.w === 0 || size.h === 0) return;
|
|
7443
7554
|
const shape = createShape({
|
|
7444
7555
|
position,
|
|
7445
7556
|
size,
|
|
@@ -7447,6 +7558,7 @@ var ShapeTool = class {
|
|
|
7447
7558
|
strokeColor: this.strokeColor,
|
|
7448
7559
|
strokeWidth: this.strokeWidth,
|
|
7449
7560
|
fillColor: this.fillColor,
|
|
7561
|
+
...isLine ? { flip: this.end.x > this.start.x !== this.end.y > this.start.y } : {},
|
|
7450
7562
|
layerId: ctx.activeLayerId ?? ""
|
|
7451
7563
|
});
|
|
7452
7564
|
ctx.store.add(shape);
|
|
@@ -7480,6 +7592,13 @@ var ShapeTool = class {
|
|
|
7480
7592
|
ctx.stroke();
|
|
7481
7593
|
break;
|
|
7482
7594
|
}
|
|
7595
|
+
case "line":
|
|
7596
|
+
ctx.lineCap = "round";
|
|
7597
|
+
ctx.beginPath();
|
|
7598
|
+
ctx.moveTo(this.start.x, this.start.y);
|
|
7599
|
+
ctx.lineTo(this.end.x, this.end.y);
|
|
7600
|
+
ctx.stroke();
|
|
7601
|
+
break;
|
|
7483
7602
|
}
|
|
7484
7603
|
ctx.restore();
|
|
7485
7604
|
}
|
|
@@ -7488,7 +7607,7 @@ var ShapeTool = class {
|
|
|
7488
7607
|
let y = Math.min(this.start.y, this.end.y);
|
|
7489
7608
|
let w = Math.abs(this.end.x - this.start.x);
|
|
7490
7609
|
let h = Math.abs(this.end.y - this.start.y);
|
|
7491
|
-
if (this.shiftHeld) {
|
|
7610
|
+
if (this.shiftHeld && this.shape !== "line") {
|
|
7492
7611
|
const side = Math.max(w, h);
|
|
7493
7612
|
w = side;
|
|
7494
7613
|
h = side;
|
|
@@ -7904,7 +8023,7 @@ var TemplateTool = class {
|
|
|
7904
8023
|
};
|
|
7905
8024
|
|
|
7906
8025
|
// src/index.ts
|
|
7907
|
-
var VERSION = "0.
|
|
8026
|
+
var VERSION = "0.31.0";
|
|
7908
8027
|
export {
|
|
7909
8028
|
ArrowTool,
|
|
7910
8029
|
AutoSave,
|