autumnnote 1.0.9 → 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.
- package/README.md +85 -6
- package/dist/autumnnote.css +169 -0
- package/dist/autumnnote.es.js +805 -22
- package/dist/autumnnote.es.js.map +1 -1
- package/dist/autumnnote.umd.js +805 -22
- package/dist/autumnnote.umd.js.map +1 -1
- package/package.json +3 -2
- package/src/js/Context.js +14 -2
- package/src/js/core/func.js +5 -5
- package/src/js/module/AutoSaveRestore.js +126 -0
- package/src/js/module/BubbleToolbar.js +243 -0
- package/src/js/module/ContextMenu.js +28 -26
- package/src/js/module/MarkdownShortcuts.js +253 -0
- package/src/js/module/Mention.js +337 -0
- package/src/js/settings.js +19 -0
- package/src/styles/autumnnote.scss +191 -0
- package/types/index.d.ts +56 -0
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Autumn Note
|
|
2
2
|
|
|
3
3
|
<p align="center"><img src="image/banner.png" width="120" alt="AutumnNote Banner"/></p>
|
|
4
4
|
|
|
5
|
-
[](#)
|
|
6
6
|
[](https://github.com/cmm-cmm/Autumn-Note/actions/workflows/pages.yml)
|
|
7
7
|
[](https://developer.mozilla.org/en-US/docs/Web/JavaScript)
|
|
8
8
|
[](https://vitejs.dev/)
|
|
@@ -30,9 +30,10 @@ A modern, lightweight WYSIWYG rich-text editor built with vanilla JavaScript (ES
|
|
|
30
30
|
5. [Options](#options)
|
|
31
31
|
6. [Toolbar Customisation](#toolbar-customisation)
|
|
32
32
|
7. [Keyboard Shortcuts](#keyboard-shortcuts)
|
|
33
|
-
8. [
|
|
34
|
-
9. [
|
|
35
|
-
10. [
|
|
33
|
+
8. [Mentions](#mentions)
|
|
34
|
+
9. [Project Structure](#project-structure)
|
|
35
|
+
10. [Comparison](#comparison)
|
|
36
|
+
11. [License](#license)
|
|
36
37
|
|
|
37
38
|
---
|
|
38
39
|
|
|
@@ -97,6 +98,9 @@ Right-click inside the editor opens a context menu with: **Undo**, **Redo**, **C
|
|
|
97
98
|
- **Placeholder** — CSS `::before` pseudo-element, zero DOM node cost
|
|
98
99
|
- **Read-only mode** — `readOnly: true` renders a non-editable preview with toolbar hidden; toggle at runtime via `editor.setDisabled()`
|
|
99
100
|
- **Auto-save** — `autoSave: true` persists content to `localStorage` on every change; key configurable via `autoSaveKey`
|
|
101
|
+
- **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`
|
|
102
|
+
- **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`
|
|
103
|
+
- **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
104
|
- **Custom focus ring** — `focusColor` accepts any CSS colour string to override the default blue focus ring
|
|
101
105
|
- **Spellcheck** — browser spellcheck enabled by default (`spellcheck: true`)
|
|
102
106
|
|
|
@@ -107,6 +111,7 @@ Right-click inside the editor opens a context menu with: **Undo**, **Redo**, **C
|
|
|
107
111
|
- **Plugin-ready** — register custom modules via `AutumnNote.defaults`
|
|
108
112
|
- **Tree-shakeable** — ES module build; all core utilities individually exported
|
|
109
113
|
- **TypeScript definitions** — bundled `types/index.d.ts` with full JSDoc coverage
|
|
114
|
+
- **@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
115
|
|
|
111
116
|
### Security
|
|
112
117
|
- 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 +236,36 @@ const editor = AutumnNote.create('#my-editor', {
|
|
|
231
236
|
});
|
|
232
237
|
```
|
|
233
238
|
|
|
239
|
+
### Bubble toolbar
|
|
240
|
+
|
|
241
|
+
```js
|
|
242
|
+
const editor = AutumnNote.create('#my-editor', {
|
|
243
|
+
bubbleToolbar: true,
|
|
244
|
+
bubbleToolbarItems: ['bold', 'italic', 'underline', 'strikethrough', 'link', 'removeFormat'],
|
|
245
|
+
});
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### @mention autocomplete
|
|
249
|
+
|
|
250
|
+
```js
|
|
251
|
+
const editor = AutumnNote.create('#my-editor', {
|
|
252
|
+
mention: {
|
|
253
|
+
onSearch(query, callback) {
|
|
254
|
+
const users = [
|
|
255
|
+
{ id: 1, label: 'Alice' },
|
|
256
|
+
{ id: 2, label: 'Bob' },
|
|
257
|
+
{ id: 3, label: 'Charlie' },
|
|
258
|
+
];
|
|
259
|
+
callback(users.filter(u => u.label.toLowerCase().includes(query.toLowerCase())));
|
|
260
|
+
},
|
|
261
|
+
onInsert(item) {
|
|
262
|
+
// optional: return custom HTML for the mention chip
|
|
263
|
+
return `<span class="mention" data-id="${item.id}">@${item.label}</span>`;
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
});
|
|
267
|
+
```
|
|
268
|
+
|
|
234
269
|
---
|
|
235
270
|
|
|
236
271
|
## API
|
|
@@ -320,6 +355,13 @@ const editor = AutumnNote.create('#my-editor', {
|
|
|
320
355
|
| `colorSwatches` | `string[]` | `[]` | Custom brand colour swatches prepended to the colour picker palette. |
|
|
321
356
|
| `focusColor` | `string` | `null` | Custom focus ring colour (any valid CSS colour). Overrides the default blue. |
|
|
322
357
|
| `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. |
|
|
358
|
+
| `markdownShortcuts` | `boolean` | `true` | Convert Markdown-style syntax typed in the editor to HTML in real time (block and inline rules). |
|
|
359
|
+
| `bubbleToolbar` | `boolean` | `false` | Show a mini floating toolbar above the text selection for quick formatting. |
|
|
360
|
+
| `bubbleToolbarItems` | `string[]` | `['bold','italic','underline','link','foreColor','removeFormat']` | Buttons shown in the bubble toolbar. Available names: `'bold'`, `'italic'`, `'underline'`, `'strikethrough'`, `'link'`, `'foreColor'`, `'removeFormat'`, `'inlineCode'`. |
|
|
361
|
+
| `autoSaveRestore` | `boolean` | `false` | When `autoSave` is also `true`, show a restore banner on load if a draft exists. |
|
|
362
|
+
| `autoSaveRestoreTimeout` | `number` | `7` | Max draft age in days before it is auto-discarded. `0` = no expiry. |
|
|
363
|
+
| `onAutoSaveRestore` | `Function` | `null` | `(html, context) => void` — called after the user restores a draft. |
|
|
364
|
+
| `mention` | `object` | `null` | @mention configuration object. Set `mention.onSearch` to activate. See [Mentions](#mentions). |
|
|
323
365
|
| `onChange` | `Function` | `null` | `(html: string) => void` — called on every content change. |
|
|
324
366
|
| `onFocus` | `Function` | `null` | `(context) => void` — called when the editor gains focus. |
|
|
325
367
|
| `onBlur` | `Function` | `null` | `(context) => void` — called when the editor loses focus. |
|
|
@@ -452,6 +494,39 @@ Object.assign(AutumnNote.defaults, {
|
|
|
452
494
|
|
|
453
495
|
---
|
|
454
496
|
|
|
497
|
+
## Mentions
|
|
498
|
+
|
|
499
|
+
The `mention` option object activates `@mention` autocomplete. Only `onSearch` is required; all other fields are optional.
|
|
500
|
+
|
|
501
|
+
| Field | Type | Default | Description |
|
|
502
|
+
|---|---|---|---|
|
|
503
|
+
| `onSearch` | `Function` | — | `(query, callback) => void` — called when the user types after the trigger character. Pass an array of `{ id, label, avatar? }` to the callback. |
|
|
504
|
+
| `onInsert` | `Function` | `null` | `(item) => string \| null` — return custom HTML for the inserted mention chip. Return `null` to use the built-in chip. |
|
|
505
|
+
| `trigger` | `string` | `'@'` | Character that opens the dropdown. |
|
|
506
|
+
| `minChars` | `number` | `0` | Minimum characters after the trigger before `onSearch` is called. `0` = open immediately. |
|
|
507
|
+
| `maxResults` | `number` | `8` | Maximum items shown in the dropdown. |
|
|
508
|
+
| `debounce` | `number` | `200` | Debounce delay in milliseconds for `onSearch` calls. |
|
|
509
|
+
| `mentionClass` | `string` | `'an-mention'` | CSS class applied to the inserted mention chip. |
|
|
510
|
+
| `allowSpaces` | `boolean` | `false` | Allow spaces in the query string before the dropdown closes. |
|
|
511
|
+
|
|
512
|
+
### Example
|
|
513
|
+
|
|
514
|
+
```js
|
|
515
|
+
AutumnNote.create('#editor', {
|
|
516
|
+
mention: {
|
|
517
|
+
trigger: '@',
|
|
518
|
+
minChars: 1,
|
|
519
|
+
onSearch(query, callback) {
|
|
520
|
+
fetch(`/api/users?q=${encodeURIComponent(query)}`)
|
|
521
|
+
.then(r => r.json())
|
|
522
|
+
.then(users => callback(users)); // [{ id, label, avatar? }]
|
|
523
|
+
},
|
|
524
|
+
},
|
|
525
|
+
});
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
---
|
|
529
|
+
|
|
455
530
|
## Project Structure
|
|
456
531
|
|
|
457
532
|
```
|
|
@@ -495,7 +570,11 @@ src/
|
|
|
495
570
|
│ │ ├── CodeTooltip.js Floating toolbar for code blocks (copy/delete)
|
|
496
571
|
│ │ ├── EmojiDialog.js Unicode emoji picker (~380 emoji, 7 categories)
|
|
497
572
|
│ │ ├── IconDialog.js FontAwesome icon picker (FA 6 Free Solid, 8 categories)
|
|
498
|
-
│ │
|
|
573
|
+
│ │ ├── ShortcutsDialog.js Keyboard shortcuts reference dialog (Shift+?)
|
|
574
|
+
│ │ ├── BubbleToolbar.js Mini floating toolbar above text selection
|
|
575
|
+
│ │ ├── MarkdownShortcuts.js Inline Markdown-to-HTML input rules
|
|
576
|
+
│ │ ├── AutoSaveRestore.js Draft restore banner for localStorage drafts
|
|
577
|
+
│ │ └── Mention.js @mention autocomplete with floating dropdown
|
|
499
578
|
│ ├── Context.js Editor instance hub: module registry and event bus
|
|
500
579
|
│ ├── settings.js Default options (AsnOptions)
|
|
501
580
|
│ ├── renderer.js DOM layout builder
|
package/dist/autumnnote.css
CHANGED
|
@@ -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*/
|