autumnnote 1.6.6 → 1.7.0
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/README.md +11 -5
- package/dist/autumnnote.css +164 -0
- package/dist/autumnnote.es.js +426 -31
- package/dist/autumnnote.es.js.map +1 -1
- package/dist/autumnnote.umd.js +426 -31
- package/dist/autumnnote.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/js/Context.js +37 -1
- package/src/js/editing/History.js +10 -0
- package/src/js/i18n/de.js +7 -0
- package/src/js/i18n/en.js +14 -0
- package/src/js/i18n/es.js +7 -0
- package/src/js/i18n/fr.js +7 -0
- package/src/js/i18n/ja.js +7 -0
- package/src/js/i18n/ko.js +7 -0
- package/src/js/i18n/vi.js +7 -0
- package/src/js/i18n/zh.js +7 -0
- package/src/js/index.js +1 -1
- package/src/js/module/BaseDialog.js +7 -0
- package/src/js/module/CodeTooltip.js +23 -0
- package/src/js/module/Editor.js +8 -0
- package/src/js/module/FindReplace.js +31 -6
- package/src/js/module/Mention.js +6 -2
- package/src/js/module/Statusbar.js +3 -3
- package/src/js/module/TableTooltip.js +287 -2
- package/src/js/renderer.js +5 -2
- package/src/styles/autumnnote.scss +181 -0
- package/types/index.d.ts +18 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "autumnnote",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "WYSIWYG rich-text editor built with vanilla JavaScript — zero dependencies, no jQuery. Dark mode, @mention, markdown shortcuts, bubble toolbar. React and Vue 3 wrappers included.",
|
|
5
5
|
"main": "dist/autumnnote.umd.js",
|
|
6
6
|
"module": "dist/autumnnote.es.js",
|
package/src/js/Context.js
CHANGED
|
@@ -385,6 +385,22 @@ export class Context {
|
|
|
385
385
|
this.invoke('editor.clearHistory');
|
|
386
386
|
}
|
|
387
387
|
|
|
388
|
+
/**
|
|
389
|
+
* Returns the number of available undo steps.
|
|
390
|
+
* @returns {number}
|
|
391
|
+
*/
|
|
392
|
+
getUndoCount() {
|
|
393
|
+
return this.invoke('editor.getUndoCount') ?? 0;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Returns the number of available redo steps.
|
|
398
|
+
* @returns {number}
|
|
399
|
+
*/
|
|
400
|
+
getRedoCount() {
|
|
401
|
+
return this.invoke('editor.getRedoCount') ?? 0;
|
|
402
|
+
}
|
|
403
|
+
|
|
388
404
|
/**
|
|
389
405
|
* Returns true when the editor has no meaningful content.
|
|
390
406
|
* @returns {boolean}
|
|
@@ -513,6 +529,22 @@ export class Context {
|
|
|
513
529
|
});
|
|
514
530
|
}
|
|
515
531
|
|
|
532
|
+
/**
|
|
533
|
+
* Returns an array of heading objects representing the table of contents.
|
|
534
|
+
* Each entry has: level (1-6), text (heading text), element (DOM element).
|
|
535
|
+
* @returns {{ level: number, text: string, element: HTMLElement }[]}
|
|
536
|
+
*/
|
|
537
|
+
getTableOfContents() {
|
|
538
|
+
const headings = Array.from(
|
|
539
|
+
this.layoutInfo.editable.querySelectorAll('h1,h2,h3,h4,h5,h6')
|
|
540
|
+
);
|
|
541
|
+
return headings.map((el) => ({
|
|
542
|
+
level: parseInt(el.tagName[1], 10),
|
|
543
|
+
text: el.textContent?.trim() ?? '',
|
|
544
|
+
element: /** @type {HTMLElement} */ (el),
|
|
545
|
+
}));
|
|
546
|
+
}
|
|
547
|
+
|
|
516
548
|
/**
|
|
517
549
|
* Sets whether the editor is disabled (readonly).
|
|
518
550
|
* @param {boolean} disabled
|
|
@@ -561,15 +593,19 @@ export class Context {
|
|
|
561
593
|
|
|
562
594
|
const container = this.layoutInfo.container;
|
|
563
595
|
const wasDark = container?.classList.contains('an-theme-dark');
|
|
596
|
+
const wasAuto = container?.classList.contains('an-theme-auto');
|
|
564
597
|
if (container?.parentNode) {
|
|
565
598
|
// Restore original element
|
|
566
599
|
this.targetEl.style.display = '';
|
|
567
600
|
container.remove();
|
|
568
601
|
}
|
|
569
|
-
//
|
|
602
|
+
// Clean up body theme classes if no other editors of that type remain
|
|
570
603
|
if (wasDark && !document.querySelector('.an-container.an-theme-dark')) {
|
|
571
604
|
document.body.classList.remove('an-theme-dark');
|
|
572
605
|
}
|
|
606
|
+
if (wasAuto && !document.querySelector('.an-container.an-theme-auto')) {
|
|
607
|
+
document.body.classList.remove('an-theme-auto');
|
|
608
|
+
}
|
|
573
609
|
|
|
574
610
|
if (typeof this.options.onDestroy === 'function') {
|
|
575
611
|
this.options.onDestroy(this);
|
|
@@ -221,4 +221,14 @@ export class History {
|
|
|
221
221
|
canRedo() {
|
|
222
222
|
return this.stackOffset < this.stack.length - 1;
|
|
223
223
|
}
|
|
224
|
+
|
|
225
|
+
/** @returns {number} */
|
|
226
|
+
getUndoCount() {
|
|
227
|
+
return Math.max(0, this.stackOffset);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** @returns {number} */
|
|
231
|
+
getRedoCount() {
|
|
232
|
+
return Math.max(0, this.stack.length - 1 - this.stackOffset);
|
|
233
|
+
}
|
|
224
234
|
}
|
package/src/js/i18n/de.js
CHANGED
|
@@ -157,6 +157,7 @@ export const de = {
|
|
|
157
157
|
replaceAriaLabel: 'Ersetzen durch',
|
|
158
158
|
replaceBtn: 'Ersetzen',
|
|
159
159
|
noResults: 'Keine Ergebnisse',
|
|
160
|
+
useRegex: 'Regulären Ausdruck verwenden',
|
|
160
161
|
replaceAllBtn: 'Alle ersetzen',
|
|
161
162
|
close: '×',
|
|
162
163
|
},
|
|
@@ -275,6 +276,7 @@ export const de = {
|
|
|
275
276
|
disableWordWrap: 'Zeilenumbruch deaktivieren',
|
|
276
277
|
convertToParagraph: 'In Absatz umwandeln',
|
|
277
278
|
deleteCodeBlock: 'Codeblock löschen',
|
|
279
|
+
lineNumbers: 'Zeilennummern umschalten',
|
|
278
280
|
},
|
|
279
281
|
table: {
|
|
280
282
|
ariaLabel: 'Tabellen-Aktionen',
|
|
@@ -297,6 +299,11 @@ export const de = {
|
|
|
297
299
|
tableBorderWidthPx: 'Tabellenrahmenbreite (px)',
|
|
298
300
|
cancelBtn: 'Abbrechen',
|
|
299
301
|
applyBtn: 'Anwenden',
|
|
302
|
+
sortAsc: 'Aufsteigend sortieren',
|
|
303
|
+
sortDesc: 'Absteigend sortieren',
|
|
304
|
+
exportCSV: 'Als CSV exportieren',
|
|
305
|
+
cellPadding: 'Zellauffüllung',
|
|
306
|
+
cellPaddingPx: 'Zellauffüllung (px)',
|
|
300
307
|
},
|
|
301
308
|
video: {
|
|
302
309
|
ariaLabel: 'Video-Aktionen',
|
package/src/js/i18n/en.js
CHANGED
|
@@ -160,6 +160,7 @@ export const en = {
|
|
|
160
160
|
replaceBtn: 'Replace',
|
|
161
161
|
replaceAllBtn: 'Replace All',
|
|
162
162
|
noResults: 'No results',
|
|
163
|
+
useRegex: 'Use Regular Expression',
|
|
163
164
|
close: '\u00d7',
|
|
164
165
|
},
|
|
165
166
|
|
|
@@ -281,6 +282,7 @@ export const en = {
|
|
|
281
282
|
disableWordWrap: 'Disable Word Wrap',
|
|
282
283
|
convertToParagraph: 'Convert to Paragraph',
|
|
283
284
|
deleteCodeBlock: 'Delete Code Block',
|
|
285
|
+
lineNumbers: 'Toggle Line Numbers',
|
|
284
286
|
},
|
|
285
287
|
table: {
|
|
286
288
|
ariaLabel: 'Table actions',
|
|
@@ -297,14 +299,26 @@ export const en = {
|
|
|
297
299
|
columnWidth: 'Column Width',
|
|
298
300
|
rowHeight: 'Row Height',
|
|
299
301
|
tableBorderWidth: 'Table Border Width',
|
|
302
|
+
tableBorderColor: 'Table Border Color',
|
|
300
303
|
deleteTable: 'Delete Table',
|
|
304
|
+
cellAlignLeft: 'Align Left',
|
|
305
|
+
cellAlignCenter: 'Align Center',
|
|
306
|
+
cellAlignRight: 'Align Right',
|
|
307
|
+
cellAlignJustify: 'Align Justify',
|
|
308
|
+
toggleHeaderRow: 'Toggle Header Row',
|
|
301
309
|
cellBackground: 'Cell Background',
|
|
302
310
|
noShading: 'No Shading',
|
|
311
|
+
noBorderColor: 'No Border Color',
|
|
303
312
|
columnWidthPx: 'Column Width (px)',
|
|
304
313
|
rowHeightPx: 'Row Height (px)',
|
|
305
314
|
tableBorderWidthPx: 'Table Border Width (px)',
|
|
306
315
|
cancelBtn: 'Cancel',
|
|
307
316
|
applyBtn: 'Apply',
|
|
317
|
+
sortAsc: 'Sort Ascending',
|
|
318
|
+
sortDesc: 'Sort Descending',
|
|
319
|
+
exportCSV: 'Export as CSV',
|
|
320
|
+
cellPadding: 'Cell Padding',
|
|
321
|
+
cellPaddingPx: 'Cell Padding (px)',
|
|
308
322
|
},
|
|
309
323
|
video: {
|
|
310
324
|
ariaLabel: 'Video actions',
|
package/src/js/i18n/es.js
CHANGED
|
@@ -157,6 +157,7 @@ export const es = {
|
|
|
157
157
|
replaceAriaLabel: 'Reemplazar con',
|
|
158
158
|
replaceBtn: 'Reemplazar',
|
|
159
159
|
noResults: 'Sin resultados',
|
|
160
|
+
useRegex: 'Usar expresión regular',
|
|
160
161
|
replaceAllBtn: 'Reemplazar todo',
|
|
161
162
|
close: '×',
|
|
162
163
|
},
|
|
@@ -275,6 +276,7 @@ export const es = {
|
|
|
275
276
|
disableWordWrap: 'Desactivar ajuste de línea',
|
|
276
277
|
convertToParagraph: 'Convertir en párrafo',
|
|
277
278
|
deleteCodeBlock: 'Eliminar bloque de código',
|
|
279
|
+
lineNumbers: 'Alternar números de línea',
|
|
278
280
|
},
|
|
279
281
|
table: {
|
|
280
282
|
ariaLabel: 'Acciones de tabla',
|
|
@@ -297,6 +299,11 @@ export const es = {
|
|
|
297
299
|
tableBorderWidthPx: 'Grosor del borde (px)',
|
|
298
300
|
cancelBtn: 'Cancelar',
|
|
299
301
|
applyBtn: 'Aplicar',
|
|
302
|
+
sortAsc: 'Ordenar ascendente',
|
|
303
|
+
sortDesc: 'Ordenar descendente',
|
|
304
|
+
exportCSV: 'Exportar como CSV',
|
|
305
|
+
cellPadding: 'Relleno de celda',
|
|
306
|
+
cellPaddingPx: 'Relleno de celda (px)',
|
|
300
307
|
},
|
|
301
308
|
video: {
|
|
302
309
|
ariaLabel: 'Acciones de vídeo',
|
package/src/js/i18n/fr.js
CHANGED
|
@@ -158,6 +158,7 @@ export const fr = {
|
|
|
158
158
|
replaceAriaLabel: 'Remplacer par',
|
|
159
159
|
replaceBtn: 'Remplacer',
|
|
160
160
|
noResults: 'Aucun résultat',
|
|
161
|
+
useRegex: 'Utiliser une expression régulière',
|
|
161
162
|
replaceAllBtn: 'Tout remplacer',
|
|
162
163
|
close: '\u00d7',
|
|
163
164
|
},
|
|
@@ -276,6 +277,7 @@ export const fr = {
|
|
|
276
277
|
disableWordWrap: 'Désactiver le retour à la ligne',
|
|
277
278
|
convertToParagraph: 'Convertir en paragraphe',
|
|
278
279
|
deleteCodeBlock: 'Supprimer le bloc de code',
|
|
280
|
+
lineNumbers: 'Activer/désactiver les numéros de ligne',
|
|
279
281
|
},
|
|
280
282
|
table: {
|
|
281
283
|
ariaLabel: 'Actions du tableau',
|
|
@@ -298,6 +300,11 @@ export const fr = {
|
|
|
298
300
|
tableBorderWidthPx: 'Épaisseur des bordures (px)',
|
|
299
301
|
cancelBtn: 'Annuler',
|
|
300
302
|
applyBtn: 'Appliquer',
|
|
303
|
+
sortAsc: 'Trier par ordre croissant',
|
|
304
|
+
sortDesc: 'Trier par ordre décroissant',
|
|
305
|
+
exportCSV: 'Exporter en CSV',
|
|
306
|
+
cellPadding: 'Rembourrage des cellules',
|
|
307
|
+
cellPaddingPx: 'Rembourrage des cellules (px)',
|
|
301
308
|
},
|
|
302
309
|
video: {
|
|
303
310
|
ariaLabel: 'Actions de la vidéo',
|
package/src/js/i18n/ja.js
CHANGED
|
@@ -158,6 +158,7 @@ export const ja = {
|
|
|
158
158
|
replaceAriaLabel: '置換後のテキスト',
|
|
159
159
|
replaceBtn: '置換',
|
|
160
160
|
noResults: '結果なし',
|
|
161
|
+
useRegex: '正規表現を使用',
|
|
161
162
|
replaceAllBtn: 'すべて置換',
|
|
162
163
|
close: '\u00d7',
|
|
163
164
|
},
|
|
@@ -276,6 +277,7 @@ export const ja = {
|
|
|
276
277
|
disableWordWrap: '折り返しを無効にする',
|
|
277
278
|
convertToParagraph: '段落に変換',
|
|
278
279
|
deleteCodeBlock: 'コードブロックを削除',
|
|
280
|
+
lineNumbers: '行番号を切り替え',
|
|
279
281
|
},
|
|
280
282
|
table: {
|
|
281
283
|
ariaLabel: 'テーブル操作',
|
|
@@ -298,6 +300,11 @@ export const ja = {
|
|
|
298
300
|
tableBorderWidthPx: 'テーブルの枠幅 (px)',
|
|
299
301
|
cancelBtn: 'キャンセル',
|
|
300
302
|
applyBtn: '適用',
|
|
303
|
+
sortAsc: '昇順で並べ替え',
|
|
304
|
+
sortDesc: '降順で並べ替え',
|
|
305
|
+
exportCSV: 'CSVとしてエクスポート',
|
|
306
|
+
cellPadding: 'セルの余白',
|
|
307
|
+
cellPaddingPx: 'セルの余白 (px)',
|
|
301
308
|
},
|
|
302
309
|
video: {
|
|
303
310
|
ariaLabel: '動画操作',
|
package/src/js/i18n/ko.js
CHANGED
|
@@ -157,6 +157,7 @@ export const ko = {
|
|
|
157
157
|
replaceAriaLabel: '바꿀 내용',
|
|
158
158
|
replaceBtn: '바꾸기',
|
|
159
159
|
noResults: '결과 없음',
|
|
160
|
+
useRegex: '정규식 사용',
|
|
160
161
|
replaceAllBtn: '모두 바꾸기',
|
|
161
162
|
close: '×',
|
|
162
163
|
},
|
|
@@ -275,6 +276,7 @@ export const ko = {
|
|
|
275
276
|
disableWordWrap: '줄 바꿈 해제',
|
|
276
277
|
convertToParagraph: '단락으로 변환',
|
|
277
278
|
deleteCodeBlock: '코드 블록 삭제',
|
|
279
|
+
lineNumbers: '줄 번호 전환',
|
|
278
280
|
},
|
|
279
281
|
table: {
|
|
280
282
|
ariaLabel: '표 작업',
|
|
@@ -297,6 +299,11 @@ export const ko = {
|
|
|
297
299
|
tableBorderWidthPx: '표 테두리 너비 (px)',
|
|
298
300
|
cancelBtn: '취소',
|
|
299
301
|
applyBtn: '적용',
|
|
302
|
+
sortAsc: '오름차순 정렬',
|
|
303
|
+
sortDesc: '내림차순 정렬',
|
|
304
|
+
exportCSV: 'CSV로 내보내기',
|
|
305
|
+
cellPadding: '셀 패딩',
|
|
306
|
+
cellPaddingPx: '셀 패딩 (px)',
|
|
300
307
|
},
|
|
301
308
|
video: {
|
|
302
309
|
ariaLabel: '동영상 작업',
|
package/src/js/i18n/vi.js
CHANGED
|
@@ -159,6 +159,7 @@ export const vi = {
|
|
|
159
159
|
replaceBtn: 'Thay thế',
|
|
160
160
|
replaceAllBtn: 'Thay thế tất cả',
|
|
161
161
|
noResults: 'Không có kết quả',
|
|
162
|
+
useRegex: 'Dùng biểu thức chính quy',
|
|
162
163
|
close: '\u00d7',
|
|
163
164
|
},
|
|
164
165
|
|
|
@@ -276,6 +277,7 @@ export const vi = {
|
|
|
276
277
|
disableWordWrap: 'Tắt xuống dòng',
|
|
277
278
|
convertToParagraph: 'Chuyển thành đoạn văn',
|
|
278
279
|
deleteCodeBlock: 'Xóa khối mã',
|
|
280
|
+
lineNumbers: 'Bật/Tắt số dòng',
|
|
279
281
|
},
|
|
280
282
|
table: {
|
|
281
283
|
ariaLabel: 'Hành động bảng',
|
|
@@ -300,6 +302,11 @@ export const vi = {
|
|
|
300
302
|
tableBorderWidthPx: 'Độ rộng viền bảng (px)',
|
|
301
303
|
cancelBtn: 'Hủy',
|
|
302
304
|
applyBtn: 'Áp dụng',
|
|
305
|
+
sortAsc: 'Sắp xếp tăng dần',
|
|
306
|
+
sortDesc: 'Sắp xếp giảm dần',
|
|
307
|
+
exportCSV: 'Xuất CSV',
|
|
308
|
+
cellPadding: 'Khoảng đệm ô',
|
|
309
|
+
cellPaddingPx: 'Khoảng đệm ô (px)',
|
|
303
310
|
},
|
|
304
311
|
video: {
|
|
305
312
|
ariaLabel: 'Hành động video',
|
package/src/js/i18n/zh.js
CHANGED
|
@@ -158,6 +158,7 @@ export const zh = {
|
|
|
158
158
|
replaceAriaLabel: '替换为',
|
|
159
159
|
replaceBtn: '替换',
|
|
160
160
|
noResults: '没有结果',
|
|
161
|
+
useRegex: '使用正则表达式',
|
|
161
162
|
replaceAllBtn: '全部替换',
|
|
162
163
|
close: '\u00d7',
|
|
163
164
|
},
|
|
@@ -276,6 +277,7 @@ export const zh = {
|
|
|
276
277
|
disableWordWrap: '禁用自动换行',
|
|
277
278
|
convertToParagraph: '转换为段落',
|
|
278
279
|
deleteCodeBlock: '删除代码块',
|
|
280
|
+
lineNumbers: '切换行号',
|
|
279
281
|
},
|
|
280
282
|
table: {
|
|
281
283
|
ariaLabel: '表格操作',
|
|
@@ -298,6 +300,11 @@ export const zh = {
|
|
|
298
300
|
tableBorderWidthPx: '表格边框宽度 (px)',
|
|
299
301
|
cancelBtn: '取消',
|
|
300
302
|
applyBtn: '应用',
|
|
303
|
+
sortAsc: '升序排列',
|
|
304
|
+
sortDesc: '降序排列',
|
|
305
|
+
exportCSV: '导出为 CSV',
|
|
306
|
+
cellPadding: '单元格内边距',
|
|
307
|
+
cellPaddingPx: '单元格内边距 (px)',
|
|
301
308
|
},
|
|
302
309
|
video: {
|
|
303
310
|
ariaLabel: '视频操作',
|
package/src/js/index.js
CHANGED
|
@@ -141,7 +141,7 @@ const AutumnNote = {
|
|
|
141
141
|
registerButton(btnDef) { registerButton(btnDef); return this; },
|
|
142
142
|
|
|
143
143
|
/** Library version */
|
|
144
|
-
version: '1.
|
|
144
|
+
version: '1.7.0',
|
|
145
145
|
};
|
|
146
146
|
|
|
147
147
|
// ---------------------------------------------------------------------------
|
|
@@ -19,6 +19,13 @@ export class BaseDialog {
|
|
|
19
19
|
this._removeTrap = null;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// Abstract — subclasses must override
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
|
|
26
|
+
/** @returns {HTMLElement} */
|
|
27
|
+
_buildDialog() { throw new Error('_buildDialog() must be implemented by subclass'); }
|
|
28
|
+
|
|
22
29
|
// ---------------------------------------------------------------------------
|
|
23
30
|
// Lifecycle (shared)
|
|
24
31
|
// ---------------------------------------------------------------------------
|
|
@@ -14,6 +14,7 @@ const ICONS = {
|
|
|
14
14
|
wrapOn: `<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="3" y1="6" x2="21" y2="6"/><path d="M3 12h15a3 3 0 0 1 0 6H3"/><polyline points="6 15 3 18 6 21"/></svg>`,
|
|
15
15
|
toParagraph:`<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M13 4v16"/><path d="M17 4v16"/><path d="M9 4h8a4 4 0 0 1 0 8H9V4z"/></svg>`,
|
|
16
16
|
deleteCode: `<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"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/></svg>`,
|
|
17
|
+
lineNumbers: `<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="10" y1="6" x2="21" y2="6"/><line x1="10" y1="12" x2="21" y2="12"/><line x1="10" y1="18" x2="21" y2="18"/><path d="M4 6h1v4"/><path d="M4 10h2"/><path d="M6 18H4c0-1 2-2 2-3s-1-2-2-2"/></svg>`,
|
|
17
18
|
};
|
|
18
19
|
|
|
19
20
|
export class CodeTooltip {
|
|
@@ -26,6 +27,8 @@ export class CodeTooltip {
|
|
|
26
27
|
this._hideTimer = null;
|
|
27
28
|
this._disposers = [];
|
|
28
29
|
this._copyBtn = null;
|
|
30
|
+
this._wrapBtn = null;
|
|
31
|
+
this._lineNumbersBtn = null;
|
|
29
32
|
this._langSelect = null;
|
|
30
33
|
this._prismScript = null; // script element while Prism is loading
|
|
31
34
|
}
|
|
@@ -127,6 +130,10 @@ export class CodeTooltip {
|
|
|
127
130
|
this._wrapBtn = this._makeBtn(ICONS.wrapOn, L.toggleWordWrap, () => this._toggleWrap());
|
|
128
131
|
el.appendChild(this._wrapBtn);
|
|
129
132
|
|
|
133
|
+
// Toggle line numbers
|
|
134
|
+
this._lineNumbersBtn = this._makeBtn(ICONS.lineNumbers, L.lineNumbers, () => this._toggleLineNumbers());
|
|
135
|
+
el.appendChild(this._lineNumbersBtn);
|
|
136
|
+
|
|
130
137
|
el.appendChild(this._sep());
|
|
131
138
|
|
|
132
139
|
// Convert to normal paragraph
|
|
@@ -183,6 +190,7 @@ export class CodeTooltip {
|
|
|
183
190
|
this._showTimer = setTimeout(() => {
|
|
184
191
|
this._activePre = pre;
|
|
185
192
|
this._syncWrapBtn();
|
|
193
|
+
this._syncLineNumbersBtn();
|
|
186
194
|
this._syncLangSelect();
|
|
187
195
|
this._show(pre);
|
|
188
196
|
}, SHOW_DELAY);
|
|
@@ -248,6 +256,13 @@ export class CodeTooltip {
|
|
|
248
256
|
: this.context.locale.tooltips.code.enableWordWrap;
|
|
249
257
|
}
|
|
250
258
|
|
|
259
|
+
_syncLineNumbersBtn() {
|
|
260
|
+
if (!this._activePre || !this._lineNumbersBtn) return;
|
|
261
|
+
const on = this._activePre.classList.contains('an-code-line-numbers');
|
|
262
|
+
this._lineNumbersBtn.classList.toggle('active', on);
|
|
263
|
+
this._lineNumbersBtn.title = this.context.locale.tooltips.code.lineNumbers;
|
|
264
|
+
}
|
|
265
|
+
|
|
251
266
|
_syncLangSelect() {
|
|
252
267
|
if (!this._activePre || !this._langSelect) return;
|
|
253
268
|
const codeEl = this._activePre.querySelector('code');
|
|
@@ -301,6 +316,14 @@ export class CodeTooltip {
|
|
|
301
316
|
this._positionNear(pre);
|
|
302
317
|
}
|
|
303
318
|
|
|
319
|
+
_toggleLineNumbers() {
|
|
320
|
+
const pre = this._activePre;
|
|
321
|
+
if (!pre) return;
|
|
322
|
+
pre.classList.toggle('an-code-line-numbers');
|
|
323
|
+
this._syncLineNumbersBtn();
|
|
324
|
+
this.context.invoke('editor.afterCommand');
|
|
325
|
+
}
|
|
326
|
+
|
|
304
327
|
/**
|
|
305
328
|
* Applies a language to a given <pre> element: sets classes, data-language,
|
|
306
329
|
* and triggers Prism highlighting. Called by the auto-detect flow.
|
package/src/js/module/Editor.js
CHANGED
|
@@ -525,6 +525,14 @@ export class Editor {
|
|
|
525
525
|
return this._history ? this._history.canRedo() : false;
|
|
526
526
|
}
|
|
527
527
|
|
|
528
|
+
getUndoCount() {
|
|
529
|
+
return this._history ? this._history.getUndoCount() : 0;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
getRedoCount() {
|
|
533
|
+
return this._history ? this._history.getRedoCount() : 0;
|
|
534
|
+
}
|
|
535
|
+
|
|
528
536
|
// ---------------------------------------------------------------------------
|
|
529
537
|
// Style commands (delegated to Style module)
|
|
530
538
|
// ---------------------------------------------------------------------------
|
|
@@ -30,6 +30,7 @@ export class FindReplace extends BaseDialog {
|
|
|
30
30
|
this._matches = [];
|
|
31
31
|
this._currentIndex = -1;
|
|
32
32
|
this._caseSensitive = false;
|
|
33
|
+
this._useRegex = false;
|
|
33
34
|
/** @type {'find'|'replace'} */
|
|
34
35
|
this._mode = 'find';
|
|
35
36
|
|
|
@@ -37,6 +38,7 @@ export class FindReplace extends BaseDialog {
|
|
|
37
38
|
this._queryRegex = null;
|
|
38
39
|
this._lastQuery = null;
|
|
39
40
|
this._lastCaseSensitive = null;
|
|
41
|
+
this._lastUseRegex = null;
|
|
40
42
|
|
|
41
43
|
this._focusTimer = null;
|
|
42
44
|
}
|
|
@@ -169,6 +171,14 @@ export class FindReplace extends BaseDialog {
|
|
|
169
171
|
});
|
|
170
172
|
caseBtn.textContent = 'Aa';
|
|
171
173
|
|
|
174
|
+
const regexBtn = createElement('button', {
|
|
175
|
+
type: 'button',
|
|
176
|
+
class: 'an-fr-icon-btn',
|
|
177
|
+
title: L.useRegex,
|
|
178
|
+
'aria-label': L.useRegex,
|
|
179
|
+
});
|
|
180
|
+
regexBtn.textContent = '.*';
|
|
181
|
+
|
|
172
182
|
const prevBtn = createElement('button', {
|
|
173
183
|
type: 'button',
|
|
174
184
|
class: 'an-fr-icon-btn',
|
|
@@ -188,7 +198,7 @@ export class FindReplace extends BaseDialog {
|
|
|
188
198
|
const counter = createElement('span', { class: 'an-fr-counter' });
|
|
189
199
|
this._counterEl = counter;
|
|
190
200
|
|
|
191
|
-
searchBar.append(findInput, caseCheckbox, caseBtn, prevBtn, nextBtn, counter);
|
|
201
|
+
searchBar.append(findInput, caseCheckbox, caseBtn, regexBtn, prevBtn, nextBtn, counter);
|
|
192
202
|
box.appendChild(searchBar);
|
|
193
203
|
|
|
194
204
|
// ---- Replace row: [input] [Replace] [All] (hidden by default) ----
|
|
@@ -249,7 +259,14 @@ export class FindReplace extends BaseDialog {
|
|
|
249
259
|
this._replace();
|
|
250
260
|
}
|
|
251
261
|
});
|
|
252
|
-
|
|
262
|
+
const dRegex = on(regexBtn, 'click', () => {
|
|
263
|
+
this._useRegex = !this._useRegex;
|
|
264
|
+
regexBtn.classList.toggle('an-fr-icon-btn--active', this._useRegex);
|
|
265
|
+
this._queryRegex = null; // force recompile
|
|
266
|
+
this._lastQuery = null;
|
|
267
|
+
this._onSearch();
|
|
268
|
+
});
|
|
269
|
+
this._disposers.push(d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, dRegex);
|
|
253
270
|
|
|
254
271
|
return overlay;
|
|
255
272
|
}
|
|
@@ -324,14 +341,22 @@ export class FindReplace extends BaseDialog {
|
|
|
324
341
|
*/
|
|
325
342
|
_findRawMatches(query, root) {
|
|
326
343
|
const results = [];
|
|
327
|
-
// Reuse compiled regex when query
|
|
328
|
-
if (this._lastQuery !== query || this._lastCaseSensitive !== this._caseSensitive) {
|
|
344
|
+
// Reuse compiled regex when query, case-sensitivity, and regex mode haven't changed
|
|
345
|
+
if (this._lastQuery !== query || this._lastCaseSensitive !== this._caseSensitive || this._lastUseRegex !== this._useRegex) {
|
|
329
346
|
const flags = this._caseSensitive ? 'g' : 'gi';
|
|
330
|
-
|
|
331
|
-
|
|
347
|
+
try {
|
|
348
|
+
const pattern = this._useRegex
|
|
349
|
+
? query
|
|
350
|
+
: query.replace(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
|
|
351
|
+
this._queryRegex = new RegExp(pattern, flags);
|
|
352
|
+
} catch (_) {
|
|
353
|
+
this._queryRegex = null;
|
|
354
|
+
}
|
|
332
355
|
this._lastQuery = query;
|
|
333
356
|
this._lastCaseSensitive = this._caseSensitive;
|
|
357
|
+
this._lastUseRegex = this._useRegex;
|
|
334
358
|
}
|
|
359
|
+
if (!this._queryRegex) return results;
|
|
335
360
|
const re = this._queryRegex;
|
|
336
361
|
|
|
337
362
|
// Cap results to prevent blocking the main thread on very large documents
|
package/src/js/module/Mention.js
CHANGED
|
@@ -269,14 +269,18 @@ export class Mention {
|
|
|
269
269
|
this._query = query;
|
|
270
270
|
clearTimeout(this._debounceTimer);
|
|
271
271
|
this._debounceTimer = setTimeout(() => {
|
|
272
|
-
|
|
272
|
+
const cb = (items) => {
|
|
273
273
|
if (!Array.isArray(items) || items.length === 0) {
|
|
274
274
|
this._hideDropdown();
|
|
275
275
|
return;
|
|
276
276
|
}
|
|
277
277
|
this._renderItems(items);
|
|
278
278
|
this._showDropdown();
|
|
279
|
-
}
|
|
279
|
+
};
|
|
280
|
+
const result = this._cfg.onSearch(this._query, cb);
|
|
281
|
+
if (result && typeof result.then === 'function') {
|
|
282
|
+
result.then(cb).catch(() => this._hideDropdown());
|
|
283
|
+
}
|
|
280
284
|
}, this._cfg.debounce);
|
|
281
285
|
}
|
|
282
286
|
|
|
@@ -90,9 +90,9 @@ export class Statusbar {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
// Counters
|
|
93
|
-
this._wordCountEl = createElement('span', { class: 'an-word-count' });
|
|
94
|
-
this._charCountEl = createElement('span', { class: 'an-char-count' });
|
|
95
|
-
const info = createElement('div', { class: 'an-status-info' });
|
|
93
|
+
this._wordCountEl = createElement('span', { class: 'an-word-count', role: 'status', 'aria-live': 'polite', 'aria-atomic': 'true' });
|
|
94
|
+
this._charCountEl = createElement('span', { class: 'an-char-count', 'aria-live': 'polite', 'aria-atomic': 'true' });
|
|
95
|
+
const info = createElement('div', { class: 'an-status-info', 'aria-label': 'Editor statistics' });
|
|
96
96
|
info.appendChild(this._wordCountEl);
|
|
97
97
|
info.appendChild(this._charCountEl);
|
|
98
98
|
this.el.appendChild(info);
|