autumnnote 1.0.9 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,15 +1,16 @@
1
- # AutumnNote
1
+ # Autumn Note
2
2
 
3
3
  <p align="center"><img src="image/banner.png" width="120" alt="AutumnNote Banner"/></p>
4
4
 
5
- [![Version](https://img.shields.io/badge/version-1.0.9-blue)](#)
5
+ [![npm](https://img.shields.io/npm/v/autumnnote?label=version&color=blue&logo=npm)](https://www.npmjs.com/package/autumnnote)
6
+ [![npm downloads](https://img.shields.io/npm/dw/autumnnote?label=downloads%2Fweek&logo=npm&color=cb3837)](https://www.npmjs.com/package/autumnnote)
7
+ [![Bundle Size](https://img.shields.io/bundlephobia/minzip/autumnnote?label=minzipped&color=success)](https://bundlephobia.com/package/autumnnote)
8
+ [![GitHub Stars](https://img.shields.io/github/stars/cmm-cmm/Autumn-Note?style=flat&logo=github&color=yellow)](https://github.com/cmm-cmm/Autumn-Note)
6
9
  [![CI](https://github.com/cmm-cmm/Autumn-Note/actions/workflows/pages.yml/badge.svg)](https://github.com/cmm-cmm/Autumn-Note/actions/workflows/pages.yml)
7
10
  [![JavaScript](https://img.shields.io/badge/JavaScript-ES2022-F7DF1E?logo=javascript&logoColor=black)](https://developer.mozilla.org/en-US/docs/Web/JavaScript)
8
- [![Build](https://img.shields.io/badge/Build-Vite-646CFF?logo=vite&logoColor=white)](https://vitejs.dev/)
9
- [![Test](https://img.shields.io/badge/Test-Vitest-6E9F18?logo=vitest&logoColor=white)](https://vitest.dev/)
10
11
  [![License](https://img.shields.io/badge/License-MIT-brightgreen)](https://opensource.org/licenses/MIT)
11
12
  [![jQuery](https://img.shields.io/badge/jQuery-free-lightgrey)](#)
12
- [![FontAwesome](https://img.shields.io/badge/FontAwesome-6.5.2-528DD7?logo=fontawesome&logoColor=white)](https://fontawesome.com/)
13
+ [![TypeScript](https://img.shields.io/badge/TypeScript-definitions-3178C6?logo=typescript&logoColor=white)](types/index.d.ts)
13
14
 
14
15
  A modern, lightweight WYSIWYG rich-text editor built with vanilla JavaScript (ES2022+) — no jQuery, no heavy dependencies.
15
16
 
@@ -30,9 +31,10 @@ A modern, lightweight WYSIWYG rich-text editor built with vanilla JavaScript (ES
30
31
  5. [Options](#options)
31
32
  6. [Toolbar Customisation](#toolbar-customisation)
32
33
  7. [Keyboard Shortcuts](#keyboard-shortcuts)
33
- 8. [Project Structure](#project-structure)
34
- 9. [Comparison](#comparison)
35
- 10. [License](#license)
34
+ 8. [Mentions](#mentions)
35
+ 9. [Project Structure](#project-structure)
36
+ 10. [Comparison](#comparison)
37
+ 11. [License](#license)
36
38
 
37
39
  ---
38
40
 
@@ -97,6 +99,9 @@ Right-click inside the editor opens a context menu with: **Undo**, **Redo**, **C
97
99
  - **Placeholder** — CSS `::before` pseudo-element, zero DOM node cost
98
100
  - **Read-only mode** — `readOnly: true` renders a non-editable preview with toolbar hidden; toggle at runtime via `editor.setDisabled()`
99
101
  - **Auto-save** — `autoSave: true` persists content to `localStorage` on every change; key configurable via `autoSaveKey`
102
+ - **Auto-save restore** — when `autoSave` and `autoSaveRestore` are both `true`, a dismissible banner prompts the user to restore or discard a previously saved draft on load; configurable age window via `autoSaveRestoreTimeout`
103
+ - **Bubble toolbar** — `bubbleToolbar: true` shows a compact floating toolbar above selected text with quick-access buttons (bold, italic, underline, strikethrough, link, text colour, remove format, inline code); button set configurable via `bubbleToolbarItems`
104
+ - **Markdown shortcuts** — `markdownShortcuts: true` (default) converts Markdown syntax typed in the editor into HTML in real time: `# ` → H1–H3, `> ` → blockquote, `- ` / `* ` → unordered list, `1. ` → ordered list, `[ ] ` → checklist, `---` → HR, ` ``` ` → code block; inline: `**bold**`, `*italic*`, `~~strikethrough~~`, `` `code` ``
100
105
  - **Custom focus ring** — `focusColor` accepts any CSS colour string to override the default blue focus ring
101
106
  - **Spellcheck** — browser spellcheck enabled by default (`spellcheck: true`)
102
107
 
@@ -107,6 +112,7 @@ Right-click inside the editor opens a context menu with: **Undo**, **Redo**, **C
107
112
  - **Plugin-ready** — register custom modules via `AutumnNote.defaults`
108
113
  - **Tree-shakeable** — ES module build; all core utilities individually exported
109
114
  - **TypeScript definitions** — bundled `types/index.d.ts` with full JSDoc coverage
115
+ - **@mention autocomplete** — type `@` (or any custom trigger) to open a floating dropdown backed by a user-supplied `onSearch` function; inserts a non-editable mention chip; customisable chip HTML via `onInsert`
110
116
 
111
117
  ### Security
112
118
  - All HTML (pasted content, `setHTML()`, or code-view output) passes through a DOM-based sanitiser that strips `<script>`, `<object>`, `<embed>`, and all `on*` event handler attributes
@@ -231,6 +237,36 @@ const editor = AutumnNote.create('#my-editor', {
231
237
  });
232
238
  ```
233
239
 
240
+ ### Bubble toolbar
241
+
242
+ ```js
243
+ const editor = AutumnNote.create('#my-editor', {
244
+ bubbleToolbar: true,
245
+ bubbleToolbarItems: ['bold', 'italic', 'underline', 'strikethrough', 'link', 'removeFormat'],
246
+ });
247
+ ```
248
+
249
+ ### @mention autocomplete
250
+
251
+ ```js
252
+ const editor = AutumnNote.create('#my-editor', {
253
+ mention: {
254
+ onSearch(query, callback) {
255
+ const users = [
256
+ { id: 1, label: 'Alice' },
257
+ { id: 2, label: 'Bob' },
258
+ { id: 3, label: 'Charlie' },
259
+ ];
260
+ callback(users.filter(u => u.label.toLowerCase().includes(query.toLowerCase())));
261
+ },
262
+ onInsert(item) {
263
+ // optional: return custom HTML for the mention chip
264
+ return `<span class="mention" data-id="${item.id}">@${item.label}</span>`;
265
+ },
266
+ },
267
+ });
268
+ ```
269
+
234
270
  ---
235
271
 
236
272
  ## API
@@ -320,6 +356,13 @@ const editor = AutumnNote.create('#my-editor', {
320
356
  | `colorSwatches` | `string[]` | `[]` | Custom brand colour swatches prepended to the colour picker palette. |
321
357
  | `focusColor` | `string` | `null` | Custom focus ring colour (any valid CSS colour). Overrides the default blue. |
322
358
  | `lang` | `string \| object` | `'en'` | UI display language. Built-in codes: `'en'`, `'vi'`, `'ja'`, `'zh'`, `'fr'`, `'de'`, `'es'`, `'ko'`. Pass a partial locale object for custom overrides. |
359
+ | `markdownShortcuts` | `boolean` | `true` | Convert Markdown-style syntax typed in the editor to HTML in real time (block and inline rules). |
360
+ | `bubbleToolbar` | `boolean` | `false` | Show a mini floating toolbar above the text selection for quick formatting. |
361
+ | `bubbleToolbarItems` | `string[]` | `['bold','italic','underline','link','foreColor','removeFormat']` | Buttons shown in the bubble toolbar. Available names: `'bold'`, `'italic'`, `'underline'`, `'strikethrough'`, `'link'`, `'foreColor'`, `'removeFormat'`, `'inlineCode'`. |
362
+ | `autoSaveRestore` | `boolean` | `false` | When `autoSave` is also `true`, show a restore banner on load if a draft exists. |
363
+ | `autoSaveRestoreTimeout` | `number` | `7` | Max draft age in days before it is auto-discarded. `0` = no expiry. |
364
+ | `onAutoSaveRestore` | `Function` | `null` | `(html, context) => void` — called after the user restores a draft. |
365
+ | `mention` | `object` | `null` | @mention configuration object. Set `mention.onSearch` to activate. See [Mentions](#mentions). |
323
366
  | `onChange` | `Function` | `null` | `(html: string) => void` — called on every content change. |
324
367
  | `onFocus` | `Function` | `null` | `(context) => void` — called when the editor gains focus. |
325
368
  | `onBlur` | `Function` | `null` | `(context) => void` — called when the editor loses focus. |
@@ -452,6 +495,39 @@ Object.assign(AutumnNote.defaults, {
452
495
 
453
496
  ---
454
497
 
498
+ ## Mentions
499
+
500
+ The `mention` option object activates `@mention` autocomplete. Only `onSearch` is required; all other fields are optional.
501
+
502
+ | Field | Type | Default | Description |
503
+ |---|---|---|---|
504
+ | `onSearch` | `Function` | — | `(query, callback) => void` — called when the user types after the trigger character. Pass an array of `{ id, label, avatar? }` to the callback. |
505
+ | `onInsert` | `Function` | `null` | `(item) => string \| null` — return custom HTML for the inserted mention chip. Return `null` to use the built-in chip. |
506
+ | `trigger` | `string` | `'@'` | Character that opens the dropdown. |
507
+ | `minChars` | `number` | `0` | Minimum characters after the trigger before `onSearch` is called. `0` = open immediately. |
508
+ | `maxResults` | `number` | `8` | Maximum items shown in the dropdown. |
509
+ | `debounce` | `number` | `200` | Debounce delay in milliseconds for `onSearch` calls. |
510
+ | `mentionClass` | `string` | `'an-mention'` | CSS class applied to the inserted mention chip. |
511
+ | `allowSpaces` | `boolean` | `false` | Allow spaces in the query string before the dropdown closes. |
512
+
513
+ ### Example
514
+
515
+ ```js
516
+ AutumnNote.create('#editor', {
517
+ mention: {
518
+ trigger: '@',
519
+ minChars: 1,
520
+ onSearch(query, callback) {
521
+ fetch(`/api/users?q=${encodeURIComponent(query)}`)
522
+ .then(r => r.json())
523
+ .then(users => callback(users)); // [{ id, label, avatar? }]
524
+ },
525
+ },
526
+ });
527
+ ```
528
+
529
+ ---
530
+
455
531
  ## Project Structure
456
532
 
457
533
  ```
@@ -495,7 +571,11 @@ src/
495
571
  │ │ ├── CodeTooltip.js Floating toolbar for code blocks (copy/delete)
496
572
  │ │ ├── EmojiDialog.js Unicode emoji picker (~380 emoji, 7 categories)
497
573
  │ │ ├── IconDialog.js FontAwesome icon picker (FA 6 Free Solid, 8 categories)
498
- │ │ └── ShortcutsDialog.js Keyboard shortcuts reference dialog (Shift+?)
574
+ │ │ ├── ShortcutsDialog.js Keyboard shortcuts reference dialog (Shift+?)
575
+ │ │ ├── BubbleToolbar.js Mini floating toolbar above text selection
576
+ │ │ ├── MarkdownShortcuts.js Inline Markdown-to-HTML input rules
577
+ │ │ ├── AutoSaveRestore.js Draft restore banner for localStorage drafts
578
+ │ │ └── Mention.js @mention autocomplete with floating dropdown
499
579
  │ ├── Context.js Editor instance hub: module registry and event bus
500
580
  │ ├── settings.js Default options (AsnOptions)
501
581
  │ ├── renderer.js DOM layout builder
@@ -529,7 +609,7 @@ Build output in `dist/`:
529
609
 
530
610
  ## Comparison
531
611
 
532
- The table below compares AutumnNote against editors commonly used in this category. Specific library names are omitted to avoid bias.
612
+ The table below compares AutumnNote against editors commonly used in this category — such as Summernote, Quill, TinyMCE, and Froala. Specific library names are kept generic to avoid direct bias; the comparison is based on publicly documented feature sets.
533
613
 
534
614
  | Feature | Editor A | Editor B | Editor C | **AutumnNote** |
535
615
  |---|---|---|---|---|
@@ -1802,4 +1802,173 @@
1802
1802
  .an-video-resizer {
1803
1803
  display: none !important;
1804
1804
  }
1805
+ }
1806
+ .an-asr-banner {
1807
+ display: flex;
1808
+ align-items: center;
1809
+ gap: 8px;
1810
+ padding: 8px 12px;
1811
+ background: #eff6ff;
1812
+ border-bottom: 1px solid #bfdbfe;
1813
+ font-size: 13px;
1814
+ color: #1e40af;
1815
+ flex-wrap: wrap;
1816
+ }
1817
+ .an-theme-dark .an-asr-banner {
1818
+ background: #1e3a5f;
1819
+ border-bottom-color: #1e4d8c;
1820
+ color: #93c5fd;
1821
+ }
1822
+
1823
+ .an-asr-msg {
1824
+ flex: 1;
1825
+ min-width: 0;
1826
+ }
1827
+
1828
+ .an-asr-btn-restore {
1829
+ padding: 3px 10px;
1830
+ font-size: 12px;
1831
+ font-weight: 600;
1832
+ background: #3b82f6;
1833
+ color: #fff;
1834
+ border: none;
1835
+ border-radius: 4px;
1836
+ cursor: pointer;
1837
+ line-height: 1.5;
1838
+ }
1839
+ .an-asr-btn-restore:hover {
1840
+ background: #2563eb;
1841
+ }
1842
+
1843
+ .an-asr-btn-discard {
1844
+ padding: 3px 10px;
1845
+ font-size: 12px;
1846
+ background: transparent;
1847
+ color: #6b7280;
1848
+ border: 1px solid #d1d5db;
1849
+ border-radius: 4px;
1850
+ cursor: pointer;
1851
+ line-height: 1.5;
1852
+ }
1853
+ .an-asr-btn-discard:hover {
1854
+ background: #f3f4f6;
1855
+ }
1856
+
1857
+ .an-bubble-toolbar {
1858
+ display: none;
1859
+ position: fixed;
1860
+ z-index: 10040;
1861
+ background: #ffffff;
1862
+ border: 1px solid #d1d5db;
1863
+ border-radius: 8px;
1864
+ padding: 4px 6px;
1865
+ gap: 2px;
1866
+ box-shadow: 0 8px 28px rgba(0, 0, 0, 0.14);
1867
+ align-items: center;
1868
+ animation: an-bubble-in 0.1s ease;
1869
+ pointer-events: auto;
1870
+ user-select: none;
1871
+ }
1872
+ .an-theme-dark .an-bubble-toolbar {
1873
+ background: #24273a;
1874
+ border-color: #3f3f5f;
1875
+ }
1876
+
1877
+ @keyframes an-bubble-in {
1878
+ from {
1879
+ opacity: 0;
1880
+ transform: translateY(4px);
1881
+ }
1882
+ to {
1883
+ opacity: 1;
1884
+ transform: translateY(0);
1885
+ }
1886
+ }
1887
+ .an-bubble-btn {
1888
+ display: flex;
1889
+ align-items: center;
1890
+ justify-content: center;
1891
+ width: 28px;
1892
+ height: 28px;
1893
+ padding: 0;
1894
+ background: transparent;
1895
+ border: none;
1896
+ border-radius: 5px;
1897
+ color: #111827;
1898
+ cursor: pointer;
1899
+ transition: background 0.15s ease, color 0.15s ease;
1900
+ }
1901
+ .an-bubble-btn svg {
1902
+ pointer-events: none;
1903
+ stroke: currentColor;
1904
+ }
1905
+ .an-bubble-btn:hover {
1906
+ background: #f3f4f6;
1907
+ color: #111827;
1908
+ }
1909
+ .an-bubble-btn.an-active {
1910
+ background: #dbeafe;
1911
+ color: #3b82f6;
1912
+ }
1913
+ .an-theme-dark .an-bubble-btn {
1914
+ color: #cdd6f4;
1915
+ }
1916
+ .an-theme-dark .an-bubble-btn:hover {
1917
+ background: #313244;
1918
+ }
1919
+ .an-theme-dark .an-bubble-btn.an-active {
1920
+ background: #1e3a5f;
1921
+ color: #93c5fd;
1922
+ }
1923
+
1924
+ .an-mention-dropdown {
1925
+ display: none;
1926
+ position: fixed;
1927
+ z-index: 9998;
1928
+ background: #ffffff;
1929
+ border: 1px solid #d1d5db;
1930
+ border-radius: 6px;
1931
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
1932
+ min-width: 180px;
1933
+ max-width: 280px;
1934
+ max-height: 240px;
1935
+ overflow-y: auto;
1936
+ padding: 4px 0;
1937
+ }
1938
+
1939
+ .an-mention-item {
1940
+ display: flex;
1941
+ align-items: center;
1942
+ gap: 8px;
1943
+ padding: 6px 12px;
1944
+ cursor: pointer;
1945
+ font-size: 13px;
1946
+ color: #111827;
1947
+ transition: background 0.15s ease;
1948
+ }
1949
+ .an-mention-item:hover, .an-mention-item.an-mention-active {
1950
+ background: #f3f4f6;
1951
+ }
1952
+
1953
+ .an-mention-avatar {
1954
+ width: 24px;
1955
+ height: 24px;
1956
+ border-radius: 50%;
1957
+ object-fit: cover;
1958
+ flex-shrink: 0;
1959
+ }
1960
+
1961
+ .an-mention {
1962
+ display: inline;
1963
+ background: #eff6ff;
1964
+ color: #3b82f6;
1965
+ border-radius: 3px;
1966
+ padding: 0 3px;
1967
+ font-weight: 500;
1968
+ cursor: default;
1969
+ user-select: all;
1970
+ }
1971
+ .an-theme-dark .an-mention {
1972
+ background: #1e3a5f;
1973
+ color: #93c5fd;
1805
1974
  }/*$vite$:1*/