@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.cjs
CHANGED
|
@@ -40,24 +40,35 @@ __export(index_exports, {
|
|
|
40
40
|
PencilTool: () => PencilTool,
|
|
41
41
|
RemoveElementCommand: () => RemoveElementCommand,
|
|
42
42
|
SelectTool: () => SelectTool,
|
|
43
|
+
TextTool: () => TextTool,
|
|
43
44
|
ToolManager: () => ToolManager,
|
|
44
45
|
UpdateElementCommand: () => UpdateElementCommand,
|
|
45
46
|
VERSION: () => VERSION,
|
|
46
47
|
Viewport: () => Viewport,
|
|
48
|
+
clearStaleBindings: () => clearStaleBindings,
|
|
47
49
|
createArrow: () => createArrow,
|
|
48
50
|
createHtmlElement: () => createHtmlElement,
|
|
49
51
|
createId: () => createId,
|
|
50
52
|
createImage: () => createImage,
|
|
51
53
|
createNote: () => createNote,
|
|
52
54
|
createStroke: () => createStroke,
|
|
55
|
+
createText: () => createText,
|
|
53
56
|
exportState: () => exportState,
|
|
57
|
+
findBindTarget: () => findBindTarget,
|
|
58
|
+
findBoundArrows: () => findBoundArrows,
|
|
54
59
|
getArrowBounds: () => getArrowBounds,
|
|
55
60
|
getArrowControlPoint: () => getArrowControlPoint,
|
|
56
61
|
getArrowMidpoint: () => getArrowMidpoint,
|
|
57
62
|
getArrowTangentAngle: () => getArrowTangentAngle,
|
|
58
63
|
getBendFromPoint: () => getBendFromPoint,
|
|
64
|
+
getEdgeIntersection: () => getEdgeIntersection,
|
|
65
|
+
getElementBounds: () => getElementBounds,
|
|
66
|
+
getElementCenter: () => getElementCenter,
|
|
67
|
+
isBindable: () => isBindable,
|
|
59
68
|
isNearBezier: () => isNearBezier,
|
|
60
|
-
parseState: () => parseState
|
|
69
|
+
parseState: () => parseState,
|
|
70
|
+
unbindArrow: () => unbindArrow,
|
|
71
|
+
updateBoundArrow: () => updateBoundArrow
|
|
61
72
|
});
|
|
62
73
|
module.exports = __toCommonJS(index_exports);
|
|
63
74
|
|
|
@@ -131,8 +142,9 @@ function validateState(data) {
|
|
|
131
142
|
validateElement(el);
|
|
132
143
|
migrateElement(el);
|
|
133
144
|
}
|
|
145
|
+
cleanBindings(obj["elements"]);
|
|
134
146
|
}
|
|
135
|
-
var VALID_TYPES = /* @__PURE__ */ new Set(["stroke", "note", "arrow", "image", "html"]);
|
|
147
|
+
var VALID_TYPES = /* @__PURE__ */ new Set(["stroke", "note", "arrow", "image", "html", "text"]);
|
|
136
148
|
function validateElement(el) {
|
|
137
149
|
if (!el || typeof el !== "object") {
|
|
138
150
|
throw new Error("Invalid element: expected an object");
|
|
@@ -148,6 +160,20 @@ function validateElement(el) {
|
|
|
148
160
|
throw new Error("Invalid element: missing zIndex");
|
|
149
161
|
}
|
|
150
162
|
}
|
|
163
|
+
function cleanBindings(elements) {
|
|
164
|
+
const ids = new Set(elements.map((el) => el["id"]));
|
|
165
|
+
for (const el of elements) {
|
|
166
|
+
if (el["type"] !== "arrow") continue;
|
|
167
|
+
const fromBinding = el["fromBinding"];
|
|
168
|
+
if (fromBinding && !ids.has(fromBinding["elementId"])) {
|
|
169
|
+
el["fromBinding"] = void 0;
|
|
170
|
+
}
|
|
171
|
+
const toBinding = el["toBinding"];
|
|
172
|
+
if (toBinding && !ids.has(toBinding["elementId"])) {
|
|
173
|
+
el["toBinding"] = void 0;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
151
177
|
function migrateElement(obj) {
|
|
152
178
|
if (obj["type"] === "arrow" && typeof obj["bend"] !== "number") {
|
|
153
179
|
obj["bend"] = 0;
|
|
@@ -741,6 +767,134 @@ function isNearLine(point, a, b, threshold) {
|
|
|
741
767
|
return Math.hypot(point.x - projX, point.y - projY) <= threshold;
|
|
742
768
|
}
|
|
743
769
|
|
|
770
|
+
// src/elements/arrow-binding.ts
|
|
771
|
+
var BINDABLE_TYPES = /* @__PURE__ */ new Set(["note", "text", "image", "html"]);
|
|
772
|
+
function isBindable(element) {
|
|
773
|
+
return BINDABLE_TYPES.has(element.type);
|
|
774
|
+
}
|
|
775
|
+
function getElementCenter(element) {
|
|
776
|
+
if (!("size" in element)) {
|
|
777
|
+
throw new Error(`getElementCenter: element type "${element.type}" has no size`);
|
|
778
|
+
}
|
|
779
|
+
return {
|
|
780
|
+
x: element.position.x + element.size.w / 2,
|
|
781
|
+
y: element.position.y + element.size.h / 2
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
function getElementBounds(element) {
|
|
785
|
+
if (!("size" in element)) return null;
|
|
786
|
+
return {
|
|
787
|
+
x: element.position.x,
|
|
788
|
+
y: element.position.y,
|
|
789
|
+
w: element.size.w,
|
|
790
|
+
h: element.size.h
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
function getEdgeIntersection(bounds, outsidePoint) {
|
|
794
|
+
const cx = bounds.x + bounds.w / 2;
|
|
795
|
+
const cy = bounds.y + bounds.h / 2;
|
|
796
|
+
const dx = outsidePoint.x - cx;
|
|
797
|
+
const dy = outsidePoint.y - cy;
|
|
798
|
+
if (dx === 0 && dy === 0) return { x: cx, y: cy };
|
|
799
|
+
const halfW = bounds.w / 2;
|
|
800
|
+
const halfH = bounds.h / 2;
|
|
801
|
+
const scaleX = dx !== 0 ? halfW / Math.abs(dx) : Infinity;
|
|
802
|
+
const scaleY = dy !== 0 ? halfH / Math.abs(dy) : Infinity;
|
|
803
|
+
const scale = Math.min(scaleX, scaleY);
|
|
804
|
+
return {
|
|
805
|
+
x: cx + dx * scale,
|
|
806
|
+
y: cy + dy * scale
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
function findBindTarget(point, store, threshold, excludeId) {
|
|
810
|
+
let closest = null;
|
|
811
|
+
let closestDist = Infinity;
|
|
812
|
+
for (const el of store.getAll()) {
|
|
813
|
+
if (!isBindable(el)) continue;
|
|
814
|
+
if (excludeId && el.id === excludeId) continue;
|
|
815
|
+
const bounds = getElementBounds(el);
|
|
816
|
+
if (!bounds) continue;
|
|
817
|
+
const dist = distToBounds(point, bounds);
|
|
818
|
+
if (dist <= threshold && dist < closestDist) {
|
|
819
|
+
closest = el;
|
|
820
|
+
closestDist = dist;
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
return closest;
|
|
824
|
+
}
|
|
825
|
+
function distToBounds(point, bounds) {
|
|
826
|
+
const clampedX = Math.max(bounds.x, Math.min(point.x, bounds.x + bounds.w));
|
|
827
|
+
const clampedY = Math.max(bounds.y, Math.min(point.y, bounds.y + bounds.h));
|
|
828
|
+
return Math.hypot(point.x - clampedX, point.y - clampedY);
|
|
829
|
+
}
|
|
830
|
+
function findBoundArrows(elementId, store) {
|
|
831
|
+
return store.getElementsByType("arrow").filter((a) => a.fromBinding?.elementId === elementId || a.toBinding?.elementId === elementId);
|
|
832
|
+
}
|
|
833
|
+
function updateBoundArrow(arrow, store) {
|
|
834
|
+
if (!arrow.fromBinding && !arrow.toBinding) return null;
|
|
835
|
+
const updates = {};
|
|
836
|
+
if (arrow.fromBinding) {
|
|
837
|
+
const el = store.getById(arrow.fromBinding.elementId);
|
|
838
|
+
if (el) {
|
|
839
|
+
const center = getElementCenter(el);
|
|
840
|
+
updates.from = center;
|
|
841
|
+
updates.position = center;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
if (arrow.toBinding) {
|
|
845
|
+
const el = store.getById(arrow.toBinding.elementId);
|
|
846
|
+
if (el) {
|
|
847
|
+
updates.to = getElementCenter(el);
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
return Object.keys(updates).length > 0 ? updates : null;
|
|
851
|
+
}
|
|
852
|
+
function clearStaleBindings(arrow, store) {
|
|
853
|
+
const updates = {};
|
|
854
|
+
let hasUpdates = false;
|
|
855
|
+
if (arrow.fromBinding && !store.getById(arrow.fromBinding.elementId)) {
|
|
856
|
+
updates.fromBinding = void 0;
|
|
857
|
+
hasUpdates = true;
|
|
858
|
+
}
|
|
859
|
+
if (arrow.toBinding && !store.getById(arrow.toBinding.elementId)) {
|
|
860
|
+
updates.toBinding = void 0;
|
|
861
|
+
hasUpdates = true;
|
|
862
|
+
}
|
|
863
|
+
return hasUpdates ? updates : null;
|
|
864
|
+
}
|
|
865
|
+
function unbindArrow(arrow, store) {
|
|
866
|
+
const updates = {};
|
|
867
|
+
if (arrow.fromBinding) {
|
|
868
|
+
const el = store.getById(arrow.fromBinding.elementId);
|
|
869
|
+
const bounds = el ? getElementBounds(el) : null;
|
|
870
|
+
if (bounds) {
|
|
871
|
+
const angle = getArrowTangentAngle(arrow.from, arrow.to, arrow.bend, 0);
|
|
872
|
+
const rayTarget = {
|
|
873
|
+
x: arrow.from.x + Math.cos(angle) * 1e3,
|
|
874
|
+
y: arrow.from.y + Math.sin(angle) * 1e3
|
|
875
|
+
};
|
|
876
|
+
const edge = getEdgeIntersection(bounds, rayTarget);
|
|
877
|
+
updates.from = edge;
|
|
878
|
+
updates.position = edge;
|
|
879
|
+
}
|
|
880
|
+
updates.fromBinding = void 0;
|
|
881
|
+
}
|
|
882
|
+
if (arrow.toBinding) {
|
|
883
|
+
const el = store.getById(arrow.toBinding.elementId);
|
|
884
|
+
const bounds = el ? getElementBounds(el) : null;
|
|
885
|
+
if (bounds) {
|
|
886
|
+
const angle = getArrowTangentAngle(arrow.from, arrow.to, arrow.bend, 1);
|
|
887
|
+
const rayTarget = {
|
|
888
|
+
x: arrow.to.x - Math.cos(angle) * 1e3,
|
|
889
|
+
y: arrow.to.y - Math.sin(angle) * 1e3
|
|
890
|
+
};
|
|
891
|
+
updates.to = getEdgeIntersection(bounds, rayTarget);
|
|
892
|
+
}
|
|
893
|
+
updates.toBinding = void 0;
|
|
894
|
+
}
|
|
895
|
+
return updates;
|
|
896
|
+
}
|
|
897
|
+
|
|
744
898
|
// src/elements/stroke-smoothing.ts
|
|
745
899
|
var MIN_PRESSURE_SCALE = 0.2;
|
|
746
900
|
function pressureToWidth(pressure, baseWidth) {
|
|
@@ -815,10 +969,14 @@ function smoothToSegments(points) {
|
|
|
815
969
|
}
|
|
816
970
|
|
|
817
971
|
// src/elements/element-renderer.ts
|
|
818
|
-
var DOM_ELEMENT_TYPES = /* @__PURE__ */ new Set(["note", "image", "html"]);
|
|
972
|
+
var DOM_ELEMENT_TYPES = /* @__PURE__ */ new Set(["note", "image", "html", "text"]);
|
|
819
973
|
var ARROWHEAD_LENGTH = 12;
|
|
820
974
|
var ARROWHEAD_ANGLE = Math.PI / 6;
|
|
821
975
|
var ElementRenderer = class {
|
|
976
|
+
store = null;
|
|
977
|
+
setStore(store) {
|
|
978
|
+
this.store = store;
|
|
979
|
+
}
|
|
822
980
|
isDomElement(element) {
|
|
823
981
|
return DOM_ELEMENT_TYPES.has(element.type);
|
|
824
982
|
}
|
|
@@ -852,38 +1010,76 @@ var ElementRenderer = class {
|
|
|
852
1010
|
ctx.restore();
|
|
853
1011
|
}
|
|
854
1012
|
renderArrow(ctx, arrow) {
|
|
1013
|
+
const { visualFrom, visualTo } = this.getVisualEndpoints(arrow);
|
|
855
1014
|
ctx.save();
|
|
856
1015
|
ctx.strokeStyle = arrow.color;
|
|
857
1016
|
ctx.lineWidth = arrow.width;
|
|
858
1017
|
ctx.lineCap = "round";
|
|
1018
|
+
if (arrow.fromBinding || arrow.toBinding) {
|
|
1019
|
+
ctx.setLineDash([8, 4]);
|
|
1020
|
+
}
|
|
859
1021
|
ctx.beginPath();
|
|
860
|
-
ctx.moveTo(
|
|
1022
|
+
ctx.moveTo(visualFrom.x, visualFrom.y);
|
|
861
1023
|
if (arrow.bend !== 0) {
|
|
862
1024
|
const cp = getArrowControlPoint(arrow.from, arrow.to, arrow.bend);
|
|
863
|
-
ctx.quadraticCurveTo(cp.x, cp.y,
|
|
1025
|
+
ctx.quadraticCurveTo(cp.x, cp.y, visualTo.x, visualTo.y);
|
|
864
1026
|
} else {
|
|
865
|
-
ctx.lineTo(
|
|
1027
|
+
ctx.lineTo(visualTo.x, visualTo.y);
|
|
866
1028
|
}
|
|
867
1029
|
ctx.stroke();
|
|
868
|
-
this.renderArrowhead(ctx, arrow);
|
|
1030
|
+
this.renderArrowhead(ctx, arrow, visualTo);
|
|
869
1031
|
ctx.restore();
|
|
870
1032
|
}
|
|
871
|
-
renderArrowhead(ctx, arrow) {
|
|
1033
|
+
renderArrowhead(ctx, arrow, tip) {
|
|
872
1034
|
const angle = getArrowTangentAngle(arrow.from, arrow.to, arrow.bend, 1);
|
|
873
1035
|
ctx.beginPath();
|
|
874
|
-
ctx.moveTo(
|
|
1036
|
+
ctx.moveTo(tip.x, tip.y);
|
|
875
1037
|
ctx.lineTo(
|
|
876
|
-
|
|
877
|
-
|
|
1038
|
+
tip.x - ARROWHEAD_LENGTH * Math.cos(angle - ARROWHEAD_ANGLE),
|
|
1039
|
+
tip.y - ARROWHEAD_LENGTH * Math.sin(angle - ARROWHEAD_ANGLE)
|
|
878
1040
|
);
|
|
879
1041
|
ctx.lineTo(
|
|
880
|
-
|
|
881
|
-
|
|
1042
|
+
tip.x - ARROWHEAD_LENGTH * Math.cos(angle + ARROWHEAD_ANGLE),
|
|
1043
|
+
tip.y - ARROWHEAD_LENGTH * Math.sin(angle + ARROWHEAD_ANGLE)
|
|
882
1044
|
);
|
|
883
1045
|
ctx.closePath();
|
|
884
1046
|
ctx.fillStyle = arrow.color;
|
|
885
1047
|
ctx.fill();
|
|
886
1048
|
}
|
|
1049
|
+
getVisualEndpoints(arrow) {
|
|
1050
|
+
let visualFrom = arrow.from;
|
|
1051
|
+
let visualTo = arrow.to;
|
|
1052
|
+
if (!this.store) return { visualFrom, visualTo };
|
|
1053
|
+
if (arrow.fromBinding) {
|
|
1054
|
+
const el = this.store.getById(arrow.fromBinding.elementId);
|
|
1055
|
+
if (el) {
|
|
1056
|
+
const bounds = getElementBounds(el);
|
|
1057
|
+
if (bounds) {
|
|
1058
|
+
const tangentAngle = getArrowTangentAngle(arrow.from, arrow.to, arrow.bend, 0);
|
|
1059
|
+
const rayTarget = {
|
|
1060
|
+
x: arrow.from.x + Math.cos(tangentAngle) * 1e3,
|
|
1061
|
+
y: arrow.from.y + Math.sin(tangentAngle) * 1e3
|
|
1062
|
+
};
|
|
1063
|
+
visualFrom = getEdgeIntersection(bounds, rayTarget);
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
if (arrow.toBinding) {
|
|
1068
|
+
const el = this.store.getById(arrow.toBinding.elementId);
|
|
1069
|
+
if (el) {
|
|
1070
|
+
const bounds = getElementBounds(el);
|
|
1071
|
+
if (bounds) {
|
|
1072
|
+
const tangentAngle = getArrowTangentAngle(arrow.from, arrow.to, arrow.bend, 1);
|
|
1073
|
+
const rayTarget = {
|
|
1074
|
+
x: arrow.to.x - Math.cos(tangentAngle) * 1e3,
|
|
1075
|
+
y: arrow.to.y - Math.sin(tangentAngle) * 1e3
|
|
1076
|
+
};
|
|
1077
|
+
visualTo = getEdgeIntersection(bounds, rayTarget);
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
return { visualFrom, visualTo };
|
|
1082
|
+
}
|
|
887
1083
|
};
|
|
888
1084
|
|
|
889
1085
|
// src/elements/note-editor.ts
|
|
@@ -894,12 +1090,16 @@ var NoteEditor = class {
|
|
|
894
1090
|
keyHandler = null;
|
|
895
1091
|
pointerHandler = null;
|
|
896
1092
|
pendingEditId = null;
|
|
1093
|
+
onStopCallback = null;
|
|
897
1094
|
get isEditing() {
|
|
898
1095
|
return this.editingId !== null;
|
|
899
1096
|
}
|
|
900
1097
|
get editingElementId() {
|
|
901
1098
|
return this.editingId;
|
|
902
1099
|
}
|
|
1100
|
+
setOnStop(callback) {
|
|
1101
|
+
this.onStopCallback = callback;
|
|
1102
|
+
}
|
|
903
1103
|
startEditing(node, elementId, store) {
|
|
904
1104
|
if (this.editingId === elementId) return;
|
|
905
1105
|
if (this.editingId) {
|
|
@@ -931,6 +1131,9 @@ var NoteEditor = class {
|
|
|
931
1131
|
if (this.pointerHandler) {
|
|
932
1132
|
this.editingNode.removeEventListener("pointerdown", this.pointerHandler);
|
|
933
1133
|
}
|
|
1134
|
+
if (this.editingId && this.onStopCallback) {
|
|
1135
|
+
this.onStopCallback(this.editingId);
|
|
1136
|
+
}
|
|
934
1137
|
this.editingId = null;
|
|
935
1138
|
this.editingNode = null;
|
|
936
1139
|
this.blurHandler = null;
|
|
@@ -1247,7 +1450,7 @@ function createNote(input) {
|
|
|
1247
1450
|
};
|
|
1248
1451
|
}
|
|
1249
1452
|
function createArrow(input) {
|
|
1250
|
-
|
|
1453
|
+
const result = {
|
|
1251
1454
|
id: createId("arrow"),
|
|
1252
1455
|
type: "arrow",
|
|
1253
1456
|
position: input.position ?? { x: 0, y: 0 },
|
|
@@ -1259,6 +1462,9 @@ function createArrow(input) {
|
|
|
1259
1462
|
color: input.color ?? "#000000",
|
|
1260
1463
|
width: input.width ?? 2
|
|
1261
1464
|
};
|
|
1465
|
+
if (input.fromBinding) result.fromBinding = input.fromBinding;
|
|
1466
|
+
if (input.toBinding) result.toBinding = input.toBinding;
|
|
1467
|
+
return result;
|
|
1262
1468
|
}
|
|
1263
1469
|
function createImage(input) {
|
|
1264
1470
|
return {
|
|
@@ -1281,6 +1487,20 @@ function createHtmlElement(input) {
|
|
|
1281
1487
|
size: input.size
|
|
1282
1488
|
};
|
|
1283
1489
|
}
|
|
1490
|
+
function createText(input) {
|
|
1491
|
+
return {
|
|
1492
|
+
id: createId("text"),
|
|
1493
|
+
type: "text",
|
|
1494
|
+
position: input.position,
|
|
1495
|
+
zIndex: input.zIndex ?? 0,
|
|
1496
|
+
locked: input.locked ?? false,
|
|
1497
|
+
size: input.size ?? { w: 200, h: 28 },
|
|
1498
|
+
text: input.text ?? "",
|
|
1499
|
+
fontSize: input.fontSize ?? 16,
|
|
1500
|
+
color: input.color ?? "#1a1a1a",
|
|
1501
|
+
textAlign: input.textAlign ?? "left"
|
|
1502
|
+
};
|
|
1503
|
+
}
|
|
1284
1504
|
|
|
1285
1505
|
// src/canvas/viewport.ts
|
|
1286
1506
|
var Viewport = class {
|
|
@@ -1291,7 +1511,9 @@ var Viewport = class {
|
|
|
1291
1511
|
this.store = new ElementStore();
|
|
1292
1512
|
this.toolManager = new ToolManager();
|
|
1293
1513
|
this.renderer = new ElementRenderer();
|
|
1514
|
+
this.renderer.setStore(this.store);
|
|
1294
1515
|
this.noteEditor = new NoteEditor();
|
|
1516
|
+
this.noteEditor.setOnStop((id) => this.onTextEditStop(id));
|
|
1295
1517
|
this.history = new HistoryStack();
|
|
1296
1518
|
this.historyRecorder = new HistoryRecorder(this.store, this.history);
|
|
1297
1519
|
this.wrapper = this.createWrapper();
|
|
@@ -1305,7 +1527,7 @@ var Viewport = class {
|
|
|
1305
1527
|
store: this.store,
|
|
1306
1528
|
requestRender: () => this.requestRender(),
|
|
1307
1529
|
switchTool: (name) => this.toolManager.setTool(name, this.toolContext),
|
|
1308
|
-
editElement: (id) => this.
|
|
1530
|
+
editElement: (id) => this.startEditingElement(id),
|
|
1309
1531
|
setCursor: (cursor) => {
|
|
1310
1532
|
this.wrapper.style.cursor = cursor;
|
|
1311
1533
|
}
|
|
@@ -1322,7 +1544,10 @@ var Viewport = class {
|
|
|
1322
1544
|
});
|
|
1323
1545
|
this.unsubStore = [
|
|
1324
1546
|
this.store.on("add", () => this.requestRender()),
|
|
1325
|
-
this.store.on("remove", (el) =>
|
|
1547
|
+
this.store.on("remove", (el) => {
|
|
1548
|
+
this.unbindArrowsFrom(el);
|
|
1549
|
+
this.removeDomNode(el.id);
|
|
1550
|
+
}),
|
|
1326
1551
|
this.store.on("update", () => this.requestRender()),
|
|
1327
1552
|
this.store.on("clear", () => this.clearDomNodes())
|
|
1328
1553
|
];
|
|
@@ -1459,15 +1684,34 @@ var Viewport = class {
|
|
|
1459
1684
|
ctx.restore();
|
|
1460
1685
|
ctx.restore();
|
|
1461
1686
|
}
|
|
1462
|
-
|
|
1687
|
+
startEditingElement(id) {
|
|
1463
1688
|
const element = this.store.getById(id);
|
|
1464
|
-
if (!element || element.type !== "note") return;
|
|
1689
|
+
if (!element || element.type !== "note" && element.type !== "text") return;
|
|
1465
1690
|
this.render();
|
|
1466
1691
|
const node = this.domNodes.get(id);
|
|
1467
1692
|
if (node) {
|
|
1468
1693
|
this.noteEditor.startEditing(node, id, this.store);
|
|
1469
1694
|
}
|
|
1470
1695
|
}
|
|
1696
|
+
onTextEditStop(elementId) {
|
|
1697
|
+
const element = this.store.getById(elementId);
|
|
1698
|
+
if (!element || element.type !== "text") return;
|
|
1699
|
+
if (!element.text || element.text.trim() === "") {
|
|
1700
|
+
this.historyRecorder.begin();
|
|
1701
|
+
this.store.remove(elementId);
|
|
1702
|
+
this.historyRecorder.commit();
|
|
1703
|
+
return;
|
|
1704
|
+
}
|
|
1705
|
+
const node = this.domNodes.get(elementId);
|
|
1706
|
+
if (node && "size" in element) {
|
|
1707
|
+
const measuredHeight = node.scrollHeight;
|
|
1708
|
+
if (measuredHeight !== element.size.h) {
|
|
1709
|
+
this.store.update(elementId, {
|
|
1710
|
+
size: { w: element.size.w, h: measuredHeight }
|
|
1711
|
+
});
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1471
1715
|
onDblClick = (e) => {
|
|
1472
1716
|
const el = document.elementFromPoint(e.clientX, e.clientY);
|
|
1473
1717
|
const nodeEl = el?.closest("[data-element-id]");
|
|
@@ -1475,8 +1719,8 @@ var Viewport = class {
|
|
|
1475
1719
|
const elementId = nodeEl.dataset["elementId"];
|
|
1476
1720
|
if (elementId) {
|
|
1477
1721
|
const element = this.store.getById(elementId);
|
|
1478
|
-
if (element?.type === "note") {
|
|
1479
|
-
this.
|
|
1722
|
+
if (element?.type === "note" || element?.type === "text") {
|
|
1723
|
+
this.startEditingElement(elementId);
|
|
1480
1724
|
return;
|
|
1481
1725
|
}
|
|
1482
1726
|
}
|
|
@@ -1600,7 +1844,7 @@ var Viewport = class {
|
|
|
1600
1844
|
node.addEventListener("dblclick", (e) => {
|
|
1601
1845
|
e.stopPropagation();
|
|
1602
1846
|
const id = node.dataset["elementId"];
|
|
1603
|
-
if (id) this.
|
|
1847
|
+
if (id) this.startEditingElement(id);
|
|
1604
1848
|
});
|
|
1605
1849
|
}
|
|
1606
1850
|
if (!this.noteEditor.isEditing || this.noteEditor.editingElementId !== element.id) {
|
|
@@ -1641,6 +1885,76 @@ var Viewport = class {
|
|
|
1641
1885
|
node.appendChild(content);
|
|
1642
1886
|
}
|
|
1643
1887
|
}
|
|
1888
|
+
if (element.type === "text") {
|
|
1889
|
+
if (!node.dataset["initialized"]) {
|
|
1890
|
+
node.dataset["initialized"] = "true";
|
|
1891
|
+
Object.assign(node.style, {
|
|
1892
|
+
padding: "2px",
|
|
1893
|
+
fontSize: `${element.fontSize}px`,
|
|
1894
|
+
color: element.color,
|
|
1895
|
+
textAlign: element.textAlign,
|
|
1896
|
+
background: "none",
|
|
1897
|
+
border: "none",
|
|
1898
|
+
boxShadow: "none",
|
|
1899
|
+
overflow: "visible",
|
|
1900
|
+
cursor: "default",
|
|
1901
|
+
userSelect: "none",
|
|
1902
|
+
wordWrap: "break-word",
|
|
1903
|
+
whiteSpace: "pre-wrap",
|
|
1904
|
+
lineHeight: "1.4"
|
|
1905
|
+
});
|
|
1906
|
+
node.textContent = element.text || "";
|
|
1907
|
+
node.addEventListener("dblclick", (e) => {
|
|
1908
|
+
e.stopPropagation();
|
|
1909
|
+
const id = node.dataset["elementId"];
|
|
1910
|
+
if (id) this.startEditingElement(id);
|
|
1911
|
+
});
|
|
1912
|
+
}
|
|
1913
|
+
if (!this.noteEditor.isEditing || this.noteEditor.editingElementId !== element.id) {
|
|
1914
|
+
if (node.textContent !== element.text) {
|
|
1915
|
+
node.textContent = element.text || "";
|
|
1916
|
+
}
|
|
1917
|
+
Object.assign(node.style, {
|
|
1918
|
+
fontSize: `${element.fontSize}px`,
|
|
1919
|
+
color: element.color,
|
|
1920
|
+
textAlign: element.textAlign
|
|
1921
|
+
});
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
unbindArrowsFrom(removedElement) {
|
|
1926
|
+
const boundArrows = findBoundArrows(removedElement.id, this.store);
|
|
1927
|
+
const bounds = getElementBounds(removedElement);
|
|
1928
|
+
for (const arrow of boundArrows) {
|
|
1929
|
+
const updates = {};
|
|
1930
|
+
if (arrow.fromBinding?.elementId === removedElement.id) {
|
|
1931
|
+
updates.fromBinding = void 0;
|
|
1932
|
+
if (bounds) {
|
|
1933
|
+
const angle = getArrowTangentAngle(arrow.from, arrow.to, arrow.bend, 0);
|
|
1934
|
+
const rayTarget = {
|
|
1935
|
+
x: arrow.from.x + Math.cos(angle) * 1e3,
|
|
1936
|
+
y: arrow.from.y + Math.sin(angle) * 1e3
|
|
1937
|
+
};
|
|
1938
|
+
const edge = getEdgeIntersection(bounds, rayTarget);
|
|
1939
|
+
updates.from = edge;
|
|
1940
|
+
updates.position = edge;
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
if (arrow.toBinding?.elementId === removedElement.id) {
|
|
1944
|
+
updates.toBinding = void 0;
|
|
1945
|
+
if (bounds) {
|
|
1946
|
+
const angle = getArrowTangentAngle(arrow.from, arrow.to, arrow.bend, 1);
|
|
1947
|
+
const rayTarget = {
|
|
1948
|
+
x: arrow.to.x - Math.cos(angle) * 1e3,
|
|
1949
|
+
y: arrow.to.y - Math.sin(angle) * 1e3
|
|
1950
|
+
};
|
|
1951
|
+
updates.to = getEdgeIntersection(bounds, rayTarget);
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
if (Object.keys(updates).length > 0) {
|
|
1955
|
+
this.store.update(arrow.id, updates);
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1644
1958
|
}
|
|
1645
1959
|
removeDomNode(id) {
|
|
1646
1960
|
this.htmlContent.delete(id);
|
|
@@ -1870,6 +2184,7 @@ var EraserTool = class {
|
|
|
1870
2184
|
};
|
|
1871
2185
|
|
|
1872
2186
|
// src/tools/arrow-handles.ts
|
|
2187
|
+
var BIND_THRESHOLD = 20;
|
|
1873
2188
|
var HANDLE_RADIUS = 5;
|
|
1874
2189
|
var HANDLE_HIT_PADDING = 4;
|
|
1875
2190
|
var ARROW_HANDLE_CURSORS = {
|
|
@@ -1910,18 +2225,44 @@ function hitTestArrowHandles(world, selectedIds, ctx) {
|
|
|
1910
2225
|
function applyArrowHandleDrag(handle, elementId, world, ctx) {
|
|
1911
2226
|
const el = ctx.store.getById(elementId);
|
|
1912
2227
|
if (!el || el.type !== "arrow") return;
|
|
2228
|
+
const threshold = BIND_THRESHOLD / ctx.camera.zoom;
|
|
1913
2229
|
switch (handle) {
|
|
1914
|
-
case "start":
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
2230
|
+
case "start": {
|
|
2231
|
+
const excludeId = el.toBinding?.elementId;
|
|
2232
|
+
const target = findBindTarget(world, ctx.store, threshold, excludeId);
|
|
2233
|
+
if (target) {
|
|
2234
|
+
const center = getElementCenter(target);
|
|
2235
|
+
ctx.store.update(elementId, {
|
|
2236
|
+
from: center,
|
|
2237
|
+
position: center,
|
|
2238
|
+
fromBinding: { elementId: target.id }
|
|
2239
|
+
});
|
|
2240
|
+
} else {
|
|
2241
|
+
ctx.store.update(elementId, {
|
|
2242
|
+
from: { x: world.x, y: world.y },
|
|
2243
|
+
position: { x: world.x, y: world.y },
|
|
2244
|
+
fromBinding: void 0
|
|
2245
|
+
});
|
|
2246
|
+
}
|
|
1919
2247
|
break;
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
2248
|
+
}
|
|
2249
|
+
case "end": {
|
|
2250
|
+
const excludeId = el.fromBinding?.elementId;
|
|
2251
|
+
const target = findBindTarget(world, ctx.store, threshold, excludeId);
|
|
2252
|
+
if (target) {
|
|
2253
|
+
const center = getElementCenter(target);
|
|
2254
|
+
ctx.store.update(elementId, {
|
|
2255
|
+
to: center,
|
|
2256
|
+
toBinding: { elementId: target.id }
|
|
2257
|
+
});
|
|
2258
|
+
} else {
|
|
2259
|
+
ctx.store.update(elementId, {
|
|
2260
|
+
to: { x: world.x, y: world.y },
|
|
2261
|
+
toBinding: void 0
|
|
2262
|
+
});
|
|
2263
|
+
}
|
|
1924
2264
|
break;
|
|
2265
|
+
}
|
|
1925
2266
|
case "mid": {
|
|
1926
2267
|
const bend = getBendFromPoint(el.from, el.to, world);
|
|
1927
2268
|
ctx.store.update(elementId, { bend });
|
|
@@ -1930,6 +2271,16 @@ function applyArrowHandleDrag(handle, elementId, world, ctx) {
|
|
|
1930
2271
|
}
|
|
1931
2272
|
ctx.requestRender();
|
|
1932
2273
|
}
|
|
2274
|
+
function getArrowHandleDragTarget(handle, elementId, world, ctx) {
|
|
2275
|
+
if (handle === "mid") return null;
|
|
2276
|
+
const el = ctx.store.getById(elementId);
|
|
2277
|
+
if (!el || el.type !== "arrow") return null;
|
|
2278
|
+
const threshold = BIND_THRESHOLD / ctx.camera.zoom;
|
|
2279
|
+
const excludeId = handle === "start" ? el.toBinding?.elementId : el.fromBinding?.elementId;
|
|
2280
|
+
const target = findBindTarget(world, ctx.store, threshold, excludeId);
|
|
2281
|
+
if (!target) return null;
|
|
2282
|
+
return getElementBounds(target);
|
|
2283
|
+
}
|
|
1933
2284
|
function renderArrowHandles(canvasCtx, arrow, zoom) {
|
|
1934
2285
|
const radius = HANDLE_RADIUS / zoom;
|
|
1935
2286
|
const handles = getArrowHandlePositions(arrow);
|
|
@@ -2040,6 +2391,9 @@ var SelectTool = class {
|
|
|
2040
2391
|
const el = ctx.store.getById(id);
|
|
2041
2392
|
if (!el || el.locked) continue;
|
|
2042
2393
|
if (el.type === "arrow") {
|
|
2394
|
+
if (el.fromBinding || el.toBinding) {
|
|
2395
|
+
continue;
|
|
2396
|
+
}
|
|
2043
2397
|
ctx.store.update(id, {
|
|
2044
2398
|
position: { x: el.position.x + dx, y: el.position.y + dy },
|
|
2045
2399
|
from: { x: el.from.x + dx, y: el.from.y + dy },
|
|
@@ -2051,6 +2405,23 @@ var SelectTool = class {
|
|
|
2051
2405
|
});
|
|
2052
2406
|
}
|
|
2053
2407
|
}
|
|
2408
|
+
const movedNonArrowIds = /* @__PURE__ */ new Set();
|
|
2409
|
+
for (const id of this._selectedIds) {
|
|
2410
|
+
const el = ctx.store.getById(id);
|
|
2411
|
+
if (el && el.type !== "arrow") movedNonArrowIds.add(id);
|
|
2412
|
+
}
|
|
2413
|
+
if (movedNonArrowIds.size > 0) {
|
|
2414
|
+
const updatedArrows = /* @__PURE__ */ new Set();
|
|
2415
|
+
for (const id of movedNonArrowIds) {
|
|
2416
|
+
const boundArrows = findBoundArrows(id, ctx.store);
|
|
2417
|
+
for (const ba of boundArrows) {
|
|
2418
|
+
if (updatedArrows.has(ba.id)) continue;
|
|
2419
|
+
updatedArrows.add(ba.id);
|
|
2420
|
+
const updates = updateBoundArrow(ba, ctx.store);
|
|
2421
|
+
if (updates) ctx.store.update(ba.id, updates);
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
}
|
|
2054
2425
|
ctx.requestRender();
|
|
2055
2426
|
return;
|
|
2056
2427
|
}
|
|
@@ -2079,6 +2450,22 @@ var SelectTool = class {
|
|
|
2079
2450
|
renderOverlay(canvasCtx) {
|
|
2080
2451
|
this.renderMarquee(canvasCtx);
|
|
2081
2452
|
this.renderSelectionBoxes(canvasCtx);
|
|
2453
|
+
if (this.mode.type === "arrow-handle" && this.ctx) {
|
|
2454
|
+
const target = getArrowHandleDragTarget(
|
|
2455
|
+
this.mode.handle,
|
|
2456
|
+
this.mode.elementId,
|
|
2457
|
+
this.currentWorld,
|
|
2458
|
+
this.ctx
|
|
2459
|
+
);
|
|
2460
|
+
if (target) {
|
|
2461
|
+
canvasCtx.save();
|
|
2462
|
+
canvasCtx.strokeStyle = "#2196F3";
|
|
2463
|
+
canvasCtx.lineWidth = 2 / this.ctx.camera.zoom;
|
|
2464
|
+
canvasCtx.setLineDash([]);
|
|
2465
|
+
canvasCtx.strokeRect(target.x, target.y, target.w, target.h);
|
|
2466
|
+
canvasCtx.restore();
|
|
2467
|
+
}
|
|
2468
|
+
}
|
|
2082
2469
|
}
|
|
2083
2470
|
updateHoverCursor(world, ctx) {
|
|
2084
2471
|
const arrowHit = hitTestArrowHandles(world, this._selectedIds, ctx);
|
|
@@ -2137,6 +2524,11 @@ var SelectTool = class {
|
|
|
2137
2524
|
position: { x, y },
|
|
2138
2525
|
size: { w, h }
|
|
2139
2526
|
});
|
|
2527
|
+
const boundArrows = findBoundArrows(this.mode.elementId, ctx.store);
|
|
2528
|
+
for (const ba of boundArrows) {
|
|
2529
|
+
const updates = updateBoundArrow(ba, ctx.store);
|
|
2530
|
+
if (updates) ctx.store.update(ba.id, updates);
|
|
2531
|
+
}
|
|
2140
2532
|
ctx.requestRender();
|
|
2141
2533
|
}
|
|
2142
2534
|
hitTestResizeHandle(world, ctx) {
|
|
@@ -2191,6 +2583,7 @@ var SelectTool = class {
|
|
|
2191
2583
|
if (!el) continue;
|
|
2192
2584
|
if (el.type === "arrow") {
|
|
2193
2585
|
renderArrowHandles(canvasCtx, el, zoom);
|
|
2586
|
+
this.renderBindingHighlights(canvasCtx, el, zoom);
|
|
2194
2587
|
continue;
|
|
2195
2588
|
}
|
|
2196
2589
|
const bounds = this.getElementBounds(el);
|
|
@@ -2220,6 +2613,26 @@ var SelectTool = class {
|
|
|
2220
2613
|
}
|
|
2221
2614
|
canvasCtx.restore();
|
|
2222
2615
|
}
|
|
2616
|
+
renderBindingHighlights(canvasCtx, arrow, zoom) {
|
|
2617
|
+
if (!this.ctx) return;
|
|
2618
|
+
if (!arrow.fromBinding && !arrow.toBinding) return;
|
|
2619
|
+
const pad = SELECTION_PAD / zoom;
|
|
2620
|
+
canvasCtx.save();
|
|
2621
|
+
canvasCtx.strokeStyle = "#2196F3";
|
|
2622
|
+
canvasCtx.lineWidth = 2 / zoom;
|
|
2623
|
+
canvasCtx.setLineDash([]);
|
|
2624
|
+
const drawn = /* @__PURE__ */ new Set();
|
|
2625
|
+
for (const binding of [arrow.fromBinding, arrow.toBinding]) {
|
|
2626
|
+
if (!binding || drawn.has(binding.elementId)) continue;
|
|
2627
|
+
drawn.add(binding.elementId);
|
|
2628
|
+
const target = this.ctx.store.getById(binding.elementId);
|
|
2629
|
+
if (!target) continue;
|
|
2630
|
+
const bounds = getElementBounds(target);
|
|
2631
|
+
if (!bounds) continue;
|
|
2632
|
+
canvasCtx.strokeRect(bounds.x - pad, bounds.y - pad, bounds.w + pad * 2, bounds.h + pad * 2);
|
|
2633
|
+
}
|
|
2634
|
+
canvasCtx.restore();
|
|
2635
|
+
}
|
|
2223
2636
|
getMarqueeRect() {
|
|
2224
2637
|
if (this.mode.type !== "marquee") return null;
|
|
2225
2638
|
const { start } = this.mode;
|
|
@@ -2293,6 +2706,7 @@ var SelectTool = class {
|
|
|
2293
2706
|
};
|
|
2294
2707
|
|
|
2295
2708
|
// src/tools/arrow-tool.ts
|
|
2709
|
+
var BIND_THRESHOLD2 = 20;
|
|
2296
2710
|
var ArrowTool = class {
|
|
2297
2711
|
name = "arrow";
|
|
2298
2712
|
drawing = false;
|
|
@@ -2300,6 +2714,9 @@ var ArrowTool = class {
|
|
|
2300
2714
|
end = { x: 0, y: 0 };
|
|
2301
2715
|
color;
|
|
2302
2716
|
width;
|
|
2717
|
+
fromBinding;
|
|
2718
|
+
fromTarget = null;
|
|
2719
|
+
toTarget = null;
|
|
2303
2720
|
constructor(options = {}) {
|
|
2304
2721
|
this.color = options.color ?? "#000000";
|
|
2305
2722
|
this.width = options.width ?? 2;
|
|
@@ -2310,12 +2727,34 @@ var ArrowTool = class {
|
|
|
2310
2727
|
}
|
|
2311
2728
|
onPointerDown(state, ctx) {
|
|
2312
2729
|
this.drawing = true;
|
|
2313
|
-
|
|
2730
|
+
const world = ctx.camera.screenToWorld({ x: state.x, y: state.y });
|
|
2731
|
+
const threshold = BIND_THRESHOLD2 / ctx.camera.zoom;
|
|
2732
|
+
const target = findBindTarget(world, ctx.store, threshold);
|
|
2733
|
+
if (target) {
|
|
2734
|
+
this.start = getElementCenter(target);
|
|
2735
|
+
this.fromBinding = { elementId: target.id };
|
|
2736
|
+
this.fromTarget = target;
|
|
2737
|
+
} else {
|
|
2738
|
+
this.start = world;
|
|
2739
|
+
this.fromBinding = void 0;
|
|
2740
|
+
this.fromTarget = null;
|
|
2741
|
+
}
|
|
2314
2742
|
this.end = { ...this.start };
|
|
2743
|
+
this.toTarget = null;
|
|
2315
2744
|
}
|
|
2316
2745
|
onPointerMove(state, ctx) {
|
|
2317
2746
|
if (!this.drawing) return;
|
|
2318
|
-
|
|
2747
|
+
const world = ctx.camera.screenToWorld({ x: state.x, y: state.y });
|
|
2748
|
+
const threshold = BIND_THRESHOLD2 / ctx.camera.zoom;
|
|
2749
|
+
const excludeId = this.fromBinding?.elementId;
|
|
2750
|
+
const target = findBindTarget(world, ctx.store, threshold, excludeId);
|
|
2751
|
+
if (target) {
|
|
2752
|
+
this.end = getElementCenter(target);
|
|
2753
|
+
this.toTarget = target;
|
|
2754
|
+
} else {
|
|
2755
|
+
this.end = world;
|
|
2756
|
+
this.toTarget = null;
|
|
2757
|
+
}
|
|
2319
2758
|
ctx.requestRender();
|
|
2320
2759
|
}
|
|
2321
2760
|
onPointerUp(_state, ctx) {
|
|
@@ -2325,16 +2764,40 @@ var ArrowTool = class {
|
|
|
2325
2764
|
const arrow = createArrow({
|
|
2326
2765
|
from: this.start,
|
|
2327
2766
|
to: this.end,
|
|
2767
|
+
position: this.start,
|
|
2328
2768
|
color: this.color,
|
|
2329
|
-
width: this.width
|
|
2769
|
+
width: this.width,
|
|
2770
|
+
fromBinding: this.fromBinding,
|
|
2771
|
+
toBinding: this.toTarget ? { elementId: this.toTarget.id } : void 0
|
|
2330
2772
|
});
|
|
2331
2773
|
ctx.store.add(arrow);
|
|
2774
|
+
this.fromTarget = null;
|
|
2775
|
+
this.toTarget = null;
|
|
2332
2776
|
ctx.requestRender();
|
|
2777
|
+
ctx.switchTool?.("select");
|
|
2333
2778
|
}
|
|
2334
2779
|
renderOverlay(ctx) {
|
|
2335
2780
|
if (!this.drawing) return;
|
|
2336
2781
|
if (this.start.x === this.end.x && this.start.y === this.end.y) return;
|
|
2337
2782
|
ctx.save();
|
|
2783
|
+
if (this.fromTarget) {
|
|
2784
|
+
const bounds = getElementBounds(this.fromTarget);
|
|
2785
|
+
if (bounds) {
|
|
2786
|
+
ctx.strokeStyle = "#2196F3";
|
|
2787
|
+
ctx.lineWidth = 2;
|
|
2788
|
+
ctx.setLineDash([]);
|
|
2789
|
+
ctx.strokeRect(bounds.x, bounds.y, bounds.w, bounds.h);
|
|
2790
|
+
}
|
|
2791
|
+
}
|
|
2792
|
+
if (this.toTarget) {
|
|
2793
|
+
const bounds = getElementBounds(this.toTarget);
|
|
2794
|
+
if (bounds) {
|
|
2795
|
+
ctx.strokeStyle = "#2196F3";
|
|
2796
|
+
ctx.lineWidth = 2;
|
|
2797
|
+
ctx.setLineDash([]);
|
|
2798
|
+
ctx.strokeRect(bounds.x, bounds.y, bounds.w, bounds.h);
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2338
2801
|
ctx.strokeStyle = this.color;
|
|
2339
2802
|
ctx.lineWidth = this.width;
|
|
2340
2803
|
ctx.lineCap = "round";
|
|
@@ -2394,6 +2857,47 @@ var NoteTool = class {
|
|
|
2394
2857
|
}
|
|
2395
2858
|
};
|
|
2396
2859
|
|
|
2860
|
+
// src/tools/text-tool.ts
|
|
2861
|
+
var TextTool = class {
|
|
2862
|
+
name = "text";
|
|
2863
|
+
fontSize;
|
|
2864
|
+
color;
|
|
2865
|
+
textAlign;
|
|
2866
|
+
constructor(options = {}) {
|
|
2867
|
+
this.fontSize = options.fontSize ?? 16;
|
|
2868
|
+
this.color = options.color ?? "#1a1a1a";
|
|
2869
|
+
this.textAlign = options.textAlign ?? "left";
|
|
2870
|
+
}
|
|
2871
|
+
setOptions(options) {
|
|
2872
|
+
if (options.fontSize !== void 0) this.fontSize = options.fontSize;
|
|
2873
|
+
if (options.color !== void 0) this.color = options.color;
|
|
2874
|
+
if (options.textAlign !== void 0) this.textAlign = options.textAlign;
|
|
2875
|
+
}
|
|
2876
|
+
onActivate(ctx) {
|
|
2877
|
+
ctx.setCursor?.("text");
|
|
2878
|
+
}
|
|
2879
|
+
onDeactivate(ctx) {
|
|
2880
|
+
ctx.setCursor?.("default");
|
|
2881
|
+
}
|
|
2882
|
+
onPointerDown(_state, _ctx) {
|
|
2883
|
+
}
|
|
2884
|
+
onPointerMove(_state, _ctx) {
|
|
2885
|
+
}
|
|
2886
|
+
onPointerUp(state, ctx) {
|
|
2887
|
+
const world = ctx.camera.screenToWorld({ x: state.x, y: state.y });
|
|
2888
|
+
const textEl = createText({
|
|
2889
|
+
position: world,
|
|
2890
|
+
fontSize: this.fontSize,
|
|
2891
|
+
color: this.color,
|
|
2892
|
+
textAlign: this.textAlign
|
|
2893
|
+
});
|
|
2894
|
+
ctx.store.add(textEl);
|
|
2895
|
+
ctx.requestRender();
|
|
2896
|
+
ctx.switchTool?.("select");
|
|
2897
|
+
ctx.editElement?.(textEl.id);
|
|
2898
|
+
}
|
|
2899
|
+
};
|
|
2900
|
+
|
|
2397
2901
|
// src/tools/image-tool.ts
|
|
2398
2902
|
var ImageTool = class {
|
|
2399
2903
|
name = "image";
|
|
@@ -2425,7 +2929,7 @@ var ImageTool = class {
|
|
|
2425
2929
|
};
|
|
2426
2930
|
|
|
2427
2931
|
// src/index.ts
|
|
2428
|
-
var VERSION = "0.
|
|
2932
|
+
var VERSION = "0.3.1";
|
|
2429
2933
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2430
2934
|
0 && (module.exports = {
|
|
2431
2935
|
AddElementCommand,
|
|
@@ -2448,23 +2952,34 @@ var VERSION = "0.2.3";
|
|
|
2448
2952
|
PencilTool,
|
|
2449
2953
|
RemoveElementCommand,
|
|
2450
2954
|
SelectTool,
|
|
2955
|
+
TextTool,
|
|
2451
2956
|
ToolManager,
|
|
2452
2957
|
UpdateElementCommand,
|
|
2453
2958
|
VERSION,
|
|
2454
2959
|
Viewport,
|
|
2960
|
+
clearStaleBindings,
|
|
2455
2961
|
createArrow,
|
|
2456
2962
|
createHtmlElement,
|
|
2457
2963
|
createId,
|
|
2458
2964
|
createImage,
|
|
2459
2965
|
createNote,
|
|
2460
2966
|
createStroke,
|
|
2967
|
+
createText,
|
|
2461
2968
|
exportState,
|
|
2969
|
+
findBindTarget,
|
|
2970
|
+
findBoundArrows,
|
|
2462
2971
|
getArrowBounds,
|
|
2463
2972
|
getArrowControlPoint,
|
|
2464
2973
|
getArrowMidpoint,
|
|
2465
2974
|
getArrowTangentAngle,
|
|
2466
2975
|
getBendFromPoint,
|
|
2976
|
+
getEdgeIntersection,
|
|
2977
|
+
getElementBounds,
|
|
2978
|
+
getElementCenter,
|
|
2979
|
+
isBindable,
|
|
2467
2980
|
isNearBezier,
|
|
2468
|
-
parseState
|
|
2981
|
+
parseState,
|
|
2982
|
+
unbindArrow,
|
|
2983
|
+
updateBoundArrow
|
|
2469
2984
|
});
|
|
2470
2985
|
//# sourceMappingURL=index.cjs.map
|