autumnnote 1.6.3 → 1.6.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/autumnnote.es.js +133 -119
- package/dist/autumnnote.es.js.map +1 -1
- package/dist/autumnnote.umd.js +131 -117
- package/dist/autumnnote.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/js/Context.js +4 -4
- package/src/js/core/detectLang.js +1 -1
- package/src/js/core/dom.js +6 -8
- package/src/js/core/markdown.js +2 -2
- package/src/js/core/range.js +1 -1
- package/src/js/editing/History.js +2 -2
- package/src/js/editing/Style.js +17 -15
- package/src/js/editing/Typing.js +7 -7
- package/src/js/index.js +1 -1
- package/src/js/module/AutoSaveRestore.js +3 -2
- package/src/js/module/BaseDialog.js +1 -1
- package/src/js/module/BubbleToolbar.js +3 -3
- package/src/js/module/Buttons.js +8 -8
- package/src/js/module/Clipboard.js +4 -4
- package/src/js/module/CodeTooltip.js +1 -1
- package/src/js/module/ContextMenu.js +14 -12
- package/src/js/module/Editor.js +10 -10
- package/src/js/module/EmojiDialog.js +1 -1
- package/src/js/module/FindReplace.js +6 -7
- package/src/js/module/IconDialog.js +2 -2
- package/src/js/module/ImageCropOverlay.js +2 -1
- package/src/js/module/ImageDialog.js +3 -3
- package/src/js/module/ImageResizer.js +3 -3
- package/src/js/module/ImageTooltip.js +5 -3
- package/src/js/module/LinkDialog.js +1 -1
- package/src/js/module/LinkTooltip.js +3 -3
- package/src/js/module/Mention.js +3 -3
- package/src/js/module/TableTooltip.js +57 -39
- package/src/js/module/Toolbar.js +21 -20
- package/src/js/module/VideoDialog.js +5 -2
- package/src/js/renderer.js +2 -2
|
@@ -298,8 +298,7 @@ export class FindReplace extends BaseDialog {
|
|
|
298
298
|
range.surroundContents(mark);
|
|
299
299
|
this._matches.push({ mark });
|
|
300
300
|
} catch (_) {
|
|
301
|
-
// surroundContents fails when the range crosses element boundaries
|
|
302
|
-
// This can happen with <br> inside matched text — skip safely.
|
|
301
|
+
void _; // surroundContents fails when the range crosses element boundaries
|
|
303
302
|
this._matches.push({ mark: null });
|
|
304
303
|
}
|
|
305
304
|
}
|
|
@@ -311,7 +310,7 @@ export class FindReplace extends BaseDialog {
|
|
|
311
310
|
if (this._matches.length === 0) return;
|
|
312
311
|
|
|
313
312
|
// Highlight the first (current) match
|
|
314
|
-
if (this._matches[0]
|
|
313
|
+
if (this._matches[0]?.mark) {
|
|
315
314
|
this._matches[0].mark.className = 'an-highlight an-highlight-current';
|
|
316
315
|
this._matches[0].mark.scrollIntoView({ block: 'center', behavior: 'smooth' });
|
|
317
316
|
}
|
|
@@ -328,7 +327,7 @@ export class FindReplace extends BaseDialog {
|
|
|
328
327
|
// Reuse compiled regex when query and case-sensitivity haven't changed
|
|
329
328
|
if (this._lastQuery !== query || this._lastCaseSensitive !== this._caseSensitive) {
|
|
330
329
|
const flags = this._caseSensitive ? 'g' : 'gi';
|
|
331
|
-
const escaped = query.replace(/[.*+?^${}()|[\]\\]/g,
|
|
330
|
+
const escaped = query.replace(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
|
|
332
331
|
this._queryRegex = new RegExp(escaped, flags);
|
|
333
332
|
this._lastQuery = query;
|
|
334
333
|
this._lastCaseSensitive = this._caseSensitive;
|
|
@@ -370,7 +369,7 @@ export class FindReplace extends BaseDialog {
|
|
|
370
369
|
|
|
371
370
|
_scrollToMatch(index) {
|
|
372
371
|
const match = this._matches[index];
|
|
373
|
-
if (!match
|
|
372
|
+
if (!match?.mark) return;
|
|
374
373
|
// Update CSS classes
|
|
375
374
|
this._matches.forEach((m, i) => {
|
|
376
375
|
if (m.mark) {
|
|
@@ -389,7 +388,7 @@ export class FindReplace extends BaseDialog {
|
|
|
389
388
|
_replace() {
|
|
390
389
|
if (this._matches.length === 0 || this._currentIndex < 0) return;
|
|
391
390
|
const match = this._matches[this._currentIndex];
|
|
392
|
-
if (!match
|
|
391
|
+
if (!match?.mark?.parentNode) return;
|
|
393
392
|
|
|
394
393
|
const replacement = this._replaceInput ? this._replaceInput.value : '';
|
|
395
394
|
const parent = match.mark.parentNode;
|
|
@@ -412,7 +411,7 @@ export class FindReplace extends BaseDialog {
|
|
|
412
411
|
const replacement = this._replaceInput ? this._replaceInput.value : '';
|
|
413
412
|
|
|
414
413
|
this._matches.forEach(({ mark }) => {
|
|
415
|
-
if (!mark
|
|
414
|
+
if (!mark?.parentNode) return;
|
|
416
415
|
const textNode = document.createTextNode(replacement);
|
|
417
416
|
mark.parentNode.insertBefore(textNode, mark);
|
|
418
417
|
mark.remove();
|
|
@@ -570,7 +570,7 @@ export class IconDialog extends BaseDialog {
|
|
|
570
570
|
const _tdAnchor = /** @type {Element|null} */ (_sc.nodeType === 1 ? _sc : _sc.parentElement)
|
|
571
571
|
?.closest('td, th');
|
|
572
572
|
range.deleteContents();
|
|
573
|
-
if (_tdAnchor
|
|
573
|
+
if (_tdAnchor?.isConnected && !_tdAnchor.contains(range.startContainer)) {
|
|
574
574
|
range.setStart(_tdAnchor, 0);
|
|
575
575
|
range.collapse(true);
|
|
576
576
|
}
|
|
@@ -580,7 +580,7 @@ export class IconDialog extends BaseDialog {
|
|
|
580
580
|
// Keep a text anchor (\u200B) after the icon so ArrowLeft/ArrowRight and
|
|
581
581
|
// caret placement at end-of-line stay stable across browsers.
|
|
582
582
|
let caretTextNode = iconEl.nextSibling;
|
|
583
|
-
if (
|
|
583
|
+
if (caretTextNode?.nodeType !== Node.TEXT_NODE) {
|
|
584
584
|
caretTextNode = document.createTextNode('\u200B');
|
|
585
585
|
iconEl.parentNode.insertBefore(caretTextNode, iconEl.nextSibling);
|
|
586
586
|
} else if (!caretTextNode.textContent) {
|
|
@@ -58,6 +58,7 @@ function drawCropToCanvas(img, naturalRect, renderW, renderH) {
|
|
|
58
58
|
ctx.getImageData(0, 0, 1, 1);
|
|
59
59
|
resolve(canvas);
|
|
60
60
|
} catch (_) {
|
|
61
|
+
void _; // canvas tainted — resolve with null
|
|
61
62
|
resolve(null);
|
|
62
63
|
}
|
|
63
64
|
};
|
|
@@ -399,7 +400,7 @@ export class ImageCropOverlay {
|
|
|
399
400
|
const startY = e.clientY;
|
|
400
401
|
const orig = { ...this._box };
|
|
401
402
|
const r = this._imgRect;
|
|
402
|
-
const lockAR = this._arCheck
|
|
403
|
+
const lockAR = this._arCheck?.checked;
|
|
403
404
|
const aspect = orig.w / (orig.h || 1);
|
|
404
405
|
|
|
405
406
|
const onMove = (me) => {
|
|
@@ -107,8 +107,8 @@ export class ImageDialog extends BaseDialog {
|
|
|
107
107
|
// ---------------------------------------------------------------------------
|
|
108
108
|
|
|
109
109
|
_onFileChange() {
|
|
110
|
-
const file = this._fileInput
|
|
111
|
-
if (!file
|
|
110
|
+
const file = this._fileInput?.files?.[0];
|
|
111
|
+
if (!file?.type?.startsWith('image/')) return;
|
|
112
112
|
|
|
113
113
|
// C2: Only allow web-displayable formats. TIFF, BMP, RAW and similar
|
|
114
114
|
// formats are not rendered by browsers — reject them with a clear message.
|
|
@@ -146,7 +146,7 @@ export class ImageDialog extends BaseDialog {
|
|
|
146
146
|
_onInsert() {
|
|
147
147
|
const src = this._urlInput.value.trim();
|
|
148
148
|
const alt = this._altInput.value.trim();
|
|
149
|
-
const alignRadio = this._alignRow
|
|
149
|
+
const alignRadio = this._alignRow?.querySelector('input[name="an-img-align"]:checked');
|
|
150
150
|
const align = alignRadio ? /** @type {HTMLInputElement} */ (alignRadio).value : '';
|
|
151
151
|
|
|
152
152
|
if (!src) {
|
|
@@ -75,7 +75,7 @@ export class ImageResizer {
|
|
|
75
75
|
this._positionRaf = null;
|
|
76
76
|
}
|
|
77
77
|
this._deselect();
|
|
78
|
-
if (this._overlay
|
|
78
|
+
if (this._overlay?.parentNode) {
|
|
79
79
|
this._overlay.remove();
|
|
80
80
|
}
|
|
81
81
|
this._overlay = null;
|
|
@@ -140,7 +140,7 @@ export class ImageResizer {
|
|
|
140
140
|
_onDocClick(e) {
|
|
141
141
|
if (!this._activeImg) return;
|
|
142
142
|
if (e.target === this._activeImg) return;
|
|
143
|
-
if (this._overlay
|
|
143
|
+
if (this._overlay?.contains(e.target)) return;
|
|
144
144
|
// Don't deselect while interacting with the context menu
|
|
145
145
|
if (e.target.closest('.an-contextmenu')) return;
|
|
146
146
|
this._deselect();
|
|
@@ -181,7 +181,7 @@ export class ImageResizer {
|
|
|
181
181
|
|
|
182
182
|
// Skip DOM writes when position hasn't changed (common during non-scroll rAF ticks)
|
|
183
183
|
const p = this._lastOverlayPos;
|
|
184
|
-
if (p
|
|
184
|
+
if (p?.l === rect.left && p.t === rect.top && p.w === rect.width && p.h === rect.height) return;
|
|
185
185
|
this._lastOverlayPos = { l: rect.left, t: rect.top, w: rect.width, h: rect.height };
|
|
186
186
|
|
|
187
187
|
const left = rect.left - containerRect.left + offsetParent.scrollLeft;
|
|
@@ -284,9 +284,11 @@ export class ImageTooltip {
|
|
|
284
284
|
const next = (prev + delta + 360) % 360; // normalise to [0, 360)
|
|
285
285
|
// Preserve any other transform functions (e.g. scale), replace only rotate()
|
|
286
286
|
const cleaned = current.replace(/rotate\(-?[\d.]+deg\)/, '').trim();
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
287
|
+
if (cleaned) {
|
|
288
|
+
img.style.transform = `${cleaned} rotate(${next}deg)`;
|
|
289
|
+
} else {
|
|
290
|
+
img.style.transform = next === 0 ? '' : `rotate(${next}deg)`;
|
|
291
|
+
}
|
|
290
292
|
this.context.invoke('editor.afterCommand');
|
|
291
293
|
this.context.invoke('imageResizer.updateOverlay');
|
|
292
294
|
requestAnimationFrame(() => { if (this._activeImg) this._positionNear(this._activeImg); });
|
|
@@ -88,7 +88,7 @@ export class LinkDialog extends BaseDialog {
|
|
|
88
88
|
// Try to find currently selected anchor
|
|
89
89
|
const sel = globalThis.getSelection();
|
|
90
90
|
let anchor = null;
|
|
91
|
-
if (sel
|
|
91
|
+
if (sel?.rangeCount > 0) {
|
|
92
92
|
let node = sel.getRangeAt(0).startContainer;
|
|
93
93
|
while (node) {
|
|
94
94
|
if (node.nodeName === 'A') { anchor = node; break; }
|
|
@@ -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
|
|
60
|
+
if (this._el?.parentNode) this._el.remove();
|
|
61
61
|
this._el = null;
|
|
62
62
|
}
|
|
63
63
|
|
|
@@ -178,7 +178,7 @@ export class LinkTooltip {
|
|
|
178
178
|
_truncateUrl(url) {
|
|
179
179
|
try {
|
|
180
180
|
const u = new URL(url);
|
|
181
|
-
const display = u.host + (u.pathname
|
|
181
|
+
const display = u.host + (u.pathname === '/' ? '' : u.pathname);
|
|
182
182
|
return display.length > 48 ? display.slice(0, 48) + '…' : display;
|
|
183
183
|
} catch {
|
|
184
184
|
return url.length > 48 ? url.slice(0, 48) + '…' : url;
|
|
@@ -213,7 +213,7 @@ export class LinkTooltip {
|
|
|
213
213
|
// Brief visual feedback
|
|
214
214
|
if (this._copyBtn) {
|
|
215
215
|
this._copyBtn.classList.add('an-link-tooltip-btn--copied');
|
|
216
|
-
setTimeout(() => this._copyBtn
|
|
216
|
+
setTimeout(() => this._copyBtn?.classList.remove('an-link-tooltip-btn--copied'), 1000);
|
|
217
217
|
}
|
|
218
218
|
}
|
|
219
219
|
|
package/src/js/module/Mention.js
CHANGED
|
@@ -205,12 +205,12 @@ export class Mention {
|
|
|
205
205
|
this._caretRect = candidate;
|
|
206
206
|
return;
|
|
207
207
|
}
|
|
208
|
-
} catch (_) {}
|
|
208
|
+
} catch (_) { void _; }
|
|
209
209
|
}
|
|
210
210
|
|
|
211
211
|
// Fallback: use getClientRects() on the current collapsed selection
|
|
212
212
|
const sel = globalThis.getSelection();
|
|
213
|
-
if (!sel
|
|
213
|
+
if (!sel?.rangeCount) return;
|
|
214
214
|
const rects = sel.getRangeAt(0).getClientRects();
|
|
215
215
|
if (rects.length > 0) {
|
|
216
216
|
this._caretRect = rects[rects.length - 1];
|
|
@@ -223,7 +223,7 @@ export class Mention {
|
|
|
223
223
|
|
|
224
224
|
_getQueryAtCursor() {
|
|
225
225
|
const sel = globalThis.getSelection();
|
|
226
|
-
if (!sel
|
|
226
|
+
if (!sel?.rangeCount) return null;
|
|
227
227
|
const range = sel.getRangeAt(0);
|
|
228
228
|
if (!range.collapsed) return null;
|
|
229
229
|
|
|
@@ -58,7 +58,7 @@ function getCellAfterVisualCol(row, visualIdx) {
|
|
|
58
58
|
if (vIdx > visualIdx) {
|
|
59
59
|
// next cell after the one that starts at / spans visualIdx
|
|
60
60
|
const next = c.nextElementSibling;
|
|
61
|
-
return (next
|
|
61
|
+
return (next?.tagName === 'TD' || next?.tagName === 'TH') ? /** @type {HTMLTableCellElement} */ (next) : null;
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
return null;
|
|
@@ -190,10 +190,7 @@ export class TableTooltip {
|
|
|
190
190
|
on(editable, 'mousedown', onSelMousedown),
|
|
191
191
|
on(editable, 'mousemove', onSelMousemove),
|
|
192
192
|
on(document, 'mouseup', onSelMouseup),
|
|
193
|
-
|
|
194
|
-
// ────────────────────────────────────────────────────────────────────────
|
|
195
|
-
|
|
196
|
-
this._disposers.push(
|
|
193
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
197
194
|
on(editable, 'mouseover', (e) => {
|
|
198
195
|
if (this.context.layoutInfo.container.classList.contains('an-disabled')) return;
|
|
199
196
|
const table = /** @type {Element} */ (e.target)?.closest('table');
|
|
@@ -309,22 +306,27 @@ export class TableTooltip {
|
|
|
309
306
|
_startY = e.clientY;
|
|
310
307
|
_table = _nearCell.closest('table');
|
|
311
308
|
if (_edge === 'col') {
|
|
312
|
-
|
|
313
|
-
_colIdx
|
|
314
|
-
// Cache
|
|
315
|
-
// F-1:
|
|
316
|
-
// distributes it equally across all spanned columns instead of resizing
|
|
317
|
-
// only the target column. Rows that have an individual cell at this
|
|
318
|
-
// visual column index are resized independently as expected.
|
|
309
|
+
// Right edge of a merged cell = last spanned column, not the start column
|
|
310
|
+
_colIdx = getVisualColIndex(_nearCell) + (_nearCell.colSpan || 1) - 1;
|
|
311
|
+
// Cache non-merged cells at that column — merged cells cannot be individually
|
|
312
|
+
// resized per column (F-1: colSpan > 1 distributes width across all spanned cols)
|
|
319
313
|
_colCells = _colIdx >= 0
|
|
320
314
|
? Array.from(_table.querySelectorAll('tr'))
|
|
321
315
|
.map(r => getCellAtVisualCol(r, _colIdx))
|
|
322
316
|
.filter(Boolean)
|
|
323
317
|
.filter(c => (c.colSpan || 1) === 1)
|
|
324
318
|
: [];
|
|
319
|
+
// Use the actual column width from a non-merged cell; fall back to per-column estimate
|
|
320
|
+
_startW = _colCells.length > 0
|
|
321
|
+
? _colCells[0].offsetWidth
|
|
322
|
+
: Math.max(30, Math.round(_nearCell.offsetWidth / (_nearCell.colSpan || 1)));
|
|
325
323
|
document.body.style.cursor = 'col-resize';
|
|
326
324
|
} else {
|
|
327
|
-
|
|
325
|
+
// Bottom edge of a merged cell = last spanned row, not the cell's own <tr>
|
|
326
|
+
const tr = _nearCell.closest('tr');
|
|
327
|
+
const rowStart = tr ? Array.from(_table.rows).indexOf(tr) : 0;
|
|
328
|
+
const lastRowIdx = rowStart + (_nearCell.rowSpan || 1) - 1;
|
|
329
|
+
_row = _table.rows[lastRowIdx] || tr;
|
|
328
330
|
_startH = _row ? _row.offsetHeight : 40;
|
|
329
331
|
document.body.style.cursor = 'row-resize';
|
|
330
332
|
}
|
|
@@ -386,13 +388,13 @@ export class TableTooltip {
|
|
|
386
388
|
this._clearTimers();
|
|
387
389
|
this._disposers.forEach((d) => d());
|
|
388
390
|
this._disposers = [];
|
|
389
|
-
if (this._el
|
|
391
|
+
if (this._el?.parentNode) this._el.remove();
|
|
390
392
|
this._el = null;
|
|
391
|
-
if (this._sizePopover
|
|
393
|
+
if (this._sizePopover?.parentNode) {
|
|
392
394
|
this._sizePopover.remove();
|
|
393
395
|
}
|
|
394
396
|
this._sizePopover = null;
|
|
395
|
-
if (this._shadePopover
|
|
397
|
+
if (this._shadePopover?.parentNode) {
|
|
396
398
|
this._shadePopover.remove();
|
|
397
399
|
}
|
|
398
400
|
this._shadePopover = null;
|
|
@@ -604,7 +606,7 @@ export class TableTooltip {
|
|
|
604
606
|
_getCell() {
|
|
605
607
|
// Prefer the cell under the current text cursor (most intuitive for operations)
|
|
606
608
|
const sel = globalThis.getSelection();
|
|
607
|
-
if (sel
|
|
609
|
+
if (sel?.rangeCount) {
|
|
608
610
|
let container = sel.getRangeAt(0).commonAncestorContainer;
|
|
609
611
|
if (container.nodeType === 3) container = container.parentElement;
|
|
610
612
|
const cellFromSel = /** @type {Element} */ (container)?.closest('td, th');
|
|
@@ -659,7 +661,7 @@ export class TableTooltip {
|
|
|
659
661
|
if (!startCell) return [];
|
|
660
662
|
if (!endCell || startCell === endCell) return [startCell];
|
|
661
663
|
const table = startCell.closest('table');
|
|
662
|
-
if (!table
|
|
664
|
+
if (!table?.contains(endCell)) return [startCell];
|
|
663
665
|
|
|
664
666
|
const { gridMap, cellPos } = buildGridMap(table);
|
|
665
667
|
const sp = cellPos.get(startCell);
|
|
@@ -734,7 +736,8 @@ export class TableTooltip {
|
|
|
734
736
|
const refRow = selectedRows.reduce((best, r) => {
|
|
735
737
|
const bi = allRows.indexOf(best);
|
|
736
738
|
const ri = allRows.indexOf(r);
|
|
737
|
-
|
|
739
|
+
if (position === 'above') return ri < bi ? r : best;
|
|
740
|
+
return ri > bi ? r : best;
|
|
738
741
|
}, selectedRows[0]);
|
|
739
742
|
const colCount = Array.from(refRow.cells).reduce((sum, c) => sum + (c.colSpan || 1), 0);
|
|
740
743
|
const newRow = document.createElement('tr');
|
|
@@ -742,12 +745,12 @@ export class TableTooltip {
|
|
|
742
745
|
for (let i = 0; i < colCount; i++) {
|
|
743
746
|
const td = createElement('td', {}, ['\u00a0']);
|
|
744
747
|
const ref = refCells[i];
|
|
745
|
-
if (ref
|
|
746
|
-
if (ref
|
|
748
|
+
if (ref?.style.width) td.style.width = ref.style.width;
|
|
749
|
+
if (ref?.style.minWidth) td.style.minWidth = ref.style.minWidth;
|
|
747
750
|
newRow.appendChild(td);
|
|
748
751
|
}
|
|
749
752
|
if (position === 'above') refRow.parentElement?.insertBefore(newRow, refRow);
|
|
750
|
-
else refRow.
|
|
753
|
+
else refRow.after(newRow);
|
|
751
754
|
requestAnimationFrame(() => this._positionNear(this._activeTable));
|
|
752
755
|
this.context.invoke('editor.afterCommand');
|
|
753
756
|
}
|
|
@@ -865,8 +868,21 @@ export class TableTooltip {
|
|
|
865
868
|
first.colSpan = maxC - minC + 1;
|
|
866
869
|
first.rowSpan = maxR - minR + 1;
|
|
867
870
|
first.style.verticalAlign = 'middle';
|
|
868
|
-
first.
|
|
869
|
-
|
|
871
|
+
first.style.height = '';
|
|
872
|
+
first.style.minHeight = '';
|
|
873
|
+
|
|
874
|
+
// Merge content: collect non-empty inner HTML, join with <br> separator
|
|
875
|
+
const parts = rectCells
|
|
876
|
+
.map((c) => c.innerHTML.replace(/^(<br\s*\/?>|\s)+$/i, '').trim())
|
|
877
|
+
.filter((h) => h !== '');
|
|
878
|
+
first.innerHTML = parts.length ? parts.join('<br>') : '<br>';
|
|
879
|
+
|
|
880
|
+
rectCells.slice(1).forEach((c) => {
|
|
881
|
+
const row = c.parentElement;
|
|
882
|
+
c.remove();
|
|
883
|
+
// Remove the parent <tr> if it is now completely empty
|
|
884
|
+
if (row && row.cells.length === 0) row.remove();
|
|
885
|
+
});
|
|
870
886
|
|
|
871
887
|
this._clearSelection();
|
|
872
888
|
this.context.invoke('editor.afterCommand');
|
|
@@ -939,7 +955,7 @@ export class TableTooltip {
|
|
|
939
955
|
let ref = null;
|
|
940
956
|
for (const tc of targetRow.cells) {
|
|
941
957
|
const tp = cellPos.get(tc);
|
|
942
|
-
if (tp
|
|
958
|
+
if (tp?.c > c) { ref = tc; break; }
|
|
943
959
|
}
|
|
944
960
|
for (let dc = 0; dc < cs; dc++) {
|
|
945
961
|
targetRow.insertBefore(createElement(tag, {}, ['\u00a0']), ref);
|
|
@@ -1043,9 +1059,12 @@ export class TableTooltip {
|
|
|
1043
1059
|
: this.context.locale.tooltips.table.rowHeightPx;
|
|
1044
1060
|
this._sizeInputEl.min = '1';
|
|
1045
1061
|
this._sizeInputEl.max = '2000';
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1062
|
+
if (isCol) {
|
|
1063
|
+
this._sizeInputEl.value = cell.offsetWidth || 120;
|
|
1064
|
+
} else {
|
|
1065
|
+
const refRow = cell.closest('tr');
|
|
1066
|
+
this._sizeInputEl.value = refRow ? (refRow.offsetHeight || 40) : 40;
|
|
1067
|
+
}
|
|
1049
1068
|
this._sizeApply = (val) => {
|
|
1050
1069
|
const table = cell.closest('table');
|
|
1051
1070
|
if (!table) return;
|
|
@@ -1140,19 +1159,18 @@ export class TableTooltip {
|
|
|
1140
1159
|
on(pop, 'mousedown', (e) => e.preventDefault()),
|
|
1141
1160
|
on(pop, 'mouseenter', () => this._clearTimers()),
|
|
1142
1161
|
on(pop, 'mouseleave', () => this._scheduleHide()),
|
|
1162
|
+
// Close on outside click
|
|
1163
|
+
on(document, 'click', (e) => {
|
|
1164
|
+
const et = /** @type {Node} */ (e.target);
|
|
1165
|
+
if (this._shadePopover &&
|
|
1166
|
+
this._shadePopover.style.display !== 'none' &&
|
|
1167
|
+
!this._shadePopover.contains(et) &&
|
|
1168
|
+
!this._el?.contains(et)) {
|
|
1169
|
+
this._hideCellShadePopover();
|
|
1170
|
+
}
|
|
1171
|
+
}),
|
|
1143
1172
|
);
|
|
1144
1173
|
|
|
1145
|
-
// Close on outside click
|
|
1146
|
-
this._disposers.push(on(document, 'click', (e) => {
|
|
1147
|
-
const et = /** @type {Node} */ (e.target);
|
|
1148
|
-
if (this._shadePopover &&
|
|
1149
|
-
this._shadePopover.style.display !== 'none' &&
|
|
1150
|
-
!this._shadePopover.contains(et) &&
|
|
1151
|
-
!this._el?.contains(et)) {
|
|
1152
|
-
this._hideCellShadePopover();
|
|
1153
|
-
}
|
|
1154
|
-
}));
|
|
1155
|
-
|
|
1156
1174
|
return pop;
|
|
1157
1175
|
}
|
|
1158
1176
|
|
package/src/js/module/Toolbar.js
CHANGED
|
@@ -145,7 +145,7 @@ export class Toolbar {
|
|
|
145
145
|
this._refreshRaf = null;
|
|
146
146
|
this._disposers.forEach((d) => d());
|
|
147
147
|
this._disposers = [];
|
|
148
|
-
if (this.el
|
|
148
|
+
if (this.el?.parentNode) {
|
|
149
149
|
this.el.remove();
|
|
150
150
|
}
|
|
151
151
|
this.el = null;
|
|
@@ -399,7 +399,7 @@ export class Toolbar {
|
|
|
399
399
|
|
|
400
400
|
const saveSelection = () => {
|
|
401
401
|
const sel = globalThis.getSelection();
|
|
402
|
-
savedRange =
|
|
402
|
+
savedRange = sel?.rangeCount ? sel.getRangeAt(0).cloneRange() : null;
|
|
403
403
|
};
|
|
404
404
|
|
|
405
405
|
const restoreSelection = () => {
|
|
@@ -410,7 +410,7 @@ export class Toolbar {
|
|
|
410
410
|
sel.removeAllRanges();
|
|
411
411
|
sel.addRange(savedRange);
|
|
412
412
|
} catch (_) {
|
|
413
|
-
//
|
|
413
|
+
void _; // range may be stale if DOM changed while popup was open
|
|
414
414
|
}
|
|
415
415
|
};
|
|
416
416
|
|
|
@@ -541,11 +541,14 @@ export class Toolbar {
|
|
|
541
541
|
|
|
542
542
|
items.forEach((item) => {
|
|
543
543
|
const value = (typeof item === 'object') ? item.value : item;
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
544
|
+
let label;
|
|
545
|
+
if (typeof item !== 'object') {
|
|
546
|
+
label = item;
|
|
547
|
+
} else if (def.name === 'paragraphStyle') {
|
|
548
|
+
label = this.context.locale.toolbar.paragraphItems?.[item.value] || item.label;
|
|
549
|
+
} else {
|
|
550
|
+
label = item.label;
|
|
551
|
+
}
|
|
549
552
|
const isHeader = (typeof item === 'object') && !!item.disabled;
|
|
550
553
|
const attrs = { value };
|
|
551
554
|
if (isHeader) attrs.disabled = '';
|
|
@@ -563,7 +566,7 @@ export class Toolbar {
|
|
|
563
566
|
let _savedRange = null;
|
|
564
567
|
const dMousedown = on(select, 'mousedown', () => {
|
|
565
568
|
const sel = globalThis.getSelection();
|
|
566
|
-
_savedRange =
|
|
569
|
+
_savedRange = sel?.rangeCount ? sel.getRangeAt(0).cloneRange() : null;
|
|
567
570
|
});
|
|
568
571
|
|
|
569
572
|
const disposer = on(select, 'change', (e) => {
|
|
@@ -576,7 +579,7 @@ export class Toolbar {
|
|
|
576
579
|
try {
|
|
577
580
|
const sel = globalThis.getSelection();
|
|
578
581
|
if (sel) { sel.removeAllRanges(); sel.addRange(_savedRange); }
|
|
579
|
-
} catch (_) { /* range may be stale if DOM changed */ }
|
|
582
|
+
} catch (_) { void _; /* range may be stale if DOM changed */ }
|
|
580
583
|
}
|
|
581
584
|
def.action(this.context, value);
|
|
582
585
|
this.context.invoke('editor.afterCommand');
|
|
@@ -617,15 +620,13 @@ export class Toolbar {
|
|
|
617
620
|
} else {
|
|
618
621
|
btn.textContent = btnDef.icon || btnDef.name;
|
|
619
622
|
}
|
|
620
|
-
} else {
|
|
623
|
+
} else if (_SVG_MAP.has(btnDef.icon)) {
|
|
621
624
|
// FontAwesome absent: use SVG fallback when available
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
btn.textContent = btnDef.icon || btnDef.name;
|
|
628
|
-
}
|
|
625
|
+
btn.innerHTML = _SVG_MAP.get(btnDef.icon);
|
|
626
|
+
} else if (_SVG_MAP.has(btnDef.name)) {
|
|
627
|
+
btn.innerHTML = _SVG_MAP.get(btnDef.name);
|
|
628
|
+
} else {
|
|
629
|
+
btn.textContent = btnDef.icon || btnDef.name;
|
|
629
630
|
}
|
|
630
631
|
|
|
631
632
|
const disposer = on(btn, 'click', (event) => {
|
|
@@ -702,13 +703,13 @@ export class Toolbar {
|
|
|
702
703
|
// Fallback: when no selection/font set, use the configured default font
|
|
703
704
|
if (!raw) {
|
|
704
705
|
raw = this.options.defaultFontFamily
|
|
705
|
-
||
|
|
706
|
+
|| this.options.fontFamilies?.[0]
|
|
706
707
|
|| '';
|
|
707
708
|
}
|
|
708
709
|
// Try to match against available options (case-insensitive)
|
|
709
710
|
const sel = /** @type {HTMLSelectElement} */ (select);
|
|
710
711
|
const matched = Array.from(sel.options).find(
|
|
711
|
-
(opt) => opt.value
|
|
712
|
+
(opt) => opt.value?.toLowerCase() === raw.toLowerCase()
|
|
712
713
|
);
|
|
713
714
|
sel.value = matched ? matched.value : '';
|
|
714
715
|
});
|
|
@@ -68,7 +68,10 @@ export class VideoDialog extends BaseDialog {
|
|
|
68
68
|
// Live URL hint
|
|
69
69
|
const d0 = on(urlInput, 'input', () => {
|
|
70
70
|
const info = this._parseVideoUrl(urlInput.value.trim());
|
|
71
|
-
|
|
71
|
+
let hint = '';
|
|
72
|
+
if (info) { hint = this.context.locale.videoDialog.detected(info.type); }
|
|
73
|
+
else if (urlInput.value) { hint = this.context.locale.videoDialog.unknownFormat; }
|
|
74
|
+
hintEl.textContent = hint;
|
|
72
75
|
});
|
|
73
76
|
const d4 = on(urlInput, 'keydown', (e) => { if (/** @type {KeyboardEvent} */ (e).key === 'Enter') { e.preventDefault(); this._onInsert(); } });
|
|
74
77
|
this._disposers.push(d0, d4);
|
|
@@ -167,7 +170,7 @@ export class VideoDialog extends BaseDialog {
|
|
|
167
170
|
);
|
|
168
171
|
}
|
|
169
172
|
|
|
170
|
-
if (info
|
|
173
|
+
if (info?.type === 'Direct video') {
|
|
171
174
|
const src = info.embedUrl.replaceAll('"', '%22');
|
|
172
175
|
return (
|
|
173
176
|
`<div class="an-video-wrapper" style="position:relative;display:block;width:${width}px;max-width:100%">` +
|
package/src/js/renderer.js
CHANGED
|
@@ -36,7 +36,7 @@ export function renderLayout(targetEl, options) {
|
|
|
36
36
|
// Restore auto-saved content when available; fall back to element content
|
|
37
37
|
let initialContent = '';
|
|
38
38
|
if (options.autoSave && options.autoSaveKey) {
|
|
39
|
-
try { initialContent = localStorage.getItem(options.autoSaveKey) || ''; } catch (_) {}
|
|
39
|
+
try { initialContent = localStorage.getItem(options.autoSaveKey) || ''; } catch (_) { void _; }
|
|
40
40
|
}
|
|
41
41
|
if (!initialContent) {
|
|
42
42
|
initialContent = targetEl.tagName === 'TEXTAREA'
|
|
@@ -111,7 +111,7 @@ export function renderLayout(targetEl, options) {
|
|
|
111
111
|
|
|
112
112
|
// Hide the original element; keep it in DOM for form submission
|
|
113
113
|
targetEl.style.display = 'none';
|
|
114
|
-
targetEl.
|
|
114
|
+
targetEl.after(container);
|
|
115
115
|
|
|
116
116
|
return { container, editable };
|
|
117
117
|
}
|