autumnnote 1.5.0 → 1.6.1

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.
Files changed (53) hide show
  1. package/README.md +10 -8
  2. package/dist/autumnnote.css +324 -4
  3. package/dist/autumnnote.es.js +943 -526
  4. package/dist/autumnnote.es.js.map +1 -1
  5. package/dist/autumnnote.umd.js +935 -519
  6. package/dist/autumnnote.umd.js.map +1 -1
  7. package/package.json +21 -3
  8. package/src/js/Context.js +23 -16
  9. package/src/js/core/detectLang.js +98 -0
  10. package/src/js/core/dom.js +67 -11
  11. package/src/js/core/env.js +1 -1
  12. package/src/js/core/func.js +1 -1
  13. package/src/js/core/lists.js +1 -1
  14. package/src/js/core/markdown.js +32 -31
  15. package/src/js/core/range.js +8 -8
  16. package/src/js/editing/History.js +10 -10
  17. package/src/js/editing/Style.js +44 -44
  18. package/src/js/editing/Table.js +5 -7
  19. package/src/js/editing/Typing.js +19 -19
  20. package/src/js/i18n/en.js +2 -0
  21. package/src/js/i18n/vi.js +2 -0
  22. package/src/js/index.js +3 -2
  23. package/src/js/module/AutoSaveRestore.js +2 -4
  24. package/src/js/module/BubbleToolbar.js +51 -34
  25. package/src/js/module/Buttons.js +20 -18
  26. package/src/js/module/Clipboard.js +16 -17
  27. package/src/js/module/CodeTooltip.js +48 -24
  28. package/src/js/module/Codeview.js +5 -7
  29. package/src/js/module/ContextMenu.js +37 -37
  30. package/src/js/module/Editor.js +77 -26
  31. package/src/js/module/EmojiDialog.js +24 -18
  32. package/src/js/module/FindReplace.js +93 -66
  33. package/src/js/module/Fullscreen.js +1 -1
  34. package/src/js/module/IconDialog.js +39 -35
  35. package/src/js/module/ImageCropOverlay.js +13 -13
  36. package/src/js/module/ImageDialog.js +19 -13
  37. package/src/js/module/ImageResizer.js +5 -5
  38. package/src/js/module/ImageTooltip.js +20 -16
  39. package/src/js/module/LinkDialog.js +20 -14
  40. package/src/js/module/LinkTooltip.js +15 -12
  41. package/src/js/module/MarkdownShortcuts.js +11 -14
  42. package/src/js/module/Mention.js +11 -13
  43. package/src/js/module/Placeholder.js +1 -1
  44. package/src/js/module/ShortcutsDialog.js +2 -4
  45. package/src/js/module/Statusbar.js +5 -7
  46. package/src/js/module/TableTooltip.js +196 -36
  47. package/src/js/module/Toolbar.js +35 -34
  48. package/src/js/module/VideoDialog.js +16 -10
  49. package/src/js/module/VideoResizer.js +7 -9
  50. package/src/js/module/VideoTooltip.js +15 -10
  51. package/src/js/renderer.js +5 -3
  52. package/src/js/settings.js +53 -36
  53. package/src/styles/autumnnote.scss +332 -7
@@ -114,27 +114,71 @@
114
114
  const handler = (e) => {
115
115
  if (e.key === "Escape") {
116
116
  e.stopPropagation();
117
- onEscape && onEscape();
117
+ onEscape?.();
118
118
  return;
119
119
  }
120
120
  if (e.key !== "Tab") return;
121
121
  const els = getFocusable();
122
122
  if (!els.length) return;
123
123
  const first = els[0];
124
- const last = els[els.length - 1];
124
+ const last = els.at(-1);
125
125
  if (e.shiftKey) {
126
126
  if (document.activeElement === first) {
127
127
  e.preventDefault();
128
- last.focus();
128
+ /** @type {HTMLElement} */ last.focus();
129
129
  }
130
130
  } else if (document.activeElement === last) {
131
131
  e.preventDefault();
132
- first.focus();
132
+ /** @type {HTMLElement} */ first.focus();
133
133
  }
134
134
  };
135
135
  document.addEventListener("keydown", handler);
136
136
  return () => document.removeEventListener("keydown", handler);
137
137
  }
138
+ /**
139
+ * Makes a dialog box draggable by its handle element.
140
+ * On first drag the box is pinned to its current viewport coordinates via
141
+ * `position:fixed`, freeing it from the parent flex container's centering.
142
+ * The position is clamped to the visible viewport.
143
+ *
144
+ * @param {HTMLElement} handle Element the user grabs (title bar / header)
145
+ * @param {HTMLElement} box Element that actually moves
146
+ * @returns {Function} Cleanup function (removes the mousedown listener)
147
+ */
148
+ function makeDraggable(handle, box) {
149
+ handle.style.cursor = "grab";
150
+ const onMousedown = (e) => {
151
+ if (e.button !== 0) return;
152
+ if (e.target.closest("button, input, select, textarea, a")) return;
153
+ e.preventDefault();
154
+ if (!box.dataset.anDragPinned) {
155
+ const r = box.getBoundingClientRect();
156
+ box.style.position = "fixed";
157
+ box.style.margin = "0";
158
+ box.style.left = `${r.left}px`;
159
+ box.style.top = `${r.top}px`;
160
+ box.dataset.anDragPinned = "1";
161
+ }
162
+ const startX = e.clientX - Number.parseFloat(box.style.left);
163
+ const startY = e.clientY - Number.parseFloat(box.style.top);
164
+ handle.style.cursor = "grabbing";
165
+ const onMove = (ev) => {
166
+ const bw = box.offsetWidth;
167
+ const bh = box.offsetHeight;
168
+ box.style.left = `${Math.max(0, Math.min(ev.clientX - startX, globalThis.innerWidth - bw))}px`;
169
+ box.style.top = `${Math.max(0, Math.min(ev.clientY - startY, globalThis.innerHeight - bh))}px`;
170
+ };
171
+ const onUp = () => {
172
+ handle.style.cursor = "grab";
173
+ document.removeEventListener("mousemove", onMove);
174
+ document.removeEventListener("mouseup", onUp);
175
+ };
176
+ document.addEventListener("mousemove", onMove);
177
+ document.addEventListener("mouseup", onUp);
178
+ };
179
+ handle.addEventListener("mousedown", onMousedown);
180
+ return () => handle.removeEventListener("mousedown", onMousedown);
181
+ }
138
182
  //#endregion
139
183
  //#region src/js/core/range.js
140
184
  /**
@@ -168,10 +212,10 @@
168
212
  return range;
169
213
  }
170
214
  /**
171
- * Select this wrapped range in the window.
215
+ * Select this wrapped range in the globalThis.
172
216
  */
173
217
  select() {
174
- const sel = window.getSelection();
218
+ const sel = globalThis.getSelection();
175
219
  if (!sel) return;
176
220
  sel.removeAllRanges();
177
221
  sel.addRange(this.toNativeRange());
@@ -224,13 +268,13 @@
224
268
  return new WrappedRange(range.startContainer, range.startOffset, range.endContainer, range.endOffset);
225
269
  }
226
270
  /**
227
- * Returns a WrappedRange for the current window selection,
271
+ * Returns a WrappedRange for the current globalThis selection,
228
272
  * optionally restricted to a given editable element.
229
273
  * @param {HTMLElement} [editable]
230
274
  * @returns {WrappedRange|null}
231
275
  */
232
276
  function currentRange(editable) {
233
- const sel = window.getSelection();
277
+ const sel = globalThis.getSelection();
234
278
  if (!sel || sel.rangeCount === 0) return null;
235
279
  const native = sel.getRangeAt(0);
236
280
  if (editable && !editable.contains(native.commonAncestorContainer)) return null;
@@ -241,7 +285,7 @@
241
285
  * @param {Function} fn
242
286
  */
243
287
  function withSavedRange(fn) {
244
- const sel = window.getSelection();
288
+ const sel = globalThis.getSelection();
245
289
  if (!sel || sel.rangeCount === 0) {
246
290
  fn(null);
247
291
  return;
@@ -276,16 +320,16 @@
276
320
  * execCommand's state detection is unreliable.
277
321
  */
278
322
  function underline() {
279
- const sel = window.getSelection();
323
+ const sel = globalThis.getSelection();
280
324
  if (!sel || !sel.rangeCount) return;
281
325
  let container = sel.getRangeAt(0).commonAncestorContainer;
282
326
  if (container.nodeType === 3) container = container.parentElement;
283
- const uEl = container && container.closest && container.closest("u");
327
+ const uEl = container?.closest("u");
284
328
  const nativeState = document.queryCommandState("underline");
285
329
  if (uEl && !nativeState) {
286
330
  const parent = uEl.parentNode;
287
331
  while (uEl.firstChild) parent.insertBefore(uEl.firstChild, uEl);
288
- parent.removeChild(uEl);
332
+ uEl.remove();
289
333
  return;
290
334
  }
291
335
  execCommand("underline");
@@ -296,16 +340,16 @@
296
340
  * execCommand's state detection is unreliable (mirrors underline() logic).
297
341
  */
298
342
  function strikethrough() {
299
- const sel = window.getSelection();
343
+ const sel = globalThis.getSelection();
300
344
  if (!sel || !sel.rangeCount) return;
301
345
  let sc = sel.getRangeAt(0).startContainer;
302
346
  if (sc.nodeType === 3) sc = sc.parentElement;
303
- const sEl = sc && sc.closest && (sc.closest("s") || sc.closest("strike"));
347
+ const sEl = sc?.closest("s") || sc?.closest("strike");
304
348
  const nativeState = document.queryCommandState("strikeThrough");
305
349
  if (sEl && !nativeState) {
306
350
  const parent = sEl.parentNode;
307
351
  while (sEl.firstChild) parent.insertBefore(sEl.firstChild, sEl);
308
- parent.removeChild(sEl);
352
+ sEl.remove();
309
353
  return;
310
354
  }
311
355
  execCommand("strikeThrough");
@@ -337,10 +381,10 @@
337
381
  * Sets the font size (in pt or with unit) for the selection.
338
382
  * Uses a span-based approach to set px sizes precisely.
339
383
  * @param {string} size - e.g. '14px'
340
- * @param {HTMLElement} [editable] - scoping element to avoid touching nodes outside this editor
384
+ * @param {HTMLElement|Document} [editable] - scoping element to avoid touching nodes outside this editor
341
385
  */
342
386
  function fontSize(size, editable = document) {
343
- const sel = window.getSelection();
387
+ const sel = globalThis.getSelection();
344
388
  const wasCollapsed = !sel || !sel.rangeCount || sel.getRangeAt(0).collapsed;
345
389
  if (wasCollapsed && sel && sel.rangeCount > 0) {
346
390
  try {
@@ -366,12 +410,12 @@
366
410
  span.style.fontSize = size;
367
411
  el.parentNode.insertBefore(span, el);
368
412
  while (el.firstChild) span.appendChild(el.firstChild);
369
- el.parentNode.removeChild(el);
413
+ el.remove();
370
414
  newSpans.push(span);
371
415
  });
372
416
  if (!wasCollapsed && sel && newSpans.length > 0) {
373
417
  const first = newSpans[0];
374
- const last = newSpans[newSpans.length - 1];
418
+ const last = newSpans.at(-1);
375
419
  try {
376
420
  const nr = document.createRange();
377
421
  const startNode = first.firstChild || first;
@@ -415,11 +459,11 @@
415
459
  * (which would destroy the ul > li checklist structure).
416
460
  */
417
461
  function outdent() {
418
- const sel = window.getSelection();
462
+ const sel = globalThis.getSelection();
419
463
  if (sel && sel.rangeCount) {
420
464
  let container = sel.getRangeAt(0).commonAncestorContainer;
421
465
  if (container.nodeType === 3) container = container.parentElement;
422
- const checkLi = container && container.closest && container.closest(".an-checklist li");
466
+ const checkLi = container?.closest(".an-checklist li");
423
467
  if (checkLi) {
424
468
  _checklistItemToP(checkLi);
425
469
  return;
@@ -449,7 +493,7 @@
449
493
  if (child.nodeType === 1 && child.tagName === "INPUT") continue;
450
494
  p.appendChild(child.cloneNode(true));
451
495
  }
452
- p.innerHTML = p.innerHTML.replace(/\u200B/g, "");
496
+ p.innerHTML = p.innerHTML.replaceAll("​", "");
453
497
  if (!p.hasChildNodes() || !p.textContent.trim()) {
454
498
  p.innerHTML = "";
455
499
  p.appendChild(document.createTextNode("\xA0"));
@@ -461,14 +505,14 @@
461
505
  checkUl.parentNode.insertBefore(newUl, checkUl.nextSibling);
462
506
  }
463
507
  checkUl.parentNode.insertBefore(p, checkUl.nextSibling);
464
- checkUl.removeChild(checkLi);
465
- if (checkUl.children.length === 0) checkUl.parentNode.removeChild(checkUl);
508
+ checkLi.remove();
509
+ if (checkUl.children.length === 0) checkUl.remove();
466
510
  try {
467
511
  const nr = document.createRange();
468
512
  const firstChild = p.firstChild;
469
513
  nr.setStart(firstChild && firstChild.nodeType === 3 ? firstChild : p, 0);
470
514
  nr.collapse(true);
471
- const s = window.getSelection();
515
+ const s = globalThis.getSelection();
472
516
  if (s) {
473
517
  s.removeAllRanges();
474
518
  s.addRange(nr);
@@ -492,7 +536,7 @@
492
536
  * @param {string} value - Line-height value to apply; typically a unitless multiplier (for example, "1.5").
493
537
  */
494
538
  function lineHeight(value) {
495
- const sel = window.getSelection();
539
+ const sel = globalThis.getSelection();
496
540
  if (!sel || sel.rangeCount === 0) return;
497
541
  const range = sel.getRangeAt(0);
498
542
  const BLOCK_TAGS = new Set([
@@ -541,25 +585,25 @@
541
585
  /**
542
586
  * Wraps the selection in an inline <code> element, or unwraps it if the
543
587
  * cursor is already inside a <code> that is not inside a <pre>.
544
- * @param {HTMLElement} [editable]
588
+ * @param {HTMLElement} [_editable]
545
589
  */
546
- function toggleInlineCode(editable) {
547
- const sel = window.getSelection();
590
+ function toggleInlineCode(_editable) {
591
+ const sel = globalThis.getSelection();
548
592
  if (!sel || !sel.rangeCount) return;
549
593
  const range = sel.getRangeAt(0);
550
594
  let container = range.commonAncestorContainer;
551
595
  if (container.nodeType === 3) container = container.parentElement;
552
- const codeEl = container && container.closest ? container.closest("code") : null;
596
+ const codeEl = container?.closest("code");
553
597
  if (codeEl && !codeEl.closest("pre")) {
554
598
  const parent = codeEl.parentNode;
555
599
  const prevSibling = codeEl.previousSibling;
556
600
  const movedChildren = Array.from(codeEl.childNodes);
557
601
  while (codeEl.firstChild) parent.insertBefore(codeEl.firstChild, codeEl);
558
- parent.removeChild(codeEl);
559
- if (parent && parent.normalize) parent.normalize();
602
+ codeEl.remove();
603
+ parent?.normalize();
560
604
  if (movedChildren.length > 0) try {
561
605
  const firstMoved = movedChildren[0];
562
- const lastMoved = movedChildren[movedChildren.length - 1];
606
+ const lastMoved = movedChildren.at(-1);
563
607
  const nr = document.createRange();
564
608
  const anchorNode = firstMoved.parentNode === parent ? firstMoved : prevSibling ? prevSibling.nextSibling : parent.firstChild;
565
609
  if (anchorNode) {
@@ -600,11 +644,11 @@
600
644
  * @returns {boolean}
601
645
  */
602
646
  function isInlineCode() {
603
- const sel = window.getSelection();
647
+ const sel = globalThis.getSelection();
604
648
  if (!sel || !sel.rangeCount) return false;
605
649
  let sc = sel.getRangeAt(0).startContainer;
606
650
  if (sc.nodeType === 3) sc = sc.parentElement;
607
- const code = sc && sc.closest ? sc.closest("code") : null;
651
+ const code = sc?.closest("code");
608
652
  return !!(code && !code.closest("pre"));
609
653
  }
610
654
  /**
@@ -622,30 +666,30 @@
622
666
  * Empty or whitespace-only selections do not create a checklist.
623
667
  */
624
668
  function toggleChecklist() {
625
- const sel = window.getSelection();
669
+ const sel = globalThis.getSelection();
626
670
  if (!sel || !sel.rangeCount) return;
627
671
  const range = sel.getRangeAt(0);
628
672
  let container = range.commonAncestorContainer;
629
673
  if (container.nodeType === 3) container = container.parentElement;
630
- const ul = container.closest && container.closest(".an-checklist");
674
+ const ul = container?.closest(".an-checklist");
631
675
  if (ul) {
632
676
  const selectedLis = Array.from(ul.querySelectorAll("li")).filter((li) => sel.containsNode(li, true));
633
677
  if (selectedLis.length > 0) {
634
- let firstP = null;
678
+ /** @type {HTMLElement|null} */ let firstP = null;
635
679
  selectedLis.forEach((li) => {
636
680
  const p = document.createElement("p");
637
681
  for (const child of li.childNodes) {
638
682
  if (child.nodeType === 1 && child.tagName === "INPUT") continue;
639
683
  p.appendChild(child.cloneNode(true));
640
684
  }
641
- p.innerHTML = p.innerHTML.replace(/\u200b/g, "");
685
+ p.innerHTML = p.innerHTML.replaceAll("​", "");
642
686
  if (!p.hasChildNodes() || !p.textContent.trim()) {
643
687
  p.innerHTML = "";
644
688
  p.appendChild(document.createTextNode("\xA0"));
645
689
  }
646
690
  ul.parentNode.insertBefore(p, ul);
647
691
  if (!firstP) firstP = p;
648
- ul.removeChild(li);
692
+ li.remove();
649
693
  });
650
694
  if (ul.children.length === 0) ul.remove();
651
695
  if (firstP) {
@@ -673,7 +717,7 @@
673
717
  ]);
674
718
  let block = container;
675
719
  while (block && block.parentNode && !BLOCK_TAGS.has(block.tagName)) block = block.parentNode;
676
- const itemText = block && BLOCK_TAGS.has(block.tagName) ? Array.from(block.childNodes).map((n) => n.textContent).join("").replace(/\u00a0/g, " ") : "";
720
+ const itemText = block && BLOCK_TAGS.has(block.tagName) ? Array.from(block.childNodes).map((n) => n.textContent).join("").replaceAll("\xA0", " ") : "";
677
721
  const ul = document.createElement("ul");
678
722
  ul.className = "an-checklist";
679
723
  const li = document.createElement("li");
@@ -729,7 +773,7 @@
729
773
  if (blocks.length === 0) return;
730
774
  const newUl = document.createElement("ul");
731
775
  newUl.className = "an-checklist";
732
- let lastTextNode = null;
776
+ /** @type {Text|null} */ let lastTextNode = null;
733
777
  blocks.forEach((block) => {
734
778
  const li = document.createElement("li");
735
779
  const cb = document.createElement("input");
@@ -744,7 +788,7 @@
744
788
  });
745
789
  const firstBlock = blocks[0];
746
790
  firstBlock.parentNode.insertBefore(newUl, firstBlock);
747
- blocks.forEach((block) => block.parentNode && block.parentNode.removeChild(block));
791
+ blocks.forEach((block) => block.remove());
748
792
  if (lastTextNode) {
749
793
  const nr = document.createRange();
750
794
  nr.setStart(lastTextNode, lastTextNode.textContent.length);
@@ -758,11 +802,11 @@
758
802
  * @returns {boolean}
759
803
  */
760
804
  function isInChecklist() {
761
- const sel = window.getSelection();
805
+ const sel = globalThis.getSelection();
762
806
  if (!sel || !sel.rangeCount) return false;
763
807
  let container = sel.getRangeAt(0).commonAncestorContainer;
764
808
  if (container.nodeType === 3) container = container.parentElement;
765
- return !!(container && container.closest && container.closest(".an-checklist li"));
809
+ return !!container?.closest(".an-checklist li");
766
810
  }
767
811
  //#endregion
768
812
  //#region src/js/module/Buttons.js
@@ -773,12 +817,14 @@
773
817
  */
774
818
  /**
775
819
  * @typedef {object} DropdownDef
776
- * @property {string} name - unique identifier
777
- * @property {'select'} type - discriminator for Toolbar renderer
820
+ * @property {string} name - unique identifier
821
+ * @property {'select'} type - discriminator for Toolbar renderer
778
822
  * @property {string} tooltip
779
- * @property {string[]} [items] - overridden at render time from options
780
- * @property {Function} action - called with (context, value)
781
- * @property {Function} [getValue] - called with (context) to get current value
823
+ * @property {Array<string|{value:string,label:string,disabled?:boolean}>} [items] - overridden at render time from options
824
+ * @property {Function} action - called with (context, value)
825
+ * @property {Function} [getValue] - called with (context) to get current value
826
+ * @property {string} [selectClass] - extra CSS class(es) for the <select>
827
+ * @property {string} [placeholder] - placeholder text for the empty option
782
828
  */
783
829
  /**
784
830
  * @typedef {object} ButtonDef
@@ -842,11 +888,11 @@
842
888
  var italicBtn = btn("italic", "italic", "Italic (Ctrl+I)", () => italic(), () => document.queryCommandState("italic"));
843
889
  var underlineBtn = btn("underline", "underline", "Underline (Ctrl+U)", () => underline(), () => {
844
890
  if (document.queryCommandState("underline")) return true;
845
- const sel = window.getSelection();
891
+ const sel = globalThis.getSelection();
846
892
  if (!sel || !sel.rangeCount) return false;
847
893
  let sc = sel.getRangeAt(0).startContainer;
848
894
  if (sc.nodeType === 3) sc = sc.parentElement;
849
- return !!(sc && sc.closest && sc.closest("u"));
895
+ return !!(sc && sc.closest("u"));
850
896
  });
851
897
  var strikeBtn = btn("strikethrough", "strikethrough", "Strikethrough", () => strikethrough(), () => document.queryCommandState("strikeThrough"));
852
898
  var superscriptBtn = btn("superscript", "superscript", "Superscript", () => superscript(), () => document.queryCommandState("superscript"));
@@ -905,15 +951,15 @@
905
951
  action: (ctx, value) => fontSize(value, ctx.layoutInfo.editable),
906
952
  getValue: (ctx) => {
907
953
  try {
908
- const sel = window.getSelection();
954
+ const sel = globalThis.getSelection();
909
955
  if (sel && sel.rangeCount) {
910
956
  let el = sel.getRangeAt(0).startContainer;
911
- if (el.nodeType === 3) el = el.parentElement;
957
+ if (el && el.nodeType === 3) el = el.parentElement;
912
958
  while (el && el.nodeType === 1 && !el.style.fontSize) el = el.parentElement;
913
- const size = el && el.style && el.style.fontSize ? el.style.fontSize : "";
959
+ const size = el && el.style.fontSize ? el.style.fontSize : "";
914
960
  if (size) return size;
915
961
  }
916
- const editable = ctx && ctx.layoutInfo && ctx.layoutInfo.editable;
962
+ const editable = ctx?.layoutInfo?.editable;
917
963
  if (editable) return editable.style.fontSize || "";
918
964
  return "";
919
965
  } catch {
@@ -1010,7 +1056,7 @@
1010
1056
  action: (_ctx, value) => lineHeight(value),
1011
1057
  getValue: () => {
1012
1058
  try {
1013
- const sel = window.getSelection();
1059
+ const sel = globalThis.getSelection();
1014
1060
  if (!sel || !sel.rangeCount) return "";
1015
1061
  const BLOCKS = new Set([
1016
1062
  "P",
@@ -1028,8 +1074,11 @@
1028
1074
  "TH"
1029
1075
  ]);
1030
1076
  let el = sel.getRangeAt(0).startContainer;
1031
- if (el.nodeType === 3) el = el.parentElement;
1032
- while (el && !BLOCKS.has(el.tagName)) el = el.parentElement;
1077
+ if (el && el.nodeType === 3) el = el.parentElement;
1078
+ while (el && !BLOCKS.has(
1079
+ /** @type {Element} */
1080
+ el.tagName
1081
+ )) el = el.parentElement;
1033
1082
  if (!el) return "";
1034
1083
  return el.style.lineHeight || getComputedStyle(el).lineHeight || "";
1035
1084
  } catch {
@@ -1052,47 +1101,64 @@
1052
1101
  */
1053
1102
  /**
1054
1103
  * @typedef {object} AsnOptions
1055
- * @property {string} [placeholder] - Placeholder text when editor is empty
1056
- * @property {number} [height] - Editor height in px (min)
1057
- * @property {number} [minHeight] - Minimum height in px
1058
- * @property {number} [maxHeight] - Maximum height in px (0 = unlimited)
1059
- * @property {boolean} [focus] - Auto-focus on init
1060
- * @property {boolean} [resizable] - Show resize handle
1061
- * @property {Array} [toolbar] - Toolbar button group config
1062
- * @property {boolean} [pasteAsPlainText] - Force plain-text paste
1063
- * @property {boolean} [pasteCleanHTML] - Sanitise HTML on paste
1104
+ * @property {string} [placeholder] - Placeholder text when editor is empty
1105
+ * @property {number} [height] - Editor height in px (min)
1106
+ * @property {number} [minHeight] - Minimum height in px
1107
+ * @property {number} [maxHeight] - Maximum height in px (0 = unlimited)
1108
+ * @property {boolean} [focus] - Auto-focus on init
1109
+ * @property {boolean} [resizable] - Show resize handle
1110
+ * @property {Array} [toolbar] - Toolbar button group config
1111
+ * @property {boolean} [useBootstrap] - Use Bootstrap button classes on toolbar buttons
1112
+ * @property {string} [toolbarButtonClass] - CSS classes for Bootstrap toolbar buttons
1113
+ * @property {boolean} [useFontAwesome] - Use Font Awesome icons (default: true)
1114
+ * @property {string} [fontAwesomeClass] - Font Awesome prefix class, e.g. 'fas' or 'fa-solid'
1115
+ * @property {boolean} [pasteAsPlainText] - Force plain-text paste
1116
+ * @property {boolean} [pasteCleanHTML] - Sanitise HTML on paste
1064
1117
  * @property {boolean} [pasteStripAttributes] - Strip class/style/data-* from pasted HTML (default: false)
1065
- * @property {boolean} [allowImageUpload] - Allow file upload in image dialog
1066
- * @property {number} [maxImageSize] - Max upload size in MB
1067
- * @property {number} [tabSize] - Spaces per tab in non-list context
1068
- * @property {Function} [onChange] - Callback on content change
1069
- * @property {Function} [onFocus] - Callback on focus
1070
- * @property {Function} [onBlur] - Callback on blur
1071
- * @property {Function} [onImageUpload] - Custom upload handler: (files) => void
1072
- * @property {boolean} [stickyToolbar] - Stick the toolbar to the viewport top when scrolling
1073
- * @property {number} [stickyToolbarOffset] - Top offset in px for sticky toolbar (e.g. fixed nav height)
1074
- * @property {string} [theme] - 'light' (default) | 'dark'
1075
- * @property {boolean} [codeHighlight] - Auto-load Prism.js for syntax highlighting of code blocks
1076
- * @property {string} [codeHighlightCDN] - CDN base URL for Prism assets (defaults to cdnjs)
1077
- * @property {boolean} [markdownPaste] - Convert pasted Markdown text to HTML (default: true)
1078
- * @property {boolean} [readOnly] - Start editor in read-only / non-editable mode
1079
- * @property {boolean} [spellcheck] - Enable browser spellcheck in the editable area (default: true)
1080
- * @property {string} [direction] - Text direction: 'ltr' (default) | 'rtl'
1081
- * @property {string} [toolbarOverflow] - Toolbar overflow strategy: 'wrap' (default) | 'scroll'
1082
- * @property {boolean} [autoSave] - Auto-save content to localStorage on change
1083
- * @property {string} [autoSaveKey] - localStorage key used for auto-save (default: 'autumnnote-autosave')
1084
- * @property {number} [maxChars] - Maximum character count (0 = unlimited). Shows warning in statusbar.
1085
- * @property {number} [maxWords] - Maximum word count (0 = unlimited). Shows warning in statusbar.
1086
- * @property {boolean} [tableHeaderRow] - Insert a header row (<thead><th>) when creating tables
1087
- * @property {Function} [onPaste] - Callback fired on every paste: ({ text, html }) => void
1088
- * @property {Function} [onSelectionChange] - Callback fired on cursor/selection change: (context) => void
1089
- * @property {string[]} [colorSwatches] - Custom brand colour swatches prepended to the colour-picker palette
1118
+ * @property {boolean} [allowImageUpload] - Allow file upload in image dialog
1119
+ * @property {number} [maxImageSize] - Max upload size in MB
1120
+ * @property {number} [tabSize] - Spaces per tab in non-list context
1121
+ * @property {number} [historyLimit] - Maximum undo/redo history steps
1122
+ * @property {string} [defaultFontFamily] - Default font family applied to the editable area on init
1123
+ * @property {string} [defaultFontSize] - Default font size applied to the editable area on init (e.g. '14px')
1124
+ * @property {string[]} [fontFamilies] - Font families shown in the font-family toolbar dropdown
1125
+ * @property {Function} [onChange] - Callback on content change
1126
+ * @property {Function} [onFocus] - Callback on focus
1127
+ * @property {Function} [onBlur] - Callback on blur
1128
+ * @property {Function} [onInit] - Callback after the editor has initialised
1129
+ * @property {Function} [onImageUpload] - Custom upload handler: (files) => void
1130
+ * @property {Function} [onImageError] - Callback when an image upload error occurs
1131
+ * @property {boolean} [stickyToolbar] - Stick the toolbar to the viewport top when scrolling
1132
+ * @property {number} [stickyToolbarOffset] - Top offset in px for sticky toolbar (e.g. fixed nav height)
1133
+ * @property {string} [theme] - 'light' (default) | 'dark'
1134
+ * @property {boolean} [codeHighlight] - Auto-load Prism.js for syntax highlighting of code blocks
1135
+ * @property {string} [codeHighlightCDN] - CDN base URL for Prism assets (defaults to cdnjs)
1136
+ * @property {boolean} [markdownPaste] - Convert pasted Markdown text to HTML (default: true)
1137
+ * @property {boolean} [readOnly] - Start editor in read-only / non-editable mode
1138
+ * @property {boolean} [spellcheck] - Enable browser spellcheck in the editable area (default: true)
1139
+ * @property {string} [direction] - Text direction: 'ltr' (default) | 'rtl'
1140
+ * @property {string} [toolbarOverflow] - Toolbar overflow strategy: 'wrap' (default) | 'scroll'
1141
+ * @property {boolean} [autoSave] - Auto-save content to localStorage on change
1142
+ * @property {string} [autoSaveKey] - localStorage key used for auto-save (default: 'autumnnote-autosave')
1143
+ * @property {number} [maxChars] - Maximum character count (0 = unlimited). Shows warning in statusbar.
1144
+ * @property {number} [maxWords] - Maximum word count (0 = unlimited). Shows warning in statusbar.
1145
+ * @property {boolean} [tableHeaderRow] - Insert a header row (<thead><th>) when creating tables
1146
+ * @property {Function} [onPaste] - Callback fired on every paste: ({ text, html }) => void
1147
+ * @property {Function} [onSelectionChange] - Callback fired on cursor/selection change: (context) => void
1148
+ * @property {string[]} [colorSwatches] - Custom brand colour swatches prepended to the colour-picker palette
1090
1149
  * @property {Function} [onDestroy] - Callback fired when the editor is destroyed: (context) => void
1091
1150
  * @property {Function} [onCharLimitReached] - Callback fired when the character limit is hit: (context) => void
1092
1151
  * @property {Function} [onWordLimitReached] - Callback fired when the word limit is hit: (context) => void
1093
- * @property {string} [focusColor] - Custom focus ring colour, e.g. '#f97316'. Overrides the default blue.
1152
+ * @property {string} [focusColor] - Custom focus ring colour, e.g. '#f97316'. Overrides the default blue.
1153
+ * @property {boolean} [autoSaveRestore] - Show a restore banner when a previously auto-saved draft exists
1154
+ * @property {number} [autoSaveRestoreTimeout] - Maximum age in days for a draft to be offered for restore (0 = no expiry)
1155
+ * @property {Function} [onAutoSaveRestore] - Callback fired after the user chooses to restore a draft
1156
+ * @property {boolean} [markdownShortcuts] - Convert markdown syntax typed inline to HTML
1157
+ * @property {boolean} [bubbleToolbar] - Show a mini floating toolbar above text selections
1158
+ * @property {string[]} [bubbleToolbarItems] - Button names for the bubble toolbar
1159
+ * @property {object|null} [mention] - @mention configuration (onSearch, minChars, ...)
1160
+ * @property {string} [lang] - Display language or partial locale object override
1094
1161
  */
1095
- /** @type {AsnOptions} */
1096
1162
  var defaultOptions = {
1097
1163
  placeholder: "",
1098
1164
  height: 200,
@@ -1552,6 +1618,8 @@
1552
1618
  rowHeight: "Row Height",
1553
1619
  tableBorderWidth: "Table Border Width",
1554
1620
  deleteTable: "Delete Table",
1621
+ cellBackground: "Cell Background",
1622
+ noShading: "No Shading",
1555
1623
  columnWidthPx: "Column Width (px)",
1556
1624
  rowHeightPx: "Row Height (px)",
1557
1625
  tableBorderWidthPx: "Table Border Width (px)",
@@ -1907,6 +1975,8 @@
1907
1975
  rowHeight: "Chiều cao hàng",
1908
1976
  tableBorderWidth: "Độ rộng viền bảng",
1909
1977
  deleteTable: "Xóa bảng",
1978
+ cellBackground: "Màu Nền Ô",
1979
+ noShading: "Xóa Màu Nền",
1910
1980
  columnWidthPx: "Chiều rộng cột (px)",
1911
1981
  rowHeightPx: "Chiều cao hàng (px)",
1912
1982
  tableBorderWidthPx: "Độ rộng viền bảng (px)",
@@ -4101,14 +4171,17 @@
4101
4171
  } catch (_) {}
4102
4172
  if (!initialContent) initialContent = targetEl.tagName === "TEXTAREA" ? (targetEl.value || "").trim() : (targetEl.innerHTML || "").trim();
4103
4173
  editable.innerHTML = sanitiseHTML(initialContent, { allowIframes: true });
4104
- const defaultFont = options.defaultFontFamily || options.fontFamilies && options.fontFamilies[0];
4174
+ const defaultFont = options.defaultFontFamily || options.fontFamilies?.[0];
4105
4175
  if (defaultFont) editable.style.fontFamily = defaultFont;
4106
4176
  if (options.defaultFontSize) editable.style.fontSize = options.defaultFontSize;
4107
4177
  if (options.height) editable.style.minHeight = `${options.height}px`;
4108
4178
  else if (options.minHeight) editable.style.minHeight = `${options.minHeight}px`;
4109
4179
  if (options.maxHeight) editable.style.maxHeight = `${options.maxHeight}px`;
4110
4180
  container.appendChild(editable);
4111
- if (options.theme === "dark") container.classList.add("an-theme-dark");
4181
+ if (options.theme === "dark") {
4182
+ container.classList.add("an-theme-dark");
4183
+ document.body.classList.add("an-theme-dark");
4184
+ }
4112
4185
  if (options.readOnly) {
4113
4186
  container.classList.add("an-disabled");
4114
4187
  editable.querySelectorAll("ul.an-checklist input[type=\"checkbox\"]").forEach((cb) => {
@@ -4146,7 +4219,7 @@
4146
4219
  constructor(editable, limit = 100) {
4147
4220
  this.editable = editable;
4148
4221
  this._limit = limit;
4149
- /** @type {Array<{html: string, range: {sc: string, so: number, ec: string, eo: number}|null}>} */
4222
+ /** @type {Array<{html: string, images?: Record<string,string>, sel: {start: number, end: number}|null}>} */
4150
4223
  this.stack = [];
4151
4224
  this.stackOffset = -1;
4152
4225
  this._savePoint();
@@ -4160,7 +4233,7 @@
4160
4233
  * @returns {{ start: number, end: number }|null}
4161
4234
  */
4162
4235
  _serializeSelection() {
4163
- const sel = window.getSelection();
4236
+ const sel = globalThis.getSelection();
4164
4237
  if (!sel || sel.rangeCount === 0) return null;
4165
4238
  const range = sel.getRangeAt(0);
4166
4239
  if (!this.editable.contains(range.startContainer)) return null;
@@ -4226,7 +4299,7 @@
4226
4299
  const range = document.createRange();
4227
4300
  range.setStart(startNode, startOff);
4228
4301
  range.setEnd(endNode, endOff);
4229
- const sel = window.getSelection();
4302
+ const sel = globalThis.getSelection();
4230
4303
  sel.removeAllRanges();
4231
4304
  sel.addRange(range);
4232
4305
  } catch (_) {
@@ -4234,7 +4307,7 @@
4234
4307
  const fb = document.createRange();
4235
4308
  fb.setStart(this.editable, 0);
4236
4309
  fb.collapse(true);
4237
- const s = window.getSelection();
4310
+ const s = globalThis.getSelection();
4238
4311
  if (s) {
4239
4312
  s.removeAllRanges();
4240
4313
  s.addRange(fb);
@@ -4298,8 +4371,7 @@
4298
4371
  recordUndo() {
4299
4372
  const current = this._serialize();
4300
4373
  const { html: tokenized } = this._tokenizeImages(current);
4301
- const prev = this.stack[this.stackOffset];
4302
- if (prev && prev.html === tokenized) return;
4374
+ if (this.stack[this.stackOffset]?.html === tokenized) return;
4303
4375
  this._savePoint();
4304
4376
  }
4305
4377
  /**
@@ -4345,8 +4417,7 @@
4345
4417
  * Build an HTML table with the given number of columns and rows, optionally including a header row.
4346
4418
  * @param {number} cols - Number of columns in each row.
4347
4419
  * @param {number} rows - Total number of rows to create (including header when `headerRow` is true).
4348
- * @param {{ headerRow?: boolean }} [opts] - Options object.
4349
- * @param {boolean} [opts.headerRow=false] - When true and `rows > 0`, creates a header row (`<thead>`) plus body rows for the remainder.
4420
+ * @param {{ headerRow?: boolean }} [opts] - Options: `headerRow` creates a `<thead>` when true.
4350
4421
  * @returns {HTMLTableElement} The constructed `<table>` element with a `<tbody>` and optional `<thead>`; each cell contains a `<br>` placeholder.
4351
4422
  */
4352
4423
  function createTable(cols, rows, opts = {}) {
@@ -4380,12 +4451,11 @@
4380
4451
  * @param {number} cols - Number of columns for the new table.
4381
4452
  * @param {number} rows - Number of rows for the new table.
4382
4453
  * @param {{ headerRow?: boolean }} [opts] - Options for table creation.
4383
- * @param {boolean} [opts.headerRow=false] - If true, include a header row as the first row.
4384
4454
  */
4385
4455
  function insertTable(cols, rows, opts = {}) {
4386
4456
  if (cols <= 0 || rows <= 0) return;
4387
4457
  const table = createTable(cols, rows, opts);
4388
- const sel = window.getSelection();
4458
+ const sel = globalThis.getSelection();
4389
4459
  if (!sel || sel.rangeCount === 0) return;
4390
4460
  const range = sel.getRangeAt(0);
4391
4461
  range.deleteContents();
@@ -4403,7 +4473,7 @@
4403
4473
  "PRE"
4404
4474
  ]);
4405
4475
  let anchor = range.startContainer;
4406
- if (anchor.nodeType === 3) anchor = anchor.parentElement;
4476
+ if (anchor?.nodeType === 3) anchor = anchor.parentElement;
4407
4477
  while (anchor && !BLOCK.has(anchor.tagName?.toUpperCase()) && anchor.parentElement) anchor = anchor.parentElement;
4408
4478
  if (anchor && BLOCK.has(anchor.tagName?.toUpperCase()) && anchor.parentNode) {
4409
4479
  anchor.after(table);
@@ -4493,8 +4563,8 @@
4493
4563
  * Inspired by Summernote's Typing module
4494
4564
  */
4495
4565
  var _FA_PATTERN = /\bfa-/;
4496
- var isFAIcon = (n) => !!(n && n.nodeName === "I" && _FA_PATTERN.test(n.className || ""));
4497
- var isZwsAnchor = (n) => !!(n && n.nodeType === Node.TEXT_NODE && (n.textContent === "​" || n.textContent === ""));
4566
+ var isFAIcon = (n) => !!(n?.nodeName === "I" && _FA_PATTERN.test(n.className || ""));
4567
+ var isZwsAnchor = (n) => !!(n?.nodeType === Node.TEXT_NODE && (n.textContent === "​" || n.textContent === ""));
4498
4568
  /**
4499
4569
  * Handles special keydown behaviour inside the editor.
4500
4570
  * @param {KeyboardEvent} event
@@ -4504,7 +4574,7 @@
4504
4574
  */
4505
4575
  function handleKeydown(event, editable, options = {}) {
4506
4576
  const moveCaret = (setFn) => {
4507
- const sel = window.getSelection();
4577
+ const sel = globalThis.getSelection();
4508
4578
  if (!sel) return false;
4509
4579
  const nr = document.createRange();
4510
4580
  setFn(nr);
@@ -4514,14 +4584,14 @@
4514
4584
  return true;
4515
4585
  };
4516
4586
  if (isKey(event, key.BACKSPACE)) {
4517
- const sel = window.getSelection();
4518
- if (sel && sel.rangeCount > 0) {
4587
+ const sel = globalThis.getSelection();
4588
+ if (sel?.rangeCount > 0) {
4519
4589
  const r = sel.getRangeAt(0);
4520
4590
  if (r.collapsed && r.startContainer.nodeType === Node.TEXT_NODE) {
4521
4591
  const textNode = r.startContainer;
4522
4592
  if (r.startOffset === 0 && isFAIcon(textNode.previousSibling)) {
4523
4593
  event.preventDefault();
4524
- textNode.previousSibling.remove();
4594
+ /** @type {ChildNode} */ textNode.previousSibling.remove();
4525
4595
  return true;
4526
4596
  }
4527
4597
  if (r.startOffset === 1 && textNode.textContent === "​" && isFAIcon(textNode.previousSibling)) {
@@ -4545,7 +4615,7 @@
4545
4615
  return false;
4546
4616
  }
4547
4617
  if (isKey(event, key.LEFT) || isKey(event, key.RIGHT)) {
4548
- const sel = window.getSelection();
4618
+ const sel = globalThis.getSelection();
4549
4619
  if (!sel || sel.rangeCount === 0) return false;
4550
4620
  const r = sel.getRangeAt(0);
4551
4621
  if (!r.collapsed) return false;
@@ -4633,7 +4703,7 @@
4633
4703
  else execCommand("indent");
4634
4704
  return true;
4635
4705
  }
4636
- if (para && para.nodeName.toUpperCase() === "PRE") {
4706
+ if (para?.nodeName.toUpperCase() === "PRE") {
4637
4707
  if (event.shiftKey) return false;
4638
4708
  event.preventDefault();
4639
4709
  execCommand("insertText", " ".repeat(options.tabSize || 4));
@@ -4656,18 +4726,18 @@
4656
4726
  if (!range) return false;
4657
4727
  const sc = range.sc;
4658
4728
  const el = sc.nodeType === 3 ? sc.parentElement : sc;
4659
- if (el && el.nodeName === "I" && /\bfa-/.test(el.className || "")) {
4729
+ if (el?.nodeName === "I" && /\bfa-/.test(el.className || "")) {
4660
4730
  const nr = document.createRange();
4661
4731
  nr.setStartAfter(el);
4662
4732
  nr.collapse(true);
4663
- const selI = window.getSelection();
4733
+ const selI = globalThis.getSelection();
4664
4734
  if (selI) {
4665
4735
  selI.removeAllRanges();
4666
4736
  selI.addRange(nr);
4667
4737
  }
4668
4738
  return false;
4669
4739
  }
4670
- const videoWrapper = el && el.closest && el.closest(".an-video-wrapper");
4740
+ const videoWrapper = el?.closest(".an-video-wrapper");
4671
4741
  if (videoWrapper) {
4672
4742
  event.preventDefault();
4673
4743
  const p = document.createElement("p");
@@ -4676,16 +4746,16 @@
4676
4746
  const nr = document.createRange();
4677
4747
  nr.setStart(p, 0);
4678
4748
  nr.collapse(true);
4679
- const sel = window.getSelection();
4749
+ const sel = globalThis.getSelection();
4680
4750
  sel.removeAllRanges();
4681
4751
  sel.addRange(nr);
4682
4752
  return true;
4683
4753
  }
4684
- const checkLi = el && el.closest && el.closest(".an-checklist li");
4754
+ const checkLi = el?.closest(".an-checklist li");
4685
4755
  if (checkLi) {
4686
4756
  event.preventDefault();
4687
4757
  const ul = checkLi.closest(".an-checklist");
4688
- const sel = window.getSelection();
4758
+ const sel = globalThis.getSelection();
4689
4759
  let nativeRange = sel.getRangeAt(0);
4690
4760
  const liText = (li) => Array.from(li.childNodes).filter((n) => !(n.nodeType === 1 && n.tagName === "INPUT")).map((n) => n.textContent).join("").replace(/[\u00a0\u200B]/g, " ").trim();
4691
4761
  if (!liText(checkLi)) {
@@ -4729,12 +4799,12 @@
4729
4799
  return true;
4730
4800
  }
4731
4801
  const para = closestPara(range.sc, editable);
4732
- if (para && para.nodeName.toUpperCase() === "PRE") {
4802
+ if (para?.nodeName.toUpperCase() === "PRE") {
4733
4803
  event.preventDefault();
4734
4804
  execCommand("insertText", "\n");
4735
4805
  return true;
4736
4806
  }
4737
- if (para && para.nodeName.toUpperCase() === "BLOCKQUOTE") {
4807
+ if (para?.nodeName.toUpperCase() === "BLOCKQUOTE") {
4738
4808
  const native = range.toNativeRange();
4739
4809
  native.setEnd(para, para.childNodes.length);
4740
4810
  if (native.toString() === "" && range.isCollapsed()) {
@@ -4781,8 +4851,9 @@
4781
4851
  function _domToMd(node, depth = 0) {
4782
4852
  if (node.nodeType === 3) return node.textContent.replace(/\s+/g, " ");
4783
4853
  if (node.nodeType !== 1) return "";
4784
- const tag = node.nodeName.toLowerCase();
4785
- const inner = () => Array.from(node.childNodes).map((n) => _domToMd(n, depth)).join("");
4854
+ const el = node;
4855
+ const tag = el.nodeName.toLowerCase();
4856
+ const inner = () => Array.from(el.childNodes).map((n) => _domToMd(n, depth)).join("");
4786
4857
  switch (tag) {
4787
4858
  case "p":
4788
4859
  case "div": return `\n\n${inner()}\n\n`;
@@ -4800,34 +4871,34 @@
4800
4871
  case "del":
4801
4872
  case "s":
4802
4873
  case "strike": return `~~${inner()}~~`;
4803
- case "sup": return `^${inner()}`;
4804
- case "sub": return `~${inner()}`;
4874
+ case "sup": return `^${inner()}^`;
4875
+ case "sub": return `~${inner()}~`;
4805
4876
  case "code":
4806
- if (node.closest("pre")) return inner();
4877
+ if (el.closest("pre")) return inner();
4807
4878
  return `\`${inner()}\``;
4808
4879
  case "pre": {
4809
- const codeEl = node.querySelector("code");
4810
- const langMatch = (codeEl && codeEl.className || "").match(/language-(\S+)/);
4811
- return `\n\n\`\`\`${langMatch ? langMatch[1] : ""}\n${(codeEl || node).textContent || ""}\n\`\`\`\n\n`;
4880
+ const codeEl = el.querySelector("code");
4881
+ const langMatch = /language-(\S+)/.exec(codeEl && codeEl.className || "");
4882
+ return `\n\n\`\`\`${langMatch ? langMatch[1] : ""}\n${(codeEl || el).textContent || ""}\n\`\`\`\n\n`;
4812
4883
  }
4813
4884
  case "blockquote": return `\n\n${inner().trim().split("\n").map((l) => `> ${l}`).join("\n")}\n\n`;
4814
4885
  case "a": {
4815
- const href = node.getAttribute("href") || "";
4886
+ const href = el.getAttribute("href") || "";
4816
4887
  return `[${inner()}](${href})`;
4817
4888
  }
4818
4889
  case "img": {
4819
- const src = node.getAttribute("src") || "";
4820
- return `![${node.getAttribute("alt") || ""}](${src})`;
4890
+ const src = el.getAttribute("src") || "";
4891
+ return `![${el.getAttribute("alt") || ""}](${src})`;
4821
4892
  }
4822
4893
  case "ul": {
4823
- const items = Array.from(node.querySelectorAll(":scope > li"));
4894
+ const items = Array.from(el.querySelectorAll(":scope > li"));
4824
4895
  if (!items.length) return inner();
4825
4896
  const indent = " ".repeat(depth);
4826
4897
  const lines = items.map((li) => `${indent}- ${_domToMd(li, depth + 1).trim()}`).join("\n");
4827
4898
  return depth === 0 ? `\n\n${lines}\n\n` : `\n${lines}`;
4828
4899
  }
4829
4900
  case "ol": {
4830
- const items = Array.from(node.querySelectorAll(":scope > li"));
4901
+ const items = Array.from(el.querySelectorAll(":scope > li"));
4831
4902
  if (!items.length) return inner();
4832
4903
  const indent = " ".repeat(depth);
4833
4904
  const lines = items.map((li, i) => `${indent}${i + 1}. ${_domToMd(li, depth + 1).trim()}`).join("\n");
@@ -4836,9 +4907,9 @@
4836
4907
  case "li": return inner();
4837
4908
  case "hr": return "\n\n---\n\n";
4838
4909
  case "table": {
4839
- const rows = Array.from(node.querySelectorAll("tr"));
4910
+ const rows = Array.from(el.querySelectorAll("tr"));
4840
4911
  if (!rows.length) return inner();
4841
- const cellTexts = rows.map((tr) => Array.from(tr.querySelectorAll("th, td")).map((c) => c.textContent.trim().replace(/\|/g, "\\|")));
4912
+ const cellTexts = rows.map((tr) => Array.from(tr.querySelectorAll("th, td")).map((c) => c.textContent.trim().replaceAll("|", "\\|")));
4842
4913
  const cols = Math.max(...cellTexts.map((r) => r.length));
4843
4914
  const padRow = (row) => {
4844
4915
  const r = [...row];
@@ -4847,7 +4918,7 @@
4847
4918
  };
4848
4919
  let md = "\n\n";
4849
4920
  md += `| ${padRow(cellTexts[0]).join(" | ")} |\n`;
4850
- md += `| ${Array(cols).fill("---").join(" | ")} |\n`;
4921
+ md += `| ${new Array(cols).fill("---").join(" | ")} |\n`;
4851
4922
  for (let r = 1; r < cellTexts.length; r++) md += `| ${padRow(cellTexts[r]).join(" | ")} |\n`;
4852
4923
  return md + "\n";
4853
4924
  }
@@ -4871,12 +4942,12 @@
4871
4942
  * @returns {string}
4872
4943
  */
4873
4944
  function markdownToHTML(text) {
4874
- const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
4945
+ const lines = text.replaceAll("\r\n", "\n").replaceAll("\r", "\n").split("\n");
4875
4946
  const out = [];
4876
4947
  let i = 0;
4877
4948
  while (i < lines.length) {
4878
4949
  const line = lines[i];
4879
- const fenceMatch = line.match(/^```(\S*)$/);
4950
+ const fenceMatch = /^```(\S*)$/.exec(line);
4880
4951
  if (fenceMatch) {
4881
4952
  const lang = fenceMatch[1];
4882
4953
  const codeLines = [];
@@ -4895,7 +4966,7 @@
4895
4966
  i++;
4896
4967
  continue;
4897
4968
  }
4898
- const hMatch = line.match(/^(#{1,6})\s+(.+)$/);
4969
+ const hMatch = /^(#{1,6})\s+(.+)$/.exec(line);
4899
4970
  if (hMatch) {
4900
4971
  const level = hMatch[1].length;
4901
4972
  out.push(`<h${level}>${_inline(hMatch[2])}</h${level}>`);
@@ -4942,7 +5013,8 @@
4942
5013
  i++;
4943
5014
  }
4944
5015
  const thead = `<thead><tr>${headerCells.map((c) => `<th>${_inline(c)}</th>`).join("")}</tr></thead>`;
4945
- const tbody = bodyRows.length ? `<tbody>${bodyRows.map((row) => `<tr>${row.map((c) => `<td>${_inline(c)}</td>`).join("")}</tr>`).join("")}</tbody>` : "";
5016
+ const renderRow = (row) => `<tr>${row.map((c) => `<td>${_inline(c)}</td>`).join("")}</tr>`;
5017
+ const tbody = bodyRows.length ? `<tbody>${bodyRows.map(renderRow).join("")}</tbody>` : "";
4946
5018
  out.push(`<table>${thead}${tbody}</table>`);
4947
5019
  continue;
4948
5020
  }
@@ -4978,10 +5050,51 @@
4978
5050
  return text;
4979
5051
  }
4980
5052
  function _esc(v) {
4981
- return String(v).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
5053
+ return String(v).replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
4982
5054
  }
4983
5055
  function _escAttr(v) {
4984
- return String(v).replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
5056
+ return String(v).replaceAll("&", "&amp;").replaceAll("\"", "&quot;").replaceAll("'", "&#39;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
5057
+ }
5058
+ //#endregion
5059
+ //#region src/js/core/detectLang.js
5060
+ /**
5061
+ * detectLang.js — Heuristic programming-language detection for code snippets.
5062
+ *
5063
+ * Returns a Prism.js language identifier or null when no language can be
5064
+ * determined with reasonable confidence.
5065
+ *
5066
+ * Detection order (conflicts in parentheses):
5067
+ * TypeScript → Rust → PHP → Java → Kotlin → Swift → Go
5068
+ * → JavaScript → HTML → CSS → JSON → SQL → Python → Ruby
5069
+ * → Bash → C++ → C# → C → XML
5070
+ *
5071
+ * @param {string} code
5072
+ * @returns {string|null}
5073
+ */
5074
+ function detectLang(code) {
5075
+ if (!code || !code.trim()) return null;
5076
+ const s = code.trim();
5077
+ if (/(:\s*(string|number|boolean|void|never|any|unknown)\b|interface\s+\w+\s*\{|type\s+\w+\s*[=<(]|<\w+>\s*[;,)]|readonly\s+\w|enum\s+\w+\s*\{|\?\s*:\s*\w|as\s+\w+\s*[;,)\]])/.test(s)) return "typescript";
5078
+ if (/\bprintln!\s*\(|\bprint!\s*\(|\bfn\s+\w+\s*(<[^>]*>)?\s*\(|\blet\s+mut\s|\bpub\s+fn\s|\buse\s+std::|\bimpl\s+\w+|\bOption<|\bResult<\w+/.test(s)) return "rust";
5079
+ if (/(<\?php\b|<\?=|\becho\s+.*\$\w|\$this->|\$\w+\s*=\s*\w|\bforeach\s*\(\s*\$|Illuminate\\)/.test(s)) return "php";
5080
+ if (/\bpublic\s+(class|static|void|int|String)\s+\w|System\.out\.(print|println)\s*\(|@(Override|Autowired|Component|Service|Controller)\b|import\s+java\.(util|io|lang|net)\.|throws\s+\w+Exception/.test(s)) return "java";
5081
+ if (/\bfun\s+\w+\s*\(|\bdata\s+class\s+\w+|\bcompanion\s+object\b|\bval\s+\w+\s*:\s*\w|\bprintln\s*\(/.test(s)) return "kotlin";
5082
+ if (/\bguard\s+(let|var)\b|\bprotocol\s+\w+\s*\{|\bextension\s+\w+|\bfunc\s+\w+[^(]*\([^)]*\)\s*->\s*\w|\blet\s+\w+\s*:\s*[A-Z]\w*|\bSwiftUI\b/.test(s)) return "swift";
5083
+ if (/\bpackage\s+\w+\b|\bfmt\.(Print|Println|Sprintf|Errorf|Fprintf)\s*\(|:=\s*\w|\bgoroutine\b|\bchan\s+\w|\bgo\s+func\b/.test(s)) return "go";
5084
+ if (/\b(const\s+\w|let\s+\w+\s*=|var\s+\w+\s*=|function\s+\w|\=>\s*[{(]|import\s+.*\bfrom\b\s*['"]|require\s*\(|console\.(log|error|warn|info)|document\.\w|window\.\w|async\s+function|\bPromise\b|React\.|useState\s*\(|\.then\s*\()/.test(s)) return "javascript";
5085
+ if (/^<!DOCTYPE html/i.test(s) || /<(html|head|body|div|section|article|nav|p|a|img|ul|ol|li|table|form|input|button|script|style)\b[^>]*>/i.test(s)) return "html";
5086
+ if (/(^|\n)\s*(\/\/\s+\S|&[:.[\w]|\$\w+\s*:|@(mixin|include|extend|each|if|for|use|forward)\b|#\{)/.test(s) && /[\w#.*&[\]:(),>+~ -]+\s*\{/.test(s)) return "scss";
5087
+ if (/(^|\n)\s*[\w#.*:[\]&, +-]+\s*\{[^}]*[\w-]+\s*:[^{}:;]+[;}\n]/m.test(s) && !/<\w|function\s|def\s|:\s*(string|number)/.test(s)) return "css";
5088
+ if (/^\s*[{[]/.test(s) && /"\w[\w\s-]*"\s*:/.test(s) && !/\bfunction\b|\bdef\b/.test(s)) return "json";
5089
+ if (/(^|\n)\s*(SELECT\s|INSERT\s+INTO|UPDATE\s+\w|DELETE\s+FROM|CREATE\s+(TABLE|DATABASE|INDEX|VIEW)|DROP\s+(TABLE|DATABASE)|ALTER\s+TABLE|WITH\s+\w+\s+AS\s*\()/im.test(s)) return "sql";
5090
+ if (/\bdef\s+\w+\s*\([^)]*\)\s*:|(^|\n)\s*class\s+\w+.*:\s*$|(^|\n)\s*import\s+\w|(^|\n)\s*from\s+\w+\s+import\s+|\bprint\s*\(|if\s+__name__\s*==\s*['"]__main__['"]/m.test(s)) return "python";
5091
+ if (/\bputs\s+\S|\battr_(accessor|reader|writer)\s|\.each\s+do\s*\|\w+\s*\||\bdo\s*\|\w+\s*\|.*\bend\b|\bdef\s+\w+[^:]*\n[\s\S]*?\bend\b/.test(s)) return "ruby";
5092
+ if (/^#!.*\/(ba|z|da|fi|k)?sh\b/m.test(s) || /\b(echo\s+["']|grep\s+|awk\s+|sed\s+['"\\/-]|chmod\s+|sudo\s+|apt(-get)?\s+install|brew\s+install|npm\s+(install|run|start|build)|pip\s+(install|3\s)|docker\s+(run|build|compose)|kubectl\s+|git\s+(clone|add|commit|push|pull|checkout))\b/.test(s)) return "bash";
5093
+ if (/\bcout\s*<<|\bcin\s*>>|using\s+namespace\s+std\b|std::\w|\btemplate\s*<\w|\b#include\s*<(iostream|vector|map|set|algorithm|string|memory)>/.test(s)) return "cpp";
5094
+ if (/\busing\s+System\b|Console\.(Write|WriteLine)\s*\(|\bget;\s*set;|\basync\s+Task[<\s]|IEnumerable<|\bLINQ\b|\.Select\s*\(|\.Where\s*\(/.test(s)) return "csharp";
5095
+ if (/\b#include\s*<(stdio|stdlib|string|math|time|ctype)\.h>|\bprintf\s*\(|\bscanf\s*\(|int\s+main\s*\(\s*(void|int\s+argc)|\bmalloc\s*\(|\bfree\s*\(/.test(s) && !/namespace|cout|cin|std::/.test(s)) return "c";
5096
+ if (/^<\?xml\s/i.test(s) || /xmlns:|<\/[\w:]+>/.test(s)) return "xml";
5097
+ return null;
4985
5098
  }
4986
5099
  //#endregion
4987
5100
  //#region src/js/module/Editor.js
@@ -5022,7 +5135,7 @@
5022
5135
  const onBeforeInput = (event) => this._enforceLimit(event);
5023
5136
  const onSelChange = () => {
5024
5137
  if (!this.context._alive) return;
5025
- const sel = window.getSelection();
5138
+ const sel = globalThis.getSelection();
5026
5139
  if (sel && sel.rangeCount > 0 && editable.contains(sel.anchorNode)) {
5027
5140
  this.context.invoke("toolbar.refresh");
5028
5141
  if (typeof this.options.onSelectionChange === "function") this.options.onSelectionChange(this.context);
@@ -5032,13 +5145,14 @@
5032
5145
  if (e.target.type === "checkbox" && e.target.closest(".an-checklist")) this.afterCommand();
5033
5146
  };
5034
5147
  const fixChecklistCursor = (event) => {
5035
- const sel = window.getSelection();
5148
+ const sel = globalThis.getSelection();
5036
5149
  if (!sel || !sel.rangeCount) return;
5037
5150
  const r = sel.getRangeAt(0);
5038
5151
  if (!r.collapsed) return;
5039
5152
  const sc = r.startContainer;
5040
5153
  if (sc.nodeType !== Node.ELEMENT_NODE) return;
5041
- const li = sc.matches && sc.matches(".an-checklist li") ? sc : null;
5154
+ const scEl = sc;
5155
+ const li = scEl.matches(".an-checklist li") ? scEl : null;
5042
5156
  if (!li) return;
5043
5157
  const cb = li.querySelector("input[type=\"checkbox\"]");
5044
5158
  if (!cb) return;
@@ -5078,33 +5192,37 @@
5078
5192
  return;
5079
5193
  }
5080
5194
  const target = e.target;
5081
- if (target && (target.nodeName === "IFRAME" || target.closest && target.closest(".an-video-wrapper"))) e.preventDefault();
5195
+ if (target && (target.nodeName === "IFRAME" || target.closest(".an-video-wrapper"))) e.preventDefault();
5082
5196
  }), on(editable, "drop", (e) => {
5083
5197
  if (isReadOnly()) e.preventDefault();
5084
5198
  }));
5085
5199
  /** @type {string|null} 'superscript' | 'subscript' | null */
5086
5200
  let _compositionSupSub = null;
5087
5201
  const onCompositionStart = () => {
5088
- const sel = window.getSelection();
5202
+ const sel = globalThis.getSelection();
5089
5203
  if (!sel || !sel.rangeCount) {
5090
5204
  _compositionSupSub = null;
5091
5205
  return;
5092
5206
  }
5093
5207
  let node = sel.getRangeAt(0).startContainer;
5094
5208
  if (node.nodeType === Node.TEXT_NODE) node = node.parentElement;
5095
- if (node && node.closest) if (node.closest("sup")) _compositionSupSub = "superscript";
5096
- else if (node.closest("sub")) _compositionSupSub = "subscript";
5097
- else _compositionSupSub = null;
5209
+ if (node) {
5210
+ const el = node;
5211
+ if (el.closest("sup")) _compositionSupSub = "superscript";
5212
+ else if (el.closest("sub")) _compositionSupSub = "subscript";
5213
+ else _compositionSupSub = null;
5214
+ }
5098
5215
  };
5099
5216
  const onCompositionEnd = () => {
5100
5217
  const tag = _compositionSupSub;
5101
5218
  _compositionSupSub = null;
5102
5219
  if (!tag) return;
5103
- const sel = window.getSelection();
5220
+ const sel = globalThis.getSelection();
5104
5221
  if (!sel || !sel.rangeCount) return;
5105
5222
  let node = sel.getRangeAt(0).startContainer;
5106
5223
  if (node.nodeType === Node.TEXT_NODE) node = node.parentElement;
5107
- if (!(node && node.closest && (tag === "superscript" ? node.closest("sup") : node.closest("sub")))) document.execCommand(tag);
5224
+ const el = node;
5225
+ if (!(tag === "superscript" ? el?.closest("sup") : el?.closest("sub"))) document.execCommand(tag);
5108
5226
  };
5109
5227
  this._disposers.push(on(editable, "compositionstart", onCompositionStart), on(editable, "compositionend", onCompositionEnd));
5110
5228
  }
@@ -5178,7 +5296,7 @@
5178
5296
  if (type === "insertFromPaste" || type === "insertFromDrop") return;
5179
5297
  if (!type.startsWith("insert")) return;
5180
5298
  const text = this.context.layoutInfo.editable.innerText || "";
5181
- const chars = text.replace(/\n/g, "").length;
5299
+ const chars = text.replaceAll("\n", "").length;
5182
5300
  if (maxChars && chars >= maxChars) {
5183
5301
  event.preventDefault();
5184
5302
  if (typeof this.options.onCharLimitReached === "function") this.options.onCharLimitReached(this.context);
@@ -5193,6 +5311,7 @@
5193
5311
  }
5194
5312
  afterCommand() {
5195
5313
  this._cleanOrphanedFigures();
5314
+ this._ensureTrailingParagraph();
5196
5315
  this.context.invoke("toolbar.refresh");
5197
5316
  this.context.invoke("statusbar.update");
5198
5317
  this._scheduleSnapshot();
@@ -5216,9 +5335,34 @@
5216
5335
  */
5217
5336
  _cleanOrphanedFigures() {
5218
5337
  this.context.layoutInfo.editable.querySelectorAll("figure.an-figure").forEach((fig) => {
5219
- if (!fig.querySelector("img")) fig.parentNode.removeChild(fig);
5338
+ if (!fig.querySelector("img")) fig.remove();
5220
5339
  });
5221
5340
  }
5341
+ /**
5342
+ * Ensures the editable always ends with a plain paragraph so the cursor can
5343
+ * be placed after block elements that do not naturally allow it
5344
+ * (pre, blockquote, table, figure, ul, ol, hr).
5345
+ * Without this, clicking below the last such element does nothing.
5346
+ */
5347
+ _ensureTrailingParagraph() {
5348
+ const editable = this.context.layoutInfo.editable;
5349
+ if (!editable) return;
5350
+ const last = editable.lastElementChild;
5351
+ if (!last) return;
5352
+ if (new Set([
5353
+ "PRE",
5354
+ "BLOCKQUOTE",
5355
+ "TABLE",
5356
+ "FIGURE",
5357
+ "UL",
5358
+ "OL",
5359
+ "HR"
5360
+ ]).has(last.nodeName)) {
5361
+ const p = document.createElement("p");
5362
+ p.innerHTML = "<br>";
5363
+ editable.appendChild(p);
5364
+ }
5365
+ }
5222
5366
  focus() {
5223
5367
  this.context.layoutInfo.editable.focus();
5224
5368
  }
@@ -5227,7 +5371,7 @@
5227
5371
  * @returns {string}
5228
5372
  */
5229
5373
  getHTML() {
5230
- const raw = this.context.layoutInfo.editable.innerHTML.replace(/\u200B/g, "");
5374
+ const raw = this.context.layoutInfo.editable.innerHTML.replaceAll("​", "");
5231
5375
  return this.context.invoke("clipboard.resolveImages", raw) ?? raw;
5232
5376
  }
5233
5377
  /**
@@ -5272,7 +5416,7 @@
5272
5416
  * @returns {boolean}
5273
5417
  */
5274
5418
  isEmpty() {
5275
- const text = (this.context.layoutInfo.editable.innerText || "").trim().replace(/\u00a0/g, "");
5419
+ const text = (this.context.layoutInfo.editable.innerText || "").trim().replaceAll("\xA0", "");
5276
5420
  const hasMedia = !!this.context.layoutInfo.editable.querySelector("img, video, iframe, table");
5277
5421
  return !text && !hasMedia;
5278
5422
  }
@@ -5402,10 +5546,24 @@
5402
5546
  this.context.print();
5403
5547
  }
5404
5548
  /**
5405
- * @param {string} tagName - e.g. 'h1', 'p', 'blockquote'
5549
+ * @param {string} tagName - e.g. 'h1', 'p', 'blockquote', 'pre'
5406
5550
  */
5407
5551
  formatBlock(tagName) {
5408
5552
  formatBlock(tagName);
5553
+ if (tagName === "pre") {
5554
+ const sel = globalThis.getSelection();
5555
+ if (sel && sel.rangeCount > 0) {
5556
+ const container = sel.getRangeAt(0).commonAncestorContainer;
5557
+ const pre = container.nodeType === 1 ? container.closest("pre") : container.parentElement?.closest("pre");
5558
+ if (pre && !pre.dataset.language) {
5559
+ const lang = detectLang(pre.textContent || "");
5560
+ if (lang) {
5561
+ this.context.invoke("codeTooltip.applyLanguage", pre, lang);
5562
+ return;
5563
+ }
5564
+ }
5565
+ }
5566
+ }
5409
5567
  this.afterCommand();
5410
5568
  }
5411
5569
  /**
@@ -5450,7 +5608,7 @@
5450
5608
  * @param {boolean} [openInNewTab=false]
5451
5609
  */
5452
5610
  insertLink(url, text, openInNewTab = false) {
5453
- const sel = window.getSelection();
5611
+ const sel = globalThis.getSelection();
5454
5612
  if (!sel || sel.rangeCount === 0) return;
5455
5613
  const safeUrl = sanitiseUrl(url);
5456
5614
  if (!safeUrl) return;
@@ -5462,8 +5620,8 @@
5462
5620
  if (openInNewTab) {
5463
5621
  const link = this._getClosestAnchor();
5464
5622
  if (link) {
5465
- link.setAttribute("target", "_blank");
5466
- link.setAttribute("rel", "noopener noreferrer");
5623
+ /** @type {Element} */ link.setAttribute("target", "_blank");
5624
+ /** @type {Element} */ link.setAttribute("rel", "noopener noreferrer");
5467
5625
  }
5468
5626
  }
5469
5627
  }
@@ -5513,7 +5671,7 @@
5513
5671
  this.afterCommand();
5514
5672
  }
5515
5673
  _getClosestAnchor() {
5516
- const sel = window.getSelection();
5674
+ const sel = globalThis.getSelection();
5517
5675
  if (!sel || sel.rangeCount === 0) return null;
5518
5676
  let node = sel.getRangeAt(0).startContainer;
5519
5677
  while (node) {
@@ -5528,7 +5686,7 @@
5528
5686
  * @returns {string}
5529
5687
  */
5530
5688
  _escapeAttr(str) {
5531
- return String(str ?? "").replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
5689
+ return String(str ?? "").replaceAll("&", "&amp;").replaceAll("\"", "&quot;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
5532
5690
  }
5533
5691
  };
5534
5692
  //#endregion
@@ -5643,7 +5801,7 @@
5643
5801
  this._refreshRaf = null;
5644
5802
  this._disposers.forEach((d) => d());
5645
5803
  this._disposers = [];
5646
- if (this.el && this.el.parentNode) this.el.parentNode.removeChild(this.el);
5804
+ if (this.el && this.el.parentNode) this.el.remove();
5647
5805
  this.el = null;
5648
5806
  }
5649
5807
  _buildButtons() {
@@ -5711,8 +5869,8 @@
5711
5869
  let isOpen = false;
5712
5870
  const setHighlight = (rows, cols) => {
5713
5871
  cells.forEach((cell) => {
5714
- const r = +cell.getAttribute("data-row");
5715
- const c = +cell.getAttribute("data-col");
5872
+ const r = +cell.dataset.row;
5873
+ const c = +cell.dataset.col;
5716
5874
  cell.classList.toggle("active", r <= rows && c <= cols);
5717
5875
  });
5718
5876
  label.textContent = rows && cols ? `${rows} × ${cols}` : this.context.locale.toolbar.insertTableLabel || "Insert Table";
@@ -5726,8 +5884,8 @@
5726
5884
  const ph = popup.offsetHeight;
5727
5885
  let left = rect.left;
5728
5886
  let top = rect.bottom + 4;
5729
- if (left + pw > window.innerWidth - 8) left = Math.max(8, window.innerWidth - pw - 8);
5730
- if (top + ph > window.innerHeight - 8) top = rect.top - ph - 4;
5887
+ if (left + pw > globalThis.innerWidth - 8) left = Math.max(8, globalThis.innerWidth - pw - 8);
5888
+ if (top + ph > globalThis.innerHeight - 8) top = rect.top - ph - 4;
5731
5889
  popup.style.left = `${left}px`;
5732
5890
  popup.style.top = `${top}px`;
5733
5891
  popup.style.visibility = "";
@@ -5745,16 +5903,16 @@
5745
5903
  else openPopup();
5746
5904
  });
5747
5905
  const d2 = on(grid, "mouseover", (e) => {
5748
- const cell = e.target.closest(".an-table-cell");
5906
+ const cell = e.target?.closest(".an-table-cell");
5749
5907
  if (!cell) return;
5750
- setHighlight(+cell.getAttribute("data-row"), +cell.getAttribute("data-col"));
5908
+ setHighlight(+cell.dataset.row, +cell.dataset.col);
5751
5909
  });
5752
5910
  const d3 = on(grid, "mouseleave", () => setHighlight(0, 0));
5753
5911
  const d4 = on(grid, "click", (e) => {
5754
- const cell = e.target.closest(".an-table-cell");
5912
+ const cell = e.target?.closest(".an-table-cell");
5755
5913
  if (!cell) return;
5756
- const rows = +cell.getAttribute("data-row");
5757
- const cols = +cell.getAttribute("data-col");
5914
+ const rows = +cell.dataset.row;
5915
+ const cols = +cell.dataset.col;
5758
5916
  closePopup();
5759
5917
  this.context.invoke("editor.focus");
5760
5918
  def.action(this.context, rows, cols);
@@ -5763,7 +5921,7 @@
5763
5921
  if (isOpen) closePopup();
5764
5922
  });
5765
5923
  this._disposers.push(d1, d2, d3, d4, d5, () => {
5766
- if (popup.parentNode) popup.parentNode.removeChild(popup);
5924
+ if (popup.parentNode) popup.remove();
5767
5925
  });
5768
5926
  wrap.appendChild(btn);
5769
5927
  document.body.appendChild(popup);
@@ -5853,13 +6011,13 @@
5853
6011
  /** @type {Range|null} saved selection range before popup opens */
5854
6012
  let savedRange = null;
5855
6013
  const saveSelection = () => {
5856
- const sel = window.getSelection();
6014
+ const sel = globalThis.getSelection();
5857
6015
  savedRange = sel && sel.rangeCount ? sel.getRangeAt(0).cloneRange() : null;
5858
6016
  };
5859
6017
  const restoreSelection = () => {
5860
6018
  if (!savedRange) return;
5861
6019
  try {
5862
- const sel = window.getSelection();
6020
+ const sel = globalThis.getSelection();
5863
6021
  if (!sel) return;
5864
6022
  sel.removeAllRanges();
5865
6023
  sel.addRange(savedRange);
@@ -5874,7 +6032,7 @@
5874
6032
  const rect = arrowBtn.getBoundingClientRect();
5875
6033
  const popupMinW = 184;
5876
6034
  let left = rect.left;
5877
- if (left + popupMinW > window.innerWidth) left = rect.right - popupMinW;
6035
+ if (left + popupMinW > globalThis.innerWidth) left = rect.right - popupMinW;
5878
6036
  popup.style.top = `${rect.bottom + 4}px`;
5879
6037
  popup.style.left = `${Math.max(4, left)}px`;
5880
6038
  popup.style.display = "block";
@@ -5914,11 +6072,17 @@
5914
6072
  e.preventDefault();
5915
6073
  });
5916
6074
  const d3b = on(swatches, "click", (e) => {
5917
- const sw = e.target.closest(".an-color-swatch");
5918
- if (sw) applyColor(sw.dataset.color);
6075
+ const sw = e.target?.closest(".an-color-swatch");
6076
+ if (sw) applyColor(
6077
+ /** @type {HTMLElement} */
6078
+ sw.dataset.color
6079
+ );
5919
6080
  });
5920
6081
  const d4 = on(colorInput, "change", (e) => {
5921
- applyColor(e.target.value);
6082
+ applyColor(
6083
+ /** @type {HTMLInputElement} */
6084
+ e.target.value
6085
+ );
5922
6086
  });
5923
6087
  const d5 = on(document, "click", (e) => {
5924
6088
  if (isOpen && !wrap.contains(e.target) && !popup.contains(e.target)) closePopup();
@@ -5931,9 +6095,9 @@
5931
6095
  passive: true,
5932
6096
  capture: true
5933
6097
  });
5934
- window.addEventListener("resize", onScrollResize, { passive: true });
5935
- this._disposers.push(d1, d2, d2b, d3, d3b, d4, d5, d6, () => document.removeEventListener("scroll", onScrollResize, { capture: true }), () => window.removeEventListener("resize", onScrollResize), () => {
5936
- if (popup.parentNode) popup.parentNode.removeChild(popup);
6098
+ globalThis.addEventListener("resize", onScrollResize, { passive: true });
6099
+ this._disposers.push(d1, d2, d2b, d3, d3b, d4, d5, d6, () => document.removeEventListener("scroll", onScrollResize, { capture: true }), () => globalThis.removeEventListener("resize", onScrollResize), () => {
6100
+ if (popup.parentNode) popup.remove();
5937
6101
  });
5938
6102
  this._colorPickerClosers.push(closePopup);
5939
6103
  this._disposers.push(() => {
@@ -5977,7 +6141,7 @@
5977
6141
  /** @type {Range|null} */
5978
6142
  let _savedRange = null;
5979
6143
  const dMousedown = on(select, "mousedown", () => {
5980
- const sel = window.getSelection();
6144
+ const sel = globalThis.getSelection();
5981
6145
  _savedRange = sel && sel.rangeCount ? sel.getRangeAt(0).cloneRange() : null;
5982
6146
  });
5983
6147
  const disposer = on(select, "change", (e) => {
@@ -5986,7 +6150,7 @@
5986
6150
  if (!value || selectedOpt.disabled) return;
5987
6151
  this.context.invoke("editor.focus");
5988
6152
  if (_savedRange) try {
5989
- const sel = window.getSelection();
6153
+ const sel = globalThis.getSelection();
5990
6154
  if (sel) {
5991
6155
  sel.removeAllRanges();
5992
6156
  sel.addRange(_savedRange);
@@ -6051,17 +6215,25 @@
6051
6215
  if (!this.el) return;
6052
6216
  const btnMap = this._btnMap || /* @__PURE__ */ new Map();
6053
6217
  this.el.querySelectorAll("button[data-btn]").forEach((btn) => {
6054
- const def = btnMap.get(btn.getAttribute("data-btn"));
6218
+ const def = btnMap.get(
6219
+ /** @type {HTMLElement} */
6220
+ btn.dataset.btn
6221
+ );
6055
6222
  if (def && typeof def.isActive === "function") btn.classList.toggle("active", !!def.isActive(this.context));
6056
- if (def && typeof def.isDisabled === "function") btn.disabled = !!def.isDisabled(this.context);
6223
+ if (def && typeof def.isDisabled === "function")
6224
+ /** @type {HTMLButtonElement} */ btn.disabled = !!def.isDisabled(this.context);
6057
6225
  });
6058
6226
  this.el.querySelectorAll("select[data-btn]").forEach((select) => {
6059
- const def = btnMap.get(select.getAttribute("data-btn"));
6227
+ const def = btnMap.get(
6228
+ /** @type {HTMLElement} */
6229
+ select.dataset.btn
6230
+ );
6060
6231
  if (!def || typeof def.getValue !== "function") return;
6061
6232
  let raw = (def.getValue(this.context) || "").replace(/["']/g, "").trim();
6062
6233
  if (!raw) raw = this.options.defaultFontFamily || this.options.fontFamilies && this.options.fontFamilies[0] || "";
6063
- const matched = Array.from(select.options).find((opt) => opt.value && opt.value.toLowerCase() === raw.toLowerCase());
6064
- select.value = matched ? matched.value : "";
6234
+ const sel = select;
6235
+ const matched = Array.from(sel.options).find((opt) => opt.value && opt.value.toLowerCase() === raw.toLowerCase());
6236
+ sel.value = matched ? matched.value : "";
6065
6237
  });
6066
6238
  }
6067
6239
  /**
@@ -6181,7 +6353,7 @@
6181
6353
  this._dragDisposers.forEach((d) => d());
6182
6354
  this._dragDisposers = null;
6183
6355
  }
6184
- if (this.el && this.el.parentNode) this.el.parentNode.removeChild(this.el);
6356
+ this.el?.remove();
6185
6357
  this.el = null;
6186
6358
  }
6187
6359
  _bindResize(handle) {
@@ -6245,7 +6417,7 @@
6245
6417
  if (!this._wordCountEl || !this._charCountEl) return;
6246
6418
  const text = this.context.layoutInfo.editable.textContent || "";
6247
6419
  const words = _countWords(text);
6248
- const chars = text.replace(/\n/g, "").length;
6420
+ const chars = text.replaceAll("\n", "").length;
6249
6421
  const maxWords = this.options.maxWords || 0;
6250
6422
  const maxChars = this.options.maxChars || 0;
6251
6423
  const LS = this.context.locale.statusbar;
@@ -6267,7 +6439,7 @@
6267
6439
  * @returns {number}
6268
6440
  */
6269
6441
  getCharCount() {
6270
- return (this.context.layoutInfo.editable.innerText || "").replace(/\n/g, "").length;
6442
+ return (this.context.layoutInfo.editable.innerText || "").replaceAll("\n", "").length;
6271
6443
  }
6272
6444
  };
6273
6445
  //#endregion
@@ -6360,7 +6532,7 @@
6360
6532
  if (el.querySelector("a, strong, em, b, i, ul, ol, li, table, img, blockquote, pre, code, h1, h2, h3, h4, h5, h6")) continue;
6361
6533
  const parent = el.parentNode;
6362
6534
  while (el.firstChild) parent.insertBefore(el.firstChild, el);
6363
- parent.removeChild(el);
6535
+ el.remove();
6364
6536
  }
6365
6537
  doc.querySelectorAll("*").forEach((el) => {
6366
6538
  el.removeAttribute("class");
@@ -6402,7 +6574,7 @@
6402
6574
  this._forcePlain = !!val;
6403
6575
  }
6404
6576
  _onPaste(event) {
6405
- const clipboardData = event.clipboardData || window.clipboardData;
6577
+ const clipboardData = event.clipboardData || globalThis.clipboardData;
6406
6578
  if (!clipboardData) return;
6407
6579
  const forcePlain = this._forcePlain;
6408
6580
  this._forcePlain = false;
@@ -6446,7 +6618,6 @@
6446
6618
  if (this.options.pasteStripAttributes) html = this._stripAttributes(html);
6447
6619
  execCommand("insertHTML", html);
6448
6620
  this.context.invoke("editor.afterCommand");
6449
- return;
6450
6621
  }
6451
6622
  }
6452
6623
  _onDragover(event) {
@@ -6478,17 +6649,17 @@
6478
6649
  this.options.onImageUpload(files);
6479
6650
  return;
6480
6651
  }
6481
- const UNSUPPORTED = [
6652
+ const UNSUPPORTED = new Set([
6482
6653
  "image/tiff",
6483
6654
  "image/x-tiff",
6484
6655
  "image/bmp",
6485
6656
  "image/x-bmp",
6486
6657
  "image/x-ms-bmp"
6487
- ];
6658
+ ]);
6488
6659
  const maxBytes = (this.options.maxImageSize || 5) * 1024 * 1024;
6489
6660
  files.forEach((file) => {
6490
6661
  if (!file || !file.type.startsWith("image/")) return;
6491
- if (UNSUPPORTED.includes(file.type)) {
6662
+ if (UNSUPPORTED.has(file.type)) {
6492
6663
  const message = `Image format "${file.type}" is not supported for display in web browsers. Please convert to PNG, JPEG, or WebP first.`;
6493
6664
  this.context.triggerEvent("imageError", {
6494
6665
  file,
@@ -6540,10 +6711,10 @@
6540
6711
  */
6541
6712
  _dataUrlToBlob(dataUrl) {
6542
6713
  const [header, b64] = dataUrl.split(",");
6543
- const mime = header.match(/:(.*?);/)?.[1] ?? "image/png";
6714
+ const mime = /:(.*?);/.exec(header)?.[1] ?? "image/png";
6544
6715
  const binary = atob(b64);
6545
6716
  const arr = new Uint8Array(binary.length);
6546
- for (let i = 0; i < binary.length; i++) arr[i] = binary.charCodeAt(i);
6717
+ for (let i = 0; i < binary.length; i++) arr[i] = binary.codePointAt(i);
6547
6718
  return new Blob([arr], { type: mime });
6548
6719
  }
6549
6720
  /**
@@ -6611,7 +6782,7 @@
6611
6782
  }
6612
6783
  }
6613
6784
  if (!range) return;
6614
- const sel = window.getSelection();
6785
+ const sel = globalThis.getSelection();
6615
6786
  if (sel) {
6616
6787
  sel.removeAllRanges();
6617
6788
  sel.addRange(range);
@@ -6623,7 +6794,7 @@
6623
6794
  * @returns {string}
6624
6795
  */
6625
6796
  _escapeHTML(str) {
6626
- return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
6797
+ return str.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&#039;");
6627
6798
  }
6628
6799
  };
6629
6800
  //#endregion
@@ -6660,7 +6831,7 @@
6660
6831
  _update() {
6661
6832
  const editable = this.context.layoutInfo.editable;
6662
6833
  const isFocused = document.activeElement === editable;
6663
- const isEmpty = !(editable.textContent.replace(/\u200B/g, "").trim().length > 0) && !editable.querySelector("img, table, hr, .an-video-wrapper");
6834
+ const isEmpty = !(editable.textContent.replaceAll("​", "").trim().length > 0) && !editable.querySelector("img, table, hr, .an-video-wrapper");
6664
6835
  editable.classList.toggle("an-placeholder", isEmpty && !isFocused);
6665
6836
  }
6666
6837
  };
@@ -6688,7 +6859,7 @@
6688
6859
  destroy() {
6689
6860
  this._disposers.forEach((d) => d());
6690
6861
  this._disposers = [];
6691
- if (this._textarea && this._textarea.parentNode) this._textarea.parentNode.removeChild(this._textarea);
6862
+ this._textarea?.remove();
6692
6863
  this._textarea = null;
6693
6864
  }
6694
6865
  toggle() {
@@ -6719,7 +6890,7 @@
6719
6890
  if (!this._active || !this._textarea) return;
6720
6891
  const { editable } = this.context.layoutInfo;
6721
6892
  editable.innerHTML = sanitiseHTML(this._textarea.value, { allowIframes: true });
6722
- this._textarea.parentNode.removeChild(this._textarea);
6893
+ this._textarea.remove();
6723
6894
  this._textarea = null;
6724
6895
  editable.style.display = "";
6725
6896
  this._active = false;
@@ -6743,7 +6914,7 @@
6743
6914
  }).split("\n").map((line) => {
6744
6915
  const stripped = line.trim();
6745
6916
  if (!stripped) return "";
6746
- if (/^<\//.test(stripped)) indent = Math.max(0, indent - 1);
6917
+ if (stripped.startsWith("</")) indent = Math.max(0, indent - 1);
6747
6918
  const out = " ".repeat(indent) + stripped;
6748
6919
  if (/^<[^/!][^>]*[^/]>/.test(stripped) && !INLINE_RE.test(stripped) && !/^<(br|hr|img|input|link|meta)/.test(stripped)) indent++;
6749
6920
  return out;
@@ -6832,7 +7003,7 @@
6832
7003
  destroy() {
6833
7004
  this._disposers.forEach((d) => d());
6834
7005
  this._disposers = [];
6835
- if (this._dialog && this._dialog.parentNode) this._dialog.parentNode.removeChild(this._dialog);
7006
+ if (this._dialog && this._dialog.parentNode) this._dialog.remove();
6836
7007
  this._dialog = null;
6837
7008
  }
6838
7009
  /**
@@ -6855,8 +7026,13 @@
6855
7026
  "aria-label": L.ariaLabel
6856
7027
  });
6857
7028
  const box = createElement("div", { class: "an-dialog-box" });
7029
+ const header = createElement("div", { class: "an-dialog-header" });
7030
+ const iconEl = createElement("span", { class: "an-dialog-icon" });
7031
+ iconEl.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>`;
6858
7032
  const title = createElement("h3", { class: "an-dialog-title" });
6859
7033
  title.textContent = L.title;
7034
+ header.appendChild(iconEl);
7035
+ header.appendChild(title);
6860
7036
  const urlLabel = createElement("label", { class: "an-label" });
6861
7037
  urlLabel.textContent = L.url;
6862
7038
  const urlInput = createElement("input", {
@@ -6901,8 +7077,9 @@
6901
7077
  cancelBtn.textContent = L.cancelBtn;
6902
7078
  btnRow.appendChild(insertBtn);
6903
7079
  btnRow.appendChild(cancelBtn);
6904
- box.append(title, urlLabel, urlInput, textLabel, textInput, tabLabel, btnRow);
7080
+ box.append(header, urlLabel, urlInput, textLabel, textInput, tabLabel, btnRow);
6905
7081
  overlay.appendChild(box);
7082
+ makeDraggable(header, box);
6906
7083
  const d1 = on(insertBtn, "click", () => this._onInsert());
6907
7084
  const d2 = on(cancelBtn, "click", () => this._close());
6908
7085
  const d3 = on(overlay, "click", (e) => {
@@ -6924,7 +7101,7 @@
6924
7101
  return overlay;
6925
7102
  }
6926
7103
  _prefill() {
6927
- const sel = window.getSelection();
7104
+ const sel = globalThis.getSelection();
6928
7105
  let anchor = null;
6929
7106
  if (sel && sel.rangeCount > 0) {
6930
7107
  let node = sel.getRangeAt(0).startContainer;
@@ -7011,7 +7188,7 @@
7011
7188
  destroy() {
7012
7189
  this._disposers.forEach((d) => d());
7013
7190
  this._disposers = [];
7014
- if (this._dialog && this._dialog.parentNode) this._dialog.parentNode.removeChild(this._dialog);
7191
+ if (this._dialog && this._dialog.parentNode) this._dialog.remove();
7015
7192
  this._dialog = null;
7016
7193
  }
7017
7194
  show() {
@@ -7032,8 +7209,13 @@
7032
7209
  "aria-label": L.ariaLabel
7033
7210
  });
7034
7211
  const box = createElement("div", { class: "an-dialog-box" });
7212
+ const header = createElement("div", { class: "an-dialog-header" });
7213
+ const iconEl = createElement("span", { class: "an-dialog-icon" });
7214
+ iconEl.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>`;
7035
7215
  const title = createElement("h3", { class: "an-dialog-title" });
7036
7216
  title.textContent = L.title;
7217
+ header.appendChild(iconEl);
7218
+ header.appendChild(title);
7037
7219
  const urlLabel = createElement("label", { class: "an-label" });
7038
7220
  urlLabel.textContent = L.imageUrl;
7039
7221
  const urlInput = createElement("input", {
@@ -7052,7 +7234,7 @@
7052
7234
  autocomplete: "off"
7053
7235
  });
7054
7236
  this._altInput = altInput;
7055
- box.append(title, urlLabel, urlInput, altLabel, altInput);
7237
+ box.append(header, urlLabel, urlInput, altLabel, altInput);
7056
7238
  const alignLabel = createElement("label", { class: "an-label" });
7057
7239
  alignLabel.textContent = L.alignment;
7058
7240
  const alignRow = createElement("div", { class: "an-align-row" });
@@ -7121,6 +7303,7 @@
7121
7303
  btnRow.appendChild(cancelBtn);
7122
7304
  box.append(btnRow);
7123
7305
  overlay.appendChild(box);
7306
+ makeDraggable(header, box);
7124
7307
  const d1 = on(insertBtn, "click", () => this._onInsert());
7125
7308
  const d2 = on(cancelBtn, "click", () => this._close());
7126
7309
  const d3 = on(overlay, "click", (e) => {
@@ -7237,7 +7420,7 @@
7237
7420
  destroy() {
7238
7421
  this._disposers.forEach((d) => d());
7239
7422
  this._disposers = [];
7240
- if (this._dialog && this._dialog.parentNode) this._dialog.parentNode.removeChild(this._dialog);
7423
+ if (this._dialog && this._dialog.parentNode) this._dialog.remove();
7241
7424
  this._dialog = null;
7242
7425
  }
7243
7426
  show() {
@@ -7258,8 +7441,13 @@
7258
7441
  "aria-label": L.ariaLabel
7259
7442
  });
7260
7443
  const box = createElement("div", { class: "an-dialog-box" });
7444
+ const header = createElement("div", { class: "an-dialog-header" });
7445
+ const iconEl = createElement("span", { class: "an-dialog-icon" });
7446
+ iconEl.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2"/></svg>`;
7261
7447
  const title = createElement("h3", { class: "an-dialog-title" });
7262
7448
  title.textContent = L.title;
7449
+ header.appendChild(iconEl);
7450
+ header.appendChild(title);
7263
7451
  const urlLabel = createElement("label", { class: "an-label" });
7264
7452
  urlLabel.textContent = L.videoUrl;
7265
7453
  const urlInput = createElement("input", {
@@ -7295,8 +7483,9 @@
7295
7483
  cancelBtn.textContent = L.cancelBtn;
7296
7484
  btnRow.appendChild(insertBtn);
7297
7485
  btnRow.appendChild(cancelBtn);
7298
- box.append(title, urlLabel, urlInput, hintEl, widthLabel, widthInput, btnRow);
7486
+ box.append(header, urlLabel, urlInput, hintEl, widthLabel, widthInput, btnRow);
7299
7487
  overlay.appendChild(box);
7488
+ makeDraggable(header, box);
7300
7489
  const d0 = on(urlInput, "input", () => {
7301
7490
  const info = this._parseVideoUrl(urlInput.value.trim());
7302
7491
  hintEl.textContent = info ? this.context.locale.videoDialog.detected(info.type) : urlInput.value ? this.context.locale.videoDialog.unknownFormat : "";
@@ -7317,7 +7506,7 @@
7317
7506
  }
7318
7507
  _onInsert() {
7319
7508
  const rawUrl = this._urlInput.value.trim();
7320
- const width = Math.max(80, parseInt(this._widthInput.value, 10) || 560);
7509
+ const width = Math.max(80, Number.parseInt(this._widthInput.value, 10) || 560);
7321
7510
  if (!rawUrl) {
7322
7511
  this._urlInput.focus();
7323
7512
  return;
@@ -7360,22 +7549,22 @@
7360
7549
  } catch {
7361
7550
  return null;
7362
7551
  }
7363
- const ytWatch = url.match(/(?:youtube\.com\/watch\?(?:.*&)?v=|youtube\.com\/embed\/)([a-zA-Z0-9_-]{11})/);
7552
+ const ytWatch = /(?:youtube\.com\/watch\?(?:.*&)?v=|youtube\.com\/embed\/)([a-zA-Z0-9_-]{11})/.exec(url);
7364
7553
  if (ytWatch) return {
7365
7554
  type: "YouTube",
7366
7555
  embedUrl: `https://www.youtube.com/embed/${ytWatch[1]}`
7367
7556
  };
7368
- const ytShort = url.match(/youtu\.be\/([a-zA-Z0-9_-]{11})/);
7557
+ const ytShort = /youtu\.be\/([a-zA-Z0-9_-]{11})/.exec(url);
7369
7558
  if (ytShort) return {
7370
7559
  type: "YouTube",
7371
7560
  embedUrl: `https://www.youtube.com/embed/${ytShort[1]}`
7372
7561
  };
7373
- const ytShorts = url.match(/youtube\.com\/shorts\/([a-zA-Z0-9_-]{11})/);
7562
+ const ytShorts = /youtube\.com\/shorts\/([a-zA-Z0-9_-]{11})/.exec(url);
7374
7563
  if (ytShorts) return {
7375
7564
  type: "YouTube Shorts",
7376
7565
  embedUrl: `https://www.youtube.com/embed/${ytShorts[1]}`
7377
7566
  };
7378
- const vimeo = url.match(/vimeo\.com\/(\d+)/);
7567
+ const vimeo = /vimeo\.com\/(\d+)/.exec(url);
7379
7568
  if (vimeo) return {
7380
7569
  type: "Vimeo",
7381
7570
  embedUrl: `https://player.vimeo.com/video/${vimeo[1]}`
@@ -7399,7 +7588,7 @@
7399
7588
  const iframeTitle = `${info.type} video player`;
7400
7589
  return `<div class="an-video-wrapper" style="position:relative;display:block;width:${width}px;max-width:100%"><iframe src="${info.embedUrl}" width="${width}" height="${height}" title="${iframeTitle}" frameborder="0" allowfullscreen allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" style="display:block;max-width:100%"></iframe><div class="an-video-shield"></div></div>`;
7401
7590
  }
7402
- if (info && info.type === "Direct video") return `<div class="an-video-wrapper" style="position:relative;display:block;width:${width}px;max-width:100%"><video src="${info.embedUrl.replace(/"/g, "%22")}" width="${width}" height="${height}" controls style="display:block;max-width:100%"></video><div class="an-video-shield"></div></div>`;
7591
+ if (info && info.type === "Direct video") return `<div class="an-video-wrapper" style="position:relative;display:block;width:${width}px;max-width:100%"><video src="${info.embedUrl.replaceAll("\"", "%22")}" width="${width}" height="${height}" controls style="display:block;max-width:100%"></video><div class="an-video-shield"></div></div>`;
7403
7592
  const safeSrc = (() => {
7404
7593
  try {
7405
7594
  const p = new URL(url);
@@ -7410,7 +7599,7 @@
7410
7599
  }
7411
7600
  })();
7412
7601
  if (!safeSrc) return null;
7413
- return `<div class="an-video-wrapper" style="position:relative;display:block;width:${width}px;max-width:100%"><video src="${safeSrc.replace(/"/g, "%22")}" width="${width}" height="${height}" controls style="display:block;max-width:100%"></video><div class="an-video-shield"></div></div>`;
7602
+ return `<div class="an-video-wrapper" style="position:relative;display:block;width:${width}px;max-width:100%"><video src="${safeSrc.replaceAll("\"", "%22")}" width="${width}" height="${height}" controls style="display:block;max-width:100%"></video><div class="an-video-shield"></div></div>`;
7414
7603
  }
7415
7604
  };
7416
7605
  //#endregion
@@ -7479,9 +7668,9 @@
7479
7668
  };
7480
7669
  this._disposers.push(on(editable, "click", (e) => this._onEditorClick(e)), on(editable, "contextmenu", (e) => {
7481
7670
  if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
7482
- const img = e.target.closest("img");
7671
+ const img = e.target?.closest("img");
7483
7672
  if (img) this._select(img);
7484
- }), on(document, "click", (e) => this._onDocClick(e)), on(window, "scroll", () => this._updateOverlayPosition(), { passive: true }), on(window, "resize", onWindowResize, { passive: true }), on(editable, "scroll", () => this._updateOverlayPosition(), { passive: true }));
7673
+ }), on(document, "click", (e) => this._onDocClick(e)), on(globalThis, "scroll", () => this._updateOverlayPosition(), { passive: true }), on(globalThis, "resize", onWindowResize, { passive: true }), on(editable, "scroll", () => this._updateOverlayPosition(), { passive: true }));
7485
7674
  return this;
7486
7675
  }
7487
7676
  destroy() {
@@ -7496,7 +7685,7 @@
7496
7685
  this._positionRaf = null;
7497
7686
  }
7498
7687
  this._deselect();
7499
- if (this._overlay && this._overlay.parentNode) this._overlay.parentNode.removeChild(this._overlay);
7688
+ if (this._overlay && this._overlay.parentNode) this._overlay.remove();
7500
7689
  this._overlay = null;
7501
7690
  }
7502
7691
  /** @returns {HTMLImageElement|null} */
@@ -7698,7 +7887,7 @@
7698
7887
  if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
7699
7888
  const wrapper = this._findWrapper(e.target);
7700
7889
  if (wrapper) this._select(wrapper);
7701
- }), on(document, "click", (e) => this._onDocClick(e)), on(window, "scroll", () => this._updateOverlayPosition(), { passive: true }), on(window, "resize", onWindowResize), on(editable, "scroll", () => this._updateOverlayPosition(), { passive: true }), on(editable, "dragstart", (e) => {
7890
+ }), on(document, "click", (e) => this._onDocClick(e)), on(globalThis, "scroll", () => this._updateOverlayPosition(), { passive: true }), on(globalThis, "resize", onWindowResize), on(editable, "scroll", () => this._updateOverlayPosition(), { passive: true }), on(editable, "dragstart", (e) => {
7702
7891
  if (e.target instanceof Element && e.target.closest(".an-video-wrapper")) e.preventDefault();
7703
7892
  }));
7704
7893
  return this;
@@ -7715,7 +7904,7 @@
7715
7904
  this._positionRaf = null;
7716
7905
  }
7717
7906
  this._deselect();
7718
- if (this._overlay && this._overlay.parentNode) this._overlay.parentNode.removeChild(this._overlay);
7907
+ this._overlay?.remove();
7719
7908
  this._overlay = null;
7720
7909
  }
7721
7910
  /** @returns {HTMLElement|null} */
@@ -7736,7 +7925,7 @@
7736
7925
  */
7737
7926
  _findWrapper(el) {
7738
7927
  if (!el || !(el instanceof Element)) return null;
7739
- if (el.classList && el.classList.contains("an-video-wrapper")) return el;
7928
+ if (el.classList?.contains("an-video-wrapper")) return el;
7740
7929
  const w = el.closest(".an-video-wrapper");
7741
7930
  if (w) return w;
7742
7931
  return null;
@@ -7769,7 +7958,7 @@
7769
7958
  _onDocClick(e) {
7770
7959
  if (!this._activeWrapper) return;
7771
7960
  if (this._activeWrapper.contains(e.target)) return;
7772
- if (this._overlay && this._overlay.contains(e.target)) return;
7961
+ if (this._overlay?.contains(e.target)) return;
7773
7962
  if (e.target.closest(".an-contextmenu")) return;
7774
7963
  this._deselect();
7775
7964
  }
@@ -7889,19 +8078,19 @@
7889
8078
  document.body.appendChild(this._el);
7890
8079
  const editable = this.context.layoutInfo.editable;
7891
8080
  this._disposers.push(on(editable, "mouseover", (e) => {
7892
- const anchor = e.target.closest("a[href]");
8081
+ const anchor = e.target?.closest("a[href]");
7893
8082
  if (anchor && editable.contains(anchor)) this._scheduleShow(anchor);
7894
8083
  }), on(editable, "mouseout", (e) => {
7895
8084
  const to = e.relatedTarget;
7896
8085
  if (!to || !editable.contains(to) && !this._el.contains(to)) this._scheduleHide();
7897
- }));
8086
+ }), on(globalThis, "scroll", () => this._hide(), { passive: true }), on(globalThis, "resize", () => this._hide(), { passive: true }));
7898
8087
  return this;
7899
8088
  }
7900
8089
  destroy() {
7901
8090
  this._clearTimers();
7902
8091
  this._disposers.forEach((d) => d());
7903
8092
  this._disposers = [];
7904
- if (this._el && this._el.parentNode) this._el.parentNode.removeChild(this._el);
8093
+ if (this._el && this._el.parentNode) this._el.remove();
7905
8094
  this._el = null;
7906
8095
  }
7907
8096
  _buildTooltip() {
@@ -7986,8 +8175,8 @@
7986
8175
  const margin = 6;
7987
8176
  let top = rect.bottom + margin;
7988
8177
  let left = rect.left;
7989
- if (top + tipH > window.innerHeight - margin) top = rect.top - tipH - margin;
7990
- if (left + tipW > window.innerWidth - margin) left = window.innerWidth - tipW - margin;
8178
+ if (top + tipH > globalThis.innerHeight - margin) top = rect.top - tipH - margin;
8179
+ if (left + tipW > globalThis.innerWidth - margin) left = globalThis.innerWidth - tipW - margin;
7991
8180
  if (left < margin) left = margin;
7992
8181
  this._el.style.top = `${top}px`;
7993
8182
  this._el.style.left = `${left}px`;
@@ -8002,12 +8191,12 @@
8002
8191
  }
8003
8192
  }
8004
8193
  _openLink() {
8005
- const url = this._activeAnchor && this._activeAnchor.getAttribute("href");
8006
- if (url) window.open(url, "_blank", "noopener,noreferrer");
8194
+ const url = this._activeAnchor?.getAttribute("href");
8195
+ if (url) globalThis.open(url, "_blank", "noopener,noreferrer");
8007
8196
  this._hide();
8008
8197
  }
8009
8198
  _copyLink() {
8010
- const url = this._activeAnchor && this._activeAnchor.getAttribute("href");
8199
+ const url = this._activeAnchor?.getAttribute("href");
8011
8200
  if (url) navigator.clipboard.writeText(url).catch(() => {
8012
8201
  const ta = document.createElement("textarea");
8013
8202
  ta.value = url;
@@ -8016,7 +8205,7 @@
8016
8205
  document.body.appendChild(ta);
8017
8206
  ta.select();
8018
8207
  document.execCommand("copy");
8019
- document.body.removeChild(ta);
8208
+ ta.remove();
8020
8209
  });
8021
8210
  if (this._copyBtn) {
8022
8211
  this._copyBtn.classList.add("an-link-tooltip-btn--copied");
@@ -8027,7 +8216,7 @@
8027
8216
  const anchor = this._activeAnchor;
8028
8217
  if (!anchor) return;
8029
8218
  this._hide();
8030
- const sel = window.getSelection();
8219
+ const sel = globalThis.getSelection();
8031
8220
  const range = document.createRange();
8032
8221
  range.selectNodeContents(anchor);
8033
8222
  sel.removeAllRanges();
@@ -8038,7 +8227,7 @@
8038
8227
  const anchor = this._activeAnchor;
8039
8228
  if (!anchor) return;
8040
8229
  this._hide();
8041
- const sel = window.getSelection();
8230
+ const sel = globalThis.getSelection();
8042
8231
  const range = document.createRange();
8043
8232
  range.selectNode(anchor);
8044
8233
  sel.removeAllRanges();
@@ -8079,21 +8268,22 @@
8079
8268
  const editable = this.context.layoutInfo.editable;
8080
8269
  this._disposers.push(on(editable, "mouseover", (e) => {
8081
8270
  if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
8082
- const img = e.target.closest("img");
8271
+ const img = e.target?.closest("img");
8083
8272
  if (img && editable.contains(img) && !img.closest("a[href]")) this._scheduleShow(img);
8084
8273
  }, { passive: true }), on(editable, "mouseout", (e) => {
8085
8274
  const to = e.relatedTarget;
8086
8275
  if (!to || !editable.contains(to) && !this._el.contains(to)) this._scheduleHide();
8087
8276
  }, { passive: true }), on(document, "click", (e) => {
8088
- if (this._activeImg && !this._activeImg.contains(e.target) && !this._el.contains(e.target)) this._hide();
8089
- }));
8277
+ const et = e.target;
8278
+ if (this._activeImg && !this._activeImg.contains(et) && !this._el.contains(et)) this._hide();
8279
+ }), on(globalThis, "scroll", () => this._hide(), { passive: true }), on(globalThis, "resize", () => this._hide(), { passive: true }));
8090
8280
  return this;
8091
8281
  }
8092
8282
  destroy() {
8093
8283
  this._clearTimers();
8094
8284
  this._disposers.forEach((d) => d());
8095
8285
  this._disposers = [];
8096
- if (this._el && this._el.parentNode) this._el.parentNode.removeChild(this._el);
8286
+ this._el?.remove();
8097
8287
  this._el = null;
8098
8288
  }
8099
8289
  _buildTooltip() {
@@ -8170,7 +8360,7 @@
8170
8360
  clearTimeout(this._hideTimer);
8171
8361
  this._hideTimer = setTimeout(() => this._hide(), HIDE_DELAY$3);
8172
8362
  }
8173
- _show(img) {
8363
+ _show(_img) {
8174
8364
  this._el.style.display = "flex";
8175
8365
  requestAnimationFrame(() => {
8176
8366
  if (this._activeImg) this._positionNear(this._activeImg);
@@ -8194,8 +8384,8 @@
8194
8384
  const margin = 6;
8195
8385
  let top = rect.bottom + margin;
8196
8386
  let left = rect.left + (rect.width - tipW) / 2;
8197
- if (top + tipH > window.innerHeight - margin) top = rect.top - tipH - margin;
8198
- if (left + tipW > window.innerWidth - margin) left = window.innerWidth - tipW - margin;
8387
+ if (top + tipH > globalThis.innerHeight - margin) top = rect.top - tipH - margin;
8388
+ if (left + tipW > globalThis.innerWidth - margin) left = globalThis.innerWidth - tipW - margin;
8199
8389
  if (left < margin) left = margin;
8200
8390
  this._el.style.top = `${top}px`;
8201
8391
  this._el.style.left = `${left}px`;
@@ -8251,8 +8441,8 @@
8251
8441
  const img = this._activeImg;
8252
8442
  if (!img) return;
8253
8443
  const current = img.style.transform || "";
8254
- const match = current.match(/rotate\((-?[\d.]+)deg\)/);
8255
- const next = ((match ? parseFloat(match[1]) : 0) + delta + 360) % 360;
8444
+ const match = /rotate\((-?[\d.]+)deg\)/.exec(current);
8445
+ const next = ((match ? Number.parseFloat(match[1]) : 0) + delta + 360) % 360;
8256
8446
  const cleaned = current.replace(/rotate\(-?[\d.]+deg\)/, "").trim();
8257
8447
  img.style.transform = cleaned ? `${cleaned} rotate(${next}deg)` : next === 0 ? "" : `rotate(${next}deg)`;
8258
8448
  this.context.invoke("editor.afterCommand");
@@ -8267,8 +8457,8 @@
8267
8457
  this._hide();
8268
8458
  this.context.invoke("imageResizer.deselect");
8269
8459
  const figure = img.closest("figure.an-figure");
8270
- if (figure && figure.parentNode) figure.parentNode.removeChild(figure);
8271
- else if (img.parentNode) img.parentNode.removeChild(img);
8460
+ if (figure) figure.remove();
8461
+ else img.remove();
8272
8462
  this.context.invoke("editor.afterCommand");
8273
8463
  }
8274
8464
  _crop() {
@@ -8287,7 +8477,7 @@
8287
8477
  this._hide();
8288
8478
  const range = document.createRange();
8289
8479
  range.selectNodeContents(cap);
8290
- const sel = window.getSelection();
8480
+ const sel = globalThis.getSelection();
8291
8481
  if (sel) {
8292
8482
  sel.removeAllRanges();
8293
8483
  sel.addRange(range);
@@ -8321,7 +8511,7 @@
8321
8511
  figure.appendChild(figcaption);
8322
8512
  const range = document.createRange();
8323
8513
  range.selectNodeContents(figcaption);
8324
- const sel = window.getSelection();
8514
+ const sel = globalThis.getSelection();
8325
8515
  if (sel) {
8326
8516
  sel.removeAllRanges();
8327
8517
  sel.addRange(range);
@@ -8362,14 +8552,15 @@
8362
8552
  const editable = this.context.layoutInfo.editable;
8363
8553
  this._disposers.push(on(editable, "mouseover", (e) => {
8364
8554
  if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
8365
- const wrapper = e.target.closest(".an-video-wrapper");
8555
+ const wrapper = e.target?.closest(".an-video-wrapper");
8366
8556
  if (wrapper && editable.contains(wrapper)) this._scheduleShow(wrapper);
8367
8557
  }, { passive: true }), on(editable, "mouseout", (e) => {
8368
8558
  const to = e.relatedTarget;
8369
8559
  if (!to || !editable.contains(to) && !this._el.contains(to)) this._scheduleHide();
8370
8560
  }, { passive: true }), on(document, "click", (e) => {
8371
- if (this._activeWrapper && !this._activeWrapper.contains(e.target) && !this._el.contains(e.target)) this._hide();
8372
- }));
8561
+ const target = e.target;
8562
+ if (this._activeWrapper && !this._activeWrapper.contains(target) && !this._el.contains(target)) this._hide();
8563
+ }), on(globalThis, "scroll", () => this._hide(), { passive: true }), on(globalThis, "resize", () => this._hide(), { passive: true }));
8373
8564
  return this;
8374
8565
  }
8375
8566
  destroy() {
@@ -8377,7 +8568,7 @@
8377
8568
  this._clearTimers();
8378
8569
  this._disposers.forEach((d) => d());
8379
8570
  this._disposers = [];
8380
- if (this._el && this._el.parentNode) this._el.parentNode.removeChild(this._el);
8571
+ this._el?.remove();
8381
8572
  this._el = null;
8382
8573
  }
8383
8574
  _buildTooltip() {
@@ -8449,7 +8640,7 @@
8449
8640
  if (this._hideTimer) return;
8450
8641
  this._hideTimer = setTimeout(() => this._hide(), HIDE_DELAY$2);
8451
8642
  }
8452
- _show(wrapper) {
8643
+ _show(_wrapper) {
8453
8644
  this._el.style.display = "flex";
8454
8645
  requestAnimationFrame(() => {
8455
8646
  if (this._activeWrapper) this._positionNear(this._activeWrapper);
@@ -8474,8 +8665,8 @@
8474
8665
  const margin = 6;
8475
8666
  let top = rect.bottom + margin;
8476
8667
  let left = rect.left + (rect.width - tipW) / 2;
8477
- if (top + tipH > window.innerHeight - margin) top = rect.top - tipH - margin;
8478
- if (left + tipW > window.innerWidth - margin) left = window.innerWidth - tipW - margin;
8668
+ if (top + tipH > globalThis.innerHeight - margin) top = rect.top - tipH - margin;
8669
+ if (left + tipW > globalThis.innerWidth - margin) left = globalThis.innerWidth - tipW - margin;
8479
8670
  if (left < margin) left = margin;
8480
8671
  this._el.style.top = `${top}px`;
8481
8672
  this._el.style.left = `${left}px`;
@@ -8529,7 +8720,7 @@
8529
8720
  if (!wrapper) return;
8530
8721
  this._hide();
8531
8722
  this.context.invoke("videoResizer.deselect");
8532
- if (wrapper.parentNode) wrapper.parentNode.removeChild(wrapper);
8723
+ wrapper.remove();
8533
8724
  this.context.invoke("editor.afterCommand");
8534
8725
  }
8535
8726
  _togglePreview() {
@@ -8673,8 +8864,35 @@
8673
8864
  rowHeight: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="7" x2="20" y2="7"/><line x1="4" y1="17" x2="20" y2="17"/><line x1="12" y1="7" x2="12" y2="17"/><path d="M9 10l3-3 3 3"/><path d="M9 14l3 3 3-3"/></svg>`,
8674
8865
  tableBorder: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round"><line x1="3" y1="6" x2="21" y2="6" stroke-width="1"/><line x1="3" y1="13" x2="21" y2="13" stroke-width="2"/><line x1="3" y1="20" x2="21" y2="20" stroke-width="3"/></svg>`,
8675
8866
  deleteTable: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="1"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="3" y1="15" x2="21" y2="15"/><line x1="9" y1="3" x2="9" y2="21"/><line x1="15" y1="3" x2="15" y2="21"/><line x1="16" y1="16" x2="22" y2="22" stroke="#ef4444"/><line x1="22" y1="16" x2="16" y2="22" stroke="#ef4444"/></svg>`,
8676
- selectCells: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M4 4 L4 20 L9 15 L12 21 L14 20 L11 14 L17 14 Z" fill="currentColor" opacity="0.15"/><path d="M4 4 L4 20 L9 15 L12 21 L14 20 L11 14 L17 14 Z"/></svg>`
8867
+ selectCells: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M4 4 L4 20 L9 15 L12 21 L14 20 L11 14 L17 14 Z" fill="currentColor" opacity="0.15"/><path d="M4 4 L4 20 L9 15 L12 21 L14 20 L11 14 L17 14 Z"/></svg>`,
8868
+ cellShade: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 11L8.93 3.36a1 1 0 0 0-1.29.08L3.22 7.8a1 1 0 0 0-.07 1.29L11 20"/><path d="m5 14 5-5"/><path d="M22 22a2 2 0 0 1-2 2h-3a2 2 0 0 1-2-2c0-1.5 2.5-5 3.5-5s3.5 3.5 3.5 5z"/></svg>`
8677
8869
  };
8870
+ var SHADE_PRESETS = [
8871
+ "#000000",
8872
+ "#434343",
8873
+ "#666666",
8874
+ "#999999",
8875
+ "#b7b7b7",
8876
+ "#cccccc",
8877
+ "#efefef",
8878
+ "#ffffff",
8879
+ "#ff0000",
8880
+ "#ff9900",
8881
+ "#ffff00",
8882
+ "#00ff00",
8883
+ "#00ffff",
8884
+ "#4a86e8",
8885
+ "#9900ff",
8886
+ "#ff00ff",
8887
+ "#f4cccc",
8888
+ "#fce5cd",
8889
+ "#fff2cc",
8890
+ "#d9ead3",
8891
+ "#d0e0e3",
8892
+ "#c9daf8",
8893
+ "#d9d2e9",
8894
+ "#ead1dc"
8895
+ ];
8678
8896
  var TableTooltip = class {
8679
8897
  /** @param {import('../Context.js').Context} context */
8680
8898
  constructor(context) {
@@ -8689,6 +8907,9 @@
8689
8907
  this._sizeApply = null;
8690
8908
  this._sizeTitleEl = null;
8691
8909
  this._sizeInputEl = null;
8910
+ this._shadePopover = null;
8911
+ this._shadeTitleEl = null;
8912
+ this._shadeColorStrip = null;
8692
8913
  this._selectMode = false;
8693
8914
  this._selectedCells = [];
8694
8915
  this._selectStart = null;
@@ -8701,6 +8922,8 @@
8701
8922
  document.body.appendChild(this._el);
8702
8923
  this._sizePopover = this._buildSizePopover();
8703
8924
  document.body.appendChild(this._sizePopover);
8925
+ this._shadePopover = this._buildCellShadePopover();
8926
+ document.body.appendChild(this._shadePopover);
8704
8927
  const editable = this.context.layoutInfo.editable;
8705
8928
  this._editable = editable;
8706
8929
  const onSelMousedown = (e) => {
@@ -8727,20 +8950,24 @@
8727
8950
  this._disposers.push(on(editable, "mousedown", onSelMousedown), on(editable, "mousemove", onSelMousemove), on(document, "mouseup", onSelMouseup));
8728
8951
  this._disposers.push(on(editable, "mouseover", (e) => {
8729
8952
  if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
8730
- const table = e.target.closest("table");
8953
+ const table = e.target?.closest("table");
8731
8954
  if (table && editable.contains(table)) {
8732
- const cell = e.target.closest("td, th");
8733
- if (cell) this._activeCell = cell;
8955
+ const cell = e.target?.closest("td, th");
8956
+ if (cell) {
8957
+ this._activeCell = cell;
8958
+ this._syncShadeStrip();
8959
+ }
8734
8960
  this._scheduleShow(table);
8735
8961
  }
8736
8962
  }, { passive: true }), on(editable, "mouseout", (e) => {
8737
8963
  if (this._selectMode) return;
8738
8964
  const to = e.relatedTarget;
8739
- if (!to || !editable.contains(to) && !this._el.contains(to) && !(this._sizePopover && this._sizePopover.contains(to))) this._scheduleHide();
8965
+ if (!to || !editable.contains(to) && !this._el.contains(to) && !this._sizePopover?.contains(to)) this._scheduleHide();
8740
8966
  }, { passive: true }), on(document, "click", (e) => {
8741
- if (this._selectMode && this._activeTable && this._activeTable.contains(e.target)) return;
8742
- if (this._activeTable && !this._activeTable.contains(e.target) && !this._el.contains(e.target) && !(this._sizePopover && this._sizePopover.contains(e.target))) this._hide();
8743
- }));
8967
+ const et = e.target;
8968
+ if (this._selectMode && this._activeTable?.contains(et)) return;
8969
+ if (this._activeTable && !this._activeTable.contains(et) && !this._el.contains(et) && !this._sizePopover?.contains(et)) this._hide();
8970
+ }), on(document, "selectionchange", () => this._syncShadeStrip()), on(globalThis, "scroll", () => this._hide(), { passive: true }), on(globalThis, "resize", () => this._hide(), { passive: true }));
8744
8971
  this._initResize();
8745
8972
  return this;
8746
8973
  }
@@ -8866,10 +9093,12 @@
8866
9093
  this._clearTimers();
8867
9094
  this._disposers.forEach((d) => d());
8868
9095
  this._disposers = [];
8869
- if (this._el && this._el.parentNode) this._el.parentNode.removeChild(this._el);
9096
+ if (this._el && this._el.parentNode) this._el.remove();
8870
9097
  this._el = null;
8871
- if (this._sizePopover && this._sizePopover.parentNode) this._sizePopover.parentNode.removeChild(this._sizePopover);
9098
+ if (this._sizePopover && this._sizePopover.parentNode) this._sizePopover.remove();
8872
9099
  this._sizePopover = null;
9100
+ if (this._shadePopover && this._shadePopover.parentNode) this._shadePopover.remove();
9101
+ this._shadePopover = null;
8873
9102
  }
8874
9103
  _buildTooltip() {
8875
9104
  const L = this.context.locale.tooltips.table;
@@ -8897,6 +9126,24 @@
8897
9126
  el.appendChild(this._makeBtn(ICONS$2.mergeCells, L.mergeCells, () => this._mergeCells()));
8898
9127
  el.appendChild(this._makeBtn(ICONS$2.unmergeCells, L.unmergeCells, () => this._unmergeCells()));
8899
9128
  el.appendChild(this._sep());
9129
+ const shadeBtn = createElement("button", {
9130
+ type: "button",
9131
+ class: "an-link-tooltip-btn an-link-tooltip-btn--shade",
9132
+ title: L.cellBackground
9133
+ });
9134
+ const shadeSvgWrap = createElement("span", { class: "an-bubble-btn-svg" });
9135
+ shadeSvgWrap.innerHTML = ICONS$2.cellShade;
9136
+ const shadeStrip = createElement("span", { class: "an-link-tooltip-color-strip" });
9137
+ shadeBtn.appendChild(shadeSvgWrap);
9138
+ shadeBtn.appendChild(shadeStrip);
9139
+ this._shadeColorStrip = shadeStrip;
9140
+ this._disposers.push(on(shadeBtn, "click", (e) => {
9141
+ e.preventDefault();
9142
+ e.stopPropagation();
9143
+ this._openCellShadePopover();
9144
+ }));
9145
+ el.appendChild(shadeBtn);
9146
+ el.appendChild(this._sep());
8900
9147
  el.appendChild(this._makeBtn(ICONS$2.colWidth, L.columnWidth, () => this._openSizePopover("col")));
8901
9148
  el.appendChild(this._makeBtn(ICONS$2.rowHeight, L.rowHeight, () => this._openSizePopover("row")));
8902
9149
  el.appendChild(this._makeBtn(ICONS$2.tableBorder, L.tableBorderWidth, () => this._openSizePopover("border")));
@@ -8905,6 +9152,7 @@
8905
9152
  this._disposers.push(on(el, "mouseenter", () => this._clearTimers()), on(el, "mouseleave", () => {
8906
9153
  if (this._selectMode) return;
8907
9154
  if (this._sizePopover && this._sizePopover.style.display !== "none") return;
9155
+ if (this._shadePopover && this._shadePopover.style.display !== "none") return;
8908
9156
  this._scheduleHide();
8909
9157
  }));
8910
9158
  return el;
@@ -8951,10 +9199,16 @@
8951
9199
  _show() {
8952
9200
  if (!this._activeTable) return;
8953
9201
  this._el.style.display = "flex";
9202
+ this._syncShadeStrip();
8954
9203
  requestAnimationFrame(() => {
8955
9204
  if (this._activeTable) this._positionNear(this._activeTable);
8956
9205
  });
8957
9206
  }
9207
+ _syncShadeStrip() {
9208
+ if (!this._shadeColorStrip || !this._el || this._el.style.display === "none") return;
9209
+ const cell = this._getCell();
9210
+ this._shadeColorStrip.style.background = cell?.style.backgroundColor || "transparent";
9211
+ }
8958
9212
  _hide() {
8959
9213
  this._el.style.display = "none";
8960
9214
  this._activeTable = null;
@@ -8983,20 +9237,20 @@
8983
9237
  let left = rect.left + (rect.width - tipW) / 2;
8984
9238
  let top = rect.top - tipH - margin;
8985
9239
  if (top < margin) top = rect.bottom + margin;
8986
- if (left + tipW > window.innerWidth - margin) left = window.innerWidth - tipW - margin;
9240
+ if (left + tipW > globalThis.innerWidth - margin) left = globalThis.innerWidth - tipW - margin;
8987
9241
  if (left < margin) left = margin;
8988
9242
  this._el.style.left = `${left}px`;
8989
9243
  this._el.style.top = `${top}px`;
8990
9244
  }
8991
9245
  _getCell() {
8992
- const sel = window.getSelection();
9246
+ const sel = globalThis.getSelection();
8993
9247
  if (sel && sel.rangeCount) {
8994
9248
  let container = sel.getRangeAt(0).commonAncestorContainer;
8995
9249
  if (container.nodeType === 3) container = container.parentElement;
8996
- const cellFromSel = container && container.closest && container.closest("td, th");
8997
- if (cellFromSel && this._activeTable && this._activeTable.contains(cellFromSel)) return cellFromSel;
9250
+ const cellFromSel = container?.closest("td, th");
9251
+ if (cellFromSel && this._activeTable?.contains(cellFromSel)) return cellFromSel;
8998
9252
  }
8999
- return this._activeCell || this._activeTable && this._activeTable.querySelector("td, th");
9253
+ return this._activeCell || this._activeTable?.querySelector("td, th");
9000
9254
  }
9001
9255
  _toggleSelectMode() {
9002
9256
  this._selectMode = !this._selectMode;
@@ -9095,11 +9349,12 @@
9095
9349
  const table = cells[0].closest("table");
9096
9350
  if (!table) return;
9097
9351
  const allRows = Array.from(table.querySelectorAll("tr"));
9098
- const refRow = [...new Set(cells.map((c) => c.closest("tr")).filter(Boolean))].reduce((best, r) => {
9352
+ const selectedRows = [...new Set(cells.map((c) => c.closest("tr")).filter(Boolean))];
9353
+ const refRow = selectedRows.reduce((best, r) => {
9099
9354
  const bi = allRows.indexOf(best);
9100
9355
  const ri = allRows.indexOf(r);
9101
9356
  return position === "above" ? ri < bi ? r : best : ri > bi ? r : best;
9102
- });
9357
+ }, selectedRows[0]);
9103
9358
  const colCount = Array.from(refRow.cells).reduce((sum, c) => sum + (c.colSpan || 1), 0);
9104
9359
  const newRow = document.createElement("tr");
9105
9360
  const refCells = Array.from(refRow.cells);
@@ -9142,7 +9397,7 @@
9142
9397
  if (selectedRows.filter((r) => r.closest("tbody")).length >= totalBodyRows) return;
9143
9398
  this._activeCell = null;
9144
9399
  this._clearSelection();
9145
- selectedRows.forEach((r) => r.parentElement?.removeChild(r));
9400
+ selectedRows.forEach((r) => r.remove());
9146
9401
  requestAnimationFrame(() => this._positionNear(this._activeTable));
9147
9402
  this.context.invoke("editor.afterCommand");
9148
9403
  }
@@ -9164,7 +9419,7 @@
9164
9419
  });
9165
9420
  this._activeCell = null;
9166
9421
  this._clearSelection();
9167
- cellsToDelete.forEach((c) => c.parentElement?.removeChild(c));
9422
+ cellsToDelete.forEach((c) => c.remove());
9168
9423
  requestAnimationFrame(() => this._positionNear(this._activeTable));
9169
9424
  this.context.invoke("editor.afterCommand");
9170
9425
  }
@@ -9175,7 +9430,7 @@
9175
9430
  if (!table) return;
9176
9431
  let selected = this._getSelectedCells().filter((c) => table.contains(c));
9177
9432
  if (selected.length < 2) {
9178
- const sel = window.getSelection();
9433
+ const sel = globalThis.getSelection();
9179
9434
  if (!sel || sel.rangeCount === 0) return;
9180
9435
  const range = sel.getRangeAt(0);
9181
9436
  selected = Array.from(table.querySelectorAll("td, th")).filter((c) => {
@@ -9217,7 +9472,7 @@
9217
9472
  first.rowSpan = maxR - minR + 1;
9218
9473
  first.style.verticalAlign = "middle";
9219
9474
  first.innerHTML = rectCells.map((c) => c.innerHTML).join("");
9220
- rectCells.slice(1).forEach((c) => c.parentElement?.removeChild(c));
9475
+ rectCells.slice(1).forEach((c) => c.remove());
9221
9476
  this._clearSelection();
9222
9477
  this.context.invoke("editor.afterCommand");
9223
9478
  }
@@ -9225,7 +9480,7 @@
9225
9480
  const table = this._activeTable;
9226
9481
  if (!table) return;
9227
9482
  this._hide();
9228
- if (table.parentNode) table.parentNode.removeChild(table);
9483
+ if (table.parentNode) table.remove();
9229
9484
  this.context.invoke("editor.afterCommand");
9230
9485
  }
9231
9486
  _unmergeCells() {
@@ -9314,20 +9569,22 @@
9314
9569
  this._sizeInputEl = inputEl;
9315
9570
  this._sizeApply = null;
9316
9571
  const d1 = on(applyBtn, "click", () => {
9317
- const val = parseInt(this._sizeInputEl.value, 10);
9572
+ const val = Number.parseInt(this._sizeInputEl.value, 10);
9318
9573
  if (val > 0 && typeof this._sizeApply === "function") this._sizeApply(val);
9319
9574
  this._hideSizePopover();
9320
9575
  });
9321
9576
  const d2 = on(cancelBtn, "click", () => this._hideSizePopover());
9322
9577
  const d3 = on(inputEl, "keydown", (e) => {
9323
- if (e.key === "Enter") {
9578
+ const ke = e;
9579
+ if (ke.key === "Enter") {
9324
9580
  e.preventDefault();
9325
9581
  applyBtn.click();
9326
9582
  }
9327
- if (e.key === "Escape") this._hideSizePopover();
9583
+ if (ke.key === "Escape") this._hideSizePopover();
9328
9584
  });
9329
9585
  const d4 = on(document, "click", (e) => {
9330
- if (this._sizePopover && this._sizePopover.style.display !== "none" && !this._sizePopover.contains(e.target) && !this._el.contains(e.target)) this._hideSizePopover();
9586
+ const et = e.target;
9587
+ if (this._sizePopover && this._sizePopover.style.display !== "none" && !this._sizePopover.contains(et) && !this._el.contains(et)) this._hideSizePopover();
9331
9588
  });
9332
9589
  const d5 = on(popover, "mouseenter", () => this._clearTimers());
9333
9590
  const d6 = on(popover, "mouseleave", () => this._scheduleHide());
@@ -9341,11 +9598,11 @@
9341
9598
  const table = cell.closest("table");
9342
9599
  if (!table) return;
9343
9600
  const firstCell = table.querySelector("td, th");
9344
- const currentPx = firstCell ? parseInt(firstCell.style.borderWidth, 10) || parseInt(window.getComputedStyle(firstCell).borderWidth, 10) || 1 : 1;
9601
+ const currentPx = firstCell ? Number.parseInt(firstCell.style.borderWidth, 10) || Number.parseInt(globalThis.getComputedStyle(firstCell).borderWidth, 10) || 1 : 1;
9345
9602
  this._sizeTitleEl.textContent = this.context.locale.tooltips.table.tableBorderWidthPx;
9346
9603
  this._sizeInputEl.min = "0";
9347
9604
  this._sizeInputEl.max = "10";
9348
- this._sizeInputEl.value = currentPx;
9605
+ this._sizeInputEl.value = String(currentPx);
9349
9606
  this._sizeApply = (val) => {
9350
9607
  const cells = Array.from(table.querySelectorAll("td, th"));
9351
9608
  if (val === 0) cells.forEach((c) => {
@@ -9400,8 +9657,8 @@
9400
9657
  const ph = this._sizePopover.offsetHeight || 110;
9401
9658
  let left = tipRect.left;
9402
9659
  let top = tipRect.bottom + 6;
9403
- if (left + pw > window.innerWidth - 8) left = window.innerWidth - pw - 8;
9404
- if (top + ph > window.innerHeight - 8) top = tipRect.top - ph - 6;
9660
+ if (left + pw > globalThis.innerWidth - 8) left = globalThis.innerWidth - pw - 8;
9661
+ if (top + ph > globalThis.innerHeight - 8) top = tipRect.top - ph - 6;
9405
9662
  this._sizePopover.style.left = `${left}px`;
9406
9663
  this._sizePopover.style.top = `${top}px`;
9407
9664
  if (this._sizeInputEl) {
@@ -9414,6 +9671,84 @@
9414
9671
  if (this._sizePopover) this._sizePopover.style.display = "none";
9415
9672
  this._sizeApply = null;
9416
9673
  }
9674
+ _buildCellShadePopover() {
9675
+ const pop = createElement("div", { class: "an-cell-shade-popover" });
9676
+ pop.style.display = "none";
9677
+ const title = createElement("div", { class: "an-size-popover-title" });
9678
+ pop.appendChild(title);
9679
+ this._shadeTitleEl = title;
9680
+ const palette = createElement("div", { class: "an-context-color-palette" });
9681
+ SHADE_PRESETS.forEach((color) => {
9682
+ const sw = createElement("div", {
9683
+ class: "an-context-color-swatch",
9684
+ title: color
9685
+ });
9686
+ sw.style.background = color;
9687
+ this._disposers.push(on(sw, "click", (e) => {
9688
+ e.stopPropagation();
9689
+ this._applyCellShade(color);
9690
+ }));
9691
+ palette.appendChild(sw);
9692
+ });
9693
+ pop.appendChild(palette);
9694
+ const noShadeRow = createElement("div", { class: "an-context-color-custom" });
9695
+ const noShadeBtn = createElement("button", {
9696
+ type: "button",
9697
+ class: "an-shade-no-color"
9698
+ });
9699
+ this._disposers.push(on(noShadeBtn, "click", () => this._applyCellShade("")));
9700
+ noShadeRow.appendChild(noShadeBtn);
9701
+ pop.appendChild(noShadeRow);
9702
+ this._shadeNoBtn = noShadeBtn;
9703
+ const customRow = createElement("div", { class: "an-context-color-custom" });
9704
+ const colorInput = createElement("input", {
9705
+ type: "color",
9706
+ class: "an-shade-color-input",
9707
+ value: "#ffffff"
9708
+ });
9709
+ const customLabel = createElement("span");
9710
+ customLabel.textContent = "Custom…";
9711
+ this._disposers.push(on(colorInput, "change", () => this._applyCellShade(colorInput.value)));
9712
+ customRow.appendChild(colorInput);
9713
+ customRow.appendChild(customLabel);
9714
+ pop.appendChild(customRow);
9715
+ this._disposers.push(on(pop, "mousedown", (e) => e.preventDefault()), on(pop, "mouseenter", () => this._clearTimers()), on(pop, "mouseleave", () => this._scheduleHide()));
9716
+ this._disposers.push(on(document, "click", (e) => {
9717
+ const et = e.target;
9718
+ if (this._shadePopover && this._shadePopover.style.display !== "none" && !this._shadePopover.contains(et) && !this._el?.contains(et)) this._hideCellShadePopover();
9719
+ }));
9720
+ return pop;
9721
+ }
9722
+ _openCellShadePopover() {
9723
+ if (!this._shadePopover) return;
9724
+ const L = this.context.locale.tooltips.table;
9725
+ if (this._shadeTitleEl) this._shadeTitleEl.textContent = L.cellBackground;
9726
+ if (this._shadeNoBtn) this._shadeNoBtn.textContent = L.noShading;
9727
+ this._shadePopover.style.display = "block";
9728
+ requestAnimationFrame(() => {
9729
+ if (!this._shadePopover || !this._el) return;
9730
+ const pw = this._shadePopover.offsetWidth || 170;
9731
+ const ph = this._shadePopover.offsetHeight || 120;
9732
+ const tipRect = this._el.getBoundingClientRect();
9733
+ let left = tipRect.left;
9734
+ let top = tipRect.bottom + 6;
9735
+ if (left + pw > globalThis.innerWidth - 8) left = globalThis.innerWidth - pw - 8;
9736
+ if (top + ph > globalThis.innerHeight - 8) top = tipRect.top - ph - 6;
9737
+ this._shadePopover.style.left = `${Math.max(8, left)}px`;
9738
+ this._shadePopover.style.top = `${Math.max(8, top)}px`;
9739
+ });
9740
+ }
9741
+ _hideCellShadePopover() {
9742
+ if (this._shadePopover) this._shadePopover.style.display = "none";
9743
+ }
9744
+ _applyCellShade(color) {
9745
+ (this._selectMode ? this._selectedCells : [this._getCell()]).forEach((cell) => {
9746
+ if (cell) cell.style.backgroundColor = color;
9747
+ });
9748
+ if (this._shadeColorStrip) this._shadeColorStrip.style.background = color || "transparent";
9749
+ this._hideCellShadePopover();
9750
+ this.context.invoke("editor.afterCommand");
9751
+ }
9417
9752
  };
9418
9753
  //#endregion
9419
9754
  //#region src/js/module/CodeTooltip.js
@@ -9446,13 +9781,14 @@
9446
9781
  const editable = this.context.layoutInfo.editable;
9447
9782
  this._disposers.push(on(editable, "mouseover", (e) => {
9448
9783
  if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
9449
- const pre = e.target.closest("pre");
9784
+ const pre = e.target?.closest("pre");
9450
9785
  if (pre && editable.contains(pre)) this._scheduleShow(pre);
9451
9786
  }), on(editable, "mouseout", (e) => {
9452
9787
  const to = e.relatedTarget;
9453
9788
  if (!to || !editable.contains(to) && !this._el.contains(to)) this._scheduleHide();
9454
9789
  }), on(document, "click", (e) => {
9455
- if (this._activePre && !this._activePre.contains(e.target) && !this._el.contains(e.target)) this._hide();
9790
+ const et = e.target;
9791
+ if (this._activePre && !this._activePre.contains(et) && !this._el.contains(et)) this._hide();
9456
9792
  }));
9457
9793
  return this;
9458
9794
  }
@@ -9460,7 +9796,7 @@
9460
9796
  this._clearTimers();
9461
9797
  this._disposers.forEach((d) => d());
9462
9798
  this._disposers = [];
9463
- if (this._el && this._el.parentNode) this._el.parentNode.removeChild(this._el);
9799
+ this._el?.remove();
9464
9800
  this._el = null;
9465
9801
  }
9466
9802
  _buildTooltip() {
@@ -9487,6 +9823,7 @@
9487
9823
  ["python", "Python"],
9488
9824
  ["html", "HTML"],
9489
9825
  ["css", "CSS"],
9826
+ ["scss", "SCSS"],
9490
9827
  ["json", "JSON"],
9491
9828
  ["xml", "XML"],
9492
9829
  ["bash", "Bash / Shell"],
@@ -9585,21 +9922,21 @@
9585
9922
  let top = rect.top - tipH - margin;
9586
9923
  let left = rect.left + (rect.width - tipW) / 2;
9587
9924
  if (top < margin) top = rect.bottom + margin;
9588
- if (left + tipW > window.innerWidth - margin) left = window.innerWidth - tipW - margin;
9925
+ if (left + tipW > globalThis.innerWidth - margin) left = globalThis.innerWidth - tipW - margin;
9589
9926
  if (left < margin) left = margin;
9590
9927
  this._el.style.top = `${top}px`;
9591
9928
  this._el.style.left = `${left}px`;
9592
9929
  }
9593
9930
  _syncWrapBtn() {
9594
9931
  if (!this._activePre || !this._wrapBtn) return;
9595
- const wrapped = (this._activePre.style.whiteSpace || "").includes("pre-wrap") || window.getComputedStyle(this._activePre).whiteSpace === "pre-wrap";
9932
+ const wrapped = (this._activePre.style.whiteSpace || "").includes("pre-wrap") || globalThis.getComputedStyle(this._activePre).whiteSpace === "pre-wrap";
9596
9933
  this._wrapBtn.classList.toggle("active", wrapped);
9597
9934
  this._wrapBtn.title = wrapped ? this.context.locale.tooltips.code.disableWordWrap : this.context.locale.tooltips.code.enableWordWrap;
9598
9935
  }
9599
9936
  _syncLangSelect() {
9600
9937
  if (!this._activePre || !this._langSelect) return;
9601
9938
  const codeEl = this._activePre.querySelector("code");
9602
- const fromAttr = this._activePre.getAttribute("data-language") || "";
9939
+ const fromAttr = this._activePre.dataset.language || "";
9603
9940
  const fromClass = codeEl ? (_LANG_CLASS_RE.exec(codeEl.className) || [])[1] || "" : "";
9604
9941
  this._langSelect.value = fromAttr || fromClass || "";
9605
9942
  }
@@ -9618,7 +9955,7 @@
9618
9955
  document.execCommand("copy");
9619
9956
  this._flashCopied();
9620
9957
  } catch (_) {}
9621
- document.body.removeChild(ta);
9958
+ ta.remove();
9622
9959
  }
9623
9960
  }
9624
9961
  _flashCopied() {
@@ -9642,10 +9979,26 @@
9642
9979
  this.context.invoke("editor.afterCommand");
9643
9980
  this._positionNear(pre);
9644
9981
  }
9982
+ /**
9983
+ * Applies a language to a given <pre> element: sets classes, data-language,
9984
+ * and triggers Prism highlighting. Called by the auto-detect flow.
9985
+ * @param {HTMLElement} pre
9986
+ * @param {string} lang - Prism language identifier, e.g. 'javascript'
9987
+ */
9988
+ applyLanguage(pre, lang) {
9989
+ if (!pre || !lang) return;
9990
+ const savedPre = this._activePre;
9991
+ this._activePre = pre;
9992
+ if (this._langSelect) this._langSelect.value = lang;
9993
+ this._onLangChange();
9994
+ if (this._langSelect) this._langSelect.value = lang;
9995
+ this._activePre = savedPre || pre;
9996
+ }
9645
9997
  _onLangChange() {
9646
9998
  const pre = this._activePre;
9647
9999
  if (!pre) return;
9648
10000
  const lang = this._langSelect.value;
10001
+ const _w = globalThis;
9649
10002
  let codeEl = pre.querySelector("code");
9650
10003
  if (!codeEl) {
9651
10004
  codeEl = document.createElement("code");
@@ -9655,16 +10008,16 @@
9655
10008
  }
9656
10009
  codeEl.className = lang ? `language-${lang}` : "";
9657
10010
  pre.className = lang ? `language-${lang}` : "";
9658
- if (lang) pre.setAttribute("data-language", lang);
9659
- else pre.removeAttribute("data-language");
10011
+ if (lang) pre.dataset.language = lang;
10012
+ else delete pre.dataset.language;
9660
10013
  const applyPrism = () => {
9661
10014
  codeEl.querySelectorAll("br").forEach((br) => br.replaceWith("\n"));
9662
- window.Prism.highlightElement(codeEl);
10015
+ _w.Prism.highlightElement(codeEl);
9663
10016
  this.context.invoke("editor.afterCommand");
9664
10017
  };
9665
10018
  if (lang) {
9666
- if (typeof window.Prism !== "undefined") {
9667
- if (window.Prism.languages[lang]) {
10019
+ if (_w.Prism !== void 0) {
10020
+ if (_w.Prism.languages[lang]) {
9668
10021
  applyPrism();
9669
10022
  return;
9670
10023
  }
@@ -9672,7 +10025,7 @@
9672
10025
  return;
9673
10026
  } else if (this._prismScript) {
9674
10027
  this._prismScript.addEventListener("load", () => {
9675
- if (window.Prism.languages[lang]) applyPrism();
10028
+ if (_w.Prism.languages[lang]) applyPrism();
9676
10029
  else this._loadPrismComponent(lang, applyPrism);
9677
10030
  }, { once: true });
9678
10031
  return;
@@ -9685,7 +10038,8 @@
9685
10038
  * Called once at initialize time. Fire-and-forget; errors are silent.
9686
10039
  */
9687
10040
  _ensurePrism() {
9688
- if (!this.context.options.codeHighlight || window.Prism) return;
10041
+ const _w = globalThis;
10042
+ if (!this.context.options.codeHighlight || _w.Prism) return;
9689
10043
  const cdn = this.context.options.codeHighlightCDN;
9690
10044
  const themeHref = `${cdn}/themes/prism-tomorrow.min.css`;
9691
10045
  const scriptSrc = `${cdn}/prism.min.js`;
@@ -9697,7 +10051,7 @@
9697
10051
  }
9698
10052
  const existingScript = document.querySelector(`script[src="${scriptSrc}"]`);
9699
10053
  if (existingScript) {
9700
- this._prismScript = window.Prism ? null : existingScript;
10054
+ this._prismScript = _w.Prism ? null : existingScript;
9701
10055
  return;
9702
10056
  }
9703
10057
  const script = document.createElement("script");
@@ -9717,10 +10071,11 @@
9717
10071
  * @param {Function} cb – called once the grammar is ready
9718
10072
  */
9719
10073
  _loadPrismComponent(lang, cb) {
10074
+ const _w = globalThis;
9720
10075
  const src = `${this.context.options.codeHighlightCDN}/components/prism-${lang}.min.js`;
9721
10076
  if (document.querySelector(`script[src="${src}"]`)) {
9722
10077
  const poll = setInterval(() => {
9723
- if (window.Prism && window.Prism.languages[lang]) {
10078
+ if (_w.Prism?.languages[lang]) {
9724
10079
  clearInterval(poll);
9725
10080
  cb();
9726
10081
  }
@@ -9751,7 +10106,7 @@
9751
10106
  const pre = this._activePre;
9752
10107
  if (!pre) return;
9753
10108
  this._hide();
9754
- if (pre.parentNode) pre.parentNode.removeChild(pre);
10109
+ pre.remove();
9755
10110
  this.context.invoke("editor.afterCommand");
9756
10111
  }
9757
10112
  };
@@ -12106,7 +12461,7 @@
12106
12461
  destroy() {
12107
12462
  this._disposers.forEach((d) => d());
12108
12463
  this._disposers = [];
12109
- if (this._dialog && this._dialog.parentNode) this._dialog.parentNode.removeChild(this._dialog);
12464
+ if (this._dialog && this._dialog.parentNode) this._dialog.remove();
12110
12465
  this._dialog = null;
12111
12466
  }
12112
12467
  show() {
@@ -12133,15 +12488,19 @@
12133
12488
  });
12134
12489
  const box = createElement("div", { class: "an-dialog-box an-emoji-box" });
12135
12490
  const titleRow = createElement("div", { class: "an-icon-title-row" });
12491
+ const titleGroup = createElement("div", { class: "an-dialog-title-group" });
12492
+ const iconEl = createElement("span", { class: "an-dialog-icon an-dialog-icon--sm" });
12493
+ iconEl.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M8 13s1.5 2 4 2 4-2 4-2"/><line x1="9" y1="9" x2="9.01" y2="9"/><line x1="15" y1="9" x2="15.01" y2="9"/></svg>`;
12136
12494
  const title = createElement("h3", { class: "an-dialog-title" });
12137
12495
  title.textContent = L.title;
12496
+ titleGroup.append(iconEl, title);
12138
12497
  const closeBtn = createElement("button", {
12139
12498
  type: "button",
12140
12499
  class: "an-icon-close",
12141
12500
  "aria-label": L.close
12142
12501
  });
12143
12502
  closeBtn.innerHTML = "&times;";
12144
- titleRow.append(title, closeBtn);
12503
+ titleRow.append(titleGroup, closeBtn);
12145
12504
  const searchInput = createElement("input", {
12146
12505
  type: "search",
12147
12506
  class: "an-input an-icon-search",
@@ -12163,7 +12522,7 @@
12163
12522
  class: "an-icon-cat",
12164
12523
  "data-cat": id
12165
12524
  });
12166
- tab.textContent = L.categories && L.categories[id] || label;
12525
+ tab.textContent = L.categories?.[id] || label;
12167
12526
  catBar.appendChild(tab);
12168
12527
  });
12169
12528
  this._catBar = catBar;
@@ -12190,6 +12549,7 @@
12190
12549
  btnRow.appendChild(cancelBtn);
12191
12550
  box.append(titleRow, searchInput, catBar, grid, btnRow);
12192
12551
  overlay.appendChild(box);
12552
+ makeDraggable(titleRow, box);
12193
12553
  const d1 = on(closeBtn, "click", () => this._close());
12194
12554
  const d2 = on(cancelBtn, "click", () => this._close());
12195
12555
  const d3 = on(overlay, "click", (e) => {
@@ -12197,7 +12557,7 @@
12197
12557
  });
12198
12558
  const d4 = on(searchInput, "input", () => this._filterEmojis(searchInput.value, this._activeCat));
12199
12559
  const d5 = on(catBar, "click", (e) => {
12200
- const tab = e.target.closest("[data-cat]");
12560
+ const tab = e.target?.closest("[data-cat]");
12201
12561
  if (tab) {
12202
12562
  this._activeCat = tab.dataset.cat;
12203
12563
  this._updateCatTabs();
@@ -12205,7 +12565,7 @@
12205
12565
  }
12206
12566
  });
12207
12567
  const d6 = on(grid, "click", (e) => {
12208
- const cell = e.target.closest(".an-emoji-cell");
12568
+ const cell = e.target?.closest(".an-emoji-cell");
12209
12569
  if (cell) this._onEmojiClick(cell.dataset.char);
12210
12570
  });
12211
12571
  this._disposers.push(d1, d2, d3, d4, d5, d6);
@@ -12213,17 +12573,22 @@
12213
12573
  }
12214
12574
  _updateCatTabs() {
12215
12575
  this._catBar.querySelectorAll(".an-icon-cat").forEach((tab) => {
12216
- tab.classList.toggle("active", tab.dataset.cat === this._activeCat);
12576
+ tab.classList.toggle(
12577
+ "active",
12578
+ /** @type {HTMLElement} */
12579
+ tab.dataset.cat === this._activeCat
12580
+ );
12217
12581
  });
12218
12582
  }
12219
12583
  _filterEmojis(query, cat) {
12220
12584
  const q = (query || "").trim().toLowerCase();
12221
12585
  let count = 0;
12222
12586
  this._grid.querySelectorAll(".an-emoji-cell").forEach((cell) => {
12223
- const matchCat = !cat || cat === "all" || cell.dataset.cat === cat;
12224
- const matchQuery = !q || cell.dataset.keywords.includes(q) || cell.dataset.char === q;
12587
+ const hCell = cell;
12588
+ const matchCat = !cat || cat === "all" || hCell.dataset.cat === cat;
12589
+ const matchQuery = !q || hCell.dataset.keywords.includes(q) || hCell.dataset.char === q;
12225
12590
  const visible = matchCat && matchQuery;
12226
- cell.style.display = visible ? "" : "none";
12591
+ hCell.style.display = visible ? "" : "none";
12227
12592
  if (visible) count++;
12228
12593
  });
12229
12594
  let empty = this._grid.querySelector(".an-icon-empty");
@@ -12232,13 +12597,13 @@
12232
12597
  empty.textContent = "No emojis found";
12233
12598
  this._grid.appendChild(empty);
12234
12599
  }
12235
- empty.style.display = count > 0 ? "none" : "";
12600
+ /** @type {HTMLElement} */ empty.style.display = count > 0 ? "none" : "";
12236
12601
  }
12237
12602
  _onEmojiClick(char) {
12238
12603
  const savedRange = this._savedRange;
12239
12604
  const editable = this.context.layoutInfo.editable;
12240
12605
  if (savedRange) savedRange.select();
12241
- const sel = window.getSelection();
12606
+ const sel = globalThis.getSelection();
12242
12607
  let range = sel && sel.rangeCount > 0 ? sel.getRangeAt(0) : null;
12243
12608
  if (!range) {
12244
12609
  range = document.createRange();
@@ -12246,9 +12611,9 @@
12246
12611
  range.collapse(false);
12247
12612
  }
12248
12613
  const _sc = range.startContainer;
12249
- const _tdAnchor = (_sc.nodeType === 1 ? _sc : _sc.parentElement)?.closest?.("td, th");
12614
+ const _tdAnchor = (_sc.nodeType === 1 ? _sc : _sc.parentElement)?.closest("td, th");
12250
12615
  range.deleteContents();
12251
- if (_tdAnchor && _tdAnchor.isConnected && !_tdAnchor.contains(range.startContainer)) {
12616
+ if (_tdAnchor?.isConnected && !_tdAnchor.contains(range.startContainer)) {
12252
12617
  range.setStart(_tdAnchor, 0);
12253
12618
  range.collapse(true);
12254
12619
  }
@@ -12268,7 +12633,7 @@
12268
12633
  if (this._dialog) {
12269
12634
  this._dialog.style.display = "flex";
12270
12635
  this._removeTrap = trapFocus(this._dialog, () => this._close());
12271
- setTimeout(() => this._searchInput && this._searchInput.focus(), 50);
12636
+ setTimeout(() => this._searchInput?.focus(), 50);
12272
12637
  }
12273
12638
  }
12274
12639
  _close() {
@@ -12575,7 +12940,7 @@
12575
12940
  destroy() {
12576
12941
  this._disposers.forEach((d) => d());
12577
12942
  this._disposers = [];
12578
- if (this._dialog && this._dialog.parentNode) this._dialog.parentNode.removeChild(this._dialog);
12943
+ this._dialog?.remove();
12579
12944
  this._dialog = null;
12580
12945
  }
12581
12946
  show() {
@@ -12606,15 +12971,19 @@
12606
12971
  });
12607
12972
  const box = createElement("div", { class: "an-dialog-box an-icon-box" });
12608
12973
  const titleRow = createElement("div", { class: "an-icon-title-row" });
12974
+ const titleGroup = createElement("div", { class: "an-dialog-title-group" });
12975
+ const iconEl = createElement("span", { class: "an-dialog-icon an-dialog-icon--sm" });
12976
+ iconEl.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg>`;
12609
12977
  const title = createElement("h3", { class: "an-dialog-title" });
12610
12978
  title.textContent = L.title;
12979
+ titleGroup.append(iconEl, title);
12611
12980
  const closeBtn = createElement("button", {
12612
12981
  type: "button",
12613
12982
  class: "an-icon-close",
12614
12983
  "aria-label": L.close
12615
12984
  });
12616
12985
  closeBtn.innerHTML = "&times;";
12617
- titleRow.append(title, closeBtn);
12986
+ titleRow.append(titleGroup, closeBtn);
12618
12987
  const searchInput = createElement("input", {
12619
12988
  type: "search",
12620
12989
  class: "an-input an-icon-search",
@@ -12636,7 +13005,7 @@
12636
13005
  class: "an-icon-cat",
12637
13006
  "data-cat": id
12638
13007
  });
12639
- tab.textContent = L.categories && L.categories[id] || label;
13008
+ tab.textContent = L.categories?.[id] || label;
12640
13009
  catBar.appendChild(tab);
12641
13010
  });
12642
13011
  this._catBar = catBar;
@@ -12729,6 +13098,7 @@
12729
13098
  this._insertBtn = insertBtn;
12730
13099
  box.append(titleRow, searchInput, catBar, grid, optRow, preview, btnRow);
12731
13100
  overlay.appendChild(box);
13101
+ makeDraggable(titleRow, box);
12732
13102
  const d1 = on(closeBtn, "click", () => this._close());
12733
13103
  const d2 = on(cancelBtn, "click", () => this._close());
12734
13104
  const d3 = on(insertBtn, "click", () => this._onInsert());
@@ -12737,7 +13107,7 @@
12737
13107
  });
12738
13108
  const d5 = on(searchInput, "input", () => this._filterIcons(searchInput.value, this._activeCat));
12739
13109
  const d6 = on(catBar, "click", (e) => {
12740
- const tab = e.target.closest("[data-cat]");
13110
+ const tab = e.target?.closest("[data-cat]");
12741
13111
  if (tab) {
12742
13112
  this._activeCat = tab.dataset.cat;
12743
13113
  this._updateCatTabs();
@@ -12745,7 +13115,7 @@
12745
13115
  }
12746
13116
  });
12747
13117
  const d7 = on(grid, "click", (e) => {
12748
- const cell = e.target.closest(".an-icon-cell");
13118
+ const cell = e.target?.closest(".an-icon-cell");
12749
13119
  if (cell) this._selectIcon(cell.dataset.name);
12750
13120
  });
12751
13121
  const d8 = on(styleSelect, "change", () => this._updatePreview(this._selectedIcon));
@@ -12757,19 +13127,24 @@
12757
13127
  }
12758
13128
  _updateCatTabs() {
12759
13129
  this._catBar.querySelectorAll(".an-icon-cat").forEach((tab) => {
12760
- tab.classList.toggle("active", tab.dataset.cat === this._activeCat);
13130
+ tab.classList.toggle(
13131
+ "active",
13132
+ /** @type {HTMLElement} */
13133
+ tab.dataset.cat === this._activeCat
13134
+ );
12761
13135
  });
12762
13136
  }
12763
13137
  _filterIcons(query, cat) {
12764
13138
  const q = (query || "").trim().toLowerCase();
12765
13139
  let visibleCount = 0;
12766
13140
  this._grid.querySelectorAll(".an-icon-cell").forEach((cell) => {
12767
- const name = cell.dataset.name;
12768
- const cellCat = cell.dataset.cat;
13141
+ const hCell = cell;
13142
+ const name = hCell.dataset.name;
13143
+ const cellCat = hCell.dataset.cat;
12769
13144
  const matchesCat = !cat || cat === "all" || cellCat === cat;
12770
13145
  const matchesQuery = !q || name.includes(q);
12771
13146
  const visible = matchesCat && matchesQuery;
12772
- cell.style.display = visible ? "" : "none";
13147
+ hCell.style.display = visible ? "" : "none";
12773
13148
  if (visible) visibleCount++;
12774
13149
  });
12775
13150
  let empty = this._grid.querySelector(".an-icon-empty");
@@ -12778,12 +13153,16 @@
12778
13153
  empty.textContent = "No icons found";
12779
13154
  this._grid.appendChild(empty);
12780
13155
  }
12781
- empty.style.display = visibleCount > 0 ? "none" : "";
13156
+ /** @type {HTMLElement} */ empty.style.display = visibleCount > 0 ? "none" : "";
12782
13157
  }
12783
13158
  _selectIcon(name) {
12784
13159
  this._selectedIcon = name;
12785
13160
  this._grid.querySelectorAll(".an-icon-cell").forEach((cell) => {
12786
- cell.classList.toggle("active", cell.dataset.name === name);
13161
+ cell.classList.toggle(
13162
+ "active",
13163
+ /** @type {HTMLElement} */
13164
+ cell.dataset.name === name
13165
+ );
12787
13166
  });
12788
13167
  this._insertBtn.removeAttribute("disabled");
12789
13168
  this._updatePreview(name);
@@ -12794,17 +13173,17 @@
12794
13173
  this._preview.innerHTML = "<span class=\"an-icon-preview-hint\">Select an icon</span>";
12795
13174
  return;
12796
13175
  }
12797
- const cls = this._styleSelect && this._styleSelect.value || "fa-solid";
12798
- const size = this._sizeSelect && this._sizeSelect.value || "1em";
12799
- const color = (this._useColorCb ? this._useColorCb.checked : false) && this._colorInput ? this._colorInput.value : "";
13176
+ const cls = this._styleSelect?.value || "fa-solid";
13177
+ const size = this._sizeSelect?.value || "1em";
13178
+ const color = this._useColorCb?.checked ?? false ? this._colorInput?.value ?? "" : "";
12800
13179
  const styleAttr = [size ? `font-size:${size}` : "", color ? `color:${color}` : ""].filter(Boolean).join(";");
12801
13180
  this._preview.innerHTML = `<i class="${cls} fa-${name}" aria-hidden="true"${styleAttr ? ` style="${styleAttr}"` : ""}></i><div class="an-icon-preview-name">${cls} fa-${name}</div>`;
12802
13181
  }
12803
13182
  _onInsert() {
12804
13183
  if (!this._selectedIcon) return;
12805
- const cls = this._styleSelect && this._styleSelect.value || "fa-solid";
12806
- const size = this._sizeSelect && this._sizeSelect.value || "";
12807
- const color = (this._useColorCb ? this._useColorCb.checked : false) && this._colorInput ? this._colorInput.value : "";
13184
+ const cls = this._styleSelect?.value || "fa-solid";
13185
+ const size = this._sizeSelect?.value || "";
13186
+ const color = this._useColorCb?.checked ?? false ? this._colorInput?.value ?? "" : "";
12808
13187
  const styleParts = [size ? `font-size:${size}` : "", color ? `color:${color}` : ""].filter(Boolean);
12809
13188
  const iconEl = document.createElement("i");
12810
13189
  iconEl.className = `${cls} fa-${this._selectedIcon}`;
@@ -12814,15 +13193,15 @@
12814
13193
  const savedRange = this._savedRange;
12815
13194
  const editable = this.context.layoutInfo.editable;
12816
13195
  if (savedRange) savedRange.select();
12817
- const sel = window.getSelection();
12818
- let range = sel && sel.rangeCount > 0 ? sel.getRangeAt(0) : null;
13196
+ const sel = globalThis.getSelection();
13197
+ let range = (sel?.rangeCount ?? 0) > 0 ? sel.getRangeAt(0) : null;
12819
13198
  if (!range) {
12820
13199
  range = document.createRange();
12821
13200
  range.selectNodeContents(editable);
12822
13201
  range.collapse(false);
12823
13202
  }
12824
13203
  const _sc = range.startContainer;
12825
- const _tdAnchor = (_sc.nodeType === 1 ? _sc : _sc.parentElement)?.closest?.("td, th");
13204
+ const _tdAnchor = (_sc.nodeType === 1 ? _sc : _sc.parentElement)?.closest("td, th");
12826
13205
  range.deleteContents();
12827
13206
  if (_tdAnchor && _tdAnchor.isConnected && !_tdAnchor.contains(range.startContainer)) {
12828
13207
  range.setStart(_tdAnchor, 0);
@@ -12854,7 +13233,7 @@
12854
13233
  if (this._dialog) {
12855
13234
  this._dialog.style.display = "flex";
12856
13235
  this._removeTrap = trapFocus(this._dialog, () => this._close());
12857
- setTimeout(() => this._searchInput && this._searchInput.focus(), 50);
13236
+ setTimeout(() => this._searchInput?.focus(), 50);
12858
13237
  }
12859
13238
  }
12860
13239
  _close() {
@@ -12955,7 +13334,7 @@
12955
13334
  icon: ICONS.paste,
12956
13335
  action: (ctx) => {
12957
13336
  if (!navigator.clipboard) return;
12958
- const editable = ctx.layoutInfo && ctx.layoutInfo.editable;
13337
+ const editable = ctx.layoutInfo?.editable;
12959
13338
  if (!editable) return;
12960
13339
  const doInsert = (html, text) => {
12961
13340
  editable.focus();
@@ -13080,29 +13459,29 @@
13080
13459
  this.el.style.display = "none";
13081
13460
  document.body.appendChild(this.el);
13082
13461
  this._renderItems(this._items);
13083
- const editable = this.context.layoutInfo && this.context.layoutInfo.editable;
13462
+ const editable = this.context.layoutInfo?.editable;
13084
13463
  if (editable) this._disposers.push(on(editable, "contextmenu", (e) => this._onContextMenu(e)));
13085
13464
  this._disposers.push(on(document, "click", (e) => this._maybeHide(e)));
13086
13465
  this._disposers.push(on(document, "keydown", (e) => {
13087
13466
  if (e.key === "Escape") this.hide();
13088
13467
  }));
13089
- this._disposers.push(on(window, "scroll", () => this.hide(), { passive: true }));
13468
+ this._disposers.push(on(globalThis, "scroll", () => this.hide(), { passive: true }));
13090
13469
  return this;
13091
13470
  }
13092
13471
  destroy() {
13093
13472
  this._menuDisposers.forEach((d) => {
13094
13473
  try {
13095
13474
  d();
13096
- } catch (e) {}
13475
+ } catch (_e) {}
13097
13476
  });
13098
13477
  this._menuDisposers = [];
13099
13478
  this._disposers.forEach((d) => {
13100
13479
  try {
13101
13480
  d();
13102
- } catch (e) {}
13481
+ } catch (_e) {}
13103
13482
  });
13104
13483
  this._disposers = [];
13105
- if (this.el && this.el.parentNode) this.el.parentNode.removeChild(this.el);
13484
+ if (this.el) this.el.remove();
13106
13485
  this.el = null;
13107
13486
  }
13108
13487
  _renderItems(items) {
@@ -13130,8 +13509,8 @@
13130
13509
  backBtn.appendChild(createElement("span", { class: "an-context-label" }, [backLabel]));
13131
13510
  const off = on(backBtn, "click", (e) => {
13132
13511
  e.stopPropagation();
13133
- const curLeft = parseFloat(this.el.style.left);
13134
- const curTop = parseFloat(this.el.style.top);
13512
+ const curLeft = Number.parseFloat(this.el.style.left);
13513
+ const curTop = Number.parseFloat(this.el.style.top);
13135
13514
  this._renderItems(it.navigate());
13136
13515
  this._reposition(curLeft, curTop);
13137
13516
  });
@@ -13174,8 +13553,8 @@
13174
13553
  btn.appendChild(chevron);
13175
13554
  const off = on(btn, "click", (e) => {
13176
13555
  e.stopPropagation();
13177
- const curLeft = parseFloat(this.el.style.left);
13178
- const curTop = parseFloat(this.el.style.top);
13556
+ const curLeft = Number.parseFloat(this.el.style.left);
13557
+ const curTop = Number.parseFloat(this.el.style.top);
13179
13558
  this._renderItems(it.navigate());
13180
13559
  this._reposition(curLeft, curTop);
13181
13560
  });
@@ -13290,20 +13669,20 @@
13290
13669
  });
13291
13670
  this._menuDisposers.push(offHeader);
13292
13671
  const offMove = on(gridEl, "mousemove", (e) => {
13293
- const cell = e.target.closest("[data-row]");
13672
+ const cell = e.target?.closest("[data-row]");
13294
13673
  if (!cell) return;
13295
13674
  setHighlight(+cell.dataset.row, +cell.dataset.col);
13296
13675
  });
13297
13676
  const offLeave = on(gridEl, "mouseleave", () => setHighlight(0, 0));
13298
13677
  const offClick = on(gridEl, "click", (e) => {
13299
- const cell = e.target.closest("[data-row]");
13678
+ const cell = e.target?.closest("[data-row]");
13300
13679
  if (!cell) return;
13301
13680
  const rows = +cell.dataset.row;
13302
13681
  const cols = +cell.dataset.col;
13303
- const editable = this.context.layoutInfo && this.context.layoutInfo.editable;
13682
+ const editable = this.context.layoutInfo?.editable;
13304
13683
  if (editable && this._savedRange) {
13305
13684
  editable.focus();
13306
- const sel = window.getSelection();
13685
+ const sel = globalThis.getSelection();
13307
13686
  sel.removeAllRanges();
13308
13687
  sel.addRange(this._savedRange.cloneRange());
13309
13688
  }
@@ -13321,7 +13700,7 @@
13321
13700
  class: "an-context-item",
13322
13701
  "data-name": it.name || ""
13323
13702
  });
13324
- if (typeof it.disabled === "function" ? it.disabled(this.context) : !!it.disabled) btn.disabled = true;
13703
+ if (typeof it.disabled === "function" ? it.disabled(this.context) : !!it.disabled) /** @type {HTMLButtonElement} */ btn.disabled = true;
13325
13704
  if (it.icon) {
13326
13705
  const iconSpan = createElement("span", {
13327
13706
  class: "an-context-icon",
@@ -13345,15 +13724,15 @@
13345
13724
  });
13346
13725
  }
13347
13726
  _onContextMenu(event) {
13348
- const editable = this.context.layoutInfo && this.context.layoutInfo.editable;
13727
+ const editable = this.context.layoutInfo?.editable;
13349
13728
  if (!editable) return;
13350
13729
  if (!editable.contains(event.target)) return;
13351
13730
  if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
13352
13731
  event.preventDefault();
13353
- const winSel = window.getSelection();
13732
+ const winSel = globalThis.getSelection();
13354
13733
  this._savedRange = winSel && winSel.rangeCount > 0 ? winSel.getRangeAt(0).cloneRange() : null;
13355
13734
  this._renderItems(this._items);
13356
- let openX = event.clientX;
13735
+ const openX = event.clientX;
13357
13736
  let openY = event.clientY;
13358
13737
  if (this._savedRange && !this._savedRange.collapsed) try {
13359
13738
  const selRect = this._savedRange.getBoundingClientRect();
@@ -13382,9 +13761,9 @@
13382
13761
  const h = this.el.offsetHeight;
13383
13762
  let left = rx;
13384
13763
  let top = ry;
13385
- if (left + w > window.innerWidth - 8) left = window.innerWidth - w - 8;
13764
+ if (left + w > globalThis.innerWidth - 8) left = globalThis.innerWidth - w - 8;
13386
13765
  if (left < 8) left = 8;
13387
- if (top + h > window.innerHeight - 8) top = window.innerHeight - h - 8;
13766
+ if (top + h > globalThis.innerHeight - 8) top = globalThis.innerHeight - h - 8;
13388
13767
  if (top < 8) top = 8;
13389
13768
  this.el.style.left = `${left}px`;
13390
13769
  this.el.style.top = `${top}px`;
@@ -13405,17 +13784,17 @@
13405
13784
  let node = range.startContainer;
13406
13785
  if (node.nodeType === Node.TEXT_NODE) node = node.parentElement;
13407
13786
  if (!node) return type === "foreColor" ? "#000000" : "transparent";
13408
- const cs = window.getComputedStyle(node);
13787
+ const cs = globalThis.getComputedStyle(node);
13409
13788
  if (type === "foreColor") return cs.color || "#000000";
13410
13789
  const bg = cs.backgroundColor;
13411
13790
  return !bg || bg === "rgba(0, 0, 0, 0)" || bg === "transparent" ? "transparent" : bg;
13412
13791
  }
13413
13792
  /** Restore selection, apply a color command, then hide the menu. */
13414
13793
  _applyColor(type, color) {
13415
- const editable = this.context.layoutInfo && this.context.layoutInfo.editable;
13794
+ const editable = this.context.layoutInfo?.editable;
13416
13795
  if (!editable || !this._savedRange) return;
13417
13796
  editable.focus();
13418
- const sel = window.getSelection();
13797
+ const sel = globalThis.getSelection();
13419
13798
  sel.removeAllRanges();
13420
13799
  sel.addRange(this._savedRange.cloneRange());
13421
13800
  document.execCommand(type, false, color);
@@ -13430,15 +13809,15 @@
13430
13809
  copyFormat() {
13431
13810
  const range = this._savedRange;
13432
13811
  if (!range) return;
13433
- const editable = this.context.layoutInfo && this.context.layoutInfo.editable;
13812
+ const editable = this.context.layoutInfo?.editable;
13434
13813
  let node = range.startContainer;
13435
13814
  if (node.nodeType === Node.TEXT_NODE) node = node.parentElement;
13436
13815
  if (!node || !editable || !editable.contains(node)) return;
13437
- const cs = window.getComputedStyle(node);
13816
+ const cs = globalThis.getComputedStyle(node);
13438
13817
  const explicitFontFamily = this._findExplicitStyle(node, editable, "fontFamily");
13439
13818
  const explicitFontSize = this._findExplicitStyle(node, editable, "fontSize");
13440
13819
  this._copiedFormat = {
13441
- bold: parseInt(cs.fontWeight, 10) >= 700,
13820
+ bold: Number.parseInt(cs.fontWeight, 10) >= 700,
13442
13821
  italic: cs.fontStyle === "italic" || cs.fontStyle === "oblique",
13443
13822
  underline: (cs.textDecorationLine || "").includes("underline"),
13444
13823
  strikethrough: (cs.textDecorationLine || "").includes("line-through"),
@@ -13469,10 +13848,10 @@
13469
13848
  pasteFormat() {
13470
13849
  if (!this._copiedFormat || !this._savedRange) return;
13471
13850
  const fmt = this._copiedFormat;
13472
- const editable = this.context.layoutInfo && this.context.layoutInfo.editable;
13851
+ const editable = this.context.layoutInfo?.editable;
13473
13852
  if (!editable) return;
13474
13853
  editable.focus();
13475
- const sel = window.getSelection();
13854
+ const sel = globalThis.getSelection();
13476
13855
  sel.removeAllRanges();
13477
13856
  sel.addRange(this._savedRange.cloneRange());
13478
13857
  document.execCommand("removeFormat");
@@ -13489,14 +13868,14 @@
13489
13868
  const preExisting = new Set(editable.querySelectorAll("font[size=\"7\"]"));
13490
13869
  document.execCommand("fontSize", false, "7");
13491
13870
  editable.querySelectorAll("font[size=\"7\"]").forEach((el) => {
13492
- if (!preExisting.has(el)) el.setAttribute("data-an-tmp", marker);
13871
+ if (!preExisting.has(el)) /** @type {HTMLElement} */ el.dataset.anTmp = marker;
13493
13872
  });
13494
13873
  editable.querySelectorAll(`[data-an-tmp="${marker}"]`).forEach((el) => {
13495
13874
  const span = document.createElement("span");
13496
13875
  span.style.fontSize = fmt.fontSize;
13497
13876
  el.parentNode.insertBefore(span, el);
13498
13877
  while (el.firstChild) span.appendChild(el.firstChild);
13499
- el.parentNode.removeChild(el);
13878
+ el.remove();
13500
13879
  });
13501
13880
  }
13502
13881
  this.context.invoke("editor.afterCommand");
@@ -13504,10 +13883,10 @@
13504
13883
  /** Strip all inline formatting from the saved selection. */
13505
13884
  removeFormat() {
13506
13885
  if (!this._savedRange) return;
13507
- const editable = this.context.layoutInfo && this.context.layoutInfo.editable;
13886
+ const editable = this.context.layoutInfo?.editable;
13508
13887
  if (!editable) return;
13509
13888
  editable.focus();
13510
- const sel = window.getSelection();
13889
+ const sel = globalThis.getSelection();
13511
13890
  sel.removeAllRanges();
13512
13891
  sel.addRange(this._savedRange.cloneRange());
13513
13892
  document.execCommand("removeFormat");
@@ -13519,7 +13898,7 @@
13519
13898
  while (el = iter.nextNode()) {
13520
13899
  if (!editable.contains(el) || el === editable) continue;
13521
13900
  try {
13522
- if (range.intersectsNode(el)) el.removeAttribute("style");
13901
+ if (range.intersectsNode(el)) /** @type {Element} */ el.removeAttribute("style");
13523
13902
  } catch {}
13524
13903
  }
13525
13904
  this.context.invoke("editor.afterCommand");
@@ -13621,14 +14000,14 @@
13621
14000
  destroy() {
13622
14001
  this._disposers.forEach((d) => d());
13623
14002
  this._disposers = [];
13624
- if (this._dialog && this._dialog.parentNode) this._dialog.parentNode.removeChild(this._dialog);
14003
+ this._dialog?.remove();
13625
14004
  this._dialog = null;
13626
14005
  }
13627
14006
  show() {
13628
14007
  if (this._dialog) {
13629
14008
  this._dialog.style.display = "flex";
13630
14009
  this._removeTrap = trapFocus(this._dialog, () => this._close());
13631
- setTimeout(() => this._closeBtn && this._closeBtn.focus(), 50);
14010
+ setTimeout(() => this._closeBtn?.focus(), 50);
13632
14011
  }
13633
14012
  }
13634
14013
  _close() {
@@ -13734,7 +14113,7 @@
13734
14113
  this._clearHighlights();
13735
14114
  this._disposers.forEach((d) => d());
13736
14115
  this._disposers = [];
13737
- if (this._dialog && this._dialog.parentNode) this._dialog.parentNode.removeChild(this._dialog);
14116
+ if (this._dialog && this._dialog.parentNode) this._dialog.remove();
13738
14117
  this._dialog = null;
13739
14118
  }
13740
14119
  /**
@@ -13781,8 +14160,8 @@
13781
14160
  const replaceActions = this._dialog.querySelector(".an-fr-replace-actions");
13782
14161
  const title = this._dialog.querySelector(".an-dialog-title");
13783
14162
  const isReplace = this._mode === "replace";
13784
- if (replaceRow) replaceRow.style.display = isReplace ? "" : "none";
13785
- if (replaceActions) replaceActions.style.display = isReplace ? "" : "none";
14163
+ if (replaceRow) /** @type {HTMLElement} */ replaceRow.style.display = isReplace ? "" : "none";
14164
+ if (replaceActions) /** @type {HTMLElement} */ replaceActions.style.display = isReplace ? "" : "none";
13786
14165
  if (title) title.textContent = isReplace ? this.context.locale.findReplace.findReplaceTitle : this.context.locale.findReplace.findTitle;
13787
14166
  }
13788
14167
  _buildDialog() {
@@ -13793,106 +14172,123 @@
13793
14172
  "aria-modal": "true",
13794
14173
  "aria-label": L.findReplaceTitle
13795
14174
  });
13796
- const box = createElement("div", { class: "an-dialog-box" });
13797
- const titleRow = createElement("div", { class: "an-icon-title-row" });
14175
+ const box = createElement("div", { class: "an-dialog-box an-fr-box" });
14176
+ const header = createElement("div", { class: "an-fr-header" });
14177
+ const titleGroup = createElement("div", { class: "an-dialog-title-group" });
14178
+ const iconEl = createElement("span", { class: "an-dialog-icon an-dialog-icon--sm" });
14179
+ iconEl.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>`;
13798
14180
  const title = createElement("h3", { class: "an-dialog-title" });
13799
14181
  title.textContent = L.findTitle;
14182
+ titleGroup.append(iconEl, title);
13800
14183
  const closeBtn = createElement("button", {
13801
14184
  type: "button",
13802
14185
  class: "an-icon-close",
13803
- "aria-label": "Close"
14186
+ title: L.close,
14187
+ "aria-label": L.close
13804
14188
  });
13805
- closeBtn.textContent = L.close;
14189
+ closeBtn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>`;
13806
14190
  this._closeBtn = closeBtn;
13807
- titleRow.append(title, closeBtn);
13808
- box.appendChild(titleRow);
13809
- const findRow = createElement("div", { class: "an-fr-find-row" });
14191
+ header.append(titleGroup, closeBtn);
14192
+ box.appendChild(header);
14193
+ const searchBar = createElement("div", { class: "an-fr-search-bar" });
13810
14194
  const findInput = createElement("input", {
13811
14195
  type: "text",
13812
- class: "an-input",
14196
+ class: "an-input an-fr-input",
13813
14197
  placeholder: L.findPlaceholder,
13814
14198
  "aria-label": L.searchAriaLabel
13815
14199
  });
13816
14200
  this._findInput = findInput;
13817
- findRow.appendChild(findInput);
13818
- box.appendChild(findRow);
13819
- const optRow = createElement("div", { class: "an-fr-options-row" });
13820
- const caseLabel = createElement("label", { class: "an-label an-label-inline" });
13821
14201
  const caseCheckbox = createElement("input", {
13822
14202
  type: "checkbox",
13823
- "aria-label": "Case sensitive"
14203
+ style: "display:none",
14204
+ "aria-hidden": "true"
13824
14205
  });
13825
14206
  this._caseCheckbox = caseCheckbox;
13826
- caseLabel.append(caseCheckbox, document.createTextNode(L.caseSensitive));
13827
- const counter = createElement("span", { class: "an-fr-counter" });
13828
- this._counterEl = counter;
13829
- optRow.append(caseLabel, counter);
13830
- box.appendChild(optRow);
13831
- const findActions = createElement("div", { class: "an-dialog-actions an-fr-find-actions" });
14207
+ const caseBtn = createElement("button", {
14208
+ type: "button",
14209
+ class: "an-fr-icon-btn",
14210
+ title: "Case sensitive",
14211
+ "aria-label": "Case sensitive"
14212
+ });
14213
+ caseBtn.textContent = "Aa";
13832
14214
  const prevBtn = createElement("button", {
13833
14215
  type: "button",
13834
- class: "an-btn"
14216
+ class: "an-fr-icon-btn",
14217
+ title: "Previous (Shift+Enter)",
14218
+ "aria-label": "Previous"
13835
14219
  });
13836
- prevBtn.textContent = L.prevBtn;
14220
+ prevBtn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="18 15 12 9 6 15"/></svg>`;
13837
14221
  const nextBtn = createElement("button", {
13838
14222
  type: "button",
13839
- class: "an-btn an-btn-primary"
14223
+ class: "an-fr-icon-btn",
14224
+ title: "Next (Enter)",
14225
+ "aria-label": "Next"
13840
14226
  });
13841
- nextBtn.textContent = L.nextBtn;
13842
- findActions.append(prevBtn, nextBtn);
13843
- box.appendChild(findActions);
14227
+ nextBtn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg>`;
14228
+ const counter = createElement("span", { class: "an-fr-counter" });
14229
+ this._counterEl = counter;
14230
+ searchBar.append(findInput, caseCheckbox, caseBtn, prevBtn, nextBtn, counter);
14231
+ box.appendChild(searchBar);
13844
14232
  const replaceRow = createElement("div", { class: "an-fr-replace-row" });
13845
14233
  replaceRow.style.display = "none";
13846
14234
  const replaceInput = createElement("input", {
13847
14235
  type: "text",
13848
- class: "an-input",
14236
+ class: "an-input an-fr-input",
13849
14237
  placeholder: L.replacePlaceholder,
13850
14238
  "aria-label": L.replaceAriaLabel
13851
14239
  });
13852
14240
  this._replaceInput = replaceInput;
13853
- replaceRow.appendChild(replaceInput);
13854
- box.appendChild(replaceRow);
13855
- const replaceActions = createElement("div", { class: "an-dialog-actions an-fr-replace-actions" });
13856
- replaceActions.style.display = "none";
13857
14241
  const replaceBtn = createElement("button", {
13858
14242
  type: "button",
13859
- class: "an-btn"
14243
+ class: "an-btn an-fr-replace-btn"
13860
14244
  });
13861
14245
  replaceBtn.textContent = L.replaceBtn;
13862
14246
  const replaceAllBtn = createElement("button", {
13863
14247
  type: "button",
13864
- class: "an-btn an-btn-primary"
14248
+ class: "an-btn an-btn-primary an-fr-replace-btn"
13865
14249
  });
13866
14250
  replaceAllBtn.textContent = L.replaceAllBtn;
13867
- replaceActions.append(replaceBtn, replaceAllBtn);
14251
+ replaceRow.append(replaceInput, replaceBtn, replaceAllBtn);
14252
+ box.appendChild(replaceRow);
14253
+ const replaceActions = createElement("div", { class: "an-fr-replace-actions" });
14254
+ replaceActions.style.display = "none";
13868
14255
  box.appendChild(replaceActions);
13869
14256
  overlay.appendChild(box);
14257
+ makeDraggable(header, box);
13870
14258
  const d1 = on(closeBtn, "click", () => this._close());
13871
14259
  const d2 = on(overlay, "click", (e) => {
13872
14260
  if (e.target === overlay) this._close();
13873
14261
  });
13874
14262
  const d3 = on(findInput, "input", () => this._onSearch());
13875
- const d4 = on(caseCheckbox, "change", () => {
14263
+ const d4 = on(caseBtn, "click", () => {
14264
+ this._caseSensitive = !this._caseSensitive;
14265
+ caseCheckbox.checked = this._caseSensitive;
14266
+ caseBtn.classList.toggle("an-fr-icon-btn--active", this._caseSensitive);
14267
+ this._onSearch();
14268
+ });
14269
+ const d5 = on(caseCheckbox, "change", () => {
13876
14270
  this._caseSensitive = caseCheckbox.checked;
14271
+ caseBtn.classList.toggle("an-fr-icon-btn--active", this._caseSensitive);
13877
14272
  this._onSearch();
13878
14273
  });
13879
- const d5 = on(nextBtn, "click", () => this._next());
13880
- const d6 = on(prevBtn, "click", () => this._prev());
13881
- const d7 = on(replaceBtn, "click", () => this._replace());
13882
- const d8 = on(replaceAllBtn, "click", () => this._replaceAll());
13883
- const d9 = on(findInput, "keydown", (e) => {
13884
- if (e.key === "Enter") {
14274
+ const d6 = on(nextBtn, "click", () => this._next());
14275
+ const d7 = on(prevBtn, "click", () => this._prev());
14276
+ const d8 = on(replaceBtn, "click", () => this._replace());
14277
+ const d9 = on(replaceAllBtn, "click", () => this._replaceAll());
14278
+ const d10 = on(findInput, "keydown", (e) => {
14279
+ const ke = e;
14280
+ if (ke.key === "Enter") {
13885
14281
  e.preventDefault();
13886
- e.shiftKey ? this._prev() : this._next();
14282
+ ke.shiftKey ? this._prev() : this._next();
13887
14283
  }
13888
14284
  });
13889
- const d10 = on(replaceInput, "keydown", (e) => {
14285
+ const d11 = on(replaceInput, "keydown", (e) => {
13890
14286
  if (e.key === "Enter") {
13891
14287
  e.preventDefault();
13892
14288
  this._replace();
13893
14289
  }
13894
14290
  });
13895
- this._disposers.push(d1, d2, d3, d4, d5, d6, d7, d8, d9, d10);
14291
+ this._disposers.push(d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11);
13896
14292
  return overlay;
13897
14293
  }
13898
14294
  _onSearch() {
@@ -13960,15 +14356,19 @@
13960
14356
  const re = this._queryRegex;
13961
14357
  const MAX_RESULTS = 500;
13962
14358
  const walker = document.createTreeWalker(root, 4);
13963
- let node;
13964
- while ((node = walker.nextNode()) && results.length < MAX_RESULTS) {
14359
+ let node = walker.nextNode();
14360
+ while (node && results.length < MAX_RESULTS) {
13965
14361
  re.lastIndex = 0;
13966
14362
  let m;
13967
- while ((m = re.exec(node.textContent)) !== null && results.length < MAX_RESULTS) results.push({
14363
+ while ((m = re.exec(
14364
+ /** @type {Text} */
14365
+ node.textContent
14366
+ )) !== null && results.length < MAX_RESULTS) results.push({
13968
14367
  node,
13969
14368
  start: m.index,
13970
14369
  end: m.index + m[0].length
13971
14370
  });
14371
+ node = walker.nextNode();
13972
14372
  }
13973
14373
  return results;
13974
14374
  }
@@ -14003,7 +14403,7 @@
14003
14403
  const parent = match.mark.parentNode;
14004
14404
  const textNode = document.createTextNode(replacement);
14005
14405
  parent.insertBefore(textNode, match.mark);
14006
- parent.removeChild(match.mark);
14406
+ match.mark.remove();
14007
14407
  parent.normalize();
14008
14408
  this.context.invoke("editor.afterCommand");
14009
14409
  const savedIndex = this._currentIndex;
@@ -14021,7 +14421,7 @@
14021
14421
  if (!mark || !mark.parentNode) return;
14022
14422
  const textNode = document.createTextNode(replacement);
14023
14423
  mark.parentNode.insertBefore(textNode, mark);
14024
- mark.parentNode.removeChild(mark);
14424
+ mark.remove();
14025
14425
  });
14026
14426
  if (this.context.layoutInfo.editable) this.context.layoutInfo.editable.normalize();
14027
14427
  this._matches = [];
@@ -14040,7 +14440,7 @@
14040
14440
  const parent = mark.parentNode;
14041
14441
  if (!parent) return;
14042
14442
  while (mark.firstChild) parent.insertBefore(mark.firstChild, mark);
14043
- parent.removeChild(mark);
14443
+ mark.remove();
14044
14444
  });
14045
14445
  editable.normalize();
14046
14446
  this._matches = [];
@@ -14091,7 +14491,7 @@
14091
14491
  resolve(null);
14092
14492
  }
14093
14493
  };
14094
- if (img.src.startsWith("data:") || img.src.startsWith("blob:") || img.src.startsWith(window.location.origin)) {
14494
+ if (img.src.startsWith("data:") || img.src.startsWith("blob:") || img.src.startsWith(globalThis.location.origin)) {
14095
14495
  tryDraw(img);
14096
14496
  return;
14097
14497
  }
@@ -14253,9 +14653,10 @@
14253
14653
  }), on(h, "touchstart", (e) => {
14254
14654
  e.preventDefault();
14255
14655
  e.stopPropagation();
14656
+ const te = e;
14256
14657
  this._startHandleDrag({
14257
- clientX: e.touches[0].clientX,
14258
- clientY: e.touches[0].clientY
14658
+ clientX: te.touches[0].clientX,
14659
+ clientY: te.touches[0].clientY
14259
14660
  }, id);
14260
14661
  }, { passive: false }));
14261
14662
  this._handles[id] = h;
@@ -14272,9 +14673,10 @@
14272
14673
  if (e.target !== cropBox && e.target !== grid) return;
14273
14674
  e.preventDefault();
14274
14675
  e.stopPropagation();
14676
+ const te2 = e;
14275
14677
  this._startBoxMove({
14276
- clientX: e.touches[0].clientX,
14277
- clientY: e.touches[0].clientY
14678
+ clientX: te2.touches[0].clientX,
14679
+ clientY: te2.touches[0].clientY
14278
14680
  });
14279
14681
  }, { passive: false }));
14280
14682
  const infoEl = document.createElement("div");
@@ -14347,8 +14749,8 @@
14347
14749
  this._cropBox.style.top = `${y}px`;
14348
14750
  this._cropBox.style.width = `${w}px`;
14349
14751
  this._cropBox.style.height = `${h}px`;
14350
- const vw = window.innerWidth;
14351
- const vh = window.innerHeight;
14752
+ const vw = globalThis.innerWidth;
14753
+ const vh = globalThis.innerHeight;
14352
14754
  this._scrim.style.clipPath = [
14353
14755
  `polygon(`,
14354
14756
  `0 0, ${vw}px 0, ${vw}px ${vh}px, 0 ${vh}px, 0 0,`,
@@ -14372,7 +14774,7 @@
14372
14774
  }
14373
14775
  const margin = 8;
14374
14776
  let tbTop = y + h + margin;
14375
- if (tbTop + 40 > window.innerHeight - margin) tbTop = y - 40 - margin;
14777
+ if (tbTop + 40 > globalThis.innerHeight - margin) tbTop = y - 40 - margin;
14376
14778
  this._toolbar.style.left = `${x}px`;
14377
14779
  this._toolbar.style.top = `${tbTop}px`;
14378
14780
  }
@@ -14436,9 +14838,10 @@
14436
14838
  _attachDocDrag(onMove) {
14437
14839
  const onTouchMove = (e) => {
14438
14840
  e.preventDefault();
14841
+ const te3 = e;
14439
14842
  onMove({
14440
- clientX: e.touches[0].clientX,
14441
- clientY: e.touches[0].clientY
14843
+ clientX: te3.touches[0].clientX,
14844
+ clientY: te3.touches[0].clientY
14442
14845
  });
14443
14846
  };
14444
14847
  const cleanup = () => {
@@ -14498,7 +14901,7 @@
14498
14901
  this._close(false);
14499
14902
  return;
14500
14903
  }
14501
- const fmt = img.src.match(/^data:image\/(jpe?g)/i) ? "image/jpeg" : "image/png";
14904
+ const fmt = /^data:image\/(jpe?g)/i.exec(img.src) ? "image/jpeg" : "image/png";
14502
14905
  const quality = fmt === "image/jpeg" ? .92 : void 0;
14503
14906
  const newSrc = canvas.toDataURL(fmt, quality);
14504
14907
  this._close(false);
@@ -14538,7 +14941,7 @@
14538
14941
  banner.textContent = msg;
14539
14942
  document.body.appendChild(banner);
14540
14943
  setTimeout(() => {
14541
- if (banner.parentNode) banner.parentNode.removeChild(banner);
14944
+ banner.remove();
14542
14945
  }, 4e3);
14543
14946
  }
14544
14947
  /**
@@ -14553,7 +14956,7 @@
14553
14956
  this._cropBox,
14554
14957
  this._toolbar
14555
14958
  ].forEach((el) => {
14556
- if (el && el.parentNode) el.parentNode.removeChild(el);
14959
+ el?.remove();
14557
14960
  });
14558
14961
  this._scrim = null;
14559
14962
  this._cropBox = null;
@@ -14574,7 +14977,7 @@
14574
14977
  *
14575
14978
  * Activated when both `autoSave` and `autoSaveRestore` options are true.
14576
14979
  * On initialize it checks localStorage for a draft that is within the
14577
- * `autoSaveRestoreTimeout` day window. If one is found a dismissible banner
14980
+ * `autoSaveRestoreTimeout` day globalThis. If one is found a dismissible banner
14578
14981
  * is prepended to the editor container.
14579
14982
  */
14580
14983
  var AutoSaveRestore = class {
@@ -14660,7 +15063,7 @@
14660
15063
  this._removeBanner();
14661
15064
  }
14662
15065
  _removeBanner() {
14663
- if (this._banner && this._banner.parentNode) this._banner.parentNode.removeChild(this._banner);
15066
+ this._banner?.remove();
14664
15067
  this._banner = null;
14665
15068
  }
14666
15069
  };
@@ -14723,8 +15126,8 @@
14723
15126
  * @returns {{ text: string, range: Range, lineNode: Node } | null}
14724
15127
  */
14725
15128
  _getLineContext() {
14726
- const sel = window.getSelection();
14727
- if (!sel || !sel.rangeCount) return null;
15129
+ const sel = globalThis.getSelection();
15130
+ if (!sel?.rangeCount) return null;
14728
15131
  const range = sel.getRangeAt(0);
14729
15132
  if (!range.collapsed) return null;
14730
15133
  const editable = this.context.layoutInfo.editable;
@@ -14743,7 +15146,7 @@
14743
15146
  }
14744
15147
  _isBlock(node) {
14745
15148
  if (node.nodeType !== Node.ELEMENT_NODE) return false;
14746
- const display = window.getComputedStyle(node).display;
15149
+ const display = globalThis.getComputedStyle(node).display;
14747
15150
  return display === "block" || display === "list-item" || display === "table-cell";
14748
15151
  }
14749
15152
  /** Applies block rule on Space key. Returns true if a rule fired. */
@@ -14774,7 +15177,7 @@
14774
15177
  }
14775
15178
  ];
14776
15179
  for (const { re, handler } of blockPatterns) {
14777
- const m = text.match(re);
15180
+ const m = re.exec(text);
14778
15181
  if (m) {
14779
15182
  handler(m);
14780
15183
  return true;
@@ -14798,8 +15201,8 @@
14798
15201
  return false;
14799
15202
  }
14800
15203
  _selectLineAndDelete() {
14801
- const sel = window.getSelection();
14802
- if (!sel || !sel.rangeCount) return;
15204
+ const sel = globalThis.getSelection();
15205
+ if (!sel?.rangeCount) return;
14803
15206
  const range = sel.getRangeAt(0);
14804
15207
  const startRange = document.createRange();
14805
15208
  startRange.setStart(range.startContainer.parentNode || range.startContainer, 0);
@@ -14837,8 +15240,8 @@
14837
15240
  this.context.triggerEvent("change", this.context.getHTML());
14838
15241
  }
14839
15242
  _onInput() {
14840
- const sel = window.getSelection();
14841
- if (!sel || !sel.rangeCount) return;
15243
+ const sel = globalThis.getSelection();
15244
+ if (!sel?.rangeCount) return;
14842
15245
  const range = sel.getRangeAt(0);
14843
15246
  if (!range.collapsed) return;
14844
15247
  if (!this.context.layoutInfo.editable.contains(range.startContainer)) return;
@@ -14866,7 +15269,7 @@
14866
15269
  ];
14867
15270
  const upToCursor = text.slice(0, offset);
14868
15271
  for (const { re, tag } of inlineRules) {
14869
- const m = upToCursor.match(re);
15272
+ const m = re.exec(upToCursor);
14870
15273
  if (!m) continue;
14871
15274
  const matchStart = upToCursor.length - m[0].length;
14872
15275
  const matchEnd = offset;
@@ -14877,11 +15280,8 @@
14877
15280
  el.textContent = innerText;
14878
15281
  const beforeNode = document.createTextNode(before);
14879
15282
  const afterNode = document.createTextNode("​" + after);
14880
- const parent = node.parentNode;
14881
- parent.insertBefore(beforeNode, node);
14882
- parent.insertBefore(el, node);
14883
- parent.insertBefore(afterNode, node);
14884
- parent.removeChild(node);
15283
+ /** @type {ChildNode} */ node.before(beforeNode, el, afterNode);
15284
+ /** @type {ChildNode} */ node.remove();
14885
15285
  const newRange = document.createRange();
14886
15286
  newRange.setStart(afterNode, 1);
14887
15287
  newRange.collapse(true);
@@ -14952,12 +15352,12 @@
14952
15352
  strikethrough: (ctx) => ctx.invoke("editor.strikethrough"),
14953
15353
  link: (ctx) => ctx.invoke("linkDialog.show"),
14954
15354
  removeFormat: (ctx) => {
14955
- const editable = ctx.layoutInfo && ctx.layoutInfo.editable;
15355
+ const editable = ctx.layoutInfo?.editable;
14956
15356
  if (!editable) return;
14957
15357
  editable.focus();
14958
15358
  document.execCommand("removeFormat");
14959
- const sel = window.getSelection();
14960
- if (sel && sel.rangeCount > 0 && !sel.getRangeAt(0).collapsed) {
15359
+ const sel = globalThis.getSelection();
15360
+ if (sel?.rangeCount > 0 && !sel.getRangeAt(0).collapsed) {
14961
15361
  const range = sel.getRangeAt(0);
14962
15362
  const ancestor = range.commonAncestorContainer;
14963
15363
  const root = ancestor.nodeType === 1 ? ancestor : ancestor.parentElement;
@@ -15015,13 +15415,15 @@
15015
15415
  const d6 = this.context.on("contextMenu:hide", () => {
15016
15416
  this._contextMenuOpen = false;
15017
15417
  });
15018
- this._disposers.push(d1, d2, d3, d4, d5, d6);
15418
+ const d7 = on(globalThis, "scroll", () => this._hide(), { passive: true });
15419
+ const d8 = on(globalThis, "resize", () => this._hide(), { passive: true });
15420
+ this._disposers.push(d1, d2, d3, d4, d5, d6, d7, d8);
15019
15421
  return this;
15020
15422
  }
15021
15423
  destroy() {
15022
- if (this._el && this._el.parentNode) this._el.parentNode.removeChild(this._el);
15424
+ this._el?.remove();
15023
15425
  this._el = null;
15024
- if (this._picker && this._picker.parentNode) this._picker.parentNode.removeChild(this._picker);
15426
+ this._picker?.remove();
15025
15427
  this._picker = null;
15026
15428
  this._disposers.forEach((d) => d());
15027
15429
  this._disposers = [];
@@ -15129,20 +15531,22 @@
15129
15531
  picker.appendChild(customRow);
15130
15532
  document.body.appendChild(picker);
15131
15533
  this._picker = picker;
15132
- this._picker._paletteEl = palette;
15133
- this._picker._noColorBtn = noColorBtn;
15134
- this._picker._colorInput = colorInput;
15534
+ const pickerAny = picker;
15535
+ pickerAny._paletteEl = palette;
15536
+ pickerAny._noColorBtn = noColorBtn;
15537
+ pickerAny._colorInput = colorInput;
15135
15538
  }
15136
15539
  _openColorPicker(type, anchorBtn) {
15137
- const sel = window.getSelection();
15138
- if (sel && sel.rangeCount > 0) this._savedRange = sel.getRangeAt(0).cloneRange();
15540
+ const sel = globalThis.getSelection();
15541
+ if (sel?.rangeCount > 0) this._savedRange = sel.getRangeAt(0).cloneRange();
15139
15542
  this._pickerType = type;
15140
- const palette = this._picker._paletteEl;
15141
- const noColorBtn = this._picker._noColorBtn;
15543
+ const pickerAny = this._picker;
15544
+ const palette = pickerAny._paletteEl;
15545
+ const noColorBtn = pickerAny._noColorBtn;
15142
15546
  if (type === "hiliteColor") {
15143
15547
  if (!palette.contains(noColorBtn)) palette.appendChild(noColorBtn);
15144
- } else if (palette.contains(noColorBtn)) palette.removeChild(noColorBtn);
15145
- this._picker._colorInput.value = type === "foreColor" ? "#000000" : "#ffff00";
15548
+ } else if (palette.contains(noColorBtn)) noColorBtn.remove();
15549
+ /** @type {any} */ this._picker._colorInput.value = type === "foreColor" ? "#000000" : "#ffff00";
15146
15550
  this._picker.style.display = "block";
15147
15551
  const pw = this._picker.offsetWidth;
15148
15552
  const ph = this._picker.offsetHeight;
@@ -15150,7 +15554,7 @@
15150
15554
  let top = toolbarRect.top - ph - 6;
15151
15555
  if (top < 8) top = toolbarRect.bottom + 6;
15152
15556
  let left = anchorBtn.getBoundingClientRect().left;
15153
- left = Math.max(8, Math.min(left, window.innerWidth - pw - 8));
15557
+ left = Math.max(8, Math.min(left, globalThis.innerWidth - pw - 8));
15154
15558
  this._picker.style.left = `${left}px`;
15155
15559
  this._picker.style.top = `${top}px`;
15156
15560
  }
@@ -15160,10 +15564,10 @@
15160
15564
  }
15161
15565
  /** Restore the saved selection, apply execCommand, update the color strip, then close the picker. */
15162
15566
  _applyColor(type, color) {
15163
- const editable = this.context.layoutInfo && this.context.layoutInfo.editable;
15567
+ const editable = this.context.layoutInfo?.editable;
15164
15568
  if (!editable || !this._savedRange) return;
15165
15569
  editable.focus();
15166
- const sel = window.getSelection();
15570
+ const sel = globalThis.getSelection();
15167
15571
  sel.removeAllRanges();
15168
15572
  try {
15169
15573
  sel.addRange(this._savedRange.cloneRange());
@@ -15174,9 +15578,8 @@
15174
15578
  if (!document.execCommand(cmd, false, color) && cmd === "hiliteColor") document.execCommand("backColor", false, color);
15175
15579
  this.context.invoke("editor.afterCommand");
15176
15580
  const name = type === "hiliteColor" ? "hiliteColor" : "foreColor";
15177
- const btn = this._el && this._el.querySelector(`[data-name="${name}"]`);
15178
- const strip = btn && btn.querySelector(".an-bubble-color-strip");
15179
- if (strip) strip.style.background = color === "transparent" ? "transparent" : color;
15581
+ const strip = (this._el?.querySelector(`[data-name="${name}"]`))?.querySelector(".an-bubble-color-strip");
15582
+ if (strip) /** @type {HTMLElement} */ strip.style.background = color === "transparent" ? "transparent" : color;
15180
15583
  this._closeColorPicker();
15181
15584
  this._syncActive();
15182
15585
  }
@@ -15190,8 +15593,16 @@
15190
15593
  const gap = 8;
15191
15594
  let left = rect.left + rect.width / 2 - bw / 2;
15192
15595
  let top = rect.top - bh - gap;
15193
- left = Math.max(8, Math.min(left, window.innerWidth - bw - 8));
15596
+ left = Math.max(8, Math.min(left, globalThis.innerWidth - bw - 8));
15194
15597
  if (top < 8) top = rect.bottom + gap;
15598
+ const tableTooltipEl = document.querySelector(".an-table-tooltip");
15599
+ if (tableTooltipEl && tableTooltipEl.style.display !== "none") {
15600
+ const ttRect = tableTooltipEl.getBoundingClientRect();
15601
+ if (top < ttRect.bottom + gap && top + bh > ttRect.top - gap) {
15602
+ top = rect.bottom + gap;
15603
+ if (top + bh > globalThis.innerHeight - 8) top = ttRect.bottom + gap;
15604
+ }
15605
+ }
15195
15606
  el.style.top = `${top}px`;
15196
15607
  el.style.left = `${left}px`;
15197
15608
  el.style.visibility = "";
@@ -15215,20 +15626,18 @@
15215
15626
  /** Read the current selection's color and update the color-strip indicators. */
15216
15627
  _syncColorStrips() {
15217
15628
  if (!this._el) return;
15218
- const sel = window.getSelection();
15629
+ const sel = globalThis.getSelection();
15219
15630
  if (!sel || !sel.rangeCount) return;
15220
15631
  let node = sel.getRangeAt(0).startContainer;
15221
15632
  if (node.nodeType === Node.TEXT_NODE) node = node.parentElement;
15222
15633
  if (!node) return;
15223
- const cs = window.getComputedStyle(node);
15224
- const foreBtn = this._el.querySelector("[data-name=\"foreColor\"]");
15225
- const foreStrip = foreBtn && foreBtn.querySelector(".an-bubble-color-strip");
15226
- if (foreStrip) foreStrip.style.background = cs.color || "#000000";
15227
- const hiliteBtn = this._el.querySelector("[data-name=\"hiliteColor\"]");
15228
- const hiliteStrip = hiliteBtn && hiliteBtn.querySelector(".an-bubble-color-strip");
15634
+ const cs = globalThis.getComputedStyle(node);
15635
+ const foreStrip = this._el.querySelector("[data-name=\"foreColor\"]")?.querySelector(".an-bubble-color-strip");
15636
+ if (foreStrip) /** @type {HTMLElement} */ foreStrip.style.background = cs.color || "#000000";
15637
+ const hiliteStrip = this._el.querySelector("[data-name=\"hiliteColor\"]")?.querySelector(".an-bubble-color-strip");
15229
15638
  if (hiliteStrip) {
15230
15639
  const bg = cs.backgroundColor;
15231
- hiliteStrip.style.background = !bg || bg === "rgba(0, 0, 0, 0)" || bg === "transparent" ? "transparent" : bg;
15640
+ /** @type {HTMLElement} */ hiliteStrip.style.background = !bg || bg === "rgba(0, 0, 0, 0)" || bg === "transparent" ? "transparent" : bg;
15232
15641
  }
15233
15642
  }
15234
15643
  _onSelectionChange() {
@@ -15236,7 +15645,7 @@
15236
15645
  this._rafId = requestAnimationFrame(() => {
15237
15646
  if (this._contextMenuOpen) return;
15238
15647
  if (this._picker && this._picker.style.display !== "none") return;
15239
- const sel = window.getSelection();
15648
+ const sel = globalThis.getSelection();
15240
15649
  if (!sel || sel.isCollapsed || !sel.rangeCount) {
15241
15650
  this._hide();
15242
15651
  return;
@@ -15259,8 +15668,8 @@
15259
15668
  }
15260
15669
  _onMousedown(e) {
15261
15670
  if (!this._visible) return;
15262
- if (this._el && this._el.contains(e.target)) return;
15263
- if (this._picker && this._picker.contains(e.target)) return;
15671
+ if (this._el?.contains(e.target)) return;
15672
+ if (this._picker?.contains(e.target)) return;
15264
15673
  if (this.context.layoutInfo.editable.contains(e.target)) return;
15265
15674
  this._hide();
15266
15675
  }
@@ -15345,7 +15754,7 @@
15345
15754
  }
15346
15755
  destroy() {
15347
15756
  clearTimeout(this._debounceTimer);
15348
- if (this._dropdown && this._dropdown.parentNode) this._dropdown.parentNode.removeChild(this._dropdown);
15757
+ this._dropdown?.remove();
15349
15758
  this._dropdown = null;
15350
15759
  this._disposers.forEach((d) => d());
15351
15760
  this._disposers = [];
@@ -15356,11 +15765,11 @@
15356
15765
  el.setAttribute("role", "listbox");
15357
15766
  el.addEventListener("mousedown", (e) => e.preventDefault());
15358
15767
  el.addEventListener("click", (e) => {
15359
- const item = e.target.closest(".an-mention-item");
15768
+ const item = e.target?.closest(".an-mention-item");
15360
15769
  if (item) this._select(+item.dataset.index);
15361
15770
  });
15362
15771
  el.addEventListener("mousemove", (e) => {
15363
- const item = e.target.closest(".an-mention-item");
15772
+ const item = e.target?.closest(".an-mention-item");
15364
15773
  if (item) this._highlightItem(+item.dataset.index);
15365
15774
  });
15366
15775
  document.body.appendChild(el);
@@ -15375,7 +15784,7 @@
15375
15784
  const li = document.createElement("div");
15376
15785
  li.className = "an-mention-item";
15377
15786
  li.setAttribute("role", "option");
15378
- li.dataset.index = i;
15787
+ li.dataset.index = String(i);
15379
15788
  if (item.avatar) {
15380
15789
  const img = document.createElement("img");
15381
15790
  img.src = item.avatar;
@@ -15409,8 +15818,8 @@
15409
15818
  const ddw = dd.offsetWidth;
15410
15819
  let top = rect.bottom + 4;
15411
15820
  let left = rect.left;
15412
- if (rect.bottom + ddh + 8 > window.innerHeight) top = rect.top - ddh - 4;
15413
- left = Math.max(8, Math.min(left, window.innerWidth - ddw - 8));
15821
+ if (rect.bottom + ddh + 8 > globalThis.innerHeight) top = rect.top - ddh - 4;
15822
+ left = Math.max(8, Math.min(left, globalThis.innerWidth - ddw - 8));
15414
15823
  dd.style.top = `${top}px`;
15415
15824
  dd.style.left = `${left}px`;
15416
15825
  dd.style.visibility = "";
@@ -15434,7 +15843,7 @@
15434
15843
  * collapsed range is reliable at this point but often empty inside async callbacks.
15435
15844
  */
15436
15845
  _captureCaretRect() {
15437
- if (this._triggerNode && this._triggerNode.isConnected) try {
15846
+ if (this._triggerNode?.isConnected) try {
15438
15847
  const r = document.createRange();
15439
15848
  const end = Math.min(this._triggerOffset + 1, this._triggerNode.textContent.length);
15440
15849
  r.setStart(this._triggerNode, this._triggerOffset);
@@ -15445,13 +15854,13 @@
15445
15854
  return;
15446
15855
  }
15447
15856
  } catch (_) {}
15448
- const sel = window.getSelection();
15857
+ const sel = globalThis.getSelection();
15449
15858
  if (!sel || !sel.rangeCount) return;
15450
15859
  const rects = sel.getRangeAt(0).getClientRects();
15451
15860
  if (rects.length > 0) this._caretRect = rects[rects.length - 1];
15452
15861
  }
15453
15862
  _getQueryAtCursor() {
15454
- const sel = window.getSelection();
15863
+ const sel = globalThis.getSelection();
15455
15864
  if (!sel || !sel.rangeCount) return null;
15456
15865
  const range = sel.getRangeAt(0);
15457
15866
  if (!range.collapsed) return null;
@@ -15508,7 +15917,7 @@
15508
15917
  }
15509
15918
  _onDocClick(e) {
15510
15919
  if (!this._open) return;
15511
- if (this._dropdown && this._dropdown.contains(e.target)) return;
15920
+ if (this._dropdown?.contains(e.target)) return;
15512
15921
  this._hideDropdown();
15513
15922
  }
15514
15923
  _select(index) {
@@ -15517,7 +15926,7 @@
15517
15926
  if (this._triggerNode) {
15518
15927
  const node = this._triggerNode;
15519
15928
  node.textContent = node.textContent.slice(0, this._triggerOffset) + node.textContent.slice(this._triggerOffset + this._cfg.trigger.length + this._query.length);
15520
- const sel = window.getSelection();
15929
+ const sel = globalThis.getSelection();
15521
15930
  const range = document.createRange();
15522
15931
  range.setStart(node, this._triggerOffset);
15523
15932
  range.collapse(true);
@@ -15625,7 +16034,7 @@
15625
16034
  /**
15626
16035
  * Registers and initialises a custom module on this instance.
15627
16036
  * @param {string} name
15628
- * @param {Function} ModuleClass
16037
+ * @param {new (ctx: this) => any} ModuleClass
15629
16038
  * @returns {this}
15630
16039
  */
15631
16040
  registerModule(name, ModuleClass) {
@@ -15878,23 +16287,27 @@
15878
16287
  a.style.display = "none";
15879
16288
  document.body.appendChild(a);
15880
16289
  a.click();
15881
- document.body.removeChild(a);
16290
+ a.remove();
15882
16291
  URL.revokeObjectURL(url);
15883
16292
  }
15884
16293
  /**
15885
- * Opens the editor content in a new window and triggers the browser print dialog.
16294
+ * Opens the editor content in a new globalThis and triggers the browser print dialog.
15886
16295
  * @param {string} [title='']
15887
16296
  */
15888
16297
  print(title = "") {
15889
16298
  const content = this.getHTML();
15890
- const safeTitle = (title || "").replace(/</g, "&lt;").replace(/>/g, "&gt;");
15891
- const w = window.open("", "_blank");
15892
- if (!w) return;
15893
- w.document.write(`<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>${safeTitle}</title><style>body{font-family:system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:14px;line-height:1.6;padding:20mm;color:#111827;}ul.an-checklist{list-style:none;padding-left:0;}ul.an-checklist li{padding-left:24px;position:relative;margin:2px 0;}ul.an-checklist li input[type="checkbox"]{position:absolute;left:0;top:3px;}code{background:#f3f4f6;border-radius:3px;padding:.1em .35em;font-family:monospace;}pre{background:#f3f4f6;padding:.75em 1em;border-radius:4px;overflow-x:auto;}table{border-collapse:collapse;}td,th{border:1px solid #d1d5db;padding:4px 8px;}</style></head><body>${content}</body></html>`);
15894
- w.document.close();
15895
- w.onload = () => {
16299
+ const markup = `<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>${(title || "").replace(/[<>&"']/g, (c) => `&#${c.charCodeAt(0)};`)}</title><style>body{font-family:system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:14px;line-height:1.6;padding:20mm;color:#111827;}ul.an-checklist{list-style:none;padding-left:0;}ul.an-checklist li{padding-left:24px;position:relative;margin:2px 0;}ul.an-checklist li input[type="checkbox"]{position:absolute;left:0;top:3px;}code{background:#f3f4f6;border-radius:3px;padding:.1em .35em;font-family:monospace;}pre{background:#f3f4f6;padding:.75em 1em;border-radius:4px;overflow-x:auto;}table{border-collapse:collapse;}td,th{border:1px solid #d1d5db;padding:4px 8px;}</style></head><body>${content}</body></html>`;
16300
+ const blob = new Blob([markup], { type: "text/html" });
16301
+ const url = URL.createObjectURL(blob);
16302
+ const w = globalThis.open(url, "_blank");
16303
+ if (!w) {
16304
+ URL.revokeObjectURL(url);
16305
+ return;
16306
+ }
16307
+ w.addEventListener("load", () => {
15896
16308
  w.print();
15897
- };
16309
+ URL.revokeObjectURL(url);
16310
+ });
15898
16311
  }
15899
16312
  /**
15900
16313
  * Sets whether the editor is disabled (readonly).
@@ -15932,10 +16345,12 @@
15932
16345
  this._disposers.forEach((d) => d());
15933
16346
  this._disposers = [];
15934
16347
  const container = this.layoutInfo.container;
15935
- if (container && container.parentNode) {
16348
+ const wasDark = container?.classList.contains("an-theme-dark");
16349
+ if (container?.parentNode) {
15936
16350
  this.targetEl.style.display = "";
15937
- container.parentNode.removeChild(container);
16351
+ container.remove();
15938
16352
  }
16353
+ if (wasDark && !document.querySelector(".an-container.an-theme-dark")) document.body.classList.remove("an-theme-dark");
15939
16354
  if (typeof this.options.onDestroy === "function") this.options.onDestroy(this);
15940
16355
  this._alive = false;
15941
16356
  this._listeners.clear();
@@ -15944,7 +16359,8 @@
15944
16359
  * Syncs editor HTML back into the original textarea/input for form submission.
15945
16360
  */
15946
16361
  _syncToTarget() {
15947
- if (this.targetEl.tagName === "TEXTAREA" || this.targetEl.tagName === "INPUT") this.targetEl.value = this.getHTML();
16362
+ if (this.targetEl.tagName === "TEXTAREA" || this.targetEl.tagName === "INPUT")
16363
+ /** @type {HTMLInputElement} */ this.targetEl.value = this.getHTML();
15948
16364
  }
15949
16365
  };
15950
16366
  //#endregion
@@ -15954,7 +16370,7 @@
15954
16370
  * Inspired by Summernote's env.js
15955
16371
  */
15956
16372
  var userAgent = navigator.userAgent;
15957
- /Chrome\//.test(userAgent), /Firefox\//.test(userAgent), /^((?!chrome|android).)*safari/i.test(userAgent), /Edg\//.test(userAgent), /Macintosh/.test(userAgent), /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent), "ontouchstart" in window || navigator.maxTouchPoints, /Macintosh/.test(userAgent);
16373
+ /Chrome\//.test(userAgent), /Firefox\//.test(userAgent), /^((?!chrome|android).)*safari/i.test(userAgent), /Edg\//.test(userAgent), /Macintosh/.test(userAgent), /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent), "ontouchstart" in globalThis || navigator.maxTouchPoints, /Macintosh/.test(userAgent);
15958
16374
  //#endregion
15959
16375
  //#region src/js/index.js
15960
16376
  var _originalDefaults = { ...defaultOptions };