@fieldnotes/core 0.19.0 → 0.21.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/README.md +9 -0
- package/dist/index.cjs +169 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -2
- package/dist/index.d.ts +16 -2
- package/dist/index.js +168 -19
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -401,6 +401,15 @@ new Viewport(container, {
|
|
|
401
401
|
spacing: 24, // grid spacing in px (default: 24)
|
|
402
402
|
color: '#d0d0d0', // dot/line color (default: '#d0d0d0')
|
|
403
403
|
},
|
|
404
|
+
// Called for every drop; replaces the built-in image-drop handling
|
|
405
|
+
onDrop: (event, worldPosition) => {
|
|
406
|
+
/* handle drop */
|
|
407
|
+
},
|
|
408
|
+
// Called when an image element fails to load; failed images render a gray
|
|
409
|
+
// placeholder. Falls back to console.warn when unset.
|
|
410
|
+
onImageError: ({ src, elementIds }) => {
|
|
411
|
+
/* handle broken image */
|
|
412
|
+
},
|
|
404
413
|
});
|
|
405
414
|
```
|
|
406
415
|
|
package/dist/index.cjs
CHANGED
|
@@ -92,6 +92,7 @@ __export(index_exports, {
|
|
|
92
92
|
getHexDistance: () => getHexDistance,
|
|
93
93
|
isBindable: () => isBindable,
|
|
94
94
|
isNearBezier: () => isNearBezier,
|
|
95
|
+
isNoteContentEmpty: () => isNoteContentEmpty,
|
|
95
96
|
parseState: () => parseState,
|
|
96
97
|
sanitizeNoteHtml: () => sanitizeNoteHtml,
|
|
97
98
|
setFontSize: () => setFontSize,
|
|
@@ -382,6 +383,12 @@ function sanitizeNode(node) {
|
|
|
382
383
|
sanitizeNode(el);
|
|
383
384
|
}
|
|
384
385
|
}
|
|
386
|
+
function isNoteContentEmpty(html) {
|
|
387
|
+
if (!html) return true;
|
|
388
|
+
const doc = new DOMParser().parseFromString(html, "text/html");
|
|
389
|
+
const text = doc.body.textContent ?? "";
|
|
390
|
+
return text.replace(/\u00a0/g, " ").trim().length === 0;
|
|
391
|
+
}
|
|
385
392
|
function sanitizeAttributes(el, tag) {
|
|
386
393
|
const attrs = Array.from(el.attributes);
|
|
387
394
|
for (const attr of attrs) {
|
|
@@ -2712,6 +2719,7 @@ var ElementRenderer = class {
|
|
|
2712
2719
|
store = null;
|
|
2713
2720
|
imageCache = /* @__PURE__ */ new Map();
|
|
2714
2721
|
onImageLoad = null;
|
|
2722
|
+
onImageError = null;
|
|
2715
2723
|
camera = null;
|
|
2716
2724
|
canvasSize = null;
|
|
2717
2725
|
hexTileCache = null;
|
|
@@ -2722,6 +2730,9 @@ var ElementRenderer = class {
|
|
|
2722
2730
|
setOnImageLoad(callback) {
|
|
2723
2731
|
this.onImageLoad = callback;
|
|
2724
2732
|
}
|
|
2733
|
+
setOnImageError(callback) {
|
|
2734
|
+
this.onImageError = callback;
|
|
2735
|
+
}
|
|
2725
2736
|
setCamera(camera) {
|
|
2726
2737
|
this.camera = camera;
|
|
2727
2738
|
}
|
|
@@ -3080,6 +3091,10 @@ var ElementRenderer = class {
|
|
|
3080
3091
|
ctx.restore();
|
|
3081
3092
|
}
|
|
3082
3093
|
renderImage(ctx, image) {
|
|
3094
|
+
if (this.imageCache.get(image.src) === "failed") {
|
|
3095
|
+
this.renderImagePlaceholder(ctx, image);
|
|
3096
|
+
return;
|
|
3097
|
+
}
|
|
3083
3098
|
const img = this.getImage(image.src);
|
|
3084
3099
|
if (!img) return;
|
|
3085
3100
|
ctx.drawImage(
|
|
@@ -3090,6 +3105,27 @@ var ElementRenderer = class {
|
|
|
3090
3105
|
image.size.h
|
|
3091
3106
|
);
|
|
3092
3107
|
}
|
|
3108
|
+
renderImagePlaceholder(ctx, image) {
|
|
3109
|
+
const { x, y } = image.position;
|
|
3110
|
+
const { w, h } = image.size;
|
|
3111
|
+
ctx.save();
|
|
3112
|
+
ctx.fillStyle = "#eeeeee";
|
|
3113
|
+
ctx.fillRect(x, y, w, h);
|
|
3114
|
+
ctx.strokeStyle = "#bdbdbd";
|
|
3115
|
+
ctx.lineWidth = 1;
|
|
3116
|
+
ctx.strokeRect(x, y, w, h);
|
|
3117
|
+
const glyph = Math.min(24, w / 2, h / 2);
|
|
3118
|
+
const cx = x + w / 2;
|
|
3119
|
+
const cy = y + h / 2;
|
|
3120
|
+
ctx.strokeStyle = "#9e9e9e";
|
|
3121
|
+
ctx.lineWidth = 2;
|
|
3122
|
+
ctx.beginPath();
|
|
3123
|
+
ctx.arc(cx, cy, glyph / 2, 0, Math.PI * 2);
|
|
3124
|
+
ctx.moveTo(cx - glyph / 2, cy + glyph / 2);
|
|
3125
|
+
ctx.lineTo(cx + glyph / 2, cy - glyph / 2);
|
|
3126
|
+
ctx.stroke();
|
|
3127
|
+
ctx.restore();
|
|
3128
|
+
}
|
|
3093
3129
|
getHexTile(cellSize, orientation, strokeColor, strokeWidth, opacity, scale) {
|
|
3094
3130
|
const key = `${cellSize}:${orientation}:${strokeColor}:${strokeWidth}:${opacity}:${scale}`;
|
|
3095
3131
|
if (this.hexTileCacheKey === key && this.hexTileCache) {
|
|
@@ -3105,6 +3141,7 @@ var ElementRenderer = class {
|
|
|
3105
3141
|
getImage(src) {
|
|
3106
3142
|
const cached = this.imageCache.get(src);
|
|
3107
3143
|
if (cached) {
|
|
3144
|
+
if (cached === "failed") return null;
|
|
3108
3145
|
if (cached instanceof HTMLImageElement) return cached.complete ? cached : null;
|
|
3109
3146
|
return cached;
|
|
3110
3147
|
}
|
|
@@ -3121,6 +3158,11 @@ var ElementRenderer = class {
|
|
|
3121
3158
|
});
|
|
3122
3159
|
}
|
|
3123
3160
|
};
|
|
3161
|
+
img.onerror = () => {
|
|
3162
|
+
this.imageCache.set(src, "failed");
|
|
3163
|
+
this.onImageError?.(src);
|
|
3164
|
+
this.onImageLoad?.();
|
|
3165
|
+
};
|
|
3124
3166
|
return null;
|
|
3125
3167
|
}
|
|
3126
3168
|
};
|
|
@@ -3473,17 +3515,36 @@ var FORMAT_SHORTCUTS = {
|
|
|
3473
3515
|
i: toggleItalic,
|
|
3474
3516
|
u: toggleUnderline
|
|
3475
3517
|
};
|
|
3518
|
+
function ensureEditorStyles() {
|
|
3519
|
+
if (document.querySelector("style[data-fieldnotes-editor]")) return;
|
|
3520
|
+
const style = document.createElement("style");
|
|
3521
|
+
style.setAttribute("data-fieldnotes-editor", "");
|
|
3522
|
+
style.textContent = `[data-fn-placeholder][data-fn-empty='true']::before {
|
|
3523
|
+
content: attr(data-fn-placeholder);
|
|
3524
|
+
color: #9e9e9e;
|
|
3525
|
+
position: absolute;
|
|
3526
|
+
pointer-events: none;
|
|
3527
|
+
}`;
|
|
3528
|
+
document.head.appendChild(style);
|
|
3529
|
+
}
|
|
3530
|
+
function isNodeEmpty(node) {
|
|
3531
|
+
const text = node.textContent ?? "";
|
|
3532
|
+
return text.replace(/\u00a0/g, " ").trim().length === 0;
|
|
3533
|
+
}
|
|
3476
3534
|
var NoteEditor = class {
|
|
3477
3535
|
editingId = null;
|
|
3478
3536
|
editingNode = null;
|
|
3479
3537
|
blurHandler = null;
|
|
3480
3538
|
keyHandler = null;
|
|
3481
3539
|
pointerHandler = null;
|
|
3540
|
+
inputHandler = null;
|
|
3482
3541
|
pendingEditId = null;
|
|
3483
3542
|
onStopCallback = null;
|
|
3484
3543
|
toolbar;
|
|
3544
|
+
placeholder;
|
|
3485
3545
|
constructor(options) {
|
|
3486
3546
|
this.toolbar = options?.toolbar === false ? null : new NoteToolbar(options?.fontSizePresets);
|
|
3547
|
+
this.placeholder = options?.placeholder ?? "Type\u2026";
|
|
3487
3548
|
}
|
|
3488
3549
|
get isEditing() {
|
|
3489
3550
|
return this.editingId !== null;
|
|
@@ -3518,6 +3579,11 @@ var NoteEditor = class {
|
|
|
3518
3579
|
if (this.pointerHandler) {
|
|
3519
3580
|
this.editingNode.removeEventListener("pointerdown", this.pointerHandler);
|
|
3520
3581
|
}
|
|
3582
|
+
if (this.inputHandler) {
|
|
3583
|
+
this.editingNode.removeEventListener("input", this.inputHandler);
|
|
3584
|
+
}
|
|
3585
|
+
this.editingNode.removeAttribute("data-fn-placeholder");
|
|
3586
|
+
this.editingNode.removeAttribute("data-fn-empty");
|
|
3521
3587
|
const text = sanitizeNoteHtml(this.editingNode.innerHTML);
|
|
3522
3588
|
store.update(this.editingId, { text });
|
|
3523
3589
|
this.editingNode.contentEditable = "false";
|
|
@@ -3534,6 +3600,7 @@ var NoteEditor = class {
|
|
|
3534
3600
|
this.blurHandler = null;
|
|
3535
3601
|
this.keyHandler = null;
|
|
3536
3602
|
this.pointerHandler = null;
|
|
3603
|
+
this.inputHandler = null;
|
|
3537
3604
|
}
|
|
3538
3605
|
destroy(store) {
|
|
3539
3606
|
this.pendingEditId = null;
|
|
@@ -3564,6 +3631,13 @@ var NoteEditor = class {
|
|
|
3564
3631
|
selection.removeAllRanges();
|
|
3565
3632
|
selection.addRange(range);
|
|
3566
3633
|
}
|
|
3634
|
+
ensureEditorStyles();
|
|
3635
|
+
node.setAttribute("data-fn-placeholder", this.placeholder);
|
|
3636
|
+
node.setAttribute("data-fn-empty", String(isNodeEmpty(node)));
|
|
3637
|
+
this.inputHandler = () => {
|
|
3638
|
+
node.setAttribute("data-fn-empty", String(isNodeEmpty(node)));
|
|
3639
|
+
};
|
|
3640
|
+
node.addEventListener("input", this.inputHandler);
|
|
3567
3641
|
this.toolbar?.show(node);
|
|
3568
3642
|
this.blurHandler = (e) => {
|
|
3569
3643
|
const related = e.relatedTarget;
|
|
@@ -5000,9 +5074,21 @@ var Viewport = class {
|
|
|
5000
5074
|
this.renderLoop.markAllLayersDirty();
|
|
5001
5075
|
this.requestRender();
|
|
5002
5076
|
});
|
|
5077
|
+
this.renderer.setOnImageError((src) => {
|
|
5078
|
+
const elementIds = [];
|
|
5079
|
+
for (const el of this.store.getAll()) {
|
|
5080
|
+
if (el.type === "image" && el.src === src) elementIds.push(el.id);
|
|
5081
|
+
}
|
|
5082
|
+
if (options.onImageError) {
|
|
5083
|
+
options.onImageError({ src, elementIds });
|
|
5084
|
+
} else {
|
|
5085
|
+
console.warn(`[fieldnotes] image failed to load: ${src}`);
|
|
5086
|
+
}
|
|
5087
|
+
});
|
|
5003
5088
|
this.noteEditor = new NoteEditor({
|
|
5004
5089
|
fontSizePresets: options.fontSizePresets,
|
|
5005
|
-
toolbar: options.toolbar
|
|
5090
|
+
toolbar: options.toolbar,
|
|
5091
|
+
placeholder: options.placeholder
|
|
5006
5092
|
});
|
|
5007
5093
|
this.noteEditor.setOnStop((id) => this.onTextEditStop(id));
|
|
5008
5094
|
this.onHtmlElementMount = options.onHtmlElementMount;
|
|
@@ -5348,7 +5434,16 @@ var Viewport = class {
|
|
|
5348
5434
|
}
|
|
5349
5435
|
onTextEditStop(elementId) {
|
|
5350
5436
|
const element = this.store.getById(elementId);
|
|
5351
|
-
if (!element
|
|
5437
|
+
if (!element) return;
|
|
5438
|
+
if (element.type === "note") {
|
|
5439
|
+
if (isNoteContentEmpty(element.text)) {
|
|
5440
|
+
this.historyRecorder.begin();
|
|
5441
|
+
this.store.remove(elementId);
|
|
5442
|
+
this.historyRecorder.commit();
|
|
5443
|
+
}
|
|
5444
|
+
return;
|
|
5445
|
+
}
|
|
5446
|
+
if (element.type !== "text") return;
|
|
5352
5447
|
if (!element.text || element.text.trim() === "") {
|
|
5353
5448
|
this.historyRecorder.begin();
|
|
5354
5449
|
this.store.remove(elementId);
|
|
@@ -5694,6 +5789,43 @@ var PencilTool = class {
|
|
|
5694
5789
|
}
|
|
5695
5790
|
};
|
|
5696
5791
|
|
|
5792
|
+
// src/elements/stroke-hit.ts
|
|
5793
|
+
function distSqToSegment(p, a, b) {
|
|
5794
|
+
const abx = b.x - a.x;
|
|
5795
|
+
const aby = b.y - a.y;
|
|
5796
|
+
const apx = p.x - a.x;
|
|
5797
|
+
const apy = p.y - a.y;
|
|
5798
|
+
const lenSq = abx * abx + aby * aby;
|
|
5799
|
+
if (lenSq === 0) {
|
|
5800
|
+
return apx * apx + apy * apy;
|
|
5801
|
+
}
|
|
5802
|
+
const t = Math.max(0, Math.min(1, (apx * abx + apy * aby) / lenSq));
|
|
5803
|
+
const dx = p.x - (a.x + t * abx);
|
|
5804
|
+
const dy = p.y - (a.y + t * aby);
|
|
5805
|
+
return dx * dx + dy * dy;
|
|
5806
|
+
}
|
|
5807
|
+
function hitTestStroke(stroke, point, radius) {
|
|
5808
|
+
const bounds = getElementBounds(stroke);
|
|
5809
|
+
if (!bounds) return false;
|
|
5810
|
+
if (point.x < bounds.x - radius || point.x > bounds.x + bounds.w + radius || point.y < bounds.y - radius || point.y > bounds.y + bounds.h + radius) {
|
|
5811
|
+
return false;
|
|
5812
|
+
}
|
|
5813
|
+
const radiusSq = radius * radius;
|
|
5814
|
+
const local = { x: point.x - stroke.position.x, y: point.y - stroke.position.y };
|
|
5815
|
+
const { segments } = getStrokeRenderData(stroke);
|
|
5816
|
+
if (segments.length === 0) {
|
|
5817
|
+
const p = stroke.points[0];
|
|
5818
|
+
if (!p) return false;
|
|
5819
|
+
const dx = p.x - local.x;
|
|
5820
|
+
const dy = p.y - local.y;
|
|
5821
|
+
return dx * dx + dy * dy <= radiusSq;
|
|
5822
|
+
}
|
|
5823
|
+
for (const seg of segments) {
|
|
5824
|
+
if (distSqToSegment(local, seg.start, seg.end) <= radiusSq) return true;
|
|
5825
|
+
}
|
|
5826
|
+
return false;
|
|
5827
|
+
}
|
|
5828
|
+
|
|
5697
5829
|
// src/tools/eraser-tool.ts
|
|
5698
5830
|
var DEFAULT_RADIUS = 20;
|
|
5699
5831
|
function makeEraserCursor(radius) {
|
|
@@ -5752,12 +5884,7 @@ var EraserTool = class {
|
|
|
5752
5884
|
if (erased) ctx.requestRender();
|
|
5753
5885
|
}
|
|
5754
5886
|
strokeIntersects(stroke, point) {
|
|
5755
|
-
|
|
5756
|
-
return stroke.points.some((p) => {
|
|
5757
|
-
const dx = p.x + stroke.position.x - point.x;
|
|
5758
|
-
const dy = p.y + stroke.position.y - point.y;
|
|
5759
|
-
return dx * dx + dy * dy <= radiusSq;
|
|
5760
|
-
});
|
|
5887
|
+
return hitTestStroke(stroke, point, this.radius);
|
|
5761
5888
|
}
|
|
5762
5889
|
};
|
|
5763
5890
|
|
|
@@ -5897,6 +6024,7 @@ var SelectTool = class {
|
|
|
5897
6024
|
pendingSingleSelectId = null;
|
|
5898
6025
|
hasDragged = false;
|
|
5899
6026
|
resizeAspectRatio = 0;
|
|
6027
|
+
hoveredId = null;
|
|
5900
6028
|
get selectedIds() {
|
|
5901
6029
|
return [...this._selectedIds];
|
|
5902
6030
|
}
|
|
@@ -5913,6 +6041,7 @@ var SelectTool = class {
|
|
|
5913
6041
|
onDeactivate(ctx) {
|
|
5914
6042
|
this._selectedIds = [];
|
|
5915
6043
|
this.mode = { type: "idle" };
|
|
6044
|
+
this.hoveredId = null;
|
|
5916
6045
|
ctx.setCursor?.("default");
|
|
5917
6046
|
}
|
|
5918
6047
|
snap(point, ctx) {
|
|
@@ -5920,6 +6049,7 @@ var SelectTool = class {
|
|
|
5920
6049
|
}
|
|
5921
6050
|
onPointerDown(state, ctx) {
|
|
5922
6051
|
this.ctx = ctx;
|
|
6052
|
+
this.setHovered(null, ctx);
|
|
5923
6053
|
const world = ctx.camera.screenToWorld({ x: state.x, y: state.y });
|
|
5924
6054
|
this.lastWorld = this.snap(world, ctx);
|
|
5925
6055
|
this.currentWorld = world;
|
|
@@ -6062,7 +6192,8 @@ var SelectTool = class {
|
|
|
6062
6192
|
}
|
|
6063
6193
|
onHover(state, ctx) {
|
|
6064
6194
|
const world = ctx.camera.screenToWorld({ x: state.x, y: state.y });
|
|
6065
|
-
this.updateHoverCursor(world, ctx);
|
|
6195
|
+
const hoverId = this.updateHoverCursor(world, ctx);
|
|
6196
|
+
this.setHovered(hoverId, ctx);
|
|
6066
6197
|
}
|
|
6067
6198
|
renderOverlay(canvasCtx) {
|
|
6068
6199
|
this.renderMarquee(canvasCtx);
|
|
@@ -6083,6 +6214,23 @@ var SelectTool = class {
|
|
|
6083
6214
|
canvasCtx.restore();
|
|
6084
6215
|
}
|
|
6085
6216
|
}
|
|
6217
|
+
if (this.hoveredId && this.ctx && this.mode.type === "idle") {
|
|
6218
|
+
if (!this._selectedIds.includes(this.hoveredId)) {
|
|
6219
|
+
const el = this.ctx.store.getById(this.hoveredId);
|
|
6220
|
+
if (el) {
|
|
6221
|
+
const b = getElementBounds(el);
|
|
6222
|
+
if (b) {
|
|
6223
|
+
canvasCtx.save();
|
|
6224
|
+
canvasCtx.strokeStyle = "#2196F3";
|
|
6225
|
+
canvasCtx.globalAlpha = 0.35;
|
|
6226
|
+
canvasCtx.lineWidth = 1.5 / this.ctx.camera.zoom;
|
|
6227
|
+
canvasCtx.setLineDash([]);
|
|
6228
|
+
canvasCtx.strokeRect(b.x, b.y, b.w, b.h);
|
|
6229
|
+
canvasCtx.restore();
|
|
6230
|
+
}
|
|
6231
|
+
}
|
|
6232
|
+
}
|
|
6233
|
+
}
|
|
6086
6234
|
}
|
|
6087
6235
|
updateArrowsBoundTo(ids, ctx) {
|
|
6088
6236
|
const movedNonArrowIds = /* @__PURE__ */ new Set();
|
|
@@ -6131,20 +6279,26 @@ var SelectTool = class {
|
|
|
6131
6279
|
const arrowHit = hitTestArrowHandles(world, this._selectedIds, ctx);
|
|
6132
6280
|
if (arrowHit) {
|
|
6133
6281
|
ctx.setCursor?.(getArrowHandleCursor(arrowHit.handle, false));
|
|
6134
|
-
return;
|
|
6282
|
+
return null;
|
|
6135
6283
|
}
|
|
6136
6284
|
const templateResizeHit = this.hitTestTemplateResizeHandle(world, ctx);
|
|
6137
6285
|
if (templateResizeHit) {
|
|
6138
6286
|
ctx.setCursor?.("nwse-resize");
|
|
6139
|
-
return;
|
|
6287
|
+
return null;
|
|
6140
6288
|
}
|
|
6141
6289
|
const resizeHit = this.hitTestResizeHandle(world, ctx);
|
|
6142
6290
|
if (resizeHit) {
|
|
6143
6291
|
ctx.setCursor?.(HANDLE_CURSORS[resizeHit.handle]);
|
|
6144
|
-
return;
|
|
6292
|
+
return null;
|
|
6145
6293
|
}
|
|
6146
6294
|
const hit = this.hitTest(world, ctx);
|
|
6147
6295
|
ctx.setCursor?.(hit ? "move" : "default");
|
|
6296
|
+
return hit ? hit.id : null;
|
|
6297
|
+
}
|
|
6298
|
+
setHovered(id, ctx) {
|
|
6299
|
+
if (this.hoveredId === id) return;
|
|
6300
|
+
this.hoveredId = id;
|
|
6301
|
+
ctx.requestRender();
|
|
6148
6302
|
}
|
|
6149
6303
|
handleResize(world, ctx, shiftKey = false) {
|
|
6150
6304
|
if (this.mode.type !== "resizing") return;
|
|
@@ -6410,12 +6564,7 @@ var SelectTool = class {
|
|
|
6410
6564
|
return point.x >= el.position.x && point.x <= el.position.x + s.w && point.y >= el.position.y && point.y <= el.position.y + s.h;
|
|
6411
6565
|
}
|
|
6412
6566
|
if (el.type === "stroke") {
|
|
6413
|
-
|
|
6414
|
-
return el.points.some((p) => {
|
|
6415
|
-
const dx = p.x + el.position.x - point.x;
|
|
6416
|
-
const dy = p.y + el.position.y - point.y;
|
|
6417
|
-
return dx * dx + dy * dy <= HIT_RADIUS * HIT_RADIUS;
|
|
6418
|
-
});
|
|
6567
|
+
return hitTestStroke(el, point, 10);
|
|
6419
6568
|
}
|
|
6420
6569
|
if (el.type === "arrow") {
|
|
6421
6570
|
return isNearBezier(point, el.from, el.to, el.bend, 10);
|
|
@@ -7251,7 +7400,7 @@ var TemplateTool = class {
|
|
|
7251
7400
|
};
|
|
7252
7401
|
|
|
7253
7402
|
// src/index.ts
|
|
7254
|
-
var VERSION = "0.
|
|
7403
|
+
var VERSION = "0.21.0";
|
|
7255
7404
|
// Annotate the CommonJS export names for ESM import in node:
|
|
7256
7405
|
0 && (module.exports = {
|
|
7257
7406
|
AddElementCommand,
|
|
@@ -7326,6 +7475,7 @@ var VERSION = "0.19.0";
|
|
|
7326
7475
|
getHexDistance,
|
|
7327
7476
|
isBindable,
|
|
7328
7477
|
isNearBezier,
|
|
7478
|
+
isNoteContentEmpty,
|
|
7329
7479
|
parseState,
|
|
7330
7480
|
sanitizeNoteHtml,
|
|
7331
7481
|
setFontSize,
|