@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.cjs
CHANGED
|
@@ -20,50 +20,30 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
-
AddElementCommand: () => AddElementCommand,
|
|
24
23
|
ArrowTool: () => ArrowTool,
|
|
25
24
|
AutoSave: () => AutoSave,
|
|
26
|
-
Background: () => Background,
|
|
27
|
-
BatchCommand: () => BatchCommand,
|
|
28
25
|
Camera: () => Camera,
|
|
29
|
-
CreateLayerCommand: () => CreateLayerCommand,
|
|
30
|
-
DEFAULT_FONT_SIZE_PRESETS: () => DEFAULT_FONT_SIZE_PRESETS,
|
|
31
26
|
DEFAULT_NOTE_FONT_SIZE: () => DEFAULT_NOTE_FONT_SIZE,
|
|
32
|
-
DoubleTapDetector: () => DoubleTapDetector,
|
|
33
|
-
ElementRenderer: () => ElementRenderer,
|
|
34
27
|
ElementStore: () => ElementStore,
|
|
35
28
|
EraserTool: () => EraserTool,
|
|
36
|
-
EventBus: () => EventBus,
|
|
37
29
|
HandTool: () => HandTool,
|
|
38
|
-
HistoryRecorder: () => HistoryRecorder,
|
|
39
30
|
HistoryStack: () => HistoryStack,
|
|
40
31
|
ImageTool: () => ImageTool,
|
|
41
|
-
InputFilter: () => InputFilter,
|
|
42
|
-
InputHandler: () => InputHandler,
|
|
43
32
|
LayerManager: () => LayerManager,
|
|
44
33
|
MeasureTool: () => MeasureTool,
|
|
45
|
-
NoteEditor: () => NoteEditor,
|
|
46
34
|
NoteTool: () => NoteTool,
|
|
47
|
-
NoteToolbar: () => NoteToolbar,
|
|
48
35
|
PencilTool: () => PencilTool,
|
|
49
|
-
Quadtree: () => Quadtree,
|
|
50
|
-
RemoveElementCommand: () => RemoveElementCommand,
|
|
51
|
-
RemoveLayerCommand: () => RemoveLayerCommand,
|
|
52
36
|
SelectTool: () => SelectTool,
|
|
53
37
|
ShapeTool: () => ShapeTool,
|
|
54
38
|
TemplateTool: () => TemplateTool,
|
|
55
39
|
TextTool: () => TextTool,
|
|
56
40
|
ToolManager: () => ToolManager,
|
|
57
|
-
UpdateElementCommand: () => UpdateElementCommand,
|
|
58
|
-
UpdateLayerCommand: () => UpdateLayerCommand,
|
|
59
41
|
VERSION: () => VERSION,
|
|
60
42
|
Viewport: () => Viewport,
|
|
61
43
|
boundsIntersect: () => boundsIntersect,
|
|
62
|
-
clearStaleBindings: () => clearStaleBindings,
|
|
63
44
|
createArrow: () => createArrow,
|
|
64
45
|
createGrid: () => createGrid,
|
|
65
46
|
createHtmlElement: () => createHtmlElement,
|
|
66
|
-
createId: () => createId,
|
|
67
47
|
createImage: () => createImage,
|
|
68
48
|
createNote: () => createNote,
|
|
69
49
|
createShape: () => createShape,
|
|
@@ -72,29 +52,20 @@ __export(index_exports, {
|
|
|
72
52
|
createText: () => createText,
|
|
73
53
|
drawHexPath: () => drawHexPath,
|
|
74
54
|
exportImage: () => exportImage,
|
|
75
|
-
exportState: () => exportState,
|
|
76
|
-
findBindTarget: () => findBindTarget,
|
|
77
|
-
findBoundArrows: () => findBoundArrows,
|
|
78
55
|
getActiveFormats: () => getActiveFormats,
|
|
79
56
|
getArrowBounds: () => getArrowBounds,
|
|
80
57
|
getArrowControlPoint: () => getArrowControlPoint,
|
|
81
58
|
getArrowMidpoint: () => getArrowMidpoint,
|
|
82
59
|
getArrowTangentAngle: () => getArrowTangentAngle,
|
|
83
60
|
getBendFromPoint: () => getBendFromPoint,
|
|
84
|
-
getEdgeIntersection: () => getEdgeIntersection,
|
|
85
61
|
getElementBounds: () => getElementBounds,
|
|
86
|
-
getElementCenter: () => getElementCenter,
|
|
87
62
|
getElementsBoundingBox: () => getElementsBoundingBox,
|
|
88
63
|
getHexCellsInCone: () => getHexCellsInCone,
|
|
89
64
|
getHexCellsInLine: () => getHexCellsInLine,
|
|
90
65
|
getHexCellsInRadius: () => getHexCellsInRadius,
|
|
91
66
|
getHexCellsInSquare: () => getHexCellsInSquare,
|
|
92
67
|
getHexDistance: () => getHexDistance,
|
|
93
|
-
isBindable: () => isBindable,
|
|
94
68
|
isNearBezier: () => isNearBezier,
|
|
95
|
-
isNoteContentEmpty: () => isNoteContentEmpty,
|
|
96
|
-
parseState: () => parseState,
|
|
97
|
-
sanitizeNoteHtml: () => sanitizeNoteHtml,
|
|
98
69
|
setFontSize: () => setFontSize,
|
|
99
70
|
smartSnap: () => smartSnap,
|
|
100
71
|
snapPoint: () => snapPoint,
|
|
@@ -102,188 +73,41 @@ __export(index_exports, {
|
|
|
102
73
|
toggleBold: () => toggleBold,
|
|
103
74
|
toggleItalic: () => toggleItalic,
|
|
104
75
|
toggleStrikethrough: () => toggleStrikethrough,
|
|
105
|
-
toggleUnderline: () => toggleUnderline
|
|
106
|
-
unbindArrow: () => unbindArrow,
|
|
107
|
-
updateBoundArrow: () => updateBoundArrow
|
|
76
|
+
toggleUnderline: () => toggleUnderline
|
|
108
77
|
});
|
|
109
78
|
module.exports = __toCommonJS(index_exports);
|
|
110
79
|
|
|
111
|
-
// src/core/
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
existing.add(listener);
|
|
118
|
-
} else {
|
|
119
|
-
const set = /* @__PURE__ */ new Set([listener]);
|
|
120
|
-
this.listeners.set(event, set);
|
|
121
|
-
}
|
|
122
|
-
return () => this.off(event, listener);
|
|
123
|
-
}
|
|
124
|
-
off(event, listener) {
|
|
125
|
-
this.listeners.get(event)?.delete(listener);
|
|
126
|
-
}
|
|
127
|
-
emit(event, data) {
|
|
128
|
-
this.listeners.get(event)?.forEach((listener) => {
|
|
129
|
-
try {
|
|
130
|
-
listener(data);
|
|
131
|
-
} catch (err) {
|
|
132
|
-
console.error(`[fieldnotes] listener error for "${String(event)}"`, err);
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
clear() {
|
|
137
|
-
this.listeners.clear();
|
|
138
|
-
}
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
// src/core/quadtree.ts
|
|
142
|
-
var MAX_ITEMS = 8;
|
|
143
|
-
var MAX_DEPTH = 8;
|
|
144
|
-
function intersects(a, b) {
|
|
145
|
-
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;
|
|
80
|
+
// src/core/snap.ts
|
|
81
|
+
function snapPoint(point, gridSize) {
|
|
82
|
+
return {
|
|
83
|
+
x: Math.round(point.x / gridSize) * gridSize || 0,
|
|
84
|
+
y: Math.round(point.y / gridSize) * gridSize || 0
|
|
85
|
+
};
|
|
146
86
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
this.items.push(entry);
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
this.items.push(entry);
|
|
166
|
-
if (this.items.length > MAX_ITEMS && this.depth < MAX_DEPTH) {
|
|
167
|
-
this.split();
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
remove(id) {
|
|
171
|
-
const idx = this.items.findIndex((e) => e.id === id);
|
|
172
|
-
if (idx !== -1) {
|
|
173
|
-
this.items.splice(idx, 1);
|
|
174
|
-
return true;
|
|
175
|
-
}
|
|
176
|
-
if (this.children) {
|
|
177
|
-
for (const child of this.children) {
|
|
178
|
-
if (child.remove(id)) {
|
|
179
|
-
this.collapseIfEmpty();
|
|
180
|
-
return true;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
return false;
|
|
185
|
-
}
|
|
186
|
-
query(rect, result) {
|
|
187
|
-
if (!intersects(this.bounds, rect)) return;
|
|
188
|
-
for (const item of this.items) {
|
|
189
|
-
if (intersects(item.bounds, rect)) {
|
|
190
|
-
result.push(item.id);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
if (this.children) {
|
|
194
|
-
for (const child of this.children) {
|
|
195
|
-
child.query(rect, result);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
getChildIndex(itemBounds) {
|
|
200
|
-
const midX = this.bounds.x + this.bounds.w / 2;
|
|
201
|
-
const midY = this.bounds.y + this.bounds.h / 2;
|
|
202
|
-
const left = itemBounds.x >= this.bounds.x && itemBounds.x + itemBounds.w <= midX;
|
|
203
|
-
const right = itemBounds.x >= midX && itemBounds.x + itemBounds.w <= this.bounds.x + this.bounds.w;
|
|
204
|
-
const top = itemBounds.y >= this.bounds.y && itemBounds.y + itemBounds.h <= midY;
|
|
205
|
-
const bottom = itemBounds.y >= midY && itemBounds.y + itemBounds.h <= this.bounds.y + this.bounds.h;
|
|
206
|
-
if (left && top) return 0;
|
|
207
|
-
if (right && top) return 1;
|
|
208
|
-
if (left && bottom) return 2;
|
|
209
|
-
if (right && bottom) return 3;
|
|
210
|
-
return -1;
|
|
211
|
-
}
|
|
212
|
-
split() {
|
|
213
|
-
const { x, y, w, h } = this.bounds;
|
|
214
|
-
const halfW = w / 2;
|
|
215
|
-
const halfH = h / 2;
|
|
216
|
-
const d = this.depth + 1;
|
|
217
|
-
this.children = [
|
|
218
|
-
new _QuadNode({ x, y, w: halfW, h: halfH }, d),
|
|
219
|
-
new _QuadNode({ x: x + halfW, y, w: halfW, h: halfH }, d),
|
|
220
|
-
new _QuadNode({ x, y: y + halfH, w: halfW, h: halfH }, d),
|
|
221
|
-
new _QuadNode({ x: x + halfW, y: y + halfH, w: halfW, h: halfH }, d)
|
|
222
|
-
];
|
|
223
|
-
const remaining = [];
|
|
224
|
-
for (const item of this.items) {
|
|
225
|
-
const idx = this.getChildIndex(item.bounds);
|
|
226
|
-
if (idx !== -1) {
|
|
227
|
-
const target = this.children[idx];
|
|
228
|
-
if (target) target.insert(item);
|
|
229
|
-
} else {
|
|
230
|
-
remaining.push(item);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
this.items = remaining;
|
|
234
|
-
}
|
|
235
|
-
collapseIfEmpty() {
|
|
236
|
-
if (!this.children) return;
|
|
237
|
-
let totalItems = this.items.length;
|
|
238
|
-
for (const child of this.children) {
|
|
239
|
-
if (child.children) return;
|
|
240
|
-
totalItems += child.items.length;
|
|
241
|
-
}
|
|
242
|
-
if (totalItems <= MAX_ITEMS) {
|
|
243
|
-
for (const child of this.children) {
|
|
244
|
-
this.items.push(...child.items);
|
|
245
|
-
}
|
|
246
|
-
this.children = null;
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
};
|
|
250
|
-
var Quadtree = class {
|
|
251
|
-
root;
|
|
252
|
-
_size = 0;
|
|
253
|
-
worldBounds;
|
|
254
|
-
constructor(worldBounds) {
|
|
255
|
-
this.worldBounds = worldBounds;
|
|
256
|
-
this.root = new QuadNode(worldBounds, 0);
|
|
257
|
-
}
|
|
258
|
-
get size() {
|
|
259
|
-
return this._size;
|
|
260
|
-
}
|
|
261
|
-
insert(id, bounds) {
|
|
262
|
-
this.root.insert({ id, bounds });
|
|
263
|
-
this._size++;
|
|
264
|
-
}
|
|
265
|
-
remove(id) {
|
|
266
|
-
if (this.root.remove(id)) {
|
|
267
|
-
this._size--;
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
update(id, newBounds) {
|
|
271
|
-
this.remove(id);
|
|
272
|
-
this.insert(id, newBounds);
|
|
273
|
-
}
|
|
274
|
-
query(rect) {
|
|
275
|
-
const result = [];
|
|
276
|
-
this.root.query(rect, result);
|
|
277
|
-
return result;
|
|
278
|
-
}
|
|
279
|
-
queryPoint(point) {
|
|
280
|
-
return this.query({ x: point.x, y: point.y, w: 0, h: 0 });
|
|
87
|
+
function snapToHexCenter(point, cellSize, orientation) {
|
|
88
|
+
if (orientation === "pointy") {
|
|
89
|
+
const hexW = Math.sqrt(3) * cellSize;
|
|
90
|
+
const rowH = 1.5 * cellSize;
|
|
91
|
+
const row = Math.round(point.y / rowH);
|
|
92
|
+
const offsetX = row % 2 !== 0 ? hexW / 2 : 0;
|
|
93
|
+
const col = Math.round((point.x - offsetX) / hexW);
|
|
94
|
+
return { x: col * hexW + offsetX || 0, y: row * rowH || 0 };
|
|
95
|
+
} else {
|
|
96
|
+
const hexH = Math.sqrt(3) * cellSize;
|
|
97
|
+
const colW = 1.5 * cellSize;
|
|
98
|
+
const col = Math.round(point.x / colW);
|
|
99
|
+
const offsetY = col % 2 !== 0 ? hexH / 2 : 0;
|
|
100
|
+
const row = Math.round((point.y - offsetY) / hexH);
|
|
101
|
+
return { x: col * colW || 0, y: row * hexH + offsetY || 0 };
|
|
281
102
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
103
|
+
}
|
|
104
|
+
function smartSnap(point, ctx) {
|
|
105
|
+
if (!ctx.snapToGrid || !ctx.gridSize) return point;
|
|
106
|
+
if (ctx.gridType === "hex" && ctx.hexOrientation) {
|
|
107
|
+
return snapToHexCenter(point, ctx.gridSize, ctx.hexOrientation);
|
|
285
108
|
}
|
|
286
|
-
|
|
109
|
+
return snapPoint(point, ctx.gridSize);
|
|
110
|
+
}
|
|
287
111
|
|
|
288
112
|
// src/elements/note-sanitizer.ts
|
|
289
113
|
var BOLD_TAGS = /* @__PURE__ */ new Set(["b", "strong"]);
|
|
@@ -540,38 +364,6 @@ function migrateElement(obj) {
|
|
|
540
364
|
}
|
|
541
365
|
}
|
|
542
366
|
|
|
543
|
-
// src/core/snap.ts
|
|
544
|
-
function snapPoint(point, gridSize) {
|
|
545
|
-
return {
|
|
546
|
-
x: Math.round(point.x / gridSize) * gridSize || 0,
|
|
547
|
-
y: Math.round(point.y / gridSize) * gridSize || 0
|
|
548
|
-
};
|
|
549
|
-
}
|
|
550
|
-
function snapToHexCenter(point, cellSize, orientation) {
|
|
551
|
-
if (orientation === "pointy") {
|
|
552
|
-
const hexW = Math.sqrt(3) * cellSize;
|
|
553
|
-
const rowH = 1.5 * cellSize;
|
|
554
|
-
const row = Math.round(point.y / rowH);
|
|
555
|
-
const offsetX = row % 2 !== 0 ? hexW / 2 : 0;
|
|
556
|
-
const col = Math.round((point.x - offsetX) / hexW);
|
|
557
|
-
return { x: col * hexW + offsetX || 0, y: row * rowH || 0 };
|
|
558
|
-
} else {
|
|
559
|
-
const hexH = Math.sqrt(3) * cellSize;
|
|
560
|
-
const colW = 1.5 * cellSize;
|
|
561
|
-
const col = Math.round(point.x / colW);
|
|
562
|
-
const offsetY = col % 2 !== 0 ? hexH / 2 : 0;
|
|
563
|
-
const row = Math.round((point.y - offsetY) / hexH);
|
|
564
|
-
return { x: col * colW || 0, y: row * hexH + offsetY || 0 };
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
function smartSnap(point, ctx) {
|
|
568
|
-
if (!ctx.snapToGrid || !ctx.gridSize) return point;
|
|
569
|
-
if (ctx.gridType === "hex" && ctx.hexOrientation) {
|
|
570
|
-
return snapToHexCenter(point, ctx.gridSize, ctx.hexOrientation);
|
|
571
|
-
}
|
|
572
|
-
return snapPoint(point, ctx.gridSize);
|
|
573
|
-
}
|
|
574
|
-
|
|
575
367
|
// src/core/auto-save.ts
|
|
576
368
|
var DEFAULT_KEY = "fieldnotes-autosave";
|
|
577
369
|
var DEFAULT_DEBOUNCE_MS = 1e3;
|
|
@@ -735,143 +527,6 @@ var Camera = class {
|
|
|
735
527
|
}
|
|
736
528
|
};
|
|
737
529
|
|
|
738
|
-
// src/canvas/background.ts
|
|
739
|
-
var MIN_PATTERN_SPACING = 16;
|
|
740
|
-
var DEFAULTS = {
|
|
741
|
-
pattern: "dots",
|
|
742
|
-
spacing: 24,
|
|
743
|
-
color: "#d0d0d0",
|
|
744
|
-
dotRadius: 1,
|
|
745
|
-
lineWidth: 0.5
|
|
746
|
-
};
|
|
747
|
-
var Background = class {
|
|
748
|
-
pattern;
|
|
749
|
-
spacing;
|
|
750
|
-
color;
|
|
751
|
-
dotRadius;
|
|
752
|
-
lineWidth;
|
|
753
|
-
cachedCanvas = null;
|
|
754
|
-
cachedCtx = null;
|
|
755
|
-
lastZoom = -1;
|
|
756
|
-
lastOffsetX = -Infinity;
|
|
757
|
-
lastOffsetY = -Infinity;
|
|
758
|
-
lastWidth = 0;
|
|
759
|
-
lastHeight = 0;
|
|
760
|
-
constructor(options = {}) {
|
|
761
|
-
this.pattern = options.pattern ?? DEFAULTS.pattern;
|
|
762
|
-
this.spacing = options.spacing ?? DEFAULTS.spacing;
|
|
763
|
-
this.color = options.color ?? DEFAULTS.color;
|
|
764
|
-
this.dotRadius = options.dotRadius ?? DEFAULTS.dotRadius;
|
|
765
|
-
this.lineWidth = options.lineWidth ?? DEFAULTS.lineWidth;
|
|
766
|
-
}
|
|
767
|
-
render(ctx, camera) {
|
|
768
|
-
const { width, height } = ctx.canvas;
|
|
769
|
-
const dpr = typeof devicePixelRatio !== "undefined" ? devicePixelRatio : 1;
|
|
770
|
-
const cssWidth = width / dpr;
|
|
771
|
-
const cssHeight = height / dpr;
|
|
772
|
-
ctx.save();
|
|
773
|
-
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
774
|
-
ctx.clearRect(0, 0, cssWidth, cssHeight);
|
|
775
|
-
if (this.pattern === "none") {
|
|
776
|
-
ctx.restore();
|
|
777
|
-
return;
|
|
778
|
-
}
|
|
779
|
-
const spacing = this.adaptSpacing(this.spacing, camera.zoom);
|
|
780
|
-
const keyZoom = camera.zoom;
|
|
781
|
-
const keyX = Math.floor(camera.position.x % spacing);
|
|
782
|
-
const keyY = Math.floor(camera.position.y % spacing);
|
|
783
|
-
if (this.cachedCanvas !== null && keyZoom === this.lastZoom && keyX === this.lastOffsetX && keyY === this.lastOffsetY && cssWidth === this.lastWidth && cssHeight === this.lastHeight) {
|
|
784
|
-
ctx.drawImage(this.cachedCanvas, 0, 0);
|
|
785
|
-
ctx.restore();
|
|
786
|
-
return;
|
|
787
|
-
}
|
|
788
|
-
this.ensureCachedCanvas(cssWidth, cssHeight, dpr);
|
|
789
|
-
if (this.cachedCtx === null) {
|
|
790
|
-
if (this.pattern === "dots") {
|
|
791
|
-
this.renderDots(ctx, camera, cssWidth, cssHeight);
|
|
792
|
-
} else if (this.pattern === "grid") {
|
|
793
|
-
this.renderGrid(ctx, camera, cssWidth, cssHeight);
|
|
794
|
-
}
|
|
795
|
-
ctx.restore();
|
|
796
|
-
return;
|
|
797
|
-
}
|
|
798
|
-
const offCtx = this.cachedCtx;
|
|
799
|
-
offCtx.clearRect(0, 0, cssWidth, cssHeight);
|
|
800
|
-
if (this.pattern === "dots") {
|
|
801
|
-
this.renderDots(offCtx, camera, cssWidth, cssHeight);
|
|
802
|
-
} else if (this.pattern === "grid") {
|
|
803
|
-
this.renderGrid(offCtx, camera, cssWidth, cssHeight);
|
|
804
|
-
}
|
|
805
|
-
this.lastZoom = keyZoom;
|
|
806
|
-
this.lastOffsetX = keyX;
|
|
807
|
-
this.lastOffsetY = keyY;
|
|
808
|
-
this.lastWidth = cssWidth;
|
|
809
|
-
this.lastHeight = cssHeight;
|
|
810
|
-
ctx.drawImage(this.cachedCanvas, 0, 0);
|
|
811
|
-
ctx.restore();
|
|
812
|
-
}
|
|
813
|
-
ensureCachedCanvas(cssWidth, cssHeight, dpr) {
|
|
814
|
-
if (this.cachedCanvas !== null && this.lastWidth === cssWidth && this.lastHeight === cssHeight) {
|
|
815
|
-
return;
|
|
816
|
-
}
|
|
817
|
-
const physWidth = Math.round(cssWidth * dpr);
|
|
818
|
-
const physHeight = Math.round(cssHeight * dpr);
|
|
819
|
-
if (typeof OffscreenCanvas !== "undefined") {
|
|
820
|
-
this.cachedCanvas = new OffscreenCanvas(physWidth, physHeight);
|
|
821
|
-
} else if (typeof document !== "undefined") {
|
|
822
|
-
const el = document.createElement("canvas");
|
|
823
|
-
el.width = physWidth;
|
|
824
|
-
el.height = physHeight;
|
|
825
|
-
this.cachedCanvas = el;
|
|
826
|
-
} else {
|
|
827
|
-
this.cachedCanvas = null;
|
|
828
|
-
this.cachedCtx = null;
|
|
829
|
-
return;
|
|
830
|
-
}
|
|
831
|
-
const offCtx = this.cachedCanvas.getContext("2d");
|
|
832
|
-
if (offCtx !== null) {
|
|
833
|
-
offCtx.scale(dpr, dpr);
|
|
834
|
-
}
|
|
835
|
-
this.cachedCtx = offCtx;
|
|
836
|
-
this.lastZoom = -1;
|
|
837
|
-
}
|
|
838
|
-
adaptSpacing(baseSpacing, zoom) {
|
|
839
|
-
let spacing = baseSpacing * zoom;
|
|
840
|
-
while (spacing < MIN_PATTERN_SPACING) {
|
|
841
|
-
spacing *= 2;
|
|
842
|
-
}
|
|
843
|
-
return spacing;
|
|
844
|
-
}
|
|
845
|
-
renderDots(ctx, camera, width, height) {
|
|
846
|
-
const spacing = this.adaptSpacing(this.spacing, camera.zoom);
|
|
847
|
-
const offsetX = camera.position.x % spacing;
|
|
848
|
-
const offsetY = camera.position.y % spacing;
|
|
849
|
-
const radius = this.dotRadius * Math.min(camera.zoom, 2);
|
|
850
|
-
ctx.fillStyle = this.color;
|
|
851
|
-
ctx.beginPath();
|
|
852
|
-
for (let x = offsetX; x < width; x += spacing) {
|
|
853
|
-
for (let y = offsetY; y < height; y += spacing) {
|
|
854
|
-
ctx.moveTo(x + radius, y);
|
|
855
|
-
ctx.arc(x, y, radius, 0, Math.PI * 2);
|
|
856
|
-
}
|
|
857
|
-
}
|
|
858
|
-
ctx.fill();
|
|
859
|
-
}
|
|
860
|
-
renderGrid(ctx, camera, width, height) {
|
|
861
|
-
const spacing = this.adaptSpacing(this.spacing, camera.zoom);
|
|
862
|
-
const offsetX = camera.position.x % spacing;
|
|
863
|
-
const offsetY = camera.position.y % spacing;
|
|
864
|
-
const lineW = this.lineWidth * Math.min(camera.zoom, 2);
|
|
865
|
-
ctx.fillStyle = this.color;
|
|
866
|
-
for (let x = offsetX; x < width; x += spacing) {
|
|
867
|
-
ctx.fillRect(x, 0, lineW, height);
|
|
868
|
-
}
|
|
869
|
-
for (let y = offsetY; y < height; y += spacing) {
|
|
870
|
-
ctx.fillRect(0, y, width, lineW);
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
};
|
|
874
|
-
|
|
875
530
|
// src/canvas/input-filter.ts
|
|
876
531
|
var InputFilter = class _InputFilter {
|
|
877
532
|
activePenId = null;
|
|
@@ -1597,115 +1252,387 @@ var InputHandler = class {
|
|
|
1597
1252
|
const newZoom = this.camera.zoom * scale;
|
|
1598
1253
|
this.camera.zoomAt(newZoom, center);
|
|
1599
1254
|
}
|
|
1600
|
-
const dx = center.x - this.lastPointer.x;
|
|
1601
|
-
const dy = center.y - this.lastPointer.y;
|
|
1602
|
-
this.camera.pan(dx, dy);
|
|
1603
|
-
this.lastPinchDistance = dist;
|
|
1604
|
-
this.lastPinchCenter = center;
|
|
1605
|
-
this.lastPointer = { ...center };
|
|
1606
|
-
}
|
|
1607
|
-
getPinchPoints() {
|
|
1608
|
-
const pts = [...this.activePointers.values()];
|
|
1609
|
-
return [pts[0] ?? { x: 0, y: 0 }, pts[1] ?? { x: 0, y: 0 }];
|
|
1610
|
-
}
|
|
1611
|
-
distance(a, b) {
|
|
1612
|
-
const dx = a.x - b.x;
|
|
1613
|
-
const dy = a.y - b.y;
|
|
1614
|
-
return Math.sqrt(dx * dx + dy * dy);
|
|
1255
|
+
const dx = center.x - this.lastPointer.x;
|
|
1256
|
+
const dy = center.y - this.lastPointer.y;
|
|
1257
|
+
this.camera.pan(dx, dy);
|
|
1258
|
+
this.lastPinchDistance = dist;
|
|
1259
|
+
this.lastPinchCenter = center;
|
|
1260
|
+
this.lastPointer = { ...center };
|
|
1261
|
+
}
|
|
1262
|
+
getPinchPoints() {
|
|
1263
|
+
const pts = [...this.activePointers.values()];
|
|
1264
|
+
return [pts[0] ?? { x: 0, y: 0 }, pts[1] ?? { x: 0, y: 0 }];
|
|
1265
|
+
}
|
|
1266
|
+
distance(a, b) {
|
|
1267
|
+
const dx = a.x - b.x;
|
|
1268
|
+
const dy = a.y - b.y;
|
|
1269
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
1270
|
+
}
|
|
1271
|
+
midpoint(a, b) {
|
|
1272
|
+
return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
|
|
1273
|
+
}
|
|
1274
|
+
toPointerState(e) {
|
|
1275
|
+
const rect = this.element.getBoundingClientRect();
|
|
1276
|
+
return {
|
|
1277
|
+
x: e.clientX - rect.left,
|
|
1278
|
+
y: e.clientY - rect.top,
|
|
1279
|
+
pressure: e.pressure,
|
|
1280
|
+
pointerType: e.pointerType === "touch" || e.pointerType === "pen" ? e.pointerType : "mouse",
|
|
1281
|
+
shiftKey: e.shiftKey
|
|
1282
|
+
};
|
|
1283
|
+
}
|
|
1284
|
+
dispatchToolDown(e) {
|
|
1285
|
+
if (!this.toolManager || !this.toolContext) return;
|
|
1286
|
+
this.actions.flushPendingNudge();
|
|
1287
|
+
this.historyRecorder?.begin();
|
|
1288
|
+
this.isToolActive = true;
|
|
1289
|
+
this.toolManager.handlePointerDown(this.toPointerState(e), this.toolContext);
|
|
1290
|
+
}
|
|
1291
|
+
dispatchToolMove(e) {
|
|
1292
|
+
if (!this.toolManager || !this.toolContext) return;
|
|
1293
|
+
this.toolManager.handlePointerMove(this.toPointerState(e), this.toolContext);
|
|
1294
|
+
}
|
|
1295
|
+
dispatchToolHover(e) {
|
|
1296
|
+
if (!this.toolManager?.activeTool || !this.toolContext) return;
|
|
1297
|
+
const tool = this.toolManager.activeTool;
|
|
1298
|
+
if (tool.onHover) {
|
|
1299
|
+
tool.onHover(this.toPointerState(e), this.toolContext);
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
dispatchToolUp(e) {
|
|
1303
|
+
if (!this.toolManager || !this.toolContext) return;
|
|
1304
|
+
this.toolManager.handlePointerUp(this.toPointerState(e), this.toolContext);
|
|
1305
|
+
this.historyRecorder?.commit();
|
|
1306
|
+
}
|
|
1307
|
+
isInScope() {
|
|
1308
|
+
if (this.scope === "window") return true;
|
|
1309
|
+
const active = document.activeElement;
|
|
1310
|
+
return active === this.element || this.element.contains(active);
|
|
1311
|
+
}
|
|
1312
|
+
focusSelf() {
|
|
1313
|
+
if (this.scope !== "focus" || this.isInScope()) return;
|
|
1314
|
+
this.element.focus({ preventScroll: true });
|
|
1315
|
+
}
|
|
1316
|
+
cancelToolIfActive(e) {
|
|
1317
|
+
if (this.isToolActive) {
|
|
1318
|
+
this.dispatchToolUp(e);
|
|
1319
|
+
this.isToolActive = false;
|
|
1320
|
+
}
|
|
1321
|
+
this.deferredDown = null;
|
|
1322
|
+
}
|
|
1323
|
+
};
|
|
1324
|
+
|
|
1325
|
+
// src/canvas/background.ts
|
|
1326
|
+
var MIN_PATTERN_SPACING = 16;
|
|
1327
|
+
var DEFAULTS = {
|
|
1328
|
+
pattern: "dots",
|
|
1329
|
+
spacing: 24,
|
|
1330
|
+
color: "#d0d0d0",
|
|
1331
|
+
dotRadius: 1,
|
|
1332
|
+
lineWidth: 0.5
|
|
1333
|
+
};
|
|
1334
|
+
var Background = class {
|
|
1335
|
+
pattern;
|
|
1336
|
+
spacing;
|
|
1337
|
+
color;
|
|
1338
|
+
dotRadius;
|
|
1339
|
+
lineWidth;
|
|
1340
|
+
cachedCanvas = null;
|
|
1341
|
+
cachedCtx = null;
|
|
1342
|
+
lastZoom = -1;
|
|
1343
|
+
lastOffsetX = -Infinity;
|
|
1344
|
+
lastOffsetY = -Infinity;
|
|
1345
|
+
lastWidth = 0;
|
|
1346
|
+
lastHeight = 0;
|
|
1347
|
+
constructor(options = {}) {
|
|
1348
|
+
this.pattern = options.pattern ?? DEFAULTS.pattern;
|
|
1349
|
+
this.spacing = options.spacing ?? DEFAULTS.spacing;
|
|
1350
|
+
this.color = options.color ?? DEFAULTS.color;
|
|
1351
|
+
this.dotRadius = options.dotRadius ?? DEFAULTS.dotRadius;
|
|
1352
|
+
this.lineWidth = options.lineWidth ?? DEFAULTS.lineWidth;
|
|
1353
|
+
}
|
|
1354
|
+
render(ctx, camera) {
|
|
1355
|
+
const { width, height } = ctx.canvas;
|
|
1356
|
+
const dpr = typeof devicePixelRatio !== "undefined" ? devicePixelRatio : 1;
|
|
1357
|
+
const cssWidth = width / dpr;
|
|
1358
|
+
const cssHeight = height / dpr;
|
|
1359
|
+
ctx.save();
|
|
1360
|
+
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
1361
|
+
ctx.clearRect(0, 0, cssWidth, cssHeight);
|
|
1362
|
+
if (this.pattern === "none") {
|
|
1363
|
+
ctx.restore();
|
|
1364
|
+
return;
|
|
1365
|
+
}
|
|
1366
|
+
const spacing = this.adaptSpacing(this.spacing, camera.zoom);
|
|
1367
|
+
const keyZoom = camera.zoom;
|
|
1368
|
+
const keyX = Math.floor(camera.position.x % spacing);
|
|
1369
|
+
const keyY = Math.floor(camera.position.y % spacing);
|
|
1370
|
+
if (this.cachedCanvas !== null && keyZoom === this.lastZoom && keyX === this.lastOffsetX && keyY === this.lastOffsetY && cssWidth === this.lastWidth && cssHeight === this.lastHeight) {
|
|
1371
|
+
ctx.drawImage(this.cachedCanvas, 0, 0);
|
|
1372
|
+
ctx.restore();
|
|
1373
|
+
return;
|
|
1374
|
+
}
|
|
1375
|
+
this.ensureCachedCanvas(cssWidth, cssHeight, dpr);
|
|
1376
|
+
if (this.cachedCtx === null) {
|
|
1377
|
+
if (this.pattern === "dots") {
|
|
1378
|
+
this.renderDots(ctx, camera, cssWidth, cssHeight);
|
|
1379
|
+
} else if (this.pattern === "grid") {
|
|
1380
|
+
this.renderGrid(ctx, camera, cssWidth, cssHeight);
|
|
1381
|
+
}
|
|
1382
|
+
ctx.restore();
|
|
1383
|
+
return;
|
|
1384
|
+
}
|
|
1385
|
+
const offCtx = this.cachedCtx;
|
|
1386
|
+
offCtx.clearRect(0, 0, cssWidth, cssHeight);
|
|
1387
|
+
if (this.pattern === "dots") {
|
|
1388
|
+
this.renderDots(offCtx, camera, cssWidth, cssHeight);
|
|
1389
|
+
} else if (this.pattern === "grid") {
|
|
1390
|
+
this.renderGrid(offCtx, camera, cssWidth, cssHeight);
|
|
1391
|
+
}
|
|
1392
|
+
this.lastZoom = keyZoom;
|
|
1393
|
+
this.lastOffsetX = keyX;
|
|
1394
|
+
this.lastOffsetY = keyY;
|
|
1395
|
+
this.lastWidth = cssWidth;
|
|
1396
|
+
this.lastHeight = cssHeight;
|
|
1397
|
+
ctx.drawImage(this.cachedCanvas, 0, 0);
|
|
1398
|
+
ctx.restore();
|
|
1399
|
+
}
|
|
1400
|
+
ensureCachedCanvas(cssWidth, cssHeight, dpr) {
|
|
1401
|
+
if (this.cachedCanvas !== null && this.lastWidth === cssWidth && this.lastHeight === cssHeight) {
|
|
1402
|
+
return;
|
|
1403
|
+
}
|
|
1404
|
+
const physWidth = Math.round(cssWidth * dpr);
|
|
1405
|
+
const physHeight = Math.round(cssHeight * dpr);
|
|
1406
|
+
if (typeof OffscreenCanvas !== "undefined") {
|
|
1407
|
+
this.cachedCanvas = new OffscreenCanvas(physWidth, physHeight);
|
|
1408
|
+
} else if (typeof document !== "undefined") {
|
|
1409
|
+
const el = document.createElement("canvas");
|
|
1410
|
+
el.width = physWidth;
|
|
1411
|
+
el.height = physHeight;
|
|
1412
|
+
this.cachedCanvas = el;
|
|
1413
|
+
} else {
|
|
1414
|
+
this.cachedCanvas = null;
|
|
1415
|
+
this.cachedCtx = null;
|
|
1416
|
+
return;
|
|
1417
|
+
}
|
|
1418
|
+
const offCtx = this.cachedCanvas.getContext("2d");
|
|
1419
|
+
if (offCtx !== null) {
|
|
1420
|
+
offCtx.scale(dpr, dpr);
|
|
1421
|
+
}
|
|
1422
|
+
this.cachedCtx = offCtx;
|
|
1423
|
+
this.lastZoom = -1;
|
|
1424
|
+
}
|
|
1425
|
+
adaptSpacing(baseSpacing, zoom) {
|
|
1426
|
+
let spacing = baseSpacing * zoom;
|
|
1427
|
+
while (spacing < MIN_PATTERN_SPACING) {
|
|
1428
|
+
spacing *= 2;
|
|
1429
|
+
}
|
|
1430
|
+
return spacing;
|
|
1431
|
+
}
|
|
1432
|
+
renderDots(ctx, camera, width, height) {
|
|
1433
|
+
const spacing = this.adaptSpacing(this.spacing, camera.zoom);
|
|
1434
|
+
const offsetX = camera.position.x % spacing;
|
|
1435
|
+
const offsetY = camera.position.y % spacing;
|
|
1436
|
+
const radius = this.dotRadius * Math.min(camera.zoom, 2);
|
|
1437
|
+
ctx.fillStyle = this.color;
|
|
1438
|
+
ctx.beginPath();
|
|
1439
|
+
for (let x = offsetX; x < width; x += spacing) {
|
|
1440
|
+
for (let y = offsetY; y < height; y += spacing) {
|
|
1441
|
+
ctx.moveTo(x + radius, y);
|
|
1442
|
+
ctx.arc(x, y, radius, 0, Math.PI * 2);
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
ctx.fill();
|
|
1446
|
+
}
|
|
1447
|
+
renderGrid(ctx, camera, width, height) {
|
|
1448
|
+
const spacing = this.adaptSpacing(this.spacing, camera.zoom);
|
|
1449
|
+
const offsetX = camera.position.x % spacing;
|
|
1450
|
+
const offsetY = camera.position.y % spacing;
|
|
1451
|
+
const lineW = this.lineWidth * Math.min(camera.zoom, 2);
|
|
1452
|
+
ctx.fillStyle = this.color;
|
|
1453
|
+
for (let x = offsetX; x < width; x += spacing) {
|
|
1454
|
+
ctx.fillRect(x, 0, lineW, height);
|
|
1455
|
+
}
|
|
1456
|
+
for (let y = offsetY; y < height; y += spacing) {
|
|
1457
|
+
ctx.fillRect(0, y, width, lineW);
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
};
|
|
1461
|
+
|
|
1462
|
+
// src/core/event-bus.ts
|
|
1463
|
+
var EventBus = class {
|
|
1464
|
+
listeners = /* @__PURE__ */ new Map();
|
|
1465
|
+
on(event, listener) {
|
|
1466
|
+
const existing = this.listeners.get(event);
|
|
1467
|
+
if (existing) {
|
|
1468
|
+
existing.add(listener);
|
|
1469
|
+
} else {
|
|
1470
|
+
const set = /* @__PURE__ */ new Set([listener]);
|
|
1471
|
+
this.listeners.set(event, set);
|
|
1472
|
+
}
|
|
1473
|
+
return () => this.off(event, listener);
|
|
1474
|
+
}
|
|
1475
|
+
off(event, listener) {
|
|
1476
|
+
this.listeners.get(event)?.delete(listener);
|
|
1477
|
+
}
|
|
1478
|
+
emit(event, data) {
|
|
1479
|
+
this.listeners.get(event)?.forEach((listener) => {
|
|
1480
|
+
try {
|
|
1481
|
+
listener(data);
|
|
1482
|
+
} catch (err) {
|
|
1483
|
+
console.error(`[fieldnotes] listener error for "${String(event)}"`, err);
|
|
1484
|
+
}
|
|
1485
|
+
});
|
|
1486
|
+
}
|
|
1487
|
+
clear() {
|
|
1488
|
+
this.listeners.clear();
|
|
1489
|
+
}
|
|
1490
|
+
};
|
|
1491
|
+
|
|
1492
|
+
// src/core/quadtree.ts
|
|
1493
|
+
var MAX_ITEMS = 8;
|
|
1494
|
+
var MAX_DEPTH = 8;
|
|
1495
|
+
function intersects(a, b) {
|
|
1496
|
+
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;
|
|
1497
|
+
}
|
|
1498
|
+
var QuadNode = class _QuadNode {
|
|
1499
|
+
constructor(bounds, depth) {
|
|
1500
|
+
this.bounds = bounds;
|
|
1501
|
+
this.depth = depth;
|
|
1502
|
+
}
|
|
1503
|
+
items = [];
|
|
1504
|
+
children = null;
|
|
1505
|
+
insert(entry) {
|
|
1506
|
+
if (this.children) {
|
|
1507
|
+
const idx = this.getChildIndex(entry.bounds);
|
|
1508
|
+
if (idx !== -1) {
|
|
1509
|
+
const child = this.children[idx];
|
|
1510
|
+
if (child) child.insert(entry);
|
|
1511
|
+
return;
|
|
1512
|
+
}
|
|
1513
|
+
this.items.push(entry);
|
|
1514
|
+
return;
|
|
1515
|
+
}
|
|
1516
|
+
this.items.push(entry);
|
|
1517
|
+
if (this.items.length > MAX_ITEMS && this.depth < MAX_DEPTH) {
|
|
1518
|
+
this.split();
|
|
1519
|
+
}
|
|
1615
1520
|
}
|
|
1616
|
-
|
|
1617
|
-
|
|
1521
|
+
remove(id) {
|
|
1522
|
+
const idx = this.items.findIndex((e) => e.id === id);
|
|
1523
|
+
if (idx !== -1) {
|
|
1524
|
+
this.items.splice(idx, 1);
|
|
1525
|
+
return true;
|
|
1526
|
+
}
|
|
1527
|
+
if (this.children) {
|
|
1528
|
+
for (const child of this.children) {
|
|
1529
|
+
if (child.remove(id)) {
|
|
1530
|
+
this.collapseIfEmpty();
|
|
1531
|
+
return true;
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
return false;
|
|
1618
1536
|
}
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1537
|
+
query(rect, result) {
|
|
1538
|
+
if (!intersects(this.bounds, rect)) return;
|
|
1539
|
+
for (const item of this.items) {
|
|
1540
|
+
if (intersects(item.bounds, rect)) {
|
|
1541
|
+
result.push(item.id);
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
if (this.children) {
|
|
1545
|
+
for (const child of this.children) {
|
|
1546
|
+
child.query(rect, result);
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1628
1549
|
}
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
this.
|
|
1632
|
-
this.
|
|
1633
|
-
this.
|
|
1634
|
-
this.
|
|
1550
|
+
getChildIndex(itemBounds) {
|
|
1551
|
+
const midX = this.bounds.x + this.bounds.w / 2;
|
|
1552
|
+
const midY = this.bounds.y + this.bounds.h / 2;
|
|
1553
|
+
const left = itemBounds.x >= this.bounds.x && itemBounds.x + itemBounds.w <= midX;
|
|
1554
|
+
const right = itemBounds.x >= midX && itemBounds.x + itemBounds.w <= this.bounds.x + this.bounds.w;
|
|
1555
|
+
const top = itemBounds.y >= this.bounds.y && itemBounds.y + itemBounds.h <= midY;
|
|
1556
|
+
const bottom = itemBounds.y >= midY && itemBounds.y + itemBounds.h <= this.bounds.y + this.bounds.h;
|
|
1557
|
+
if (left && top) return 0;
|
|
1558
|
+
if (right && top) return 1;
|
|
1559
|
+
if (left && bottom) return 2;
|
|
1560
|
+
if (right && bottom) return 3;
|
|
1561
|
+
return -1;
|
|
1635
1562
|
}
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1563
|
+
split() {
|
|
1564
|
+
const { x, y, w, h } = this.bounds;
|
|
1565
|
+
const halfW = w / 2;
|
|
1566
|
+
const halfH = h / 2;
|
|
1567
|
+
const d = this.depth + 1;
|
|
1568
|
+
this.children = [
|
|
1569
|
+
new _QuadNode({ x, y, w: halfW, h: halfH }, d),
|
|
1570
|
+
new _QuadNode({ x: x + halfW, y, w: halfW, h: halfH }, d),
|
|
1571
|
+
new _QuadNode({ x, y: y + halfH, w: halfW, h: halfH }, d),
|
|
1572
|
+
new _QuadNode({ x: x + halfW, y: y + halfH, w: halfW, h: halfH }, d)
|
|
1573
|
+
];
|
|
1574
|
+
const remaining = [];
|
|
1575
|
+
for (const item of this.items) {
|
|
1576
|
+
const idx = this.getChildIndex(item.bounds);
|
|
1577
|
+
if (idx !== -1) {
|
|
1578
|
+
const target = this.children[idx];
|
|
1579
|
+
if (target) target.insert(item);
|
|
1580
|
+
} else {
|
|
1581
|
+
remaining.push(item);
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
this.items = remaining;
|
|
1639
1585
|
}
|
|
1640
|
-
|
|
1641
|
-
if (!this.
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1586
|
+
collapseIfEmpty() {
|
|
1587
|
+
if (!this.children) return;
|
|
1588
|
+
let totalItems = this.items.length;
|
|
1589
|
+
for (const child of this.children) {
|
|
1590
|
+
if (child.children) return;
|
|
1591
|
+
totalItems += child.items.length;
|
|
1592
|
+
}
|
|
1593
|
+
if (totalItems <= MAX_ITEMS) {
|
|
1594
|
+
for (const child of this.children) {
|
|
1595
|
+
this.items.push(...child.items);
|
|
1596
|
+
}
|
|
1597
|
+
this.children = null;
|
|
1645
1598
|
}
|
|
1646
1599
|
}
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1600
|
+
};
|
|
1601
|
+
var Quadtree = class {
|
|
1602
|
+
root;
|
|
1603
|
+
_size = 0;
|
|
1604
|
+
worldBounds;
|
|
1605
|
+
constructor(worldBounds) {
|
|
1606
|
+
this.worldBounds = worldBounds;
|
|
1607
|
+
this.root = new QuadNode(worldBounds, 0);
|
|
1651
1608
|
}
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
const active = document.activeElement;
|
|
1655
|
-
return active === this.element || this.element.contains(active);
|
|
1609
|
+
get size() {
|
|
1610
|
+
return this._size;
|
|
1656
1611
|
}
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
this.
|
|
1612
|
+
insert(id, bounds) {
|
|
1613
|
+
this.root.insert({ id, bounds });
|
|
1614
|
+
this._size++;
|
|
1660
1615
|
}
|
|
1661
|
-
|
|
1662
|
-
if (this.
|
|
1663
|
-
this.
|
|
1664
|
-
this.isToolActive = false;
|
|
1616
|
+
remove(id) {
|
|
1617
|
+
if (this.root.remove(id)) {
|
|
1618
|
+
this._size--;
|
|
1665
1619
|
}
|
|
1666
|
-
this.deferredDown = null;
|
|
1667
1620
|
}
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
var DEFAULT_TIMEOUT = 300;
|
|
1672
|
-
var DEFAULT_MAX_DISTANCE = 20;
|
|
1673
|
-
var DoubleTapDetector = class {
|
|
1674
|
-
timeout;
|
|
1675
|
-
maxDistance;
|
|
1676
|
-
lastTapTime = 0;
|
|
1677
|
-
lastTapX = 0;
|
|
1678
|
-
lastTapY = 0;
|
|
1679
|
-
hasPendingTap = false;
|
|
1680
|
-
constructor(options) {
|
|
1681
|
-
this.timeout = options?.timeout ?? DEFAULT_TIMEOUT;
|
|
1682
|
-
this.maxDistance = options?.maxDistance ?? DEFAULT_MAX_DISTANCE;
|
|
1621
|
+
update(id, newBounds) {
|
|
1622
|
+
this.remove(id);
|
|
1623
|
+
this.insert(id, newBounds);
|
|
1683
1624
|
}
|
|
1684
|
-
|
|
1685
|
-
const
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
if (this.hasPendingTap) {
|
|
1689
|
-
const elapsed = now - this.lastTapTime;
|
|
1690
|
-
const dx = x - this.lastTapX;
|
|
1691
|
-
const dy = y - this.lastTapY;
|
|
1692
|
-
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
1693
|
-
if (elapsed <= this.timeout && dist <= this.maxDistance) {
|
|
1694
|
-
this.reset();
|
|
1695
|
-
return true;
|
|
1696
|
-
}
|
|
1697
|
-
}
|
|
1698
|
-
this.lastTapTime = now;
|
|
1699
|
-
this.lastTapX = x;
|
|
1700
|
-
this.lastTapY = y;
|
|
1701
|
-
this.hasPendingTap = true;
|
|
1702
|
-
return false;
|
|
1625
|
+
query(rect) {
|
|
1626
|
+
const result = [];
|
|
1627
|
+
this.root.query(rect, result);
|
|
1628
|
+
return result;
|
|
1703
1629
|
}
|
|
1704
|
-
|
|
1705
|
-
this.
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
this.
|
|
1630
|
+
queryPoint(point) {
|
|
1631
|
+
return this.query({ x: point.x, y: point.y, w: 0, h: 0 });
|
|
1632
|
+
}
|
|
1633
|
+
clear() {
|
|
1634
|
+
this.root = new QuadNode(this.worldBounds, 0);
|
|
1635
|
+
this._size = 0;
|
|
1709
1636
|
}
|
|
1710
1637
|
};
|
|
1711
1638
|
|
|
@@ -2354,51 +2281,6 @@ function updateBoundArrow(arrow, store) {
|
|
|
2354
2281
|
}
|
|
2355
2282
|
return Object.keys(updates).length > 0 ? updates : null;
|
|
2356
2283
|
}
|
|
2357
|
-
function clearStaleBindings(arrow, store) {
|
|
2358
|
-
const updates = {};
|
|
2359
|
-
let hasUpdates = false;
|
|
2360
|
-
if (arrow.fromBinding && !store.getById(arrow.fromBinding.elementId)) {
|
|
2361
|
-
updates.fromBinding = void 0;
|
|
2362
|
-
hasUpdates = true;
|
|
2363
|
-
}
|
|
2364
|
-
if (arrow.toBinding && !store.getById(arrow.toBinding.elementId)) {
|
|
2365
|
-
updates.toBinding = void 0;
|
|
2366
|
-
hasUpdates = true;
|
|
2367
|
-
}
|
|
2368
|
-
return hasUpdates ? updates : null;
|
|
2369
|
-
}
|
|
2370
|
-
function unbindArrow(arrow, store) {
|
|
2371
|
-
const updates = {};
|
|
2372
|
-
if (arrow.fromBinding) {
|
|
2373
|
-
const el = store.getById(arrow.fromBinding.elementId);
|
|
2374
|
-
const bounds = el ? getElementBounds(el) : null;
|
|
2375
|
-
if (bounds) {
|
|
2376
|
-
const angle = getArrowTangentAngle(arrow.from, arrow.to, arrow.bend, 0);
|
|
2377
|
-
const rayTarget = {
|
|
2378
|
-
x: arrow.from.x + Math.cos(angle) * 1e3,
|
|
2379
|
-
y: arrow.from.y + Math.sin(angle) * 1e3
|
|
2380
|
-
};
|
|
2381
|
-
const edge = getEdgeIntersection(bounds, rayTarget);
|
|
2382
|
-
updates.from = edge;
|
|
2383
|
-
updates.position = edge;
|
|
2384
|
-
}
|
|
2385
|
-
updates.fromBinding = void 0;
|
|
2386
|
-
}
|
|
2387
|
-
if (arrow.toBinding) {
|
|
2388
|
-
const el = store.getById(arrow.toBinding.elementId);
|
|
2389
|
-
const bounds = el ? getElementBounds(el) : null;
|
|
2390
|
-
if (bounds) {
|
|
2391
|
-
const angle = getArrowTangentAngle(arrow.from, arrow.to, arrow.bend, 1);
|
|
2392
|
-
const rayTarget = {
|
|
2393
|
-
x: arrow.to.x - Math.cos(angle) * 1e3,
|
|
2394
|
-
y: arrow.to.y - Math.sin(angle) * 1e3
|
|
2395
|
-
};
|
|
2396
|
-
updates.to = getEdgeIntersection(bounds, rayTarget);
|
|
2397
|
-
}
|
|
2398
|
-
updates.toBinding = void 0;
|
|
2399
|
-
}
|
|
2400
|
-
return updates;
|
|
2401
|
-
}
|
|
2402
2284
|
|
|
2403
2285
|
// src/elements/grid-renderer.ts
|
|
2404
2286
|
function getSquareGridLines(bounds, cellSize) {
|
|
@@ -4591,6 +4473,48 @@ var InteractMode = class {
|
|
|
4591
4473
|
};
|
|
4592
4474
|
};
|
|
4593
4475
|
|
|
4476
|
+
// src/canvas/double-tap-detector.ts
|
|
4477
|
+
var DEFAULT_TIMEOUT = 300;
|
|
4478
|
+
var DEFAULT_MAX_DISTANCE = 20;
|
|
4479
|
+
var DoubleTapDetector = class {
|
|
4480
|
+
timeout;
|
|
4481
|
+
maxDistance;
|
|
4482
|
+
lastTapTime = 0;
|
|
4483
|
+
lastTapX = 0;
|
|
4484
|
+
lastTapY = 0;
|
|
4485
|
+
hasPendingTap = false;
|
|
4486
|
+
constructor(options) {
|
|
4487
|
+
this.timeout = options?.timeout ?? DEFAULT_TIMEOUT;
|
|
4488
|
+
this.maxDistance = options?.maxDistance ?? DEFAULT_MAX_DISTANCE;
|
|
4489
|
+
}
|
|
4490
|
+
feed(e) {
|
|
4491
|
+
const now = Date.now();
|
|
4492
|
+
const x = e.clientX;
|
|
4493
|
+
const y = e.clientY;
|
|
4494
|
+
if (this.hasPendingTap) {
|
|
4495
|
+
const elapsed = now - this.lastTapTime;
|
|
4496
|
+
const dx = x - this.lastTapX;
|
|
4497
|
+
const dy = y - this.lastTapY;
|
|
4498
|
+
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
4499
|
+
if (elapsed <= this.timeout && dist <= this.maxDistance) {
|
|
4500
|
+
this.reset();
|
|
4501
|
+
return true;
|
|
4502
|
+
}
|
|
4503
|
+
}
|
|
4504
|
+
this.lastTapTime = now;
|
|
4505
|
+
this.lastTapX = x;
|
|
4506
|
+
this.lastTapY = y;
|
|
4507
|
+
this.hasPendingTap = true;
|
|
4508
|
+
return false;
|
|
4509
|
+
}
|
|
4510
|
+
reset() {
|
|
4511
|
+
this.hasPendingTap = false;
|
|
4512
|
+
this.lastTapTime = 0;
|
|
4513
|
+
this.lastTapX = 0;
|
|
4514
|
+
this.lastTapY = 0;
|
|
4515
|
+
}
|
|
4516
|
+
};
|
|
4517
|
+
|
|
4594
4518
|
// src/canvas/dom-node-manager.ts
|
|
4595
4519
|
var DomNodeManager = class {
|
|
4596
4520
|
domNodes = /* @__PURE__ */ new Map();
|
|
@@ -7592,53 +7516,33 @@ var TemplateTool = class {
|
|
|
7592
7516
|
};
|
|
7593
7517
|
|
|
7594
7518
|
// src/index.ts
|
|
7595
|
-
var VERSION = "0.
|
|
7519
|
+
var VERSION = "0.25.0";
|
|
7596
7520
|
// Annotate the CommonJS export names for ESM import in node:
|
|
7597
7521
|
0 && (module.exports = {
|
|
7598
|
-
AddElementCommand,
|
|
7599
7522
|
ArrowTool,
|
|
7600
7523
|
AutoSave,
|
|
7601
|
-
Background,
|
|
7602
|
-
BatchCommand,
|
|
7603
7524
|
Camera,
|
|
7604
|
-
CreateLayerCommand,
|
|
7605
|
-
DEFAULT_FONT_SIZE_PRESETS,
|
|
7606
7525
|
DEFAULT_NOTE_FONT_SIZE,
|
|
7607
|
-
DoubleTapDetector,
|
|
7608
|
-
ElementRenderer,
|
|
7609
7526
|
ElementStore,
|
|
7610
7527
|
EraserTool,
|
|
7611
|
-
EventBus,
|
|
7612
7528
|
HandTool,
|
|
7613
|
-
HistoryRecorder,
|
|
7614
7529
|
HistoryStack,
|
|
7615
7530
|
ImageTool,
|
|
7616
|
-
InputFilter,
|
|
7617
|
-
InputHandler,
|
|
7618
7531
|
LayerManager,
|
|
7619
7532
|
MeasureTool,
|
|
7620
|
-
NoteEditor,
|
|
7621
7533
|
NoteTool,
|
|
7622
|
-
NoteToolbar,
|
|
7623
7534
|
PencilTool,
|
|
7624
|
-
Quadtree,
|
|
7625
|
-
RemoveElementCommand,
|
|
7626
|
-
RemoveLayerCommand,
|
|
7627
7535
|
SelectTool,
|
|
7628
7536
|
ShapeTool,
|
|
7629
7537
|
TemplateTool,
|
|
7630
7538
|
TextTool,
|
|
7631
7539
|
ToolManager,
|
|
7632
|
-
UpdateElementCommand,
|
|
7633
|
-
UpdateLayerCommand,
|
|
7634
7540
|
VERSION,
|
|
7635
7541
|
Viewport,
|
|
7636
7542
|
boundsIntersect,
|
|
7637
|
-
clearStaleBindings,
|
|
7638
7543
|
createArrow,
|
|
7639
7544
|
createGrid,
|
|
7640
7545
|
createHtmlElement,
|
|
7641
|
-
createId,
|
|
7642
7546
|
createImage,
|
|
7643
7547
|
createNote,
|
|
7644
7548
|
createShape,
|
|
@@ -7647,29 +7551,20 @@ var VERSION = "0.24.0";
|
|
|
7647
7551
|
createText,
|
|
7648
7552
|
drawHexPath,
|
|
7649
7553
|
exportImage,
|
|
7650
|
-
exportState,
|
|
7651
|
-
findBindTarget,
|
|
7652
|
-
findBoundArrows,
|
|
7653
7554
|
getActiveFormats,
|
|
7654
7555
|
getArrowBounds,
|
|
7655
7556
|
getArrowControlPoint,
|
|
7656
7557
|
getArrowMidpoint,
|
|
7657
7558
|
getArrowTangentAngle,
|
|
7658
7559
|
getBendFromPoint,
|
|
7659
|
-
getEdgeIntersection,
|
|
7660
7560
|
getElementBounds,
|
|
7661
|
-
getElementCenter,
|
|
7662
7561
|
getElementsBoundingBox,
|
|
7663
7562
|
getHexCellsInCone,
|
|
7664
7563
|
getHexCellsInLine,
|
|
7665
7564
|
getHexCellsInRadius,
|
|
7666
7565
|
getHexCellsInSquare,
|
|
7667
7566
|
getHexDistance,
|
|
7668
|
-
isBindable,
|
|
7669
7567
|
isNearBezier,
|
|
7670
|
-
isNoteContentEmpty,
|
|
7671
|
-
parseState,
|
|
7672
|
-
sanitizeNoteHtml,
|
|
7673
7568
|
setFontSize,
|
|
7674
7569
|
smartSnap,
|
|
7675
7570
|
snapPoint,
|
|
@@ -7677,8 +7572,6 @@ var VERSION = "0.24.0";
|
|
|
7677
7572
|
toggleBold,
|
|
7678
7573
|
toggleItalic,
|
|
7679
7574
|
toggleStrikethrough,
|
|
7680
|
-
toggleUnderline
|
|
7681
|
-
unbindArrow,
|
|
7682
|
-
updateBoundArrow
|
|
7575
|
+
toggleUnderline
|
|
7683
7576
|
});
|
|
7684
7577
|
//# sourceMappingURL=index.cjs.map
|