@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.js
CHANGED
|
@@ -169,6 +169,120 @@ var Quadtree = class {
|
|
|
169
169
|
}
|
|
170
170
|
};
|
|
171
171
|
|
|
172
|
+
// src/elements/note-sanitizer.ts
|
|
173
|
+
var BOLD_TAGS = /* @__PURE__ */ new Set(["b", "strong"]);
|
|
174
|
+
var ITALIC_TAGS = /* @__PURE__ */ new Set(["i", "em"]);
|
|
175
|
+
var UNDERLINE_TAGS = /* @__PURE__ */ new Set(["u"]);
|
|
176
|
+
var STRIKE_TAGS = /* @__PURE__ */ new Set(["s", "strike", "del"]);
|
|
177
|
+
var BLOCK_TAGS = /* @__PURE__ */ new Set(["div"]);
|
|
178
|
+
function parseStyledRuns(html, baseFontSize) {
|
|
179
|
+
if (!html) return [];
|
|
180
|
+
const doc = new DOMParser().parseFromString(html, "text/html");
|
|
181
|
+
const runs = [];
|
|
182
|
+
const baseStyle = {
|
|
183
|
+
bold: false,
|
|
184
|
+
italic: false,
|
|
185
|
+
underline: false,
|
|
186
|
+
strikethrough: false,
|
|
187
|
+
fontSize: baseFontSize
|
|
188
|
+
};
|
|
189
|
+
walkNodes(doc.body, baseStyle, runs);
|
|
190
|
+
return runs;
|
|
191
|
+
}
|
|
192
|
+
function walkNodes(node, style, runs) {
|
|
193
|
+
for (const child of Array.from(node.childNodes)) {
|
|
194
|
+
if (child.nodeType === Node.TEXT_NODE) {
|
|
195
|
+
const text = child.textContent ?? "";
|
|
196
|
+
if (text) {
|
|
197
|
+
runs.push({ text, ...style });
|
|
198
|
+
}
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (child.nodeType !== Node.ELEMENT_NODE) continue;
|
|
202
|
+
const el = child;
|
|
203
|
+
const tag = el.tagName.toLowerCase();
|
|
204
|
+
if (tag === "br") {
|
|
205
|
+
runs.push({ text: "\n", ...style });
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (BLOCK_TAGS.has(tag) && runs.length > 0) {
|
|
209
|
+
const lastRun = runs[runs.length - 1];
|
|
210
|
+
if (lastRun && !lastRun.text.endsWith("\n")) {
|
|
211
|
+
runs.push({ text: "\n", ...style });
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
const childStyle = { ...style };
|
|
215
|
+
if (BOLD_TAGS.has(tag)) childStyle.bold = true;
|
|
216
|
+
if (ITALIC_TAGS.has(tag)) childStyle.italic = true;
|
|
217
|
+
if (UNDERLINE_TAGS.has(tag)) childStyle.underline = true;
|
|
218
|
+
if (STRIKE_TAGS.has(tag)) childStyle.strikethrough = true;
|
|
219
|
+
if (tag === "span") {
|
|
220
|
+
const fontSize = el.style.fontSize;
|
|
221
|
+
if (fontSize) {
|
|
222
|
+
childStyle.fontSize = parseInt(fontSize, 10) || style.fontSize;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
walkNodes(el, childStyle, runs);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
var ALLOWED_TAGS = /* @__PURE__ */ new Set([
|
|
229
|
+
"b",
|
|
230
|
+
"strong",
|
|
231
|
+
"i",
|
|
232
|
+
"em",
|
|
233
|
+
"u",
|
|
234
|
+
"s",
|
|
235
|
+
"strike",
|
|
236
|
+
"del",
|
|
237
|
+
"span",
|
|
238
|
+
"br",
|
|
239
|
+
"div"
|
|
240
|
+
]);
|
|
241
|
+
function sanitizeNoteHtml(html) {
|
|
242
|
+
if (!html) return "";
|
|
243
|
+
const doc = new DOMParser().parseFromString(html, "text/html");
|
|
244
|
+
sanitizeNode(doc.body);
|
|
245
|
+
return doc.body.innerHTML;
|
|
246
|
+
}
|
|
247
|
+
function sanitizeNode(node) {
|
|
248
|
+
const children = Array.from(node.childNodes);
|
|
249
|
+
for (const child of children) {
|
|
250
|
+
if (child.nodeType === Node.TEXT_NODE) continue;
|
|
251
|
+
if (child.nodeType !== Node.ELEMENT_NODE) {
|
|
252
|
+
child.remove();
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
const el = child;
|
|
256
|
+
const tag = el.tagName.toLowerCase();
|
|
257
|
+
if (!ALLOWED_TAGS.has(tag)) {
|
|
258
|
+
const fragment = document.createDocumentFragment();
|
|
259
|
+
while (el.firstChild) {
|
|
260
|
+
fragment.appendChild(el.firstChild);
|
|
261
|
+
}
|
|
262
|
+
node.replaceChild(fragment, el);
|
|
263
|
+
sanitizeNode(node);
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
sanitizeAttributes(el, tag);
|
|
267
|
+
sanitizeNode(el);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
function sanitizeAttributes(el, tag) {
|
|
271
|
+
const attrs = Array.from(el.attributes);
|
|
272
|
+
for (const attr of attrs) {
|
|
273
|
+
if (tag === "span" && attr.name === "style") {
|
|
274
|
+
const fontSize = el.style.fontSize;
|
|
275
|
+
if (fontSize) {
|
|
276
|
+
el.setAttribute("style", `font-size: ${fontSize};`);
|
|
277
|
+
} else {
|
|
278
|
+
el.removeAttribute("style");
|
|
279
|
+
}
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
el.removeAttribute(attr.name);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
172
286
|
// src/core/state-serializer.ts
|
|
173
287
|
var CURRENT_VERSION = 2;
|
|
174
288
|
function exportState(elements, camera, layers = []) {
|
|
@@ -231,7 +345,17 @@ function validateState(data) {
|
|
|
231
345
|
];
|
|
232
346
|
}
|
|
233
347
|
}
|
|
234
|
-
var VALID_TYPES = /* @__PURE__ */ new Set([
|
|
348
|
+
var VALID_TYPES = /* @__PURE__ */ new Set([
|
|
349
|
+
"stroke",
|
|
350
|
+
"note",
|
|
351
|
+
"arrow",
|
|
352
|
+
"image",
|
|
353
|
+
"html",
|
|
354
|
+
"text",
|
|
355
|
+
"shape",
|
|
356
|
+
"grid",
|
|
357
|
+
"template"
|
|
358
|
+
]);
|
|
235
359
|
function validateElement(el) {
|
|
236
360
|
if (!el || typeof el !== "object") {
|
|
237
361
|
throw new Error("Invalid element: expected an object");
|
|
@@ -281,6 +405,9 @@ function migrateElement(obj) {
|
|
|
281
405
|
if (obj["type"] === "note" && typeof obj["textColor"] !== "string") {
|
|
282
406
|
obj["textColor"] = "#000000";
|
|
283
407
|
}
|
|
408
|
+
if (obj["type"] === "note" && typeof obj["text"] === "string") {
|
|
409
|
+
obj["text"] = sanitizeNoteHtml(obj["text"]);
|
|
410
|
+
}
|
|
284
411
|
}
|
|
285
412
|
|
|
286
413
|
// src/core/snap.ts
|
|
@@ -2190,7 +2317,359 @@ var ElementRenderer = class {
|
|
|
2190
2317
|
}
|
|
2191
2318
|
};
|
|
2192
2319
|
|
|
2320
|
+
// src/elements/create-id.ts
|
|
2321
|
+
var counter = 0;
|
|
2322
|
+
function createId(prefix) {
|
|
2323
|
+
return `${prefix}_${Date.now().toString(36)}_${(counter++).toString(36)}`;
|
|
2324
|
+
}
|
|
2325
|
+
|
|
2326
|
+
// src/elements/element-factory.ts
|
|
2327
|
+
var DEFAULT_NOTE_FONT_SIZE = 18;
|
|
2328
|
+
function createStroke(input) {
|
|
2329
|
+
return {
|
|
2330
|
+
id: createId("stroke"),
|
|
2331
|
+
type: "stroke",
|
|
2332
|
+
position: input.position ?? { x: 0, y: 0 },
|
|
2333
|
+
zIndex: input.zIndex ?? 0,
|
|
2334
|
+
locked: input.locked ?? false,
|
|
2335
|
+
layerId: input.layerId ?? "",
|
|
2336
|
+
points: input.points,
|
|
2337
|
+
color: input.color ?? "#000000",
|
|
2338
|
+
width: input.width ?? 2,
|
|
2339
|
+
opacity: input.opacity ?? 1
|
|
2340
|
+
};
|
|
2341
|
+
}
|
|
2342
|
+
function createNote(input) {
|
|
2343
|
+
return {
|
|
2344
|
+
id: createId("note"),
|
|
2345
|
+
type: "note",
|
|
2346
|
+
position: input.position,
|
|
2347
|
+
zIndex: input.zIndex ?? 0,
|
|
2348
|
+
locked: input.locked ?? false,
|
|
2349
|
+
layerId: input.layerId ?? "",
|
|
2350
|
+
size: input.size ?? { w: 200, h: 100 },
|
|
2351
|
+
text: input.text ?? "",
|
|
2352
|
+
backgroundColor: input.backgroundColor ?? "#ffeb3b",
|
|
2353
|
+
textColor: input.textColor ?? "#000000",
|
|
2354
|
+
fontSize: input.fontSize ?? DEFAULT_NOTE_FONT_SIZE
|
|
2355
|
+
};
|
|
2356
|
+
}
|
|
2357
|
+
function createArrow(input) {
|
|
2358
|
+
const bend = input.bend ?? 0;
|
|
2359
|
+
const result = {
|
|
2360
|
+
id: createId("arrow"),
|
|
2361
|
+
type: "arrow",
|
|
2362
|
+
position: input.position ?? { x: 0, y: 0 },
|
|
2363
|
+
zIndex: input.zIndex ?? 0,
|
|
2364
|
+
locked: input.locked ?? false,
|
|
2365
|
+
layerId: input.layerId ?? "",
|
|
2366
|
+
from: input.from,
|
|
2367
|
+
to: input.to,
|
|
2368
|
+
bend,
|
|
2369
|
+
color: input.color ?? "#000000",
|
|
2370
|
+
width: input.width ?? 2,
|
|
2371
|
+
cachedControlPoint: getArrowControlPoint(input.from, input.to, bend)
|
|
2372
|
+
};
|
|
2373
|
+
if (input.fromBinding) result.fromBinding = input.fromBinding;
|
|
2374
|
+
if (input.toBinding) result.toBinding = input.toBinding;
|
|
2375
|
+
return result;
|
|
2376
|
+
}
|
|
2377
|
+
function createImage(input) {
|
|
2378
|
+
return {
|
|
2379
|
+
id: createId("image"),
|
|
2380
|
+
type: "image",
|
|
2381
|
+
position: input.position,
|
|
2382
|
+
zIndex: input.zIndex ?? 0,
|
|
2383
|
+
locked: input.locked ?? false,
|
|
2384
|
+
layerId: input.layerId ?? "",
|
|
2385
|
+
size: input.size,
|
|
2386
|
+
src: input.src
|
|
2387
|
+
};
|
|
2388
|
+
}
|
|
2389
|
+
function createHtmlElement(input) {
|
|
2390
|
+
const el = {
|
|
2391
|
+
id: createId("html"),
|
|
2392
|
+
type: "html",
|
|
2393
|
+
position: input.position,
|
|
2394
|
+
zIndex: input.zIndex ?? 0,
|
|
2395
|
+
locked: input.locked ?? false,
|
|
2396
|
+
layerId: input.layerId ?? "",
|
|
2397
|
+
size: input.size
|
|
2398
|
+
};
|
|
2399
|
+
if (input.domId) el.domId = input.domId;
|
|
2400
|
+
return el;
|
|
2401
|
+
}
|
|
2402
|
+
function createShape(input) {
|
|
2403
|
+
return {
|
|
2404
|
+
id: createId("shape"),
|
|
2405
|
+
type: "shape",
|
|
2406
|
+
position: input.position,
|
|
2407
|
+
zIndex: input.zIndex ?? 0,
|
|
2408
|
+
locked: input.locked ?? false,
|
|
2409
|
+
layerId: input.layerId ?? "",
|
|
2410
|
+
shape: input.shape ?? "rectangle",
|
|
2411
|
+
size: input.size,
|
|
2412
|
+
strokeColor: input.strokeColor ?? "#000000",
|
|
2413
|
+
strokeWidth: input.strokeWidth ?? 2,
|
|
2414
|
+
fillColor: input.fillColor ?? "none"
|
|
2415
|
+
};
|
|
2416
|
+
}
|
|
2417
|
+
function createGrid(input) {
|
|
2418
|
+
return {
|
|
2419
|
+
id: createId("grid"),
|
|
2420
|
+
type: "grid",
|
|
2421
|
+
position: input.position ?? { x: 0, y: 0 },
|
|
2422
|
+
zIndex: input.zIndex ?? 0,
|
|
2423
|
+
locked: input.locked ?? false,
|
|
2424
|
+
layerId: input.layerId ?? "",
|
|
2425
|
+
gridType: input.gridType ?? "square",
|
|
2426
|
+
hexOrientation: input.hexOrientation ?? "pointy",
|
|
2427
|
+
cellSize: input.cellSize ?? 40,
|
|
2428
|
+
strokeColor: input.strokeColor ?? "#000000",
|
|
2429
|
+
strokeWidth: input.strokeWidth ?? 1,
|
|
2430
|
+
opacity: input.opacity ?? 1
|
|
2431
|
+
};
|
|
2432
|
+
}
|
|
2433
|
+
function createText(input) {
|
|
2434
|
+
return {
|
|
2435
|
+
id: createId("text"),
|
|
2436
|
+
type: "text",
|
|
2437
|
+
position: input.position,
|
|
2438
|
+
zIndex: input.zIndex ?? 0,
|
|
2439
|
+
locked: input.locked ?? false,
|
|
2440
|
+
layerId: input.layerId ?? "",
|
|
2441
|
+
size: input.size ?? { w: 200, h: 28 },
|
|
2442
|
+
text: input.text ?? "",
|
|
2443
|
+
fontSize: input.fontSize ?? 16,
|
|
2444
|
+
color: input.color ?? "#1a1a1a",
|
|
2445
|
+
textAlign: input.textAlign ?? "left"
|
|
2446
|
+
};
|
|
2447
|
+
}
|
|
2448
|
+
function createTemplate(input) {
|
|
2449
|
+
return {
|
|
2450
|
+
id: createId("template"),
|
|
2451
|
+
type: "template",
|
|
2452
|
+
position: input.position,
|
|
2453
|
+
zIndex: input.zIndex ?? 0,
|
|
2454
|
+
locked: input.locked ?? false,
|
|
2455
|
+
layerId: input.layerId ?? "",
|
|
2456
|
+
templateShape: input.templateShape,
|
|
2457
|
+
radius: input.radius,
|
|
2458
|
+
angle: input.angle ?? 0,
|
|
2459
|
+
fillColor: input.fillColor ?? "rgba(255, 87, 34, 0.2)",
|
|
2460
|
+
strokeColor: input.strokeColor ?? "#FF5722",
|
|
2461
|
+
strokeWidth: input.strokeWidth ?? 2,
|
|
2462
|
+
opacity: input.opacity ?? 0.6,
|
|
2463
|
+
feetPerCell: input.feetPerCell,
|
|
2464
|
+
radiusFeet: input.radiusFeet
|
|
2465
|
+
};
|
|
2466
|
+
}
|
|
2467
|
+
|
|
2468
|
+
// src/elements/note-formatting.ts
|
|
2469
|
+
function toggleBold() {
|
|
2470
|
+
document.execCommand("bold");
|
|
2471
|
+
}
|
|
2472
|
+
function toggleItalic() {
|
|
2473
|
+
document.execCommand("italic");
|
|
2474
|
+
}
|
|
2475
|
+
function toggleUnderline() {
|
|
2476
|
+
document.execCommand("underline");
|
|
2477
|
+
}
|
|
2478
|
+
function toggleStrikethrough() {
|
|
2479
|
+
document.execCommand("strikeThrough");
|
|
2480
|
+
}
|
|
2481
|
+
function setFontSize(size) {
|
|
2482
|
+
const sel = window.getSelection();
|
|
2483
|
+
if (!sel || sel.rangeCount === 0) return;
|
|
2484
|
+
const range = sel.getRangeAt(0);
|
|
2485
|
+
if (range.collapsed) return;
|
|
2486
|
+
const span = document.createElement("span");
|
|
2487
|
+
span.style.fontSize = `${size}px`;
|
|
2488
|
+
try {
|
|
2489
|
+
range.surroundContents(span);
|
|
2490
|
+
} catch {
|
|
2491
|
+
span.appendChild(range.extractContents());
|
|
2492
|
+
range.insertNode(span);
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2495
|
+
function getActiveFormats() {
|
|
2496
|
+
const query = (cmd) => {
|
|
2497
|
+
try {
|
|
2498
|
+
return document.queryCommandState(cmd);
|
|
2499
|
+
} catch {
|
|
2500
|
+
return false;
|
|
2501
|
+
}
|
|
2502
|
+
};
|
|
2503
|
+
return {
|
|
2504
|
+
bold: query("bold"),
|
|
2505
|
+
italic: query("italic"),
|
|
2506
|
+
underline: query("underline"),
|
|
2507
|
+
strikethrough: query("strikeThrough")
|
|
2508
|
+
};
|
|
2509
|
+
}
|
|
2510
|
+
|
|
2511
|
+
// src/elements/note-toolbar.ts
|
|
2512
|
+
var TOOLBAR_HEIGHT = 32;
|
|
2513
|
+
var TOOLBAR_GAP = 4;
|
|
2514
|
+
var FORMAT_BUTTONS = [
|
|
2515
|
+
{ label: "B", format: "bold", command: "bold" },
|
|
2516
|
+
{ label: "I", format: "italic", command: "italic" },
|
|
2517
|
+
{ label: "U", format: "underline", command: "underline" },
|
|
2518
|
+
{ label: "S", format: "strikethrough", command: "strikeThrough" }
|
|
2519
|
+
];
|
|
2520
|
+
var DEFAULT_FONT_SIZE_PRESETS = [
|
|
2521
|
+
{ label: "Small", size: 14 },
|
|
2522
|
+
{ label: "Normal", size: 18 },
|
|
2523
|
+
{ label: "Large", size: 24 },
|
|
2524
|
+
{ label: "Heading", size: 32 }
|
|
2525
|
+
];
|
|
2526
|
+
var NoteToolbar = class {
|
|
2527
|
+
el = null;
|
|
2528
|
+
anchor = null;
|
|
2529
|
+
selectionListener = null;
|
|
2530
|
+
fontSizePresets;
|
|
2531
|
+
constructor(fontSizePresets) {
|
|
2532
|
+
this.fontSizePresets = fontSizePresets ?? DEFAULT_FONT_SIZE_PRESETS;
|
|
2533
|
+
}
|
|
2534
|
+
show(anchor) {
|
|
2535
|
+
this.hide();
|
|
2536
|
+
this.anchor = anchor;
|
|
2537
|
+
this.el = this.createToolbarElement();
|
|
2538
|
+
document.body.appendChild(this.el);
|
|
2539
|
+
this.positionToolbar(anchor);
|
|
2540
|
+
this.selectionListener = () => this.updateActiveStates();
|
|
2541
|
+
document.addEventListener("selectionchange", this.selectionListener);
|
|
2542
|
+
}
|
|
2543
|
+
hide() {
|
|
2544
|
+
if (this.selectionListener) {
|
|
2545
|
+
document.removeEventListener("selectionchange", this.selectionListener);
|
|
2546
|
+
this.selectionListener = null;
|
|
2547
|
+
}
|
|
2548
|
+
if (this.el) {
|
|
2549
|
+
this.el.remove();
|
|
2550
|
+
this.el = null;
|
|
2551
|
+
}
|
|
2552
|
+
this.anchor = null;
|
|
2553
|
+
}
|
|
2554
|
+
getElement() {
|
|
2555
|
+
return this.el;
|
|
2556
|
+
}
|
|
2557
|
+
updatePosition(anchor) {
|
|
2558
|
+
if (this.el) {
|
|
2559
|
+
this.positionToolbar(anchor);
|
|
2560
|
+
}
|
|
2561
|
+
}
|
|
2562
|
+
createToolbarElement() {
|
|
2563
|
+
const toolbar = document.createElement("div");
|
|
2564
|
+
toolbar.dataset["noteToolbar"] = "";
|
|
2565
|
+
Object.assign(toolbar.style, {
|
|
2566
|
+
position: "fixed",
|
|
2567
|
+
display: "flex",
|
|
2568
|
+
alignItems: "center",
|
|
2569
|
+
gap: "2px",
|
|
2570
|
+
padding: "2px 4px",
|
|
2571
|
+
background: "#fff",
|
|
2572
|
+
border: "1px solid #ccc",
|
|
2573
|
+
borderRadius: "4px",
|
|
2574
|
+
boxShadow: "0 2px 8px rgba(0,0,0,0.15)",
|
|
2575
|
+
zIndex: "10000",
|
|
2576
|
+
height: `${TOOLBAR_HEIGHT}px`,
|
|
2577
|
+
userSelect: "none"
|
|
2578
|
+
});
|
|
2579
|
+
for (const btn of FORMAT_BUTTONS) {
|
|
2580
|
+
toolbar.appendChild(this.createFormatButton(btn));
|
|
2581
|
+
}
|
|
2582
|
+
toolbar.appendChild(this.createFontSizeSelect());
|
|
2583
|
+
return toolbar;
|
|
2584
|
+
}
|
|
2585
|
+
createFormatButton(config) {
|
|
2586
|
+
const btn = document.createElement("button");
|
|
2587
|
+
btn.dataset["format"] = config.format;
|
|
2588
|
+
btn.textContent = config.label;
|
|
2589
|
+
Object.assign(btn.style, {
|
|
2590
|
+
border: "1px solid transparent",
|
|
2591
|
+
borderRadius: "3px",
|
|
2592
|
+
background: "none",
|
|
2593
|
+
cursor: "pointer",
|
|
2594
|
+
padding: "2px 6px",
|
|
2595
|
+
fontSize: "13px",
|
|
2596
|
+
fontWeight: config.format === "bold" ? "bold" : "normal",
|
|
2597
|
+
fontStyle: config.format === "italic" ? "italic" : "normal",
|
|
2598
|
+
textDecoration: config.format === "underline" ? "underline" : config.format === "strikethrough" ? "line-through" : "none",
|
|
2599
|
+
minWidth: "24px",
|
|
2600
|
+
height: "24px",
|
|
2601
|
+
lineHeight: "24px"
|
|
2602
|
+
});
|
|
2603
|
+
btn.addEventListener("pointerdown", (e) => {
|
|
2604
|
+
e.preventDefault();
|
|
2605
|
+
document.execCommand(config.command);
|
|
2606
|
+
this.updateActiveStates();
|
|
2607
|
+
});
|
|
2608
|
+
return btn;
|
|
2609
|
+
}
|
|
2610
|
+
createFontSizeSelect() {
|
|
2611
|
+
const select = document.createElement("select");
|
|
2612
|
+
Object.assign(select.style, {
|
|
2613
|
+
border: "1px solid #ccc",
|
|
2614
|
+
borderRadius: "3px",
|
|
2615
|
+
background: "#fff",
|
|
2616
|
+
cursor: "pointer",
|
|
2617
|
+
padding: "2px",
|
|
2618
|
+
fontSize: "12px",
|
|
2619
|
+
height: "24px",
|
|
2620
|
+
marginLeft: "4px"
|
|
2621
|
+
});
|
|
2622
|
+
for (const preset of this.fontSizePresets) {
|
|
2623
|
+
const option = document.createElement("option");
|
|
2624
|
+
option.value = String(preset.size);
|
|
2625
|
+
option.textContent = preset.label;
|
|
2626
|
+
select.appendChild(option);
|
|
2627
|
+
}
|
|
2628
|
+
select.value = String(DEFAULT_NOTE_FONT_SIZE);
|
|
2629
|
+
select.addEventListener("pointerdown", (e) => {
|
|
2630
|
+
e.stopPropagation();
|
|
2631
|
+
});
|
|
2632
|
+
select.addEventListener("change", () => {
|
|
2633
|
+
setFontSize(Number(select.value));
|
|
2634
|
+
this.updateActiveStates();
|
|
2635
|
+
this.anchor?.focus();
|
|
2636
|
+
});
|
|
2637
|
+
return select;
|
|
2638
|
+
}
|
|
2639
|
+
positionToolbar(anchor) {
|
|
2640
|
+
if (!this.el) return;
|
|
2641
|
+
const rect = anchor.getBoundingClientRect();
|
|
2642
|
+
const toolbarWidth = this.el.offsetWidth || 200;
|
|
2643
|
+
let top = rect.top - TOOLBAR_HEIGHT - TOOLBAR_GAP;
|
|
2644
|
+
if (top < 0) {
|
|
2645
|
+
top = rect.bottom + TOOLBAR_GAP;
|
|
2646
|
+
}
|
|
2647
|
+
let left = rect.left + (rect.width - toolbarWidth) / 2;
|
|
2648
|
+
left = Math.max(4, left);
|
|
2649
|
+
Object.assign(this.el.style, {
|
|
2650
|
+
top: `${top}px`,
|
|
2651
|
+
left: `${left}px`
|
|
2652
|
+
});
|
|
2653
|
+
}
|
|
2654
|
+
updateActiveStates() {
|
|
2655
|
+
if (!this.el) return;
|
|
2656
|
+
const active = getActiveFormats();
|
|
2657
|
+
for (const config of FORMAT_BUTTONS) {
|
|
2658
|
+
const btn = this.el.querySelector(`[data-format="${config.format}"]`);
|
|
2659
|
+
if (!btn) continue;
|
|
2660
|
+
const isActive = active[config.format] ?? false;
|
|
2661
|
+
btn.style.background = isActive ? "#e0e0e0" : "none";
|
|
2662
|
+
btn.style.borderColor = isActive ? "#bbb" : "transparent";
|
|
2663
|
+
}
|
|
2664
|
+
}
|
|
2665
|
+
};
|
|
2666
|
+
|
|
2193
2667
|
// src/elements/note-editor.ts
|
|
2668
|
+
var FORMAT_SHORTCUTS = {
|
|
2669
|
+
b: toggleBold,
|
|
2670
|
+
i: toggleItalic,
|
|
2671
|
+
u: toggleUnderline
|
|
2672
|
+
};
|
|
2194
2673
|
var NoteEditor = class {
|
|
2195
2674
|
editingId = null;
|
|
2196
2675
|
editingNode = null;
|
|
@@ -2199,6 +2678,10 @@ var NoteEditor = class {
|
|
|
2199
2678
|
pointerHandler = null;
|
|
2200
2679
|
pendingEditId = null;
|
|
2201
2680
|
onStopCallback = null;
|
|
2681
|
+
toolbar;
|
|
2682
|
+
constructor(options) {
|
|
2683
|
+
this.toolbar = options?.toolbar === false ? null : new NoteToolbar(options?.fontSizePresets);
|
|
2684
|
+
}
|
|
2202
2685
|
get isEditing() {
|
|
2203
2686
|
return this.editingId !== null;
|
|
2204
2687
|
}
|
|
@@ -2223,13 +2706,6 @@ var NoteEditor = class {
|
|
|
2223
2706
|
stopEditing(store) {
|
|
2224
2707
|
this.pendingEditId = null;
|
|
2225
2708
|
if (!this.editingId || !this.editingNode) return;
|
|
2226
|
-
const text = this.editingNode.textContent ?? "";
|
|
2227
|
-
store.update(this.editingId, { text });
|
|
2228
|
-
this.editingNode.contentEditable = "false";
|
|
2229
|
-
Object.assign(this.editingNode.style, {
|
|
2230
|
-
userSelect: "none",
|
|
2231
|
-
cursor: "default"
|
|
2232
|
-
});
|
|
2233
2709
|
if (this.blurHandler) {
|
|
2234
2710
|
this.editingNode.removeEventListener("blur", this.blurHandler);
|
|
2235
2711
|
}
|
|
@@ -2239,6 +2715,14 @@ var NoteEditor = class {
|
|
|
2239
2715
|
if (this.pointerHandler) {
|
|
2240
2716
|
this.editingNode.removeEventListener("pointerdown", this.pointerHandler);
|
|
2241
2717
|
}
|
|
2718
|
+
const text = sanitizeNoteHtml(this.editingNode.innerHTML);
|
|
2719
|
+
store.update(this.editingId, { text });
|
|
2720
|
+
this.editingNode.contentEditable = "false";
|
|
2721
|
+
Object.assign(this.editingNode.style, {
|
|
2722
|
+
userSelect: "none",
|
|
2723
|
+
cursor: "default"
|
|
2724
|
+
});
|
|
2725
|
+
this.toolbar?.hide();
|
|
2242
2726
|
if (this.editingId && this.onStopCallback) {
|
|
2243
2727
|
this.onStopCallback(this.editingId);
|
|
2244
2728
|
}
|
|
@@ -2254,6 +2738,11 @@ var NoteEditor = class {
|
|
|
2254
2738
|
this.stopEditing(store);
|
|
2255
2739
|
}
|
|
2256
2740
|
}
|
|
2741
|
+
updateToolbarPosition() {
|
|
2742
|
+
if (this.editingNode) {
|
|
2743
|
+
this.toolbar?.updatePosition(this.editingNode);
|
|
2744
|
+
}
|
|
2745
|
+
}
|
|
2257
2746
|
activateEditing(node, elementId, store) {
|
|
2258
2747
|
this.editingId = elementId;
|
|
2259
2748
|
this.editingNode = node;
|
|
@@ -2272,8 +2761,21 @@ var NoteEditor = class {
|
|
|
2272
2761
|
selection.removeAllRanges();
|
|
2273
2762
|
selection.addRange(range);
|
|
2274
2763
|
}
|
|
2275
|
-
this.
|
|
2764
|
+
this.toolbar?.show(node);
|
|
2765
|
+
this.blurHandler = (e) => {
|
|
2766
|
+
const related = e.relatedTarget;
|
|
2767
|
+
if (related && this.toolbar?.getElement()?.contains(related)) return;
|
|
2768
|
+
this.stopEditing(store);
|
|
2769
|
+
};
|
|
2276
2770
|
this.keyHandler = (e) => {
|
|
2771
|
+
if ((e.ctrlKey || e.metaKey) && !e.shiftKey && !e.altKey) {
|
|
2772
|
+
const action = FORMAT_SHORTCUTS[e.key.toLowerCase()];
|
|
2773
|
+
if (action) {
|
|
2774
|
+
e.preventDefault();
|
|
2775
|
+
action();
|
|
2776
|
+
return;
|
|
2777
|
+
}
|
|
2778
|
+
}
|
|
2277
2779
|
if (e.key === "Escape") {
|
|
2278
2780
|
node.blur();
|
|
2279
2781
|
}
|
|
@@ -2525,150 +3027,86 @@ var HistoryRecorder = class {
|
|
|
2525
3027
|
}
|
|
2526
3028
|
};
|
|
2527
3029
|
|
|
2528
|
-
// src/
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
locked: input.locked ?? false,
|
|
2556
|
-
layerId: input.layerId ?? "",
|
|
2557
|
-
size: input.size ?? { w: 200, h: 100 },
|
|
2558
|
-
text: input.text ?? "",
|
|
2559
|
-
backgroundColor: input.backgroundColor ?? "#ffeb3b",
|
|
2560
|
-
textColor: input.textColor ?? "#000000"
|
|
2561
|
-
};
|
|
2562
|
-
}
|
|
2563
|
-
function createArrow(input) {
|
|
2564
|
-
const bend = input.bend ?? 0;
|
|
2565
|
-
const result = {
|
|
2566
|
-
id: createId("arrow"),
|
|
2567
|
-
type: "arrow",
|
|
2568
|
-
position: input.position ?? { x: 0, y: 0 },
|
|
2569
|
-
zIndex: input.zIndex ?? 0,
|
|
2570
|
-
locked: input.locked ?? false,
|
|
2571
|
-
layerId: input.layerId ?? "",
|
|
2572
|
-
from: input.from,
|
|
2573
|
-
to: input.to,
|
|
2574
|
-
bend,
|
|
2575
|
-
color: input.color ?? "#000000",
|
|
2576
|
-
width: input.width ?? 2,
|
|
2577
|
-
cachedControlPoint: getArrowControlPoint(input.from, input.to, bend)
|
|
2578
|
-
};
|
|
2579
|
-
if (input.fromBinding) result.fromBinding = input.fromBinding;
|
|
2580
|
-
if (input.toBinding) result.toBinding = input.toBinding;
|
|
2581
|
-
return result;
|
|
2582
|
-
}
|
|
2583
|
-
function createImage(input) {
|
|
2584
|
-
return {
|
|
2585
|
-
id: createId("image"),
|
|
2586
|
-
type: "image",
|
|
2587
|
-
position: input.position,
|
|
2588
|
-
zIndex: input.zIndex ?? 0,
|
|
2589
|
-
locked: input.locked ?? false,
|
|
2590
|
-
layerId: input.layerId ?? "",
|
|
2591
|
-
size: input.size,
|
|
2592
|
-
src: input.src
|
|
2593
|
-
};
|
|
2594
|
-
}
|
|
2595
|
-
function createHtmlElement(input) {
|
|
2596
|
-
const el = {
|
|
2597
|
-
id: createId("html"),
|
|
2598
|
-
type: "html",
|
|
2599
|
-
position: input.position,
|
|
2600
|
-
zIndex: input.zIndex ?? 0,
|
|
2601
|
-
locked: input.locked ?? false,
|
|
2602
|
-
layerId: input.layerId ?? "",
|
|
2603
|
-
size: input.size
|
|
2604
|
-
};
|
|
2605
|
-
if (input.domId) el.domId = input.domId;
|
|
2606
|
-
return el;
|
|
2607
|
-
}
|
|
2608
|
-
function createShape(input) {
|
|
2609
|
-
return {
|
|
2610
|
-
id: createId("shape"),
|
|
2611
|
-
type: "shape",
|
|
2612
|
-
position: input.position,
|
|
2613
|
-
zIndex: input.zIndex ?? 0,
|
|
2614
|
-
locked: input.locked ?? false,
|
|
2615
|
-
layerId: input.layerId ?? "",
|
|
2616
|
-
shape: input.shape ?? "rectangle",
|
|
2617
|
-
size: input.size,
|
|
2618
|
-
strokeColor: input.strokeColor ?? "#000000",
|
|
2619
|
-
strokeWidth: input.strokeWidth ?? 2,
|
|
2620
|
-
fillColor: input.fillColor ?? "none"
|
|
2621
|
-
};
|
|
2622
|
-
}
|
|
2623
|
-
function createGrid(input) {
|
|
2624
|
-
return {
|
|
2625
|
-
id: createId("grid"),
|
|
2626
|
-
type: "grid",
|
|
2627
|
-
position: input.position ?? { x: 0, y: 0 },
|
|
2628
|
-
zIndex: input.zIndex ?? 0,
|
|
2629
|
-
locked: input.locked ?? false,
|
|
2630
|
-
layerId: input.layerId ?? "",
|
|
2631
|
-
gridType: input.gridType ?? "square",
|
|
2632
|
-
hexOrientation: input.hexOrientation ?? "pointy",
|
|
2633
|
-
cellSize: input.cellSize ?? 40,
|
|
2634
|
-
strokeColor: input.strokeColor ?? "#000000",
|
|
2635
|
-
strokeWidth: input.strokeWidth ?? 1,
|
|
2636
|
-
opacity: input.opacity ?? 1
|
|
2637
|
-
};
|
|
3030
|
+
// src/canvas/note-canvas-renderer.ts
|
|
3031
|
+
function renderNoteOnCanvas(ctx, note) {
|
|
3032
|
+
const { x, y } = note.position;
|
|
3033
|
+
const { w, h } = note.size;
|
|
3034
|
+
const r = 4;
|
|
3035
|
+
const pad = 8;
|
|
3036
|
+
const baseFontSize = note.fontSize ?? DEFAULT_NOTE_FONT_SIZE;
|
|
3037
|
+
ctx.save();
|
|
3038
|
+
ctx.fillStyle = note.backgroundColor;
|
|
3039
|
+
ctx.beginPath();
|
|
3040
|
+
ctx.moveTo(x + r, y);
|
|
3041
|
+
ctx.lineTo(x + w - r, y);
|
|
3042
|
+
ctx.arcTo(x + w, y, x + w, y + r, r);
|
|
3043
|
+
ctx.lineTo(x + w, y + h - r);
|
|
3044
|
+
ctx.arcTo(x + w, y + h, x + w - r, y + h, r);
|
|
3045
|
+
ctx.lineTo(x + r, y + h);
|
|
3046
|
+
ctx.arcTo(x, y + h, x, y + h - r, r);
|
|
3047
|
+
ctx.lineTo(x, y + r);
|
|
3048
|
+
ctx.arcTo(x, y, x + r, y, r);
|
|
3049
|
+
ctx.closePath();
|
|
3050
|
+
ctx.fill();
|
|
3051
|
+
if (note.text) {
|
|
3052
|
+
ctx.fillStyle = note.textColor;
|
|
3053
|
+
const runs = parseStyledRuns(note.text, baseFontSize);
|
|
3054
|
+
renderStyledRuns(ctx, runs, x + pad, y + pad, w - pad * 2);
|
|
3055
|
+
}
|
|
3056
|
+
ctx.restore();
|
|
2638
3057
|
}
|
|
2639
|
-
function
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
position: input.position,
|
|
2644
|
-
zIndex: input.zIndex ?? 0,
|
|
2645
|
-
locked: input.locked ?? false,
|
|
2646
|
-
layerId: input.layerId ?? "",
|
|
2647
|
-
size: input.size ?? { w: 200, h: 28 },
|
|
2648
|
-
text: input.text ?? "",
|
|
2649
|
-
fontSize: input.fontSize ?? 16,
|
|
2650
|
-
color: input.color ?? "#1a1a1a",
|
|
2651
|
-
textAlign: input.textAlign ?? "left"
|
|
2652
|
-
};
|
|
3058
|
+
function buildFontString(run) {
|
|
3059
|
+
const style = run.italic ? "italic" : "normal";
|
|
3060
|
+
const weight = run.bold ? "bold" : "normal";
|
|
3061
|
+
return `${style} ${weight} ${run.fontSize}px system-ui, sans-serif`;
|
|
2653
3062
|
}
|
|
2654
|
-
function
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
3063
|
+
function renderStyledRuns(ctx, runs, startX, startY, maxWidth) {
|
|
3064
|
+
ctx.textBaseline = "top";
|
|
3065
|
+
let cursorX = startX;
|
|
3066
|
+
let cursorY = startY;
|
|
3067
|
+
let lineHeight = 0;
|
|
3068
|
+
for (const run of runs) {
|
|
3069
|
+
ctx.font = buildFontString(run);
|
|
3070
|
+
const runLineHeight = run.fontSize * 1.3;
|
|
3071
|
+
lineHeight = Math.max(lineHeight, runLineHeight);
|
|
3072
|
+
const words = run.text.split(/(\n| )/);
|
|
3073
|
+
for (const word of words) {
|
|
3074
|
+
if (word === "\n") {
|
|
3075
|
+
cursorX = startX;
|
|
3076
|
+
cursorY += lineHeight;
|
|
3077
|
+
lineHeight = runLineHeight;
|
|
3078
|
+
continue;
|
|
3079
|
+
}
|
|
3080
|
+
if (word === " ") {
|
|
3081
|
+
const spaceWidth = ctx.measureText(" ").width;
|
|
3082
|
+
if (cursorX + spaceWidth > startX + maxWidth && cursorX > startX) {
|
|
3083
|
+
cursorX = startX;
|
|
3084
|
+
cursorY += lineHeight;
|
|
3085
|
+
lineHeight = runLineHeight;
|
|
3086
|
+
} else {
|
|
3087
|
+
cursorX += spaceWidth;
|
|
3088
|
+
}
|
|
3089
|
+
continue;
|
|
3090
|
+
}
|
|
3091
|
+
if (!word) continue;
|
|
3092
|
+
const metrics = ctx.measureText(word);
|
|
3093
|
+
if (cursorX + metrics.width > startX + maxWidth && cursorX > startX) {
|
|
3094
|
+
cursorX = startX;
|
|
3095
|
+
cursorY += lineHeight;
|
|
3096
|
+
lineHeight = runLineHeight;
|
|
3097
|
+
}
|
|
3098
|
+
ctx.fillText(word, cursorX, cursorY);
|
|
3099
|
+
if (run.underline) {
|
|
3100
|
+
const underY = cursorY + run.fontSize + 1;
|
|
3101
|
+
ctx.fillRect(cursorX, underY, metrics.width, 1);
|
|
3102
|
+
}
|
|
3103
|
+
if (run.strikethrough) {
|
|
3104
|
+
const strikeY = cursorY + run.fontSize * 0.55;
|
|
3105
|
+
ctx.fillRect(cursorX, strikeY, metrics.width, 1);
|
|
3106
|
+
}
|
|
3107
|
+
cursorX += metrics.width;
|
|
3108
|
+
}
|
|
3109
|
+
}
|
|
2672
3110
|
}
|
|
2673
3111
|
|
|
2674
3112
|
// src/canvas/export-image.ts
|
|
@@ -2747,33 +3185,6 @@ function computeBounds(elements, padding) {
|
|
|
2747
3185
|
h: maxY - minY + padding * 2
|
|
2748
3186
|
};
|
|
2749
3187
|
}
|
|
2750
|
-
function renderNoteOnCanvas(ctx, note) {
|
|
2751
|
-
const { x, y } = note.position;
|
|
2752
|
-
const { w, h } = note.size;
|
|
2753
|
-
const r = 4;
|
|
2754
|
-
const pad = 8;
|
|
2755
|
-
ctx.save();
|
|
2756
|
-
ctx.fillStyle = note.backgroundColor;
|
|
2757
|
-
ctx.beginPath();
|
|
2758
|
-
ctx.moveTo(x + r, y);
|
|
2759
|
-
ctx.lineTo(x + w - r, y);
|
|
2760
|
-
ctx.arcTo(x + w, y, x + w, y + r, r);
|
|
2761
|
-
ctx.lineTo(x + w, y + h - r);
|
|
2762
|
-
ctx.arcTo(x + w, y + h, x + w - r, y + h, r);
|
|
2763
|
-
ctx.lineTo(x + r, y + h);
|
|
2764
|
-
ctx.arcTo(x, y + h, x, y + h - r, r);
|
|
2765
|
-
ctx.lineTo(x, y + r);
|
|
2766
|
-
ctx.arcTo(x, y, x + r, y, r);
|
|
2767
|
-
ctx.closePath();
|
|
2768
|
-
ctx.fill();
|
|
2769
|
-
if (note.text) {
|
|
2770
|
-
ctx.fillStyle = note.textColor;
|
|
2771
|
-
ctx.font = "14px system-ui, sans-serif";
|
|
2772
|
-
ctx.textBaseline = "top";
|
|
2773
|
-
wrapText(ctx, note.text, x + pad, y + pad, w - pad * 2, 18);
|
|
2774
|
-
}
|
|
2775
|
-
ctx.restore();
|
|
2776
|
-
}
|
|
2777
3188
|
function renderTextOnCanvas(ctx, text) {
|
|
2778
3189
|
if (!text.text) return;
|
|
2779
3190
|
ctx.save();
|
|
@@ -2798,25 +3209,6 @@ function renderTextOnCanvas(ctx, text) {
|
|
|
2798
3209
|
}
|
|
2799
3210
|
ctx.restore();
|
|
2800
3211
|
}
|
|
2801
|
-
function wrapText(ctx, text, x, y, maxWidth, lineHeight) {
|
|
2802
|
-
const words = text.split(" ");
|
|
2803
|
-
let line = "";
|
|
2804
|
-
let offsetY = 0;
|
|
2805
|
-
for (const word of words) {
|
|
2806
|
-
const testLine = line ? `${line} ${word}` : word;
|
|
2807
|
-
const metrics = ctx.measureText(testLine);
|
|
2808
|
-
if (metrics.width > maxWidth && line) {
|
|
2809
|
-
ctx.fillText(line, x, y + offsetY);
|
|
2810
|
-
line = word;
|
|
2811
|
-
offsetY += lineHeight;
|
|
2812
|
-
} else {
|
|
2813
|
-
line = testLine;
|
|
2814
|
-
}
|
|
2815
|
-
}
|
|
2816
|
-
if (line) {
|
|
2817
|
-
ctx.fillText(line, x, y + offsetY);
|
|
2818
|
-
}
|
|
2819
|
-
}
|
|
2820
3212
|
function renderGridForBounds(ctx, grid, bounds) {
|
|
2821
3213
|
const visibleBounds = {
|
|
2822
3214
|
minX: bounds.x,
|
|
@@ -3215,13 +3607,13 @@ var DomNodeManager = class {
|
|
|
3215
3607
|
padding: "8px",
|
|
3216
3608
|
borderRadius: "4px",
|
|
3217
3609
|
boxShadow: "0 2px 8px rgba(0,0,0,0.15)",
|
|
3218
|
-
fontSize:
|
|
3610
|
+
fontSize: `${element.fontSize ?? DEFAULT_NOTE_FONT_SIZE}px`,
|
|
3219
3611
|
overflow: "hidden",
|
|
3220
3612
|
cursor: "default",
|
|
3221
3613
|
userSelect: "none",
|
|
3222
3614
|
wordWrap: "break-word"
|
|
3223
3615
|
});
|
|
3224
|
-
node.
|
|
3616
|
+
node.innerHTML = element.text || "";
|
|
3225
3617
|
node.addEventListener("dblclick", (e) => {
|
|
3226
3618
|
e.stopPropagation();
|
|
3227
3619
|
const id = node.dataset["elementId"];
|
|
@@ -3229,11 +3621,13 @@ var DomNodeManager = class {
|
|
|
3229
3621
|
});
|
|
3230
3622
|
}
|
|
3231
3623
|
if (!this.isEditingElement(element.id)) {
|
|
3232
|
-
|
|
3233
|
-
|
|
3624
|
+
const text = element.text || "";
|
|
3625
|
+
if (node.innerHTML !== text) {
|
|
3626
|
+
node.innerHTML = text;
|
|
3234
3627
|
}
|
|
3235
3628
|
node.style.backgroundColor = element.backgroundColor;
|
|
3236
3629
|
node.style.color = element.textColor;
|
|
3630
|
+
node.style.fontSize = `${element.fontSize ?? DEFAULT_NOTE_FONT_SIZE}px`;
|
|
3237
3631
|
}
|
|
3238
3632
|
}
|
|
3239
3633
|
if (element.type === "html" && !node.dataset["initialized"]) {
|
|
@@ -3664,7 +4058,10 @@ var Viewport = class {
|
|
|
3664
4058
|
this.renderLoop.markAllLayersDirty();
|
|
3665
4059
|
this.requestRender();
|
|
3666
4060
|
});
|
|
3667
|
-
this.noteEditor = new NoteEditor(
|
|
4061
|
+
this.noteEditor = new NoteEditor({
|
|
4062
|
+
fontSizePresets: options.fontSizePresets,
|
|
4063
|
+
toolbar: options.toolbar
|
|
4064
|
+
});
|
|
3668
4065
|
this.noteEditor.setOnStop((id) => this.onTextEditStop(id));
|
|
3669
4066
|
this.history = new HistoryStack();
|
|
3670
4067
|
this.historyRecorder = new HistoryRecorder(this.store, this.history);
|
|
@@ -3720,6 +4117,7 @@ var Viewport = class {
|
|
|
3720
4117
|
});
|
|
3721
4118
|
this.unsubCamera = this.camera.onChange(() => {
|
|
3722
4119
|
this.applyCameraTransform();
|
|
4120
|
+
this.noteEditor.updateToolbarPosition();
|
|
3723
4121
|
this.requestRender();
|
|
3724
4122
|
});
|
|
3725
4123
|
this.unsubStore = [
|
|
@@ -3784,6 +4182,7 @@ var Viewport = class {
|
|
|
3784
4182
|
renderLoop;
|
|
3785
4183
|
domNodeManager;
|
|
3786
4184
|
interactMode;
|
|
4185
|
+
gridChangeListeners = /* @__PURE__ */ new Set();
|
|
3787
4186
|
get ctx() {
|
|
3788
4187
|
return this.canvasEl.getContext("2d");
|
|
3789
4188
|
}
|
|
@@ -3888,6 +4287,22 @@ var Viewport = class {
|
|
|
3888
4287
|
this.historyRecorder.commit();
|
|
3889
4288
|
this.requestRender();
|
|
3890
4289
|
}
|
|
4290
|
+
getGridInfo() {
|
|
4291
|
+
const grid = this.store.getElementsByType("grid")[0];
|
|
4292
|
+
if (!grid) return null;
|
|
4293
|
+
return {
|
|
4294
|
+
gridType: grid.gridType,
|
|
4295
|
+
hexOrientation: grid.hexOrientation,
|
|
4296
|
+
cellSize: grid.cellSize,
|
|
4297
|
+
cellRadius: grid.gridType === "hex" ? grid.cellSize : grid.cellSize / 2
|
|
4298
|
+
};
|
|
4299
|
+
}
|
|
4300
|
+
onGridChange(listener) {
|
|
4301
|
+
this.gridChangeListeners.add(listener);
|
|
4302
|
+
return () => {
|
|
4303
|
+
this.gridChangeListeners.delete(listener);
|
|
4304
|
+
};
|
|
4305
|
+
}
|
|
3891
4306
|
getRenderStats() {
|
|
3892
4307
|
return this.renderLoop.getStats();
|
|
3893
4308
|
}
|
|
@@ -4088,6 +4503,13 @@ var Viewport = class {
|
|
|
4088
4503
|
this.toolContext.gridType = void 0;
|
|
4089
4504
|
this.toolContext.hexOrientation = void 0;
|
|
4090
4505
|
}
|
|
4506
|
+
this.notifyGridChangeListeners();
|
|
4507
|
+
}
|
|
4508
|
+
notifyGridChangeListeners() {
|
|
4509
|
+
const info = this.getGridInfo();
|
|
4510
|
+
for (const listener of this.gridChangeListeners) {
|
|
4511
|
+
listener(info);
|
|
4512
|
+
}
|
|
4091
4513
|
}
|
|
4092
4514
|
observeResize() {
|
|
4093
4515
|
if (typeof ResizeObserver === "undefined") return;
|
|
@@ -5069,17 +5491,20 @@ var NoteTool = class {
|
|
|
5069
5491
|
backgroundColor;
|
|
5070
5492
|
textColor;
|
|
5071
5493
|
size;
|
|
5494
|
+
fontSize;
|
|
5072
5495
|
optionListeners = /* @__PURE__ */ new Set();
|
|
5073
5496
|
constructor(options = {}) {
|
|
5074
5497
|
this.backgroundColor = options.backgroundColor ?? "#ffeb3b";
|
|
5075
5498
|
this.textColor = options.textColor ?? "#000000";
|
|
5076
5499
|
this.size = options.size ?? { w: 200, h: 100 };
|
|
5500
|
+
this.fontSize = options.fontSize ?? DEFAULT_NOTE_FONT_SIZE;
|
|
5077
5501
|
}
|
|
5078
5502
|
getOptions() {
|
|
5079
5503
|
return {
|
|
5080
5504
|
backgroundColor: this.backgroundColor,
|
|
5081
5505
|
textColor: this.textColor,
|
|
5082
|
-
size: { ...this.size }
|
|
5506
|
+
size: { ...this.size },
|
|
5507
|
+
fontSize: this.fontSize
|
|
5083
5508
|
};
|
|
5084
5509
|
}
|
|
5085
5510
|
onOptionsChange(listener) {
|
|
@@ -5090,6 +5515,7 @@ var NoteTool = class {
|
|
|
5090
5515
|
if (options.backgroundColor !== void 0) this.backgroundColor = options.backgroundColor;
|
|
5091
5516
|
if (options.textColor !== void 0) this.textColor = options.textColor;
|
|
5092
5517
|
if (options.size !== void 0) this.size = options.size;
|
|
5518
|
+
if (options.fontSize !== void 0) this.fontSize = options.fontSize;
|
|
5093
5519
|
this.notifyOptionsChange();
|
|
5094
5520
|
}
|
|
5095
5521
|
notifyOptionsChange() {
|
|
@@ -5107,6 +5533,7 @@ var NoteTool = class {
|
|
|
5107
5533
|
size: { ...this.size },
|
|
5108
5534
|
backgroundColor: this.backgroundColor,
|
|
5109
5535
|
textColor: this.textColor,
|
|
5536
|
+
fontSize: this.fontSize,
|
|
5110
5537
|
layerId: ctx.activeLayerId ?? ""
|
|
5111
5538
|
});
|
|
5112
5539
|
ctx.store.add(note);
|
|
@@ -5774,7 +6201,7 @@ var UpdateLayerCommand = class {
|
|
|
5774
6201
|
};
|
|
5775
6202
|
|
|
5776
6203
|
// src/index.ts
|
|
5777
|
-
var VERSION = "0.
|
|
6204
|
+
var VERSION = "0.11.0";
|
|
5778
6205
|
export {
|
|
5779
6206
|
AddElementCommand,
|
|
5780
6207
|
ArrowTool,
|
|
@@ -5783,6 +6210,8 @@ export {
|
|
|
5783
6210
|
BatchCommand,
|
|
5784
6211
|
Camera,
|
|
5785
6212
|
CreateLayerCommand,
|
|
6213
|
+
DEFAULT_FONT_SIZE_PRESETS,
|
|
6214
|
+
DEFAULT_NOTE_FONT_SIZE,
|
|
5786
6215
|
ElementRenderer,
|
|
5787
6216
|
ElementStore,
|
|
5788
6217
|
EraserTool,
|
|
@@ -5796,6 +6225,7 @@ export {
|
|
|
5796
6225
|
MeasureTool,
|
|
5797
6226
|
NoteEditor,
|
|
5798
6227
|
NoteTool,
|
|
6228
|
+
NoteToolbar,
|
|
5799
6229
|
PencilTool,
|
|
5800
6230
|
Quadtree,
|
|
5801
6231
|
RemoveElementCommand,
|
|
@@ -5826,6 +6256,7 @@ export {
|
|
|
5826
6256
|
exportState,
|
|
5827
6257
|
findBindTarget,
|
|
5828
6258
|
findBoundArrows,
|
|
6259
|
+
getActiveFormats,
|
|
5829
6260
|
getArrowBounds,
|
|
5830
6261
|
getArrowControlPoint,
|
|
5831
6262
|
getArrowMidpoint,
|
|
@@ -5842,9 +6273,15 @@ export {
|
|
|
5842
6273
|
isBindable,
|
|
5843
6274
|
isNearBezier,
|
|
5844
6275
|
parseState,
|
|
6276
|
+
sanitizeNoteHtml,
|
|
6277
|
+
setFontSize,
|
|
5845
6278
|
smartSnap,
|
|
5846
6279
|
snapPoint,
|
|
5847
6280
|
snapToHexCenter,
|
|
6281
|
+
toggleBold,
|
|
6282
|
+
toggleItalic,
|
|
6283
|
+
toggleStrikethrough,
|
|
6284
|
+
toggleUnderline,
|
|
5848
6285
|
unbindArrow,
|
|
5849
6286
|
updateBoundArrow
|
|
5850
6287
|
};
|