@fieldnotes/core 0.16.0 → 0.17.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 +347 -175
- package/dist/index.d.cts +7 -9
- package/dist/index.d.ts +7 -9
- package/dist/index.js +347 -175
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -920,9 +920,228 @@ function createId(prefix) {
|
|
|
920
920
|
return `${prefix}_${Date.now().toString(36)}_${(counter++).toString(36)}`;
|
|
921
921
|
}
|
|
922
922
|
|
|
923
|
+
// src/canvas/keyboard-actions.ts
|
|
924
|
+
var KeyboardActions = class {
|
|
925
|
+
constructor(deps) {
|
|
926
|
+
this.deps = deps;
|
|
927
|
+
}
|
|
928
|
+
clipboard = [];
|
|
929
|
+
pasteCount = 0;
|
|
930
|
+
nudgeTimer = null;
|
|
931
|
+
dispose() {
|
|
932
|
+
this.flushPendingNudge();
|
|
933
|
+
}
|
|
934
|
+
selectTool() {
|
|
935
|
+
const tm = this.deps.getToolManager();
|
|
936
|
+
const ctx = this.deps.getToolContext();
|
|
937
|
+
if (!tm || !ctx) return null;
|
|
938
|
+
const tool = tm.activeTool;
|
|
939
|
+
if (tool?.name !== "select") return null;
|
|
940
|
+
return { tool, ctx };
|
|
941
|
+
}
|
|
942
|
+
nudge(dx, dy, byCell) {
|
|
943
|
+
if (this.deps.isToolActive()) return false;
|
|
944
|
+
const sel = this.selectTool();
|
|
945
|
+
if (!sel) return false;
|
|
946
|
+
if (sel.tool.selectedIds.length === 0) return false;
|
|
947
|
+
const step = byCell ? sel.ctx.gridSize ?? 10 : 1;
|
|
948
|
+
if (this.nudgeTimer === null) {
|
|
949
|
+
this.deps.getHistoryRecorder()?.begin();
|
|
950
|
+
} else {
|
|
951
|
+
clearTimeout(this.nudgeTimer);
|
|
952
|
+
}
|
|
953
|
+
const moved = sel.tool.nudgeSelection(dx * step, dy * step, sel.ctx);
|
|
954
|
+
this.nudgeTimer = setTimeout(() => this.flushPendingNudge(), 400);
|
|
955
|
+
return moved;
|
|
956
|
+
}
|
|
957
|
+
flushPendingNudge() {
|
|
958
|
+
if (this.nudgeTimer === null) return;
|
|
959
|
+
clearTimeout(this.nudgeTimer);
|
|
960
|
+
this.nudgeTimer = null;
|
|
961
|
+
this.deps.getHistoryRecorder()?.commit();
|
|
962
|
+
}
|
|
963
|
+
deleteSelected() {
|
|
964
|
+
this.flushPendingNudge();
|
|
965
|
+
const sel = this.selectTool();
|
|
966
|
+
if (!sel) return;
|
|
967
|
+
const ids = sel.tool.selectedIds;
|
|
968
|
+
if (ids.length === 0) return;
|
|
969
|
+
const recorder = this.deps.getHistoryRecorder();
|
|
970
|
+
recorder?.begin();
|
|
971
|
+
for (const id of ids) {
|
|
972
|
+
sel.ctx.store.remove(id);
|
|
973
|
+
}
|
|
974
|
+
recorder?.commit();
|
|
975
|
+
sel.ctx.requestRender();
|
|
976
|
+
}
|
|
977
|
+
undo() {
|
|
978
|
+
this.flushPendingNudge();
|
|
979
|
+
const ctx = this.deps.getToolContext();
|
|
980
|
+
const stack = this.deps.getHistoryStack();
|
|
981
|
+
if (!stack || !ctx) return;
|
|
982
|
+
const recorder = this.deps.getHistoryRecorder();
|
|
983
|
+
recorder?.pause();
|
|
984
|
+
stack.undo(ctx.store);
|
|
985
|
+
recorder?.resume();
|
|
986
|
+
ctx.requestRender();
|
|
987
|
+
}
|
|
988
|
+
redo() {
|
|
989
|
+
this.flushPendingNudge();
|
|
990
|
+
const ctx = this.deps.getToolContext();
|
|
991
|
+
const stack = this.deps.getHistoryStack();
|
|
992
|
+
if (!stack || !ctx) return;
|
|
993
|
+
const recorder = this.deps.getHistoryRecorder();
|
|
994
|
+
recorder?.pause();
|
|
995
|
+
stack.redo(ctx.store);
|
|
996
|
+
recorder?.resume();
|
|
997
|
+
ctx.requestRender();
|
|
998
|
+
}
|
|
999
|
+
copy() {
|
|
1000
|
+
if (this.deps.isToolActive()) return;
|
|
1001
|
+
const sel = this.selectTool();
|
|
1002
|
+
if (!sel) return;
|
|
1003
|
+
const ids = sel.tool.selectedIds;
|
|
1004
|
+
if (ids.length === 0) return;
|
|
1005
|
+
this.clipboard = [];
|
|
1006
|
+
for (const id of ids) {
|
|
1007
|
+
const el = sel.ctx.store.getById(id);
|
|
1008
|
+
if (el) this.clipboard.push(structuredClone(el));
|
|
1009
|
+
}
|
|
1010
|
+
this.pasteCount = 0;
|
|
1011
|
+
}
|
|
1012
|
+
paste() {
|
|
1013
|
+
this.flushPendingNudge();
|
|
1014
|
+
if (this.clipboard.length === 0 || this.deps.isToolActive()) return;
|
|
1015
|
+
const sel = this.selectTool();
|
|
1016
|
+
if (!sel) return;
|
|
1017
|
+
this.pasteCount++;
|
|
1018
|
+
this.insertClones(this.clipboard, this.pasteCount * 20, sel);
|
|
1019
|
+
}
|
|
1020
|
+
duplicate() {
|
|
1021
|
+
this.flushPendingNudge();
|
|
1022
|
+
if (this.deps.isToolActive()) return;
|
|
1023
|
+
const sel = this.selectTool();
|
|
1024
|
+
if (!sel) return;
|
|
1025
|
+
const source = [];
|
|
1026
|
+
for (const id of sel.tool.selectedIds) {
|
|
1027
|
+
const el = sel.ctx.store.getById(id);
|
|
1028
|
+
if (el) source.push(el);
|
|
1029
|
+
}
|
|
1030
|
+
if (source.length === 0) return;
|
|
1031
|
+
this.insertClones(source, 20, sel);
|
|
1032
|
+
}
|
|
1033
|
+
deselect() {
|
|
1034
|
+
if (this.deps.isToolActive()) return;
|
|
1035
|
+
const sel = this.selectTool();
|
|
1036
|
+
if (!sel) return;
|
|
1037
|
+
if (sel.tool.selectedIds.length === 0) return;
|
|
1038
|
+
sel.tool.setSelection([]);
|
|
1039
|
+
sel.ctx.requestRender();
|
|
1040
|
+
}
|
|
1041
|
+
selectAll() {
|
|
1042
|
+
if (this.deps.isToolActive()) return;
|
|
1043
|
+
const tm = this.deps.getToolManager();
|
|
1044
|
+
const ctx = this.deps.getToolContext();
|
|
1045
|
+
if (!tm || !ctx) return;
|
|
1046
|
+
if (tm.activeTool?.name !== "select") {
|
|
1047
|
+
ctx.switchTool?.("select");
|
|
1048
|
+
}
|
|
1049
|
+
const sel = this.selectTool();
|
|
1050
|
+
if (!sel) return;
|
|
1051
|
+
const ids = sel.ctx.store.getAll().filter(
|
|
1052
|
+
(el) => !el.locked && (sel.ctx.isLayerVisible?.(el.layerId) ?? true) && !(sel.ctx.isLayerLocked?.(el.layerId) ?? false)
|
|
1053
|
+
).map((el) => el.id);
|
|
1054
|
+
sel.tool.setSelection(ids);
|
|
1055
|
+
sel.ctx.requestRender();
|
|
1056
|
+
}
|
|
1057
|
+
zoomToFit() {
|
|
1058
|
+
if (this.deps.isToolActive()) return;
|
|
1059
|
+
this.deps.fitToContent?.();
|
|
1060
|
+
}
|
|
1061
|
+
zOrder(operation) {
|
|
1062
|
+
this.flushPendingNudge();
|
|
1063
|
+
const sel = this.selectTool();
|
|
1064
|
+
if (!sel) return;
|
|
1065
|
+
const ids = sel.tool.selectedIds;
|
|
1066
|
+
if (ids.length === 0) return;
|
|
1067
|
+
const recorder = this.deps.getHistoryRecorder();
|
|
1068
|
+
recorder?.begin();
|
|
1069
|
+
for (const id of ids) {
|
|
1070
|
+
switch (operation) {
|
|
1071
|
+
case "forward":
|
|
1072
|
+
sel.ctx.store.bringForward(id);
|
|
1073
|
+
break;
|
|
1074
|
+
case "backward":
|
|
1075
|
+
sel.ctx.store.sendBackward(id);
|
|
1076
|
+
break;
|
|
1077
|
+
case "front":
|
|
1078
|
+
sel.ctx.store.bringToFront(id);
|
|
1079
|
+
break;
|
|
1080
|
+
case "back":
|
|
1081
|
+
sel.ctx.store.sendToBack(id);
|
|
1082
|
+
break;
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
recorder?.commit();
|
|
1086
|
+
sel.ctx.requestRender();
|
|
1087
|
+
}
|
|
1088
|
+
insertClones(source, offset, sel) {
|
|
1089
|
+
const idMap = /* @__PURE__ */ new Map();
|
|
1090
|
+
for (const el of source) {
|
|
1091
|
+
idMap.set(el.id, createId(el.type));
|
|
1092
|
+
}
|
|
1093
|
+
const newIds = [];
|
|
1094
|
+
const recorder = this.deps.getHistoryRecorder();
|
|
1095
|
+
recorder?.begin();
|
|
1096
|
+
for (const el of source) {
|
|
1097
|
+
const clone = structuredClone(el);
|
|
1098
|
+
const newId = idMap.get(el.id);
|
|
1099
|
+
if (!newId) continue;
|
|
1100
|
+
clone.id = newId;
|
|
1101
|
+
clone.position = { x: clone.position.x + offset, y: clone.position.y + offset };
|
|
1102
|
+
if (clone.type === "arrow") {
|
|
1103
|
+
const arrow = clone;
|
|
1104
|
+
arrow.from = { x: arrow.from.x + offset, y: arrow.from.y + offset };
|
|
1105
|
+
arrow.to = { x: arrow.to.x + offset, y: arrow.to.y + offset };
|
|
1106
|
+
delete arrow.cachedControlPoint;
|
|
1107
|
+
if (arrow.fromBinding) {
|
|
1108
|
+
const newTarget = idMap.get(arrow.fromBinding.elementId);
|
|
1109
|
+
if (newTarget) {
|
|
1110
|
+
arrow.fromBinding = { elementId: newTarget };
|
|
1111
|
+
} else {
|
|
1112
|
+
delete arrow.fromBinding;
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
if (arrow.toBinding) {
|
|
1116
|
+
const newTarget = idMap.get(arrow.toBinding.elementId);
|
|
1117
|
+
if (newTarget) {
|
|
1118
|
+
arrow.toBinding = { elementId: newTarget };
|
|
1119
|
+
} else {
|
|
1120
|
+
delete arrow.toBinding;
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
if (sel.ctx.activeLayerId) {
|
|
1125
|
+
clone.layerId = sel.ctx.activeLayerId;
|
|
1126
|
+
}
|
|
1127
|
+
sel.ctx.store.add(clone);
|
|
1128
|
+
newIds.push(clone.id);
|
|
1129
|
+
}
|
|
1130
|
+
recorder?.commit();
|
|
1131
|
+
sel.tool.setSelection(newIds);
|
|
1132
|
+
sel.ctx.requestRender();
|
|
1133
|
+
}
|
|
1134
|
+
};
|
|
1135
|
+
|
|
923
1136
|
// src/canvas/input-handler.ts
|
|
924
1137
|
var ZOOM_SENSITIVITY = 1e-3;
|
|
925
1138
|
var MIDDLE_BUTTON = 1;
|
|
1139
|
+
var NUDGE_KEYS = {
|
|
1140
|
+
ArrowLeft: [-1, 0],
|
|
1141
|
+
ArrowRight: [1, 0],
|
|
1142
|
+
ArrowUp: [0, -1],
|
|
1143
|
+
ArrowDown: [0, 1]
|
|
1144
|
+
};
|
|
926
1145
|
var InputHandler = class {
|
|
927
1146
|
constructor(element, camera, options = {}) {
|
|
928
1147
|
this.element = element;
|
|
@@ -931,6 +1150,14 @@ var InputHandler = class {
|
|
|
931
1150
|
this.toolContext = options.toolContext ?? null;
|
|
932
1151
|
this.historyRecorder = options.historyRecorder ?? null;
|
|
933
1152
|
this.historyStack = options.historyStack ?? null;
|
|
1153
|
+
this.actions = new KeyboardActions({
|
|
1154
|
+
getToolManager: () => this.toolManager,
|
|
1155
|
+
getToolContext: () => this.toolContext,
|
|
1156
|
+
getHistoryRecorder: () => this.historyRecorder,
|
|
1157
|
+
getHistoryStack: () => this.historyStack,
|
|
1158
|
+
isToolActive: () => this.isToolActive,
|
|
1159
|
+
fitToContent: options.fitToContent
|
|
1160
|
+
});
|
|
934
1161
|
this.element.style.touchAction = "none";
|
|
935
1162
|
this.bind();
|
|
936
1163
|
}
|
|
@@ -949,13 +1176,16 @@ var InputHandler = class {
|
|
|
949
1176
|
inputFilter = new InputFilter();
|
|
950
1177
|
deferredDown = null;
|
|
951
1178
|
abortController = new AbortController();
|
|
952
|
-
|
|
953
|
-
pasteCount = 0;
|
|
1179
|
+
actions;
|
|
954
1180
|
setToolManager(toolManager, toolContext) {
|
|
955
1181
|
this.toolManager = toolManager;
|
|
956
1182
|
this.toolContext = toolContext;
|
|
957
1183
|
}
|
|
1184
|
+
flushPendingHistory() {
|
|
1185
|
+
this.actions.flushPendingNudge();
|
|
1186
|
+
}
|
|
958
1187
|
destroy() {
|
|
1188
|
+
this.actions.dispose();
|
|
959
1189
|
this.abortController.abort();
|
|
960
1190
|
this.inputFilter.reset();
|
|
961
1191
|
this.deferredDown = null;
|
|
@@ -1061,36 +1291,61 @@ var InputHandler = class {
|
|
|
1061
1291
|
}
|
|
1062
1292
|
};
|
|
1063
1293
|
onKeyDown = (e) => {
|
|
1064
|
-
|
|
1294
|
+
const target = e.target;
|
|
1295
|
+
if (target?.isContentEditable) return;
|
|
1296
|
+
const tag = target?.tagName;
|
|
1297
|
+
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return;
|
|
1065
1298
|
if (e.key === " ") {
|
|
1066
1299
|
this.spaceHeld = true;
|
|
1067
1300
|
}
|
|
1068
1301
|
if (e.key === "Delete" || e.key === "Backspace") {
|
|
1069
|
-
this.deleteSelected();
|
|
1302
|
+
this.actions.deleteSelected();
|
|
1303
|
+
}
|
|
1304
|
+
if (e.key === "Escape") {
|
|
1305
|
+
this.actions.deselect();
|
|
1070
1306
|
}
|
|
1071
1307
|
if ((e.ctrlKey || e.metaKey) && e.key === "z" && !e.shiftKey) {
|
|
1072
1308
|
e.preventDefault();
|
|
1073
|
-
this.
|
|
1309
|
+
this.actions.undo();
|
|
1074
1310
|
}
|
|
1075
1311
|
if ((e.ctrlKey || e.metaKey) && (e.key === "y" || e.key === "z" && e.shiftKey)) {
|
|
1076
1312
|
e.preventDefault();
|
|
1077
|
-
this.
|
|
1313
|
+
this.actions.redo();
|
|
1314
|
+
}
|
|
1315
|
+
if ((e.ctrlKey || e.metaKey) && e.key === "a") {
|
|
1316
|
+
e.preventDefault();
|
|
1317
|
+
this.actions.selectAll();
|
|
1078
1318
|
}
|
|
1079
1319
|
if ((e.ctrlKey || e.metaKey) && e.key === "c") {
|
|
1080
1320
|
e.preventDefault();
|
|
1081
|
-
this.
|
|
1321
|
+
this.actions.copy();
|
|
1082
1322
|
}
|
|
1083
1323
|
if ((e.ctrlKey || e.metaKey) && e.key === "v") {
|
|
1084
1324
|
e.preventDefault();
|
|
1085
|
-
this.
|
|
1325
|
+
this.actions.paste();
|
|
1326
|
+
}
|
|
1327
|
+
if ((e.ctrlKey || e.metaKey) && e.key === "d") {
|
|
1328
|
+
e.preventDefault();
|
|
1329
|
+
this.actions.duplicate();
|
|
1086
1330
|
}
|
|
1087
1331
|
if (e.key === "]") {
|
|
1088
1332
|
e.preventDefault();
|
|
1089
|
-
this.
|
|
1333
|
+
this.actions.zOrder(e.ctrlKey || e.metaKey ? "front" : "forward");
|
|
1090
1334
|
}
|
|
1091
1335
|
if (e.key === "[") {
|
|
1092
1336
|
e.preventDefault();
|
|
1093
|
-
this.
|
|
1337
|
+
this.actions.zOrder(e.ctrlKey || e.metaKey ? "back" : "backward");
|
|
1338
|
+
}
|
|
1339
|
+
if (e.shiftKey && e.code === "Digit1" && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
|
1340
|
+
e.preventDefault();
|
|
1341
|
+
this.actions.zoomToFit();
|
|
1342
|
+
}
|
|
1343
|
+
const nudgeDelta = NUDGE_KEYS[e.key];
|
|
1344
|
+
if (nudgeDelta) {
|
|
1345
|
+
const [dx, dy] = nudgeDelta;
|
|
1346
|
+
if (this.actions.nudge(dx, dy, e.shiftKey)) {
|
|
1347
|
+
e.preventDefault();
|
|
1348
|
+
}
|
|
1094
1349
|
}
|
|
1095
1350
|
};
|
|
1096
1351
|
onKeyUp = (e) => {
|
|
@@ -1154,6 +1409,7 @@ var InputHandler = class {
|
|
|
1154
1409
|
}
|
|
1155
1410
|
dispatchToolDown(e) {
|
|
1156
1411
|
if (!this.toolManager || !this.toolContext) return;
|
|
1412
|
+
this.actions.flushPendingNudge();
|
|
1157
1413
|
this.historyRecorder?.begin();
|
|
1158
1414
|
this.isToolActive = true;
|
|
1159
1415
|
this.toolManager.handlePointerDown(this.toPointerState(e), this.toolContext);
|
|
@@ -1174,127 +1430,6 @@ var InputHandler = class {
|
|
|
1174
1430
|
this.toolManager.handlePointerUp(this.toPointerState(e), this.toolContext);
|
|
1175
1431
|
this.historyRecorder?.commit();
|
|
1176
1432
|
}
|
|
1177
|
-
deleteSelected() {
|
|
1178
|
-
if (!this.toolManager || !this.toolContext) return;
|
|
1179
|
-
const tool = this.toolManager.activeTool;
|
|
1180
|
-
if (tool?.name !== "select") return;
|
|
1181
|
-
const selectTool = tool;
|
|
1182
|
-
const ids = selectTool.selectedIds;
|
|
1183
|
-
if (ids.length === 0) return;
|
|
1184
|
-
this.historyRecorder?.begin();
|
|
1185
|
-
for (const id of ids) {
|
|
1186
|
-
this.toolContext.store.remove(id);
|
|
1187
|
-
}
|
|
1188
|
-
this.historyRecorder?.commit();
|
|
1189
|
-
this.toolContext.requestRender();
|
|
1190
|
-
}
|
|
1191
|
-
handleUndo() {
|
|
1192
|
-
if (!this.historyStack || !this.toolContext) return;
|
|
1193
|
-
this.historyRecorder?.pause();
|
|
1194
|
-
this.historyStack.undo(this.toolContext.store);
|
|
1195
|
-
this.historyRecorder?.resume();
|
|
1196
|
-
this.toolContext.requestRender();
|
|
1197
|
-
}
|
|
1198
|
-
handleRedo() {
|
|
1199
|
-
if (!this.historyStack || !this.toolContext) return;
|
|
1200
|
-
this.historyRecorder?.pause();
|
|
1201
|
-
this.historyStack.redo(this.toolContext.store);
|
|
1202
|
-
this.historyRecorder?.resume();
|
|
1203
|
-
this.toolContext.requestRender();
|
|
1204
|
-
}
|
|
1205
|
-
handleCopy() {
|
|
1206
|
-
if (!this.toolManager || !this.toolContext || this.isToolActive) return;
|
|
1207
|
-
const tool = this.toolManager.activeTool;
|
|
1208
|
-
if (tool?.name !== "select") return;
|
|
1209
|
-
const selectTool = tool;
|
|
1210
|
-
const ids = selectTool.selectedIds;
|
|
1211
|
-
if (ids.length === 0) return;
|
|
1212
|
-
this.clipboard = [];
|
|
1213
|
-
for (const id of ids) {
|
|
1214
|
-
const el = this.toolContext.store.getById(id);
|
|
1215
|
-
if (el) this.clipboard.push(structuredClone(el));
|
|
1216
|
-
}
|
|
1217
|
-
this.pasteCount = 0;
|
|
1218
|
-
}
|
|
1219
|
-
handlePaste() {
|
|
1220
|
-
if (!this.toolManager || !this.toolContext || this.clipboard.length === 0 || this.isToolActive)
|
|
1221
|
-
return;
|
|
1222
|
-
const tool = this.toolManager.activeTool;
|
|
1223
|
-
if (tool?.name !== "select") return;
|
|
1224
|
-
const selectTool = tool;
|
|
1225
|
-
this.pasteCount++;
|
|
1226
|
-
const offset = this.pasteCount * 20;
|
|
1227
|
-
const idMap = /* @__PURE__ */ new Map();
|
|
1228
|
-
for (const el of this.clipboard) {
|
|
1229
|
-
idMap.set(el.id, createId(el.type));
|
|
1230
|
-
}
|
|
1231
|
-
const newIds = [];
|
|
1232
|
-
this.historyRecorder?.begin();
|
|
1233
|
-
for (const el of this.clipboard) {
|
|
1234
|
-
const clone = structuredClone(el);
|
|
1235
|
-
const newId = idMap.get(el.id);
|
|
1236
|
-
if (!newId) continue;
|
|
1237
|
-
clone.id = newId;
|
|
1238
|
-
clone.position = { x: clone.position.x + offset, y: clone.position.y + offset };
|
|
1239
|
-
if (clone.type === "arrow") {
|
|
1240
|
-
const arrow = clone;
|
|
1241
|
-
arrow.from = { x: arrow.from.x + offset, y: arrow.from.y + offset };
|
|
1242
|
-
arrow.to = { x: arrow.to.x + offset, y: arrow.to.y + offset };
|
|
1243
|
-
delete arrow.cachedControlPoint;
|
|
1244
|
-
if (arrow.fromBinding) {
|
|
1245
|
-
const newTarget = idMap.get(arrow.fromBinding.elementId);
|
|
1246
|
-
if (newTarget) {
|
|
1247
|
-
arrow.fromBinding = { elementId: newTarget };
|
|
1248
|
-
} else {
|
|
1249
|
-
delete arrow.fromBinding;
|
|
1250
|
-
}
|
|
1251
|
-
}
|
|
1252
|
-
if (arrow.toBinding) {
|
|
1253
|
-
const newTarget = idMap.get(arrow.toBinding.elementId);
|
|
1254
|
-
if (newTarget) {
|
|
1255
|
-
arrow.toBinding = { elementId: newTarget };
|
|
1256
|
-
} else {
|
|
1257
|
-
delete arrow.toBinding;
|
|
1258
|
-
}
|
|
1259
|
-
}
|
|
1260
|
-
}
|
|
1261
|
-
if (this.toolContext.activeLayerId) {
|
|
1262
|
-
clone.layerId = this.toolContext.activeLayerId;
|
|
1263
|
-
}
|
|
1264
|
-
this.toolContext.store.add(clone);
|
|
1265
|
-
newIds.push(clone.id);
|
|
1266
|
-
}
|
|
1267
|
-
this.historyRecorder?.commit();
|
|
1268
|
-
selectTool.setSelection(newIds);
|
|
1269
|
-
this.toolContext.requestRender();
|
|
1270
|
-
}
|
|
1271
|
-
handleZOrder(operation) {
|
|
1272
|
-
if (!this.toolManager || !this.toolContext) return;
|
|
1273
|
-
const tool = this.toolManager.activeTool;
|
|
1274
|
-
if (tool?.name !== "select") return;
|
|
1275
|
-
const selectTool = tool;
|
|
1276
|
-
const ids = selectTool.selectedIds;
|
|
1277
|
-
if (ids.length === 0) return;
|
|
1278
|
-
this.historyRecorder?.begin();
|
|
1279
|
-
for (const id of ids) {
|
|
1280
|
-
switch (operation) {
|
|
1281
|
-
case "forward":
|
|
1282
|
-
this.toolContext.store.bringForward(id);
|
|
1283
|
-
break;
|
|
1284
|
-
case "backward":
|
|
1285
|
-
this.toolContext.store.sendBackward(id);
|
|
1286
|
-
break;
|
|
1287
|
-
case "front":
|
|
1288
|
-
this.toolContext.store.bringToFront(id);
|
|
1289
|
-
break;
|
|
1290
|
-
case "back":
|
|
1291
|
-
this.toolContext.store.sendToBack(id);
|
|
1292
|
-
break;
|
|
1293
|
-
}
|
|
1294
|
-
}
|
|
1295
|
-
this.historyRecorder?.commit();
|
|
1296
|
-
this.toolContext.requestRender();
|
|
1297
|
-
}
|
|
1298
1433
|
cancelToolIfActive(e) {
|
|
1299
1434
|
if (this.isToolActive) {
|
|
1300
1435
|
this.dispatchToolUp(e);
|
|
@@ -3247,6 +3382,26 @@ var NoteEditor = class {
|
|
|
3247
3382
|
}
|
|
3248
3383
|
};
|
|
3249
3384
|
|
|
3385
|
+
// src/elements/bounds.ts
|
|
3386
|
+
function getElementsBoundingBox(elements) {
|
|
3387
|
+
let minX = Infinity;
|
|
3388
|
+
let minY = Infinity;
|
|
3389
|
+
let maxX = -Infinity;
|
|
3390
|
+
let maxY = -Infinity;
|
|
3391
|
+
let found = false;
|
|
3392
|
+
for (const el of elements) {
|
|
3393
|
+
const b = getElementBounds(el);
|
|
3394
|
+
if (!b) continue;
|
|
3395
|
+
found = true;
|
|
3396
|
+
if (b.x < minX) minX = b.x;
|
|
3397
|
+
if (b.y < minY) minY = b.y;
|
|
3398
|
+
if (b.x + b.w > maxX) maxX = b.x + b.w;
|
|
3399
|
+
if (b.y + b.h > maxY) maxY = b.y + b.h;
|
|
3400
|
+
}
|
|
3401
|
+
if (!found) return null;
|
|
3402
|
+
return { x: minX, y: minY, w: maxX - minX, h: maxY - minY };
|
|
3403
|
+
}
|
|
3404
|
+
|
|
3250
3405
|
// src/tools/tool-manager.ts
|
|
3251
3406
|
var ToolManager = class {
|
|
3252
3407
|
tools = /* @__PURE__ */ new Map();
|
|
@@ -3472,6 +3627,9 @@ var HistoryRecorder = class {
|
|
|
3472
3627
|
this.recording = true;
|
|
3473
3628
|
}
|
|
3474
3629
|
begin() {
|
|
3630
|
+
if (this.transaction !== null) {
|
|
3631
|
+
this.commit();
|
|
3632
|
+
}
|
|
3475
3633
|
this.transaction = [];
|
|
3476
3634
|
this.updateSnapshots.clear();
|
|
3477
3635
|
}
|
|
@@ -4660,7 +4818,8 @@ var Viewport = class {
|
|
|
4660
4818
|
toolManager: this.toolManager,
|
|
4661
4819
|
toolContext: this.toolContext,
|
|
4662
4820
|
historyRecorder: this.historyRecorder,
|
|
4663
|
-
historyStack: this.history
|
|
4821
|
+
historyStack: this.history,
|
|
4822
|
+
fitToContent: () => this.fitToContent()
|
|
4664
4823
|
});
|
|
4665
4824
|
this.domNodeManager = new DomNodeManager({
|
|
4666
4825
|
domLayer: this.domLayer,
|
|
@@ -4770,6 +4929,13 @@ var Viewport = class {
|
|
|
4770
4929
|
this._snapToGrid = enabled;
|
|
4771
4930
|
this.toolContext.snapToGrid = enabled;
|
|
4772
4931
|
}
|
|
4932
|
+
fitToContent(padding = 40) {
|
|
4933
|
+
if (this.wrapper.clientWidth === 0 || this.wrapper.clientHeight === 0) return;
|
|
4934
|
+
const visibleElements = this.store.getAll().filter((el) => this.layerManager.isLayerVisible(el.layerId));
|
|
4935
|
+
const bbox = getElementsBoundingBox(visibleElements);
|
|
4936
|
+
if (!bbox) return;
|
|
4937
|
+
this.camera.fitToContent(bbox, this.wrapper.clientWidth, this.wrapper.clientHeight, padding);
|
|
4938
|
+
}
|
|
4773
4939
|
requestRender() {
|
|
4774
4940
|
this.renderLoop.requestRender();
|
|
4775
4941
|
}
|
|
@@ -4788,6 +4954,7 @@ var Viewport = class {
|
|
|
4788
4954
|
return exportImage(this.store, options, this.layerManager);
|
|
4789
4955
|
}
|
|
4790
4956
|
loadState(state) {
|
|
4957
|
+
this.inputHandler.flushPendingHistory();
|
|
4791
4958
|
this.historyRecorder.pause();
|
|
4792
4959
|
this.noteEditor.destroy(this.store);
|
|
4793
4960
|
this.domNodeManager.clearDomNodes();
|
|
@@ -4824,6 +4991,7 @@ var Viewport = class {
|
|
|
4824
4991
|
this.loadState(parseState(json));
|
|
4825
4992
|
}
|
|
4826
4993
|
undo() {
|
|
4994
|
+
this.inputHandler.flushPendingHistory();
|
|
4827
4995
|
this.historyRecorder.pause();
|
|
4828
4996
|
const result = this.history.undo(this.store);
|
|
4829
4997
|
this.historyRecorder.resume();
|
|
@@ -4831,6 +4999,7 @@ var Viewport = class {
|
|
|
4831
4999
|
return result;
|
|
4832
5000
|
}
|
|
4833
5001
|
redo() {
|
|
5002
|
+
this.inputHandler.flushPendingHistory();
|
|
4834
5003
|
this.historyRecorder.pause();
|
|
4835
5004
|
const result = this.history.redo(this.store);
|
|
4836
5005
|
this.historyRecorder.resume();
|
|
@@ -5150,26 +5319,6 @@ var Viewport = class {
|
|
|
5150
5319
|
}
|
|
5151
5320
|
};
|
|
5152
5321
|
|
|
5153
|
-
// src/elements/bounds.ts
|
|
5154
|
-
function getElementsBoundingBox(elements) {
|
|
5155
|
-
let minX = Infinity;
|
|
5156
|
-
let minY = Infinity;
|
|
5157
|
-
let maxX = -Infinity;
|
|
5158
|
-
let maxY = -Infinity;
|
|
5159
|
-
let found = false;
|
|
5160
|
-
for (const el of elements) {
|
|
5161
|
-
const b = getElementBounds(el);
|
|
5162
|
-
if (!b) continue;
|
|
5163
|
-
found = true;
|
|
5164
|
-
if (b.x < minX) minX = b.x;
|
|
5165
|
-
if (b.y < minY) minY = b.y;
|
|
5166
|
-
if (b.x + b.w > maxX) maxX = b.x + b.w;
|
|
5167
|
-
if (b.y + b.h > maxY) maxY = b.y + b.h;
|
|
5168
|
-
}
|
|
5169
|
-
if (!found) return null;
|
|
5170
|
-
return { x: minX, y: minY, w: maxX - minX, h: maxY - minY };
|
|
5171
|
-
}
|
|
5172
|
-
|
|
5173
5322
|
// src/tools/hand-tool.ts
|
|
5174
5323
|
var HandTool = class {
|
|
5175
5324
|
name = "hand";
|
|
@@ -5661,23 +5810,7 @@ var SelectTool = class {
|
|
|
5661
5810
|
});
|
|
5662
5811
|
}
|
|
5663
5812
|
}
|
|
5664
|
-
|
|
5665
|
-
for (const id of this._selectedIds) {
|
|
5666
|
-
const el = ctx.store.getById(id);
|
|
5667
|
-
if (el && el.type !== "arrow") movedNonArrowIds.add(id);
|
|
5668
|
-
}
|
|
5669
|
-
if (movedNonArrowIds.size > 0) {
|
|
5670
|
-
const updatedArrows = /* @__PURE__ */ new Set();
|
|
5671
|
-
for (const id of movedNonArrowIds) {
|
|
5672
|
-
const boundArrows = findBoundArrows(id, ctx.store);
|
|
5673
|
-
for (const ba of boundArrows) {
|
|
5674
|
-
if (updatedArrows.has(ba.id)) continue;
|
|
5675
|
-
updatedArrows.add(ba.id);
|
|
5676
|
-
const updates = updateBoundArrow(ba, ctx.store);
|
|
5677
|
-
if (updates) ctx.store.update(ba.id, updates);
|
|
5678
|
-
}
|
|
5679
|
-
}
|
|
5680
|
-
}
|
|
5813
|
+
this.updateArrowsBoundTo(this._selectedIds, ctx);
|
|
5681
5814
|
ctx.requestRender();
|
|
5682
5815
|
return;
|
|
5683
5816
|
}
|
|
@@ -5728,6 +5861,49 @@ var SelectTool = class {
|
|
|
5728
5861
|
}
|
|
5729
5862
|
}
|
|
5730
5863
|
}
|
|
5864
|
+
updateArrowsBoundTo(ids, ctx) {
|
|
5865
|
+
const movedNonArrowIds = /* @__PURE__ */ new Set();
|
|
5866
|
+
for (const id of ids) {
|
|
5867
|
+
const el = ctx.store.getById(id);
|
|
5868
|
+
if (el && el.type !== "arrow") movedNonArrowIds.add(id);
|
|
5869
|
+
}
|
|
5870
|
+
if (movedNonArrowIds.size === 0) return;
|
|
5871
|
+
const updatedArrows = /* @__PURE__ */ new Set();
|
|
5872
|
+
for (const id of movedNonArrowIds) {
|
|
5873
|
+
const boundArrows = findBoundArrows(id, ctx.store);
|
|
5874
|
+
for (const ba of boundArrows) {
|
|
5875
|
+
if (updatedArrows.has(ba.id)) continue;
|
|
5876
|
+
updatedArrows.add(ba.id);
|
|
5877
|
+
const updates = updateBoundArrow(ba, ctx.store);
|
|
5878
|
+
if (updates) ctx.store.update(ba.id, updates);
|
|
5879
|
+
}
|
|
5880
|
+
}
|
|
5881
|
+
}
|
|
5882
|
+
nudgeSelection(dx, dy, ctx) {
|
|
5883
|
+
let moved = false;
|
|
5884
|
+
for (const id of this._selectedIds) {
|
|
5885
|
+
const el = ctx.store.getById(id);
|
|
5886
|
+
if (!el || el.locked) continue;
|
|
5887
|
+
if (el.type === "arrow") {
|
|
5888
|
+
if (el.fromBinding || el.toBinding) continue;
|
|
5889
|
+
ctx.store.update(id, {
|
|
5890
|
+
position: { x: el.position.x + dx, y: el.position.y + dy },
|
|
5891
|
+
from: { x: el.from.x + dx, y: el.from.y + dy },
|
|
5892
|
+
to: { x: el.to.x + dx, y: el.to.y + dy }
|
|
5893
|
+
});
|
|
5894
|
+
} else {
|
|
5895
|
+
ctx.store.update(id, {
|
|
5896
|
+
position: { x: el.position.x + dx, y: el.position.y + dy }
|
|
5897
|
+
});
|
|
5898
|
+
}
|
|
5899
|
+
moved = true;
|
|
5900
|
+
}
|
|
5901
|
+
if (moved) {
|
|
5902
|
+
this.updateArrowsBoundTo(this._selectedIds, ctx);
|
|
5903
|
+
ctx.requestRender();
|
|
5904
|
+
}
|
|
5905
|
+
return moved;
|
|
5906
|
+
}
|
|
5731
5907
|
updateHoverCursor(world, ctx) {
|
|
5732
5908
|
const arrowHit = hitTestArrowHandles(world, this._selectedIds, ctx);
|
|
5733
5909
|
if (arrowHit) {
|
|
@@ -5805,11 +5981,7 @@ var SelectTool = class {
|
|
|
5805
5981
|
position: { x, y },
|
|
5806
5982
|
size: { w, h }
|
|
5807
5983
|
});
|
|
5808
|
-
|
|
5809
|
-
for (const ba of boundArrows) {
|
|
5810
|
-
const updates = updateBoundArrow(ba, ctx.store);
|
|
5811
|
-
if (updates) ctx.store.update(ba.id, updates);
|
|
5812
|
-
}
|
|
5984
|
+
this.updateArrowsBoundTo([this.mode.elementId], ctx);
|
|
5813
5985
|
ctx.requestRender();
|
|
5814
5986
|
}
|
|
5815
5987
|
hitTestResizeHandle(world, ctx) {
|
|
@@ -6856,7 +7028,7 @@ var TemplateTool = class {
|
|
|
6856
7028
|
};
|
|
6857
7029
|
|
|
6858
7030
|
// src/index.ts
|
|
6859
|
-
var VERSION = "0.
|
|
7031
|
+
var VERSION = "0.17.0";
|
|
6860
7032
|
// Annotate the CommonJS export names for ESM import in node:
|
|
6861
7033
|
0 && (module.exports = {
|
|
6862
7034
|
AddElementCommand,
|