@fieldnotes/core 0.18.0 → 0.20.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/LICENSE +21 -21
- package/README.md +472 -442
- package/dist/index.cjs +336 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +30 -2
- package/dist/index.d.ts +30 -2
- package/dist/index.js +335 -61
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -273,6 +273,12 @@ function sanitizeNode(node) {
|
|
|
273
273
|
sanitizeNode(el);
|
|
274
274
|
}
|
|
275
275
|
}
|
|
276
|
+
function isNoteContentEmpty(html) {
|
|
277
|
+
if (!html) return true;
|
|
278
|
+
const doc = new DOMParser().parseFromString(html, "text/html");
|
|
279
|
+
const text = doc.body.textContent ?? "";
|
|
280
|
+
return text.replace(/\u00a0/g, " ").trim().length === 0;
|
|
281
|
+
}
|
|
276
282
|
function sanitizeAttributes(el, tag) {
|
|
277
283
|
const attrs = Array.from(el.attributes);
|
|
278
284
|
for (const attr of attrs) {
|
|
@@ -1041,14 +1047,158 @@ var KeyboardActions = class {
|
|
|
1041
1047
|
}
|
|
1042
1048
|
};
|
|
1043
1049
|
|
|
1050
|
+
// src/canvas/shortcut-map.ts
|
|
1051
|
+
var DEFAULT_BINDINGS = [
|
|
1052
|
+
["delete", ["delete", "backspace"]],
|
|
1053
|
+
["deselect", ["escape"]],
|
|
1054
|
+
["undo", ["mod+z"]],
|
|
1055
|
+
["redo", ["mod+y", "mod+shift+z"]],
|
|
1056
|
+
["select-all", ["mod+a"]],
|
|
1057
|
+
["copy", ["mod+c"]],
|
|
1058
|
+
["paste", ["mod+v"]],
|
|
1059
|
+
["duplicate", ["mod+d"]],
|
|
1060
|
+
["z-forward", ["]"]],
|
|
1061
|
+
["z-backward", ["["]],
|
|
1062
|
+
["z-front", ["mod+]"]],
|
|
1063
|
+
["z-back", ["mod+["]],
|
|
1064
|
+
["zoom-fit", ["shift+1"]],
|
|
1065
|
+
["nudge-left", ["arrowleft"]],
|
|
1066
|
+
["nudge-right", ["arrowright"]],
|
|
1067
|
+
["nudge-up", ["arrowup"]],
|
|
1068
|
+
["nudge-down", ["arrowdown"]],
|
|
1069
|
+
["tool:select", ["v"]],
|
|
1070
|
+
["tool:hand", ["h"]],
|
|
1071
|
+
["tool:pencil", ["p"]],
|
|
1072
|
+
["tool:eraser", ["e"]],
|
|
1073
|
+
["tool:arrow", ["a"]],
|
|
1074
|
+
["tool:note", ["n"]],
|
|
1075
|
+
["tool:text", ["t"]],
|
|
1076
|
+
["tool:shape", ["s"]],
|
|
1077
|
+
["tool:measure", ["m"]],
|
|
1078
|
+
["tool:template", ["g"]]
|
|
1079
|
+
];
|
|
1080
|
+
var ALLOW_SHIFT = /* @__PURE__ */ new Set(["nudge-left", "nudge-right", "nudge-up", "nudge-down"]);
|
|
1081
|
+
var MODIFIERS = /* @__PURE__ */ new Set(["mod", "ctrl", "meta", "shift", "alt"]);
|
|
1082
|
+
function parseBinding(binding) {
|
|
1083
|
+
const parts = binding.toLowerCase().split("+");
|
|
1084
|
+
const key = parts.pop();
|
|
1085
|
+
if (key === void 0 || key.length === 0 || MODIFIERS.has(key)) {
|
|
1086
|
+
throw new Error(`Invalid shortcut binding "${binding}": missing key`);
|
|
1087
|
+
}
|
|
1088
|
+
const normalizedKey = key === "space" ? " " : key;
|
|
1089
|
+
const parsed = {
|
|
1090
|
+
mod: false,
|
|
1091
|
+
ctrl: false,
|
|
1092
|
+
meta: false,
|
|
1093
|
+
shift: false,
|
|
1094
|
+
alt: false,
|
|
1095
|
+
key: normalizedKey,
|
|
1096
|
+
digit: /^[0-9]$/.test(normalizedKey)
|
|
1097
|
+
};
|
|
1098
|
+
for (const part of parts) {
|
|
1099
|
+
switch (part) {
|
|
1100
|
+
case "mod":
|
|
1101
|
+
parsed.mod = true;
|
|
1102
|
+
break;
|
|
1103
|
+
case "ctrl":
|
|
1104
|
+
parsed.ctrl = true;
|
|
1105
|
+
break;
|
|
1106
|
+
case "meta":
|
|
1107
|
+
parsed.meta = true;
|
|
1108
|
+
break;
|
|
1109
|
+
case "shift":
|
|
1110
|
+
parsed.shift = true;
|
|
1111
|
+
break;
|
|
1112
|
+
case "alt":
|
|
1113
|
+
parsed.alt = true;
|
|
1114
|
+
break;
|
|
1115
|
+
default:
|
|
1116
|
+
throw new Error(`Invalid shortcut binding "${binding}": unknown modifier "${part}"`);
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
return parsed;
|
|
1120
|
+
}
|
|
1121
|
+
function bindingMatches(p, e, allowShift) {
|
|
1122
|
+
if (p.mod) {
|
|
1123
|
+
if (!e.ctrlKey && !e.metaKey) return false;
|
|
1124
|
+
} else if (e.ctrlKey !== p.ctrl || e.metaKey !== p.meta) {
|
|
1125
|
+
return false;
|
|
1126
|
+
}
|
|
1127
|
+
if (!allowShift && e.shiftKey !== p.shift) return false;
|
|
1128
|
+
if (e.altKey !== p.alt) return false;
|
|
1129
|
+
return p.digit ? e.code === `Digit${p.key}` : e.key.toLowerCase() === p.key;
|
|
1130
|
+
}
|
|
1131
|
+
function toArray(bindings) {
|
|
1132
|
+
if (bindings === null) return [];
|
|
1133
|
+
return Array.isArray(bindings) ? [...bindings] : [bindings];
|
|
1134
|
+
}
|
|
1135
|
+
var ShortcutMap = class {
|
|
1136
|
+
raw = /* @__PURE__ */ new Map();
|
|
1137
|
+
parsed = /* @__PURE__ */ new Map();
|
|
1138
|
+
constructor(overrides) {
|
|
1139
|
+
this.applyDefaults();
|
|
1140
|
+
if (overrides) {
|
|
1141
|
+
for (const [action, bindings] of Object.entries(overrides)) {
|
|
1142
|
+
this.rebind(action, bindings);
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
/** First matching action in registration order wins when bindings conflict. */
|
|
1147
|
+
match(e) {
|
|
1148
|
+
for (const [action, parsedList] of this.parsed) {
|
|
1149
|
+
const allowShift = ALLOW_SHIFT.has(action);
|
|
1150
|
+
for (const p of parsedList) {
|
|
1151
|
+
if (bindingMatches(p, e, allowShift)) return action;
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
return null;
|
|
1155
|
+
}
|
|
1156
|
+
rebind(action, bindings) {
|
|
1157
|
+
const list = toArray(bindings);
|
|
1158
|
+
const parsedList = list.map(parseBinding);
|
|
1159
|
+
this.raw.set(action, list);
|
|
1160
|
+
this.parsed.set(action, parsedList);
|
|
1161
|
+
}
|
|
1162
|
+
disable(action) {
|
|
1163
|
+
this.rebind(action, null);
|
|
1164
|
+
}
|
|
1165
|
+
reset(action) {
|
|
1166
|
+
if (action === void 0) {
|
|
1167
|
+
this.raw.clear();
|
|
1168
|
+
this.parsed.clear();
|
|
1169
|
+
this.applyDefaults();
|
|
1170
|
+
return;
|
|
1171
|
+
}
|
|
1172
|
+
const def = DEFAULT_BINDINGS.find(([name]) => name === action);
|
|
1173
|
+
if (def) {
|
|
1174
|
+
this.rebind(action, [...def[1]]);
|
|
1175
|
+
} else if (this.raw.has(action)) {
|
|
1176
|
+
this.raw.delete(action);
|
|
1177
|
+
this.parsed.delete(action);
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
getBindings() {
|
|
1181
|
+
const out = {};
|
|
1182
|
+
for (const [action, list] of this.raw) {
|
|
1183
|
+
out[action] = [...list];
|
|
1184
|
+
}
|
|
1185
|
+
return out;
|
|
1186
|
+
}
|
|
1187
|
+
applyDefaults() {
|
|
1188
|
+
for (const [action, bindings] of DEFAULT_BINDINGS) {
|
|
1189
|
+
this.rebind(action, [...bindings]);
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
};
|
|
1193
|
+
|
|
1044
1194
|
// src/canvas/input-handler.ts
|
|
1045
1195
|
var ZOOM_SENSITIVITY = 1e-3;
|
|
1046
1196
|
var MIDDLE_BUTTON = 1;
|
|
1047
|
-
var
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1197
|
+
var NUDGE_DELTAS = {
|
|
1198
|
+
"nudge-left": [-1, 0],
|
|
1199
|
+
"nudge-right": [1, 0],
|
|
1200
|
+
"nudge-up": [0, -1],
|
|
1201
|
+
"nudge-down": [0, 1]
|
|
1052
1202
|
};
|
|
1053
1203
|
var InputHandler = class {
|
|
1054
1204
|
constructor(element, camera, options = {}) {
|
|
@@ -1066,7 +1216,13 @@ var InputHandler = class {
|
|
|
1066
1216
|
isToolActive: () => this.isToolActive,
|
|
1067
1217
|
fitToContent: options.fitToContent
|
|
1068
1218
|
});
|
|
1219
|
+
this.shortcutMap = new ShortcutMap(options.shortcuts?.bindings);
|
|
1220
|
+
this.scope = options.shortcuts?.scope ?? "focus";
|
|
1069
1221
|
this.element.style.touchAction = "none";
|
|
1222
|
+
if (this.scope === "focus") {
|
|
1223
|
+
this.element.tabIndex = 0;
|
|
1224
|
+
this.element.style.outline = "none";
|
|
1225
|
+
}
|
|
1070
1226
|
this.bind();
|
|
1071
1227
|
}
|
|
1072
1228
|
isPanning = false;
|
|
@@ -1085,6 +1241,8 @@ var InputHandler = class {
|
|
|
1085
1241
|
deferredDown = null;
|
|
1086
1242
|
abortController = new AbortController();
|
|
1087
1243
|
actions;
|
|
1244
|
+
shortcutMap;
|
|
1245
|
+
scope;
|
|
1088
1246
|
setToolManager(toolManager, toolContext) {
|
|
1089
1247
|
this.toolManager = toolManager;
|
|
1090
1248
|
this.toolContext = toolContext;
|
|
@@ -1092,6 +1250,9 @@ var InputHandler = class {
|
|
|
1092
1250
|
flushPendingHistory() {
|
|
1093
1251
|
this.actions.flushPendingNudge();
|
|
1094
1252
|
}
|
|
1253
|
+
get shortcuts() {
|
|
1254
|
+
return this.shortcutMap;
|
|
1255
|
+
}
|
|
1095
1256
|
destroy() {
|
|
1096
1257
|
this.actions.dispose();
|
|
1097
1258
|
this.abortController.abort();
|
|
@@ -1121,6 +1282,7 @@ var InputHandler = class {
|
|
|
1121
1282
|
});
|
|
1122
1283
|
};
|
|
1123
1284
|
onPointerDown = (e) => {
|
|
1285
|
+
this.focusSelf();
|
|
1124
1286
|
this.activePointers.set(e.pointerId, { x: e.clientX, y: e.clientY });
|
|
1125
1287
|
this.element.setPointerCapture?.(e.pointerId);
|
|
1126
1288
|
if (this.activePointers.size === 2) {
|
|
@@ -1203,57 +1365,13 @@ var InputHandler = class {
|
|
|
1203
1365
|
if (target?.isContentEditable) return;
|
|
1204
1366
|
const tag = target?.tagName;
|
|
1205
1367
|
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return;
|
|
1368
|
+
if (!this.isInScope()) return;
|
|
1206
1369
|
if (e.key === " ") {
|
|
1207
1370
|
this.spaceHeld = true;
|
|
1208
1371
|
}
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
if (e.key === "Escape") {
|
|
1213
|
-
this.actions.deselect();
|
|
1214
|
-
}
|
|
1215
|
-
if ((e.ctrlKey || e.metaKey) && e.key === "z" && !e.shiftKey) {
|
|
1216
|
-
e.preventDefault();
|
|
1217
|
-
this.actions.undo();
|
|
1218
|
-
}
|
|
1219
|
-
if ((e.ctrlKey || e.metaKey) && (e.key === "y" || e.key === "z" && e.shiftKey)) {
|
|
1220
|
-
e.preventDefault();
|
|
1221
|
-
this.actions.redo();
|
|
1222
|
-
}
|
|
1223
|
-
if ((e.ctrlKey || e.metaKey) && e.key === "a") {
|
|
1224
|
-
e.preventDefault();
|
|
1225
|
-
this.actions.selectAll();
|
|
1226
|
-
}
|
|
1227
|
-
if ((e.ctrlKey || e.metaKey) && e.key === "c") {
|
|
1228
|
-
e.preventDefault();
|
|
1229
|
-
this.actions.copy();
|
|
1230
|
-
}
|
|
1231
|
-
if ((e.ctrlKey || e.metaKey) && e.key === "v") {
|
|
1232
|
-
e.preventDefault();
|
|
1233
|
-
this.actions.paste();
|
|
1234
|
-
}
|
|
1235
|
-
if ((e.ctrlKey || e.metaKey) && e.key === "d") {
|
|
1236
|
-
e.preventDefault();
|
|
1237
|
-
this.actions.duplicate();
|
|
1238
|
-
}
|
|
1239
|
-
if (e.key === "]") {
|
|
1240
|
-
e.preventDefault();
|
|
1241
|
-
this.actions.zOrder(e.ctrlKey || e.metaKey ? "front" : "forward");
|
|
1242
|
-
}
|
|
1243
|
-
if (e.key === "[") {
|
|
1244
|
-
e.preventDefault();
|
|
1245
|
-
this.actions.zOrder(e.ctrlKey || e.metaKey ? "back" : "backward");
|
|
1246
|
-
}
|
|
1247
|
-
if (e.shiftKey && e.code === "Digit1" && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
|
1248
|
-
e.preventDefault();
|
|
1249
|
-
this.actions.zoomToFit();
|
|
1250
|
-
}
|
|
1251
|
-
const nudgeDelta = NUDGE_KEYS[e.key];
|
|
1252
|
-
if (nudgeDelta) {
|
|
1253
|
-
const [dx, dy] = nudgeDelta;
|
|
1254
|
-
if (this.actions.nudge(dx, dy, e.shiftKey)) {
|
|
1255
|
-
e.preventDefault();
|
|
1256
|
-
}
|
|
1372
|
+
const action = this.shortcutMap.match(e);
|
|
1373
|
+
if (action !== null) {
|
|
1374
|
+
this.runAction(action, e);
|
|
1257
1375
|
}
|
|
1258
1376
|
};
|
|
1259
1377
|
onKeyUp = (e) => {
|
|
@@ -1268,6 +1386,79 @@ var InputHandler = class {
|
|
|
1268
1386
|
}
|
|
1269
1387
|
}
|
|
1270
1388
|
};
|
|
1389
|
+
runAction(action, e) {
|
|
1390
|
+
switch (action) {
|
|
1391
|
+
case "delete":
|
|
1392
|
+
e.preventDefault();
|
|
1393
|
+
this.actions.deleteSelected();
|
|
1394
|
+
return;
|
|
1395
|
+
case "deselect":
|
|
1396
|
+
this.actions.deselect();
|
|
1397
|
+
return;
|
|
1398
|
+
case "undo":
|
|
1399
|
+
e.preventDefault();
|
|
1400
|
+
this.actions.undo();
|
|
1401
|
+
return;
|
|
1402
|
+
case "redo":
|
|
1403
|
+
e.preventDefault();
|
|
1404
|
+
this.actions.redo();
|
|
1405
|
+
return;
|
|
1406
|
+
case "select-all":
|
|
1407
|
+
e.preventDefault();
|
|
1408
|
+
this.actions.selectAll();
|
|
1409
|
+
return;
|
|
1410
|
+
case "copy":
|
|
1411
|
+
e.preventDefault();
|
|
1412
|
+
this.actions.copy();
|
|
1413
|
+
return;
|
|
1414
|
+
case "paste":
|
|
1415
|
+
e.preventDefault();
|
|
1416
|
+
this.actions.paste();
|
|
1417
|
+
return;
|
|
1418
|
+
case "duplicate":
|
|
1419
|
+
e.preventDefault();
|
|
1420
|
+
this.actions.duplicate();
|
|
1421
|
+
return;
|
|
1422
|
+
case "z-forward":
|
|
1423
|
+
e.preventDefault();
|
|
1424
|
+
this.actions.zOrder("forward");
|
|
1425
|
+
return;
|
|
1426
|
+
case "z-backward":
|
|
1427
|
+
e.preventDefault();
|
|
1428
|
+
this.actions.zOrder("backward");
|
|
1429
|
+
return;
|
|
1430
|
+
case "z-front":
|
|
1431
|
+
e.preventDefault();
|
|
1432
|
+
this.actions.zOrder("front");
|
|
1433
|
+
return;
|
|
1434
|
+
case "z-back":
|
|
1435
|
+
e.preventDefault();
|
|
1436
|
+
this.actions.zOrder("back");
|
|
1437
|
+
return;
|
|
1438
|
+
case "zoom-fit":
|
|
1439
|
+
e.preventDefault();
|
|
1440
|
+
this.actions.zoomToFit();
|
|
1441
|
+
return;
|
|
1442
|
+
case "nudge-left":
|
|
1443
|
+
case "nudge-right":
|
|
1444
|
+
case "nudge-up":
|
|
1445
|
+
case "nudge-down": {
|
|
1446
|
+
const delta = NUDGE_DELTAS[action];
|
|
1447
|
+
if (delta && this.actions.nudge(delta[0], delta[1], e.shiftKey)) {
|
|
1448
|
+
e.preventDefault();
|
|
1449
|
+
}
|
|
1450
|
+
return;
|
|
1451
|
+
}
|
|
1452
|
+
default:
|
|
1453
|
+
if (action.startsWith("tool:")) {
|
|
1454
|
+
if (this.isToolActive) return;
|
|
1455
|
+
e.preventDefault();
|
|
1456
|
+
this.toolContext?.switchTool?.(action.slice("tool:".length));
|
|
1457
|
+
return;
|
|
1458
|
+
}
|
|
1459
|
+
console.warn(`[fieldnotes] unknown shortcut action "${action}"`);
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1271
1462
|
startPinch() {
|
|
1272
1463
|
this.inputFilter.reset();
|
|
1273
1464
|
this.deferredDown = null;
|
|
@@ -1338,6 +1529,15 @@ var InputHandler = class {
|
|
|
1338
1529
|
this.toolManager.handlePointerUp(this.toPointerState(e), this.toolContext);
|
|
1339
1530
|
this.historyRecorder?.commit();
|
|
1340
1531
|
}
|
|
1532
|
+
isInScope() {
|
|
1533
|
+
if (this.scope === "window") return true;
|
|
1534
|
+
const active = document.activeElement;
|
|
1535
|
+
return active === this.element || this.element.contains(active);
|
|
1536
|
+
}
|
|
1537
|
+
focusSelf() {
|
|
1538
|
+
if (this.scope !== "focus" || this.isInScope()) return;
|
|
1539
|
+
this.element.focus({ preventScroll: true });
|
|
1540
|
+
}
|
|
1341
1541
|
cancelToolIfActive(e) {
|
|
1342
1542
|
if (this.isToolActive) {
|
|
1343
1543
|
this.dispatchToolUp(e);
|
|
@@ -3170,17 +3370,36 @@ var FORMAT_SHORTCUTS = {
|
|
|
3170
3370
|
i: toggleItalic,
|
|
3171
3371
|
u: toggleUnderline
|
|
3172
3372
|
};
|
|
3373
|
+
function ensureEditorStyles() {
|
|
3374
|
+
if (document.querySelector("style[data-fieldnotes-editor]")) return;
|
|
3375
|
+
const style = document.createElement("style");
|
|
3376
|
+
style.setAttribute("data-fieldnotes-editor", "");
|
|
3377
|
+
style.textContent = `[data-fn-placeholder][data-fn-empty='true']::before {
|
|
3378
|
+
content: attr(data-fn-placeholder);
|
|
3379
|
+
color: #9e9e9e;
|
|
3380
|
+
position: absolute;
|
|
3381
|
+
pointer-events: none;
|
|
3382
|
+
}`;
|
|
3383
|
+
document.head.appendChild(style);
|
|
3384
|
+
}
|
|
3385
|
+
function isNodeEmpty(node) {
|
|
3386
|
+
const text = node.textContent ?? "";
|
|
3387
|
+
return text.replace(/\u00a0/g, " ").trim().length === 0;
|
|
3388
|
+
}
|
|
3173
3389
|
var NoteEditor = class {
|
|
3174
3390
|
editingId = null;
|
|
3175
3391
|
editingNode = null;
|
|
3176
3392
|
blurHandler = null;
|
|
3177
3393
|
keyHandler = null;
|
|
3178
3394
|
pointerHandler = null;
|
|
3395
|
+
inputHandler = null;
|
|
3179
3396
|
pendingEditId = null;
|
|
3180
3397
|
onStopCallback = null;
|
|
3181
3398
|
toolbar;
|
|
3399
|
+
placeholder;
|
|
3182
3400
|
constructor(options) {
|
|
3183
3401
|
this.toolbar = options?.toolbar === false ? null : new NoteToolbar(options?.fontSizePresets);
|
|
3402
|
+
this.placeholder = options?.placeholder ?? "Type\u2026";
|
|
3184
3403
|
}
|
|
3185
3404
|
get isEditing() {
|
|
3186
3405
|
return this.editingId !== null;
|
|
@@ -3215,6 +3434,11 @@ var NoteEditor = class {
|
|
|
3215
3434
|
if (this.pointerHandler) {
|
|
3216
3435
|
this.editingNode.removeEventListener("pointerdown", this.pointerHandler);
|
|
3217
3436
|
}
|
|
3437
|
+
if (this.inputHandler) {
|
|
3438
|
+
this.editingNode.removeEventListener("input", this.inputHandler);
|
|
3439
|
+
}
|
|
3440
|
+
this.editingNode.removeAttribute("data-fn-placeholder");
|
|
3441
|
+
this.editingNode.removeAttribute("data-fn-empty");
|
|
3218
3442
|
const text = sanitizeNoteHtml(this.editingNode.innerHTML);
|
|
3219
3443
|
store.update(this.editingId, { text });
|
|
3220
3444
|
this.editingNode.contentEditable = "false";
|
|
@@ -3231,6 +3455,7 @@ var NoteEditor = class {
|
|
|
3231
3455
|
this.blurHandler = null;
|
|
3232
3456
|
this.keyHandler = null;
|
|
3233
3457
|
this.pointerHandler = null;
|
|
3458
|
+
this.inputHandler = null;
|
|
3234
3459
|
}
|
|
3235
3460
|
destroy(store) {
|
|
3236
3461
|
this.pendingEditId = null;
|
|
@@ -3261,6 +3486,13 @@ var NoteEditor = class {
|
|
|
3261
3486
|
selection.removeAllRanges();
|
|
3262
3487
|
selection.addRange(range);
|
|
3263
3488
|
}
|
|
3489
|
+
ensureEditorStyles();
|
|
3490
|
+
node.setAttribute("data-fn-placeholder", this.placeholder);
|
|
3491
|
+
node.setAttribute("data-fn-empty", String(isNodeEmpty(node)));
|
|
3492
|
+
this.inputHandler = () => {
|
|
3493
|
+
node.setAttribute("data-fn-empty", String(isNodeEmpty(node)));
|
|
3494
|
+
};
|
|
3495
|
+
node.addEventListener("input", this.inputHandler);
|
|
3264
3496
|
this.toolbar?.show(node);
|
|
3265
3497
|
this.blurHandler = (e) => {
|
|
3266
3498
|
const related = e.relatedTarget;
|
|
@@ -4699,7 +4931,8 @@ var Viewport = class {
|
|
|
4699
4931
|
});
|
|
4700
4932
|
this.noteEditor = new NoteEditor({
|
|
4701
4933
|
fontSizePresets: options.fontSizePresets,
|
|
4702
|
-
toolbar: options.toolbar
|
|
4934
|
+
toolbar: options.toolbar,
|
|
4935
|
+
placeholder: options.placeholder
|
|
4703
4936
|
});
|
|
4704
4937
|
this.noteEditor.setOnStop((id) => this.onTextEditStop(id));
|
|
4705
4938
|
this.onHtmlElementMount = options.onHtmlElementMount;
|
|
@@ -4732,7 +4965,8 @@ var Viewport = class {
|
|
|
4732
4965
|
toolContext: this.toolContext,
|
|
4733
4966
|
historyRecorder: this.historyRecorder,
|
|
4734
4967
|
historyStack: this.history,
|
|
4735
|
-
fitToContent: () => this.fitToContent()
|
|
4968
|
+
fitToContent: () => this.fitToContent(),
|
|
4969
|
+
shortcuts: options.shortcuts
|
|
4736
4970
|
});
|
|
4737
4971
|
this.domNodeManager = new DomNodeManager({
|
|
4738
4972
|
domLayer: this.domLayer,
|
|
@@ -4906,6 +5140,9 @@ var Viewport = class {
|
|
|
4906
5140
|
setTool(name) {
|
|
4907
5141
|
this.toolManager.setTool(name, this.toolContext);
|
|
4908
5142
|
}
|
|
5143
|
+
get shortcuts() {
|
|
5144
|
+
return this.inputHandler.shortcuts;
|
|
5145
|
+
}
|
|
4909
5146
|
undo() {
|
|
4910
5147
|
this.inputHandler.flushPendingHistory();
|
|
4911
5148
|
this.historyRecorder.pause();
|
|
@@ -5041,7 +5278,16 @@ var Viewport = class {
|
|
|
5041
5278
|
}
|
|
5042
5279
|
onTextEditStop(elementId) {
|
|
5043
5280
|
const element = this.store.getById(elementId);
|
|
5044
|
-
if (!element
|
|
5281
|
+
if (!element) return;
|
|
5282
|
+
if (element.type === "note") {
|
|
5283
|
+
if (isNoteContentEmpty(element.text)) {
|
|
5284
|
+
this.historyRecorder.begin();
|
|
5285
|
+
this.store.remove(elementId);
|
|
5286
|
+
this.historyRecorder.commit();
|
|
5287
|
+
}
|
|
5288
|
+
return;
|
|
5289
|
+
}
|
|
5290
|
+
if (element.type !== "text") return;
|
|
5045
5291
|
if (!element.text || element.text.trim() === "") {
|
|
5046
5292
|
this.historyRecorder.begin();
|
|
5047
5293
|
this.store.remove(elementId);
|
|
@@ -5590,6 +5836,7 @@ var SelectTool = class {
|
|
|
5590
5836
|
pendingSingleSelectId = null;
|
|
5591
5837
|
hasDragged = false;
|
|
5592
5838
|
resizeAspectRatio = 0;
|
|
5839
|
+
hoveredId = null;
|
|
5593
5840
|
get selectedIds() {
|
|
5594
5841
|
return [...this._selectedIds];
|
|
5595
5842
|
}
|
|
@@ -5606,6 +5853,7 @@ var SelectTool = class {
|
|
|
5606
5853
|
onDeactivate(ctx) {
|
|
5607
5854
|
this._selectedIds = [];
|
|
5608
5855
|
this.mode = { type: "idle" };
|
|
5856
|
+
this.hoveredId = null;
|
|
5609
5857
|
ctx.setCursor?.("default");
|
|
5610
5858
|
}
|
|
5611
5859
|
snap(point, ctx) {
|
|
@@ -5613,6 +5861,7 @@ var SelectTool = class {
|
|
|
5613
5861
|
}
|
|
5614
5862
|
onPointerDown(state, ctx) {
|
|
5615
5863
|
this.ctx = ctx;
|
|
5864
|
+
this.setHovered(null, ctx);
|
|
5616
5865
|
const world = ctx.camera.screenToWorld({ x: state.x, y: state.y });
|
|
5617
5866
|
this.lastWorld = this.snap(world, ctx);
|
|
5618
5867
|
this.currentWorld = world;
|
|
@@ -5755,7 +6004,8 @@ var SelectTool = class {
|
|
|
5755
6004
|
}
|
|
5756
6005
|
onHover(state, ctx) {
|
|
5757
6006
|
const world = ctx.camera.screenToWorld({ x: state.x, y: state.y });
|
|
5758
|
-
this.updateHoverCursor(world, ctx);
|
|
6007
|
+
const hoverId = this.updateHoverCursor(world, ctx);
|
|
6008
|
+
this.setHovered(hoverId, ctx);
|
|
5759
6009
|
}
|
|
5760
6010
|
renderOverlay(canvasCtx) {
|
|
5761
6011
|
this.renderMarquee(canvasCtx);
|
|
@@ -5776,6 +6026,23 @@ var SelectTool = class {
|
|
|
5776
6026
|
canvasCtx.restore();
|
|
5777
6027
|
}
|
|
5778
6028
|
}
|
|
6029
|
+
if (this.hoveredId && this.ctx && this.mode.type === "idle") {
|
|
6030
|
+
if (!this._selectedIds.includes(this.hoveredId)) {
|
|
6031
|
+
const el = this.ctx.store.getById(this.hoveredId);
|
|
6032
|
+
if (el) {
|
|
6033
|
+
const b = getElementBounds(el);
|
|
6034
|
+
if (b) {
|
|
6035
|
+
canvasCtx.save();
|
|
6036
|
+
canvasCtx.strokeStyle = "#2196F3";
|
|
6037
|
+
canvasCtx.globalAlpha = 0.35;
|
|
6038
|
+
canvasCtx.lineWidth = 1.5 / this.ctx.camera.zoom;
|
|
6039
|
+
canvasCtx.setLineDash([]);
|
|
6040
|
+
canvasCtx.strokeRect(b.x, b.y, b.w, b.h);
|
|
6041
|
+
canvasCtx.restore();
|
|
6042
|
+
}
|
|
6043
|
+
}
|
|
6044
|
+
}
|
|
6045
|
+
}
|
|
5779
6046
|
}
|
|
5780
6047
|
updateArrowsBoundTo(ids, ctx) {
|
|
5781
6048
|
const movedNonArrowIds = /* @__PURE__ */ new Set();
|
|
@@ -5824,20 +6091,26 @@ var SelectTool = class {
|
|
|
5824
6091
|
const arrowHit = hitTestArrowHandles(world, this._selectedIds, ctx);
|
|
5825
6092
|
if (arrowHit) {
|
|
5826
6093
|
ctx.setCursor?.(getArrowHandleCursor(arrowHit.handle, false));
|
|
5827
|
-
return;
|
|
6094
|
+
return null;
|
|
5828
6095
|
}
|
|
5829
6096
|
const templateResizeHit = this.hitTestTemplateResizeHandle(world, ctx);
|
|
5830
6097
|
if (templateResizeHit) {
|
|
5831
6098
|
ctx.setCursor?.("nwse-resize");
|
|
5832
|
-
return;
|
|
6099
|
+
return null;
|
|
5833
6100
|
}
|
|
5834
6101
|
const resizeHit = this.hitTestResizeHandle(world, ctx);
|
|
5835
6102
|
if (resizeHit) {
|
|
5836
6103
|
ctx.setCursor?.(HANDLE_CURSORS[resizeHit.handle]);
|
|
5837
|
-
return;
|
|
6104
|
+
return null;
|
|
5838
6105
|
}
|
|
5839
6106
|
const hit = this.hitTest(world, ctx);
|
|
5840
6107
|
ctx.setCursor?.(hit ? "move" : "default");
|
|
6108
|
+
return hit ? hit.id : null;
|
|
6109
|
+
}
|
|
6110
|
+
setHovered(id, ctx) {
|
|
6111
|
+
if (this.hoveredId === id) return;
|
|
6112
|
+
this.hoveredId = id;
|
|
6113
|
+
ctx.requestRender();
|
|
5841
6114
|
}
|
|
5842
6115
|
handleResize(world, ctx, shiftKey = false) {
|
|
5843
6116
|
if (this.mode.type !== "resizing") return;
|
|
@@ -6944,7 +7217,7 @@ var TemplateTool = class {
|
|
|
6944
7217
|
};
|
|
6945
7218
|
|
|
6946
7219
|
// src/index.ts
|
|
6947
|
-
var VERSION = "0.
|
|
7220
|
+
var VERSION = "0.20.0";
|
|
6948
7221
|
export {
|
|
6949
7222
|
AddElementCommand,
|
|
6950
7223
|
ArrowTool,
|
|
@@ -7018,6 +7291,7 @@ export {
|
|
|
7018
7291
|
getHexDistance,
|
|
7019
7292
|
isBindable,
|
|
7020
7293
|
isNearBezier,
|
|
7294
|
+
isNoteContentEmpty,
|
|
7021
7295
|
parseState,
|
|
7022
7296
|
sanitizeNoteHtml,
|
|
7023
7297
|
setFontSize,
|