@fieldnotes/core 0.12.0 → 0.14.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/dist/index.cjs +147 -31
- package/dist/index.d.cts +32 -4
- package/dist/index.d.ts +32 -4
- package/dist/index.js +145 -31
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -29,6 +29,7 @@ __export(index_exports, {
|
|
|
29
29
|
CreateLayerCommand: () => CreateLayerCommand,
|
|
30
30
|
DEFAULT_FONT_SIZE_PRESETS: () => DEFAULT_FONT_SIZE_PRESETS,
|
|
31
31
|
DEFAULT_NOTE_FONT_SIZE: () => DEFAULT_NOTE_FONT_SIZE,
|
|
32
|
+
DoubleTapDetector: () => DoubleTapDetector,
|
|
32
33
|
ElementRenderer: () => ElementRenderer,
|
|
33
34
|
ElementStore: () => ElementStore,
|
|
34
35
|
EraserTool: () => EraserTool,
|
|
@@ -83,6 +84,7 @@ __export(index_exports, {
|
|
|
83
84
|
getEdgeIntersection: () => getEdgeIntersection,
|
|
84
85
|
getElementBounds: () => getElementBounds,
|
|
85
86
|
getElementCenter: () => getElementCenter,
|
|
87
|
+
getElementsBoundingBox: () => getElementsBoundingBox,
|
|
86
88
|
getHexCellsInCone: () => getHexCellsInCone,
|
|
87
89
|
getHexCellsInLine: () => getHexCellsInLine,
|
|
88
90
|
getHexCellsInRadius: () => getHexCellsInRadius,
|
|
@@ -392,8 +394,8 @@ function sanitizeAttributes(el, tag) {
|
|
|
392
394
|
|
|
393
395
|
// src/core/state-serializer.ts
|
|
394
396
|
var CURRENT_VERSION = 2;
|
|
395
|
-
function exportState(elements, camera, layers = []) {
|
|
396
|
-
|
|
397
|
+
function exportState(elements, camera, layers = [], activeLayerId) {
|
|
398
|
+
const state = {
|
|
397
399
|
version: CURRENT_VERSION,
|
|
398
400
|
camera: {
|
|
399
401
|
position: { ...camera.position },
|
|
@@ -408,6 +410,8 @@ function exportState(elements, camera, layers = []) {
|
|
|
408
410
|
}),
|
|
409
411
|
layers: layers.map((l) => ({ ...l }))
|
|
410
412
|
};
|
|
413
|
+
if (activeLayerId) state.activeLayerId = activeLayerId;
|
|
414
|
+
return state;
|
|
411
415
|
}
|
|
412
416
|
function parseState(json) {
|
|
413
417
|
const data = JSON.parse(json);
|
|
@@ -565,12 +569,14 @@ var AutoSave = class {
|
|
|
565
569
|
this.key = options.key ?? DEFAULT_KEY;
|
|
566
570
|
this.debounceMs = options.debounceMs ?? DEFAULT_DEBOUNCE_MS;
|
|
567
571
|
this.layerManager = options.layerManager;
|
|
572
|
+
this.onError = options.onError;
|
|
568
573
|
}
|
|
569
574
|
key;
|
|
570
575
|
debounceMs;
|
|
571
576
|
layerManager;
|
|
572
577
|
timerId = null;
|
|
573
578
|
unsubscribers = [];
|
|
579
|
+
onError;
|
|
574
580
|
start() {
|
|
575
581
|
const schedule = () => this.scheduleSave();
|
|
576
582
|
this.unsubscribers = [
|
|
@@ -618,8 +624,9 @@ var AutoSave = class {
|
|
|
618
624
|
const state = exportState(this.store.snapshot(), this.camera, layers);
|
|
619
625
|
try {
|
|
620
626
|
localStorage.setItem(this.key, JSON.stringify(state));
|
|
621
|
-
} catch {
|
|
627
|
+
} catch (e) {
|
|
622
628
|
console.warn("Auto-save failed: storage quota exceeded. State too large for localStorage.");
|
|
629
|
+
this.onError?.(e instanceof Error ? e : new Error(String(e)));
|
|
623
630
|
}
|
|
624
631
|
}
|
|
625
632
|
};
|
|
@@ -688,6 +695,15 @@ var Camera = class {
|
|
|
688
695
|
h: bottomRight.y - topLeft.y
|
|
689
696
|
};
|
|
690
697
|
}
|
|
698
|
+
fitToContent(boundingBox, canvasWidth, canvasHeight, padding = 40) {
|
|
699
|
+
if (boundingBox.w === 0 && boundingBox.h === 0) return;
|
|
700
|
+
const scaleX = canvasWidth / (boundingBox.w + 2 * padding);
|
|
701
|
+
const scaleY = canvasHeight / (boundingBox.h + 2 * padding);
|
|
702
|
+
this.z = Math.min(this.maxZoom, Math.max(this.minZoom, Math.min(scaleX, scaleY)));
|
|
703
|
+
this.x = (canvasWidth - boundingBox.w * this.z) / 2 - boundingBox.x * this.z;
|
|
704
|
+
this.y = (canvasHeight - boundingBox.h * this.z) / 2 - boundingBox.y * this.z;
|
|
705
|
+
this.notifyPanAndZoom();
|
|
706
|
+
}
|
|
691
707
|
toCSSTransform() {
|
|
692
708
|
return `translate3d(${this.x}px, ${this.y}px, 0) scale(${this.z})`;
|
|
693
709
|
}
|
|
@@ -1005,7 +1021,9 @@ var InputHandler = class {
|
|
|
1005
1021
|
this.camera.pan(dx, dy);
|
|
1006
1022
|
return;
|
|
1007
1023
|
}
|
|
1008
|
-
if (this.
|
|
1024
|
+
if (e.pointerType === "pen" && !this.activePointers.has(e.pointerId)) {
|
|
1025
|
+
this.dispatchToolHover(e);
|
|
1026
|
+
} else if (this.isToolActive) {
|
|
1009
1027
|
this.dispatchToolMove(e);
|
|
1010
1028
|
} else if (this.deferredDown) {
|
|
1011
1029
|
const result = this.inputFilter.filterMove(e);
|
|
@@ -1251,6 +1269,48 @@ var InputHandler = class {
|
|
|
1251
1269
|
}
|
|
1252
1270
|
};
|
|
1253
1271
|
|
|
1272
|
+
// src/canvas/double-tap-detector.ts
|
|
1273
|
+
var DEFAULT_TIMEOUT = 300;
|
|
1274
|
+
var DEFAULT_MAX_DISTANCE = 20;
|
|
1275
|
+
var DoubleTapDetector = class {
|
|
1276
|
+
timeout;
|
|
1277
|
+
maxDistance;
|
|
1278
|
+
lastTapTime = 0;
|
|
1279
|
+
lastTapX = 0;
|
|
1280
|
+
lastTapY = 0;
|
|
1281
|
+
hasPendingTap = false;
|
|
1282
|
+
constructor(options) {
|
|
1283
|
+
this.timeout = options?.timeout ?? DEFAULT_TIMEOUT;
|
|
1284
|
+
this.maxDistance = options?.maxDistance ?? DEFAULT_MAX_DISTANCE;
|
|
1285
|
+
}
|
|
1286
|
+
feed(e) {
|
|
1287
|
+
const now = Date.now();
|
|
1288
|
+
const x = e.clientX;
|
|
1289
|
+
const y = e.clientY;
|
|
1290
|
+
if (this.hasPendingTap) {
|
|
1291
|
+
const elapsed = now - this.lastTapTime;
|
|
1292
|
+
const dx = x - this.lastTapX;
|
|
1293
|
+
const dy = y - this.lastTapY;
|
|
1294
|
+
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
1295
|
+
if (elapsed <= this.timeout && dist <= this.maxDistance) {
|
|
1296
|
+
this.reset();
|
|
1297
|
+
return true;
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
this.lastTapTime = now;
|
|
1301
|
+
this.lastTapX = x;
|
|
1302
|
+
this.lastTapY = y;
|
|
1303
|
+
this.hasPendingTap = true;
|
|
1304
|
+
return false;
|
|
1305
|
+
}
|
|
1306
|
+
reset() {
|
|
1307
|
+
this.hasPendingTap = false;
|
|
1308
|
+
this.lastTapTime = 0;
|
|
1309
|
+
this.lastTapX = 0;
|
|
1310
|
+
this.lastTapY = 0;
|
|
1311
|
+
}
|
|
1312
|
+
};
|
|
1313
|
+
|
|
1254
1314
|
// src/elements/arrow-geometry.ts
|
|
1255
1315
|
function getArrowControlPoint(from, to, bend) {
|
|
1256
1316
|
const midX = (from.x + to.x) / 2;
|
|
@@ -2700,6 +2760,7 @@ function createHtmlElement(input) {
|
|
|
2700
2760
|
size: input.size
|
|
2701
2761
|
};
|
|
2702
2762
|
if (input.domId) el.domId = input.domId;
|
|
2763
|
+
if (input.interactive) el.interactive = input.interactive;
|
|
2703
2764
|
return el;
|
|
2704
2765
|
}
|
|
2705
2766
|
function createShape(input) {
|
|
@@ -2812,7 +2873,7 @@ function getActiveFormats() {
|
|
|
2812
2873
|
}
|
|
2813
2874
|
|
|
2814
2875
|
// src/elements/note-toolbar.ts
|
|
2815
|
-
var TOOLBAR_HEIGHT =
|
|
2876
|
+
var TOOLBAR_HEIGHT = 52;
|
|
2816
2877
|
var TOOLBAR_GAP = 4;
|
|
2817
2878
|
var FORMAT_BUTTONS = [
|
|
2818
2879
|
{ label: "B", format: "bold", command: "bold" },
|
|
@@ -2899,9 +2960,9 @@ var NoteToolbar = class {
|
|
|
2899
2960
|
fontWeight: config.format === "bold" ? "bold" : "normal",
|
|
2900
2961
|
fontStyle: config.format === "italic" ? "italic" : "normal",
|
|
2901
2962
|
textDecoration: config.format === "underline" ? "underline" : config.format === "strikethrough" ? "line-through" : "none",
|
|
2902
|
-
minWidth: "
|
|
2903
|
-
height: "
|
|
2904
|
-
lineHeight: "
|
|
2963
|
+
minWidth: "44px",
|
|
2964
|
+
height: "44px",
|
|
2965
|
+
lineHeight: "44px"
|
|
2905
2966
|
});
|
|
2906
2967
|
btn.addEventListener("pointerdown", (e) => {
|
|
2907
2968
|
e.preventDefault();
|
|
@@ -2919,7 +2980,7 @@ var NoteToolbar = class {
|
|
|
2919
2980
|
cursor: "pointer",
|
|
2920
2981
|
padding: "2px",
|
|
2921
2982
|
fontSize: "12px",
|
|
2922
|
-
height: "
|
|
2983
|
+
height: "44px",
|
|
2923
2984
|
marginLeft: "4px"
|
|
2924
2985
|
});
|
|
2925
2986
|
for (const preset of this.fontSizePresets) {
|
|
@@ -3917,10 +3978,13 @@ var DomNodeManager = class {
|
|
|
3917
3978
|
wordWrap: "break-word"
|
|
3918
3979
|
});
|
|
3919
3980
|
node.innerHTML = element.text || "";
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3981
|
+
const detector = new DoubleTapDetector();
|
|
3982
|
+
node.addEventListener("pointerup", (e) => {
|
|
3983
|
+
if (detector.feed(e)) {
|
|
3984
|
+
e.stopPropagation();
|
|
3985
|
+
const id = node.dataset["elementId"];
|
|
3986
|
+
if (id) this.onEditRequest(id);
|
|
3987
|
+
}
|
|
3924
3988
|
});
|
|
3925
3989
|
}
|
|
3926
3990
|
if (!this.isEditingElement(element.id)) {
|
|
@@ -3933,15 +3997,19 @@ var DomNodeManager = class {
|
|
|
3933
3997
|
node.style.fontSize = `${element.fontSize ?? DEFAULT_NOTE_FONT_SIZE}px`;
|
|
3934
3998
|
}
|
|
3935
3999
|
}
|
|
3936
|
-
if (element.type === "html"
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
4000
|
+
if (element.type === "html") {
|
|
4001
|
+
if (!node.dataset["initialized"]) {
|
|
4002
|
+
const content = this.htmlContent.get(element.id);
|
|
4003
|
+
if (content) {
|
|
4004
|
+
node.dataset["initialized"] = "true";
|
|
4005
|
+
Object.assign(node.style, {
|
|
4006
|
+
overflow: "hidden",
|
|
4007
|
+
pointerEvents: element.interactive ? "auto" : "none"
|
|
4008
|
+
});
|
|
4009
|
+
node.appendChild(content);
|
|
4010
|
+
}
|
|
4011
|
+
} else {
|
|
4012
|
+
node.style.pointerEvents = element.interactive ? "auto" : "none";
|
|
3945
4013
|
}
|
|
3946
4014
|
}
|
|
3947
4015
|
if (element.type === "text") {
|
|
@@ -3963,10 +4031,13 @@ var DomNodeManager = class {
|
|
|
3963
4031
|
lineHeight: "1.4"
|
|
3964
4032
|
});
|
|
3965
4033
|
node.textContent = element.text || "";
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
4034
|
+
const detector = new DoubleTapDetector();
|
|
4035
|
+
node.addEventListener("pointerup", (e) => {
|
|
4036
|
+
if (detector.feed(e)) {
|
|
4037
|
+
e.stopPropagation();
|
|
4038
|
+
const id = node.dataset["elementId"];
|
|
4039
|
+
if (id) this.onEditRequest(id);
|
|
4040
|
+
}
|
|
3970
4041
|
});
|
|
3971
4042
|
}
|
|
3972
4043
|
if (!this.isEditingElement(element.id)) {
|
|
@@ -4455,7 +4526,8 @@ var Viewport = class {
|
|
|
4455
4526
|
this.toolContext.activeLayerId = this.layerManager.activeLayerId;
|
|
4456
4527
|
this.requestRender();
|
|
4457
4528
|
});
|
|
4458
|
-
this.wrapper.addEventListener("
|
|
4529
|
+
this.wrapper.addEventListener("pointerdown", this.onTapDown);
|
|
4530
|
+
this.wrapper.addEventListener("pointerup", this.onDoubleTap);
|
|
4459
4531
|
this.wrapper.addEventListener("dragover", this.onDragOver);
|
|
4460
4532
|
this.wrapper.addEventListener("drop", this.onDrop);
|
|
4461
4533
|
this.observeResize();
|
|
@@ -4486,6 +4558,9 @@ var Viewport = class {
|
|
|
4486
4558
|
domNodeManager;
|
|
4487
4559
|
interactMode;
|
|
4488
4560
|
gridChangeListeners = /* @__PURE__ */ new Set();
|
|
4561
|
+
doubleTapDetector = new DoubleTapDetector();
|
|
4562
|
+
tapDownX = 0;
|
|
4563
|
+
tapDownY = 0;
|
|
4489
4564
|
get ctx() {
|
|
4490
4565
|
return this.canvasEl.getContext("2d");
|
|
4491
4566
|
}
|
|
@@ -4500,7 +4575,12 @@ var Viewport = class {
|
|
|
4500
4575
|
this.renderLoop.requestRender();
|
|
4501
4576
|
}
|
|
4502
4577
|
exportState() {
|
|
4503
|
-
return exportState(
|
|
4578
|
+
return exportState(
|
|
4579
|
+
this.store.snapshot(),
|
|
4580
|
+
this.camera,
|
|
4581
|
+
this.layerManager.snapshot(),
|
|
4582
|
+
this.layerManager.activeLayerId
|
|
4583
|
+
);
|
|
4504
4584
|
}
|
|
4505
4585
|
exportJSON() {
|
|
4506
4586
|
return JSON.stringify(this.exportState());
|
|
@@ -4516,6 +4596,9 @@ var Viewport = class {
|
|
|
4516
4596
|
if (state.layers && state.layers.length > 0) {
|
|
4517
4597
|
this.layerManager.loadSnapshot(state.layers);
|
|
4518
4598
|
}
|
|
4599
|
+
if (state.activeLayerId) {
|
|
4600
|
+
this.layerManager.setActiveLayer(state.activeLayerId);
|
|
4601
|
+
}
|
|
4519
4602
|
this.domNodeManager.reattachHtmlContent(this.store);
|
|
4520
4603
|
this.history.clear();
|
|
4521
4604
|
this.historyRecorder.resume();
|
|
@@ -4623,7 +4706,8 @@ var Viewport = class {
|
|
|
4623
4706
|
this.interactMode.destroy();
|
|
4624
4707
|
this.noteEditor.destroy(this.store);
|
|
4625
4708
|
this.historyRecorder.destroy();
|
|
4626
|
-
this.wrapper.removeEventListener("
|
|
4709
|
+
this.wrapper.removeEventListener("pointerdown", this.onTapDown);
|
|
4710
|
+
this.wrapper.removeEventListener("pointerup", this.onDoubleTap);
|
|
4627
4711
|
this.wrapper.removeEventListener("dragover", this.onDragOver);
|
|
4628
4712
|
this.wrapper.removeEventListener("drop", this.onDrop);
|
|
4629
4713
|
this.inputHandler.destroy();
|
|
@@ -4661,7 +4745,17 @@ var Viewport = class {
|
|
|
4661
4745
|
}
|
|
4662
4746
|
}
|
|
4663
4747
|
}
|
|
4664
|
-
|
|
4748
|
+
onTapDown = (e) => {
|
|
4749
|
+
this.tapDownX = e.clientX;
|
|
4750
|
+
this.tapDownY = e.clientY;
|
|
4751
|
+
};
|
|
4752
|
+
onDoubleTap = (e) => {
|
|
4753
|
+
const dx = e.clientX - this.tapDownX;
|
|
4754
|
+
const dy = e.clientY - this.tapDownY;
|
|
4755
|
+
const moved = Math.sqrt(dx * dx + dy * dy);
|
|
4756
|
+
if (moved > 10) return;
|
|
4757
|
+
if (!this.doubleTapDetector.feed(e)) return;
|
|
4758
|
+
if (typeof document.elementFromPoint !== "function") return;
|
|
4665
4759
|
const el = document.elementFromPoint(e.clientX, e.clientY);
|
|
4666
4760
|
const nodeEl = el?.closest("[data-element-id]");
|
|
4667
4761
|
if (nodeEl) {
|
|
@@ -4824,6 +4918,26 @@ var Viewport = class {
|
|
|
4824
4918
|
}
|
|
4825
4919
|
};
|
|
4826
4920
|
|
|
4921
|
+
// src/elements/bounds.ts
|
|
4922
|
+
function getElementsBoundingBox(elements) {
|
|
4923
|
+
let minX = Infinity;
|
|
4924
|
+
let minY = Infinity;
|
|
4925
|
+
let maxX = -Infinity;
|
|
4926
|
+
let maxY = -Infinity;
|
|
4927
|
+
let found = false;
|
|
4928
|
+
for (const el of elements) {
|
|
4929
|
+
const b = getElementBounds(el);
|
|
4930
|
+
if (!b) continue;
|
|
4931
|
+
found = true;
|
|
4932
|
+
if (b.x < minX) minX = b.x;
|
|
4933
|
+
if (b.y < minY) minY = b.y;
|
|
4934
|
+
if (b.x + b.w > maxX) maxX = b.x + b.w;
|
|
4935
|
+
if (b.y + b.h > maxY) maxY = b.y + b.h;
|
|
4936
|
+
}
|
|
4937
|
+
if (!found) return null;
|
|
4938
|
+
return { x: minX, y: minY, w: maxX - minX, h: maxY - minY };
|
|
4939
|
+
}
|
|
4940
|
+
|
|
4827
4941
|
// src/tools/hand-tool.ts
|
|
4828
4942
|
var HandTool = class {
|
|
4829
4943
|
name = "hand";
|
|
@@ -6533,7 +6647,7 @@ var UpdateLayerCommand = class {
|
|
|
6533
6647
|
};
|
|
6534
6648
|
|
|
6535
6649
|
// src/index.ts
|
|
6536
|
-
var VERSION = "0.
|
|
6650
|
+
var VERSION = "0.14.0";
|
|
6537
6651
|
// Annotate the CommonJS export names for ESM import in node:
|
|
6538
6652
|
0 && (module.exports = {
|
|
6539
6653
|
AddElementCommand,
|
|
@@ -6545,6 +6659,7 @@ var VERSION = "0.12.0";
|
|
|
6545
6659
|
CreateLayerCommand,
|
|
6546
6660
|
DEFAULT_FONT_SIZE_PRESETS,
|
|
6547
6661
|
DEFAULT_NOTE_FONT_SIZE,
|
|
6662
|
+
DoubleTapDetector,
|
|
6548
6663
|
ElementRenderer,
|
|
6549
6664
|
ElementStore,
|
|
6550
6665
|
EraserTool,
|
|
@@ -6599,6 +6714,7 @@ var VERSION = "0.12.0";
|
|
|
6599
6714
|
getEdgeIntersection,
|
|
6600
6715
|
getElementBounds,
|
|
6601
6716
|
getElementCenter,
|
|
6717
|
+
getElementsBoundingBox,
|
|
6602
6718
|
getHexCellsInCone,
|
|
6603
6719
|
getHexCellsInLine,
|
|
6604
6720
|
getHexCellsInRadius,
|
package/dist/index.d.cts
CHANGED
|
@@ -85,6 +85,7 @@ interface HtmlElement extends BaseElement {
|
|
|
85
85
|
type: 'html';
|
|
86
86
|
size: Size;
|
|
87
87
|
domId?: string;
|
|
88
|
+
interactive?: boolean;
|
|
88
89
|
}
|
|
89
90
|
interface TextElement extends BaseElement {
|
|
90
91
|
type: 'text';
|
|
@@ -146,11 +147,12 @@ interface CanvasState {
|
|
|
146
147
|
};
|
|
147
148
|
elements: CanvasElement[];
|
|
148
149
|
layers?: Layer[];
|
|
150
|
+
activeLayerId?: string;
|
|
149
151
|
}
|
|
150
152
|
declare function exportState(elements: CanvasElement[], camera: {
|
|
151
153
|
position: Point;
|
|
152
154
|
zoom: number;
|
|
153
|
-
}, layers?: Layer[]): CanvasState;
|
|
155
|
+
}, layers?: Layer[], activeLayerId?: string): CanvasState;
|
|
154
156
|
declare function parseState(json: string): CanvasState;
|
|
155
157
|
|
|
156
158
|
interface CameraOptions {
|
|
@@ -178,6 +180,7 @@ declare class Camera {
|
|
|
178
180
|
screenToWorld(screen: Point): Point;
|
|
179
181
|
worldToScreen(world: Point): Point;
|
|
180
182
|
getVisibleRect(canvasWidth: number, canvasHeight: number): Bounds;
|
|
183
|
+
fitToContent(boundingBox: Bounds, canvasWidth: number, canvasHeight: number, padding?: number): void;
|
|
181
184
|
toCSSTransform(): string;
|
|
182
185
|
onChange(listener: (info: CameraChangeInfo) => void): () => void;
|
|
183
186
|
private notifyPan;
|
|
@@ -296,6 +299,7 @@ interface AutoSaveOptions {
|
|
|
296
299
|
key?: string;
|
|
297
300
|
debounceMs?: number;
|
|
298
301
|
layerManager?: LayerManager;
|
|
302
|
+
onError?: (error: Error) => void;
|
|
299
303
|
}
|
|
300
304
|
declare class AutoSave {
|
|
301
305
|
private readonly store;
|
|
@@ -305,6 +309,7 @@ declare class AutoSave {
|
|
|
305
309
|
private readonly layerManager?;
|
|
306
310
|
private timerId;
|
|
307
311
|
private unsubscribers;
|
|
312
|
+
private readonly onError?;
|
|
308
313
|
constructor(store: ElementStore, camera: Camera, options?: AutoSaveOptions);
|
|
309
314
|
start(): void;
|
|
310
315
|
stop(): void;
|
|
@@ -481,6 +486,22 @@ declare class InputFilter {
|
|
|
481
486
|
reset(): void;
|
|
482
487
|
}
|
|
483
488
|
|
|
489
|
+
interface DoubleTapDetectorOptions {
|
|
490
|
+
timeout?: number;
|
|
491
|
+
maxDistance?: number;
|
|
492
|
+
}
|
|
493
|
+
declare class DoubleTapDetector {
|
|
494
|
+
private readonly timeout;
|
|
495
|
+
private readonly maxDistance;
|
|
496
|
+
private lastTapTime;
|
|
497
|
+
private lastTapX;
|
|
498
|
+
private lastTapY;
|
|
499
|
+
private hasPendingTap;
|
|
500
|
+
constructor(options?: DoubleTapDetectorOptions);
|
|
501
|
+
feed(e: PointerEvent): boolean;
|
|
502
|
+
reset(): void;
|
|
503
|
+
}
|
|
504
|
+
|
|
484
505
|
interface FontSizePreset {
|
|
485
506
|
label: string;
|
|
486
507
|
size: number;
|
|
@@ -557,6 +578,9 @@ declare class Viewport {
|
|
|
557
578
|
private readonly domNodeManager;
|
|
558
579
|
private readonly interactMode;
|
|
559
580
|
private readonly gridChangeListeners;
|
|
581
|
+
private readonly doubleTapDetector;
|
|
582
|
+
private tapDownX;
|
|
583
|
+
private tapDownY;
|
|
560
584
|
constructor(container: HTMLElement, options?: ViewportOptions);
|
|
561
585
|
get ctx(): CanvasRenderingContext2D | null;
|
|
562
586
|
get snapToGrid(): boolean;
|
|
@@ -600,7 +624,8 @@ declare class Viewport {
|
|
|
600
624
|
destroy(): void;
|
|
601
625
|
private startEditingElement;
|
|
602
626
|
private onTextEditStop;
|
|
603
|
-
private
|
|
627
|
+
private onTapDown;
|
|
628
|
+
private onDoubleTap;
|
|
604
629
|
private hitTestWorld;
|
|
605
630
|
stopInteracting(): void;
|
|
606
631
|
private onDragOver;
|
|
@@ -737,6 +762,7 @@ interface HtmlInput extends BaseDefaults {
|
|
|
737
762
|
position: Point;
|
|
738
763
|
size: Size;
|
|
739
764
|
domId?: string;
|
|
765
|
+
interactive?: boolean;
|
|
740
766
|
}
|
|
741
767
|
interface TextInput extends BaseDefaults {
|
|
742
768
|
position: Point;
|
|
@@ -803,6 +829,8 @@ declare function unbindArrow(arrow: ArrowElement, store: ElementStore): Partial<
|
|
|
803
829
|
declare function getElementBounds(element: CanvasElement): Bounds | null;
|
|
804
830
|
declare function boundsIntersect(a: Bounds, b: Bounds): boolean;
|
|
805
831
|
|
|
832
|
+
declare function getElementsBoundingBox(elements: CanvasElement[]): Bounds | null;
|
|
833
|
+
|
|
806
834
|
declare function getHexDistance(a: Point, b: Point, cellSize: number, orientation: HexOrientation): number;
|
|
807
835
|
declare function getHexCellsInRadius(center: Point, radiusCells: number, cellSize: number, orientation: HexOrientation): Point[];
|
|
808
836
|
declare function getHexCellsInCone(center: Point, angle: number, radiusCells: number, cellSize: number, orientation: HexOrientation): Point[];
|
|
@@ -1153,6 +1181,6 @@ declare class UpdateLayerCommand implements Command {
|
|
|
1153
1181
|
undo(_store: ElementStore): void;
|
|
1154
1182
|
}
|
|
1155
1183
|
|
|
1156
|
-
declare const VERSION = "0.
|
|
1184
|
+
declare const VERSION = "0.14.0";
|
|
1157
1185
|
|
|
1158
|
-
export { type ActiveFormats, AddElementCommand, type ArrowElement, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, Background, type BackgroundOptions, type BackgroundPattern, BatchCommand, type Binding, type Bounds, Camera, type CameraChangeInfo, type CameraOptions, type CanvasElement, type CanvasState, type Command, CreateLayerCommand, DEFAULT_FONT_SIZE_PRESETS, DEFAULT_NOTE_FONT_SIZE, ElementRenderer, ElementStore, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, EventBus, type ExportImageOptions, type FilterAction, type FilteredEvent, type FilteredUpEvent, type FontSizePreset, type GridElement, type GridInfo, HandTool, type HexOrientation, HistoryRecorder, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, InputFilter, InputHandler, type Layer, LayerManager, MeasureTool, type MeasureToolOptions, type Measurement, NoteEditor, type NoteEditorOptions, type NoteElement, NoteTool, type NoteToolOptions, NoteToolbar, PencilTool, type PencilToolOptions, type Point, type PointerState, Quadtree, RemoveElementCommand, RemoveLayerCommand, type RenderStatsSnapshot, SelectTool, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type Size, type StrokeElement, type StrokePoint, type StyledRun, type TemplateElement, type TemplateShape, TemplateTool, type TemplateToolOptions, type TextElement, TextTool, type TextToolOptions, type Tool, type ToolContext, ToolManager, type ToolName, UpdateElementCommand, UpdateLayerCommand, VERSION, Viewport, type ViewportOptions, boundsIntersect, clearStaleBindings, createArrow, createGrid, createHtmlElement, createId, createImage, createNote, createShape, createStroke, createTemplate, createText, drawHexPath, exportImage, exportState, findBindTarget, findBoundArrows, getActiveFormats, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getEdgeIntersection, getElementBounds, getElementCenter, getHexCellsInCone, getHexCellsInLine, getHexCellsInRadius, getHexCellsInSquare, getHexDistance, isBindable, isNearBezier, parseState, sanitizeNoteHtml, setFontSize, smartSnap, snapPoint, snapToHexCenter, toggleBold, toggleItalic, toggleStrikethrough, toggleUnderline, unbindArrow, updateBoundArrow };
|
|
1186
|
+
export { type ActiveFormats, AddElementCommand, type ArrowElement, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, Background, type BackgroundOptions, type BackgroundPattern, BatchCommand, type Binding, type Bounds, Camera, type CameraChangeInfo, type CameraOptions, type CanvasElement, type CanvasState, type Command, CreateLayerCommand, DEFAULT_FONT_SIZE_PRESETS, DEFAULT_NOTE_FONT_SIZE, DoubleTapDetector, type DoubleTapDetectorOptions, ElementRenderer, ElementStore, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, EventBus, type ExportImageOptions, type FilterAction, type FilteredEvent, type FilteredUpEvent, type FontSizePreset, type GridElement, type GridInfo, HandTool, type HexOrientation, HistoryRecorder, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, InputFilter, InputHandler, type Layer, LayerManager, MeasureTool, type MeasureToolOptions, type Measurement, NoteEditor, type NoteEditorOptions, type NoteElement, NoteTool, type NoteToolOptions, NoteToolbar, PencilTool, type PencilToolOptions, type Point, type PointerState, Quadtree, RemoveElementCommand, RemoveLayerCommand, type RenderStatsSnapshot, SelectTool, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type Size, type StrokeElement, type StrokePoint, type StyledRun, type TemplateElement, type TemplateShape, TemplateTool, type TemplateToolOptions, type TextElement, TextTool, type TextToolOptions, type Tool, type ToolContext, ToolManager, type ToolName, UpdateElementCommand, UpdateLayerCommand, VERSION, Viewport, type ViewportOptions, boundsIntersect, clearStaleBindings, createArrow, createGrid, createHtmlElement, createId, createImage, createNote, createShape, createStroke, createTemplate, createText, drawHexPath, exportImage, exportState, findBindTarget, findBoundArrows, getActiveFormats, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getEdgeIntersection, getElementBounds, getElementCenter, getElementsBoundingBox, getHexCellsInCone, getHexCellsInLine, getHexCellsInRadius, getHexCellsInSquare, getHexDistance, isBindable, isNearBezier, parseState, sanitizeNoteHtml, setFontSize, smartSnap, snapPoint, snapToHexCenter, toggleBold, toggleItalic, toggleStrikethrough, toggleUnderline, unbindArrow, updateBoundArrow };
|
package/dist/index.d.ts
CHANGED
|
@@ -85,6 +85,7 @@ interface HtmlElement extends BaseElement {
|
|
|
85
85
|
type: 'html';
|
|
86
86
|
size: Size;
|
|
87
87
|
domId?: string;
|
|
88
|
+
interactive?: boolean;
|
|
88
89
|
}
|
|
89
90
|
interface TextElement extends BaseElement {
|
|
90
91
|
type: 'text';
|
|
@@ -146,11 +147,12 @@ interface CanvasState {
|
|
|
146
147
|
};
|
|
147
148
|
elements: CanvasElement[];
|
|
148
149
|
layers?: Layer[];
|
|
150
|
+
activeLayerId?: string;
|
|
149
151
|
}
|
|
150
152
|
declare function exportState(elements: CanvasElement[], camera: {
|
|
151
153
|
position: Point;
|
|
152
154
|
zoom: number;
|
|
153
|
-
}, layers?: Layer[]): CanvasState;
|
|
155
|
+
}, layers?: Layer[], activeLayerId?: string): CanvasState;
|
|
154
156
|
declare function parseState(json: string): CanvasState;
|
|
155
157
|
|
|
156
158
|
interface CameraOptions {
|
|
@@ -178,6 +180,7 @@ declare class Camera {
|
|
|
178
180
|
screenToWorld(screen: Point): Point;
|
|
179
181
|
worldToScreen(world: Point): Point;
|
|
180
182
|
getVisibleRect(canvasWidth: number, canvasHeight: number): Bounds;
|
|
183
|
+
fitToContent(boundingBox: Bounds, canvasWidth: number, canvasHeight: number, padding?: number): void;
|
|
181
184
|
toCSSTransform(): string;
|
|
182
185
|
onChange(listener: (info: CameraChangeInfo) => void): () => void;
|
|
183
186
|
private notifyPan;
|
|
@@ -296,6 +299,7 @@ interface AutoSaveOptions {
|
|
|
296
299
|
key?: string;
|
|
297
300
|
debounceMs?: number;
|
|
298
301
|
layerManager?: LayerManager;
|
|
302
|
+
onError?: (error: Error) => void;
|
|
299
303
|
}
|
|
300
304
|
declare class AutoSave {
|
|
301
305
|
private readonly store;
|
|
@@ -305,6 +309,7 @@ declare class AutoSave {
|
|
|
305
309
|
private readonly layerManager?;
|
|
306
310
|
private timerId;
|
|
307
311
|
private unsubscribers;
|
|
312
|
+
private readonly onError?;
|
|
308
313
|
constructor(store: ElementStore, camera: Camera, options?: AutoSaveOptions);
|
|
309
314
|
start(): void;
|
|
310
315
|
stop(): void;
|
|
@@ -481,6 +486,22 @@ declare class InputFilter {
|
|
|
481
486
|
reset(): void;
|
|
482
487
|
}
|
|
483
488
|
|
|
489
|
+
interface DoubleTapDetectorOptions {
|
|
490
|
+
timeout?: number;
|
|
491
|
+
maxDistance?: number;
|
|
492
|
+
}
|
|
493
|
+
declare class DoubleTapDetector {
|
|
494
|
+
private readonly timeout;
|
|
495
|
+
private readonly maxDistance;
|
|
496
|
+
private lastTapTime;
|
|
497
|
+
private lastTapX;
|
|
498
|
+
private lastTapY;
|
|
499
|
+
private hasPendingTap;
|
|
500
|
+
constructor(options?: DoubleTapDetectorOptions);
|
|
501
|
+
feed(e: PointerEvent): boolean;
|
|
502
|
+
reset(): void;
|
|
503
|
+
}
|
|
504
|
+
|
|
484
505
|
interface FontSizePreset {
|
|
485
506
|
label: string;
|
|
486
507
|
size: number;
|
|
@@ -557,6 +578,9 @@ declare class Viewport {
|
|
|
557
578
|
private readonly domNodeManager;
|
|
558
579
|
private readonly interactMode;
|
|
559
580
|
private readonly gridChangeListeners;
|
|
581
|
+
private readonly doubleTapDetector;
|
|
582
|
+
private tapDownX;
|
|
583
|
+
private tapDownY;
|
|
560
584
|
constructor(container: HTMLElement, options?: ViewportOptions);
|
|
561
585
|
get ctx(): CanvasRenderingContext2D | null;
|
|
562
586
|
get snapToGrid(): boolean;
|
|
@@ -600,7 +624,8 @@ declare class Viewport {
|
|
|
600
624
|
destroy(): void;
|
|
601
625
|
private startEditingElement;
|
|
602
626
|
private onTextEditStop;
|
|
603
|
-
private
|
|
627
|
+
private onTapDown;
|
|
628
|
+
private onDoubleTap;
|
|
604
629
|
private hitTestWorld;
|
|
605
630
|
stopInteracting(): void;
|
|
606
631
|
private onDragOver;
|
|
@@ -737,6 +762,7 @@ interface HtmlInput extends BaseDefaults {
|
|
|
737
762
|
position: Point;
|
|
738
763
|
size: Size;
|
|
739
764
|
domId?: string;
|
|
765
|
+
interactive?: boolean;
|
|
740
766
|
}
|
|
741
767
|
interface TextInput extends BaseDefaults {
|
|
742
768
|
position: Point;
|
|
@@ -803,6 +829,8 @@ declare function unbindArrow(arrow: ArrowElement, store: ElementStore): Partial<
|
|
|
803
829
|
declare function getElementBounds(element: CanvasElement): Bounds | null;
|
|
804
830
|
declare function boundsIntersect(a: Bounds, b: Bounds): boolean;
|
|
805
831
|
|
|
832
|
+
declare function getElementsBoundingBox(elements: CanvasElement[]): Bounds | null;
|
|
833
|
+
|
|
806
834
|
declare function getHexDistance(a: Point, b: Point, cellSize: number, orientation: HexOrientation): number;
|
|
807
835
|
declare function getHexCellsInRadius(center: Point, radiusCells: number, cellSize: number, orientation: HexOrientation): Point[];
|
|
808
836
|
declare function getHexCellsInCone(center: Point, angle: number, radiusCells: number, cellSize: number, orientation: HexOrientation): Point[];
|
|
@@ -1153,6 +1181,6 @@ declare class UpdateLayerCommand implements Command {
|
|
|
1153
1181
|
undo(_store: ElementStore): void;
|
|
1154
1182
|
}
|
|
1155
1183
|
|
|
1156
|
-
declare const VERSION = "0.
|
|
1184
|
+
declare const VERSION = "0.14.0";
|
|
1157
1185
|
|
|
1158
|
-
export { type ActiveFormats, AddElementCommand, type ArrowElement, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, Background, type BackgroundOptions, type BackgroundPattern, BatchCommand, type Binding, type Bounds, Camera, type CameraChangeInfo, type CameraOptions, type CanvasElement, type CanvasState, type Command, CreateLayerCommand, DEFAULT_FONT_SIZE_PRESETS, DEFAULT_NOTE_FONT_SIZE, ElementRenderer, ElementStore, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, EventBus, type ExportImageOptions, type FilterAction, type FilteredEvent, type FilteredUpEvent, type FontSizePreset, type GridElement, type GridInfo, HandTool, type HexOrientation, HistoryRecorder, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, InputFilter, InputHandler, type Layer, LayerManager, MeasureTool, type MeasureToolOptions, type Measurement, NoteEditor, type NoteEditorOptions, type NoteElement, NoteTool, type NoteToolOptions, NoteToolbar, PencilTool, type PencilToolOptions, type Point, type PointerState, Quadtree, RemoveElementCommand, RemoveLayerCommand, type RenderStatsSnapshot, SelectTool, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type Size, type StrokeElement, type StrokePoint, type StyledRun, type TemplateElement, type TemplateShape, TemplateTool, type TemplateToolOptions, type TextElement, TextTool, type TextToolOptions, type Tool, type ToolContext, ToolManager, type ToolName, UpdateElementCommand, UpdateLayerCommand, VERSION, Viewport, type ViewportOptions, boundsIntersect, clearStaleBindings, createArrow, createGrid, createHtmlElement, createId, createImage, createNote, createShape, createStroke, createTemplate, createText, drawHexPath, exportImage, exportState, findBindTarget, findBoundArrows, getActiveFormats, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getEdgeIntersection, getElementBounds, getElementCenter, getHexCellsInCone, getHexCellsInLine, getHexCellsInRadius, getHexCellsInSquare, getHexDistance, isBindable, isNearBezier, parseState, sanitizeNoteHtml, setFontSize, smartSnap, snapPoint, snapToHexCenter, toggleBold, toggleItalic, toggleStrikethrough, toggleUnderline, unbindArrow, updateBoundArrow };
|
|
1186
|
+
export { type ActiveFormats, AddElementCommand, type ArrowElement, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, Background, type BackgroundOptions, type BackgroundPattern, BatchCommand, type Binding, type Bounds, Camera, type CameraChangeInfo, type CameraOptions, type CanvasElement, type CanvasState, type Command, CreateLayerCommand, DEFAULT_FONT_SIZE_PRESETS, DEFAULT_NOTE_FONT_SIZE, DoubleTapDetector, type DoubleTapDetectorOptions, ElementRenderer, ElementStore, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, EventBus, type ExportImageOptions, type FilterAction, type FilteredEvent, type FilteredUpEvent, type FontSizePreset, type GridElement, type GridInfo, HandTool, type HexOrientation, HistoryRecorder, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, InputFilter, InputHandler, type Layer, LayerManager, MeasureTool, type MeasureToolOptions, type Measurement, NoteEditor, type NoteEditorOptions, type NoteElement, NoteTool, type NoteToolOptions, NoteToolbar, PencilTool, type PencilToolOptions, type Point, type PointerState, Quadtree, RemoveElementCommand, RemoveLayerCommand, type RenderStatsSnapshot, SelectTool, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type Size, type StrokeElement, type StrokePoint, type StyledRun, type TemplateElement, type TemplateShape, TemplateTool, type TemplateToolOptions, type TextElement, TextTool, type TextToolOptions, type Tool, type ToolContext, ToolManager, type ToolName, UpdateElementCommand, UpdateLayerCommand, VERSION, Viewport, type ViewportOptions, boundsIntersect, clearStaleBindings, createArrow, createGrid, createHtmlElement, createId, createImage, createNote, createShape, createStroke, createTemplate, createText, drawHexPath, exportImage, exportState, findBindTarget, findBoundArrows, getActiveFormats, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getEdgeIntersection, getElementBounds, getElementCenter, getElementsBoundingBox, getHexCellsInCone, getHexCellsInLine, getHexCellsInRadius, getHexCellsInSquare, getHexDistance, isBindable, isNearBezier, parseState, sanitizeNoteHtml, setFontSize, smartSnap, snapPoint, snapToHexCenter, toggleBold, toggleItalic, toggleStrikethrough, toggleUnderline, unbindArrow, updateBoundArrow };
|
package/dist/index.js
CHANGED
|
@@ -285,8 +285,8 @@ function sanitizeAttributes(el, tag) {
|
|
|
285
285
|
|
|
286
286
|
// src/core/state-serializer.ts
|
|
287
287
|
var CURRENT_VERSION = 2;
|
|
288
|
-
function exportState(elements, camera, layers = []) {
|
|
289
|
-
|
|
288
|
+
function exportState(elements, camera, layers = [], activeLayerId) {
|
|
289
|
+
const state = {
|
|
290
290
|
version: CURRENT_VERSION,
|
|
291
291
|
camera: {
|
|
292
292
|
position: { ...camera.position },
|
|
@@ -301,6 +301,8 @@ function exportState(elements, camera, layers = []) {
|
|
|
301
301
|
}),
|
|
302
302
|
layers: layers.map((l) => ({ ...l }))
|
|
303
303
|
};
|
|
304
|
+
if (activeLayerId) state.activeLayerId = activeLayerId;
|
|
305
|
+
return state;
|
|
304
306
|
}
|
|
305
307
|
function parseState(json) {
|
|
306
308
|
const data = JSON.parse(json);
|
|
@@ -458,12 +460,14 @@ var AutoSave = class {
|
|
|
458
460
|
this.key = options.key ?? DEFAULT_KEY;
|
|
459
461
|
this.debounceMs = options.debounceMs ?? DEFAULT_DEBOUNCE_MS;
|
|
460
462
|
this.layerManager = options.layerManager;
|
|
463
|
+
this.onError = options.onError;
|
|
461
464
|
}
|
|
462
465
|
key;
|
|
463
466
|
debounceMs;
|
|
464
467
|
layerManager;
|
|
465
468
|
timerId = null;
|
|
466
469
|
unsubscribers = [];
|
|
470
|
+
onError;
|
|
467
471
|
start() {
|
|
468
472
|
const schedule = () => this.scheduleSave();
|
|
469
473
|
this.unsubscribers = [
|
|
@@ -511,8 +515,9 @@ var AutoSave = class {
|
|
|
511
515
|
const state = exportState(this.store.snapshot(), this.camera, layers);
|
|
512
516
|
try {
|
|
513
517
|
localStorage.setItem(this.key, JSON.stringify(state));
|
|
514
|
-
} catch {
|
|
518
|
+
} catch (e) {
|
|
515
519
|
console.warn("Auto-save failed: storage quota exceeded. State too large for localStorage.");
|
|
520
|
+
this.onError?.(e instanceof Error ? e : new Error(String(e)));
|
|
516
521
|
}
|
|
517
522
|
}
|
|
518
523
|
};
|
|
@@ -581,6 +586,15 @@ var Camera = class {
|
|
|
581
586
|
h: bottomRight.y - topLeft.y
|
|
582
587
|
};
|
|
583
588
|
}
|
|
589
|
+
fitToContent(boundingBox, canvasWidth, canvasHeight, padding = 40) {
|
|
590
|
+
if (boundingBox.w === 0 && boundingBox.h === 0) return;
|
|
591
|
+
const scaleX = canvasWidth / (boundingBox.w + 2 * padding);
|
|
592
|
+
const scaleY = canvasHeight / (boundingBox.h + 2 * padding);
|
|
593
|
+
this.z = Math.min(this.maxZoom, Math.max(this.minZoom, Math.min(scaleX, scaleY)));
|
|
594
|
+
this.x = (canvasWidth - boundingBox.w * this.z) / 2 - boundingBox.x * this.z;
|
|
595
|
+
this.y = (canvasHeight - boundingBox.h * this.z) / 2 - boundingBox.y * this.z;
|
|
596
|
+
this.notifyPanAndZoom();
|
|
597
|
+
}
|
|
584
598
|
toCSSTransform() {
|
|
585
599
|
return `translate3d(${this.x}px, ${this.y}px, 0) scale(${this.z})`;
|
|
586
600
|
}
|
|
@@ -898,7 +912,9 @@ var InputHandler = class {
|
|
|
898
912
|
this.camera.pan(dx, dy);
|
|
899
913
|
return;
|
|
900
914
|
}
|
|
901
|
-
if (this.
|
|
915
|
+
if (e.pointerType === "pen" && !this.activePointers.has(e.pointerId)) {
|
|
916
|
+
this.dispatchToolHover(e);
|
|
917
|
+
} else if (this.isToolActive) {
|
|
902
918
|
this.dispatchToolMove(e);
|
|
903
919
|
} else if (this.deferredDown) {
|
|
904
920
|
const result = this.inputFilter.filterMove(e);
|
|
@@ -1144,6 +1160,48 @@ var InputHandler = class {
|
|
|
1144
1160
|
}
|
|
1145
1161
|
};
|
|
1146
1162
|
|
|
1163
|
+
// src/canvas/double-tap-detector.ts
|
|
1164
|
+
var DEFAULT_TIMEOUT = 300;
|
|
1165
|
+
var DEFAULT_MAX_DISTANCE = 20;
|
|
1166
|
+
var DoubleTapDetector = class {
|
|
1167
|
+
timeout;
|
|
1168
|
+
maxDistance;
|
|
1169
|
+
lastTapTime = 0;
|
|
1170
|
+
lastTapX = 0;
|
|
1171
|
+
lastTapY = 0;
|
|
1172
|
+
hasPendingTap = false;
|
|
1173
|
+
constructor(options) {
|
|
1174
|
+
this.timeout = options?.timeout ?? DEFAULT_TIMEOUT;
|
|
1175
|
+
this.maxDistance = options?.maxDistance ?? DEFAULT_MAX_DISTANCE;
|
|
1176
|
+
}
|
|
1177
|
+
feed(e) {
|
|
1178
|
+
const now = Date.now();
|
|
1179
|
+
const x = e.clientX;
|
|
1180
|
+
const y = e.clientY;
|
|
1181
|
+
if (this.hasPendingTap) {
|
|
1182
|
+
const elapsed = now - this.lastTapTime;
|
|
1183
|
+
const dx = x - this.lastTapX;
|
|
1184
|
+
const dy = y - this.lastTapY;
|
|
1185
|
+
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
1186
|
+
if (elapsed <= this.timeout && dist <= this.maxDistance) {
|
|
1187
|
+
this.reset();
|
|
1188
|
+
return true;
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
this.lastTapTime = now;
|
|
1192
|
+
this.lastTapX = x;
|
|
1193
|
+
this.lastTapY = y;
|
|
1194
|
+
this.hasPendingTap = true;
|
|
1195
|
+
return false;
|
|
1196
|
+
}
|
|
1197
|
+
reset() {
|
|
1198
|
+
this.hasPendingTap = false;
|
|
1199
|
+
this.lastTapTime = 0;
|
|
1200
|
+
this.lastTapX = 0;
|
|
1201
|
+
this.lastTapY = 0;
|
|
1202
|
+
}
|
|
1203
|
+
};
|
|
1204
|
+
|
|
1147
1205
|
// src/elements/arrow-geometry.ts
|
|
1148
1206
|
function getArrowControlPoint(from, to, bend) {
|
|
1149
1207
|
const midX = (from.x + to.x) / 2;
|
|
@@ -2593,6 +2651,7 @@ function createHtmlElement(input) {
|
|
|
2593
2651
|
size: input.size
|
|
2594
2652
|
};
|
|
2595
2653
|
if (input.domId) el.domId = input.domId;
|
|
2654
|
+
if (input.interactive) el.interactive = input.interactive;
|
|
2596
2655
|
return el;
|
|
2597
2656
|
}
|
|
2598
2657
|
function createShape(input) {
|
|
@@ -2705,7 +2764,7 @@ function getActiveFormats() {
|
|
|
2705
2764
|
}
|
|
2706
2765
|
|
|
2707
2766
|
// src/elements/note-toolbar.ts
|
|
2708
|
-
var TOOLBAR_HEIGHT =
|
|
2767
|
+
var TOOLBAR_HEIGHT = 52;
|
|
2709
2768
|
var TOOLBAR_GAP = 4;
|
|
2710
2769
|
var FORMAT_BUTTONS = [
|
|
2711
2770
|
{ label: "B", format: "bold", command: "bold" },
|
|
@@ -2792,9 +2851,9 @@ var NoteToolbar = class {
|
|
|
2792
2851
|
fontWeight: config.format === "bold" ? "bold" : "normal",
|
|
2793
2852
|
fontStyle: config.format === "italic" ? "italic" : "normal",
|
|
2794
2853
|
textDecoration: config.format === "underline" ? "underline" : config.format === "strikethrough" ? "line-through" : "none",
|
|
2795
|
-
minWidth: "
|
|
2796
|
-
height: "
|
|
2797
|
-
lineHeight: "
|
|
2854
|
+
minWidth: "44px",
|
|
2855
|
+
height: "44px",
|
|
2856
|
+
lineHeight: "44px"
|
|
2798
2857
|
});
|
|
2799
2858
|
btn.addEventListener("pointerdown", (e) => {
|
|
2800
2859
|
e.preventDefault();
|
|
@@ -2812,7 +2871,7 @@ var NoteToolbar = class {
|
|
|
2812
2871
|
cursor: "pointer",
|
|
2813
2872
|
padding: "2px",
|
|
2814
2873
|
fontSize: "12px",
|
|
2815
|
-
height: "
|
|
2874
|
+
height: "44px",
|
|
2816
2875
|
marginLeft: "4px"
|
|
2817
2876
|
});
|
|
2818
2877
|
for (const preset of this.fontSizePresets) {
|
|
@@ -3810,10 +3869,13 @@ var DomNodeManager = class {
|
|
|
3810
3869
|
wordWrap: "break-word"
|
|
3811
3870
|
});
|
|
3812
3871
|
node.innerHTML = element.text || "";
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3872
|
+
const detector = new DoubleTapDetector();
|
|
3873
|
+
node.addEventListener("pointerup", (e) => {
|
|
3874
|
+
if (detector.feed(e)) {
|
|
3875
|
+
e.stopPropagation();
|
|
3876
|
+
const id = node.dataset["elementId"];
|
|
3877
|
+
if (id) this.onEditRequest(id);
|
|
3878
|
+
}
|
|
3817
3879
|
});
|
|
3818
3880
|
}
|
|
3819
3881
|
if (!this.isEditingElement(element.id)) {
|
|
@@ -3826,15 +3888,19 @@ var DomNodeManager = class {
|
|
|
3826
3888
|
node.style.fontSize = `${element.fontSize ?? DEFAULT_NOTE_FONT_SIZE}px`;
|
|
3827
3889
|
}
|
|
3828
3890
|
}
|
|
3829
|
-
if (element.type === "html"
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3891
|
+
if (element.type === "html") {
|
|
3892
|
+
if (!node.dataset["initialized"]) {
|
|
3893
|
+
const content = this.htmlContent.get(element.id);
|
|
3894
|
+
if (content) {
|
|
3895
|
+
node.dataset["initialized"] = "true";
|
|
3896
|
+
Object.assign(node.style, {
|
|
3897
|
+
overflow: "hidden",
|
|
3898
|
+
pointerEvents: element.interactive ? "auto" : "none"
|
|
3899
|
+
});
|
|
3900
|
+
node.appendChild(content);
|
|
3901
|
+
}
|
|
3902
|
+
} else {
|
|
3903
|
+
node.style.pointerEvents = element.interactive ? "auto" : "none";
|
|
3838
3904
|
}
|
|
3839
3905
|
}
|
|
3840
3906
|
if (element.type === "text") {
|
|
@@ -3856,10 +3922,13 @@ var DomNodeManager = class {
|
|
|
3856
3922
|
lineHeight: "1.4"
|
|
3857
3923
|
});
|
|
3858
3924
|
node.textContent = element.text || "";
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3925
|
+
const detector = new DoubleTapDetector();
|
|
3926
|
+
node.addEventListener("pointerup", (e) => {
|
|
3927
|
+
if (detector.feed(e)) {
|
|
3928
|
+
e.stopPropagation();
|
|
3929
|
+
const id = node.dataset["elementId"];
|
|
3930
|
+
if (id) this.onEditRequest(id);
|
|
3931
|
+
}
|
|
3863
3932
|
});
|
|
3864
3933
|
}
|
|
3865
3934
|
if (!this.isEditingElement(element.id)) {
|
|
@@ -4348,7 +4417,8 @@ var Viewport = class {
|
|
|
4348
4417
|
this.toolContext.activeLayerId = this.layerManager.activeLayerId;
|
|
4349
4418
|
this.requestRender();
|
|
4350
4419
|
});
|
|
4351
|
-
this.wrapper.addEventListener("
|
|
4420
|
+
this.wrapper.addEventListener("pointerdown", this.onTapDown);
|
|
4421
|
+
this.wrapper.addEventListener("pointerup", this.onDoubleTap);
|
|
4352
4422
|
this.wrapper.addEventListener("dragover", this.onDragOver);
|
|
4353
4423
|
this.wrapper.addEventListener("drop", this.onDrop);
|
|
4354
4424
|
this.observeResize();
|
|
@@ -4379,6 +4449,9 @@ var Viewport = class {
|
|
|
4379
4449
|
domNodeManager;
|
|
4380
4450
|
interactMode;
|
|
4381
4451
|
gridChangeListeners = /* @__PURE__ */ new Set();
|
|
4452
|
+
doubleTapDetector = new DoubleTapDetector();
|
|
4453
|
+
tapDownX = 0;
|
|
4454
|
+
tapDownY = 0;
|
|
4382
4455
|
get ctx() {
|
|
4383
4456
|
return this.canvasEl.getContext("2d");
|
|
4384
4457
|
}
|
|
@@ -4393,7 +4466,12 @@ var Viewport = class {
|
|
|
4393
4466
|
this.renderLoop.requestRender();
|
|
4394
4467
|
}
|
|
4395
4468
|
exportState() {
|
|
4396
|
-
return exportState(
|
|
4469
|
+
return exportState(
|
|
4470
|
+
this.store.snapshot(),
|
|
4471
|
+
this.camera,
|
|
4472
|
+
this.layerManager.snapshot(),
|
|
4473
|
+
this.layerManager.activeLayerId
|
|
4474
|
+
);
|
|
4397
4475
|
}
|
|
4398
4476
|
exportJSON() {
|
|
4399
4477
|
return JSON.stringify(this.exportState());
|
|
@@ -4409,6 +4487,9 @@ var Viewport = class {
|
|
|
4409
4487
|
if (state.layers && state.layers.length > 0) {
|
|
4410
4488
|
this.layerManager.loadSnapshot(state.layers);
|
|
4411
4489
|
}
|
|
4490
|
+
if (state.activeLayerId) {
|
|
4491
|
+
this.layerManager.setActiveLayer(state.activeLayerId);
|
|
4492
|
+
}
|
|
4412
4493
|
this.domNodeManager.reattachHtmlContent(this.store);
|
|
4413
4494
|
this.history.clear();
|
|
4414
4495
|
this.historyRecorder.resume();
|
|
@@ -4516,7 +4597,8 @@ var Viewport = class {
|
|
|
4516
4597
|
this.interactMode.destroy();
|
|
4517
4598
|
this.noteEditor.destroy(this.store);
|
|
4518
4599
|
this.historyRecorder.destroy();
|
|
4519
|
-
this.wrapper.removeEventListener("
|
|
4600
|
+
this.wrapper.removeEventListener("pointerdown", this.onTapDown);
|
|
4601
|
+
this.wrapper.removeEventListener("pointerup", this.onDoubleTap);
|
|
4520
4602
|
this.wrapper.removeEventListener("dragover", this.onDragOver);
|
|
4521
4603
|
this.wrapper.removeEventListener("drop", this.onDrop);
|
|
4522
4604
|
this.inputHandler.destroy();
|
|
@@ -4554,7 +4636,17 @@ var Viewport = class {
|
|
|
4554
4636
|
}
|
|
4555
4637
|
}
|
|
4556
4638
|
}
|
|
4557
|
-
|
|
4639
|
+
onTapDown = (e) => {
|
|
4640
|
+
this.tapDownX = e.clientX;
|
|
4641
|
+
this.tapDownY = e.clientY;
|
|
4642
|
+
};
|
|
4643
|
+
onDoubleTap = (e) => {
|
|
4644
|
+
const dx = e.clientX - this.tapDownX;
|
|
4645
|
+
const dy = e.clientY - this.tapDownY;
|
|
4646
|
+
const moved = Math.sqrt(dx * dx + dy * dy);
|
|
4647
|
+
if (moved > 10) return;
|
|
4648
|
+
if (!this.doubleTapDetector.feed(e)) return;
|
|
4649
|
+
if (typeof document.elementFromPoint !== "function") return;
|
|
4558
4650
|
const el = document.elementFromPoint(e.clientX, e.clientY);
|
|
4559
4651
|
const nodeEl = el?.closest("[data-element-id]");
|
|
4560
4652
|
if (nodeEl) {
|
|
@@ -4717,6 +4809,26 @@ var Viewport = class {
|
|
|
4717
4809
|
}
|
|
4718
4810
|
};
|
|
4719
4811
|
|
|
4812
|
+
// src/elements/bounds.ts
|
|
4813
|
+
function getElementsBoundingBox(elements) {
|
|
4814
|
+
let minX = Infinity;
|
|
4815
|
+
let minY = Infinity;
|
|
4816
|
+
let maxX = -Infinity;
|
|
4817
|
+
let maxY = -Infinity;
|
|
4818
|
+
let found = false;
|
|
4819
|
+
for (const el of elements) {
|
|
4820
|
+
const b = getElementBounds(el);
|
|
4821
|
+
if (!b) continue;
|
|
4822
|
+
found = true;
|
|
4823
|
+
if (b.x < minX) minX = b.x;
|
|
4824
|
+
if (b.y < minY) minY = b.y;
|
|
4825
|
+
if (b.x + b.w > maxX) maxX = b.x + b.w;
|
|
4826
|
+
if (b.y + b.h > maxY) maxY = b.y + b.h;
|
|
4827
|
+
}
|
|
4828
|
+
if (!found) return null;
|
|
4829
|
+
return { x: minX, y: minY, w: maxX - minX, h: maxY - minY };
|
|
4830
|
+
}
|
|
4831
|
+
|
|
4720
4832
|
// src/tools/hand-tool.ts
|
|
4721
4833
|
var HandTool = class {
|
|
4722
4834
|
name = "hand";
|
|
@@ -6426,7 +6538,7 @@ var UpdateLayerCommand = class {
|
|
|
6426
6538
|
};
|
|
6427
6539
|
|
|
6428
6540
|
// src/index.ts
|
|
6429
|
-
var VERSION = "0.
|
|
6541
|
+
var VERSION = "0.14.0";
|
|
6430
6542
|
export {
|
|
6431
6543
|
AddElementCommand,
|
|
6432
6544
|
ArrowTool,
|
|
@@ -6437,6 +6549,7 @@ export {
|
|
|
6437
6549
|
CreateLayerCommand,
|
|
6438
6550
|
DEFAULT_FONT_SIZE_PRESETS,
|
|
6439
6551
|
DEFAULT_NOTE_FONT_SIZE,
|
|
6552
|
+
DoubleTapDetector,
|
|
6440
6553
|
ElementRenderer,
|
|
6441
6554
|
ElementStore,
|
|
6442
6555
|
EraserTool,
|
|
@@ -6491,6 +6604,7 @@ export {
|
|
|
6491
6604
|
getEdgeIntersection,
|
|
6492
6605
|
getElementBounds,
|
|
6493
6606
|
getElementCenter,
|
|
6607
|
+
getElementsBoundingBox,
|
|
6494
6608
|
getHexCellsInCone,
|
|
6495
6609
|
getHexCellsInLine,
|
|
6496
6610
|
getHexCellsInRadius,
|