@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.cjs
CHANGED
|
@@ -92,6 +92,7 @@ __export(index_exports, {
|
|
|
92
92
|
getHexDistance: () => getHexDistance,
|
|
93
93
|
isBindable: () => isBindable,
|
|
94
94
|
isNearBezier: () => isNearBezier,
|
|
95
|
+
isNoteContentEmpty: () => isNoteContentEmpty,
|
|
95
96
|
parseState: () => parseState,
|
|
96
97
|
sanitizeNoteHtml: () => sanitizeNoteHtml,
|
|
97
98
|
setFontSize: () => setFontSize,
|
|
@@ -382,6 +383,12 @@ function sanitizeNode(node) {
|
|
|
382
383
|
sanitizeNode(el);
|
|
383
384
|
}
|
|
384
385
|
}
|
|
386
|
+
function isNoteContentEmpty(html) {
|
|
387
|
+
if (!html) return true;
|
|
388
|
+
const doc = new DOMParser().parseFromString(html, "text/html");
|
|
389
|
+
const text = doc.body.textContent ?? "";
|
|
390
|
+
return text.replace(/\u00a0/g, " ").trim().length === 0;
|
|
391
|
+
}
|
|
385
392
|
function sanitizeAttributes(el, tag) {
|
|
386
393
|
const attrs = Array.from(el.attributes);
|
|
387
394
|
for (const attr of attrs) {
|
|
@@ -1150,14 +1157,158 @@ var KeyboardActions = class {
|
|
|
1150
1157
|
}
|
|
1151
1158
|
};
|
|
1152
1159
|
|
|
1160
|
+
// src/canvas/shortcut-map.ts
|
|
1161
|
+
var DEFAULT_BINDINGS = [
|
|
1162
|
+
["delete", ["delete", "backspace"]],
|
|
1163
|
+
["deselect", ["escape"]],
|
|
1164
|
+
["undo", ["mod+z"]],
|
|
1165
|
+
["redo", ["mod+y", "mod+shift+z"]],
|
|
1166
|
+
["select-all", ["mod+a"]],
|
|
1167
|
+
["copy", ["mod+c"]],
|
|
1168
|
+
["paste", ["mod+v"]],
|
|
1169
|
+
["duplicate", ["mod+d"]],
|
|
1170
|
+
["z-forward", ["]"]],
|
|
1171
|
+
["z-backward", ["["]],
|
|
1172
|
+
["z-front", ["mod+]"]],
|
|
1173
|
+
["z-back", ["mod+["]],
|
|
1174
|
+
["zoom-fit", ["shift+1"]],
|
|
1175
|
+
["nudge-left", ["arrowleft"]],
|
|
1176
|
+
["nudge-right", ["arrowright"]],
|
|
1177
|
+
["nudge-up", ["arrowup"]],
|
|
1178
|
+
["nudge-down", ["arrowdown"]],
|
|
1179
|
+
["tool:select", ["v"]],
|
|
1180
|
+
["tool:hand", ["h"]],
|
|
1181
|
+
["tool:pencil", ["p"]],
|
|
1182
|
+
["tool:eraser", ["e"]],
|
|
1183
|
+
["tool:arrow", ["a"]],
|
|
1184
|
+
["tool:note", ["n"]],
|
|
1185
|
+
["tool:text", ["t"]],
|
|
1186
|
+
["tool:shape", ["s"]],
|
|
1187
|
+
["tool:measure", ["m"]],
|
|
1188
|
+
["tool:template", ["g"]]
|
|
1189
|
+
];
|
|
1190
|
+
var ALLOW_SHIFT = /* @__PURE__ */ new Set(["nudge-left", "nudge-right", "nudge-up", "nudge-down"]);
|
|
1191
|
+
var MODIFIERS = /* @__PURE__ */ new Set(["mod", "ctrl", "meta", "shift", "alt"]);
|
|
1192
|
+
function parseBinding(binding) {
|
|
1193
|
+
const parts = binding.toLowerCase().split("+");
|
|
1194
|
+
const key = parts.pop();
|
|
1195
|
+
if (key === void 0 || key.length === 0 || MODIFIERS.has(key)) {
|
|
1196
|
+
throw new Error(`Invalid shortcut binding "${binding}": missing key`);
|
|
1197
|
+
}
|
|
1198
|
+
const normalizedKey = key === "space" ? " " : key;
|
|
1199
|
+
const parsed = {
|
|
1200
|
+
mod: false,
|
|
1201
|
+
ctrl: false,
|
|
1202
|
+
meta: false,
|
|
1203
|
+
shift: false,
|
|
1204
|
+
alt: false,
|
|
1205
|
+
key: normalizedKey,
|
|
1206
|
+
digit: /^[0-9]$/.test(normalizedKey)
|
|
1207
|
+
};
|
|
1208
|
+
for (const part of parts) {
|
|
1209
|
+
switch (part) {
|
|
1210
|
+
case "mod":
|
|
1211
|
+
parsed.mod = true;
|
|
1212
|
+
break;
|
|
1213
|
+
case "ctrl":
|
|
1214
|
+
parsed.ctrl = true;
|
|
1215
|
+
break;
|
|
1216
|
+
case "meta":
|
|
1217
|
+
parsed.meta = true;
|
|
1218
|
+
break;
|
|
1219
|
+
case "shift":
|
|
1220
|
+
parsed.shift = true;
|
|
1221
|
+
break;
|
|
1222
|
+
case "alt":
|
|
1223
|
+
parsed.alt = true;
|
|
1224
|
+
break;
|
|
1225
|
+
default:
|
|
1226
|
+
throw new Error(`Invalid shortcut binding "${binding}": unknown modifier "${part}"`);
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
return parsed;
|
|
1230
|
+
}
|
|
1231
|
+
function bindingMatches(p, e, allowShift) {
|
|
1232
|
+
if (p.mod) {
|
|
1233
|
+
if (!e.ctrlKey && !e.metaKey) return false;
|
|
1234
|
+
} else if (e.ctrlKey !== p.ctrl || e.metaKey !== p.meta) {
|
|
1235
|
+
return false;
|
|
1236
|
+
}
|
|
1237
|
+
if (!allowShift && e.shiftKey !== p.shift) return false;
|
|
1238
|
+
if (e.altKey !== p.alt) return false;
|
|
1239
|
+
return p.digit ? e.code === `Digit${p.key}` : e.key.toLowerCase() === p.key;
|
|
1240
|
+
}
|
|
1241
|
+
function toArray(bindings) {
|
|
1242
|
+
if (bindings === null) return [];
|
|
1243
|
+
return Array.isArray(bindings) ? [...bindings] : [bindings];
|
|
1244
|
+
}
|
|
1245
|
+
var ShortcutMap = class {
|
|
1246
|
+
raw = /* @__PURE__ */ new Map();
|
|
1247
|
+
parsed = /* @__PURE__ */ new Map();
|
|
1248
|
+
constructor(overrides) {
|
|
1249
|
+
this.applyDefaults();
|
|
1250
|
+
if (overrides) {
|
|
1251
|
+
for (const [action, bindings] of Object.entries(overrides)) {
|
|
1252
|
+
this.rebind(action, bindings);
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
/** First matching action in registration order wins when bindings conflict. */
|
|
1257
|
+
match(e) {
|
|
1258
|
+
for (const [action, parsedList] of this.parsed) {
|
|
1259
|
+
const allowShift = ALLOW_SHIFT.has(action);
|
|
1260
|
+
for (const p of parsedList) {
|
|
1261
|
+
if (bindingMatches(p, e, allowShift)) return action;
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
return null;
|
|
1265
|
+
}
|
|
1266
|
+
rebind(action, bindings) {
|
|
1267
|
+
const list = toArray(bindings);
|
|
1268
|
+
const parsedList = list.map(parseBinding);
|
|
1269
|
+
this.raw.set(action, list);
|
|
1270
|
+
this.parsed.set(action, parsedList);
|
|
1271
|
+
}
|
|
1272
|
+
disable(action) {
|
|
1273
|
+
this.rebind(action, null);
|
|
1274
|
+
}
|
|
1275
|
+
reset(action) {
|
|
1276
|
+
if (action === void 0) {
|
|
1277
|
+
this.raw.clear();
|
|
1278
|
+
this.parsed.clear();
|
|
1279
|
+
this.applyDefaults();
|
|
1280
|
+
return;
|
|
1281
|
+
}
|
|
1282
|
+
const def = DEFAULT_BINDINGS.find(([name]) => name === action);
|
|
1283
|
+
if (def) {
|
|
1284
|
+
this.rebind(action, [...def[1]]);
|
|
1285
|
+
} else if (this.raw.has(action)) {
|
|
1286
|
+
this.raw.delete(action);
|
|
1287
|
+
this.parsed.delete(action);
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
getBindings() {
|
|
1291
|
+
const out = {};
|
|
1292
|
+
for (const [action, list] of this.raw) {
|
|
1293
|
+
out[action] = [...list];
|
|
1294
|
+
}
|
|
1295
|
+
return out;
|
|
1296
|
+
}
|
|
1297
|
+
applyDefaults() {
|
|
1298
|
+
for (const [action, bindings] of DEFAULT_BINDINGS) {
|
|
1299
|
+
this.rebind(action, [...bindings]);
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
};
|
|
1303
|
+
|
|
1153
1304
|
// src/canvas/input-handler.ts
|
|
1154
1305
|
var ZOOM_SENSITIVITY = 1e-3;
|
|
1155
1306
|
var MIDDLE_BUTTON = 1;
|
|
1156
|
-
var
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1307
|
+
var NUDGE_DELTAS = {
|
|
1308
|
+
"nudge-left": [-1, 0],
|
|
1309
|
+
"nudge-right": [1, 0],
|
|
1310
|
+
"nudge-up": [0, -1],
|
|
1311
|
+
"nudge-down": [0, 1]
|
|
1161
1312
|
};
|
|
1162
1313
|
var InputHandler = class {
|
|
1163
1314
|
constructor(element, camera, options = {}) {
|
|
@@ -1175,7 +1326,13 @@ var InputHandler = class {
|
|
|
1175
1326
|
isToolActive: () => this.isToolActive,
|
|
1176
1327
|
fitToContent: options.fitToContent
|
|
1177
1328
|
});
|
|
1329
|
+
this.shortcutMap = new ShortcutMap(options.shortcuts?.bindings);
|
|
1330
|
+
this.scope = options.shortcuts?.scope ?? "focus";
|
|
1178
1331
|
this.element.style.touchAction = "none";
|
|
1332
|
+
if (this.scope === "focus") {
|
|
1333
|
+
this.element.tabIndex = 0;
|
|
1334
|
+
this.element.style.outline = "none";
|
|
1335
|
+
}
|
|
1179
1336
|
this.bind();
|
|
1180
1337
|
}
|
|
1181
1338
|
isPanning = false;
|
|
@@ -1194,6 +1351,8 @@ var InputHandler = class {
|
|
|
1194
1351
|
deferredDown = null;
|
|
1195
1352
|
abortController = new AbortController();
|
|
1196
1353
|
actions;
|
|
1354
|
+
shortcutMap;
|
|
1355
|
+
scope;
|
|
1197
1356
|
setToolManager(toolManager, toolContext) {
|
|
1198
1357
|
this.toolManager = toolManager;
|
|
1199
1358
|
this.toolContext = toolContext;
|
|
@@ -1201,6 +1360,9 @@ var InputHandler = class {
|
|
|
1201
1360
|
flushPendingHistory() {
|
|
1202
1361
|
this.actions.flushPendingNudge();
|
|
1203
1362
|
}
|
|
1363
|
+
get shortcuts() {
|
|
1364
|
+
return this.shortcutMap;
|
|
1365
|
+
}
|
|
1204
1366
|
destroy() {
|
|
1205
1367
|
this.actions.dispose();
|
|
1206
1368
|
this.abortController.abort();
|
|
@@ -1230,6 +1392,7 @@ var InputHandler = class {
|
|
|
1230
1392
|
});
|
|
1231
1393
|
};
|
|
1232
1394
|
onPointerDown = (e) => {
|
|
1395
|
+
this.focusSelf();
|
|
1233
1396
|
this.activePointers.set(e.pointerId, { x: e.clientX, y: e.clientY });
|
|
1234
1397
|
this.element.setPointerCapture?.(e.pointerId);
|
|
1235
1398
|
if (this.activePointers.size === 2) {
|
|
@@ -1312,57 +1475,13 @@ var InputHandler = class {
|
|
|
1312
1475
|
if (target?.isContentEditable) return;
|
|
1313
1476
|
const tag = target?.tagName;
|
|
1314
1477
|
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return;
|
|
1478
|
+
if (!this.isInScope()) return;
|
|
1315
1479
|
if (e.key === " ") {
|
|
1316
1480
|
this.spaceHeld = true;
|
|
1317
1481
|
}
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
if (e.key === "Escape") {
|
|
1322
|
-
this.actions.deselect();
|
|
1323
|
-
}
|
|
1324
|
-
if ((e.ctrlKey || e.metaKey) && e.key === "z" && !e.shiftKey) {
|
|
1325
|
-
e.preventDefault();
|
|
1326
|
-
this.actions.undo();
|
|
1327
|
-
}
|
|
1328
|
-
if ((e.ctrlKey || e.metaKey) && (e.key === "y" || e.key === "z" && e.shiftKey)) {
|
|
1329
|
-
e.preventDefault();
|
|
1330
|
-
this.actions.redo();
|
|
1331
|
-
}
|
|
1332
|
-
if ((e.ctrlKey || e.metaKey) && e.key === "a") {
|
|
1333
|
-
e.preventDefault();
|
|
1334
|
-
this.actions.selectAll();
|
|
1335
|
-
}
|
|
1336
|
-
if ((e.ctrlKey || e.metaKey) && e.key === "c") {
|
|
1337
|
-
e.preventDefault();
|
|
1338
|
-
this.actions.copy();
|
|
1339
|
-
}
|
|
1340
|
-
if ((e.ctrlKey || e.metaKey) && e.key === "v") {
|
|
1341
|
-
e.preventDefault();
|
|
1342
|
-
this.actions.paste();
|
|
1343
|
-
}
|
|
1344
|
-
if ((e.ctrlKey || e.metaKey) && e.key === "d") {
|
|
1345
|
-
e.preventDefault();
|
|
1346
|
-
this.actions.duplicate();
|
|
1347
|
-
}
|
|
1348
|
-
if (e.key === "]") {
|
|
1349
|
-
e.preventDefault();
|
|
1350
|
-
this.actions.zOrder(e.ctrlKey || e.metaKey ? "front" : "forward");
|
|
1351
|
-
}
|
|
1352
|
-
if (e.key === "[") {
|
|
1353
|
-
e.preventDefault();
|
|
1354
|
-
this.actions.zOrder(e.ctrlKey || e.metaKey ? "back" : "backward");
|
|
1355
|
-
}
|
|
1356
|
-
if (e.shiftKey && e.code === "Digit1" && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
|
1357
|
-
e.preventDefault();
|
|
1358
|
-
this.actions.zoomToFit();
|
|
1359
|
-
}
|
|
1360
|
-
const nudgeDelta = NUDGE_KEYS[e.key];
|
|
1361
|
-
if (nudgeDelta) {
|
|
1362
|
-
const [dx, dy] = nudgeDelta;
|
|
1363
|
-
if (this.actions.nudge(dx, dy, e.shiftKey)) {
|
|
1364
|
-
e.preventDefault();
|
|
1365
|
-
}
|
|
1482
|
+
const action = this.shortcutMap.match(e);
|
|
1483
|
+
if (action !== null) {
|
|
1484
|
+
this.runAction(action, e);
|
|
1366
1485
|
}
|
|
1367
1486
|
};
|
|
1368
1487
|
onKeyUp = (e) => {
|
|
@@ -1377,6 +1496,79 @@ var InputHandler = class {
|
|
|
1377
1496
|
}
|
|
1378
1497
|
}
|
|
1379
1498
|
};
|
|
1499
|
+
runAction(action, e) {
|
|
1500
|
+
switch (action) {
|
|
1501
|
+
case "delete":
|
|
1502
|
+
e.preventDefault();
|
|
1503
|
+
this.actions.deleteSelected();
|
|
1504
|
+
return;
|
|
1505
|
+
case "deselect":
|
|
1506
|
+
this.actions.deselect();
|
|
1507
|
+
return;
|
|
1508
|
+
case "undo":
|
|
1509
|
+
e.preventDefault();
|
|
1510
|
+
this.actions.undo();
|
|
1511
|
+
return;
|
|
1512
|
+
case "redo":
|
|
1513
|
+
e.preventDefault();
|
|
1514
|
+
this.actions.redo();
|
|
1515
|
+
return;
|
|
1516
|
+
case "select-all":
|
|
1517
|
+
e.preventDefault();
|
|
1518
|
+
this.actions.selectAll();
|
|
1519
|
+
return;
|
|
1520
|
+
case "copy":
|
|
1521
|
+
e.preventDefault();
|
|
1522
|
+
this.actions.copy();
|
|
1523
|
+
return;
|
|
1524
|
+
case "paste":
|
|
1525
|
+
e.preventDefault();
|
|
1526
|
+
this.actions.paste();
|
|
1527
|
+
return;
|
|
1528
|
+
case "duplicate":
|
|
1529
|
+
e.preventDefault();
|
|
1530
|
+
this.actions.duplicate();
|
|
1531
|
+
return;
|
|
1532
|
+
case "z-forward":
|
|
1533
|
+
e.preventDefault();
|
|
1534
|
+
this.actions.zOrder("forward");
|
|
1535
|
+
return;
|
|
1536
|
+
case "z-backward":
|
|
1537
|
+
e.preventDefault();
|
|
1538
|
+
this.actions.zOrder("backward");
|
|
1539
|
+
return;
|
|
1540
|
+
case "z-front":
|
|
1541
|
+
e.preventDefault();
|
|
1542
|
+
this.actions.zOrder("front");
|
|
1543
|
+
return;
|
|
1544
|
+
case "z-back":
|
|
1545
|
+
e.preventDefault();
|
|
1546
|
+
this.actions.zOrder("back");
|
|
1547
|
+
return;
|
|
1548
|
+
case "zoom-fit":
|
|
1549
|
+
e.preventDefault();
|
|
1550
|
+
this.actions.zoomToFit();
|
|
1551
|
+
return;
|
|
1552
|
+
case "nudge-left":
|
|
1553
|
+
case "nudge-right":
|
|
1554
|
+
case "nudge-up":
|
|
1555
|
+
case "nudge-down": {
|
|
1556
|
+
const delta = NUDGE_DELTAS[action];
|
|
1557
|
+
if (delta && this.actions.nudge(delta[0], delta[1], e.shiftKey)) {
|
|
1558
|
+
e.preventDefault();
|
|
1559
|
+
}
|
|
1560
|
+
return;
|
|
1561
|
+
}
|
|
1562
|
+
default:
|
|
1563
|
+
if (action.startsWith("tool:")) {
|
|
1564
|
+
if (this.isToolActive) return;
|
|
1565
|
+
e.preventDefault();
|
|
1566
|
+
this.toolContext?.switchTool?.(action.slice("tool:".length));
|
|
1567
|
+
return;
|
|
1568
|
+
}
|
|
1569
|
+
console.warn(`[fieldnotes] unknown shortcut action "${action}"`);
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1380
1572
|
startPinch() {
|
|
1381
1573
|
this.inputFilter.reset();
|
|
1382
1574
|
this.deferredDown = null;
|
|
@@ -1447,6 +1639,15 @@ var InputHandler = class {
|
|
|
1447
1639
|
this.toolManager.handlePointerUp(this.toPointerState(e), this.toolContext);
|
|
1448
1640
|
this.historyRecorder?.commit();
|
|
1449
1641
|
}
|
|
1642
|
+
isInScope() {
|
|
1643
|
+
if (this.scope === "window") return true;
|
|
1644
|
+
const active = document.activeElement;
|
|
1645
|
+
return active === this.element || this.element.contains(active);
|
|
1646
|
+
}
|
|
1647
|
+
focusSelf() {
|
|
1648
|
+
if (this.scope !== "focus" || this.isInScope()) return;
|
|
1649
|
+
this.element.focus({ preventScroll: true });
|
|
1650
|
+
}
|
|
1450
1651
|
cancelToolIfActive(e) {
|
|
1451
1652
|
if (this.isToolActive) {
|
|
1452
1653
|
this.dispatchToolUp(e);
|
|
@@ -3279,17 +3480,36 @@ var FORMAT_SHORTCUTS = {
|
|
|
3279
3480
|
i: toggleItalic,
|
|
3280
3481
|
u: toggleUnderline
|
|
3281
3482
|
};
|
|
3483
|
+
function ensureEditorStyles() {
|
|
3484
|
+
if (document.querySelector("style[data-fieldnotes-editor]")) return;
|
|
3485
|
+
const style = document.createElement("style");
|
|
3486
|
+
style.setAttribute("data-fieldnotes-editor", "");
|
|
3487
|
+
style.textContent = `[data-fn-placeholder][data-fn-empty='true']::before {
|
|
3488
|
+
content: attr(data-fn-placeholder);
|
|
3489
|
+
color: #9e9e9e;
|
|
3490
|
+
position: absolute;
|
|
3491
|
+
pointer-events: none;
|
|
3492
|
+
}`;
|
|
3493
|
+
document.head.appendChild(style);
|
|
3494
|
+
}
|
|
3495
|
+
function isNodeEmpty(node) {
|
|
3496
|
+
const text = node.textContent ?? "";
|
|
3497
|
+
return text.replace(/\u00a0/g, " ").trim().length === 0;
|
|
3498
|
+
}
|
|
3282
3499
|
var NoteEditor = class {
|
|
3283
3500
|
editingId = null;
|
|
3284
3501
|
editingNode = null;
|
|
3285
3502
|
blurHandler = null;
|
|
3286
3503
|
keyHandler = null;
|
|
3287
3504
|
pointerHandler = null;
|
|
3505
|
+
inputHandler = null;
|
|
3288
3506
|
pendingEditId = null;
|
|
3289
3507
|
onStopCallback = null;
|
|
3290
3508
|
toolbar;
|
|
3509
|
+
placeholder;
|
|
3291
3510
|
constructor(options) {
|
|
3292
3511
|
this.toolbar = options?.toolbar === false ? null : new NoteToolbar(options?.fontSizePresets);
|
|
3512
|
+
this.placeholder = options?.placeholder ?? "Type\u2026";
|
|
3293
3513
|
}
|
|
3294
3514
|
get isEditing() {
|
|
3295
3515
|
return this.editingId !== null;
|
|
@@ -3324,6 +3544,11 @@ var NoteEditor = class {
|
|
|
3324
3544
|
if (this.pointerHandler) {
|
|
3325
3545
|
this.editingNode.removeEventListener("pointerdown", this.pointerHandler);
|
|
3326
3546
|
}
|
|
3547
|
+
if (this.inputHandler) {
|
|
3548
|
+
this.editingNode.removeEventListener("input", this.inputHandler);
|
|
3549
|
+
}
|
|
3550
|
+
this.editingNode.removeAttribute("data-fn-placeholder");
|
|
3551
|
+
this.editingNode.removeAttribute("data-fn-empty");
|
|
3327
3552
|
const text = sanitizeNoteHtml(this.editingNode.innerHTML);
|
|
3328
3553
|
store.update(this.editingId, { text });
|
|
3329
3554
|
this.editingNode.contentEditable = "false";
|
|
@@ -3340,6 +3565,7 @@ var NoteEditor = class {
|
|
|
3340
3565
|
this.blurHandler = null;
|
|
3341
3566
|
this.keyHandler = null;
|
|
3342
3567
|
this.pointerHandler = null;
|
|
3568
|
+
this.inputHandler = null;
|
|
3343
3569
|
}
|
|
3344
3570
|
destroy(store) {
|
|
3345
3571
|
this.pendingEditId = null;
|
|
@@ -3370,6 +3596,13 @@ var NoteEditor = class {
|
|
|
3370
3596
|
selection.removeAllRanges();
|
|
3371
3597
|
selection.addRange(range);
|
|
3372
3598
|
}
|
|
3599
|
+
ensureEditorStyles();
|
|
3600
|
+
node.setAttribute("data-fn-placeholder", this.placeholder);
|
|
3601
|
+
node.setAttribute("data-fn-empty", String(isNodeEmpty(node)));
|
|
3602
|
+
this.inputHandler = () => {
|
|
3603
|
+
node.setAttribute("data-fn-empty", String(isNodeEmpty(node)));
|
|
3604
|
+
};
|
|
3605
|
+
node.addEventListener("input", this.inputHandler);
|
|
3373
3606
|
this.toolbar?.show(node);
|
|
3374
3607
|
this.blurHandler = (e) => {
|
|
3375
3608
|
const related = e.relatedTarget;
|
|
@@ -4808,7 +5041,8 @@ var Viewport = class {
|
|
|
4808
5041
|
});
|
|
4809
5042
|
this.noteEditor = new NoteEditor({
|
|
4810
5043
|
fontSizePresets: options.fontSizePresets,
|
|
4811
|
-
toolbar: options.toolbar
|
|
5044
|
+
toolbar: options.toolbar,
|
|
5045
|
+
placeholder: options.placeholder
|
|
4812
5046
|
});
|
|
4813
5047
|
this.noteEditor.setOnStop((id) => this.onTextEditStop(id));
|
|
4814
5048
|
this.onHtmlElementMount = options.onHtmlElementMount;
|
|
@@ -4841,7 +5075,8 @@ var Viewport = class {
|
|
|
4841
5075
|
toolContext: this.toolContext,
|
|
4842
5076
|
historyRecorder: this.historyRecorder,
|
|
4843
5077
|
historyStack: this.history,
|
|
4844
|
-
fitToContent: () => this.fitToContent()
|
|
5078
|
+
fitToContent: () => this.fitToContent(),
|
|
5079
|
+
shortcuts: options.shortcuts
|
|
4845
5080
|
});
|
|
4846
5081
|
this.domNodeManager = new DomNodeManager({
|
|
4847
5082
|
domLayer: this.domLayer,
|
|
@@ -5015,6 +5250,9 @@ var Viewport = class {
|
|
|
5015
5250
|
setTool(name) {
|
|
5016
5251
|
this.toolManager.setTool(name, this.toolContext);
|
|
5017
5252
|
}
|
|
5253
|
+
get shortcuts() {
|
|
5254
|
+
return this.inputHandler.shortcuts;
|
|
5255
|
+
}
|
|
5018
5256
|
undo() {
|
|
5019
5257
|
this.inputHandler.flushPendingHistory();
|
|
5020
5258
|
this.historyRecorder.pause();
|
|
@@ -5150,7 +5388,16 @@ var Viewport = class {
|
|
|
5150
5388
|
}
|
|
5151
5389
|
onTextEditStop(elementId) {
|
|
5152
5390
|
const element = this.store.getById(elementId);
|
|
5153
|
-
if (!element
|
|
5391
|
+
if (!element) return;
|
|
5392
|
+
if (element.type === "note") {
|
|
5393
|
+
if (isNoteContentEmpty(element.text)) {
|
|
5394
|
+
this.historyRecorder.begin();
|
|
5395
|
+
this.store.remove(elementId);
|
|
5396
|
+
this.historyRecorder.commit();
|
|
5397
|
+
}
|
|
5398
|
+
return;
|
|
5399
|
+
}
|
|
5400
|
+
if (element.type !== "text") return;
|
|
5154
5401
|
if (!element.text || element.text.trim() === "") {
|
|
5155
5402
|
this.historyRecorder.begin();
|
|
5156
5403
|
this.store.remove(elementId);
|
|
@@ -5699,6 +5946,7 @@ var SelectTool = class {
|
|
|
5699
5946
|
pendingSingleSelectId = null;
|
|
5700
5947
|
hasDragged = false;
|
|
5701
5948
|
resizeAspectRatio = 0;
|
|
5949
|
+
hoveredId = null;
|
|
5702
5950
|
get selectedIds() {
|
|
5703
5951
|
return [...this._selectedIds];
|
|
5704
5952
|
}
|
|
@@ -5715,6 +5963,7 @@ var SelectTool = class {
|
|
|
5715
5963
|
onDeactivate(ctx) {
|
|
5716
5964
|
this._selectedIds = [];
|
|
5717
5965
|
this.mode = { type: "idle" };
|
|
5966
|
+
this.hoveredId = null;
|
|
5718
5967
|
ctx.setCursor?.("default");
|
|
5719
5968
|
}
|
|
5720
5969
|
snap(point, ctx) {
|
|
@@ -5722,6 +5971,7 @@ var SelectTool = class {
|
|
|
5722
5971
|
}
|
|
5723
5972
|
onPointerDown(state, ctx) {
|
|
5724
5973
|
this.ctx = ctx;
|
|
5974
|
+
this.setHovered(null, ctx);
|
|
5725
5975
|
const world = ctx.camera.screenToWorld({ x: state.x, y: state.y });
|
|
5726
5976
|
this.lastWorld = this.snap(world, ctx);
|
|
5727
5977
|
this.currentWorld = world;
|
|
@@ -5864,7 +6114,8 @@ var SelectTool = class {
|
|
|
5864
6114
|
}
|
|
5865
6115
|
onHover(state, ctx) {
|
|
5866
6116
|
const world = ctx.camera.screenToWorld({ x: state.x, y: state.y });
|
|
5867
|
-
this.updateHoverCursor(world, ctx);
|
|
6117
|
+
const hoverId = this.updateHoverCursor(world, ctx);
|
|
6118
|
+
this.setHovered(hoverId, ctx);
|
|
5868
6119
|
}
|
|
5869
6120
|
renderOverlay(canvasCtx) {
|
|
5870
6121
|
this.renderMarquee(canvasCtx);
|
|
@@ -5885,6 +6136,23 @@ var SelectTool = class {
|
|
|
5885
6136
|
canvasCtx.restore();
|
|
5886
6137
|
}
|
|
5887
6138
|
}
|
|
6139
|
+
if (this.hoveredId && this.ctx && this.mode.type === "idle") {
|
|
6140
|
+
if (!this._selectedIds.includes(this.hoveredId)) {
|
|
6141
|
+
const el = this.ctx.store.getById(this.hoveredId);
|
|
6142
|
+
if (el) {
|
|
6143
|
+
const b = getElementBounds(el);
|
|
6144
|
+
if (b) {
|
|
6145
|
+
canvasCtx.save();
|
|
6146
|
+
canvasCtx.strokeStyle = "#2196F3";
|
|
6147
|
+
canvasCtx.globalAlpha = 0.35;
|
|
6148
|
+
canvasCtx.lineWidth = 1.5 / this.ctx.camera.zoom;
|
|
6149
|
+
canvasCtx.setLineDash([]);
|
|
6150
|
+
canvasCtx.strokeRect(b.x, b.y, b.w, b.h);
|
|
6151
|
+
canvasCtx.restore();
|
|
6152
|
+
}
|
|
6153
|
+
}
|
|
6154
|
+
}
|
|
6155
|
+
}
|
|
5888
6156
|
}
|
|
5889
6157
|
updateArrowsBoundTo(ids, ctx) {
|
|
5890
6158
|
const movedNonArrowIds = /* @__PURE__ */ new Set();
|
|
@@ -5933,20 +6201,26 @@ var SelectTool = class {
|
|
|
5933
6201
|
const arrowHit = hitTestArrowHandles(world, this._selectedIds, ctx);
|
|
5934
6202
|
if (arrowHit) {
|
|
5935
6203
|
ctx.setCursor?.(getArrowHandleCursor(arrowHit.handle, false));
|
|
5936
|
-
return;
|
|
6204
|
+
return null;
|
|
5937
6205
|
}
|
|
5938
6206
|
const templateResizeHit = this.hitTestTemplateResizeHandle(world, ctx);
|
|
5939
6207
|
if (templateResizeHit) {
|
|
5940
6208
|
ctx.setCursor?.("nwse-resize");
|
|
5941
|
-
return;
|
|
6209
|
+
return null;
|
|
5942
6210
|
}
|
|
5943
6211
|
const resizeHit = this.hitTestResizeHandle(world, ctx);
|
|
5944
6212
|
if (resizeHit) {
|
|
5945
6213
|
ctx.setCursor?.(HANDLE_CURSORS[resizeHit.handle]);
|
|
5946
|
-
return;
|
|
6214
|
+
return null;
|
|
5947
6215
|
}
|
|
5948
6216
|
const hit = this.hitTest(world, ctx);
|
|
5949
6217
|
ctx.setCursor?.(hit ? "move" : "default");
|
|
6218
|
+
return hit ? hit.id : null;
|
|
6219
|
+
}
|
|
6220
|
+
setHovered(id, ctx) {
|
|
6221
|
+
if (this.hoveredId === id) return;
|
|
6222
|
+
this.hoveredId = id;
|
|
6223
|
+
ctx.requestRender();
|
|
5950
6224
|
}
|
|
5951
6225
|
handleResize(world, ctx, shiftKey = false) {
|
|
5952
6226
|
if (this.mode.type !== "resizing") return;
|
|
@@ -7053,7 +7327,7 @@ var TemplateTool = class {
|
|
|
7053
7327
|
};
|
|
7054
7328
|
|
|
7055
7329
|
// src/index.ts
|
|
7056
|
-
var VERSION = "0.
|
|
7330
|
+
var VERSION = "0.20.0";
|
|
7057
7331
|
// Annotate the CommonJS export names for ESM import in node:
|
|
7058
7332
|
0 && (module.exports = {
|
|
7059
7333
|
AddElementCommand,
|
|
@@ -7128,6 +7402,7 @@ var VERSION = "0.18.0";
|
|
|
7128
7402
|
getHexDistance,
|
|
7129
7403
|
isBindable,
|
|
7130
7404
|
isNearBezier,
|
|
7405
|
+
isNoteContentEmpty,
|
|
7131
7406
|
parseState,
|
|
7132
7407
|
sanitizeNoteHtml,
|
|
7133
7408
|
setFontSize,
|