@fieldnotes/core 0.9.0 → 0.11.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 +651 -204
- package/dist/index.d.cts +73 -2
- package/dist/index.d.ts +73 -2
- package/dist/index.js +641 -204
- package/package.json +7 -7
package/dist/index.cjs
CHANGED
|
@@ -27,6 +27,8 @@ __export(index_exports, {
|
|
|
27
27
|
BatchCommand: () => BatchCommand,
|
|
28
28
|
Camera: () => Camera,
|
|
29
29
|
CreateLayerCommand: () => CreateLayerCommand,
|
|
30
|
+
DEFAULT_FONT_SIZE_PRESETS: () => DEFAULT_FONT_SIZE_PRESETS,
|
|
31
|
+
DEFAULT_NOTE_FONT_SIZE: () => DEFAULT_NOTE_FONT_SIZE,
|
|
30
32
|
ElementRenderer: () => ElementRenderer,
|
|
31
33
|
ElementStore: () => ElementStore,
|
|
32
34
|
EraserTool: () => EraserTool,
|
|
@@ -40,6 +42,7 @@ __export(index_exports, {
|
|
|
40
42
|
MeasureTool: () => MeasureTool,
|
|
41
43
|
NoteEditor: () => NoteEditor,
|
|
42
44
|
NoteTool: () => NoteTool,
|
|
45
|
+
NoteToolbar: () => NoteToolbar,
|
|
43
46
|
PencilTool: () => PencilTool,
|
|
44
47
|
Quadtree: () => Quadtree,
|
|
45
48
|
RemoveElementCommand: () => RemoveElementCommand,
|
|
@@ -70,6 +73,7 @@ __export(index_exports, {
|
|
|
70
73
|
exportState: () => exportState,
|
|
71
74
|
findBindTarget: () => findBindTarget,
|
|
72
75
|
findBoundArrows: () => findBoundArrows,
|
|
76
|
+
getActiveFormats: () => getActiveFormats,
|
|
73
77
|
getArrowBounds: () => getArrowBounds,
|
|
74
78
|
getArrowControlPoint: () => getArrowControlPoint,
|
|
75
79
|
getArrowMidpoint: () => getArrowMidpoint,
|
|
@@ -86,9 +90,15 @@ __export(index_exports, {
|
|
|
86
90
|
isBindable: () => isBindable,
|
|
87
91
|
isNearBezier: () => isNearBezier,
|
|
88
92
|
parseState: () => parseState,
|
|
93
|
+
sanitizeNoteHtml: () => sanitizeNoteHtml,
|
|
94
|
+
setFontSize: () => setFontSize,
|
|
89
95
|
smartSnap: () => smartSnap,
|
|
90
96
|
snapPoint: () => snapPoint,
|
|
91
97
|
snapToHexCenter: () => snapToHexCenter,
|
|
98
|
+
toggleBold: () => toggleBold,
|
|
99
|
+
toggleItalic: () => toggleItalic,
|
|
100
|
+
toggleStrikethrough: () => toggleStrikethrough,
|
|
101
|
+
toggleUnderline: () => toggleUnderline,
|
|
92
102
|
unbindArrow: () => unbindArrow,
|
|
93
103
|
updateBoundArrow: () => updateBoundArrow
|
|
94
104
|
});
|
|
@@ -265,6 +275,120 @@ var Quadtree = class {
|
|
|
265
275
|
}
|
|
266
276
|
};
|
|
267
277
|
|
|
278
|
+
// src/elements/note-sanitizer.ts
|
|
279
|
+
var BOLD_TAGS = /* @__PURE__ */ new Set(["b", "strong"]);
|
|
280
|
+
var ITALIC_TAGS = /* @__PURE__ */ new Set(["i", "em"]);
|
|
281
|
+
var UNDERLINE_TAGS = /* @__PURE__ */ new Set(["u"]);
|
|
282
|
+
var STRIKE_TAGS = /* @__PURE__ */ new Set(["s", "strike", "del"]);
|
|
283
|
+
var BLOCK_TAGS = /* @__PURE__ */ new Set(["div"]);
|
|
284
|
+
function parseStyledRuns(html, baseFontSize) {
|
|
285
|
+
if (!html) return [];
|
|
286
|
+
const doc = new DOMParser().parseFromString(html, "text/html");
|
|
287
|
+
const runs = [];
|
|
288
|
+
const baseStyle = {
|
|
289
|
+
bold: false,
|
|
290
|
+
italic: false,
|
|
291
|
+
underline: false,
|
|
292
|
+
strikethrough: false,
|
|
293
|
+
fontSize: baseFontSize
|
|
294
|
+
};
|
|
295
|
+
walkNodes(doc.body, baseStyle, runs);
|
|
296
|
+
return runs;
|
|
297
|
+
}
|
|
298
|
+
function walkNodes(node, style, runs) {
|
|
299
|
+
for (const child of Array.from(node.childNodes)) {
|
|
300
|
+
if (child.nodeType === Node.TEXT_NODE) {
|
|
301
|
+
const text = child.textContent ?? "";
|
|
302
|
+
if (text) {
|
|
303
|
+
runs.push({ text, ...style });
|
|
304
|
+
}
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
if (child.nodeType !== Node.ELEMENT_NODE) continue;
|
|
308
|
+
const el = child;
|
|
309
|
+
const tag = el.tagName.toLowerCase();
|
|
310
|
+
if (tag === "br") {
|
|
311
|
+
runs.push({ text: "\n", ...style });
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
if (BLOCK_TAGS.has(tag) && runs.length > 0) {
|
|
315
|
+
const lastRun = runs[runs.length - 1];
|
|
316
|
+
if (lastRun && !lastRun.text.endsWith("\n")) {
|
|
317
|
+
runs.push({ text: "\n", ...style });
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
const childStyle = { ...style };
|
|
321
|
+
if (BOLD_TAGS.has(tag)) childStyle.bold = true;
|
|
322
|
+
if (ITALIC_TAGS.has(tag)) childStyle.italic = true;
|
|
323
|
+
if (UNDERLINE_TAGS.has(tag)) childStyle.underline = true;
|
|
324
|
+
if (STRIKE_TAGS.has(tag)) childStyle.strikethrough = true;
|
|
325
|
+
if (tag === "span") {
|
|
326
|
+
const fontSize = el.style.fontSize;
|
|
327
|
+
if (fontSize) {
|
|
328
|
+
childStyle.fontSize = parseInt(fontSize, 10) || style.fontSize;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
walkNodes(el, childStyle, runs);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
var ALLOWED_TAGS = /* @__PURE__ */ new Set([
|
|
335
|
+
"b",
|
|
336
|
+
"strong",
|
|
337
|
+
"i",
|
|
338
|
+
"em",
|
|
339
|
+
"u",
|
|
340
|
+
"s",
|
|
341
|
+
"strike",
|
|
342
|
+
"del",
|
|
343
|
+
"span",
|
|
344
|
+
"br",
|
|
345
|
+
"div"
|
|
346
|
+
]);
|
|
347
|
+
function sanitizeNoteHtml(html) {
|
|
348
|
+
if (!html) return "";
|
|
349
|
+
const doc = new DOMParser().parseFromString(html, "text/html");
|
|
350
|
+
sanitizeNode(doc.body);
|
|
351
|
+
return doc.body.innerHTML;
|
|
352
|
+
}
|
|
353
|
+
function sanitizeNode(node) {
|
|
354
|
+
const children = Array.from(node.childNodes);
|
|
355
|
+
for (const child of children) {
|
|
356
|
+
if (child.nodeType === Node.TEXT_NODE) continue;
|
|
357
|
+
if (child.nodeType !== Node.ELEMENT_NODE) {
|
|
358
|
+
child.remove();
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
const el = child;
|
|
362
|
+
const tag = el.tagName.toLowerCase();
|
|
363
|
+
if (!ALLOWED_TAGS.has(tag)) {
|
|
364
|
+
const fragment = document.createDocumentFragment();
|
|
365
|
+
while (el.firstChild) {
|
|
366
|
+
fragment.appendChild(el.firstChild);
|
|
367
|
+
}
|
|
368
|
+
node.replaceChild(fragment, el);
|
|
369
|
+
sanitizeNode(node);
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
sanitizeAttributes(el, tag);
|
|
373
|
+
sanitizeNode(el);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
function sanitizeAttributes(el, tag) {
|
|
377
|
+
const attrs = Array.from(el.attributes);
|
|
378
|
+
for (const attr of attrs) {
|
|
379
|
+
if (tag === "span" && attr.name === "style") {
|
|
380
|
+
const fontSize = el.style.fontSize;
|
|
381
|
+
if (fontSize) {
|
|
382
|
+
el.setAttribute("style", `font-size: ${fontSize};`);
|
|
383
|
+
} else {
|
|
384
|
+
el.removeAttribute("style");
|
|
385
|
+
}
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
el.removeAttribute(attr.name);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
268
392
|
// src/core/state-serializer.ts
|
|
269
393
|
var CURRENT_VERSION = 2;
|
|
270
394
|
function exportState(elements, camera, layers = []) {
|
|
@@ -327,7 +451,17 @@ function validateState(data) {
|
|
|
327
451
|
];
|
|
328
452
|
}
|
|
329
453
|
}
|
|
330
|
-
var VALID_TYPES = /* @__PURE__ */ new Set([
|
|
454
|
+
var VALID_TYPES = /* @__PURE__ */ new Set([
|
|
455
|
+
"stroke",
|
|
456
|
+
"note",
|
|
457
|
+
"arrow",
|
|
458
|
+
"image",
|
|
459
|
+
"html",
|
|
460
|
+
"text",
|
|
461
|
+
"shape",
|
|
462
|
+
"grid",
|
|
463
|
+
"template"
|
|
464
|
+
]);
|
|
331
465
|
function validateElement(el) {
|
|
332
466
|
if (!el || typeof el !== "object") {
|
|
333
467
|
throw new Error("Invalid element: expected an object");
|
|
@@ -377,6 +511,9 @@ function migrateElement(obj) {
|
|
|
377
511
|
if (obj["type"] === "note" && typeof obj["textColor"] !== "string") {
|
|
378
512
|
obj["textColor"] = "#000000";
|
|
379
513
|
}
|
|
514
|
+
if (obj["type"] === "note" && typeof obj["text"] === "string") {
|
|
515
|
+
obj["text"] = sanitizeNoteHtml(obj["text"]);
|
|
516
|
+
}
|
|
380
517
|
}
|
|
381
518
|
|
|
382
519
|
// src/core/snap.ts
|
|
@@ -2286,7 +2423,359 @@ var ElementRenderer = class {
|
|
|
2286
2423
|
}
|
|
2287
2424
|
};
|
|
2288
2425
|
|
|
2426
|
+
// src/elements/create-id.ts
|
|
2427
|
+
var counter = 0;
|
|
2428
|
+
function createId(prefix) {
|
|
2429
|
+
return `${prefix}_${Date.now().toString(36)}_${(counter++).toString(36)}`;
|
|
2430
|
+
}
|
|
2431
|
+
|
|
2432
|
+
// src/elements/element-factory.ts
|
|
2433
|
+
var DEFAULT_NOTE_FONT_SIZE = 18;
|
|
2434
|
+
function createStroke(input) {
|
|
2435
|
+
return {
|
|
2436
|
+
id: createId("stroke"),
|
|
2437
|
+
type: "stroke",
|
|
2438
|
+
position: input.position ?? { x: 0, y: 0 },
|
|
2439
|
+
zIndex: input.zIndex ?? 0,
|
|
2440
|
+
locked: input.locked ?? false,
|
|
2441
|
+
layerId: input.layerId ?? "",
|
|
2442
|
+
points: input.points,
|
|
2443
|
+
color: input.color ?? "#000000",
|
|
2444
|
+
width: input.width ?? 2,
|
|
2445
|
+
opacity: input.opacity ?? 1
|
|
2446
|
+
};
|
|
2447
|
+
}
|
|
2448
|
+
function createNote(input) {
|
|
2449
|
+
return {
|
|
2450
|
+
id: createId("note"),
|
|
2451
|
+
type: "note",
|
|
2452
|
+
position: input.position,
|
|
2453
|
+
zIndex: input.zIndex ?? 0,
|
|
2454
|
+
locked: input.locked ?? false,
|
|
2455
|
+
layerId: input.layerId ?? "",
|
|
2456
|
+
size: input.size ?? { w: 200, h: 100 },
|
|
2457
|
+
text: input.text ?? "",
|
|
2458
|
+
backgroundColor: input.backgroundColor ?? "#ffeb3b",
|
|
2459
|
+
textColor: input.textColor ?? "#000000",
|
|
2460
|
+
fontSize: input.fontSize ?? DEFAULT_NOTE_FONT_SIZE
|
|
2461
|
+
};
|
|
2462
|
+
}
|
|
2463
|
+
function createArrow(input) {
|
|
2464
|
+
const bend = input.bend ?? 0;
|
|
2465
|
+
const result = {
|
|
2466
|
+
id: createId("arrow"),
|
|
2467
|
+
type: "arrow",
|
|
2468
|
+
position: input.position ?? { x: 0, y: 0 },
|
|
2469
|
+
zIndex: input.zIndex ?? 0,
|
|
2470
|
+
locked: input.locked ?? false,
|
|
2471
|
+
layerId: input.layerId ?? "",
|
|
2472
|
+
from: input.from,
|
|
2473
|
+
to: input.to,
|
|
2474
|
+
bend,
|
|
2475
|
+
color: input.color ?? "#000000",
|
|
2476
|
+
width: input.width ?? 2,
|
|
2477
|
+
cachedControlPoint: getArrowControlPoint(input.from, input.to, bend)
|
|
2478
|
+
};
|
|
2479
|
+
if (input.fromBinding) result.fromBinding = input.fromBinding;
|
|
2480
|
+
if (input.toBinding) result.toBinding = input.toBinding;
|
|
2481
|
+
return result;
|
|
2482
|
+
}
|
|
2483
|
+
function createImage(input) {
|
|
2484
|
+
return {
|
|
2485
|
+
id: createId("image"),
|
|
2486
|
+
type: "image",
|
|
2487
|
+
position: input.position,
|
|
2488
|
+
zIndex: input.zIndex ?? 0,
|
|
2489
|
+
locked: input.locked ?? false,
|
|
2490
|
+
layerId: input.layerId ?? "",
|
|
2491
|
+
size: input.size,
|
|
2492
|
+
src: input.src
|
|
2493
|
+
};
|
|
2494
|
+
}
|
|
2495
|
+
function createHtmlElement(input) {
|
|
2496
|
+
const el = {
|
|
2497
|
+
id: createId("html"),
|
|
2498
|
+
type: "html",
|
|
2499
|
+
position: input.position,
|
|
2500
|
+
zIndex: input.zIndex ?? 0,
|
|
2501
|
+
locked: input.locked ?? false,
|
|
2502
|
+
layerId: input.layerId ?? "",
|
|
2503
|
+
size: input.size
|
|
2504
|
+
};
|
|
2505
|
+
if (input.domId) el.domId = input.domId;
|
|
2506
|
+
return el;
|
|
2507
|
+
}
|
|
2508
|
+
function createShape(input) {
|
|
2509
|
+
return {
|
|
2510
|
+
id: createId("shape"),
|
|
2511
|
+
type: "shape",
|
|
2512
|
+
position: input.position,
|
|
2513
|
+
zIndex: input.zIndex ?? 0,
|
|
2514
|
+
locked: input.locked ?? false,
|
|
2515
|
+
layerId: input.layerId ?? "",
|
|
2516
|
+
shape: input.shape ?? "rectangle",
|
|
2517
|
+
size: input.size,
|
|
2518
|
+
strokeColor: input.strokeColor ?? "#000000",
|
|
2519
|
+
strokeWidth: input.strokeWidth ?? 2,
|
|
2520
|
+
fillColor: input.fillColor ?? "none"
|
|
2521
|
+
};
|
|
2522
|
+
}
|
|
2523
|
+
function createGrid(input) {
|
|
2524
|
+
return {
|
|
2525
|
+
id: createId("grid"),
|
|
2526
|
+
type: "grid",
|
|
2527
|
+
position: input.position ?? { x: 0, y: 0 },
|
|
2528
|
+
zIndex: input.zIndex ?? 0,
|
|
2529
|
+
locked: input.locked ?? false,
|
|
2530
|
+
layerId: input.layerId ?? "",
|
|
2531
|
+
gridType: input.gridType ?? "square",
|
|
2532
|
+
hexOrientation: input.hexOrientation ?? "pointy",
|
|
2533
|
+
cellSize: input.cellSize ?? 40,
|
|
2534
|
+
strokeColor: input.strokeColor ?? "#000000",
|
|
2535
|
+
strokeWidth: input.strokeWidth ?? 1,
|
|
2536
|
+
opacity: input.opacity ?? 1
|
|
2537
|
+
};
|
|
2538
|
+
}
|
|
2539
|
+
function createText(input) {
|
|
2540
|
+
return {
|
|
2541
|
+
id: createId("text"),
|
|
2542
|
+
type: "text",
|
|
2543
|
+
position: input.position,
|
|
2544
|
+
zIndex: input.zIndex ?? 0,
|
|
2545
|
+
locked: input.locked ?? false,
|
|
2546
|
+
layerId: input.layerId ?? "",
|
|
2547
|
+
size: input.size ?? { w: 200, h: 28 },
|
|
2548
|
+
text: input.text ?? "",
|
|
2549
|
+
fontSize: input.fontSize ?? 16,
|
|
2550
|
+
color: input.color ?? "#1a1a1a",
|
|
2551
|
+
textAlign: input.textAlign ?? "left"
|
|
2552
|
+
};
|
|
2553
|
+
}
|
|
2554
|
+
function createTemplate(input) {
|
|
2555
|
+
return {
|
|
2556
|
+
id: createId("template"),
|
|
2557
|
+
type: "template",
|
|
2558
|
+
position: input.position,
|
|
2559
|
+
zIndex: input.zIndex ?? 0,
|
|
2560
|
+
locked: input.locked ?? false,
|
|
2561
|
+
layerId: input.layerId ?? "",
|
|
2562
|
+
templateShape: input.templateShape,
|
|
2563
|
+
radius: input.radius,
|
|
2564
|
+
angle: input.angle ?? 0,
|
|
2565
|
+
fillColor: input.fillColor ?? "rgba(255, 87, 34, 0.2)",
|
|
2566
|
+
strokeColor: input.strokeColor ?? "#FF5722",
|
|
2567
|
+
strokeWidth: input.strokeWidth ?? 2,
|
|
2568
|
+
opacity: input.opacity ?? 0.6,
|
|
2569
|
+
feetPerCell: input.feetPerCell,
|
|
2570
|
+
radiusFeet: input.radiusFeet
|
|
2571
|
+
};
|
|
2572
|
+
}
|
|
2573
|
+
|
|
2574
|
+
// src/elements/note-formatting.ts
|
|
2575
|
+
function toggleBold() {
|
|
2576
|
+
document.execCommand("bold");
|
|
2577
|
+
}
|
|
2578
|
+
function toggleItalic() {
|
|
2579
|
+
document.execCommand("italic");
|
|
2580
|
+
}
|
|
2581
|
+
function toggleUnderline() {
|
|
2582
|
+
document.execCommand("underline");
|
|
2583
|
+
}
|
|
2584
|
+
function toggleStrikethrough() {
|
|
2585
|
+
document.execCommand("strikeThrough");
|
|
2586
|
+
}
|
|
2587
|
+
function setFontSize(size) {
|
|
2588
|
+
const sel = window.getSelection();
|
|
2589
|
+
if (!sel || sel.rangeCount === 0) return;
|
|
2590
|
+
const range = sel.getRangeAt(0);
|
|
2591
|
+
if (range.collapsed) return;
|
|
2592
|
+
const span = document.createElement("span");
|
|
2593
|
+
span.style.fontSize = `${size}px`;
|
|
2594
|
+
try {
|
|
2595
|
+
range.surroundContents(span);
|
|
2596
|
+
} catch {
|
|
2597
|
+
span.appendChild(range.extractContents());
|
|
2598
|
+
range.insertNode(span);
|
|
2599
|
+
}
|
|
2600
|
+
}
|
|
2601
|
+
function getActiveFormats() {
|
|
2602
|
+
const query = (cmd) => {
|
|
2603
|
+
try {
|
|
2604
|
+
return document.queryCommandState(cmd);
|
|
2605
|
+
} catch {
|
|
2606
|
+
return false;
|
|
2607
|
+
}
|
|
2608
|
+
};
|
|
2609
|
+
return {
|
|
2610
|
+
bold: query("bold"),
|
|
2611
|
+
italic: query("italic"),
|
|
2612
|
+
underline: query("underline"),
|
|
2613
|
+
strikethrough: query("strikeThrough")
|
|
2614
|
+
};
|
|
2615
|
+
}
|
|
2616
|
+
|
|
2617
|
+
// src/elements/note-toolbar.ts
|
|
2618
|
+
var TOOLBAR_HEIGHT = 32;
|
|
2619
|
+
var TOOLBAR_GAP = 4;
|
|
2620
|
+
var FORMAT_BUTTONS = [
|
|
2621
|
+
{ label: "B", format: "bold", command: "bold" },
|
|
2622
|
+
{ label: "I", format: "italic", command: "italic" },
|
|
2623
|
+
{ label: "U", format: "underline", command: "underline" },
|
|
2624
|
+
{ label: "S", format: "strikethrough", command: "strikeThrough" }
|
|
2625
|
+
];
|
|
2626
|
+
var DEFAULT_FONT_SIZE_PRESETS = [
|
|
2627
|
+
{ label: "Small", size: 14 },
|
|
2628
|
+
{ label: "Normal", size: 18 },
|
|
2629
|
+
{ label: "Large", size: 24 },
|
|
2630
|
+
{ label: "Heading", size: 32 }
|
|
2631
|
+
];
|
|
2632
|
+
var NoteToolbar = class {
|
|
2633
|
+
el = null;
|
|
2634
|
+
anchor = null;
|
|
2635
|
+
selectionListener = null;
|
|
2636
|
+
fontSizePresets;
|
|
2637
|
+
constructor(fontSizePresets) {
|
|
2638
|
+
this.fontSizePresets = fontSizePresets ?? DEFAULT_FONT_SIZE_PRESETS;
|
|
2639
|
+
}
|
|
2640
|
+
show(anchor) {
|
|
2641
|
+
this.hide();
|
|
2642
|
+
this.anchor = anchor;
|
|
2643
|
+
this.el = this.createToolbarElement();
|
|
2644
|
+
document.body.appendChild(this.el);
|
|
2645
|
+
this.positionToolbar(anchor);
|
|
2646
|
+
this.selectionListener = () => this.updateActiveStates();
|
|
2647
|
+
document.addEventListener("selectionchange", this.selectionListener);
|
|
2648
|
+
}
|
|
2649
|
+
hide() {
|
|
2650
|
+
if (this.selectionListener) {
|
|
2651
|
+
document.removeEventListener("selectionchange", this.selectionListener);
|
|
2652
|
+
this.selectionListener = null;
|
|
2653
|
+
}
|
|
2654
|
+
if (this.el) {
|
|
2655
|
+
this.el.remove();
|
|
2656
|
+
this.el = null;
|
|
2657
|
+
}
|
|
2658
|
+
this.anchor = null;
|
|
2659
|
+
}
|
|
2660
|
+
getElement() {
|
|
2661
|
+
return this.el;
|
|
2662
|
+
}
|
|
2663
|
+
updatePosition(anchor) {
|
|
2664
|
+
if (this.el) {
|
|
2665
|
+
this.positionToolbar(anchor);
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
createToolbarElement() {
|
|
2669
|
+
const toolbar = document.createElement("div");
|
|
2670
|
+
toolbar.dataset["noteToolbar"] = "";
|
|
2671
|
+
Object.assign(toolbar.style, {
|
|
2672
|
+
position: "fixed",
|
|
2673
|
+
display: "flex",
|
|
2674
|
+
alignItems: "center",
|
|
2675
|
+
gap: "2px",
|
|
2676
|
+
padding: "2px 4px",
|
|
2677
|
+
background: "#fff",
|
|
2678
|
+
border: "1px solid #ccc",
|
|
2679
|
+
borderRadius: "4px",
|
|
2680
|
+
boxShadow: "0 2px 8px rgba(0,0,0,0.15)",
|
|
2681
|
+
zIndex: "10000",
|
|
2682
|
+
height: `${TOOLBAR_HEIGHT}px`,
|
|
2683
|
+
userSelect: "none"
|
|
2684
|
+
});
|
|
2685
|
+
for (const btn of FORMAT_BUTTONS) {
|
|
2686
|
+
toolbar.appendChild(this.createFormatButton(btn));
|
|
2687
|
+
}
|
|
2688
|
+
toolbar.appendChild(this.createFontSizeSelect());
|
|
2689
|
+
return toolbar;
|
|
2690
|
+
}
|
|
2691
|
+
createFormatButton(config) {
|
|
2692
|
+
const btn = document.createElement("button");
|
|
2693
|
+
btn.dataset["format"] = config.format;
|
|
2694
|
+
btn.textContent = config.label;
|
|
2695
|
+
Object.assign(btn.style, {
|
|
2696
|
+
border: "1px solid transparent",
|
|
2697
|
+
borderRadius: "3px",
|
|
2698
|
+
background: "none",
|
|
2699
|
+
cursor: "pointer",
|
|
2700
|
+
padding: "2px 6px",
|
|
2701
|
+
fontSize: "13px",
|
|
2702
|
+
fontWeight: config.format === "bold" ? "bold" : "normal",
|
|
2703
|
+
fontStyle: config.format === "italic" ? "italic" : "normal",
|
|
2704
|
+
textDecoration: config.format === "underline" ? "underline" : config.format === "strikethrough" ? "line-through" : "none",
|
|
2705
|
+
minWidth: "24px",
|
|
2706
|
+
height: "24px",
|
|
2707
|
+
lineHeight: "24px"
|
|
2708
|
+
});
|
|
2709
|
+
btn.addEventListener("pointerdown", (e) => {
|
|
2710
|
+
e.preventDefault();
|
|
2711
|
+
document.execCommand(config.command);
|
|
2712
|
+
this.updateActiveStates();
|
|
2713
|
+
});
|
|
2714
|
+
return btn;
|
|
2715
|
+
}
|
|
2716
|
+
createFontSizeSelect() {
|
|
2717
|
+
const select = document.createElement("select");
|
|
2718
|
+
Object.assign(select.style, {
|
|
2719
|
+
border: "1px solid #ccc",
|
|
2720
|
+
borderRadius: "3px",
|
|
2721
|
+
background: "#fff",
|
|
2722
|
+
cursor: "pointer",
|
|
2723
|
+
padding: "2px",
|
|
2724
|
+
fontSize: "12px",
|
|
2725
|
+
height: "24px",
|
|
2726
|
+
marginLeft: "4px"
|
|
2727
|
+
});
|
|
2728
|
+
for (const preset of this.fontSizePresets) {
|
|
2729
|
+
const option = document.createElement("option");
|
|
2730
|
+
option.value = String(preset.size);
|
|
2731
|
+
option.textContent = preset.label;
|
|
2732
|
+
select.appendChild(option);
|
|
2733
|
+
}
|
|
2734
|
+
select.value = String(DEFAULT_NOTE_FONT_SIZE);
|
|
2735
|
+
select.addEventListener("pointerdown", (e) => {
|
|
2736
|
+
e.stopPropagation();
|
|
2737
|
+
});
|
|
2738
|
+
select.addEventListener("change", () => {
|
|
2739
|
+
setFontSize(Number(select.value));
|
|
2740
|
+
this.updateActiveStates();
|
|
2741
|
+
this.anchor?.focus();
|
|
2742
|
+
});
|
|
2743
|
+
return select;
|
|
2744
|
+
}
|
|
2745
|
+
positionToolbar(anchor) {
|
|
2746
|
+
if (!this.el) return;
|
|
2747
|
+
const rect = anchor.getBoundingClientRect();
|
|
2748
|
+
const toolbarWidth = this.el.offsetWidth || 200;
|
|
2749
|
+
let top = rect.top - TOOLBAR_HEIGHT - TOOLBAR_GAP;
|
|
2750
|
+
if (top < 0) {
|
|
2751
|
+
top = rect.bottom + TOOLBAR_GAP;
|
|
2752
|
+
}
|
|
2753
|
+
let left = rect.left + (rect.width - toolbarWidth) / 2;
|
|
2754
|
+
left = Math.max(4, left);
|
|
2755
|
+
Object.assign(this.el.style, {
|
|
2756
|
+
top: `${top}px`,
|
|
2757
|
+
left: `${left}px`
|
|
2758
|
+
});
|
|
2759
|
+
}
|
|
2760
|
+
updateActiveStates() {
|
|
2761
|
+
if (!this.el) return;
|
|
2762
|
+
const active = getActiveFormats();
|
|
2763
|
+
for (const config of FORMAT_BUTTONS) {
|
|
2764
|
+
const btn = this.el.querySelector(`[data-format="${config.format}"]`);
|
|
2765
|
+
if (!btn) continue;
|
|
2766
|
+
const isActive = active[config.format] ?? false;
|
|
2767
|
+
btn.style.background = isActive ? "#e0e0e0" : "none";
|
|
2768
|
+
btn.style.borderColor = isActive ? "#bbb" : "transparent";
|
|
2769
|
+
}
|
|
2770
|
+
}
|
|
2771
|
+
};
|
|
2772
|
+
|
|
2289
2773
|
// src/elements/note-editor.ts
|
|
2774
|
+
var FORMAT_SHORTCUTS = {
|
|
2775
|
+
b: toggleBold,
|
|
2776
|
+
i: toggleItalic,
|
|
2777
|
+
u: toggleUnderline
|
|
2778
|
+
};
|
|
2290
2779
|
var NoteEditor = class {
|
|
2291
2780
|
editingId = null;
|
|
2292
2781
|
editingNode = null;
|
|
@@ -2295,6 +2784,10 @@ var NoteEditor = class {
|
|
|
2295
2784
|
pointerHandler = null;
|
|
2296
2785
|
pendingEditId = null;
|
|
2297
2786
|
onStopCallback = null;
|
|
2787
|
+
toolbar;
|
|
2788
|
+
constructor(options) {
|
|
2789
|
+
this.toolbar = options?.toolbar === false ? null : new NoteToolbar(options?.fontSizePresets);
|
|
2790
|
+
}
|
|
2298
2791
|
get isEditing() {
|
|
2299
2792
|
return this.editingId !== null;
|
|
2300
2793
|
}
|
|
@@ -2319,13 +2812,6 @@ var NoteEditor = class {
|
|
|
2319
2812
|
stopEditing(store) {
|
|
2320
2813
|
this.pendingEditId = null;
|
|
2321
2814
|
if (!this.editingId || !this.editingNode) return;
|
|
2322
|
-
const text = this.editingNode.textContent ?? "";
|
|
2323
|
-
store.update(this.editingId, { text });
|
|
2324
|
-
this.editingNode.contentEditable = "false";
|
|
2325
|
-
Object.assign(this.editingNode.style, {
|
|
2326
|
-
userSelect: "none",
|
|
2327
|
-
cursor: "default"
|
|
2328
|
-
});
|
|
2329
2815
|
if (this.blurHandler) {
|
|
2330
2816
|
this.editingNode.removeEventListener("blur", this.blurHandler);
|
|
2331
2817
|
}
|
|
@@ -2335,6 +2821,14 @@ var NoteEditor = class {
|
|
|
2335
2821
|
if (this.pointerHandler) {
|
|
2336
2822
|
this.editingNode.removeEventListener("pointerdown", this.pointerHandler);
|
|
2337
2823
|
}
|
|
2824
|
+
const text = sanitizeNoteHtml(this.editingNode.innerHTML);
|
|
2825
|
+
store.update(this.editingId, { text });
|
|
2826
|
+
this.editingNode.contentEditable = "false";
|
|
2827
|
+
Object.assign(this.editingNode.style, {
|
|
2828
|
+
userSelect: "none",
|
|
2829
|
+
cursor: "default"
|
|
2830
|
+
});
|
|
2831
|
+
this.toolbar?.hide();
|
|
2338
2832
|
if (this.editingId && this.onStopCallback) {
|
|
2339
2833
|
this.onStopCallback(this.editingId);
|
|
2340
2834
|
}
|
|
@@ -2350,6 +2844,11 @@ var NoteEditor = class {
|
|
|
2350
2844
|
this.stopEditing(store);
|
|
2351
2845
|
}
|
|
2352
2846
|
}
|
|
2847
|
+
updateToolbarPosition() {
|
|
2848
|
+
if (this.editingNode) {
|
|
2849
|
+
this.toolbar?.updatePosition(this.editingNode);
|
|
2850
|
+
}
|
|
2851
|
+
}
|
|
2353
2852
|
activateEditing(node, elementId, store) {
|
|
2354
2853
|
this.editingId = elementId;
|
|
2355
2854
|
this.editingNode = node;
|
|
@@ -2368,8 +2867,21 @@ var NoteEditor = class {
|
|
|
2368
2867
|
selection.removeAllRanges();
|
|
2369
2868
|
selection.addRange(range);
|
|
2370
2869
|
}
|
|
2371
|
-
this.
|
|
2870
|
+
this.toolbar?.show(node);
|
|
2871
|
+
this.blurHandler = (e) => {
|
|
2872
|
+
const related = e.relatedTarget;
|
|
2873
|
+
if (related && this.toolbar?.getElement()?.contains(related)) return;
|
|
2874
|
+
this.stopEditing(store);
|
|
2875
|
+
};
|
|
2372
2876
|
this.keyHandler = (e) => {
|
|
2877
|
+
if ((e.ctrlKey || e.metaKey) && !e.shiftKey && !e.altKey) {
|
|
2878
|
+
const action = FORMAT_SHORTCUTS[e.key.toLowerCase()];
|
|
2879
|
+
if (action) {
|
|
2880
|
+
e.preventDefault();
|
|
2881
|
+
action();
|
|
2882
|
+
return;
|
|
2883
|
+
}
|
|
2884
|
+
}
|
|
2373
2885
|
if (e.key === "Escape") {
|
|
2374
2886
|
node.blur();
|
|
2375
2887
|
}
|
|
@@ -2621,150 +3133,86 @@ var HistoryRecorder = class {
|
|
|
2621
3133
|
}
|
|
2622
3134
|
};
|
|
2623
3135
|
|
|
2624
|
-
// src/
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
locked: input.locked ?? false,
|
|
2652
|
-
layerId: input.layerId ?? "",
|
|
2653
|
-
size: input.size ?? { w: 200, h: 100 },
|
|
2654
|
-
text: input.text ?? "",
|
|
2655
|
-
backgroundColor: input.backgroundColor ?? "#ffeb3b",
|
|
2656
|
-
textColor: input.textColor ?? "#000000"
|
|
2657
|
-
};
|
|
2658
|
-
}
|
|
2659
|
-
function createArrow(input) {
|
|
2660
|
-
const bend = input.bend ?? 0;
|
|
2661
|
-
const result = {
|
|
2662
|
-
id: createId("arrow"),
|
|
2663
|
-
type: "arrow",
|
|
2664
|
-
position: input.position ?? { x: 0, y: 0 },
|
|
2665
|
-
zIndex: input.zIndex ?? 0,
|
|
2666
|
-
locked: input.locked ?? false,
|
|
2667
|
-
layerId: input.layerId ?? "",
|
|
2668
|
-
from: input.from,
|
|
2669
|
-
to: input.to,
|
|
2670
|
-
bend,
|
|
2671
|
-
color: input.color ?? "#000000",
|
|
2672
|
-
width: input.width ?? 2,
|
|
2673
|
-
cachedControlPoint: getArrowControlPoint(input.from, input.to, bend)
|
|
2674
|
-
};
|
|
2675
|
-
if (input.fromBinding) result.fromBinding = input.fromBinding;
|
|
2676
|
-
if (input.toBinding) result.toBinding = input.toBinding;
|
|
2677
|
-
return result;
|
|
2678
|
-
}
|
|
2679
|
-
function createImage(input) {
|
|
2680
|
-
return {
|
|
2681
|
-
id: createId("image"),
|
|
2682
|
-
type: "image",
|
|
2683
|
-
position: input.position,
|
|
2684
|
-
zIndex: input.zIndex ?? 0,
|
|
2685
|
-
locked: input.locked ?? false,
|
|
2686
|
-
layerId: input.layerId ?? "",
|
|
2687
|
-
size: input.size,
|
|
2688
|
-
src: input.src
|
|
2689
|
-
};
|
|
2690
|
-
}
|
|
2691
|
-
function createHtmlElement(input) {
|
|
2692
|
-
const el = {
|
|
2693
|
-
id: createId("html"),
|
|
2694
|
-
type: "html",
|
|
2695
|
-
position: input.position,
|
|
2696
|
-
zIndex: input.zIndex ?? 0,
|
|
2697
|
-
locked: input.locked ?? false,
|
|
2698
|
-
layerId: input.layerId ?? "",
|
|
2699
|
-
size: input.size
|
|
2700
|
-
};
|
|
2701
|
-
if (input.domId) el.domId = input.domId;
|
|
2702
|
-
return el;
|
|
2703
|
-
}
|
|
2704
|
-
function createShape(input) {
|
|
2705
|
-
return {
|
|
2706
|
-
id: createId("shape"),
|
|
2707
|
-
type: "shape",
|
|
2708
|
-
position: input.position,
|
|
2709
|
-
zIndex: input.zIndex ?? 0,
|
|
2710
|
-
locked: input.locked ?? false,
|
|
2711
|
-
layerId: input.layerId ?? "",
|
|
2712
|
-
shape: input.shape ?? "rectangle",
|
|
2713
|
-
size: input.size,
|
|
2714
|
-
strokeColor: input.strokeColor ?? "#000000",
|
|
2715
|
-
strokeWidth: input.strokeWidth ?? 2,
|
|
2716
|
-
fillColor: input.fillColor ?? "none"
|
|
2717
|
-
};
|
|
2718
|
-
}
|
|
2719
|
-
function createGrid(input) {
|
|
2720
|
-
return {
|
|
2721
|
-
id: createId("grid"),
|
|
2722
|
-
type: "grid",
|
|
2723
|
-
position: input.position ?? { x: 0, y: 0 },
|
|
2724
|
-
zIndex: input.zIndex ?? 0,
|
|
2725
|
-
locked: input.locked ?? false,
|
|
2726
|
-
layerId: input.layerId ?? "",
|
|
2727
|
-
gridType: input.gridType ?? "square",
|
|
2728
|
-
hexOrientation: input.hexOrientation ?? "pointy",
|
|
2729
|
-
cellSize: input.cellSize ?? 40,
|
|
2730
|
-
strokeColor: input.strokeColor ?? "#000000",
|
|
2731
|
-
strokeWidth: input.strokeWidth ?? 1,
|
|
2732
|
-
opacity: input.opacity ?? 1
|
|
2733
|
-
};
|
|
3136
|
+
// src/canvas/note-canvas-renderer.ts
|
|
3137
|
+
function renderNoteOnCanvas(ctx, note) {
|
|
3138
|
+
const { x, y } = note.position;
|
|
3139
|
+
const { w, h } = note.size;
|
|
3140
|
+
const r = 4;
|
|
3141
|
+
const pad = 8;
|
|
3142
|
+
const baseFontSize = note.fontSize ?? DEFAULT_NOTE_FONT_SIZE;
|
|
3143
|
+
ctx.save();
|
|
3144
|
+
ctx.fillStyle = note.backgroundColor;
|
|
3145
|
+
ctx.beginPath();
|
|
3146
|
+
ctx.moveTo(x + r, y);
|
|
3147
|
+
ctx.lineTo(x + w - r, y);
|
|
3148
|
+
ctx.arcTo(x + w, y, x + w, y + r, r);
|
|
3149
|
+
ctx.lineTo(x + w, y + h - r);
|
|
3150
|
+
ctx.arcTo(x + w, y + h, x + w - r, y + h, r);
|
|
3151
|
+
ctx.lineTo(x + r, y + h);
|
|
3152
|
+
ctx.arcTo(x, y + h, x, y + h - r, r);
|
|
3153
|
+
ctx.lineTo(x, y + r);
|
|
3154
|
+
ctx.arcTo(x, y, x + r, y, r);
|
|
3155
|
+
ctx.closePath();
|
|
3156
|
+
ctx.fill();
|
|
3157
|
+
if (note.text) {
|
|
3158
|
+
ctx.fillStyle = note.textColor;
|
|
3159
|
+
const runs = parseStyledRuns(note.text, baseFontSize);
|
|
3160
|
+
renderStyledRuns(ctx, runs, x + pad, y + pad, w - pad * 2);
|
|
3161
|
+
}
|
|
3162
|
+
ctx.restore();
|
|
2734
3163
|
}
|
|
2735
|
-
function
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
position: input.position,
|
|
2740
|
-
zIndex: input.zIndex ?? 0,
|
|
2741
|
-
locked: input.locked ?? false,
|
|
2742
|
-
layerId: input.layerId ?? "",
|
|
2743
|
-
size: input.size ?? { w: 200, h: 28 },
|
|
2744
|
-
text: input.text ?? "",
|
|
2745
|
-
fontSize: input.fontSize ?? 16,
|
|
2746
|
-
color: input.color ?? "#1a1a1a",
|
|
2747
|
-
textAlign: input.textAlign ?? "left"
|
|
2748
|
-
};
|
|
3164
|
+
function buildFontString(run) {
|
|
3165
|
+
const style = run.italic ? "italic" : "normal";
|
|
3166
|
+
const weight = run.bold ? "bold" : "normal";
|
|
3167
|
+
return `${style} ${weight} ${run.fontSize}px system-ui, sans-serif`;
|
|
2749
3168
|
}
|
|
2750
|
-
function
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
3169
|
+
function renderStyledRuns(ctx, runs, startX, startY, maxWidth) {
|
|
3170
|
+
ctx.textBaseline = "top";
|
|
3171
|
+
let cursorX = startX;
|
|
3172
|
+
let cursorY = startY;
|
|
3173
|
+
let lineHeight = 0;
|
|
3174
|
+
for (const run of runs) {
|
|
3175
|
+
ctx.font = buildFontString(run);
|
|
3176
|
+
const runLineHeight = run.fontSize * 1.3;
|
|
3177
|
+
lineHeight = Math.max(lineHeight, runLineHeight);
|
|
3178
|
+
const words = run.text.split(/(\n| )/);
|
|
3179
|
+
for (const word of words) {
|
|
3180
|
+
if (word === "\n") {
|
|
3181
|
+
cursorX = startX;
|
|
3182
|
+
cursorY += lineHeight;
|
|
3183
|
+
lineHeight = runLineHeight;
|
|
3184
|
+
continue;
|
|
3185
|
+
}
|
|
3186
|
+
if (word === " ") {
|
|
3187
|
+
const spaceWidth = ctx.measureText(" ").width;
|
|
3188
|
+
if (cursorX + spaceWidth > startX + maxWidth && cursorX > startX) {
|
|
3189
|
+
cursorX = startX;
|
|
3190
|
+
cursorY += lineHeight;
|
|
3191
|
+
lineHeight = runLineHeight;
|
|
3192
|
+
} else {
|
|
3193
|
+
cursorX += spaceWidth;
|
|
3194
|
+
}
|
|
3195
|
+
continue;
|
|
3196
|
+
}
|
|
3197
|
+
if (!word) continue;
|
|
3198
|
+
const metrics = ctx.measureText(word);
|
|
3199
|
+
if (cursorX + metrics.width > startX + maxWidth && cursorX > startX) {
|
|
3200
|
+
cursorX = startX;
|
|
3201
|
+
cursorY += lineHeight;
|
|
3202
|
+
lineHeight = runLineHeight;
|
|
3203
|
+
}
|
|
3204
|
+
ctx.fillText(word, cursorX, cursorY);
|
|
3205
|
+
if (run.underline) {
|
|
3206
|
+
const underY = cursorY + run.fontSize + 1;
|
|
3207
|
+
ctx.fillRect(cursorX, underY, metrics.width, 1);
|
|
3208
|
+
}
|
|
3209
|
+
if (run.strikethrough) {
|
|
3210
|
+
const strikeY = cursorY + run.fontSize * 0.55;
|
|
3211
|
+
ctx.fillRect(cursorX, strikeY, metrics.width, 1);
|
|
3212
|
+
}
|
|
3213
|
+
cursorX += metrics.width;
|
|
3214
|
+
}
|
|
3215
|
+
}
|
|
2768
3216
|
}
|
|
2769
3217
|
|
|
2770
3218
|
// src/canvas/export-image.ts
|
|
@@ -2843,33 +3291,6 @@ function computeBounds(elements, padding) {
|
|
|
2843
3291
|
h: maxY - minY + padding * 2
|
|
2844
3292
|
};
|
|
2845
3293
|
}
|
|
2846
|
-
function renderNoteOnCanvas(ctx, note) {
|
|
2847
|
-
const { x, y } = note.position;
|
|
2848
|
-
const { w, h } = note.size;
|
|
2849
|
-
const r = 4;
|
|
2850
|
-
const pad = 8;
|
|
2851
|
-
ctx.save();
|
|
2852
|
-
ctx.fillStyle = note.backgroundColor;
|
|
2853
|
-
ctx.beginPath();
|
|
2854
|
-
ctx.moveTo(x + r, y);
|
|
2855
|
-
ctx.lineTo(x + w - r, y);
|
|
2856
|
-
ctx.arcTo(x + w, y, x + w, y + r, r);
|
|
2857
|
-
ctx.lineTo(x + w, y + h - r);
|
|
2858
|
-
ctx.arcTo(x + w, y + h, x + w - r, y + h, r);
|
|
2859
|
-
ctx.lineTo(x + r, y + h);
|
|
2860
|
-
ctx.arcTo(x, y + h, x, y + h - r, r);
|
|
2861
|
-
ctx.lineTo(x, y + r);
|
|
2862
|
-
ctx.arcTo(x, y, x + r, y, r);
|
|
2863
|
-
ctx.closePath();
|
|
2864
|
-
ctx.fill();
|
|
2865
|
-
if (note.text) {
|
|
2866
|
-
ctx.fillStyle = note.textColor;
|
|
2867
|
-
ctx.font = "14px system-ui, sans-serif";
|
|
2868
|
-
ctx.textBaseline = "top";
|
|
2869
|
-
wrapText(ctx, note.text, x + pad, y + pad, w - pad * 2, 18);
|
|
2870
|
-
}
|
|
2871
|
-
ctx.restore();
|
|
2872
|
-
}
|
|
2873
3294
|
function renderTextOnCanvas(ctx, text) {
|
|
2874
3295
|
if (!text.text) return;
|
|
2875
3296
|
ctx.save();
|
|
@@ -2894,25 +3315,6 @@ function renderTextOnCanvas(ctx, text) {
|
|
|
2894
3315
|
}
|
|
2895
3316
|
ctx.restore();
|
|
2896
3317
|
}
|
|
2897
|
-
function wrapText(ctx, text, x, y, maxWidth, lineHeight) {
|
|
2898
|
-
const words = text.split(" ");
|
|
2899
|
-
let line = "";
|
|
2900
|
-
let offsetY = 0;
|
|
2901
|
-
for (const word of words) {
|
|
2902
|
-
const testLine = line ? `${line} ${word}` : word;
|
|
2903
|
-
const metrics = ctx.measureText(testLine);
|
|
2904
|
-
if (metrics.width > maxWidth && line) {
|
|
2905
|
-
ctx.fillText(line, x, y + offsetY);
|
|
2906
|
-
line = word;
|
|
2907
|
-
offsetY += lineHeight;
|
|
2908
|
-
} else {
|
|
2909
|
-
line = testLine;
|
|
2910
|
-
}
|
|
2911
|
-
}
|
|
2912
|
-
if (line) {
|
|
2913
|
-
ctx.fillText(line, x, y + offsetY);
|
|
2914
|
-
}
|
|
2915
|
-
}
|
|
2916
3318
|
function renderGridForBounds(ctx, grid, bounds) {
|
|
2917
3319
|
const visibleBounds = {
|
|
2918
3320
|
minX: bounds.x,
|
|
@@ -3311,13 +3713,13 @@ var DomNodeManager = class {
|
|
|
3311
3713
|
padding: "8px",
|
|
3312
3714
|
borderRadius: "4px",
|
|
3313
3715
|
boxShadow: "0 2px 8px rgba(0,0,0,0.15)",
|
|
3314
|
-
fontSize:
|
|
3716
|
+
fontSize: `${element.fontSize ?? DEFAULT_NOTE_FONT_SIZE}px`,
|
|
3315
3717
|
overflow: "hidden",
|
|
3316
3718
|
cursor: "default",
|
|
3317
3719
|
userSelect: "none",
|
|
3318
3720
|
wordWrap: "break-word"
|
|
3319
3721
|
});
|
|
3320
|
-
node.
|
|
3722
|
+
node.innerHTML = element.text || "";
|
|
3321
3723
|
node.addEventListener("dblclick", (e) => {
|
|
3322
3724
|
e.stopPropagation();
|
|
3323
3725
|
const id = node.dataset["elementId"];
|
|
@@ -3325,11 +3727,13 @@ var DomNodeManager = class {
|
|
|
3325
3727
|
});
|
|
3326
3728
|
}
|
|
3327
3729
|
if (!this.isEditingElement(element.id)) {
|
|
3328
|
-
|
|
3329
|
-
|
|
3730
|
+
const text = element.text || "";
|
|
3731
|
+
if (node.innerHTML !== text) {
|
|
3732
|
+
node.innerHTML = text;
|
|
3330
3733
|
}
|
|
3331
3734
|
node.style.backgroundColor = element.backgroundColor;
|
|
3332
3735
|
node.style.color = element.textColor;
|
|
3736
|
+
node.style.fontSize = `${element.fontSize ?? DEFAULT_NOTE_FONT_SIZE}px`;
|
|
3333
3737
|
}
|
|
3334
3738
|
}
|
|
3335
3739
|
if (element.type === "html" && !node.dataset["initialized"]) {
|
|
@@ -3760,7 +4164,10 @@ var Viewport = class {
|
|
|
3760
4164
|
this.renderLoop.markAllLayersDirty();
|
|
3761
4165
|
this.requestRender();
|
|
3762
4166
|
});
|
|
3763
|
-
this.noteEditor = new NoteEditor(
|
|
4167
|
+
this.noteEditor = new NoteEditor({
|
|
4168
|
+
fontSizePresets: options.fontSizePresets,
|
|
4169
|
+
toolbar: options.toolbar
|
|
4170
|
+
});
|
|
3764
4171
|
this.noteEditor.setOnStop((id) => this.onTextEditStop(id));
|
|
3765
4172
|
this.history = new HistoryStack();
|
|
3766
4173
|
this.historyRecorder = new HistoryRecorder(this.store, this.history);
|
|
@@ -3816,6 +4223,7 @@ var Viewport = class {
|
|
|
3816
4223
|
});
|
|
3817
4224
|
this.unsubCamera = this.camera.onChange(() => {
|
|
3818
4225
|
this.applyCameraTransform();
|
|
4226
|
+
this.noteEditor.updateToolbarPosition();
|
|
3819
4227
|
this.requestRender();
|
|
3820
4228
|
});
|
|
3821
4229
|
this.unsubStore = [
|
|
@@ -3880,6 +4288,7 @@ var Viewport = class {
|
|
|
3880
4288
|
renderLoop;
|
|
3881
4289
|
domNodeManager;
|
|
3882
4290
|
interactMode;
|
|
4291
|
+
gridChangeListeners = /* @__PURE__ */ new Set();
|
|
3883
4292
|
get ctx() {
|
|
3884
4293
|
return this.canvasEl.getContext("2d");
|
|
3885
4294
|
}
|
|
@@ -3984,6 +4393,22 @@ var Viewport = class {
|
|
|
3984
4393
|
this.historyRecorder.commit();
|
|
3985
4394
|
this.requestRender();
|
|
3986
4395
|
}
|
|
4396
|
+
getGridInfo() {
|
|
4397
|
+
const grid = this.store.getElementsByType("grid")[0];
|
|
4398
|
+
if (!grid) return null;
|
|
4399
|
+
return {
|
|
4400
|
+
gridType: grid.gridType,
|
|
4401
|
+
hexOrientation: grid.hexOrientation,
|
|
4402
|
+
cellSize: grid.cellSize,
|
|
4403
|
+
cellRadius: grid.gridType === "hex" ? grid.cellSize : grid.cellSize / 2
|
|
4404
|
+
};
|
|
4405
|
+
}
|
|
4406
|
+
onGridChange(listener) {
|
|
4407
|
+
this.gridChangeListeners.add(listener);
|
|
4408
|
+
return () => {
|
|
4409
|
+
this.gridChangeListeners.delete(listener);
|
|
4410
|
+
};
|
|
4411
|
+
}
|
|
3987
4412
|
getRenderStats() {
|
|
3988
4413
|
return this.renderLoop.getStats();
|
|
3989
4414
|
}
|
|
@@ -4184,6 +4609,13 @@ var Viewport = class {
|
|
|
4184
4609
|
this.toolContext.gridType = void 0;
|
|
4185
4610
|
this.toolContext.hexOrientation = void 0;
|
|
4186
4611
|
}
|
|
4612
|
+
this.notifyGridChangeListeners();
|
|
4613
|
+
}
|
|
4614
|
+
notifyGridChangeListeners() {
|
|
4615
|
+
const info = this.getGridInfo();
|
|
4616
|
+
for (const listener of this.gridChangeListeners) {
|
|
4617
|
+
listener(info);
|
|
4618
|
+
}
|
|
4187
4619
|
}
|
|
4188
4620
|
observeResize() {
|
|
4189
4621
|
if (typeof ResizeObserver === "undefined") return;
|
|
@@ -5165,17 +5597,20 @@ var NoteTool = class {
|
|
|
5165
5597
|
backgroundColor;
|
|
5166
5598
|
textColor;
|
|
5167
5599
|
size;
|
|
5600
|
+
fontSize;
|
|
5168
5601
|
optionListeners = /* @__PURE__ */ new Set();
|
|
5169
5602
|
constructor(options = {}) {
|
|
5170
5603
|
this.backgroundColor = options.backgroundColor ?? "#ffeb3b";
|
|
5171
5604
|
this.textColor = options.textColor ?? "#000000";
|
|
5172
5605
|
this.size = options.size ?? { w: 200, h: 100 };
|
|
5606
|
+
this.fontSize = options.fontSize ?? DEFAULT_NOTE_FONT_SIZE;
|
|
5173
5607
|
}
|
|
5174
5608
|
getOptions() {
|
|
5175
5609
|
return {
|
|
5176
5610
|
backgroundColor: this.backgroundColor,
|
|
5177
5611
|
textColor: this.textColor,
|
|
5178
|
-
size: { ...this.size }
|
|
5612
|
+
size: { ...this.size },
|
|
5613
|
+
fontSize: this.fontSize
|
|
5179
5614
|
};
|
|
5180
5615
|
}
|
|
5181
5616
|
onOptionsChange(listener) {
|
|
@@ -5186,6 +5621,7 @@ var NoteTool = class {
|
|
|
5186
5621
|
if (options.backgroundColor !== void 0) this.backgroundColor = options.backgroundColor;
|
|
5187
5622
|
if (options.textColor !== void 0) this.textColor = options.textColor;
|
|
5188
5623
|
if (options.size !== void 0) this.size = options.size;
|
|
5624
|
+
if (options.fontSize !== void 0) this.fontSize = options.fontSize;
|
|
5189
5625
|
this.notifyOptionsChange();
|
|
5190
5626
|
}
|
|
5191
5627
|
notifyOptionsChange() {
|
|
@@ -5203,6 +5639,7 @@ var NoteTool = class {
|
|
|
5203
5639
|
size: { ...this.size },
|
|
5204
5640
|
backgroundColor: this.backgroundColor,
|
|
5205
5641
|
textColor: this.textColor,
|
|
5642
|
+
fontSize: this.fontSize,
|
|
5206
5643
|
layerId: ctx.activeLayerId ?? ""
|
|
5207
5644
|
});
|
|
5208
5645
|
ctx.store.add(note);
|
|
@@ -5870,7 +6307,7 @@ var UpdateLayerCommand = class {
|
|
|
5870
6307
|
};
|
|
5871
6308
|
|
|
5872
6309
|
// src/index.ts
|
|
5873
|
-
var VERSION = "0.
|
|
6310
|
+
var VERSION = "0.11.0";
|
|
5874
6311
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5875
6312
|
0 && (module.exports = {
|
|
5876
6313
|
AddElementCommand,
|
|
@@ -5880,6 +6317,8 @@ var VERSION = "0.9.0";
|
|
|
5880
6317
|
BatchCommand,
|
|
5881
6318
|
Camera,
|
|
5882
6319
|
CreateLayerCommand,
|
|
6320
|
+
DEFAULT_FONT_SIZE_PRESETS,
|
|
6321
|
+
DEFAULT_NOTE_FONT_SIZE,
|
|
5883
6322
|
ElementRenderer,
|
|
5884
6323
|
ElementStore,
|
|
5885
6324
|
EraserTool,
|
|
@@ -5893,6 +6332,7 @@ var VERSION = "0.9.0";
|
|
|
5893
6332
|
MeasureTool,
|
|
5894
6333
|
NoteEditor,
|
|
5895
6334
|
NoteTool,
|
|
6335
|
+
NoteToolbar,
|
|
5896
6336
|
PencilTool,
|
|
5897
6337
|
Quadtree,
|
|
5898
6338
|
RemoveElementCommand,
|
|
@@ -5923,6 +6363,7 @@ var VERSION = "0.9.0";
|
|
|
5923
6363
|
exportState,
|
|
5924
6364
|
findBindTarget,
|
|
5925
6365
|
findBoundArrows,
|
|
6366
|
+
getActiveFormats,
|
|
5926
6367
|
getArrowBounds,
|
|
5927
6368
|
getArrowControlPoint,
|
|
5928
6369
|
getArrowMidpoint,
|
|
@@ -5939,9 +6380,15 @@ var VERSION = "0.9.0";
|
|
|
5939
6380
|
isBindable,
|
|
5940
6381
|
isNearBezier,
|
|
5941
6382
|
parseState,
|
|
6383
|
+
sanitizeNoteHtml,
|
|
6384
|
+
setFontSize,
|
|
5942
6385
|
smartSnap,
|
|
5943
6386
|
snapPoint,
|
|
5944
6387
|
snapToHexCenter,
|
|
6388
|
+
toggleBold,
|
|
6389
|
+
toggleItalic,
|
|
6390
|
+
toggleStrikethrough,
|
|
6391
|
+
toggleUnderline,
|
|
5945
6392
|
unbindArrow,
|
|
5946
6393
|
updateBoundArrow
|
|
5947
6394
|
});
|