@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.js
CHANGED
|
@@ -811,9 +811,228 @@ function createId(prefix) {
|
|
|
811
811
|
return `${prefix}_${Date.now().toString(36)}_${(counter++).toString(36)}`;
|
|
812
812
|
}
|
|
813
813
|
|
|
814
|
+
// src/canvas/keyboard-actions.ts
|
|
815
|
+
var KeyboardActions = class {
|
|
816
|
+
constructor(deps) {
|
|
817
|
+
this.deps = deps;
|
|
818
|
+
}
|
|
819
|
+
clipboard = [];
|
|
820
|
+
pasteCount = 0;
|
|
821
|
+
nudgeTimer = null;
|
|
822
|
+
dispose() {
|
|
823
|
+
this.flushPendingNudge();
|
|
824
|
+
}
|
|
825
|
+
selectTool() {
|
|
826
|
+
const tm = this.deps.getToolManager();
|
|
827
|
+
const ctx = this.deps.getToolContext();
|
|
828
|
+
if (!tm || !ctx) return null;
|
|
829
|
+
const tool = tm.activeTool;
|
|
830
|
+
if (tool?.name !== "select") return null;
|
|
831
|
+
return { tool, ctx };
|
|
832
|
+
}
|
|
833
|
+
nudge(dx, dy, byCell) {
|
|
834
|
+
if (this.deps.isToolActive()) return false;
|
|
835
|
+
const sel = this.selectTool();
|
|
836
|
+
if (!sel) return false;
|
|
837
|
+
if (sel.tool.selectedIds.length === 0) return false;
|
|
838
|
+
const step = byCell ? sel.ctx.gridSize ?? 10 : 1;
|
|
839
|
+
if (this.nudgeTimer === null) {
|
|
840
|
+
this.deps.getHistoryRecorder()?.begin();
|
|
841
|
+
} else {
|
|
842
|
+
clearTimeout(this.nudgeTimer);
|
|
843
|
+
}
|
|
844
|
+
const moved = sel.tool.nudgeSelection(dx * step, dy * step, sel.ctx);
|
|
845
|
+
this.nudgeTimer = setTimeout(() => this.flushPendingNudge(), 400);
|
|
846
|
+
return moved;
|
|
847
|
+
}
|
|
848
|
+
flushPendingNudge() {
|
|
849
|
+
if (this.nudgeTimer === null) return;
|
|
850
|
+
clearTimeout(this.nudgeTimer);
|
|
851
|
+
this.nudgeTimer = null;
|
|
852
|
+
this.deps.getHistoryRecorder()?.commit();
|
|
853
|
+
}
|
|
854
|
+
deleteSelected() {
|
|
855
|
+
this.flushPendingNudge();
|
|
856
|
+
const sel = this.selectTool();
|
|
857
|
+
if (!sel) return;
|
|
858
|
+
const ids = sel.tool.selectedIds;
|
|
859
|
+
if (ids.length === 0) return;
|
|
860
|
+
const recorder = this.deps.getHistoryRecorder();
|
|
861
|
+
recorder?.begin();
|
|
862
|
+
for (const id of ids) {
|
|
863
|
+
sel.ctx.store.remove(id);
|
|
864
|
+
}
|
|
865
|
+
recorder?.commit();
|
|
866
|
+
sel.ctx.requestRender();
|
|
867
|
+
}
|
|
868
|
+
undo() {
|
|
869
|
+
this.flushPendingNudge();
|
|
870
|
+
const ctx = this.deps.getToolContext();
|
|
871
|
+
const stack = this.deps.getHistoryStack();
|
|
872
|
+
if (!stack || !ctx) return;
|
|
873
|
+
const recorder = this.deps.getHistoryRecorder();
|
|
874
|
+
recorder?.pause();
|
|
875
|
+
stack.undo(ctx.store);
|
|
876
|
+
recorder?.resume();
|
|
877
|
+
ctx.requestRender();
|
|
878
|
+
}
|
|
879
|
+
redo() {
|
|
880
|
+
this.flushPendingNudge();
|
|
881
|
+
const ctx = this.deps.getToolContext();
|
|
882
|
+
const stack = this.deps.getHistoryStack();
|
|
883
|
+
if (!stack || !ctx) return;
|
|
884
|
+
const recorder = this.deps.getHistoryRecorder();
|
|
885
|
+
recorder?.pause();
|
|
886
|
+
stack.redo(ctx.store);
|
|
887
|
+
recorder?.resume();
|
|
888
|
+
ctx.requestRender();
|
|
889
|
+
}
|
|
890
|
+
copy() {
|
|
891
|
+
if (this.deps.isToolActive()) return;
|
|
892
|
+
const sel = this.selectTool();
|
|
893
|
+
if (!sel) return;
|
|
894
|
+
const ids = sel.tool.selectedIds;
|
|
895
|
+
if (ids.length === 0) return;
|
|
896
|
+
this.clipboard = [];
|
|
897
|
+
for (const id of ids) {
|
|
898
|
+
const el = sel.ctx.store.getById(id);
|
|
899
|
+
if (el) this.clipboard.push(structuredClone(el));
|
|
900
|
+
}
|
|
901
|
+
this.pasteCount = 0;
|
|
902
|
+
}
|
|
903
|
+
paste() {
|
|
904
|
+
this.flushPendingNudge();
|
|
905
|
+
if (this.clipboard.length === 0 || this.deps.isToolActive()) return;
|
|
906
|
+
const sel = this.selectTool();
|
|
907
|
+
if (!sel) return;
|
|
908
|
+
this.pasteCount++;
|
|
909
|
+
this.insertClones(this.clipboard, this.pasteCount * 20, sel);
|
|
910
|
+
}
|
|
911
|
+
duplicate() {
|
|
912
|
+
this.flushPendingNudge();
|
|
913
|
+
if (this.deps.isToolActive()) return;
|
|
914
|
+
const sel = this.selectTool();
|
|
915
|
+
if (!sel) return;
|
|
916
|
+
const source = [];
|
|
917
|
+
for (const id of sel.tool.selectedIds) {
|
|
918
|
+
const el = sel.ctx.store.getById(id);
|
|
919
|
+
if (el) source.push(el);
|
|
920
|
+
}
|
|
921
|
+
if (source.length === 0) return;
|
|
922
|
+
this.insertClones(source, 20, sel);
|
|
923
|
+
}
|
|
924
|
+
deselect() {
|
|
925
|
+
if (this.deps.isToolActive()) return;
|
|
926
|
+
const sel = this.selectTool();
|
|
927
|
+
if (!sel) return;
|
|
928
|
+
if (sel.tool.selectedIds.length === 0) return;
|
|
929
|
+
sel.tool.setSelection([]);
|
|
930
|
+
sel.ctx.requestRender();
|
|
931
|
+
}
|
|
932
|
+
selectAll() {
|
|
933
|
+
if (this.deps.isToolActive()) return;
|
|
934
|
+
const tm = this.deps.getToolManager();
|
|
935
|
+
const ctx = this.deps.getToolContext();
|
|
936
|
+
if (!tm || !ctx) return;
|
|
937
|
+
if (tm.activeTool?.name !== "select") {
|
|
938
|
+
ctx.switchTool?.("select");
|
|
939
|
+
}
|
|
940
|
+
const sel = this.selectTool();
|
|
941
|
+
if (!sel) return;
|
|
942
|
+
const ids = sel.ctx.store.getAll().filter(
|
|
943
|
+
(el) => !el.locked && (sel.ctx.isLayerVisible?.(el.layerId) ?? true) && !(sel.ctx.isLayerLocked?.(el.layerId) ?? false)
|
|
944
|
+
).map((el) => el.id);
|
|
945
|
+
sel.tool.setSelection(ids);
|
|
946
|
+
sel.ctx.requestRender();
|
|
947
|
+
}
|
|
948
|
+
zoomToFit() {
|
|
949
|
+
if (this.deps.isToolActive()) return;
|
|
950
|
+
this.deps.fitToContent?.();
|
|
951
|
+
}
|
|
952
|
+
zOrder(operation) {
|
|
953
|
+
this.flushPendingNudge();
|
|
954
|
+
const sel = this.selectTool();
|
|
955
|
+
if (!sel) return;
|
|
956
|
+
const ids = sel.tool.selectedIds;
|
|
957
|
+
if (ids.length === 0) return;
|
|
958
|
+
const recorder = this.deps.getHistoryRecorder();
|
|
959
|
+
recorder?.begin();
|
|
960
|
+
for (const id of ids) {
|
|
961
|
+
switch (operation) {
|
|
962
|
+
case "forward":
|
|
963
|
+
sel.ctx.store.bringForward(id);
|
|
964
|
+
break;
|
|
965
|
+
case "backward":
|
|
966
|
+
sel.ctx.store.sendBackward(id);
|
|
967
|
+
break;
|
|
968
|
+
case "front":
|
|
969
|
+
sel.ctx.store.bringToFront(id);
|
|
970
|
+
break;
|
|
971
|
+
case "back":
|
|
972
|
+
sel.ctx.store.sendToBack(id);
|
|
973
|
+
break;
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
recorder?.commit();
|
|
977
|
+
sel.ctx.requestRender();
|
|
978
|
+
}
|
|
979
|
+
insertClones(source, offset, sel) {
|
|
980
|
+
const idMap = /* @__PURE__ */ new Map();
|
|
981
|
+
for (const el of source) {
|
|
982
|
+
idMap.set(el.id, createId(el.type));
|
|
983
|
+
}
|
|
984
|
+
const newIds = [];
|
|
985
|
+
const recorder = this.deps.getHistoryRecorder();
|
|
986
|
+
recorder?.begin();
|
|
987
|
+
for (const el of source) {
|
|
988
|
+
const clone = structuredClone(el);
|
|
989
|
+
const newId = idMap.get(el.id);
|
|
990
|
+
if (!newId) continue;
|
|
991
|
+
clone.id = newId;
|
|
992
|
+
clone.position = { x: clone.position.x + offset, y: clone.position.y + offset };
|
|
993
|
+
if (clone.type === "arrow") {
|
|
994
|
+
const arrow = clone;
|
|
995
|
+
arrow.from = { x: arrow.from.x + offset, y: arrow.from.y + offset };
|
|
996
|
+
arrow.to = { x: arrow.to.x + offset, y: arrow.to.y + offset };
|
|
997
|
+
delete arrow.cachedControlPoint;
|
|
998
|
+
if (arrow.fromBinding) {
|
|
999
|
+
const newTarget = idMap.get(arrow.fromBinding.elementId);
|
|
1000
|
+
if (newTarget) {
|
|
1001
|
+
arrow.fromBinding = { elementId: newTarget };
|
|
1002
|
+
} else {
|
|
1003
|
+
delete arrow.fromBinding;
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
if (arrow.toBinding) {
|
|
1007
|
+
const newTarget = idMap.get(arrow.toBinding.elementId);
|
|
1008
|
+
if (newTarget) {
|
|
1009
|
+
arrow.toBinding = { elementId: newTarget };
|
|
1010
|
+
} else {
|
|
1011
|
+
delete arrow.toBinding;
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
if (sel.ctx.activeLayerId) {
|
|
1016
|
+
clone.layerId = sel.ctx.activeLayerId;
|
|
1017
|
+
}
|
|
1018
|
+
sel.ctx.store.add(clone);
|
|
1019
|
+
newIds.push(clone.id);
|
|
1020
|
+
}
|
|
1021
|
+
recorder?.commit();
|
|
1022
|
+
sel.tool.setSelection(newIds);
|
|
1023
|
+
sel.ctx.requestRender();
|
|
1024
|
+
}
|
|
1025
|
+
};
|
|
1026
|
+
|
|
814
1027
|
// src/canvas/input-handler.ts
|
|
815
1028
|
var ZOOM_SENSITIVITY = 1e-3;
|
|
816
1029
|
var MIDDLE_BUTTON = 1;
|
|
1030
|
+
var NUDGE_KEYS = {
|
|
1031
|
+
ArrowLeft: [-1, 0],
|
|
1032
|
+
ArrowRight: [1, 0],
|
|
1033
|
+
ArrowUp: [0, -1],
|
|
1034
|
+
ArrowDown: [0, 1]
|
|
1035
|
+
};
|
|
817
1036
|
var InputHandler = class {
|
|
818
1037
|
constructor(element, camera, options = {}) {
|
|
819
1038
|
this.element = element;
|
|
@@ -822,6 +1041,14 @@ var InputHandler = class {
|
|
|
822
1041
|
this.toolContext = options.toolContext ?? null;
|
|
823
1042
|
this.historyRecorder = options.historyRecorder ?? null;
|
|
824
1043
|
this.historyStack = options.historyStack ?? null;
|
|
1044
|
+
this.actions = new KeyboardActions({
|
|
1045
|
+
getToolManager: () => this.toolManager,
|
|
1046
|
+
getToolContext: () => this.toolContext,
|
|
1047
|
+
getHistoryRecorder: () => this.historyRecorder,
|
|
1048
|
+
getHistoryStack: () => this.historyStack,
|
|
1049
|
+
isToolActive: () => this.isToolActive,
|
|
1050
|
+
fitToContent: options.fitToContent
|
|
1051
|
+
});
|
|
825
1052
|
this.element.style.touchAction = "none";
|
|
826
1053
|
this.bind();
|
|
827
1054
|
}
|
|
@@ -840,13 +1067,16 @@ var InputHandler = class {
|
|
|
840
1067
|
inputFilter = new InputFilter();
|
|
841
1068
|
deferredDown = null;
|
|
842
1069
|
abortController = new AbortController();
|
|
843
|
-
|
|
844
|
-
pasteCount = 0;
|
|
1070
|
+
actions;
|
|
845
1071
|
setToolManager(toolManager, toolContext) {
|
|
846
1072
|
this.toolManager = toolManager;
|
|
847
1073
|
this.toolContext = toolContext;
|
|
848
1074
|
}
|
|
1075
|
+
flushPendingHistory() {
|
|
1076
|
+
this.actions.flushPendingNudge();
|
|
1077
|
+
}
|
|
849
1078
|
destroy() {
|
|
1079
|
+
this.actions.dispose();
|
|
850
1080
|
this.abortController.abort();
|
|
851
1081
|
this.inputFilter.reset();
|
|
852
1082
|
this.deferredDown = null;
|
|
@@ -952,36 +1182,61 @@ var InputHandler = class {
|
|
|
952
1182
|
}
|
|
953
1183
|
};
|
|
954
1184
|
onKeyDown = (e) => {
|
|
955
|
-
|
|
1185
|
+
const target = e.target;
|
|
1186
|
+
if (target?.isContentEditable) return;
|
|
1187
|
+
const tag = target?.tagName;
|
|
1188
|
+
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return;
|
|
956
1189
|
if (e.key === " ") {
|
|
957
1190
|
this.spaceHeld = true;
|
|
958
1191
|
}
|
|
959
1192
|
if (e.key === "Delete" || e.key === "Backspace") {
|
|
960
|
-
this.deleteSelected();
|
|
1193
|
+
this.actions.deleteSelected();
|
|
1194
|
+
}
|
|
1195
|
+
if (e.key === "Escape") {
|
|
1196
|
+
this.actions.deselect();
|
|
961
1197
|
}
|
|
962
1198
|
if ((e.ctrlKey || e.metaKey) && e.key === "z" && !e.shiftKey) {
|
|
963
1199
|
e.preventDefault();
|
|
964
|
-
this.
|
|
1200
|
+
this.actions.undo();
|
|
965
1201
|
}
|
|
966
1202
|
if ((e.ctrlKey || e.metaKey) && (e.key === "y" || e.key === "z" && e.shiftKey)) {
|
|
967
1203
|
e.preventDefault();
|
|
968
|
-
this.
|
|
1204
|
+
this.actions.redo();
|
|
1205
|
+
}
|
|
1206
|
+
if ((e.ctrlKey || e.metaKey) && e.key === "a") {
|
|
1207
|
+
e.preventDefault();
|
|
1208
|
+
this.actions.selectAll();
|
|
969
1209
|
}
|
|
970
1210
|
if ((e.ctrlKey || e.metaKey) && e.key === "c") {
|
|
971
1211
|
e.preventDefault();
|
|
972
|
-
this.
|
|
1212
|
+
this.actions.copy();
|
|
973
1213
|
}
|
|
974
1214
|
if ((e.ctrlKey || e.metaKey) && e.key === "v") {
|
|
975
1215
|
e.preventDefault();
|
|
976
|
-
this.
|
|
1216
|
+
this.actions.paste();
|
|
1217
|
+
}
|
|
1218
|
+
if ((e.ctrlKey || e.metaKey) && e.key === "d") {
|
|
1219
|
+
e.preventDefault();
|
|
1220
|
+
this.actions.duplicate();
|
|
977
1221
|
}
|
|
978
1222
|
if (e.key === "]") {
|
|
979
1223
|
e.preventDefault();
|
|
980
|
-
this.
|
|
1224
|
+
this.actions.zOrder(e.ctrlKey || e.metaKey ? "front" : "forward");
|
|
981
1225
|
}
|
|
982
1226
|
if (e.key === "[") {
|
|
983
1227
|
e.preventDefault();
|
|
984
|
-
this.
|
|
1228
|
+
this.actions.zOrder(e.ctrlKey || e.metaKey ? "back" : "backward");
|
|
1229
|
+
}
|
|
1230
|
+
if (e.shiftKey && e.code === "Digit1" && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
|
1231
|
+
e.preventDefault();
|
|
1232
|
+
this.actions.zoomToFit();
|
|
1233
|
+
}
|
|
1234
|
+
const nudgeDelta = NUDGE_KEYS[e.key];
|
|
1235
|
+
if (nudgeDelta) {
|
|
1236
|
+
const [dx, dy] = nudgeDelta;
|
|
1237
|
+
if (this.actions.nudge(dx, dy, e.shiftKey)) {
|
|
1238
|
+
e.preventDefault();
|
|
1239
|
+
}
|
|
985
1240
|
}
|
|
986
1241
|
};
|
|
987
1242
|
onKeyUp = (e) => {
|
|
@@ -1045,6 +1300,7 @@ var InputHandler = class {
|
|
|
1045
1300
|
}
|
|
1046
1301
|
dispatchToolDown(e) {
|
|
1047
1302
|
if (!this.toolManager || !this.toolContext) return;
|
|
1303
|
+
this.actions.flushPendingNudge();
|
|
1048
1304
|
this.historyRecorder?.begin();
|
|
1049
1305
|
this.isToolActive = true;
|
|
1050
1306
|
this.toolManager.handlePointerDown(this.toPointerState(e), this.toolContext);
|
|
@@ -1065,127 +1321,6 @@ var InputHandler = class {
|
|
|
1065
1321
|
this.toolManager.handlePointerUp(this.toPointerState(e), this.toolContext);
|
|
1066
1322
|
this.historyRecorder?.commit();
|
|
1067
1323
|
}
|
|
1068
|
-
deleteSelected() {
|
|
1069
|
-
if (!this.toolManager || !this.toolContext) return;
|
|
1070
|
-
const tool = this.toolManager.activeTool;
|
|
1071
|
-
if (tool?.name !== "select") return;
|
|
1072
|
-
const selectTool = tool;
|
|
1073
|
-
const ids = selectTool.selectedIds;
|
|
1074
|
-
if (ids.length === 0) return;
|
|
1075
|
-
this.historyRecorder?.begin();
|
|
1076
|
-
for (const id of ids) {
|
|
1077
|
-
this.toolContext.store.remove(id);
|
|
1078
|
-
}
|
|
1079
|
-
this.historyRecorder?.commit();
|
|
1080
|
-
this.toolContext.requestRender();
|
|
1081
|
-
}
|
|
1082
|
-
handleUndo() {
|
|
1083
|
-
if (!this.historyStack || !this.toolContext) return;
|
|
1084
|
-
this.historyRecorder?.pause();
|
|
1085
|
-
this.historyStack.undo(this.toolContext.store);
|
|
1086
|
-
this.historyRecorder?.resume();
|
|
1087
|
-
this.toolContext.requestRender();
|
|
1088
|
-
}
|
|
1089
|
-
handleRedo() {
|
|
1090
|
-
if (!this.historyStack || !this.toolContext) return;
|
|
1091
|
-
this.historyRecorder?.pause();
|
|
1092
|
-
this.historyStack.redo(this.toolContext.store);
|
|
1093
|
-
this.historyRecorder?.resume();
|
|
1094
|
-
this.toolContext.requestRender();
|
|
1095
|
-
}
|
|
1096
|
-
handleCopy() {
|
|
1097
|
-
if (!this.toolManager || !this.toolContext || this.isToolActive) return;
|
|
1098
|
-
const tool = this.toolManager.activeTool;
|
|
1099
|
-
if (tool?.name !== "select") return;
|
|
1100
|
-
const selectTool = tool;
|
|
1101
|
-
const ids = selectTool.selectedIds;
|
|
1102
|
-
if (ids.length === 0) return;
|
|
1103
|
-
this.clipboard = [];
|
|
1104
|
-
for (const id of ids) {
|
|
1105
|
-
const el = this.toolContext.store.getById(id);
|
|
1106
|
-
if (el) this.clipboard.push(structuredClone(el));
|
|
1107
|
-
}
|
|
1108
|
-
this.pasteCount = 0;
|
|
1109
|
-
}
|
|
1110
|
-
handlePaste() {
|
|
1111
|
-
if (!this.toolManager || !this.toolContext || this.clipboard.length === 0 || this.isToolActive)
|
|
1112
|
-
return;
|
|
1113
|
-
const tool = this.toolManager.activeTool;
|
|
1114
|
-
if (tool?.name !== "select") return;
|
|
1115
|
-
const selectTool = tool;
|
|
1116
|
-
this.pasteCount++;
|
|
1117
|
-
const offset = this.pasteCount * 20;
|
|
1118
|
-
const idMap = /* @__PURE__ */ new Map();
|
|
1119
|
-
for (const el of this.clipboard) {
|
|
1120
|
-
idMap.set(el.id, createId(el.type));
|
|
1121
|
-
}
|
|
1122
|
-
const newIds = [];
|
|
1123
|
-
this.historyRecorder?.begin();
|
|
1124
|
-
for (const el of this.clipboard) {
|
|
1125
|
-
const clone = structuredClone(el);
|
|
1126
|
-
const newId = idMap.get(el.id);
|
|
1127
|
-
if (!newId) continue;
|
|
1128
|
-
clone.id = newId;
|
|
1129
|
-
clone.position = { x: clone.position.x + offset, y: clone.position.y + offset };
|
|
1130
|
-
if (clone.type === "arrow") {
|
|
1131
|
-
const arrow = clone;
|
|
1132
|
-
arrow.from = { x: arrow.from.x + offset, y: arrow.from.y + offset };
|
|
1133
|
-
arrow.to = { x: arrow.to.x + offset, y: arrow.to.y + offset };
|
|
1134
|
-
delete arrow.cachedControlPoint;
|
|
1135
|
-
if (arrow.fromBinding) {
|
|
1136
|
-
const newTarget = idMap.get(arrow.fromBinding.elementId);
|
|
1137
|
-
if (newTarget) {
|
|
1138
|
-
arrow.fromBinding = { elementId: newTarget };
|
|
1139
|
-
} else {
|
|
1140
|
-
delete arrow.fromBinding;
|
|
1141
|
-
}
|
|
1142
|
-
}
|
|
1143
|
-
if (arrow.toBinding) {
|
|
1144
|
-
const newTarget = idMap.get(arrow.toBinding.elementId);
|
|
1145
|
-
if (newTarget) {
|
|
1146
|
-
arrow.toBinding = { elementId: newTarget };
|
|
1147
|
-
} else {
|
|
1148
|
-
delete arrow.toBinding;
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
1151
|
-
}
|
|
1152
|
-
if (this.toolContext.activeLayerId) {
|
|
1153
|
-
clone.layerId = this.toolContext.activeLayerId;
|
|
1154
|
-
}
|
|
1155
|
-
this.toolContext.store.add(clone);
|
|
1156
|
-
newIds.push(clone.id);
|
|
1157
|
-
}
|
|
1158
|
-
this.historyRecorder?.commit();
|
|
1159
|
-
selectTool.setSelection(newIds);
|
|
1160
|
-
this.toolContext.requestRender();
|
|
1161
|
-
}
|
|
1162
|
-
handleZOrder(operation) {
|
|
1163
|
-
if (!this.toolManager || !this.toolContext) return;
|
|
1164
|
-
const tool = this.toolManager.activeTool;
|
|
1165
|
-
if (tool?.name !== "select") return;
|
|
1166
|
-
const selectTool = tool;
|
|
1167
|
-
const ids = selectTool.selectedIds;
|
|
1168
|
-
if (ids.length === 0) return;
|
|
1169
|
-
this.historyRecorder?.begin();
|
|
1170
|
-
for (const id of ids) {
|
|
1171
|
-
switch (operation) {
|
|
1172
|
-
case "forward":
|
|
1173
|
-
this.toolContext.store.bringForward(id);
|
|
1174
|
-
break;
|
|
1175
|
-
case "backward":
|
|
1176
|
-
this.toolContext.store.sendBackward(id);
|
|
1177
|
-
break;
|
|
1178
|
-
case "front":
|
|
1179
|
-
this.toolContext.store.bringToFront(id);
|
|
1180
|
-
break;
|
|
1181
|
-
case "back":
|
|
1182
|
-
this.toolContext.store.sendToBack(id);
|
|
1183
|
-
break;
|
|
1184
|
-
}
|
|
1185
|
-
}
|
|
1186
|
-
this.historyRecorder?.commit();
|
|
1187
|
-
this.toolContext.requestRender();
|
|
1188
|
-
}
|
|
1189
1324
|
cancelToolIfActive(e) {
|
|
1190
1325
|
if (this.isToolActive) {
|
|
1191
1326
|
this.dispatchToolUp(e);
|
|
@@ -3138,6 +3273,26 @@ var NoteEditor = class {
|
|
|
3138
3273
|
}
|
|
3139
3274
|
};
|
|
3140
3275
|
|
|
3276
|
+
// src/elements/bounds.ts
|
|
3277
|
+
function getElementsBoundingBox(elements) {
|
|
3278
|
+
let minX = Infinity;
|
|
3279
|
+
let minY = Infinity;
|
|
3280
|
+
let maxX = -Infinity;
|
|
3281
|
+
let maxY = -Infinity;
|
|
3282
|
+
let found = false;
|
|
3283
|
+
for (const el of elements) {
|
|
3284
|
+
const b = getElementBounds(el);
|
|
3285
|
+
if (!b) continue;
|
|
3286
|
+
found = true;
|
|
3287
|
+
if (b.x < minX) minX = b.x;
|
|
3288
|
+
if (b.y < minY) minY = b.y;
|
|
3289
|
+
if (b.x + b.w > maxX) maxX = b.x + b.w;
|
|
3290
|
+
if (b.y + b.h > maxY) maxY = b.y + b.h;
|
|
3291
|
+
}
|
|
3292
|
+
if (!found) return null;
|
|
3293
|
+
return { x: minX, y: minY, w: maxX - minX, h: maxY - minY };
|
|
3294
|
+
}
|
|
3295
|
+
|
|
3141
3296
|
// src/tools/tool-manager.ts
|
|
3142
3297
|
var ToolManager = class {
|
|
3143
3298
|
tools = /* @__PURE__ */ new Map();
|
|
@@ -3363,6 +3518,9 @@ var HistoryRecorder = class {
|
|
|
3363
3518
|
this.recording = true;
|
|
3364
3519
|
}
|
|
3365
3520
|
begin() {
|
|
3521
|
+
if (this.transaction !== null) {
|
|
3522
|
+
this.commit();
|
|
3523
|
+
}
|
|
3366
3524
|
this.transaction = [];
|
|
3367
3525
|
this.updateSnapshots.clear();
|
|
3368
3526
|
}
|
|
@@ -4551,7 +4709,8 @@ var Viewport = class {
|
|
|
4551
4709
|
toolManager: this.toolManager,
|
|
4552
4710
|
toolContext: this.toolContext,
|
|
4553
4711
|
historyRecorder: this.historyRecorder,
|
|
4554
|
-
historyStack: this.history
|
|
4712
|
+
historyStack: this.history,
|
|
4713
|
+
fitToContent: () => this.fitToContent()
|
|
4555
4714
|
});
|
|
4556
4715
|
this.domNodeManager = new DomNodeManager({
|
|
4557
4716
|
domLayer: this.domLayer,
|
|
@@ -4661,6 +4820,13 @@ var Viewport = class {
|
|
|
4661
4820
|
this._snapToGrid = enabled;
|
|
4662
4821
|
this.toolContext.snapToGrid = enabled;
|
|
4663
4822
|
}
|
|
4823
|
+
fitToContent(padding = 40) {
|
|
4824
|
+
if (this.wrapper.clientWidth === 0 || this.wrapper.clientHeight === 0) return;
|
|
4825
|
+
const visibleElements = this.store.getAll().filter((el) => this.layerManager.isLayerVisible(el.layerId));
|
|
4826
|
+
const bbox = getElementsBoundingBox(visibleElements);
|
|
4827
|
+
if (!bbox) return;
|
|
4828
|
+
this.camera.fitToContent(bbox, this.wrapper.clientWidth, this.wrapper.clientHeight, padding);
|
|
4829
|
+
}
|
|
4664
4830
|
requestRender() {
|
|
4665
4831
|
this.renderLoop.requestRender();
|
|
4666
4832
|
}
|
|
@@ -4679,6 +4845,7 @@ var Viewport = class {
|
|
|
4679
4845
|
return exportImage(this.store, options, this.layerManager);
|
|
4680
4846
|
}
|
|
4681
4847
|
loadState(state) {
|
|
4848
|
+
this.inputHandler.flushPendingHistory();
|
|
4682
4849
|
this.historyRecorder.pause();
|
|
4683
4850
|
this.noteEditor.destroy(this.store);
|
|
4684
4851
|
this.domNodeManager.clearDomNodes();
|
|
@@ -4715,6 +4882,7 @@ var Viewport = class {
|
|
|
4715
4882
|
this.loadState(parseState(json));
|
|
4716
4883
|
}
|
|
4717
4884
|
undo() {
|
|
4885
|
+
this.inputHandler.flushPendingHistory();
|
|
4718
4886
|
this.historyRecorder.pause();
|
|
4719
4887
|
const result = this.history.undo(this.store);
|
|
4720
4888
|
this.historyRecorder.resume();
|
|
@@ -4722,6 +4890,7 @@ var Viewport = class {
|
|
|
4722
4890
|
return result;
|
|
4723
4891
|
}
|
|
4724
4892
|
redo() {
|
|
4893
|
+
this.inputHandler.flushPendingHistory();
|
|
4725
4894
|
this.historyRecorder.pause();
|
|
4726
4895
|
const result = this.history.redo(this.store);
|
|
4727
4896
|
this.historyRecorder.resume();
|
|
@@ -5041,26 +5210,6 @@ var Viewport = class {
|
|
|
5041
5210
|
}
|
|
5042
5211
|
};
|
|
5043
5212
|
|
|
5044
|
-
// src/elements/bounds.ts
|
|
5045
|
-
function getElementsBoundingBox(elements) {
|
|
5046
|
-
let minX = Infinity;
|
|
5047
|
-
let minY = Infinity;
|
|
5048
|
-
let maxX = -Infinity;
|
|
5049
|
-
let maxY = -Infinity;
|
|
5050
|
-
let found = false;
|
|
5051
|
-
for (const el of elements) {
|
|
5052
|
-
const b = getElementBounds(el);
|
|
5053
|
-
if (!b) continue;
|
|
5054
|
-
found = true;
|
|
5055
|
-
if (b.x < minX) minX = b.x;
|
|
5056
|
-
if (b.y < minY) minY = b.y;
|
|
5057
|
-
if (b.x + b.w > maxX) maxX = b.x + b.w;
|
|
5058
|
-
if (b.y + b.h > maxY) maxY = b.y + b.h;
|
|
5059
|
-
}
|
|
5060
|
-
if (!found) return null;
|
|
5061
|
-
return { x: minX, y: minY, w: maxX - minX, h: maxY - minY };
|
|
5062
|
-
}
|
|
5063
|
-
|
|
5064
5213
|
// src/tools/hand-tool.ts
|
|
5065
5214
|
var HandTool = class {
|
|
5066
5215
|
name = "hand";
|
|
@@ -5552,23 +5701,7 @@ var SelectTool = class {
|
|
|
5552
5701
|
});
|
|
5553
5702
|
}
|
|
5554
5703
|
}
|
|
5555
|
-
|
|
5556
|
-
for (const id of this._selectedIds) {
|
|
5557
|
-
const el = ctx.store.getById(id);
|
|
5558
|
-
if (el && el.type !== "arrow") movedNonArrowIds.add(id);
|
|
5559
|
-
}
|
|
5560
|
-
if (movedNonArrowIds.size > 0) {
|
|
5561
|
-
const updatedArrows = /* @__PURE__ */ new Set();
|
|
5562
|
-
for (const id of movedNonArrowIds) {
|
|
5563
|
-
const boundArrows = findBoundArrows(id, ctx.store);
|
|
5564
|
-
for (const ba of boundArrows) {
|
|
5565
|
-
if (updatedArrows.has(ba.id)) continue;
|
|
5566
|
-
updatedArrows.add(ba.id);
|
|
5567
|
-
const updates = updateBoundArrow(ba, ctx.store);
|
|
5568
|
-
if (updates) ctx.store.update(ba.id, updates);
|
|
5569
|
-
}
|
|
5570
|
-
}
|
|
5571
|
-
}
|
|
5704
|
+
this.updateArrowsBoundTo(this._selectedIds, ctx);
|
|
5572
5705
|
ctx.requestRender();
|
|
5573
5706
|
return;
|
|
5574
5707
|
}
|
|
@@ -5619,6 +5752,49 @@ var SelectTool = class {
|
|
|
5619
5752
|
}
|
|
5620
5753
|
}
|
|
5621
5754
|
}
|
|
5755
|
+
updateArrowsBoundTo(ids, ctx) {
|
|
5756
|
+
const movedNonArrowIds = /* @__PURE__ */ new Set();
|
|
5757
|
+
for (const id of ids) {
|
|
5758
|
+
const el = ctx.store.getById(id);
|
|
5759
|
+
if (el && el.type !== "arrow") movedNonArrowIds.add(id);
|
|
5760
|
+
}
|
|
5761
|
+
if (movedNonArrowIds.size === 0) return;
|
|
5762
|
+
const updatedArrows = /* @__PURE__ */ new Set();
|
|
5763
|
+
for (const id of movedNonArrowIds) {
|
|
5764
|
+
const boundArrows = findBoundArrows(id, ctx.store);
|
|
5765
|
+
for (const ba of boundArrows) {
|
|
5766
|
+
if (updatedArrows.has(ba.id)) continue;
|
|
5767
|
+
updatedArrows.add(ba.id);
|
|
5768
|
+
const updates = updateBoundArrow(ba, ctx.store);
|
|
5769
|
+
if (updates) ctx.store.update(ba.id, updates);
|
|
5770
|
+
}
|
|
5771
|
+
}
|
|
5772
|
+
}
|
|
5773
|
+
nudgeSelection(dx, dy, ctx) {
|
|
5774
|
+
let moved = false;
|
|
5775
|
+
for (const id of this._selectedIds) {
|
|
5776
|
+
const el = ctx.store.getById(id);
|
|
5777
|
+
if (!el || el.locked) continue;
|
|
5778
|
+
if (el.type === "arrow") {
|
|
5779
|
+
if (el.fromBinding || el.toBinding) continue;
|
|
5780
|
+
ctx.store.update(id, {
|
|
5781
|
+
position: { x: el.position.x + dx, y: el.position.y + dy },
|
|
5782
|
+
from: { x: el.from.x + dx, y: el.from.y + dy },
|
|
5783
|
+
to: { x: el.to.x + dx, y: el.to.y + dy }
|
|
5784
|
+
});
|
|
5785
|
+
} else {
|
|
5786
|
+
ctx.store.update(id, {
|
|
5787
|
+
position: { x: el.position.x + dx, y: el.position.y + dy }
|
|
5788
|
+
});
|
|
5789
|
+
}
|
|
5790
|
+
moved = true;
|
|
5791
|
+
}
|
|
5792
|
+
if (moved) {
|
|
5793
|
+
this.updateArrowsBoundTo(this._selectedIds, ctx);
|
|
5794
|
+
ctx.requestRender();
|
|
5795
|
+
}
|
|
5796
|
+
return moved;
|
|
5797
|
+
}
|
|
5622
5798
|
updateHoverCursor(world, ctx) {
|
|
5623
5799
|
const arrowHit = hitTestArrowHandles(world, this._selectedIds, ctx);
|
|
5624
5800
|
if (arrowHit) {
|
|
@@ -5696,11 +5872,7 @@ var SelectTool = class {
|
|
|
5696
5872
|
position: { x, y },
|
|
5697
5873
|
size: { w, h }
|
|
5698
5874
|
});
|
|
5699
|
-
|
|
5700
|
-
for (const ba of boundArrows) {
|
|
5701
|
-
const updates = updateBoundArrow(ba, ctx.store);
|
|
5702
|
-
if (updates) ctx.store.update(ba.id, updates);
|
|
5703
|
-
}
|
|
5875
|
+
this.updateArrowsBoundTo([this.mode.elementId], ctx);
|
|
5704
5876
|
ctx.requestRender();
|
|
5705
5877
|
}
|
|
5706
5878
|
hitTestResizeHandle(world, ctx) {
|
|
@@ -6747,7 +6919,7 @@ var TemplateTool = class {
|
|
|
6747
6919
|
};
|
|
6748
6920
|
|
|
6749
6921
|
// src/index.ts
|
|
6750
|
-
var VERSION = "0.
|
|
6922
|
+
var VERSION = "0.17.0";
|
|
6751
6923
|
export {
|
|
6752
6924
|
AddElementCommand,
|
|
6753
6925
|
ArrowTool,
|