autumnnote 2.1.0 → 2.3.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 (65) hide show
  1. package/README.md +74 -6
  2. package/dist/autumnnote.cjs +27 -22
  3. package/dist/autumnnote.es.js +474 -591
  4. package/dist/autumnnote.es.js.map +1 -1
  5. package/dist/autumnnote.min.js +27 -22
  6. package/dist/autumnnote.umd.js +27 -22
  7. package/dist/autumnnote.umd.js.map +1 -1
  8. package/dist/icon-data-V0Xqv-wX.js +255 -0
  9. package/dist/icon-data-V0Xqv-wX.js.map +1 -0
  10. package/package.json +12 -3
  11. package/types/index.d.ts +8 -0
  12. package/src/js/Context.js +0 -854
  13. package/src/js/core/detectLang.js +0 -98
  14. package/src/js/core/dom.js +0 -372
  15. package/src/js/core/env.js +0 -38
  16. package/src/js/core/key.js +0 -66
  17. package/src/js/core/lists.js +0 -121
  18. package/src/js/core/markdown.js +0 -695
  19. package/src/js/core/range.js +0 -194
  20. package/src/js/core/sanitise.js +0 -304
  21. package/src/js/editing/History.js +0 -266
  22. package/src/js/editing/Style.js +0 -812
  23. package/src/js/editing/Table.js +0 -105
  24. package/src/js/editing/Typing.js +0 -397
  25. package/src/js/index.js +0 -193
  26. package/src/js/index.umd.js +0 -17
  27. package/src/js/module/AutoSaveRestore.js +0 -125
  28. package/src/js/module/BaseDialog.js +0 -133
  29. package/src/js/module/BaseMediaTooltip.js +0 -142
  30. package/src/js/module/BaseResizer.js +0 -322
  31. package/src/js/module/BubbleToolbar.js +0 -483
  32. package/src/js/module/Buttons.js +0 -399
  33. package/src/js/module/Clipboard.js +0 -579
  34. package/src/js/module/CodeTooltip.js +0 -493
  35. package/src/js/module/Codeview.js +0 -125
  36. package/src/js/module/ContextMenu.js +0 -621
  37. package/src/js/module/Editor.js +0 -747
  38. package/src/js/module/EmojiDialog.js +0 -254
  39. package/src/js/module/FindReplace.js +0 -512
  40. package/src/js/module/Fullscreen.js +0 -80
  41. package/src/js/module/IconDialog.js +0 -618
  42. package/src/js/module/ImageCropOverlay.js +0 -586
  43. package/src/js/module/ImageDialog.js +0 -193
  44. package/src/js/module/ImageResizer.js +0 -42
  45. package/src/js/module/ImageTooltip.js +0 -285
  46. package/src/js/module/LinkDialog.js +0 -145
  47. package/src/js/module/LinkTooltip.js +0 -250
  48. package/src/js/module/MarkdownShortcuts.js +0 -250
  49. package/src/js/module/Mention.js +0 -365
  50. package/src/js/module/Placeholder.js +0 -51
  51. package/src/js/module/ShortcutsDialog.js +0 -111
  52. package/src/js/module/SlashMenu.js +0 -376
  53. package/src/js/module/Statusbar.js +0 -246
  54. package/src/js/module/TableTooltip.js +0 -1392
  55. package/src/js/module/Toolbar.js +0 -855
  56. package/src/js/module/VideoDialog.js +0 -193
  57. package/src/js/module/VideoResizer.js +0 -66
  58. package/src/js/module/VideoTooltip.js +0 -248
  59. package/src/js/module/emoji-data.js +0 -496
  60. package/src/js/module/table-grid.js +0 -102
  61. package/src/js/module/table-icons.js +0 -33
  62. package/src/js/renderer.js +0 -120
  63. package/src/js/settings.js +0 -214
  64. package/src/styles/_variables.scss +0 -48
  65. package/src/styles/autumnnote.scss +0 -2877
@@ -1,254 +0,0 @@
1
- /**
2
- * EmojiDialog.js - Browse and insert Unicode emoji / symbols (UTF-8 / utf8mb4)
3
- * Click an emoji to insert it directly at the caret — no extra "Insert" step.
4
- */
5
-
6
- import { createElement, on, makeDraggable } from '../core/dom.js';
7
- import { BaseDialog } from './BaseDialog.js';
8
-
9
- /**
10
- * Resolved emoji catalogue, shared by every editor instance on the page.
11
- * @type {{ EMOJI_CATS: Array, EMOJI_LIST: Array }|null}
12
- */
13
- let _emojiData = null;
14
- /** @type {Promise<{ EMOJI_CATS: Array, EMOJI_LIST: Array }>|null} */
15
- let _emojiDataPromise = null;
16
-
17
- /**
18
- * Loads the emoji catalogue on first use. The table is ~25 KB (≈6 KB gzip),
19
- * so it is split into its own chunk rather than shipped to every consumer —
20
- * including those whose toolbar never shows an emoji button.
21
- * @returns {Promise<{ EMOJI_CATS: Array, EMOJI_LIST: Array }>}
22
- */
23
- function loadEmojiData() {
24
- if (_emojiData) return Promise.resolve(_emojiData);
25
- _emojiDataPromise ??= import('./emoji-data.js').then((mod) => {
26
- _emojiData = { EMOJI_CATS: mod.EMOJI_CATS, EMOJI_LIST: mod.EMOJI_LIST };
27
- return _emojiData;
28
- });
29
- return _emojiDataPromise;
30
- }
31
-
32
- // ---------------------------------------------------------------------------
33
- // Dialog class
34
- // ---------------------------------------------------------------------------
35
-
36
- export class EmojiDialog extends BaseDialog {
37
- _activeCat = 'all';
38
-
39
- // ---------------------------------------------------------------------------
40
- // Lifecycle
41
- // ---------------------------------------------------------------------------
42
-
43
- initialize() {
44
- // Grid is built lazily in show() to avoid ~500 DOM nodes at load time.
45
- return this;
46
- }
47
-
48
- // ---------------------------------------------------------------------------
49
- // Public API
50
- // ---------------------------------------------------------------------------
51
-
52
- /**
53
- * Opens the picker, fetching the emoji catalogue chunk on first call.
54
- * Awaiting is optional — the dialog opens on its own once data arrives.
55
- * @returns {Promise<void>}
56
- */
57
- async show() {
58
- // Capture the caret before awaiting: the selection must be read while the
59
- // click that opened the dialog is still the current interaction.
60
- this._saveRange();
61
-
62
- if (!this._dialog) {
63
- const { EMOJI_CATS, EMOJI_LIST } = await loadEmojiData();
64
- // A second show() may have won the race while this one awaited.
65
- if (!this._dialog) {
66
- this._cats = EMOJI_CATS;
67
- this._list = EMOJI_LIST;
68
- this._dialog = this._buildDialog();
69
- document.body.appendChild(this._dialog);
70
- }
71
- }
72
- this._activeCat = 'all';
73
- this._searchInput.value = '';
74
- this._updateCatTabs();
75
- this._filterEmojis('', 'all');
76
- this._open();
77
- }
78
-
79
- // ---------------------------------------------------------------------------
80
- // Build dialog
81
- // ---------------------------------------------------------------------------
82
-
83
- _buildDialog() {
84
- const L = this.context.locale.emojiDialog;
85
- const overlay = createElement('div', {
86
- class: 'an-dialog-overlay',
87
- role: 'dialog',
88
- 'aria-modal': 'true',
89
- 'aria-label': L.ariaLabel,
90
- });
91
- const box = createElement('div', { class: 'an-dialog-box an-emoji-box' });
92
-
93
- // Title row
94
- const titleRow = createElement('div', { class: 'an-icon-title-row' });
95
- const titleGroup = createElement('div', { class: 'an-dialog-title-group' });
96
- const iconEl = createElement('span', { class: 'an-dialog-icon an-dialog-icon--sm' });
97
- iconEl.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M8 13s1.5 2 4 2 4-2 4-2"/><line x1="9" y1="9" x2="9.01" y2="9"/><line x1="15" y1="9" x2="15.01" y2="9"/></svg>`;
98
- const title = createElement('h3', { class: 'an-dialog-title' });
99
- title.textContent = L.title;
100
- titleGroup.append(iconEl, title);
101
- const closeBtn = createElement('button', { type: 'button', class: 'an-icon-close', 'aria-label': L.close });
102
- closeBtn.innerHTML = '&times;';
103
- titleRow.append(titleGroup, closeBtn);
104
-
105
- // Search
106
- const searchInput = /** @type {HTMLInputElement} */ (createElement('input', {
107
- type: 'search',
108
- class: 'an-input an-icon-search',
109
- placeholder: L.searchPlaceholder,
110
- autocomplete: 'off',
111
- }));
112
- this._searchInput = searchInput;
113
-
114
- // Category tabs
115
- const catBar = createElement('div', { class: 'an-icon-cats' });
116
- const allTab = createElement('button', { type: 'button', class: 'an-icon-cat active', 'data-cat': 'all' });
117
- allTab.textContent = L.all;
118
- catBar.appendChild(allTab);
119
- this._cats.forEach(({ id, label }) => {
120
- const tab = createElement('button', { type: 'button', class: 'an-icon-cat', 'data-cat': id });
121
- tab.textContent = L.categories?.[id] || label;
122
- catBar.appendChild(tab);
123
- });
124
- this._catBar = catBar;
125
-
126
- // Emoji grid — one button per emoji, click = immediate insert
127
- const grid = createElement('div', { class: 'an-emoji-grid' });
128
- this._list.forEach(([char, keywords, cat]) => {
129
- const cell = createElement('button', {
130
- type: 'button',
131
- class: 'an-emoji-cell',
132
- 'data-char': char,
133
- 'data-keywords': keywords,
134
- 'data-cat': cat,
135
- title: keywords.split(' ').slice(0, 2).join(' '),
136
- });
137
- cell.textContent = char;
138
- grid.appendChild(cell);
139
- });
140
- this._grid = grid;
141
- this._firstInput = searchInput;
142
-
143
- // Cancel only — clicking an emoji inserts immediately
144
- const btnRow = createElement('div', { class: 'an-dialog-actions' });
145
- const cancelBtn = createElement('button', { type: 'button', class: 'an-btn' });
146
- cancelBtn.textContent = L.cancelBtn;
147
- btnRow.appendChild(cancelBtn);
148
-
149
- box.append(titleRow, searchInput, catBar, grid, btnRow);
150
- overlay.appendChild(box);
151
- makeDraggable(titleRow, box);
152
-
153
- // Events
154
- const d1 = on(closeBtn, 'click', () => this._close());
155
- const d2 = on(cancelBtn, 'click', () => this._close());
156
- const d3 = on(overlay, 'click', (e) => { if (e.target === overlay) this._close(); });
157
- const d4 = on(searchInput, 'input', () => this._filterEmojis(searchInput.value, this._activeCat));
158
- const d5 = on(catBar, 'click', (e) => {
159
- const tab = /** @type {HTMLElement} */ (/** @type {Element} */ (e.target)?.closest('[data-cat]'));
160
- if (tab) {
161
- this._activeCat = tab.dataset.cat;
162
- this._updateCatTabs();
163
- this._filterEmojis(this._searchInput.value, this._activeCat);
164
- }
165
- });
166
- const d6 = on(grid, 'click', (e) => {
167
- const cell = /** @type {HTMLElement} */ (/** @type {Element} */ (e.target)?.closest('.an-emoji-cell'));
168
- if (cell) this._onEmojiClick(cell.dataset.char);
169
- });
170
- this._disposers.push(d1, d2, d3, d4, d5, d6);
171
- return overlay;
172
- }
173
-
174
- // ---------------------------------------------------------------------------
175
- // Filter
176
- // ---------------------------------------------------------------------------
177
-
178
- _updateCatTabs() {
179
- this._catBar.querySelectorAll('.an-icon-cat').forEach((tab) => {
180
- tab.classList.toggle('active', /** @type {HTMLElement} */ (tab).dataset.cat === this._activeCat);
181
- });
182
- }
183
-
184
- _filterEmojis(query, cat) {
185
- const q = (query || '').trim().toLowerCase();
186
- let count = 0;
187
- this._grid.querySelectorAll('.an-emoji-cell').forEach((cell) => {
188
- const hCell = /** @type {HTMLElement} */ (cell);
189
- const matchCat = !cat || cat === 'all' || hCell.dataset.cat === cat;
190
- const matchQuery = !q || hCell.dataset.keywords.includes(q) || hCell.dataset.char === q;
191
- const visible = matchCat && matchQuery;
192
- hCell.style.display = visible ? '' : 'none';
193
- if (visible) count++;
194
- });
195
- let empty = this._grid.querySelector('.an-icon-empty');
196
- if (!empty) {
197
- empty = createElement('div', { class: 'an-icon-empty' });
198
- empty.textContent = 'No emojis found';
199
- this._grid.appendChild(empty);
200
- }
201
- /** @type {HTMLElement} */ (empty).style.display = count > 0 ? 'none' : '';
202
- }
203
-
204
- // ---------------------------------------------------------------------------
205
- // Insert
206
- // ---------------------------------------------------------------------------
207
-
208
- _onEmojiClick(char) {
209
- const savedRange = this._savedRange;
210
- const editable = this.context.layoutInfo.editable;
211
-
212
- // 1. Restore selection while dialog is still mounted (same pattern as ImageDialog)
213
- if (savedRange) savedRange.select();
214
-
215
- const sel = globalThis.getSelection();
216
- let range = sel?.rangeCount > 0 ? sel.getRangeAt(0) : null;
217
- if (!range) {
218
- range = document.createRange();
219
- range.selectNodeContents(editable);
220
- range.collapse(false);
221
- }
222
-
223
- // 2. Insert emoji as a plain text node — no ZWS or execCommand needed.
224
- // Text nodes are natively navigable so the caret lands cleanly after.
225
- //
226
- // E-1: When the range covers the entire content of a <td>/<th>,
227
- // deleteContents() can drift the range endpoint outside the cell in some
228
- // browsers. Save the cell reference first and re-anchor after deletion.
229
- const _sc = range.startContainer;
230
- const _tdAnchor = /** @type {Element|null} */ (_sc.nodeType === 1 ? _sc : _sc.parentElement)
231
- ?.closest('td, th');
232
- range.deleteContents();
233
- if (_tdAnchor?.isConnected && !_tdAnchor.contains(range.startContainer)) {
234
- range.setStart(_tdAnchor, 0);
235
- range.collapse(true);
236
- }
237
- const textNode = document.createTextNode(char);
238
- range.insertNode(textNode);
239
-
240
- // 3. Place caret immediately after the emoji character
241
- range.setStartAfter(textNode);
242
- range.collapse(true);
243
- if (sel) {
244
- sel.removeAllRanges();
245
- sel.addRange(range);
246
- }
247
-
248
- // 4. Close and restore editor focus
249
- this._close();
250
- editable.focus();
251
- this.context.invoke('editor.afterCommand');
252
- }
253
-
254
- }