autumnnote 1.0.8 → 1.1.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.
Files changed (41) hide show
  1. package/README.md +401 -628
  2. package/dist/autumnnote.css +169 -0
  3. package/dist/autumnnote.es.js +3669 -179
  4. package/dist/autumnnote.es.js.map +1 -1
  5. package/dist/autumnnote.umd.js +3668 -178
  6. package/dist/autumnnote.umd.js.map +1 -1
  7. package/package.json +3 -2
  8. package/src/js/Context.js +18 -2
  9. package/src/js/core/func.js +5 -5
  10. package/src/js/i18n/de.js +320 -0
  11. package/src/js/i18n/en.js +328 -0
  12. package/src/js/i18n/es.js +320 -0
  13. package/src/js/i18n/fr.js +321 -0
  14. package/src/js/i18n/index.js +59 -0
  15. package/src/js/i18n/ja.js +321 -0
  16. package/src/js/i18n/ko.js +320 -0
  17. package/src/js/i18n/vi.js +321 -0
  18. package/src/js/i18n/zh.js +321 -0
  19. package/src/js/index.js +2 -1
  20. package/src/js/module/AutoSaveRestore.js +126 -0
  21. package/src/js/module/BubbleToolbar.js +243 -0
  22. package/src/js/module/CodeTooltip.js +12 -9
  23. package/src/js/module/ContextMenu.js +41 -37
  24. package/src/js/module/EmojiDialog.js +8 -7
  25. package/src/js/module/FindReplace.js +14 -13
  26. package/src/js/module/IconDialog.js +14 -13
  27. package/src/js/module/ImageDialog.js +17 -16
  28. package/src/js/module/ImageTooltip.js +13 -12
  29. package/src/js/module/LinkDialog.js +10 -9
  30. package/src/js/module/LinkTooltip.js +6 -5
  31. package/src/js/module/MarkdownShortcuts.js +253 -0
  32. package/src/js/module/Mention.js +337 -0
  33. package/src/js/module/ShortcutsDialog.js +5 -4
  34. package/src/js/module/Statusbar.js +6 -5
  35. package/src/js/module/TableTooltip.js +22 -19
  36. package/src/js/module/Toolbar.js +21 -15
  37. package/src/js/module/VideoDialog.js +10 -9
  38. package/src/js/module/VideoTooltip.js +12 -11
  39. package/src/js/settings.js +23 -0
  40. package/src/styles/autumnnote.scss +191 -0
  41. package/types/index.d.ts +114 -1
@@ -96,8 +96,7 @@ function isFunction(val) {
96
96
  function mergeDeep(target, source) {
97
97
  const output = {};
98
98
  for (const key of Object.keys(target)) output[key] = Array.isArray(target[key]) ? [...target[key]] : target[key];
99
- if (isPlainObject(target) && isPlainObject(source)) for (const key of Object.keys(source)) if (isPlainObject(source[key])) if (!(key in target)) output[key] = mergeDeep({}, source[key]);
100
- else output[key] = mergeDeep(target[key], source[key]);
99
+ if (isPlainObject(target) && isPlainObject(source)) for (const key of Object.keys(source)) if (isPlainObject(source[key])) output[key] = mergeDeep(isPlainObject(target[key]) ? target[key] : {}, source[key]);
101
100
  else if (Array.isArray(source[key])) output[key] = [...source[key]];
102
101
  else output[key] = source[key];
103
102
  return output;
@@ -1443,9 +1442,2713 @@ var defaultOptions = {
1443
1442
  onDestroy: null,
1444
1443
  onCharLimitReached: null,
1445
1444
  onWordLimitReached: null,
1446
- focusColor: null
1445
+ focusColor: null,
1446
+ lang: "en",
1447
+ autoSaveRestore: false,
1448
+ autoSaveRestoreTimeout: 7,
1449
+ onAutoSaveRestore: null,
1450
+ markdownShortcuts: true,
1451
+ bubbleToolbar: false,
1452
+ bubbleToolbarItems: [
1453
+ "bold",
1454
+ "italic",
1455
+ "underline",
1456
+ "link",
1457
+ "foreColor",
1458
+ "removeFormat"
1459
+ ],
1460
+ mention: null
1447
1461
  };
1448
1462
  //#endregion
1463
+ //#region src/js/i18n/en.js
1464
+ /**
1465
+ * en.js - English locale (canonical reference)
1466
+ * All other locales deep-merge against this to fill missing keys.
1467
+ */
1468
+ /** @type {import('../../../types/index.js').AsnLocale} */
1469
+ var en = {
1470
+ toolbar: {
1471
+ bold: "Bold (Ctrl+B)",
1472
+ italic: "Italic (Ctrl+I)",
1473
+ underline: "Underline (Ctrl+U)",
1474
+ strikethrough: "Strikethrough",
1475
+ superscript: "Superscript",
1476
+ subscript: "Subscript",
1477
+ alignLeft: "Align Left",
1478
+ alignCenter: "Align Center",
1479
+ alignRight: "Align Right",
1480
+ alignJustify: "Justify",
1481
+ ul: "Unordered List",
1482
+ ol: "Ordered List",
1483
+ checklist: "Checklist",
1484
+ indent: "Indent",
1485
+ outdent: "Outdent",
1486
+ undo: "Undo (Ctrl+Z)",
1487
+ redo: "Redo (Ctrl+Y)",
1488
+ hr: "Horizontal Rule",
1489
+ link: "Insert Link",
1490
+ image: "Insert Image",
1491
+ video: "Insert Video",
1492
+ emoji: "Insert Emoji",
1493
+ icon: "Insert FA Icon",
1494
+ table: "Insert Table",
1495
+ fontSize: "Font Size",
1496
+ fontSizePlaceholder: "Size",
1497
+ removeFormat: "Remove Format",
1498
+ direction: "Toggle Text Direction (LTR / RTL)",
1499
+ fontFamily: "Font Family",
1500
+ paragraphStyle: "Paragraph Style",
1501
+ paragraphStylePlaceholder: "Style",
1502
+ lineHeight: "Line Height",
1503
+ lineHeightPlaceholder: "↕ Line",
1504
+ codeview: "HTML Code View",
1505
+ fullscreen: "Fullscreen",
1506
+ shortcuts: "Keyboard Shortcuts (Ctrl+Shift+/)",
1507
+ find: "Find (Ctrl+F)",
1508
+ findReplace: "Find & Replace (Ctrl+H)",
1509
+ inlineCode: "Inline Code (Ctrl+`)",
1510
+ print: "Print",
1511
+ foreColor: "Text Color",
1512
+ backColor: "Highlight Color",
1513
+ chooseTextColor: "Choose text color",
1514
+ chooseHighlightColor: "Choose highlight color",
1515
+ customColor: "Custom color",
1516
+ insertTableLabel: "Insert Table",
1517
+ paragraphItems: {
1518
+ p: "Normal",
1519
+ blockquote: "Quote",
1520
+ pre: "Code"
1521
+ }
1522
+ },
1523
+ linkDialog: {
1524
+ ariaLabel: "Insert link",
1525
+ title: "Insert Link",
1526
+ url: "URL",
1527
+ urlPlaceholder: "https://",
1528
+ displayText: "Display Text",
1529
+ textPlaceholder: "Link text",
1530
+ openInNewTab: "Open in new tab",
1531
+ insertBtn: "Insert",
1532
+ cancelBtn: "Cancel"
1533
+ },
1534
+ imageDialog: {
1535
+ ariaLabel: "Insert image",
1536
+ title: "Insert Image",
1537
+ imageUrl: "Image URL",
1538
+ urlPlaceholder: "https://example.com/image.png",
1539
+ altText: "Alt Text",
1540
+ altPlaceholder: "Describe the image",
1541
+ alignment: "Alignment",
1542
+ alignNone: "None",
1543
+ alignLeft: "Left",
1544
+ alignCenter: "Center",
1545
+ alignRight: "Right",
1546
+ uploadLabel: "Or upload a file",
1547
+ insertBtn: "Insert",
1548
+ cancelBtn: "Cancel"
1549
+ },
1550
+ videoDialog: {
1551
+ ariaLabel: "Insert video",
1552
+ title: "Insert Video",
1553
+ videoUrl: "Video URL",
1554
+ urlPlaceholder: "YouTube, Vimeo, or direct .mp4 URL",
1555
+ widthLabel: "Width (px)",
1556
+ widthPlaceholder: "560",
1557
+ insertBtn: "Insert",
1558
+ cancelBtn: "Cancel",
1559
+ detected: (type) => `Detected: ${type}`,
1560
+ unknownFormat: "Unknown format — will try direct video embed",
1561
+ invalidUrl: "Invalid URL — please enter a valid video link."
1562
+ },
1563
+ emojiDialog: {
1564
+ ariaLabel: "Insert emoji",
1565
+ title: "Insert Emoji",
1566
+ searchPlaceholder: "Search emojis…",
1567
+ all: "All",
1568
+ cancelBtn: "Cancel",
1569
+ close: "Close",
1570
+ categories: {
1571
+ smileys: "Smileys",
1572
+ people: "People",
1573
+ animals: "Animals",
1574
+ food: "Food",
1575
+ travel: "Travel",
1576
+ objects: "Objects",
1577
+ symbols: "Symbols"
1578
+ }
1579
+ },
1580
+ iconDialog: {
1581
+ ariaLabel: "Insert FA icon",
1582
+ title: "Insert FA Icon",
1583
+ searchPlaceholder: "Search icons…",
1584
+ all: "All",
1585
+ style: "Style",
1586
+ size: "Size",
1587
+ color: "Color",
1588
+ useColor: " Use color",
1589
+ selectHint: "Select an icon",
1590
+ insertBtn: "Insert FA Icon",
1591
+ cancelBtn: "Cancel",
1592
+ close: "Close",
1593
+ categories: {
1594
+ popular: "Popular",
1595
+ interface: "Interface",
1596
+ navigation: "Navigation",
1597
+ media: "Media",
1598
+ communication: "Communication",
1599
+ files: "Files",
1600
+ people: "People",
1601
+ objects: "Objects"
1602
+ }
1603
+ },
1604
+ findReplace: {
1605
+ findTitle: "Find",
1606
+ findReplaceTitle: "Find & Replace",
1607
+ findPlaceholder: "Find…",
1608
+ searchAriaLabel: "Search text",
1609
+ caseSensitive: "\xA0Case sensitive",
1610
+ prevBtn: "← Prev",
1611
+ nextBtn: "Next →",
1612
+ replacePlaceholder: "Replace with…",
1613
+ replaceAriaLabel: "Replace with",
1614
+ replaceBtn: "Replace",
1615
+ replaceAllBtn: "Replace All",
1616
+ close: "×"
1617
+ },
1618
+ shortcutsDialog: {
1619
+ title: "Keyboard Shortcuts",
1620
+ ariaLabel: "Keyboard Shortcuts",
1621
+ close: "Close",
1622
+ shortcuts: [
1623
+ {
1624
+ category: "Text Formatting",
1625
+ items: [
1626
+ {
1627
+ keys: "Ctrl + B",
1628
+ action: "Bold"
1629
+ },
1630
+ {
1631
+ keys: "Ctrl + I",
1632
+ action: "Italic"
1633
+ },
1634
+ {
1635
+ keys: "Ctrl + U",
1636
+ action: "Underline"
1637
+ },
1638
+ {
1639
+ keys: "Ctrl + K",
1640
+ action: "Insert / edit link"
1641
+ }
1642
+ ]
1643
+ },
1644
+ {
1645
+ category: "History",
1646
+ items: [{
1647
+ keys: "Ctrl + Z",
1648
+ action: "Undo"
1649
+ }, {
1650
+ keys: "Ctrl + Y / Ctrl + Shift + Z",
1651
+ action: "Redo"
1652
+ }]
1653
+ },
1654
+ {
1655
+ category: "Selection & Navigation",
1656
+ items: [
1657
+ {
1658
+ keys: "Ctrl + A",
1659
+ action: "Select all content"
1660
+ },
1661
+ {
1662
+ keys: "Tab",
1663
+ action: "Indent list item / insert spaces"
1664
+ },
1665
+ {
1666
+ keys: "Shift + Tab",
1667
+ action: "Outdent list item"
1668
+ }
1669
+ ]
1670
+ },
1671
+ {
1672
+ category: "Clipboard",
1673
+ items: [{
1674
+ keys: "Ctrl + Shift + V",
1675
+ action: "Paste as plain text"
1676
+ }]
1677
+ },
1678
+ {
1679
+ category: "Find & Replace",
1680
+ items: [{
1681
+ keys: "Ctrl + F",
1682
+ action: "Find in document"
1683
+ }, {
1684
+ keys: "Ctrl + H",
1685
+ action: "Find & Replace"
1686
+ }]
1687
+ },
1688
+ {
1689
+ category: "Editor",
1690
+ items: [{
1691
+ keys: "Ctrl + Shift + /",
1692
+ action: "Show this keyboard shortcuts dialog"
1693
+ }]
1694
+ }
1695
+ ]
1696
+ },
1697
+ contextMenu: {
1698
+ cut: "Cut",
1699
+ copy: "Copy",
1700
+ paste: "Paste",
1701
+ bold: "Bold",
1702
+ italic: "Italic",
1703
+ underline: "Underline",
1704
+ textColor: "Text Color",
1705
+ highlightColor: "Highlight Color",
1706
+ copyFormat: "Copy Format",
1707
+ pasteFormat: "Paste Format",
1708
+ removeFormat: "Remove Format",
1709
+ link: "Insert Link",
1710
+ image: "Insert Image",
1711
+ video: "Insert Video",
1712
+ table: "Insert Table",
1713
+ back: "Back",
1714
+ noHighlight: "No highlight",
1715
+ customColor: "Custom color",
1716
+ customColorLabel: "Custom…"
1717
+ },
1718
+ statusbar: {
1719
+ resizeHandle: "Resize editor",
1720
+ words: (n) => `Words: ${n}`,
1721
+ wordsLimit: (n, max) => `Words: ${n}/${max}`,
1722
+ chars: (n) => `Chars: ${n}`,
1723
+ charsLimit: (n, max) => `Chars: ${n}/${max}`
1724
+ },
1725
+ tooltips: {
1726
+ link: {
1727
+ ariaLabel: "Link actions",
1728
+ openLink: "Open link",
1729
+ copyUrl: "Copy URL",
1730
+ editLink: "Edit link",
1731
+ removeLink: "Remove link"
1732
+ },
1733
+ image: {
1734
+ ariaLabel: "Image actions",
1735
+ label: "Image",
1736
+ floatLeft: "Float Left",
1737
+ noFloat: "No Float",
1738
+ alignCenter: "Align Center",
1739
+ floatRight: "Float Right",
1740
+ originalSize: "Original Size",
1741
+ rotateLeft: "Rotate Left",
1742
+ rotateRight: "Rotate Right",
1743
+ cropImage: "Crop Image",
1744
+ addCaption: "Add / Edit Caption",
1745
+ deleteImage: "Delete Image"
1746
+ },
1747
+ code: {
1748
+ ariaLabel: "Code block actions",
1749
+ label: "Code",
1750
+ syntaxLanguage: "Syntax Language",
1751
+ syntaxAriaLabel: "Syntax language",
1752
+ copyCode: "Copy Code",
1753
+ toggleWordWrap: "Toggle Word Wrap",
1754
+ enableWordWrap: "Enable Word Wrap",
1755
+ disableWordWrap: "Disable Word Wrap",
1756
+ convertToParagraph: "Convert to Paragraph",
1757
+ deleteCodeBlock: "Delete Code Block"
1758
+ },
1759
+ table: {
1760
+ ariaLabel: "Table actions",
1761
+ label: "Table",
1762
+ selectCells: "Select Cells",
1763
+ addRowAbove: "Add Row Above",
1764
+ addRowBelow: "Add Row Below",
1765
+ deleteRow: "Delete Row",
1766
+ addColumnLeft: "Add Column Left",
1767
+ addColumnRight: "Add Column Right",
1768
+ deleteColumn: "Delete Column",
1769
+ mergeCells: "Merge Cells",
1770
+ unmergeCells: "Unmerge Cells",
1771
+ columnWidth: "Column Width",
1772
+ rowHeight: "Row Height",
1773
+ tableBorderWidth: "Table Border Width",
1774
+ deleteTable: "Delete Table",
1775
+ columnWidthPx: "Column Width (px)",
1776
+ rowHeightPx: "Row Height (px)",
1777
+ tableBorderWidthPx: "Table Border Width (px)",
1778
+ cancelBtn: "Cancel",
1779
+ applyBtn: "Apply"
1780
+ },
1781
+ video: {
1782
+ ariaLabel: "Video actions",
1783
+ label: "Video",
1784
+ floatLeft: "Float Left",
1785
+ noFloat: "No Float",
1786
+ alignCenter: "Align Center",
1787
+ floatRight: "Float Right",
1788
+ originalSize: "Original Size",
1789
+ previewVideo: "Preview Video",
1790
+ exitPreview: "Exit Preview",
1791
+ deleteVideo: "Delete Video"
1792
+ }
1793
+ },
1794
+ errors: {
1795
+ imageFormat: (type) => `Format "${type}" is not supported for display in web browsers. Please convert to JPEG, PNG, or WebP first.`,
1796
+ imageSize: (maxSize) => `Image file is too large. Maximum allowed size is ${maxSize} MB.`
1797
+ }
1798
+ };
1799
+ //#endregion
1800
+ //#region src/js/i18n/index.js
1801
+ /**
1802
+ * i18n/index.js — Locale registry and resolver for autumn-note-ce.
1803
+ *
1804
+ * Usage:
1805
+ * lang: 'en' → built-in English (default)
1806
+ * lang: 'vi' → built-in Vietnamese
1807
+ * lang: 'ja' → built-in Japanese
1808
+ * lang: 'zh' → built-in Simplified Chinese
1809
+ * lang: 'fr' → built-in French
1810
+ * lang: 'de' → built-in German
1811
+ * lang: 'es' → built-in Spanish
1812
+ * lang: 'ko' → built-in Korean
1813
+ * lang: { ... } → custom locale object, deep-merged over English
1814
+ */
1815
+ /**
1816
+ * All built-in locales keyed by their language code.
1817
+ * @type {Record<string, Partial<AsnLocale>>}
1818
+ */
1819
+ var locales = {
1820
+ en,
1821
+ vi: {
1822
+ toolbar: {
1823
+ bold: "Đậm (Ctrl+B)",
1824
+ italic: "Nghiêng (Ctrl+I)",
1825
+ underline: "Gạch chân (Ctrl+U)",
1826
+ strikethrough: "Gạch ngang",
1827
+ superscript: "Chỉ số trên",
1828
+ subscript: "Chỉ số dưới",
1829
+ alignLeft: "Căn trái",
1830
+ alignCenter: "Căn giữa",
1831
+ alignRight: "Căn phải",
1832
+ alignJustify: "Căn đều",
1833
+ ul: "Danh sách không thứ tự",
1834
+ ol: "Danh sách có thứ tự",
1835
+ checklist: "Danh sách kiểm tra",
1836
+ indent: "Tăng thụt đầu dòng",
1837
+ outdent: "Giảm thụt đầu dòng",
1838
+ undo: "Hoàn tác (Ctrl+Z)",
1839
+ redo: "Làm lại (Ctrl+Y)",
1840
+ hr: "Đường kẻ ngang",
1841
+ link: "Chèn liên kết",
1842
+ image: "Chèn hình ảnh",
1843
+ video: "Chèn video",
1844
+ emoji: "Chèn biểu tượng cảm xúc",
1845
+ icon: "Chèn biểu tượng FA",
1846
+ table: "Chèn bảng",
1847
+ fontSize: "Cỡ chữ",
1848
+ fontSizePlaceholder: "Cỡ",
1849
+ removeFormat: "Xóa định dạng",
1850
+ direction: "Chuyển hướng văn bản (LTR / RTL)",
1851
+ fontFamily: "Phông chữ",
1852
+ paragraphStyle: "Kiểu đoạn văn",
1853
+ paragraphStylePlaceholder: "Kiểu",
1854
+ lineHeight: "Khoảng cách dòng",
1855
+ lineHeightPlaceholder: "↕ Dòng",
1856
+ codeview: "Xem mã HTML",
1857
+ fullscreen: "Toàn màn hình",
1858
+ shortcuts: "Phím tắt (Ctrl+Shift+/)",
1859
+ find: "Tìm kiếm (Ctrl+F)",
1860
+ findReplace: "Tìm & Thay thế (Ctrl+H)",
1861
+ inlineCode: "Mã nội tuyến (Ctrl+`)",
1862
+ print: "In",
1863
+ foreColor: "Màu chữ",
1864
+ backColor: "Màu nền chữ",
1865
+ chooseTextColor: "Chọn màu chữ",
1866
+ chooseHighlightColor: "Chọn màu nền",
1867
+ customColor: "Màu tùy chỉnh",
1868
+ insertTableLabel: "Chèn bảng",
1869
+ paragraphItems: {
1870
+ p: "Bình thường",
1871
+ blockquote: "Trích dẫn",
1872
+ pre: "Mã"
1873
+ }
1874
+ },
1875
+ linkDialog: {
1876
+ ariaLabel: "Chèn liên kết",
1877
+ title: "Chèn liên kết",
1878
+ url: "Địa chỉ URL",
1879
+ urlPlaceholder: "https://",
1880
+ displayText: "Văn bản hiển thị",
1881
+ textPlaceholder: "Nội dung liên kết",
1882
+ openInNewTab: "Mở trong tab mới",
1883
+ insertBtn: "Chèn",
1884
+ cancelBtn: "Hủy"
1885
+ },
1886
+ imageDialog: {
1887
+ ariaLabel: "Chèn hình ảnh",
1888
+ title: "Chèn hình ảnh",
1889
+ imageUrl: "URL hình ảnh",
1890
+ urlPlaceholder: "https://example.com/anh.png",
1891
+ altText: "Văn bản thay thế",
1892
+ altPlaceholder: "Mô tả hình ảnh",
1893
+ alignment: "Căn chỉnh",
1894
+ alignNone: "Không",
1895
+ alignLeft: "Trái",
1896
+ alignCenter: "Giữa",
1897
+ alignRight: "Phải",
1898
+ uploadLabel: "Hoặc tải lên tệp",
1899
+ insertBtn: "Chèn",
1900
+ cancelBtn: "Hủy"
1901
+ },
1902
+ videoDialog: {
1903
+ ariaLabel: "Chèn video",
1904
+ title: "Chèn video",
1905
+ videoUrl: "URL video",
1906
+ urlPlaceholder: "YouTube, Vimeo, hoặc URL .mp4 trực tiếp",
1907
+ widthLabel: "Chiều rộng (px)",
1908
+ widthPlaceholder: "560",
1909
+ insertBtn: "Chèn",
1910
+ cancelBtn: "Hủy",
1911
+ detected: (type) => `Đã phát hiện: ${type}`,
1912
+ unknownFormat: "Định dạng không xác định — sẽ thử nhúng video trực tiếp",
1913
+ invalidUrl: "URL không hợp lệ — vui lòng nhập đường dẫn video hợp lệ."
1914
+ },
1915
+ emojiDialog: {
1916
+ ariaLabel: "Chèn biểu tượng cảm xúc",
1917
+ title: "Chèn biểu tượng cảm xúc",
1918
+ searchPlaceholder: "Tìm kiếm biểu tượng…",
1919
+ all: "Tất cả",
1920
+ cancelBtn: "Hủy",
1921
+ close: "Đóng",
1922
+ categories: {
1923
+ smileys: "Mặt cười",
1924
+ people: "Con người",
1925
+ animals: "Động vật",
1926
+ food: "Thức ăn",
1927
+ travel: "Du lịch",
1928
+ objects: "Đồ vật",
1929
+ symbols: "Ký hiệu"
1930
+ }
1931
+ },
1932
+ iconDialog: {
1933
+ ariaLabel: "Chèn biểu tượng FA",
1934
+ title: "Chèn biểu tượng FA",
1935
+ searchPlaceholder: "Tìm kiếm biểu tượng…",
1936
+ all: "Tất cả",
1937
+ style: "Kiểu",
1938
+ size: "Kích thước",
1939
+ color: "Màu sắc",
1940
+ useColor: " Dùng màu",
1941
+ selectHint: "Chọn một biểu tượng",
1942
+ insertBtn: "Chèn biểu tượng FA",
1943
+ cancelBtn: "Hủy",
1944
+ close: "Đóng",
1945
+ categories: {
1946
+ popular: "Phổ biến",
1947
+ interface: "Giao diện",
1948
+ navigation: "Điều hướng",
1949
+ media: "Phương tiện",
1950
+ communication: "Liên lạc",
1951
+ files: "Tệp tin",
1952
+ people: "Con người",
1953
+ objects: "Đồ vật"
1954
+ }
1955
+ },
1956
+ findReplace: {
1957
+ findTitle: "Tìm kiếm",
1958
+ findReplaceTitle: "Tìm & Thay thế",
1959
+ findPlaceholder: "Tìm…",
1960
+ searchAriaLabel: "Văn bản tìm kiếm",
1961
+ caseSensitive: "\xA0Phân biệt hoa thường",
1962
+ prevBtn: "← Trước",
1963
+ nextBtn: "Tiếp →",
1964
+ replacePlaceholder: "Thay thế bằng…",
1965
+ replaceAriaLabel: "Thay thế bằng",
1966
+ replaceBtn: "Thay thế",
1967
+ replaceAllBtn: "Thay thế tất cả",
1968
+ close: "×"
1969
+ },
1970
+ shortcutsDialog: {
1971
+ title: "Phím tắt bàn phím",
1972
+ ariaLabel: "Phím tắt bàn phím",
1973
+ close: "Đóng",
1974
+ shortcuts: [
1975
+ {
1976
+ category: "Định dạng văn bản",
1977
+ items: [
1978
+ {
1979
+ keys: "Ctrl + B",
1980
+ action: "Đậm"
1981
+ },
1982
+ {
1983
+ keys: "Ctrl + I",
1984
+ action: "Nghiêng"
1985
+ },
1986
+ {
1987
+ keys: "Ctrl + U",
1988
+ action: "Gạch chân"
1989
+ },
1990
+ {
1991
+ keys: "Ctrl + K",
1992
+ action: "Chèn / sửa liên kết"
1993
+ }
1994
+ ]
1995
+ },
1996
+ {
1997
+ category: "Lịch sử",
1998
+ items: [{
1999
+ keys: "Ctrl + Z",
2000
+ action: "Hoàn tác"
2001
+ }, {
2002
+ keys: "Ctrl + Y / Ctrl + Shift + Z",
2003
+ action: "Làm lại"
2004
+ }]
2005
+ },
2006
+ {
2007
+ category: "Chọn & Điều hướng",
2008
+ items: [
2009
+ {
2010
+ keys: "Ctrl + A",
2011
+ action: "Chọn tất cả"
2012
+ },
2013
+ {
2014
+ keys: "Tab",
2015
+ action: "Tăng thụt đầu dòng / chèn khoảng trắng"
2016
+ },
2017
+ {
2018
+ keys: "Shift + Tab",
2019
+ action: "Giảm thụt đầu dòng"
2020
+ }
2021
+ ]
2022
+ },
2023
+ {
2024
+ category: "Bảng nhớ tạm",
2025
+ items: [{
2026
+ keys: "Ctrl + Shift + V",
2027
+ action: "Dán dưới dạng văn bản thuần"
2028
+ }]
2029
+ },
2030
+ {
2031
+ category: "Tìm & Thay thế",
2032
+ items: [{
2033
+ keys: "Ctrl + F",
2034
+ action: "Tìm trong tài liệu"
2035
+ }, {
2036
+ keys: "Ctrl + H",
2037
+ action: "Tìm & Thay thế"
2038
+ }]
2039
+ },
2040
+ {
2041
+ category: "Trình soạn thảo",
2042
+ items: [{
2043
+ keys: "Ctrl + Shift + /",
2044
+ action: "Hiện hộp thoại phím tắt"
2045
+ }]
2046
+ }
2047
+ ]
2048
+ },
2049
+ contextMenu: {
2050
+ cut: "Cắt",
2051
+ copy: "Sao chép",
2052
+ paste: "Dán",
2053
+ bold: "Đậm",
2054
+ italic: "Nghiêng",
2055
+ underline: "Gạch chân",
2056
+ textColor: "Màu chữ",
2057
+ highlightColor: "Màu nền chữ",
2058
+ copyFormat: "Sao chép định dạng",
2059
+ pasteFormat: "Dán định dạng",
2060
+ removeFormat: "Xóa định dạng",
2061
+ link: "Chèn liên kết",
2062
+ image: "Chèn hình ảnh",
2063
+ video: "Chèn video",
2064
+ table: "Chèn bảng",
2065
+ back: "Quay lại",
2066
+ noHighlight: "Không tô màu",
2067
+ customColor: "Màu tùy chỉnh",
2068
+ customColorLabel: "Tùy chỉnh…"
2069
+ },
2070
+ statusbar: {
2071
+ resizeHandle: "Kéo để thay đổi kích thước",
2072
+ words: (n) => `Từ: ${n}`,
2073
+ wordsLimit: (n, max) => `Từ: ${n}/${max}`,
2074
+ chars: (n) => `Ký tự: ${n}`,
2075
+ charsLimit: (n, max) => `Ký tự: ${n}/${max}`
2076
+ },
2077
+ tooltips: {
2078
+ link: {
2079
+ ariaLabel: "Hành động liên kết",
2080
+ openLink: "Mở liên kết",
2081
+ copyUrl: "Sao chép URL",
2082
+ editLink: "Sửa liên kết",
2083
+ removeLink: "Xóa liên kết"
2084
+ },
2085
+ image: {
2086
+ ariaLabel: "Hành động hình ảnh",
2087
+ label: "Hình ảnh",
2088
+ floatLeft: "Nổi trái",
2089
+ noFloat: "Không nổi",
2090
+ alignCenter: "Căn giữa",
2091
+ floatRight: "Nổi phải",
2092
+ originalSize: "Kích thước gốc",
2093
+ rotateLeft: "Xoay trái",
2094
+ rotateRight: "Xoay phải",
2095
+ cropImage: "Cắt ảnh",
2096
+ addCaption: "Thêm / Sửa chú thích",
2097
+ deleteImage: "Xóa hình ảnh"
2098
+ },
2099
+ code: {
2100
+ ariaLabel: "Hành động khối mã",
2101
+ label: "Mã",
2102
+ syntaxLanguage: "Ngôn ngữ cú pháp",
2103
+ syntaxAriaLabel: "Ngôn ngữ cú pháp",
2104
+ copyCode: "Sao chép mã",
2105
+ toggleWordWrap: "Chuyển đổi xuống dòng",
2106
+ enableWordWrap: "Bật xuống dòng",
2107
+ disableWordWrap: "Tắt xuống dòng",
2108
+ convertToParagraph: "Chuyển thành đoạn văn",
2109
+ deleteCodeBlock: "Xóa khối mã"
2110
+ },
2111
+ table: {
2112
+ ariaLabel: "Hành động bảng",
2113
+ label: "Bảng",
2114
+ selectCells: "Chọn ô",
2115
+ addRowAbove: "Thêm hàng phía trên",
2116
+ addRowBelow: "Thêm hàng phía dưới",
2117
+ deleteRow: "Xóa hàng",
2118
+ addColumnLeft: "Thêm cột bên trái",
2119
+ addColumnRight: "Thêm cột bên phải",
2120
+ deleteColumn: "Xóa cột",
2121
+ mergeCells: "Gộp ô",
2122
+ unmergeCells: "Tách ô",
2123
+ columnWidth: "Chiều rộng cột",
2124
+ rowHeight: "Chiều cao hàng",
2125
+ tableBorderWidth: "Độ rộng viền bảng",
2126
+ deleteTable: "Xóa bảng",
2127
+ columnWidthPx: "Chiều rộng cột (px)",
2128
+ rowHeightPx: "Chiều cao hàng (px)",
2129
+ tableBorderWidthPx: "Độ rộng viền bảng (px)",
2130
+ cancelBtn: "Hủy",
2131
+ applyBtn: "Áp dụng"
2132
+ },
2133
+ video: {
2134
+ ariaLabel: "Hành động video",
2135
+ label: "Video",
2136
+ floatLeft: "Nổi trái",
2137
+ noFloat: "Không nổi",
2138
+ alignCenter: "Căn giữa",
2139
+ floatRight: "Nổi phải",
2140
+ originalSize: "Kích thước gốc",
2141
+ previewVideo: "Xem trước",
2142
+ exitPreview: "Thoát xem trước",
2143
+ deleteVideo: "Xóa video"
2144
+ }
2145
+ },
2146
+ errors: {
2147
+ imageFormat: (type) => `Định dạng "${type}" không được hỗ trợ hiển thị trên trình duyệt. Vui lòng chuyển đổi sang JPEG, PNG hoặc WebP.`,
2148
+ imageSize: (maxSize) => `Tệp hình ảnh quá lớn. Kích thước tối đa cho phép là ${maxSize} MB.`
2149
+ }
2150
+ },
2151
+ ja: {
2152
+ toolbar: {
2153
+ bold: "太字 (Ctrl+B)",
2154
+ italic: "斜体 (Ctrl+I)",
2155
+ underline: "下線 (Ctrl+U)",
2156
+ strikethrough: "取り消し線",
2157
+ superscript: "上付き文字",
2158
+ subscript: "下付き文字",
2159
+ alignLeft: "左揃え",
2160
+ alignCenter: "中央揃え",
2161
+ alignRight: "右揃え",
2162
+ alignJustify: "両端揃え",
2163
+ ul: "箇条書きリスト",
2164
+ ol: "番号付きリスト",
2165
+ checklist: "チェックリスト",
2166
+ indent: "インデントを増やす",
2167
+ outdent: "インデントを減らす",
2168
+ undo: "元に戻す (Ctrl+Z)",
2169
+ redo: "やり直す (Ctrl+Y)",
2170
+ hr: "水平線",
2171
+ link: "リンクを挿入",
2172
+ image: "画像を挿入",
2173
+ video: "動画を挿入",
2174
+ emoji: "絵文字を挿入",
2175
+ icon: "FAアイコンを挿入",
2176
+ table: "テーブルを挿入",
2177
+ fontSize: "フォントサイズ",
2178
+ fontSizePlaceholder: "サイズ",
2179
+ removeFormat: "書式をリセット",
2180
+ direction: "文字方向を切り替え (LTR / RTL)",
2181
+ fontFamily: "フォント",
2182
+ paragraphStyle: "段落スタイル",
2183
+ paragraphStylePlaceholder: "スタイル",
2184
+ lineHeight: "行の高さ",
2185
+ lineHeightPlaceholder: "↕ 行間",
2186
+ codeview: "HTMLソースを表示",
2187
+ fullscreen: "全画面表示",
2188
+ shortcuts: "キーボードショートカット (Ctrl+Shift+/)",
2189
+ find: "検索 (Ctrl+F)",
2190
+ findReplace: "検索と置換 (Ctrl+H)",
2191
+ inlineCode: "インラインコード (Ctrl+`)",
2192
+ print: "印刷",
2193
+ foreColor: "文字色",
2194
+ backColor: "ハイライト色",
2195
+ chooseTextColor: "文字色を選択",
2196
+ chooseHighlightColor: "ハイライト色を選択",
2197
+ customColor: "カスタムカラー",
2198
+ insertTableLabel: "テーブルを挿入",
2199
+ paragraphItems: {
2200
+ p: "標準",
2201
+ blockquote: "引用",
2202
+ pre: "コード"
2203
+ }
2204
+ },
2205
+ linkDialog: {
2206
+ ariaLabel: "リンクを挿入",
2207
+ title: "リンクを挿入",
2208
+ url: "URL",
2209
+ urlPlaceholder: "https://",
2210
+ displayText: "表示テキスト",
2211
+ textPlaceholder: "リンクテキスト",
2212
+ openInNewTab: "新しいタブで開く",
2213
+ insertBtn: "挿入",
2214
+ cancelBtn: "キャンセル"
2215
+ },
2216
+ imageDialog: {
2217
+ ariaLabel: "画像を挿入",
2218
+ title: "画像を挿入",
2219
+ imageUrl: "画像URL",
2220
+ urlPlaceholder: "https://example.com/image.png",
2221
+ altText: "代替テキスト",
2222
+ altPlaceholder: "画像の説明",
2223
+ alignment: "配置",
2224
+ alignNone: "なし",
2225
+ alignLeft: "左",
2226
+ alignCenter: "中央",
2227
+ alignRight: "右",
2228
+ uploadLabel: "ファイルをアップロード",
2229
+ insertBtn: "挿入",
2230
+ cancelBtn: "キャンセル"
2231
+ },
2232
+ videoDialog: {
2233
+ ariaLabel: "動画を挿入",
2234
+ title: "動画を挿入",
2235
+ videoUrl: "動画URL",
2236
+ urlPlaceholder: "YouTube、Vimeo、または直接動画URL",
2237
+ widthLabel: "幅 (px)",
2238
+ widthPlaceholder: "560",
2239
+ insertBtn: "挿入",
2240
+ cancelBtn: "キャンセル",
2241
+ detected: (type) => `検出: ${type}`,
2242
+ unknownFormat: "不明な形式 — 直接動画埋め込みを試みます",
2243
+ invalidUrl: "URLが無効です — 有効な動画URLを入力してください。"
2244
+ },
2245
+ emojiDialog: {
2246
+ ariaLabel: "絵文字を挿入",
2247
+ title: "絵文字を挿入",
2248
+ searchPlaceholder: "絵文字を検索…",
2249
+ all: "すべて",
2250
+ cancelBtn: "キャンセル",
2251
+ close: "閉じる",
2252
+ categories: {
2253
+ smileys: "顔文字",
2254
+ people: "人物",
2255
+ animals: "動物",
2256
+ food: "食べ物",
2257
+ travel: "旅行",
2258
+ objects: "モノ",
2259
+ symbols: "記号"
2260
+ }
2261
+ },
2262
+ iconDialog: {
2263
+ ariaLabel: "FAアイコンを挿入",
2264
+ title: "FAアイコンを挿入",
2265
+ searchPlaceholder: "アイコンを検索…",
2266
+ all: "すべて",
2267
+ style: "スタイル",
2268
+ size: "サイズ",
2269
+ color: "カラー",
2270
+ useColor: " カラーを使用",
2271
+ selectHint: "アイコンを選択してください",
2272
+ insertBtn: "FAアイコンを挿入",
2273
+ cancelBtn: "キャンセル",
2274
+ close: "閉じる",
2275
+ categories: {
2276
+ popular: "人気",
2277
+ interface: "インターフェース",
2278
+ navigation: "ナビゲーション",
2279
+ media: "メディア",
2280
+ communication: "通信",
2281
+ files: "ファイル",
2282
+ people: "人物",
2283
+ objects: "モノ"
2284
+ }
2285
+ },
2286
+ findReplace: {
2287
+ findTitle: "検索",
2288
+ findReplaceTitle: "検索と置換",
2289
+ findPlaceholder: "検索…",
2290
+ searchAriaLabel: "検索テキスト",
2291
+ caseSensitive: "\xA0大文字/小文字を区別",
2292
+ prevBtn: "← 前へ",
2293
+ nextBtn: "次へ →",
2294
+ replacePlaceholder: "置換後…",
2295
+ replaceAriaLabel: "置換後のテキスト",
2296
+ replaceBtn: "置換",
2297
+ replaceAllBtn: "すべて置換",
2298
+ close: "×"
2299
+ },
2300
+ shortcutsDialog: {
2301
+ title: "キーボードショートカット",
2302
+ ariaLabel: "キーボードショートカット",
2303
+ close: "閉じる",
2304
+ shortcuts: [
2305
+ {
2306
+ category: "テキスト書式",
2307
+ items: [
2308
+ {
2309
+ keys: "Ctrl + B",
2310
+ action: "太字"
2311
+ },
2312
+ {
2313
+ keys: "Ctrl + I",
2314
+ action: "斜体"
2315
+ },
2316
+ {
2317
+ keys: "Ctrl + U",
2318
+ action: "下線"
2319
+ },
2320
+ {
2321
+ keys: "Ctrl + K",
2322
+ action: "リンクの挿入 / 編集"
2323
+ }
2324
+ ]
2325
+ },
2326
+ {
2327
+ category: "履歴",
2328
+ items: [{
2329
+ keys: "Ctrl + Z",
2330
+ action: "元に戻す"
2331
+ }, {
2332
+ keys: "Ctrl + Y / Ctrl + Shift + Z",
2333
+ action: "やり直す"
2334
+ }]
2335
+ },
2336
+ {
2337
+ category: "選択と移動",
2338
+ items: [
2339
+ {
2340
+ keys: "Ctrl + A",
2341
+ action: "すべて選択"
2342
+ },
2343
+ {
2344
+ keys: "Tab",
2345
+ action: "インデント増加 / スペース挿入"
2346
+ },
2347
+ {
2348
+ keys: "Shift + Tab",
2349
+ action: "インデント減少"
2350
+ }
2351
+ ]
2352
+ },
2353
+ {
2354
+ category: "クリップボード",
2355
+ items: [{
2356
+ keys: "Ctrl + Shift + V",
2357
+ action: "プレーンテキストとして貼り付け"
2358
+ }]
2359
+ },
2360
+ {
2361
+ category: "検索と置換",
2362
+ items: [{
2363
+ keys: "Ctrl + F",
2364
+ action: "文書内を検索"
2365
+ }, {
2366
+ keys: "Ctrl + H",
2367
+ action: "検索と置換"
2368
+ }]
2369
+ },
2370
+ {
2371
+ category: "エディター",
2372
+ items: [{
2373
+ keys: "Ctrl + Shift + /",
2374
+ action: "ショートカットダイアログを表示"
2375
+ }]
2376
+ }
2377
+ ]
2378
+ },
2379
+ contextMenu: {
2380
+ cut: "切り取り",
2381
+ copy: "コピー",
2382
+ paste: "貼り付け",
2383
+ bold: "太字",
2384
+ italic: "斜体",
2385
+ underline: "下線",
2386
+ textColor: "文字色",
2387
+ highlightColor: "ハイライト色",
2388
+ copyFormat: "書式をコピー",
2389
+ pasteFormat: "書式を貼り付け",
2390
+ removeFormat: "書式をリセット",
2391
+ link: "リンクを挿入",
2392
+ image: "画像を挿入",
2393
+ video: "動画を挿入",
2394
+ table: "テーブルを挿入",
2395
+ back: "戻る",
2396
+ noHighlight: "ハイライトなし",
2397
+ customColor: "カスタムカラー",
2398
+ customColorLabel: "カスタム…"
2399
+ },
2400
+ statusbar: {
2401
+ resizeHandle: "ドラッグしてリサイズ",
2402
+ words: (n) => `単語数: ${n}`,
2403
+ wordsLimit: (n, max) => `単語数: ${n}/${max}`,
2404
+ chars: (n) => `文字数: ${n}`,
2405
+ charsLimit: (n, max) => `文字数: ${n}/${max}`
2406
+ },
2407
+ tooltips: {
2408
+ link: {
2409
+ ariaLabel: "リンク操作",
2410
+ openLink: "リンクを開く",
2411
+ copyUrl: "URLをコピー",
2412
+ editLink: "リンクを編集",
2413
+ removeLink: "リンクを削除"
2414
+ },
2415
+ image: {
2416
+ ariaLabel: "画像操作",
2417
+ label: "画像",
2418
+ floatLeft: "左に回り込み",
2419
+ noFloat: "回り込みなし",
2420
+ alignCenter: "中央揃え",
2421
+ floatRight: "右に回り込み",
2422
+ originalSize: "元のサイズ",
2423
+ rotateLeft: "左に回転",
2424
+ rotateRight: "右に回転",
2425
+ cropImage: "画像をトリミング",
2426
+ addCaption: "キャプションを追加 / 編集",
2427
+ deleteImage: "画像を削除"
2428
+ },
2429
+ code: {
2430
+ ariaLabel: "コードブロック操作",
2431
+ label: "コード",
2432
+ syntaxLanguage: "言語",
2433
+ syntaxAriaLabel: "構文言語",
2434
+ copyCode: "コードをコピー",
2435
+ toggleWordWrap: "折り返しを切り替え",
2436
+ enableWordWrap: "折り返しを有効にする",
2437
+ disableWordWrap: "折り返しを無効にする",
2438
+ convertToParagraph: "段落に変換",
2439
+ deleteCodeBlock: "コードブロックを削除"
2440
+ },
2441
+ table: {
2442
+ ariaLabel: "テーブル操作",
2443
+ label: "テーブル",
2444
+ selectCells: "セルを選択",
2445
+ addRowAbove: "上に行を追加",
2446
+ addRowBelow: "下に行を追加",
2447
+ deleteRow: "行を削除",
2448
+ addColumnLeft: "左に列を追加",
2449
+ addColumnRight: "右に列を追加",
2450
+ deleteColumn: "列を削除",
2451
+ mergeCells: "セルを結合",
2452
+ unmergeCells: "セルの結合を解除",
2453
+ columnWidth: "列幅",
2454
+ rowHeight: "行の高さ",
2455
+ tableBorderWidth: "テーブルの枠幅",
2456
+ deleteTable: "テーブルを削除",
2457
+ columnWidthPx: "列幅 (px)",
2458
+ rowHeightPx: "行の高さ (px)",
2459
+ tableBorderWidthPx: "テーブルの枠幅 (px)",
2460
+ cancelBtn: "キャンセル",
2461
+ applyBtn: "適用"
2462
+ },
2463
+ video: {
2464
+ ariaLabel: "動画操作",
2465
+ label: "動画",
2466
+ floatLeft: "左に回り込み",
2467
+ noFloat: "回り込みなし",
2468
+ alignCenter: "中央揃え",
2469
+ floatRight: "右に回り込み",
2470
+ originalSize: "元のサイズ",
2471
+ previewVideo: "プレビュー",
2472
+ exitPreview: "プレビューを終了",
2473
+ deleteVideo: "動画を削除"
2474
+ }
2475
+ },
2476
+ errors: {
2477
+ imageFormat: (type) => `形式 "${type}" はブラウザでの表示をサポートしていません。JPEG、PNG、または WebP に変換してください。`,
2478
+ imageSize: (maxSize) => `画像ファイルが大きすぎます。最大許容サイズは ${maxSize} MB です。`
2479
+ }
2480
+ },
2481
+ zh: {
2482
+ toolbar: {
2483
+ bold: "粗体 (Ctrl+B)",
2484
+ italic: "斜体 (Ctrl+I)",
2485
+ underline: "下划线 (Ctrl+U)",
2486
+ strikethrough: "删除线",
2487
+ superscript: "上标",
2488
+ subscript: "下标",
2489
+ alignLeft: "左对齐",
2490
+ alignCenter: "居中",
2491
+ alignRight: "右对齐",
2492
+ alignJustify: "两端对齐",
2493
+ ul: "无序列表",
2494
+ ol: "有序列表",
2495
+ checklist: "待办列表",
2496
+ indent: "增加缩进",
2497
+ outdent: "减少缩进",
2498
+ undo: "撤销 (Ctrl+Z)",
2499
+ redo: "重做 (Ctrl+Y)",
2500
+ hr: "水平分割线",
2501
+ link: "插入链接",
2502
+ image: "插入图片",
2503
+ video: "插入视频",
2504
+ emoji: "插入表情",
2505
+ icon: "插入 FA 图标",
2506
+ table: "插入表格",
2507
+ fontSize: "字号",
2508
+ fontSizePlaceholder: "大小",
2509
+ removeFormat: "清除格式",
2510
+ direction: "切换文字方向 (LTR / RTL)",
2511
+ fontFamily: "字体",
2512
+ paragraphStyle: "段落样式",
2513
+ paragraphStylePlaceholder: "样式",
2514
+ lineHeight: "行高",
2515
+ lineHeightPlaceholder: "↕ 行距",
2516
+ codeview: "查看 HTML 源码",
2517
+ fullscreen: "全屏",
2518
+ shortcuts: "键盘快捷键 (Ctrl+Shift+/)",
2519
+ find: "搜索 (Ctrl+F)",
2520
+ findReplace: "查找和替换 (Ctrl+H)",
2521
+ inlineCode: "行内代码 (Ctrl+`)",
2522
+ print: "打印",
2523
+ foreColor: "文字颜色",
2524
+ backColor: "高亮颜色",
2525
+ chooseTextColor: "选择文字颜色",
2526
+ chooseHighlightColor: "选择高亮颜色",
2527
+ customColor: "自定义颜色",
2528
+ insertTableLabel: "插入表格",
2529
+ paragraphItems: {
2530
+ p: "正文",
2531
+ blockquote: "引用",
2532
+ pre: "代码"
2533
+ }
2534
+ },
2535
+ linkDialog: {
2536
+ ariaLabel: "插入链接",
2537
+ title: "插入链接",
2538
+ url: "链接地址",
2539
+ urlPlaceholder: "https://",
2540
+ displayText: "显示文字",
2541
+ textPlaceholder: "链接文字",
2542
+ openInNewTab: "在新标签页中打开",
2543
+ insertBtn: "插入",
2544
+ cancelBtn: "取消"
2545
+ },
2546
+ imageDialog: {
2547
+ ariaLabel: "插入图片",
2548
+ title: "插入图片",
2549
+ imageUrl: "图片地址",
2550
+ urlPlaceholder: "https://example.com/image.png",
2551
+ altText: "替代文字",
2552
+ altPlaceholder: "图片描述",
2553
+ alignment: "对齐方式",
2554
+ alignNone: "无",
2555
+ alignLeft: "左",
2556
+ alignCenter: "居中",
2557
+ alignRight: "右",
2558
+ uploadLabel: "或上传文件",
2559
+ insertBtn: "插入",
2560
+ cancelBtn: "取消"
2561
+ },
2562
+ videoDialog: {
2563
+ ariaLabel: "插入视频",
2564
+ title: "插入视频",
2565
+ videoUrl: "视频地址",
2566
+ urlPlaceholder: "YouTube、Vimeo 或直接 .mp4 链接",
2567
+ widthLabel: "宽度 (px)",
2568
+ widthPlaceholder: "560",
2569
+ insertBtn: "插入",
2570
+ cancelBtn: "取消",
2571
+ detected: (type) => `已识别: ${type}`,
2572
+ unknownFormat: "未知格式 — 将尝试直接嵌入视频",
2573
+ invalidUrl: "URL 无效 — 请输入有效的视频链接。"
2574
+ },
2575
+ emojiDialog: {
2576
+ ariaLabel: "插入表情",
2577
+ title: "插入表情",
2578
+ searchPlaceholder: "搜索表情…",
2579
+ all: "全部",
2580
+ cancelBtn: "取消",
2581
+ close: "关闭",
2582
+ categories: {
2583
+ smileys: "笑脸",
2584
+ people: "人物",
2585
+ animals: "动物",
2586
+ food: "食物",
2587
+ travel: "旅行",
2588
+ objects: "物品",
2589
+ symbols: "符号"
2590
+ }
2591
+ },
2592
+ iconDialog: {
2593
+ ariaLabel: "插入 FA 图标",
2594
+ title: "插入 FA 图标",
2595
+ searchPlaceholder: "搜索图标…",
2596
+ all: "全部",
2597
+ style: "样式",
2598
+ size: "大小",
2599
+ color: "颜色",
2600
+ useColor: " 使用颜色",
2601
+ selectHint: "请选择一个图标",
2602
+ insertBtn: "插入 FA 图标",
2603
+ cancelBtn: "取消",
2604
+ close: "关闭",
2605
+ categories: {
2606
+ popular: "热门",
2607
+ interface: "界面",
2608
+ navigation: "导航",
2609
+ media: "媒体",
2610
+ communication: "通讯",
2611
+ files: "文件",
2612
+ people: "人物",
2613
+ objects: "物品"
2614
+ }
2615
+ },
2616
+ findReplace: {
2617
+ findTitle: "搜索",
2618
+ findReplaceTitle: "查找和替换",
2619
+ findPlaceholder: "查找…",
2620
+ searchAriaLabel: "搜索文字",
2621
+ caseSensitive: "\xA0区分大小写",
2622
+ prevBtn: "← 上一个",
2623
+ nextBtn: "下一个 →",
2624
+ replacePlaceholder: "替换为…",
2625
+ replaceAriaLabel: "替换为",
2626
+ replaceBtn: "替换",
2627
+ replaceAllBtn: "全部替换",
2628
+ close: "×"
2629
+ },
2630
+ shortcutsDialog: {
2631
+ title: "键盘快捷键",
2632
+ ariaLabel: "键盘快捷键",
2633
+ close: "关闭",
2634
+ shortcuts: [
2635
+ {
2636
+ category: "文字格式",
2637
+ items: [
2638
+ {
2639
+ keys: "Ctrl + B",
2640
+ action: "粗体"
2641
+ },
2642
+ {
2643
+ keys: "Ctrl + I",
2644
+ action: "斜体"
2645
+ },
2646
+ {
2647
+ keys: "Ctrl + U",
2648
+ action: "下划线"
2649
+ },
2650
+ {
2651
+ keys: "Ctrl + K",
2652
+ action: "插入 / 编辑链接"
2653
+ }
2654
+ ]
2655
+ },
2656
+ {
2657
+ category: "历史记录",
2658
+ items: [{
2659
+ keys: "Ctrl + Z",
2660
+ action: "撤销"
2661
+ }, {
2662
+ keys: "Ctrl + Y / Ctrl + Shift + Z",
2663
+ action: "重做"
2664
+ }]
2665
+ },
2666
+ {
2667
+ category: "选择与导航",
2668
+ items: [
2669
+ {
2670
+ keys: "Ctrl + A",
2671
+ action: "全选"
2672
+ },
2673
+ {
2674
+ keys: "Tab",
2675
+ action: "增加缩进 / 插入空格"
2676
+ },
2677
+ {
2678
+ keys: "Shift + Tab",
2679
+ action: "减少缩进"
2680
+ }
2681
+ ]
2682
+ },
2683
+ {
2684
+ category: "剪贴板",
2685
+ items: [{
2686
+ keys: "Ctrl + Shift + V",
2687
+ action: "粘贴为纯文本"
2688
+ }]
2689
+ },
2690
+ {
2691
+ category: "查找和替换",
2692
+ items: [{
2693
+ keys: "Ctrl + F",
2694
+ action: "在文档中搜索"
2695
+ }, {
2696
+ keys: "Ctrl + H",
2697
+ action: "查找和替换"
2698
+ }]
2699
+ },
2700
+ {
2701
+ category: "编辑器",
2702
+ items: [{
2703
+ keys: "Ctrl + Shift + /",
2704
+ action: "显示快捷键对话框"
2705
+ }]
2706
+ }
2707
+ ]
2708
+ },
2709
+ contextMenu: {
2710
+ cut: "剪切",
2711
+ copy: "复制",
2712
+ paste: "粘贴",
2713
+ bold: "粗体",
2714
+ italic: "斜体",
2715
+ underline: "下划线",
2716
+ textColor: "文字颜色",
2717
+ highlightColor: "高亮颜色",
2718
+ copyFormat: "复制格式",
2719
+ pasteFormat: "粘贴格式",
2720
+ removeFormat: "清除格式",
2721
+ link: "插入链接",
2722
+ image: "插入图片",
2723
+ video: "插入视频",
2724
+ table: "插入表格",
2725
+ back: "返回",
2726
+ noHighlight: "无高亮",
2727
+ customColor: "自定义颜色",
2728
+ customColorLabel: "自定义…"
2729
+ },
2730
+ statusbar: {
2731
+ resizeHandle: "拖动以调整大小",
2732
+ words: (n) => `字数: ${n}`,
2733
+ wordsLimit: (n, max) => `字数: ${n}/${max}`,
2734
+ chars: (n) => `字符数: ${n}`,
2735
+ charsLimit: (n, max) => `字符数: ${n}/${max}`
2736
+ },
2737
+ tooltips: {
2738
+ link: {
2739
+ ariaLabel: "链接操作",
2740
+ openLink: "打开链接",
2741
+ copyUrl: "复制 URL",
2742
+ editLink: "编辑链接",
2743
+ removeLink: "删除链接"
2744
+ },
2745
+ image: {
2746
+ ariaLabel: "图片操作",
2747
+ label: "图片",
2748
+ floatLeft: "左浮动",
2749
+ noFloat: "不浮动",
2750
+ alignCenter: "居中对齐",
2751
+ floatRight: "右浮动",
2752
+ originalSize: "原始尺寸",
2753
+ rotateLeft: "向左旋转",
2754
+ rotateRight: "向右旋转",
2755
+ cropImage: "裁剪图片",
2756
+ addCaption: "添加 / 编辑说明",
2757
+ deleteImage: "删除图片"
2758
+ },
2759
+ code: {
2760
+ ariaLabel: "代码块操作",
2761
+ label: "代码",
2762
+ syntaxLanguage: "语法语言",
2763
+ syntaxAriaLabel: "语法语言",
2764
+ copyCode: "复制代码",
2765
+ toggleWordWrap: "切换自动换行",
2766
+ enableWordWrap: "启用自动换行",
2767
+ disableWordWrap: "禁用自动换行",
2768
+ convertToParagraph: "转换为段落",
2769
+ deleteCodeBlock: "删除代码块"
2770
+ },
2771
+ table: {
2772
+ ariaLabel: "表格操作",
2773
+ label: "表格",
2774
+ selectCells: "选择单元格",
2775
+ addRowAbove: "在上方插入行",
2776
+ addRowBelow: "在下方插入行",
2777
+ deleteRow: "删除行",
2778
+ addColumnLeft: "在左侧插入列",
2779
+ addColumnRight: "在右侧插入列",
2780
+ deleteColumn: "删除列",
2781
+ mergeCells: "合并单元格",
2782
+ unmergeCells: "拆分单元格",
2783
+ columnWidth: "列宽",
2784
+ rowHeight: "行高",
2785
+ tableBorderWidth: "表格边框宽度",
2786
+ deleteTable: "删除表格",
2787
+ columnWidthPx: "列宽 (px)",
2788
+ rowHeightPx: "行高 (px)",
2789
+ tableBorderWidthPx: "表格边框宽度 (px)",
2790
+ cancelBtn: "取消",
2791
+ applyBtn: "应用"
2792
+ },
2793
+ video: {
2794
+ ariaLabel: "视频操作",
2795
+ label: "视频",
2796
+ floatLeft: "左浮动",
2797
+ noFloat: "不浮动",
2798
+ alignCenter: "居中对齐",
2799
+ floatRight: "右浮动",
2800
+ originalSize: "原始尺寸",
2801
+ previewVideo: "预览视频",
2802
+ exitPreview: "退出预览",
2803
+ deleteVideo: "删除视频"
2804
+ }
2805
+ },
2806
+ errors: {
2807
+ imageFormat: (type) => `格式 "${type}" 不支持在浏览器中显示。请转换为 JPEG、PNG 或 WebP。`,
2808
+ imageSize: (maxSize) => `图片文件过大。最大允许大小为 ${maxSize} MB。`
2809
+ }
2810
+ },
2811
+ fr: {
2812
+ toolbar: {
2813
+ bold: "Gras (Ctrl+B)",
2814
+ italic: "Italique (Ctrl+I)",
2815
+ underline: "Souligné (Ctrl+U)",
2816
+ strikethrough: "Barré",
2817
+ superscript: "Exposant",
2818
+ subscript: "Indice",
2819
+ alignLeft: "Aligner à gauche",
2820
+ alignCenter: "Centrer",
2821
+ alignRight: "Aligner à droite",
2822
+ alignJustify: "Justifier",
2823
+ ul: "Liste à puces",
2824
+ ol: "Liste numérotée",
2825
+ checklist: "Liste de tâches",
2826
+ indent: "Augmenter le retrait",
2827
+ outdent: "Diminuer le retrait",
2828
+ undo: "Annuler (Ctrl+Z)",
2829
+ redo: "Rétablir (Ctrl+Y)",
2830
+ hr: "Ligne horizontale",
2831
+ link: "Insérer un lien",
2832
+ image: "Insérer une image",
2833
+ video: "Insérer une vidéo",
2834
+ emoji: "Insérer un emoji",
2835
+ icon: "Insérer une icône FA",
2836
+ table: "Insérer un tableau",
2837
+ fontSize: "Taille de police",
2838
+ fontSizePlaceholder: "Taille",
2839
+ removeFormat: "Effacer la mise en forme",
2840
+ direction: "Basculer la direction du texte (LTR / RTL)",
2841
+ fontFamily: "Police",
2842
+ paragraphStyle: "Style de paragraphe",
2843
+ paragraphStylePlaceholder: "Style",
2844
+ lineHeight: "Interligne",
2845
+ lineHeightPlaceholder: "↕ Ligne",
2846
+ codeview: "Afficher le code HTML",
2847
+ fullscreen: "Plein écran",
2848
+ shortcuts: "Raccourcis clavier (Ctrl+Shift+/)",
2849
+ find: "Rechercher (Ctrl+F)",
2850
+ findReplace: "Rechercher et remplacer (Ctrl+H)",
2851
+ inlineCode: "Code inline (Ctrl+`)",
2852
+ print: "Imprimer",
2853
+ foreColor: "Couleur du texte",
2854
+ backColor: "Couleur de surbrillance",
2855
+ chooseTextColor: "Choisir la couleur du texte",
2856
+ chooseHighlightColor: "Choisir la couleur de surbrillance",
2857
+ customColor: "Couleur personnalisée",
2858
+ insertTableLabel: "Insérer un tableau",
2859
+ paragraphItems: {
2860
+ p: "Normal",
2861
+ blockquote: "Citation",
2862
+ pre: "Code"
2863
+ }
2864
+ },
2865
+ linkDialog: {
2866
+ ariaLabel: "Insérer un lien",
2867
+ title: "Insérer un lien",
2868
+ url: "URL",
2869
+ urlPlaceholder: "https://",
2870
+ displayText: "Texte affiché",
2871
+ textPlaceholder: "Texte du lien",
2872
+ openInNewTab: "Ouvrir dans un nouvel onglet",
2873
+ insertBtn: "Insérer",
2874
+ cancelBtn: "Annuler"
2875
+ },
2876
+ imageDialog: {
2877
+ ariaLabel: "Insérer une image",
2878
+ title: "Insérer une image",
2879
+ imageUrl: "URL de l'image",
2880
+ urlPlaceholder: "https://example.com/image.png",
2881
+ altText: "Texte alternatif",
2882
+ altPlaceholder: "Description de l'image",
2883
+ alignment: "Alignement",
2884
+ alignNone: "Aucun",
2885
+ alignLeft: "Gauche",
2886
+ alignCenter: "Centre",
2887
+ alignRight: "Droite",
2888
+ uploadLabel: "Ou téléverser un fichier",
2889
+ insertBtn: "Insérer",
2890
+ cancelBtn: "Annuler"
2891
+ },
2892
+ videoDialog: {
2893
+ ariaLabel: "Insérer une vidéo",
2894
+ title: "Insérer une vidéo",
2895
+ videoUrl: "URL de la vidéo",
2896
+ urlPlaceholder: "YouTube, Vimeo ou URL .mp4 directe",
2897
+ widthLabel: "Largeur (px)",
2898
+ widthPlaceholder: "560",
2899
+ insertBtn: "Insérer",
2900
+ cancelBtn: "Annuler",
2901
+ detected: (type) => `Détecté\u00a0: ${type}`,
2902
+ unknownFormat: "Format inconnu — tentative d'intégration directe",
2903
+ invalidUrl: "URL invalide — veuillez saisir un lien vidéo valide."
2904
+ },
2905
+ emojiDialog: {
2906
+ ariaLabel: "Insérer un emoji",
2907
+ title: "Insérer un emoji",
2908
+ searchPlaceholder: "Rechercher des emojis…",
2909
+ all: "Tout",
2910
+ cancelBtn: "Annuler",
2911
+ close: "Fermer",
2912
+ categories: {
2913
+ smileys: "Smileys",
2914
+ people: "Personnes",
2915
+ animals: "Animaux",
2916
+ food: "Nourriture",
2917
+ travel: "Voyage",
2918
+ objects: "Objets",
2919
+ symbols: "Symboles"
2920
+ }
2921
+ },
2922
+ iconDialog: {
2923
+ ariaLabel: "Insérer une icône FA",
2924
+ title: "Insérer une icône FA",
2925
+ searchPlaceholder: "Rechercher des icônes…",
2926
+ all: "Tout",
2927
+ style: "Style",
2928
+ size: "Taille",
2929
+ color: "Couleur",
2930
+ useColor: " Utiliser la couleur",
2931
+ selectHint: "Sélectionner une icône",
2932
+ insertBtn: "Insérer une icône FA",
2933
+ cancelBtn: "Annuler",
2934
+ close: "Fermer",
2935
+ categories: {
2936
+ popular: "Populaire",
2937
+ interface: "Interface",
2938
+ navigation: "Navigation",
2939
+ media: "Média",
2940
+ communication: "Communication",
2941
+ files: "Fichiers",
2942
+ people: "Personnes",
2943
+ objects: "Objets"
2944
+ }
2945
+ },
2946
+ findReplace: {
2947
+ findTitle: "Rechercher",
2948
+ findReplaceTitle: "Rechercher et remplacer",
2949
+ findPlaceholder: "Rechercher…",
2950
+ searchAriaLabel: "Texte à rechercher",
2951
+ caseSensitive: "\xA0Respecter la casse",
2952
+ prevBtn: "← Préc.",
2953
+ nextBtn: "Suiv. →",
2954
+ replacePlaceholder: "Remplacer par…",
2955
+ replaceAriaLabel: "Remplacer par",
2956
+ replaceBtn: "Remplacer",
2957
+ replaceAllBtn: "Tout remplacer",
2958
+ close: "×"
2959
+ },
2960
+ shortcutsDialog: {
2961
+ title: "Raccourcis clavier",
2962
+ ariaLabel: "Raccourcis clavier",
2963
+ close: "Fermer",
2964
+ shortcuts: [
2965
+ {
2966
+ category: "Mise en forme du texte",
2967
+ items: [
2968
+ {
2969
+ keys: "Ctrl + B",
2970
+ action: "Gras"
2971
+ },
2972
+ {
2973
+ keys: "Ctrl + I",
2974
+ action: "Italique"
2975
+ },
2976
+ {
2977
+ keys: "Ctrl + U",
2978
+ action: "Souligné"
2979
+ },
2980
+ {
2981
+ keys: "Ctrl + K",
2982
+ action: "Insérer / modifier un lien"
2983
+ }
2984
+ ]
2985
+ },
2986
+ {
2987
+ category: "Historique",
2988
+ items: [{
2989
+ keys: "Ctrl + Z",
2990
+ action: "Annuler"
2991
+ }, {
2992
+ keys: "Ctrl + Y / Ctrl + Shift + Z",
2993
+ action: "Rétablir"
2994
+ }]
2995
+ },
2996
+ {
2997
+ category: "Sélection et navigation",
2998
+ items: [
2999
+ {
3000
+ keys: "Ctrl + A",
3001
+ action: "Tout sélectionner"
3002
+ },
3003
+ {
3004
+ keys: "Tab",
3005
+ action: "Augmenter le retrait / insérer des espaces"
3006
+ },
3007
+ {
3008
+ keys: "Shift + Tab",
3009
+ action: "Diminuer le retrait"
3010
+ }
3011
+ ]
3012
+ },
3013
+ {
3014
+ category: "Presse-papiers",
3015
+ items: [{
3016
+ keys: "Ctrl + Shift + V",
3017
+ action: "Coller en texte brut"
3018
+ }]
3019
+ },
3020
+ {
3021
+ category: "Rechercher et remplacer",
3022
+ items: [{
3023
+ keys: "Ctrl + F",
3024
+ action: "Rechercher dans le document"
3025
+ }, {
3026
+ keys: "Ctrl + H",
3027
+ action: "Rechercher et remplacer"
3028
+ }]
3029
+ },
3030
+ {
3031
+ category: "Éditeur",
3032
+ items: [{
3033
+ keys: "Ctrl + Shift + /",
3034
+ action: "Afficher les raccourcis clavier"
3035
+ }]
3036
+ }
3037
+ ]
3038
+ },
3039
+ contextMenu: {
3040
+ cut: "Couper",
3041
+ copy: "Copier",
3042
+ paste: "Coller",
3043
+ bold: "Gras",
3044
+ italic: "Italique",
3045
+ underline: "Souligné",
3046
+ textColor: "Couleur du texte",
3047
+ highlightColor: "Couleur de surbrillance",
3048
+ copyFormat: "Copier la mise en forme",
3049
+ pasteFormat: "Coller la mise en forme",
3050
+ removeFormat: "Effacer la mise en forme",
3051
+ link: "Insérer un lien",
3052
+ image: "Insérer une image",
3053
+ video: "Insérer une vidéo",
3054
+ table: "Insérer un tableau",
3055
+ back: "Retour",
3056
+ noHighlight: "Sans surbrillance",
3057
+ customColor: "Couleur personnalisée",
3058
+ customColorLabel: "Personnaliser…"
3059
+ },
3060
+ statusbar: {
3061
+ resizeHandle: "Faire glisser pour redimensionner",
3062
+ words: (n) => `Mots\u00a0: ${n}`,
3063
+ wordsLimit: (n, max) => `Mots\u00a0: ${n}/${max}`,
3064
+ chars: (n) => `Caractères\u00a0: ${n}`,
3065
+ charsLimit: (n, max) => `Caractères\u00a0: ${n}/${max}`
3066
+ },
3067
+ tooltips: {
3068
+ link: {
3069
+ ariaLabel: "Actions du lien",
3070
+ openLink: "Ouvrir le lien",
3071
+ copyUrl: "Copier l'URL",
3072
+ editLink: "Modifier le lien",
3073
+ removeLink: "Supprimer le lien"
3074
+ },
3075
+ image: {
3076
+ ariaLabel: "Actions de l'image",
3077
+ label: "Image",
3078
+ floatLeft: "Flottant à gauche",
3079
+ noFloat: "Sans flottant",
3080
+ alignCenter: "Centré",
3081
+ floatRight: "Flottant à droite",
3082
+ originalSize: "Taille originale",
3083
+ rotateLeft: "Rotation à gauche",
3084
+ rotateRight: "Rotation à droite",
3085
+ cropImage: "Recadrer l'image",
3086
+ addCaption: "Ajouter / modifier la légende",
3087
+ deleteImage: "Supprimer l'image"
3088
+ },
3089
+ code: {
3090
+ ariaLabel: "Actions du bloc de code",
3091
+ label: "Code",
3092
+ syntaxLanguage: "Langage de syntaxe",
3093
+ syntaxAriaLabel: "Langage de syntaxe",
3094
+ copyCode: "Copier le code",
3095
+ toggleWordWrap: "Activer/désactiver le retour à la ligne",
3096
+ enableWordWrap: "Activer le retour à la ligne",
3097
+ disableWordWrap: "Désactiver le retour à la ligne",
3098
+ convertToParagraph: "Convertir en paragraphe",
3099
+ deleteCodeBlock: "Supprimer le bloc de code"
3100
+ },
3101
+ table: {
3102
+ ariaLabel: "Actions du tableau",
3103
+ label: "Tableau",
3104
+ selectCells: "Sélectionner des cellules",
3105
+ addRowAbove: "Ajouter une ligne au-dessus",
3106
+ addRowBelow: "Ajouter une ligne en-dessous",
3107
+ deleteRow: "Supprimer la ligne",
3108
+ addColumnLeft: "Ajouter une colonne à gauche",
3109
+ addColumnRight: "Ajouter une colonne à droite",
3110
+ deleteColumn: "Supprimer la colonne",
3111
+ mergeCells: "Fusionner les cellules",
3112
+ unmergeCells: "Scinder les cellules",
3113
+ columnWidth: "Largeur de colonne",
3114
+ rowHeight: "Hauteur de ligne",
3115
+ tableBorderWidth: "Épaisseur des bordures",
3116
+ deleteTable: "Supprimer le tableau",
3117
+ columnWidthPx: "Largeur de colonne (px)",
3118
+ rowHeightPx: "Hauteur de ligne (px)",
3119
+ tableBorderWidthPx: "Épaisseur des bordures (px)",
3120
+ cancelBtn: "Annuler",
3121
+ applyBtn: "Appliquer"
3122
+ },
3123
+ video: {
3124
+ ariaLabel: "Actions de la vidéo",
3125
+ label: "Vidéo",
3126
+ floatLeft: "Flottant à gauche",
3127
+ noFloat: "Sans flottant",
3128
+ alignCenter: "Centré",
3129
+ floatRight: "Flottant à droite",
3130
+ originalSize: "Taille originale",
3131
+ previewVideo: "Aperçu",
3132
+ exitPreview: "Quitter l'aperçu",
3133
+ deleteVideo: "Supprimer la vidéo"
3134
+ }
3135
+ },
3136
+ errors: {
3137
+ imageFormat: (type) => `Le format "${type}" n'est pas pris en charge par le navigateur. Veuillez le convertir en JPEG, PNG ou WebP.`,
3138
+ imageSize: (maxSize) => `Le fichier image est trop volumineux. La taille maximale autorisée est de ${maxSize}\u00a0Mo.`
3139
+ }
3140
+ },
3141
+ de: {
3142
+ toolbar: {
3143
+ bold: "Fett (Ctrl+B)",
3144
+ italic: "Kursiv (Ctrl+I)",
3145
+ underline: "Unterstrichen (Ctrl+U)",
3146
+ strikethrough: "Durchgestrichen",
3147
+ superscript: "Hochgestellt",
3148
+ subscript: "Tiefgestellt",
3149
+ alignLeft: "Linksbündig",
3150
+ alignCenter: "Zentriert",
3151
+ alignRight: "Rechtsbündig",
3152
+ alignJustify: "Blocksatz",
3153
+ ul: "Ungeordnete Liste",
3154
+ ol: "Geordnete Liste",
3155
+ checklist: "Checkliste",
3156
+ indent: "Einzug vergrößern",
3157
+ outdent: "Einzug verkleinern",
3158
+ undo: "Rückgängig (Ctrl+Z)",
3159
+ redo: "Wiederholen (Ctrl+Y)",
3160
+ hr: "Horizontale Linie",
3161
+ link: "Link einfügen",
3162
+ image: "Bild einfügen",
3163
+ video: "Video einfügen",
3164
+ emoji: "Emoji einfügen",
3165
+ icon: "FA-Symbol einfügen",
3166
+ table: "Tabelle einfügen",
3167
+ fontSize: "Schriftgröße",
3168
+ fontSizePlaceholder: "Größe",
3169
+ removeFormat: "Formatierung entfernen",
3170
+ direction: "Textrichtung umschalten (LTR / RTL)",
3171
+ fontFamily: "Schriftart",
3172
+ paragraphStyle: "Absatzstil",
3173
+ paragraphStylePlaceholder: "Stil",
3174
+ lineHeight: "Zeilenhöhe",
3175
+ lineHeightPlaceholder: "↕ Zeile",
3176
+ codeview: "HTML-Code-Ansicht",
3177
+ fullscreen: "Vollbild",
3178
+ shortcuts: "Tastenkürzel (Ctrl+Shift+/)",
3179
+ find: "Suchen (Ctrl+F)",
3180
+ findReplace: "Suchen & Ersetzen (Ctrl+H)",
3181
+ inlineCode: "Inline-Code (Ctrl+`)",
3182
+ print: "Drucken",
3183
+ foreColor: "Textfarbe",
3184
+ backColor: "Hervorhebungsfarbe",
3185
+ chooseTextColor: "Textfarbe auswählen",
3186
+ chooseHighlightColor: "Hervorhebungsfarbe auswählen",
3187
+ customColor: "Benutzerdefinierte Farbe",
3188
+ insertTableLabel: "Tabelle einfügen",
3189
+ paragraphItems: {
3190
+ p: "Normal",
3191
+ blockquote: "Zitat",
3192
+ pre: "Code"
3193
+ }
3194
+ },
3195
+ linkDialog: {
3196
+ ariaLabel: "Link einfügen",
3197
+ title: "Link einfügen",
3198
+ url: "URL",
3199
+ urlPlaceholder: "https://",
3200
+ displayText: "Anzeigetext",
3201
+ textPlaceholder: "Linktext",
3202
+ openInNewTab: "In neuem Tab öffnen",
3203
+ insertBtn: "Einfügen",
3204
+ cancelBtn: "Abbrechen"
3205
+ },
3206
+ imageDialog: {
3207
+ ariaLabel: "Bild einfügen",
3208
+ title: "Bild einfügen",
3209
+ imageUrl: "Bild-URL",
3210
+ urlPlaceholder: "https://example.com/image.png",
3211
+ altText: "Alt-Text",
3212
+ altPlaceholder: "Bild beschreiben",
3213
+ alignment: "Ausrichtung",
3214
+ alignNone: "Keine",
3215
+ alignLeft: "Links",
3216
+ alignCenter: "Mitte",
3217
+ alignRight: "Rechts",
3218
+ uploadLabel: "Oder Datei hochladen",
3219
+ insertBtn: "Einfügen",
3220
+ cancelBtn: "Abbrechen"
3221
+ },
3222
+ videoDialog: {
3223
+ ariaLabel: "Video einfügen",
3224
+ title: "Video einfügen",
3225
+ videoUrl: "Video-URL",
3226
+ urlPlaceholder: "YouTube, Vimeo oder direkte .mp4-URL",
3227
+ widthLabel: "Breite (px)",
3228
+ widthPlaceholder: "560",
3229
+ insertBtn: "Einfügen",
3230
+ cancelBtn: "Abbrechen",
3231
+ detected: (type) => `Erkannt: ${type}`,
3232
+ unknownFormat: "Unbekanntes Format — direktes Video-Einbetten wird versucht",
3233
+ invalidUrl: "Ungültige URL — bitte geben Sie einen gültigen Videolink ein."
3234
+ },
3235
+ emojiDialog: {
3236
+ ariaLabel: "Emoji einfügen",
3237
+ title: "Emoji einfügen",
3238
+ searchPlaceholder: "Emojis suchen…",
3239
+ all: "Alle",
3240
+ cancelBtn: "Abbrechen",
3241
+ close: "Schließen",
3242
+ categories: {
3243
+ smileys: "Smileys",
3244
+ people: "Menschen",
3245
+ animals: "Tiere",
3246
+ food: "Essen",
3247
+ travel: "Reisen",
3248
+ objects: "Objekte",
3249
+ symbols: "Symbole"
3250
+ }
3251
+ },
3252
+ iconDialog: {
3253
+ ariaLabel: "FA-Symbol einfügen",
3254
+ title: "FA-Symbol einfügen",
3255
+ searchPlaceholder: "Symbole suchen…",
3256
+ all: "Alle",
3257
+ style: "Stil",
3258
+ size: "Größe",
3259
+ color: "Farbe",
3260
+ useColor: " Farbe verwenden",
3261
+ selectHint: "Symbol auswählen",
3262
+ insertBtn: "FA-Symbol einfügen",
3263
+ cancelBtn: "Abbrechen",
3264
+ close: "Schließen",
3265
+ categories: {
3266
+ popular: "Beliebt",
3267
+ interface: "Benutzeroberfläche",
3268
+ navigation: "Navigation",
3269
+ media: "Medien",
3270
+ communication: "Kommunikation",
3271
+ files: "Dateien",
3272
+ people: "Menschen",
3273
+ objects: "Objekte"
3274
+ }
3275
+ },
3276
+ findReplace: {
3277
+ findTitle: "Suchen",
3278
+ findReplaceTitle: "Suchen & Ersetzen",
3279
+ findPlaceholder: "Suchen…",
3280
+ searchAriaLabel: "Suchtext",
3281
+ caseSensitive: "\xA0Groß-/Kleinschreibung",
3282
+ prevBtn: "← Zurück",
3283
+ nextBtn: "Weiter →",
3284
+ replacePlaceholder: "Ersetzen durch…",
3285
+ replaceAriaLabel: "Ersetzen durch",
3286
+ replaceBtn: "Ersetzen",
3287
+ replaceAllBtn: "Alle ersetzen",
3288
+ close: "×"
3289
+ },
3290
+ shortcutsDialog: {
3291
+ title: "Tastenkürzel",
3292
+ ariaLabel: "Tastenkürzel",
3293
+ close: "Schließen",
3294
+ shortcuts: [
3295
+ {
3296
+ category: "Textformatierung",
3297
+ items: [
3298
+ {
3299
+ keys: "Ctrl + B",
3300
+ action: "Fett"
3301
+ },
3302
+ {
3303
+ keys: "Ctrl + I",
3304
+ action: "Kursiv"
3305
+ },
3306
+ {
3307
+ keys: "Ctrl + U",
3308
+ action: "Unterstrichen"
3309
+ },
3310
+ {
3311
+ keys: "Ctrl + K",
3312
+ action: "Link einfügen / bearbeiten"
3313
+ }
3314
+ ]
3315
+ },
3316
+ {
3317
+ category: "Verlauf",
3318
+ items: [{
3319
+ keys: "Ctrl + Z",
3320
+ action: "Rückgängig"
3321
+ }, {
3322
+ keys: "Ctrl + Y / Ctrl + Shift + Z",
3323
+ action: "Wiederholen"
3324
+ }]
3325
+ },
3326
+ {
3327
+ category: "Auswahl & Navigation",
3328
+ items: [
3329
+ {
3330
+ keys: "Ctrl + A",
3331
+ action: "Alles auswählen"
3332
+ },
3333
+ {
3334
+ keys: "Tab",
3335
+ action: "Listenebene erhöhen / Leerzeichen einfügen"
3336
+ },
3337
+ {
3338
+ keys: "Shift + Tab",
3339
+ action: "Listenebene verringern"
3340
+ }
3341
+ ]
3342
+ },
3343
+ {
3344
+ category: "Zwischenablage",
3345
+ items: [{
3346
+ keys: "Ctrl + Shift + V",
3347
+ action: "Als einfachen Text einfügen"
3348
+ }]
3349
+ },
3350
+ {
3351
+ category: "Suchen & Ersetzen",
3352
+ items: [{
3353
+ keys: "Ctrl + F",
3354
+ action: "Im Dokument suchen"
3355
+ }, {
3356
+ keys: "Ctrl + H",
3357
+ action: "Suchen & Ersetzen"
3358
+ }]
3359
+ },
3360
+ {
3361
+ category: "Editor",
3362
+ items: [{
3363
+ keys: "Ctrl + Shift + /",
3364
+ action: "Diesen Tastenkürzel-Dialog anzeigen"
3365
+ }]
3366
+ }
3367
+ ]
3368
+ },
3369
+ contextMenu: {
3370
+ cut: "Ausschneiden",
3371
+ copy: "Kopieren",
3372
+ paste: "Einfügen",
3373
+ bold: "Fett",
3374
+ italic: "Kursiv",
3375
+ underline: "Unterstrichen",
3376
+ textColor: "Textfarbe",
3377
+ highlightColor: "Hervorhebungsfarbe",
3378
+ copyFormat: "Format kopieren",
3379
+ pasteFormat: "Format einfügen",
3380
+ removeFormat: "Formatierung entfernen",
3381
+ link: "Link einfügen",
3382
+ image: "Bild einfügen",
3383
+ video: "Video einfügen",
3384
+ table: "Tabelle einfügen",
3385
+ back: "Zurück",
3386
+ noHighlight: "Keine Hervorhebung",
3387
+ customColor: "Benutzerdefinierte Farbe",
3388
+ customColorLabel: "Benutzerdefiniert…"
3389
+ },
3390
+ statusbar: {
3391
+ resizeHandle: "Editor-Größe ändern",
3392
+ words: (n) => `Wörter: ${n}`,
3393
+ wordsLimit: (n, max) => `Wörter: ${n}/${max}`,
3394
+ chars: (n) => `Zeichen: ${n}`,
3395
+ charsLimit: (n, max) => `Zeichen: ${n}/${max}`
3396
+ },
3397
+ tooltips: {
3398
+ link: {
3399
+ ariaLabel: "Link-Aktionen",
3400
+ openLink: "Link öffnen",
3401
+ copyUrl: "URL kopieren",
3402
+ editLink: "Link bearbeiten",
3403
+ removeLink: "Link entfernen"
3404
+ },
3405
+ image: {
3406
+ ariaLabel: "Bild-Aktionen",
3407
+ label: "Bild",
3408
+ floatLeft: "Links umfließen",
3409
+ noFloat: "Kein Umfluss",
3410
+ alignCenter: "Zentriert",
3411
+ floatRight: "Rechts umfließen",
3412
+ originalSize: "Originalgröße",
3413
+ rotateLeft: "Links drehen",
3414
+ rotateRight: "Rechts drehen",
3415
+ cropImage: "Bild zuschneiden",
3416
+ addCaption: "Beschriftung hinzufügen / bearbeiten",
3417
+ deleteImage: "Bild löschen"
3418
+ },
3419
+ code: {
3420
+ ariaLabel: "Codeblock-Aktionen",
3421
+ label: "Code",
3422
+ syntaxLanguage: "Syntaxsprache",
3423
+ syntaxAriaLabel: "Syntaxsprache",
3424
+ copyCode: "Code kopieren",
3425
+ toggleWordWrap: "Zeilenumbruch umschalten",
3426
+ enableWordWrap: "Zeilenumbruch aktivieren",
3427
+ disableWordWrap: "Zeilenumbruch deaktivieren",
3428
+ convertToParagraph: "In Absatz umwandeln",
3429
+ deleteCodeBlock: "Codeblock löschen"
3430
+ },
3431
+ table: {
3432
+ ariaLabel: "Tabellen-Aktionen",
3433
+ label: "Tabelle",
3434
+ selectCells: "Zellen auswählen",
3435
+ addRowAbove: "Zeile oben hinzufügen",
3436
+ addRowBelow: "Zeile unten hinzufügen",
3437
+ deleteRow: "Zeile löschen",
3438
+ addColumnLeft: "Spalte links hinzufügen",
3439
+ addColumnRight: "Spalte rechts hinzufügen",
3440
+ deleteColumn: "Spalte löschen",
3441
+ mergeCells: "Zellen zusammenführen",
3442
+ unmergeCells: "Zellen trennen",
3443
+ columnWidth: "Spaltenbreite",
3444
+ rowHeight: "Zeilenhöhe",
3445
+ tableBorderWidth: "Tabellenrahmenbreite",
3446
+ deleteTable: "Tabelle löschen",
3447
+ columnWidthPx: "Spaltenbreite (px)",
3448
+ rowHeightPx: "Zeilenhöhe (px)",
3449
+ tableBorderWidthPx: "Tabellenrahmenbreite (px)",
3450
+ cancelBtn: "Abbrechen",
3451
+ applyBtn: "Anwenden"
3452
+ },
3453
+ video: {
3454
+ ariaLabel: "Video-Aktionen",
3455
+ label: "Video",
3456
+ floatLeft: "Links umfließen",
3457
+ noFloat: "Kein Umfluss",
3458
+ alignCenter: "Zentriert",
3459
+ floatRight: "Rechts umfließen",
3460
+ originalSize: "Originalgröße",
3461
+ previewVideo: "Video-Vorschau",
3462
+ exitPreview: "Vorschau beenden",
3463
+ deleteVideo: "Video löschen"
3464
+ }
3465
+ },
3466
+ errors: {
3467
+ imageFormat: (type) => `Das Format „${type}" wird in Webbrowsern nicht unterstützt. Bitte konvertieren Sie es zuerst in JPEG, PNG oder WebP.`,
3468
+ imageSize: (maxSize) => `Die Bilddatei ist zu groß. Die maximal zulässige Größe beträgt ${maxSize} MB.`
3469
+ }
3470
+ },
3471
+ es: {
3472
+ toolbar: {
3473
+ bold: "Negrita (Ctrl+B)",
3474
+ italic: "Cursiva (Ctrl+I)",
3475
+ underline: "Subrayado (Ctrl+U)",
3476
+ strikethrough: "Tachado",
3477
+ superscript: "Superíndice",
3478
+ subscript: "Subíndice",
3479
+ alignLeft: "Alinear a la izquierda",
3480
+ alignCenter: "Centrar",
3481
+ alignRight: "Alinear a la derecha",
3482
+ alignJustify: "Justificar",
3483
+ ul: "Lista sin orden",
3484
+ ol: "Lista ordenada",
3485
+ checklist: "Lista de verificación",
3486
+ indent: "Aumentar sangría",
3487
+ outdent: "Reducir sangría",
3488
+ undo: "Deshacer (Ctrl+Z)",
3489
+ redo: "Rehacer (Ctrl+Y)",
3490
+ hr: "Línea horizontal",
3491
+ link: "Insertar enlace",
3492
+ image: "Insertar imagen",
3493
+ video: "Insertar vídeo",
3494
+ emoji: "Insertar emoji",
3495
+ icon: "Insertar icono FA",
3496
+ table: "Insertar tabla",
3497
+ fontSize: "Tamaño de fuente",
3498
+ fontSizePlaceholder: "Tamaño",
3499
+ removeFormat: "Eliminar formato",
3500
+ direction: "Cambiar dirección del texto (LTR / RTL)",
3501
+ fontFamily: "Fuente",
3502
+ paragraphStyle: "Estilo de párrafo",
3503
+ paragraphStylePlaceholder: "Estilo",
3504
+ lineHeight: "Interlineado",
3505
+ lineHeightPlaceholder: "↕ Línea",
3506
+ codeview: "Vista de código HTML",
3507
+ fullscreen: "Pantalla completa",
3508
+ shortcuts: "Atajos de teclado (Ctrl+Shift+/)",
3509
+ find: "Buscar (Ctrl+F)",
3510
+ findReplace: "Buscar y reemplazar (Ctrl+H)",
3511
+ inlineCode: "Código en línea (Ctrl+`)",
3512
+ print: "Imprimir",
3513
+ foreColor: "Color del texto",
3514
+ backColor: "Color de resaltado",
3515
+ chooseTextColor: "Elegir color del texto",
3516
+ chooseHighlightColor: "Elegir color de resaltado",
3517
+ customColor: "Color personalizado",
3518
+ insertTableLabel: "Insertar tabla",
3519
+ paragraphItems: {
3520
+ p: "Normal",
3521
+ blockquote: "Cita",
3522
+ pre: "Código"
3523
+ }
3524
+ },
3525
+ linkDialog: {
3526
+ ariaLabel: "Insertar enlace",
3527
+ title: "Insertar enlace",
3528
+ url: "URL",
3529
+ urlPlaceholder: "https://",
3530
+ displayText: "Texto a mostrar",
3531
+ textPlaceholder: "Texto del enlace",
3532
+ openInNewTab: "Abrir en nueva pestaña",
3533
+ insertBtn: "Insertar",
3534
+ cancelBtn: "Cancelar"
3535
+ },
3536
+ imageDialog: {
3537
+ ariaLabel: "Insertar imagen",
3538
+ title: "Insertar imagen",
3539
+ imageUrl: "URL de imagen",
3540
+ urlPlaceholder: "https://example.com/image.png",
3541
+ altText: "Texto alternativo",
3542
+ altPlaceholder: "Describir la imagen",
3543
+ alignment: "Alineación",
3544
+ alignNone: "Ninguna",
3545
+ alignLeft: "Izquierda",
3546
+ alignCenter: "Centro",
3547
+ alignRight: "Derecha",
3548
+ uploadLabel: "O subir un archivo",
3549
+ insertBtn: "Insertar",
3550
+ cancelBtn: "Cancelar"
3551
+ },
3552
+ videoDialog: {
3553
+ ariaLabel: "Insertar vídeo",
3554
+ title: "Insertar vídeo",
3555
+ videoUrl: "URL del vídeo",
3556
+ urlPlaceholder: "YouTube, Vimeo o URL directa .mp4",
3557
+ widthLabel: "Ancho (px)",
3558
+ widthPlaceholder: "560",
3559
+ insertBtn: "Insertar",
3560
+ cancelBtn: "Cancelar",
3561
+ detected: (type) => `Detectado: ${type}`,
3562
+ unknownFormat: "Formato desconocido — se intentará incrustar el vídeo directamente",
3563
+ invalidUrl: "URL no válida — introduzca un enlace de vídeo válido."
3564
+ },
3565
+ emojiDialog: {
3566
+ ariaLabel: "Insertar emoji",
3567
+ title: "Insertar emoji",
3568
+ searchPlaceholder: "Buscar emojis…",
3569
+ all: "Todos",
3570
+ cancelBtn: "Cancelar",
3571
+ close: "Cerrar",
3572
+ categories: {
3573
+ smileys: "Caritas",
3574
+ people: "Personas",
3575
+ animals: "Animales",
3576
+ food: "Comida",
3577
+ travel: "Viajes",
3578
+ objects: "Objetos",
3579
+ symbols: "Símbolos"
3580
+ }
3581
+ },
3582
+ iconDialog: {
3583
+ ariaLabel: "Insertar icono FA",
3584
+ title: "Insertar icono FA",
3585
+ searchPlaceholder: "Buscar iconos…",
3586
+ all: "Todos",
3587
+ style: "Estilo",
3588
+ size: "Tamaño",
3589
+ color: "Color",
3590
+ useColor: " Usar color",
3591
+ selectHint: "Seleccionar un icono",
3592
+ insertBtn: "Insertar icono FA",
3593
+ cancelBtn: "Cancelar",
3594
+ close: "Cerrar",
3595
+ categories: {
3596
+ popular: "Popular",
3597
+ interface: "Interfaz",
3598
+ navigation: "Navegación",
3599
+ media: "Medios",
3600
+ communication: "Comunicación",
3601
+ files: "Archivos",
3602
+ people: "Personas",
3603
+ objects: "Objetos"
3604
+ }
3605
+ },
3606
+ findReplace: {
3607
+ findTitle: "Buscar",
3608
+ findReplaceTitle: "Buscar y reemplazar",
3609
+ findPlaceholder: "Buscar…",
3610
+ searchAriaLabel: "Texto de búsqueda",
3611
+ caseSensitive: "\xA0Distinguir mayúsculas",
3612
+ prevBtn: "← Anterior",
3613
+ nextBtn: "Siguiente →",
3614
+ replacePlaceholder: "Reemplazar con…",
3615
+ replaceAriaLabel: "Reemplazar con",
3616
+ replaceBtn: "Reemplazar",
3617
+ replaceAllBtn: "Reemplazar todo",
3618
+ close: "×"
3619
+ },
3620
+ shortcutsDialog: {
3621
+ title: "Atajos de teclado",
3622
+ ariaLabel: "Atajos de teclado",
3623
+ close: "Cerrar",
3624
+ shortcuts: [
3625
+ {
3626
+ category: "Formato de texto",
3627
+ items: [
3628
+ {
3629
+ keys: "Ctrl + B",
3630
+ action: "Negrita"
3631
+ },
3632
+ {
3633
+ keys: "Ctrl + I",
3634
+ action: "Cursiva"
3635
+ },
3636
+ {
3637
+ keys: "Ctrl + U",
3638
+ action: "Subrayado"
3639
+ },
3640
+ {
3641
+ keys: "Ctrl + K",
3642
+ action: "Insertar / editar enlace"
3643
+ }
3644
+ ]
3645
+ },
3646
+ {
3647
+ category: "Historial",
3648
+ items: [{
3649
+ keys: "Ctrl + Z",
3650
+ action: "Deshacer"
3651
+ }, {
3652
+ keys: "Ctrl + Y / Ctrl + Shift + Z",
3653
+ action: "Rehacer"
3654
+ }]
3655
+ },
3656
+ {
3657
+ category: "Selección y navegación",
3658
+ items: [
3659
+ {
3660
+ keys: "Ctrl + A",
3661
+ action: "Seleccionar todo"
3662
+ },
3663
+ {
3664
+ keys: "Tab",
3665
+ action: "Aumentar sangría / insertar espacios"
3666
+ },
3667
+ {
3668
+ keys: "Shift + Tab",
3669
+ action: "Reducir sangría"
3670
+ }
3671
+ ]
3672
+ },
3673
+ {
3674
+ category: "Portapapeles",
3675
+ items: [{
3676
+ keys: "Ctrl + Shift + V",
3677
+ action: "Pegar como texto plano"
3678
+ }]
3679
+ },
3680
+ {
3681
+ category: "Buscar y reemplazar",
3682
+ items: [{
3683
+ keys: "Ctrl + F",
3684
+ action: "Buscar en el documento"
3685
+ }, {
3686
+ keys: "Ctrl + H",
3687
+ action: "Buscar y reemplazar"
3688
+ }]
3689
+ },
3690
+ {
3691
+ category: "Editor",
3692
+ items: [{
3693
+ keys: "Ctrl + Shift + /",
3694
+ action: "Mostrar este diálogo de atajos"
3695
+ }]
3696
+ }
3697
+ ]
3698
+ },
3699
+ contextMenu: {
3700
+ cut: "Cortar",
3701
+ copy: "Copiar",
3702
+ paste: "Pegar",
3703
+ bold: "Negrita",
3704
+ italic: "Cursiva",
3705
+ underline: "Subrayado",
3706
+ textColor: "Color del texto",
3707
+ highlightColor: "Color de resaltado",
3708
+ copyFormat: "Copiar formato",
3709
+ pasteFormat: "Pegar formato",
3710
+ removeFormat: "Eliminar formato",
3711
+ link: "Insertar enlace",
3712
+ image: "Insertar imagen",
3713
+ video: "Insertar vídeo",
3714
+ table: "Insertar tabla",
3715
+ back: "Atrás",
3716
+ noHighlight: "Sin resaltado",
3717
+ customColor: "Color personalizado",
3718
+ customColorLabel: "Personalizado…"
3719
+ },
3720
+ statusbar: {
3721
+ resizeHandle: "Redimensionar editor",
3722
+ words: (n) => `Palabras: ${n}`,
3723
+ wordsLimit: (n, max) => `Palabras: ${n}/${max}`,
3724
+ chars: (n) => `Caracteres: ${n}`,
3725
+ charsLimit: (n, max) => `Caracteres: ${n}/${max}`
3726
+ },
3727
+ tooltips: {
3728
+ link: {
3729
+ ariaLabel: "Acciones de enlace",
3730
+ openLink: "Abrir enlace",
3731
+ copyUrl: "Copiar URL",
3732
+ editLink: "Editar enlace",
3733
+ removeLink: "Eliminar enlace"
3734
+ },
3735
+ image: {
3736
+ ariaLabel: "Acciones de imagen",
3737
+ label: "Imagen",
3738
+ floatLeft: "Flotar a la izquierda",
3739
+ noFloat: "Sin flotado",
3740
+ alignCenter: "Centrar",
3741
+ floatRight: "Flotar a la derecha",
3742
+ originalSize: "Tamaño original",
3743
+ rotateLeft: "Rotar a la izquierda",
3744
+ rotateRight: "Rotar a la derecha",
3745
+ cropImage: "Recortar imagen",
3746
+ addCaption: "Añadir / editar pie de foto",
3747
+ deleteImage: "Eliminar imagen"
3748
+ },
3749
+ code: {
3750
+ ariaLabel: "Acciones de bloque de código",
3751
+ label: "Código",
3752
+ syntaxLanguage: "Lenguaje de sintaxis",
3753
+ syntaxAriaLabel: "Lenguaje de sintaxis",
3754
+ copyCode: "Copiar código",
3755
+ toggleWordWrap: "Alternar ajuste de línea",
3756
+ enableWordWrap: "Activar ajuste de línea",
3757
+ disableWordWrap: "Desactivar ajuste de línea",
3758
+ convertToParagraph: "Convertir en párrafo",
3759
+ deleteCodeBlock: "Eliminar bloque de código"
3760
+ },
3761
+ table: {
3762
+ ariaLabel: "Acciones de tabla",
3763
+ label: "Tabla",
3764
+ selectCells: "Seleccionar celdas",
3765
+ addRowAbove: "Añadir fila encima",
3766
+ addRowBelow: "Añadir fila debajo",
3767
+ deleteRow: "Eliminar fila",
3768
+ addColumnLeft: "Añadir columna a la izquierda",
3769
+ addColumnRight: "Añadir columna a la derecha",
3770
+ deleteColumn: "Eliminar columna",
3771
+ mergeCells: "Combinar celdas",
3772
+ unmergeCells: "Separar celdas",
3773
+ columnWidth: "Ancho de columna",
3774
+ rowHeight: "Alto de fila",
3775
+ tableBorderWidth: "Grosor del borde de la tabla",
3776
+ deleteTable: "Eliminar tabla",
3777
+ columnWidthPx: "Ancho de columna (px)",
3778
+ rowHeightPx: "Alto de fila (px)",
3779
+ tableBorderWidthPx: "Grosor del borde (px)",
3780
+ cancelBtn: "Cancelar",
3781
+ applyBtn: "Aplicar"
3782
+ },
3783
+ video: {
3784
+ ariaLabel: "Acciones de vídeo",
3785
+ label: "Vídeo",
3786
+ floatLeft: "Flotar a la izquierda",
3787
+ noFloat: "Sin flotado",
3788
+ alignCenter: "Centrar",
3789
+ floatRight: "Flotar a la derecha",
3790
+ originalSize: "Tamaño original",
3791
+ previewVideo: "Vista previa de vídeo",
3792
+ exitPreview: "Salir de la vista previa",
3793
+ deleteVideo: "Eliminar vídeo"
3794
+ }
3795
+ },
3796
+ errors: {
3797
+ imageFormat: (type) => `El formato "${type}" no es compatible con los navegadores web. Por favor, conviértalo primero a JPEG, PNG o WebP.`,
3798
+ imageSize: (maxSize) => `El archivo de imagen es demasiado grande. El tamaño máximo permitido es ${maxSize} MB.`
3799
+ }
3800
+ },
3801
+ ko: {
3802
+ toolbar: {
3803
+ bold: "굵게 (Ctrl+B)",
3804
+ italic: "기울임꼴 (Ctrl+I)",
3805
+ underline: "밑줄 (Ctrl+U)",
3806
+ strikethrough: "취소선",
3807
+ superscript: "위 첨자",
3808
+ subscript: "아래 첨자",
3809
+ alignLeft: "왼쪽 정렬",
3810
+ alignCenter: "가운데 정렬",
3811
+ alignRight: "오른쪽 정렬",
3812
+ alignJustify: "양쪽 정렬",
3813
+ ul: "순서 없는 목록",
3814
+ ol: "순서 있는 목록",
3815
+ checklist: "체크리스트",
3816
+ indent: "들여쓰기",
3817
+ outdent: "내어쓰기",
3818
+ undo: "실행 취소 (Ctrl+Z)",
3819
+ redo: "다시 실행 (Ctrl+Y)",
3820
+ hr: "수평선",
3821
+ link: "링크 삽입",
3822
+ image: "이미지 삽입",
3823
+ video: "동영상 삽입",
3824
+ emoji: "이모지 삽입",
3825
+ icon: "FA 아이콘 삽입",
3826
+ table: "표 삽입",
3827
+ fontSize: "글꼴 크기",
3828
+ fontSizePlaceholder: "크기",
3829
+ removeFormat: "서식 제거",
3830
+ direction: "텍스트 방향 전환 (LTR / RTL)",
3831
+ fontFamily: "글꼴",
3832
+ paragraphStyle: "단락 스타일",
3833
+ paragraphStylePlaceholder: "스타일",
3834
+ lineHeight: "줄 간격",
3835
+ lineHeightPlaceholder: "↕ 줄",
3836
+ codeview: "HTML 코드 보기",
3837
+ fullscreen: "전체 화면",
3838
+ shortcuts: "키보드 단축키 (Ctrl+Shift+/)",
3839
+ find: "찾기 (Ctrl+F)",
3840
+ findReplace: "찾기 및 바꾸기 (Ctrl+H)",
3841
+ inlineCode: "인라인 코드 (Ctrl+`)",
3842
+ print: "인쇄",
3843
+ foreColor: "글자 색",
3844
+ backColor: "강조 색",
3845
+ chooseTextColor: "글자 색 선택",
3846
+ chooseHighlightColor: "강조 색 선택",
3847
+ customColor: "사용자 지정 색",
3848
+ insertTableLabel: "표 삽입",
3849
+ paragraphItems: {
3850
+ p: "기본",
3851
+ blockquote: "인용",
3852
+ pre: "코드"
3853
+ }
3854
+ },
3855
+ linkDialog: {
3856
+ ariaLabel: "링크 삽입",
3857
+ title: "링크 삽입",
3858
+ url: "URL",
3859
+ urlPlaceholder: "https://",
3860
+ displayText: "표시 텍스트",
3861
+ textPlaceholder: "링크 텍스트",
3862
+ openInNewTab: "새 탭에서 열기",
3863
+ insertBtn: "삽입",
3864
+ cancelBtn: "취소"
3865
+ },
3866
+ imageDialog: {
3867
+ ariaLabel: "이미지 삽입",
3868
+ title: "이미지 삽입",
3869
+ imageUrl: "이미지 URL",
3870
+ urlPlaceholder: "https://example.com/image.png",
3871
+ altText: "대체 텍스트",
3872
+ altPlaceholder: "이미지 설명",
3873
+ alignment: "정렬",
3874
+ alignNone: "없음",
3875
+ alignLeft: "왼쪽",
3876
+ alignCenter: "가운데",
3877
+ alignRight: "오른쪽",
3878
+ uploadLabel: "또는 파일 업로드",
3879
+ insertBtn: "삽입",
3880
+ cancelBtn: "취소"
3881
+ },
3882
+ videoDialog: {
3883
+ ariaLabel: "동영상 삽입",
3884
+ title: "동영상 삽입",
3885
+ videoUrl: "동영상 URL",
3886
+ urlPlaceholder: "YouTube, Vimeo 또는 직접 .mp4 URL",
3887
+ widthLabel: "너비 (px)",
3888
+ widthPlaceholder: "560",
3889
+ insertBtn: "삽입",
3890
+ cancelBtn: "취소",
3891
+ detected: (type) => `감지됨: ${type}`,
3892
+ unknownFormat: "알 수 없는 형식 — 직접 동영상 임베드를 시도합니다",
3893
+ invalidUrl: "유효하지 않은 URL — 올바른 동영상 링크를 입력하세요."
3894
+ },
3895
+ emojiDialog: {
3896
+ ariaLabel: "이모지 삽입",
3897
+ title: "이모지 삽입",
3898
+ searchPlaceholder: "이모지 검색…",
3899
+ all: "전체",
3900
+ cancelBtn: "취소",
3901
+ close: "닫기",
3902
+ categories: {
3903
+ smileys: "스마일",
3904
+ people: "사람",
3905
+ animals: "동물",
3906
+ food: "음식",
3907
+ travel: "여행",
3908
+ objects: "사물",
3909
+ symbols: "기호"
3910
+ }
3911
+ },
3912
+ iconDialog: {
3913
+ ariaLabel: "FA 아이콘 삽입",
3914
+ title: "FA 아이콘 삽입",
3915
+ searchPlaceholder: "아이콘 검색…",
3916
+ all: "전체",
3917
+ style: "스타일",
3918
+ size: "크기",
3919
+ color: "색상",
3920
+ useColor: " 색상 사용",
3921
+ selectHint: "아이콘을 선택하세요",
3922
+ insertBtn: "FA 아이콘 삽입",
3923
+ cancelBtn: "취소",
3924
+ close: "닫기",
3925
+ categories: {
3926
+ popular: "인기",
3927
+ interface: "인터페이스",
3928
+ navigation: "탐색",
3929
+ media: "미디어",
3930
+ communication: "커뮤니케이션",
3931
+ files: "파일",
3932
+ people: "사람",
3933
+ objects: "사물"
3934
+ }
3935
+ },
3936
+ findReplace: {
3937
+ findTitle: "찾기",
3938
+ findReplaceTitle: "찾기 및 바꾸기",
3939
+ findPlaceholder: "찾기…",
3940
+ searchAriaLabel: "검색 텍스트",
3941
+ caseSensitive: "\xA0대소문자 구분",
3942
+ prevBtn: "← 이전",
3943
+ nextBtn: "다음 →",
3944
+ replacePlaceholder: "바꿀 내용…",
3945
+ replaceAriaLabel: "바꿀 내용",
3946
+ replaceBtn: "바꾸기",
3947
+ replaceAllBtn: "모두 바꾸기",
3948
+ close: "×"
3949
+ },
3950
+ shortcutsDialog: {
3951
+ title: "키보드 단축키",
3952
+ ariaLabel: "키보드 단축키",
3953
+ close: "닫기",
3954
+ shortcuts: [
3955
+ {
3956
+ category: "텍스트 서식",
3957
+ items: [
3958
+ {
3959
+ keys: "Ctrl + B",
3960
+ action: "굵게"
3961
+ },
3962
+ {
3963
+ keys: "Ctrl + I",
3964
+ action: "기울임꼴"
3965
+ },
3966
+ {
3967
+ keys: "Ctrl + U",
3968
+ action: "밑줄"
3969
+ },
3970
+ {
3971
+ keys: "Ctrl + K",
3972
+ action: "링크 삽입 / 편집"
3973
+ }
3974
+ ]
3975
+ },
3976
+ {
3977
+ category: "실행 기록",
3978
+ items: [{
3979
+ keys: "Ctrl + Z",
3980
+ action: "실행 취소"
3981
+ }, {
3982
+ keys: "Ctrl + Y / Ctrl + Shift + Z",
3983
+ action: "다시 실행"
3984
+ }]
3985
+ },
3986
+ {
3987
+ category: "선택 및 탐색",
3988
+ items: [
3989
+ {
3990
+ keys: "Ctrl + A",
3991
+ action: "전체 선택"
3992
+ },
3993
+ {
3994
+ keys: "Tab",
3995
+ action: "목록 들여쓰기 / 공백 삽입"
3996
+ },
3997
+ {
3998
+ keys: "Shift + Tab",
3999
+ action: "목록 내어쓰기"
4000
+ }
4001
+ ]
4002
+ },
4003
+ {
4004
+ category: "클립보드",
4005
+ items: [{
4006
+ keys: "Ctrl + Shift + V",
4007
+ action: "일반 텍스트로 붙여넣기"
4008
+ }]
4009
+ },
4010
+ {
4011
+ category: "찾기 및 바꾸기",
4012
+ items: [{
4013
+ keys: "Ctrl + F",
4014
+ action: "문서에서 찾기"
4015
+ }, {
4016
+ keys: "Ctrl + H",
4017
+ action: "찾기 및 바꾸기"
4018
+ }]
4019
+ },
4020
+ {
4021
+ category: "편집기",
4022
+ items: [{
4023
+ keys: "Ctrl + Shift + /",
4024
+ action: "이 단축키 대화상자 표시"
4025
+ }]
4026
+ }
4027
+ ]
4028
+ },
4029
+ contextMenu: {
4030
+ cut: "잘라내기",
4031
+ copy: "복사",
4032
+ paste: "붙여넣기",
4033
+ bold: "굵게",
4034
+ italic: "기울임꼴",
4035
+ underline: "밑줄",
4036
+ textColor: "글자 색",
4037
+ highlightColor: "강조 색",
4038
+ copyFormat: "서식 복사",
4039
+ pasteFormat: "서식 붙여넣기",
4040
+ removeFormat: "서식 제거",
4041
+ link: "링크 삽입",
4042
+ image: "이미지 삽입",
4043
+ video: "동영상 삽입",
4044
+ table: "표 삽입",
4045
+ back: "뒤로",
4046
+ noHighlight: "강조 없음",
4047
+ customColor: "사용자 지정 색",
4048
+ customColorLabel: "사용자 지정…"
4049
+ },
4050
+ statusbar: {
4051
+ resizeHandle: "편집기 크기 조정",
4052
+ words: (n) => `단어: ${n}`,
4053
+ wordsLimit: (n, max) => `단어: ${n}/${max}`,
4054
+ chars: (n) => `글자: ${n}`,
4055
+ charsLimit: (n, max) => `글자: ${n}/${max}`
4056
+ },
4057
+ tooltips: {
4058
+ link: {
4059
+ ariaLabel: "링크 작업",
4060
+ openLink: "링크 열기",
4061
+ copyUrl: "URL 복사",
4062
+ editLink: "링크 편집",
4063
+ removeLink: "링크 제거"
4064
+ },
4065
+ image: {
4066
+ ariaLabel: "이미지 작업",
4067
+ label: "이미지",
4068
+ floatLeft: "왼쪽 배치",
4069
+ noFloat: "배치 없음",
4070
+ alignCenter: "가운데 정렬",
4071
+ floatRight: "오른쪽 배치",
4072
+ originalSize: "원본 크기",
4073
+ rotateLeft: "왼쪽 회전",
4074
+ rotateRight: "오른쪽 회전",
4075
+ cropImage: "이미지 자르기",
4076
+ addCaption: "캡션 추가 / 편집",
4077
+ deleteImage: "이미지 삭제"
4078
+ },
4079
+ code: {
4080
+ ariaLabel: "코드 블록 작업",
4081
+ label: "코드",
4082
+ syntaxLanguage: "구문 언어",
4083
+ syntaxAriaLabel: "구문 언어",
4084
+ copyCode: "코드 복사",
4085
+ toggleWordWrap: "줄 바꿈 전환",
4086
+ enableWordWrap: "줄 바꿈 사용",
4087
+ disableWordWrap: "줄 바꿈 해제",
4088
+ convertToParagraph: "단락으로 변환",
4089
+ deleteCodeBlock: "코드 블록 삭제"
4090
+ },
4091
+ table: {
4092
+ ariaLabel: "표 작업",
4093
+ label: "표",
4094
+ selectCells: "셀 선택",
4095
+ addRowAbove: "위에 행 추가",
4096
+ addRowBelow: "아래에 행 추가",
4097
+ deleteRow: "행 삭제",
4098
+ addColumnLeft: "왼쪽에 열 추가",
4099
+ addColumnRight: "오른쪽에 열 추가",
4100
+ deleteColumn: "열 삭제",
4101
+ mergeCells: "셀 병합",
4102
+ unmergeCells: "셀 분할",
4103
+ columnWidth: "열 너비",
4104
+ rowHeight: "행 높이",
4105
+ tableBorderWidth: "표 테두리 너비",
4106
+ deleteTable: "표 삭제",
4107
+ columnWidthPx: "열 너비 (px)",
4108
+ rowHeightPx: "행 높이 (px)",
4109
+ tableBorderWidthPx: "표 테두리 너비 (px)",
4110
+ cancelBtn: "취소",
4111
+ applyBtn: "적용"
4112
+ },
4113
+ video: {
4114
+ ariaLabel: "동영상 작업",
4115
+ label: "동영상",
4116
+ floatLeft: "왼쪽 배치",
4117
+ noFloat: "배치 없음",
4118
+ alignCenter: "가운데 정렬",
4119
+ floatRight: "오른쪽 배치",
4120
+ originalSize: "원본 크기",
4121
+ previewVideo: "동영상 미리보기",
4122
+ exitPreview: "미리보기 종료",
4123
+ deleteVideo: "동영상 삭제"
4124
+ }
4125
+ },
4126
+ errors: {
4127
+ imageFormat: (type) => `"${type}" 형식은 웹 브라우저에서 지원되지 않습니다. JPEG, PNG 또는 WebP로 변환해 주세요.`,
4128
+ imageSize: (maxSize) => `이미지 파일이 너무 큽니다. 최대 허용 크기는 ${maxSize} MB입니다.`
4129
+ }
4130
+ }
4131
+ };
4132
+ /**
4133
+ * Resolve a locale object from a lang option value.
4134
+ *
4135
+ * @param {string | Partial<AsnLocale> | null | undefined} lang
4136
+ * @returns {AsnLocale} A fully-populated locale (always contains every key from en.js).
4137
+ */
4138
+ function resolveLocale(lang) {
4139
+ if (!lang || lang === "en") return en;
4140
+ if (typeof lang === "string") {
4141
+ const partial = locales[lang];
4142
+ if (!partial) return en;
4143
+ return mergeDeep(mergeDeep({}, en), partial);
4144
+ }
4145
+ if (typeof lang === "object") return mergeDeep(mergeDeep({}, en), lang);
4146
+ return en;
4147
+ }
4148
+ /**
4149
+ * @typedef {Object} AsnLocale (see types/index.d.ts for the full definition)
4150
+ */
4151
+ //#endregion
1449
4152
  //#region src/js/core/sanitise.js
1450
4153
  /**
1451
4154
  * sanitise.js - Shared HTML and URL sanitisation utilities
@@ -2965,8 +5668,8 @@ var Editor = class {
2965
5668
  * Inspired by Summernote's Toolbar module — rewritten without jQuery
2966
5669
  */
2967
5670
  var _faPageLevelReady = null;
2968
- var _S = "stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"";
2969
- var _svgWrap = (paths) => `<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" ${_S} style="display:block">${paths}</svg>`;
5671
+ var _S$1 = "stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"";
5672
+ var _svgWrap = (paths) => `<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" ${_S$1} style="display:block">${paths}</svg>`;
2970
5673
  var _SVG_MAP = new Map([
2971
5674
  ["bold", _svgWrap("<path d=\"M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z\"/><path d=\"M6 12h9a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z\"/>")],
2972
5675
  ["italic", _svgWrap("<line x1=\"19\" y1=\"4\" x2=\"10\" y2=\"4\"/><line x1=\"14\" y1=\"20\" x2=\"5\" y2=\"20\"/><line x1=\"15\" y1=\"4\" x2=\"9\" y2=\"20\"/>")],
@@ -3096,9 +5799,9 @@ var Toolbar = class {
3096
5799
  const btn = createElement("button", {
3097
5800
  type: "button",
3098
5801
  class: !!this.options.useBootstrap ? this.options.toolbarButtonClass || "btn btn-sm btn-light" : "an-btn",
3099
- title: def.tooltip || "",
5802
+ title: this.context.locale.toolbar[def.name] || def.tooltip || "",
3100
5803
  "data-btn": def.name,
3101
- "aria-label": def.tooltip || def.name,
5804
+ "aria-label": this.context.locale.toolbar[def.name] || def.tooltip || def.name,
3102
5805
  "aria-haspopup": "true",
3103
5806
  "aria-expanded": "false"
3104
5807
  });
@@ -3111,7 +5814,7 @@ var Toolbar = class {
3111
5814
  });
3112
5815
  const grid = createElement("div", { class: "an-table-grid" });
3113
5816
  const label = createElement("div", { class: "an-table-label" });
3114
- label.textContent = "Insert Table";
5817
+ label.textContent = this.context.locale.toolbar.insertTableLabel || "Insert Table";
3115
5818
  const cells = [];
3116
5819
  for (let r = 1; r <= ROWS; r++) for (let c = 1; c <= COLS; c++) {
3117
5820
  const cell = createElement("div", {
@@ -3131,7 +5834,7 @@ var Toolbar = class {
3131
5834
  const c = +cell.getAttribute("data-col");
3132
5835
  cell.classList.toggle("active", r <= rows && c <= cols);
3133
5836
  });
3134
- label.textContent = rows && cols ? `${rows} × ${cols}` : "Insert Table";
5837
+ label.textContent = rows && cols ? `${rows} × ${cols}` : this.context.locale.toolbar.insertTableLabel || "Insert Table";
3135
5838
  };
3136
5839
  const openPopup = () => {
3137
5840
  isOpen = true;
@@ -3211,9 +5914,9 @@ var Toolbar = class {
3211
5914
  const applyBtn = createElement("button", {
3212
5915
  type: "button",
3213
5916
  class: `${baseClass} an-color-btn`,
3214
- title: def.tooltip || "",
5917
+ title: this.context.locale.toolbar[def.name] || def.tooltip || "",
3215
5918
  "data-btn": def.name,
3216
- "aria-label": def.tooltip || def.name
5919
+ "aria-label": this.context.locale.toolbar[def.name] || def.tooltip || def.name
3217
5920
  });
3218
5921
  const S = "stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"";
3219
5922
  applyBtn.innerHTML = def.name === "foreColor" ? `<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" ${S} style="display:block"><path d="M4 20L12 4L20 20"/><line x1="7.5" y1="14" x2="16.5" y2="14"/></svg>` : `<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" ${S} style="display:block"><path d="M3 21v-4l9-9 4 4-9 9z"/><path d="M12 8l4 4"/></svg>`;
@@ -3223,7 +5926,7 @@ var Toolbar = class {
3223
5926
  const arrowBtn = createElement("button", {
3224
5927
  type: "button",
3225
5928
  class: `${baseClass} an-color-arrow`,
3226
- title: `Choose ${def.name === "foreColor" ? "text" : "highlight"} color`,
5929
+ title: def.name === "foreColor" ? this.context.locale.toolbar.chooseTextColor || "Choose text color" : this.context.locale.toolbar.chooseHighlightColor || "Choose highlight color",
3227
5930
  "aria-haspopup": "true",
3228
5931
  "aria-expanded": "false"
3229
5932
  });
@@ -3245,9 +5948,9 @@ var Toolbar = class {
3245
5948
  const colorInput = createElement("input", {
3246
5949
  type: "color",
3247
5950
  value: currentColor,
3248
- title: "Custom color"
5951
+ title: this.context.locale.toolbar.customColor || "Custom color"
3249
5952
  });
3250
- const customLabel = createElement("span", {}, ["Custom color"]);
5953
+ const customLabel = createElement("span", {}, [this.context.locale.toolbar.customColor || "Custom color"]);
3251
5954
  customRow.appendChild(colorInput);
3252
5955
  customRow.appendChild(customLabel);
3253
5956
  popup.appendChild(swatches);
@@ -3357,19 +6060,19 @@ var Toolbar = class {
3357
6060
  const items = def.name === "fontFamily" ? this.options.fontFamilies || [] : def.items || [];
3358
6061
  const select = createElement("select", {
3359
6062
  class: def.selectClass ? `an-select ${def.selectClass}` : "an-select",
3360
- title: def.tooltip || "",
6063
+ title: this.context.locale.toolbar[def.name] || def.tooltip || "",
3361
6064
  "data-btn": def.name,
3362
- "aria-label": def.tooltip || def.name
6065
+ "aria-label": this.context.locale.toolbar[def.name] || def.tooltip || def.name
3363
6066
  });
3364
6067
  const placeholder = createElement("option", {
3365
6068
  value: "",
3366
6069
  disabled: "",
3367
6070
  hidden: ""
3368
- }, [def.placeholder || "Font"]);
6071
+ }, [this.context.locale.toolbar[def.name + "Placeholder"] || def.placeholder || "Font"]);
3369
6072
  select.appendChild(placeholder);
3370
6073
  items.forEach((item) => {
3371
6074
  const value = typeof item === "object" ? item.value : item;
3372
- const label = typeof item === "object" ? item.label : item;
6075
+ const label = typeof item === "object" ? def.name === "paragraphStyle" ? (this.context.locale.toolbar.paragraphItems || {})[item.value] || item.label : item.label : item;
3373
6076
  const isHeader = typeof item === "object" && !!item.disabled;
3374
6077
  const attrs = { value };
3375
6078
  if (isHeader) attrs.disabled = "";
@@ -3409,9 +6112,9 @@ var Toolbar = class {
3409
6112
  const btn = createElement("button", {
3410
6113
  type: "button",
3411
6114
  class: `${!!this.options.useBootstrap ? this.options.toolbarButtonClass || "btn btn-sm btn-light" : `an-btn`}${btnDef.className ? ` ${btnDef.className}` : ""}`,
3412
- title: btnDef.tooltip || "",
6115
+ title: this.context.locale.toolbar[btnDef.name] || btnDef.tooltip || "",
3413
6116
  "data-btn": btnDef.name,
3414
- "aria-label": btnDef.tooltip || btnDef.name
6117
+ "aria-label": this.context.locale.toolbar[btnDef.name] || btnDef.tooltip || btnDef.name
3415
6118
  });
3416
6119
  const faPrefix = this.options.fontAwesomeClass || "fas";
3417
6120
  if (this._faReady) {
@@ -3536,7 +6239,7 @@ var Statusbar = class {
3536
6239
  if (this.options.resizable !== false) {
3537
6240
  const handle = createElement("div", {
3538
6241
  class: "an-resize-handle",
3539
- title: "Resize editor",
6242
+ title: this.context.locale.statusbar.resizeHandle,
3540
6243
  "aria-hidden": "true"
3541
6244
  });
3542
6245
  this._bindResize(handle);
@@ -3626,8 +6329,9 @@ var Statusbar = class {
3626
6329
  const chars = text.replace(/\n/g, "").length;
3627
6330
  const maxWords = this.options.maxWords || 0;
3628
6331
  const maxChars = this.options.maxChars || 0;
3629
- this._wordCountEl.textContent = maxWords ? `Words: ${words}/${maxWords}` : `Words: ${words}`;
3630
- this._charCountEl.textContent = maxChars ? `Chars: ${chars}/${maxChars}` : `Chars: ${chars}`;
6332
+ const LS = this.context.locale.statusbar;
6333
+ this._wordCountEl.textContent = maxWords ? LS.wordsLimit(words, maxWords) : LS.words(words);
6334
+ this._charCountEl.textContent = maxChars ? LS.charsLimit(chars, maxChars) : LS.chars(chars);
3631
6335
  _applyLimitClass(this._wordCountEl, words, maxWords);
3632
6336
  _applyLimitClass(this._charCountEl, chars, maxChars);
3633
6337
  }
@@ -4224,32 +6928,33 @@ var LinkDialog = class {
4224
6928
  this._open();
4225
6929
  }
4226
6930
  _buildDialog() {
6931
+ const L = this.context.locale.linkDialog;
4227
6932
  const overlay = createElement("div", {
4228
6933
  class: "an-dialog-overlay",
4229
6934
  role: "dialog",
4230
6935
  "aria-modal": "true",
4231
- "aria-label": "Insert link"
6936
+ "aria-label": L.ariaLabel
4232
6937
  });
4233
6938
  const box = createElement("div", { class: "an-dialog-box" });
4234
6939
  const title = createElement("h3", { class: "an-dialog-title" });
4235
- title.textContent = "Insert Link";
6940
+ title.textContent = L.title;
4236
6941
  const urlLabel = createElement("label", { class: "an-label" });
4237
- urlLabel.textContent = "URL";
6942
+ urlLabel.textContent = L.url;
4238
6943
  const urlInput = createElement("input", {
4239
6944
  type: "url",
4240
6945
  class: "an-input",
4241
- placeholder: "https://",
6946
+ placeholder: L.urlPlaceholder,
4242
6947
  id: "an-link-url",
4243
6948
  name: "url",
4244
6949
  autocomplete: "off"
4245
6950
  });
4246
6951
  this._urlInput = urlInput;
4247
6952
  const textLabel = createElement("label", { class: "an-label" });
4248
- textLabel.textContent = "Display Text";
6953
+ textLabel.textContent = L.displayText;
4249
6954
  const textInput = createElement("input", {
4250
6955
  type: "text",
4251
6956
  class: "an-input",
4252
- placeholder: "Link text",
6957
+ placeholder: L.textPlaceholder,
4253
6958
  id: "an-link-text",
4254
6959
  name: "linkText",
4255
6960
  autocomplete: "off"
@@ -4263,18 +6968,18 @@ var LinkDialog = class {
4263
6968
  });
4264
6969
  this._tabCheckbox = tabCheckbox;
4265
6970
  tabLabel.appendChild(tabCheckbox);
4266
- tabLabel.appendChild(document.createTextNode(" Open in new tab"));
6971
+ tabLabel.appendChild(document.createTextNode(" " + L.openInNewTab));
4267
6972
  const btnRow = createElement("div", { class: "an-dialog-actions" });
4268
6973
  const insertBtn = createElement("button", {
4269
6974
  type: "button",
4270
6975
  class: "an-btn an-btn-primary"
4271
6976
  });
4272
- insertBtn.textContent = "Insert";
6977
+ insertBtn.textContent = L.insertBtn;
4273
6978
  const cancelBtn = createElement("button", {
4274
6979
  type: "button",
4275
6980
  class: "an-btn"
4276
6981
  });
4277
- cancelBtn.textContent = "Cancel";
6982
+ cancelBtn.textContent = L.cancelBtn;
4278
6983
  btnRow.appendChild(insertBtn);
4279
6984
  btnRow.appendChild(cancelBtn);
4280
6985
  box.append(title, urlLabel, urlInput, textLabel, textInput, tabLabel, btnRow);
@@ -4400,53 +7105,54 @@ var ImageDialog = class {
4400
7105
  this._open();
4401
7106
  }
4402
7107
  _buildDialog() {
7108
+ const L = this.context.locale.imageDialog;
4403
7109
  const overlay = createElement("div", {
4404
7110
  class: "an-dialog-overlay",
4405
7111
  role: "dialog",
4406
7112
  "aria-modal": "true",
4407
- "aria-label": "Insert image"
7113
+ "aria-label": L.ariaLabel
4408
7114
  });
4409
7115
  const box = createElement("div", { class: "an-dialog-box" });
4410
7116
  const title = createElement("h3", { class: "an-dialog-title" });
4411
- title.textContent = "Insert Image";
7117
+ title.textContent = L.title;
4412
7118
  const urlLabel = createElement("label", { class: "an-label" });
4413
- urlLabel.textContent = "Image URL";
7119
+ urlLabel.textContent = L.imageUrl;
4414
7120
  const urlInput = createElement("input", {
4415
7121
  type: "url",
4416
7122
  class: "an-input",
4417
- placeholder: "https://example.com/image.png",
7123
+ placeholder: L.urlPlaceholder,
4418
7124
  autocomplete: "off"
4419
7125
  });
4420
7126
  this._urlInput = urlInput;
4421
7127
  const altLabel = createElement("label", { class: "an-label" });
4422
- altLabel.textContent = "Alt Text";
7128
+ altLabel.textContent = L.altText;
4423
7129
  const altInput = createElement("input", {
4424
7130
  type: "text",
4425
7131
  class: "an-input",
4426
- placeholder: "Describe the image",
7132
+ placeholder: L.altPlaceholder,
4427
7133
  autocomplete: "off"
4428
7134
  });
4429
7135
  this._altInput = altInput;
4430
7136
  box.append(title, urlLabel, urlInput, altLabel, altInput);
4431
7137
  const alignLabel = createElement("label", { class: "an-label" });
4432
- alignLabel.textContent = "Alignment";
7138
+ alignLabel.textContent = L.alignment;
4433
7139
  const alignRow = createElement("div", { class: "an-align-row" });
4434
7140
  [
4435
7141
  {
4436
7142
  value: "",
4437
- label: "None"
7143
+ label: L.alignNone
4438
7144
  },
4439
7145
  {
4440
7146
  value: "left",
4441
- label: "Left"
7147
+ label: L.alignLeft
4442
7148
  },
4443
7149
  {
4444
7150
  value: "center",
4445
- label: "Center"
7151
+ label: L.alignCenter
4446
7152
  },
4447
7153
  {
4448
7154
  value: "right",
4449
- label: "Right"
7155
+ label: L.alignRight
4450
7156
  }
4451
7157
  ].forEach(({ value, label }) => {
4452
7158
  const radioId = `an-align-${value || "none"}`;
@@ -4468,7 +7174,7 @@ var ImageDialog = class {
4468
7174
  box.append(alignLabel, alignRow);
4469
7175
  if (this.options.allowImageUpload !== false) {
4470
7176
  const fileLabel = createElement("label", { class: "an-label" });
4471
- fileLabel.textContent = "Or upload a file";
7177
+ fileLabel.textContent = L.uploadLabel;
4472
7178
  const fileInput = createElement("input", {
4473
7179
  type: "file",
4474
7180
  class: "an-input",
@@ -4486,12 +7192,12 @@ var ImageDialog = class {
4486
7192
  type: "button",
4487
7193
  class: "an-btn an-btn-primary"
4488
7194
  });
4489
- insertBtn.textContent = "Insert";
7195
+ insertBtn.textContent = L.insertBtn;
4490
7196
  const cancelBtn = createElement("button", {
4491
7197
  type: "button",
4492
7198
  class: "an-btn"
4493
7199
  });
4494
- cancelBtn.textContent = "Cancel";
7200
+ cancelBtn.textContent = L.cancelBtn;
4495
7201
  btnRow.appendChild(insertBtn);
4496
7202
  btnRow.appendChild(cancelBtn);
4497
7203
  box.append(btnRow);
@@ -4527,7 +7233,7 @@ var ImageDialog = class {
4527
7233
  "image/svg+xml",
4528
7234
  "image/avif"
4529
7235
  ]).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.`;
7236
+ const message = this.context.locale.errors.imageFormat(file.type);
4531
7237
  if (this._fileHint) this._fileHint.textContent = message;
4532
7238
  this.context.triggerEvent("imageError", {
4533
7239
  file,
@@ -4539,7 +7245,7 @@ var ImageDialog = class {
4539
7245
  if (this._fileHint) this._fileHint.textContent = "";
4540
7246
  const maxSize = (this.options.maxImageSize || 5) * 1024 * 1024;
4541
7247
  if (file.size > maxSize) {
4542
- const message = `Image file is too large. Maximum allowed size is ${this.options.maxImageSize || 5} MB.`;
7248
+ const message = this.context.locale.errors.imageSize(this.options.maxImageSize || 5);
4543
7249
  if (this._fileHint) this._fileHint.textContent = message;
4544
7250
  console.warn("[AutumnNote] ImageDialog:", message);
4545
7251
  this.context.triggerEvent("imageError", {
@@ -4625,28 +7331,29 @@ var VideoDialog = class {
4625
7331
  this._open();
4626
7332
  }
4627
7333
  _buildDialog() {
7334
+ const L = this.context.locale.videoDialog;
4628
7335
  const overlay = createElement("div", {
4629
7336
  class: "an-dialog-overlay",
4630
7337
  role: "dialog",
4631
7338
  "aria-modal": "true",
4632
- "aria-label": "Insert video"
7339
+ "aria-label": L.ariaLabel
4633
7340
  });
4634
7341
  const box = createElement("div", { class: "an-dialog-box" });
4635
7342
  const title = createElement("h3", { class: "an-dialog-title" });
4636
- title.textContent = "Insert Video";
7343
+ title.textContent = L.title;
4637
7344
  const urlLabel = createElement("label", { class: "an-label" });
4638
- urlLabel.textContent = "Video URL";
7345
+ urlLabel.textContent = L.videoUrl;
4639
7346
  const urlInput = createElement("input", {
4640
7347
  type: "url",
4641
7348
  class: "an-input",
4642
- placeholder: "YouTube, Vimeo, or direct .mp4 URL",
7349
+ placeholder: L.urlPlaceholder,
4643
7350
  autocomplete: "off"
4644
7351
  });
4645
7352
  this._urlInput = urlInput;
4646
7353
  const hintEl = createElement("p", { class: "an-dialog-hint" });
4647
7354
  this._hintEl = hintEl;
4648
7355
  const widthLabel = createElement("label", { class: "an-label" });
4649
- widthLabel.textContent = "Width (px)";
7356
+ widthLabel.textContent = L.widthLabel;
4650
7357
  const widthInput = createElement("input", {
4651
7358
  type: "number",
4652
7359
  class: "an-input",
@@ -4661,19 +7368,19 @@ var VideoDialog = class {
4661
7368
  type: "button",
4662
7369
  class: "an-btn an-btn-primary"
4663
7370
  });
4664
- insertBtn.textContent = "Insert";
7371
+ insertBtn.textContent = L.insertBtn;
4665
7372
  const cancelBtn = createElement("button", {
4666
7373
  type: "button",
4667
7374
  class: "an-btn"
4668
7375
  });
4669
- cancelBtn.textContent = "Cancel";
7376
+ cancelBtn.textContent = L.cancelBtn;
4670
7377
  btnRow.appendChild(insertBtn);
4671
7378
  btnRow.appendChild(cancelBtn);
4672
7379
  box.append(title, urlLabel, urlInput, hintEl, widthLabel, widthInput, btnRow);
4673
7380
  overlay.appendChild(box);
4674
7381
  const d0 = on(urlInput, "input", () => {
4675
7382
  const info = this._parseVideoUrl(urlInput.value.trim());
4676
- hintEl.textContent = info ? `Detected: ${info.type}` : urlInput.value ? "Unknown format — will try direct video embed" : "";
7383
+ hintEl.textContent = info ? this.context.locale.videoDialog.detected(info.type) : urlInput.value ? this.context.locale.videoDialog.unknownFormat : "";
4677
7384
  });
4678
7385
  const d1 = on(insertBtn, "click", () => this._onInsert());
4679
7386
  const d2 = on(cancelBtn, "click", () => this._close());
@@ -4698,7 +7405,7 @@ var VideoDialog = class {
4698
7405
  }
4699
7406
  const html = this._buildEmbedHtml(rawUrl, width);
4700
7407
  if (!html) {
4701
- this._hintEl.textContent = "Invalid URL — please enter a valid video link.";
7408
+ this._hintEl.textContent = this.context.locale.videoDialog.invalidUrl;
4702
7409
  this._urlInput.focus();
4703
7410
  return;
4704
7411
  }
@@ -5270,19 +7977,20 @@ var LinkTooltip = class {
5270
7977
  this._el = null;
5271
7978
  }
5272
7979
  _buildTooltip() {
7980
+ const L = this.context.locale.tooltips.link;
5273
7981
  const el = createElement("div", {
5274
7982
  class: "an-link-tooltip",
5275
7983
  role: "toolbar",
5276
- "aria-label": "Link actions"
7984
+ "aria-label": L.ariaLabel
5277
7985
  });
5278
7986
  el.style.display = "none";
5279
7987
  this._urlLabel = createElement("span", { class: "an-link-tooltip-url" });
5280
7988
  el.appendChild(this._urlLabel);
5281
7989
  el.appendChild(createElement("div", { class: "an-link-tooltip-sep" }));
5282
- this._openBtn = this._makeBtn(ICONS$5.open, "Open link", () => this._openLink());
5283
- this._copyBtn = this._makeBtn(ICONS$5.copy, "Copy URL", () => this._copyLink());
5284
- this._editBtn = this._makeBtn(ICONS$5.edit, "Edit link", () => this._editLink());
5285
- this._unlinkBtn = this._makeBtn(ICONS$5.unlink, "Remove link", () => this._unlink());
7990
+ this._openBtn = this._makeBtn(ICONS$5.open, L.openLink, () => this._openLink());
7991
+ this._copyBtn = this._makeBtn(ICONS$5.copy, L.copyUrl, () => this._copyLink());
7992
+ this._editBtn = this._makeBtn(ICONS$5.edit, L.editLink, () => this._editLink());
7993
+ this._unlinkBtn = this._makeBtn(ICONS$5.unlink, L.removeLink, () => this._unlink());
5286
7994
  el.appendChild(this._openBtn);
5287
7995
  el.appendChild(this._copyBtn);
5288
7996
  el.appendChild(this._editBtn);
@@ -5461,38 +8169,39 @@ var ImageTooltip = class {
5461
8169
  this._el = null;
5462
8170
  }
5463
8171
  _buildTooltip() {
8172
+ const L = this.context.locale.tooltips.image;
5464
8173
  const el = createElement("div", {
5465
8174
  class: "an-link-tooltip an-image-tooltip",
5466
8175
  role: "toolbar",
5467
- "aria-label": "Image actions"
8176
+ "aria-label": L.ariaLabel
5468
8177
  });
5469
8178
  el.style.display = "none";
5470
8179
  this._label = createElement("span", { class: "an-link-tooltip-url" });
5471
- this._label.textContent = "Image";
8180
+ this._label.textContent = L.label;
5472
8181
  el.appendChild(this._label);
5473
8182
  el.appendChild(createElement("div", { class: "an-link-tooltip-sep" }));
5474
- this._floatLeftBtn = this._makeBtn(ICONS$4.floatLeft, "Float Left", () => this._setFloat("left"));
5475
- this._floatNoneBtn = this._makeBtn(ICONS$4.floatNone, "No Float", () => this._setFloat(""));
5476
- this._alignCenterBtn = this._makeBtn(ICONS$4.alignCenter, "Align Center", () => this._setCenter());
5477
- this._floatRightBtn = this._makeBtn(ICONS$4.floatRight, "Float Right", () => this._setFloat("right"));
8183
+ this._floatLeftBtn = this._makeBtn(ICONS$4.floatLeft, L.floatLeft, () => this._setFloat("left"));
8184
+ this._floatNoneBtn = this._makeBtn(ICONS$4.floatNone, L.noFloat, () => this._setFloat(""));
8185
+ this._alignCenterBtn = this._makeBtn(ICONS$4.alignCenter, L.alignCenter, () => this._setCenter());
8186
+ this._floatRightBtn = this._makeBtn(ICONS$4.floatRight, L.floatRight, () => this._setFloat("right"));
5478
8187
  el.appendChild(this._floatLeftBtn);
5479
8188
  el.appendChild(this._floatNoneBtn);
5480
8189
  el.appendChild(this._alignCenterBtn);
5481
8190
  el.appendChild(this._floatRightBtn);
5482
8191
  el.appendChild(createElement("div", { class: "an-link-tooltip-sep" }));
5483
- this._originalBtn = this._makeBtn(ICONS$4.originalSize, "Original Size", () => this._resetSize());
8192
+ this._originalBtn = this._makeBtn(ICONS$4.originalSize, L.originalSize, () => this._resetSize());
5484
8193
  el.appendChild(this._originalBtn);
5485
8194
  el.appendChild(createElement("div", { class: "an-link-tooltip-sep" }));
5486
- el.appendChild(this._makeBtn(ICONS$4.rotateLeft, "Rotate Left", () => this._rotate(-90)));
5487
- el.appendChild(this._makeBtn(ICONS$4.rotateRight, "Rotate Right", () => this._rotate(90)));
8195
+ el.appendChild(this._makeBtn(ICONS$4.rotateLeft, L.rotateLeft, () => this._rotate(-90)));
8196
+ el.appendChild(this._makeBtn(ICONS$4.rotateRight, L.rotateRight, () => this._rotate(90)));
5488
8197
  el.appendChild(createElement("div", { class: "an-link-tooltip-sep" }));
5489
- this._cropBtn = this._makeBtn(ICONS$4.crop, "Crop Image", () => this._crop());
8198
+ this._cropBtn = this._makeBtn(ICONS$4.crop, L.cropImage, () => this._crop());
5490
8199
  el.appendChild(this._cropBtn);
5491
8200
  el.appendChild(createElement("div", { class: "an-link-tooltip-sep" }));
5492
- this._captionBtn = this._makeBtn(ICONS$4.caption, "Add / Edit Caption", () => this._toggleCaption());
8201
+ this._captionBtn = this._makeBtn(ICONS$4.caption, L.addCaption, () => this._toggleCaption());
5493
8202
  el.appendChild(this._captionBtn);
5494
8203
  el.appendChild(createElement("div", { class: "an-link-tooltip-sep" }));
5495
- this._deleteBtn = this._makeBtn(ICONS$4.deleteImg, "Delete Image", () => this._delete(), true);
8204
+ this._deleteBtn = this._makeBtn(ICONS$4.deleteImg, L.deleteImage, () => this._delete(), true);
5496
8205
  el.appendChild(this._deleteBtn);
5497
8206
  this._disposers.push(on(el, "mouseenter", () => this._clearTimers()), on(el, "mouseleave", () => this._scheduleHide()));
5498
8207
  return el;
@@ -5744,32 +8453,33 @@ var VideoTooltip = class {
5744
8453
  this._el = null;
5745
8454
  }
5746
8455
  _buildTooltip() {
8456
+ const L = this.context.locale.tooltips.video;
5747
8457
  const el = createElement("div", {
5748
8458
  class: "an-link-tooltip an-video-tooltip",
5749
8459
  role: "toolbar",
5750
- "aria-label": "Video actions"
8460
+ "aria-label": L.ariaLabel
5751
8461
  });
5752
8462
  el.style.display = "none";
5753
8463
  this._label = createElement("span", { class: "an-link-tooltip-url" });
5754
- this._label.textContent = "Video";
8464
+ this._label.textContent = L.label;
5755
8465
  el.appendChild(this._label);
5756
8466
  el.appendChild(createElement("div", { class: "an-link-tooltip-sep" }));
5757
- this._floatLeftBtn = this._makeBtn(ICONS$3.floatLeft, "Float Left", () => this._setFloat("left"));
5758
- this._floatNoneBtn = this._makeBtn(ICONS$3.floatNone, "No Float", () => this._setFloat(""));
5759
- this._alignCenterBtn = this._makeBtn(ICONS$3.alignCenter, "Align Center", () => this._setCenter());
5760
- this._floatRightBtn = this._makeBtn(ICONS$3.floatRight, "Float Right", () => this._setFloat("right"));
8467
+ this._floatLeftBtn = this._makeBtn(ICONS$3.floatLeft, L.floatLeft, () => this._setFloat("left"));
8468
+ this._floatNoneBtn = this._makeBtn(ICONS$3.floatNone, L.noFloat, () => this._setFloat(""));
8469
+ this._alignCenterBtn = this._makeBtn(ICONS$3.alignCenter, L.alignCenter, () => this._setCenter());
8470
+ this._floatRightBtn = this._makeBtn(ICONS$3.floatRight, L.floatRight, () => this._setFloat("right"));
5761
8471
  el.appendChild(this._floatLeftBtn);
5762
8472
  el.appendChild(this._floatNoneBtn);
5763
8473
  el.appendChild(this._alignCenterBtn);
5764
8474
  el.appendChild(this._floatRightBtn);
5765
8475
  el.appendChild(createElement("div", { class: "an-link-tooltip-sep" }));
5766
- this._originalBtn = this._makeBtn(ICONS$3.originalSize, "Original Size", () => this._resetSize());
8476
+ this._originalBtn = this._makeBtn(ICONS$3.originalSize, L.originalSize, () => this._resetSize());
5767
8477
  el.appendChild(this._originalBtn);
5768
8478
  el.appendChild(createElement("div", { class: "an-link-tooltip-sep" }));
5769
- this._previewBtn = this._makeBtn(ICONS$3.preview, "Preview Video", () => this._togglePreview());
8479
+ this._previewBtn = this._makeBtn(ICONS$3.preview, L.previewVideo, () => this._togglePreview());
5770
8480
  el.appendChild(this._previewBtn);
5771
8481
  el.appendChild(createElement("div", { class: "an-link-tooltip-sep" }));
5772
- this._deleteBtn = this._makeBtn(ICONS$3.deleteVideo, "Delete Video", () => this._delete(), true);
8482
+ this._deleteBtn = this._makeBtn(ICONS$3.deleteVideo, L.deleteVideo, () => this._delete(), true);
5773
8483
  el.appendChild(this._deleteBtn);
5774
8484
  this._disposers.push(on(el, "mouseenter", () => this._clearTimers()), on(el, "mouseleave", () => this._scheduleHide()));
5775
8485
  return el;
@@ -5906,7 +8616,7 @@ var VideoTooltip = class {
5906
8616
  if (shield) shield.style.display = "none";
5907
8617
  this.context.invoke("videoResizer.deselect");
5908
8618
  this._previewBtn.classList.add("an-link-tooltip-btn--copied");
5909
- this._previewBtn.title = "Exit Preview";
8619
+ this._previewBtn.title = this.context.locale.tooltips.video.exitPreview;
5910
8620
  this._previewClickOff = (e) => {
5911
8621
  if (!wrapper.contains(e.target) && !this._el.contains(e.target)) this._exitPreview();
5912
8622
  };
@@ -5920,7 +8630,7 @@ var VideoTooltip = class {
5920
8630
  if (shield) shield.style.display = "";
5921
8631
  }
5922
8632
  this._previewBtn.classList.remove("an-link-tooltip-btn--copied");
5923
- this._previewBtn.title = "Preview Video";
8633
+ this._previewBtn.title = this.context.locale.tooltips.video.previewVideo;
5924
8634
  if (this._previewClickOff) {
5925
8635
  document.removeEventListener("mousedown", this._previewClickOff, true);
5926
8636
  this._previewClickOff = null;
@@ -6234,35 +8944,36 @@ var TableTooltip = class {
6234
8944
  this._sizePopover = null;
6235
8945
  }
6236
8946
  _buildTooltip() {
8947
+ const L = this.context.locale.tooltips.table;
6237
8948
  const el = createElement("div", {
6238
8949
  class: "an-link-tooltip an-table-tooltip",
6239
8950
  role: "toolbar",
6240
- "aria-label": "Table actions"
8951
+ "aria-label": L.ariaLabel
6241
8952
  });
6242
8953
  el.style.display = "none";
6243
8954
  this._label = createElement("span", { class: "an-link-tooltip-url" });
6244
- this._label.textContent = "Table";
8955
+ this._label.textContent = L.label;
6245
8956
  el.appendChild(this._label);
6246
8957
  el.appendChild(this._sep());
6247
- this._selectBtn = this._makeBtn(ICONS$2.selectCells, "Select Cells", () => this._toggleSelectMode());
8958
+ this._selectBtn = this._makeBtn(ICONS$2.selectCells, L.selectCells, () => this._toggleSelectMode());
6248
8959
  el.appendChild(this._selectBtn);
6249
8960
  el.appendChild(this._sep());
6250
- el.appendChild(this._makeBtn(ICONS$2.rowAbove, "Add Row Above", () => this._addRow("above")));
6251
- el.appendChild(this._makeBtn(ICONS$2.rowBelow, "Add Row Below", () => this._addRow("below")));
6252
- el.appendChild(this._makeBtn(ICONS$2.deleteRow, "Delete Row", () => this._deleteRow()));
8961
+ el.appendChild(this._makeBtn(ICONS$2.rowAbove, L.addRowAbove, () => this._addRow("above")));
8962
+ el.appendChild(this._makeBtn(ICONS$2.rowBelow, L.addRowBelow, () => this._addRow("below")));
8963
+ el.appendChild(this._makeBtn(ICONS$2.deleteRow, L.deleteRow, () => this._deleteRow()));
6253
8964
  el.appendChild(this._sep());
6254
- el.appendChild(this._makeBtn(ICONS$2.colLeft, "Add Column Left", () => this._addColumn("left")));
6255
- el.appendChild(this._makeBtn(ICONS$2.colRight, "Add Column Right", () => this._addColumn("right")));
6256
- el.appendChild(this._makeBtn(ICONS$2.deleteCol, "Delete Column", () => this._deleteColumn()));
8965
+ el.appendChild(this._makeBtn(ICONS$2.colLeft, L.addColumnLeft, () => this._addColumn("left")));
8966
+ el.appendChild(this._makeBtn(ICONS$2.colRight, L.addColumnRight, () => this._addColumn("right")));
8967
+ el.appendChild(this._makeBtn(ICONS$2.deleteCol, L.deleteColumn, () => this._deleteColumn()));
6257
8968
  el.appendChild(this._sep());
6258
- el.appendChild(this._makeBtn(ICONS$2.mergeCells, "Merge Cells", () => this._mergeCells()));
6259
- el.appendChild(this._makeBtn(ICONS$2.unmergeCells, "Unmerge Cells", () => this._unmergeCells()));
8969
+ el.appendChild(this._makeBtn(ICONS$2.mergeCells, L.mergeCells, () => this._mergeCells()));
8970
+ el.appendChild(this._makeBtn(ICONS$2.unmergeCells, L.unmergeCells, () => this._unmergeCells()));
6260
8971
  el.appendChild(this._sep());
6261
- el.appendChild(this._makeBtn(ICONS$2.colWidth, "Column Width", () => this._openSizePopover("col")));
6262
- el.appendChild(this._makeBtn(ICONS$2.rowHeight, "Row Height", () => this._openSizePopover("row")));
6263
- el.appendChild(this._makeBtn(ICONS$2.tableBorder, "Table Border Width", () => this._openSizePopover("border")));
8972
+ el.appendChild(this._makeBtn(ICONS$2.colWidth, L.columnWidth, () => this._openSizePopover("col")));
8973
+ el.appendChild(this._makeBtn(ICONS$2.rowHeight, L.rowHeight, () => this._openSizePopover("row")));
8974
+ el.appendChild(this._makeBtn(ICONS$2.tableBorder, L.tableBorderWidth, () => this._openSizePopover("border")));
6264
8975
  el.appendChild(this._sep());
6265
- el.appendChild(this._makeBtn(ICONS$2.deleteTable, "Delete Table", () => this._deleteTable(), true));
8976
+ el.appendChild(this._makeBtn(ICONS$2.deleteTable, L.deleteTable, () => this._deleteTable(), true));
6266
8977
  this._disposers.push(on(el, "mouseenter", () => this._clearTimers()), on(el, "mouseleave", () => {
6267
8978
  if (this._selectMode) return;
6268
8979
  if (this._sizePopover && this._sizePopover.style.display !== "none") return;
@@ -6660,12 +9371,12 @@ var TableTooltip = class {
6660
9371
  type: "button",
6661
9372
  class: "an-btn"
6662
9373
  });
6663
- cancelBtn.textContent = "Cancel";
9374
+ cancelBtn.textContent = this.context.locale.tooltips.table.cancelBtn;
6664
9375
  const applyBtn = createElement("button", {
6665
9376
  type: "button",
6666
9377
  class: "an-btn an-btn-primary"
6667
9378
  });
6668
- applyBtn.textContent = "Apply";
9379
+ applyBtn.textContent = this.context.locale.tooltips.table.applyBtn;
6669
9380
  actionsEl.appendChild(cancelBtn);
6670
9381
  actionsEl.appendChild(applyBtn);
6671
9382
  popover.appendChild(titleEl);
@@ -6703,7 +9414,7 @@ var TableTooltip = class {
6703
9414
  if (!table) return;
6704
9415
  const firstCell = table.querySelector("td, th");
6705
9416
  const currentPx = firstCell ? parseInt(firstCell.style.borderWidth, 10) || parseInt(window.getComputedStyle(firstCell).borderWidth, 10) || 1 : 1;
6706
- this._sizeTitleEl.textContent = "Table Border Width (px)";
9417
+ this._sizeTitleEl.textContent = this.context.locale.tooltips.table.tableBorderWidthPx;
6707
9418
  this._sizeInputEl.min = "0";
6708
9419
  this._sizeInputEl.max = "10";
6709
9420
  this._sizeInputEl.value = currentPx;
@@ -6725,7 +9436,7 @@ var TableTooltip = class {
6725
9436
  const t = c.closest("table");
6726
9437
  return t && t === cell.closest("table");
6727
9438
  });
6728
- this._sizeTitleEl.textContent = isCol ? "Column Width (px)" : "Row Height (px)";
9439
+ this._sizeTitleEl.textContent = isCol ? this.context.locale.tooltips.table.columnWidthPx : this.context.locale.tooltips.table.rowHeightPx;
6729
9440
  this._sizeInputEl.min = "1";
6730
9441
  this._sizeInputEl.max = "2000";
6731
9442
  this._sizeInputEl.value = isCol ? cell.offsetWidth || 120 : cell.closest("tr") ? cell.closest("tr").offsetHeight || 40 : 40;
@@ -6825,20 +9536,21 @@ var CodeTooltip = class {
6825
9536
  this._el = null;
6826
9537
  }
6827
9538
  _buildTooltip() {
9539
+ const L = this.context.locale.tooltips.code;
6828
9540
  const el = createElement("div", {
6829
9541
  class: "an-link-tooltip an-code-tooltip",
6830
9542
  role: "toolbar",
6831
- "aria-label": "Code block actions"
9543
+ "aria-label": L.ariaLabel
6832
9544
  });
6833
9545
  el.style.display = "none";
6834
9546
  this._label = createElement("span", { class: "an-link-tooltip-url" });
6835
- this._label.textContent = "Code";
9547
+ this._label.textContent = L.label;
6836
9548
  el.appendChild(this._label);
6837
9549
  el.appendChild(this._sep());
6838
9550
  this._langSelect = createElement("select", {
6839
9551
  class: "an-code-lang-select",
6840
- title: "Syntax Language",
6841
- "aria-label": "Syntax language"
9552
+ title: L.syntaxLanguage,
9553
+ "aria-label": L.syntaxAriaLabel
6842
9554
  });
6843
9555
  [
6844
9556
  ["", "Plain text"],
@@ -6869,15 +9581,15 @@ var CodeTooltip = class {
6869
9581
  this._disposers.push(on(this._langSelect, "change", () => this._onLangChange()));
6870
9582
  el.appendChild(this._langSelect);
6871
9583
  el.appendChild(this._sep());
6872
- this._copyBtn = this._makeBtn(ICONS$1.copy, "Copy Code", () => this._copyCode());
9584
+ this._copyBtn = this._makeBtn(ICONS$1.copy, L.copyCode, () => this._copyCode());
6873
9585
  el.appendChild(this._copyBtn);
6874
9586
  el.appendChild(this._sep());
6875
- this._wrapBtn = this._makeBtn(ICONS$1.wrapOn, "Toggle Word Wrap", () => this._toggleWrap());
9587
+ this._wrapBtn = this._makeBtn(ICONS$1.wrapOn, L.toggleWordWrap, () => this._toggleWrap());
6876
9588
  el.appendChild(this._wrapBtn);
6877
9589
  el.appendChild(this._sep());
6878
- el.appendChild(this._makeBtn(ICONS$1.toParagraph, "Convert to Paragraph", () => this._toParagraph()));
9590
+ el.appendChild(this._makeBtn(ICONS$1.toParagraph, L.convertToParagraph, () => this._toParagraph()));
6879
9591
  el.appendChild(this._sep());
6880
- el.appendChild(this._makeBtn(ICONS$1.deleteCode, "Delete Code Block", () => this._delete(), true));
9592
+ el.appendChild(this._makeBtn(ICONS$1.deleteCode, L.deleteCodeBlock, () => this._delete(), true));
6881
9593
  this._disposers.push(on(el, "mouseenter", () => this._clearTimers()), on(el, "mouseleave", () => this._scheduleHide()));
6882
9594
  return el;
6883
9595
  }
@@ -6954,7 +9666,7 @@ var CodeTooltip = class {
6954
9666
  if (!this._activePre || !this._wrapBtn) return;
6955
9667
  const wrapped = (this._activePre.style.whiteSpace || "").includes("pre-wrap") || window.getComputedStyle(this._activePre).whiteSpace === "pre-wrap";
6956
9668
  this._wrapBtn.classList.toggle("active", wrapped);
6957
- this._wrapBtn.title = wrapped ? "Disable Word Wrap" : "Enable Word Wrap";
9669
+ this._wrapBtn.title = wrapped ? this.context.locale.tooltips.code.disableWordWrap : this.context.locale.tooltips.code.enableWordWrap;
6958
9670
  }
6959
9671
  _syncLangSelect() {
6960
9672
  if (!this._activePre || !this._langSelect) return;
@@ -9484,27 +12196,28 @@ var EmojiDialog = class {
9484
12196
  this._open();
9485
12197
  }
9486
12198
  _buildDialog() {
12199
+ const L = this.context.locale.emojiDialog;
9487
12200
  const overlay = createElement("div", {
9488
12201
  class: "an-dialog-overlay",
9489
12202
  role: "dialog",
9490
12203
  "aria-modal": "true",
9491
- "aria-label": "Insert emoji"
12204
+ "aria-label": L.ariaLabel
9492
12205
  });
9493
12206
  const box = createElement("div", { class: "an-dialog-box an-emoji-box" });
9494
12207
  const titleRow = createElement("div", { class: "an-icon-title-row" });
9495
12208
  const title = createElement("h3", { class: "an-dialog-title" });
9496
- title.textContent = "Insert Emoji";
12209
+ title.textContent = L.title;
9497
12210
  const closeBtn = createElement("button", {
9498
12211
  type: "button",
9499
12212
  class: "an-icon-close",
9500
- "aria-label": "Close"
12213
+ "aria-label": L.close
9501
12214
  });
9502
12215
  closeBtn.innerHTML = "&times;";
9503
12216
  titleRow.append(title, closeBtn);
9504
12217
  const searchInput = createElement("input", {
9505
12218
  type: "search",
9506
12219
  class: "an-input an-icon-search",
9507
- placeholder: "Search emojis…",
12220
+ placeholder: L.searchPlaceholder,
9508
12221
  autocomplete: "off"
9509
12222
  });
9510
12223
  this._searchInput = searchInput;
@@ -9514,7 +12227,7 @@ var EmojiDialog = class {
9514
12227
  class: "an-icon-cat active",
9515
12228
  "data-cat": "all"
9516
12229
  });
9517
- allTab.textContent = "All";
12230
+ allTab.textContent = L.all;
9518
12231
  catBar.appendChild(allTab);
9519
12232
  EMOJI_CATS.forEach(({ id, label }) => {
9520
12233
  const tab = createElement("button", {
@@ -9522,7 +12235,7 @@ var EmojiDialog = class {
9522
12235
  class: "an-icon-cat",
9523
12236
  "data-cat": id
9524
12237
  });
9525
- tab.textContent = label;
12238
+ tab.textContent = L.categories && L.categories[id] || label;
9526
12239
  catBar.appendChild(tab);
9527
12240
  });
9528
12241
  this._catBar = catBar;
@@ -9545,7 +12258,7 @@ var EmojiDialog = class {
9545
12258
  type: "button",
9546
12259
  class: "an-btn"
9547
12260
  });
9548
- cancelBtn.textContent = "Cancel";
12261
+ cancelBtn.textContent = L.cancelBtn;
9549
12262
  btnRow.appendChild(cancelBtn);
9550
12263
  box.append(titleRow, searchInput, catBar, grid, btnRow);
9551
12264
  overlay.appendChild(box);
@@ -9956,27 +12669,28 @@ var IconDialog = class {
9956
12669
  this._open();
9957
12670
  }
9958
12671
  _buildDialog() {
12672
+ const L = this.context.locale.iconDialog;
9959
12673
  const overlay = createElement("div", {
9960
12674
  class: "an-dialog-overlay",
9961
12675
  role: "dialog",
9962
12676
  "aria-modal": "true",
9963
- "aria-label": "Insert FA icon"
12677
+ "aria-label": L.ariaLabel
9964
12678
  });
9965
12679
  const box = createElement("div", { class: "an-dialog-box an-icon-box" });
9966
12680
  const titleRow = createElement("div", { class: "an-icon-title-row" });
9967
12681
  const title = createElement("h3", { class: "an-dialog-title" });
9968
- title.textContent = "Insert FA Icon";
12682
+ title.textContent = L.title;
9969
12683
  const closeBtn = createElement("button", {
9970
12684
  type: "button",
9971
12685
  class: "an-icon-close",
9972
- "aria-label": "Close"
12686
+ "aria-label": L.close
9973
12687
  });
9974
12688
  closeBtn.innerHTML = "&times;";
9975
12689
  titleRow.append(title, closeBtn);
9976
12690
  const searchInput = createElement("input", {
9977
12691
  type: "search",
9978
12692
  class: "an-input an-icon-search",
9979
- placeholder: "Search icons…",
12693
+ placeholder: L.searchPlaceholder,
9980
12694
  autocomplete: "off"
9981
12695
  });
9982
12696
  this._searchInput = searchInput;
@@ -9986,7 +12700,7 @@ var IconDialog = class {
9986
12700
  class: "an-icon-cat active",
9987
12701
  "data-cat": "all"
9988
12702
  });
9989
- allTab.textContent = "All";
12703
+ allTab.textContent = L.all;
9990
12704
  catBar.appendChild(allTab);
9991
12705
  ICON_CATEGORIES.forEach(({ id, label }) => {
9992
12706
  const tab = createElement("button", {
@@ -9994,7 +12708,7 @@ var IconDialog = class {
9994
12708
  class: "an-icon-cat",
9995
12709
  "data-cat": id
9996
12710
  });
9997
- tab.textContent = label;
12711
+ tab.textContent = L.categories && L.categories[id] || label;
9998
12712
  catBar.appendChild(tab);
9999
12713
  });
10000
12714
  this._catBar = catBar;
@@ -10019,7 +12733,7 @@ var IconDialog = class {
10019
12733
  this._grid = grid;
10020
12734
  const optRow = createElement("div", { class: "an-icon-options" });
10021
12735
  const styleLabel = createElement("label", { class: "an-label" });
10022
- styleLabel.textContent = "Style";
12736
+ styleLabel.textContent = L.style;
10023
12737
  const styleSelect = createElement("select", { class: "an-input an-icon-option-select" });
10024
12738
  [
10025
12739
  ["fa-solid", "Solid"],
@@ -10033,7 +12747,7 @@ var IconDialog = class {
10033
12747
  styleSelect.value = "fa-solid";
10034
12748
  this._styleSelect = styleSelect;
10035
12749
  const sizeLabel = createElement("label", { class: "an-label" });
10036
- sizeLabel.textContent = "Size";
12750
+ sizeLabel.textContent = L.size;
10037
12751
  const sizeSelect = createElement("select", { class: "an-input an-icon-option-select" });
10038
12752
  [
10039
12753
  ["", "Inherit"],
@@ -10051,7 +12765,7 @@ var IconDialog = class {
10051
12765
  });
10052
12766
  this._sizeSelect = sizeSelect;
10053
12767
  const colorLabel = createElement("label", { class: "an-label" });
10054
- colorLabel.textContent = "Color";
12768
+ colorLabel.textContent = L.color;
10055
12769
  const colorInput = createElement("input", {
10056
12770
  type: "color",
10057
12771
  class: "an-icon-color",
@@ -10064,11 +12778,11 @@ var IconDialog = class {
10064
12778
  checked: ""
10065
12779
  });
10066
12780
  this._useColorCb = useColorCb;
10067
- useColorLabel.append(useColorCb, document.createTextNode(" Use color"));
12781
+ useColorLabel.append(useColorCb, document.createTextNode(L.useColor));
10068
12782
  optRow.append(styleLabel, styleSelect, sizeLabel, sizeSelect, colorLabel, colorInput, useColorLabel);
10069
12783
  const preview = createElement("div", { class: "an-icon-preview" });
10070
12784
  const previewHint = createElement("span", { class: "an-icon-preview-hint" });
10071
- previewHint.textContent = "Select an icon";
12785
+ previewHint.textContent = L.selectHint;
10072
12786
  preview.appendChild(previewHint);
10073
12787
  this._preview = preview;
10074
12788
  const btnRow = createElement("div", { class: "an-dialog-actions" });
@@ -10077,12 +12791,12 @@ var IconDialog = class {
10077
12791
  class: "an-btn an-btn-primary",
10078
12792
  disabled: ""
10079
12793
  });
10080
- insertBtn.textContent = "Insert FA Icon";
12794
+ insertBtn.textContent = L.insertBtn;
10081
12795
  const cancelBtn = createElement("button", {
10082
12796
  type: "button",
10083
12797
  class: "an-btn"
10084
12798
  });
10085
- cancelBtn.textContent = "Cancel";
12799
+ cancelBtn.textContent = L.cancelBtn;
10086
12800
  btnRow.append(insertBtn, cancelBtn);
10087
12801
  this._insertBtn = insertBtn;
10088
12802
  box.append(titleRow, searchInput, catBar, grid, optRow, preview, btnRow);
@@ -10283,9 +12997,11 @@ var COLOR_PRESETS = [
10283
12997
  ];
10284
12998
  function makeColorSubItems(colorType) {
10285
12999
  const label = colorType === "foreColor" ? "Text Color" : "Highlight Color";
13000
+ const localeKey = colorType === "foreColor" ? "textColor" : "highlightColor";
10286
13001
  return () => [{
10287
13002
  back: true,
10288
13003
  label,
13004
+ localeKey,
10289
13005
  navigate: () => defaultItems
10290
13006
  }, {
10291
13007
  colorPalette: true,
@@ -10482,7 +13198,8 @@ var ContextMenu = class {
10482
13198
  });
10483
13199
  iconSpan.innerHTML = ICONS.back;
10484
13200
  backBtn.appendChild(iconSpan);
10485
- backBtn.appendChild(createElement("span", { class: "an-context-label" }, [it.label || "Back"]));
13201
+ const backLabel = it.localeKey && this.context.locale.contextMenu[it.localeKey] || it.label || this.context.locale.contextMenu.back || "Back";
13202
+ backBtn.appendChild(createElement("span", { class: "an-context-label" }, [backLabel]));
10486
13203
  const off = on(backBtn, "click", (e) => {
10487
13204
  e.stopPropagation();
10488
13205
  const curLeft = parseFloat(this.el.style.left);
@@ -10520,7 +13237,7 @@ var ContextMenu = class {
10520
13237
  iconSpan.innerHTML = it.icon;
10521
13238
  btn.appendChild(iconSpan);
10522
13239
  }
10523
- btn.appendChild(createElement("span", { class: "an-context-label" }, [it.label || it.name]));
13240
+ btn.appendChild(createElement("span", { class: "an-context-label" }, [this.context.locale.contextMenu[it.name] || it.label || it.name]));
10524
13241
  const chevron = createElement("span", {
10525
13242
  class: "an-context-chevron",
10526
13243
  "aria-hidden": "true"
@@ -10558,9 +13275,9 @@ var ContextMenu = class {
10558
13275
  if (it.colorType === "hiliteColor") {
10559
13276
  const noColor = createElement("div", {
10560
13277
  class: "an-context-color-swatch an-context-color-none",
10561
- title: "No highlight",
13278
+ title: this.context.locale.contextMenu.noHighlight || "No highlight",
10562
13279
  role: "button",
10563
- "aria-label": "No highlight"
13280
+ "aria-label": this.context.locale.contextMenu.noHighlight || "No highlight"
10564
13281
  });
10565
13282
  noColor.innerHTML = ICONS.noColor;
10566
13283
  const offNo = on(noColor, "click", (e) => {
@@ -10575,10 +13292,10 @@ var ContextMenu = class {
10575
13292
  const colorInput = createElement("input", {
10576
13293
  type: "color",
10577
13294
  value: it.colorType === "foreColor" ? "#000000" : "#ffff00",
10578
- title: "Custom color",
10579
- "aria-label": "Custom color"
13295
+ title: this.context.locale.contextMenu.customColor || "Custom color",
13296
+ "aria-label": this.context.locale.contextMenu.customColor || "Custom color"
10580
13297
  });
10581
- const customLabel = createElement("span", {}, ["Custom…"]);
13298
+ const customLabel = createElement("span", {}, [this.context.locale.contextMenu.customColorLabel || "Custom…"]);
10582
13299
  const offCustom = on(colorInput, "change", () => this._applyColor(it.colorType, colorInput.value));
10583
13300
  this._menuDisposers.push(offCustom);
10584
13301
  customRow.appendChild(colorInput);
@@ -10602,7 +13319,7 @@ var ContextMenu = class {
10602
13319
  iconSpan.innerHTML = it.icon;
10603
13320
  headerBtn.appendChild(iconSpan);
10604
13321
  }
10605
- headerBtn.appendChild(createElement("span", { class: "an-context-label" }, [it.label || "Insert Table"]));
13322
+ headerBtn.appendChild(createElement("span", { class: "an-context-label" }, [this.context.locale.contextMenu.table || it.label || "Insert Table"]));
10606
13323
  const chevron = createElement("span", {
10607
13324
  class: "an-context-chevron",
10608
13325
  "aria-hidden": "true"
@@ -10631,7 +13348,7 @@ var ContextMenu = class {
10631
13348
  cells.forEach((cell) => {
10632
13349
  cell.classList.toggle("active", +cell.dataset.row <= rows && +cell.dataset.col <= cols);
10633
13350
  });
10634
- labelEl.textContent = rows && cols ? `${cols} × ${rows}` : "Insert Table";
13351
+ labelEl.textContent = rows && cols ? `${cols} × ${rows}` : this.context.locale.contextMenu.table || "Insert Table";
10635
13352
  };
10636
13353
  panel.appendChild(gridEl);
10637
13354
  panel.appendChild(labelEl);
@@ -10685,7 +13402,7 @@ var ContextMenu = class {
10685
13402
  iconSpan.innerHTML = it.icon;
10686
13403
  btn.appendChild(iconSpan);
10687
13404
  }
10688
- btn.appendChild(createElement("span", { class: "an-context-label" }, [it.label || it.name]));
13405
+ btn.appendChild(createElement("span", { class: "an-context-label" }, [this.context.locale.contextMenu[it.name] || it.label || it.name]));
10689
13406
  const off = on(btn, "click", (e) => {
10690
13407
  e.stopPropagation();
10691
13408
  this.hide();
@@ -10705,12 +13422,18 @@ var ContextMenu = class {
10705
13422
  if (!editable.contains(event.target)) return;
10706
13423
  if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
10707
13424
  event.preventDefault();
10708
- this._lastX = event.clientX;
10709
- this._lastY = event.clientY;
10710
13425
  const winSel = window.getSelection();
10711
13426
  this._savedRange = winSel && winSel.rangeCount > 0 ? winSel.getRangeAt(0).cloneRange() : null;
10712
13427
  this._renderItems(this._items);
10713
- this.showAt(event.clientX, event.clientY);
13428
+ let openX = event.clientX;
13429
+ let openY = event.clientY;
13430
+ if (this._savedRange && !this._savedRange.collapsed) try {
13431
+ const selRect = this._savedRange.getBoundingClientRect();
13432
+ if (selRect.width > 0 && selRect.height > 0) openY = selRect.bottom + 4;
13433
+ } catch (_) {}
13434
+ this._lastX = openX;
13435
+ this._lastY = openY;
13436
+ this.showAt(openX, openY);
10714
13437
  }
10715
13438
  _maybeHide(event) {
10716
13439
  if (!this.el) return;
@@ -10721,28 +13444,20 @@ var ContextMenu = class {
10721
13444
  this.el.style.display = "block";
10722
13445
  this._reposition(x, y);
10723
13446
  this.el.setAttribute("aria-hidden", "false");
13447
+ this.context.triggerEvent("contextMenu:show");
10724
13448
  }
10725
13449
  _reposition(x, y) {
10726
13450
  if (!this.el) return;
10727
13451
  const rx = x !== void 0 ? x : this._lastX;
10728
13452
  const ry = y !== void 0 ? y : this._lastY;
10729
- const rect = this.el.getBoundingClientRect();
13453
+ const w = this.el.offsetWidth;
13454
+ const h = this.el.offsetHeight;
10730
13455
  let left = rx;
10731
13456
  let top = ry;
10732
- if (left + rect.width > window.innerWidth) left = window.innerWidth - rect.width - 8;
13457
+ if (left + w > window.innerWidth - 8) left = window.innerWidth - w - 8;
10733
13458
  if (left < 8) left = 8;
10734
- if (top + rect.height > window.innerHeight) top = window.innerHeight - rect.height - 8;
13459
+ if (top + h > window.innerHeight - 8) top = window.innerHeight - h - 8;
10735
13460
  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 (_) {}
10746
13461
  this.el.style.left = `${left}px`;
10747
13462
  this.el.style.top = `${top}px`;
10748
13463
  }
@@ -10750,6 +13465,7 @@ var ContextMenu = class {
10750
13465
  if (!this.el) return;
10751
13466
  this.el.style.display = "none";
10752
13467
  this.el.setAttribute("aria-hidden", "true");
13468
+ this.context.triggerEvent("contextMenu:hide");
10753
13469
  }
10754
13470
  /** Read the current selection's text or highlight color for the strip.
10755
13471
  * @param {'foreColor'|'hiliteColor'} type
@@ -10999,22 +13715,22 @@ var ShortcutsDialog = class {
10999
13715
  class: "an-dialog-overlay",
11000
13716
  role: "dialog",
11001
13717
  "aria-modal": "true",
11002
- "aria-label": "Keyboard Shortcuts"
13718
+ "aria-label": this.context.locale.shortcutsDialog.ariaLabel
11003
13719
  });
11004
13720
  const box = createElement("div", { class: "an-dialog-box an-shortcuts-box" });
11005
13721
  const titleRow = createElement("div", { class: "an-icon-title-row" });
11006
13722
  const title = createElement("h3", { class: "an-dialog-title" });
11007
- title.textContent = "Keyboard Shortcuts";
13723
+ title.textContent = this.context.locale.shortcutsDialog.title;
11008
13724
  const closeBtn = createElement("button", {
11009
13725
  type: "button",
11010
13726
  class: "an-icon-close",
11011
- "aria-label": "Close"
13727
+ "aria-label": this.context.locale.shortcutsDialog.close
11012
13728
  });
11013
13729
  closeBtn.textContent = "×";
11014
13730
  this._closeBtn = closeBtn;
11015
13731
  titleRow.append(title, closeBtn);
11016
13732
  box.appendChild(titleRow);
11017
- SHORTCUTS.forEach(({ category, items }) => {
13733
+ (this.context.locale.shortcutsDialog.shortcuts || SHORTCUTS).forEach(({ category, items }) => {
11018
13734
  const catEl = createElement("div", { class: "an-shortcuts-cat" });
11019
13735
  catEl.textContent = category;
11020
13736
  box.appendChild(catEl);
@@ -11139,25 +13855,26 @@ var FindReplace = class {
11139
13855
  const isReplace = this._mode === "replace";
11140
13856
  if (replaceRow) replaceRow.style.display = isReplace ? "" : "none";
11141
13857
  if (replaceActions) replaceActions.style.display = isReplace ? "" : "none";
11142
- if (title) title.textContent = isReplace ? "Find & Replace" : "Find";
13858
+ if (title) title.textContent = isReplace ? this.context.locale.findReplace.findReplaceTitle : this.context.locale.findReplace.findTitle;
11143
13859
  }
11144
13860
  _buildDialog() {
13861
+ const L = this.context.locale.findReplace;
11145
13862
  const overlay = createElement("div", {
11146
13863
  class: "an-dialog-overlay an-fr-dialog",
11147
13864
  role: "dialog",
11148
13865
  "aria-modal": "true",
11149
- "aria-label": "Find and Replace"
13866
+ "aria-label": L.findReplaceTitle
11150
13867
  });
11151
13868
  const box = createElement("div", { class: "an-dialog-box" });
11152
13869
  const titleRow = createElement("div", { class: "an-icon-title-row" });
11153
13870
  const title = createElement("h3", { class: "an-dialog-title" });
11154
- title.textContent = "Find";
13871
+ title.textContent = L.findTitle;
11155
13872
  const closeBtn = createElement("button", {
11156
13873
  type: "button",
11157
13874
  class: "an-icon-close",
11158
13875
  "aria-label": "Close"
11159
13876
  });
11160
- closeBtn.textContent = "×";
13877
+ closeBtn.textContent = L.close;
11161
13878
  this._closeBtn = closeBtn;
11162
13879
  titleRow.append(title, closeBtn);
11163
13880
  box.appendChild(titleRow);
@@ -11165,8 +13882,8 @@ var FindReplace = class {
11165
13882
  const findInput = createElement("input", {
11166
13883
  type: "text",
11167
13884
  class: "an-input",
11168
- placeholder: "Find…",
11169
- "aria-label": "Search text"
13885
+ placeholder: L.findPlaceholder,
13886
+ "aria-label": L.searchAriaLabel
11170
13887
  });
11171
13888
  this._findInput = findInput;
11172
13889
  findRow.appendChild(findInput);
@@ -11178,7 +13895,7 @@ var FindReplace = class {
11178
13895
  "aria-label": "Case sensitive"
11179
13896
  });
11180
13897
  this._caseCheckbox = caseCheckbox;
11181
- caseLabel.append(caseCheckbox, document.createTextNode("\xA0Case sensitive"));
13898
+ caseLabel.append(caseCheckbox, document.createTextNode(L.caseSensitive));
11182
13899
  const counter = createElement("span", { class: "an-fr-counter" });
11183
13900
  this._counterEl = counter;
11184
13901
  optRow.append(caseLabel, counter);
@@ -11188,12 +13905,12 @@ var FindReplace = class {
11188
13905
  type: "button",
11189
13906
  class: "an-btn"
11190
13907
  });
11191
- prevBtn.textContent = "← Prev";
13908
+ prevBtn.textContent = L.prevBtn;
11192
13909
  const nextBtn = createElement("button", {
11193
13910
  type: "button",
11194
13911
  class: "an-btn an-btn-primary"
11195
13912
  });
11196
- nextBtn.textContent = "Next →";
13913
+ nextBtn.textContent = L.nextBtn;
11197
13914
  findActions.append(prevBtn, nextBtn);
11198
13915
  box.appendChild(findActions);
11199
13916
  const replaceRow = createElement("div", { class: "an-fr-replace-row" });
@@ -11201,8 +13918,8 @@ var FindReplace = class {
11201
13918
  const replaceInput = createElement("input", {
11202
13919
  type: "text",
11203
13920
  class: "an-input",
11204
- placeholder: "Replace with…",
11205
- "aria-label": "Replace with"
13921
+ placeholder: L.replacePlaceholder,
13922
+ "aria-label": L.replaceAriaLabel
11206
13923
  });
11207
13924
  this._replaceInput = replaceInput;
11208
13925
  replaceRow.appendChild(replaceInput);
@@ -11213,12 +13930,12 @@ var FindReplace = class {
11213
13930
  type: "button",
11214
13931
  class: "an-btn"
11215
13932
  });
11216
- replaceBtn.textContent = "Replace";
13933
+ replaceBtn.textContent = L.replaceBtn;
11217
13934
  const replaceAllBtn = createElement("button", {
11218
13935
  type: "button",
11219
13936
  class: "an-btn an-btn-primary"
11220
13937
  });
11221
- replaceAllBtn.textContent = "Replace All";
13938
+ replaceAllBtn.textContent = L.replaceAllBtn;
11222
13939
  replaceActions.append(replaceBtn, replaceAllBtn);
11223
13940
  box.appendChild(replaceActions);
11224
13941
  overlay.appendChild(box);
@@ -11863,6 +14580,771 @@ var ImageCropOverlay = class {
11863
14580
  }
11864
14581
  };
11865
14582
  //#endregion
14583
+ //#region src/js/module/AutoSaveRestore.js
14584
+ /**
14585
+ * AutoSaveRestore.js — Detects a previously auto-saved draft and offers the
14586
+ * user a banner to restore or discard it.
14587
+ *
14588
+ * Activated when both `autoSave` and `autoSaveRestore` options are true.
14589
+ * On initialize it checks localStorage for a draft that is within the
14590
+ * `autoSaveRestoreTimeout` day window. If one is found a dismissible banner
14591
+ * is prepended to the editor container.
14592
+ */
14593
+ var AutoSaveRestore = class {
14594
+ /** @param {import('../Context.js').Context} context */
14595
+ constructor(context) {
14596
+ this.context = context;
14597
+ this.options = context.options;
14598
+ /** @type {HTMLElement|null} */
14599
+ this._banner = null;
14600
+ }
14601
+ initialize() {
14602
+ if (!this.options.autoSave || !this.options.autoSaveRestore) return this;
14603
+ const key = this.options.autoSaveKey;
14604
+ const metaKey = key + ":asrmeta";
14605
+ let saved;
14606
+ let meta;
14607
+ try {
14608
+ saved = localStorage.getItem(key);
14609
+ meta = JSON.parse(localStorage.getItem(metaKey) || "{}");
14610
+ } catch (_) {
14611
+ return this;
14612
+ }
14613
+ if (!saved) return this;
14614
+ const timeout = this.options.autoSaveRestoreTimeout;
14615
+ if (timeout > 0) {
14616
+ if (Date.now() - (meta.savedAt || 0) > timeout * 864e5) {
14617
+ try {
14618
+ localStorage.removeItem(key);
14619
+ localStorage.removeItem(metaKey);
14620
+ } catch (_) {}
14621
+ return this;
14622
+ }
14623
+ }
14624
+ this._showBanner(saved, meta);
14625
+ return this;
14626
+ }
14627
+ destroy() {
14628
+ this._removeBanner();
14629
+ }
14630
+ _showBanner(draftHtml, meta) {
14631
+ const t = this.context.locale.autoSaveRestore || {};
14632
+ const label = t.found || "Draft found. Restore?";
14633
+ const restoreLabel = t.restore || "Restore";
14634
+ const discardLabel = t.discard || "Discard";
14635
+ const banner = document.createElement("div");
14636
+ banner.className = "an-asr-banner";
14637
+ banner.setAttribute("role", "alert");
14638
+ const msg = document.createElement("span");
14639
+ msg.className = "an-asr-msg";
14640
+ if (meta.savedAt) {
14641
+ const formatted = new Date(meta.savedAt).toLocaleString();
14642
+ msg.textContent = (t.foundAt || "Draft from {date}. Restore?").replace("{date}", formatted);
14643
+ } else msg.textContent = label;
14644
+ const restoreBtn = document.createElement("button");
14645
+ restoreBtn.type = "button";
14646
+ restoreBtn.className = "an-btn an-asr-btn-restore";
14647
+ restoreBtn.textContent = restoreLabel;
14648
+ restoreBtn.addEventListener("click", () => this._restore(draftHtml));
14649
+ const discardBtn = document.createElement("button");
14650
+ discardBtn.type = "button";
14651
+ discardBtn.className = "an-btn an-asr-btn-discard";
14652
+ discardBtn.textContent = discardLabel;
14653
+ discardBtn.addEventListener("click", () => this._discard());
14654
+ banner.appendChild(msg);
14655
+ banner.appendChild(restoreBtn);
14656
+ banner.appendChild(discardBtn);
14657
+ this._banner = banner;
14658
+ const container = this.context.layoutInfo.container;
14659
+ container.insertBefore(banner, container.firstChild);
14660
+ }
14661
+ _restore(draftHtml) {
14662
+ this.context.setHTML(draftHtml);
14663
+ this.context.clearHistory();
14664
+ this._removeBanner();
14665
+ if (typeof this.options.onAutoSaveRestore === "function") this.options.onAutoSaveRestore(draftHtml, this.context);
14666
+ }
14667
+ _discard() {
14668
+ const key = this.options.autoSaveKey;
14669
+ try {
14670
+ localStorage.removeItem(key);
14671
+ localStorage.removeItem(key + ":asrmeta");
14672
+ } catch (_) {}
14673
+ this._removeBanner();
14674
+ }
14675
+ _removeBanner() {
14676
+ if (this._banner && this._banner.parentNode) this._banner.parentNode.removeChild(this._banner);
14677
+ this._banner = null;
14678
+ }
14679
+ };
14680
+ //#endregion
14681
+ //#region src/js/module/MarkdownShortcuts.js
14682
+ /**
14683
+ * MarkdownShortcuts.js — Convert Markdown-style syntax typed directly in the
14684
+ * editor into rich HTML elements (input rules / auto-format).
14685
+ *
14686
+ * Activated when `markdownShortcuts: true` (the default).
14687
+ *
14688
+ * Block rules — triggered by Space or Enter at the start of a line:
14689
+ * #[#[#]]· → H1 / H2 / H3
14690
+ * >· → blockquote
14691
+ * -· or *· → unordered list item
14692
+ * 1.· → ordered list item
14693
+ * [ ]· → checklist item
14694
+ * --- → horizontal rule (on Enter)
14695
+ * ``` → code block (on Enter)
14696
+ *
14697
+ * Inline rules — triggered when the closing marker is typed:
14698
+ * **text** → <strong>
14699
+ * *text* → <em>
14700
+ * ~~text~~ → <s>
14701
+ * `code` → <code>
14702
+ */
14703
+ var MarkdownShortcuts = class {
14704
+ /** @param {import('../Context.js').Context} context */
14705
+ constructor(context) {
14706
+ this.context = context;
14707
+ this.options = context.options;
14708
+ this._disposers = [];
14709
+ }
14710
+ initialize() {
14711
+ if (!this.options.markdownShortcuts) return this;
14712
+ const editable = this.context.layoutInfo.editable;
14713
+ const d1 = on(
14714
+ editable,
14715
+ "keydown",
14716
+ /** @param {KeyboardEvent} e */
14717
+ (e) => this._onKeydown(e)
14718
+ );
14719
+ const d2 = on(editable, "input", () => this._onInput());
14720
+ this._disposers.push(d1, d2);
14721
+ return this;
14722
+ }
14723
+ destroy() {
14724
+ this._disposers.forEach((d) => d());
14725
+ this._disposers = [];
14726
+ }
14727
+ _onKeydown(e) {
14728
+ if (e.key === " ") {
14729
+ if (this._applyBlockRule()) e.preventDefault();
14730
+ } else if (e.key === "Enter") {
14731
+ if (this._applyEnterRule()) e.preventDefault();
14732
+ }
14733
+ }
14734
+ /**
14735
+ * Returns the plain text of the line the cursor is on, up to the cursor.
14736
+ * @returns {{ text: string, range: Range, lineNode: Node } | null}
14737
+ */
14738
+ _getLineContext() {
14739
+ const sel = window.getSelection();
14740
+ if (!sel || !sel.rangeCount) return null;
14741
+ const range = sel.getRangeAt(0);
14742
+ if (!range.collapsed) return null;
14743
+ const editable = this.context.layoutInfo.editable;
14744
+ if (!editable.contains(range.startContainer)) return null;
14745
+ let node = range.startContainer;
14746
+ while (node && node !== editable && !this._isBlock(node)) node = node.parentNode;
14747
+ if (!node || node === editable) node = range.startContainer;
14748
+ const tmpRange = document.createRange();
14749
+ tmpRange.setStart(node, 0);
14750
+ tmpRange.setEnd(range.startContainer, range.startOffset);
14751
+ return {
14752
+ text: tmpRange.toString(),
14753
+ range,
14754
+ lineNode: node
14755
+ };
14756
+ }
14757
+ _isBlock(node) {
14758
+ if (node.nodeType !== Node.ELEMENT_NODE) return false;
14759
+ const display = window.getComputedStyle(node).display;
14760
+ return display === "block" || display === "list-item" || display === "table-cell";
14761
+ }
14762
+ /** Applies block rule on Space key. Returns true if a rule fired. */
14763
+ _applyBlockRule() {
14764
+ const ctx = this._getLineContext();
14765
+ if (!ctx) return false;
14766
+ const { text } = ctx;
14767
+ const blockPatterns = [
14768
+ {
14769
+ re: /^(#{1,3})$/,
14770
+ handler: (m) => this._convertToHeading(m[1].length)
14771
+ },
14772
+ {
14773
+ re: /^>$/,
14774
+ handler: () => this._convertToBlockquote()
14775
+ },
14776
+ {
14777
+ re: /^[-*]$/,
14778
+ handler: () => this._convertToList("ul")
14779
+ },
14780
+ {
14781
+ re: /^1\.$/,
14782
+ handler: () => this._convertToList("ol")
14783
+ },
14784
+ {
14785
+ re: /^\[ \]$/,
14786
+ handler: () => this._convertToChecklist()
14787
+ }
14788
+ ];
14789
+ for (const { re, handler } of blockPatterns) {
14790
+ const m = text.match(re);
14791
+ if (m) {
14792
+ handler(m);
14793
+ return true;
14794
+ }
14795
+ }
14796
+ return false;
14797
+ }
14798
+ /** Applies block rule on Enter key (---, ```). Returns true if a rule fired. */
14799
+ _applyEnterRule() {
14800
+ const ctx = this._getLineContext();
14801
+ if (!ctx) return false;
14802
+ const { text } = ctx;
14803
+ if (/^-{3,}$/.test(text)) {
14804
+ this._convertToHr();
14805
+ return true;
14806
+ }
14807
+ if (/^`{3}/.test(text)) {
14808
+ this._convertToCodeBlock();
14809
+ return true;
14810
+ }
14811
+ return false;
14812
+ }
14813
+ _selectLineAndDelete() {
14814
+ const sel = window.getSelection();
14815
+ if (!sel || !sel.rangeCount) return;
14816
+ const range = sel.getRangeAt(0);
14817
+ const startRange = document.createRange();
14818
+ startRange.setStart(range.startContainer.parentNode || range.startContainer, 0);
14819
+ startRange.setEnd(range.startContainer, range.startOffset);
14820
+ startRange.deleteContents();
14821
+ }
14822
+ _convertToHeading(level) {
14823
+ this._selectLineAndDelete();
14824
+ document.execCommand("formatBlock", false, `h${level}`);
14825
+ this.context.triggerEvent("change", this.context.getHTML());
14826
+ }
14827
+ _convertToBlockquote() {
14828
+ this._selectLineAndDelete();
14829
+ document.execCommand("formatBlock", false, "blockquote");
14830
+ this.context.triggerEvent("change", this.context.getHTML());
14831
+ }
14832
+ _convertToList(type) {
14833
+ this._selectLineAndDelete();
14834
+ document.execCommand(type === "ul" ? "insertUnorderedList" : "insertOrderedList");
14835
+ this.context.triggerEvent("change", this.context.getHTML());
14836
+ }
14837
+ _convertToChecklist() {
14838
+ this._selectLineAndDelete();
14839
+ this.context.invoke("editor.checklist");
14840
+ this.context.triggerEvent("change", this.context.getHTML());
14841
+ }
14842
+ _convertToHr() {
14843
+ this._selectLineAndDelete();
14844
+ this.context.invoke("editor.insertHR");
14845
+ this.context.triggerEvent("change", this.context.getHTML());
14846
+ }
14847
+ _convertToCodeBlock() {
14848
+ this._selectLineAndDelete();
14849
+ document.execCommand("formatBlock", false, "pre");
14850
+ this.context.triggerEvent("change", this.context.getHTML());
14851
+ }
14852
+ _onInput() {
14853
+ const sel = window.getSelection();
14854
+ if (!sel || !sel.rangeCount) return;
14855
+ const range = sel.getRangeAt(0);
14856
+ if (!range.collapsed) return;
14857
+ if (!this.context.layoutInfo.editable.contains(range.startContainer)) return;
14858
+ const node = range.startContainer;
14859
+ if (node.nodeType !== Node.TEXT_NODE) return;
14860
+ const text = node.textContent;
14861
+ const offset = range.startOffset;
14862
+ const inlineRules = [
14863
+ {
14864
+ re: /\*\*(.+?)\*\*$/,
14865
+ tag: "strong"
14866
+ },
14867
+ {
14868
+ re: /(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)$/,
14869
+ tag: "em"
14870
+ },
14871
+ {
14872
+ re: /~~(.+?)~~$/,
14873
+ tag: "s"
14874
+ },
14875
+ {
14876
+ re: /`([^`]+)`$/,
14877
+ tag: "code"
14878
+ }
14879
+ ];
14880
+ const upToCursor = text.slice(0, offset);
14881
+ for (const { re, tag } of inlineRules) {
14882
+ const m = upToCursor.match(re);
14883
+ if (!m) continue;
14884
+ const matchStart = upToCursor.length - m[0].length;
14885
+ const matchEnd = offset;
14886
+ const innerText = m[1];
14887
+ const before = text.slice(0, matchStart);
14888
+ const after = text.slice(matchEnd);
14889
+ const el = document.createElement(tag);
14890
+ el.textContent = innerText;
14891
+ const beforeNode = document.createTextNode(before);
14892
+ const afterNode = document.createTextNode("​" + after);
14893
+ const parent = node.parentNode;
14894
+ parent.insertBefore(beforeNode, node);
14895
+ parent.insertBefore(el, node);
14896
+ parent.insertBefore(afterNode, node);
14897
+ parent.removeChild(node);
14898
+ const newRange = document.createRange();
14899
+ newRange.setStart(afterNode, 1);
14900
+ newRange.collapse(true);
14901
+ sel.removeAllRanges();
14902
+ sel.addRange(newRange);
14903
+ this.context.triggerEvent("change", this.context.getHTML());
14904
+ break;
14905
+ }
14906
+ }
14907
+ };
14908
+ //#endregion
14909
+ //#region src/js/module/BubbleToolbar.js
14910
+ /**
14911
+ * BubbleToolbar.js — A mini floating toolbar that appears above the current
14912
+ * text selection, allowing quick access to common formatting actions without
14913
+ * reaching for the main toolbar.
14914
+ *
14915
+ * Activated when `bubbleToolbar: true`. The set of visible buttons is
14916
+ * controlled by `bubbleToolbarItems` (array of button name strings).
14917
+ *
14918
+ * Built-in names: 'bold', 'italic', 'underline', 'strikethrough',
14919
+ * 'link', 'foreColor', 'removeFormat', 'inlineCode'.
14920
+ * Any name not found in the built-in map is silently ignored.
14921
+ */
14922
+ var _S = "stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"";
14923
+ var _svg = (p) => `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" ${_S} style="display:block">${p}</svg>`;
14924
+ var _ICONS = {
14925
+ bold: _svg("<path d=\"M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z\"/><path d=\"M6 12h9a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z\"/>"),
14926
+ italic: _svg("<line x1=\"19\" y1=\"4\" x2=\"10\" y2=\"4\"/><line x1=\"14\" y1=\"20\" x2=\"5\" y2=\"20\"/><line x1=\"15\" y1=\"4\" x2=\"9\" y2=\"20\"/>"),
14927
+ underline: _svg("<path d=\"M6 3v7a6 6 0 0 0 6 6 6 6 0 0 0 6-6V3\"/><line x1=\"4\" y1=\"21\" x2=\"20\" y2=\"21\"/>"),
14928
+ strikethrough: _svg("<path d=\"M17.3 12H6.7\"/><path d=\"M10 6.5C10 5.1 11.1 4 12.5 4c1.4 0 2.5 1.1 2.5 2.5 0 .8-.4 1.5-1 2\"/><path d=\"M14 17.5C14 19 12.9 20 11.5 20 10.1 20 9 18.9 9 17.5c0-.8.4-1.5 1-2\"/>"),
14929
+ link: _svg("<path d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"/><path d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"/>"),
14930
+ foreColor: _svg("<path d=\"M4 20L12 4L20 20\"/><line x1=\"7.5\" y1=\"14\" x2=\"16.5\" y2=\"14\"/>"),
14931
+ removeFormat: _svg("<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\"/>"),
14932
+ inlineCode: _svg("<path d=\"M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1\"/><path d=\"M16 3h1a2 2 0 0 1 2 2v5c0 1.1.9 2 2 2a2 2 0 0 1-2 2v5a2 2 0 0 1-2 2h-1\"/>")
14933
+ };
14934
+ var _ACTIONS = {
14935
+ bold: (ctx) => ctx.invoke("editor.bold"),
14936
+ italic: (ctx) => ctx.invoke("editor.italic"),
14937
+ underline: (ctx) => ctx.invoke("editor.underline"),
14938
+ strikethrough: (ctx) => ctx.invoke("editor.strikethrough"),
14939
+ link: (ctx) => ctx.invoke("linkDialog.show"),
14940
+ foreColor: (ctx) => {
14941
+ const color = window.prompt("Text color (hex or name):", "#000000");
14942
+ if (color) ctx.invoke("editor.foreColor", color);
14943
+ },
14944
+ removeFormat: (ctx) => ctx.invoke("editor.removeFormat"),
14945
+ inlineCode: (ctx) => ctx.invoke("editor.inlineCode")
14946
+ };
14947
+ var _ACTIVE = {
14948
+ bold: () => document.queryCommandState("bold"),
14949
+ italic: () => document.queryCommandState("italic"),
14950
+ underline: () => document.queryCommandState("underline"),
14951
+ strikethrough: () => document.queryCommandState("strikeThrough")
14952
+ };
14953
+ var BubbleToolbar = class {
14954
+ /** @param {import('../Context.js').Context} context */
14955
+ constructor(context) {
14956
+ this.context = context;
14957
+ this.options = context.options;
14958
+ /** @type {HTMLElement|null} */
14959
+ this._el = null;
14960
+ this._visible = false;
14961
+ this._rafId = null;
14962
+ this._contextMenuOpen = false;
14963
+ this._disposers = [];
14964
+ this._onSelectionChange = this._onSelectionChange.bind(this);
14965
+ this._onMousedown = this._onMousedown.bind(this);
14966
+ this._onKeydown = this._onKeydown.bind(this);
14967
+ this._onContextMenu = this._onContextMenu.bind(this);
14968
+ }
14969
+ initialize() {
14970
+ if (!this.options.bubbleToolbar) return this;
14971
+ this._build();
14972
+ const d1 = on(document, "selectionchange", this._onSelectionChange);
14973
+ const d2 = on(document, "mousedown", this._onMousedown);
14974
+ const d3 = on(document, "keydown", this._onKeydown);
14975
+ const d4 = on(document, "contextmenu", this._onContextMenu);
14976
+ const d5 = this.context.on("contextMenu:show", () => {
14977
+ this._contextMenuOpen = true;
14978
+ this._hide();
14979
+ });
14980
+ const d6 = this.context.on("contextMenu:hide", () => {
14981
+ this._contextMenuOpen = false;
14982
+ });
14983
+ this._disposers.push(d1, d2, d3, d4, d5, d6);
14984
+ return this;
14985
+ }
14986
+ destroy() {
14987
+ if (this._el && this._el.parentNode) this._el.parentNode.removeChild(this._el);
14988
+ this._el = null;
14989
+ this._disposers.forEach((d) => d());
14990
+ this._disposers = [];
14991
+ cancelAnimationFrame(this._rafId);
14992
+ }
14993
+ _build() {
14994
+ const el = document.createElement("div");
14995
+ el.className = "an-bubble-toolbar";
14996
+ el.setAttribute("role", "toolbar");
14997
+ el.setAttribute("aria-label", "Formatting");
14998
+ const items = this.options.bubbleToolbarItems || [
14999
+ "bold",
15000
+ "italic",
15001
+ "underline",
15002
+ "link",
15003
+ "foreColor",
15004
+ "removeFormat"
15005
+ ];
15006
+ for (const name of items) {
15007
+ if (!_ICONS[name] || !_ACTIONS[name]) continue;
15008
+ const btn = document.createElement("button");
15009
+ btn.type = "button";
15010
+ btn.className = "an-bubble-btn";
15011
+ btn.dataset.name = name;
15012
+ btn.setAttribute("aria-label", name);
15013
+ btn.innerHTML = _ICONS[name];
15014
+ btn.addEventListener("mousedown", (e) => {
15015
+ e.preventDefault();
15016
+ });
15017
+ btn.addEventListener("click", (e) => {
15018
+ e.preventDefault();
15019
+ e.stopPropagation();
15020
+ this.context.invoke("editor.focus");
15021
+ _ACTIONS[name](this.context);
15022
+ this.context.invoke("editor.afterCommand");
15023
+ this._syncActive();
15024
+ });
15025
+ el.appendChild(btn);
15026
+ }
15027
+ document.body.appendChild(el);
15028
+ this._el = el;
15029
+ }
15030
+ _show(rect) {
15031
+ if (!this._el) return;
15032
+ const el = this._el;
15033
+ el.style.visibility = "hidden";
15034
+ el.style.display = "flex";
15035
+ const bw = el.offsetWidth;
15036
+ const bh = el.offsetHeight;
15037
+ const gap = 8;
15038
+ let left = rect.left + rect.width / 2 - bw / 2;
15039
+ let top = rect.top - bh - gap;
15040
+ left = Math.max(8, Math.min(left, window.innerWidth - bw - 8));
15041
+ if (top < 8) top = rect.bottom + gap;
15042
+ el.style.top = `${top}px`;
15043
+ el.style.left = `${left}px`;
15044
+ el.style.visibility = "";
15045
+ this._syncActive();
15046
+ this._visible = true;
15047
+ }
15048
+ _hide() {
15049
+ if (!this._el) return;
15050
+ this._el.style.display = "none";
15051
+ this._visible = false;
15052
+ }
15053
+ _syncActive() {
15054
+ if (!this._el) return;
15055
+ this._el.querySelectorAll(".an-bubble-btn").forEach((btn) => {
15056
+ const activeFn = _ACTIVE[btn.dataset.name];
15057
+ btn.classList.toggle("an-active", !!(activeFn && activeFn()));
15058
+ });
15059
+ }
15060
+ _onSelectionChange() {
15061
+ cancelAnimationFrame(this._rafId);
15062
+ this._rafId = requestAnimationFrame(() => {
15063
+ if (this._contextMenuOpen) return;
15064
+ const sel = window.getSelection();
15065
+ if (!sel || sel.isCollapsed || !sel.rangeCount) {
15066
+ this._hide();
15067
+ return;
15068
+ }
15069
+ if (!this.context.layoutInfo.editable.contains(sel.anchorNode)) {
15070
+ this._hide();
15071
+ return;
15072
+ }
15073
+ if (this.context.options.readOnly) {
15074
+ this._hide();
15075
+ return;
15076
+ }
15077
+ const rect = sel.getRangeAt(0).getBoundingClientRect();
15078
+ if (!rect || rect.width === 0) {
15079
+ this._hide();
15080
+ return;
15081
+ }
15082
+ this._show(rect);
15083
+ });
15084
+ }
15085
+ _onMousedown(e) {
15086
+ if (!this._visible) return;
15087
+ if (this._el && this._el.contains(e.target)) return;
15088
+ if (this.context.layoutInfo.editable.contains(e.target)) return;
15089
+ this._hide();
15090
+ }
15091
+ _onKeydown(e) {
15092
+ if (e.key === "Escape" && this._visible) this._hide();
15093
+ }
15094
+ _onContextMenu() {
15095
+ this._hide();
15096
+ }
15097
+ };
15098
+ //#endregion
15099
+ //#region src/js/module/Mention.js
15100
+ /**
15101
+ * Mention.js — @mention autocomplete support.
15102
+ *
15103
+ * Activated when `mention.onSearch` option is provided.
15104
+ *
15105
+ * When the user types the trigger character (default `@`) followed by at least
15106
+ * `mention.minChars` characters, `onSearch(query, callback)` is called.
15107
+ * The callback receives an array of `{ id, label, avatar? }` items which are
15108
+ * rendered in a floating dropdown. Selecting an item inserts a non-editable
15109
+ * mention chip and fires `onInsert` (if provided) to override the default HTML.
15110
+ *
15111
+ * Options shape (passed as `mention` option object):
15112
+ * {
15113
+ * trigger: '@',
15114
+ * minChars: 1,
15115
+ * maxResults: 8,
15116
+ * debounce: 200,
15117
+ * onSearch: (query, callback) => void,
15118
+ * onInsert: (item) => string | null,
15119
+ * mentionClass: 'an-mention',
15120
+ * allowSpaces: false,
15121
+ * }
15122
+ */
15123
+ var Mention = class {
15124
+ /** @param {import('../Context.js').Context} context */
15125
+ constructor(context) {
15126
+ this.context = context;
15127
+ /** @type {HTMLElement|null} */
15128
+ this._dropdown = null;
15129
+ /** @type {string} */
15130
+ this._query = "";
15131
+ /** @type {number|null} */
15132
+ this._debounceTimer = null;
15133
+ /** @type {number} current highlighted index */
15134
+ this._activeIndex = -1;
15135
+ /** @type {Array<{id, label, avatar?}>} */
15136
+ this._items = [];
15137
+ /** @type {boolean} */
15138
+ this._open = false;
15139
+ /** Position of trigger character in the text node */
15140
+ this._triggerNode = null;
15141
+ this._triggerOffset = 0;
15142
+ /** @type {DOMRect|null} Caret rect captured synchronously during input event */
15143
+ this._caretRect = null;
15144
+ this._disposers = [];
15145
+ }
15146
+ initialize() {
15147
+ const cfg = this.context.options.mention;
15148
+ if (!cfg || typeof cfg.onSearch !== "function") return this;
15149
+ this._cfg = {
15150
+ trigger: cfg.trigger || "@",
15151
+ minChars: cfg.minChars ?? 0,
15152
+ maxResults: cfg.maxResults ?? 8,
15153
+ debounce: cfg.debounce ?? 200,
15154
+ onSearch: cfg.onSearch,
15155
+ onInsert: cfg.onInsert || null,
15156
+ mentionClass: cfg.mentionClass || "an-mention",
15157
+ allowSpaces: cfg.allowSpaces || false
15158
+ };
15159
+ this._buildDropdown();
15160
+ const editable = this.context.layoutInfo.editable;
15161
+ const d1 = on(editable, "keydown", (e) => this._onKeydown(e));
15162
+ const d2 = on(editable, "input", () => this._onInput());
15163
+ const d3 = on(document, "click", (e) => this._onDocClick(e));
15164
+ this._disposers.push(d1, d2, d3);
15165
+ return this;
15166
+ }
15167
+ destroy() {
15168
+ clearTimeout(this._debounceTimer);
15169
+ if (this._dropdown && this._dropdown.parentNode) this._dropdown.parentNode.removeChild(this._dropdown);
15170
+ this._dropdown = null;
15171
+ this._disposers.forEach((d) => d());
15172
+ this._disposers = [];
15173
+ }
15174
+ _buildDropdown() {
15175
+ const el = document.createElement("div");
15176
+ el.className = "an-mention-dropdown";
15177
+ el.setAttribute("role", "listbox");
15178
+ document.body.appendChild(el);
15179
+ this._dropdown = el;
15180
+ }
15181
+ _renderItems(items) {
15182
+ const dd = this._dropdown;
15183
+ dd.innerHTML = "";
15184
+ this._items = items.slice(0, this._cfg.maxResults);
15185
+ this._activeIndex = this._items.length > 0 ? 0 : -1;
15186
+ this._items.forEach((item, i) => {
15187
+ const li = document.createElement("div");
15188
+ li.className = "an-mention-item";
15189
+ li.setAttribute("role", "option");
15190
+ li.dataset.index = i;
15191
+ if (item.avatar) {
15192
+ const img = document.createElement("img");
15193
+ img.src = item.avatar;
15194
+ img.className = "an-mention-avatar";
15195
+ img.alt = "";
15196
+ li.appendChild(img);
15197
+ }
15198
+ const label = document.createElement("span");
15199
+ label.textContent = item.label;
15200
+ li.appendChild(label);
15201
+ li.addEventListener("mousedown", (e) => e.preventDefault());
15202
+ li.addEventListener("click", () => this._select(i));
15203
+ dd.appendChild(li);
15204
+ });
15205
+ this._highlightItem(this._activeIndex);
15206
+ }
15207
+ _highlightItem(index) {
15208
+ if (!this._dropdown) return;
15209
+ this._dropdown.querySelectorAll(".an-mention-item").forEach((el, i) => {
15210
+ el.classList.toggle("an-mention-active", i === index);
15211
+ });
15212
+ this._activeIndex = index;
15213
+ }
15214
+ _positionDropdown() {
15215
+ const dd = this._dropdown;
15216
+ const rect = this._caretRect;
15217
+ if (!rect || rect.height === 0) return;
15218
+ dd.style.visibility = "hidden";
15219
+ dd.style.display = "block";
15220
+ const ddh = dd.offsetHeight;
15221
+ const ddw = dd.offsetWidth;
15222
+ let top = rect.bottom + 4;
15223
+ let left = rect.left;
15224
+ if (rect.bottom + ddh + 8 > window.innerHeight) top = rect.top - ddh - 4;
15225
+ left = Math.max(8, Math.min(left, window.innerWidth - ddw - 8));
15226
+ dd.style.top = `${top}px`;
15227
+ dd.style.left = `${left}px`;
15228
+ dd.style.visibility = "";
15229
+ }
15230
+ _showDropdown() {
15231
+ this._open = true;
15232
+ this._positionDropdown();
15233
+ }
15234
+ _hideDropdown() {
15235
+ if (this._dropdown) this._dropdown.style.display = "none";
15236
+ this._open = false;
15237
+ this._items = [];
15238
+ this._activeIndex = -1;
15239
+ this._triggerNode = null;
15240
+ this._caretRect = null;
15241
+ this._query = "";
15242
+ }
15243
+ /**
15244
+ * Captures the caret rect synchronously during the input event.
15245
+ * Must be called while the DOM event is still live — getClientRects() on a
15246
+ * collapsed range is reliable at this point but often empty inside async callbacks.
15247
+ */
15248
+ _captureCaretRect() {
15249
+ if (this._triggerNode && this._triggerNode.isConnected) try {
15250
+ const r = document.createRange();
15251
+ const end = Math.min(this._triggerOffset + 1, this._triggerNode.textContent.length);
15252
+ r.setStart(this._triggerNode, this._triggerOffset);
15253
+ r.setEnd(this._triggerNode, end);
15254
+ const candidate = r.getBoundingClientRect();
15255
+ if (candidate.height > 0) {
15256
+ this._caretRect = candidate;
15257
+ return;
15258
+ }
15259
+ } catch (_) {}
15260
+ const sel = window.getSelection();
15261
+ if (!sel || !sel.rangeCount) return;
15262
+ const rects = sel.getRangeAt(0).getClientRects();
15263
+ if (rects.length > 0) this._caretRect = rects[rects.length - 1];
15264
+ }
15265
+ _getQueryAtCursor() {
15266
+ const sel = window.getSelection();
15267
+ if (!sel || !sel.rangeCount) return null;
15268
+ const range = sel.getRangeAt(0);
15269
+ if (!range.collapsed) return null;
15270
+ const node = range.startContainer;
15271
+ if (node.nodeType !== Node.TEXT_NODE) return null;
15272
+ const text = node.textContent.slice(0, range.startOffset);
15273
+ const trigger = this._cfg.trigger;
15274
+ const triggerIdx = text.lastIndexOf(trigger);
15275
+ if (triggerIdx === -1) return null;
15276
+ const afterTrigger = text.slice(triggerIdx + trigger.length);
15277
+ if (!this._cfg.allowSpaces && /\s/.test(afterTrigger)) return null;
15278
+ if (afterTrigger.length < this._cfg.minChars) return null;
15279
+ this._triggerNode = node;
15280
+ this._triggerOffset = triggerIdx;
15281
+ return afterTrigger;
15282
+ }
15283
+ _onInput() {
15284
+ if (!this._cfg) return;
15285
+ const query = this._getQueryAtCursor();
15286
+ if (query === null) {
15287
+ this._hideDropdown();
15288
+ return;
15289
+ }
15290
+ this._captureCaretRect();
15291
+ this._query = query;
15292
+ clearTimeout(this._debounceTimer);
15293
+ this._debounceTimer = setTimeout(() => {
15294
+ this._cfg.onSearch(this._query, (items) => {
15295
+ if (!Array.isArray(items) || items.length === 0) {
15296
+ this._hideDropdown();
15297
+ return;
15298
+ }
15299
+ this._renderItems(items);
15300
+ this._showDropdown();
15301
+ });
15302
+ }, this._cfg.debounce);
15303
+ }
15304
+ _onKeydown(e) {
15305
+ if (!this._cfg || !this._open) return;
15306
+ if (e.key === "ArrowDown") {
15307
+ e.preventDefault();
15308
+ const next = (this._activeIndex + 1) % this._items.length;
15309
+ this._highlightItem(next);
15310
+ } else if (e.key === "ArrowUp") {
15311
+ e.preventDefault();
15312
+ const prev = (this._activeIndex - 1 + this._items.length) % this._items.length;
15313
+ this._highlightItem(prev);
15314
+ } else if (e.key === "Enter" || e.key === "Tab") {
15315
+ if (this._activeIndex >= 0) {
15316
+ e.preventDefault();
15317
+ this._select(this._activeIndex);
15318
+ }
15319
+ } else if (e.key === "Escape") this._hideDropdown();
15320
+ }
15321
+ _onDocClick(e) {
15322
+ if (!this._open) return;
15323
+ if (this._dropdown && this._dropdown.contains(e.target)) return;
15324
+ this._hideDropdown();
15325
+ }
15326
+ _select(index) {
15327
+ const item = this._items[index];
15328
+ if (!item) return;
15329
+ if (this._triggerNode) {
15330
+ const node = this._triggerNode;
15331
+ node.textContent = node.textContent.slice(0, this._triggerOffset) + node.textContent.slice(this._triggerOffset + this._cfg.trigger.length + this._query.length);
15332
+ const sel = window.getSelection();
15333
+ const range = document.createRange();
15334
+ range.setStart(node, this._triggerOffset);
15335
+ range.collapse(true);
15336
+ sel.removeAllRanges();
15337
+ sel.addRange(range);
15338
+ }
15339
+ let html;
15340
+ if (typeof this._cfg.onInsert === "function") html = this._cfg.onInsert(item);
15341
+ if (!html) html = `<span class="${this._cfg.mentionClass}" data-mention-id="${item.id}" contenteditable="false">@${item.label}</span>`;
15342
+ this.context.invoke("editor.insertHTML", html + "&#8203;");
15343
+ this._hideDropdown();
15344
+ this.context.triggerEvent("change", this.context.getHTML());
15345
+ }
15346
+ };
15347
+ //#endregion
11866
15348
  //#region src/js/Context.js
11867
15349
  /**
11868
15350
  * Context.js - Central hub for the editor instance
@@ -11879,6 +15361,8 @@ var Context = class {
11879
15361
  constructor(targetEl, userOptions = {}) {
11880
15362
  this.targetEl = targetEl;
11881
15363
  this.options = mergeDeep(defaultOptions, userOptions);
15364
+ /** @type {import('./i18n/index.js').AsnLocale} */
15365
+ this.locale = resolveLocale(this.options.lang);
11882
15366
  /** @type {{ container: HTMLElement, editable: HTMLElement, toolbar?: HTMLElement, statusbar?: HTMLElement }} */
11883
15367
  this.layoutInfo = {};
11884
15368
  /** @type {Map<string, Function[]>} */
@@ -11939,6 +15423,10 @@ var Context = class {
11939
15423
  register("shortcutsDialog", ShortcutsDialog);
11940
15424
  register("findReplace", FindReplace);
11941
15425
  register("imageCropOverlay", ImageCropOverlay);
15426
+ register("autoSaveRestore", AutoSaveRestore);
15427
+ register("markdownShortcuts", MarkdownShortcuts);
15428
+ register("bubbleToolbar", BubbleToolbar);
15429
+ register("mention", Mention);
11942
15430
  for (const [name, ModuleClass] of _customModules) register(name, ModuleClass);
11943
15431
  }
11944
15432
  /**
@@ -11970,7 +15458,9 @@ var Context = class {
11970
15458
  if (this.options.autoSave && this.options.autoSaveKey) {
11971
15459
  const d4 = this.on("change", () => {
11972
15460
  try {
11973
- localStorage.setItem(this.options.autoSaveKey, this.getHTML());
15461
+ const key = this.options.autoSaveKey;
15462
+ localStorage.setItem(key, this.getHTML());
15463
+ localStorage.setItem(key + ":asrmeta", JSON.stringify({ savedAt: Date.now() }));
11974
15464
  } catch (_) {}
11975
15465
  });
11976
15466
  this._disposers.push(d4);
@@ -12388,7 +15878,7 @@ var AutumnNote = {
12388
15878
  registerModule(name, ModuleClass) {
12389
15879
  _customModules.set(name, ModuleClass);
12390
15880
  },
12391
- version: "1.0.4"
15881
+ version: "1.0.9"
12392
15882
  };
12393
15883
  /**
12394
15884
  * @param {string|Element|NodeList|Element[]} selector
@@ -12401,6 +15891,6 @@ function resolveElements(selector) {
12401
15891
  return [];
12402
15892
  }
12403
15893
  //#endregion
12404
- export { Context, ELEMENT_NODE, TEXT_NODE, WrappedRange, alignCenterBtn, alignJustifyBtn, alignLeftBtn, alignRightBtn, all, ancestors, any, backColorBtn, boldBtn, checklistBtn, children, chunk, clamp, closest, closestPara, codeviewBtn, collapsedRange, compose, createElement, currentRange, debounce, AutumnNote as default, defaultOptions, defaultToolbar, directionBtn, emojiBtn, env, findBtn, findReplaceBtn, first, flatten, fontFamilyBtn, fontSizeBtn, foreColorBtn, fromNativeRange, fullscreenBtn, groupBy, hrBtn, iconBtn, identity, imageBtn, indentBtn, initial, inlineCodeBtn, insertAfter, isAnchor, isEditable, isElement, isEmpty, isFunction, isImage, isInline, isInsideEditable, isKey, isLi, isList, isModifier, isNil, isPara, isPlainObject, isSelectionInside, isString, isTable, isText, isVoid, italicBtn, key, last, lineHeightBtn, linkBtn, mergeDeep, nextElement, nodeValue, olBtn, on, outdentBtn, outerHtml, paragraphStyleBtn, placeCaret, prevElement, printBtn, rangeFromElement, rect2bnd, redoBtn, remove, removeFormatBtn, sanitiseHTML, sanitiseUrl, shortcutsBtn, splitText, strikeBtn, subscriptBtn, superscriptBtn, tableBtn, tail, throttle, trapFocus, ulBtn, underlineBtn, undoBtn, unique, unwrap, videoBtn, withSavedRange, wrap };
15894
+ export { Context, ELEMENT_NODE, TEXT_NODE, WrappedRange, alignCenterBtn, alignJustifyBtn, alignLeftBtn, alignRightBtn, all, ancestors, any, backColorBtn, boldBtn, checklistBtn, children, chunk, clamp, closest, closestPara, codeviewBtn, collapsedRange, compose, createElement, currentRange, debounce, AutumnNote as default, defaultOptions, defaultToolbar, directionBtn, emojiBtn, env, findBtn, findReplaceBtn, first, flatten, fontFamilyBtn, fontSizeBtn, foreColorBtn, fromNativeRange, fullscreenBtn, groupBy, hrBtn, iconBtn, identity, imageBtn, indentBtn, initial, inlineCodeBtn, insertAfter, isAnchor, isEditable, isElement, isEmpty, isFunction, isImage, isInline, isInsideEditable, isKey, isLi, isList, isModifier, isNil, isPara, isPlainObject, isSelectionInside, isString, isTable, isText, isVoid, italicBtn, key, last, lineHeightBtn, linkBtn, locales, mergeDeep, nextElement, nodeValue, olBtn, on, outdentBtn, outerHtml, paragraphStyleBtn, placeCaret, prevElement, printBtn, rangeFromElement, rect2bnd, redoBtn, remove, removeFormatBtn, resolveLocale, sanitiseHTML, sanitiseUrl, shortcutsBtn, splitText, strikeBtn, subscriptBtn, superscriptBtn, tableBtn, tail, throttle, trapFocus, ulBtn, underlineBtn, undoBtn, unique, unwrap, videoBtn, withSavedRange, wrap };
12405
15895
 
12406
15896
  //# sourceMappingURL=autumnnote.es.js.map