@fieldnotes/core 0.2.0 → 0.2.2
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 +332 -273
- package/dist/index.cjs +193 -48
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +22 -6
- package/dist/index.d.ts +22 -6
- package/dist/index.js +193 -48
- package/dist/index.js.map +1 -1
- package/package.json +31 -31
package/dist/index.d.cts
CHANGED
|
@@ -13,6 +13,11 @@ interface Point {
|
|
|
13
13
|
x: number;
|
|
14
14
|
y: number;
|
|
15
15
|
}
|
|
16
|
+
interface StrokePoint {
|
|
17
|
+
x: number;
|
|
18
|
+
y: number;
|
|
19
|
+
pressure: number;
|
|
20
|
+
}
|
|
16
21
|
interface Size {
|
|
17
22
|
w: number;
|
|
18
23
|
h: number;
|
|
@@ -28,7 +33,7 @@ interface BaseElement {
|
|
|
28
33
|
}
|
|
29
34
|
interface StrokeElement extends BaseElement {
|
|
30
35
|
type: 'stroke';
|
|
31
|
-
points:
|
|
36
|
+
points: StrokePoint[];
|
|
32
37
|
color: string;
|
|
33
38
|
width: number;
|
|
34
39
|
opacity: number;
|
|
@@ -200,6 +205,7 @@ declare class ToolManager {
|
|
|
200
205
|
get activeTool(): Tool | null;
|
|
201
206
|
get toolNames(): string[];
|
|
202
207
|
register(tool: Tool): void;
|
|
208
|
+
getTool<T extends Tool = Tool>(name: string): T | undefined;
|
|
203
209
|
setTool(name: string, ctx: ToolContext): void;
|
|
204
210
|
handlePointerDown(state: PointerState, ctx: ToolContext): void;
|
|
205
211
|
handlePointerMove(state: PointerState, ctx: ToolContext): void;
|
|
@@ -280,7 +286,6 @@ declare class InputHandler {
|
|
|
280
286
|
destroy(): void;
|
|
281
287
|
private bind;
|
|
282
288
|
private onWheel;
|
|
283
|
-
private isInteractiveHtmlContent;
|
|
284
289
|
private onPointerDown;
|
|
285
290
|
private onPointerMove;
|
|
286
291
|
private onPointerUp;
|
|
@@ -328,6 +333,7 @@ declare class Viewport {
|
|
|
328
333
|
private needsRender;
|
|
329
334
|
private domNodes;
|
|
330
335
|
private htmlContent;
|
|
336
|
+
private interactingElementId;
|
|
331
337
|
constructor(container: HTMLElement, options?: ViewportOptions);
|
|
332
338
|
get ctx(): CanvasRenderingContext2D | null;
|
|
333
339
|
requestRender(): void;
|
|
@@ -343,7 +349,7 @@ declare class Viewport {
|
|
|
343
349
|
}, size?: {
|
|
344
350
|
w: number;
|
|
345
351
|
h: number;
|
|
346
|
-
}):
|
|
352
|
+
}): string;
|
|
347
353
|
addHtmlElement(dom: HTMLElement, position: {
|
|
348
354
|
x: number;
|
|
349
355
|
y: number;
|
|
@@ -356,6 +362,12 @@ declare class Viewport {
|
|
|
356
362
|
private render;
|
|
357
363
|
private startEditingNote;
|
|
358
364
|
private onDblClick;
|
|
365
|
+
private hitTestWorld;
|
|
366
|
+
private startInteracting;
|
|
367
|
+
stopInteracting(): void;
|
|
368
|
+
private onInteractNodePointerDown;
|
|
369
|
+
private onInteractKeyDown;
|
|
370
|
+
private onInteractPointerDown;
|
|
359
371
|
private onDragOver;
|
|
360
372
|
private onDrop;
|
|
361
373
|
private syncDomNode;
|
|
@@ -401,7 +413,7 @@ interface BaseDefaults {
|
|
|
401
413
|
locked?: boolean;
|
|
402
414
|
}
|
|
403
415
|
interface StrokeInput extends BaseDefaults {
|
|
404
|
-
points:
|
|
416
|
+
points: StrokePoint[];
|
|
405
417
|
color?: string;
|
|
406
418
|
width?: number;
|
|
407
419
|
opacity?: number;
|
|
@@ -488,6 +500,7 @@ declare class HandTool implements Tool {
|
|
|
488
500
|
interface PencilToolOptions {
|
|
489
501
|
color?: string;
|
|
490
502
|
width?: number;
|
|
503
|
+
smoothing?: number;
|
|
491
504
|
}
|
|
492
505
|
declare class PencilTool implements Tool {
|
|
493
506
|
readonly name = "pencil";
|
|
@@ -495,6 +508,7 @@ declare class PencilTool implements Tool {
|
|
|
495
508
|
private points;
|
|
496
509
|
private color;
|
|
497
510
|
private width;
|
|
511
|
+
private smoothing;
|
|
498
512
|
constructor(options?: PencilToolOptions);
|
|
499
513
|
onActivate(ctx: ToolContext): void;
|
|
500
514
|
onDeactivate(ctx: ToolContext): void;
|
|
@@ -565,6 +579,7 @@ declare class ArrowTool implements Tool {
|
|
|
565
579
|
private color;
|
|
566
580
|
private width;
|
|
567
581
|
constructor(options?: ArrowToolOptions);
|
|
582
|
+
setOptions(options: ArrowToolOptions): void;
|
|
568
583
|
onPointerDown(state: PointerState, ctx: ToolContext): void;
|
|
569
584
|
onPointerMove(state: PointerState, ctx: ToolContext): void;
|
|
570
585
|
onPointerUp(_state: PointerState, ctx: ToolContext): void;
|
|
@@ -580,6 +595,7 @@ declare class NoteTool implements Tool {
|
|
|
580
595
|
private backgroundColor;
|
|
581
596
|
private size;
|
|
582
597
|
constructor(options?: NoteToolOptions);
|
|
598
|
+
setOptions(options: NoteToolOptions): void;
|
|
583
599
|
onPointerDown(_state: PointerState, _ctx: ToolContext): void;
|
|
584
600
|
onPointerMove(_state: PointerState, _ctx: ToolContext): void;
|
|
585
601
|
onPointerUp(state: PointerState, ctx: ToolContext): void;
|
|
@@ -599,6 +615,6 @@ declare class ImageTool implements Tool {
|
|
|
599
615
|
onPointerUp(state: PointerState, ctx: ToolContext): void;
|
|
600
616
|
}
|
|
601
617
|
|
|
602
|
-
declare const VERSION = "0.
|
|
618
|
+
declare const VERSION = "0.2.2";
|
|
603
619
|
|
|
604
|
-
export { AddElementCommand, type ArrowElement, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, Background, type BackgroundOptions, type BackgroundPattern, BatchCommand, type Bounds, Camera, type CameraOptions, type CanvasElement, type CanvasState, type Command, ElementRenderer, ElementStore, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, EventBus, HandTool, HistoryRecorder, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, InputHandler, NoteEditor, type NoteElement, NoteTool, type NoteToolOptions, PencilTool, type PencilToolOptions, type Point, type PointerState, RemoveElementCommand, SelectTool, type Size, type StrokeElement, type Tool, type ToolContext, ToolManager, type ToolName, UpdateElementCommand, VERSION, Viewport, type ViewportOptions, createArrow, createHtmlElement, createId, createImage, createNote, createStroke, exportState, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, isNearBezier, parseState };
|
|
620
|
+
export { AddElementCommand, type ArrowElement, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, Background, type BackgroundOptions, type BackgroundPattern, BatchCommand, type Bounds, Camera, type CameraOptions, type CanvasElement, type CanvasState, type Command, ElementRenderer, ElementStore, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, EventBus, HandTool, HistoryRecorder, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, InputHandler, NoteEditor, type NoteElement, NoteTool, type NoteToolOptions, PencilTool, type PencilToolOptions, type Point, type PointerState, RemoveElementCommand, SelectTool, type Size, type StrokeElement, type StrokePoint, type Tool, type ToolContext, ToolManager, type ToolName, UpdateElementCommand, VERSION, Viewport, type ViewportOptions, createArrow, createHtmlElement, createId, createImage, createNote, createStroke, exportState, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, isNearBezier, parseState };
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,11 @@ interface Point {
|
|
|
13
13
|
x: number;
|
|
14
14
|
y: number;
|
|
15
15
|
}
|
|
16
|
+
interface StrokePoint {
|
|
17
|
+
x: number;
|
|
18
|
+
y: number;
|
|
19
|
+
pressure: number;
|
|
20
|
+
}
|
|
16
21
|
interface Size {
|
|
17
22
|
w: number;
|
|
18
23
|
h: number;
|
|
@@ -28,7 +33,7 @@ interface BaseElement {
|
|
|
28
33
|
}
|
|
29
34
|
interface StrokeElement extends BaseElement {
|
|
30
35
|
type: 'stroke';
|
|
31
|
-
points:
|
|
36
|
+
points: StrokePoint[];
|
|
32
37
|
color: string;
|
|
33
38
|
width: number;
|
|
34
39
|
opacity: number;
|
|
@@ -200,6 +205,7 @@ declare class ToolManager {
|
|
|
200
205
|
get activeTool(): Tool | null;
|
|
201
206
|
get toolNames(): string[];
|
|
202
207
|
register(tool: Tool): void;
|
|
208
|
+
getTool<T extends Tool = Tool>(name: string): T | undefined;
|
|
203
209
|
setTool(name: string, ctx: ToolContext): void;
|
|
204
210
|
handlePointerDown(state: PointerState, ctx: ToolContext): void;
|
|
205
211
|
handlePointerMove(state: PointerState, ctx: ToolContext): void;
|
|
@@ -280,7 +286,6 @@ declare class InputHandler {
|
|
|
280
286
|
destroy(): void;
|
|
281
287
|
private bind;
|
|
282
288
|
private onWheel;
|
|
283
|
-
private isInteractiveHtmlContent;
|
|
284
289
|
private onPointerDown;
|
|
285
290
|
private onPointerMove;
|
|
286
291
|
private onPointerUp;
|
|
@@ -328,6 +333,7 @@ declare class Viewport {
|
|
|
328
333
|
private needsRender;
|
|
329
334
|
private domNodes;
|
|
330
335
|
private htmlContent;
|
|
336
|
+
private interactingElementId;
|
|
331
337
|
constructor(container: HTMLElement, options?: ViewportOptions);
|
|
332
338
|
get ctx(): CanvasRenderingContext2D | null;
|
|
333
339
|
requestRender(): void;
|
|
@@ -343,7 +349,7 @@ declare class Viewport {
|
|
|
343
349
|
}, size?: {
|
|
344
350
|
w: number;
|
|
345
351
|
h: number;
|
|
346
|
-
}):
|
|
352
|
+
}): string;
|
|
347
353
|
addHtmlElement(dom: HTMLElement, position: {
|
|
348
354
|
x: number;
|
|
349
355
|
y: number;
|
|
@@ -356,6 +362,12 @@ declare class Viewport {
|
|
|
356
362
|
private render;
|
|
357
363
|
private startEditingNote;
|
|
358
364
|
private onDblClick;
|
|
365
|
+
private hitTestWorld;
|
|
366
|
+
private startInteracting;
|
|
367
|
+
stopInteracting(): void;
|
|
368
|
+
private onInteractNodePointerDown;
|
|
369
|
+
private onInteractKeyDown;
|
|
370
|
+
private onInteractPointerDown;
|
|
359
371
|
private onDragOver;
|
|
360
372
|
private onDrop;
|
|
361
373
|
private syncDomNode;
|
|
@@ -401,7 +413,7 @@ interface BaseDefaults {
|
|
|
401
413
|
locked?: boolean;
|
|
402
414
|
}
|
|
403
415
|
interface StrokeInput extends BaseDefaults {
|
|
404
|
-
points:
|
|
416
|
+
points: StrokePoint[];
|
|
405
417
|
color?: string;
|
|
406
418
|
width?: number;
|
|
407
419
|
opacity?: number;
|
|
@@ -488,6 +500,7 @@ declare class HandTool implements Tool {
|
|
|
488
500
|
interface PencilToolOptions {
|
|
489
501
|
color?: string;
|
|
490
502
|
width?: number;
|
|
503
|
+
smoothing?: number;
|
|
491
504
|
}
|
|
492
505
|
declare class PencilTool implements Tool {
|
|
493
506
|
readonly name = "pencil";
|
|
@@ -495,6 +508,7 @@ declare class PencilTool implements Tool {
|
|
|
495
508
|
private points;
|
|
496
509
|
private color;
|
|
497
510
|
private width;
|
|
511
|
+
private smoothing;
|
|
498
512
|
constructor(options?: PencilToolOptions);
|
|
499
513
|
onActivate(ctx: ToolContext): void;
|
|
500
514
|
onDeactivate(ctx: ToolContext): void;
|
|
@@ -565,6 +579,7 @@ declare class ArrowTool implements Tool {
|
|
|
565
579
|
private color;
|
|
566
580
|
private width;
|
|
567
581
|
constructor(options?: ArrowToolOptions);
|
|
582
|
+
setOptions(options: ArrowToolOptions): void;
|
|
568
583
|
onPointerDown(state: PointerState, ctx: ToolContext): void;
|
|
569
584
|
onPointerMove(state: PointerState, ctx: ToolContext): void;
|
|
570
585
|
onPointerUp(_state: PointerState, ctx: ToolContext): void;
|
|
@@ -580,6 +595,7 @@ declare class NoteTool implements Tool {
|
|
|
580
595
|
private backgroundColor;
|
|
581
596
|
private size;
|
|
582
597
|
constructor(options?: NoteToolOptions);
|
|
598
|
+
setOptions(options: NoteToolOptions): void;
|
|
583
599
|
onPointerDown(_state: PointerState, _ctx: ToolContext): void;
|
|
584
600
|
onPointerMove(_state: PointerState, _ctx: ToolContext): void;
|
|
585
601
|
onPointerUp(state: PointerState, ctx: ToolContext): void;
|
|
@@ -599,6 +615,6 @@ declare class ImageTool implements Tool {
|
|
|
599
615
|
onPointerUp(state: PointerState, ctx: ToolContext): void;
|
|
600
616
|
}
|
|
601
617
|
|
|
602
|
-
declare const VERSION = "0.
|
|
618
|
+
declare const VERSION = "0.2.2";
|
|
603
619
|
|
|
604
|
-
export { AddElementCommand, type ArrowElement, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, Background, type BackgroundOptions, type BackgroundPattern, BatchCommand, type Bounds, Camera, type CameraOptions, type CanvasElement, type CanvasState, type Command, ElementRenderer, ElementStore, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, EventBus, HandTool, HistoryRecorder, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, InputHandler, NoteEditor, type NoteElement, NoteTool, type NoteToolOptions, PencilTool, type PencilToolOptions, type Point, type PointerState, RemoveElementCommand, SelectTool, type Size, type StrokeElement, type Tool, type ToolContext, ToolManager, type ToolName, UpdateElementCommand, VERSION, Viewport, type ViewportOptions, createArrow, createHtmlElement, createId, createImage, createNote, createStroke, exportState, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, isNearBezier, parseState };
|
|
620
|
+
export { AddElementCommand, type ArrowElement, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, Background, type BackgroundOptions, type BackgroundPattern, BatchCommand, type Bounds, Camera, type CameraOptions, type CanvasElement, type CanvasState, type Command, ElementRenderer, ElementStore, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, EventBus, HandTool, HistoryRecorder, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, InputHandler, NoteEditor, type NoteElement, NoteTool, type NoteToolOptions, PencilTool, type PencilToolOptions, type Point, type PointerState, RemoveElementCommand, SelectTool, type Size, type StrokeElement, type StrokePoint, type Tool, type ToolContext, ToolManager, type ToolName, UpdateElementCommand, VERSION, Viewport, type ViewportOptions, createArrow, createHtmlElement, createId, createImage, createNote, createStroke, exportState, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, isNearBezier, parseState };
|
package/dist/index.js
CHANGED
|
@@ -89,6 +89,13 @@ function migrateElement(obj) {
|
|
|
89
89
|
if (obj["type"] === "arrow" && typeof obj["bend"] !== "number") {
|
|
90
90
|
obj["bend"] = 0;
|
|
91
91
|
}
|
|
92
|
+
if (obj["type"] === "stroke" && Array.isArray(obj["points"])) {
|
|
93
|
+
for (const pt of obj["points"]) {
|
|
94
|
+
if (typeof pt["pressure"] !== "number") {
|
|
95
|
+
pt["pressure"] = 0.5;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
92
99
|
}
|
|
93
100
|
|
|
94
101
|
// src/core/auto-save.ts
|
|
@@ -344,24 +351,8 @@ var InputHandler = class {
|
|
|
344
351
|
y: e.clientY - rect.top
|
|
345
352
|
});
|
|
346
353
|
};
|
|
347
|
-
isInteractiveHtmlContent(e) {
|
|
348
|
-
const target = e.target;
|
|
349
|
-
if (!target) return false;
|
|
350
|
-
const node = target.closest("[data-element-id]");
|
|
351
|
-
if (!node) return false;
|
|
352
|
-
const elementId = node.dataset["elementId"];
|
|
353
|
-
if (!elementId) return false;
|
|
354
|
-
const store = this.toolContext?.store;
|
|
355
|
-
if (!store) return false;
|
|
356
|
-
const element = store.getById(elementId);
|
|
357
|
-
if (!element || element.type !== "html") return false;
|
|
358
|
-
return true;
|
|
359
|
-
}
|
|
360
354
|
onPointerDown = (e) => {
|
|
361
355
|
this.activePointers.set(e.pointerId, { x: e.clientX, y: e.clientY });
|
|
362
|
-
if (this.isInteractiveHtmlContent(e)) {
|
|
363
|
-
return;
|
|
364
|
-
}
|
|
365
356
|
this.element.setPointerCapture?.(e.pointerId);
|
|
366
357
|
if (this.activePointers.size === 2) {
|
|
367
358
|
this.startPinch();
|
|
@@ -687,6 +678,79 @@ function isNearLine(point, a, b, threshold) {
|
|
|
687
678
|
return Math.hypot(point.x - projX, point.y - projY) <= threshold;
|
|
688
679
|
}
|
|
689
680
|
|
|
681
|
+
// src/elements/stroke-smoothing.ts
|
|
682
|
+
var MIN_PRESSURE_SCALE = 0.2;
|
|
683
|
+
function pressureToWidth(pressure, baseWidth) {
|
|
684
|
+
return baseWidth * (MIN_PRESSURE_SCALE + (1 - MIN_PRESSURE_SCALE) * pressure);
|
|
685
|
+
}
|
|
686
|
+
function simplifyPoints(points, tolerance) {
|
|
687
|
+
if (points.length <= 2) return points.slice();
|
|
688
|
+
return rdp(points, 0, points.length - 1, tolerance);
|
|
689
|
+
}
|
|
690
|
+
function rdp(points, start, end, tolerance) {
|
|
691
|
+
const first = points[start];
|
|
692
|
+
const last = points[end];
|
|
693
|
+
if (!first || !last) return [];
|
|
694
|
+
if (end - start <= 1) return [first, last];
|
|
695
|
+
let maxDist = 0;
|
|
696
|
+
let maxIndex = start;
|
|
697
|
+
for (let i = start + 1; i < end; i++) {
|
|
698
|
+
const pt = points[i];
|
|
699
|
+
if (!pt) continue;
|
|
700
|
+
const dist = perpendicularDistance(pt, first, last);
|
|
701
|
+
if (dist > maxDist) {
|
|
702
|
+
maxDist = dist;
|
|
703
|
+
maxIndex = i;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
if (maxDist <= tolerance) return [first, last];
|
|
707
|
+
const left = rdp(points, start, maxIndex, tolerance);
|
|
708
|
+
const right = rdp(points, maxIndex, end, tolerance);
|
|
709
|
+
return left.concat(right.slice(1));
|
|
710
|
+
}
|
|
711
|
+
function perpendicularDistance(pt, lineStart, lineEnd) {
|
|
712
|
+
const dx = lineEnd.x - lineStart.x;
|
|
713
|
+
const dy = lineEnd.y - lineStart.y;
|
|
714
|
+
const lenSq = dx * dx + dy * dy;
|
|
715
|
+
if (lenSq === 0) {
|
|
716
|
+
const ex = pt.x - lineStart.x;
|
|
717
|
+
const ey = pt.y - lineStart.y;
|
|
718
|
+
return Math.sqrt(ex * ex + ey * ey);
|
|
719
|
+
}
|
|
720
|
+
const num = Math.abs(dy * pt.x - dx * pt.y + lineEnd.x * lineStart.y - lineEnd.y * lineStart.x);
|
|
721
|
+
return num / Math.sqrt(lenSq);
|
|
722
|
+
}
|
|
723
|
+
function smoothToSegments(points) {
|
|
724
|
+
if (points.length < 2) return [];
|
|
725
|
+
if (points.length === 2) {
|
|
726
|
+
const p0 = points[0];
|
|
727
|
+
const p1 = points[1];
|
|
728
|
+
if (!p0 || !p1) return [];
|
|
729
|
+
const mx = (p0.x + p1.x) / 2;
|
|
730
|
+
const my = (p0.y + p1.y) / 2;
|
|
731
|
+
return [{ start: p0, cp1: { x: mx, y: my }, cp2: { x: mx, y: my }, end: p1 }];
|
|
732
|
+
}
|
|
733
|
+
const segments = [];
|
|
734
|
+
const n = points.length;
|
|
735
|
+
for (let i = 0; i < n - 1; i++) {
|
|
736
|
+
const p0 = points[Math.max(0, i - 1)];
|
|
737
|
+
const p1 = points[i];
|
|
738
|
+
const p2 = points[i + 1];
|
|
739
|
+
const p3 = points[Math.min(n - 1, i + 2)];
|
|
740
|
+
if (!p0 || !p1 || !p2 || !p3) continue;
|
|
741
|
+
const cp1 = {
|
|
742
|
+
x: p1.x + (p2.x - p0.x) / 6,
|
|
743
|
+
y: p1.y + (p2.y - p0.y) / 6
|
|
744
|
+
};
|
|
745
|
+
const cp2 = {
|
|
746
|
+
x: p2.x - (p3.x - p1.x) / 6,
|
|
747
|
+
y: p2.y - (p3.y - p1.y) / 6
|
|
748
|
+
};
|
|
749
|
+
segments.push({ start: p1, cp1, cp2, end: p2 });
|
|
750
|
+
}
|
|
751
|
+
return segments;
|
|
752
|
+
}
|
|
753
|
+
|
|
690
754
|
// src/elements/element-renderer.ts
|
|
691
755
|
var DOM_ELEMENT_TYPES = /* @__PURE__ */ new Set(["note", "image", "html"]);
|
|
692
756
|
var ARROWHEAD_LENGTH = 12;
|
|
@@ -710,22 +774,18 @@ var ElementRenderer = class {
|
|
|
710
774
|
ctx.save();
|
|
711
775
|
ctx.translate(stroke.position.x, stroke.position.y);
|
|
712
776
|
ctx.strokeStyle = stroke.color;
|
|
713
|
-
ctx.lineWidth = stroke.width;
|
|
714
777
|
ctx.lineCap = "round";
|
|
715
778
|
ctx.lineJoin = "round";
|
|
716
779
|
ctx.globalAlpha = stroke.opacity;
|
|
717
|
-
|
|
718
|
-
const
|
|
719
|
-
|
|
720
|
-
ctx.
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
ctx.lineTo(pt.x, pt.y);
|
|
726
|
-
}
|
|
780
|
+
const segments = smoothToSegments(stroke.points);
|
|
781
|
+
for (const seg of segments) {
|
|
782
|
+
const w = (pressureToWidth(seg.start.pressure, stroke.width) + pressureToWidth(seg.end.pressure, stroke.width)) / 2;
|
|
783
|
+
ctx.lineWidth = w;
|
|
784
|
+
ctx.beginPath();
|
|
785
|
+
ctx.moveTo(seg.start.x, seg.start.y);
|
|
786
|
+
ctx.bezierCurveTo(seg.cp1.x, seg.cp1.y, seg.cp2.x, seg.cp2.y, seg.end.x, seg.end.y);
|
|
787
|
+
ctx.stroke();
|
|
727
788
|
}
|
|
728
|
-
ctx.stroke();
|
|
729
789
|
ctx.restore();
|
|
730
790
|
}
|
|
731
791
|
renderArrow(ctx, arrow) {
|
|
@@ -868,6 +928,9 @@ var ToolManager = class {
|
|
|
868
928
|
register(tool) {
|
|
869
929
|
this.tools.set(tool.name, tool);
|
|
870
930
|
}
|
|
931
|
+
getTool(name) {
|
|
932
|
+
return this.tools.get(name);
|
|
933
|
+
}
|
|
871
934
|
setTool(name, ctx) {
|
|
872
935
|
const tool = this.tools.get(name);
|
|
873
936
|
if (!tool) return;
|
|
@@ -1227,6 +1290,7 @@ var Viewport = class {
|
|
|
1227
1290
|
needsRender = true;
|
|
1228
1291
|
domNodes = /* @__PURE__ */ new Map();
|
|
1229
1292
|
htmlContent = /* @__PURE__ */ new Map();
|
|
1293
|
+
interactingElementId = null;
|
|
1230
1294
|
get ctx() {
|
|
1231
1295
|
return this.canvasEl.getContext("2d");
|
|
1232
1296
|
}
|
|
@@ -1272,6 +1336,7 @@ var Viewport = class {
|
|
|
1272
1336
|
this.store.add(image);
|
|
1273
1337
|
this.historyRecorder.commit();
|
|
1274
1338
|
this.requestRender();
|
|
1339
|
+
return image.id;
|
|
1275
1340
|
}
|
|
1276
1341
|
addHtmlElement(dom, position, size = { w: 200, h: 150 }) {
|
|
1277
1342
|
const el = createHtmlElement({ position, size });
|
|
@@ -1284,6 +1349,7 @@ var Viewport = class {
|
|
|
1284
1349
|
}
|
|
1285
1350
|
destroy() {
|
|
1286
1351
|
cancelAnimationFrame(this.animFrameId);
|
|
1352
|
+
this.stopInteracting();
|
|
1287
1353
|
this.noteEditor.destroy(this.store);
|
|
1288
1354
|
this.historyRecorder.destroy();
|
|
1289
1355
|
this.wrapper.removeEventListener("dblclick", this.onDblClick);
|
|
@@ -1341,11 +1407,74 @@ var Viewport = class {
|
|
|
1341
1407
|
}
|
|
1342
1408
|
onDblClick = (e) => {
|
|
1343
1409
|
const el = document.elementFromPoint(e.clientX, e.clientY);
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1410
|
+
const nodeEl = el?.closest("[data-element-id]");
|
|
1411
|
+
if (nodeEl) {
|
|
1412
|
+
const elementId = nodeEl.dataset["elementId"];
|
|
1413
|
+
if (elementId) {
|
|
1414
|
+
const element = this.store.getById(elementId);
|
|
1415
|
+
if (element?.type === "note") {
|
|
1416
|
+
this.startEditingNote(elementId);
|
|
1417
|
+
return;
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
const rect = this.wrapper.getBoundingClientRect();
|
|
1422
|
+
const screen = { x: e.clientX - rect.left, y: e.clientY - rect.top };
|
|
1423
|
+
const world = this.camera.screenToWorld(screen);
|
|
1424
|
+
const hit = this.hitTestWorld(world);
|
|
1425
|
+
if (hit?.type === "html") {
|
|
1426
|
+
this.startInteracting(hit.id);
|
|
1427
|
+
}
|
|
1428
|
+
};
|
|
1429
|
+
hitTestWorld(world) {
|
|
1430
|
+
const elements = this.store.getAll().reverse();
|
|
1431
|
+
for (const el of elements) {
|
|
1432
|
+
if (!("size" in el)) continue;
|
|
1433
|
+
const { x, y } = el.position;
|
|
1434
|
+
const { w, h } = el.size;
|
|
1435
|
+
if (world.x >= x && world.x <= x + w && world.y >= y && world.y <= y + h) {
|
|
1436
|
+
return el;
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
return null;
|
|
1440
|
+
}
|
|
1441
|
+
startInteracting(id) {
|
|
1442
|
+
this.stopInteracting();
|
|
1443
|
+
const node = this.domNodes.get(id);
|
|
1444
|
+
if (!node) return;
|
|
1445
|
+
this.interactingElementId = id;
|
|
1446
|
+
node.style.pointerEvents = "auto";
|
|
1447
|
+
node.addEventListener("pointerdown", this.onInteractNodePointerDown);
|
|
1448
|
+
window.addEventListener("keydown", this.onInteractKeyDown);
|
|
1449
|
+
window.addEventListener("pointerdown", this.onInteractPointerDown);
|
|
1450
|
+
}
|
|
1451
|
+
stopInteracting() {
|
|
1452
|
+
if (!this.interactingElementId) return;
|
|
1453
|
+
const node = this.domNodes.get(this.interactingElementId);
|
|
1454
|
+
if (node) {
|
|
1455
|
+
node.style.pointerEvents = "none";
|
|
1456
|
+
node.removeEventListener("pointerdown", this.onInteractNodePointerDown);
|
|
1457
|
+
}
|
|
1458
|
+
this.interactingElementId = null;
|
|
1459
|
+
window.removeEventListener("keydown", this.onInteractKeyDown);
|
|
1460
|
+
window.removeEventListener("pointerdown", this.onInteractPointerDown);
|
|
1461
|
+
}
|
|
1462
|
+
onInteractNodePointerDown = (e) => {
|
|
1463
|
+
e.stopPropagation();
|
|
1464
|
+
};
|
|
1465
|
+
onInteractKeyDown = (e) => {
|
|
1466
|
+
if (e.key === "Escape") {
|
|
1467
|
+
this.stopInteracting();
|
|
1468
|
+
}
|
|
1469
|
+
};
|
|
1470
|
+
onInteractPointerDown = (e) => {
|
|
1471
|
+
if (!this.interactingElementId) return;
|
|
1472
|
+
const target = e.target;
|
|
1473
|
+
if (!target) return;
|
|
1474
|
+
const node = this.domNodes.get(this.interactingElementId);
|
|
1475
|
+
if (node && !node.contains(target)) {
|
|
1476
|
+
this.stopInteracting();
|
|
1477
|
+
}
|
|
1349
1478
|
};
|
|
1350
1479
|
onDragOver = (e) => {
|
|
1351
1480
|
e.preventDefault();
|
|
@@ -1443,7 +1572,8 @@ var Viewport = class {
|
|
|
1443
1572
|
if (content) {
|
|
1444
1573
|
node.dataset["initialized"] = "true";
|
|
1445
1574
|
Object.assign(node.style, {
|
|
1446
|
-
overflow: "hidden"
|
|
1575
|
+
overflow: "hidden",
|
|
1576
|
+
pointerEvents: "none"
|
|
1447
1577
|
});
|
|
1448
1578
|
node.appendChild(content);
|
|
1449
1579
|
}
|
|
@@ -1546,15 +1676,19 @@ var HandTool = class {
|
|
|
1546
1676
|
|
|
1547
1677
|
// src/tools/pencil-tool.ts
|
|
1548
1678
|
var MIN_POINTS_FOR_STROKE = 2;
|
|
1679
|
+
var DEFAULT_SMOOTHING = 1.5;
|
|
1680
|
+
var DEFAULT_PRESSURE = 0.5;
|
|
1549
1681
|
var PencilTool = class {
|
|
1550
1682
|
name = "pencil";
|
|
1551
1683
|
drawing = false;
|
|
1552
1684
|
points = [];
|
|
1553
1685
|
color;
|
|
1554
1686
|
width;
|
|
1687
|
+
smoothing;
|
|
1555
1688
|
constructor(options = {}) {
|
|
1556
1689
|
this.color = options.color ?? "#000000";
|
|
1557
1690
|
this.width = options.width ?? 2;
|
|
1691
|
+
this.smoothing = options.smoothing ?? DEFAULT_SMOOTHING;
|
|
1558
1692
|
}
|
|
1559
1693
|
onActivate(ctx) {
|
|
1560
1694
|
ctx.setCursor?.("crosshair");
|
|
@@ -1565,16 +1699,19 @@ var PencilTool = class {
|
|
|
1565
1699
|
setOptions(options) {
|
|
1566
1700
|
if (options.color !== void 0) this.color = options.color;
|
|
1567
1701
|
if (options.width !== void 0) this.width = options.width;
|
|
1702
|
+
if (options.smoothing !== void 0) this.smoothing = options.smoothing;
|
|
1568
1703
|
}
|
|
1569
1704
|
onPointerDown(state, ctx) {
|
|
1570
1705
|
this.drawing = true;
|
|
1571
1706
|
const world = ctx.camera.screenToWorld({ x: state.x, y: state.y });
|
|
1572
|
-
|
|
1707
|
+
const pressure = state.pressure === 0 ? DEFAULT_PRESSURE : state.pressure;
|
|
1708
|
+
this.points = [{ x: world.x, y: world.y, pressure }];
|
|
1573
1709
|
}
|
|
1574
1710
|
onPointerMove(state, ctx) {
|
|
1575
1711
|
if (!this.drawing) return;
|
|
1576
1712
|
const world = ctx.camera.screenToWorld({ x: state.x, y: state.y });
|
|
1577
|
-
|
|
1713
|
+
const pressure = state.pressure === 0 ? DEFAULT_PRESSURE : state.pressure;
|
|
1714
|
+
this.points.push({ x: world.x, y: world.y, pressure });
|
|
1578
1715
|
ctx.requestRender();
|
|
1579
1716
|
}
|
|
1580
1717
|
onPointerUp(_state, ctx) {
|
|
@@ -1584,8 +1721,9 @@ var PencilTool = class {
|
|
|
1584
1721
|
this.points = [];
|
|
1585
1722
|
return;
|
|
1586
1723
|
}
|
|
1724
|
+
const simplified = simplifyPoints(this.points, this.smoothing);
|
|
1587
1725
|
const stroke = createStroke({
|
|
1588
|
-
points:
|
|
1726
|
+
points: simplified,
|
|
1589
1727
|
color: this.color,
|
|
1590
1728
|
width: this.width
|
|
1591
1729
|
});
|
|
@@ -1597,19 +1735,18 @@ var PencilTool = class {
|
|
|
1597
1735
|
if (!this.drawing || this.points.length < 2) return;
|
|
1598
1736
|
ctx.save();
|
|
1599
1737
|
ctx.strokeStyle = this.color;
|
|
1600
|
-
ctx.lineWidth = this.width;
|
|
1601
1738
|
ctx.lineCap = "round";
|
|
1602
1739
|
ctx.lineJoin = "round";
|
|
1603
1740
|
ctx.globalAlpha = 0.8;
|
|
1604
|
-
|
|
1605
|
-
const
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1741
|
+
const segments = smoothToSegments(this.points);
|
|
1742
|
+
for (const seg of segments) {
|
|
1743
|
+
const w = (pressureToWidth(seg.start.pressure, this.width) + pressureToWidth(seg.end.pressure, this.width)) / 2;
|
|
1744
|
+
ctx.lineWidth = w;
|
|
1745
|
+
ctx.beginPath();
|
|
1746
|
+
ctx.moveTo(seg.start.x, seg.start.y);
|
|
1747
|
+
ctx.bezierCurveTo(seg.cp1.x, seg.cp1.y, seg.cp2.x, seg.cp2.y, seg.end.x, seg.end.y);
|
|
1748
|
+
ctx.stroke();
|
|
1611
1749
|
}
|
|
1612
|
-
ctx.stroke();
|
|
1613
1750
|
ctx.restore();
|
|
1614
1751
|
}
|
|
1615
1752
|
};
|
|
@@ -1897,7 +2034,7 @@ var SelectTool = class {
|
|
|
1897
2034
|
handleResize(world, ctx) {
|
|
1898
2035
|
if (this.mode.type !== "resizing") return;
|
|
1899
2036
|
const el = ctx.store.getById(this.mode.elementId);
|
|
1900
|
-
if (!el || !("size" in el)) return;
|
|
2037
|
+
if (!el || !("size" in el) || el.locked) return;
|
|
1901
2038
|
const { handle } = this.mode;
|
|
1902
2039
|
const dx = world.x - this.lastWorld.x;
|
|
1903
2040
|
const dy = world.y - this.lastWorld.y;
|
|
@@ -2104,6 +2241,10 @@ var ArrowTool = class {
|
|
|
2104
2241
|
this.color = options.color ?? "#000000";
|
|
2105
2242
|
this.width = options.width ?? 2;
|
|
2106
2243
|
}
|
|
2244
|
+
setOptions(options) {
|
|
2245
|
+
if (options.color !== void 0) this.color = options.color;
|
|
2246
|
+
if (options.width !== void 0) this.width = options.width;
|
|
2247
|
+
}
|
|
2107
2248
|
onPointerDown(state, ctx) {
|
|
2108
2249
|
this.drawing = true;
|
|
2109
2250
|
this.start = ctx.camera.screenToWorld({ x: state.x, y: state.y });
|
|
@@ -2168,6 +2309,10 @@ var NoteTool = class {
|
|
|
2168
2309
|
this.backgroundColor = options.backgroundColor ?? "#ffeb3b";
|
|
2169
2310
|
this.size = options.size ?? { w: 200, h: 100 };
|
|
2170
2311
|
}
|
|
2312
|
+
setOptions(options) {
|
|
2313
|
+
if (options.backgroundColor !== void 0) this.backgroundColor = options.backgroundColor;
|
|
2314
|
+
if (options.size !== void 0) this.size = options.size;
|
|
2315
|
+
}
|
|
2171
2316
|
onPointerDown(_state, _ctx) {
|
|
2172
2317
|
}
|
|
2173
2318
|
onPointerMove(_state, _ctx) {
|
|
@@ -2217,7 +2362,7 @@ var ImageTool = class {
|
|
|
2217
2362
|
};
|
|
2218
2363
|
|
|
2219
2364
|
// src/index.ts
|
|
2220
|
-
var VERSION = "0.
|
|
2365
|
+
var VERSION = "0.2.2";
|
|
2221
2366
|
export {
|
|
2222
2367
|
AddElementCommand,
|
|
2223
2368
|
ArrowTool,
|