autumnnote 2.0.0 → 2.2.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 (64) hide show
  1. package/README.md +72 -5
  2. package/dist/autumnnote.cjs +20 -20
  3. package/dist/autumnnote.css +1 -1
  4. package/dist/autumnnote.es.js +661 -798
  5. package/dist/autumnnote.es.js.map +1 -1
  6. package/dist/autumnnote.min.js +20 -20
  7. package/dist/autumnnote.umd.js +20 -20
  8. package/dist/autumnnote.umd.js.map +1 -1
  9. package/dist/icon-data-V0Xqv-wX.js +255 -0
  10. package/dist/icon-data-V0Xqv-wX.js.map +1 -0
  11. package/package.json +13 -4
  12. package/types/index.d.ts +8 -0
  13. package/src/js/Context.js +0 -854
  14. package/src/js/core/detectLang.js +0 -98
  15. package/src/js/core/dom.js +0 -372
  16. package/src/js/core/env.js +0 -25
  17. package/src/js/core/key.js +0 -66
  18. package/src/js/core/lists.js +0 -121
  19. package/src/js/core/markdown.js +0 -695
  20. package/src/js/core/range.js +0 -194
  21. package/src/js/core/sanitise.js +0 -231
  22. package/src/js/editing/History.js +0 -266
  23. package/src/js/editing/Style.js +0 -812
  24. package/src/js/editing/Table.js +0 -105
  25. package/src/js/editing/Typing.js +0 -397
  26. package/src/js/index.js +0 -193
  27. package/src/js/index.umd.js +0 -17
  28. package/src/js/module/AutoSaveRestore.js +0 -125
  29. package/src/js/module/BaseDialog.js +0 -133
  30. package/src/js/module/BaseMediaTooltip.js +0 -142
  31. package/src/js/module/BaseResizer.js +0 -312
  32. package/src/js/module/BubbleToolbar.js +0 -483
  33. package/src/js/module/Buttons.js +0 -399
  34. package/src/js/module/Clipboard.js +0 -579
  35. package/src/js/module/CodeTooltip.js +0 -493
  36. package/src/js/module/Codeview.js +0 -125
  37. package/src/js/module/ContextMenu.js +0 -621
  38. package/src/js/module/Editor.js +0 -747
  39. package/src/js/module/EmojiDialog.js +0 -254
  40. package/src/js/module/FindReplace.js +0 -512
  41. package/src/js/module/Fullscreen.js +0 -80
  42. package/src/js/module/IconDialog.js +0 -618
  43. package/src/js/module/ImageCropOverlay.js +0 -586
  44. package/src/js/module/ImageDialog.js +0 -193
  45. package/src/js/module/ImageResizer.js +0 -42
  46. package/src/js/module/ImageTooltip.js +0 -285
  47. package/src/js/module/LinkDialog.js +0 -145
  48. package/src/js/module/LinkTooltip.js +0 -250
  49. package/src/js/module/MarkdownShortcuts.js +0 -250
  50. package/src/js/module/Mention.js +0 -365
  51. package/src/js/module/Placeholder.js +0 -51
  52. package/src/js/module/ShortcutsDialog.js +0 -111
  53. package/src/js/module/SlashMenu.js +0 -376
  54. package/src/js/module/Statusbar.js +0 -246
  55. package/src/js/module/TableTooltip.js +0 -1521
  56. package/src/js/module/Toolbar.js +0 -750
  57. package/src/js/module/VideoDialog.js +0 -193
  58. package/src/js/module/VideoResizer.js +0 -66
  59. package/src/js/module/VideoTooltip.js +0 -248
  60. package/src/js/module/emoji-data.js +0 -496
  61. package/src/js/renderer.js +0 -120
  62. package/src/js/settings.js +0 -214
  63. package/src/styles/_variables.scss +0 -48
  64. package/src/styles/autumnnote.scss +0 -2866
@@ -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
- }