@fieldnotes/core 0.9.0 → 0.10.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 +627 -204
- package/dist/index.d.cts +63 -2
- package/dist/index.d.ts +63 -2
- package/dist/index.js +617 -204
- package/package.json +1 -1
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 = [
|
|
@@ -5165,17 +5573,20 @@ var NoteTool = class {
|
|
|
5165
5573
|
backgroundColor;
|
|
5166
5574
|
textColor;
|
|
5167
5575
|
size;
|
|
5576
|
+
fontSize;
|
|
5168
5577
|
optionListeners = /* @__PURE__ */ new Set();
|
|
5169
5578
|
constructor(options = {}) {
|
|
5170
5579
|
this.backgroundColor = options.backgroundColor ?? "#ffeb3b";
|
|
5171
5580
|
this.textColor = options.textColor ?? "#000000";
|
|
5172
5581
|
this.size = options.size ?? { w: 200, h: 100 };
|
|
5582
|
+
this.fontSize = options.fontSize ?? DEFAULT_NOTE_FONT_SIZE;
|
|
5173
5583
|
}
|
|
5174
5584
|
getOptions() {
|
|
5175
5585
|
return {
|
|
5176
5586
|
backgroundColor: this.backgroundColor,
|
|
5177
5587
|
textColor: this.textColor,
|
|
5178
|
-
size: { ...this.size }
|
|
5588
|
+
size: { ...this.size },
|
|
5589
|
+
fontSize: this.fontSize
|
|
5179
5590
|
};
|
|
5180
5591
|
}
|
|
5181
5592
|
onOptionsChange(listener) {
|
|
@@ -5186,6 +5597,7 @@ var NoteTool = class {
|
|
|
5186
5597
|
if (options.backgroundColor !== void 0) this.backgroundColor = options.backgroundColor;
|
|
5187
5598
|
if (options.textColor !== void 0) this.textColor = options.textColor;
|
|
5188
5599
|
if (options.size !== void 0) this.size = options.size;
|
|
5600
|
+
if (options.fontSize !== void 0) this.fontSize = options.fontSize;
|
|
5189
5601
|
this.notifyOptionsChange();
|
|
5190
5602
|
}
|
|
5191
5603
|
notifyOptionsChange() {
|
|
@@ -5203,6 +5615,7 @@ var NoteTool = class {
|
|
|
5203
5615
|
size: { ...this.size },
|
|
5204
5616
|
backgroundColor: this.backgroundColor,
|
|
5205
5617
|
textColor: this.textColor,
|
|
5618
|
+
fontSize: this.fontSize,
|
|
5206
5619
|
layerId: ctx.activeLayerId ?? ""
|
|
5207
5620
|
});
|
|
5208
5621
|
ctx.store.add(note);
|
|
@@ -5870,7 +6283,7 @@ var UpdateLayerCommand = class {
|
|
|
5870
6283
|
};
|
|
5871
6284
|
|
|
5872
6285
|
// src/index.ts
|
|
5873
|
-
var VERSION = "0.
|
|
6286
|
+
var VERSION = "0.10.0";
|
|
5874
6287
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5875
6288
|
0 && (module.exports = {
|
|
5876
6289
|
AddElementCommand,
|
|
@@ -5880,6 +6293,8 @@ var VERSION = "0.9.0";
|
|
|
5880
6293
|
BatchCommand,
|
|
5881
6294
|
Camera,
|
|
5882
6295
|
CreateLayerCommand,
|
|
6296
|
+
DEFAULT_FONT_SIZE_PRESETS,
|
|
6297
|
+
DEFAULT_NOTE_FONT_SIZE,
|
|
5883
6298
|
ElementRenderer,
|
|
5884
6299
|
ElementStore,
|
|
5885
6300
|
EraserTool,
|
|
@@ -5893,6 +6308,7 @@ var VERSION = "0.9.0";
|
|
|
5893
6308
|
MeasureTool,
|
|
5894
6309
|
NoteEditor,
|
|
5895
6310
|
NoteTool,
|
|
6311
|
+
NoteToolbar,
|
|
5896
6312
|
PencilTool,
|
|
5897
6313
|
Quadtree,
|
|
5898
6314
|
RemoveElementCommand,
|
|
@@ -5923,6 +6339,7 @@ var VERSION = "0.9.0";
|
|
|
5923
6339
|
exportState,
|
|
5924
6340
|
findBindTarget,
|
|
5925
6341
|
findBoundArrows,
|
|
6342
|
+
getActiveFormats,
|
|
5926
6343
|
getArrowBounds,
|
|
5927
6344
|
getArrowControlPoint,
|
|
5928
6345
|
getArrowMidpoint,
|
|
@@ -5939,9 +6356,15 @@ var VERSION = "0.9.0";
|
|
|
5939
6356
|
isBindable,
|
|
5940
6357
|
isNearBezier,
|
|
5941
6358
|
parseState,
|
|
6359
|
+
sanitizeNoteHtml,
|
|
6360
|
+
setFontSize,
|
|
5942
6361
|
smartSnap,
|
|
5943
6362
|
snapPoint,
|
|
5944
6363
|
snapToHexCenter,
|
|
6364
|
+
toggleBold,
|
|
6365
|
+
toggleItalic,
|
|
6366
|
+
toggleStrikethrough,
|
|
6367
|
+
toggleUnderline,
|
|
5945
6368
|
unbindArrow,
|
|
5946
6369
|
updateBoundArrow
|
|
5947
6370
|
});
|