autumnnote 1.6.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 (47) hide show
  1. package/README.md +4 -4
  2. package/dist/autumnnote.css +0 -2
  3. package/dist/autumnnote.es.js +348 -342
  4. package/dist/autumnnote.es.js.map +1 -1
  5. package/dist/autumnnote.umd.js +341 -336
  6. package/dist/autumnnote.umd.js.map +1 -1
  7. package/package.json +21 -3
  8. package/src/js/Context.js +16 -14
  9. package/src/js/core/dom.js +9 -9
  10. package/src/js/core/env.js +1 -1
  11. package/src/js/core/func.js +1 -1
  12. package/src/js/core/lists.js +1 -1
  13. package/src/js/core/markdown.js +18 -18
  14. package/src/js/core/range.js +6 -6
  15. package/src/js/editing/History.js +4 -4
  16. package/src/js/editing/Style.js +32 -32
  17. package/src/js/editing/Table.js +2 -2
  18. package/src/js/editing/Typing.js +15 -15
  19. package/src/js/module/AutoSaveRestore.js +2 -4
  20. package/src/js/module/BubbleToolbar.js +25 -25
  21. package/src/js/module/Buttons.js +4 -4
  22. package/src/js/module/Clipboard.js +12 -13
  23. package/src/js/module/CodeTooltip.js +14 -16
  24. package/src/js/module/Codeview.js +3 -5
  25. package/src/js/module/ContextMenu.js +27 -27
  26. package/src/js/module/Editor.js +17 -18
  27. package/src/js/module/EmojiDialog.js +5 -5
  28. package/src/js/module/FindReplace.js +8 -7
  29. package/src/js/module/IconDialog.js +13 -15
  30. package/src/js/module/ImageCropOverlay.js +7 -7
  31. package/src/js/module/ImageDialog.js +1 -1
  32. package/src/js/module/ImageResizer.js +4 -4
  33. package/src/js/module/ImageTooltip.js +14 -14
  34. package/src/js/module/LinkDialog.js +2 -2
  35. package/src/js/module/LinkTooltip.js +12 -12
  36. package/src/js/module/MarkdownShortcuts.js +11 -14
  37. package/src/js/module/Mention.js +8 -10
  38. package/src/js/module/Placeholder.js +1 -1
  39. package/src/js/module/ShortcutsDialog.js +2 -4
  40. package/src/js/module/Statusbar.js +3 -5
  41. package/src/js/module/TableTooltip.js +30 -32
  42. package/src/js/module/Toolbar.js +21 -21
  43. package/src/js/module/VideoDialog.js +8 -8
  44. package/src/js/module/VideoResizer.js +5 -7
  45. package/src/js/module/VideoTooltip.js +7 -7
  46. package/src/js/renderer.js +1 -1
  47. package/src/styles/autumnnote.scss +0 -3
@@ -33,7 +33,7 @@ export class LinkDialog {
33
33
  this._disposers.forEach((d) => d());
34
34
  this._disposers = [];
35
35
  if (this._dialog && this._dialog.parentNode) {
36
- this._dialog.parentNode.removeChild(this._dialog);
36
+ this._dialog.remove();
37
37
  }
38
38
  this._dialog = null;
39
39
  }
@@ -138,7 +138,7 @@ export class LinkDialog {
138
138
 
139
139
  _prefill() {
140
140
  // Try to find currently selected anchor
141
- const sel = window.getSelection();
141
+ const sel = globalThis.getSelection();
142
142
  let anchor = null;
143
143
  if (sel && sel.rangeCount > 0) {
144
144
  let node = sel.getRangeAt(0).startContainer;
@@ -46,8 +46,8 @@ export class LinkTooltip {
46
46
  }
47
47
  }),
48
48
  // Hide when the page scrolls or resizes — the tooltip position becomes stale
49
- on(window, 'scroll', () => this._hide(), { passive: true }),
50
- on(window, 'resize', () => this._hide(), { passive: true }),
49
+ on(globalThis, 'scroll', () => this._hide(), { passive: true }),
50
+ on(globalThis, 'resize', () => this._hide(), { passive: true }),
51
51
  );
52
52
 
53
53
  return this;
@@ -57,7 +57,7 @@ export class LinkTooltip {
57
57
  this._clearTimers();
58
58
  this._disposers.forEach((d) => d());
59
59
  this._disposers = [];
60
- if (this._el && this._el.parentNode) this._el.parentNode.removeChild(this._el);
60
+ if (this._el && this._el.parentNode) this._el.remove();
61
61
  this._el = null;
62
62
  }
63
63
 
@@ -163,11 +163,11 @@ export class LinkTooltip {
163
163
  let top = rect.bottom + margin;
164
164
  let left = rect.left;
165
165
 
166
- if (top + tipH > window.innerHeight - margin) {
166
+ if (top + tipH > globalThis.innerHeight - margin) {
167
167
  top = rect.top - tipH - margin;
168
168
  }
169
- if (left + tipW > window.innerWidth - margin) {
170
- left = window.innerWidth - tipW - margin;
169
+ if (left + tipW > globalThis.innerWidth - margin) {
170
+ left = globalThis.innerWidth - tipW - margin;
171
171
  }
172
172
  if (left < margin) left = margin;
173
173
 
@@ -190,13 +190,13 @@ export class LinkTooltip {
190
190
  // ---------------------------------------------------------------------------
191
191
 
192
192
  _openLink() {
193
- const url = this._activeAnchor && this._activeAnchor.getAttribute('href');
194
- if (url) window.open(url, '_blank', 'noopener,noreferrer');
193
+ const url = this._activeAnchor?.getAttribute('href');
194
+ if (url) globalThis.open(url, '_blank', 'noopener,noreferrer');
195
195
  this._hide();
196
196
  }
197
197
 
198
198
  _copyLink() {
199
- const url = this._activeAnchor && this._activeAnchor.getAttribute('href');
199
+ const url = this._activeAnchor?.getAttribute('href');
200
200
  if (url) {
201
201
  navigator.clipboard.writeText(url).catch(() => {
202
202
  // Fallback for environments where clipboard API is unavailable
@@ -207,7 +207,7 @@ export class LinkTooltip {
207
207
  document.body.appendChild(ta);
208
208
  ta.select();
209
209
  document.execCommand('copy');
210
- document.body.removeChild(ta);
210
+ ta.remove();
211
211
  });
212
212
  }
213
213
  // Brief visual feedback
@@ -223,7 +223,7 @@ export class LinkTooltip {
223
223
  this._hide();
224
224
 
225
225
  // Place cursor inside the anchor, then open the link dialog (which pre-fills from selection)
226
- const sel = window.getSelection();
226
+ const sel = globalThis.getSelection();
227
227
  const range = document.createRange();
228
228
  range.selectNodeContents(anchor);
229
229
  sel.removeAllRanges();
@@ -238,7 +238,7 @@ export class LinkTooltip {
238
238
  this._hide();
239
239
 
240
240
  // Select the anchor text, then remove the link
241
- const sel = window.getSelection();
241
+ const sel = globalThis.getSelection();
242
242
  const range = document.createRange();
243
243
  range.selectNode(anchor);
244
244
  sel.removeAllRanges();
@@ -61,8 +61,8 @@ export class MarkdownShortcuts {
61
61
  * @returns {{ text: string, range: Range, lineNode: Node } | null}
62
62
  */
63
63
  _getLineContext() {
64
- const sel = window.getSelection();
65
- if (!sel || !sel.rangeCount) return null;
64
+ const sel = globalThis.getSelection();
65
+ if (!sel?.rangeCount) return null;
66
66
  const range = sel.getRangeAt(0);
67
67
  if (!range.collapsed) return null;
68
68
 
@@ -87,7 +87,7 @@ export class MarkdownShortcuts {
87
87
 
88
88
  _isBlock(node) {
89
89
  if (node.nodeType !== Node.ELEMENT_NODE) return false;
90
- const display = window.getComputedStyle(node).display;
90
+ const display = globalThis.getComputedStyle(node).display;
91
91
  return display === 'block' || display === 'list-item' || display === 'table-cell';
92
92
  }
93
93
 
@@ -106,7 +106,7 @@ export class MarkdownShortcuts {
106
106
  ];
107
107
 
108
108
  for (const { re, handler } of blockPatterns) {
109
- const m = text.match(re);
109
+ const m = re.exec(text);
110
110
  if (m) {
111
111
  handler(m);
112
112
  return true;
@@ -137,8 +137,8 @@ export class MarkdownShortcuts {
137
137
  // ---------------------------------------------------------------------------
138
138
 
139
139
  _selectLineAndDelete() {
140
- const sel = window.getSelection();
141
- if (!sel || !sel.rangeCount) return;
140
+ const sel = globalThis.getSelection();
141
+ if (!sel?.rangeCount) return;
142
142
  const range = sel.getRangeAt(0);
143
143
  // Select from the start of the block to the cursor and delete
144
144
  const startRange = document.createRange();
@@ -188,8 +188,8 @@ export class MarkdownShortcuts {
188
188
  // ---------------------------------------------------------------------------
189
189
 
190
190
  _onInput() {
191
- const sel = window.getSelection();
192
- if (!sel || !sel.rangeCount) return;
191
+ const sel = globalThis.getSelection();
192
+ if (!sel?.rangeCount) return;
193
193
  const range = sel.getRangeAt(0);
194
194
  if (!range.collapsed) return;
195
195
 
@@ -215,7 +215,7 @@ export class MarkdownShortcuts {
215
215
 
216
216
  const upToCursor = text.slice(0, offset);
217
217
  for (const { re, tag } of inlineRules) {
218
- const m = upToCursor.match(re);
218
+ const m = re.exec(upToCursor);
219
219
  if (!m) continue;
220
220
 
221
221
  const matchStart = upToCursor.length - m[0].length;
@@ -233,11 +233,8 @@ export class MarkdownShortcuts {
233
233
  const beforeNode = document.createTextNode(before);
234
234
  const afterNode = document.createTextNode('​' + after);
235
235
 
236
- const parent = node.parentNode;
237
- parent.insertBefore(beforeNode, node);
238
- parent.insertBefore(el, node);
239
- parent.insertBefore(afterNode, node);
240
- parent.removeChild(node);
236
+ /** @type {ChildNode} */ (node).before(beforeNode, el, afterNode);
237
+ /** @type {ChildNode} */ (node).remove();
241
238
 
242
239
  // Place cursor after the element (after the ZWS)
243
240
  const newRange = document.createRange();
@@ -76,9 +76,7 @@ export class Mention {
76
76
 
77
77
  destroy() {
78
78
  clearTimeout(this._debounceTimer);
79
- if (this._dropdown && this._dropdown.parentNode) {
80
- this._dropdown.parentNode.removeChild(this._dropdown);
81
- }
79
+ this._dropdown?.remove();
82
80
  this._dropdown = null;
83
81
  this._disposers.forEach((d) => d());
84
82
  this._disposers = [];
@@ -163,10 +161,10 @@ export class Mention {
163
161
  let top = rect.bottom + 4;
164
162
  let left = rect.left;
165
163
 
166
- if (rect.bottom + ddh + 8 > window.innerHeight) {
164
+ if (rect.bottom + ddh + 8 > globalThis.innerHeight) {
167
165
  top = rect.top - ddh - 4;
168
166
  }
169
- left = Math.max(8, Math.min(left, window.innerWidth - ddw - 8));
167
+ left = Math.max(8, Math.min(left, globalThis.innerWidth - ddw - 8));
170
168
 
171
169
  dd.style.top = `${top}px`;
172
170
  dd.style.left = `${left}px`;
@@ -196,7 +194,7 @@ export class Mention {
196
194
  _captureCaretRect() {
197
195
  // Prefer a range over the trigger character — it has non-zero width and
198
196
  // getBoundingClientRect() reliably returns a valid rect.
199
- if (this._triggerNode && this._triggerNode.isConnected) {
197
+ if (this._triggerNode?.isConnected) {
200
198
  try {
201
199
  const r = document.createRange();
202
200
  const end = Math.min(this._triggerOffset + 1, this._triggerNode.textContent.length);
@@ -211,7 +209,7 @@ export class Mention {
211
209
  }
212
210
 
213
211
  // Fallback: use getClientRects() on the current collapsed selection
214
- const sel = window.getSelection();
212
+ const sel = globalThis.getSelection();
215
213
  if (!sel || !sel.rangeCount) return;
216
214
  const rects = sel.getRangeAt(0).getClientRects();
217
215
  if (rects.length > 0) {
@@ -224,7 +222,7 @@ export class Mention {
224
222
  // ---------------------------------------------------------------------------
225
223
 
226
224
  _getQueryAtCursor() {
227
- const sel = window.getSelection();
225
+ const sel = globalThis.getSelection();
228
226
  if (!sel || !sel.rangeCount) return null;
229
227
  const range = sel.getRangeAt(0);
230
228
  if (!range.collapsed) return null;
@@ -305,7 +303,7 @@ export class Mention {
305
303
 
306
304
  _onDocClick(e) {
307
305
  if (!this._open) return;
308
- if (this._dropdown && this._dropdown.contains(e.target)) return;
306
+ if (this._dropdown?.contains(e.target)) return;
309
307
  this._hideDropdown();
310
308
  }
311
309
 
@@ -325,7 +323,7 @@ export class Mention {
325
323
  node.textContent = before + after;
326
324
 
327
325
  // Move cursor to where the trigger was
328
- const sel = window.getSelection();
326
+ const sel = globalThis.getSelection();
329
327
  const range = document.createRange();
330
328
  range.setStart(node, this._triggerOffset);
331
329
  range.collapse(true);
@@ -43,7 +43,7 @@ export class Placeholder {
43
43
  // addition to regular whitespace before deciding if the editor is empty.
44
44
  // Without this, a freshly-created checklist item or icon leaves a ZWS in
45
45
  // the DOM that causes the placeholder to overlap real content (A-1).
46
- const hasText = editable.textContent.replace(/\u200B/g, '').trim().length > 0;
46
+ const hasText = editable.textContent.replaceAll('\u200B', '').trim().length > 0;
47
47
  const isEmpty = !hasText &&
48
48
  !editable.querySelector('img, table, hr, .an-video-wrapper');
49
49
  editable.classList.toggle('an-placeholder', isEmpty && !isFocused);
@@ -69,9 +69,7 @@ export class ShortcutsDialog {
69
69
  destroy() {
70
70
  this._disposers.forEach((d) => d());
71
71
  this._disposers = [];
72
- if (this._dialog && this._dialog.parentNode) {
73
- this._dialog.parentNode.removeChild(this._dialog);
74
- }
72
+ this._dialog?.remove();
75
73
  this._dialog = null;
76
74
  }
77
75
 
@@ -79,7 +77,7 @@ export class ShortcutsDialog {
79
77
  if (this._dialog) {
80
78
  this._dialog.style.display = 'flex';
81
79
  this._removeTrap = trapFocus(this._dialog, () => this._close());
82
- setTimeout(() => this._closeBtn && this._closeBtn.focus(), 50);
80
+ setTimeout(() => this._closeBtn?.focus(), 50);
83
81
  }
84
82
  }
85
83
 
@@ -109,9 +109,7 @@ export class Statusbar {
109
109
  this._dragDisposers.forEach((d) => d());
110
110
  this._dragDisposers = null;
111
111
  }
112
- if (this.el && this.el.parentNode) {
113
- this.el.parentNode.removeChild(this.el);
114
- }
112
+ this.el?.remove();
115
113
  this.el = null;
116
114
  }
117
115
 
@@ -217,7 +215,7 @@ export class Statusbar {
217
215
  // textContent is faster than innerText (no layout flush, no CSS visibility check)
218
216
  const text = editable.textContent || '';
219
217
  const words = _countWords(text);
220
- const chars = text.replace(/\n/g, '').length;
218
+ const chars = text.replaceAll('\n', '').length;
221
219
  const maxWords = this.options.maxWords || 0;
222
220
  const maxChars = this.options.maxChars || 0;
223
221
 
@@ -249,6 +247,6 @@ export class Statusbar {
249
247
  */
250
248
  getCharCount() {
251
249
  const editable = this.context.layoutInfo.editable;
252
- return ((editable.innerText || '').replace(/\n/g, '')).length;
250
+ return (editable.innerText || '').replaceAll('\n', '').length;
253
251
  }
254
252
  }
@@ -212,26 +212,26 @@ export class TableTooltip {
212
212
  if (!to || (
213
213
  !editable.contains(to) &&
214
214
  !this._el.contains(to) &&
215
- !(this._sizePopover && this._sizePopover.contains(to))
215
+ !this._sizePopover?.contains(to)
216
216
  )) {
217
217
  this._scheduleHide();
218
218
  }
219
219
  }, { passive: true }),
220
220
  on(document, 'click', (e) => {
221
221
  const et = /** @type {Node} */ (e.target);
222
- if (this._selectMode && this._activeTable && this._activeTable.contains(et)) return;
222
+ if (this._selectMode && this._activeTable?.contains(et)) return;
223
223
  if (this._activeTable &&
224
224
  !this._activeTable.contains(et) &&
225
225
  !this._el.contains(et) &&
226
- !(this._sizePopover && this._sizePopover.contains(et))) {
226
+ !this._sizePopover?.contains(et)) {
227
227
  this._hide();
228
228
  }
229
229
  }),
230
230
  // Sync shade strip whenever selection moves to a different cell
231
231
  on(document, 'selectionchange', () => this._syncShadeStrip()),
232
232
  // Hide when the page scrolls or resizes — the tooltip position becomes stale
233
- on(window, 'scroll', () => this._hide(), { passive: true }),
234
- on(window, 'resize', () => this._hide(), { passive: true }),
233
+ on(globalThis, 'scroll', () => this._hide(), { passive: true }),
234
+ on(globalThis, 'resize', () => this._hide(), { passive: true }),
235
235
  );
236
236
 
237
237
  this._initResize();
@@ -386,14 +386,14 @@ export class TableTooltip {
386
386
  this._clearTimers();
387
387
  this._disposers.forEach((d) => d());
388
388
  this._disposers = [];
389
- if (this._el && this._el.parentNode) this._el.parentNode.removeChild(this._el);
389
+ if (this._el && this._el.parentNode) this._el.remove();
390
390
  this._el = null;
391
391
  if (this._sizePopover && this._sizePopover.parentNode) {
392
- this._sizePopover.parentNode.removeChild(this._sizePopover);
392
+ this._sizePopover.remove();
393
393
  }
394
394
  this._sizePopover = null;
395
395
  if (this._shadePopover && this._shadePopover.parentNode) {
396
- this._shadePopover.parentNode.removeChild(this._shadePopover);
396
+ this._shadePopover.remove();
397
397
  }
398
398
  this._shadePopover = null;
399
399
  }
@@ -553,7 +553,7 @@ export class TableTooltip {
553
553
  _syncShadeStrip() {
554
554
  if (!this._shadeColorStrip || !this._el || this._el.style.display === 'none') return;
555
555
  const cell = this._getCell();
556
- this._shadeColorStrip.style.background = (cell && cell.style.backgroundColor) || 'transparent';
556
+ this._shadeColorStrip.style.background = cell?.style.backgroundColor || 'transparent';
557
557
  }
558
558
 
559
559
  _hide() {
@@ -590,7 +590,7 @@ export class TableTooltip {
590
590
  let top = rect.top - tipH - margin;
591
591
 
592
592
  if (top < margin) top = rect.bottom + margin;
593
- if (left + tipW > window.innerWidth - margin) left = window.innerWidth - tipW - margin;
593
+ if (left + tipW > globalThis.innerWidth - margin) left = globalThis.innerWidth - tipW - margin;
594
594
  if (left < margin) left = margin;
595
595
 
596
596
  this._el.style.left = `${left}px`;
@@ -603,17 +603,17 @@ export class TableTooltip {
603
603
 
604
604
  _getCell() {
605
605
  // Prefer the cell under the current text cursor (most intuitive for operations)
606
- const sel = window.getSelection();
606
+ const sel = globalThis.getSelection();
607
607
  if (sel && sel.rangeCount) {
608
608
  let container = sel.getRangeAt(0).commonAncestorContainer;
609
609
  if (container.nodeType === 3) container = container.parentElement;
610
- const cellFromSel = container && /** @type {Element} */ (container).closest('td, th');
611
- if (cellFromSel && this._activeTable && this._activeTable.contains(cellFromSel)) {
610
+ const cellFromSel = /** @type {Element} */ (container)?.closest('td, th');
611
+ if (cellFromSel && this._activeTable?.contains(cellFromSel)) {
612
612
  return cellFromSel;
613
613
  }
614
614
  }
615
615
  return this._activeCell
616
- || (this._activeTable && this._activeTable.querySelector('td, th'));
616
+ || this._activeTable?.querySelector('td, th');
617
617
  }
618
618
 
619
619
  // ---------------------------------------------------------------------------
@@ -735,7 +735,7 @@ export class TableTooltip {
735
735
  const bi = allRows.indexOf(best);
736
736
  const ri = allRows.indexOf(r);
737
737
  return position === 'above' ? (ri < bi ? r : best) : (ri > bi ? r : best);
738
- });
738
+ }, selectedRows[0]);
739
739
  const colCount = Array.from(refRow.cells).reduce((sum, c) => sum + (c.colSpan || 1), 0);
740
740
  const newRow = document.createElement('tr');
741
741
  const refCells = Array.from(refRow.cells);
@@ -786,7 +786,7 @@ export class TableTooltip {
786
786
  if (bodyRowsToDelete.length >= totalBodyRows) return;
787
787
  this._activeCell = null;
788
788
  this._clearSelection();
789
- selectedRows.forEach((r) => r.parentElement?.removeChild(r));
789
+ selectedRows.forEach((r) => r.remove());
790
790
  requestAnimationFrame(() => this._positionNear(this._activeTable));
791
791
  this.context.invoke('editor.afterCommand');
792
792
  }
@@ -810,7 +810,7 @@ export class TableTooltip {
810
810
  });
811
811
  this._activeCell = null;
812
812
  this._clearSelection();
813
- cellsToDelete.forEach((c) => c.parentElement?.removeChild(c));
813
+ cellsToDelete.forEach((c) => c.remove());
814
814
  requestAnimationFrame(() => this._positionNear(this._activeTable));
815
815
  this.context.invoke('editor.afterCommand');
816
816
  }
@@ -824,7 +824,7 @@ export class TableTooltip {
824
824
  // Prefer user panel-selected cells; fall back to text-selection range
825
825
  let selected = this._getSelectedCells().filter((c) => table.contains(c));
826
826
  if (selected.length < 2) {
827
- const sel = window.getSelection();
827
+ const sel = globalThis.getSelection();
828
828
  if (!sel || sel.rangeCount === 0) return;
829
829
  const range = sel.getRangeAt(0);
830
830
  const allCells = Array.from(table.querySelectorAll('td, th'));
@@ -866,7 +866,7 @@ export class TableTooltip {
866
866
  first.rowSpan = maxR - minR + 1;
867
867
  first.style.verticalAlign = 'middle';
868
868
  first.innerHTML = rectCells.map((c) => c.innerHTML).join('');
869
- rectCells.slice(1).forEach((c) => c.parentElement?.removeChild(c));
869
+ rectCells.slice(1).forEach((c) => c.remove());
870
870
 
871
871
  this._clearSelection();
872
872
  this.context.invoke('editor.afterCommand');
@@ -876,7 +876,7 @@ export class TableTooltip {
876
876
  const table = this._activeTable;
877
877
  if (!table) return;
878
878
  this._hide();
879
- if (table.parentNode) table.parentNode.removeChild(table);
879
+ if (table.parentNode) table.remove();
880
880
  this.context.invoke('editor.afterCommand');
881
881
  }
882
882
 
@@ -981,7 +981,7 @@ export class TableTooltip {
981
981
  this._sizeApply = null;
982
982
 
983
983
  const d1 = on(applyBtn, 'click', () => {
984
- const val = parseInt(this._sizeInputEl.value, 10);
984
+ const val = Number.parseInt(this._sizeInputEl.value, 10);
985
985
  if (val > 0 && typeof this._sizeApply === 'function') this._sizeApply(val);
986
986
  this._hideSizePopover();
987
987
  });
@@ -1016,8 +1016,8 @@ export class TableTooltip {
1016
1016
  if (!table) return;
1017
1017
  const firstCell = table.querySelector('td, th');
1018
1018
  const currentPx = firstCell
1019
- ? (parseInt(firstCell.style.borderWidth, 10) ||
1020
- parseInt(window.getComputedStyle(firstCell).borderWidth, 10) || 1)
1019
+ ? (Number.parseInt(firstCell.style.borderWidth, 10) ||
1020
+ Number.parseInt(globalThis.getComputedStyle(firstCell).borderWidth, 10) || 1)
1021
1021
  : 1;
1022
1022
  this._sizeTitleEl.textContent = this.context.locale.tooltips.table.tableBorderWidthPx;
1023
1023
  this._sizeInputEl.min = '0';
@@ -1079,8 +1079,8 @@ export class TableTooltip {
1079
1079
  const ph = this._sizePopover.offsetHeight || 110;
1080
1080
  let left = tipRect.left;
1081
1081
  let top = tipRect.bottom + 6;
1082
- if (left + pw > window.innerWidth - 8) left = window.innerWidth - pw - 8;
1083
- if (top + ph > window.innerHeight - 8) top = tipRect.top - ph - 6;
1082
+ if (left + pw > globalThis.innerWidth - 8) left = globalThis.innerWidth - pw - 8;
1083
+ if (top + ph > globalThis.innerHeight - 8) top = tipRect.top - ph - 6;
1084
1084
  this._sizePopover.style.left = `${left}px`;
1085
1085
  this._sizePopover.style.top = `${top}px`;
1086
1086
  if (this._sizeInputEl) { this._sizeInputEl.focus(); this._sizeInputEl.select(); }
@@ -1135,11 +1135,9 @@ export class TableTooltip {
1135
1135
  customRow.appendChild(customLabel);
1136
1136
  pop.appendChild(customRow);
1137
1137
 
1138
- // Prevent mousedown from collapsing editor selection
1139
- this._disposers.push(on(pop, 'mousedown', (e) => e.preventDefault()));
1140
-
1141
- // Keep tooltip alive while hovering popover
1138
+ // Prevent mousedown from collapsing editor selection; keep tooltip alive while hovering
1142
1139
  this._disposers.push(
1140
+ on(pop, 'mousedown', (e) => e.preventDefault()),
1143
1141
  on(pop, 'mouseenter', () => this._clearTimers()),
1144
1142
  on(pop, 'mouseleave', () => this._scheduleHide()),
1145
1143
  );
@@ -1150,7 +1148,7 @@ export class TableTooltip {
1150
1148
  if (this._shadePopover &&
1151
1149
  this._shadePopover.style.display !== 'none' &&
1152
1150
  !this._shadePopover.contains(et) &&
1153
- !(this._el && this._el.contains(et))) {
1151
+ !this._el?.contains(et)) {
1154
1152
  this._hideCellShadePopover();
1155
1153
  }
1156
1154
  }));
@@ -1172,8 +1170,8 @@ export class TableTooltip {
1172
1170
  const tipRect = this._el.getBoundingClientRect();
1173
1171
  let left = tipRect.left;
1174
1172
  let top = tipRect.bottom + 6;
1175
- if (left + pw > window.innerWidth - 8) left = window.innerWidth - pw - 8;
1176
- if (top + ph > window.innerHeight - 8) top = tipRect.top - ph - 6;
1173
+ if (left + pw > globalThis.innerWidth - 8) left = globalThis.innerWidth - pw - 8;
1174
+ if (top + ph > globalThis.innerHeight - 8) top = tipRect.top - ph - 6;
1177
1175
  this._shadePopover.style.left = `${Math.max(8, left)}px`;
1178
1176
  this._shadePopover.style.top = `${Math.max(8, top)}px`;
1179
1177
  });
@@ -146,7 +146,7 @@ export class Toolbar {
146
146
  this._disposers.forEach((d) => d());
147
147
  this._disposers = [];
148
148
  if (this.el && this.el.parentNode) {
149
- this.el.parentNode.removeChild(this.el);
149
+ this.el.remove();
150
150
  }
151
151
  this.el = null;
152
152
  }
@@ -244,8 +244,8 @@ export class Toolbar {
244
244
 
245
245
  const setHighlight = (rows, cols) => {
246
246
  cells.forEach((cell) => {
247
- const r = +cell.getAttribute('data-row');
248
- const c = +cell.getAttribute('data-col');
247
+ const r = +cell.dataset.row;
248
+ const c = +cell.dataset.col;
249
249
  cell.classList.toggle('active', r <= rows && c <= cols);
250
250
  });
251
251
  label.textContent = (rows && cols) ? `${rows} × ${cols}` : (this.context.locale.toolbar.insertTableLabel || 'Insert Table');
@@ -264,8 +264,8 @@ export class Toolbar {
264
264
 
265
265
  let left = rect.left;
266
266
  let top = rect.bottom + 4;
267
- if (left + pw > window.innerWidth - 8) left = Math.max(8, window.innerWidth - pw - 8);
268
- if (top + ph > window.innerHeight - 8) top = rect.top - ph - 4;
267
+ if (left + pw > globalThis.innerWidth - 8) left = Math.max(8, globalThis.innerWidth - pw - 8);
268
+ if (top + ph > globalThis.innerHeight - 8) top = rect.top - ph - 4;
269
269
 
270
270
  popup.style.left = `${left}px`;
271
271
  popup.style.top = `${top}px`;
@@ -286,18 +286,18 @@ export class Toolbar {
286
286
  });
287
287
 
288
288
  const d2 = on(grid, 'mouseover', (e) => {
289
- const cell = /** @type {Element} */ (e.target)?.closest('.an-table-cell');
289
+ const cell = /** @type {HTMLElement|null} */ (/** @type {Element} */ (e.target)?.closest('.an-table-cell'));
290
290
  if (!cell) return;
291
- setHighlight(+cell.getAttribute('data-row'), +cell.getAttribute('data-col'));
291
+ setHighlight(+cell.dataset.row, +cell.dataset.col);
292
292
  });
293
293
 
294
294
  const d3 = on(grid, 'mouseleave', () => setHighlight(0, 0));
295
295
 
296
296
  const d4 = on(grid, 'click', (e) => {
297
- const cell = /** @type {Element} */ (e.target)?.closest('.an-table-cell');
297
+ const cell = /** @type {HTMLElement|null} */ (/** @type {Element} */ (e.target)?.closest('.an-table-cell'));
298
298
  if (!cell) return;
299
- const rows = +cell.getAttribute('data-row');
300
- const cols = +cell.getAttribute('data-col');
299
+ const rows = +cell.dataset.row;
300
+ const cols = +cell.dataset.col;
301
301
  closePopup();
302
302
  this.context.invoke('editor.focus');
303
303
  def.action(this.context, rows, cols);
@@ -308,7 +308,7 @@ export class Toolbar {
308
308
  // Append popup to body so position:fixed is truly viewport-relative,
309
309
  // unaffected by any ancestor transform / filter (same pattern as color picker).
310
310
  this._disposers.push(d1, d2, d3, d4, d5, () => {
311
- if (popup.parentNode) popup.parentNode.removeChild(popup);
311
+ if (popup.parentNode) popup.remove();
312
312
  });
313
313
 
314
314
  wrap.appendChild(btn);
@@ -398,14 +398,14 @@ export class Toolbar {
398
398
  let savedRange = null;
399
399
 
400
400
  const saveSelection = () => {
401
- const sel = window.getSelection();
401
+ const sel = globalThis.getSelection();
402
402
  savedRange = (sel && sel.rangeCount) ? sel.getRangeAt(0).cloneRange() : null;
403
403
  };
404
404
 
405
405
  const restoreSelection = () => {
406
406
  if (!savedRange) return;
407
407
  try {
408
- const sel = window.getSelection();
408
+ const sel = globalThis.getSelection();
409
409
  if (!sel) return;
410
410
  sel.removeAllRanges();
411
411
  sel.addRange(savedRange);
@@ -424,7 +424,7 @@ export class Toolbar {
424
424
  const rect = arrowBtn.getBoundingClientRect();
425
425
  const popupMinW = 184;
426
426
  let left = rect.left;
427
- if (left + popupMinW > window.innerWidth) left = rect.right - popupMinW;
427
+ if (left + popupMinW > globalThis.innerWidth) left = rect.right - popupMinW;
428
428
  popup.style.top = `${rect.bottom + 4}px`;
429
429
  popup.style.left = `${Math.max(4, left)}px`;
430
430
  popup.style.display = 'block';
@@ -491,13 +491,13 @@ export class Toolbar {
491
491
  // popup doesn't drift away from the button it belongs to.
492
492
  const onScrollResize = () => { if (isOpen) closePopup(); };
493
493
  document.addEventListener('scroll', onScrollResize, { passive: true, capture: true });
494
- window.addEventListener('resize', onScrollResize, { passive: true });
494
+ globalThis.addEventListener('resize', onScrollResize, { passive: true });
495
495
 
496
496
  this._disposers.push(d1, d2, d2b, d3, d3b, d4, d5, d6,
497
497
  () => document.removeEventListener('scroll', onScrollResize, { capture: true }),
498
- () => window.removeEventListener('resize', onScrollResize),
498
+ () => globalThis.removeEventListener('resize', onScrollResize),
499
499
  // Remove popup from body on editor destroy
500
- () => { if (popup.parentNode) popup.parentNode.removeChild(popup); },
500
+ () => { if (popup.parentNode) popup.remove(); },
501
501
  );
502
502
 
503
503
  // Register this popup's closer so other color pickers can close it
@@ -562,7 +562,7 @@ export class Toolbar {
562
562
  /** @type {Range|null} */
563
563
  let _savedRange = null;
564
564
  const dMousedown = on(select, 'mousedown', () => {
565
- const sel = window.getSelection();
565
+ const sel = globalThis.getSelection();
566
566
  _savedRange = (sel && sel.rangeCount) ? sel.getRangeAt(0).cloneRange() : null;
567
567
  });
568
568
 
@@ -574,7 +574,7 @@ export class Toolbar {
574
574
  // Restore selection saved on mousedown so the action targets the correct text.
575
575
  if (_savedRange) {
576
576
  try {
577
- const sel = window.getSelection();
577
+ const sel = globalThis.getSelection();
578
578
  if (sel) { sel.removeAllRanges(); sel.addRange(_savedRange); }
579
579
  } catch (_) { /* range may be stale if DOM changed */ }
580
580
  }
@@ -684,7 +684,7 @@ export class Toolbar {
684
684
 
685
685
  // Sync button active states
686
686
  this.el.querySelectorAll('button[data-btn]').forEach((btn) => {
687
- const def = btnMap.get(btn.getAttribute('data-btn'));
687
+ const def = btnMap.get(/** @type {HTMLElement} */ (btn).dataset.btn);
688
688
  if (def && typeof def.isActive === 'function') {
689
689
  btn.classList.toggle('active', !!def.isActive(this.context));
690
690
  }
@@ -695,7 +695,7 @@ export class Toolbar {
695
695
 
696
696
  // Sync select dropdowns (e.g. font family) with current cursor position
697
697
  this.el.querySelectorAll('select[data-btn]').forEach((select) => {
698
- const def = btnMap.get(select.getAttribute('data-btn'));
698
+ const def = btnMap.get(/** @type {HTMLElement} */ (select).dataset.btn);
699
699
  if (!def || typeof def.getValue !== 'function') return;
700
700
  // queryCommandValue returns the font name, possibly quoted — strip quotes
701
701
  let raw = (def.getValue(this.context) || '').replace(/["']/g, '').trim();