@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 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
- clipboard = [];
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
- if (e.target?.isContentEditable) return;
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.handleUndo();
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.handleRedo();
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.handleCopy();
1321
+ this.actions.copy();
1082
1322
  }
1083
1323
  if ((e.ctrlKey || e.metaKey) && e.key === "v") {
1084
1324
  e.preventDefault();
1085
- this.handlePaste();
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.handleZOrder(e.ctrlKey || e.metaKey ? "front" : "forward");
1333
+ this.actions.zOrder(e.ctrlKey || e.metaKey ? "front" : "forward");
1090
1334
  }
1091
1335
  if (e.key === "[") {
1092
1336
  e.preventDefault();
1093
- this.handleZOrder(e.ctrlKey || e.metaKey ? "back" : "backward");
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
- const movedNonArrowIds = /* @__PURE__ */ new Set();
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
- const boundArrows = findBoundArrows(this.mode.elementId, ctx.store);
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.16.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,