@fieldnotes/core 0.3.0 → 0.4.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 +603 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +79 -4
- package/dist/index.d.ts +79 -4
- package/dist/index.js +591 -27
- 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", "text"]);
|
|
73
|
+
var VALID_TYPES = /* @__PURE__ */ new Set(["stroke", "note", "arrow", "image", "html", "text", "shape"]);
|
|
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;
|
|
@@ -96,6 +111,9 @@ function migrateElement(obj) {
|
|
|
96
111
|
}
|
|
97
112
|
}
|
|
98
113
|
}
|
|
114
|
+
if (obj["type"] === "shape" && typeof obj["shape"] !== "string") {
|
|
115
|
+
obj["shape"] = "rectangle";
|
|
116
|
+
}
|
|
99
117
|
}
|
|
100
118
|
|
|
101
119
|
// src/core/auto-save.ts
|
|
@@ -678,6 +696,134 @@ function isNearLine(point, a, b, threshold) {
|
|
|
678
696
|
return Math.hypot(point.x - projX, point.y - projY) <= threshold;
|
|
679
697
|
}
|
|
680
698
|
|
|
699
|
+
// src/elements/arrow-binding.ts
|
|
700
|
+
var BINDABLE_TYPES = /* @__PURE__ */ new Set(["note", "text", "image", "html", "shape"]);
|
|
701
|
+
function isBindable(element) {
|
|
702
|
+
return BINDABLE_TYPES.has(element.type);
|
|
703
|
+
}
|
|
704
|
+
function getElementCenter(element) {
|
|
705
|
+
if (!("size" in element)) {
|
|
706
|
+
throw new Error(`getElementCenter: element type "${element.type}" has no size`);
|
|
707
|
+
}
|
|
708
|
+
return {
|
|
709
|
+
x: element.position.x + element.size.w / 2,
|
|
710
|
+
y: element.position.y + element.size.h / 2
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
function getElementBounds(element) {
|
|
714
|
+
if (!("size" in element)) return null;
|
|
715
|
+
return {
|
|
716
|
+
x: element.position.x,
|
|
717
|
+
y: element.position.y,
|
|
718
|
+
w: element.size.w,
|
|
719
|
+
h: element.size.h
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
function getEdgeIntersection(bounds, outsidePoint) {
|
|
723
|
+
const cx = bounds.x + bounds.w / 2;
|
|
724
|
+
const cy = bounds.y + bounds.h / 2;
|
|
725
|
+
const dx = outsidePoint.x - cx;
|
|
726
|
+
const dy = outsidePoint.y - cy;
|
|
727
|
+
if (dx === 0 && dy === 0) return { x: cx, y: cy };
|
|
728
|
+
const halfW = bounds.w / 2;
|
|
729
|
+
const halfH = bounds.h / 2;
|
|
730
|
+
const scaleX = dx !== 0 ? halfW / Math.abs(dx) : Infinity;
|
|
731
|
+
const scaleY = dy !== 0 ? halfH / Math.abs(dy) : Infinity;
|
|
732
|
+
const scale = Math.min(scaleX, scaleY);
|
|
733
|
+
return {
|
|
734
|
+
x: cx + dx * scale,
|
|
735
|
+
y: cy + dy * scale
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
function findBindTarget(point, store, threshold, excludeId) {
|
|
739
|
+
let closest = null;
|
|
740
|
+
let closestDist = Infinity;
|
|
741
|
+
for (const el of store.getAll()) {
|
|
742
|
+
if (!isBindable(el)) continue;
|
|
743
|
+
if (excludeId && el.id === excludeId) continue;
|
|
744
|
+
const bounds = getElementBounds(el);
|
|
745
|
+
if (!bounds) continue;
|
|
746
|
+
const dist = distToBounds(point, bounds);
|
|
747
|
+
if (dist <= threshold && dist < closestDist) {
|
|
748
|
+
closest = el;
|
|
749
|
+
closestDist = dist;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
return closest;
|
|
753
|
+
}
|
|
754
|
+
function distToBounds(point, bounds) {
|
|
755
|
+
const clampedX = Math.max(bounds.x, Math.min(point.x, bounds.x + bounds.w));
|
|
756
|
+
const clampedY = Math.max(bounds.y, Math.min(point.y, bounds.y + bounds.h));
|
|
757
|
+
return Math.hypot(point.x - clampedX, point.y - clampedY);
|
|
758
|
+
}
|
|
759
|
+
function findBoundArrows(elementId, store) {
|
|
760
|
+
return store.getElementsByType("arrow").filter((a) => a.fromBinding?.elementId === elementId || a.toBinding?.elementId === elementId);
|
|
761
|
+
}
|
|
762
|
+
function updateBoundArrow(arrow, store) {
|
|
763
|
+
if (!arrow.fromBinding && !arrow.toBinding) return null;
|
|
764
|
+
const updates = {};
|
|
765
|
+
if (arrow.fromBinding) {
|
|
766
|
+
const el = store.getById(arrow.fromBinding.elementId);
|
|
767
|
+
if (el) {
|
|
768
|
+
const center = getElementCenter(el);
|
|
769
|
+
updates.from = center;
|
|
770
|
+
updates.position = center;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
if (arrow.toBinding) {
|
|
774
|
+
const el = store.getById(arrow.toBinding.elementId);
|
|
775
|
+
if (el) {
|
|
776
|
+
updates.to = getElementCenter(el);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
return Object.keys(updates).length > 0 ? updates : null;
|
|
780
|
+
}
|
|
781
|
+
function clearStaleBindings(arrow, store) {
|
|
782
|
+
const updates = {};
|
|
783
|
+
let hasUpdates = false;
|
|
784
|
+
if (arrow.fromBinding && !store.getById(arrow.fromBinding.elementId)) {
|
|
785
|
+
updates.fromBinding = void 0;
|
|
786
|
+
hasUpdates = true;
|
|
787
|
+
}
|
|
788
|
+
if (arrow.toBinding && !store.getById(arrow.toBinding.elementId)) {
|
|
789
|
+
updates.toBinding = void 0;
|
|
790
|
+
hasUpdates = true;
|
|
791
|
+
}
|
|
792
|
+
return hasUpdates ? updates : null;
|
|
793
|
+
}
|
|
794
|
+
function unbindArrow(arrow, store) {
|
|
795
|
+
const updates = {};
|
|
796
|
+
if (arrow.fromBinding) {
|
|
797
|
+
const el = store.getById(arrow.fromBinding.elementId);
|
|
798
|
+
const bounds = el ? getElementBounds(el) : null;
|
|
799
|
+
if (bounds) {
|
|
800
|
+
const angle = getArrowTangentAngle(arrow.from, arrow.to, arrow.bend, 0);
|
|
801
|
+
const rayTarget = {
|
|
802
|
+
x: arrow.from.x + Math.cos(angle) * 1e3,
|
|
803
|
+
y: arrow.from.y + Math.sin(angle) * 1e3
|
|
804
|
+
};
|
|
805
|
+
const edge = getEdgeIntersection(bounds, rayTarget);
|
|
806
|
+
updates.from = edge;
|
|
807
|
+
updates.position = edge;
|
|
808
|
+
}
|
|
809
|
+
updates.fromBinding = void 0;
|
|
810
|
+
}
|
|
811
|
+
if (arrow.toBinding) {
|
|
812
|
+
const el = store.getById(arrow.toBinding.elementId);
|
|
813
|
+
const bounds = el ? getElementBounds(el) : null;
|
|
814
|
+
if (bounds) {
|
|
815
|
+
const angle = getArrowTangentAngle(arrow.from, arrow.to, arrow.bend, 1);
|
|
816
|
+
const rayTarget = {
|
|
817
|
+
x: arrow.to.x - Math.cos(angle) * 1e3,
|
|
818
|
+
y: arrow.to.y - Math.sin(angle) * 1e3
|
|
819
|
+
};
|
|
820
|
+
updates.to = getEdgeIntersection(bounds, rayTarget);
|
|
821
|
+
}
|
|
822
|
+
updates.toBinding = void 0;
|
|
823
|
+
}
|
|
824
|
+
return updates;
|
|
825
|
+
}
|
|
826
|
+
|
|
681
827
|
// src/elements/stroke-smoothing.ts
|
|
682
828
|
var MIN_PRESSURE_SCALE = 0.2;
|
|
683
829
|
function pressureToWidth(pressure, baseWidth) {
|
|
@@ -756,6 +902,10 @@ var DOM_ELEMENT_TYPES = /* @__PURE__ */ new Set(["note", "image", "html", "text"
|
|
|
756
902
|
var ARROWHEAD_LENGTH = 12;
|
|
757
903
|
var ARROWHEAD_ANGLE = Math.PI / 6;
|
|
758
904
|
var ElementRenderer = class {
|
|
905
|
+
store = null;
|
|
906
|
+
setStore(store) {
|
|
907
|
+
this.store = store;
|
|
908
|
+
}
|
|
759
909
|
isDomElement(element) {
|
|
760
910
|
return DOM_ELEMENT_TYPES.has(element.type);
|
|
761
911
|
}
|
|
@@ -767,6 +917,9 @@ var ElementRenderer = class {
|
|
|
767
917
|
case "arrow":
|
|
768
918
|
this.renderArrow(ctx, element);
|
|
769
919
|
break;
|
|
920
|
+
case "shape":
|
|
921
|
+
this.renderShape(ctx, element);
|
|
922
|
+
break;
|
|
770
923
|
}
|
|
771
924
|
}
|
|
772
925
|
renderStroke(ctx, stroke) {
|
|
@@ -789,38 +942,119 @@ var ElementRenderer = class {
|
|
|
789
942
|
ctx.restore();
|
|
790
943
|
}
|
|
791
944
|
renderArrow(ctx, arrow) {
|
|
945
|
+
const { visualFrom, visualTo } = this.getVisualEndpoints(arrow);
|
|
792
946
|
ctx.save();
|
|
793
947
|
ctx.strokeStyle = arrow.color;
|
|
794
948
|
ctx.lineWidth = arrow.width;
|
|
795
949
|
ctx.lineCap = "round";
|
|
950
|
+
if (arrow.fromBinding || arrow.toBinding) {
|
|
951
|
+
ctx.setLineDash([8, 4]);
|
|
952
|
+
}
|
|
796
953
|
ctx.beginPath();
|
|
797
|
-
ctx.moveTo(
|
|
954
|
+
ctx.moveTo(visualFrom.x, visualFrom.y);
|
|
798
955
|
if (arrow.bend !== 0) {
|
|
799
956
|
const cp = getArrowControlPoint(arrow.from, arrow.to, arrow.bend);
|
|
800
|
-
ctx.quadraticCurveTo(cp.x, cp.y,
|
|
957
|
+
ctx.quadraticCurveTo(cp.x, cp.y, visualTo.x, visualTo.y);
|
|
801
958
|
} else {
|
|
802
|
-
ctx.lineTo(
|
|
959
|
+
ctx.lineTo(visualTo.x, visualTo.y);
|
|
803
960
|
}
|
|
804
961
|
ctx.stroke();
|
|
805
|
-
this.renderArrowhead(ctx, arrow);
|
|
962
|
+
this.renderArrowhead(ctx, arrow, visualTo);
|
|
806
963
|
ctx.restore();
|
|
807
964
|
}
|
|
808
|
-
renderArrowhead(ctx, arrow) {
|
|
965
|
+
renderArrowhead(ctx, arrow, tip) {
|
|
809
966
|
const angle = getArrowTangentAngle(arrow.from, arrow.to, arrow.bend, 1);
|
|
810
967
|
ctx.beginPath();
|
|
811
|
-
ctx.moveTo(
|
|
968
|
+
ctx.moveTo(tip.x, tip.y);
|
|
812
969
|
ctx.lineTo(
|
|
813
|
-
|
|
814
|
-
|
|
970
|
+
tip.x - ARROWHEAD_LENGTH * Math.cos(angle - ARROWHEAD_ANGLE),
|
|
971
|
+
tip.y - ARROWHEAD_LENGTH * Math.sin(angle - ARROWHEAD_ANGLE)
|
|
815
972
|
);
|
|
816
973
|
ctx.lineTo(
|
|
817
|
-
|
|
818
|
-
|
|
974
|
+
tip.x - ARROWHEAD_LENGTH * Math.cos(angle + ARROWHEAD_ANGLE),
|
|
975
|
+
tip.y - ARROWHEAD_LENGTH * Math.sin(angle + ARROWHEAD_ANGLE)
|
|
819
976
|
);
|
|
820
977
|
ctx.closePath();
|
|
821
978
|
ctx.fillStyle = arrow.color;
|
|
822
979
|
ctx.fill();
|
|
823
980
|
}
|
|
981
|
+
getVisualEndpoints(arrow) {
|
|
982
|
+
let visualFrom = arrow.from;
|
|
983
|
+
let visualTo = arrow.to;
|
|
984
|
+
if (!this.store) return { visualFrom, visualTo };
|
|
985
|
+
if (arrow.fromBinding) {
|
|
986
|
+
const el = this.store.getById(arrow.fromBinding.elementId);
|
|
987
|
+
if (el) {
|
|
988
|
+
const bounds = getElementBounds(el);
|
|
989
|
+
if (bounds) {
|
|
990
|
+
const tangentAngle = getArrowTangentAngle(arrow.from, arrow.to, arrow.bend, 0);
|
|
991
|
+
const rayTarget = {
|
|
992
|
+
x: arrow.from.x + Math.cos(tangentAngle) * 1e3,
|
|
993
|
+
y: arrow.from.y + Math.sin(tangentAngle) * 1e3
|
|
994
|
+
};
|
|
995
|
+
visualFrom = getEdgeIntersection(bounds, rayTarget);
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
if (arrow.toBinding) {
|
|
1000
|
+
const el = this.store.getById(arrow.toBinding.elementId);
|
|
1001
|
+
if (el) {
|
|
1002
|
+
const bounds = getElementBounds(el);
|
|
1003
|
+
if (bounds) {
|
|
1004
|
+
const tangentAngle = getArrowTangentAngle(arrow.from, arrow.to, arrow.bend, 1);
|
|
1005
|
+
const rayTarget = {
|
|
1006
|
+
x: arrow.to.x - Math.cos(tangentAngle) * 1e3,
|
|
1007
|
+
y: arrow.to.y - Math.sin(tangentAngle) * 1e3
|
|
1008
|
+
};
|
|
1009
|
+
visualTo = getEdgeIntersection(bounds, rayTarget);
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
return { visualFrom, visualTo };
|
|
1014
|
+
}
|
|
1015
|
+
renderShape(ctx, shape) {
|
|
1016
|
+
ctx.save();
|
|
1017
|
+
if (shape.fillColor !== "none") {
|
|
1018
|
+
ctx.fillStyle = shape.fillColor;
|
|
1019
|
+
this.fillShapePath(ctx, shape);
|
|
1020
|
+
}
|
|
1021
|
+
if (shape.strokeWidth > 0) {
|
|
1022
|
+
ctx.strokeStyle = shape.strokeColor;
|
|
1023
|
+
ctx.lineWidth = shape.strokeWidth;
|
|
1024
|
+
this.strokeShapePath(ctx, shape);
|
|
1025
|
+
}
|
|
1026
|
+
ctx.restore();
|
|
1027
|
+
}
|
|
1028
|
+
fillShapePath(ctx, shape) {
|
|
1029
|
+
switch (shape.shape) {
|
|
1030
|
+
case "rectangle":
|
|
1031
|
+
ctx.fillRect(shape.position.x, shape.position.y, shape.size.w, shape.size.h);
|
|
1032
|
+
break;
|
|
1033
|
+
case "ellipse": {
|
|
1034
|
+
const cx = shape.position.x + shape.size.w / 2;
|
|
1035
|
+
const cy = shape.position.y + shape.size.h / 2;
|
|
1036
|
+
ctx.beginPath();
|
|
1037
|
+
ctx.ellipse(cx, cy, shape.size.w / 2, shape.size.h / 2, 0, 0, Math.PI * 2);
|
|
1038
|
+
ctx.fill();
|
|
1039
|
+
break;
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
strokeShapePath(ctx, shape) {
|
|
1044
|
+
switch (shape.shape) {
|
|
1045
|
+
case "rectangle":
|
|
1046
|
+
ctx.strokeRect(shape.position.x, shape.position.y, shape.size.w, shape.size.h);
|
|
1047
|
+
break;
|
|
1048
|
+
case "ellipse": {
|
|
1049
|
+
const cx = shape.position.x + shape.size.w / 2;
|
|
1050
|
+
const cy = shape.position.y + shape.size.h / 2;
|
|
1051
|
+
ctx.beginPath();
|
|
1052
|
+
ctx.ellipse(cx, cy, shape.size.w / 2, shape.size.h / 2, 0, 0, Math.PI * 2);
|
|
1053
|
+
ctx.stroke();
|
|
1054
|
+
break;
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
824
1058
|
};
|
|
825
1059
|
|
|
826
1060
|
// src/elements/note-editor.ts
|
|
@@ -1191,7 +1425,7 @@ function createNote(input) {
|
|
|
1191
1425
|
};
|
|
1192
1426
|
}
|
|
1193
1427
|
function createArrow(input) {
|
|
1194
|
-
|
|
1428
|
+
const result = {
|
|
1195
1429
|
id: createId("arrow"),
|
|
1196
1430
|
type: "arrow",
|
|
1197
1431
|
position: input.position ?? { x: 0, y: 0 },
|
|
@@ -1203,6 +1437,9 @@ function createArrow(input) {
|
|
|
1203
1437
|
color: input.color ?? "#000000",
|
|
1204
1438
|
width: input.width ?? 2
|
|
1205
1439
|
};
|
|
1440
|
+
if (input.fromBinding) result.fromBinding = input.fromBinding;
|
|
1441
|
+
if (input.toBinding) result.toBinding = input.toBinding;
|
|
1442
|
+
return result;
|
|
1206
1443
|
}
|
|
1207
1444
|
function createImage(input) {
|
|
1208
1445
|
return {
|
|
@@ -1225,6 +1462,20 @@ function createHtmlElement(input) {
|
|
|
1225
1462
|
size: input.size
|
|
1226
1463
|
};
|
|
1227
1464
|
}
|
|
1465
|
+
function createShape(input) {
|
|
1466
|
+
return {
|
|
1467
|
+
id: createId("shape"),
|
|
1468
|
+
type: "shape",
|
|
1469
|
+
position: input.position,
|
|
1470
|
+
zIndex: input.zIndex ?? 0,
|
|
1471
|
+
locked: input.locked ?? false,
|
|
1472
|
+
shape: input.shape ?? "rectangle",
|
|
1473
|
+
size: input.size,
|
|
1474
|
+
strokeColor: input.strokeColor ?? "#000000",
|
|
1475
|
+
strokeWidth: input.strokeWidth ?? 2,
|
|
1476
|
+
fillColor: input.fillColor ?? "none"
|
|
1477
|
+
};
|
|
1478
|
+
}
|
|
1228
1479
|
function createText(input) {
|
|
1229
1480
|
return {
|
|
1230
1481
|
id: createId("text"),
|
|
@@ -1249,6 +1500,7 @@ var Viewport = class {
|
|
|
1249
1500
|
this.store = new ElementStore();
|
|
1250
1501
|
this.toolManager = new ToolManager();
|
|
1251
1502
|
this.renderer = new ElementRenderer();
|
|
1503
|
+
this.renderer.setStore(this.store);
|
|
1252
1504
|
this.noteEditor = new NoteEditor();
|
|
1253
1505
|
this.noteEditor.setOnStop((id) => this.onTextEditStop(id));
|
|
1254
1506
|
this.history = new HistoryStack();
|
|
@@ -1281,7 +1533,10 @@ var Viewport = class {
|
|
|
1281
1533
|
});
|
|
1282
1534
|
this.unsubStore = [
|
|
1283
1535
|
this.store.on("add", () => this.requestRender()),
|
|
1284
|
-
this.store.on("remove", (el) =>
|
|
1536
|
+
this.store.on("remove", (el) => {
|
|
1537
|
+
this.unbindArrowsFrom(el);
|
|
1538
|
+
this.removeDomNode(el.id);
|
|
1539
|
+
}),
|
|
1285
1540
|
this.store.on("update", () => this.requestRender()),
|
|
1286
1541
|
this.store.on("clear", () => this.clearDomNodes())
|
|
1287
1542
|
];
|
|
@@ -1656,6 +1911,40 @@ var Viewport = class {
|
|
|
1656
1911
|
}
|
|
1657
1912
|
}
|
|
1658
1913
|
}
|
|
1914
|
+
unbindArrowsFrom(removedElement) {
|
|
1915
|
+
const boundArrows = findBoundArrows(removedElement.id, this.store);
|
|
1916
|
+
const bounds = getElementBounds(removedElement);
|
|
1917
|
+
for (const arrow of boundArrows) {
|
|
1918
|
+
const updates = {};
|
|
1919
|
+
if (arrow.fromBinding?.elementId === removedElement.id) {
|
|
1920
|
+
updates.fromBinding = void 0;
|
|
1921
|
+
if (bounds) {
|
|
1922
|
+
const angle = getArrowTangentAngle(arrow.from, arrow.to, arrow.bend, 0);
|
|
1923
|
+
const rayTarget = {
|
|
1924
|
+
x: arrow.from.x + Math.cos(angle) * 1e3,
|
|
1925
|
+
y: arrow.from.y + Math.sin(angle) * 1e3
|
|
1926
|
+
};
|
|
1927
|
+
const edge = getEdgeIntersection(bounds, rayTarget);
|
|
1928
|
+
updates.from = edge;
|
|
1929
|
+
updates.position = edge;
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
if (arrow.toBinding?.elementId === removedElement.id) {
|
|
1933
|
+
updates.toBinding = void 0;
|
|
1934
|
+
if (bounds) {
|
|
1935
|
+
const angle = getArrowTangentAngle(arrow.from, arrow.to, arrow.bend, 1);
|
|
1936
|
+
const rayTarget = {
|
|
1937
|
+
x: arrow.to.x - Math.cos(angle) * 1e3,
|
|
1938
|
+
y: arrow.to.y - Math.sin(angle) * 1e3
|
|
1939
|
+
};
|
|
1940
|
+
updates.to = getEdgeIntersection(bounds, rayTarget);
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
if (Object.keys(updates).length > 0) {
|
|
1944
|
+
this.store.update(arrow.id, updates);
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1659
1948
|
removeDomNode(id) {
|
|
1660
1949
|
this.htmlContent.delete(id);
|
|
1661
1950
|
const node = this.domNodes.get(id);
|
|
@@ -1884,6 +2173,7 @@ var EraserTool = class {
|
|
|
1884
2173
|
};
|
|
1885
2174
|
|
|
1886
2175
|
// src/tools/arrow-handles.ts
|
|
2176
|
+
var BIND_THRESHOLD = 20;
|
|
1887
2177
|
var HANDLE_RADIUS = 5;
|
|
1888
2178
|
var HANDLE_HIT_PADDING = 4;
|
|
1889
2179
|
var ARROW_HANDLE_CURSORS = {
|
|
@@ -1924,18 +2214,44 @@ function hitTestArrowHandles(world, selectedIds, ctx) {
|
|
|
1924
2214
|
function applyArrowHandleDrag(handle, elementId, world, ctx) {
|
|
1925
2215
|
const el = ctx.store.getById(elementId);
|
|
1926
2216
|
if (!el || el.type !== "arrow") return;
|
|
2217
|
+
const threshold = BIND_THRESHOLD / ctx.camera.zoom;
|
|
1927
2218
|
switch (handle) {
|
|
1928
|
-
case "start":
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
2219
|
+
case "start": {
|
|
2220
|
+
const excludeId = el.toBinding?.elementId;
|
|
2221
|
+
const target = findBindTarget(world, ctx.store, threshold, excludeId);
|
|
2222
|
+
if (target) {
|
|
2223
|
+
const center = getElementCenter(target);
|
|
2224
|
+
ctx.store.update(elementId, {
|
|
2225
|
+
from: center,
|
|
2226
|
+
position: center,
|
|
2227
|
+
fromBinding: { elementId: target.id }
|
|
2228
|
+
});
|
|
2229
|
+
} else {
|
|
2230
|
+
ctx.store.update(elementId, {
|
|
2231
|
+
from: { x: world.x, y: world.y },
|
|
2232
|
+
position: { x: world.x, y: world.y },
|
|
2233
|
+
fromBinding: void 0
|
|
2234
|
+
});
|
|
2235
|
+
}
|
|
1933
2236
|
break;
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
2237
|
+
}
|
|
2238
|
+
case "end": {
|
|
2239
|
+
const excludeId = el.fromBinding?.elementId;
|
|
2240
|
+
const target = findBindTarget(world, ctx.store, threshold, excludeId);
|
|
2241
|
+
if (target) {
|
|
2242
|
+
const center = getElementCenter(target);
|
|
2243
|
+
ctx.store.update(elementId, {
|
|
2244
|
+
to: center,
|
|
2245
|
+
toBinding: { elementId: target.id }
|
|
2246
|
+
});
|
|
2247
|
+
} else {
|
|
2248
|
+
ctx.store.update(elementId, {
|
|
2249
|
+
to: { x: world.x, y: world.y },
|
|
2250
|
+
toBinding: void 0
|
|
2251
|
+
});
|
|
2252
|
+
}
|
|
1938
2253
|
break;
|
|
2254
|
+
}
|
|
1939
2255
|
case "mid": {
|
|
1940
2256
|
const bend = getBendFromPoint(el.from, el.to, world);
|
|
1941
2257
|
ctx.store.update(elementId, { bend });
|
|
@@ -1944,6 +2260,16 @@ function applyArrowHandleDrag(handle, elementId, world, ctx) {
|
|
|
1944
2260
|
}
|
|
1945
2261
|
ctx.requestRender();
|
|
1946
2262
|
}
|
|
2263
|
+
function getArrowHandleDragTarget(handle, elementId, world, ctx) {
|
|
2264
|
+
if (handle === "mid") return null;
|
|
2265
|
+
const el = ctx.store.getById(elementId);
|
|
2266
|
+
if (!el || el.type !== "arrow") return null;
|
|
2267
|
+
const threshold = BIND_THRESHOLD / ctx.camera.zoom;
|
|
2268
|
+
const excludeId = handle === "start" ? el.toBinding?.elementId : el.fromBinding?.elementId;
|
|
2269
|
+
const target = findBindTarget(world, ctx.store, threshold, excludeId);
|
|
2270
|
+
if (!target) return null;
|
|
2271
|
+
return getElementBounds(target);
|
|
2272
|
+
}
|
|
1947
2273
|
function renderArrowHandles(canvasCtx, arrow, zoom) {
|
|
1948
2274
|
const radius = HANDLE_RADIUS / zoom;
|
|
1949
2275
|
const handles = getArrowHandlePositions(arrow);
|
|
@@ -2054,6 +2380,9 @@ var SelectTool = class {
|
|
|
2054
2380
|
const el = ctx.store.getById(id);
|
|
2055
2381
|
if (!el || el.locked) continue;
|
|
2056
2382
|
if (el.type === "arrow") {
|
|
2383
|
+
if (el.fromBinding || el.toBinding) {
|
|
2384
|
+
continue;
|
|
2385
|
+
}
|
|
2057
2386
|
ctx.store.update(id, {
|
|
2058
2387
|
position: { x: el.position.x + dx, y: el.position.y + dy },
|
|
2059
2388
|
from: { x: el.from.x + dx, y: el.from.y + dy },
|
|
@@ -2065,6 +2394,23 @@ var SelectTool = class {
|
|
|
2065
2394
|
});
|
|
2066
2395
|
}
|
|
2067
2396
|
}
|
|
2397
|
+
const movedNonArrowIds = /* @__PURE__ */ new Set();
|
|
2398
|
+
for (const id of this._selectedIds) {
|
|
2399
|
+
const el = ctx.store.getById(id);
|
|
2400
|
+
if (el && el.type !== "arrow") movedNonArrowIds.add(id);
|
|
2401
|
+
}
|
|
2402
|
+
if (movedNonArrowIds.size > 0) {
|
|
2403
|
+
const updatedArrows = /* @__PURE__ */ new Set();
|
|
2404
|
+
for (const id of movedNonArrowIds) {
|
|
2405
|
+
const boundArrows = findBoundArrows(id, ctx.store);
|
|
2406
|
+
for (const ba of boundArrows) {
|
|
2407
|
+
if (updatedArrows.has(ba.id)) continue;
|
|
2408
|
+
updatedArrows.add(ba.id);
|
|
2409
|
+
const updates = updateBoundArrow(ba, ctx.store);
|
|
2410
|
+
if (updates) ctx.store.update(ba.id, updates);
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2068
2414
|
ctx.requestRender();
|
|
2069
2415
|
return;
|
|
2070
2416
|
}
|
|
@@ -2093,6 +2439,22 @@ var SelectTool = class {
|
|
|
2093
2439
|
renderOverlay(canvasCtx) {
|
|
2094
2440
|
this.renderMarquee(canvasCtx);
|
|
2095
2441
|
this.renderSelectionBoxes(canvasCtx);
|
|
2442
|
+
if (this.mode.type === "arrow-handle" && this.ctx) {
|
|
2443
|
+
const target = getArrowHandleDragTarget(
|
|
2444
|
+
this.mode.handle,
|
|
2445
|
+
this.mode.elementId,
|
|
2446
|
+
this.currentWorld,
|
|
2447
|
+
this.ctx
|
|
2448
|
+
);
|
|
2449
|
+
if (target) {
|
|
2450
|
+
canvasCtx.save();
|
|
2451
|
+
canvasCtx.strokeStyle = "#2196F3";
|
|
2452
|
+
canvasCtx.lineWidth = 2 / this.ctx.camera.zoom;
|
|
2453
|
+
canvasCtx.setLineDash([]);
|
|
2454
|
+
canvasCtx.strokeRect(target.x, target.y, target.w, target.h);
|
|
2455
|
+
canvasCtx.restore();
|
|
2456
|
+
}
|
|
2457
|
+
}
|
|
2096
2458
|
}
|
|
2097
2459
|
updateHoverCursor(world, ctx) {
|
|
2098
2460
|
const arrowHit = hitTestArrowHandles(world, this._selectedIds, ctx);
|
|
@@ -2151,6 +2513,11 @@ var SelectTool = class {
|
|
|
2151
2513
|
position: { x, y },
|
|
2152
2514
|
size: { w, h }
|
|
2153
2515
|
});
|
|
2516
|
+
const boundArrows = findBoundArrows(this.mode.elementId, ctx.store);
|
|
2517
|
+
for (const ba of boundArrows) {
|
|
2518
|
+
const updates = updateBoundArrow(ba, ctx.store);
|
|
2519
|
+
if (updates) ctx.store.update(ba.id, updates);
|
|
2520
|
+
}
|
|
2154
2521
|
ctx.requestRender();
|
|
2155
2522
|
}
|
|
2156
2523
|
hitTestResizeHandle(world, ctx) {
|
|
@@ -2205,6 +2572,7 @@ var SelectTool = class {
|
|
|
2205
2572
|
if (!el) continue;
|
|
2206
2573
|
if (el.type === "arrow") {
|
|
2207
2574
|
renderArrowHandles(canvasCtx, el, zoom);
|
|
2575
|
+
this.renderBindingHighlights(canvasCtx, el, zoom);
|
|
2208
2576
|
continue;
|
|
2209
2577
|
}
|
|
2210
2578
|
const bounds = this.getElementBounds(el);
|
|
@@ -2234,6 +2602,26 @@ var SelectTool = class {
|
|
|
2234
2602
|
}
|
|
2235
2603
|
canvasCtx.restore();
|
|
2236
2604
|
}
|
|
2605
|
+
renderBindingHighlights(canvasCtx, arrow, zoom) {
|
|
2606
|
+
if (!this.ctx) return;
|
|
2607
|
+
if (!arrow.fromBinding && !arrow.toBinding) return;
|
|
2608
|
+
const pad = SELECTION_PAD / zoom;
|
|
2609
|
+
canvasCtx.save();
|
|
2610
|
+
canvasCtx.strokeStyle = "#2196F3";
|
|
2611
|
+
canvasCtx.lineWidth = 2 / zoom;
|
|
2612
|
+
canvasCtx.setLineDash([]);
|
|
2613
|
+
const drawn = /* @__PURE__ */ new Set();
|
|
2614
|
+
for (const binding of [arrow.fromBinding, arrow.toBinding]) {
|
|
2615
|
+
if (!binding || drawn.has(binding.elementId)) continue;
|
|
2616
|
+
drawn.add(binding.elementId);
|
|
2617
|
+
const target = this.ctx.store.getById(binding.elementId);
|
|
2618
|
+
if (!target) continue;
|
|
2619
|
+
const bounds = getElementBounds(target);
|
|
2620
|
+
if (!bounds) continue;
|
|
2621
|
+
canvasCtx.strokeRect(bounds.x - pad, bounds.y - pad, bounds.w + pad * 2, bounds.h + pad * 2);
|
|
2622
|
+
}
|
|
2623
|
+
canvasCtx.restore();
|
|
2624
|
+
}
|
|
2237
2625
|
getMarqueeRect() {
|
|
2238
2626
|
if (this.mode.type !== "marquee") return null;
|
|
2239
2627
|
const { start } = this.mode;
|
|
@@ -2307,6 +2695,7 @@ var SelectTool = class {
|
|
|
2307
2695
|
};
|
|
2308
2696
|
|
|
2309
2697
|
// src/tools/arrow-tool.ts
|
|
2698
|
+
var BIND_THRESHOLD2 = 20;
|
|
2310
2699
|
var ArrowTool = class {
|
|
2311
2700
|
name = "arrow";
|
|
2312
2701
|
drawing = false;
|
|
@@ -2314,6 +2703,9 @@ var ArrowTool = class {
|
|
|
2314
2703
|
end = { x: 0, y: 0 };
|
|
2315
2704
|
color;
|
|
2316
2705
|
width;
|
|
2706
|
+
fromBinding;
|
|
2707
|
+
fromTarget = null;
|
|
2708
|
+
toTarget = null;
|
|
2317
2709
|
constructor(options = {}) {
|
|
2318
2710
|
this.color = options.color ?? "#000000";
|
|
2319
2711
|
this.width = options.width ?? 2;
|
|
@@ -2324,12 +2716,34 @@ var ArrowTool = class {
|
|
|
2324
2716
|
}
|
|
2325
2717
|
onPointerDown(state, ctx) {
|
|
2326
2718
|
this.drawing = true;
|
|
2327
|
-
|
|
2719
|
+
const world = ctx.camera.screenToWorld({ x: state.x, y: state.y });
|
|
2720
|
+
const threshold = BIND_THRESHOLD2 / ctx.camera.zoom;
|
|
2721
|
+
const target = findBindTarget(world, ctx.store, threshold);
|
|
2722
|
+
if (target) {
|
|
2723
|
+
this.start = getElementCenter(target);
|
|
2724
|
+
this.fromBinding = { elementId: target.id };
|
|
2725
|
+
this.fromTarget = target;
|
|
2726
|
+
} else {
|
|
2727
|
+
this.start = world;
|
|
2728
|
+
this.fromBinding = void 0;
|
|
2729
|
+
this.fromTarget = null;
|
|
2730
|
+
}
|
|
2328
2731
|
this.end = { ...this.start };
|
|
2732
|
+
this.toTarget = null;
|
|
2329
2733
|
}
|
|
2330
2734
|
onPointerMove(state, ctx) {
|
|
2331
2735
|
if (!this.drawing) return;
|
|
2332
|
-
|
|
2736
|
+
const world = ctx.camera.screenToWorld({ x: state.x, y: state.y });
|
|
2737
|
+
const threshold = BIND_THRESHOLD2 / ctx.camera.zoom;
|
|
2738
|
+
const excludeId = this.fromBinding?.elementId;
|
|
2739
|
+
const target = findBindTarget(world, ctx.store, threshold, excludeId);
|
|
2740
|
+
if (target) {
|
|
2741
|
+
this.end = getElementCenter(target);
|
|
2742
|
+
this.toTarget = target;
|
|
2743
|
+
} else {
|
|
2744
|
+
this.end = world;
|
|
2745
|
+
this.toTarget = null;
|
|
2746
|
+
}
|
|
2333
2747
|
ctx.requestRender();
|
|
2334
2748
|
}
|
|
2335
2749
|
onPointerUp(_state, ctx) {
|
|
@@ -2339,16 +2753,40 @@ var ArrowTool = class {
|
|
|
2339
2753
|
const arrow = createArrow({
|
|
2340
2754
|
from: this.start,
|
|
2341
2755
|
to: this.end,
|
|
2756
|
+
position: this.start,
|
|
2342
2757
|
color: this.color,
|
|
2343
|
-
width: this.width
|
|
2758
|
+
width: this.width,
|
|
2759
|
+
fromBinding: this.fromBinding,
|
|
2760
|
+
toBinding: this.toTarget ? { elementId: this.toTarget.id } : void 0
|
|
2344
2761
|
});
|
|
2345
2762
|
ctx.store.add(arrow);
|
|
2763
|
+
this.fromTarget = null;
|
|
2764
|
+
this.toTarget = null;
|
|
2346
2765
|
ctx.requestRender();
|
|
2766
|
+
ctx.switchTool?.("select");
|
|
2347
2767
|
}
|
|
2348
2768
|
renderOverlay(ctx) {
|
|
2349
2769
|
if (!this.drawing) return;
|
|
2350
2770
|
if (this.start.x === this.end.x && this.start.y === this.end.y) return;
|
|
2351
2771
|
ctx.save();
|
|
2772
|
+
if (this.fromTarget) {
|
|
2773
|
+
const bounds = getElementBounds(this.fromTarget);
|
|
2774
|
+
if (bounds) {
|
|
2775
|
+
ctx.strokeStyle = "#2196F3";
|
|
2776
|
+
ctx.lineWidth = 2;
|
|
2777
|
+
ctx.setLineDash([]);
|
|
2778
|
+
ctx.strokeRect(bounds.x, bounds.y, bounds.w, bounds.h);
|
|
2779
|
+
}
|
|
2780
|
+
}
|
|
2781
|
+
if (this.toTarget) {
|
|
2782
|
+
const bounds = getElementBounds(this.toTarget);
|
|
2783
|
+
if (bounds) {
|
|
2784
|
+
ctx.strokeStyle = "#2196F3";
|
|
2785
|
+
ctx.lineWidth = 2;
|
|
2786
|
+
ctx.setLineDash([]);
|
|
2787
|
+
ctx.strokeRect(bounds.x, bounds.y, bounds.w, bounds.h);
|
|
2788
|
+
}
|
|
2789
|
+
}
|
|
2352
2790
|
ctx.strokeStyle = this.color;
|
|
2353
2791
|
ctx.lineWidth = this.width;
|
|
2354
2792
|
ctx.lineCap = "round";
|
|
@@ -2479,8 +2917,123 @@ var ImageTool = class {
|
|
|
2479
2917
|
}
|
|
2480
2918
|
};
|
|
2481
2919
|
|
|
2920
|
+
// src/tools/shape-tool.ts
|
|
2921
|
+
var ShapeTool = class {
|
|
2922
|
+
name = "shape";
|
|
2923
|
+
drawing = false;
|
|
2924
|
+
start = { x: 0, y: 0 };
|
|
2925
|
+
end = { x: 0, y: 0 };
|
|
2926
|
+
shiftHeld = false;
|
|
2927
|
+
shape;
|
|
2928
|
+
strokeColor;
|
|
2929
|
+
strokeWidth;
|
|
2930
|
+
fillColor;
|
|
2931
|
+
constructor(options = {}) {
|
|
2932
|
+
this.shape = options.shape ?? "rectangle";
|
|
2933
|
+
this.strokeColor = options.strokeColor ?? "#000000";
|
|
2934
|
+
this.strokeWidth = options.strokeWidth ?? 2;
|
|
2935
|
+
this.fillColor = options.fillColor ?? "none";
|
|
2936
|
+
}
|
|
2937
|
+
setOptions(options) {
|
|
2938
|
+
if (options.shape !== void 0) this.shape = options.shape;
|
|
2939
|
+
if (options.strokeColor !== void 0) this.strokeColor = options.strokeColor;
|
|
2940
|
+
if (options.strokeWidth !== void 0) this.strokeWidth = options.strokeWidth;
|
|
2941
|
+
if (options.fillColor !== void 0) this.fillColor = options.fillColor;
|
|
2942
|
+
}
|
|
2943
|
+
onActivate(_ctx) {
|
|
2944
|
+
if (typeof window !== "undefined") {
|
|
2945
|
+
window.addEventListener("keydown", this.onKeyDown);
|
|
2946
|
+
window.addEventListener("keyup", this.onKeyUp);
|
|
2947
|
+
}
|
|
2948
|
+
}
|
|
2949
|
+
onDeactivate(_ctx) {
|
|
2950
|
+
this.shiftHeld = false;
|
|
2951
|
+
if (typeof window !== "undefined") {
|
|
2952
|
+
window.removeEventListener("keydown", this.onKeyDown);
|
|
2953
|
+
window.removeEventListener("keyup", this.onKeyUp);
|
|
2954
|
+
}
|
|
2955
|
+
}
|
|
2956
|
+
onPointerDown(state, ctx) {
|
|
2957
|
+
this.drawing = true;
|
|
2958
|
+
this.start = ctx.camera.screenToWorld({ x: state.x, y: state.y });
|
|
2959
|
+
this.end = { ...this.start };
|
|
2960
|
+
}
|
|
2961
|
+
onPointerMove(state, ctx) {
|
|
2962
|
+
if (!this.drawing) return;
|
|
2963
|
+
this.end = ctx.camera.screenToWorld({ x: state.x, y: state.y });
|
|
2964
|
+
ctx.requestRender();
|
|
2965
|
+
}
|
|
2966
|
+
onPointerUp(_state, ctx) {
|
|
2967
|
+
if (!this.drawing) return;
|
|
2968
|
+
this.drawing = false;
|
|
2969
|
+
const { position, size } = this.computeRect();
|
|
2970
|
+
if (size.w === 0 || size.h === 0) return;
|
|
2971
|
+
const shape = createShape({
|
|
2972
|
+
position,
|
|
2973
|
+
size,
|
|
2974
|
+
shape: this.shape,
|
|
2975
|
+
strokeColor: this.strokeColor,
|
|
2976
|
+
strokeWidth: this.strokeWidth,
|
|
2977
|
+
fillColor: this.fillColor
|
|
2978
|
+
});
|
|
2979
|
+
ctx.store.add(shape);
|
|
2980
|
+
ctx.requestRender();
|
|
2981
|
+
ctx.switchTool?.("select");
|
|
2982
|
+
}
|
|
2983
|
+
renderOverlay(ctx) {
|
|
2984
|
+
if (!this.drawing) return;
|
|
2985
|
+
const { position, size } = this.computeRect();
|
|
2986
|
+
if (size.w === 0 && size.h === 0) return;
|
|
2987
|
+
ctx.save();
|
|
2988
|
+
ctx.globalAlpha = 0.5;
|
|
2989
|
+
ctx.strokeStyle = this.strokeColor;
|
|
2990
|
+
ctx.lineWidth = this.strokeWidth;
|
|
2991
|
+
if (this.fillColor !== "none") {
|
|
2992
|
+
ctx.fillStyle = this.fillColor;
|
|
2993
|
+
}
|
|
2994
|
+
switch (this.shape) {
|
|
2995
|
+
case "rectangle":
|
|
2996
|
+
if (this.fillColor !== "none") {
|
|
2997
|
+
ctx.fillRect(position.x, position.y, size.w, size.h);
|
|
2998
|
+
}
|
|
2999
|
+
ctx.strokeRect(position.x, position.y, size.w, size.h);
|
|
3000
|
+
break;
|
|
3001
|
+
case "ellipse": {
|
|
3002
|
+
const cx = position.x + size.w / 2;
|
|
3003
|
+
const cy = position.y + size.h / 2;
|
|
3004
|
+
ctx.beginPath();
|
|
3005
|
+
ctx.ellipse(cx, cy, size.w / 2, size.h / 2, 0, 0, Math.PI * 2);
|
|
3006
|
+
if (this.fillColor !== "none") ctx.fill();
|
|
3007
|
+
ctx.stroke();
|
|
3008
|
+
break;
|
|
3009
|
+
}
|
|
3010
|
+
}
|
|
3011
|
+
ctx.restore();
|
|
3012
|
+
}
|
|
3013
|
+
computeRect() {
|
|
3014
|
+
let x = Math.min(this.start.x, this.end.x);
|
|
3015
|
+
let y = Math.min(this.start.y, this.end.y);
|
|
3016
|
+
let w = Math.abs(this.end.x - this.start.x);
|
|
3017
|
+
let h = Math.abs(this.end.y - this.start.y);
|
|
3018
|
+
if (this.shiftHeld) {
|
|
3019
|
+
const side = Math.max(w, h);
|
|
3020
|
+
w = side;
|
|
3021
|
+
h = side;
|
|
3022
|
+
x = this.end.x >= this.start.x ? this.start.x : this.start.x - side;
|
|
3023
|
+
y = this.end.y >= this.start.y ? this.start.y : this.start.y - side;
|
|
3024
|
+
}
|
|
3025
|
+
return { position: { x, y }, size: { w, h } };
|
|
3026
|
+
}
|
|
3027
|
+
onKeyDown = (e) => {
|
|
3028
|
+
if (e.key === "Shift") this.shiftHeld = true;
|
|
3029
|
+
};
|
|
3030
|
+
onKeyUp = (e) => {
|
|
3031
|
+
if (e.key === "Shift") this.shiftHeld = false;
|
|
3032
|
+
};
|
|
3033
|
+
};
|
|
3034
|
+
|
|
2482
3035
|
// src/index.ts
|
|
2483
|
-
var VERSION = "0.
|
|
3036
|
+
var VERSION = "0.4.0";
|
|
2484
3037
|
export {
|
|
2485
3038
|
AddElementCommand,
|
|
2486
3039
|
ArrowTool,
|
|
@@ -2502,25 +3055,36 @@ export {
|
|
|
2502
3055
|
PencilTool,
|
|
2503
3056
|
RemoveElementCommand,
|
|
2504
3057
|
SelectTool,
|
|
3058
|
+
ShapeTool,
|
|
2505
3059
|
TextTool,
|
|
2506
3060
|
ToolManager,
|
|
2507
3061
|
UpdateElementCommand,
|
|
2508
3062
|
VERSION,
|
|
2509
3063
|
Viewport,
|
|
3064
|
+
clearStaleBindings,
|
|
2510
3065
|
createArrow,
|
|
2511
3066
|
createHtmlElement,
|
|
2512
3067
|
createId,
|
|
2513
3068
|
createImage,
|
|
2514
3069
|
createNote,
|
|
3070
|
+
createShape,
|
|
2515
3071
|
createStroke,
|
|
2516
3072
|
createText,
|
|
2517
3073
|
exportState,
|
|
3074
|
+
findBindTarget,
|
|
3075
|
+
findBoundArrows,
|
|
2518
3076
|
getArrowBounds,
|
|
2519
3077
|
getArrowControlPoint,
|
|
2520
3078
|
getArrowMidpoint,
|
|
2521
3079
|
getArrowTangentAngle,
|
|
2522
3080
|
getBendFromPoint,
|
|
3081
|
+
getEdgeIntersection,
|
|
3082
|
+
getElementBounds,
|
|
3083
|
+
getElementCenter,
|
|
3084
|
+
isBindable,
|
|
2523
3085
|
isNearBezier,
|
|
2524
|
-
parseState
|
|
3086
|
+
parseState,
|
|
3087
|
+
unbindArrow,
|
|
3088
|
+
updateBoundArrow
|
|
2525
3089
|
};
|
|
2526
3090
|
//# sourceMappingURL=index.js.map
|