autumnnote 1.0.6 → 1.0.8

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.
@@ -566,8 +566,24 @@ function underline() {
566
566
  }
567
567
  /**
568
568
  * Strikethrough / removes strikethrough.
569
+ * Falls back to manual DOM manipulation inside nested formats where
570
+ * execCommand's state detection is unreliable (mirrors underline() logic).
569
571
  */
570
- var strikethrough = () => execCommand("strikeThrough");
572
+ function strikethrough() {
573
+ const sel = window.getSelection();
574
+ if (!sel || !sel.rangeCount) return;
575
+ let sc = sel.getRangeAt(0).startContainer;
576
+ if (sc.nodeType === 3) sc = sc.parentElement;
577
+ const sEl = sc && sc.closest && (sc.closest("s") || sc.closest("strike"));
578
+ const nativeState = document.queryCommandState("strikeThrough");
579
+ if (sEl && !nativeState) {
580
+ const parent = sEl.parentNode;
581
+ while (sEl.firstChild) parent.insertBefore(sEl.firstChild, sEl);
582
+ parent.removeChild(sEl);
583
+ return;
584
+ }
585
+ execCommand("strikeThrough");
586
+ }
571
587
  /**
572
588
  * Superscript toggle.
573
589
  */
@@ -598,14 +614,48 @@ var fontName = (name) => execCommand("fontName", name);
598
614
  * @param {HTMLElement} [editable] - scoping element to avoid touching nodes outside this editor
599
615
  */
600
616
  function fontSize(size, editable = document) {
617
+ const sel = window.getSelection();
618
+ const wasCollapsed = !sel || !sel.rangeCount || sel.getRangeAt(0).collapsed;
619
+ if (wasCollapsed && sel && sel.rangeCount > 0) {
620
+ try {
621
+ const range = sel.getRangeAt(0);
622
+ const span = document.createElement("span");
623
+ span.style.fontSize = size;
624
+ const zwsNode = document.createTextNode("​");
625
+ span.appendChild(zwsNode);
626
+ range.insertNode(span);
627
+ const nr = document.createRange();
628
+ nr.setStart(zwsNode, zwsNode.textContent.length);
629
+ nr.collapse(true);
630
+ sel.removeAllRanges();
631
+ sel.addRange(nr);
632
+ } catch (_) {}
633
+ return;
634
+ }
601
635
  execCommand("fontSize", "7");
602
- editable.querySelectorAll("font[size=\"7\"]").forEach((el) => {
636
+ const scope = editable instanceof HTMLElement ? editable : document;
637
+ const newSpans = [];
638
+ scope.querySelectorAll("font[size=\"7\"]").forEach((el) => {
603
639
  const span = document.createElement("span");
604
640
  span.style.fontSize = size;
605
641
  el.parentNode.insertBefore(span, el);
606
642
  while (el.firstChild) span.appendChild(el.firstChild);
607
643
  el.parentNode.removeChild(el);
644
+ newSpans.push(span);
608
645
  });
646
+ if (!wasCollapsed && sel && newSpans.length > 0) {
647
+ const first = newSpans[0];
648
+ const last = newSpans[newSpans.length - 1];
649
+ try {
650
+ const nr = document.createRange();
651
+ const startNode = first.firstChild || first;
652
+ const endNode = last.lastChild || last;
653
+ nr.setStart(startNode, 0);
654
+ nr.setEnd(endNode, endNode.nodeType === Node.TEXT_NODE ? endNode.textContent.length : endNode.childNodes.length);
655
+ sel.removeAllRanges();
656
+ sel.addRange(nr);
657
+ } catch (_) {}
658
+ }
609
659
  }
610
660
  /**
611
661
  * Wraps the selection in the given block tag (p, h1-h6, blockquote, pre).
@@ -634,8 +684,57 @@ var justifyFull = () => execCommand("justifyFull");
634
684
  var indent = () => execCommand("indent");
635
685
  /**
636
686
  * Outdents the list or block.
687
+ * G.5: When cursor is inside a checklist item, "outdent" means converting
688
+ * that item back to a regular <p> element rather than calling execCommand
689
+ * (which would destroy the ul > li checklist structure).
690
+ */
691
+ function outdent() {
692
+ const sel = window.getSelection();
693
+ if (sel && sel.rangeCount) {
694
+ let container = sel.getRangeAt(0).commonAncestorContainer;
695
+ if (container.nodeType === 3) container = container.parentElement;
696
+ const checkLi = container && container.closest && container.closest(".an-checklist li");
697
+ if (checkLi) {
698
+ _checklistItemToP(checkLi);
699
+ return;
700
+ }
701
+ }
702
+ execCommand("outdent");
703
+ }
704
+ /**
705
+ * G.5 helper: splits a checklist at checkLi, converts it to a <p>,
706
+ * and keeps items before/after as separate checklists.
707
+ * @param {HTMLElement} checkLi
637
708
  */
638
- var outdent = () => execCommand("outdent");
709
+ function _checklistItemToP(checkLi) {
710
+ const checkUl = checkLi.closest(".an-checklist");
711
+ if (!checkUl) return;
712
+ const allLis = Array.from(checkUl.children);
713
+ const liIndex = allLis.indexOf(checkLi);
714
+ const afterLis = allLis.slice(liIndex + 1);
715
+ const p = document.createElement("p");
716
+ p.textContent = Array.from(checkLi.childNodes).filter((n) => !(n.nodeType === 1 && n.tagName === "INPUT")).map((n) => n.textContent).join("").replace(/\u200B/g, "").trim() || "\xA0";
717
+ if (afterLis.length > 0) {
718
+ const newUl = document.createElement("ul");
719
+ newUl.className = "an-checklist";
720
+ afterLis.forEach((li) => newUl.appendChild(li));
721
+ checkUl.parentNode.insertBefore(newUl, checkUl.nextSibling);
722
+ }
723
+ checkUl.parentNode.insertBefore(p, checkUl.nextSibling);
724
+ checkUl.removeChild(checkLi);
725
+ if (checkUl.children.length === 0) checkUl.parentNode.removeChild(checkUl);
726
+ try {
727
+ const nr = document.createRange();
728
+ const firstChild = p.firstChild;
729
+ nr.setStart(firstChild && firstChild.nodeType === 3 ? firstChild : p, 0);
730
+ nr.collapse(true);
731
+ const s = window.getSelection();
732
+ if (s) {
733
+ s.removeAllRanges();
734
+ s.addRange(nr);
735
+ }
736
+ } catch {}
737
+ }
639
738
  /**
640
739
  * Inserts an unordered list or converts selection.
641
740
  */
@@ -710,9 +809,24 @@ function toggleInlineCode(editable) {
710
809
  const codeEl = container && container.closest ? container.closest("code") : null;
711
810
  if (codeEl && !codeEl.closest("pre")) {
712
811
  const parent = codeEl.parentNode;
812
+ const prevSibling = codeEl.previousSibling;
813
+ const movedChildren = Array.from(codeEl.childNodes);
713
814
  while (codeEl.firstChild) parent.insertBefore(codeEl.firstChild, codeEl);
714
815
  parent.removeChild(codeEl);
715
- if (editable) editable.normalize();
816
+ if (parent && parent.normalize) parent.normalize();
817
+ if (movedChildren.length > 0) try {
818
+ const firstMoved = movedChildren[0];
819
+ const lastMoved = movedChildren[movedChildren.length - 1];
820
+ const nr = document.createRange();
821
+ const anchorNode = firstMoved.parentNode === parent ? firstMoved : prevSibling ? prevSibling.nextSibling : parent.firstChild;
822
+ if (anchorNode) {
823
+ nr.setStart(anchorNode, 0);
824
+ const endAnchor = lastMoved.parentNode === parent ? lastMoved : anchorNode;
825
+ nr.setEnd(endAnchor, endAnchor.nodeType === Node.TEXT_NODE ? endAnchor.textContent.length : endAnchor.childNodes.length);
826
+ sel.removeAllRanges();
827
+ sel.addRange(nr);
828
+ }
829
+ } catch (_) {}
716
830
  } else {
717
831
  if (range.collapsed) return;
718
832
  try {
@@ -737,14 +851,17 @@ function toggleInlineCode(editable) {
737
851
  /**
738
852
  * Returns true when the cursor / selection is inside an inline <code>
739
853
  * (not nested in a <pre>).
854
+ * Uses startContainer for reliable cross-browser detection regardless of
855
+ * whether the selection is collapsed or a range (commonAncestorContainer
856
+ * can behave inconsistently for range selections on some browsers).
740
857
  * @returns {boolean}
741
858
  */
742
859
  function isInlineCode() {
743
860
  const sel = window.getSelection();
744
861
  if (!sel || !sel.rangeCount) return false;
745
- let container = sel.getRangeAt(0).commonAncestorContainer;
746
- if (container.nodeType === 3) container = container.parentElement;
747
- const code = container && container.closest ? container.closest("code") : null;
862
+ let sc = sel.getRangeAt(0).startContainer;
863
+ if (sc.nodeType === 3) sc = sc.parentElement;
864
+ const code = sc && sc.closest ? sc.closest("code") : null;
748
865
  return !!(code && !code.closest("pre"));
749
866
  }
750
867
  /**
@@ -755,7 +872,8 @@ function isInlineCode() {
755
872
  function toggleChecklist() {
756
873
  const sel = window.getSelection();
757
874
  if (!sel || !sel.rangeCount) return;
758
- let container = sel.getRangeAt(0).commonAncestorContainer;
875
+ const range = sel.getRangeAt(0);
876
+ let container = range.commonAncestorContainer;
759
877
  if (container.nodeType === 3) container = container.parentElement;
760
878
  const ul = container.closest && container.closest(".an-checklist");
761
879
  if (ul) {
@@ -781,10 +899,99 @@ function toggleChecklist() {
781
899
  return;
782
900
  }
783
901
  }
784
- const lines = sel.toString().split(/\r?\n/).filter((l) => l.trim().length > 0);
785
- if (lines.length === 0) return;
786
- const items = lines.map((l) => `<li><input type="checkbox" contenteditable="false">${l || "​"}</li>`).join("");
787
- document.execCommand("insertHTML", false, `<ul class="an-checklist">${items}</ul>`);
902
+ if (range.collapsed) {
903
+ const BLOCK_TAGS = new Set([
904
+ "P",
905
+ "DIV",
906
+ "H1",
907
+ "H2",
908
+ "H3",
909
+ "H4",
910
+ "H5",
911
+ "H6",
912
+ "BLOCKQUOTE",
913
+ "LI"
914
+ ]);
915
+ let block = container;
916
+ while (block && block.parentNode && !BLOCK_TAGS.has(block.tagName)) block = block.parentNode;
917
+ const itemText = block && BLOCK_TAGS.has(block.tagName) ? Array.from(block.childNodes).map((n) => n.textContent).join("").replace(/\u00a0/g, " ") : "";
918
+ const ul = document.createElement("ul");
919
+ ul.className = "an-checklist";
920
+ const li = document.createElement("li");
921
+ const checkbox = document.createElement("input");
922
+ checkbox.type = "checkbox";
923
+ checkbox.contentEditable = "false";
924
+ li.appendChild(checkbox);
925
+ li.appendChild(document.createTextNode(itemText || "​"));
926
+ ul.appendChild(li);
927
+ if (block && BLOCK_TAGS.has(block.tagName)) block.parentNode.replaceChild(ul, block);
928
+ else {
929
+ document.execCommand("insertHTML", false, ul.outerHTML);
930
+ return;
931
+ }
932
+ const textNode = li.lastChild;
933
+ const nr = document.createRange();
934
+ const offset = textNode.nodeType === Node.TEXT_NODE ? textNode.textContent.length : 0;
935
+ nr.setStart(textNode, offset);
936
+ nr.collapse(true);
937
+ sel.removeAllRanges();
938
+ sel.addRange(nr);
939
+ return;
940
+ }
941
+ if (!sel.toString().replace(/[\u00a0\u200B]/g, " ").trim()) return;
942
+ const BLOCK_TAGS_MULTI = new Set([
943
+ "P",
944
+ "DIV",
945
+ "H1",
946
+ "H2",
947
+ "H3",
948
+ "H4",
949
+ "H5",
950
+ "H6",
951
+ "BLOCKQUOTE",
952
+ "PRE",
953
+ "LI"
954
+ ]);
955
+ const blocks = [];
956
+ const seenBlocks = /* @__PURE__ */ new Set();
957
+ const commonAncestor = range.commonAncestorContainer;
958
+ const iter = document.createNodeIterator(commonAncestor.nodeType === Node.TEXT_NODE ? commonAncestor.parentNode : commonAncestor, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT, null);
959
+ let node;
960
+ while (node = iter.nextNode()) {
961
+ if (!range.intersectsNode(node)) continue;
962
+ let block = node.nodeType === Node.TEXT_NODE ? node.parentElement : node;
963
+ while (block && !BLOCK_TAGS_MULTI.has(block.tagName)) block = block.parentElement;
964
+ if (block && !seenBlocks.has(block)) {
965
+ seenBlocks.add(block);
966
+ blocks.push(block);
967
+ }
968
+ }
969
+ if (blocks.length === 0) return;
970
+ const newUl = document.createElement("ul");
971
+ newUl.className = "an-checklist";
972
+ let lastTextNode = null;
973
+ blocks.forEach((block) => {
974
+ const li = document.createElement("li");
975
+ const cb = document.createElement("input");
976
+ cb.type = "checkbox";
977
+ cb.setAttribute("contenteditable", "false");
978
+ li.appendChild(cb);
979
+ const blockText = Array.from(block.childNodes).map((n) => n.textContent).join("").replace(/[\u00a0\u200B]/g, " ").trim();
980
+ const tn = document.createTextNode(blockText || "​");
981
+ li.appendChild(tn);
982
+ newUl.appendChild(li);
983
+ lastTextNode = tn;
984
+ });
985
+ const firstBlock = blocks[0];
986
+ firstBlock.parentNode.insertBefore(newUl, firstBlock);
987
+ blocks.forEach((block) => block.parentNode && block.parentNode.removeChild(block));
988
+ if (lastTextNode) {
989
+ const nr = document.createRange();
990
+ nr.setStart(lastTextNode, lastTextNode.textContent.length);
991
+ nr.collapse(true);
992
+ sel.removeAllRanges();
993
+ sel.addRange(nr);
994
+ }
788
995
  }
789
996
  /**
790
997
  * Returns true when the cursor is inside a checklist item.
@@ -849,9 +1056,9 @@ var underlineBtn = btn("underline", "underline", "Underline (Ctrl+U)", () => und
849
1056
  if (document.queryCommandState("underline")) return true;
850
1057
  const sel = window.getSelection();
851
1058
  if (!sel || !sel.rangeCount) return false;
852
- let container = sel.getRangeAt(0).commonAncestorContainer;
853
- if (container.nodeType === 3) container = container.parentElement;
854
- return !!(container && container.closest && container.closest("u"));
1059
+ let sc = sel.getRangeAt(0).startContainer;
1060
+ if (sc.nodeType === 3) sc = sc.parentElement;
1061
+ return !!(sc && sc.closest && sc.closest("u"));
855
1062
  });
856
1063
  var strikeBtn = btn("strikethrough", "strikethrough", "Strikethrough", () => strikethrough(), () => document.queryCommandState("strikeThrough"));
857
1064
  var superscriptBtn = btn("superscript", "superscript", "Superscript", () => superscript(), () => document.queryCommandState("superscript"));
@@ -1254,7 +1461,6 @@ var PROHIBITED_TAGS = [
1254
1461
  "object",
1255
1462
  "embed",
1256
1463
  "form",
1257
- "input",
1258
1464
  "button"
1259
1465
  ];
1260
1466
  /** Attributes whose values must be sanitised as URLs. */
@@ -1278,7 +1484,8 @@ var TRUSTED_IFRAME_HOSTS = new Set([
1278
1484
  * Uses DOMParser so the sanitisation follows normal browser parsing rules —
1279
1485
  * no regex shortcuts that can be bypassed by encoding tricks.
1280
1486
  *
1281
- * - Strips PROHIBITED_TAGS (script, style, iframe, object, embed, form, input, button)
1487
+ * - Strips PROHIBITED_TAGS (script, style, iframe, object, embed, form, button)
1488
+ * - Allows input[type="checkbox"] only inside ul.an-checklist li; removes all other <input>
1282
1489
  * - Removes all on* event-handler attributes
1283
1490
  * - Rejects javascript: and vbscript: URLs in URL attributes
1284
1491
  * - Rejects data: URIs everywhere except img[src] (base64 uploads)
@@ -1317,6 +1524,16 @@ function sanitiseHTML(html, { allowIframes = false } = {}) {
1317
1524
  }
1318
1525
  });
1319
1526
  });
1527
+ doc.querySelectorAll("input").forEach((el) => {
1528
+ if (!(el.closest("ul.an-checklist") !== null && el.closest("li") !== null) || el.getAttribute("type") !== "checkbox") el.remove();
1529
+ else Array.from(el.attributes).forEach((attr) => {
1530
+ if (![
1531
+ "type",
1532
+ "checked",
1533
+ "contenteditable"
1534
+ ].includes(attr.name)) el.removeAttribute(attr.name);
1535
+ });
1536
+ });
1320
1537
  return doc.body.innerHTML;
1321
1538
  }
1322
1539
  /**
@@ -1397,7 +1614,12 @@ function renderLayout(targetEl, options) {
1397
1614
  if (options.maxHeight) editable.style.maxHeight = `${options.maxHeight}px`;
1398
1615
  container.appendChild(editable);
1399
1616
  if (options.theme === "dark") container.classList.add("an-theme-dark");
1400
- if (options.readOnly) container.classList.add("an-disabled");
1617
+ if (options.readOnly) {
1618
+ container.classList.add("an-disabled");
1619
+ editable.querySelectorAll("ul.an-checklist input[type=\"checkbox\"]").forEach((cb) => {
1620
+ cb.setAttribute("disabled", "");
1621
+ });
1622
+ }
1401
1623
  if (options.direction === "rtl") {
1402
1624
  editable.setAttribute("dir", "rtl");
1403
1625
  container.classList.add("an-dir-rtl");
@@ -1857,7 +2079,7 @@ function handleKeydown(event, editable, options = {}) {
1857
2079
  const para = closestPara(range.sc, editable);
1858
2080
  if (para && isLi(para)) {
1859
2081
  event.preventDefault();
1860
- if (event.shiftKey) execCommand("outdent");
2082
+ if (event.shiftKey) outdent();
1861
2083
  else execCommand("indent");
1862
2084
  return true;
1863
2085
  }
@@ -2276,7 +2498,42 @@ var Editor = class {
2276
2498
  sel.removeAllRanges();
2277
2499
  sel.addRange(nr);
2278
2500
  };
2279
- this._disposers.push(on(editable, "keydown", onKeydown), on(editable, "beforeinput", onBeforeInput), on(editable, "input", onInput), on(document, "selectionchange", onSelChange), on(editable, "click", onCheckboxClick), on(editable, "mouseup", fixChecklistCursor), on(editable, "keyup", fixChecklistCursor));
2501
+ const isReadOnly = () => this.context.layoutInfo.container.classList.contains("an-disabled");
2502
+ this._disposers.push(on(editable, "keydown", onKeydown), on(editable, "beforeinput", onBeforeInput), on(editable, "input", onInput), on(document, "selectionchange", onSelChange), on(editable, "click", onCheckboxClick), on(editable, "mouseup", fixChecklistCursor), on(editable, "keyup", fixChecklistCursor), on(editable, "dragstart", (e) => {
2503
+ if (isReadOnly()) {
2504
+ e.preventDefault();
2505
+ return;
2506
+ }
2507
+ const target = e.target;
2508
+ if (target && (target.nodeName === "IFRAME" || target.closest && target.closest(".an-video-wrapper"))) e.preventDefault();
2509
+ }), on(editable, "drop", (e) => {
2510
+ if (isReadOnly()) e.preventDefault();
2511
+ }));
2512
+ /** @type {string|null} 'superscript' | 'subscript' | null */
2513
+ let _compositionSupSub = null;
2514
+ const onCompositionStart = () => {
2515
+ const sel = window.getSelection();
2516
+ if (!sel || !sel.rangeCount) {
2517
+ _compositionSupSub = null;
2518
+ return;
2519
+ }
2520
+ let node = sel.getRangeAt(0).startContainer;
2521
+ if (node.nodeType === Node.TEXT_NODE) node = node.parentElement;
2522
+ if (node && node.closest) if (node.closest("sup")) _compositionSupSub = "superscript";
2523
+ else if (node.closest("sub")) _compositionSupSub = "subscript";
2524
+ else _compositionSupSub = null;
2525
+ };
2526
+ const onCompositionEnd = () => {
2527
+ const tag = _compositionSupSub;
2528
+ _compositionSupSub = null;
2529
+ if (!tag) return;
2530
+ const sel = window.getSelection();
2531
+ if (!sel || !sel.rangeCount) return;
2532
+ let node = sel.getRangeAt(0).startContainer;
2533
+ if (node.nodeType === Node.TEXT_NODE) node = node.parentElement;
2534
+ if (!(node && node.closest && (tag === "superscript" ? node.closest("sup") : node.closest("sub")))) document.execCommand(tag);
2535
+ };
2536
+ this._disposers.push(on(editable, "compositionstart", onCompositionStart), on(editable, "compositionend", onCompositionEnd));
2280
2537
  }
2281
2538
  _onKeydown(event) {
2282
2539
  const editable = this.context.layoutInfo.editable;
@@ -2362,6 +2619,7 @@ var Editor = class {
2362
2619
  }
2363
2620
  }
2364
2621
  afterCommand() {
2622
+ this._cleanOrphanedFigures();
2365
2623
  this.context.invoke("toolbar.refresh");
2366
2624
  this.context.invoke("statusbar.update");
2367
2625
  this._scheduleSnapshot();
@@ -2378,6 +2636,16 @@ var Editor = class {
2378
2636
  this.context.triggerEvent("change", this.getHTML());
2379
2637
  }, 400);
2380
2638
  }
2639
+ /**
2640
+ * C4: Removes figure.an-figure elements that no longer contain an <img>.
2641
+ * This happens when a user selects only the image (not the whole figure)
2642
+ * and deletes or replaces it, leaving a dangling figcaption.
2643
+ */
2644
+ _cleanOrphanedFigures() {
2645
+ this.context.layoutInfo.editable.querySelectorAll("figure.an-figure").forEach((fig) => {
2646
+ if (!fig.querySelector("img")) fig.parentNode.removeChild(fig);
2647
+ });
2648
+ }
2381
2649
  focus() {
2382
2650
  this.context.layoutInfo.editable.focus();
2383
2651
  }
@@ -2394,7 +2662,7 @@ var Editor = class {
2394
2662
  * @param {string} html - HTML string (will be sanitised)
2395
2663
  */
2396
2664
  setHTML(html) {
2397
- this.context.layoutInfo.editable.innerHTML = sanitiseHTML(html);
2665
+ this.context.layoutInfo.editable.innerHTML = sanitiseHTML(html, { allowIframes: true });
2398
2666
  if (this._history) this._history.reset();
2399
2667
  this.afterCommand();
2400
2668
  }
@@ -3109,15 +3377,28 @@ var Toolbar = class {
3109
3377
  if (def.name === "fontFamily" && !isHeader) opt.style.fontFamily = value;
3110
3378
  select.appendChild(opt);
3111
3379
  });
3380
+ /** @type {Range|null} */
3381
+ let _savedRange = null;
3382
+ const dMousedown = on(select, "mousedown", () => {
3383
+ const sel = window.getSelection();
3384
+ _savedRange = sel && sel.rangeCount ? sel.getRangeAt(0).cloneRange() : null;
3385
+ });
3112
3386
  const disposer = on(select, "change", (e) => {
3113
3387
  const value = e.target.value;
3114
3388
  const selectedOpt = e.target.options[e.target.selectedIndex];
3115
3389
  if (!value || selectedOpt.disabled) return;
3116
3390
  this.context.invoke("editor.focus");
3391
+ if (_savedRange) try {
3392
+ const sel = window.getSelection();
3393
+ if (sel) {
3394
+ sel.removeAllRanges();
3395
+ sel.addRange(_savedRange);
3396
+ }
3397
+ } catch (_) {}
3117
3398
  def.action(this.context, value);
3118
3399
  this.context.invoke("editor.afterCommand");
3119
3400
  });
3120
- this._disposers.push(disposer);
3401
+ this._disposers.push(dMousedown, disposer);
3121
3402
  return select;
3122
3403
  }
3123
3404
  /**
@@ -3574,9 +3855,25 @@ var Clipboard = class {
3574
3855
  this.options.onImageUpload(files);
3575
3856
  return;
3576
3857
  }
3858
+ const UNSUPPORTED = [
3859
+ "image/tiff",
3860
+ "image/x-tiff",
3861
+ "image/bmp",
3862
+ "image/x-bmp",
3863
+ "image/x-ms-bmp"
3864
+ ];
3577
3865
  const maxBytes = (this.options.maxImageSize || 5) * 1024 * 1024;
3578
3866
  files.forEach((file) => {
3579
3867
  if (!file || !file.type.startsWith("image/")) return;
3868
+ if (UNSUPPORTED.includes(file.type)) {
3869
+ const message = `Image format "${file.type}" is not supported for display in web browsers. Please convert to PNG, JPEG, or WebP first.`;
3870
+ this.context.triggerEvent("imageError", {
3871
+ file,
3872
+ message
3873
+ });
3874
+ console.warn("[AutumnNote]", message);
3875
+ return;
3876
+ }
3580
3877
  if (file.size > maxBytes) {
3581
3878
  const message = `Image "${file.name}" exceeds the ${this.options.maxImageSize || 5} MB size limit.`;
3582
3879
  this.context.triggerEvent("imageError", {
@@ -3740,7 +4037,7 @@ var Placeholder = class {
3740
4037
  _update() {
3741
4038
  const editable = this.context.layoutInfo.editable;
3742
4039
  const isFocused = document.activeElement === editable;
3743
- const isEmpty = !editable.textContent.trim() && !editable.querySelector("img, table, hr");
4040
+ const isEmpty = !(editable.textContent.replace(/\u200B/g, "").trim().length > 0) && !editable.querySelector("img, table, hr, .an-video-wrapper");
3744
4041
  editable.classList.toggle("an-placeholder", isEmpty && !isFocused);
3745
4042
  }
3746
4043
  };
@@ -4175,12 +4472,14 @@ var ImageDialog = class {
4175
4472
  const fileInput = createElement("input", {
4176
4473
  type: "file",
4177
4474
  class: "an-input",
4178
- accept: "image/*"
4475
+ accept: "image/jpeg,image/png,image/gif,image/webp,image/svg+xml,image/avif"
4179
4476
  });
4180
4477
  this._fileInput = fileInput;
4478
+ const fileHint = createElement("p", { class: "an-dialog-hint" });
4479
+ this._fileHint = fileHint;
4181
4480
  const d = on(fileInput, "change", () => this._onFileChange());
4182
4481
  this._disposers.push(d);
4183
- box.append(fileLabel, fileInput);
4482
+ box.append(fileLabel, fileInput, fileHint);
4184
4483
  }
4185
4484
  const btnRow = createElement("div", { class: "an-dialog-actions" });
4186
4485
  const insertBtn = createElement("button", {
@@ -4220,9 +4519,28 @@ var ImageDialog = class {
4220
4519
  _onFileChange() {
4221
4520
  const file = this._fileInput && this._fileInput.files && this._fileInput.files[0];
4222
4521
  if (!file || !file.type.startsWith("image/")) return;
4522
+ if (!new Set([
4523
+ "image/jpeg",
4524
+ "image/png",
4525
+ "image/gif",
4526
+ "image/webp",
4527
+ "image/svg+xml",
4528
+ "image/avif"
4529
+ ]).has(file.type)) {
4530
+ const message = `Format "${file.type}" is not supported for display in web browsers. Please convert to JPEG, PNG, or WebP first.`;
4531
+ if (this._fileHint) this._fileHint.textContent = message;
4532
+ this.context.triggerEvent("imageError", {
4533
+ file,
4534
+ message
4535
+ });
4536
+ this._fileInput.value = "";
4537
+ return;
4538
+ }
4539
+ if (this._fileHint) this._fileHint.textContent = "";
4223
4540
  const maxSize = (this.options.maxImageSize || 5) * 1024 * 1024;
4224
4541
  if (file.size > maxSize) {
4225
4542
  const message = `Image file is too large. Maximum allowed size is ${this.options.maxImageSize || 5} MB.`;
4543
+ if (this._fileHint) this._fileHint.textContent = message;
4226
4544
  console.warn("[AutumnNote] ImageDialog:", message);
4227
4545
  this.context.triggerEvent("imageError", {
4228
4546
  file,
@@ -4534,6 +4852,7 @@ var ImageResizer = class {
4534
4852
  _resizeDebounce = setTimeout(() => this._updateOverlayPosition(), 100);
4535
4853
  };
4536
4854
  this._disposers.push(on(editable, "click", (e) => this._onEditorClick(e)), on(editable, "contextmenu", (e) => {
4855
+ if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
4537
4856
  const img = e.target.closest("img");
4538
4857
  if (img) this._select(img);
4539
4858
  }), 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 }));
@@ -4584,6 +4903,7 @@ var ImageResizer = class {
4584
4903
  return overlay;
4585
4904
  }
4586
4905
  _onEditorClick(e) {
4906
+ if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
4587
4907
  const img = e.target.closest("img");
4588
4908
  if (img) {
4589
4909
  e.preventDefault();
@@ -4740,9 +5060,12 @@ var VideoResizer = class {
4740
5060
  _resizeDebounce = setTimeout(() => this._updateOverlayPosition(), 100);
4741
5061
  };
4742
5062
  this._disposers.push(on(editable, "click", (e) => this._onEditorClick(e)), on(editable, "contextmenu", (e) => {
5063
+ if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
4743
5064
  const wrapper = this._findWrapper(e.target);
4744
5065
  if (wrapper) this._select(wrapper);
4745
- }), 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 }));
5066
+ }), 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) => {
5067
+ if (e.target instanceof Element && e.target.closest(".an-video-wrapper")) e.preventDefault();
5068
+ }));
4746
5069
  return this;
4747
5070
  }
4748
5071
  destroy() {
@@ -4801,6 +5124,7 @@ var VideoResizer = class {
4801
5124
  return overlay;
4802
5125
  }
4803
5126
  _onEditorClick(e) {
5127
+ if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
4804
5128
  const wrapper = this._findWrapper(e.target);
4805
5129
  if (wrapper) {
4806
5130
  e.preventDefault();
@@ -4999,9 +5323,12 @@ var LinkTooltip = class {
4999
5323
  }, HIDE_DELAY$4);
5000
5324
  }
5001
5325
  _show(anchor) {
5326
+ const isReadOnly = this.context.layoutInfo.container.classList.contains("an-disabled");
5002
5327
  const url = anchor.getAttribute("href") || "";
5003
5328
  this._urlLabel.textContent = this._truncateUrl(url);
5004
5329
  this._urlLabel.title = url;
5330
+ this._editBtn.style.display = isReadOnly ? "none" : "";
5331
+ this._unlinkBtn.style.display = isReadOnly ? "none" : "";
5005
5332
  this._el.style.display = "flex";
5006
5333
  this._positionNear(anchor);
5007
5334
  }
@@ -5115,6 +5442,7 @@ var ImageTooltip = class {
5115
5442
  document.body.appendChild(this._el);
5116
5443
  const editable = this.context.layoutInfo.editable;
5117
5444
  this._disposers.push(on(editable, "mouseover", (e) => {
5445
+ if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
5118
5446
  const img = e.target.closest("img");
5119
5447
  if (img && editable.contains(img) && !img.closest("a[href]")) this._scheduleShow(img);
5120
5448
  }, { passive: true }), on(editable, "mouseout", (e) => {
@@ -5241,6 +5569,7 @@ var ImageTooltip = class {
5241
5569
  const target = img.closest("figure.an-figure") || img;
5242
5570
  target.style.float = value;
5243
5571
  target.style.display = "";
5572
+ if (target !== img) target.style.width = "";
5244
5573
  target.style.marginLeft = value === "right" ? "12px" : "";
5245
5574
  target.style.marginRight = value === "left" ? "12px" : "";
5246
5575
  this.context.invoke("editor.afterCommand");
@@ -5255,6 +5584,7 @@ var ImageTooltip = class {
5255
5584
  const target = img.closest("figure.an-figure") || img;
5256
5585
  target.style.float = "";
5257
5586
  target.style.display = "block";
5587
+ if (target !== img) target.style.width = "fit-content";
5258
5588
  target.style.marginLeft = "auto";
5259
5589
  target.style.marginRight = "auto";
5260
5590
  this.context.invoke("editor.afterCommand");
@@ -5339,6 +5669,7 @@ var ImageTooltip = class {
5339
5669
  img.style.marginRight = "";
5340
5670
  } else if (img.style.display === "block" && img.style.marginLeft === "auto") {
5341
5671
  figure.style.display = "block";
5672
+ figure.style.width = "fit-content";
5342
5673
  figure.style.marginLeft = "auto";
5343
5674
  figure.style.marginRight = "auto";
5344
5675
  img.style.display = "";
@@ -5393,6 +5724,7 @@ var VideoTooltip = class {
5393
5724
  document.body.appendChild(this._el);
5394
5725
  const editable = this.context.layoutInfo.editable;
5395
5726
  this._disposers.push(on(editable, "mouseover", (e) => {
5727
+ if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
5396
5728
  const wrapper = e.target.closest(".an-video-wrapper");
5397
5729
  if (wrapper && editable.contains(wrapper)) this._scheduleShow(wrapper);
5398
5730
  }, { passive: true }), on(editable, "mouseout", (e) => {
@@ -5649,6 +5981,47 @@ function getCellAfterVisualCol(row, visualIdx) {
5649
5981
  }
5650
5982
  return null;
5651
5983
  }
5984
+ /**
5985
+ * Build a 2D grid map of the table, accounting for both rowspan and colspan.
5986
+ *
5987
+ * gridMap[r][c] = the DOM cell occupying visual grid position (r, c).
5988
+ * cellPos = WeakMap: cell → { r, c, rs, cs } (top-left grid origin + span).
5989
+ *
5990
+ * Uses HTMLTableElement.rows which is scoped to the table itself and never
5991
+ * includes rows from nested tables.
5992
+ *
5993
+ * @param {HTMLTableElement} table
5994
+ * @returns {{ gridMap: Object, cellPos: WeakMap }}
5995
+ */
5996
+ function buildGridMap(table) {
5997
+ const rows = Array.from(table.rows);
5998
+ const gridMap = {};
5999
+ const cellPos = /* @__PURE__ */ new WeakMap();
6000
+ rows.forEach((row, r) => {
6001
+ if (!gridMap[r]) gridMap[r] = {};
6002
+ let c = 0;
6003
+ for (const cell of row.cells) {
6004
+ while (gridMap[r][c]) c++;
6005
+ const rs = cell.rowSpan || 1;
6006
+ const cs = cell.colSpan || 1;
6007
+ cellPos.set(cell, {
6008
+ r,
6009
+ c,
6010
+ rs,
6011
+ cs
6012
+ });
6013
+ for (let dr = 0; dr < rs; dr++) {
6014
+ if (!gridMap[r + dr]) gridMap[r + dr] = {};
6015
+ for (let dc = 0; dc < cs; dc++) gridMap[r + dr][c + dc] = cell;
6016
+ }
6017
+ c += cs;
6018
+ }
6019
+ });
6020
+ return {
6021
+ gridMap,
6022
+ cellPos
6023
+ };
6024
+ }
5652
6025
  var ICONS$2 = {
5653
6026
  rowAbove: `<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="12" x2="21" y2="12"/><path d="M12 3v7"/><path d="M9 7l3-4 3 4"/></svg>`,
5654
6027
  rowBelow: `<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="12" x2="21" y2="12"/><path d="M12 12v7"/><path d="M9 17l3 4 3-4"/></svg>`,
@@ -5657,10 +6030,12 @@ var ICONS$2 = {
5657
6030
  colRight: `<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="12" y1="3" x2="12" y2="21"/><path d="M12 12h9"/><path d="M17 8l4 4-4 4"/></svg>`,
5658
6031
  deleteCol: `<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="12" y1="3" x2="12" y2="21"/><line x1="15" y1="6" x2="21" y2="12"/><line x1="21" y1="6" x2="15" y2="12"/></svg>`,
5659
6032
  mergeCells: `<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="2" y="7" width="8" height="10" rx="1"/><rect x="14" y="7" width="8" height="10" rx="1"/><path d="M10 12h4"/><path d="M12 10l2 2-2 2"/></svg>`,
6033
+ unmergeCells: `<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="2" y="5" width="20" height="14" rx="1"/><line x1="12" y1="5" x2="12" y2="19" stroke-dasharray="2.5 2"/><line x1="2" y1="12" x2="22" y2="12" stroke-dasharray="2.5 2"/><path d="M9 9 L6 12 L9 15"/><path d="M15 9 L18 12 L15 15"/></svg>`,
5660
6034
  colWidth: `<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="7" y1="4" x2="7" y2="20"/><line x1="17" y1="4" x2="17" y2="20"/><line x1="7" y1="12" x2="17" y2="12"/><path d="M10 9l-3 3 3 3"/><path d="M14 9l3 3-3 3"/></svg>`,
5661
6035
  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>`,
5662
6036
  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>`,
5663
- 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>`
6037
+ 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>`,
6038
+ 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>`
5664
6039
  };
5665
6040
  var TableTooltip = class {
5666
6041
  /** @param {import('../Context.js').Context} context */
@@ -5676,6 +6051,12 @@ var TableTooltip = class {
5676
6051
  this._sizeApply = null;
5677
6052
  this._sizeTitleEl = null;
5678
6053
  this._sizeInputEl = null;
6054
+ this._selectMode = false;
6055
+ this._selectedCells = [];
6056
+ this._selectStart = null;
6057
+ this._selectDragging = false;
6058
+ this._selectBtn = null;
6059
+ this._editable = null;
5679
6060
  }
5680
6061
  initialize() {
5681
6062
  this._el = this._buildTooltip();
@@ -5683,7 +6064,31 @@ var TableTooltip = class {
5683
6064
  this._sizePopover = this._buildSizePopover();
5684
6065
  document.body.appendChild(this._sizePopover);
5685
6066
  const editable = this.context.layoutInfo.editable;
6067
+ this._editable = editable;
6068
+ const onSelMousedown = (e) => {
6069
+ if (!this._selectMode) return;
6070
+ const cell = e.target.closest("td, th");
6071
+ if (!cell || !editable.contains(cell)) return;
6072
+ if (cell.style.cursor === "col-resize" || cell.style.cursor === "row-resize") return;
6073
+ e.preventDefault();
6074
+ this._activeTable = cell.closest("table");
6075
+ this._selectStart = cell;
6076
+ this._selectDragging = true;
6077
+ this._setSelection([cell]);
6078
+ };
6079
+ const onSelMousemove = (e) => {
6080
+ if (!this._selectMode || !this._selectDragging || !this._selectStart) return;
6081
+ const cell = e.target.closest("td, th");
6082
+ if (!cell || !editable.contains(cell)) return;
6083
+ if (cell.closest("table") !== this._activeTable) return;
6084
+ this._setSelection(this._getRectCells(this._selectStart, cell));
6085
+ };
6086
+ const onSelMouseup = () => {
6087
+ this._selectDragging = false;
6088
+ };
6089
+ this._disposers.push(on(editable, "mousedown", onSelMousedown), on(editable, "mousemove", onSelMousemove), on(document, "mouseup", onSelMouseup));
5686
6090
  this._disposers.push(on(editable, "mouseover", (e) => {
6091
+ if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
5687
6092
  const table = e.target.closest("table");
5688
6093
  if (table && editable.contains(table)) {
5689
6094
  const cell = e.target.closest("td, th");
@@ -5691,9 +6096,11 @@ var TableTooltip = class {
5691
6096
  this._scheduleShow(table);
5692
6097
  }
5693
6098
  }, { passive: true }), on(editable, "mouseout", (e) => {
6099
+ if (this._selectMode) return;
5694
6100
  const to = e.relatedTarget;
5695
6101
  if (!to || !editable.contains(to) && !this._el.contains(to) && !(this._sizePopover && this._sizePopover.contains(to))) this._scheduleHide();
5696
6102
  }, { passive: true }), on(document, "click", (e) => {
6103
+ if (this._selectMode && this._activeTable && this._activeTable.contains(e.target)) return;
5697
6104
  if (this._activeTable && !this._activeTable.contains(e.target) && !this._el.contains(e.target) && !(this._sizePopover && this._sizePopover.contains(e.target))) this._hide();
5698
6105
  }));
5699
6106
  this._initResize();
@@ -5725,6 +6132,10 @@ var TableTooltip = class {
5725
6132
  };
5726
6133
  const onEditorMove = (e) => {
5727
6134
  if (_resizing) return;
6135
+ if (this.context.layoutInfo.container.classList.contains("an-disabled")) {
6136
+ clearHover();
6137
+ return;
6138
+ }
5728
6139
  if (_rafEditorMove !== null) return;
5729
6140
  const target = e.target;
5730
6141
  const clientX = e.clientX;
@@ -5752,6 +6163,7 @@ var TableTooltip = class {
5752
6163
  });
5753
6164
  };
5754
6165
  const onEditorDown = (e) => {
6166
+ if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
5755
6167
  if (!_nearCell || !_nearEdge) return;
5756
6168
  _resizing = true;
5757
6169
  _edge = _nearEdge;
@@ -5761,7 +6173,7 @@ var TableTooltip = class {
5761
6173
  if (_edge === "col") {
5762
6174
  _startW = _nearCell.offsetWidth;
5763
6175
  _colIdx = getVisualColIndex(_nearCell);
5764
- _colCells = _colIdx >= 0 ? Array.from(_table.querySelectorAll("tr")).map((r) => getCellAtVisualCol(r, _colIdx)).filter(Boolean) : [];
6176
+ _colCells = _colIdx >= 0 ? Array.from(_table.querySelectorAll("tr")).map((r) => getCellAtVisualCol(r, _colIdx)).filter(Boolean).filter((c) => (c.colSpan || 1) === 1) : [];
5765
6177
  document.body.style.cursor = "col-resize";
5766
6178
  } else {
5767
6179
  _row = _nearCell.closest("tr");
@@ -5832,6 +6244,9 @@ var TableTooltip = class {
5832
6244
  this._label.textContent = "Table";
5833
6245
  el.appendChild(this._label);
5834
6246
  el.appendChild(this._sep());
6247
+ this._selectBtn = this._makeBtn(ICONS$2.selectCells, "Select Cells", () => this._toggleSelectMode());
6248
+ el.appendChild(this._selectBtn);
6249
+ el.appendChild(this._sep());
5835
6250
  el.appendChild(this._makeBtn(ICONS$2.rowAbove, "Add Row Above", () => this._addRow("above")));
5836
6251
  el.appendChild(this._makeBtn(ICONS$2.rowBelow, "Add Row Below", () => this._addRow("below")));
5837
6252
  el.appendChild(this._makeBtn(ICONS$2.deleteRow, "Delete Row", () => this._deleteRow()));
@@ -5841,6 +6256,7 @@ var TableTooltip = class {
5841
6256
  el.appendChild(this._makeBtn(ICONS$2.deleteCol, "Delete Column", () => this._deleteColumn()));
5842
6257
  el.appendChild(this._sep());
5843
6258
  el.appendChild(this._makeBtn(ICONS$2.mergeCells, "Merge Cells", () => this._mergeCells()));
6259
+ el.appendChild(this._makeBtn(ICONS$2.unmergeCells, "Unmerge Cells", () => this._unmergeCells()));
5844
6260
  el.appendChild(this._sep());
5845
6261
  el.appendChild(this._makeBtn(ICONS$2.colWidth, "Column Width", () => this._openSizePopover("col")));
5846
6262
  el.appendChild(this._makeBtn(ICONS$2.rowHeight, "Row Height", () => this._openSizePopover("row")));
@@ -5848,6 +6264,7 @@ var TableTooltip = class {
5848
6264
  el.appendChild(this._sep());
5849
6265
  el.appendChild(this._makeBtn(ICONS$2.deleteTable, "Delete Table", () => this._deleteTable(), true));
5850
6266
  this._disposers.push(on(el, "mouseenter", () => this._clearTimers()), on(el, "mouseleave", () => {
6267
+ if (this._selectMode) return;
5851
6268
  if (this._sizePopover && this._sizePopover.style.display !== "none") return;
5852
6269
  this._scheduleHide();
5853
6270
  }));
@@ -5903,6 +6320,12 @@ var TableTooltip = class {
5903
6320
  this._el.style.display = "none";
5904
6321
  this._activeTable = null;
5905
6322
  this._activeCell = null;
6323
+ if (this._selectMode) {
6324
+ this._selectMode = false;
6325
+ if (this._selectBtn) this._selectBtn.classList.remove("an-link-tooltip-btn--active");
6326
+ if (this._editable) this._editable.classList.remove("an-table-select-mode");
6327
+ }
6328
+ this._clearSelection();
5906
6329
  this._clearTimers();
5907
6330
  this._hideSizePopover();
5908
6331
  }
@@ -5936,14 +6359,111 @@ var TableTooltip = class {
5936
6359
  }
5937
6360
  return this._activeCell || this._activeTable && this._activeTable.querySelector("td, th");
5938
6361
  }
6362
+ _toggleSelectMode() {
6363
+ this._selectMode = !this._selectMode;
6364
+ if (this._selectBtn) this._selectBtn.classList.toggle("an-link-tooltip-btn--active", this._selectMode);
6365
+ if (this._editable) this._editable.classList.toggle("an-table-select-mode", this._selectMode);
6366
+ if (!this._selectMode) this._clearSelection();
6367
+ }
6368
+ _clearSelection() {
6369
+ this._selectedCells.forEach((c) => c.classList.remove("an-cell-selected"));
6370
+ this._selectedCells = [];
6371
+ this._selectStart = null;
6372
+ }
6373
+ _setSelection(cells) {
6374
+ this._selectedCells.forEach((c) => {
6375
+ if (!cells.includes(c)) c.classList.remove("an-cell-selected");
6376
+ });
6377
+ this._selectedCells = cells;
6378
+ cells.forEach((c) => c.classList.add("an-cell-selected"));
6379
+ }
6380
+ /**
6381
+ * Returns all cells in the rectangular area between startCell and endCell,
6382
+ * correctly handling rowspan/colspan by using the grid map.
6383
+ * The rect is expanded iteratively until it is stable — this ensures any
6384
+ * merged cell that starts outside the initial rect but spans into it is
6385
+ * fully included.
6386
+ */
6387
+ _getRectCells(startCell, endCell) {
6388
+ if (!startCell) return [];
6389
+ if (!endCell || startCell === endCell) return [startCell];
6390
+ const table = startCell.closest("table");
6391
+ if (!table || !table.contains(endCell)) return [startCell];
6392
+ const { gridMap, cellPos } = buildGridMap(table);
6393
+ const sp = cellPos.get(startCell);
6394
+ const ep = cellPos.get(endCell);
6395
+ if (!sp || !ep) return [startCell];
6396
+ let minR = Math.min(sp.r, ep.r);
6397
+ let maxR = Math.max(sp.r + sp.rs - 1, ep.r + ep.rs - 1);
6398
+ let minC = Math.min(sp.c, ep.c);
6399
+ let maxC = Math.max(sp.c + sp.cs - 1, ep.c + ep.cs - 1);
6400
+ let changed = true;
6401
+ while (changed) {
6402
+ changed = false;
6403
+ for (let r = minR; r <= maxR; r++) {
6404
+ const rowMap = gridMap[r];
6405
+ if (!rowMap) continue;
6406
+ for (let c = minC; c <= maxC; c++) {
6407
+ const cell = rowMap[c];
6408
+ if (!cell) continue;
6409
+ const pos = cellPos.get(cell);
6410
+ if (!pos) continue;
6411
+ if (pos.r < minR) {
6412
+ minR = pos.r;
6413
+ changed = true;
6414
+ }
6415
+ if (pos.r + pos.rs - 1 > maxR) {
6416
+ maxR = pos.r + pos.rs - 1;
6417
+ changed = true;
6418
+ }
6419
+ if (pos.c < minC) {
6420
+ minC = pos.c;
6421
+ changed = true;
6422
+ }
6423
+ if (pos.c + pos.cs - 1 > maxC) {
6424
+ maxC = pos.c + pos.cs - 1;
6425
+ changed = true;
6426
+ }
6427
+ }
6428
+ }
6429
+ }
6430
+ const seen = /* @__PURE__ */ new Set();
6431
+ const result = [];
6432
+ for (let r = minR; r <= maxR; r++) {
6433
+ const rowMap = gridMap[r];
6434
+ if (!rowMap) continue;
6435
+ for (let c = minC; c <= maxC; c++) {
6436
+ const cell = rowMap[c];
6437
+ if (cell && !seen.has(cell)) {
6438
+ seen.add(cell);
6439
+ result.push(cell);
6440
+ }
6441
+ }
6442
+ }
6443
+ return result.length > 0 ? result : [startCell];
6444
+ }
6445
+ /**
6446
+ * Returns the active cell set: user-selected cells when available,
6447
+ * otherwise the single active/cursor cell.
6448
+ * @returns {HTMLTableCellElement[]}
6449
+ */
6450
+ _getSelectedCells() {
6451
+ return this._selectedCells.length > 0 ? this._selectedCells : [this._getCell()].filter(Boolean);
6452
+ }
5939
6453
  _addRow(position) {
5940
- const cell = this._getCell();
5941
- if (!cell) return;
5942
- const row = cell.closest("tr");
5943
- if (!row) return;
5944
- const colCount = Array.from(row.cells).reduce((sum, c) => sum + (c.colSpan || 1), 0);
6454
+ const cells = this._getSelectedCells();
6455
+ if (!cells.length) return;
6456
+ const table = cells[0].closest("table");
6457
+ if (!table) return;
6458
+ const allRows = Array.from(table.querySelectorAll("tr"));
6459
+ const refRow = [...new Set(cells.map((c) => c.closest("tr")).filter(Boolean))].reduce((best, r) => {
6460
+ const bi = allRows.indexOf(best);
6461
+ const ri = allRows.indexOf(r);
6462
+ return position === "above" ? ri < bi ? r : best : ri > bi ? r : best;
6463
+ });
6464
+ const colCount = Array.from(refRow.cells).reduce((sum, c) => sum + (c.colSpan || 1), 0);
5945
6465
  const newRow = document.createElement("tr");
5946
- const refCells = Array.from(row.cells);
6466
+ const refCells = Array.from(refRow.cells);
5947
6467
  for (let i = 0; i < colCount; i++) {
5948
6468
  const td = createElement("td", {}, ["\xA0"]);
5949
6469
  const ref = refCells[i];
@@ -5951,19 +6471,20 @@ var TableTooltip = class {
5951
6471
  if (ref && ref.style.minWidth) td.style.minWidth = ref.style.minWidth;
5952
6472
  newRow.appendChild(td);
5953
6473
  }
5954
- if (position === "above") row.parentElement?.insertBefore(newRow, row);
5955
- else row.insertAdjacentElement("afterend", newRow);
6474
+ if (position === "above") refRow.parentElement?.insertBefore(newRow, refRow);
6475
+ else refRow.insertAdjacentElement("afterend", newRow);
5956
6476
  requestAnimationFrame(() => this._positionNear(this._activeTable));
5957
6477
  this.context.invoke("editor.afterCommand");
5958
6478
  }
5959
6479
  _addColumn(position) {
5960
- const cell = this._getCell();
5961
- if (!cell) return;
5962
- const table = cell.closest("table");
6480
+ const cells = this._getSelectedCells();
6481
+ if (!cells.length) return;
6482
+ const table = cells[0].closest("table");
5963
6483
  if (!table) return;
5964
- const visualColIdx = getVisualColIndex(cell);
6484
+ const colIndices = cells.map((c) => getVisualColIndex(c));
6485
+ const targetColIdx = position === "left" ? Math.min(...colIndices) : Math.max(...colIndices);
5965
6486
  const rows = Array.from(table.querySelectorAll("tr"));
5966
- const refs = rows.map((r) => position === "left" ? getCellAtVisualCol(r, visualColIdx) : getCellAfterVisualCol(r, visualColIdx));
6487
+ const refs = rows.map((r) => position === "left" ? getCellAtVisualCol(r, targetColIdx) : getCellAfterVisualCol(r, targetColIdx));
5967
6488
  const isHeaders = rows.map((r) => r.closest("thead") !== null);
5968
6489
  rows.forEach((r, i) => {
5969
6490
  r.insertBefore(createElement(isHeaders[i] ? "th" : "td", {}, ["\xA0"]), refs[i]);
@@ -5972,68 +6493,93 @@ var TableTooltip = class {
5972
6493
  this.context.invoke("editor.afterCommand");
5973
6494
  }
5974
6495
  _deleteRow() {
5975
- const cell = this._getCell();
5976
- if (!cell) return;
5977
- const row = cell.closest("tr");
5978
- const table = cell.closest("table");
5979
- if (!row || !table) return;
6496
+ const cells = this._getSelectedCells();
6497
+ if (!cells.length) return;
6498
+ const table = cells[0].closest("table");
6499
+ if (!table) return;
5980
6500
  const tbody = table.querySelector("tbody");
5981
- if ((tbody ? tbody.querySelectorAll("tr").length : table.querySelectorAll("tr").length) <= 1 && row.closest("tbody")) return;
6501
+ const totalBodyRows = tbody ? tbody.querySelectorAll("tr").length : table.querySelectorAll("tr").length;
6502
+ const selectedRows = [...new Set(cells.map((c) => c.closest("tr")).filter(Boolean))];
6503
+ if (selectedRows.filter((r) => r.closest("tbody")).length >= totalBodyRows) return;
5982
6504
  this._activeCell = null;
5983
- row.parentElement?.removeChild(row);
6505
+ this._clearSelection();
6506
+ selectedRows.forEach((r) => r.parentElement?.removeChild(r));
5984
6507
  requestAnimationFrame(() => this._positionNear(this._activeTable));
5985
6508
  this.context.invoke("editor.afterCommand");
5986
6509
  }
5987
6510
  _deleteColumn() {
5988
- const cell = this._getCell();
5989
- if (!cell) return;
5990
- const table = cell.closest("table");
6511
+ const cells = this._getSelectedCells();
6512
+ if (!cells.length) return;
6513
+ const table = cells[0].closest("table");
5991
6514
  if (!table) return;
5992
- const row = cell.closest("tr");
5993
- if (row && row.cells.length <= 1) return;
5994
- const visualColIdx = getVisualColIndex(cell);
5995
- this._activeCell = null;
5996
- const rows = Array.from(table.querySelectorAll("tr"));
5997
- rows.map((r) => getCellAtVisualCol(r, visualColIdx)).forEach((c, i) => {
5998
- if (c) rows[i].removeChild(c);
6515
+ const tableRows = Array.from(table.querySelectorAll("tr"));
6516
+ if (tableRows[0] && tableRows[0].cells.length <= 1) return;
6517
+ const colIndices = [...new Set(cells.map((c) => getVisualColIndex(c)))];
6518
+ if (colIndices.length >= (tableRows[0]?.cells.length ?? 1)) return;
6519
+ const cellsToDelete = [];
6520
+ colIndices.forEach((colIdx) => {
6521
+ tableRows.forEach((r) => {
6522
+ const c = getCellAtVisualCol(r, colIdx);
6523
+ if (c) cellsToDelete.push(c);
6524
+ });
5999
6525
  });
6526
+ this._activeCell = null;
6527
+ this._clearSelection();
6528
+ cellsToDelete.forEach((c) => c.parentElement?.removeChild(c));
6000
6529
  requestAnimationFrame(() => this._positionNear(this._activeTable));
6001
6530
  this.context.invoke("editor.afterCommand");
6002
6531
  }
6003
6532
  _mergeCells() {
6004
6533
  const cell = this._getCell();
6005
6534
  if (!cell) return;
6006
- const sel = window.getSelection();
6007
- if (!sel || sel.rangeCount === 0) return;
6008
- const range = sel.getRangeAt(0);
6009
6535
  const table = cell.closest("table");
6010
6536
  if (!table) return;
6011
- const selected = Array.from(table.querySelectorAll("td, th")).filter((c) => {
6012
- try {
6013
- return range.intersectsNode(c);
6014
- } catch {
6015
- return false;
6016
- }
6017
- });
6018
- if (selected.length < 2) return;
6019
- const rows = [...new Set(selected.map((c) => c.closest("tr")))];
6020
- if (rows.length === 1) {
6021
- const row = rows[0];
6022
- const rowSelected = Array.from(row.cells).filter((c) => selected.includes(c));
6023
- if (rowSelected.length < 2) return;
6024
- const first = rowSelected[0];
6025
- first.colSpan = rowSelected.reduce((sum, c) => sum + (c.colSpan || 1), 0);
6026
- first.innerHTML = rowSelected.map((c) => c.innerHTML).join("");
6027
- rowSelected.slice(1).forEach((c) => row.removeChild(c));
6028
- } else {
6029
- if ([...new Set(selected.map((c) => getVisualColIndex(c)))].length !== 1) return;
6030
- const first = selected[0];
6031
- first.rowSpan = selected.reduce((sum, c) => sum + (c.rowSpan || 1), 0);
6032
- first.innerHTML = selected.map((c) => c.innerHTML).join("");
6033
- selected.slice(1).forEach((c) => {
6034
- if (c.closest("tr")) c.closest("tr").removeChild(c);
6537
+ let selected = this._getSelectedCells().filter((c) => table.contains(c));
6538
+ if (selected.length < 2) {
6539
+ const sel = window.getSelection();
6540
+ if (!sel || sel.rangeCount === 0) return;
6541
+ const range = sel.getRangeAt(0);
6542
+ selected = Array.from(table.querySelectorAll("td, th")).filter((c) => {
6543
+ try {
6544
+ return range.intersectsNode(c);
6545
+ } catch {
6546
+ return false;
6547
+ }
6035
6548
  });
6549
+ if (selected.length < 2) return;
6550
+ }
6551
+ const { gridMap, cellPos } = buildGridMap(table);
6552
+ let minR = Infinity, maxR = -Infinity, minC = Infinity, maxC = -Infinity;
6553
+ selected.forEach((c) => {
6554
+ const pos = cellPos.get(c);
6555
+ if (!pos) return;
6556
+ if (pos.r < minR) minR = pos.r;
6557
+ if (pos.r + pos.rs - 1 > maxR) maxR = pos.r + pos.rs - 1;
6558
+ if (pos.c < minC) minC = pos.c;
6559
+ if (pos.c + pos.cs - 1 > maxC) maxC = pos.c + pos.cs - 1;
6560
+ });
6561
+ if (minR === Infinity) return;
6562
+ const seen = /* @__PURE__ */ new Set();
6563
+ const rectCells = [];
6564
+ for (let r = minR; r <= maxR; r++) {
6565
+ const rowMap = gridMap[r];
6566
+ if (!rowMap) continue;
6567
+ for (let c = minC; c <= maxC; c++) {
6568
+ const tc = rowMap[c];
6569
+ if (tc && !seen.has(tc)) {
6570
+ seen.add(tc);
6571
+ rectCells.push(tc);
6572
+ }
6573
+ }
6036
6574
  }
6575
+ if (rectCells.length < 2) return;
6576
+ const first = rectCells[0];
6577
+ first.colSpan = maxC - minC + 1;
6578
+ first.rowSpan = maxR - minR + 1;
6579
+ first.style.verticalAlign = "middle";
6580
+ first.innerHTML = rectCells.map((c) => c.innerHTML).join("");
6581
+ rectCells.slice(1).forEach((c) => c.parentElement?.removeChild(c));
6582
+ this._clearSelection();
6037
6583
  this.context.invoke("editor.afterCommand");
6038
6584
  }
6039
6585
  _deleteTable() {
@@ -6043,6 +6589,57 @@ var TableTooltip = class {
6043
6589
  if (table.parentNode) table.parentNode.removeChild(table);
6044
6590
  this.context.invoke("editor.afterCommand");
6045
6591
  }
6592
+ _unmergeCells() {
6593
+ const cells = this._getSelectedCells();
6594
+ if (!cells.length) return;
6595
+ const table = cells[0].closest("table");
6596
+ if (!table) return;
6597
+ const mergedCells = cells.filter((c) => table.contains(c) && ((c.colSpan || 1) > 1 || (c.rowSpan || 1) > 1));
6598
+ if (!mergedCells.length) return;
6599
+ mergedCells.forEach((cell) => {
6600
+ if (table.contains(cell)) this._unmergeOne(cell, table);
6601
+ });
6602
+ this._clearSelection();
6603
+ requestAnimationFrame(() => this._positionNear(this._activeTable));
6604
+ this.context.invoke("editor.afterCommand");
6605
+ }
6606
+ /**
6607
+ * Split a single merged cell (colspan/rowspan > 1) back into individual cells.
6608
+ * New cells are empty (&nbsp;); the original cell retains its content.
6609
+ * @param {HTMLTableCellElement} cell
6610
+ * @param {HTMLTableElement} table
6611
+ */
6612
+ _unmergeOne(cell, table) {
6613
+ const cs = cell.colSpan || 1;
6614
+ const rs = cell.rowSpan || 1;
6615
+ if (cs === 1 && rs === 1) return;
6616
+ const { cellPos } = buildGridMap(table);
6617
+ const pos = cellPos.get(cell);
6618
+ if (!pos) return;
6619
+ const { r, c } = pos;
6620
+ const tableRows = Array.from(table.rows);
6621
+ const tag = cell.tagName.toLowerCase();
6622
+ cell.rowSpan = 1;
6623
+ cell.colSpan = 1;
6624
+ cell.style.verticalAlign = "";
6625
+ if (cs > 1) {
6626
+ const insertRef = cell.nextElementSibling;
6627
+ for (let dc = 1; dc < cs; dc++) tableRows[r].insertBefore(createElement(tag, {}, ["\xA0"]), insertRef);
6628
+ }
6629
+ for (let dr = 1; dr < rs; dr++) {
6630
+ const targetRow = tableRows[r + dr];
6631
+ if (!targetRow) continue;
6632
+ let ref = null;
6633
+ for (const tc of targetRow.cells) {
6634
+ const tp = cellPos.get(tc);
6635
+ if (tp && tp.c > c) {
6636
+ ref = tc;
6637
+ break;
6638
+ }
6639
+ }
6640
+ for (let dc = 0; dc < cs; dc++) targetRow.insertBefore(createElement(tag, {}, ["\xA0"]), ref);
6641
+ }
6642
+ }
6046
6643
  _buildSizePopover() {
6047
6644
  const popover = createElement("div", { class: "an-size-popover" });
6048
6645
  popover.style.display = "none";
@@ -6124,27 +6721,35 @@ var TableTooltip = class {
6124
6721
  };
6125
6722
  } else {
6126
6723
  const isCol = type === "col";
6724
+ const activeCells = this._getSelectedCells().filter((c) => {
6725
+ const t = c.closest("table");
6726
+ return t && t === cell.closest("table");
6727
+ });
6127
6728
  this._sizeTitleEl.textContent = isCol ? "Column Width (px)" : "Row Height (px)";
6128
6729
  this._sizeInputEl.min = "1";
6129
6730
  this._sizeInputEl.max = "2000";
6130
6731
  this._sizeInputEl.value = isCol ? cell.offsetWidth || 120 : cell.closest("tr") ? cell.closest("tr").offsetHeight || 40 : 40;
6131
6732
  this._sizeApply = (val) => {
6733
+ const table = cell.closest("table");
6734
+ if (!table) return;
6132
6735
  if (isCol) {
6133
- const table = cell.closest("table");
6134
- const visualColIdx = getVisualColIndex(cell);
6135
- Array.from(table.querySelectorAll("tr")).map((r) => getCellAtVisualCol(r, visualColIdx)).forEach((c) => {
6136
- if (c) {
6137
- c.style.width = `${val}px`;
6138
- c.style.minWidth = `${val}px`;
6139
- }
6736
+ const colIndices = [...new Set(activeCells.map((c) => getVisualColIndex(c)))];
6737
+ const tableRows = Array.from(table.querySelectorAll("tr"));
6738
+ colIndices.forEach((colIdx) => {
6739
+ tableRows.forEach((r) => {
6740
+ const c = getCellAtVisualCol(r, colIdx);
6741
+ if (c && (c.colSpan || 1) === 1) {
6742
+ c.style.width = `${val}px`;
6743
+ c.style.minWidth = `${val}px`;
6744
+ }
6745
+ });
6140
6746
  });
6141
- } else {
6142
- const row = cell.closest("tr");
6143
- if (row) for (const c of row.cells) {
6747
+ } else [...new Set(activeCells.map((c) => c.closest("tr")).filter(Boolean))].forEach((row) => {
6748
+ for (const c of row.cells) {
6144
6749
  c.style.height = `${val}px`;
6145
6750
  c.style.minHeight = `${val}px`;
6146
6751
  }
6147
- }
6752
+ });
6148
6753
  this.context.invoke("editor.afterCommand");
6149
6754
  };
6150
6755
  }
@@ -6201,6 +6806,7 @@ var CodeTooltip = class {
6201
6806
  this._ensurePrism();
6202
6807
  const editable = this.context.layoutInfo.editable;
6203
6808
  this._disposers.push(on(editable, "mouseover", (e) => {
6809
+ if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
6204
6810
  const pre = e.target.closest("pre");
6205
6811
  if (pre && editable.contains(pre)) this._scheduleShow(pre);
6206
6812
  }), on(editable, "mouseout", (e) => {
@@ -8998,7 +9604,13 @@ var EmojiDialog = class {
8998
9604
  range.selectNodeContents(editable);
8999
9605
  range.collapse(false);
9000
9606
  }
9607
+ const _sc = range.startContainer;
9608
+ const _tdAnchor = (_sc.nodeType === 1 ? _sc : _sc.parentElement)?.closest?.("td, th");
9001
9609
  range.deleteContents();
9610
+ if (_tdAnchor && _tdAnchor.isConnected && !_tdAnchor.contains(range.startContainer)) {
9611
+ range.setStart(_tdAnchor, 0);
9612
+ range.collapse(true);
9613
+ }
9002
9614
  const textNode = document.createTextNode(char);
9003
9615
  range.insertNode(textNode);
9004
9616
  range.setStartAfter(textNode);
@@ -9567,7 +10179,13 @@ var IconDialog = class {
9567
10179
  range.selectNodeContents(editable);
9568
10180
  range.collapse(false);
9569
10181
  }
10182
+ const _sc = range.startContainer;
10183
+ const _tdAnchor = (_sc.nodeType === 1 ? _sc : _sc.parentElement)?.closest?.("td, th");
9570
10184
  range.deleteContents();
10185
+ if (_tdAnchor && _tdAnchor.isConnected && !_tdAnchor.contains(range.startContainer)) {
10186
+ range.setStart(_tdAnchor, 0);
10187
+ range.collapse(true);
10188
+ }
9571
10189
  range.insertNode(iconEl);
9572
10190
  let caretTextNode = iconEl.nextSibling;
9573
10191
  if (!caretTextNode || caretTextNode.nodeType !== Node.TEXT_NODE) {
@@ -9631,22 +10249,50 @@ var ICONS = {
9631
10249
  copyFormat: `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>`,
9632
10250
  pasteFormat: `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/><rect x="8" y="2" width="8" height="4" rx="1"/><path d="m9 14 2 2 4-4"/></svg>`,
9633
10251
  removeFormat: `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m7 21-4.3-4.3c-1-1-1-2.5 0-3.4l9.6-9.6c1-1 2.5-1 3.4 0l5.6 5.6c1 1 1 2.5 0 3.4L13 21"/><path d="M22 21H7"/><path d="m5 11 9 9"/></svg>`,
9634
- table: `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" 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"/></svg>`
10252
+ table: `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" 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"/></svg>`,
10253
+ textColor: `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 20L12 4L20 20"/><line x1="7.5" y1="14" x2="16.5" y2="14"/></svg>`,
10254
+ highlightColor: `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 21v-4l9-9 4 4-9 9z"/><path d="M12 8l4 4"/><line x1="3" y1="21" x2="21" y2="21"/></svg>`,
10255
+ noColor: `<svg xmlns="http://www.w3.org/2000/svg" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><line x1="4" y1="4" x2="20" y2="20"/><line x1="20" y1="4" x2="4" y2="20"/></svg>`,
10256
+ back: `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"/></svg>`
9635
10257
  };
10258
+ var COLOR_PRESETS = [
10259
+ "#000000",
10260
+ "#434343",
10261
+ "#666666",
10262
+ "#999999",
10263
+ "#b7b7b7",
10264
+ "#cccccc",
10265
+ "#efefef",
10266
+ "#ffffff",
10267
+ "#ff0000",
10268
+ "#ff9900",
10269
+ "#ffff00",
10270
+ "#00ff00",
10271
+ "#00ffff",
10272
+ "#4a86e8",
10273
+ "#9900ff",
10274
+ "#ff00ff",
10275
+ "#f4cccc",
10276
+ "#fce5cd",
10277
+ "#fff2cc",
10278
+ "#d9ead3",
10279
+ "#d0e0e3",
10280
+ "#c9daf8",
10281
+ "#d9d2e9",
10282
+ "#ead1dc"
10283
+ ];
10284
+ function makeColorSubItems(colorType) {
10285
+ const label = colorType === "foreColor" ? "Text Color" : "Highlight Color";
10286
+ return () => [{
10287
+ back: true,
10288
+ label,
10289
+ navigate: () => defaultItems
10290
+ }, {
10291
+ colorPalette: true,
10292
+ colorType
10293
+ }];
10294
+ }
9636
10295
  var defaultItems = [
9637
- {
9638
- name: "undo",
9639
- label: "Undo",
9640
- icon: ICONS.undo,
9641
- action: (ctx) => ctx.invoke("editor.undo")
9642
- },
9643
- {
9644
- name: "redo",
9645
- label: "Redo",
9646
- icon: ICONS.redo,
9647
- action: (ctx) => ctx.invoke("editor.redo")
9648
- },
9649
- { separator: true },
9650
10296
  {
9651
10297
  name: "cut",
9652
10298
  label: "Cut",
@@ -9707,6 +10353,21 @@ var defaultItems = [
9707
10353
  action: (ctx) => ctx.invoke("editor.underline")
9708
10354
  },
9709
10355
  { separator: true },
10356
+ {
10357
+ name: "textColor",
10358
+ label: "Text Color",
10359
+ icon: ICONS.textColor,
10360
+ colorStrip: "foreColor",
10361
+ navigate: makeColorSubItems("foreColor")
10362
+ },
10363
+ {
10364
+ name: "highlightColor",
10365
+ label: "Highlight Color",
10366
+ icon: ICONS.highlightColor,
10367
+ colorStrip: "hiliteColor",
10368
+ navigate: makeColorSubItems("hiliteColor")
10369
+ },
10370
+ { separator: true },
9710
10371
  {
9711
10372
  name: "copyFormat",
9712
10373
  label: "Copy Format",
@@ -9824,8 +10485,10 @@ var ContextMenu = class {
9824
10485
  backBtn.appendChild(createElement("span", { class: "an-context-label" }, [it.label || "Back"]));
9825
10486
  const off = on(backBtn, "click", (e) => {
9826
10487
  e.stopPropagation();
10488
+ const curLeft = parseFloat(this.el.style.left);
10489
+ const curTop = parseFloat(this.el.style.top);
9827
10490
  this._renderItems(it.navigate());
9828
- this._reposition();
10491
+ this._reposition(curLeft, curTop);
9829
10492
  });
9830
10493
  this._menuDisposers.push(off);
9831
10494
  this.el.appendChild(backBtn);
@@ -9837,7 +10500,19 @@ var ContextMenu = class {
9837
10500
  class: "an-context-item an-context-submenu",
9838
10501
  "data-name": it.name || ""
9839
10502
  });
9840
- if (it.icon) {
10503
+ if (it.icon) if (it.colorStrip) {
10504
+ const iconWrap = createElement("span", {
10505
+ class: "an-context-icon an-context-icon--color",
10506
+ "aria-hidden": "true"
10507
+ });
10508
+ const svgSpan = createElement("span", { class: "an-context-icon-svg" });
10509
+ svgSpan.innerHTML = it.icon;
10510
+ const strip = createElement("span", { class: "an-context-color-strip" });
10511
+ strip.style.background = this._getSelectionColor(it.colorStrip);
10512
+ iconWrap.appendChild(svgSpan);
10513
+ iconWrap.appendChild(strip);
10514
+ btn.appendChild(iconWrap);
10515
+ } else {
9841
10516
  const iconSpan = createElement("span", {
9842
10517
  class: "an-context-icon",
9843
10518
  "aria-hidden": "true"
@@ -9854,13 +10529,63 @@ var ContextMenu = class {
9854
10529
  btn.appendChild(chevron);
9855
10530
  const off = on(btn, "click", (e) => {
9856
10531
  e.stopPropagation();
10532
+ const curLeft = parseFloat(this.el.style.left);
10533
+ const curTop = parseFloat(this.el.style.top);
9857
10534
  this._renderItems(it.navigate());
9858
- this._reposition();
10535
+ this._reposition(curLeft, curTop);
9859
10536
  });
9860
10537
  this._menuDisposers.push(off);
9861
10538
  this.el.appendChild(btn);
9862
10539
  return;
9863
10540
  }
10541
+ if (it.colorPalette) {
10542
+ const palette = createElement("div", { class: "an-context-color-palette" });
10543
+ COLOR_PRESETS.forEach((color) => {
10544
+ const sw = createElement("div", {
10545
+ class: "an-context-color-swatch",
10546
+ title: color,
10547
+ role: "button",
10548
+ "aria-label": color
10549
+ });
10550
+ sw.style.background = color;
10551
+ const offSw = on(sw, "click", (e) => {
10552
+ e.stopPropagation();
10553
+ this._applyColor(it.colorType, color);
10554
+ });
10555
+ this._menuDisposers.push(offSw);
10556
+ palette.appendChild(sw);
10557
+ });
10558
+ if (it.colorType === "hiliteColor") {
10559
+ const noColor = createElement("div", {
10560
+ class: "an-context-color-swatch an-context-color-none",
10561
+ title: "No highlight",
10562
+ role: "button",
10563
+ "aria-label": "No highlight"
10564
+ });
10565
+ noColor.innerHTML = ICONS.noColor;
10566
+ const offNo = on(noColor, "click", (e) => {
10567
+ e.stopPropagation();
10568
+ this._applyColor("hiliteColor", "transparent");
10569
+ });
10570
+ this._menuDisposers.push(offNo);
10571
+ palette.appendChild(noColor);
10572
+ }
10573
+ this.el.appendChild(palette);
10574
+ const customRow = createElement("div", { class: "an-context-color-custom" });
10575
+ const colorInput = createElement("input", {
10576
+ type: "color",
10577
+ value: it.colorType === "foreColor" ? "#000000" : "#ffff00",
10578
+ title: "Custom color",
10579
+ "aria-label": "Custom color"
10580
+ });
10581
+ const customLabel = createElement("span", {}, ["Custom…"]);
10582
+ const offCustom = on(colorInput, "change", () => this._applyColor(it.colorType, colorInput.value));
10583
+ this._menuDisposers.push(offCustom);
10584
+ customRow.appendChild(colorInput);
10585
+ customRow.appendChild(customLabel);
10586
+ this.el.appendChild(customRow);
10587
+ return;
10588
+ }
9864
10589
  if (it.tableGrid) {
9865
10590
  const GRID_ROWS = 8, GRID_COLS = 8;
9866
10591
  const wrapper = createElement("div", { class: "an-context-table-wrap" });
@@ -9978,6 +10703,7 @@ var ContextMenu = class {
9978
10703
  const editable = this.context.layoutInfo && this.context.layoutInfo.editable;
9979
10704
  if (!editable) return;
9980
10705
  if (!editable.contains(event.target)) return;
10706
+ if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
9981
10707
  event.preventDefault();
9982
10708
  this._lastX = event.clientX;
9983
10709
  this._lastY = event.clientY;
@@ -10004,7 +10730,19 @@ var ContextMenu = class {
10004
10730
  let left = rx;
10005
10731
  let top = ry;
10006
10732
  if (left + rect.width > window.innerWidth) left = window.innerWidth - rect.width - 8;
10733
+ if (left < 8) left = 8;
10007
10734
  if (top + rect.height > window.innerHeight) top = window.innerHeight - rect.height - 8;
10735
+ if (top < 8) top = 8;
10736
+ if (this._savedRange) try {
10737
+ const sel = this._savedRange.getBoundingClientRect();
10738
+ if (sel.width > 0 || sel.height > 0) {
10739
+ if (top < sel.bottom && top + rect.height > sel.top && left < sel.right && left + rect.width > sel.left) {
10740
+ const belowTop = sel.bottom + 6;
10741
+ if (belowTop + rect.height <= window.innerHeight - 8) top = belowTop;
10742
+ else top = Math.max(8, sel.top - rect.height - 6);
10743
+ }
10744
+ }
10745
+ } catch (_) {}
10008
10746
  this.el.style.left = `${left}px`;
10009
10747
  this.el.style.top = `${top}px`;
10010
10748
  }
@@ -10013,6 +10751,33 @@ var ContextMenu = class {
10013
10751
  this.el.style.display = "none";
10014
10752
  this.el.setAttribute("aria-hidden", "true");
10015
10753
  }
10754
+ /** Read the current selection's text or highlight color for the strip.
10755
+ * @param {'foreColor'|'hiliteColor'} type
10756
+ * @returns {string} CSS color string
10757
+ */
10758
+ _getSelectionColor(type) {
10759
+ const range = this._savedRange;
10760
+ if (!range) return type === "foreColor" ? "#000000" : "transparent";
10761
+ let node = range.startContainer;
10762
+ if (node.nodeType === Node.TEXT_NODE) node = node.parentElement;
10763
+ if (!node) return type === "foreColor" ? "#000000" : "transparent";
10764
+ const cs = window.getComputedStyle(node);
10765
+ if (type === "foreColor") return cs.color || "#000000";
10766
+ const bg = cs.backgroundColor;
10767
+ return !bg || bg === "rgba(0, 0, 0, 0)" || bg === "transparent" ? "transparent" : bg;
10768
+ }
10769
+ /** Restore selection, apply a color command, then hide the menu. */
10770
+ _applyColor(type, color) {
10771
+ const editable = this.context.layoutInfo && this.context.layoutInfo.editable;
10772
+ if (!editable || !this._savedRange) return;
10773
+ editable.focus();
10774
+ const sel = window.getSelection();
10775
+ sel.removeAllRanges();
10776
+ sel.addRange(this._savedRange.cloneRange());
10777
+ document.execCommand(type, false, color);
10778
+ this.context.invoke("editor.afterCommand");
10779
+ this.hide();
10780
+ }
10016
10781
  /** Returns true if a format has been copied — used to disable Paste Format. */
10017
10782
  hasCopiedFormat() {
10018
10783
  return !!this._copiedFormat;
@@ -10680,7 +11445,7 @@ function drawCropToCanvas(img, naturalRect, renderW, renderH) {
10680
11445
  resolve(null);
10681
11446
  }
10682
11447
  };
10683
- if (img.src.startsWith("data:") || img.src.startsWith("blob:") || img.src.startsWith(location.origin)) {
11448
+ if (img.src.startsWith("data:") || img.src.startsWith("blob:") || img.src.startsWith(window.location.origin)) {
10684
11449
  tryDraw(img);
10685
11450
  return;
10686
11451
  }
@@ -11056,7 +11821,7 @@ var ImageCropOverlay = class {
11056
11821
  height: natH
11057
11822
  }, w, h);
11058
11823
  if (!canvas) {
11059
- alert("Cannot crop this image: the image server does not allow cross-origin access.\nUpload the image directly to use the crop tool.");
11824
+ window.alert("Cannot crop this image: the image server does not allow cross-origin access.\nUpload the image directly to use the crop tool.");
11060
11825
  this._close(false);
11061
11826
  return;
11062
11827
  }
@@ -11417,9 +12182,15 @@ var Context = class {
11417
12182
  if (disabled) {
11418
12183
  editable.setAttribute("contenteditable", "false");
11419
12184
  this.layoutInfo.container.classList.add("an-disabled");
12185
+ editable.querySelectorAll("ul.an-checklist input[type=\"checkbox\"]").forEach((cb) => {
12186
+ cb.setAttribute("disabled", "");
12187
+ });
11420
12188
  } else {
11421
12189
  editable.setAttribute("contenteditable", "true");
11422
12190
  this.layoutInfo.container.classList.remove("an-disabled");
12191
+ editable.querySelectorAll("ul.an-checklist input[type=\"checkbox\"]").forEach((cb) => {
12192
+ cb.removeAttribute("disabled");
12193
+ });
11423
12194
  }
11424
12195
  }
11425
12196
  /**