@fieldnotes/core 0.24.0 → 0.25.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 +502 -496
- package/dist/index.cjs +440 -547
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +38 -369
- package/dist/index.d.ts +38 -369
- package/dist/index.js +439 -515
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,179 +1,34 @@
|
|
|
1
|
-
// src/core/
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
existing.add(listener);
|
|
8
|
-
} else {
|
|
9
|
-
const set = /* @__PURE__ */ new Set([listener]);
|
|
10
|
-
this.listeners.set(event, set);
|
|
11
|
-
}
|
|
12
|
-
return () => this.off(event, listener);
|
|
13
|
-
}
|
|
14
|
-
off(event, listener) {
|
|
15
|
-
this.listeners.get(event)?.delete(listener);
|
|
16
|
-
}
|
|
17
|
-
emit(event, data) {
|
|
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
|
-
});
|
|
25
|
-
}
|
|
26
|
-
clear() {
|
|
27
|
-
this.listeners.clear();
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
// src/core/quadtree.ts
|
|
32
|
-
var MAX_ITEMS = 8;
|
|
33
|
-
var MAX_DEPTH = 8;
|
|
34
|
-
function intersects(a, b) {
|
|
35
|
-
return a.x <= b.x + b.w && a.x + a.w >= b.x && a.y <= b.y + b.h && a.y + a.h >= b.y;
|
|
1
|
+
// src/core/snap.ts
|
|
2
|
+
function snapPoint(point, gridSize) {
|
|
3
|
+
return {
|
|
4
|
+
x: Math.round(point.x / gridSize) * gridSize || 0,
|
|
5
|
+
y: Math.round(point.y / gridSize) * gridSize || 0
|
|
6
|
+
};
|
|
36
7
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
this.items.push(entry);
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
this.items.push(entry);
|
|
56
|
-
if (this.items.length > MAX_ITEMS && this.depth < MAX_DEPTH) {
|
|
57
|
-
this.split();
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
remove(id) {
|
|
61
|
-
const idx = this.items.findIndex((e) => e.id === id);
|
|
62
|
-
if (idx !== -1) {
|
|
63
|
-
this.items.splice(idx, 1);
|
|
64
|
-
return true;
|
|
65
|
-
}
|
|
66
|
-
if (this.children) {
|
|
67
|
-
for (const child of this.children) {
|
|
68
|
-
if (child.remove(id)) {
|
|
69
|
-
this.collapseIfEmpty();
|
|
70
|
-
return true;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
return false;
|
|
75
|
-
}
|
|
76
|
-
query(rect, result) {
|
|
77
|
-
if (!intersects(this.bounds, rect)) return;
|
|
78
|
-
for (const item of this.items) {
|
|
79
|
-
if (intersects(item.bounds, rect)) {
|
|
80
|
-
result.push(item.id);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
if (this.children) {
|
|
84
|
-
for (const child of this.children) {
|
|
85
|
-
child.query(rect, result);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
getChildIndex(itemBounds) {
|
|
90
|
-
const midX = this.bounds.x + this.bounds.w / 2;
|
|
91
|
-
const midY = this.bounds.y + this.bounds.h / 2;
|
|
92
|
-
const left = itemBounds.x >= this.bounds.x && itemBounds.x + itemBounds.w <= midX;
|
|
93
|
-
const right = itemBounds.x >= midX && itemBounds.x + itemBounds.w <= this.bounds.x + this.bounds.w;
|
|
94
|
-
const top = itemBounds.y >= this.bounds.y && itemBounds.y + itemBounds.h <= midY;
|
|
95
|
-
const bottom = itemBounds.y >= midY && itemBounds.y + itemBounds.h <= this.bounds.y + this.bounds.h;
|
|
96
|
-
if (left && top) return 0;
|
|
97
|
-
if (right && top) return 1;
|
|
98
|
-
if (left && bottom) return 2;
|
|
99
|
-
if (right && bottom) return 3;
|
|
100
|
-
return -1;
|
|
101
|
-
}
|
|
102
|
-
split() {
|
|
103
|
-
const { x, y, w, h } = this.bounds;
|
|
104
|
-
const halfW = w / 2;
|
|
105
|
-
const halfH = h / 2;
|
|
106
|
-
const d = this.depth + 1;
|
|
107
|
-
this.children = [
|
|
108
|
-
new _QuadNode({ x, y, w: halfW, h: halfH }, d),
|
|
109
|
-
new _QuadNode({ x: x + halfW, y, w: halfW, h: halfH }, d),
|
|
110
|
-
new _QuadNode({ x, y: y + halfH, w: halfW, h: halfH }, d),
|
|
111
|
-
new _QuadNode({ x: x + halfW, y: y + halfH, w: halfW, h: halfH }, d)
|
|
112
|
-
];
|
|
113
|
-
const remaining = [];
|
|
114
|
-
for (const item of this.items) {
|
|
115
|
-
const idx = this.getChildIndex(item.bounds);
|
|
116
|
-
if (idx !== -1) {
|
|
117
|
-
const target = this.children[idx];
|
|
118
|
-
if (target) target.insert(item);
|
|
119
|
-
} else {
|
|
120
|
-
remaining.push(item);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
this.items = remaining;
|
|
124
|
-
}
|
|
125
|
-
collapseIfEmpty() {
|
|
126
|
-
if (!this.children) return;
|
|
127
|
-
let totalItems = this.items.length;
|
|
128
|
-
for (const child of this.children) {
|
|
129
|
-
if (child.children) return;
|
|
130
|
-
totalItems += child.items.length;
|
|
131
|
-
}
|
|
132
|
-
if (totalItems <= MAX_ITEMS) {
|
|
133
|
-
for (const child of this.children) {
|
|
134
|
-
this.items.push(...child.items);
|
|
135
|
-
}
|
|
136
|
-
this.children = null;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
};
|
|
140
|
-
var Quadtree = class {
|
|
141
|
-
root;
|
|
142
|
-
_size = 0;
|
|
143
|
-
worldBounds;
|
|
144
|
-
constructor(worldBounds) {
|
|
145
|
-
this.worldBounds = worldBounds;
|
|
146
|
-
this.root = new QuadNode(worldBounds, 0);
|
|
147
|
-
}
|
|
148
|
-
get size() {
|
|
149
|
-
return this._size;
|
|
150
|
-
}
|
|
151
|
-
insert(id, bounds) {
|
|
152
|
-
this.root.insert({ id, bounds });
|
|
153
|
-
this._size++;
|
|
154
|
-
}
|
|
155
|
-
remove(id) {
|
|
156
|
-
if (this.root.remove(id)) {
|
|
157
|
-
this._size--;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
update(id, newBounds) {
|
|
161
|
-
this.remove(id);
|
|
162
|
-
this.insert(id, newBounds);
|
|
163
|
-
}
|
|
164
|
-
query(rect) {
|
|
165
|
-
const result = [];
|
|
166
|
-
this.root.query(rect, result);
|
|
167
|
-
return result;
|
|
168
|
-
}
|
|
169
|
-
queryPoint(point) {
|
|
170
|
-
return this.query({ x: point.x, y: point.y, w: 0, h: 0 });
|
|
8
|
+
function snapToHexCenter(point, cellSize, orientation) {
|
|
9
|
+
if (orientation === "pointy") {
|
|
10
|
+
const hexW = Math.sqrt(3) * cellSize;
|
|
11
|
+
const rowH = 1.5 * cellSize;
|
|
12
|
+
const row = Math.round(point.y / rowH);
|
|
13
|
+
const offsetX = row % 2 !== 0 ? hexW / 2 : 0;
|
|
14
|
+
const col = Math.round((point.x - offsetX) / hexW);
|
|
15
|
+
return { x: col * hexW + offsetX || 0, y: row * rowH || 0 };
|
|
16
|
+
} else {
|
|
17
|
+
const hexH = Math.sqrt(3) * cellSize;
|
|
18
|
+
const colW = 1.5 * cellSize;
|
|
19
|
+
const col = Math.round(point.x / colW);
|
|
20
|
+
const offsetY = col % 2 !== 0 ? hexH / 2 : 0;
|
|
21
|
+
const row = Math.round((point.y - offsetY) / hexH);
|
|
22
|
+
return { x: col * colW || 0, y: row * hexH + offsetY || 0 };
|
|
171
23
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
24
|
+
}
|
|
25
|
+
function smartSnap(point, ctx) {
|
|
26
|
+
if (!ctx.snapToGrid || !ctx.gridSize) return point;
|
|
27
|
+
if (ctx.gridType === "hex" && ctx.hexOrientation) {
|
|
28
|
+
return snapToHexCenter(point, ctx.gridSize, ctx.hexOrientation);
|
|
175
29
|
}
|
|
176
|
-
|
|
30
|
+
return snapPoint(point, ctx.gridSize);
|
|
31
|
+
}
|
|
177
32
|
|
|
178
33
|
// src/elements/note-sanitizer.ts
|
|
179
34
|
var BOLD_TAGS = /* @__PURE__ */ new Set(["b", "strong"]);
|
|
@@ -430,38 +285,6 @@ function migrateElement(obj) {
|
|
|
430
285
|
}
|
|
431
286
|
}
|
|
432
287
|
|
|
433
|
-
// src/core/snap.ts
|
|
434
|
-
function snapPoint(point, gridSize) {
|
|
435
|
-
return {
|
|
436
|
-
x: Math.round(point.x / gridSize) * gridSize || 0,
|
|
437
|
-
y: Math.round(point.y / gridSize) * gridSize || 0
|
|
438
|
-
};
|
|
439
|
-
}
|
|
440
|
-
function snapToHexCenter(point, cellSize, orientation) {
|
|
441
|
-
if (orientation === "pointy") {
|
|
442
|
-
const hexW = Math.sqrt(3) * cellSize;
|
|
443
|
-
const rowH = 1.5 * cellSize;
|
|
444
|
-
const row = Math.round(point.y / rowH);
|
|
445
|
-
const offsetX = row % 2 !== 0 ? hexW / 2 : 0;
|
|
446
|
-
const col = Math.round((point.x - offsetX) / hexW);
|
|
447
|
-
return { x: col * hexW + offsetX || 0, y: row * rowH || 0 };
|
|
448
|
-
} else {
|
|
449
|
-
const hexH = Math.sqrt(3) * cellSize;
|
|
450
|
-
const colW = 1.5 * cellSize;
|
|
451
|
-
const col = Math.round(point.x / colW);
|
|
452
|
-
const offsetY = col % 2 !== 0 ? hexH / 2 : 0;
|
|
453
|
-
const row = Math.round((point.y - offsetY) / hexH);
|
|
454
|
-
return { x: col * colW || 0, y: row * hexH + offsetY || 0 };
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
function smartSnap(point, ctx) {
|
|
458
|
-
if (!ctx.snapToGrid || !ctx.gridSize) return point;
|
|
459
|
-
if (ctx.gridType === "hex" && ctx.hexOrientation) {
|
|
460
|
-
return snapToHexCenter(point, ctx.gridSize, ctx.hexOrientation);
|
|
461
|
-
}
|
|
462
|
-
return snapPoint(point, ctx.gridSize);
|
|
463
|
-
}
|
|
464
|
-
|
|
465
288
|
// src/core/auto-save.ts
|
|
466
289
|
var DEFAULT_KEY = "fieldnotes-autosave";
|
|
467
290
|
var DEFAULT_DEBOUNCE_MS = 1e3;
|
|
@@ -625,143 +448,6 @@ var Camera = class {
|
|
|
625
448
|
}
|
|
626
449
|
};
|
|
627
450
|
|
|
628
|
-
// src/canvas/background.ts
|
|
629
|
-
var MIN_PATTERN_SPACING = 16;
|
|
630
|
-
var DEFAULTS = {
|
|
631
|
-
pattern: "dots",
|
|
632
|
-
spacing: 24,
|
|
633
|
-
color: "#d0d0d0",
|
|
634
|
-
dotRadius: 1,
|
|
635
|
-
lineWidth: 0.5
|
|
636
|
-
};
|
|
637
|
-
var Background = class {
|
|
638
|
-
pattern;
|
|
639
|
-
spacing;
|
|
640
|
-
color;
|
|
641
|
-
dotRadius;
|
|
642
|
-
lineWidth;
|
|
643
|
-
cachedCanvas = null;
|
|
644
|
-
cachedCtx = null;
|
|
645
|
-
lastZoom = -1;
|
|
646
|
-
lastOffsetX = -Infinity;
|
|
647
|
-
lastOffsetY = -Infinity;
|
|
648
|
-
lastWidth = 0;
|
|
649
|
-
lastHeight = 0;
|
|
650
|
-
constructor(options = {}) {
|
|
651
|
-
this.pattern = options.pattern ?? DEFAULTS.pattern;
|
|
652
|
-
this.spacing = options.spacing ?? DEFAULTS.spacing;
|
|
653
|
-
this.color = options.color ?? DEFAULTS.color;
|
|
654
|
-
this.dotRadius = options.dotRadius ?? DEFAULTS.dotRadius;
|
|
655
|
-
this.lineWidth = options.lineWidth ?? DEFAULTS.lineWidth;
|
|
656
|
-
}
|
|
657
|
-
render(ctx, camera) {
|
|
658
|
-
const { width, height } = ctx.canvas;
|
|
659
|
-
const dpr = typeof devicePixelRatio !== "undefined" ? devicePixelRatio : 1;
|
|
660
|
-
const cssWidth = width / dpr;
|
|
661
|
-
const cssHeight = height / dpr;
|
|
662
|
-
ctx.save();
|
|
663
|
-
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
664
|
-
ctx.clearRect(0, 0, cssWidth, cssHeight);
|
|
665
|
-
if (this.pattern === "none") {
|
|
666
|
-
ctx.restore();
|
|
667
|
-
return;
|
|
668
|
-
}
|
|
669
|
-
const spacing = this.adaptSpacing(this.spacing, camera.zoom);
|
|
670
|
-
const keyZoom = camera.zoom;
|
|
671
|
-
const keyX = Math.floor(camera.position.x % spacing);
|
|
672
|
-
const keyY = Math.floor(camera.position.y % spacing);
|
|
673
|
-
if (this.cachedCanvas !== null && keyZoom === this.lastZoom && keyX === this.lastOffsetX && keyY === this.lastOffsetY && cssWidth === this.lastWidth && cssHeight === this.lastHeight) {
|
|
674
|
-
ctx.drawImage(this.cachedCanvas, 0, 0);
|
|
675
|
-
ctx.restore();
|
|
676
|
-
return;
|
|
677
|
-
}
|
|
678
|
-
this.ensureCachedCanvas(cssWidth, cssHeight, dpr);
|
|
679
|
-
if (this.cachedCtx === null) {
|
|
680
|
-
if (this.pattern === "dots") {
|
|
681
|
-
this.renderDots(ctx, camera, cssWidth, cssHeight);
|
|
682
|
-
} else if (this.pattern === "grid") {
|
|
683
|
-
this.renderGrid(ctx, camera, cssWidth, cssHeight);
|
|
684
|
-
}
|
|
685
|
-
ctx.restore();
|
|
686
|
-
return;
|
|
687
|
-
}
|
|
688
|
-
const offCtx = this.cachedCtx;
|
|
689
|
-
offCtx.clearRect(0, 0, cssWidth, cssHeight);
|
|
690
|
-
if (this.pattern === "dots") {
|
|
691
|
-
this.renderDots(offCtx, camera, cssWidth, cssHeight);
|
|
692
|
-
} else if (this.pattern === "grid") {
|
|
693
|
-
this.renderGrid(offCtx, camera, cssWidth, cssHeight);
|
|
694
|
-
}
|
|
695
|
-
this.lastZoom = keyZoom;
|
|
696
|
-
this.lastOffsetX = keyX;
|
|
697
|
-
this.lastOffsetY = keyY;
|
|
698
|
-
this.lastWidth = cssWidth;
|
|
699
|
-
this.lastHeight = cssHeight;
|
|
700
|
-
ctx.drawImage(this.cachedCanvas, 0, 0);
|
|
701
|
-
ctx.restore();
|
|
702
|
-
}
|
|
703
|
-
ensureCachedCanvas(cssWidth, cssHeight, dpr) {
|
|
704
|
-
if (this.cachedCanvas !== null && this.lastWidth === cssWidth && this.lastHeight === cssHeight) {
|
|
705
|
-
return;
|
|
706
|
-
}
|
|
707
|
-
const physWidth = Math.round(cssWidth * dpr);
|
|
708
|
-
const physHeight = Math.round(cssHeight * dpr);
|
|
709
|
-
if (typeof OffscreenCanvas !== "undefined") {
|
|
710
|
-
this.cachedCanvas = new OffscreenCanvas(physWidth, physHeight);
|
|
711
|
-
} else if (typeof document !== "undefined") {
|
|
712
|
-
const el = document.createElement("canvas");
|
|
713
|
-
el.width = physWidth;
|
|
714
|
-
el.height = physHeight;
|
|
715
|
-
this.cachedCanvas = el;
|
|
716
|
-
} else {
|
|
717
|
-
this.cachedCanvas = null;
|
|
718
|
-
this.cachedCtx = null;
|
|
719
|
-
return;
|
|
720
|
-
}
|
|
721
|
-
const offCtx = this.cachedCanvas.getContext("2d");
|
|
722
|
-
if (offCtx !== null) {
|
|
723
|
-
offCtx.scale(dpr, dpr);
|
|
724
|
-
}
|
|
725
|
-
this.cachedCtx = offCtx;
|
|
726
|
-
this.lastZoom = -1;
|
|
727
|
-
}
|
|
728
|
-
adaptSpacing(baseSpacing, zoom) {
|
|
729
|
-
let spacing = baseSpacing * zoom;
|
|
730
|
-
while (spacing < MIN_PATTERN_SPACING) {
|
|
731
|
-
spacing *= 2;
|
|
732
|
-
}
|
|
733
|
-
return spacing;
|
|
734
|
-
}
|
|
735
|
-
renderDots(ctx, camera, width, height) {
|
|
736
|
-
const spacing = this.adaptSpacing(this.spacing, camera.zoom);
|
|
737
|
-
const offsetX = camera.position.x % spacing;
|
|
738
|
-
const offsetY = camera.position.y % spacing;
|
|
739
|
-
const radius = this.dotRadius * Math.min(camera.zoom, 2);
|
|
740
|
-
ctx.fillStyle = this.color;
|
|
741
|
-
ctx.beginPath();
|
|
742
|
-
for (let x = offsetX; x < width; x += spacing) {
|
|
743
|
-
for (let y = offsetY; y < height; y += spacing) {
|
|
744
|
-
ctx.moveTo(x + radius, y);
|
|
745
|
-
ctx.arc(x, y, radius, 0, Math.PI * 2);
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
ctx.fill();
|
|
749
|
-
}
|
|
750
|
-
renderGrid(ctx, camera, width, height) {
|
|
751
|
-
const spacing = this.adaptSpacing(this.spacing, camera.zoom);
|
|
752
|
-
const offsetX = camera.position.x % spacing;
|
|
753
|
-
const offsetY = camera.position.y % spacing;
|
|
754
|
-
const lineW = this.lineWidth * Math.min(camera.zoom, 2);
|
|
755
|
-
ctx.fillStyle = this.color;
|
|
756
|
-
for (let x = offsetX; x < width; x += spacing) {
|
|
757
|
-
ctx.fillRect(x, 0, lineW, height);
|
|
758
|
-
}
|
|
759
|
-
for (let y = offsetY; y < height; y += spacing) {
|
|
760
|
-
ctx.fillRect(0, y, width, lineW);
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
};
|
|
764
|
-
|
|
765
451
|
// src/canvas/input-filter.ts
|
|
766
452
|
var InputFilter = class _InputFilter {
|
|
767
453
|
activePenId = null;
|
|
@@ -1487,115 +1173,387 @@ var InputHandler = class {
|
|
|
1487
1173
|
const newZoom = this.camera.zoom * scale;
|
|
1488
1174
|
this.camera.zoomAt(newZoom, center);
|
|
1489
1175
|
}
|
|
1490
|
-
const dx = center.x - this.lastPointer.x;
|
|
1491
|
-
const dy = center.y - this.lastPointer.y;
|
|
1492
|
-
this.camera.pan(dx, dy);
|
|
1493
|
-
this.lastPinchDistance = dist;
|
|
1494
|
-
this.lastPinchCenter = center;
|
|
1495
|
-
this.lastPointer = { ...center };
|
|
1496
|
-
}
|
|
1497
|
-
getPinchPoints() {
|
|
1498
|
-
const pts = [...this.activePointers.values()];
|
|
1499
|
-
return [pts[0] ?? { x: 0, y: 0 }, pts[1] ?? { x: 0, y: 0 }];
|
|
1500
|
-
}
|
|
1501
|
-
distance(a, b) {
|
|
1502
|
-
const dx = a.x - b.x;
|
|
1503
|
-
const dy = a.y - b.y;
|
|
1504
|
-
return Math.sqrt(dx * dx + dy * dy);
|
|
1176
|
+
const dx = center.x - this.lastPointer.x;
|
|
1177
|
+
const dy = center.y - this.lastPointer.y;
|
|
1178
|
+
this.camera.pan(dx, dy);
|
|
1179
|
+
this.lastPinchDistance = dist;
|
|
1180
|
+
this.lastPinchCenter = center;
|
|
1181
|
+
this.lastPointer = { ...center };
|
|
1182
|
+
}
|
|
1183
|
+
getPinchPoints() {
|
|
1184
|
+
const pts = [...this.activePointers.values()];
|
|
1185
|
+
return [pts[0] ?? { x: 0, y: 0 }, pts[1] ?? { x: 0, y: 0 }];
|
|
1186
|
+
}
|
|
1187
|
+
distance(a, b) {
|
|
1188
|
+
const dx = a.x - b.x;
|
|
1189
|
+
const dy = a.y - b.y;
|
|
1190
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
1191
|
+
}
|
|
1192
|
+
midpoint(a, b) {
|
|
1193
|
+
return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
|
|
1194
|
+
}
|
|
1195
|
+
toPointerState(e) {
|
|
1196
|
+
const rect = this.element.getBoundingClientRect();
|
|
1197
|
+
return {
|
|
1198
|
+
x: e.clientX - rect.left,
|
|
1199
|
+
y: e.clientY - rect.top,
|
|
1200
|
+
pressure: e.pressure,
|
|
1201
|
+
pointerType: e.pointerType === "touch" || e.pointerType === "pen" ? e.pointerType : "mouse",
|
|
1202
|
+
shiftKey: e.shiftKey
|
|
1203
|
+
};
|
|
1204
|
+
}
|
|
1205
|
+
dispatchToolDown(e) {
|
|
1206
|
+
if (!this.toolManager || !this.toolContext) return;
|
|
1207
|
+
this.actions.flushPendingNudge();
|
|
1208
|
+
this.historyRecorder?.begin();
|
|
1209
|
+
this.isToolActive = true;
|
|
1210
|
+
this.toolManager.handlePointerDown(this.toPointerState(e), this.toolContext);
|
|
1211
|
+
}
|
|
1212
|
+
dispatchToolMove(e) {
|
|
1213
|
+
if (!this.toolManager || !this.toolContext) return;
|
|
1214
|
+
this.toolManager.handlePointerMove(this.toPointerState(e), this.toolContext);
|
|
1215
|
+
}
|
|
1216
|
+
dispatchToolHover(e) {
|
|
1217
|
+
if (!this.toolManager?.activeTool || !this.toolContext) return;
|
|
1218
|
+
const tool = this.toolManager.activeTool;
|
|
1219
|
+
if (tool.onHover) {
|
|
1220
|
+
tool.onHover(this.toPointerState(e), this.toolContext);
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
dispatchToolUp(e) {
|
|
1224
|
+
if (!this.toolManager || !this.toolContext) return;
|
|
1225
|
+
this.toolManager.handlePointerUp(this.toPointerState(e), this.toolContext);
|
|
1226
|
+
this.historyRecorder?.commit();
|
|
1227
|
+
}
|
|
1228
|
+
isInScope() {
|
|
1229
|
+
if (this.scope === "window") return true;
|
|
1230
|
+
const active = document.activeElement;
|
|
1231
|
+
return active === this.element || this.element.contains(active);
|
|
1232
|
+
}
|
|
1233
|
+
focusSelf() {
|
|
1234
|
+
if (this.scope !== "focus" || this.isInScope()) return;
|
|
1235
|
+
this.element.focus({ preventScroll: true });
|
|
1236
|
+
}
|
|
1237
|
+
cancelToolIfActive(e) {
|
|
1238
|
+
if (this.isToolActive) {
|
|
1239
|
+
this.dispatchToolUp(e);
|
|
1240
|
+
this.isToolActive = false;
|
|
1241
|
+
}
|
|
1242
|
+
this.deferredDown = null;
|
|
1243
|
+
}
|
|
1244
|
+
};
|
|
1245
|
+
|
|
1246
|
+
// src/canvas/background.ts
|
|
1247
|
+
var MIN_PATTERN_SPACING = 16;
|
|
1248
|
+
var DEFAULTS = {
|
|
1249
|
+
pattern: "dots",
|
|
1250
|
+
spacing: 24,
|
|
1251
|
+
color: "#d0d0d0",
|
|
1252
|
+
dotRadius: 1,
|
|
1253
|
+
lineWidth: 0.5
|
|
1254
|
+
};
|
|
1255
|
+
var Background = class {
|
|
1256
|
+
pattern;
|
|
1257
|
+
spacing;
|
|
1258
|
+
color;
|
|
1259
|
+
dotRadius;
|
|
1260
|
+
lineWidth;
|
|
1261
|
+
cachedCanvas = null;
|
|
1262
|
+
cachedCtx = null;
|
|
1263
|
+
lastZoom = -1;
|
|
1264
|
+
lastOffsetX = -Infinity;
|
|
1265
|
+
lastOffsetY = -Infinity;
|
|
1266
|
+
lastWidth = 0;
|
|
1267
|
+
lastHeight = 0;
|
|
1268
|
+
constructor(options = {}) {
|
|
1269
|
+
this.pattern = options.pattern ?? DEFAULTS.pattern;
|
|
1270
|
+
this.spacing = options.spacing ?? DEFAULTS.spacing;
|
|
1271
|
+
this.color = options.color ?? DEFAULTS.color;
|
|
1272
|
+
this.dotRadius = options.dotRadius ?? DEFAULTS.dotRadius;
|
|
1273
|
+
this.lineWidth = options.lineWidth ?? DEFAULTS.lineWidth;
|
|
1274
|
+
}
|
|
1275
|
+
render(ctx, camera) {
|
|
1276
|
+
const { width, height } = ctx.canvas;
|
|
1277
|
+
const dpr = typeof devicePixelRatio !== "undefined" ? devicePixelRatio : 1;
|
|
1278
|
+
const cssWidth = width / dpr;
|
|
1279
|
+
const cssHeight = height / dpr;
|
|
1280
|
+
ctx.save();
|
|
1281
|
+
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
1282
|
+
ctx.clearRect(0, 0, cssWidth, cssHeight);
|
|
1283
|
+
if (this.pattern === "none") {
|
|
1284
|
+
ctx.restore();
|
|
1285
|
+
return;
|
|
1286
|
+
}
|
|
1287
|
+
const spacing = this.adaptSpacing(this.spacing, camera.zoom);
|
|
1288
|
+
const keyZoom = camera.zoom;
|
|
1289
|
+
const keyX = Math.floor(camera.position.x % spacing);
|
|
1290
|
+
const keyY = Math.floor(camera.position.y % spacing);
|
|
1291
|
+
if (this.cachedCanvas !== null && keyZoom === this.lastZoom && keyX === this.lastOffsetX && keyY === this.lastOffsetY && cssWidth === this.lastWidth && cssHeight === this.lastHeight) {
|
|
1292
|
+
ctx.drawImage(this.cachedCanvas, 0, 0);
|
|
1293
|
+
ctx.restore();
|
|
1294
|
+
return;
|
|
1295
|
+
}
|
|
1296
|
+
this.ensureCachedCanvas(cssWidth, cssHeight, dpr);
|
|
1297
|
+
if (this.cachedCtx === null) {
|
|
1298
|
+
if (this.pattern === "dots") {
|
|
1299
|
+
this.renderDots(ctx, camera, cssWidth, cssHeight);
|
|
1300
|
+
} else if (this.pattern === "grid") {
|
|
1301
|
+
this.renderGrid(ctx, camera, cssWidth, cssHeight);
|
|
1302
|
+
}
|
|
1303
|
+
ctx.restore();
|
|
1304
|
+
return;
|
|
1305
|
+
}
|
|
1306
|
+
const offCtx = this.cachedCtx;
|
|
1307
|
+
offCtx.clearRect(0, 0, cssWidth, cssHeight);
|
|
1308
|
+
if (this.pattern === "dots") {
|
|
1309
|
+
this.renderDots(offCtx, camera, cssWidth, cssHeight);
|
|
1310
|
+
} else if (this.pattern === "grid") {
|
|
1311
|
+
this.renderGrid(offCtx, camera, cssWidth, cssHeight);
|
|
1312
|
+
}
|
|
1313
|
+
this.lastZoom = keyZoom;
|
|
1314
|
+
this.lastOffsetX = keyX;
|
|
1315
|
+
this.lastOffsetY = keyY;
|
|
1316
|
+
this.lastWidth = cssWidth;
|
|
1317
|
+
this.lastHeight = cssHeight;
|
|
1318
|
+
ctx.drawImage(this.cachedCanvas, 0, 0);
|
|
1319
|
+
ctx.restore();
|
|
1320
|
+
}
|
|
1321
|
+
ensureCachedCanvas(cssWidth, cssHeight, dpr) {
|
|
1322
|
+
if (this.cachedCanvas !== null && this.lastWidth === cssWidth && this.lastHeight === cssHeight) {
|
|
1323
|
+
return;
|
|
1324
|
+
}
|
|
1325
|
+
const physWidth = Math.round(cssWidth * dpr);
|
|
1326
|
+
const physHeight = Math.round(cssHeight * dpr);
|
|
1327
|
+
if (typeof OffscreenCanvas !== "undefined") {
|
|
1328
|
+
this.cachedCanvas = new OffscreenCanvas(physWidth, physHeight);
|
|
1329
|
+
} else if (typeof document !== "undefined") {
|
|
1330
|
+
const el = document.createElement("canvas");
|
|
1331
|
+
el.width = physWidth;
|
|
1332
|
+
el.height = physHeight;
|
|
1333
|
+
this.cachedCanvas = el;
|
|
1334
|
+
} else {
|
|
1335
|
+
this.cachedCanvas = null;
|
|
1336
|
+
this.cachedCtx = null;
|
|
1337
|
+
return;
|
|
1338
|
+
}
|
|
1339
|
+
const offCtx = this.cachedCanvas.getContext("2d");
|
|
1340
|
+
if (offCtx !== null) {
|
|
1341
|
+
offCtx.scale(dpr, dpr);
|
|
1342
|
+
}
|
|
1343
|
+
this.cachedCtx = offCtx;
|
|
1344
|
+
this.lastZoom = -1;
|
|
1345
|
+
}
|
|
1346
|
+
adaptSpacing(baseSpacing, zoom) {
|
|
1347
|
+
let spacing = baseSpacing * zoom;
|
|
1348
|
+
while (spacing < MIN_PATTERN_SPACING) {
|
|
1349
|
+
spacing *= 2;
|
|
1350
|
+
}
|
|
1351
|
+
return spacing;
|
|
1352
|
+
}
|
|
1353
|
+
renderDots(ctx, camera, width, height) {
|
|
1354
|
+
const spacing = this.adaptSpacing(this.spacing, camera.zoom);
|
|
1355
|
+
const offsetX = camera.position.x % spacing;
|
|
1356
|
+
const offsetY = camera.position.y % spacing;
|
|
1357
|
+
const radius = this.dotRadius * Math.min(camera.zoom, 2);
|
|
1358
|
+
ctx.fillStyle = this.color;
|
|
1359
|
+
ctx.beginPath();
|
|
1360
|
+
for (let x = offsetX; x < width; x += spacing) {
|
|
1361
|
+
for (let y = offsetY; y < height; y += spacing) {
|
|
1362
|
+
ctx.moveTo(x + radius, y);
|
|
1363
|
+
ctx.arc(x, y, radius, 0, Math.PI * 2);
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
ctx.fill();
|
|
1367
|
+
}
|
|
1368
|
+
renderGrid(ctx, camera, width, height) {
|
|
1369
|
+
const spacing = this.adaptSpacing(this.spacing, camera.zoom);
|
|
1370
|
+
const offsetX = camera.position.x % spacing;
|
|
1371
|
+
const offsetY = camera.position.y % spacing;
|
|
1372
|
+
const lineW = this.lineWidth * Math.min(camera.zoom, 2);
|
|
1373
|
+
ctx.fillStyle = this.color;
|
|
1374
|
+
for (let x = offsetX; x < width; x += spacing) {
|
|
1375
|
+
ctx.fillRect(x, 0, lineW, height);
|
|
1376
|
+
}
|
|
1377
|
+
for (let y = offsetY; y < height; y += spacing) {
|
|
1378
|
+
ctx.fillRect(0, y, width, lineW);
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
};
|
|
1382
|
+
|
|
1383
|
+
// src/core/event-bus.ts
|
|
1384
|
+
var EventBus = class {
|
|
1385
|
+
listeners = /* @__PURE__ */ new Map();
|
|
1386
|
+
on(event, listener) {
|
|
1387
|
+
const existing = this.listeners.get(event);
|
|
1388
|
+
if (existing) {
|
|
1389
|
+
existing.add(listener);
|
|
1390
|
+
} else {
|
|
1391
|
+
const set = /* @__PURE__ */ new Set([listener]);
|
|
1392
|
+
this.listeners.set(event, set);
|
|
1393
|
+
}
|
|
1394
|
+
return () => this.off(event, listener);
|
|
1395
|
+
}
|
|
1396
|
+
off(event, listener) {
|
|
1397
|
+
this.listeners.get(event)?.delete(listener);
|
|
1398
|
+
}
|
|
1399
|
+
emit(event, data) {
|
|
1400
|
+
this.listeners.get(event)?.forEach((listener) => {
|
|
1401
|
+
try {
|
|
1402
|
+
listener(data);
|
|
1403
|
+
} catch (err) {
|
|
1404
|
+
console.error(`[fieldnotes] listener error for "${String(event)}"`, err);
|
|
1405
|
+
}
|
|
1406
|
+
});
|
|
1407
|
+
}
|
|
1408
|
+
clear() {
|
|
1409
|
+
this.listeners.clear();
|
|
1410
|
+
}
|
|
1411
|
+
};
|
|
1412
|
+
|
|
1413
|
+
// src/core/quadtree.ts
|
|
1414
|
+
var MAX_ITEMS = 8;
|
|
1415
|
+
var MAX_DEPTH = 8;
|
|
1416
|
+
function intersects(a, b) {
|
|
1417
|
+
return a.x <= b.x + b.w && a.x + a.w >= b.x && a.y <= b.y + b.h && a.y + a.h >= b.y;
|
|
1418
|
+
}
|
|
1419
|
+
var QuadNode = class _QuadNode {
|
|
1420
|
+
constructor(bounds, depth) {
|
|
1421
|
+
this.bounds = bounds;
|
|
1422
|
+
this.depth = depth;
|
|
1423
|
+
}
|
|
1424
|
+
items = [];
|
|
1425
|
+
children = null;
|
|
1426
|
+
insert(entry) {
|
|
1427
|
+
if (this.children) {
|
|
1428
|
+
const idx = this.getChildIndex(entry.bounds);
|
|
1429
|
+
if (idx !== -1) {
|
|
1430
|
+
const child = this.children[idx];
|
|
1431
|
+
if (child) child.insert(entry);
|
|
1432
|
+
return;
|
|
1433
|
+
}
|
|
1434
|
+
this.items.push(entry);
|
|
1435
|
+
return;
|
|
1436
|
+
}
|
|
1437
|
+
this.items.push(entry);
|
|
1438
|
+
if (this.items.length > MAX_ITEMS && this.depth < MAX_DEPTH) {
|
|
1439
|
+
this.split();
|
|
1440
|
+
}
|
|
1505
1441
|
}
|
|
1506
|
-
|
|
1507
|
-
|
|
1442
|
+
remove(id) {
|
|
1443
|
+
const idx = this.items.findIndex((e) => e.id === id);
|
|
1444
|
+
if (idx !== -1) {
|
|
1445
|
+
this.items.splice(idx, 1);
|
|
1446
|
+
return true;
|
|
1447
|
+
}
|
|
1448
|
+
if (this.children) {
|
|
1449
|
+
for (const child of this.children) {
|
|
1450
|
+
if (child.remove(id)) {
|
|
1451
|
+
this.collapseIfEmpty();
|
|
1452
|
+
return true;
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
return false;
|
|
1508
1457
|
}
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1458
|
+
query(rect, result) {
|
|
1459
|
+
if (!intersects(this.bounds, rect)) return;
|
|
1460
|
+
for (const item of this.items) {
|
|
1461
|
+
if (intersects(item.bounds, rect)) {
|
|
1462
|
+
result.push(item.id);
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
if (this.children) {
|
|
1466
|
+
for (const child of this.children) {
|
|
1467
|
+
child.query(rect, result);
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1518
1470
|
}
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
this.
|
|
1522
|
-
this.
|
|
1523
|
-
this.
|
|
1524
|
-
this.
|
|
1471
|
+
getChildIndex(itemBounds) {
|
|
1472
|
+
const midX = this.bounds.x + this.bounds.w / 2;
|
|
1473
|
+
const midY = this.bounds.y + this.bounds.h / 2;
|
|
1474
|
+
const left = itemBounds.x >= this.bounds.x && itemBounds.x + itemBounds.w <= midX;
|
|
1475
|
+
const right = itemBounds.x >= midX && itemBounds.x + itemBounds.w <= this.bounds.x + this.bounds.w;
|
|
1476
|
+
const top = itemBounds.y >= this.bounds.y && itemBounds.y + itemBounds.h <= midY;
|
|
1477
|
+
const bottom = itemBounds.y >= midY && itemBounds.y + itemBounds.h <= this.bounds.y + this.bounds.h;
|
|
1478
|
+
if (left && top) return 0;
|
|
1479
|
+
if (right && top) return 1;
|
|
1480
|
+
if (left && bottom) return 2;
|
|
1481
|
+
if (right && bottom) return 3;
|
|
1482
|
+
return -1;
|
|
1525
1483
|
}
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1484
|
+
split() {
|
|
1485
|
+
const { x, y, w, h } = this.bounds;
|
|
1486
|
+
const halfW = w / 2;
|
|
1487
|
+
const halfH = h / 2;
|
|
1488
|
+
const d = this.depth + 1;
|
|
1489
|
+
this.children = [
|
|
1490
|
+
new _QuadNode({ x, y, w: halfW, h: halfH }, d),
|
|
1491
|
+
new _QuadNode({ x: x + halfW, y, w: halfW, h: halfH }, d),
|
|
1492
|
+
new _QuadNode({ x, y: y + halfH, w: halfW, h: halfH }, d),
|
|
1493
|
+
new _QuadNode({ x: x + halfW, y: y + halfH, w: halfW, h: halfH }, d)
|
|
1494
|
+
];
|
|
1495
|
+
const remaining = [];
|
|
1496
|
+
for (const item of this.items) {
|
|
1497
|
+
const idx = this.getChildIndex(item.bounds);
|
|
1498
|
+
if (idx !== -1) {
|
|
1499
|
+
const target = this.children[idx];
|
|
1500
|
+
if (target) target.insert(item);
|
|
1501
|
+
} else {
|
|
1502
|
+
remaining.push(item);
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
this.items = remaining;
|
|
1529
1506
|
}
|
|
1530
|
-
|
|
1531
|
-
if (!this.
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1507
|
+
collapseIfEmpty() {
|
|
1508
|
+
if (!this.children) return;
|
|
1509
|
+
let totalItems = this.items.length;
|
|
1510
|
+
for (const child of this.children) {
|
|
1511
|
+
if (child.children) return;
|
|
1512
|
+
totalItems += child.items.length;
|
|
1513
|
+
}
|
|
1514
|
+
if (totalItems <= MAX_ITEMS) {
|
|
1515
|
+
for (const child of this.children) {
|
|
1516
|
+
this.items.push(...child.items);
|
|
1517
|
+
}
|
|
1518
|
+
this.children = null;
|
|
1535
1519
|
}
|
|
1536
1520
|
}
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1521
|
+
};
|
|
1522
|
+
var Quadtree = class {
|
|
1523
|
+
root;
|
|
1524
|
+
_size = 0;
|
|
1525
|
+
worldBounds;
|
|
1526
|
+
constructor(worldBounds) {
|
|
1527
|
+
this.worldBounds = worldBounds;
|
|
1528
|
+
this.root = new QuadNode(worldBounds, 0);
|
|
1541
1529
|
}
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
const active = document.activeElement;
|
|
1545
|
-
return active === this.element || this.element.contains(active);
|
|
1530
|
+
get size() {
|
|
1531
|
+
return this._size;
|
|
1546
1532
|
}
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
this.
|
|
1533
|
+
insert(id, bounds) {
|
|
1534
|
+
this.root.insert({ id, bounds });
|
|
1535
|
+
this._size++;
|
|
1550
1536
|
}
|
|
1551
|
-
|
|
1552
|
-
if (this.
|
|
1553
|
-
this.
|
|
1554
|
-
this.isToolActive = false;
|
|
1537
|
+
remove(id) {
|
|
1538
|
+
if (this.root.remove(id)) {
|
|
1539
|
+
this._size--;
|
|
1555
1540
|
}
|
|
1556
|
-
this.deferredDown = null;
|
|
1557
1541
|
}
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
var DEFAULT_TIMEOUT = 300;
|
|
1562
|
-
var DEFAULT_MAX_DISTANCE = 20;
|
|
1563
|
-
var DoubleTapDetector = class {
|
|
1564
|
-
timeout;
|
|
1565
|
-
maxDistance;
|
|
1566
|
-
lastTapTime = 0;
|
|
1567
|
-
lastTapX = 0;
|
|
1568
|
-
lastTapY = 0;
|
|
1569
|
-
hasPendingTap = false;
|
|
1570
|
-
constructor(options) {
|
|
1571
|
-
this.timeout = options?.timeout ?? DEFAULT_TIMEOUT;
|
|
1572
|
-
this.maxDistance = options?.maxDistance ?? DEFAULT_MAX_DISTANCE;
|
|
1542
|
+
update(id, newBounds) {
|
|
1543
|
+
this.remove(id);
|
|
1544
|
+
this.insert(id, newBounds);
|
|
1573
1545
|
}
|
|
1574
|
-
|
|
1575
|
-
const
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
if (this.hasPendingTap) {
|
|
1579
|
-
const elapsed = now - this.lastTapTime;
|
|
1580
|
-
const dx = x - this.lastTapX;
|
|
1581
|
-
const dy = y - this.lastTapY;
|
|
1582
|
-
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
1583
|
-
if (elapsed <= this.timeout && dist <= this.maxDistance) {
|
|
1584
|
-
this.reset();
|
|
1585
|
-
return true;
|
|
1586
|
-
}
|
|
1587
|
-
}
|
|
1588
|
-
this.lastTapTime = now;
|
|
1589
|
-
this.lastTapX = x;
|
|
1590
|
-
this.lastTapY = y;
|
|
1591
|
-
this.hasPendingTap = true;
|
|
1592
|
-
return false;
|
|
1546
|
+
query(rect) {
|
|
1547
|
+
const result = [];
|
|
1548
|
+
this.root.query(rect, result);
|
|
1549
|
+
return result;
|
|
1593
1550
|
}
|
|
1594
|
-
|
|
1595
|
-
this.
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
this.
|
|
1551
|
+
queryPoint(point) {
|
|
1552
|
+
return this.query({ x: point.x, y: point.y, w: 0, h: 0 });
|
|
1553
|
+
}
|
|
1554
|
+
clear() {
|
|
1555
|
+
this.root = new QuadNode(this.worldBounds, 0);
|
|
1556
|
+
this._size = 0;
|
|
1599
1557
|
}
|
|
1600
1558
|
};
|
|
1601
1559
|
|
|
@@ -2244,51 +2202,6 @@ function updateBoundArrow(arrow, store) {
|
|
|
2244
2202
|
}
|
|
2245
2203
|
return Object.keys(updates).length > 0 ? updates : null;
|
|
2246
2204
|
}
|
|
2247
|
-
function clearStaleBindings(arrow, store) {
|
|
2248
|
-
const updates = {};
|
|
2249
|
-
let hasUpdates = false;
|
|
2250
|
-
if (arrow.fromBinding && !store.getById(arrow.fromBinding.elementId)) {
|
|
2251
|
-
updates.fromBinding = void 0;
|
|
2252
|
-
hasUpdates = true;
|
|
2253
|
-
}
|
|
2254
|
-
if (arrow.toBinding && !store.getById(arrow.toBinding.elementId)) {
|
|
2255
|
-
updates.toBinding = void 0;
|
|
2256
|
-
hasUpdates = true;
|
|
2257
|
-
}
|
|
2258
|
-
return hasUpdates ? updates : null;
|
|
2259
|
-
}
|
|
2260
|
-
function unbindArrow(arrow, store) {
|
|
2261
|
-
const updates = {};
|
|
2262
|
-
if (arrow.fromBinding) {
|
|
2263
|
-
const el = store.getById(arrow.fromBinding.elementId);
|
|
2264
|
-
const bounds = el ? getElementBounds(el) : null;
|
|
2265
|
-
if (bounds) {
|
|
2266
|
-
const angle = getArrowTangentAngle(arrow.from, arrow.to, arrow.bend, 0);
|
|
2267
|
-
const rayTarget = {
|
|
2268
|
-
x: arrow.from.x + Math.cos(angle) * 1e3,
|
|
2269
|
-
y: arrow.from.y + Math.sin(angle) * 1e3
|
|
2270
|
-
};
|
|
2271
|
-
const edge = getEdgeIntersection(bounds, rayTarget);
|
|
2272
|
-
updates.from = edge;
|
|
2273
|
-
updates.position = edge;
|
|
2274
|
-
}
|
|
2275
|
-
updates.fromBinding = void 0;
|
|
2276
|
-
}
|
|
2277
|
-
if (arrow.toBinding) {
|
|
2278
|
-
const el = store.getById(arrow.toBinding.elementId);
|
|
2279
|
-
const bounds = el ? getElementBounds(el) : null;
|
|
2280
|
-
if (bounds) {
|
|
2281
|
-
const angle = getArrowTangentAngle(arrow.from, arrow.to, arrow.bend, 1);
|
|
2282
|
-
const rayTarget = {
|
|
2283
|
-
x: arrow.to.x - Math.cos(angle) * 1e3,
|
|
2284
|
-
y: arrow.to.y - Math.sin(angle) * 1e3
|
|
2285
|
-
};
|
|
2286
|
-
updates.to = getEdgeIntersection(bounds, rayTarget);
|
|
2287
|
-
}
|
|
2288
|
-
updates.toBinding = void 0;
|
|
2289
|
-
}
|
|
2290
|
-
return updates;
|
|
2291
|
-
}
|
|
2292
2205
|
|
|
2293
2206
|
// src/elements/grid-renderer.ts
|
|
2294
2207
|
function getSquareGridLines(bounds, cellSize) {
|
|
@@ -4481,6 +4394,48 @@ var InteractMode = class {
|
|
|
4481
4394
|
};
|
|
4482
4395
|
};
|
|
4483
4396
|
|
|
4397
|
+
// src/canvas/double-tap-detector.ts
|
|
4398
|
+
var DEFAULT_TIMEOUT = 300;
|
|
4399
|
+
var DEFAULT_MAX_DISTANCE = 20;
|
|
4400
|
+
var DoubleTapDetector = class {
|
|
4401
|
+
timeout;
|
|
4402
|
+
maxDistance;
|
|
4403
|
+
lastTapTime = 0;
|
|
4404
|
+
lastTapX = 0;
|
|
4405
|
+
lastTapY = 0;
|
|
4406
|
+
hasPendingTap = false;
|
|
4407
|
+
constructor(options) {
|
|
4408
|
+
this.timeout = options?.timeout ?? DEFAULT_TIMEOUT;
|
|
4409
|
+
this.maxDistance = options?.maxDistance ?? DEFAULT_MAX_DISTANCE;
|
|
4410
|
+
}
|
|
4411
|
+
feed(e) {
|
|
4412
|
+
const now = Date.now();
|
|
4413
|
+
const x = e.clientX;
|
|
4414
|
+
const y = e.clientY;
|
|
4415
|
+
if (this.hasPendingTap) {
|
|
4416
|
+
const elapsed = now - this.lastTapTime;
|
|
4417
|
+
const dx = x - this.lastTapX;
|
|
4418
|
+
const dy = y - this.lastTapY;
|
|
4419
|
+
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
4420
|
+
if (elapsed <= this.timeout && dist <= this.maxDistance) {
|
|
4421
|
+
this.reset();
|
|
4422
|
+
return true;
|
|
4423
|
+
}
|
|
4424
|
+
}
|
|
4425
|
+
this.lastTapTime = now;
|
|
4426
|
+
this.lastTapX = x;
|
|
4427
|
+
this.lastTapY = y;
|
|
4428
|
+
this.hasPendingTap = true;
|
|
4429
|
+
return false;
|
|
4430
|
+
}
|
|
4431
|
+
reset() {
|
|
4432
|
+
this.hasPendingTap = false;
|
|
4433
|
+
this.lastTapTime = 0;
|
|
4434
|
+
this.lastTapX = 0;
|
|
4435
|
+
this.lastTapY = 0;
|
|
4436
|
+
}
|
|
4437
|
+
};
|
|
4438
|
+
|
|
4484
4439
|
// src/canvas/dom-node-manager.ts
|
|
4485
4440
|
var DomNodeManager = class {
|
|
4486
4441
|
domNodes = /* @__PURE__ */ new Map();
|
|
@@ -7482,52 +7437,32 @@ var TemplateTool = class {
|
|
|
7482
7437
|
};
|
|
7483
7438
|
|
|
7484
7439
|
// src/index.ts
|
|
7485
|
-
var VERSION = "0.
|
|
7440
|
+
var VERSION = "0.25.0";
|
|
7486
7441
|
export {
|
|
7487
|
-
AddElementCommand,
|
|
7488
7442
|
ArrowTool,
|
|
7489
7443
|
AutoSave,
|
|
7490
|
-
Background,
|
|
7491
|
-
BatchCommand,
|
|
7492
7444
|
Camera,
|
|
7493
|
-
CreateLayerCommand,
|
|
7494
|
-
DEFAULT_FONT_SIZE_PRESETS,
|
|
7495
7445
|
DEFAULT_NOTE_FONT_SIZE,
|
|
7496
|
-
DoubleTapDetector,
|
|
7497
|
-
ElementRenderer,
|
|
7498
7446
|
ElementStore,
|
|
7499
7447
|
EraserTool,
|
|
7500
|
-
EventBus,
|
|
7501
7448
|
HandTool,
|
|
7502
|
-
HistoryRecorder,
|
|
7503
7449
|
HistoryStack,
|
|
7504
7450
|
ImageTool,
|
|
7505
|
-
InputFilter,
|
|
7506
|
-
InputHandler,
|
|
7507
7451
|
LayerManager,
|
|
7508
7452
|
MeasureTool,
|
|
7509
|
-
NoteEditor,
|
|
7510
7453
|
NoteTool,
|
|
7511
|
-
NoteToolbar,
|
|
7512
7454
|
PencilTool,
|
|
7513
|
-
Quadtree,
|
|
7514
|
-
RemoveElementCommand,
|
|
7515
|
-
RemoveLayerCommand,
|
|
7516
7455
|
SelectTool,
|
|
7517
7456
|
ShapeTool,
|
|
7518
7457
|
TemplateTool,
|
|
7519
7458
|
TextTool,
|
|
7520
7459
|
ToolManager,
|
|
7521
|
-
UpdateElementCommand,
|
|
7522
|
-
UpdateLayerCommand,
|
|
7523
7460
|
VERSION,
|
|
7524
7461
|
Viewport,
|
|
7525
7462
|
boundsIntersect,
|
|
7526
|
-
clearStaleBindings,
|
|
7527
7463
|
createArrow,
|
|
7528
7464
|
createGrid,
|
|
7529
7465
|
createHtmlElement,
|
|
7530
|
-
createId,
|
|
7531
7466
|
createImage,
|
|
7532
7467
|
createNote,
|
|
7533
7468
|
createShape,
|
|
@@ -7536,29 +7471,20 @@ export {
|
|
|
7536
7471
|
createText,
|
|
7537
7472
|
drawHexPath,
|
|
7538
7473
|
exportImage,
|
|
7539
|
-
exportState,
|
|
7540
|
-
findBindTarget,
|
|
7541
|
-
findBoundArrows,
|
|
7542
7474
|
getActiveFormats,
|
|
7543
7475
|
getArrowBounds,
|
|
7544
7476
|
getArrowControlPoint,
|
|
7545
7477
|
getArrowMidpoint,
|
|
7546
7478
|
getArrowTangentAngle,
|
|
7547
7479
|
getBendFromPoint,
|
|
7548
|
-
getEdgeIntersection,
|
|
7549
7480
|
getElementBounds,
|
|
7550
|
-
getElementCenter,
|
|
7551
7481
|
getElementsBoundingBox,
|
|
7552
7482
|
getHexCellsInCone,
|
|
7553
7483
|
getHexCellsInLine,
|
|
7554
7484
|
getHexCellsInRadius,
|
|
7555
7485
|
getHexCellsInSquare,
|
|
7556
7486
|
getHexDistance,
|
|
7557
|
-
isBindable,
|
|
7558
7487
|
isNearBezier,
|
|
7559
|
-
isNoteContentEmpty,
|
|
7560
|
-
parseState,
|
|
7561
|
-
sanitizeNoteHtml,
|
|
7562
7488
|
setFontSize,
|
|
7563
7489
|
smartSnap,
|
|
7564
7490
|
snapPoint,
|
|
@@ -7566,8 +7492,6 @@ export {
|
|
|
7566
7492
|
toggleBold,
|
|
7567
7493
|
toggleItalic,
|
|
7568
7494
|
toggleStrikethrough,
|
|
7569
|
-
toggleUnderline
|
|
7570
|
-
unbindArrow,
|
|
7571
|
-
updateBoundArrow
|
|
7495
|
+
toggleUnderline
|
|
7572
7496
|
};
|
|
7573
7497
|
//# sourceMappingURL=index.js.map
|