@fieldnotes/core 0.2.3 → 0.3.1
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 +550 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +69 -5
- package/dist/index.d.ts +69 -5
- package/dist/index.js +538 -34
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -68,8 +68,9 @@ function validateState(data) {
|
|
|
68
68
|
validateElement(el);
|
|
69
69
|
migrateElement(el);
|
|
70
70
|
}
|
|
71
|
+
cleanBindings(obj["elements"]);
|
|
71
72
|
}
|
|
72
|
-
var VALID_TYPES = /* @__PURE__ */ new Set(["stroke", "note", "arrow", "image", "html"]);
|
|
73
|
+
var VALID_TYPES = /* @__PURE__ */ new Set(["stroke", "note", "arrow", "image", "html", "text"]);
|
|
73
74
|
function validateElement(el) {
|
|
74
75
|
if (!el || typeof el !== "object") {
|
|
75
76
|
throw new Error("Invalid element: expected an object");
|
|
@@ -85,6 +86,20 @@ function validateElement(el) {
|
|
|
85
86
|
throw new Error("Invalid element: missing zIndex");
|
|
86
87
|
}
|
|
87
88
|
}
|
|
89
|
+
function cleanBindings(elements) {
|
|
90
|
+
const ids = new Set(elements.map((el) => el["id"]));
|
|
91
|
+
for (const el of elements) {
|
|
92
|
+
if (el["type"] !== "arrow") continue;
|
|
93
|
+
const fromBinding = el["fromBinding"];
|
|
94
|
+
if (fromBinding && !ids.has(fromBinding["elementId"])) {
|
|
95
|
+
el["fromBinding"] = void 0;
|
|
96
|
+
}
|
|
97
|
+
const toBinding = el["toBinding"];
|
|
98
|
+
if (toBinding && !ids.has(toBinding["elementId"])) {
|
|
99
|
+
el["toBinding"] = void 0;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
88
103
|
function migrateElement(obj) {
|
|
89
104
|
if (obj["type"] === "arrow" && typeof obj["bend"] !== "number") {
|
|
90
105
|
obj["bend"] = 0;
|
|
@@ -678,6 +693,134 @@ function isNearLine(point, a, b, threshold) {
|
|
|
678
693
|
return Math.hypot(point.x - projX, point.y - projY) <= threshold;
|
|
679
694
|
}
|
|
680
695
|
|
|
696
|
+
// src/elements/arrow-binding.ts
|
|
697
|
+
var BINDABLE_TYPES = /* @__PURE__ */ new Set(["note", "text", "image", "html"]);
|
|
698
|
+
function isBindable(element) {
|
|
699
|
+
return BINDABLE_TYPES.has(element.type);
|
|
700
|
+
}
|
|
701
|
+
function getElementCenter(element) {
|
|
702
|
+
if (!("size" in element)) {
|
|
703
|
+
throw new Error(`getElementCenter: element type "${element.type}" has no size`);
|
|
704
|
+
}
|
|
705
|
+
return {
|
|
706
|
+
x: element.position.x + element.size.w / 2,
|
|
707
|
+
y: element.position.y + element.size.h / 2
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
function getElementBounds(element) {
|
|
711
|
+
if (!("size" in element)) return null;
|
|
712
|
+
return {
|
|
713
|
+
x: element.position.x,
|
|
714
|
+
y: element.position.y,
|
|
715
|
+
w: element.size.w,
|
|
716
|
+
h: element.size.h
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
function getEdgeIntersection(bounds, outsidePoint) {
|
|
720
|
+
const cx = bounds.x + bounds.w / 2;
|
|
721
|
+
const cy = bounds.y + bounds.h / 2;
|
|
722
|
+
const dx = outsidePoint.x - cx;
|
|
723
|
+
const dy = outsidePoint.y - cy;
|
|
724
|
+
if (dx === 0 && dy === 0) return { x: cx, y: cy };
|
|
725
|
+
const halfW = bounds.w / 2;
|
|
726
|
+
const halfH = bounds.h / 2;
|
|
727
|
+
const scaleX = dx !== 0 ? halfW / Math.abs(dx) : Infinity;
|
|
728
|
+
const scaleY = dy !== 0 ? halfH / Math.abs(dy) : Infinity;
|
|
729
|
+
const scale = Math.min(scaleX, scaleY);
|
|
730
|
+
return {
|
|
731
|
+
x: cx + dx * scale,
|
|
732
|
+
y: cy + dy * scale
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
function findBindTarget(point, store, threshold, excludeId) {
|
|
736
|
+
let closest = null;
|
|
737
|
+
let closestDist = Infinity;
|
|
738
|
+
for (const el of store.getAll()) {
|
|
739
|
+
if (!isBindable(el)) continue;
|
|
740
|
+
if (excludeId && el.id === excludeId) continue;
|
|
741
|
+
const bounds = getElementBounds(el);
|
|
742
|
+
if (!bounds) continue;
|
|
743
|
+
const dist = distToBounds(point, bounds);
|
|
744
|
+
if (dist <= threshold && dist < closestDist) {
|
|
745
|
+
closest = el;
|
|
746
|
+
closestDist = dist;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
return closest;
|
|
750
|
+
}
|
|
751
|
+
function distToBounds(point, bounds) {
|
|
752
|
+
const clampedX = Math.max(bounds.x, Math.min(point.x, bounds.x + bounds.w));
|
|
753
|
+
const clampedY = Math.max(bounds.y, Math.min(point.y, bounds.y + bounds.h));
|
|
754
|
+
return Math.hypot(point.x - clampedX, point.y - clampedY);
|
|
755
|
+
}
|
|
756
|
+
function findBoundArrows(elementId, store) {
|
|
757
|
+
return store.getElementsByType("arrow").filter((a) => a.fromBinding?.elementId === elementId || a.toBinding?.elementId === elementId);
|
|
758
|
+
}
|
|
759
|
+
function updateBoundArrow(arrow, store) {
|
|
760
|
+
if (!arrow.fromBinding && !arrow.toBinding) return null;
|
|
761
|
+
const updates = {};
|
|
762
|
+
if (arrow.fromBinding) {
|
|
763
|
+
const el = store.getById(arrow.fromBinding.elementId);
|
|
764
|
+
if (el) {
|
|
765
|
+
const center = getElementCenter(el);
|
|
766
|
+
updates.from = center;
|
|
767
|
+
updates.position = center;
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
if (arrow.toBinding) {
|
|
771
|
+
const el = store.getById(arrow.toBinding.elementId);
|
|
772
|
+
if (el) {
|
|
773
|
+
updates.to = getElementCenter(el);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
return Object.keys(updates).length > 0 ? updates : null;
|
|
777
|
+
}
|
|
778
|
+
function clearStaleBindings(arrow, store) {
|
|
779
|
+
const updates = {};
|
|
780
|
+
let hasUpdates = false;
|
|
781
|
+
if (arrow.fromBinding && !store.getById(arrow.fromBinding.elementId)) {
|
|
782
|
+
updates.fromBinding = void 0;
|
|
783
|
+
hasUpdates = true;
|
|
784
|
+
}
|
|
785
|
+
if (arrow.toBinding && !store.getById(arrow.toBinding.elementId)) {
|
|
786
|
+
updates.toBinding = void 0;
|
|
787
|
+
hasUpdates = true;
|
|
788
|
+
}
|
|
789
|
+
return hasUpdates ? updates : null;
|
|
790
|
+
}
|
|
791
|
+
function unbindArrow(arrow, store) {
|
|
792
|
+
const updates = {};
|
|
793
|
+
if (arrow.fromBinding) {
|
|
794
|
+
const el = store.getById(arrow.fromBinding.elementId);
|
|
795
|
+
const bounds = el ? getElementBounds(el) : null;
|
|
796
|
+
if (bounds) {
|
|
797
|
+
const angle = getArrowTangentAngle(arrow.from, arrow.to, arrow.bend, 0);
|
|
798
|
+
const rayTarget = {
|
|
799
|
+
x: arrow.from.x + Math.cos(angle) * 1e3,
|
|
800
|
+
y: arrow.from.y + Math.sin(angle) * 1e3
|
|
801
|
+
};
|
|
802
|
+
const edge = getEdgeIntersection(bounds, rayTarget);
|
|
803
|
+
updates.from = edge;
|
|
804
|
+
updates.position = edge;
|
|
805
|
+
}
|
|
806
|
+
updates.fromBinding = void 0;
|
|
807
|
+
}
|
|
808
|
+
if (arrow.toBinding) {
|
|
809
|
+
const el = store.getById(arrow.toBinding.elementId);
|
|
810
|
+
const bounds = el ? getElementBounds(el) : null;
|
|
811
|
+
if (bounds) {
|
|
812
|
+
const angle = getArrowTangentAngle(arrow.from, arrow.to, arrow.bend, 1);
|
|
813
|
+
const rayTarget = {
|
|
814
|
+
x: arrow.to.x - Math.cos(angle) * 1e3,
|
|
815
|
+
y: arrow.to.y - Math.sin(angle) * 1e3
|
|
816
|
+
};
|
|
817
|
+
updates.to = getEdgeIntersection(bounds, rayTarget);
|
|
818
|
+
}
|
|
819
|
+
updates.toBinding = void 0;
|
|
820
|
+
}
|
|
821
|
+
return updates;
|
|
822
|
+
}
|
|
823
|
+
|
|
681
824
|
// src/elements/stroke-smoothing.ts
|
|
682
825
|
var MIN_PRESSURE_SCALE = 0.2;
|
|
683
826
|
function pressureToWidth(pressure, baseWidth) {
|
|
@@ -752,10 +895,14 @@ function smoothToSegments(points) {
|
|
|
752
895
|
}
|
|
753
896
|
|
|
754
897
|
// src/elements/element-renderer.ts
|
|
755
|
-
var DOM_ELEMENT_TYPES = /* @__PURE__ */ new Set(["note", "image", "html"]);
|
|
898
|
+
var DOM_ELEMENT_TYPES = /* @__PURE__ */ new Set(["note", "image", "html", "text"]);
|
|
756
899
|
var ARROWHEAD_LENGTH = 12;
|
|
757
900
|
var ARROWHEAD_ANGLE = Math.PI / 6;
|
|
758
901
|
var ElementRenderer = class {
|
|
902
|
+
store = null;
|
|
903
|
+
setStore(store) {
|
|
904
|
+
this.store = store;
|
|
905
|
+
}
|
|
759
906
|
isDomElement(element) {
|
|
760
907
|
return DOM_ELEMENT_TYPES.has(element.type);
|
|
761
908
|
}
|
|
@@ -789,38 +936,76 @@ var ElementRenderer = class {
|
|
|
789
936
|
ctx.restore();
|
|
790
937
|
}
|
|
791
938
|
renderArrow(ctx, arrow) {
|
|
939
|
+
const { visualFrom, visualTo } = this.getVisualEndpoints(arrow);
|
|
792
940
|
ctx.save();
|
|
793
941
|
ctx.strokeStyle = arrow.color;
|
|
794
942
|
ctx.lineWidth = arrow.width;
|
|
795
943
|
ctx.lineCap = "round";
|
|
944
|
+
if (arrow.fromBinding || arrow.toBinding) {
|
|
945
|
+
ctx.setLineDash([8, 4]);
|
|
946
|
+
}
|
|
796
947
|
ctx.beginPath();
|
|
797
|
-
ctx.moveTo(
|
|
948
|
+
ctx.moveTo(visualFrom.x, visualFrom.y);
|
|
798
949
|
if (arrow.bend !== 0) {
|
|
799
950
|
const cp = getArrowControlPoint(arrow.from, arrow.to, arrow.bend);
|
|
800
|
-
ctx.quadraticCurveTo(cp.x, cp.y,
|
|
951
|
+
ctx.quadraticCurveTo(cp.x, cp.y, visualTo.x, visualTo.y);
|
|
801
952
|
} else {
|
|
802
|
-
ctx.lineTo(
|
|
953
|
+
ctx.lineTo(visualTo.x, visualTo.y);
|
|
803
954
|
}
|
|
804
955
|
ctx.stroke();
|
|
805
|
-
this.renderArrowhead(ctx, arrow);
|
|
956
|
+
this.renderArrowhead(ctx, arrow, visualTo);
|
|
806
957
|
ctx.restore();
|
|
807
958
|
}
|
|
808
|
-
renderArrowhead(ctx, arrow) {
|
|
959
|
+
renderArrowhead(ctx, arrow, tip) {
|
|
809
960
|
const angle = getArrowTangentAngle(arrow.from, arrow.to, arrow.bend, 1);
|
|
810
961
|
ctx.beginPath();
|
|
811
|
-
ctx.moveTo(
|
|
962
|
+
ctx.moveTo(tip.x, tip.y);
|
|
812
963
|
ctx.lineTo(
|
|
813
|
-
|
|
814
|
-
|
|
964
|
+
tip.x - ARROWHEAD_LENGTH * Math.cos(angle - ARROWHEAD_ANGLE),
|
|
965
|
+
tip.y - ARROWHEAD_LENGTH * Math.sin(angle - ARROWHEAD_ANGLE)
|
|
815
966
|
);
|
|
816
967
|
ctx.lineTo(
|
|
817
|
-
|
|
818
|
-
|
|
968
|
+
tip.x - ARROWHEAD_LENGTH * Math.cos(angle + ARROWHEAD_ANGLE),
|
|
969
|
+
tip.y - ARROWHEAD_LENGTH * Math.sin(angle + ARROWHEAD_ANGLE)
|
|
819
970
|
);
|
|
820
971
|
ctx.closePath();
|
|
821
972
|
ctx.fillStyle = arrow.color;
|
|
822
973
|
ctx.fill();
|
|
823
974
|
}
|
|
975
|
+
getVisualEndpoints(arrow) {
|
|
976
|
+
let visualFrom = arrow.from;
|
|
977
|
+
let visualTo = arrow.to;
|
|
978
|
+
if (!this.store) return { visualFrom, visualTo };
|
|
979
|
+
if (arrow.fromBinding) {
|
|
980
|
+
const el = this.store.getById(arrow.fromBinding.elementId);
|
|
981
|
+
if (el) {
|
|
982
|
+
const bounds = getElementBounds(el);
|
|
983
|
+
if (bounds) {
|
|
984
|
+
const tangentAngle = getArrowTangentAngle(arrow.from, arrow.to, arrow.bend, 0);
|
|
985
|
+
const rayTarget = {
|
|
986
|
+
x: arrow.from.x + Math.cos(tangentAngle) * 1e3,
|
|
987
|
+
y: arrow.from.y + Math.sin(tangentAngle) * 1e3
|
|
988
|
+
};
|
|
989
|
+
visualFrom = getEdgeIntersection(bounds, rayTarget);
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
if (arrow.toBinding) {
|
|
994
|
+
const el = this.store.getById(arrow.toBinding.elementId);
|
|
995
|
+
if (el) {
|
|
996
|
+
const bounds = getElementBounds(el);
|
|
997
|
+
if (bounds) {
|
|
998
|
+
const tangentAngle = getArrowTangentAngle(arrow.from, arrow.to, arrow.bend, 1);
|
|
999
|
+
const rayTarget = {
|
|
1000
|
+
x: arrow.to.x - Math.cos(tangentAngle) * 1e3,
|
|
1001
|
+
y: arrow.to.y - Math.sin(tangentAngle) * 1e3
|
|
1002
|
+
};
|
|
1003
|
+
visualTo = getEdgeIntersection(bounds, rayTarget);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
return { visualFrom, visualTo };
|
|
1008
|
+
}
|
|
824
1009
|
};
|
|
825
1010
|
|
|
826
1011
|
// src/elements/note-editor.ts
|
|
@@ -831,12 +1016,16 @@ var NoteEditor = class {
|
|
|
831
1016
|
keyHandler = null;
|
|
832
1017
|
pointerHandler = null;
|
|
833
1018
|
pendingEditId = null;
|
|
1019
|
+
onStopCallback = null;
|
|
834
1020
|
get isEditing() {
|
|
835
1021
|
return this.editingId !== null;
|
|
836
1022
|
}
|
|
837
1023
|
get editingElementId() {
|
|
838
1024
|
return this.editingId;
|
|
839
1025
|
}
|
|
1026
|
+
setOnStop(callback) {
|
|
1027
|
+
this.onStopCallback = callback;
|
|
1028
|
+
}
|
|
840
1029
|
startEditing(node, elementId, store) {
|
|
841
1030
|
if (this.editingId === elementId) return;
|
|
842
1031
|
if (this.editingId) {
|
|
@@ -868,6 +1057,9 @@ var NoteEditor = class {
|
|
|
868
1057
|
if (this.pointerHandler) {
|
|
869
1058
|
this.editingNode.removeEventListener("pointerdown", this.pointerHandler);
|
|
870
1059
|
}
|
|
1060
|
+
if (this.editingId && this.onStopCallback) {
|
|
1061
|
+
this.onStopCallback(this.editingId);
|
|
1062
|
+
}
|
|
871
1063
|
this.editingId = null;
|
|
872
1064
|
this.editingNode = null;
|
|
873
1065
|
this.blurHandler = null;
|
|
@@ -1184,7 +1376,7 @@ function createNote(input) {
|
|
|
1184
1376
|
};
|
|
1185
1377
|
}
|
|
1186
1378
|
function createArrow(input) {
|
|
1187
|
-
|
|
1379
|
+
const result = {
|
|
1188
1380
|
id: createId("arrow"),
|
|
1189
1381
|
type: "arrow",
|
|
1190
1382
|
position: input.position ?? { x: 0, y: 0 },
|
|
@@ -1196,6 +1388,9 @@ function createArrow(input) {
|
|
|
1196
1388
|
color: input.color ?? "#000000",
|
|
1197
1389
|
width: input.width ?? 2
|
|
1198
1390
|
};
|
|
1391
|
+
if (input.fromBinding) result.fromBinding = input.fromBinding;
|
|
1392
|
+
if (input.toBinding) result.toBinding = input.toBinding;
|
|
1393
|
+
return result;
|
|
1199
1394
|
}
|
|
1200
1395
|
function createImage(input) {
|
|
1201
1396
|
return {
|
|
@@ -1218,6 +1413,20 @@ function createHtmlElement(input) {
|
|
|
1218
1413
|
size: input.size
|
|
1219
1414
|
};
|
|
1220
1415
|
}
|
|
1416
|
+
function createText(input) {
|
|
1417
|
+
return {
|
|
1418
|
+
id: createId("text"),
|
|
1419
|
+
type: "text",
|
|
1420
|
+
position: input.position,
|
|
1421
|
+
zIndex: input.zIndex ?? 0,
|
|
1422
|
+
locked: input.locked ?? false,
|
|
1423
|
+
size: input.size ?? { w: 200, h: 28 },
|
|
1424
|
+
text: input.text ?? "",
|
|
1425
|
+
fontSize: input.fontSize ?? 16,
|
|
1426
|
+
color: input.color ?? "#1a1a1a",
|
|
1427
|
+
textAlign: input.textAlign ?? "left"
|
|
1428
|
+
};
|
|
1429
|
+
}
|
|
1221
1430
|
|
|
1222
1431
|
// src/canvas/viewport.ts
|
|
1223
1432
|
var Viewport = class {
|
|
@@ -1228,7 +1437,9 @@ var Viewport = class {
|
|
|
1228
1437
|
this.store = new ElementStore();
|
|
1229
1438
|
this.toolManager = new ToolManager();
|
|
1230
1439
|
this.renderer = new ElementRenderer();
|
|
1440
|
+
this.renderer.setStore(this.store);
|
|
1231
1441
|
this.noteEditor = new NoteEditor();
|
|
1442
|
+
this.noteEditor.setOnStop((id) => this.onTextEditStop(id));
|
|
1232
1443
|
this.history = new HistoryStack();
|
|
1233
1444
|
this.historyRecorder = new HistoryRecorder(this.store, this.history);
|
|
1234
1445
|
this.wrapper = this.createWrapper();
|
|
@@ -1242,7 +1453,7 @@ var Viewport = class {
|
|
|
1242
1453
|
store: this.store,
|
|
1243
1454
|
requestRender: () => this.requestRender(),
|
|
1244
1455
|
switchTool: (name) => this.toolManager.setTool(name, this.toolContext),
|
|
1245
|
-
editElement: (id) => this.
|
|
1456
|
+
editElement: (id) => this.startEditingElement(id),
|
|
1246
1457
|
setCursor: (cursor) => {
|
|
1247
1458
|
this.wrapper.style.cursor = cursor;
|
|
1248
1459
|
}
|
|
@@ -1259,7 +1470,10 @@ var Viewport = class {
|
|
|
1259
1470
|
});
|
|
1260
1471
|
this.unsubStore = [
|
|
1261
1472
|
this.store.on("add", () => this.requestRender()),
|
|
1262
|
-
this.store.on("remove", (el) =>
|
|
1473
|
+
this.store.on("remove", (el) => {
|
|
1474
|
+
this.unbindArrowsFrom(el);
|
|
1475
|
+
this.removeDomNode(el.id);
|
|
1476
|
+
}),
|
|
1263
1477
|
this.store.on("update", () => this.requestRender()),
|
|
1264
1478
|
this.store.on("clear", () => this.clearDomNodes())
|
|
1265
1479
|
];
|
|
@@ -1396,15 +1610,34 @@ var Viewport = class {
|
|
|
1396
1610
|
ctx.restore();
|
|
1397
1611
|
ctx.restore();
|
|
1398
1612
|
}
|
|
1399
|
-
|
|
1613
|
+
startEditingElement(id) {
|
|
1400
1614
|
const element = this.store.getById(id);
|
|
1401
|
-
if (!element || element.type !== "note") return;
|
|
1615
|
+
if (!element || element.type !== "note" && element.type !== "text") return;
|
|
1402
1616
|
this.render();
|
|
1403
1617
|
const node = this.domNodes.get(id);
|
|
1404
1618
|
if (node) {
|
|
1405
1619
|
this.noteEditor.startEditing(node, id, this.store);
|
|
1406
1620
|
}
|
|
1407
1621
|
}
|
|
1622
|
+
onTextEditStop(elementId) {
|
|
1623
|
+
const element = this.store.getById(elementId);
|
|
1624
|
+
if (!element || element.type !== "text") return;
|
|
1625
|
+
if (!element.text || element.text.trim() === "") {
|
|
1626
|
+
this.historyRecorder.begin();
|
|
1627
|
+
this.store.remove(elementId);
|
|
1628
|
+
this.historyRecorder.commit();
|
|
1629
|
+
return;
|
|
1630
|
+
}
|
|
1631
|
+
const node = this.domNodes.get(elementId);
|
|
1632
|
+
if (node && "size" in element) {
|
|
1633
|
+
const measuredHeight = node.scrollHeight;
|
|
1634
|
+
if (measuredHeight !== element.size.h) {
|
|
1635
|
+
this.store.update(elementId, {
|
|
1636
|
+
size: { w: element.size.w, h: measuredHeight }
|
|
1637
|
+
});
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1408
1641
|
onDblClick = (e) => {
|
|
1409
1642
|
const el = document.elementFromPoint(e.clientX, e.clientY);
|
|
1410
1643
|
const nodeEl = el?.closest("[data-element-id]");
|
|
@@ -1412,8 +1645,8 @@ var Viewport = class {
|
|
|
1412
1645
|
const elementId = nodeEl.dataset["elementId"];
|
|
1413
1646
|
if (elementId) {
|
|
1414
1647
|
const element = this.store.getById(elementId);
|
|
1415
|
-
if (element?.type === "note") {
|
|
1416
|
-
this.
|
|
1648
|
+
if (element?.type === "note" || element?.type === "text") {
|
|
1649
|
+
this.startEditingElement(elementId);
|
|
1417
1650
|
return;
|
|
1418
1651
|
}
|
|
1419
1652
|
}
|
|
@@ -1537,7 +1770,7 @@ var Viewport = class {
|
|
|
1537
1770
|
node.addEventListener("dblclick", (e) => {
|
|
1538
1771
|
e.stopPropagation();
|
|
1539
1772
|
const id = node.dataset["elementId"];
|
|
1540
|
-
if (id) this.
|
|
1773
|
+
if (id) this.startEditingElement(id);
|
|
1541
1774
|
});
|
|
1542
1775
|
}
|
|
1543
1776
|
if (!this.noteEditor.isEditing || this.noteEditor.editingElementId !== element.id) {
|
|
@@ -1578,6 +1811,76 @@ var Viewport = class {
|
|
|
1578
1811
|
node.appendChild(content);
|
|
1579
1812
|
}
|
|
1580
1813
|
}
|
|
1814
|
+
if (element.type === "text") {
|
|
1815
|
+
if (!node.dataset["initialized"]) {
|
|
1816
|
+
node.dataset["initialized"] = "true";
|
|
1817
|
+
Object.assign(node.style, {
|
|
1818
|
+
padding: "2px",
|
|
1819
|
+
fontSize: `${element.fontSize}px`,
|
|
1820
|
+
color: element.color,
|
|
1821
|
+
textAlign: element.textAlign,
|
|
1822
|
+
background: "none",
|
|
1823
|
+
border: "none",
|
|
1824
|
+
boxShadow: "none",
|
|
1825
|
+
overflow: "visible",
|
|
1826
|
+
cursor: "default",
|
|
1827
|
+
userSelect: "none",
|
|
1828
|
+
wordWrap: "break-word",
|
|
1829
|
+
whiteSpace: "pre-wrap",
|
|
1830
|
+
lineHeight: "1.4"
|
|
1831
|
+
});
|
|
1832
|
+
node.textContent = element.text || "";
|
|
1833
|
+
node.addEventListener("dblclick", (e) => {
|
|
1834
|
+
e.stopPropagation();
|
|
1835
|
+
const id = node.dataset["elementId"];
|
|
1836
|
+
if (id) this.startEditingElement(id);
|
|
1837
|
+
});
|
|
1838
|
+
}
|
|
1839
|
+
if (!this.noteEditor.isEditing || this.noteEditor.editingElementId !== element.id) {
|
|
1840
|
+
if (node.textContent !== element.text) {
|
|
1841
|
+
node.textContent = element.text || "";
|
|
1842
|
+
}
|
|
1843
|
+
Object.assign(node.style, {
|
|
1844
|
+
fontSize: `${element.fontSize}px`,
|
|
1845
|
+
color: element.color,
|
|
1846
|
+
textAlign: element.textAlign
|
|
1847
|
+
});
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
unbindArrowsFrom(removedElement) {
|
|
1852
|
+
const boundArrows = findBoundArrows(removedElement.id, this.store);
|
|
1853
|
+
const bounds = getElementBounds(removedElement);
|
|
1854
|
+
for (const arrow of boundArrows) {
|
|
1855
|
+
const updates = {};
|
|
1856
|
+
if (arrow.fromBinding?.elementId === removedElement.id) {
|
|
1857
|
+
updates.fromBinding = void 0;
|
|
1858
|
+
if (bounds) {
|
|
1859
|
+
const angle = getArrowTangentAngle(arrow.from, arrow.to, arrow.bend, 0);
|
|
1860
|
+
const rayTarget = {
|
|
1861
|
+
x: arrow.from.x + Math.cos(angle) * 1e3,
|
|
1862
|
+
y: arrow.from.y + Math.sin(angle) * 1e3
|
|
1863
|
+
};
|
|
1864
|
+
const edge = getEdgeIntersection(bounds, rayTarget);
|
|
1865
|
+
updates.from = edge;
|
|
1866
|
+
updates.position = edge;
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
if (arrow.toBinding?.elementId === removedElement.id) {
|
|
1870
|
+
updates.toBinding = void 0;
|
|
1871
|
+
if (bounds) {
|
|
1872
|
+
const angle = getArrowTangentAngle(arrow.from, arrow.to, arrow.bend, 1);
|
|
1873
|
+
const rayTarget = {
|
|
1874
|
+
x: arrow.to.x - Math.cos(angle) * 1e3,
|
|
1875
|
+
y: arrow.to.y - Math.sin(angle) * 1e3
|
|
1876
|
+
};
|
|
1877
|
+
updates.to = getEdgeIntersection(bounds, rayTarget);
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
if (Object.keys(updates).length > 0) {
|
|
1881
|
+
this.store.update(arrow.id, updates);
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1581
1884
|
}
|
|
1582
1885
|
removeDomNode(id) {
|
|
1583
1886
|
this.htmlContent.delete(id);
|
|
@@ -1807,6 +2110,7 @@ var EraserTool = class {
|
|
|
1807
2110
|
};
|
|
1808
2111
|
|
|
1809
2112
|
// src/tools/arrow-handles.ts
|
|
2113
|
+
var BIND_THRESHOLD = 20;
|
|
1810
2114
|
var HANDLE_RADIUS = 5;
|
|
1811
2115
|
var HANDLE_HIT_PADDING = 4;
|
|
1812
2116
|
var ARROW_HANDLE_CURSORS = {
|
|
@@ -1847,18 +2151,44 @@ function hitTestArrowHandles(world, selectedIds, ctx) {
|
|
|
1847
2151
|
function applyArrowHandleDrag(handle, elementId, world, ctx) {
|
|
1848
2152
|
const el = ctx.store.getById(elementId);
|
|
1849
2153
|
if (!el || el.type !== "arrow") return;
|
|
2154
|
+
const threshold = BIND_THRESHOLD / ctx.camera.zoom;
|
|
1850
2155
|
switch (handle) {
|
|
1851
|
-
case "start":
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
2156
|
+
case "start": {
|
|
2157
|
+
const excludeId = el.toBinding?.elementId;
|
|
2158
|
+
const target = findBindTarget(world, ctx.store, threshold, excludeId);
|
|
2159
|
+
if (target) {
|
|
2160
|
+
const center = getElementCenter(target);
|
|
2161
|
+
ctx.store.update(elementId, {
|
|
2162
|
+
from: center,
|
|
2163
|
+
position: center,
|
|
2164
|
+
fromBinding: { elementId: target.id }
|
|
2165
|
+
});
|
|
2166
|
+
} else {
|
|
2167
|
+
ctx.store.update(elementId, {
|
|
2168
|
+
from: { x: world.x, y: world.y },
|
|
2169
|
+
position: { x: world.x, y: world.y },
|
|
2170
|
+
fromBinding: void 0
|
|
2171
|
+
});
|
|
2172
|
+
}
|
|
1856
2173
|
break;
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
2174
|
+
}
|
|
2175
|
+
case "end": {
|
|
2176
|
+
const excludeId = el.fromBinding?.elementId;
|
|
2177
|
+
const target = findBindTarget(world, ctx.store, threshold, excludeId);
|
|
2178
|
+
if (target) {
|
|
2179
|
+
const center = getElementCenter(target);
|
|
2180
|
+
ctx.store.update(elementId, {
|
|
2181
|
+
to: center,
|
|
2182
|
+
toBinding: { elementId: target.id }
|
|
2183
|
+
});
|
|
2184
|
+
} else {
|
|
2185
|
+
ctx.store.update(elementId, {
|
|
2186
|
+
to: { x: world.x, y: world.y },
|
|
2187
|
+
toBinding: void 0
|
|
2188
|
+
});
|
|
2189
|
+
}
|
|
1861
2190
|
break;
|
|
2191
|
+
}
|
|
1862
2192
|
case "mid": {
|
|
1863
2193
|
const bend = getBendFromPoint(el.from, el.to, world);
|
|
1864
2194
|
ctx.store.update(elementId, { bend });
|
|
@@ -1867,6 +2197,16 @@ function applyArrowHandleDrag(handle, elementId, world, ctx) {
|
|
|
1867
2197
|
}
|
|
1868
2198
|
ctx.requestRender();
|
|
1869
2199
|
}
|
|
2200
|
+
function getArrowHandleDragTarget(handle, elementId, world, ctx) {
|
|
2201
|
+
if (handle === "mid") return null;
|
|
2202
|
+
const el = ctx.store.getById(elementId);
|
|
2203
|
+
if (!el || el.type !== "arrow") return null;
|
|
2204
|
+
const threshold = BIND_THRESHOLD / ctx.camera.zoom;
|
|
2205
|
+
const excludeId = handle === "start" ? el.toBinding?.elementId : el.fromBinding?.elementId;
|
|
2206
|
+
const target = findBindTarget(world, ctx.store, threshold, excludeId);
|
|
2207
|
+
if (!target) return null;
|
|
2208
|
+
return getElementBounds(target);
|
|
2209
|
+
}
|
|
1870
2210
|
function renderArrowHandles(canvasCtx, arrow, zoom) {
|
|
1871
2211
|
const radius = HANDLE_RADIUS / zoom;
|
|
1872
2212
|
const handles = getArrowHandlePositions(arrow);
|
|
@@ -1977,6 +2317,9 @@ var SelectTool = class {
|
|
|
1977
2317
|
const el = ctx.store.getById(id);
|
|
1978
2318
|
if (!el || el.locked) continue;
|
|
1979
2319
|
if (el.type === "arrow") {
|
|
2320
|
+
if (el.fromBinding || el.toBinding) {
|
|
2321
|
+
continue;
|
|
2322
|
+
}
|
|
1980
2323
|
ctx.store.update(id, {
|
|
1981
2324
|
position: { x: el.position.x + dx, y: el.position.y + dy },
|
|
1982
2325
|
from: { x: el.from.x + dx, y: el.from.y + dy },
|
|
@@ -1988,6 +2331,23 @@ var SelectTool = class {
|
|
|
1988
2331
|
});
|
|
1989
2332
|
}
|
|
1990
2333
|
}
|
|
2334
|
+
const movedNonArrowIds = /* @__PURE__ */ new Set();
|
|
2335
|
+
for (const id of this._selectedIds) {
|
|
2336
|
+
const el = ctx.store.getById(id);
|
|
2337
|
+
if (el && el.type !== "arrow") movedNonArrowIds.add(id);
|
|
2338
|
+
}
|
|
2339
|
+
if (movedNonArrowIds.size > 0) {
|
|
2340
|
+
const updatedArrows = /* @__PURE__ */ new Set();
|
|
2341
|
+
for (const id of movedNonArrowIds) {
|
|
2342
|
+
const boundArrows = findBoundArrows(id, ctx.store);
|
|
2343
|
+
for (const ba of boundArrows) {
|
|
2344
|
+
if (updatedArrows.has(ba.id)) continue;
|
|
2345
|
+
updatedArrows.add(ba.id);
|
|
2346
|
+
const updates = updateBoundArrow(ba, ctx.store);
|
|
2347
|
+
if (updates) ctx.store.update(ba.id, updates);
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
1991
2351
|
ctx.requestRender();
|
|
1992
2352
|
return;
|
|
1993
2353
|
}
|
|
@@ -2016,6 +2376,22 @@ var SelectTool = class {
|
|
|
2016
2376
|
renderOverlay(canvasCtx) {
|
|
2017
2377
|
this.renderMarquee(canvasCtx);
|
|
2018
2378
|
this.renderSelectionBoxes(canvasCtx);
|
|
2379
|
+
if (this.mode.type === "arrow-handle" && this.ctx) {
|
|
2380
|
+
const target = getArrowHandleDragTarget(
|
|
2381
|
+
this.mode.handle,
|
|
2382
|
+
this.mode.elementId,
|
|
2383
|
+
this.currentWorld,
|
|
2384
|
+
this.ctx
|
|
2385
|
+
);
|
|
2386
|
+
if (target) {
|
|
2387
|
+
canvasCtx.save();
|
|
2388
|
+
canvasCtx.strokeStyle = "#2196F3";
|
|
2389
|
+
canvasCtx.lineWidth = 2 / this.ctx.camera.zoom;
|
|
2390
|
+
canvasCtx.setLineDash([]);
|
|
2391
|
+
canvasCtx.strokeRect(target.x, target.y, target.w, target.h);
|
|
2392
|
+
canvasCtx.restore();
|
|
2393
|
+
}
|
|
2394
|
+
}
|
|
2019
2395
|
}
|
|
2020
2396
|
updateHoverCursor(world, ctx) {
|
|
2021
2397
|
const arrowHit = hitTestArrowHandles(world, this._selectedIds, ctx);
|
|
@@ -2074,6 +2450,11 @@ var SelectTool = class {
|
|
|
2074
2450
|
position: { x, y },
|
|
2075
2451
|
size: { w, h }
|
|
2076
2452
|
});
|
|
2453
|
+
const boundArrows = findBoundArrows(this.mode.elementId, ctx.store);
|
|
2454
|
+
for (const ba of boundArrows) {
|
|
2455
|
+
const updates = updateBoundArrow(ba, ctx.store);
|
|
2456
|
+
if (updates) ctx.store.update(ba.id, updates);
|
|
2457
|
+
}
|
|
2077
2458
|
ctx.requestRender();
|
|
2078
2459
|
}
|
|
2079
2460
|
hitTestResizeHandle(world, ctx) {
|
|
@@ -2128,6 +2509,7 @@ var SelectTool = class {
|
|
|
2128
2509
|
if (!el) continue;
|
|
2129
2510
|
if (el.type === "arrow") {
|
|
2130
2511
|
renderArrowHandles(canvasCtx, el, zoom);
|
|
2512
|
+
this.renderBindingHighlights(canvasCtx, el, zoom);
|
|
2131
2513
|
continue;
|
|
2132
2514
|
}
|
|
2133
2515
|
const bounds = this.getElementBounds(el);
|
|
@@ -2157,6 +2539,26 @@ var SelectTool = class {
|
|
|
2157
2539
|
}
|
|
2158
2540
|
canvasCtx.restore();
|
|
2159
2541
|
}
|
|
2542
|
+
renderBindingHighlights(canvasCtx, arrow, zoom) {
|
|
2543
|
+
if (!this.ctx) return;
|
|
2544
|
+
if (!arrow.fromBinding && !arrow.toBinding) return;
|
|
2545
|
+
const pad = SELECTION_PAD / zoom;
|
|
2546
|
+
canvasCtx.save();
|
|
2547
|
+
canvasCtx.strokeStyle = "#2196F3";
|
|
2548
|
+
canvasCtx.lineWidth = 2 / zoom;
|
|
2549
|
+
canvasCtx.setLineDash([]);
|
|
2550
|
+
const drawn = /* @__PURE__ */ new Set();
|
|
2551
|
+
for (const binding of [arrow.fromBinding, arrow.toBinding]) {
|
|
2552
|
+
if (!binding || drawn.has(binding.elementId)) continue;
|
|
2553
|
+
drawn.add(binding.elementId);
|
|
2554
|
+
const target = this.ctx.store.getById(binding.elementId);
|
|
2555
|
+
if (!target) continue;
|
|
2556
|
+
const bounds = getElementBounds(target);
|
|
2557
|
+
if (!bounds) continue;
|
|
2558
|
+
canvasCtx.strokeRect(bounds.x - pad, bounds.y - pad, bounds.w + pad * 2, bounds.h + pad * 2);
|
|
2559
|
+
}
|
|
2560
|
+
canvasCtx.restore();
|
|
2561
|
+
}
|
|
2160
2562
|
getMarqueeRect() {
|
|
2161
2563
|
if (this.mode.type !== "marquee") return null;
|
|
2162
2564
|
const { start } = this.mode;
|
|
@@ -2230,6 +2632,7 @@ var SelectTool = class {
|
|
|
2230
2632
|
};
|
|
2231
2633
|
|
|
2232
2634
|
// src/tools/arrow-tool.ts
|
|
2635
|
+
var BIND_THRESHOLD2 = 20;
|
|
2233
2636
|
var ArrowTool = class {
|
|
2234
2637
|
name = "arrow";
|
|
2235
2638
|
drawing = false;
|
|
@@ -2237,6 +2640,9 @@ var ArrowTool = class {
|
|
|
2237
2640
|
end = { x: 0, y: 0 };
|
|
2238
2641
|
color;
|
|
2239
2642
|
width;
|
|
2643
|
+
fromBinding;
|
|
2644
|
+
fromTarget = null;
|
|
2645
|
+
toTarget = null;
|
|
2240
2646
|
constructor(options = {}) {
|
|
2241
2647
|
this.color = options.color ?? "#000000";
|
|
2242
2648
|
this.width = options.width ?? 2;
|
|
@@ -2247,12 +2653,34 @@ var ArrowTool = class {
|
|
|
2247
2653
|
}
|
|
2248
2654
|
onPointerDown(state, ctx) {
|
|
2249
2655
|
this.drawing = true;
|
|
2250
|
-
|
|
2656
|
+
const world = ctx.camera.screenToWorld({ x: state.x, y: state.y });
|
|
2657
|
+
const threshold = BIND_THRESHOLD2 / ctx.camera.zoom;
|
|
2658
|
+
const target = findBindTarget(world, ctx.store, threshold);
|
|
2659
|
+
if (target) {
|
|
2660
|
+
this.start = getElementCenter(target);
|
|
2661
|
+
this.fromBinding = { elementId: target.id };
|
|
2662
|
+
this.fromTarget = target;
|
|
2663
|
+
} else {
|
|
2664
|
+
this.start = world;
|
|
2665
|
+
this.fromBinding = void 0;
|
|
2666
|
+
this.fromTarget = null;
|
|
2667
|
+
}
|
|
2251
2668
|
this.end = { ...this.start };
|
|
2669
|
+
this.toTarget = null;
|
|
2252
2670
|
}
|
|
2253
2671
|
onPointerMove(state, ctx) {
|
|
2254
2672
|
if (!this.drawing) return;
|
|
2255
|
-
|
|
2673
|
+
const world = ctx.camera.screenToWorld({ x: state.x, y: state.y });
|
|
2674
|
+
const threshold = BIND_THRESHOLD2 / ctx.camera.zoom;
|
|
2675
|
+
const excludeId = this.fromBinding?.elementId;
|
|
2676
|
+
const target = findBindTarget(world, ctx.store, threshold, excludeId);
|
|
2677
|
+
if (target) {
|
|
2678
|
+
this.end = getElementCenter(target);
|
|
2679
|
+
this.toTarget = target;
|
|
2680
|
+
} else {
|
|
2681
|
+
this.end = world;
|
|
2682
|
+
this.toTarget = null;
|
|
2683
|
+
}
|
|
2256
2684
|
ctx.requestRender();
|
|
2257
2685
|
}
|
|
2258
2686
|
onPointerUp(_state, ctx) {
|
|
@@ -2262,16 +2690,40 @@ var ArrowTool = class {
|
|
|
2262
2690
|
const arrow = createArrow({
|
|
2263
2691
|
from: this.start,
|
|
2264
2692
|
to: this.end,
|
|
2693
|
+
position: this.start,
|
|
2265
2694
|
color: this.color,
|
|
2266
|
-
width: this.width
|
|
2695
|
+
width: this.width,
|
|
2696
|
+
fromBinding: this.fromBinding,
|
|
2697
|
+
toBinding: this.toTarget ? { elementId: this.toTarget.id } : void 0
|
|
2267
2698
|
});
|
|
2268
2699
|
ctx.store.add(arrow);
|
|
2700
|
+
this.fromTarget = null;
|
|
2701
|
+
this.toTarget = null;
|
|
2269
2702
|
ctx.requestRender();
|
|
2703
|
+
ctx.switchTool?.("select");
|
|
2270
2704
|
}
|
|
2271
2705
|
renderOverlay(ctx) {
|
|
2272
2706
|
if (!this.drawing) return;
|
|
2273
2707
|
if (this.start.x === this.end.x && this.start.y === this.end.y) return;
|
|
2274
2708
|
ctx.save();
|
|
2709
|
+
if (this.fromTarget) {
|
|
2710
|
+
const bounds = getElementBounds(this.fromTarget);
|
|
2711
|
+
if (bounds) {
|
|
2712
|
+
ctx.strokeStyle = "#2196F3";
|
|
2713
|
+
ctx.lineWidth = 2;
|
|
2714
|
+
ctx.setLineDash([]);
|
|
2715
|
+
ctx.strokeRect(bounds.x, bounds.y, bounds.w, bounds.h);
|
|
2716
|
+
}
|
|
2717
|
+
}
|
|
2718
|
+
if (this.toTarget) {
|
|
2719
|
+
const bounds = getElementBounds(this.toTarget);
|
|
2720
|
+
if (bounds) {
|
|
2721
|
+
ctx.strokeStyle = "#2196F3";
|
|
2722
|
+
ctx.lineWidth = 2;
|
|
2723
|
+
ctx.setLineDash([]);
|
|
2724
|
+
ctx.strokeRect(bounds.x, bounds.y, bounds.w, bounds.h);
|
|
2725
|
+
}
|
|
2726
|
+
}
|
|
2275
2727
|
ctx.strokeStyle = this.color;
|
|
2276
2728
|
ctx.lineWidth = this.width;
|
|
2277
2729
|
ctx.lineCap = "round";
|
|
@@ -2331,6 +2783,47 @@ var NoteTool = class {
|
|
|
2331
2783
|
}
|
|
2332
2784
|
};
|
|
2333
2785
|
|
|
2786
|
+
// src/tools/text-tool.ts
|
|
2787
|
+
var TextTool = class {
|
|
2788
|
+
name = "text";
|
|
2789
|
+
fontSize;
|
|
2790
|
+
color;
|
|
2791
|
+
textAlign;
|
|
2792
|
+
constructor(options = {}) {
|
|
2793
|
+
this.fontSize = options.fontSize ?? 16;
|
|
2794
|
+
this.color = options.color ?? "#1a1a1a";
|
|
2795
|
+
this.textAlign = options.textAlign ?? "left";
|
|
2796
|
+
}
|
|
2797
|
+
setOptions(options) {
|
|
2798
|
+
if (options.fontSize !== void 0) this.fontSize = options.fontSize;
|
|
2799
|
+
if (options.color !== void 0) this.color = options.color;
|
|
2800
|
+
if (options.textAlign !== void 0) this.textAlign = options.textAlign;
|
|
2801
|
+
}
|
|
2802
|
+
onActivate(ctx) {
|
|
2803
|
+
ctx.setCursor?.("text");
|
|
2804
|
+
}
|
|
2805
|
+
onDeactivate(ctx) {
|
|
2806
|
+
ctx.setCursor?.("default");
|
|
2807
|
+
}
|
|
2808
|
+
onPointerDown(_state, _ctx) {
|
|
2809
|
+
}
|
|
2810
|
+
onPointerMove(_state, _ctx) {
|
|
2811
|
+
}
|
|
2812
|
+
onPointerUp(state, ctx) {
|
|
2813
|
+
const world = ctx.camera.screenToWorld({ x: state.x, y: state.y });
|
|
2814
|
+
const textEl = createText({
|
|
2815
|
+
position: world,
|
|
2816
|
+
fontSize: this.fontSize,
|
|
2817
|
+
color: this.color,
|
|
2818
|
+
textAlign: this.textAlign
|
|
2819
|
+
});
|
|
2820
|
+
ctx.store.add(textEl);
|
|
2821
|
+
ctx.requestRender();
|
|
2822
|
+
ctx.switchTool?.("select");
|
|
2823
|
+
ctx.editElement?.(textEl.id);
|
|
2824
|
+
}
|
|
2825
|
+
};
|
|
2826
|
+
|
|
2334
2827
|
// src/tools/image-tool.ts
|
|
2335
2828
|
var ImageTool = class {
|
|
2336
2829
|
name = "image";
|
|
@@ -2362,7 +2855,7 @@ var ImageTool = class {
|
|
|
2362
2855
|
};
|
|
2363
2856
|
|
|
2364
2857
|
// src/index.ts
|
|
2365
|
-
var VERSION = "0.
|
|
2858
|
+
var VERSION = "0.3.1";
|
|
2366
2859
|
export {
|
|
2367
2860
|
AddElementCommand,
|
|
2368
2861
|
ArrowTool,
|
|
@@ -2384,23 +2877,34 @@ export {
|
|
|
2384
2877
|
PencilTool,
|
|
2385
2878
|
RemoveElementCommand,
|
|
2386
2879
|
SelectTool,
|
|
2880
|
+
TextTool,
|
|
2387
2881
|
ToolManager,
|
|
2388
2882
|
UpdateElementCommand,
|
|
2389
2883
|
VERSION,
|
|
2390
2884
|
Viewport,
|
|
2885
|
+
clearStaleBindings,
|
|
2391
2886
|
createArrow,
|
|
2392
2887
|
createHtmlElement,
|
|
2393
2888
|
createId,
|
|
2394
2889
|
createImage,
|
|
2395
2890
|
createNote,
|
|
2396
2891
|
createStroke,
|
|
2892
|
+
createText,
|
|
2397
2893
|
exportState,
|
|
2894
|
+
findBindTarget,
|
|
2895
|
+
findBoundArrows,
|
|
2398
2896
|
getArrowBounds,
|
|
2399
2897
|
getArrowControlPoint,
|
|
2400
2898
|
getArrowMidpoint,
|
|
2401
2899
|
getArrowTangentAngle,
|
|
2402
2900
|
getBendFromPoint,
|
|
2901
|
+
getEdgeIntersection,
|
|
2902
|
+
getElementBounds,
|
|
2903
|
+
getElementCenter,
|
|
2904
|
+
isBindable,
|
|
2403
2905
|
isNearBezier,
|
|
2404
|
-
parseState
|
|
2906
|
+
parseState,
|
|
2907
|
+
unbindArrow,
|
|
2908
|
+
updateBoundArrow
|
|
2405
2909
|
};
|
|
2406
2910
|
//# sourceMappingURL=index.js.map
|