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
@@ -29,7 +29,7 @@ function debounce(fn, delay) {
29
29
  /**
30
30
  * Create a wrapper that limits how often `fn` can be invoked while ensuring the last call in a burst is executed.
31
31
  * @param {Function} fn - Function to be throttled.
32
- * @param {number} limit - Time window in milliseconds during which at most one call is allowed.
32
+ * @param {number} limit - Time globalThis in milliseconds during which at most one call is allowed.
33
33
  * @returns {Function} A wrapper function that invokes `fn` at most once per `limit` milliseconds; calls preserve `this` and original arguments and schedule a trailing invocation for the final call in a burst.
34
34
  */
35
35
  function throttle(fn, limit) {
@@ -252,7 +252,8 @@ function createElement(tag, attrs = {}, childNodes = []) {
252
252
  * @param {Node} node
253
253
  */
254
254
  function remove(node) {
255
- if (node && node.parentNode) node.parentNode.removeChild(node);
255
+ if (node && node.parentNode)
256
+ /** @type {ChildNode} */ node.remove();
256
257
  }
257
258
  /**
258
259
  * Unwraps a node — replaces the node with its children.
@@ -262,7 +263,7 @@ function unwrap(node) {
262
263
  const parent = node.parentNode;
263
264
  if (!parent) return;
264
265
  while (node.firstChild) parent.insertBefore(node.firstChild, node);
265
- parent.removeChild(node);
266
+ /** @type {ChildNode} */ node.remove();
266
267
  }
267
268
  /**
268
269
  * Wraps a node with a wrapper element.
@@ -321,7 +322,7 @@ function placeCaret(el) {
321
322
  const range = document.createRange();
322
323
  range.selectNodeContents(el);
323
324
  range.collapse(false);
324
- const sel = window.getSelection();
325
+ const sel = globalThis.getSelection();
325
326
  if (sel) {
326
327
  sel.removeAllRanges();
327
328
  sel.addRange(range);
@@ -364,27 +365,71 @@ function trapFocus(container, onEscape) {
364
365
  const handler = (e) => {
365
366
  if (e.key === "Escape") {
366
367
  e.stopPropagation();
367
- onEscape && onEscape();
368
+ onEscape?.();
368
369
  return;
369
370
  }
370
371
  if (e.key !== "Tab") return;
371
372
  const els = getFocusable();
372
373
  if (!els.length) return;
373
374
  const first = els[0];
374
- const last = els[els.length - 1];
375
+ const last = els.at(-1);
375
376
  if (e.shiftKey) {
376
377
  if (document.activeElement === first) {
377
378
  e.preventDefault();
378
- last.focus();
379
+ /** @type {HTMLElement} */ last.focus();
379
380
  }
380
381
  } else if (document.activeElement === last) {
381
382
  e.preventDefault();
382
- first.focus();
383
+ /** @type {HTMLElement} */ first.focus();
383
384
  }
384
385
  };
385
386
  document.addEventListener("keydown", handler);
386
387
  return () => document.removeEventListener("keydown", handler);
387
388
  }
389
+ /**
390
+ * Makes a dialog box draggable by its handle element.
391
+ * On first drag the box is pinned to its current viewport coordinates via
392
+ * `position:fixed`, freeing it from the parent flex container's centering.
393
+ * The position is clamped to the visible viewport.
394
+ *
395
+ * @param {HTMLElement} handle Element the user grabs (title bar / header)
396
+ * @param {HTMLElement} box Element that actually moves
397
+ * @returns {Function} Cleanup function (removes the mousedown listener)
398
+ */
399
+ function makeDraggable(handle, box) {
400
+ handle.style.cursor = "grab";
401
+ const onMousedown = (e) => {
402
+ if (e.button !== 0) return;
403
+ if (e.target.closest("button, input, select, textarea, a")) return;
404
+ e.preventDefault();
405
+ if (!box.dataset.anDragPinned) {
406
+ const r = box.getBoundingClientRect();
407
+ box.style.position = "fixed";
408
+ box.style.margin = "0";
409
+ box.style.left = `${r.left}px`;
410
+ box.style.top = `${r.top}px`;
411
+ box.dataset.anDragPinned = "1";
412
+ }
413
+ const startX = e.clientX - Number.parseFloat(box.style.left);
414
+ const startY = e.clientY - Number.parseFloat(box.style.top);
415
+ handle.style.cursor = "grabbing";
416
+ const onMove = (ev) => {
417
+ const bw = box.offsetWidth;
418
+ const bh = box.offsetHeight;
419
+ box.style.left = `${Math.max(0, Math.min(ev.clientX - startX, globalThis.innerWidth - bw))}px`;
420
+ box.style.top = `${Math.max(0, Math.min(ev.clientY - startY, globalThis.innerHeight - bh))}px`;
421
+ };
422
+ const onUp = () => {
423
+ handle.style.cursor = "grab";
424
+ document.removeEventListener("mousemove", onMove);
425
+ document.removeEventListener("mouseup", onUp);
426
+ };
427
+ document.addEventListener("mousemove", onMove);
428
+ document.addEventListener("mouseup", onUp);
429
+ };
430
+ handle.addEventListener("mousedown", onMousedown);
431
+ return () => handle.removeEventListener("mousedown", onMousedown);
432
+ }
388
433
  //#endregion
389
434
  //#region src/js/core/range.js
390
435
  /**
@@ -418,10 +463,10 @@ var WrappedRange = class {
418
463
  return range;
419
464
  }
420
465
  /**
421
- * Select this wrapped range in the window.
466
+ * Select this wrapped range in the globalThis.
422
467
  */
423
468
  select() {
424
- const sel = window.getSelection();
469
+ const sel = globalThis.getSelection();
425
470
  if (!sel) return;
426
471
  sel.removeAllRanges();
427
472
  sel.addRange(this.toNativeRange());
@@ -474,13 +519,13 @@ function fromNativeRange(range) {
474
519
  return new WrappedRange(range.startContainer, range.startOffset, range.endContainer, range.endOffset);
475
520
  }
476
521
  /**
477
- * Returns a WrappedRange for the current window selection,
522
+ * Returns a WrappedRange for the current globalThis selection,
478
523
  * optionally restricted to a given editable element.
479
524
  * @param {HTMLElement} [editable]
480
525
  * @returns {WrappedRange|null}
481
526
  */
482
527
  function currentRange(editable) {
483
- const sel = window.getSelection();
528
+ const sel = globalThis.getSelection();
484
529
  if (!sel || sel.rangeCount === 0) return null;
485
530
  const native = sel.getRangeAt(0);
486
531
  if (editable && !editable.contains(native.commonAncestorContainer)) return null;
@@ -509,7 +554,7 @@ function collapsedRange(node, offset = 0) {
509
554
  * @returns {boolean}
510
555
  */
511
556
  function isSelectionInside(el) {
512
- const sel = window.getSelection();
557
+ const sel = globalThis.getSelection();
513
558
  if (!sel || sel.rangeCount === 0) return false;
514
559
  return el.contains(sel.getRangeAt(0).commonAncestorContainer);
515
560
  }
@@ -518,7 +563,7 @@ function isSelectionInside(el) {
518
563
  * @param {Function} fn
519
564
  */
520
565
  function withSavedRange(fn) {
521
- const sel = window.getSelection();
566
+ const sel = globalThis.getSelection();
522
567
  if (!sel || sel.rangeCount === 0) {
523
568
  fn(null);
524
569
  return;
@@ -562,16 +607,16 @@ var italic = () => execCommand("italic");
562
607
  * execCommand's state detection is unreliable.
563
608
  */
564
609
  function underline() {
565
- const sel = window.getSelection();
610
+ const sel = globalThis.getSelection();
566
611
  if (!sel || !sel.rangeCount) return;
567
612
  let container = sel.getRangeAt(0).commonAncestorContainer;
568
613
  if (container.nodeType === 3) container = container.parentElement;
569
- const uEl = container && container.closest && container.closest("u");
614
+ const uEl = container?.closest("u");
570
615
  const nativeState = document.queryCommandState("underline");
571
616
  if (uEl && !nativeState) {
572
617
  const parent = uEl.parentNode;
573
618
  while (uEl.firstChild) parent.insertBefore(uEl.firstChild, uEl);
574
- parent.removeChild(uEl);
619
+ uEl.remove();
575
620
  return;
576
621
  }
577
622
  execCommand("underline");
@@ -582,16 +627,16 @@ function underline() {
582
627
  * execCommand's state detection is unreliable (mirrors underline() logic).
583
628
  */
584
629
  function strikethrough() {
585
- const sel = window.getSelection();
630
+ const sel = globalThis.getSelection();
586
631
  if (!sel || !sel.rangeCount) return;
587
632
  let sc = sel.getRangeAt(0).startContainer;
588
633
  if (sc.nodeType === 3) sc = sc.parentElement;
589
- const sEl = sc && sc.closest && (sc.closest("s") || sc.closest("strike"));
634
+ const sEl = sc?.closest("s") || sc?.closest("strike");
590
635
  const nativeState = document.queryCommandState("strikeThrough");
591
636
  if (sEl && !nativeState) {
592
637
  const parent = sEl.parentNode;
593
638
  while (sEl.firstChild) parent.insertBefore(sEl.firstChild, sEl);
594
- parent.removeChild(sEl);
639
+ sEl.remove();
595
640
  return;
596
641
  }
597
642
  execCommand("strikeThrough");
@@ -623,10 +668,10 @@ var fontName = (name) => execCommand("fontName", name);
623
668
  * Sets the font size (in pt or with unit) for the selection.
624
669
  * Uses a span-based approach to set px sizes precisely.
625
670
  * @param {string} size - e.g. '14px'
626
- * @param {HTMLElement} [editable] - scoping element to avoid touching nodes outside this editor
671
+ * @param {HTMLElement|Document} [editable] - scoping element to avoid touching nodes outside this editor
627
672
  */
628
673
  function fontSize(size, editable = document) {
629
- const sel = window.getSelection();
674
+ const sel = globalThis.getSelection();
630
675
  const wasCollapsed = !sel || !sel.rangeCount || sel.getRangeAt(0).collapsed;
631
676
  if (wasCollapsed && sel && sel.rangeCount > 0) {
632
677
  try {
@@ -652,12 +697,12 @@ function fontSize(size, editable = document) {
652
697
  span.style.fontSize = size;
653
698
  el.parentNode.insertBefore(span, el);
654
699
  while (el.firstChild) span.appendChild(el.firstChild);
655
- el.parentNode.removeChild(el);
700
+ el.remove();
656
701
  newSpans.push(span);
657
702
  });
658
703
  if (!wasCollapsed && sel && newSpans.length > 0) {
659
704
  const first = newSpans[0];
660
- const last = newSpans[newSpans.length - 1];
705
+ const last = newSpans.at(-1);
661
706
  try {
662
707
  const nr = document.createRange();
663
708
  const startNode = first.firstChild || first;
@@ -701,11 +746,11 @@ var indent = () => execCommand("indent");
701
746
  * (which would destroy the ul > li checklist structure).
702
747
  */
703
748
  function outdent() {
704
- const sel = window.getSelection();
749
+ const sel = globalThis.getSelection();
705
750
  if (sel && sel.rangeCount) {
706
751
  let container = sel.getRangeAt(0).commonAncestorContainer;
707
752
  if (container.nodeType === 3) container = container.parentElement;
708
- const checkLi = container && container.closest && container.closest(".an-checklist li");
753
+ const checkLi = container?.closest(".an-checklist li");
709
754
  if (checkLi) {
710
755
  _checklistItemToP(checkLi);
711
756
  return;
@@ -735,7 +780,7 @@ function _checklistItemToP(checkLi) {
735
780
  if (child.nodeType === 1 && child.tagName === "INPUT") continue;
736
781
  p.appendChild(child.cloneNode(true));
737
782
  }
738
- p.innerHTML = p.innerHTML.replace(/\u200B/g, "");
783
+ p.innerHTML = p.innerHTML.replaceAll("​", "");
739
784
  if (!p.hasChildNodes() || !p.textContent.trim()) {
740
785
  p.innerHTML = "";
741
786
  p.appendChild(document.createTextNode("\xA0"));
@@ -747,14 +792,14 @@ function _checklistItemToP(checkLi) {
747
792
  checkUl.parentNode.insertBefore(newUl, checkUl.nextSibling);
748
793
  }
749
794
  checkUl.parentNode.insertBefore(p, checkUl.nextSibling);
750
- checkUl.removeChild(checkLi);
751
- if (checkUl.children.length === 0) checkUl.parentNode.removeChild(checkUl);
795
+ checkLi.remove();
796
+ if (checkUl.children.length === 0) checkUl.remove();
752
797
  try {
753
798
  const nr = document.createRange();
754
799
  const firstChild = p.firstChild;
755
800
  nr.setStart(firstChild && firstChild.nodeType === 3 ? firstChild : p, 0);
756
801
  nr.collapse(true);
757
- const s = window.getSelection();
802
+ const s = globalThis.getSelection();
758
803
  if (s) {
759
804
  s.removeAllRanges();
760
805
  s.addRange(nr);
@@ -778,7 +823,7 @@ var insertOrderedList = () => execCommand("insertOrderedList");
778
823
  * @param {string} value - Line-height value to apply; typically a unitless multiplier (for example, "1.5").
779
824
  */
780
825
  function lineHeight(value) {
781
- const sel = window.getSelection();
826
+ const sel = globalThis.getSelection();
782
827
  if (!sel || sel.rangeCount === 0) return;
783
828
  const range = sel.getRangeAt(0);
784
829
  const BLOCK_TAGS = new Set([
@@ -827,25 +872,25 @@ function lineHeight(value) {
827
872
  /**
828
873
  * Wraps the selection in an inline <code> element, or unwraps it if the
829
874
  * cursor is already inside a <code> that is not inside a <pre>.
830
- * @param {HTMLElement} [editable]
875
+ * @param {HTMLElement} [_editable]
831
876
  */
832
- function toggleInlineCode(editable) {
833
- const sel = window.getSelection();
877
+ function toggleInlineCode(_editable) {
878
+ const sel = globalThis.getSelection();
834
879
  if (!sel || !sel.rangeCount) return;
835
880
  const range = sel.getRangeAt(0);
836
881
  let container = range.commonAncestorContainer;
837
882
  if (container.nodeType === 3) container = container.parentElement;
838
- const codeEl = container && container.closest ? container.closest("code") : null;
883
+ const codeEl = container?.closest("code");
839
884
  if (codeEl && !codeEl.closest("pre")) {
840
885
  const parent = codeEl.parentNode;
841
886
  const prevSibling = codeEl.previousSibling;
842
887
  const movedChildren = Array.from(codeEl.childNodes);
843
888
  while (codeEl.firstChild) parent.insertBefore(codeEl.firstChild, codeEl);
844
- parent.removeChild(codeEl);
845
- if (parent && parent.normalize) parent.normalize();
889
+ codeEl.remove();
890
+ parent?.normalize();
846
891
  if (movedChildren.length > 0) try {
847
892
  const firstMoved = movedChildren[0];
848
- const lastMoved = movedChildren[movedChildren.length - 1];
893
+ const lastMoved = movedChildren.at(-1);
849
894
  const nr = document.createRange();
850
895
  const anchorNode = firstMoved.parentNode === parent ? firstMoved : prevSibling ? prevSibling.nextSibling : parent.firstChild;
851
896
  if (anchorNode) {
@@ -886,11 +931,11 @@ function toggleInlineCode(editable) {
886
931
  * @returns {boolean}
887
932
  */
888
933
  function isInlineCode() {
889
- const sel = window.getSelection();
934
+ const sel = globalThis.getSelection();
890
935
  if (!sel || !sel.rangeCount) return false;
891
936
  let sc = sel.getRangeAt(0).startContainer;
892
937
  if (sc.nodeType === 3) sc = sc.parentElement;
893
- const code = sc && sc.closest ? sc.closest("code") : null;
938
+ const code = sc?.closest("code");
894
939
  return !!(code && !code.closest("pre"));
895
940
  }
896
941
  /**
@@ -908,30 +953,30 @@ function isInlineCode() {
908
953
  * Empty or whitespace-only selections do not create a checklist.
909
954
  */
910
955
  function toggleChecklist() {
911
- const sel = window.getSelection();
956
+ const sel = globalThis.getSelection();
912
957
  if (!sel || !sel.rangeCount) return;
913
958
  const range = sel.getRangeAt(0);
914
959
  let container = range.commonAncestorContainer;
915
960
  if (container.nodeType === 3) container = container.parentElement;
916
- const ul = container.closest && container.closest(".an-checklist");
961
+ const ul = container?.closest(".an-checklist");
917
962
  if (ul) {
918
963
  const selectedLis = Array.from(ul.querySelectorAll("li")).filter((li) => sel.containsNode(li, true));
919
964
  if (selectedLis.length > 0) {
920
- let firstP = null;
965
+ /** @type {HTMLElement|null} */ let firstP = null;
921
966
  selectedLis.forEach((li) => {
922
967
  const p = document.createElement("p");
923
968
  for (const child of li.childNodes) {
924
969
  if (child.nodeType === 1 && child.tagName === "INPUT") continue;
925
970
  p.appendChild(child.cloneNode(true));
926
971
  }
927
- p.innerHTML = p.innerHTML.replace(/\u200b/g, "");
972
+ p.innerHTML = p.innerHTML.replaceAll("​", "");
928
973
  if (!p.hasChildNodes() || !p.textContent.trim()) {
929
974
  p.innerHTML = "";
930
975
  p.appendChild(document.createTextNode("\xA0"));
931
976
  }
932
977
  ul.parentNode.insertBefore(p, ul);
933
978
  if (!firstP) firstP = p;
934
- ul.removeChild(li);
979
+ li.remove();
935
980
  });
936
981
  if (ul.children.length === 0) ul.remove();
937
982
  if (firstP) {
@@ -959,7 +1004,7 @@ function toggleChecklist() {
959
1004
  ]);
960
1005
  let block = container;
961
1006
  while (block && block.parentNode && !BLOCK_TAGS.has(block.tagName)) block = block.parentNode;
962
- const itemText = block && BLOCK_TAGS.has(block.tagName) ? Array.from(block.childNodes).map((n) => n.textContent).join("").replace(/\u00a0/g, " ") : "";
1007
+ const itemText = block && BLOCK_TAGS.has(block.tagName) ? Array.from(block.childNodes).map((n) => n.textContent).join("").replaceAll("\xA0", " ") : "";
963
1008
  const ul = document.createElement("ul");
964
1009
  ul.className = "an-checklist";
965
1010
  const li = document.createElement("li");
@@ -1015,7 +1060,7 @@ function toggleChecklist() {
1015
1060
  if (blocks.length === 0) return;
1016
1061
  const newUl = document.createElement("ul");
1017
1062
  newUl.className = "an-checklist";
1018
- let lastTextNode = null;
1063
+ /** @type {Text|null} */ let lastTextNode = null;
1019
1064
  blocks.forEach((block) => {
1020
1065
  const li = document.createElement("li");
1021
1066
  const cb = document.createElement("input");
@@ -1030,7 +1075,7 @@ function toggleChecklist() {
1030
1075
  });
1031
1076
  const firstBlock = blocks[0];
1032
1077
  firstBlock.parentNode.insertBefore(newUl, firstBlock);
1033
- blocks.forEach((block) => block.parentNode && block.parentNode.removeChild(block));
1078
+ blocks.forEach((block) => block.remove());
1034
1079
  if (lastTextNode) {
1035
1080
  const nr = document.createRange();
1036
1081
  nr.setStart(lastTextNode, lastTextNode.textContent.length);
@@ -1044,11 +1089,11 @@ function toggleChecklist() {
1044
1089
  * @returns {boolean}
1045
1090
  */
1046
1091
  function isInChecklist() {
1047
- const sel = window.getSelection();
1092
+ const sel = globalThis.getSelection();
1048
1093
  if (!sel || !sel.rangeCount) return false;
1049
1094
  let container = sel.getRangeAt(0).commonAncestorContainer;
1050
1095
  if (container.nodeType === 3) container = container.parentElement;
1051
- return !!(container && container.closest && container.closest(".an-checklist li"));
1096
+ return !!container?.closest(".an-checklist li");
1052
1097
  }
1053
1098
  //#endregion
1054
1099
  //#region src/js/module/Buttons.js
@@ -1059,12 +1104,14 @@ function isInChecklist() {
1059
1104
  */
1060
1105
  /**
1061
1106
  * @typedef {object} DropdownDef
1062
- * @property {string} name - unique identifier
1063
- * @property {'select'} type - discriminator for Toolbar renderer
1107
+ * @property {string} name - unique identifier
1108
+ * @property {'select'} type - discriminator for Toolbar renderer
1064
1109
  * @property {string} tooltip
1065
- * @property {string[]} [items] - overridden at render time from options
1066
- * @property {Function} action - called with (context, value)
1067
- * @property {Function} [getValue] - called with (context) to get current value
1110
+ * @property {Array<string|{value:string,label:string,disabled?:boolean}>} [items] - overridden at render time from options
1111
+ * @property {Function} action - called with (context, value)
1112
+ * @property {Function} [getValue] - called with (context) to get current value
1113
+ * @property {string} [selectClass] - extra CSS class(es) for the <select>
1114
+ * @property {string} [placeholder] - placeholder text for the empty option
1068
1115
  */
1069
1116
  /**
1070
1117
  * @typedef {object} ButtonDef
@@ -1128,11 +1175,11 @@ var boldBtn = btn("bold", "bold", "Bold (Ctrl+B)", () => bold(), () => document.
1128
1175
  var italicBtn = btn("italic", "italic", "Italic (Ctrl+I)", () => italic(), () => document.queryCommandState("italic"));
1129
1176
  var underlineBtn = btn("underline", "underline", "Underline (Ctrl+U)", () => underline(), () => {
1130
1177
  if (document.queryCommandState("underline")) return true;
1131
- const sel = window.getSelection();
1178
+ const sel = globalThis.getSelection();
1132
1179
  if (!sel || !sel.rangeCount) return false;
1133
1180
  let sc = sel.getRangeAt(0).startContainer;
1134
1181
  if (sc.nodeType === 3) sc = sc.parentElement;
1135
- return !!(sc && sc.closest && sc.closest("u"));
1182
+ return !!(sc && sc.closest("u"));
1136
1183
  });
1137
1184
  var strikeBtn = btn("strikethrough", "strikethrough", "Strikethrough", () => strikethrough(), () => document.queryCommandState("strikeThrough"));
1138
1185
  var superscriptBtn = btn("superscript", "superscript", "Superscript", () => superscript(), () => document.queryCommandState("superscript"));
@@ -1191,15 +1238,15 @@ var fontSizeBtn = {
1191
1238
  action: (ctx, value) => fontSize(value, ctx.layoutInfo.editable),
1192
1239
  getValue: (ctx) => {
1193
1240
  try {
1194
- const sel = window.getSelection();
1241
+ const sel = globalThis.getSelection();
1195
1242
  if (sel && sel.rangeCount) {
1196
1243
  let el = sel.getRangeAt(0).startContainer;
1197
- if (el.nodeType === 3) el = el.parentElement;
1244
+ if (el && el.nodeType === 3) el = el.parentElement;
1198
1245
  while (el && el.nodeType === 1 && !el.style.fontSize) el = el.parentElement;
1199
- const size = el && el.style && el.style.fontSize ? el.style.fontSize : "";
1246
+ const size = el && el.style.fontSize ? el.style.fontSize : "";
1200
1247
  if (size) return size;
1201
1248
  }
1202
- const editable = ctx && ctx.layoutInfo && ctx.layoutInfo.editable;
1249
+ const editable = ctx?.layoutInfo?.editable;
1203
1250
  if (editable) return editable.style.fontSize || "";
1204
1251
  return "";
1205
1252
  } catch {
@@ -1303,7 +1350,7 @@ var lineHeightBtn = {
1303
1350
  action: (_ctx, value) => lineHeight(value),
1304
1351
  getValue: () => {
1305
1352
  try {
1306
- const sel = window.getSelection();
1353
+ const sel = globalThis.getSelection();
1307
1354
  if (!sel || !sel.rangeCount) return "";
1308
1355
  const BLOCKS = new Set([
1309
1356
  "P",
@@ -1321,8 +1368,11 @@ var lineHeightBtn = {
1321
1368
  "TH"
1322
1369
  ]);
1323
1370
  let el = sel.getRangeAt(0).startContainer;
1324
- if (el.nodeType === 3) el = el.parentElement;
1325
- while (el && !BLOCKS.has(el.tagName)) el = el.parentElement;
1371
+ if (el && el.nodeType === 3) el = el.parentElement;
1372
+ while (el && !BLOCKS.has(
1373
+ /** @type {Element} */
1374
+ el.tagName
1375
+ )) el = el.parentElement;
1326
1376
  if (!el) return "";
1327
1377
  return el.style.lineHeight || getComputedStyle(el).lineHeight || "";
1328
1378
  } catch {
@@ -1416,47 +1466,64 @@ var defaultToolbar = [
1416
1466
  */
1417
1467
  /**
1418
1468
  * @typedef {object} AsnOptions
1419
- * @property {string} [placeholder] - Placeholder text when editor is empty
1420
- * @property {number} [height] - Editor height in px (min)
1421
- * @property {number} [minHeight] - Minimum height in px
1422
- * @property {number} [maxHeight] - Maximum height in px (0 = unlimited)
1423
- * @property {boolean} [focus] - Auto-focus on init
1424
- * @property {boolean} [resizable] - Show resize handle
1425
- * @property {Array} [toolbar] - Toolbar button group config
1426
- * @property {boolean} [pasteAsPlainText] - Force plain-text paste
1427
- * @property {boolean} [pasteCleanHTML] - Sanitise HTML on paste
1469
+ * @property {string} [placeholder] - Placeholder text when editor is empty
1470
+ * @property {number} [height] - Editor height in px (min)
1471
+ * @property {number} [minHeight] - Minimum height in px
1472
+ * @property {number} [maxHeight] - Maximum height in px (0 = unlimited)
1473
+ * @property {boolean} [focus] - Auto-focus on init
1474
+ * @property {boolean} [resizable] - Show resize handle
1475
+ * @property {Array} [toolbar] - Toolbar button group config
1476
+ * @property {boolean} [useBootstrap] - Use Bootstrap button classes on toolbar buttons
1477
+ * @property {string} [toolbarButtonClass] - CSS classes for Bootstrap toolbar buttons
1478
+ * @property {boolean} [useFontAwesome] - Use Font Awesome icons (default: true)
1479
+ * @property {string} [fontAwesomeClass] - Font Awesome prefix class, e.g. 'fas' or 'fa-solid'
1480
+ * @property {boolean} [pasteAsPlainText] - Force plain-text paste
1481
+ * @property {boolean} [pasteCleanHTML] - Sanitise HTML on paste
1428
1482
  * @property {boolean} [pasteStripAttributes] - Strip class/style/data-* from pasted HTML (default: false)
1429
- * @property {boolean} [allowImageUpload] - Allow file upload in image dialog
1430
- * @property {number} [maxImageSize] - Max upload size in MB
1431
- * @property {number} [tabSize] - Spaces per tab in non-list context
1432
- * @property {Function} [onChange] - Callback on content change
1433
- * @property {Function} [onFocus] - Callback on focus
1434
- * @property {Function} [onBlur] - Callback on blur
1435
- * @property {Function} [onImageUpload] - Custom upload handler: (files) => void
1436
- * @property {boolean} [stickyToolbar] - Stick the toolbar to the viewport top when scrolling
1437
- * @property {number} [stickyToolbarOffset] - Top offset in px for sticky toolbar (e.g. fixed nav height)
1438
- * @property {string} [theme] - 'light' (default) | 'dark'
1439
- * @property {boolean} [codeHighlight] - Auto-load Prism.js for syntax highlighting of code blocks
1440
- * @property {string} [codeHighlightCDN] - CDN base URL for Prism assets (defaults to cdnjs)
1441
- * @property {boolean} [markdownPaste] - Convert pasted Markdown text to HTML (default: true)
1442
- * @property {boolean} [readOnly] - Start editor in read-only / non-editable mode
1443
- * @property {boolean} [spellcheck] - Enable browser spellcheck in the editable area (default: true)
1444
- * @property {string} [direction] - Text direction: 'ltr' (default) | 'rtl'
1445
- * @property {string} [toolbarOverflow] - Toolbar overflow strategy: 'wrap' (default) | 'scroll'
1446
- * @property {boolean} [autoSave] - Auto-save content to localStorage on change
1447
- * @property {string} [autoSaveKey] - localStorage key used for auto-save (default: 'autumnnote-autosave')
1448
- * @property {number} [maxChars] - Maximum character count (0 = unlimited). Shows warning in statusbar.
1449
- * @property {number} [maxWords] - Maximum word count (0 = unlimited). Shows warning in statusbar.
1450
- * @property {boolean} [tableHeaderRow] - Insert a header row (<thead><th>) when creating tables
1451
- * @property {Function} [onPaste] - Callback fired on every paste: ({ text, html }) => void
1452
- * @property {Function} [onSelectionChange] - Callback fired on cursor/selection change: (context) => void
1453
- * @property {string[]} [colorSwatches] - Custom brand colour swatches prepended to the colour-picker palette
1483
+ * @property {boolean} [allowImageUpload] - Allow file upload in image dialog
1484
+ * @property {number} [maxImageSize] - Max upload size in MB
1485
+ * @property {number} [tabSize] - Spaces per tab in non-list context
1486
+ * @property {number} [historyLimit] - Maximum undo/redo history steps
1487
+ * @property {string} [defaultFontFamily] - Default font family applied to the editable area on init
1488
+ * @property {string} [defaultFontSize] - Default font size applied to the editable area on init (e.g. '14px')
1489
+ * @property {string[]} [fontFamilies] - Font families shown in the font-family toolbar dropdown
1490
+ * @property {Function} [onChange] - Callback on content change
1491
+ * @property {Function} [onFocus] - Callback on focus
1492
+ * @property {Function} [onBlur] - Callback on blur
1493
+ * @property {Function} [onInit] - Callback after the editor has initialised
1494
+ * @property {Function} [onImageUpload] - Custom upload handler: (files) => void
1495
+ * @property {Function} [onImageError] - Callback when an image upload error occurs
1496
+ * @property {boolean} [stickyToolbar] - Stick the toolbar to the viewport top when scrolling
1497
+ * @property {number} [stickyToolbarOffset] - Top offset in px for sticky toolbar (e.g. fixed nav height)
1498
+ * @property {string} [theme] - 'light' (default) | 'dark'
1499
+ * @property {boolean} [codeHighlight] - Auto-load Prism.js for syntax highlighting of code blocks
1500
+ * @property {string} [codeHighlightCDN] - CDN base URL for Prism assets (defaults to cdnjs)
1501
+ * @property {boolean} [markdownPaste] - Convert pasted Markdown text to HTML (default: true)
1502
+ * @property {boolean} [readOnly] - Start editor in read-only / non-editable mode
1503
+ * @property {boolean} [spellcheck] - Enable browser spellcheck in the editable area (default: true)
1504
+ * @property {string} [direction] - Text direction: 'ltr' (default) | 'rtl'
1505
+ * @property {string} [toolbarOverflow] - Toolbar overflow strategy: 'wrap' (default) | 'scroll'
1506
+ * @property {boolean} [autoSave] - Auto-save content to localStorage on change
1507
+ * @property {string} [autoSaveKey] - localStorage key used for auto-save (default: 'autumnnote-autosave')
1508
+ * @property {number} [maxChars] - Maximum character count (0 = unlimited). Shows warning in statusbar.
1509
+ * @property {number} [maxWords] - Maximum word count (0 = unlimited). Shows warning in statusbar.
1510
+ * @property {boolean} [tableHeaderRow] - Insert a header row (<thead><th>) when creating tables
1511
+ * @property {Function} [onPaste] - Callback fired on every paste: ({ text, html }) => void
1512
+ * @property {Function} [onSelectionChange] - Callback fired on cursor/selection change: (context) => void
1513
+ * @property {string[]} [colorSwatches] - Custom brand colour swatches prepended to the colour-picker palette
1454
1514
  * @property {Function} [onDestroy] - Callback fired when the editor is destroyed: (context) => void
1455
1515
  * @property {Function} [onCharLimitReached] - Callback fired when the character limit is hit: (context) => void
1456
1516
  * @property {Function} [onWordLimitReached] - Callback fired when the word limit is hit: (context) => void
1457
- * @property {string} [focusColor] - Custom focus ring colour, e.g. '#f97316'. Overrides the default blue.
1517
+ * @property {string} [focusColor] - Custom focus ring colour, e.g. '#f97316'. Overrides the default blue.
1518
+ * @property {boolean} [autoSaveRestore] - Show a restore banner when a previously auto-saved draft exists
1519
+ * @property {number} [autoSaveRestoreTimeout] - Maximum age in days for a draft to be offered for restore (0 = no expiry)
1520
+ * @property {Function} [onAutoSaveRestore] - Callback fired after the user chooses to restore a draft
1521
+ * @property {boolean} [markdownShortcuts] - Convert markdown syntax typed inline to HTML
1522
+ * @property {boolean} [bubbleToolbar] - Show a mini floating toolbar above text selections
1523
+ * @property {string[]} [bubbleToolbarItems] - Button names for the bubble toolbar
1524
+ * @property {object|null} [mention] - @mention configuration (onSearch, minChars, ...)
1525
+ * @property {string} [lang] - Display language or partial locale object override
1458
1526
  */
1459
- /** @type {AsnOptions} */
1460
1527
  var defaultOptions = {
1461
1528
  placeholder: "",
1462
1529
  height: 200,
@@ -1855,6 +1922,8 @@ var en = {
1855
1922
  rowHeight: "Row Height",
1856
1923
  tableBorderWidth: "Table Border Width",
1857
1924
  deleteTable: "Delete Table",
1925
+ cellBackground: "Cell Background",
1926
+ noShading: "No Shading",
1858
1927
  columnWidthPx: "Column Width (px)",
1859
1928
  rowHeightPx: "Row Height (px)",
1860
1929
  tableBorderWidthPx: "Table Border Width (px)",
@@ -2210,6 +2279,8 @@ var locales = {
2210
2279
  rowHeight: "Chiều cao hàng",
2211
2280
  tableBorderWidth: "Độ rộng viền bảng",
2212
2281
  deleteTable: "Xóa bảng",
2282
+ cellBackground: "Màu Nền Ô",
2283
+ noShading: "Xóa Màu Nền",
2213
2284
  columnWidthPx: "Chiều rộng cột (px)",
2214
2285
  rowHeightPx: "Chiều cao hàng (px)",
2215
2286
  tableBorderWidthPx: "Độ rộng viền bảng (px)",
@@ -4404,14 +4475,17 @@ function renderLayout(targetEl, options) {
4404
4475
  } catch (_) {}
4405
4476
  if (!initialContent) initialContent = targetEl.tagName === "TEXTAREA" ? (targetEl.value || "").trim() : (targetEl.innerHTML || "").trim();
4406
4477
  editable.innerHTML = sanitiseHTML(initialContent, { allowIframes: true });
4407
- const defaultFont = options.defaultFontFamily || options.fontFamilies && options.fontFamilies[0];
4478
+ const defaultFont = options.defaultFontFamily || options.fontFamilies?.[0];
4408
4479
  if (defaultFont) editable.style.fontFamily = defaultFont;
4409
4480
  if (options.defaultFontSize) editable.style.fontSize = options.defaultFontSize;
4410
4481
  if (options.height) editable.style.minHeight = `${options.height}px`;
4411
4482
  else if (options.minHeight) editable.style.minHeight = `${options.minHeight}px`;
4412
4483
  if (options.maxHeight) editable.style.maxHeight = `${options.maxHeight}px`;
4413
4484
  container.appendChild(editable);
4414
- if (options.theme === "dark") container.classList.add("an-theme-dark");
4485
+ if (options.theme === "dark") {
4486
+ container.classList.add("an-theme-dark");
4487
+ document.body.classList.add("an-theme-dark");
4488
+ }
4415
4489
  if (options.readOnly) {
4416
4490
  container.classList.add("an-disabled");
4417
4491
  editable.querySelectorAll("ul.an-checklist input[type=\"checkbox\"]").forEach((cb) => {
@@ -4449,7 +4523,7 @@ var History = class {
4449
4523
  constructor(editable, limit = 100) {
4450
4524
  this.editable = editable;
4451
4525
  this._limit = limit;
4452
- /** @type {Array<{html: string, range: {sc: string, so: number, ec: string, eo: number}|null}>} */
4526
+ /** @type {Array<{html: string, images?: Record<string,string>, sel: {start: number, end: number}|null}>} */
4453
4527
  this.stack = [];
4454
4528
  this.stackOffset = -1;
4455
4529
  this._savePoint();
@@ -4463,7 +4537,7 @@ var History = class {
4463
4537
  * @returns {{ start: number, end: number }|null}
4464
4538
  */
4465
4539
  _serializeSelection() {
4466
- const sel = window.getSelection();
4540
+ const sel = globalThis.getSelection();
4467
4541
  if (!sel || sel.rangeCount === 0) return null;
4468
4542
  const range = sel.getRangeAt(0);
4469
4543
  if (!this.editable.contains(range.startContainer)) return null;
@@ -4529,7 +4603,7 @@ var History = class {
4529
4603
  const range = document.createRange();
4530
4604
  range.setStart(startNode, startOff);
4531
4605
  range.setEnd(endNode, endOff);
4532
- const sel = window.getSelection();
4606
+ const sel = globalThis.getSelection();
4533
4607
  sel.removeAllRanges();
4534
4608
  sel.addRange(range);
4535
4609
  } catch (_) {
@@ -4537,7 +4611,7 @@ var History = class {
4537
4611
  const fb = document.createRange();
4538
4612
  fb.setStart(this.editable, 0);
4539
4613
  fb.collapse(true);
4540
- const s = window.getSelection();
4614
+ const s = globalThis.getSelection();
4541
4615
  if (s) {
4542
4616
  s.removeAllRanges();
4543
4617
  s.addRange(fb);
@@ -4601,8 +4675,7 @@ var History = class {
4601
4675
  recordUndo() {
4602
4676
  const current = this._serialize();
4603
4677
  const { html: tokenized } = this._tokenizeImages(current);
4604
- const prev = this.stack[this.stackOffset];
4605
- if (prev && prev.html === tokenized) return;
4678
+ if (this.stack[this.stackOffset]?.html === tokenized) return;
4606
4679
  this._savePoint();
4607
4680
  }
4608
4681
  /**
@@ -4648,8 +4721,7 @@ var History = class {
4648
4721
  * Build an HTML table with the given number of columns and rows, optionally including a header row.
4649
4722
  * @param {number} cols - Number of columns in each row.
4650
4723
  * @param {number} rows - Total number of rows to create (including header when `headerRow` is true).
4651
- * @param {{ headerRow?: boolean }} [opts] - Options object.
4652
- * @param {boolean} [opts.headerRow=false] - When true and `rows > 0`, creates a header row (`<thead>`) plus body rows for the remainder.
4724
+ * @param {{ headerRow?: boolean }} [opts] - Options: `headerRow` creates a `<thead>` when true.
4653
4725
  * @returns {HTMLTableElement} The constructed `<table>` element with a `<tbody>` and optional `<thead>`; each cell contains a `<br>` placeholder.
4654
4726
  */
4655
4727
  function createTable(cols, rows, opts = {}) {
@@ -4683,12 +4755,11 @@ function createTable(cols, rows, opts = {}) {
4683
4755
  * @param {number} cols - Number of columns for the new table.
4684
4756
  * @param {number} rows - Number of rows for the new table.
4685
4757
  * @param {{ headerRow?: boolean }} [opts] - Options for table creation.
4686
- * @param {boolean} [opts.headerRow=false] - If true, include a header row as the first row.
4687
4758
  */
4688
4759
  function insertTable(cols, rows, opts = {}) {
4689
4760
  if (cols <= 0 || rows <= 0) return;
4690
4761
  const table = createTable(cols, rows, opts);
4691
- const sel = window.getSelection();
4762
+ const sel = globalThis.getSelection();
4692
4763
  if (!sel || sel.rangeCount === 0) return;
4693
4764
  const range = sel.getRangeAt(0);
4694
4765
  range.deleteContents();
@@ -4706,7 +4777,7 @@ function insertTable(cols, rows, opts = {}) {
4706
4777
  "PRE"
4707
4778
  ]);
4708
4779
  let anchor = range.startContainer;
4709
- if (anchor.nodeType === 3) anchor = anchor.parentElement;
4780
+ if (anchor?.nodeType === 3) anchor = anchor.parentElement;
4710
4781
  while (anchor && !BLOCK.has(anchor.tagName?.toUpperCase()) && anchor.parentElement) anchor = anchor.parentElement;
4711
4782
  if (anchor && BLOCK.has(anchor.tagName?.toUpperCase()) && anchor.parentNode) {
4712
4783
  anchor.after(table);
@@ -4796,8 +4867,8 @@ function isModifier(event, keyName) {
4796
4867
  * Inspired by Summernote's Typing module
4797
4868
  */
4798
4869
  var _FA_PATTERN = /\bfa-/;
4799
- var isFAIcon = (n) => !!(n && n.nodeName === "I" && _FA_PATTERN.test(n.className || ""));
4800
- var isZwsAnchor = (n) => !!(n && n.nodeType === Node.TEXT_NODE && (n.textContent === "​" || n.textContent === ""));
4870
+ var isFAIcon = (n) => !!(n?.nodeName === "I" && _FA_PATTERN.test(n.className || ""));
4871
+ var isZwsAnchor = (n) => !!(n?.nodeType === Node.TEXT_NODE && (n.textContent === "​" || n.textContent === ""));
4801
4872
  /**
4802
4873
  * Handles special keydown behaviour inside the editor.
4803
4874
  * @param {KeyboardEvent} event
@@ -4807,7 +4878,7 @@ var isZwsAnchor = (n) => !!(n && n.nodeType === Node.TEXT_NODE && (n.textContent
4807
4878
  */
4808
4879
  function handleKeydown(event, editable, options = {}) {
4809
4880
  const moveCaret = (setFn) => {
4810
- const sel = window.getSelection();
4881
+ const sel = globalThis.getSelection();
4811
4882
  if (!sel) return false;
4812
4883
  const nr = document.createRange();
4813
4884
  setFn(nr);
@@ -4817,14 +4888,14 @@ function handleKeydown(event, editable, options = {}) {
4817
4888
  return true;
4818
4889
  };
4819
4890
  if (isKey(event, key.BACKSPACE)) {
4820
- const sel = window.getSelection();
4821
- if (sel && sel.rangeCount > 0) {
4891
+ const sel = globalThis.getSelection();
4892
+ if (sel?.rangeCount > 0) {
4822
4893
  const r = sel.getRangeAt(0);
4823
4894
  if (r.collapsed && r.startContainer.nodeType === Node.TEXT_NODE) {
4824
4895
  const textNode = r.startContainer;
4825
4896
  if (r.startOffset === 0 && isFAIcon(textNode.previousSibling)) {
4826
4897
  event.preventDefault();
4827
- textNode.previousSibling.remove();
4898
+ /** @type {ChildNode} */ textNode.previousSibling.remove();
4828
4899
  return true;
4829
4900
  }
4830
4901
  if (r.startOffset === 1 && textNode.textContent === "​" && isFAIcon(textNode.previousSibling)) {
@@ -4848,7 +4919,7 @@ function handleKeydown(event, editable, options = {}) {
4848
4919
  return false;
4849
4920
  }
4850
4921
  if (isKey(event, key.LEFT) || isKey(event, key.RIGHT)) {
4851
- const sel = window.getSelection();
4922
+ const sel = globalThis.getSelection();
4852
4923
  if (!sel || sel.rangeCount === 0) return false;
4853
4924
  const r = sel.getRangeAt(0);
4854
4925
  if (!r.collapsed) return false;
@@ -4936,7 +5007,7 @@ function handleKeydown(event, editable, options = {}) {
4936
5007
  else execCommand("indent");
4937
5008
  return true;
4938
5009
  }
4939
- if (para && para.nodeName.toUpperCase() === "PRE") {
5010
+ if (para?.nodeName.toUpperCase() === "PRE") {
4940
5011
  if (event.shiftKey) return false;
4941
5012
  event.preventDefault();
4942
5013
  execCommand("insertText", " ".repeat(options.tabSize || 4));
@@ -4959,18 +5030,18 @@ function handleKeydown(event, editable, options = {}) {
4959
5030
  if (!range) return false;
4960
5031
  const sc = range.sc;
4961
5032
  const el = sc.nodeType === 3 ? sc.parentElement : sc;
4962
- if (el && el.nodeName === "I" && /\bfa-/.test(el.className || "")) {
5033
+ if (el?.nodeName === "I" && /\bfa-/.test(el.className || "")) {
4963
5034
  const nr = document.createRange();
4964
5035
  nr.setStartAfter(el);
4965
5036
  nr.collapse(true);
4966
- const selI = window.getSelection();
5037
+ const selI = globalThis.getSelection();
4967
5038
  if (selI) {
4968
5039
  selI.removeAllRanges();
4969
5040
  selI.addRange(nr);
4970
5041
  }
4971
5042
  return false;
4972
5043
  }
4973
- const videoWrapper = el && el.closest && el.closest(".an-video-wrapper");
5044
+ const videoWrapper = el?.closest(".an-video-wrapper");
4974
5045
  if (videoWrapper) {
4975
5046
  event.preventDefault();
4976
5047
  const p = document.createElement("p");
@@ -4979,16 +5050,16 @@ function handleKeydown(event, editable, options = {}) {
4979
5050
  const nr = document.createRange();
4980
5051
  nr.setStart(p, 0);
4981
5052
  nr.collapse(true);
4982
- const sel = window.getSelection();
5053
+ const sel = globalThis.getSelection();
4983
5054
  sel.removeAllRanges();
4984
5055
  sel.addRange(nr);
4985
5056
  return true;
4986
5057
  }
4987
- const checkLi = el && el.closest && el.closest(".an-checklist li");
5058
+ const checkLi = el?.closest(".an-checklist li");
4988
5059
  if (checkLi) {
4989
5060
  event.preventDefault();
4990
5061
  const ul = checkLi.closest(".an-checklist");
4991
- const sel = window.getSelection();
5062
+ const sel = globalThis.getSelection();
4992
5063
  let nativeRange = sel.getRangeAt(0);
4993
5064
  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();
4994
5065
  if (!liText(checkLi)) {
@@ -5032,12 +5103,12 @@ function handleKeydown(event, editable, options = {}) {
5032
5103
  return true;
5033
5104
  }
5034
5105
  const para = closestPara(range.sc, editable);
5035
- if (para && para.nodeName.toUpperCase() === "PRE") {
5106
+ if (para?.nodeName.toUpperCase() === "PRE") {
5036
5107
  event.preventDefault();
5037
5108
  execCommand("insertText", "\n");
5038
5109
  return true;
5039
5110
  }
5040
- if (para && para.nodeName.toUpperCase() === "BLOCKQUOTE") {
5111
+ if (para?.nodeName.toUpperCase() === "BLOCKQUOTE") {
5041
5112
  const native = range.toNativeRange();
5042
5113
  native.setEnd(para, para.childNodes.length);
5043
5114
  if (native.toString() === "" && range.isCollapsed()) {
@@ -5084,8 +5155,9 @@ function htmlToMarkdown(html) {
5084
5155
  function _domToMd(node, depth = 0) {
5085
5156
  if (node.nodeType === 3) return node.textContent.replace(/\s+/g, " ");
5086
5157
  if (node.nodeType !== 1) return "";
5087
- const tag = node.nodeName.toLowerCase();
5088
- const inner = () => Array.from(node.childNodes).map((n) => _domToMd(n, depth)).join("");
5158
+ const el = node;
5159
+ const tag = el.nodeName.toLowerCase();
5160
+ const inner = () => Array.from(el.childNodes).map((n) => _domToMd(n, depth)).join("");
5089
5161
  switch (tag) {
5090
5162
  case "p":
5091
5163
  case "div": return `\n\n${inner()}\n\n`;
@@ -5103,34 +5175,34 @@ function _domToMd(node, depth = 0) {
5103
5175
  case "del":
5104
5176
  case "s":
5105
5177
  case "strike": return `~~${inner()}~~`;
5106
- case "sup": return `^${inner()}`;
5107
- case "sub": return `~${inner()}`;
5178
+ case "sup": return `^${inner()}^`;
5179
+ case "sub": return `~${inner()}~`;
5108
5180
  case "code":
5109
- if (node.closest("pre")) return inner();
5181
+ if (el.closest("pre")) return inner();
5110
5182
  return `\`${inner()}\``;
5111
5183
  case "pre": {
5112
- const codeEl = node.querySelector("code");
5113
- const langMatch = (codeEl && codeEl.className || "").match(/language-(\S+)/);
5114
- return `\n\n\`\`\`${langMatch ? langMatch[1] : ""}\n${(codeEl || node).textContent || ""}\n\`\`\`\n\n`;
5184
+ const codeEl = el.querySelector("code");
5185
+ const langMatch = /language-(\S+)/.exec(codeEl && codeEl.className || "");
5186
+ return `\n\n\`\`\`${langMatch ? langMatch[1] : ""}\n${(codeEl || el).textContent || ""}\n\`\`\`\n\n`;
5115
5187
  }
5116
5188
  case "blockquote": return `\n\n${inner().trim().split("\n").map((l) => `> ${l}`).join("\n")}\n\n`;
5117
5189
  case "a": {
5118
- const href = node.getAttribute("href") || "";
5190
+ const href = el.getAttribute("href") || "";
5119
5191
  return `[${inner()}](${href})`;
5120
5192
  }
5121
5193
  case "img": {
5122
- const src = node.getAttribute("src") || "";
5123
- return `![${node.getAttribute("alt") || ""}](${src})`;
5194
+ const src = el.getAttribute("src") || "";
5195
+ return `![${el.getAttribute("alt") || ""}](${src})`;
5124
5196
  }
5125
5197
  case "ul": {
5126
- const items = Array.from(node.querySelectorAll(":scope > li"));
5198
+ const items = Array.from(el.querySelectorAll(":scope > li"));
5127
5199
  if (!items.length) return inner();
5128
5200
  const indent = " ".repeat(depth);
5129
5201
  const lines = items.map((li) => `${indent}- ${_domToMd(li, depth + 1).trim()}`).join("\n");
5130
5202
  return depth === 0 ? `\n\n${lines}\n\n` : `\n${lines}`;
5131
5203
  }
5132
5204
  case "ol": {
5133
- const items = Array.from(node.querySelectorAll(":scope > li"));
5205
+ const items = Array.from(el.querySelectorAll(":scope > li"));
5134
5206
  if (!items.length) return inner();
5135
5207
  const indent = " ".repeat(depth);
5136
5208
  const lines = items.map((li, i) => `${indent}${i + 1}. ${_domToMd(li, depth + 1).trim()}`).join("\n");
@@ -5139,9 +5211,9 @@ function _domToMd(node, depth = 0) {
5139
5211
  case "li": return inner();
5140
5212
  case "hr": return "\n\n---\n\n";
5141
5213
  case "table": {
5142
- const rows = Array.from(node.querySelectorAll("tr"));
5214
+ const rows = Array.from(el.querySelectorAll("tr"));
5143
5215
  if (!rows.length) return inner();
5144
- const cellTexts = rows.map((tr) => Array.from(tr.querySelectorAll("th, td")).map((c) => c.textContent.trim().replace(/\|/g, "\\|")));
5216
+ const cellTexts = rows.map((tr) => Array.from(tr.querySelectorAll("th, td")).map((c) => c.textContent.trim().replaceAll("|", "\\|")));
5145
5217
  const cols = Math.max(...cellTexts.map((r) => r.length));
5146
5218
  const padRow = (row) => {
5147
5219
  const r = [...row];
@@ -5150,7 +5222,7 @@ function _domToMd(node, depth = 0) {
5150
5222
  };
5151
5223
  let md = "\n\n";
5152
5224
  md += `| ${padRow(cellTexts[0]).join(" | ")} |\n`;
5153
- md += `| ${Array(cols).fill("---").join(" | ")} |\n`;
5225
+ md += `| ${new Array(cols).fill("---").join(" | ")} |\n`;
5154
5226
  for (let r = 1; r < cellTexts.length; r++) md += `| ${padRow(cellTexts[r]).join(" | ")} |\n`;
5155
5227
  return md + "\n";
5156
5228
  }
@@ -5174,12 +5246,12 @@ function isMarkdown(text) {
5174
5246
  * @returns {string}
5175
5247
  */
5176
5248
  function markdownToHTML(text) {
5177
- const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
5249
+ const lines = text.replaceAll("\r\n", "\n").replaceAll("\r", "\n").split("\n");
5178
5250
  const out = [];
5179
5251
  let i = 0;
5180
5252
  while (i < lines.length) {
5181
5253
  const line = lines[i];
5182
- const fenceMatch = line.match(/^```(\S*)$/);
5254
+ const fenceMatch = /^```(\S*)$/.exec(line);
5183
5255
  if (fenceMatch) {
5184
5256
  const lang = fenceMatch[1];
5185
5257
  const codeLines = [];
@@ -5198,7 +5270,7 @@ function markdownToHTML(text) {
5198
5270
  i++;
5199
5271
  continue;
5200
5272
  }
5201
- const hMatch = line.match(/^(#{1,6})\s+(.+)$/);
5273
+ const hMatch = /^(#{1,6})\s+(.+)$/.exec(line);
5202
5274
  if (hMatch) {
5203
5275
  const level = hMatch[1].length;
5204
5276
  out.push(`<h${level}>${_inline(hMatch[2])}</h${level}>`);
@@ -5245,7 +5317,8 @@ function markdownToHTML(text) {
5245
5317
  i++;
5246
5318
  }
5247
5319
  const thead = `<thead><tr>${headerCells.map((c) => `<th>${_inline(c)}</th>`).join("")}</tr></thead>`;
5248
- const tbody = bodyRows.length ? `<tbody>${bodyRows.map((row) => `<tr>${row.map((c) => `<td>${_inline(c)}</td>`).join("")}</tr>`).join("")}</tbody>` : "";
5320
+ const renderRow = (row) => `<tr>${row.map((c) => `<td>${_inline(c)}</td>`).join("")}</tr>`;
5321
+ const tbody = bodyRows.length ? `<tbody>${bodyRows.map(renderRow).join("")}</tbody>` : "";
5249
5322
  out.push(`<table>${thead}${tbody}</table>`);
5250
5323
  continue;
5251
5324
  }
@@ -5281,10 +5354,51 @@ function _inline(text) {
5281
5354
  return text;
5282
5355
  }
5283
5356
  function _esc(v) {
5284
- return String(v).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
5357
+ return String(v).replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
5285
5358
  }
5286
5359
  function _escAttr(v) {
5287
- return String(v).replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
5360
+ return String(v).replaceAll("&", "&amp;").replaceAll("\"", "&quot;").replaceAll("'", "&#39;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
5361
+ }
5362
+ //#endregion
5363
+ //#region src/js/core/detectLang.js
5364
+ /**
5365
+ * detectLang.js — Heuristic programming-language detection for code snippets.
5366
+ *
5367
+ * Returns a Prism.js language identifier or null when no language can be
5368
+ * determined with reasonable confidence.
5369
+ *
5370
+ * Detection order (conflicts in parentheses):
5371
+ * TypeScript → Rust → PHP → Java → Kotlin → Swift → Go
5372
+ * → JavaScript → HTML → CSS → JSON → SQL → Python → Ruby
5373
+ * → Bash → C++ → C# → C → XML
5374
+ *
5375
+ * @param {string} code
5376
+ * @returns {string|null}
5377
+ */
5378
+ function detectLang(code) {
5379
+ if (!code || !code.trim()) return null;
5380
+ const s = code.trim();
5381
+ 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";
5382
+ 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";
5383
+ if (/(<\?php\b|<\?=|\becho\s+.*\$\w|\$this->|\$\w+\s*=\s*\w|\bforeach\s*\(\s*\$|Illuminate\\)/.test(s)) return "php";
5384
+ 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";
5385
+ 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";
5386
+ 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";
5387
+ 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";
5388
+ 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";
5389
+ 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";
5390
+ 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";
5391
+ if (/(^|\n)\s*[\w#.*:[\]&, +-]+\s*\{[^}]*[\w-]+\s*:[^{}:;]+[;}\n]/m.test(s) && !/<\w|function\s|def\s|:\s*(string|number)/.test(s)) return "css";
5392
+ if (/^\s*[{[]/.test(s) && /"\w[\w\s-]*"\s*:/.test(s) && !/\bfunction\b|\bdef\b/.test(s)) return "json";
5393
+ 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";
5394
+ 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";
5395
+ 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";
5396
+ 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";
5397
+ 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";
5398
+ 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";
5399
+ 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";
5400
+ if (/^<\?xml\s/i.test(s) || /xmlns:|<\/[\w:]+>/.test(s)) return "xml";
5401
+ return null;
5288
5402
  }
5289
5403
  //#endregion
5290
5404
  //#region src/js/module/Editor.js
@@ -5325,7 +5439,7 @@ var Editor = class {
5325
5439
  const onBeforeInput = (event) => this._enforceLimit(event);
5326
5440
  const onSelChange = () => {
5327
5441
  if (!this.context._alive) return;
5328
- const sel = window.getSelection();
5442
+ const sel = globalThis.getSelection();
5329
5443
  if (sel && sel.rangeCount > 0 && editable.contains(sel.anchorNode)) {
5330
5444
  this.context.invoke("toolbar.refresh");
5331
5445
  if (typeof this.options.onSelectionChange === "function") this.options.onSelectionChange(this.context);
@@ -5335,13 +5449,14 @@ var Editor = class {
5335
5449
  if (e.target.type === "checkbox" && e.target.closest(".an-checklist")) this.afterCommand();
5336
5450
  };
5337
5451
  const fixChecklistCursor = (event) => {
5338
- const sel = window.getSelection();
5452
+ const sel = globalThis.getSelection();
5339
5453
  if (!sel || !sel.rangeCount) return;
5340
5454
  const r = sel.getRangeAt(0);
5341
5455
  if (!r.collapsed) return;
5342
5456
  const sc = r.startContainer;
5343
5457
  if (sc.nodeType !== Node.ELEMENT_NODE) return;
5344
- const li = sc.matches && sc.matches(".an-checklist li") ? sc : null;
5458
+ const scEl = sc;
5459
+ const li = scEl.matches(".an-checklist li") ? scEl : null;
5345
5460
  if (!li) return;
5346
5461
  const cb = li.querySelector("input[type=\"checkbox\"]");
5347
5462
  if (!cb) return;
@@ -5381,33 +5496,37 @@ var Editor = class {
5381
5496
  return;
5382
5497
  }
5383
5498
  const target = e.target;
5384
- if (target && (target.nodeName === "IFRAME" || target.closest && target.closest(".an-video-wrapper"))) e.preventDefault();
5499
+ if (target && (target.nodeName === "IFRAME" || target.closest(".an-video-wrapper"))) e.preventDefault();
5385
5500
  }), on(editable, "drop", (e) => {
5386
5501
  if (isReadOnly()) e.preventDefault();
5387
5502
  }));
5388
5503
  /** @type {string|null} 'superscript' | 'subscript' | null */
5389
5504
  let _compositionSupSub = null;
5390
5505
  const onCompositionStart = () => {
5391
- const sel = window.getSelection();
5506
+ const sel = globalThis.getSelection();
5392
5507
  if (!sel || !sel.rangeCount) {
5393
5508
  _compositionSupSub = null;
5394
5509
  return;
5395
5510
  }
5396
5511
  let node = sel.getRangeAt(0).startContainer;
5397
5512
  if (node.nodeType === Node.TEXT_NODE) node = node.parentElement;
5398
- if (node && node.closest) if (node.closest("sup")) _compositionSupSub = "superscript";
5399
- else if (node.closest("sub")) _compositionSupSub = "subscript";
5400
- else _compositionSupSub = null;
5513
+ if (node) {
5514
+ const el = node;
5515
+ if (el.closest("sup")) _compositionSupSub = "superscript";
5516
+ else if (el.closest("sub")) _compositionSupSub = "subscript";
5517
+ else _compositionSupSub = null;
5518
+ }
5401
5519
  };
5402
5520
  const onCompositionEnd = () => {
5403
5521
  const tag = _compositionSupSub;
5404
5522
  _compositionSupSub = null;
5405
5523
  if (!tag) return;
5406
- const sel = window.getSelection();
5524
+ const sel = globalThis.getSelection();
5407
5525
  if (!sel || !sel.rangeCount) return;
5408
5526
  let node = sel.getRangeAt(0).startContainer;
5409
5527
  if (node.nodeType === Node.TEXT_NODE) node = node.parentElement;
5410
- if (!(node && node.closest && (tag === "superscript" ? node.closest("sup") : node.closest("sub")))) document.execCommand(tag);
5528
+ const el = node;
5529
+ if (!(tag === "superscript" ? el?.closest("sup") : el?.closest("sub"))) document.execCommand(tag);
5411
5530
  };
5412
5531
  this._disposers.push(on(editable, "compositionstart", onCompositionStart), on(editable, "compositionend", onCompositionEnd));
5413
5532
  }
@@ -5481,7 +5600,7 @@ var Editor = class {
5481
5600
  if (type === "insertFromPaste" || type === "insertFromDrop") return;
5482
5601
  if (!type.startsWith("insert")) return;
5483
5602
  const text = this.context.layoutInfo.editable.innerText || "";
5484
- const chars = text.replace(/\n/g, "").length;
5603
+ const chars = text.replaceAll("\n", "").length;
5485
5604
  if (maxChars && chars >= maxChars) {
5486
5605
  event.preventDefault();
5487
5606
  if (typeof this.options.onCharLimitReached === "function") this.options.onCharLimitReached(this.context);
@@ -5496,6 +5615,7 @@ var Editor = class {
5496
5615
  }
5497
5616
  afterCommand() {
5498
5617
  this._cleanOrphanedFigures();
5618
+ this._ensureTrailingParagraph();
5499
5619
  this.context.invoke("toolbar.refresh");
5500
5620
  this.context.invoke("statusbar.update");
5501
5621
  this._scheduleSnapshot();
@@ -5519,9 +5639,34 @@ var Editor = class {
5519
5639
  */
5520
5640
  _cleanOrphanedFigures() {
5521
5641
  this.context.layoutInfo.editable.querySelectorAll("figure.an-figure").forEach((fig) => {
5522
- if (!fig.querySelector("img")) fig.parentNode.removeChild(fig);
5642
+ if (!fig.querySelector("img")) fig.remove();
5523
5643
  });
5524
5644
  }
5645
+ /**
5646
+ * Ensures the editable always ends with a plain paragraph so the cursor can
5647
+ * be placed after block elements that do not naturally allow it
5648
+ * (pre, blockquote, table, figure, ul, ol, hr).
5649
+ * Without this, clicking below the last such element does nothing.
5650
+ */
5651
+ _ensureTrailingParagraph() {
5652
+ const editable = this.context.layoutInfo.editable;
5653
+ if (!editable) return;
5654
+ const last = editable.lastElementChild;
5655
+ if (!last) return;
5656
+ if (new Set([
5657
+ "PRE",
5658
+ "BLOCKQUOTE",
5659
+ "TABLE",
5660
+ "FIGURE",
5661
+ "UL",
5662
+ "OL",
5663
+ "HR"
5664
+ ]).has(last.nodeName)) {
5665
+ const p = document.createElement("p");
5666
+ p.innerHTML = "<br>";
5667
+ editable.appendChild(p);
5668
+ }
5669
+ }
5525
5670
  focus() {
5526
5671
  this.context.layoutInfo.editable.focus();
5527
5672
  }
@@ -5530,7 +5675,7 @@ var Editor = class {
5530
5675
  * @returns {string}
5531
5676
  */
5532
5677
  getHTML() {
5533
- const raw = this.context.layoutInfo.editable.innerHTML.replace(/\u200B/g, "");
5678
+ const raw = this.context.layoutInfo.editable.innerHTML.replaceAll("​", "");
5534
5679
  return this.context.invoke("clipboard.resolveImages", raw) ?? raw;
5535
5680
  }
5536
5681
  /**
@@ -5575,7 +5720,7 @@ var Editor = class {
5575
5720
  * @returns {boolean}
5576
5721
  */
5577
5722
  isEmpty() {
5578
- const text = (this.context.layoutInfo.editable.innerText || "").trim().replace(/\u00a0/g, "");
5723
+ const text = (this.context.layoutInfo.editable.innerText || "").trim().replaceAll("\xA0", "");
5579
5724
  const hasMedia = !!this.context.layoutInfo.editable.querySelector("img, video, iframe, table");
5580
5725
  return !text && !hasMedia;
5581
5726
  }
@@ -5705,10 +5850,24 @@ var Editor = class {
5705
5850
  this.context.print();
5706
5851
  }
5707
5852
  /**
5708
- * @param {string} tagName - e.g. 'h1', 'p', 'blockquote'
5853
+ * @param {string} tagName - e.g. 'h1', 'p', 'blockquote', 'pre'
5709
5854
  */
5710
5855
  formatBlock(tagName) {
5711
5856
  formatBlock(tagName);
5857
+ if (tagName === "pre") {
5858
+ const sel = globalThis.getSelection();
5859
+ if (sel && sel.rangeCount > 0) {
5860
+ const container = sel.getRangeAt(0).commonAncestorContainer;
5861
+ const pre = container.nodeType === 1 ? container.closest("pre") : container.parentElement?.closest("pre");
5862
+ if (pre && !pre.dataset.language) {
5863
+ const lang = detectLang(pre.textContent || "");
5864
+ if (lang) {
5865
+ this.context.invoke("codeTooltip.applyLanguage", pre, lang);
5866
+ return;
5867
+ }
5868
+ }
5869
+ }
5870
+ }
5712
5871
  this.afterCommand();
5713
5872
  }
5714
5873
  /**
@@ -5753,7 +5912,7 @@ var Editor = class {
5753
5912
  * @param {boolean} [openInNewTab=false]
5754
5913
  */
5755
5914
  insertLink(url, text, openInNewTab = false) {
5756
- const sel = window.getSelection();
5915
+ const sel = globalThis.getSelection();
5757
5916
  if (!sel || sel.rangeCount === 0) return;
5758
5917
  const safeUrl = sanitiseUrl(url);
5759
5918
  if (!safeUrl) return;
@@ -5765,8 +5924,8 @@ var Editor = class {
5765
5924
  if (openInNewTab) {
5766
5925
  const link = this._getClosestAnchor();
5767
5926
  if (link) {
5768
- link.setAttribute("target", "_blank");
5769
- link.setAttribute("rel", "noopener noreferrer");
5927
+ /** @type {Element} */ link.setAttribute("target", "_blank");
5928
+ /** @type {Element} */ link.setAttribute("rel", "noopener noreferrer");
5770
5929
  }
5771
5930
  }
5772
5931
  }
@@ -5816,7 +5975,7 @@ var Editor = class {
5816
5975
  this.afterCommand();
5817
5976
  }
5818
5977
  _getClosestAnchor() {
5819
- const sel = window.getSelection();
5978
+ const sel = globalThis.getSelection();
5820
5979
  if (!sel || sel.rangeCount === 0) return null;
5821
5980
  let node = sel.getRangeAt(0).startContainer;
5822
5981
  while (node) {
@@ -5831,7 +5990,7 @@ var Editor = class {
5831
5990
  * @returns {string}
5832
5991
  */
5833
5992
  _escapeAttr(str) {
5834
- return String(str ?? "").replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
5993
+ return String(str ?? "").replaceAll("&", "&amp;").replaceAll("\"", "&quot;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
5835
5994
  }
5836
5995
  };
5837
5996
  //#endregion
@@ -5946,7 +6105,7 @@ var Toolbar = class {
5946
6105
  this._refreshRaf = null;
5947
6106
  this._disposers.forEach((d) => d());
5948
6107
  this._disposers = [];
5949
- if (this.el && this.el.parentNode) this.el.parentNode.removeChild(this.el);
6108
+ if (this.el && this.el.parentNode) this.el.remove();
5950
6109
  this.el = null;
5951
6110
  }
5952
6111
  _buildButtons() {
@@ -6014,8 +6173,8 @@ var Toolbar = class {
6014
6173
  let isOpen = false;
6015
6174
  const setHighlight = (rows, cols) => {
6016
6175
  cells.forEach((cell) => {
6017
- const r = +cell.getAttribute("data-row");
6018
- const c = +cell.getAttribute("data-col");
6176
+ const r = +cell.dataset.row;
6177
+ const c = +cell.dataset.col;
6019
6178
  cell.classList.toggle("active", r <= rows && c <= cols);
6020
6179
  });
6021
6180
  label.textContent = rows && cols ? `${rows} × ${cols}` : this.context.locale.toolbar.insertTableLabel || "Insert Table";
@@ -6029,8 +6188,8 @@ var Toolbar = class {
6029
6188
  const ph = popup.offsetHeight;
6030
6189
  let left = rect.left;
6031
6190
  let top = rect.bottom + 4;
6032
- if (left + pw > window.innerWidth - 8) left = Math.max(8, window.innerWidth - pw - 8);
6033
- if (top + ph > window.innerHeight - 8) top = rect.top - ph - 4;
6191
+ if (left + pw > globalThis.innerWidth - 8) left = Math.max(8, globalThis.innerWidth - pw - 8);
6192
+ if (top + ph > globalThis.innerHeight - 8) top = rect.top - ph - 4;
6034
6193
  popup.style.left = `${left}px`;
6035
6194
  popup.style.top = `${top}px`;
6036
6195
  popup.style.visibility = "";
@@ -6048,16 +6207,16 @@ var Toolbar = class {
6048
6207
  else openPopup();
6049
6208
  });
6050
6209
  const d2 = on(grid, "mouseover", (e) => {
6051
- const cell = e.target.closest(".an-table-cell");
6210
+ const cell = e.target?.closest(".an-table-cell");
6052
6211
  if (!cell) return;
6053
- setHighlight(+cell.getAttribute("data-row"), +cell.getAttribute("data-col"));
6212
+ setHighlight(+cell.dataset.row, +cell.dataset.col);
6054
6213
  });
6055
6214
  const d3 = on(grid, "mouseleave", () => setHighlight(0, 0));
6056
6215
  const d4 = on(grid, "click", (e) => {
6057
- const cell = e.target.closest(".an-table-cell");
6216
+ const cell = e.target?.closest(".an-table-cell");
6058
6217
  if (!cell) return;
6059
- const rows = +cell.getAttribute("data-row");
6060
- const cols = +cell.getAttribute("data-col");
6218
+ const rows = +cell.dataset.row;
6219
+ const cols = +cell.dataset.col;
6061
6220
  closePopup();
6062
6221
  this.context.invoke("editor.focus");
6063
6222
  def.action(this.context, rows, cols);
@@ -6066,7 +6225,7 @@ var Toolbar = class {
6066
6225
  if (isOpen) closePopup();
6067
6226
  });
6068
6227
  this._disposers.push(d1, d2, d3, d4, d5, () => {
6069
- if (popup.parentNode) popup.parentNode.removeChild(popup);
6228
+ if (popup.parentNode) popup.remove();
6070
6229
  });
6071
6230
  wrap.appendChild(btn);
6072
6231
  document.body.appendChild(popup);
@@ -6156,13 +6315,13 @@ var Toolbar = class {
6156
6315
  /** @type {Range|null} saved selection range before popup opens */
6157
6316
  let savedRange = null;
6158
6317
  const saveSelection = () => {
6159
- const sel = window.getSelection();
6318
+ const sel = globalThis.getSelection();
6160
6319
  savedRange = sel && sel.rangeCount ? sel.getRangeAt(0).cloneRange() : null;
6161
6320
  };
6162
6321
  const restoreSelection = () => {
6163
6322
  if (!savedRange) return;
6164
6323
  try {
6165
- const sel = window.getSelection();
6324
+ const sel = globalThis.getSelection();
6166
6325
  if (!sel) return;
6167
6326
  sel.removeAllRanges();
6168
6327
  sel.addRange(savedRange);
@@ -6177,7 +6336,7 @@ var Toolbar = class {
6177
6336
  const rect = arrowBtn.getBoundingClientRect();
6178
6337
  const popupMinW = 184;
6179
6338
  let left = rect.left;
6180
- if (left + popupMinW > window.innerWidth) left = rect.right - popupMinW;
6339
+ if (left + popupMinW > globalThis.innerWidth) left = rect.right - popupMinW;
6181
6340
  popup.style.top = `${rect.bottom + 4}px`;
6182
6341
  popup.style.left = `${Math.max(4, left)}px`;
6183
6342
  popup.style.display = "block";
@@ -6217,11 +6376,17 @@ var Toolbar = class {
6217
6376
  e.preventDefault();
6218
6377
  });
6219
6378
  const d3b = on(swatches, "click", (e) => {
6220
- const sw = e.target.closest(".an-color-swatch");
6221
- if (sw) applyColor(sw.dataset.color);
6379
+ const sw = e.target?.closest(".an-color-swatch");
6380
+ if (sw) applyColor(
6381
+ /** @type {HTMLElement} */
6382
+ sw.dataset.color
6383
+ );
6222
6384
  });
6223
6385
  const d4 = on(colorInput, "change", (e) => {
6224
- applyColor(e.target.value);
6386
+ applyColor(
6387
+ /** @type {HTMLInputElement} */
6388
+ e.target.value
6389
+ );
6225
6390
  });
6226
6391
  const d5 = on(document, "click", (e) => {
6227
6392
  if (isOpen && !wrap.contains(e.target) && !popup.contains(e.target)) closePopup();
@@ -6234,9 +6399,9 @@ var Toolbar = class {
6234
6399
  passive: true,
6235
6400
  capture: true
6236
6401
  });
6237
- window.addEventListener("resize", onScrollResize, { passive: true });
6238
- this._disposers.push(d1, d2, d2b, d3, d3b, d4, d5, d6, () => document.removeEventListener("scroll", onScrollResize, { capture: true }), () => window.removeEventListener("resize", onScrollResize), () => {
6239
- if (popup.parentNode) popup.parentNode.removeChild(popup);
6402
+ globalThis.addEventListener("resize", onScrollResize, { passive: true });
6403
+ this._disposers.push(d1, d2, d2b, d3, d3b, d4, d5, d6, () => document.removeEventListener("scroll", onScrollResize, { capture: true }), () => globalThis.removeEventListener("resize", onScrollResize), () => {
6404
+ if (popup.parentNode) popup.remove();
6240
6405
  });
6241
6406
  this._colorPickerClosers.push(closePopup);
6242
6407
  this._disposers.push(() => {
@@ -6280,7 +6445,7 @@ var Toolbar = class {
6280
6445
  /** @type {Range|null} */
6281
6446
  let _savedRange = null;
6282
6447
  const dMousedown = on(select, "mousedown", () => {
6283
- const sel = window.getSelection();
6448
+ const sel = globalThis.getSelection();
6284
6449
  _savedRange = sel && sel.rangeCount ? sel.getRangeAt(0).cloneRange() : null;
6285
6450
  });
6286
6451
  const disposer = on(select, "change", (e) => {
@@ -6289,7 +6454,7 @@ var Toolbar = class {
6289
6454
  if (!value || selectedOpt.disabled) return;
6290
6455
  this.context.invoke("editor.focus");
6291
6456
  if (_savedRange) try {
6292
- const sel = window.getSelection();
6457
+ const sel = globalThis.getSelection();
6293
6458
  if (sel) {
6294
6459
  sel.removeAllRanges();
6295
6460
  sel.addRange(_savedRange);
@@ -6354,17 +6519,25 @@ var Toolbar = class {
6354
6519
  if (!this.el) return;
6355
6520
  const btnMap = this._btnMap || /* @__PURE__ */ new Map();
6356
6521
  this.el.querySelectorAll("button[data-btn]").forEach((btn) => {
6357
- const def = btnMap.get(btn.getAttribute("data-btn"));
6522
+ const def = btnMap.get(
6523
+ /** @type {HTMLElement} */
6524
+ btn.dataset.btn
6525
+ );
6358
6526
  if (def && typeof def.isActive === "function") btn.classList.toggle("active", !!def.isActive(this.context));
6359
- if (def && typeof def.isDisabled === "function") btn.disabled = !!def.isDisabled(this.context);
6527
+ if (def && typeof def.isDisabled === "function")
6528
+ /** @type {HTMLButtonElement} */ btn.disabled = !!def.isDisabled(this.context);
6360
6529
  });
6361
6530
  this.el.querySelectorAll("select[data-btn]").forEach((select) => {
6362
- const def = btnMap.get(select.getAttribute("data-btn"));
6531
+ const def = btnMap.get(
6532
+ /** @type {HTMLElement} */
6533
+ select.dataset.btn
6534
+ );
6363
6535
  if (!def || typeof def.getValue !== "function") return;
6364
6536
  let raw = (def.getValue(this.context) || "").replace(/["']/g, "").trim();
6365
6537
  if (!raw) raw = this.options.defaultFontFamily || this.options.fontFamilies && this.options.fontFamilies[0] || "";
6366
- const matched = Array.from(select.options).find((opt) => opt.value && opt.value.toLowerCase() === raw.toLowerCase());
6367
- select.value = matched ? matched.value : "";
6538
+ const sel = select;
6539
+ const matched = Array.from(sel.options).find((opt) => opt.value && opt.value.toLowerCase() === raw.toLowerCase());
6540
+ sel.value = matched ? matched.value : "";
6368
6541
  });
6369
6542
  }
6370
6543
  /**
@@ -6484,7 +6657,7 @@ var Statusbar = class {
6484
6657
  this._dragDisposers.forEach((d) => d());
6485
6658
  this._dragDisposers = null;
6486
6659
  }
6487
- if (this.el && this.el.parentNode) this.el.parentNode.removeChild(this.el);
6660
+ this.el?.remove();
6488
6661
  this.el = null;
6489
6662
  }
6490
6663
  _bindResize(handle) {
@@ -6548,7 +6721,7 @@ var Statusbar = class {
6548
6721
  if (!this._wordCountEl || !this._charCountEl) return;
6549
6722
  const text = this.context.layoutInfo.editable.textContent || "";
6550
6723
  const words = _countWords(text);
6551
- const chars = text.replace(/\n/g, "").length;
6724
+ const chars = text.replaceAll("\n", "").length;
6552
6725
  const maxWords = this.options.maxWords || 0;
6553
6726
  const maxChars = this.options.maxChars || 0;
6554
6727
  const LS = this.context.locale.statusbar;
@@ -6570,7 +6743,7 @@ var Statusbar = class {
6570
6743
  * @returns {number}
6571
6744
  */
6572
6745
  getCharCount() {
6573
- return (this.context.layoutInfo.editable.innerText || "").replace(/\n/g, "").length;
6746
+ return (this.context.layoutInfo.editable.innerText || "").replaceAll("\n", "").length;
6574
6747
  }
6575
6748
  };
6576
6749
  //#endregion
@@ -6663,7 +6836,7 @@ var Clipboard = class {
6663
6836
  if (el.querySelector("a, strong, em, b, i, ul, ol, li, table, img, blockquote, pre, code, h1, h2, h3, h4, h5, h6")) continue;
6664
6837
  const parent = el.parentNode;
6665
6838
  while (el.firstChild) parent.insertBefore(el.firstChild, el);
6666
- parent.removeChild(el);
6839
+ el.remove();
6667
6840
  }
6668
6841
  doc.querySelectorAll("*").forEach((el) => {
6669
6842
  el.removeAttribute("class");
@@ -6705,7 +6878,7 @@ var Clipboard = class {
6705
6878
  this._forcePlain = !!val;
6706
6879
  }
6707
6880
  _onPaste(event) {
6708
- const clipboardData = event.clipboardData || window.clipboardData;
6881
+ const clipboardData = event.clipboardData || globalThis.clipboardData;
6709
6882
  if (!clipboardData) return;
6710
6883
  const forcePlain = this._forcePlain;
6711
6884
  this._forcePlain = false;
@@ -6749,7 +6922,6 @@ var Clipboard = class {
6749
6922
  if (this.options.pasteStripAttributes) html = this._stripAttributes(html);
6750
6923
  execCommand("insertHTML", html);
6751
6924
  this.context.invoke("editor.afterCommand");
6752
- return;
6753
6925
  }
6754
6926
  }
6755
6927
  _onDragover(event) {
@@ -6781,17 +6953,17 @@ var Clipboard = class {
6781
6953
  this.options.onImageUpload(files);
6782
6954
  return;
6783
6955
  }
6784
- const UNSUPPORTED = [
6956
+ const UNSUPPORTED = new Set([
6785
6957
  "image/tiff",
6786
6958
  "image/x-tiff",
6787
6959
  "image/bmp",
6788
6960
  "image/x-bmp",
6789
6961
  "image/x-ms-bmp"
6790
- ];
6962
+ ]);
6791
6963
  const maxBytes = (this.options.maxImageSize || 5) * 1024 * 1024;
6792
6964
  files.forEach((file) => {
6793
6965
  if (!file || !file.type.startsWith("image/")) return;
6794
- if (UNSUPPORTED.includes(file.type)) {
6966
+ if (UNSUPPORTED.has(file.type)) {
6795
6967
  const message = `Image format "${file.type}" is not supported for display in web browsers. Please convert to PNG, JPEG, or WebP first.`;
6796
6968
  this.context.triggerEvent("imageError", {
6797
6969
  file,
@@ -6843,10 +7015,10 @@ var Clipboard = class {
6843
7015
  */
6844
7016
  _dataUrlToBlob(dataUrl) {
6845
7017
  const [header, b64] = dataUrl.split(",");
6846
- const mime = header.match(/:(.*?);/)?.[1] ?? "image/png";
7018
+ const mime = /:(.*?);/.exec(header)?.[1] ?? "image/png";
6847
7019
  const binary = atob(b64);
6848
7020
  const arr = new Uint8Array(binary.length);
6849
- for (let i = 0; i < binary.length; i++) arr[i] = binary.charCodeAt(i);
7021
+ for (let i = 0; i < binary.length; i++) arr[i] = binary.codePointAt(i);
6850
7022
  return new Blob([arr], { type: mime });
6851
7023
  }
6852
7024
  /**
@@ -6914,7 +7086,7 @@ var Clipboard = class {
6914
7086
  }
6915
7087
  }
6916
7088
  if (!range) return;
6917
- const sel = window.getSelection();
7089
+ const sel = globalThis.getSelection();
6918
7090
  if (sel) {
6919
7091
  sel.removeAllRanges();
6920
7092
  sel.addRange(range);
@@ -6926,7 +7098,7 @@ var Clipboard = class {
6926
7098
  * @returns {string}
6927
7099
  */
6928
7100
  _escapeHTML(str) {
6929
- return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
7101
+ return str.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&#039;");
6930
7102
  }
6931
7103
  };
6932
7104
  //#endregion
@@ -6963,7 +7135,7 @@ var Placeholder = class {
6963
7135
  _update() {
6964
7136
  const editable = this.context.layoutInfo.editable;
6965
7137
  const isFocused = document.activeElement === editable;
6966
- const isEmpty = !(editable.textContent.replace(/\u200B/g, "").trim().length > 0) && !editable.querySelector("img, table, hr, .an-video-wrapper");
7138
+ const isEmpty = !(editable.textContent.replaceAll("​", "").trim().length > 0) && !editable.querySelector("img, table, hr, .an-video-wrapper");
6967
7139
  editable.classList.toggle("an-placeholder", isEmpty && !isFocused);
6968
7140
  }
6969
7141
  };
@@ -6991,7 +7163,7 @@ var Codeview = class {
6991
7163
  destroy() {
6992
7164
  this._disposers.forEach((d) => d());
6993
7165
  this._disposers = [];
6994
- if (this._textarea && this._textarea.parentNode) this._textarea.parentNode.removeChild(this._textarea);
7166
+ this._textarea?.remove();
6995
7167
  this._textarea = null;
6996
7168
  }
6997
7169
  toggle() {
@@ -7022,7 +7194,7 @@ var Codeview = class {
7022
7194
  if (!this._active || !this._textarea) return;
7023
7195
  const { editable } = this.context.layoutInfo;
7024
7196
  editable.innerHTML = sanitiseHTML(this._textarea.value, { allowIframes: true });
7025
- this._textarea.parentNode.removeChild(this._textarea);
7197
+ this._textarea.remove();
7026
7198
  this._textarea = null;
7027
7199
  editable.style.display = "";
7028
7200
  this._active = false;
@@ -7046,7 +7218,7 @@ var Codeview = class {
7046
7218
  }).split("\n").map((line) => {
7047
7219
  const stripped = line.trim();
7048
7220
  if (!stripped) return "";
7049
- if (/^<\//.test(stripped)) indent = Math.max(0, indent - 1);
7221
+ if (stripped.startsWith("</")) indent = Math.max(0, indent - 1);
7050
7222
  const out = " ".repeat(indent) + stripped;
7051
7223
  if (/^<[^/!][^>]*[^/]>/.test(stripped) && !INLINE_RE.test(stripped) && !/^<(br|hr|img|input|link|meta)/.test(stripped)) indent++;
7052
7224
  return out;
@@ -7135,7 +7307,7 @@ var LinkDialog = class {
7135
7307
  destroy() {
7136
7308
  this._disposers.forEach((d) => d());
7137
7309
  this._disposers = [];
7138
- if (this._dialog && this._dialog.parentNode) this._dialog.parentNode.removeChild(this._dialog);
7310
+ if (this._dialog && this._dialog.parentNode) this._dialog.remove();
7139
7311
  this._dialog = null;
7140
7312
  }
7141
7313
  /**
@@ -7158,8 +7330,13 @@ var LinkDialog = class {
7158
7330
  "aria-label": L.ariaLabel
7159
7331
  });
7160
7332
  const box = createElement("div", { class: "an-dialog-box" });
7333
+ const header = createElement("div", { class: "an-dialog-header" });
7334
+ const iconEl = createElement("span", { class: "an-dialog-icon" });
7335
+ 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>`;
7161
7336
  const title = createElement("h3", { class: "an-dialog-title" });
7162
7337
  title.textContent = L.title;
7338
+ header.appendChild(iconEl);
7339
+ header.appendChild(title);
7163
7340
  const urlLabel = createElement("label", { class: "an-label" });
7164
7341
  urlLabel.textContent = L.url;
7165
7342
  const urlInput = createElement("input", {
@@ -7204,8 +7381,9 @@ var LinkDialog = class {
7204
7381
  cancelBtn.textContent = L.cancelBtn;
7205
7382
  btnRow.appendChild(insertBtn);
7206
7383
  btnRow.appendChild(cancelBtn);
7207
- box.append(title, urlLabel, urlInput, textLabel, textInput, tabLabel, btnRow);
7384
+ box.append(header, urlLabel, urlInput, textLabel, textInput, tabLabel, btnRow);
7208
7385
  overlay.appendChild(box);
7386
+ makeDraggable(header, box);
7209
7387
  const d1 = on(insertBtn, "click", () => this._onInsert());
7210
7388
  const d2 = on(cancelBtn, "click", () => this._close());
7211
7389
  const d3 = on(overlay, "click", (e) => {
@@ -7227,7 +7405,7 @@ var LinkDialog = class {
7227
7405
  return overlay;
7228
7406
  }
7229
7407
  _prefill() {
7230
- const sel = window.getSelection();
7408
+ const sel = globalThis.getSelection();
7231
7409
  let anchor = null;
7232
7410
  if (sel && sel.rangeCount > 0) {
7233
7411
  let node = sel.getRangeAt(0).startContainer;
@@ -7314,7 +7492,7 @@ var ImageDialog = class {
7314
7492
  destroy() {
7315
7493
  this._disposers.forEach((d) => d());
7316
7494
  this._disposers = [];
7317
- if (this._dialog && this._dialog.parentNode) this._dialog.parentNode.removeChild(this._dialog);
7495
+ if (this._dialog && this._dialog.parentNode) this._dialog.remove();
7318
7496
  this._dialog = null;
7319
7497
  }
7320
7498
  show() {
@@ -7335,8 +7513,13 @@ var ImageDialog = class {
7335
7513
  "aria-label": L.ariaLabel
7336
7514
  });
7337
7515
  const box = createElement("div", { class: "an-dialog-box" });
7516
+ const header = createElement("div", { class: "an-dialog-header" });
7517
+ const iconEl = createElement("span", { class: "an-dialog-icon" });
7518
+ 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>`;
7338
7519
  const title = createElement("h3", { class: "an-dialog-title" });
7339
7520
  title.textContent = L.title;
7521
+ header.appendChild(iconEl);
7522
+ header.appendChild(title);
7340
7523
  const urlLabel = createElement("label", { class: "an-label" });
7341
7524
  urlLabel.textContent = L.imageUrl;
7342
7525
  const urlInput = createElement("input", {
@@ -7355,7 +7538,7 @@ var ImageDialog = class {
7355
7538
  autocomplete: "off"
7356
7539
  });
7357
7540
  this._altInput = altInput;
7358
- box.append(title, urlLabel, urlInput, altLabel, altInput);
7541
+ box.append(header, urlLabel, urlInput, altLabel, altInput);
7359
7542
  const alignLabel = createElement("label", { class: "an-label" });
7360
7543
  alignLabel.textContent = L.alignment;
7361
7544
  const alignRow = createElement("div", { class: "an-align-row" });
@@ -7424,6 +7607,7 @@ var ImageDialog = class {
7424
7607
  btnRow.appendChild(cancelBtn);
7425
7608
  box.append(btnRow);
7426
7609
  overlay.appendChild(box);
7610
+ makeDraggable(header, box);
7427
7611
  const d1 = on(insertBtn, "click", () => this._onInsert());
7428
7612
  const d2 = on(cancelBtn, "click", () => this._close());
7429
7613
  const d3 = on(overlay, "click", (e) => {
@@ -7540,7 +7724,7 @@ var VideoDialog = class {
7540
7724
  destroy() {
7541
7725
  this._disposers.forEach((d) => d());
7542
7726
  this._disposers = [];
7543
- if (this._dialog && this._dialog.parentNode) this._dialog.parentNode.removeChild(this._dialog);
7727
+ if (this._dialog && this._dialog.parentNode) this._dialog.remove();
7544
7728
  this._dialog = null;
7545
7729
  }
7546
7730
  show() {
@@ -7561,8 +7745,13 @@ var VideoDialog = class {
7561
7745
  "aria-label": L.ariaLabel
7562
7746
  });
7563
7747
  const box = createElement("div", { class: "an-dialog-box" });
7748
+ const header = createElement("div", { class: "an-dialog-header" });
7749
+ const iconEl = createElement("span", { class: "an-dialog-icon" });
7750
+ 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>`;
7564
7751
  const title = createElement("h3", { class: "an-dialog-title" });
7565
7752
  title.textContent = L.title;
7753
+ header.appendChild(iconEl);
7754
+ header.appendChild(title);
7566
7755
  const urlLabel = createElement("label", { class: "an-label" });
7567
7756
  urlLabel.textContent = L.videoUrl;
7568
7757
  const urlInput = createElement("input", {
@@ -7598,8 +7787,9 @@ var VideoDialog = class {
7598
7787
  cancelBtn.textContent = L.cancelBtn;
7599
7788
  btnRow.appendChild(insertBtn);
7600
7789
  btnRow.appendChild(cancelBtn);
7601
- box.append(title, urlLabel, urlInput, hintEl, widthLabel, widthInput, btnRow);
7790
+ box.append(header, urlLabel, urlInput, hintEl, widthLabel, widthInput, btnRow);
7602
7791
  overlay.appendChild(box);
7792
+ makeDraggable(header, box);
7603
7793
  const d0 = on(urlInput, "input", () => {
7604
7794
  const info = this._parseVideoUrl(urlInput.value.trim());
7605
7795
  hintEl.textContent = info ? this.context.locale.videoDialog.detected(info.type) : urlInput.value ? this.context.locale.videoDialog.unknownFormat : "";
@@ -7620,7 +7810,7 @@ var VideoDialog = class {
7620
7810
  }
7621
7811
  _onInsert() {
7622
7812
  const rawUrl = this._urlInput.value.trim();
7623
- const width = Math.max(80, parseInt(this._widthInput.value, 10) || 560);
7813
+ const width = Math.max(80, Number.parseInt(this._widthInput.value, 10) || 560);
7624
7814
  if (!rawUrl) {
7625
7815
  this._urlInput.focus();
7626
7816
  return;
@@ -7663,22 +7853,22 @@ var VideoDialog = class {
7663
7853
  } catch {
7664
7854
  return null;
7665
7855
  }
7666
- const ytWatch = url.match(/(?:youtube\.com\/watch\?(?:.*&)?v=|youtube\.com\/embed\/)([a-zA-Z0-9_-]{11})/);
7856
+ const ytWatch = /(?:youtube\.com\/watch\?(?:.*&)?v=|youtube\.com\/embed\/)([a-zA-Z0-9_-]{11})/.exec(url);
7667
7857
  if (ytWatch) return {
7668
7858
  type: "YouTube",
7669
7859
  embedUrl: `https://www.youtube.com/embed/${ytWatch[1]}`
7670
7860
  };
7671
- const ytShort = url.match(/youtu\.be\/([a-zA-Z0-9_-]{11})/);
7861
+ const ytShort = /youtu\.be\/([a-zA-Z0-9_-]{11})/.exec(url);
7672
7862
  if (ytShort) return {
7673
7863
  type: "YouTube",
7674
7864
  embedUrl: `https://www.youtube.com/embed/${ytShort[1]}`
7675
7865
  };
7676
- const ytShorts = url.match(/youtube\.com\/shorts\/([a-zA-Z0-9_-]{11})/);
7866
+ const ytShorts = /youtube\.com\/shorts\/([a-zA-Z0-9_-]{11})/.exec(url);
7677
7867
  if (ytShorts) return {
7678
7868
  type: "YouTube Shorts",
7679
7869
  embedUrl: `https://www.youtube.com/embed/${ytShorts[1]}`
7680
7870
  };
7681
- const vimeo = url.match(/vimeo\.com\/(\d+)/);
7871
+ const vimeo = /vimeo\.com\/(\d+)/.exec(url);
7682
7872
  if (vimeo) return {
7683
7873
  type: "Vimeo",
7684
7874
  embedUrl: `https://player.vimeo.com/video/${vimeo[1]}`
@@ -7702,7 +7892,7 @@ var VideoDialog = class {
7702
7892
  const iframeTitle = `${info.type} video player`;
7703
7893
  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>`;
7704
7894
  }
7705
- 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>`;
7895
+ 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>`;
7706
7896
  const safeSrc = (() => {
7707
7897
  try {
7708
7898
  const p = new URL(url);
@@ -7713,7 +7903,7 @@ var VideoDialog = class {
7713
7903
  }
7714
7904
  })();
7715
7905
  if (!safeSrc) return null;
7716
- 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>`;
7906
+ 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>`;
7717
7907
  }
7718
7908
  };
7719
7909
  //#endregion
@@ -7782,9 +7972,9 @@ var ImageResizer = class {
7782
7972
  };
7783
7973
  this._disposers.push(on(editable, "click", (e) => this._onEditorClick(e)), on(editable, "contextmenu", (e) => {
7784
7974
  if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
7785
- const img = e.target.closest("img");
7975
+ const img = e.target?.closest("img");
7786
7976
  if (img) this._select(img);
7787
- }), 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 }));
7977
+ }), 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 }));
7788
7978
  return this;
7789
7979
  }
7790
7980
  destroy() {
@@ -7799,7 +7989,7 @@ var ImageResizer = class {
7799
7989
  this._positionRaf = null;
7800
7990
  }
7801
7991
  this._deselect();
7802
- if (this._overlay && this._overlay.parentNode) this._overlay.parentNode.removeChild(this._overlay);
7992
+ if (this._overlay && this._overlay.parentNode) this._overlay.remove();
7803
7993
  this._overlay = null;
7804
7994
  }
7805
7995
  /** @returns {HTMLImageElement|null} */
@@ -8001,7 +8191,7 @@ var VideoResizer = class {
8001
8191
  if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
8002
8192
  const wrapper = this._findWrapper(e.target);
8003
8193
  if (wrapper) this._select(wrapper);
8004
- }), 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) => {
8194
+ }), 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) => {
8005
8195
  if (e.target instanceof Element && e.target.closest(".an-video-wrapper")) e.preventDefault();
8006
8196
  }));
8007
8197
  return this;
@@ -8018,7 +8208,7 @@ var VideoResizer = class {
8018
8208
  this._positionRaf = null;
8019
8209
  }
8020
8210
  this._deselect();
8021
- if (this._overlay && this._overlay.parentNode) this._overlay.parentNode.removeChild(this._overlay);
8211
+ this._overlay?.remove();
8022
8212
  this._overlay = null;
8023
8213
  }
8024
8214
  /** @returns {HTMLElement|null} */
@@ -8039,7 +8229,7 @@ var VideoResizer = class {
8039
8229
  */
8040
8230
  _findWrapper(el) {
8041
8231
  if (!el || !(el instanceof Element)) return null;
8042
- if (el.classList && el.classList.contains("an-video-wrapper")) return el;
8232
+ if (el.classList?.contains("an-video-wrapper")) return el;
8043
8233
  const w = el.closest(".an-video-wrapper");
8044
8234
  if (w) return w;
8045
8235
  return null;
@@ -8072,7 +8262,7 @@ var VideoResizer = class {
8072
8262
  _onDocClick(e) {
8073
8263
  if (!this._activeWrapper) return;
8074
8264
  if (this._activeWrapper.contains(e.target)) return;
8075
- if (this._overlay && this._overlay.contains(e.target)) return;
8265
+ if (this._overlay?.contains(e.target)) return;
8076
8266
  if (e.target.closest(".an-contextmenu")) return;
8077
8267
  this._deselect();
8078
8268
  }
@@ -8192,19 +8382,19 @@ var LinkTooltip = class {
8192
8382
  document.body.appendChild(this._el);
8193
8383
  const editable = this.context.layoutInfo.editable;
8194
8384
  this._disposers.push(on(editable, "mouseover", (e) => {
8195
- const anchor = e.target.closest("a[href]");
8385
+ const anchor = e.target?.closest("a[href]");
8196
8386
  if (anchor && editable.contains(anchor)) this._scheduleShow(anchor);
8197
8387
  }), on(editable, "mouseout", (e) => {
8198
8388
  const to = e.relatedTarget;
8199
8389
  if (!to || !editable.contains(to) && !this._el.contains(to)) this._scheduleHide();
8200
- }));
8390
+ }), on(globalThis, "scroll", () => this._hide(), { passive: true }), on(globalThis, "resize", () => this._hide(), { passive: true }));
8201
8391
  return this;
8202
8392
  }
8203
8393
  destroy() {
8204
8394
  this._clearTimers();
8205
8395
  this._disposers.forEach((d) => d());
8206
8396
  this._disposers = [];
8207
- if (this._el && this._el.parentNode) this._el.parentNode.removeChild(this._el);
8397
+ if (this._el && this._el.parentNode) this._el.remove();
8208
8398
  this._el = null;
8209
8399
  }
8210
8400
  _buildTooltip() {
@@ -8289,8 +8479,8 @@ var LinkTooltip = class {
8289
8479
  const margin = 6;
8290
8480
  let top = rect.bottom + margin;
8291
8481
  let left = rect.left;
8292
- if (top + tipH > window.innerHeight - margin) top = rect.top - tipH - margin;
8293
- if (left + tipW > window.innerWidth - margin) left = window.innerWidth - tipW - margin;
8482
+ if (top + tipH > globalThis.innerHeight - margin) top = rect.top - tipH - margin;
8483
+ if (left + tipW > globalThis.innerWidth - margin) left = globalThis.innerWidth - tipW - margin;
8294
8484
  if (left < margin) left = margin;
8295
8485
  this._el.style.top = `${top}px`;
8296
8486
  this._el.style.left = `${left}px`;
@@ -8305,12 +8495,12 @@ var LinkTooltip = class {
8305
8495
  }
8306
8496
  }
8307
8497
  _openLink() {
8308
- const url = this._activeAnchor && this._activeAnchor.getAttribute("href");
8309
- if (url) window.open(url, "_blank", "noopener,noreferrer");
8498
+ const url = this._activeAnchor?.getAttribute("href");
8499
+ if (url) globalThis.open(url, "_blank", "noopener,noreferrer");
8310
8500
  this._hide();
8311
8501
  }
8312
8502
  _copyLink() {
8313
- const url = this._activeAnchor && this._activeAnchor.getAttribute("href");
8503
+ const url = this._activeAnchor?.getAttribute("href");
8314
8504
  if (url) navigator.clipboard.writeText(url).catch(() => {
8315
8505
  const ta = document.createElement("textarea");
8316
8506
  ta.value = url;
@@ -8319,7 +8509,7 @@ var LinkTooltip = class {
8319
8509
  document.body.appendChild(ta);
8320
8510
  ta.select();
8321
8511
  document.execCommand("copy");
8322
- document.body.removeChild(ta);
8512
+ ta.remove();
8323
8513
  });
8324
8514
  if (this._copyBtn) {
8325
8515
  this._copyBtn.classList.add("an-link-tooltip-btn--copied");
@@ -8330,7 +8520,7 @@ var LinkTooltip = class {
8330
8520
  const anchor = this._activeAnchor;
8331
8521
  if (!anchor) return;
8332
8522
  this._hide();
8333
- const sel = window.getSelection();
8523
+ const sel = globalThis.getSelection();
8334
8524
  const range = document.createRange();
8335
8525
  range.selectNodeContents(anchor);
8336
8526
  sel.removeAllRanges();
@@ -8341,7 +8531,7 @@ var LinkTooltip = class {
8341
8531
  const anchor = this._activeAnchor;
8342
8532
  if (!anchor) return;
8343
8533
  this._hide();
8344
- const sel = window.getSelection();
8534
+ const sel = globalThis.getSelection();
8345
8535
  const range = document.createRange();
8346
8536
  range.selectNode(anchor);
8347
8537
  sel.removeAllRanges();
@@ -8382,21 +8572,22 @@ var ImageTooltip = class {
8382
8572
  const editable = this.context.layoutInfo.editable;
8383
8573
  this._disposers.push(on(editable, "mouseover", (e) => {
8384
8574
  if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
8385
- const img = e.target.closest("img");
8575
+ const img = e.target?.closest("img");
8386
8576
  if (img && editable.contains(img) && !img.closest("a[href]")) this._scheduleShow(img);
8387
8577
  }, { passive: true }), on(editable, "mouseout", (e) => {
8388
8578
  const to = e.relatedTarget;
8389
8579
  if (!to || !editable.contains(to) && !this._el.contains(to)) this._scheduleHide();
8390
8580
  }, { passive: true }), on(document, "click", (e) => {
8391
- if (this._activeImg && !this._activeImg.contains(e.target) && !this._el.contains(e.target)) this._hide();
8392
- }));
8581
+ const et = e.target;
8582
+ if (this._activeImg && !this._activeImg.contains(et) && !this._el.contains(et)) this._hide();
8583
+ }), on(globalThis, "scroll", () => this._hide(), { passive: true }), on(globalThis, "resize", () => this._hide(), { passive: true }));
8393
8584
  return this;
8394
8585
  }
8395
8586
  destroy() {
8396
8587
  this._clearTimers();
8397
8588
  this._disposers.forEach((d) => d());
8398
8589
  this._disposers = [];
8399
- if (this._el && this._el.parentNode) this._el.parentNode.removeChild(this._el);
8590
+ this._el?.remove();
8400
8591
  this._el = null;
8401
8592
  }
8402
8593
  _buildTooltip() {
@@ -8473,7 +8664,7 @@ var ImageTooltip = class {
8473
8664
  clearTimeout(this._hideTimer);
8474
8665
  this._hideTimer = setTimeout(() => this._hide(), HIDE_DELAY$3);
8475
8666
  }
8476
- _show(img) {
8667
+ _show(_img) {
8477
8668
  this._el.style.display = "flex";
8478
8669
  requestAnimationFrame(() => {
8479
8670
  if (this._activeImg) this._positionNear(this._activeImg);
@@ -8497,8 +8688,8 @@ var ImageTooltip = class {
8497
8688
  const margin = 6;
8498
8689
  let top = rect.bottom + margin;
8499
8690
  let left = rect.left + (rect.width - tipW) / 2;
8500
- if (top + tipH > window.innerHeight - margin) top = rect.top - tipH - margin;
8501
- if (left + tipW > window.innerWidth - margin) left = window.innerWidth - tipW - margin;
8691
+ if (top + tipH > globalThis.innerHeight - margin) top = rect.top - tipH - margin;
8692
+ if (left + tipW > globalThis.innerWidth - margin) left = globalThis.innerWidth - tipW - margin;
8502
8693
  if (left < margin) left = margin;
8503
8694
  this._el.style.top = `${top}px`;
8504
8695
  this._el.style.left = `${left}px`;
@@ -8554,8 +8745,8 @@ var ImageTooltip = class {
8554
8745
  const img = this._activeImg;
8555
8746
  if (!img) return;
8556
8747
  const current = img.style.transform || "";
8557
- const match = current.match(/rotate\((-?[\d.]+)deg\)/);
8558
- const next = ((match ? parseFloat(match[1]) : 0) + delta + 360) % 360;
8748
+ const match = /rotate\((-?[\d.]+)deg\)/.exec(current);
8749
+ const next = ((match ? Number.parseFloat(match[1]) : 0) + delta + 360) % 360;
8559
8750
  const cleaned = current.replace(/rotate\(-?[\d.]+deg\)/, "").trim();
8560
8751
  img.style.transform = cleaned ? `${cleaned} rotate(${next}deg)` : next === 0 ? "" : `rotate(${next}deg)`;
8561
8752
  this.context.invoke("editor.afterCommand");
@@ -8570,8 +8761,8 @@ var ImageTooltip = class {
8570
8761
  this._hide();
8571
8762
  this.context.invoke("imageResizer.deselect");
8572
8763
  const figure = img.closest("figure.an-figure");
8573
- if (figure && figure.parentNode) figure.parentNode.removeChild(figure);
8574
- else if (img.parentNode) img.parentNode.removeChild(img);
8764
+ if (figure) figure.remove();
8765
+ else img.remove();
8575
8766
  this.context.invoke("editor.afterCommand");
8576
8767
  }
8577
8768
  _crop() {
@@ -8590,7 +8781,7 @@ var ImageTooltip = class {
8590
8781
  this._hide();
8591
8782
  const range = document.createRange();
8592
8783
  range.selectNodeContents(cap);
8593
- const sel = window.getSelection();
8784
+ const sel = globalThis.getSelection();
8594
8785
  if (sel) {
8595
8786
  sel.removeAllRanges();
8596
8787
  sel.addRange(range);
@@ -8624,7 +8815,7 @@ var ImageTooltip = class {
8624
8815
  figure.appendChild(figcaption);
8625
8816
  const range = document.createRange();
8626
8817
  range.selectNodeContents(figcaption);
8627
- const sel = window.getSelection();
8818
+ const sel = globalThis.getSelection();
8628
8819
  if (sel) {
8629
8820
  sel.removeAllRanges();
8630
8821
  sel.addRange(range);
@@ -8665,14 +8856,15 @@ var VideoTooltip = class {
8665
8856
  const editable = this.context.layoutInfo.editable;
8666
8857
  this._disposers.push(on(editable, "mouseover", (e) => {
8667
8858
  if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
8668
- const wrapper = e.target.closest(".an-video-wrapper");
8859
+ const wrapper = e.target?.closest(".an-video-wrapper");
8669
8860
  if (wrapper && editable.contains(wrapper)) this._scheduleShow(wrapper);
8670
8861
  }, { passive: true }), on(editable, "mouseout", (e) => {
8671
8862
  const to = e.relatedTarget;
8672
8863
  if (!to || !editable.contains(to) && !this._el.contains(to)) this._scheduleHide();
8673
8864
  }, { passive: true }), on(document, "click", (e) => {
8674
- if (this._activeWrapper && !this._activeWrapper.contains(e.target) && !this._el.contains(e.target)) this._hide();
8675
- }));
8865
+ const target = e.target;
8866
+ if (this._activeWrapper && !this._activeWrapper.contains(target) && !this._el.contains(target)) this._hide();
8867
+ }), on(globalThis, "scroll", () => this._hide(), { passive: true }), on(globalThis, "resize", () => this._hide(), { passive: true }));
8676
8868
  return this;
8677
8869
  }
8678
8870
  destroy() {
@@ -8680,7 +8872,7 @@ var VideoTooltip = class {
8680
8872
  this._clearTimers();
8681
8873
  this._disposers.forEach((d) => d());
8682
8874
  this._disposers = [];
8683
- if (this._el && this._el.parentNode) this._el.parentNode.removeChild(this._el);
8875
+ this._el?.remove();
8684
8876
  this._el = null;
8685
8877
  }
8686
8878
  _buildTooltip() {
@@ -8752,7 +8944,7 @@ var VideoTooltip = class {
8752
8944
  if (this._hideTimer) return;
8753
8945
  this._hideTimer = setTimeout(() => this._hide(), HIDE_DELAY$2);
8754
8946
  }
8755
- _show(wrapper) {
8947
+ _show(_wrapper) {
8756
8948
  this._el.style.display = "flex";
8757
8949
  requestAnimationFrame(() => {
8758
8950
  if (this._activeWrapper) this._positionNear(this._activeWrapper);
@@ -8777,8 +8969,8 @@ var VideoTooltip = class {
8777
8969
  const margin = 6;
8778
8970
  let top = rect.bottom + margin;
8779
8971
  let left = rect.left + (rect.width - tipW) / 2;
8780
- if (top + tipH > window.innerHeight - margin) top = rect.top - tipH - margin;
8781
- if (left + tipW > window.innerWidth - margin) left = window.innerWidth - tipW - margin;
8972
+ if (top + tipH > globalThis.innerHeight - margin) top = rect.top - tipH - margin;
8973
+ if (left + tipW > globalThis.innerWidth - margin) left = globalThis.innerWidth - tipW - margin;
8782
8974
  if (left < margin) left = margin;
8783
8975
  this._el.style.top = `${top}px`;
8784
8976
  this._el.style.left = `${left}px`;
@@ -8832,7 +9024,7 @@ var VideoTooltip = class {
8832
9024
  if (!wrapper) return;
8833
9025
  this._hide();
8834
9026
  this.context.invoke("videoResizer.deselect");
8835
- if (wrapper.parentNode) wrapper.parentNode.removeChild(wrapper);
9027
+ wrapper.remove();
8836
9028
  this.context.invoke("editor.afterCommand");
8837
9029
  }
8838
9030
  _togglePreview() {
@@ -8976,8 +9168,35 @@ var ICONS$2 = {
8976
9168
  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>`,
8977
9169
  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>`,
8978
9170
  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>`,
8979
- 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>`
9171
+ 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>`,
9172
+ 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>`
8980
9173
  };
9174
+ var SHADE_PRESETS = [
9175
+ "#000000",
9176
+ "#434343",
9177
+ "#666666",
9178
+ "#999999",
9179
+ "#b7b7b7",
9180
+ "#cccccc",
9181
+ "#efefef",
9182
+ "#ffffff",
9183
+ "#ff0000",
9184
+ "#ff9900",
9185
+ "#ffff00",
9186
+ "#00ff00",
9187
+ "#00ffff",
9188
+ "#4a86e8",
9189
+ "#9900ff",
9190
+ "#ff00ff",
9191
+ "#f4cccc",
9192
+ "#fce5cd",
9193
+ "#fff2cc",
9194
+ "#d9ead3",
9195
+ "#d0e0e3",
9196
+ "#c9daf8",
9197
+ "#d9d2e9",
9198
+ "#ead1dc"
9199
+ ];
8981
9200
  var TableTooltip = class {
8982
9201
  /** @param {import('../Context.js').Context} context */
8983
9202
  constructor(context) {
@@ -8992,6 +9211,9 @@ var TableTooltip = class {
8992
9211
  this._sizeApply = null;
8993
9212
  this._sizeTitleEl = null;
8994
9213
  this._sizeInputEl = null;
9214
+ this._shadePopover = null;
9215
+ this._shadeTitleEl = null;
9216
+ this._shadeColorStrip = null;
8995
9217
  this._selectMode = false;
8996
9218
  this._selectedCells = [];
8997
9219
  this._selectStart = null;
@@ -9004,6 +9226,8 @@ var TableTooltip = class {
9004
9226
  document.body.appendChild(this._el);
9005
9227
  this._sizePopover = this._buildSizePopover();
9006
9228
  document.body.appendChild(this._sizePopover);
9229
+ this._shadePopover = this._buildCellShadePopover();
9230
+ document.body.appendChild(this._shadePopover);
9007
9231
  const editable = this.context.layoutInfo.editable;
9008
9232
  this._editable = editable;
9009
9233
  const onSelMousedown = (e) => {
@@ -9030,20 +9254,24 @@ var TableTooltip = class {
9030
9254
  this._disposers.push(on(editable, "mousedown", onSelMousedown), on(editable, "mousemove", onSelMousemove), on(document, "mouseup", onSelMouseup));
9031
9255
  this._disposers.push(on(editable, "mouseover", (e) => {
9032
9256
  if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
9033
- const table = e.target.closest("table");
9257
+ const table = e.target?.closest("table");
9034
9258
  if (table && editable.contains(table)) {
9035
- const cell = e.target.closest("td, th");
9036
- if (cell) this._activeCell = cell;
9259
+ const cell = e.target?.closest("td, th");
9260
+ if (cell) {
9261
+ this._activeCell = cell;
9262
+ this._syncShadeStrip();
9263
+ }
9037
9264
  this._scheduleShow(table);
9038
9265
  }
9039
9266
  }, { passive: true }), on(editable, "mouseout", (e) => {
9040
9267
  if (this._selectMode) return;
9041
9268
  const to = e.relatedTarget;
9042
- if (!to || !editable.contains(to) && !this._el.contains(to) && !(this._sizePopover && this._sizePopover.contains(to))) this._scheduleHide();
9269
+ if (!to || !editable.contains(to) && !this._el.contains(to) && !this._sizePopover?.contains(to)) this._scheduleHide();
9043
9270
  }, { passive: true }), on(document, "click", (e) => {
9044
- if (this._selectMode && this._activeTable && this._activeTable.contains(e.target)) return;
9045
- if (this._activeTable && !this._activeTable.contains(e.target) && !this._el.contains(e.target) && !(this._sizePopover && this._sizePopover.contains(e.target))) this._hide();
9046
- }));
9271
+ const et = e.target;
9272
+ if (this._selectMode && this._activeTable?.contains(et)) return;
9273
+ if (this._activeTable && !this._activeTable.contains(et) && !this._el.contains(et) && !this._sizePopover?.contains(et)) this._hide();
9274
+ }), on(document, "selectionchange", () => this._syncShadeStrip()), on(globalThis, "scroll", () => this._hide(), { passive: true }), on(globalThis, "resize", () => this._hide(), { passive: true }));
9047
9275
  this._initResize();
9048
9276
  return this;
9049
9277
  }
@@ -9169,10 +9397,12 @@ var TableTooltip = class {
9169
9397
  this._clearTimers();
9170
9398
  this._disposers.forEach((d) => d());
9171
9399
  this._disposers = [];
9172
- if (this._el && this._el.parentNode) this._el.parentNode.removeChild(this._el);
9400
+ if (this._el && this._el.parentNode) this._el.remove();
9173
9401
  this._el = null;
9174
- if (this._sizePopover && this._sizePopover.parentNode) this._sizePopover.parentNode.removeChild(this._sizePopover);
9402
+ if (this._sizePopover && this._sizePopover.parentNode) this._sizePopover.remove();
9175
9403
  this._sizePopover = null;
9404
+ if (this._shadePopover && this._shadePopover.parentNode) this._shadePopover.remove();
9405
+ this._shadePopover = null;
9176
9406
  }
9177
9407
  _buildTooltip() {
9178
9408
  const L = this.context.locale.tooltips.table;
@@ -9200,6 +9430,24 @@ var TableTooltip = class {
9200
9430
  el.appendChild(this._makeBtn(ICONS$2.mergeCells, L.mergeCells, () => this._mergeCells()));
9201
9431
  el.appendChild(this._makeBtn(ICONS$2.unmergeCells, L.unmergeCells, () => this._unmergeCells()));
9202
9432
  el.appendChild(this._sep());
9433
+ const shadeBtn = createElement("button", {
9434
+ type: "button",
9435
+ class: "an-link-tooltip-btn an-link-tooltip-btn--shade",
9436
+ title: L.cellBackground
9437
+ });
9438
+ const shadeSvgWrap = createElement("span", { class: "an-bubble-btn-svg" });
9439
+ shadeSvgWrap.innerHTML = ICONS$2.cellShade;
9440
+ const shadeStrip = createElement("span", { class: "an-link-tooltip-color-strip" });
9441
+ shadeBtn.appendChild(shadeSvgWrap);
9442
+ shadeBtn.appendChild(shadeStrip);
9443
+ this._shadeColorStrip = shadeStrip;
9444
+ this._disposers.push(on(shadeBtn, "click", (e) => {
9445
+ e.preventDefault();
9446
+ e.stopPropagation();
9447
+ this._openCellShadePopover();
9448
+ }));
9449
+ el.appendChild(shadeBtn);
9450
+ el.appendChild(this._sep());
9203
9451
  el.appendChild(this._makeBtn(ICONS$2.colWidth, L.columnWidth, () => this._openSizePopover("col")));
9204
9452
  el.appendChild(this._makeBtn(ICONS$2.rowHeight, L.rowHeight, () => this._openSizePopover("row")));
9205
9453
  el.appendChild(this._makeBtn(ICONS$2.tableBorder, L.tableBorderWidth, () => this._openSizePopover("border")));
@@ -9208,6 +9456,7 @@ var TableTooltip = class {
9208
9456
  this._disposers.push(on(el, "mouseenter", () => this._clearTimers()), on(el, "mouseleave", () => {
9209
9457
  if (this._selectMode) return;
9210
9458
  if (this._sizePopover && this._sizePopover.style.display !== "none") return;
9459
+ if (this._shadePopover && this._shadePopover.style.display !== "none") return;
9211
9460
  this._scheduleHide();
9212
9461
  }));
9213
9462
  return el;
@@ -9254,10 +9503,16 @@ var TableTooltip = class {
9254
9503
  _show() {
9255
9504
  if (!this._activeTable) return;
9256
9505
  this._el.style.display = "flex";
9506
+ this._syncShadeStrip();
9257
9507
  requestAnimationFrame(() => {
9258
9508
  if (this._activeTable) this._positionNear(this._activeTable);
9259
9509
  });
9260
9510
  }
9511
+ _syncShadeStrip() {
9512
+ if (!this._shadeColorStrip || !this._el || this._el.style.display === "none") return;
9513
+ const cell = this._getCell();
9514
+ this._shadeColorStrip.style.background = cell?.style.backgroundColor || "transparent";
9515
+ }
9261
9516
  _hide() {
9262
9517
  this._el.style.display = "none";
9263
9518
  this._activeTable = null;
@@ -9286,20 +9541,20 @@ var TableTooltip = class {
9286
9541
  let left = rect.left + (rect.width - tipW) / 2;
9287
9542
  let top = rect.top - tipH - margin;
9288
9543
  if (top < margin) top = rect.bottom + margin;
9289
- if (left + tipW > window.innerWidth - margin) left = window.innerWidth - tipW - margin;
9544
+ if (left + tipW > globalThis.innerWidth - margin) left = globalThis.innerWidth - tipW - margin;
9290
9545
  if (left < margin) left = margin;
9291
9546
  this._el.style.left = `${left}px`;
9292
9547
  this._el.style.top = `${top}px`;
9293
9548
  }
9294
9549
  _getCell() {
9295
- const sel = window.getSelection();
9550
+ const sel = globalThis.getSelection();
9296
9551
  if (sel && sel.rangeCount) {
9297
9552
  let container = sel.getRangeAt(0).commonAncestorContainer;
9298
9553
  if (container.nodeType === 3) container = container.parentElement;
9299
- const cellFromSel = container && container.closest && container.closest("td, th");
9300
- if (cellFromSel && this._activeTable && this._activeTable.contains(cellFromSel)) return cellFromSel;
9554
+ const cellFromSel = container?.closest("td, th");
9555
+ if (cellFromSel && this._activeTable?.contains(cellFromSel)) return cellFromSel;
9301
9556
  }
9302
- return this._activeCell || this._activeTable && this._activeTable.querySelector("td, th");
9557
+ return this._activeCell || this._activeTable?.querySelector("td, th");
9303
9558
  }
9304
9559
  _toggleSelectMode() {
9305
9560
  this._selectMode = !this._selectMode;
@@ -9398,11 +9653,12 @@ var TableTooltip = class {
9398
9653
  const table = cells[0].closest("table");
9399
9654
  if (!table) return;
9400
9655
  const allRows = Array.from(table.querySelectorAll("tr"));
9401
- const refRow = [...new Set(cells.map((c) => c.closest("tr")).filter(Boolean))].reduce((best, r) => {
9656
+ const selectedRows = [...new Set(cells.map((c) => c.closest("tr")).filter(Boolean))];
9657
+ const refRow = selectedRows.reduce((best, r) => {
9402
9658
  const bi = allRows.indexOf(best);
9403
9659
  const ri = allRows.indexOf(r);
9404
9660
  return position === "above" ? ri < bi ? r : best : ri > bi ? r : best;
9405
- });
9661
+ }, selectedRows[0]);
9406
9662
  const colCount = Array.from(refRow.cells).reduce((sum, c) => sum + (c.colSpan || 1), 0);
9407
9663
  const newRow = document.createElement("tr");
9408
9664
  const refCells = Array.from(refRow.cells);
@@ -9445,7 +9701,7 @@ var TableTooltip = class {
9445
9701
  if (selectedRows.filter((r) => r.closest("tbody")).length >= totalBodyRows) return;
9446
9702
  this._activeCell = null;
9447
9703
  this._clearSelection();
9448
- selectedRows.forEach((r) => r.parentElement?.removeChild(r));
9704
+ selectedRows.forEach((r) => r.remove());
9449
9705
  requestAnimationFrame(() => this._positionNear(this._activeTable));
9450
9706
  this.context.invoke("editor.afterCommand");
9451
9707
  }
@@ -9467,7 +9723,7 @@ var TableTooltip = class {
9467
9723
  });
9468
9724
  this._activeCell = null;
9469
9725
  this._clearSelection();
9470
- cellsToDelete.forEach((c) => c.parentElement?.removeChild(c));
9726
+ cellsToDelete.forEach((c) => c.remove());
9471
9727
  requestAnimationFrame(() => this._positionNear(this._activeTable));
9472
9728
  this.context.invoke("editor.afterCommand");
9473
9729
  }
@@ -9478,7 +9734,7 @@ var TableTooltip = class {
9478
9734
  if (!table) return;
9479
9735
  let selected = this._getSelectedCells().filter((c) => table.contains(c));
9480
9736
  if (selected.length < 2) {
9481
- const sel = window.getSelection();
9737
+ const sel = globalThis.getSelection();
9482
9738
  if (!sel || sel.rangeCount === 0) return;
9483
9739
  const range = sel.getRangeAt(0);
9484
9740
  selected = Array.from(table.querySelectorAll("td, th")).filter((c) => {
@@ -9520,7 +9776,7 @@ var TableTooltip = class {
9520
9776
  first.rowSpan = maxR - minR + 1;
9521
9777
  first.style.verticalAlign = "middle";
9522
9778
  first.innerHTML = rectCells.map((c) => c.innerHTML).join("");
9523
- rectCells.slice(1).forEach((c) => c.parentElement?.removeChild(c));
9779
+ rectCells.slice(1).forEach((c) => c.remove());
9524
9780
  this._clearSelection();
9525
9781
  this.context.invoke("editor.afterCommand");
9526
9782
  }
@@ -9528,7 +9784,7 @@ var TableTooltip = class {
9528
9784
  const table = this._activeTable;
9529
9785
  if (!table) return;
9530
9786
  this._hide();
9531
- if (table.parentNode) table.parentNode.removeChild(table);
9787
+ if (table.parentNode) table.remove();
9532
9788
  this.context.invoke("editor.afterCommand");
9533
9789
  }
9534
9790
  _unmergeCells() {
@@ -9617,20 +9873,22 @@ var TableTooltip = class {
9617
9873
  this._sizeInputEl = inputEl;
9618
9874
  this._sizeApply = null;
9619
9875
  const d1 = on(applyBtn, "click", () => {
9620
- const val = parseInt(this._sizeInputEl.value, 10);
9876
+ const val = Number.parseInt(this._sizeInputEl.value, 10);
9621
9877
  if (val > 0 && typeof this._sizeApply === "function") this._sizeApply(val);
9622
9878
  this._hideSizePopover();
9623
9879
  });
9624
9880
  const d2 = on(cancelBtn, "click", () => this._hideSizePopover());
9625
9881
  const d3 = on(inputEl, "keydown", (e) => {
9626
- if (e.key === "Enter") {
9882
+ const ke = e;
9883
+ if (ke.key === "Enter") {
9627
9884
  e.preventDefault();
9628
9885
  applyBtn.click();
9629
9886
  }
9630
- if (e.key === "Escape") this._hideSizePopover();
9887
+ if (ke.key === "Escape") this._hideSizePopover();
9631
9888
  });
9632
9889
  const d4 = on(document, "click", (e) => {
9633
- if (this._sizePopover && this._sizePopover.style.display !== "none" && !this._sizePopover.contains(e.target) && !this._el.contains(e.target)) this._hideSizePopover();
9890
+ const et = e.target;
9891
+ if (this._sizePopover && this._sizePopover.style.display !== "none" && !this._sizePopover.contains(et) && !this._el.contains(et)) this._hideSizePopover();
9634
9892
  });
9635
9893
  const d5 = on(popover, "mouseenter", () => this._clearTimers());
9636
9894
  const d6 = on(popover, "mouseleave", () => this._scheduleHide());
@@ -9644,11 +9902,11 @@ var TableTooltip = class {
9644
9902
  const table = cell.closest("table");
9645
9903
  if (!table) return;
9646
9904
  const firstCell = table.querySelector("td, th");
9647
- const currentPx = firstCell ? parseInt(firstCell.style.borderWidth, 10) || parseInt(window.getComputedStyle(firstCell).borderWidth, 10) || 1 : 1;
9905
+ const currentPx = firstCell ? Number.parseInt(firstCell.style.borderWidth, 10) || Number.parseInt(globalThis.getComputedStyle(firstCell).borderWidth, 10) || 1 : 1;
9648
9906
  this._sizeTitleEl.textContent = this.context.locale.tooltips.table.tableBorderWidthPx;
9649
9907
  this._sizeInputEl.min = "0";
9650
9908
  this._sizeInputEl.max = "10";
9651
- this._sizeInputEl.value = currentPx;
9909
+ this._sizeInputEl.value = String(currentPx);
9652
9910
  this._sizeApply = (val) => {
9653
9911
  const cells = Array.from(table.querySelectorAll("td, th"));
9654
9912
  if (val === 0) cells.forEach((c) => {
@@ -9703,8 +9961,8 @@ var TableTooltip = class {
9703
9961
  const ph = this._sizePopover.offsetHeight || 110;
9704
9962
  let left = tipRect.left;
9705
9963
  let top = tipRect.bottom + 6;
9706
- if (left + pw > window.innerWidth - 8) left = window.innerWidth - pw - 8;
9707
- if (top + ph > window.innerHeight - 8) top = tipRect.top - ph - 6;
9964
+ if (left + pw > globalThis.innerWidth - 8) left = globalThis.innerWidth - pw - 8;
9965
+ if (top + ph > globalThis.innerHeight - 8) top = tipRect.top - ph - 6;
9708
9966
  this._sizePopover.style.left = `${left}px`;
9709
9967
  this._sizePopover.style.top = `${top}px`;
9710
9968
  if (this._sizeInputEl) {
@@ -9717,6 +9975,84 @@ var TableTooltip = class {
9717
9975
  if (this._sizePopover) this._sizePopover.style.display = "none";
9718
9976
  this._sizeApply = null;
9719
9977
  }
9978
+ _buildCellShadePopover() {
9979
+ const pop = createElement("div", { class: "an-cell-shade-popover" });
9980
+ pop.style.display = "none";
9981
+ const title = createElement("div", { class: "an-size-popover-title" });
9982
+ pop.appendChild(title);
9983
+ this._shadeTitleEl = title;
9984
+ const palette = createElement("div", { class: "an-context-color-palette" });
9985
+ SHADE_PRESETS.forEach((color) => {
9986
+ const sw = createElement("div", {
9987
+ class: "an-context-color-swatch",
9988
+ title: color
9989
+ });
9990
+ sw.style.background = color;
9991
+ this._disposers.push(on(sw, "click", (e) => {
9992
+ e.stopPropagation();
9993
+ this._applyCellShade(color);
9994
+ }));
9995
+ palette.appendChild(sw);
9996
+ });
9997
+ pop.appendChild(palette);
9998
+ const noShadeRow = createElement("div", { class: "an-context-color-custom" });
9999
+ const noShadeBtn = createElement("button", {
10000
+ type: "button",
10001
+ class: "an-shade-no-color"
10002
+ });
10003
+ this._disposers.push(on(noShadeBtn, "click", () => this._applyCellShade("")));
10004
+ noShadeRow.appendChild(noShadeBtn);
10005
+ pop.appendChild(noShadeRow);
10006
+ this._shadeNoBtn = noShadeBtn;
10007
+ const customRow = createElement("div", { class: "an-context-color-custom" });
10008
+ const colorInput = createElement("input", {
10009
+ type: "color",
10010
+ class: "an-shade-color-input",
10011
+ value: "#ffffff"
10012
+ });
10013
+ const customLabel = createElement("span");
10014
+ customLabel.textContent = "Custom…";
10015
+ this._disposers.push(on(colorInput, "change", () => this._applyCellShade(colorInput.value)));
10016
+ customRow.appendChild(colorInput);
10017
+ customRow.appendChild(customLabel);
10018
+ pop.appendChild(customRow);
10019
+ this._disposers.push(on(pop, "mousedown", (e) => e.preventDefault()), on(pop, "mouseenter", () => this._clearTimers()), on(pop, "mouseleave", () => this._scheduleHide()));
10020
+ this._disposers.push(on(document, "click", (e) => {
10021
+ const et = e.target;
10022
+ if (this._shadePopover && this._shadePopover.style.display !== "none" && !this._shadePopover.contains(et) && !this._el?.contains(et)) this._hideCellShadePopover();
10023
+ }));
10024
+ return pop;
10025
+ }
10026
+ _openCellShadePopover() {
10027
+ if (!this._shadePopover) return;
10028
+ const L = this.context.locale.tooltips.table;
10029
+ if (this._shadeTitleEl) this._shadeTitleEl.textContent = L.cellBackground;
10030
+ if (this._shadeNoBtn) this._shadeNoBtn.textContent = L.noShading;
10031
+ this._shadePopover.style.display = "block";
10032
+ requestAnimationFrame(() => {
10033
+ if (!this._shadePopover || !this._el) return;
10034
+ const pw = this._shadePopover.offsetWidth || 170;
10035
+ const ph = this._shadePopover.offsetHeight || 120;
10036
+ const tipRect = this._el.getBoundingClientRect();
10037
+ let left = tipRect.left;
10038
+ let top = tipRect.bottom + 6;
10039
+ if (left + pw > globalThis.innerWidth - 8) left = globalThis.innerWidth - pw - 8;
10040
+ if (top + ph > globalThis.innerHeight - 8) top = tipRect.top - ph - 6;
10041
+ this._shadePopover.style.left = `${Math.max(8, left)}px`;
10042
+ this._shadePopover.style.top = `${Math.max(8, top)}px`;
10043
+ });
10044
+ }
10045
+ _hideCellShadePopover() {
10046
+ if (this._shadePopover) this._shadePopover.style.display = "none";
10047
+ }
10048
+ _applyCellShade(color) {
10049
+ (this._selectMode ? this._selectedCells : [this._getCell()]).forEach((cell) => {
10050
+ if (cell) cell.style.backgroundColor = color;
10051
+ });
10052
+ if (this._shadeColorStrip) this._shadeColorStrip.style.background = color || "transparent";
10053
+ this._hideCellShadePopover();
10054
+ this.context.invoke("editor.afterCommand");
10055
+ }
9720
10056
  };
9721
10057
  //#endregion
9722
10058
  //#region src/js/module/CodeTooltip.js
@@ -9749,13 +10085,14 @@ var CodeTooltip = class {
9749
10085
  const editable = this.context.layoutInfo.editable;
9750
10086
  this._disposers.push(on(editable, "mouseover", (e) => {
9751
10087
  if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
9752
- const pre = e.target.closest("pre");
10088
+ const pre = e.target?.closest("pre");
9753
10089
  if (pre && editable.contains(pre)) this._scheduleShow(pre);
9754
10090
  }), on(editable, "mouseout", (e) => {
9755
10091
  const to = e.relatedTarget;
9756
10092
  if (!to || !editable.contains(to) && !this._el.contains(to)) this._scheduleHide();
9757
10093
  }), on(document, "click", (e) => {
9758
- if (this._activePre && !this._activePre.contains(e.target) && !this._el.contains(e.target)) this._hide();
10094
+ const et = e.target;
10095
+ if (this._activePre && !this._activePre.contains(et) && !this._el.contains(et)) this._hide();
9759
10096
  }));
9760
10097
  return this;
9761
10098
  }
@@ -9763,7 +10100,7 @@ var CodeTooltip = class {
9763
10100
  this._clearTimers();
9764
10101
  this._disposers.forEach((d) => d());
9765
10102
  this._disposers = [];
9766
- if (this._el && this._el.parentNode) this._el.parentNode.removeChild(this._el);
10103
+ this._el?.remove();
9767
10104
  this._el = null;
9768
10105
  }
9769
10106
  _buildTooltip() {
@@ -9790,6 +10127,7 @@ var CodeTooltip = class {
9790
10127
  ["python", "Python"],
9791
10128
  ["html", "HTML"],
9792
10129
  ["css", "CSS"],
10130
+ ["scss", "SCSS"],
9793
10131
  ["json", "JSON"],
9794
10132
  ["xml", "XML"],
9795
10133
  ["bash", "Bash / Shell"],
@@ -9888,21 +10226,21 @@ var CodeTooltip = class {
9888
10226
  let top = rect.top - tipH - margin;
9889
10227
  let left = rect.left + (rect.width - tipW) / 2;
9890
10228
  if (top < margin) top = rect.bottom + margin;
9891
- if (left + tipW > window.innerWidth - margin) left = window.innerWidth - tipW - margin;
10229
+ if (left + tipW > globalThis.innerWidth - margin) left = globalThis.innerWidth - tipW - margin;
9892
10230
  if (left < margin) left = margin;
9893
10231
  this._el.style.top = `${top}px`;
9894
10232
  this._el.style.left = `${left}px`;
9895
10233
  }
9896
10234
  _syncWrapBtn() {
9897
10235
  if (!this._activePre || !this._wrapBtn) return;
9898
- const wrapped = (this._activePre.style.whiteSpace || "").includes("pre-wrap") || window.getComputedStyle(this._activePre).whiteSpace === "pre-wrap";
10236
+ const wrapped = (this._activePre.style.whiteSpace || "").includes("pre-wrap") || globalThis.getComputedStyle(this._activePre).whiteSpace === "pre-wrap";
9899
10237
  this._wrapBtn.classList.toggle("active", wrapped);
9900
10238
  this._wrapBtn.title = wrapped ? this.context.locale.tooltips.code.disableWordWrap : this.context.locale.tooltips.code.enableWordWrap;
9901
10239
  }
9902
10240
  _syncLangSelect() {
9903
10241
  if (!this._activePre || !this._langSelect) return;
9904
10242
  const codeEl = this._activePre.querySelector("code");
9905
- const fromAttr = this._activePre.getAttribute("data-language") || "";
10243
+ const fromAttr = this._activePre.dataset.language || "";
9906
10244
  const fromClass = codeEl ? (_LANG_CLASS_RE.exec(codeEl.className) || [])[1] || "" : "";
9907
10245
  this._langSelect.value = fromAttr || fromClass || "";
9908
10246
  }
@@ -9921,7 +10259,7 @@ var CodeTooltip = class {
9921
10259
  document.execCommand("copy");
9922
10260
  this._flashCopied();
9923
10261
  } catch (_) {}
9924
- document.body.removeChild(ta);
10262
+ ta.remove();
9925
10263
  }
9926
10264
  }
9927
10265
  _flashCopied() {
@@ -9945,10 +10283,26 @@ var CodeTooltip = class {
9945
10283
  this.context.invoke("editor.afterCommand");
9946
10284
  this._positionNear(pre);
9947
10285
  }
10286
+ /**
10287
+ * Applies a language to a given <pre> element: sets classes, data-language,
10288
+ * and triggers Prism highlighting. Called by the auto-detect flow.
10289
+ * @param {HTMLElement} pre
10290
+ * @param {string} lang - Prism language identifier, e.g. 'javascript'
10291
+ */
10292
+ applyLanguage(pre, lang) {
10293
+ if (!pre || !lang) return;
10294
+ const savedPre = this._activePre;
10295
+ this._activePre = pre;
10296
+ if (this._langSelect) this._langSelect.value = lang;
10297
+ this._onLangChange();
10298
+ if (this._langSelect) this._langSelect.value = lang;
10299
+ this._activePre = savedPre || pre;
10300
+ }
9948
10301
  _onLangChange() {
9949
10302
  const pre = this._activePre;
9950
10303
  if (!pre) return;
9951
10304
  const lang = this._langSelect.value;
10305
+ const _w = globalThis;
9952
10306
  let codeEl = pre.querySelector("code");
9953
10307
  if (!codeEl) {
9954
10308
  codeEl = document.createElement("code");
@@ -9958,16 +10312,16 @@ var CodeTooltip = class {
9958
10312
  }
9959
10313
  codeEl.className = lang ? `language-${lang}` : "";
9960
10314
  pre.className = lang ? `language-${lang}` : "";
9961
- if (lang) pre.setAttribute("data-language", lang);
9962
- else pre.removeAttribute("data-language");
10315
+ if (lang) pre.dataset.language = lang;
10316
+ else delete pre.dataset.language;
9963
10317
  const applyPrism = () => {
9964
10318
  codeEl.querySelectorAll("br").forEach((br) => br.replaceWith("\n"));
9965
- window.Prism.highlightElement(codeEl);
10319
+ _w.Prism.highlightElement(codeEl);
9966
10320
  this.context.invoke("editor.afterCommand");
9967
10321
  };
9968
10322
  if (lang) {
9969
- if (typeof window.Prism !== "undefined") {
9970
- if (window.Prism.languages[lang]) {
10323
+ if (_w.Prism !== void 0) {
10324
+ if (_w.Prism.languages[lang]) {
9971
10325
  applyPrism();
9972
10326
  return;
9973
10327
  }
@@ -9975,7 +10329,7 @@ var CodeTooltip = class {
9975
10329
  return;
9976
10330
  } else if (this._prismScript) {
9977
10331
  this._prismScript.addEventListener("load", () => {
9978
- if (window.Prism.languages[lang]) applyPrism();
10332
+ if (_w.Prism.languages[lang]) applyPrism();
9979
10333
  else this._loadPrismComponent(lang, applyPrism);
9980
10334
  }, { once: true });
9981
10335
  return;
@@ -9988,7 +10342,8 @@ var CodeTooltip = class {
9988
10342
  * Called once at initialize time. Fire-and-forget; errors are silent.
9989
10343
  */
9990
10344
  _ensurePrism() {
9991
- if (!this.context.options.codeHighlight || window.Prism) return;
10345
+ const _w = globalThis;
10346
+ if (!this.context.options.codeHighlight || _w.Prism) return;
9992
10347
  const cdn = this.context.options.codeHighlightCDN;
9993
10348
  const themeHref = `${cdn}/themes/prism-tomorrow.min.css`;
9994
10349
  const scriptSrc = `${cdn}/prism.min.js`;
@@ -10000,7 +10355,7 @@ var CodeTooltip = class {
10000
10355
  }
10001
10356
  const existingScript = document.querySelector(`script[src="${scriptSrc}"]`);
10002
10357
  if (existingScript) {
10003
- this._prismScript = window.Prism ? null : existingScript;
10358
+ this._prismScript = _w.Prism ? null : existingScript;
10004
10359
  return;
10005
10360
  }
10006
10361
  const script = document.createElement("script");
@@ -10020,10 +10375,11 @@ var CodeTooltip = class {
10020
10375
  * @param {Function} cb – called once the grammar is ready
10021
10376
  */
10022
10377
  _loadPrismComponent(lang, cb) {
10378
+ const _w = globalThis;
10023
10379
  const src = `${this.context.options.codeHighlightCDN}/components/prism-${lang}.min.js`;
10024
10380
  if (document.querySelector(`script[src="${src}"]`)) {
10025
10381
  const poll = setInterval(() => {
10026
- if (window.Prism && window.Prism.languages[lang]) {
10382
+ if (_w.Prism?.languages[lang]) {
10027
10383
  clearInterval(poll);
10028
10384
  cb();
10029
10385
  }
@@ -10054,7 +10410,7 @@ var CodeTooltip = class {
10054
10410
  const pre = this._activePre;
10055
10411
  if (!pre) return;
10056
10412
  this._hide();
10057
- if (pre.parentNode) pre.parentNode.removeChild(pre);
10413
+ pre.remove();
10058
10414
  this.context.invoke("editor.afterCommand");
10059
10415
  }
10060
10416
  };
@@ -12409,7 +12765,7 @@ var EmojiDialog = class {
12409
12765
  destroy() {
12410
12766
  this._disposers.forEach((d) => d());
12411
12767
  this._disposers = [];
12412
- if (this._dialog && this._dialog.parentNode) this._dialog.parentNode.removeChild(this._dialog);
12768
+ if (this._dialog && this._dialog.parentNode) this._dialog.remove();
12413
12769
  this._dialog = null;
12414
12770
  }
12415
12771
  show() {
@@ -12436,15 +12792,19 @@ var EmojiDialog = class {
12436
12792
  });
12437
12793
  const box = createElement("div", { class: "an-dialog-box an-emoji-box" });
12438
12794
  const titleRow = createElement("div", { class: "an-icon-title-row" });
12795
+ const titleGroup = createElement("div", { class: "an-dialog-title-group" });
12796
+ const iconEl = createElement("span", { class: "an-dialog-icon an-dialog-icon--sm" });
12797
+ 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>`;
12439
12798
  const title = createElement("h3", { class: "an-dialog-title" });
12440
12799
  title.textContent = L.title;
12800
+ titleGroup.append(iconEl, title);
12441
12801
  const closeBtn = createElement("button", {
12442
12802
  type: "button",
12443
12803
  class: "an-icon-close",
12444
12804
  "aria-label": L.close
12445
12805
  });
12446
12806
  closeBtn.innerHTML = "&times;";
12447
- titleRow.append(title, closeBtn);
12807
+ titleRow.append(titleGroup, closeBtn);
12448
12808
  const searchInput = createElement("input", {
12449
12809
  type: "search",
12450
12810
  class: "an-input an-icon-search",
@@ -12466,7 +12826,7 @@ var EmojiDialog = class {
12466
12826
  class: "an-icon-cat",
12467
12827
  "data-cat": id
12468
12828
  });
12469
- tab.textContent = L.categories && L.categories[id] || label;
12829
+ tab.textContent = L.categories?.[id] || label;
12470
12830
  catBar.appendChild(tab);
12471
12831
  });
12472
12832
  this._catBar = catBar;
@@ -12493,6 +12853,7 @@ var EmojiDialog = class {
12493
12853
  btnRow.appendChild(cancelBtn);
12494
12854
  box.append(titleRow, searchInput, catBar, grid, btnRow);
12495
12855
  overlay.appendChild(box);
12856
+ makeDraggable(titleRow, box);
12496
12857
  const d1 = on(closeBtn, "click", () => this._close());
12497
12858
  const d2 = on(cancelBtn, "click", () => this._close());
12498
12859
  const d3 = on(overlay, "click", (e) => {
@@ -12500,7 +12861,7 @@ var EmojiDialog = class {
12500
12861
  });
12501
12862
  const d4 = on(searchInput, "input", () => this._filterEmojis(searchInput.value, this._activeCat));
12502
12863
  const d5 = on(catBar, "click", (e) => {
12503
- const tab = e.target.closest("[data-cat]");
12864
+ const tab = e.target?.closest("[data-cat]");
12504
12865
  if (tab) {
12505
12866
  this._activeCat = tab.dataset.cat;
12506
12867
  this._updateCatTabs();
@@ -12508,7 +12869,7 @@ var EmojiDialog = class {
12508
12869
  }
12509
12870
  });
12510
12871
  const d6 = on(grid, "click", (e) => {
12511
- const cell = e.target.closest(".an-emoji-cell");
12872
+ const cell = e.target?.closest(".an-emoji-cell");
12512
12873
  if (cell) this._onEmojiClick(cell.dataset.char);
12513
12874
  });
12514
12875
  this._disposers.push(d1, d2, d3, d4, d5, d6);
@@ -12516,17 +12877,22 @@ var EmojiDialog = class {
12516
12877
  }
12517
12878
  _updateCatTabs() {
12518
12879
  this._catBar.querySelectorAll(".an-icon-cat").forEach((tab) => {
12519
- tab.classList.toggle("active", tab.dataset.cat === this._activeCat);
12880
+ tab.classList.toggle(
12881
+ "active",
12882
+ /** @type {HTMLElement} */
12883
+ tab.dataset.cat === this._activeCat
12884
+ );
12520
12885
  });
12521
12886
  }
12522
12887
  _filterEmojis(query, cat) {
12523
12888
  const q = (query || "").trim().toLowerCase();
12524
12889
  let count = 0;
12525
12890
  this._grid.querySelectorAll(".an-emoji-cell").forEach((cell) => {
12526
- const matchCat = !cat || cat === "all" || cell.dataset.cat === cat;
12527
- const matchQuery = !q || cell.dataset.keywords.includes(q) || cell.dataset.char === q;
12891
+ const hCell = cell;
12892
+ const matchCat = !cat || cat === "all" || hCell.dataset.cat === cat;
12893
+ const matchQuery = !q || hCell.dataset.keywords.includes(q) || hCell.dataset.char === q;
12528
12894
  const visible = matchCat && matchQuery;
12529
- cell.style.display = visible ? "" : "none";
12895
+ hCell.style.display = visible ? "" : "none";
12530
12896
  if (visible) count++;
12531
12897
  });
12532
12898
  let empty = this._grid.querySelector(".an-icon-empty");
@@ -12535,13 +12901,13 @@ var EmojiDialog = class {
12535
12901
  empty.textContent = "No emojis found";
12536
12902
  this._grid.appendChild(empty);
12537
12903
  }
12538
- empty.style.display = count > 0 ? "none" : "";
12904
+ /** @type {HTMLElement} */ empty.style.display = count > 0 ? "none" : "";
12539
12905
  }
12540
12906
  _onEmojiClick(char) {
12541
12907
  const savedRange = this._savedRange;
12542
12908
  const editable = this.context.layoutInfo.editable;
12543
12909
  if (savedRange) savedRange.select();
12544
- const sel = window.getSelection();
12910
+ const sel = globalThis.getSelection();
12545
12911
  let range = sel && sel.rangeCount > 0 ? sel.getRangeAt(0) : null;
12546
12912
  if (!range) {
12547
12913
  range = document.createRange();
@@ -12549,9 +12915,9 @@ var EmojiDialog = class {
12549
12915
  range.collapse(false);
12550
12916
  }
12551
12917
  const _sc = range.startContainer;
12552
- const _tdAnchor = (_sc.nodeType === 1 ? _sc : _sc.parentElement)?.closest?.("td, th");
12918
+ const _tdAnchor = (_sc.nodeType === 1 ? _sc : _sc.parentElement)?.closest("td, th");
12553
12919
  range.deleteContents();
12554
- if (_tdAnchor && _tdAnchor.isConnected && !_tdAnchor.contains(range.startContainer)) {
12920
+ if (_tdAnchor?.isConnected && !_tdAnchor.contains(range.startContainer)) {
12555
12921
  range.setStart(_tdAnchor, 0);
12556
12922
  range.collapse(true);
12557
12923
  }
@@ -12571,7 +12937,7 @@ var EmojiDialog = class {
12571
12937
  if (this._dialog) {
12572
12938
  this._dialog.style.display = "flex";
12573
12939
  this._removeTrap = trapFocus(this._dialog, () => this._close());
12574
- setTimeout(() => this._searchInput && this._searchInput.focus(), 50);
12940
+ setTimeout(() => this._searchInput?.focus(), 50);
12575
12941
  }
12576
12942
  }
12577
12943
  _close() {
@@ -12878,7 +13244,7 @@ var IconDialog = class {
12878
13244
  destroy() {
12879
13245
  this._disposers.forEach((d) => d());
12880
13246
  this._disposers = [];
12881
- if (this._dialog && this._dialog.parentNode) this._dialog.parentNode.removeChild(this._dialog);
13247
+ this._dialog?.remove();
12882
13248
  this._dialog = null;
12883
13249
  }
12884
13250
  show() {
@@ -12909,15 +13275,19 @@ var IconDialog = class {
12909
13275
  });
12910
13276
  const box = createElement("div", { class: "an-dialog-box an-icon-box" });
12911
13277
  const titleRow = createElement("div", { class: "an-icon-title-row" });
13278
+ const titleGroup = createElement("div", { class: "an-dialog-title-group" });
13279
+ const iconEl = createElement("span", { class: "an-dialog-icon an-dialog-icon--sm" });
13280
+ 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>`;
12912
13281
  const title = createElement("h3", { class: "an-dialog-title" });
12913
13282
  title.textContent = L.title;
13283
+ titleGroup.append(iconEl, title);
12914
13284
  const closeBtn = createElement("button", {
12915
13285
  type: "button",
12916
13286
  class: "an-icon-close",
12917
13287
  "aria-label": L.close
12918
13288
  });
12919
13289
  closeBtn.innerHTML = "&times;";
12920
- titleRow.append(title, closeBtn);
13290
+ titleRow.append(titleGroup, closeBtn);
12921
13291
  const searchInput = createElement("input", {
12922
13292
  type: "search",
12923
13293
  class: "an-input an-icon-search",
@@ -12939,7 +13309,7 @@ var IconDialog = class {
12939
13309
  class: "an-icon-cat",
12940
13310
  "data-cat": id
12941
13311
  });
12942
- tab.textContent = L.categories && L.categories[id] || label;
13312
+ tab.textContent = L.categories?.[id] || label;
12943
13313
  catBar.appendChild(tab);
12944
13314
  });
12945
13315
  this._catBar = catBar;
@@ -13032,6 +13402,7 @@ var IconDialog = class {
13032
13402
  this._insertBtn = insertBtn;
13033
13403
  box.append(titleRow, searchInput, catBar, grid, optRow, preview, btnRow);
13034
13404
  overlay.appendChild(box);
13405
+ makeDraggable(titleRow, box);
13035
13406
  const d1 = on(closeBtn, "click", () => this._close());
13036
13407
  const d2 = on(cancelBtn, "click", () => this._close());
13037
13408
  const d3 = on(insertBtn, "click", () => this._onInsert());
@@ -13040,7 +13411,7 @@ var IconDialog = class {
13040
13411
  });
13041
13412
  const d5 = on(searchInput, "input", () => this._filterIcons(searchInput.value, this._activeCat));
13042
13413
  const d6 = on(catBar, "click", (e) => {
13043
- const tab = e.target.closest("[data-cat]");
13414
+ const tab = e.target?.closest("[data-cat]");
13044
13415
  if (tab) {
13045
13416
  this._activeCat = tab.dataset.cat;
13046
13417
  this._updateCatTabs();
@@ -13048,7 +13419,7 @@ var IconDialog = class {
13048
13419
  }
13049
13420
  });
13050
13421
  const d7 = on(grid, "click", (e) => {
13051
- const cell = e.target.closest(".an-icon-cell");
13422
+ const cell = e.target?.closest(".an-icon-cell");
13052
13423
  if (cell) this._selectIcon(cell.dataset.name);
13053
13424
  });
13054
13425
  const d8 = on(styleSelect, "change", () => this._updatePreview(this._selectedIcon));
@@ -13060,19 +13431,24 @@ var IconDialog = class {
13060
13431
  }
13061
13432
  _updateCatTabs() {
13062
13433
  this._catBar.querySelectorAll(".an-icon-cat").forEach((tab) => {
13063
- tab.classList.toggle("active", tab.dataset.cat === this._activeCat);
13434
+ tab.classList.toggle(
13435
+ "active",
13436
+ /** @type {HTMLElement} */
13437
+ tab.dataset.cat === this._activeCat
13438
+ );
13064
13439
  });
13065
13440
  }
13066
13441
  _filterIcons(query, cat) {
13067
13442
  const q = (query || "").trim().toLowerCase();
13068
13443
  let visibleCount = 0;
13069
13444
  this._grid.querySelectorAll(".an-icon-cell").forEach((cell) => {
13070
- const name = cell.dataset.name;
13071
- const cellCat = cell.dataset.cat;
13445
+ const hCell = cell;
13446
+ const name = hCell.dataset.name;
13447
+ const cellCat = hCell.dataset.cat;
13072
13448
  const matchesCat = !cat || cat === "all" || cellCat === cat;
13073
13449
  const matchesQuery = !q || name.includes(q);
13074
13450
  const visible = matchesCat && matchesQuery;
13075
- cell.style.display = visible ? "" : "none";
13451
+ hCell.style.display = visible ? "" : "none";
13076
13452
  if (visible) visibleCount++;
13077
13453
  });
13078
13454
  let empty = this._grid.querySelector(".an-icon-empty");
@@ -13081,12 +13457,16 @@ var IconDialog = class {
13081
13457
  empty.textContent = "No icons found";
13082
13458
  this._grid.appendChild(empty);
13083
13459
  }
13084
- empty.style.display = visibleCount > 0 ? "none" : "";
13460
+ /** @type {HTMLElement} */ empty.style.display = visibleCount > 0 ? "none" : "";
13085
13461
  }
13086
13462
  _selectIcon(name) {
13087
13463
  this._selectedIcon = name;
13088
13464
  this._grid.querySelectorAll(".an-icon-cell").forEach((cell) => {
13089
- cell.classList.toggle("active", cell.dataset.name === name);
13465
+ cell.classList.toggle(
13466
+ "active",
13467
+ /** @type {HTMLElement} */
13468
+ cell.dataset.name === name
13469
+ );
13090
13470
  });
13091
13471
  this._insertBtn.removeAttribute("disabled");
13092
13472
  this._updatePreview(name);
@@ -13097,17 +13477,17 @@ var IconDialog = class {
13097
13477
  this._preview.innerHTML = "<span class=\"an-icon-preview-hint\">Select an icon</span>";
13098
13478
  return;
13099
13479
  }
13100
- const cls = this._styleSelect && this._styleSelect.value || "fa-solid";
13101
- const size = this._sizeSelect && this._sizeSelect.value || "1em";
13102
- const color = (this._useColorCb ? this._useColorCb.checked : false) && this._colorInput ? this._colorInput.value : "";
13480
+ const cls = this._styleSelect?.value || "fa-solid";
13481
+ const size = this._sizeSelect?.value || "1em";
13482
+ const color = this._useColorCb?.checked ?? false ? this._colorInput?.value ?? "" : "";
13103
13483
  const styleAttr = [size ? `font-size:${size}` : "", color ? `color:${color}` : ""].filter(Boolean).join(";");
13104
13484
  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>`;
13105
13485
  }
13106
13486
  _onInsert() {
13107
13487
  if (!this._selectedIcon) return;
13108
- const cls = this._styleSelect && this._styleSelect.value || "fa-solid";
13109
- const size = this._sizeSelect && this._sizeSelect.value || "";
13110
- const color = (this._useColorCb ? this._useColorCb.checked : false) && this._colorInput ? this._colorInput.value : "";
13488
+ const cls = this._styleSelect?.value || "fa-solid";
13489
+ const size = this._sizeSelect?.value || "";
13490
+ const color = this._useColorCb?.checked ?? false ? this._colorInput?.value ?? "" : "";
13111
13491
  const styleParts = [size ? `font-size:${size}` : "", color ? `color:${color}` : ""].filter(Boolean);
13112
13492
  const iconEl = document.createElement("i");
13113
13493
  iconEl.className = `${cls} fa-${this._selectedIcon}`;
@@ -13117,15 +13497,15 @@ var IconDialog = class {
13117
13497
  const savedRange = this._savedRange;
13118
13498
  const editable = this.context.layoutInfo.editable;
13119
13499
  if (savedRange) savedRange.select();
13120
- const sel = window.getSelection();
13121
- let range = sel && sel.rangeCount > 0 ? sel.getRangeAt(0) : null;
13500
+ const sel = globalThis.getSelection();
13501
+ let range = (sel?.rangeCount ?? 0) > 0 ? sel.getRangeAt(0) : null;
13122
13502
  if (!range) {
13123
13503
  range = document.createRange();
13124
13504
  range.selectNodeContents(editable);
13125
13505
  range.collapse(false);
13126
13506
  }
13127
13507
  const _sc = range.startContainer;
13128
- const _tdAnchor = (_sc.nodeType === 1 ? _sc : _sc.parentElement)?.closest?.("td, th");
13508
+ const _tdAnchor = (_sc.nodeType === 1 ? _sc : _sc.parentElement)?.closest("td, th");
13129
13509
  range.deleteContents();
13130
13510
  if (_tdAnchor && _tdAnchor.isConnected && !_tdAnchor.contains(range.startContainer)) {
13131
13511
  range.setStart(_tdAnchor, 0);
@@ -13157,7 +13537,7 @@ var IconDialog = class {
13157
13537
  if (this._dialog) {
13158
13538
  this._dialog.style.display = "flex";
13159
13539
  this._removeTrap = trapFocus(this._dialog, () => this._close());
13160
- setTimeout(() => this._searchInput && this._searchInput.focus(), 50);
13540
+ setTimeout(() => this._searchInput?.focus(), 50);
13161
13541
  }
13162
13542
  }
13163
13543
  _close() {
@@ -13258,7 +13638,7 @@ var defaultItems = [
13258
13638
  icon: ICONS.paste,
13259
13639
  action: (ctx) => {
13260
13640
  if (!navigator.clipboard) return;
13261
- const editable = ctx.layoutInfo && ctx.layoutInfo.editable;
13641
+ const editable = ctx.layoutInfo?.editable;
13262
13642
  if (!editable) return;
13263
13643
  const doInsert = (html, text) => {
13264
13644
  editable.focus();
@@ -13383,29 +13763,29 @@ var ContextMenu = class {
13383
13763
  this.el.style.display = "none";
13384
13764
  document.body.appendChild(this.el);
13385
13765
  this._renderItems(this._items);
13386
- const editable = this.context.layoutInfo && this.context.layoutInfo.editable;
13766
+ const editable = this.context.layoutInfo?.editable;
13387
13767
  if (editable) this._disposers.push(on(editable, "contextmenu", (e) => this._onContextMenu(e)));
13388
13768
  this._disposers.push(on(document, "click", (e) => this._maybeHide(e)));
13389
13769
  this._disposers.push(on(document, "keydown", (e) => {
13390
13770
  if (e.key === "Escape") this.hide();
13391
13771
  }));
13392
- this._disposers.push(on(window, "scroll", () => this.hide(), { passive: true }));
13772
+ this._disposers.push(on(globalThis, "scroll", () => this.hide(), { passive: true }));
13393
13773
  return this;
13394
13774
  }
13395
13775
  destroy() {
13396
13776
  this._menuDisposers.forEach((d) => {
13397
13777
  try {
13398
13778
  d();
13399
- } catch (e) {}
13779
+ } catch (_e) {}
13400
13780
  });
13401
13781
  this._menuDisposers = [];
13402
13782
  this._disposers.forEach((d) => {
13403
13783
  try {
13404
13784
  d();
13405
- } catch (e) {}
13785
+ } catch (_e) {}
13406
13786
  });
13407
13787
  this._disposers = [];
13408
- if (this.el && this.el.parentNode) this.el.parentNode.removeChild(this.el);
13788
+ if (this.el) this.el.remove();
13409
13789
  this.el = null;
13410
13790
  }
13411
13791
  _renderItems(items) {
@@ -13433,8 +13813,8 @@ var ContextMenu = class {
13433
13813
  backBtn.appendChild(createElement("span", { class: "an-context-label" }, [backLabel]));
13434
13814
  const off = on(backBtn, "click", (e) => {
13435
13815
  e.stopPropagation();
13436
- const curLeft = parseFloat(this.el.style.left);
13437
- const curTop = parseFloat(this.el.style.top);
13816
+ const curLeft = Number.parseFloat(this.el.style.left);
13817
+ const curTop = Number.parseFloat(this.el.style.top);
13438
13818
  this._renderItems(it.navigate());
13439
13819
  this._reposition(curLeft, curTop);
13440
13820
  });
@@ -13477,8 +13857,8 @@ var ContextMenu = class {
13477
13857
  btn.appendChild(chevron);
13478
13858
  const off = on(btn, "click", (e) => {
13479
13859
  e.stopPropagation();
13480
- const curLeft = parseFloat(this.el.style.left);
13481
- const curTop = parseFloat(this.el.style.top);
13860
+ const curLeft = Number.parseFloat(this.el.style.left);
13861
+ const curTop = Number.parseFloat(this.el.style.top);
13482
13862
  this._renderItems(it.navigate());
13483
13863
  this._reposition(curLeft, curTop);
13484
13864
  });
@@ -13593,20 +13973,20 @@ var ContextMenu = class {
13593
13973
  });
13594
13974
  this._menuDisposers.push(offHeader);
13595
13975
  const offMove = on(gridEl, "mousemove", (e) => {
13596
- const cell = e.target.closest("[data-row]");
13976
+ const cell = e.target?.closest("[data-row]");
13597
13977
  if (!cell) return;
13598
13978
  setHighlight(+cell.dataset.row, +cell.dataset.col);
13599
13979
  });
13600
13980
  const offLeave = on(gridEl, "mouseleave", () => setHighlight(0, 0));
13601
13981
  const offClick = on(gridEl, "click", (e) => {
13602
- const cell = e.target.closest("[data-row]");
13982
+ const cell = e.target?.closest("[data-row]");
13603
13983
  if (!cell) return;
13604
13984
  const rows = +cell.dataset.row;
13605
13985
  const cols = +cell.dataset.col;
13606
- const editable = this.context.layoutInfo && this.context.layoutInfo.editable;
13986
+ const editable = this.context.layoutInfo?.editable;
13607
13987
  if (editable && this._savedRange) {
13608
13988
  editable.focus();
13609
- const sel = window.getSelection();
13989
+ const sel = globalThis.getSelection();
13610
13990
  sel.removeAllRanges();
13611
13991
  sel.addRange(this._savedRange.cloneRange());
13612
13992
  }
@@ -13624,7 +14004,7 @@ var ContextMenu = class {
13624
14004
  class: "an-context-item",
13625
14005
  "data-name": it.name || ""
13626
14006
  });
13627
- if (typeof it.disabled === "function" ? it.disabled(this.context) : !!it.disabled) btn.disabled = true;
14007
+ if (typeof it.disabled === "function" ? it.disabled(this.context) : !!it.disabled) /** @type {HTMLButtonElement} */ btn.disabled = true;
13628
14008
  if (it.icon) {
13629
14009
  const iconSpan = createElement("span", {
13630
14010
  class: "an-context-icon",
@@ -13648,15 +14028,15 @@ var ContextMenu = class {
13648
14028
  });
13649
14029
  }
13650
14030
  _onContextMenu(event) {
13651
- const editable = this.context.layoutInfo && this.context.layoutInfo.editable;
14031
+ const editable = this.context.layoutInfo?.editable;
13652
14032
  if (!editable) return;
13653
14033
  if (!editable.contains(event.target)) return;
13654
14034
  if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
13655
14035
  event.preventDefault();
13656
- const winSel = window.getSelection();
14036
+ const winSel = globalThis.getSelection();
13657
14037
  this._savedRange = winSel && winSel.rangeCount > 0 ? winSel.getRangeAt(0).cloneRange() : null;
13658
14038
  this._renderItems(this._items);
13659
- let openX = event.clientX;
14039
+ const openX = event.clientX;
13660
14040
  let openY = event.clientY;
13661
14041
  if (this._savedRange && !this._savedRange.collapsed) try {
13662
14042
  const selRect = this._savedRange.getBoundingClientRect();
@@ -13685,9 +14065,9 @@ var ContextMenu = class {
13685
14065
  const h = this.el.offsetHeight;
13686
14066
  let left = rx;
13687
14067
  let top = ry;
13688
- if (left + w > window.innerWidth - 8) left = window.innerWidth - w - 8;
14068
+ if (left + w > globalThis.innerWidth - 8) left = globalThis.innerWidth - w - 8;
13689
14069
  if (left < 8) left = 8;
13690
- if (top + h > window.innerHeight - 8) top = window.innerHeight - h - 8;
14070
+ if (top + h > globalThis.innerHeight - 8) top = globalThis.innerHeight - h - 8;
13691
14071
  if (top < 8) top = 8;
13692
14072
  this.el.style.left = `${left}px`;
13693
14073
  this.el.style.top = `${top}px`;
@@ -13708,17 +14088,17 @@ var ContextMenu = class {
13708
14088
  let node = range.startContainer;
13709
14089
  if (node.nodeType === Node.TEXT_NODE) node = node.parentElement;
13710
14090
  if (!node) return type === "foreColor" ? "#000000" : "transparent";
13711
- const cs = window.getComputedStyle(node);
14091
+ const cs = globalThis.getComputedStyle(node);
13712
14092
  if (type === "foreColor") return cs.color || "#000000";
13713
14093
  const bg = cs.backgroundColor;
13714
14094
  return !bg || bg === "rgba(0, 0, 0, 0)" || bg === "transparent" ? "transparent" : bg;
13715
14095
  }
13716
14096
  /** Restore selection, apply a color command, then hide the menu. */
13717
14097
  _applyColor(type, color) {
13718
- const editable = this.context.layoutInfo && this.context.layoutInfo.editable;
14098
+ const editable = this.context.layoutInfo?.editable;
13719
14099
  if (!editable || !this._savedRange) return;
13720
14100
  editable.focus();
13721
- const sel = window.getSelection();
14101
+ const sel = globalThis.getSelection();
13722
14102
  sel.removeAllRanges();
13723
14103
  sel.addRange(this._savedRange.cloneRange());
13724
14104
  document.execCommand(type, false, color);
@@ -13733,15 +14113,15 @@ var ContextMenu = class {
13733
14113
  copyFormat() {
13734
14114
  const range = this._savedRange;
13735
14115
  if (!range) return;
13736
- const editable = this.context.layoutInfo && this.context.layoutInfo.editable;
14116
+ const editable = this.context.layoutInfo?.editable;
13737
14117
  let node = range.startContainer;
13738
14118
  if (node.nodeType === Node.TEXT_NODE) node = node.parentElement;
13739
14119
  if (!node || !editable || !editable.contains(node)) return;
13740
- const cs = window.getComputedStyle(node);
14120
+ const cs = globalThis.getComputedStyle(node);
13741
14121
  const explicitFontFamily = this._findExplicitStyle(node, editable, "fontFamily");
13742
14122
  const explicitFontSize = this._findExplicitStyle(node, editable, "fontSize");
13743
14123
  this._copiedFormat = {
13744
- bold: parseInt(cs.fontWeight, 10) >= 700,
14124
+ bold: Number.parseInt(cs.fontWeight, 10) >= 700,
13745
14125
  italic: cs.fontStyle === "italic" || cs.fontStyle === "oblique",
13746
14126
  underline: (cs.textDecorationLine || "").includes("underline"),
13747
14127
  strikethrough: (cs.textDecorationLine || "").includes("line-through"),
@@ -13772,10 +14152,10 @@ var ContextMenu = class {
13772
14152
  pasteFormat() {
13773
14153
  if (!this._copiedFormat || !this._savedRange) return;
13774
14154
  const fmt = this._copiedFormat;
13775
- const editable = this.context.layoutInfo && this.context.layoutInfo.editable;
14155
+ const editable = this.context.layoutInfo?.editable;
13776
14156
  if (!editable) return;
13777
14157
  editable.focus();
13778
- const sel = window.getSelection();
14158
+ const sel = globalThis.getSelection();
13779
14159
  sel.removeAllRanges();
13780
14160
  sel.addRange(this._savedRange.cloneRange());
13781
14161
  document.execCommand("removeFormat");
@@ -13792,14 +14172,14 @@ var ContextMenu = class {
13792
14172
  const preExisting = new Set(editable.querySelectorAll("font[size=\"7\"]"));
13793
14173
  document.execCommand("fontSize", false, "7");
13794
14174
  editable.querySelectorAll("font[size=\"7\"]").forEach((el) => {
13795
- if (!preExisting.has(el)) el.setAttribute("data-an-tmp", marker);
14175
+ if (!preExisting.has(el)) /** @type {HTMLElement} */ el.dataset.anTmp = marker;
13796
14176
  });
13797
14177
  editable.querySelectorAll(`[data-an-tmp="${marker}"]`).forEach((el) => {
13798
14178
  const span = document.createElement("span");
13799
14179
  span.style.fontSize = fmt.fontSize;
13800
14180
  el.parentNode.insertBefore(span, el);
13801
14181
  while (el.firstChild) span.appendChild(el.firstChild);
13802
- el.parentNode.removeChild(el);
14182
+ el.remove();
13803
14183
  });
13804
14184
  }
13805
14185
  this.context.invoke("editor.afterCommand");
@@ -13807,10 +14187,10 @@ var ContextMenu = class {
13807
14187
  /** Strip all inline formatting from the saved selection. */
13808
14188
  removeFormat() {
13809
14189
  if (!this._savedRange) return;
13810
- const editable = this.context.layoutInfo && this.context.layoutInfo.editable;
14190
+ const editable = this.context.layoutInfo?.editable;
13811
14191
  if (!editable) return;
13812
14192
  editable.focus();
13813
- const sel = window.getSelection();
14193
+ const sel = globalThis.getSelection();
13814
14194
  sel.removeAllRanges();
13815
14195
  sel.addRange(this._savedRange.cloneRange());
13816
14196
  document.execCommand("removeFormat");
@@ -13822,7 +14202,7 @@ var ContextMenu = class {
13822
14202
  while (el = iter.nextNode()) {
13823
14203
  if (!editable.contains(el) || el === editable) continue;
13824
14204
  try {
13825
- if (range.intersectsNode(el)) el.removeAttribute("style");
14205
+ if (range.intersectsNode(el)) /** @type {Element} */ el.removeAttribute("style");
13826
14206
  } catch {}
13827
14207
  }
13828
14208
  this.context.invoke("editor.afterCommand");
@@ -13924,14 +14304,14 @@ var ShortcutsDialog = class {
13924
14304
  destroy() {
13925
14305
  this._disposers.forEach((d) => d());
13926
14306
  this._disposers = [];
13927
- if (this._dialog && this._dialog.parentNode) this._dialog.parentNode.removeChild(this._dialog);
14307
+ this._dialog?.remove();
13928
14308
  this._dialog = null;
13929
14309
  }
13930
14310
  show() {
13931
14311
  if (this._dialog) {
13932
14312
  this._dialog.style.display = "flex";
13933
14313
  this._removeTrap = trapFocus(this._dialog, () => this._close());
13934
- setTimeout(() => this._closeBtn && this._closeBtn.focus(), 50);
14314
+ setTimeout(() => this._closeBtn?.focus(), 50);
13935
14315
  }
13936
14316
  }
13937
14317
  _close() {
@@ -14037,7 +14417,7 @@ var FindReplace = class {
14037
14417
  this._clearHighlights();
14038
14418
  this._disposers.forEach((d) => d());
14039
14419
  this._disposers = [];
14040
- if (this._dialog && this._dialog.parentNode) this._dialog.parentNode.removeChild(this._dialog);
14420
+ if (this._dialog && this._dialog.parentNode) this._dialog.remove();
14041
14421
  this._dialog = null;
14042
14422
  }
14043
14423
  /**
@@ -14084,8 +14464,8 @@ var FindReplace = class {
14084
14464
  const replaceActions = this._dialog.querySelector(".an-fr-replace-actions");
14085
14465
  const title = this._dialog.querySelector(".an-dialog-title");
14086
14466
  const isReplace = this._mode === "replace";
14087
- if (replaceRow) replaceRow.style.display = isReplace ? "" : "none";
14088
- if (replaceActions) replaceActions.style.display = isReplace ? "" : "none";
14467
+ if (replaceRow) /** @type {HTMLElement} */ replaceRow.style.display = isReplace ? "" : "none";
14468
+ if (replaceActions) /** @type {HTMLElement} */ replaceActions.style.display = isReplace ? "" : "none";
14089
14469
  if (title) title.textContent = isReplace ? this.context.locale.findReplace.findReplaceTitle : this.context.locale.findReplace.findTitle;
14090
14470
  }
14091
14471
  _buildDialog() {
@@ -14096,106 +14476,123 @@ var FindReplace = class {
14096
14476
  "aria-modal": "true",
14097
14477
  "aria-label": L.findReplaceTitle
14098
14478
  });
14099
- const box = createElement("div", { class: "an-dialog-box" });
14100
- const titleRow = createElement("div", { class: "an-icon-title-row" });
14479
+ const box = createElement("div", { class: "an-dialog-box an-fr-box" });
14480
+ const header = createElement("div", { class: "an-fr-header" });
14481
+ const titleGroup = createElement("div", { class: "an-dialog-title-group" });
14482
+ const iconEl = createElement("span", { class: "an-dialog-icon an-dialog-icon--sm" });
14483
+ 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>`;
14101
14484
  const title = createElement("h3", { class: "an-dialog-title" });
14102
14485
  title.textContent = L.findTitle;
14486
+ titleGroup.append(iconEl, title);
14103
14487
  const closeBtn = createElement("button", {
14104
14488
  type: "button",
14105
14489
  class: "an-icon-close",
14106
- "aria-label": "Close"
14490
+ title: L.close,
14491
+ "aria-label": L.close
14107
14492
  });
14108
- closeBtn.textContent = L.close;
14493
+ 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>`;
14109
14494
  this._closeBtn = closeBtn;
14110
- titleRow.append(title, closeBtn);
14111
- box.appendChild(titleRow);
14112
- const findRow = createElement("div", { class: "an-fr-find-row" });
14495
+ header.append(titleGroup, closeBtn);
14496
+ box.appendChild(header);
14497
+ const searchBar = createElement("div", { class: "an-fr-search-bar" });
14113
14498
  const findInput = createElement("input", {
14114
14499
  type: "text",
14115
- class: "an-input",
14500
+ class: "an-input an-fr-input",
14116
14501
  placeholder: L.findPlaceholder,
14117
14502
  "aria-label": L.searchAriaLabel
14118
14503
  });
14119
14504
  this._findInput = findInput;
14120
- findRow.appendChild(findInput);
14121
- box.appendChild(findRow);
14122
- const optRow = createElement("div", { class: "an-fr-options-row" });
14123
- const caseLabel = createElement("label", { class: "an-label an-label-inline" });
14124
14505
  const caseCheckbox = createElement("input", {
14125
14506
  type: "checkbox",
14126
- "aria-label": "Case sensitive"
14507
+ style: "display:none",
14508
+ "aria-hidden": "true"
14127
14509
  });
14128
14510
  this._caseCheckbox = caseCheckbox;
14129
- caseLabel.append(caseCheckbox, document.createTextNode(L.caseSensitive));
14130
- const counter = createElement("span", { class: "an-fr-counter" });
14131
- this._counterEl = counter;
14132
- optRow.append(caseLabel, counter);
14133
- box.appendChild(optRow);
14134
- const findActions = createElement("div", { class: "an-dialog-actions an-fr-find-actions" });
14511
+ const caseBtn = createElement("button", {
14512
+ type: "button",
14513
+ class: "an-fr-icon-btn",
14514
+ title: "Case sensitive",
14515
+ "aria-label": "Case sensitive"
14516
+ });
14517
+ caseBtn.textContent = "Aa";
14135
14518
  const prevBtn = createElement("button", {
14136
14519
  type: "button",
14137
- class: "an-btn"
14520
+ class: "an-fr-icon-btn",
14521
+ title: "Previous (Shift+Enter)",
14522
+ "aria-label": "Previous"
14138
14523
  });
14139
- prevBtn.textContent = L.prevBtn;
14524
+ 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>`;
14140
14525
  const nextBtn = createElement("button", {
14141
14526
  type: "button",
14142
- class: "an-btn an-btn-primary"
14527
+ class: "an-fr-icon-btn",
14528
+ title: "Next (Enter)",
14529
+ "aria-label": "Next"
14143
14530
  });
14144
- nextBtn.textContent = L.nextBtn;
14145
- findActions.append(prevBtn, nextBtn);
14146
- box.appendChild(findActions);
14531
+ 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>`;
14532
+ const counter = createElement("span", { class: "an-fr-counter" });
14533
+ this._counterEl = counter;
14534
+ searchBar.append(findInput, caseCheckbox, caseBtn, prevBtn, nextBtn, counter);
14535
+ box.appendChild(searchBar);
14147
14536
  const replaceRow = createElement("div", { class: "an-fr-replace-row" });
14148
14537
  replaceRow.style.display = "none";
14149
14538
  const replaceInput = createElement("input", {
14150
14539
  type: "text",
14151
- class: "an-input",
14540
+ class: "an-input an-fr-input",
14152
14541
  placeholder: L.replacePlaceholder,
14153
14542
  "aria-label": L.replaceAriaLabel
14154
14543
  });
14155
14544
  this._replaceInput = replaceInput;
14156
- replaceRow.appendChild(replaceInput);
14157
- box.appendChild(replaceRow);
14158
- const replaceActions = createElement("div", { class: "an-dialog-actions an-fr-replace-actions" });
14159
- replaceActions.style.display = "none";
14160
14545
  const replaceBtn = createElement("button", {
14161
14546
  type: "button",
14162
- class: "an-btn"
14547
+ class: "an-btn an-fr-replace-btn"
14163
14548
  });
14164
14549
  replaceBtn.textContent = L.replaceBtn;
14165
14550
  const replaceAllBtn = createElement("button", {
14166
14551
  type: "button",
14167
- class: "an-btn an-btn-primary"
14552
+ class: "an-btn an-btn-primary an-fr-replace-btn"
14168
14553
  });
14169
14554
  replaceAllBtn.textContent = L.replaceAllBtn;
14170
- replaceActions.append(replaceBtn, replaceAllBtn);
14555
+ replaceRow.append(replaceInput, replaceBtn, replaceAllBtn);
14556
+ box.appendChild(replaceRow);
14557
+ const replaceActions = createElement("div", { class: "an-fr-replace-actions" });
14558
+ replaceActions.style.display = "none";
14171
14559
  box.appendChild(replaceActions);
14172
14560
  overlay.appendChild(box);
14561
+ makeDraggable(header, box);
14173
14562
  const d1 = on(closeBtn, "click", () => this._close());
14174
14563
  const d2 = on(overlay, "click", (e) => {
14175
14564
  if (e.target === overlay) this._close();
14176
14565
  });
14177
14566
  const d3 = on(findInput, "input", () => this._onSearch());
14178
- const d4 = on(caseCheckbox, "change", () => {
14567
+ const d4 = on(caseBtn, "click", () => {
14568
+ this._caseSensitive = !this._caseSensitive;
14569
+ caseCheckbox.checked = this._caseSensitive;
14570
+ caseBtn.classList.toggle("an-fr-icon-btn--active", this._caseSensitive);
14571
+ this._onSearch();
14572
+ });
14573
+ const d5 = on(caseCheckbox, "change", () => {
14179
14574
  this._caseSensitive = caseCheckbox.checked;
14575
+ caseBtn.classList.toggle("an-fr-icon-btn--active", this._caseSensitive);
14180
14576
  this._onSearch();
14181
14577
  });
14182
- const d5 = on(nextBtn, "click", () => this._next());
14183
- const d6 = on(prevBtn, "click", () => this._prev());
14184
- const d7 = on(replaceBtn, "click", () => this._replace());
14185
- const d8 = on(replaceAllBtn, "click", () => this._replaceAll());
14186
- const d9 = on(findInput, "keydown", (e) => {
14187
- if (e.key === "Enter") {
14578
+ const d6 = on(nextBtn, "click", () => this._next());
14579
+ const d7 = on(prevBtn, "click", () => this._prev());
14580
+ const d8 = on(replaceBtn, "click", () => this._replace());
14581
+ const d9 = on(replaceAllBtn, "click", () => this._replaceAll());
14582
+ const d10 = on(findInput, "keydown", (e) => {
14583
+ const ke = e;
14584
+ if (ke.key === "Enter") {
14188
14585
  e.preventDefault();
14189
- e.shiftKey ? this._prev() : this._next();
14586
+ ke.shiftKey ? this._prev() : this._next();
14190
14587
  }
14191
14588
  });
14192
- const d10 = on(replaceInput, "keydown", (e) => {
14589
+ const d11 = on(replaceInput, "keydown", (e) => {
14193
14590
  if (e.key === "Enter") {
14194
14591
  e.preventDefault();
14195
14592
  this._replace();
14196
14593
  }
14197
14594
  });
14198
- this._disposers.push(d1, d2, d3, d4, d5, d6, d7, d8, d9, d10);
14595
+ this._disposers.push(d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11);
14199
14596
  return overlay;
14200
14597
  }
14201
14598
  _onSearch() {
@@ -14263,15 +14660,19 @@ var FindReplace = class {
14263
14660
  const re = this._queryRegex;
14264
14661
  const MAX_RESULTS = 500;
14265
14662
  const walker = document.createTreeWalker(root, 4);
14266
- let node;
14267
- while ((node = walker.nextNode()) && results.length < MAX_RESULTS) {
14663
+ let node = walker.nextNode();
14664
+ while (node && results.length < MAX_RESULTS) {
14268
14665
  re.lastIndex = 0;
14269
14666
  let m;
14270
- while ((m = re.exec(node.textContent)) !== null && results.length < MAX_RESULTS) results.push({
14667
+ while ((m = re.exec(
14668
+ /** @type {Text} */
14669
+ node.textContent
14670
+ )) !== null && results.length < MAX_RESULTS) results.push({
14271
14671
  node,
14272
14672
  start: m.index,
14273
14673
  end: m.index + m[0].length
14274
14674
  });
14675
+ node = walker.nextNode();
14275
14676
  }
14276
14677
  return results;
14277
14678
  }
@@ -14306,7 +14707,7 @@ var FindReplace = class {
14306
14707
  const parent = match.mark.parentNode;
14307
14708
  const textNode = document.createTextNode(replacement);
14308
14709
  parent.insertBefore(textNode, match.mark);
14309
- parent.removeChild(match.mark);
14710
+ match.mark.remove();
14310
14711
  parent.normalize();
14311
14712
  this.context.invoke("editor.afterCommand");
14312
14713
  const savedIndex = this._currentIndex;
@@ -14324,7 +14725,7 @@ var FindReplace = class {
14324
14725
  if (!mark || !mark.parentNode) return;
14325
14726
  const textNode = document.createTextNode(replacement);
14326
14727
  mark.parentNode.insertBefore(textNode, mark);
14327
- mark.parentNode.removeChild(mark);
14728
+ mark.remove();
14328
14729
  });
14329
14730
  if (this.context.layoutInfo.editable) this.context.layoutInfo.editable.normalize();
14330
14731
  this._matches = [];
@@ -14343,7 +14744,7 @@ var FindReplace = class {
14343
14744
  const parent = mark.parentNode;
14344
14745
  if (!parent) return;
14345
14746
  while (mark.firstChild) parent.insertBefore(mark.firstChild, mark);
14346
- parent.removeChild(mark);
14747
+ mark.remove();
14347
14748
  });
14348
14749
  editable.normalize();
14349
14750
  this._matches = [];
@@ -14394,7 +14795,7 @@ function drawCropToCanvas(img, naturalRect, renderW, renderH) {
14394
14795
  resolve(null);
14395
14796
  }
14396
14797
  };
14397
- if (img.src.startsWith("data:") || img.src.startsWith("blob:") || img.src.startsWith(window.location.origin)) {
14798
+ if (img.src.startsWith("data:") || img.src.startsWith("blob:") || img.src.startsWith(globalThis.location.origin)) {
14398
14799
  tryDraw(img);
14399
14800
  return;
14400
14801
  }
@@ -14556,9 +14957,10 @@ var ImageCropOverlay = class {
14556
14957
  }), on(h, "touchstart", (e) => {
14557
14958
  e.preventDefault();
14558
14959
  e.stopPropagation();
14960
+ const te = e;
14559
14961
  this._startHandleDrag({
14560
- clientX: e.touches[0].clientX,
14561
- clientY: e.touches[0].clientY
14962
+ clientX: te.touches[0].clientX,
14963
+ clientY: te.touches[0].clientY
14562
14964
  }, id);
14563
14965
  }, { passive: false }));
14564
14966
  this._handles[id] = h;
@@ -14575,9 +14977,10 @@ var ImageCropOverlay = class {
14575
14977
  if (e.target !== cropBox && e.target !== grid) return;
14576
14978
  e.preventDefault();
14577
14979
  e.stopPropagation();
14980
+ const te2 = e;
14578
14981
  this._startBoxMove({
14579
- clientX: e.touches[0].clientX,
14580
- clientY: e.touches[0].clientY
14982
+ clientX: te2.touches[0].clientX,
14983
+ clientY: te2.touches[0].clientY
14581
14984
  });
14582
14985
  }, { passive: false }));
14583
14986
  const infoEl = document.createElement("div");
@@ -14650,8 +15053,8 @@ var ImageCropOverlay = class {
14650
15053
  this._cropBox.style.top = `${y}px`;
14651
15054
  this._cropBox.style.width = `${w}px`;
14652
15055
  this._cropBox.style.height = `${h}px`;
14653
- const vw = window.innerWidth;
14654
- const vh = window.innerHeight;
15056
+ const vw = globalThis.innerWidth;
15057
+ const vh = globalThis.innerHeight;
14655
15058
  this._scrim.style.clipPath = [
14656
15059
  `polygon(`,
14657
15060
  `0 0, ${vw}px 0, ${vw}px ${vh}px, 0 ${vh}px, 0 0,`,
@@ -14675,7 +15078,7 @@ var ImageCropOverlay = class {
14675
15078
  }
14676
15079
  const margin = 8;
14677
15080
  let tbTop = y + h + margin;
14678
- if (tbTop + 40 > window.innerHeight - margin) tbTop = y - 40 - margin;
15081
+ if (tbTop + 40 > globalThis.innerHeight - margin) tbTop = y - 40 - margin;
14679
15082
  this._toolbar.style.left = `${x}px`;
14680
15083
  this._toolbar.style.top = `${tbTop}px`;
14681
15084
  }
@@ -14739,9 +15142,10 @@ var ImageCropOverlay = class {
14739
15142
  _attachDocDrag(onMove) {
14740
15143
  const onTouchMove = (e) => {
14741
15144
  e.preventDefault();
15145
+ const te3 = e;
14742
15146
  onMove({
14743
- clientX: e.touches[0].clientX,
14744
- clientY: e.touches[0].clientY
15147
+ clientX: te3.touches[0].clientX,
15148
+ clientY: te3.touches[0].clientY
14745
15149
  });
14746
15150
  };
14747
15151
  const cleanup = () => {
@@ -14801,7 +15205,7 @@ var ImageCropOverlay = class {
14801
15205
  this._close(false);
14802
15206
  return;
14803
15207
  }
14804
- const fmt = img.src.match(/^data:image\/(jpe?g)/i) ? "image/jpeg" : "image/png";
15208
+ const fmt = /^data:image\/(jpe?g)/i.exec(img.src) ? "image/jpeg" : "image/png";
14805
15209
  const quality = fmt === "image/jpeg" ? .92 : void 0;
14806
15210
  const newSrc = canvas.toDataURL(fmt, quality);
14807
15211
  this._close(false);
@@ -14841,7 +15245,7 @@ var ImageCropOverlay = class {
14841
15245
  banner.textContent = msg;
14842
15246
  document.body.appendChild(banner);
14843
15247
  setTimeout(() => {
14844
- if (banner.parentNode) banner.parentNode.removeChild(banner);
15248
+ banner.remove();
14845
15249
  }, 4e3);
14846
15250
  }
14847
15251
  /**
@@ -14856,7 +15260,7 @@ var ImageCropOverlay = class {
14856
15260
  this._cropBox,
14857
15261
  this._toolbar
14858
15262
  ].forEach((el) => {
14859
- if (el && el.parentNode) el.parentNode.removeChild(el);
15263
+ el?.remove();
14860
15264
  });
14861
15265
  this._scrim = null;
14862
15266
  this._cropBox = null;
@@ -14877,7 +15281,7 @@ var ImageCropOverlay = class {
14877
15281
  *
14878
15282
  * Activated when both `autoSave` and `autoSaveRestore` options are true.
14879
15283
  * On initialize it checks localStorage for a draft that is within the
14880
- * `autoSaveRestoreTimeout` day window. If one is found a dismissible banner
15284
+ * `autoSaveRestoreTimeout` day globalThis. If one is found a dismissible banner
14881
15285
  * is prepended to the editor container.
14882
15286
  */
14883
15287
  var AutoSaveRestore = class {
@@ -14963,7 +15367,7 @@ var AutoSaveRestore = class {
14963
15367
  this._removeBanner();
14964
15368
  }
14965
15369
  _removeBanner() {
14966
- if (this._banner && this._banner.parentNode) this._banner.parentNode.removeChild(this._banner);
15370
+ this._banner?.remove();
14967
15371
  this._banner = null;
14968
15372
  }
14969
15373
  };
@@ -15026,8 +15430,8 @@ var MarkdownShortcuts = class {
15026
15430
  * @returns {{ text: string, range: Range, lineNode: Node } | null}
15027
15431
  */
15028
15432
  _getLineContext() {
15029
- const sel = window.getSelection();
15030
- if (!sel || !sel.rangeCount) return null;
15433
+ const sel = globalThis.getSelection();
15434
+ if (!sel?.rangeCount) return null;
15031
15435
  const range = sel.getRangeAt(0);
15032
15436
  if (!range.collapsed) return null;
15033
15437
  const editable = this.context.layoutInfo.editable;
@@ -15046,7 +15450,7 @@ var MarkdownShortcuts = class {
15046
15450
  }
15047
15451
  _isBlock(node) {
15048
15452
  if (node.nodeType !== Node.ELEMENT_NODE) return false;
15049
- const display = window.getComputedStyle(node).display;
15453
+ const display = globalThis.getComputedStyle(node).display;
15050
15454
  return display === "block" || display === "list-item" || display === "table-cell";
15051
15455
  }
15052
15456
  /** Applies block rule on Space key. Returns true if a rule fired. */
@@ -15077,7 +15481,7 @@ var MarkdownShortcuts = class {
15077
15481
  }
15078
15482
  ];
15079
15483
  for (const { re, handler } of blockPatterns) {
15080
- const m = text.match(re);
15484
+ const m = re.exec(text);
15081
15485
  if (m) {
15082
15486
  handler(m);
15083
15487
  return true;
@@ -15101,8 +15505,8 @@ var MarkdownShortcuts = class {
15101
15505
  return false;
15102
15506
  }
15103
15507
  _selectLineAndDelete() {
15104
- const sel = window.getSelection();
15105
- if (!sel || !sel.rangeCount) return;
15508
+ const sel = globalThis.getSelection();
15509
+ if (!sel?.rangeCount) return;
15106
15510
  const range = sel.getRangeAt(0);
15107
15511
  const startRange = document.createRange();
15108
15512
  startRange.setStart(range.startContainer.parentNode || range.startContainer, 0);
@@ -15140,8 +15544,8 @@ var MarkdownShortcuts = class {
15140
15544
  this.context.triggerEvent("change", this.context.getHTML());
15141
15545
  }
15142
15546
  _onInput() {
15143
- const sel = window.getSelection();
15144
- if (!sel || !sel.rangeCount) return;
15547
+ const sel = globalThis.getSelection();
15548
+ if (!sel?.rangeCount) return;
15145
15549
  const range = sel.getRangeAt(0);
15146
15550
  if (!range.collapsed) return;
15147
15551
  if (!this.context.layoutInfo.editable.contains(range.startContainer)) return;
@@ -15169,7 +15573,7 @@ var MarkdownShortcuts = class {
15169
15573
  ];
15170
15574
  const upToCursor = text.slice(0, offset);
15171
15575
  for (const { re, tag } of inlineRules) {
15172
- const m = upToCursor.match(re);
15576
+ const m = re.exec(upToCursor);
15173
15577
  if (!m) continue;
15174
15578
  const matchStart = upToCursor.length - m[0].length;
15175
15579
  const matchEnd = offset;
@@ -15180,11 +15584,8 @@ var MarkdownShortcuts = class {
15180
15584
  el.textContent = innerText;
15181
15585
  const beforeNode = document.createTextNode(before);
15182
15586
  const afterNode = document.createTextNode("​" + after);
15183
- const parent = node.parentNode;
15184
- parent.insertBefore(beforeNode, node);
15185
- parent.insertBefore(el, node);
15186
- parent.insertBefore(afterNode, node);
15187
- parent.removeChild(node);
15587
+ /** @type {ChildNode} */ node.before(beforeNode, el, afterNode);
15588
+ /** @type {ChildNode} */ node.remove();
15188
15589
  const newRange = document.createRange();
15189
15590
  newRange.setStart(afterNode, 1);
15190
15591
  newRange.collapse(true);
@@ -15255,12 +15656,12 @@ var _ACTIONS = {
15255
15656
  strikethrough: (ctx) => ctx.invoke("editor.strikethrough"),
15256
15657
  link: (ctx) => ctx.invoke("linkDialog.show"),
15257
15658
  removeFormat: (ctx) => {
15258
- const editable = ctx.layoutInfo && ctx.layoutInfo.editable;
15659
+ const editable = ctx.layoutInfo?.editable;
15259
15660
  if (!editable) return;
15260
15661
  editable.focus();
15261
15662
  document.execCommand("removeFormat");
15262
- const sel = window.getSelection();
15263
- if (sel && sel.rangeCount > 0 && !sel.getRangeAt(0).collapsed) {
15663
+ const sel = globalThis.getSelection();
15664
+ if (sel?.rangeCount > 0 && !sel.getRangeAt(0).collapsed) {
15264
15665
  const range = sel.getRangeAt(0);
15265
15666
  const ancestor = range.commonAncestorContainer;
15266
15667
  const root = ancestor.nodeType === 1 ? ancestor : ancestor.parentElement;
@@ -15318,13 +15719,15 @@ var BubbleToolbar = class {
15318
15719
  const d6 = this.context.on("contextMenu:hide", () => {
15319
15720
  this._contextMenuOpen = false;
15320
15721
  });
15321
- this._disposers.push(d1, d2, d3, d4, d5, d6);
15722
+ const d7 = on(globalThis, "scroll", () => this._hide(), { passive: true });
15723
+ const d8 = on(globalThis, "resize", () => this._hide(), { passive: true });
15724
+ this._disposers.push(d1, d2, d3, d4, d5, d6, d7, d8);
15322
15725
  return this;
15323
15726
  }
15324
15727
  destroy() {
15325
- if (this._el && this._el.parentNode) this._el.parentNode.removeChild(this._el);
15728
+ this._el?.remove();
15326
15729
  this._el = null;
15327
- if (this._picker && this._picker.parentNode) this._picker.parentNode.removeChild(this._picker);
15730
+ this._picker?.remove();
15328
15731
  this._picker = null;
15329
15732
  this._disposers.forEach((d) => d());
15330
15733
  this._disposers = [];
@@ -15432,20 +15835,22 @@ var BubbleToolbar = class {
15432
15835
  picker.appendChild(customRow);
15433
15836
  document.body.appendChild(picker);
15434
15837
  this._picker = picker;
15435
- this._picker._paletteEl = palette;
15436
- this._picker._noColorBtn = noColorBtn;
15437
- this._picker._colorInput = colorInput;
15838
+ const pickerAny = picker;
15839
+ pickerAny._paletteEl = palette;
15840
+ pickerAny._noColorBtn = noColorBtn;
15841
+ pickerAny._colorInput = colorInput;
15438
15842
  }
15439
15843
  _openColorPicker(type, anchorBtn) {
15440
- const sel = window.getSelection();
15441
- if (sel && sel.rangeCount > 0) this._savedRange = sel.getRangeAt(0).cloneRange();
15844
+ const sel = globalThis.getSelection();
15845
+ if (sel?.rangeCount > 0) this._savedRange = sel.getRangeAt(0).cloneRange();
15442
15846
  this._pickerType = type;
15443
- const palette = this._picker._paletteEl;
15444
- const noColorBtn = this._picker._noColorBtn;
15847
+ const pickerAny = this._picker;
15848
+ const palette = pickerAny._paletteEl;
15849
+ const noColorBtn = pickerAny._noColorBtn;
15445
15850
  if (type === "hiliteColor") {
15446
15851
  if (!palette.contains(noColorBtn)) palette.appendChild(noColorBtn);
15447
- } else if (palette.contains(noColorBtn)) palette.removeChild(noColorBtn);
15448
- this._picker._colorInput.value = type === "foreColor" ? "#000000" : "#ffff00";
15852
+ } else if (palette.contains(noColorBtn)) noColorBtn.remove();
15853
+ /** @type {any} */ this._picker._colorInput.value = type === "foreColor" ? "#000000" : "#ffff00";
15449
15854
  this._picker.style.display = "block";
15450
15855
  const pw = this._picker.offsetWidth;
15451
15856
  const ph = this._picker.offsetHeight;
@@ -15453,7 +15858,7 @@ var BubbleToolbar = class {
15453
15858
  let top = toolbarRect.top - ph - 6;
15454
15859
  if (top < 8) top = toolbarRect.bottom + 6;
15455
15860
  let left = anchorBtn.getBoundingClientRect().left;
15456
- left = Math.max(8, Math.min(left, window.innerWidth - pw - 8));
15861
+ left = Math.max(8, Math.min(left, globalThis.innerWidth - pw - 8));
15457
15862
  this._picker.style.left = `${left}px`;
15458
15863
  this._picker.style.top = `${top}px`;
15459
15864
  }
@@ -15463,10 +15868,10 @@ var BubbleToolbar = class {
15463
15868
  }
15464
15869
  /** Restore the saved selection, apply execCommand, update the color strip, then close the picker. */
15465
15870
  _applyColor(type, color) {
15466
- const editable = this.context.layoutInfo && this.context.layoutInfo.editable;
15871
+ const editable = this.context.layoutInfo?.editable;
15467
15872
  if (!editable || !this._savedRange) return;
15468
15873
  editable.focus();
15469
- const sel = window.getSelection();
15874
+ const sel = globalThis.getSelection();
15470
15875
  sel.removeAllRanges();
15471
15876
  try {
15472
15877
  sel.addRange(this._savedRange.cloneRange());
@@ -15477,9 +15882,8 @@ var BubbleToolbar = class {
15477
15882
  if (!document.execCommand(cmd, false, color) && cmd === "hiliteColor") document.execCommand("backColor", false, color);
15478
15883
  this.context.invoke("editor.afterCommand");
15479
15884
  const name = type === "hiliteColor" ? "hiliteColor" : "foreColor";
15480
- const btn = this._el && this._el.querySelector(`[data-name="${name}"]`);
15481
- const strip = btn && btn.querySelector(".an-bubble-color-strip");
15482
- if (strip) strip.style.background = color === "transparent" ? "transparent" : color;
15885
+ const strip = (this._el?.querySelector(`[data-name="${name}"]`))?.querySelector(".an-bubble-color-strip");
15886
+ if (strip) /** @type {HTMLElement} */ strip.style.background = color === "transparent" ? "transparent" : color;
15483
15887
  this._closeColorPicker();
15484
15888
  this._syncActive();
15485
15889
  }
@@ -15493,8 +15897,16 @@ var BubbleToolbar = class {
15493
15897
  const gap = 8;
15494
15898
  let left = rect.left + rect.width / 2 - bw / 2;
15495
15899
  let top = rect.top - bh - gap;
15496
- left = Math.max(8, Math.min(left, window.innerWidth - bw - 8));
15900
+ left = Math.max(8, Math.min(left, globalThis.innerWidth - bw - 8));
15497
15901
  if (top < 8) top = rect.bottom + gap;
15902
+ const tableTooltipEl = document.querySelector(".an-table-tooltip");
15903
+ if (tableTooltipEl && tableTooltipEl.style.display !== "none") {
15904
+ const ttRect = tableTooltipEl.getBoundingClientRect();
15905
+ if (top < ttRect.bottom + gap && top + bh > ttRect.top - gap) {
15906
+ top = rect.bottom + gap;
15907
+ if (top + bh > globalThis.innerHeight - 8) top = ttRect.bottom + gap;
15908
+ }
15909
+ }
15498
15910
  el.style.top = `${top}px`;
15499
15911
  el.style.left = `${left}px`;
15500
15912
  el.style.visibility = "";
@@ -15518,20 +15930,18 @@ var BubbleToolbar = class {
15518
15930
  /** Read the current selection's color and update the color-strip indicators. */
15519
15931
  _syncColorStrips() {
15520
15932
  if (!this._el) return;
15521
- const sel = window.getSelection();
15933
+ const sel = globalThis.getSelection();
15522
15934
  if (!sel || !sel.rangeCount) return;
15523
15935
  let node = sel.getRangeAt(0).startContainer;
15524
15936
  if (node.nodeType === Node.TEXT_NODE) node = node.parentElement;
15525
15937
  if (!node) return;
15526
- const cs = window.getComputedStyle(node);
15527
- const foreBtn = this._el.querySelector("[data-name=\"foreColor\"]");
15528
- const foreStrip = foreBtn && foreBtn.querySelector(".an-bubble-color-strip");
15529
- if (foreStrip) foreStrip.style.background = cs.color || "#000000";
15530
- const hiliteBtn = this._el.querySelector("[data-name=\"hiliteColor\"]");
15531
- const hiliteStrip = hiliteBtn && hiliteBtn.querySelector(".an-bubble-color-strip");
15938
+ const cs = globalThis.getComputedStyle(node);
15939
+ const foreStrip = this._el.querySelector("[data-name=\"foreColor\"]")?.querySelector(".an-bubble-color-strip");
15940
+ if (foreStrip) /** @type {HTMLElement} */ foreStrip.style.background = cs.color || "#000000";
15941
+ const hiliteStrip = this._el.querySelector("[data-name=\"hiliteColor\"]")?.querySelector(".an-bubble-color-strip");
15532
15942
  if (hiliteStrip) {
15533
15943
  const bg = cs.backgroundColor;
15534
- hiliteStrip.style.background = !bg || bg === "rgba(0, 0, 0, 0)" || bg === "transparent" ? "transparent" : bg;
15944
+ /** @type {HTMLElement} */ hiliteStrip.style.background = !bg || bg === "rgba(0, 0, 0, 0)" || bg === "transparent" ? "transparent" : bg;
15535
15945
  }
15536
15946
  }
15537
15947
  _onSelectionChange() {
@@ -15539,7 +15949,7 @@ var BubbleToolbar = class {
15539
15949
  this._rafId = requestAnimationFrame(() => {
15540
15950
  if (this._contextMenuOpen) return;
15541
15951
  if (this._picker && this._picker.style.display !== "none") return;
15542
- const sel = window.getSelection();
15952
+ const sel = globalThis.getSelection();
15543
15953
  if (!sel || sel.isCollapsed || !sel.rangeCount) {
15544
15954
  this._hide();
15545
15955
  return;
@@ -15562,8 +15972,8 @@ var BubbleToolbar = class {
15562
15972
  }
15563
15973
  _onMousedown(e) {
15564
15974
  if (!this._visible) return;
15565
- if (this._el && this._el.contains(e.target)) return;
15566
- if (this._picker && this._picker.contains(e.target)) return;
15975
+ if (this._el?.contains(e.target)) return;
15976
+ if (this._picker?.contains(e.target)) return;
15567
15977
  if (this.context.layoutInfo.editable.contains(e.target)) return;
15568
15978
  this._hide();
15569
15979
  }
@@ -15648,7 +16058,7 @@ var Mention = class {
15648
16058
  }
15649
16059
  destroy() {
15650
16060
  clearTimeout(this._debounceTimer);
15651
- if (this._dropdown && this._dropdown.parentNode) this._dropdown.parentNode.removeChild(this._dropdown);
16061
+ this._dropdown?.remove();
15652
16062
  this._dropdown = null;
15653
16063
  this._disposers.forEach((d) => d());
15654
16064
  this._disposers = [];
@@ -15659,11 +16069,11 @@ var Mention = class {
15659
16069
  el.setAttribute("role", "listbox");
15660
16070
  el.addEventListener("mousedown", (e) => e.preventDefault());
15661
16071
  el.addEventListener("click", (e) => {
15662
- const item = e.target.closest(".an-mention-item");
16072
+ const item = e.target?.closest(".an-mention-item");
15663
16073
  if (item) this._select(+item.dataset.index);
15664
16074
  });
15665
16075
  el.addEventListener("mousemove", (e) => {
15666
- const item = e.target.closest(".an-mention-item");
16076
+ const item = e.target?.closest(".an-mention-item");
15667
16077
  if (item) this._highlightItem(+item.dataset.index);
15668
16078
  });
15669
16079
  document.body.appendChild(el);
@@ -15678,7 +16088,7 @@ var Mention = class {
15678
16088
  const li = document.createElement("div");
15679
16089
  li.className = "an-mention-item";
15680
16090
  li.setAttribute("role", "option");
15681
- li.dataset.index = i;
16091
+ li.dataset.index = String(i);
15682
16092
  if (item.avatar) {
15683
16093
  const img = document.createElement("img");
15684
16094
  img.src = item.avatar;
@@ -15712,8 +16122,8 @@ var Mention = class {
15712
16122
  const ddw = dd.offsetWidth;
15713
16123
  let top = rect.bottom + 4;
15714
16124
  let left = rect.left;
15715
- if (rect.bottom + ddh + 8 > window.innerHeight) top = rect.top - ddh - 4;
15716
- left = Math.max(8, Math.min(left, window.innerWidth - ddw - 8));
16125
+ if (rect.bottom + ddh + 8 > globalThis.innerHeight) top = rect.top - ddh - 4;
16126
+ left = Math.max(8, Math.min(left, globalThis.innerWidth - ddw - 8));
15717
16127
  dd.style.top = `${top}px`;
15718
16128
  dd.style.left = `${left}px`;
15719
16129
  dd.style.visibility = "";
@@ -15737,7 +16147,7 @@ var Mention = class {
15737
16147
  * collapsed range is reliable at this point but often empty inside async callbacks.
15738
16148
  */
15739
16149
  _captureCaretRect() {
15740
- if (this._triggerNode && this._triggerNode.isConnected) try {
16150
+ if (this._triggerNode?.isConnected) try {
15741
16151
  const r = document.createRange();
15742
16152
  const end = Math.min(this._triggerOffset + 1, this._triggerNode.textContent.length);
15743
16153
  r.setStart(this._triggerNode, this._triggerOffset);
@@ -15748,13 +16158,13 @@ var Mention = class {
15748
16158
  return;
15749
16159
  }
15750
16160
  } catch (_) {}
15751
- const sel = window.getSelection();
16161
+ const sel = globalThis.getSelection();
15752
16162
  if (!sel || !sel.rangeCount) return;
15753
16163
  const rects = sel.getRangeAt(0).getClientRects();
15754
16164
  if (rects.length > 0) this._caretRect = rects[rects.length - 1];
15755
16165
  }
15756
16166
  _getQueryAtCursor() {
15757
- const sel = window.getSelection();
16167
+ const sel = globalThis.getSelection();
15758
16168
  if (!sel || !sel.rangeCount) return null;
15759
16169
  const range = sel.getRangeAt(0);
15760
16170
  if (!range.collapsed) return null;
@@ -15811,7 +16221,7 @@ var Mention = class {
15811
16221
  }
15812
16222
  _onDocClick(e) {
15813
16223
  if (!this._open) return;
15814
- if (this._dropdown && this._dropdown.contains(e.target)) return;
16224
+ if (this._dropdown?.contains(e.target)) return;
15815
16225
  this._hideDropdown();
15816
16226
  }
15817
16227
  _select(index) {
@@ -15820,7 +16230,7 @@ var Mention = class {
15820
16230
  if (this._triggerNode) {
15821
16231
  const node = this._triggerNode;
15822
16232
  node.textContent = node.textContent.slice(0, this._triggerOffset) + node.textContent.slice(this._triggerOffset + this._cfg.trigger.length + this._query.length);
15823
- const sel = window.getSelection();
16233
+ const sel = globalThis.getSelection();
15824
16234
  const range = document.createRange();
15825
16235
  range.setStart(node, this._triggerOffset);
15826
16236
  range.collapse(true);
@@ -15928,7 +16338,7 @@ var Context = class {
15928
16338
  /**
15929
16339
  * Registers and initialises a custom module on this instance.
15930
16340
  * @param {string} name
15931
- * @param {Function} ModuleClass
16341
+ * @param {new (ctx: this) => any} ModuleClass
15932
16342
  * @returns {this}
15933
16343
  */
15934
16344
  registerModule(name, ModuleClass) {
@@ -16181,23 +16591,27 @@ var Context = class {
16181
16591
  a.style.display = "none";
16182
16592
  document.body.appendChild(a);
16183
16593
  a.click();
16184
- document.body.removeChild(a);
16594
+ a.remove();
16185
16595
  URL.revokeObjectURL(url);
16186
16596
  }
16187
16597
  /**
16188
- * Opens the editor content in a new window and triggers the browser print dialog.
16598
+ * Opens the editor content in a new globalThis and triggers the browser print dialog.
16189
16599
  * @param {string} [title='']
16190
16600
  */
16191
16601
  print(title = "") {
16192
16602
  const content = this.getHTML();
16193
- const safeTitle = (title || "").replace(/</g, "&lt;").replace(/>/g, "&gt;");
16194
- const w = window.open("", "_blank");
16195
- if (!w) return;
16196
- 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>`);
16197
- w.document.close();
16198
- w.onload = () => {
16603
+ 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>`;
16604
+ const blob = new Blob([markup], { type: "text/html" });
16605
+ const url = URL.createObjectURL(blob);
16606
+ const w = globalThis.open(url, "_blank");
16607
+ if (!w) {
16608
+ URL.revokeObjectURL(url);
16609
+ return;
16610
+ }
16611
+ w.addEventListener("load", () => {
16199
16612
  w.print();
16200
- };
16613
+ URL.revokeObjectURL(url);
16614
+ });
16201
16615
  }
16202
16616
  /**
16203
16617
  * Sets whether the editor is disabled (readonly).
@@ -16235,10 +16649,12 @@ var Context = class {
16235
16649
  this._disposers.forEach((d) => d());
16236
16650
  this._disposers = [];
16237
16651
  const container = this.layoutInfo.container;
16238
- if (container && container.parentNode) {
16652
+ const wasDark = container?.classList.contains("an-theme-dark");
16653
+ if (container?.parentNode) {
16239
16654
  this.targetEl.style.display = "";
16240
- container.parentNode.removeChild(container);
16655
+ container.remove();
16241
16656
  }
16657
+ if (wasDark && !document.querySelector(".an-container.an-theme-dark")) document.body.classList.remove("an-theme-dark");
16242
16658
  if (typeof this.options.onDestroy === "function") this.options.onDestroy(this);
16243
16659
  this._alive = false;
16244
16660
  this._listeners.clear();
@@ -16247,7 +16663,8 @@ var Context = class {
16247
16663
  * Syncs editor HTML back into the original textarea/input for form submission.
16248
16664
  */
16249
16665
  _syncToTarget() {
16250
- if (this.targetEl.tagName === "TEXTAREA" || this.targetEl.tagName === "INPUT") this.targetEl.value = this.getHTML();
16666
+ if (this.targetEl.tagName === "TEXTAREA" || this.targetEl.tagName === "INPUT")
16667
+ /** @type {HTMLInputElement} */ this.targetEl.value = this.getHTML();
16251
16668
  }
16252
16669
  };
16253
16670
  //#endregion
@@ -16301,7 +16718,7 @@ function tail(arr, n = 1) {
16301
16718
  * @returns {T[]}
16302
16719
  */
16303
16720
  function flatten(arr) {
16304
- return arr.reduce((acc, val) => acc.concat(val), []);
16721
+ return arr.flat();
16305
16722
  }
16306
16723
  /**
16307
16724
  * Returns unique elements of an array (using Set).
@@ -16380,7 +16797,7 @@ var env = {
16380
16797
  /** True if running on mobile */
16381
16798
  isMobile: /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent),
16382
16799
  /** True if touch is supported */
16383
- isTouch: "ontouchstart" in window || navigator.maxTouchPoints > 0,
16800
+ isTouch: "ontouchstart" in globalThis || navigator.maxTouchPoints > 0,
16384
16801
  /** Modifier key name depending on platform */
16385
16802
  modifierKey: /Macintosh/.test(userAgent) ? "metaKey" : "ctrlKey"
16386
16803
  };
@@ -16504,6 +16921,6 @@ function resolveElements(selector) {
16504
16921
  return [];
16505
16922
  }
16506
16923
  //#endregion
16507
- export { Context, ELEMENT_NODE, TEXT_NODE, WrappedRange, _buttonRegistry, alignCenterBtn, alignJustifyBtn, alignLeftBtn, alignRightBtn, all, ancestors, any, backColorBtn, boldBtn, checklistBtn, children, chunk, clamp, closest, closestPara, codeviewBtn, collapsedRange, compose, createElement, currentRange, debounce, AutumnNote as default, defaultOptions, defaultToolbar, directionBtn, emojiBtn, env, findBtn, findReplaceBtn, first, flatten, fontFamilyBtn, fontSizeBtn, foreColorBtn, fromNativeRange, fullscreenBtn, getButton, groupBy, hrBtn, iconBtn, identity, imageBtn, indentBtn, initial, inlineCodeBtn, insertAfter, isAnchor, isEditable, isElement, isEmpty, isFunction, isImage, isInline, isInsideEditable, isKey, isLi, isList, isModifier, isNil, isPara, isPlainObject, isSelectionInside, isString, isTable, isText, isVoid, italicBtn, key, last, lineHeightBtn, linkBtn, locales, mergeDeep, nextElement, nodeValue, olBtn, on, outdentBtn, outerHtml, paragraphStyleBtn, placeCaret, prevElement, printBtn, rangeFromElement, rect2bnd, redoBtn, registerButton, remove, removeFormatBtn, resolveLocale, sanitiseHTML, sanitiseUrl, shortcutsBtn, splitText, strikeBtn, subscriptBtn, superscriptBtn, tableBtn, tail, throttle, trapFocus, ulBtn, underlineBtn, undoBtn, unique, unwrap, videoBtn, withSavedRange, wrap };
16924
+ export { Context, ELEMENT_NODE, TEXT_NODE, WrappedRange, _buttonRegistry, alignCenterBtn, alignJustifyBtn, alignLeftBtn, alignRightBtn, all, ancestors, any, backColorBtn, boldBtn, checklistBtn, children, chunk, clamp, closest, closestPara, codeviewBtn, collapsedRange, compose, createElement, currentRange, debounce, AutumnNote as default, defaultOptions, defaultToolbar, directionBtn, emojiBtn, env, findBtn, findReplaceBtn, first, flatten, fontFamilyBtn, fontSizeBtn, foreColorBtn, fromNativeRange, fullscreenBtn, getButton, groupBy, hrBtn, iconBtn, identity, imageBtn, indentBtn, initial, inlineCodeBtn, insertAfter, isAnchor, isEditable, isElement, isEmpty, isFunction, isImage, isInline, isInsideEditable, isKey, isLi, isList, isModifier, isNil, isPara, isPlainObject, isSelectionInside, isString, isTable, isText, isVoid, italicBtn, key, last, lineHeightBtn, linkBtn, locales, makeDraggable, mergeDeep, nextElement, nodeValue, olBtn, on, outdentBtn, outerHtml, paragraphStyleBtn, placeCaret, prevElement, printBtn, rangeFromElement, rect2bnd, redoBtn, registerButton, remove, removeFormatBtn, resolveLocale, sanitiseHTML, sanitiseUrl, shortcutsBtn, splitText, strikeBtn, subscriptBtn, superscriptBtn, tableBtn, tail, throttle, trapFocus, ulBtn, underlineBtn, undoBtn, unique, unwrap, videoBtn, withSavedRange, wrap };
16508
16925
 
16509
16926
  //# sourceMappingURL=autumnnote.es.js.map