autumnnote 1.0.8 → 1.0.9

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
@@ -2,7 +2,7 @@
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.8-blue)](#)
5
+ [![Version](https://img.shields.io/badge/version-1.0.9-blue)](#)
6
6
  [![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
7
  [![JavaScript](https://img.shields.io/badge/JavaScript-ES2022-F7DF1E?logo=javascript&logoColor=black)](https://developer.mozilla.org/en-US/docs/Web/JavaScript)
8
8
  [![Build](https://img.shields.io/badge/Build-Vite-646CFF?logo=vite&logoColor=white)](https://vitejs.dev/)
@@ -11,37 +11,59 @@
11
11
  [![jQuery](https://img.shields.io/badge/jQuery-free-lightgrey)](#)
12
12
  [![FontAwesome](https://img.shields.io/badge/FontAwesome-6.5.2-528DD7?logo=fontawesome&logoColor=white)](https://fontawesome.com/)
13
13
 
14
- A modern, lightweight WYSIWYG rich-text editor with vanilla JavaScript (ES2022+), no jQuery dependency.
14
+ A modern, lightweight WYSIWYG rich-text editor built with vanilla JavaScript (ES2022+) no jQuery, no heavy dependencies.
15
15
 
16
- > ✍️ *Write rich text. No dependencies. No drama.*
16
+ > Write rich text. No dependencies. No drama.
17
17
 
18
- 🔗 **[Live Demo](https://cmm-cmm.github.io/Autumn-Note/)**
18
+ [Live Demo](https://cmm-cmm.github.io/Autumn-Note/)
19
19
 
20
20
  <p align="center"><img src="demo/Screenshot.png" alt="AutumnNote Screenshot"/></p>
21
21
 
22
22
  ---
23
23
 
24
+ ## Table of Contents
25
+
26
+ 1. [Features](#features)
27
+ 2. [Installation](#installation)
28
+ 3. [Quick Start](#quick-start)
29
+ 4. [API](#api)
30
+ 5. [Options](#options)
31
+ 6. [Toolbar Customisation](#toolbar-customisation)
32
+ 7. [Keyboard Shortcuts](#keyboard-shortcuts)
33
+ 8. [Project Structure](#project-structure)
34
+ 9. [Comparison](#comparison)
35
+ 10. [License](#license)
36
+
37
+ ---
38
+
24
39
  ## Features
25
40
 
26
41
  ### Editing
27
42
  - **Text formatting** — bold, italic, underline, strikethrough, superscript, subscript
28
43
  - **Paragraph styles** — Normal, H1–H6, Blockquote, Code block
29
44
  - **Font family** — customisable dropdown (10 families by default)
45
+ - **Font size** — configurable default via `defaultFontSize`; applied to new content
30
46
  - **Line height** — dropdown from 1.0 to 3.0
31
- - **Text & highlight colour** — native colour picker with last-used colour memory
47
+ - **Text and highlight colour** — native colour picker with last-used colour memory; custom brand swatches via `colorSwatches`
32
48
  - **Alignment** — left, center, right, justify
33
49
  - **Lists** — unordered and ordered, with indent / outdent; Tab/Shift+Tab in list context
34
- - **Undo / redo** — built-in history stack (100 levels, `Ctrl+Z` / `Ctrl+Y`)
50
+ - **Checklist** — interactive checkbox list; toggle items by clicking; converts to/from plain paragraphs on outdent
51
+ - **Undo / redo** — built-in history stack (configurable depth via `historyLimit`, `Ctrl+Z` / `Ctrl+Y`)
35
52
  - **Tab key** — configurable spaces-per-tab; smart list indentation inside `<li>`
53
+ - **RTL support** — set `direction: 'rtl'` to flip the editor layout and text direction
36
54
 
37
55
  ### Insert
38
56
  - **Horizontal rule** — inserts an `<hr>` at the current caret position
39
57
  - **Link dialog** — URL, display text (auto-filled from selection), "Open in new tab" checkbox; edits existing links when caret is inside an `<a>`
40
- - **Image dialog** — insert by URL with alt text, or file upload (base64 embed); enforces `maxImageSize`; supports custom `onImageUpload` handler for server-side upload
58
+ - **Image dialog** — insert by URL with alt text, or file upload (base64 embed); enforces `maxImageSize`; file input restricted to browser-renderable MIME types; supports custom `onImageUpload` handler for server-side upload
59
+ - **Image crop overlay** — inline interactive crop tool triggered from the image tooltip; corner and edge drag handles; canvas-based crop export; CORS fallback warning
41
60
  - **Video dialog** — paste a YouTube watch/short URL, Vimeo URL, or direct `.mp4 / .webm / .ogg` URL; configurable width; renders as responsive `<iframe>` or `<video>`
42
- - **Table** — interactive grid picker (up to 10×10); floating tooltip on table click for full row/column/cell management (see [Table tooltip](#table-tooltip))
43
- - **Emoji picker** — ~380 Unicode emoji across 7 categories (Smileys, People, Animals, Food, Travel, Objects, Symbols); keyword search; click to insert instantly as a plain text character (UTF-8 / utf8mb4 safe)
44
- - **FA Icon picker** — FontAwesome 6 Free Solid icons across 8 categories (Popular, Interface, Navigation, Media, Communication, Files, People, Objects); keyword search; configurable style (Solid / Regular / Light), size, and colour; inserts as `<i>` element; auto-injects FA CDN if not detected on the page
61
+ - **Table** — interactive grid picker (up to 10x10); optional header row (`tableHeaderRow`); floating tooltip for full row/column/cell management
62
+ - **Emoji picker** — approximately 380 Unicode emoji across 7 categories (Smileys, People, Animals, Food, Travel, Objects, Symbols); keyword search; click to insert as plain UTF-8 character
63
+ - **FA Icon picker** — FontAwesome 6 Free Solid icons across 8 categories; keyword search; configurable style, size, and colour; inserts as `<i>` element; auto-injects FA CDN if not on the page
64
+
65
+ ### Search
66
+ - **Find and Replace** — `Ctrl+F` to find, `Ctrl+H` for find-and-replace; TreeWalker text matching; `<mark>` highlighting; case-sensitive toggle; Prev/Next navigation; single and replace-all modes
45
67
 
46
68
  ### Inline tooltips
47
69
  Floating toolbars appear automatically when the user clicks on an editable element:
@@ -49,34 +71,49 @@ Floating toolbars appear automatically when the user clicks on an editable eleme
49
71
  | Element | Actions |
50
72
  |---|---|
51
73
  | **Link** | Open in new tab, Edit (reopens dialog), Unlink |
52
- | **Image** | Edit alt/URL (reopens dialog), Delete |
74
+ | **Image** | Edit alt/URL (reopens dialog), Crop, Delete |
53
75
  | **Video** | Edit (reopens dialog), Delete |
54
76
  | **Table cell** | Row above/below, Delete row, Column left/right, Delete column, Merge cells, Unmerge cells, Cell selection mode, Column width, Row height, Border width, Delete table |
55
- | **Code block** (`<pre>`) | Copy code, Delete block |
77
+ | **Code block** | Copy code, Delete block |
56
78
 
57
79
  ### Context menu
58
- Right-click inside the editor opens a context menu with: **Undo**, **Redo**, **Cut**, **Copy**, **Paste**, **Bold**, **Italic**, **Underline**, **Copy Format**, **Paste Format**, **Remove Format**.
80
+ Right-click inside the editor opens a context menu with: **Undo**, **Redo**, **Cut**, **Copy**, **Paste**, **Bold**, **Italic**, **Underline**, **Copy Format**, **Paste Format**, **Remove Format**, and a **colour palette** for quick text/highlight colour changes.
81
+
82
+ ### Internationalisation
83
+ - **Built-in locales** — English (`en`), Vietnamese (`vi`), Japanese (`ja`), Simplified Chinese (`zh`), French (`fr`), German (`de`), Spanish (`es`), Korean (`ko`)
84
+ - **Custom locale** — pass any partial locale object to override individual strings
85
+ - **Per-instance language** — set a different `lang` per editor instance on the same page
86
+ - **Auto-fallback** — unknown codes or missing keys fall back to English
59
87
 
60
88
  ### UI
61
- - **Toolbar** — fully configurable button groups; renders with FontAwesome icons when detected, falls back to inline SVG icons
89
+ - **Toolbar** — fully configurable button groups; overflow strategy: `wrap` (default) or `scroll`; FontAwesome icons with built-in SVG fallback
90
+ - **Sticky toolbar** — `stickyToolbar: true` pins the toolbar to the viewport top; configurable offset for fixed nav bars
91
+ - **Dark / light theme** — `theme: 'dark'` or `theme: 'light'` (default); full SCSS variable coverage
62
92
  - **Image resizer** — drag handle on selected image to resize proportionally
63
93
  - **Video resizer** — drag handle on selected video embed to resize
64
- - **Statusbar** — live word and character count; drag handle to resize editor height
94
+ - **Statusbar** — live word and character count; drag handle to resize editor height; limit warnings when `maxChars` or `maxWords` is reached
65
95
  - **Code view** — toggle raw HTML; sanitised before applying back to the editor
66
96
  - **Fullscreen** — expands the editor to fill the viewport
67
97
  - **Placeholder** — CSS `::before` pseudo-element, zero DOM node cost
98
+ - **Read-only mode** — `readOnly: true` renders a non-editable preview with toolbar hidden; toggle at runtime via `editor.setDisabled()`
99
+ - **Auto-save** — `autoSave: true` persists content to `localStorage` on every change; key configurable via `autoSaveKey`
100
+ - **Custom focus ring** — `focusColor` accepts any CSS colour string to override the default blue focus ring
101
+ - **Spellcheck** — browser spellcheck enabled by default (`spellcheck: true`)
68
102
 
69
103
  ### Integration
70
104
  - **No jQuery** — pure vanilla ES2022, zero runtime dependencies
71
- - **Bootstrap friendly** — optional Bootstrap 4/5 styling for toolbar buttons (`useBootstrap: true`)
105
+ - **Bootstrap friendly** — optional Bootstrap 4/5 styling (`useBootstrap: true`)
72
106
  - **FontAwesome ready** — auto-detects FA on the page; falls back to built-in SVG icons
73
107
  - **Plugin-ready** — register custom modules via `AutumnNote.defaults`
74
- - **Tree-shakeable** — ES module build; all core utilities are individually exported
108
+ - **Tree-shakeable** — ES module build; all core utilities individually exported
109
+ - **TypeScript definitions** — bundled `types/index.d.ts` with full JSDoc coverage
75
110
 
76
111
  ### Security
77
- - All HTML (pasted content, `setHTML()`, or code-view output) is passed through a DOM-based sanitiser that strips `<script>`, `<object>`, `<embed>`, and all `on*` event handler attributes; `<iframe>` elements are permitted in `setHTML()` to support embedded content
112
+ - 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
113
+ - `<iframe>` elements are permitted in `setHTML()` with src restricted to trusted CDN hosts; `srcdoc` is stripped
78
114
  - `javascript:` and `data:` URLs are rejected in links and images
79
115
  - Clipboard paste sanitises rich content to remove XSS vectors before inserting
116
+ - `pasteStripAttributes` option strips `class`, `style`, and `data-*` from pasted HTML
80
117
 
81
118
  ---
82
119
 
@@ -85,14 +122,32 @@ Right-click inside the editor opens a context menu with: **Undo**, **Redo**, **C
85
122
  ### npm / pnpm / yarn
86
123
 
87
124
  ```bash
88
- npm install AutumnNote
125
+ npm install autumnnote
126
+ ```
127
+
128
+ ```js
129
+ import AutumnNote from 'autumnnote';
130
+ import 'autumnnote/dist/autumnnote.css';
89
131
  ```
90
132
 
91
133
  ### CDN
92
134
 
135
+ **jsDelivr** (Recommended):
136
+
93
137
  ```html
94
- <link rel="stylesheet" href="dist/AutumnNote.css" />
95
- <script src="dist/AutumnNote.umd.js"></script>
138
+ <script src="https://cdn.jsdelivr.net/npm/autumnnote"></script>
139
+ ```
140
+
141
+ **unpkg:**
142
+
143
+ ```html
144
+ <script src="https://unpkg.com/autumnnote"></script>
145
+ ```
146
+
147
+
148
+ ```html
149
+ <link rel="stylesheet" href="dist/autumnnote.css" />
150
+ <script src="dist/autumnnote.umd.js"></script>
96
151
  ```
97
152
 
98
153
  > **FontAwesome icons** — the editor auto-detects FontAwesome on the page and falls back to built-in SVG icons when absent. To enable FA icons, include the FA stylesheet:
@@ -101,8 +156,6 @@ npm install AutumnNote
101
156
  > <!-- FontAwesome 6 Free (recommended) -->
102
157
  > <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css">
103
158
  > ```
104
- >
105
- > If FA is not on the page but the **FA Icon picker** is used, the editor will automatically inject the FontAwesome CDN stylesheet on first open.
106
159
 
107
160
  ---
108
161
 
@@ -111,7 +164,7 @@ npm install AutumnNote
111
164
  ### ES Module
112
165
 
113
166
  ```js
114
- import AutumnNote from 'AutumnNote';
167
+ import AutumnNote from 'autumnnote';
115
168
 
116
169
  const editor = AutumnNote.create('#my-editor', {
117
170
  placeholder: 'Start typing…',
@@ -126,7 +179,7 @@ const editor = AutumnNote.create('#my-editor', {
126
179
 
127
180
  ```html
128
181
  <div id="my-editor"><p>Hello!</p></div>
129
- <script src="dist/AutumnNote.umd.js"></script>
182
+ <script src="dist/autumnnote.umd.js"></script>
130
183
  <script>
131
184
  const editor = AutumnNote.create('#my-editor');
132
185
  </script>
@@ -142,66 +195,40 @@ const editor = AutumnNote.create('#my-editor', {
142
195
  });
143
196
  ```
144
197
 
145
- ### With FontAwesome 6
198
+ ### Dark mode
146
199
 
147
200
  ```js
148
- const editor = AutumnNote.create('#my-editor', {
149
- useFontAwesome: true,
150
- fontAwesomeClass: 'fa-solid', // FA 6 prefix
151
- });
201
+ const editor = AutumnNote.create('#my-editor', { theme: 'dark' });
152
202
  ```
153
203
 
154
- ---
155
-
156
- ## Usage
157
-
158
- ### Read content on demand
159
-
160
- The most common pattern — attach a button that reads back the current HTML:
161
-
162
- ```html
163
- <link rel="stylesheet" href="dist/autumnnote.css" />
164
-
165
- <div id="editor"><p>Hello, <strong>AutumnNote</strong>!</p></div>
166
-
167
- <button onclick="getHTML()">Get HTML</button>
168
- <pre id="output"></pre>
169
-
170
- <script src="dist/autumnnote.umd.js"></script>
171
- <script>
172
- const editor = AutumnNote.create('#editor', { height: 300 });
204
+ ### Read-only preview
173
205
 
174
- function getHTML() {
175
- document.getElementById('output').innerText = editor.getHTML();
176
- }
177
- </script>
206
+ ```js
207
+ const preview = AutumnNote.create('#preview', { readOnly: true });
208
+ preview.setHTML(savedHtml);
178
209
  ```
179
210
 
180
- ### React to every change
181
-
182
- Use `onChange` (or `editor.on('change', fn)`) to update a live preview automatically:
183
-
184
- ```html
185
- <div id="editor"><p>Start typing…</p></div>
186
- <pre id="preview"></pre>
211
+ ### Auto-save draft
187
212
 
188
- <script src="dist/autumnnote.umd.js"></script>
189
- <script>
190
- const editor = AutumnNote.create('#editor', {
191
- height: 300,
192
- onChange(html) {
193
- document.getElementById('preview').innerText = html;
194
- },
195
- });
196
- </script>
213
+ ```js
214
+ const editor = AutumnNote.create('#my-editor', {
215
+ autoSave: true,
216
+ autoSaveKey: 'my-draft',
217
+ });
197
218
  ```
198
219
 
199
- ### Set and clear content programmatically
220
+ ### Custom image upload
200
221
 
201
222
  ```js
202
- editor.setHTML('<p>New <em>content</em></p>'); // set
203
- editor.clear(); // clear to empty <p>
204
- console.log(editor.getText()); // plain text, no markup
223
+ const editor = AutumnNote.create('#my-editor', {
224
+ onImageUpload(files) {
225
+ const fd = new FormData();
226
+ fd.append('file', files[0]);
227
+ fetch('/api/upload', { method: 'POST', body: fd })
228
+ .then(r => r.json())
229
+ .then(({ url }) => editor.insertImage(url, files[0].name));
230
+ },
231
+ });
205
232
  ```
206
233
 
207
234
  ---
@@ -212,67 +239,40 @@ console.log(editor.getText()); // plain text, no markup
212
239
 
213
240
  | Method | Description |
214
241
  |---|---|
215
- | `AutumnNote.create(selector, options?)` | Creates editor instance(s). `selector` can be a CSS string, `Element`, `NodeList`, or `Element[]`. Returns a `Context` for a single match, or `Context[]` for multiple. |
216
- | `AutumnNote.destroy(selector)` | Destroys editor(s) matching the selector and restores the original element. |
217
- | `AutumnNote.getInstance(selector)` | Returns the `Context` for a given element, or `null` if not initialised. |
218
- | `AutumnNote.defaults` | Global default options object. Mutate before calling `create()` to apply project-wide settings. |
242
+ | `AutumnNote.create(selector, options?)` | Creates editor instance(s). `selector` can be a CSS string, `Element`, `NodeList`, or `Element[]`. Returns a `Context` or `Context[]`. |
243
+ | `AutumnNote.destroy(selector)` | Destroys editor(s) and restores the original element. |
244
+ | `AutumnNote.getInstance(selector)` | Returns the `Context` for a given element, or `null`. |
245
+ | `AutumnNote.defaults` | Global default options object mutate before calling `create()` to apply project-wide settings. |
219
246
 
220
- ### Context (editor instance)
247
+ ### Context (instance methods)
221
248
 
222
249
  | Method | Description |
223
250
  |---|---|
224
- | `editor.getHTML()` | Returns the current HTML content. Zero-width spaces inserted by the icon picker are stripped automatically. |
225
- | `editor.setHTML(html)` | Sets HTML content. Input is sanitised before rendering. |
251
+ | `editor.getHTML()` | Returns the current HTML. Zero-width spaces from the icon picker are stripped automatically. |
252
+ | `editor.setHTML(html)` | Sets HTML content (sanitised). `<iframe>` elements are preserved. |
226
253
  | `editor.getText()` | Returns plain text with no markup. |
227
254
  | `editor.clear()` | Clears all content, resets to an empty `<p>`. |
228
255
  | `editor.setDisabled(bool)` | Disables (`true`) or re-enables (`false`) the editor and toolbar. |
229
256
  | `editor.destroy()` | Removes the editor, disposes all modules, and restores the original element. |
230
- | `editor.on(event, fn)` | Subscribes to an editor event. Returns an unsubscribe function — call it to remove the listener. |
231
- | `editor.invoke('module.method', ...args)` | Calls any registered module method by dot-separated name. Returns the method's return value. |
257
+ | `editor.on(event, fn)` | Subscribes to an editor event. Returns an unsubscribe function. |
258
+ | `editor.off(event, fn)` | Removes a previously registered listener. |
259
+ | `editor.invoke('module.method', ...args)` | Calls any registered module method by dot-separated name. |
232
260
 
233
261
  ### Events
234
262
 
235
263
  | Name | Payload | Description |
236
264
  |---|---|---|
237
265
  | `change` | `html: string` | Fired after every content mutation. Debounced internally. |
238
- | `focus` | | Editor's editable area gained focus. |
239
- | `blur` | | Editor's editable area lost focus. |
240
-
241
- #### Subscribing to events
242
-
243
- ```js
244
- const editor = AutumnNote.create('#editor');
245
-
246
- // Subscribe
247
- const unsub = editor.on('change', (html) => {
248
- document.getElementById('output').innerHTML = html;
249
- });
250
-
251
- // Unsubscribe later
252
- unsub();
253
- ```
254
-
255
- ### `invoke()` examples
256
-
257
- ```js
258
- // Programmatically open a dialog
259
- editor.invoke('linkDialog.show');
260
- editor.invoke('imageDialog.show');
261
- editor.invoke('emojiDialog.show');
262
- editor.invoke('iconDialog.show');
263
-
264
- // Undo / redo
265
- editor.invoke('editor.undo');
266
- editor.invoke('editor.redo');
267
-
268
- // Toggle views
269
- editor.invoke('codeview.toggle');
270
- editor.invoke('fullscreen.toggle');
271
-
272
- // Query state
273
- const isCodeview = editor.invoke('codeview.isActive'); // boolean
274
- const isFullscreen = editor.invoke('fullscreen.isActive'); // boolean
275
- ```
266
+ | `focus` | `context` | Editor gained focus. |
267
+ | `blur` | `context` | Editor lost focus. |
268
+ | `init` | `context` | Fired once after the editor has fully initialised. |
269
+ | `imageUpload` | `files: FileList` | Fired when images are dropped or pasted (when `onImageUpload` is provided). |
270
+ | `imageError` | `{ file, message }` | Fired when an image is rejected (e.g. over `maxImageSize`). |
271
+ | `paste` | `{ text, html }` | Fired after every paste event. |
272
+ | `selectionChange` | `context` | Fired when the cursor or selection changes. |
273
+ | `destroy` | `context` | Fired just before the editor is destroyed. |
274
+ | `charLimitReached` | `context` | Fired when `maxChars` is hit. |
275
+ | `wordLimitReached` | `context` | Fired when `maxWords` is hit. |
276
276
 
277
277
  ---
278
278
 
@@ -281,485 +281,118 @@ const isFullscreen = editor.invoke('fullscreen.isActive'); // boolean
281
281
  | Option | Type | Default | Description |
282
282
  |---|---|---|---|
283
283
  | `placeholder` | `string` | `''` | Placeholder text shown when the editor is empty. |
284
- | `height` | `number` | `200` | Initial / minimum editor height in px. |
285
- | `minHeight` | `number` | `100` | Hard minimum height in px (enforced during resize). |
286
- | `maxHeight` | `number` | `0` | Maximum height in px. `0` = unlimited. |
287
- | `focus` | `boolean` | `false` | Auto-focus the editor on initialisation. |
288
- | `resizeable` | `boolean` | `true` | Show the drag-to-resize handle in the statusbar. |
289
- | `toolbar` | `Array` | default | Array of button group arrays. See [Toolbar customisation](#toolbar-customisation). |
290
- | `useBootstrap` | `boolean` | `false` | Apply Bootstrap button classes to toolbar buttons. |
291
- | `bootstrapVersion` | `number` | `5` | Bootstrap major version to target (`4` or `5`). |
292
- | `toolbarButtonClass` | `string` | `'btn btn-sm btn-light'` | CSS classes applied to toolbar buttons when `useBootstrap` is `true`. |
293
- | `useFontAwesome` | `boolean` | `true` | Render toolbar icons via FontAwesome when FA is detected on the page. |
294
- | `fontAwesomeClass` | `string` | `'fas'` | FontAwesome prefix class. Use `'fas'` for FA 5, `'fa-solid'` for FA 6. |
295
- | `pasteAsPlainText` | `boolean` | `false` | Force all pasted content to plain text, stripping all formatting. |
296
- | `pasteCleanHTML` | `boolean` | `true` | Sanitise HTML on paste — strips scripts and dangerous attributes. |
297
- | `allowImageUpload` | `boolean` | `true` | Show the file upload input in the image dialog. |
298
- | `maxImageSize` | `number` | `5` | Maximum image upload file size in MB. Files exceeding this are rejected with an alert. |
299
- | `onImageUpload` | `Function` | `null` | `(files: FileList) => void` custom upload handler. When provided, overrides the default base64 embed behaviour. Insert the resulting URL yourself via `editor.invoke('editor.insertImage', url, alt)`. |
300
- | `tabSize` | `number` | `0` | Number of spaces inserted per Tab key press outside of lists. `0` = browser default Tab behaviour. |
301
- | `defaultFontFamily` | `string` | `'Arial'` | Font family applied as the default style for the editable area. |
302
- | `fontFamilies` | `string[]` | (10 fonts) | Font families listed in the Font Family dropdown. Default: Arial, Arial Black, Comic Sans MS, Courier New, Georgia, Impact, Tahoma, Times New Roman, Trebuchet MS, Verdana. |
303
- | `onChange` | `Function` | `null` | `(html: string) => void` — shorthand for `editor.on('change', fn)`. |
304
- | `onFocus` | `Function` | `null` | `(context: Context) => void` shorthand for `editor.on('focus', fn)`. |
305
- | `onBlur` | `Function` | `null` | `(context: Context) => void` shorthand for `editor.on('blur', fn)`. |
306
- | `stickyToolbar` | `boolean` | `false` | Stick the toolbar to the viewport top when the page is scrolled. |
307
- | `stickyToolbarOffset` | `number` | `0` | Top offset in px for the sticky toolbar (e.g. height of a fixed navigation bar). |
284
+ | `height` | `number` | `200` | Initial editor height in pixels. |
285
+ | `minHeight` | `number` | `100` | Minimum resizable height in pixels. |
286
+ | `maxHeight` | `number` | `0` | Maximum resizable height in pixels. `0` = unlimited. |
287
+ | `focus` | `boolean` | `false` | Automatically focus the editor on creation. |
288
+ | `resizable` | `boolean` | `true` | Show the resize handle at the bottom of the editor. |
289
+ | `toolbar` | `Array[]` | all buttons | Toolbar layout. See [Toolbar Customisation](#toolbar-customisation). |
290
+ | `toolbarOverflow` | `string` | `'wrap'` | Toolbar overflow strategy: `'wrap'` or `'scroll'`. |
291
+ | `useBootstrap` | `boolean` | `false` | Apply Bootstrap CSS classes to toolbar buttons. |
292
+ | `bootstrapVersion` | `number` | `5` | Bootstrap major version (`4` or `5`). |
293
+ | `toolbarButtonClass` | `string` | `'btn btn-sm btn-light'` | CSS classes for toolbar buttons when `useBootstrap` is `true`. |
294
+ | `useFontAwesome` | `boolean` | `true` | Use FA icons when FontAwesome is detected on the page. |
295
+ | `fontAwesomeClass` | `string` | `'fas'` | FA prefix: `'fas'` for FA 5, `'fa-solid'` for FA 6. |
296
+ | `pasteAsPlainText` | `boolean` | `false` | Strip all formatting when pasting. |
297
+ | `pasteCleanHTML` | `boolean` | `true` | Sanitise HTML on paste. |
298
+ | `pasteStripAttributes` | `boolean` | `false` | Strip `class`, `style`, and `data-*` attributes from pasted HTML. |
299
+ | `markdownPaste` | `boolean` | `true` | Convert pasted Markdown shortcuts to HTML as you type. |
300
+ | `allowImageUpload` | `boolean` | `true` | Allow drag/paste/file upload in the image dialog. |
301
+ | `maxImageSize` | `number` | `5` | Maximum image file size in megabytes. |
302
+ | `tabSize` | `number` | `4` | Number of spaces inserted when Tab is pressed. |
303
+ | `historyLimit` | `number` | `100` | Maximum undo steps to retain. |
304
+ | `defaultFontFamily` | `string` | `'Arial'` | Font shown by default in the font-family dropdown. |
305
+ | `defaultFontSize` | `string` | `'14px'` | Default font size applied to new content. |
306
+ | `fontFamilies` | `string[]` | 10 fonts | Font families available in the font-family dropdown. |
307
+ | `stickyToolbar` | `boolean` | `false` | Pin the toolbar to the viewport top when the page is scrolled. |
308
+ | `stickyToolbarOffset` | `number` | `0` | Top offset in pixels for the sticky toolbar (e.g. height of a fixed nav bar). |
308
309
  | `theme` | `string` | `'light'` | Colour theme: `'light'` or `'dark'`. |
309
- | `codeHighlight` | `boolean` | `false` | Auto-load Prism.js for syntax highlighting inside `<pre><code>` blocks. |
310
- | `codeHighlightCDN` | `string` | cdnjs Prism 1.29.0 | Base CDN URL used when auto-loading Prism assets. |
311
- | `markdownPaste` | `boolean` | `true` | Convert pasted Markdown text to HTML when no HTML is present in the clipboard. |
312
- | `readOnly` | `boolean` | `false` | Start the editor in read-only (non-editable) mode. The toolbar is hidden and the content area is non-interactive. Toggle programmatically with `editor.setDisabled(bool)`. |
313
- | `focusColor` | `string` | `null` | Custom CSS colour for the editor focus ring, e.g. `'#f97316'`. Overrides the default blue when set. |
314
-
315
- ---
316
-
317
- ## Dialogs
318
-
319
- ### Link dialog
320
- Fields: **URL** (type=url, required), **Display text** (auto-populated from the current selection), **Open in new tab** checkbox.
321
- When the caret is inside an existing `<a>` element, the dialog pre-fills with the current link's values and updates in-place on confirm.
322
-
323
- ### Image dialog
324
- Fields: **Image URL** (type=url), **Alt text**, and — when `allowImageUpload` is `true` a **file picker** (accepts `image/*`). Selecting a file embeds it as a base64 data URI unless `onImageUpload` is provided.
325
-
326
- ### Video dialog
327
- Fields: **Video URL** (YouTube / Vimeo / direct file), **Width** (px, default 560). Supported URL formats:
328
- - `https://www.youtube.com/watch?v=…` `<iframe>` embed
329
- - `https://youtu.be/…` `<iframe>` embed
330
- - `https://vimeo.com/…` `<iframe>` embed
331
- - Direct `.mp4`, `.webm`, `.ogg` URL `<video controls>` element
332
-
333
- ### Emoji picker
334
- Displays ~380 Unicode emoji in a scrollable grid grouped into 7 categories. Filter by category tab or keyword search. Clicking an emoji inserts it immediately as a plain Unicode character — no extra "Insert" step. All characters are UTF-8 / utf8mb4 compatible.
335
-
336
- ### FA Icon picker
337
- Displays FontAwesome 6 Free Solid icons grouped into 8 categories: **Popular**, **Interface**, **Navigation**, **Media**, **Communication**, **Files**, **People**, **Objects**. Filter by category or keyword search. Before inserting, configure:
338
- - **Style** — Solid, Regular, or Light (Pro)
339
- - **Size** — Inherit, 0.75em, 1em, 1.25em, 1.5em, 2em, 3em
340
- - **Colour** — colour picker + "Use colour" toggle
341
-
342
- The icon is inserted as `<i class="fa-solid fa-{name}" style="…">`. If FontAwesome is not loaded on the page, the dialog automatically injects the FA 6 CDN stylesheet on first open.
343
-
344
- ---
345
-
346
- ## Table tooltip
347
-
348
- Clicking inside any table opens a floating tooltip with the following actions:
349
-
350
- | Group | Actions |
351
- |---|---|
352
- | Rows | Add Row Above, Add Row Below, Delete Row |
353
- | Columns | Add Column Left, Add Column Right, Delete Column |
354
- | Cells | Merge Cells |
355
- | Resize | Column Width (px / %), Row Height (px) |
356
- | Danger | Delete Table |
310
+ | `readOnly` | `boolean` | `false` | Start the editor in non-editable (read-only) mode with toolbar hidden. |
311
+ | `spellcheck` | `boolean` | `true` | Enable browser spellcheck in the editable area. |
312
+ | `direction` | `string` | `'ltr'` | Text direction: `'ltr'` or `'rtl'`. |
313
+ | `autoSave` | `boolean` | `false` | Persist content to `localStorage` on every change. |
314
+ | `autoSaveKey` | `string` | `'autumnnote-autosave'` | `localStorage` key used when `autoSave` is enabled. |
315
+ | `maxChars` | `number` | `0` | Maximum character count. `0` = unlimited. Shows warning in statusbar. |
316
+ | `maxWords` | `number` | `0` | Maximum word count. `0` = unlimited. Shows warning in statusbar. |
317
+ | `tableHeaderRow` | `boolean` | `false` | Insert a `<thead>` header row when creating new tables. |
318
+ | `codeHighlight` | `boolean` | `true` | Auto-load Prism.js for syntax highlighting inside `<pre><code>` blocks. |
319
+ | `codeHighlightCDN` | `string` | cdnjs Prism 1.29.0 | Base CDN URL for Prism assets. |
320
+ | `colorSwatches` | `string[]` | `[]` | Custom brand colour swatches prepended to the colour picker palette. |
321
+ | `focusColor` | `string` | `null` | Custom focus ring colour (any valid CSS colour). Overrides the default blue. |
322
+ | `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. |
323
+ | `onChange` | `Function` | `null` | `(html: string) => void` — called on every content change. |
324
+ | `onFocus` | `Function` | `null` | `(context) => void` — called when the editor gains focus. |
325
+ | `onBlur` | `Function` | `null` | `(context) => void` called when the editor loses focus. |
326
+ | `onInit` | `Function` | `null` | `(context) => void` — called once after the editor is initialised. |
327
+ | `onImageUpload` | `Function` | `null` | `(files: FileList) => void` — custom upload handler. Overrides base64 embed. |
328
+ | `onImageError` | `Function` | `null` | `({ file, message }) => void` called when an image is rejected. |
329
+ | `onPaste` | `Function` | `null` | `({ text, html }) => void` — called after every paste event. |
330
+ | `onSelectionChange` | `Function` | `null` | `(context) => void` — called when cursor or selection changes. |
331
+ | `onDestroy` | `Function` | `null` | `(context) => void` — called just before the editor is destroyed. |
332
+ | `onCharLimitReached` | `Function` | `null` | `(context) => void` — called when `maxChars` is hit. |
333
+ | `onWordLimitReached` | `Function` | `null` | `(context) => void` — called when `maxWords` is hit. |
357
334
 
358
335
  ---
359
336
 
360
337
  ## Toolbar Customisation
361
338
 
362
- The `toolbar` option accepts an array of button groups. Each group is a sub-array of button definition objects:
339
+ The `toolbar` option accepts an array of **groups**. Each group is an array of button definition objects exported from the package:
363
340
 
364
341
  ```js
365
- import AutumnNote from 'AutumnNote';
366
- import {
342
+ import AutumnNote, {
367
343
  boldBtn, italicBtn, underlineBtn, strikeBtn,
344
+ superscriptBtn, subscriptBtn,
345
+ alignLeftBtn, alignCenterBtn, alignRightBtn, alignJustifyBtn,
346
+ ulBtn, olBtn, checklistBtn, indentBtn, outdentBtn,
347
+ undoBtn, redoBtn,
348
+ hrBtn, linkBtn, imageBtn, videoBtn,
349
+ emojiBtn, iconBtn, tableBtn,
350
+ fontFamilyBtn, paragraphStyleBtn, lineHeightBtn,
368
351
  foreColorBtn, backColorBtn,
369
- linkBtn, imageBtn, videoBtn, tableBtn,
370
- emojiBtn, iconBtn,
371
- codeviewBtn, fullscreenBtn,
372
- } from 'AutumnNote/src/js/module/Buttons.js';
373
-
374
- AutumnNote.create('#editor', {
375
- toolbar: [
376
- [boldBtn, italicBtn, underlineBtn, strikeBtn],
377
- [foreColorBtn, backColorBtn],
378
- [linkBtn, imageBtn, videoBtn, tableBtn],
379
- [emojiBtn, iconBtn],
380
- [codeviewBtn, fullscreenBtn],
381
- ],
382
- });
352
+ findBtn, findReplaceBtn,
353
+ codeviewBtn, fullscreenBtn, shortcutsBtn,
354
+ } from 'autumnnote';
383
355
  ```
384
356
 
385
- **Available buttons**
386
-
387
- | Export | Type | Tooltip |
388
- |---|---|---|
389
- | `paragraphStyleBtn` | dropdown | Paragraph Style |
390
- | `fontFamilyBtn` | dropdown | Font Family |
391
- | `lineHeightBtn` | dropdown | Line Height |
392
- | `undoBtn` / `redoBtn` | button | Undo / Redo |
393
- | `boldBtn` / `italicBtn` / `underlineBtn` / `strikeBtn` | button | Text style |
394
- | `superscriptBtn` / `subscriptBtn` | button | Super / Subscript |
395
- | `foreColorBtn` / `backColorBtn` | color picker | Text colour / Highlight colour |
396
- | `alignLeftBtn` / `alignCenterBtn` / `alignRightBtn` / `alignJustifyBtn` | button | Alignment |
397
- | `ulBtn` / `olBtn` / `indentBtn` / `outdentBtn` | button | Lists & indentation |
398
- | `hrBtn` | button | Horizontal Rule |
399
- | `linkBtn` | button | Insert Link |
400
- | `imageBtn` | button | Insert Image |
401
- | `videoBtn` | button | Insert Video |
402
- | `tableBtn` | grid picker | Insert Table |
403
- | `emojiBtn` | button | Insert Emoji |
404
- | `iconBtn` | button | Insert FA Icon |
405
- | `codeviewBtn` | button | HTML Code View |
406
- | `fullscreenBtn` | button | Fullscreen |
407
-
408
- ### Setting global defaults
357
+ ### Default toolbar layout
409
358
 
410
359
  ```js
411
- import AutumnNote from 'AutumnNote';
412
-
413
- // Apply once before any create() calls
414
- Object.assign(AutumnNote.defaults, {
415
- height: 400,
416
- placeholder: 'Write something…',
417
- fontAwesomeClass: 'fa-solid',
418
- fontFamilies: ['Inter', 'Roboto', 'Georgia', 'Courier New'],
419
- });
360
+ [
361
+ [paragraphStyleBtn, fontFamilyBtn, lineHeightBtn],
362
+ [undoBtn, redoBtn],
363
+ [boldBtn, italicBtn, underlineBtn, strikeBtn],
364
+ [superscriptBtn, subscriptBtn],
365
+ [foreColorBtn, backColorBtn],
366
+ [alignLeftBtn, alignCenterBtn, alignRightBtn, alignJustifyBtn],
367
+ [ulBtn, olBtn, checklistBtn, indentBtn, outdentBtn],
368
+ [hrBtn, linkBtn, imageBtn, videoBtn, tableBtn, emojiBtn, iconBtn],
369
+ [codeviewBtn, fullscreenBtn, shortcutsBtn],
370
+ ]
420
371
  ```
421
372
 
422
- ---
423
-
424
- ## Custom Image Upload
373
+ ### Custom toolbar example
425
374
 
426
375
  ```js
427
376
  AutumnNote.create('#editor', {
428
- allowImageUpload: true,
429
- onImageUpload(files) {
430
- const formData = new FormData();
431
- formData.append('file', files[0]);
432
-
433
- fetch('/api/upload', { method: 'POST', body: formData })
434
- .then(r => r.json())
435
- .then(({ url }) => {
436
- // Insert the returned URL into the editor
437
- this.invoke('editor.insertImage', url, files[0].name);
438
- });
439
- },
440
- });
441
- ```
442
-
443
- ---
444
-
445
- ## Multiple Instances
446
-
447
- ```js
448
- const editors = AutumnNote.create('.rich-editor', { height: 250 });
449
- // editors is Context[] when selector matches multiple elements
450
-
451
- // Iterate all instances
452
- document.querySelectorAll('.rich-editor').forEach((el) => {
453
- const editor = AutumnNote.getInstance(el);
454
- console.log(editor.getHTML());
455
- });
456
- ```
457
-
458
- ---
459
-
460
- ## Project Structure
461
-
462
- ```
463
- src/
464
- ├── js/
465
- │ ├── core/
466
- │ │ ├── dom.js DOM utilities (createElement, on, closest, …)
467
- │ │ ├── range.js Selection / Range API helpers (withSavedRange, …)
468
- │ │ ├── func.js General helpers (mergeDeep, debounce, …)
469
- │ │ ├── key.js Keyboard key constants
470
- │ │ ├── lists.js Array helpers
471
- │ │ ├── env.js Browser / platform detection
472
- │ │ ├── markdown.js Lightweight Markdown → HTML converter (paste handling)
473
- │ │ └── sanitise.js DOM-based HTML and URL sanitiser (shared by all modules)
474
- │ ├── editing/
475
- │ │ ├── History.js Undo / redo stack (100 levels)
476
- │ │ ├── Style.js execCommand style wrappers
477
- │ │ ├── Table.js Table creation and cell manipulation
478
- │ │ └── Typing.js Tab / Enter key behaviour
479
- │ ├── module/
480
- │ │ ├── Editor.js Core editing commands + getHTML / setHTML + sanitiser
481
- │ │ ├── Toolbar.js Toolbar UI, button rendering (SVG + FA), dropdowns, colour picker
482
- │ │ ├── Buttons.js Button / dropdown / colorpicker definitions and defaultToolbar
483
- │ │ ├── Statusbar.js Word & character count + drag-to-resize
484
- │ │ ├── Clipboard.js Paste sanitisation (HTML clean + plain-text mode)
485
- │ │ ├── ContextMenu.js Right-click context menu (cut, copy, paste, format tools)
486
- │ │ ├── Placeholder.js CSS-based placeholder
487
- │ │ ├── Codeview.js HTML source view toggle
488
- │ │ ├── Fullscreen.js Fullscreen mode
489
- │ │ ├── LinkDialog.js Link insert / edit dialog
490
- │ │ ├── LinkTooltip.js Floating toolbar for links (open / edit / unlink)
491
- │ │ ├── ImageDialog.js Image insert dialog (URL + optional file upload)
492
- │ │ ├── ImageTooltip.js Floating toolbar for images (edit / delete)
493
- │ │ ├── ImageResizer.js Drag handle to resize images
494
- │ │ ├── VideoDialog.js Video embed dialog (YouTube, Vimeo, direct file)
495
- │ │ ├── VideoTooltip.js Floating toolbar for video embeds (edit / delete)
496
- │ │ ├── VideoResizer.js Drag handle to resize video embeds
497
- │ │ ├── TableTooltip.js Floating toolbar for tables (row/col/cell management)
498
- │ │ ├── CodeTooltip.js Floating toolbar for code blocks (copy / delete)
499
- │ │ ├── EmojiDialog.js Unicode emoji picker (~380 emoji, 7 categories)
500
- │ │ ├── IconDialog.js FontAwesome icon picker (FA 6 Free Solid, 8 categories)
501
- │ │ └── ShortcutsDialog.js Keyboard shortcuts reference dialog (Shift+?)
502
- │ ├── Context.js Editor instance hub — module registry and event bus
503
- │ ├── settings.js Default options (AsnOptions)
504
- │ ├── renderer.js DOM layout builder
505
- │ └── index.js Public entry point + AutumnNote factory
506
- └── styles/
507
- ├── _variables.scss SCSS design tokens (colours, spacing, radii, transitions)
508
- └── AutumnNote.scss Main stylesheet
509
- ```
510
-
511
- ---
512
-
513
- ## Development
514
-
515
- ```bash
516
- # Install dependencies
517
- npm install
518
-
519
- # Start dev server with HMR (Vite)
520
- npm run dev
521
-
522
- # Build library (ES module + UMD + CSS)
523
- npm run build
524
-
525
- # Run unit tests (Vitest)
526
- npm test
527
- ```
528
-
529
- Build output in `dist/`:
530
- - `AutumnNote.es.js` — ES module (tree-shakeable)
531
- - `AutumnNote.umd.js` — UMD bundle (script tag / CommonJS)
532
- - `AutumnNote.css` — compiled stylesheet
533
-
534
- ---
535
-
536
- ## Comparison with Summernote
537
-
538
- | Feature | Summernote | AutumnNote |
539
- |---|---|---|
540
- | jQuery required | Yes | No |
541
- | Bootstrap required | Optional | No |
542
- | Build system | Grunt | Vite |
543
- | Module format | IIFE | ES module + UMD |
544
- | Written in | ES5 / ES6 mix | ES2022 |
545
- | HTML sanitisation | Basic | DOM-based (strips scripts, XSS vectors) |
546
- | Emoji picker | No | Yes (~380 Unicode emoji, 7 categories) |
547
- | FA icon picker | No | Yes (FA 6 Free Solid, 8 categories, searchable) |
548
- | Video embeds | No | Yes (YouTube, Vimeo, direct file) |
549
- | Image / video resize | No | Yes (drag handles) |
550
- | Inline tooltips | No | Yes (link, image, video, table, code) |
551
- | Context menu | No | Yes (with format copy/paste) |
552
- | Right-click context menu | No | Yes |
553
-
554
- ---
555
-
556
- ## License
557
-
558
- MIT
559
-
560
- ---
561
-
562
- ## Features
563
-
564
- ### Editing
565
- - **Text formatting** — bold, italic, underline, strikethrough, superscript, subscript
566
- - **Paragraph styles** — Normal, H1–H6, Blockquote, Code block
567
- - **Font family** — customisable dropdown (10 families by default)
568
- - **Line height** — dropdown from 1.0 to 3.0
569
- - **Text & highlight colour** — native colour picker with last-used colour
570
- - **Alignment** — left, center, right, justify
571
- - **Lists** — unordered and ordered, with indent / outdent
572
- - **Undo / redo** — built-in history stack (100 levels, `Ctrl+Z` / `Ctrl+Y`)
573
- - **Tab key** — configurable spaces-per-tab, smart list indentation
574
-
575
- ### Insert
576
- - **Horizontal rule**
577
- - **Link dialog** — insert / edit hyperlinks with text and target options
578
- - **Image dialog** — insert by URL or file upload (base64 embed); configurable max size
579
- - **Video dialog** — insert YouTube / Vimeo / direct video URLs as responsive embeds
580
- - **Table** — interactive grid picker (up to 10×10), context-menu actions (add/delete row/col, merge, split)
581
- - **Emoji picker** — ~380 Unicode emoji across 7 categories (Smileys, People, Animals, Food, Travel, Objects, Symbols) with keyword search; click to insert instantly
582
- - **FA Icon picker** — browse FontAwesome 6 Free Solid icons by category with keyword search; configurable style, size, and colour
583
-
584
- ### UI
585
- - **Toolbar** — fully configurable button groups; auto-renders with SVG fallback or FontAwesome icons
586
- - **Inline tooltips** — link, image, video, table, and code-block context tooltips with edit/delete actions
587
- - **Image resizer** — drag handle to resize inserted images
588
- - **Video resizer** — drag handle to resize inserted video embeds
589
- - **Statusbar** — word and character count + drag-to-resize editor height
590
- - **Code view** — toggle raw HTML source editor with sanitisation on apply
591
- - **Fullscreen** — expand to fill the viewport
592
- - **Placeholder** — CSS-based, zero DOM pollution
593
- - **Context menu** — right-click menu for common actions
594
-
595
- ### Integration
596
- - **No jQuery** — pure vanilla ES2022, zero runtime dependencies
597
- - **Bootstrap friendly** — optional Bootstrap 4/5 styling for toolbar buttons (`useBootstrap: true`)
598
- - **FontAwesome ready** — auto-detects FA on the page; falls back to inline SVG icons
599
- - **Plugin-ready** — register custom modules via `AutumnNote.defaults`
600
- - **Tree-shakeable** — ES module build; all core utilities are individually exported
601
-
602
- ### Security
603
- - All HTML (pasted, loaded via `setHTML`, or applied from code view) is sanitised through a DOM-based parser — strips `<script>`, `<iframe>`, `<object>`, all `on*` attributes
604
- - `javascript:` URLs are rejected in links and images
605
- - Clipboard paste sanitises and removes XSS vectors before inserting
606
-
607
- ---
608
-
609
- ## Installation
610
-
611
- ### npm / pnpm / yarn
612
-
613
- ```bash
614
- npm install AutumnNote
615
- ```
616
-
617
- ### CDN
618
-
619
- ```html
620
- <link rel="stylesheet" href="dist/AutumnNote.css" />
621
- <script src="dist/AutumnNote.umd.js"></script>
622
- ```
623
-
624
- > To use FontAwesome toolbar icons, include the FA stylesheet on your page. The editor auto-detects it and falls back to built-in SVG icons when FA is absent.
625
- >
626
- > ```html
627
- > <!-- FontAwesome 6 Free -->
628
- > <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css">
629
- > ```
630
-
631
- ---
632
-
633
- ## Quick Start
634
-
635
- ### ES Module
636
-
637
- ```js
638
- import AutumnNote from 'AutumnNote';
639
-
640
- const editor = AutumnNote.create('#my-editor', {
641
- placeholder: 'Start typing…',
642
- height: 300,
643
- onChange(html) {
644
- console.log(html);
645
- },
646
- });
647
- ```
648
-
649
- ### Script tag (UMD)
650
-
651
- ```html
652
- <div id="my-editor"><p>Hello!</p></div>
653
- <script src="dist/AutumnNote.umd.js"></script>
654
- <script>
655
- const editor = AutumnNote.create('#my-editor');
656
- </script>
657
- ```
658
-
659
- ### With Bootstrap 5
660
-
661
- ```js
662
- const editor = AutumnNote.create('#my-editor', {
663
- useBootstrap: true,
664
- bootstrapVersion: 5,
665
- toolbarButtonClass: 'btn btn-sm btn-light',
377
+ toolbar: [
378
+ [undoBtn, redoBtn],
379
+ [boldBtn, italicBtn, underlineBtn],
380
+ [ulBtn, olBtn, checklistBtn],
381
+ [linkBtn, imageBtn],
382
+ [findBtn, findReplaceBtn],
383
+ ],
666
384
  });
667
385
  ```
668
386
 
669
- ---
670
-
671
- ## API
672
-
673
- ### Factory
674
-
675
- | Method | Description |
676
- |---|---|
677
- | `AutumnNote.create(selector, options?)` | Creates editor instance(s). Returns a `Context` (or array of `Context`). |
678
- | `AutumnNote.destroy(selector)` | Destroys editor(s) and restores the original element. |
679
- | `AutumnNote.getInstance(selector)` | Returns the `Context` for a given element, or `null`. |
680
- | `AutumnNote.defaults` | Global default options — mutate before calling `create()` to set project-wide defaults. |
681
-
682
- ### Context (editor instance)
683
-
684
- | Method | Description |
685
- |---|---|
686
- | `editor.getHTML()` | Returns the current HTML content (sanitised). |
687
- | `editor.setHTML(html)` | Sets HTML content (sanitised). |
688
- | `editor.getText()` | Returns plain text (no markup). |
689
- | `editor.clear()` | Clears all content. |
690
- | `editor.setDisabled(bool)` | Enables or disables the editor. |
691
- | `editor.destroy()` | Removes the editor and restores the original element. |
692
- | `editor.on(event, fn)` | Subscribes to an editor event. Returns an unsubscribe function. |
693
- | `editor.invoke('module.method', ...args)` | Calls any registered module method by name. |
694
-
695
- ### Events
696
-
697
- | Name | Payload | Description |
698
- |---|---|---|
699
- | `change` | `html: string` | Fired after every content mutation. |
700
- | `focus` | — | Editor gained focus. |
701
- | `blur` | — | Editor lost focus. |
702
-
703
- ---
704
-
705
- ## Options
706
-
707
- | Option | Type | Default | Description |
708
- |---|---|---|---|
709
- | `placeholder` | `string` | `''` | Placeholder text shown when the editor is empty. |
710
- | `height` | `number` | `200` | Initial / minimum editor height in px. |
711
- | `minHeight` | `number` | `100` | Hard minimum height in px. |
712
- | `maxHeight` | `number` | `0` | Maximum height in px. `0` = unlimited. |
713
- | `focus` | `boolean` | `false` | Auto-focus the editor on initialisation. |
714
- | `resizeable` | `boolean` | `true` | Show the drag-to-resize handle in the statusbar. |
715
- | `toolbar` | `Array` | default | Array of button group arrays. See [Toolbar customisation](#toolbar-customisation). |
716
- | `useBootstrap` | `boolean` | `false` | Apply Bootstrap classes to toolbar buttons. |
717
- | `bootstrapVersion` | `number` | `5` | Bootstrap major version (`4` or `5`). |
718
- | `toolbarButtonClass` | `string` | `'btn btn-sm btn-light'` | CSS classes for toolbar buttons when `useBootstrap` is `true`. |
719
- | `useFontAwesome` | `boolean` | `true` | Use FA icons when FontAwesome is detected on the page. |
720
- | `fontAwesomeClass` | `string` | `'fas'` | FA prefix — `'fas'` for FA 5, `'fa-solid'` for FA 6. |
721
- | `pasteAsPlainText` | `boolean` | `false` | Strip all formatting on paste. |
722
- | `pasteCleanHTML` | `boolean` | `true` | Sanitise HTML on paste. |
723
- | `allowImageUpload` | `boolean` | `true` | Allow file upload in the image dialog. |
724
- | `maxImageSize` | `number` | `5` | Maximum image upload size in MB. |
725
- | `onImageUpload` | `Function` | `null` | Custom upload handler `(files: FileList) => void`. Overrides base64 embed. |
726
- | `tabSize` | `number` | `0` | Spaces inserted per Tab key press. `0` = browser default. |
727
- | `defaultFontFamily` | `string` | `'Arial'` | Font applied to the editable area. |
728
- | `fontFamilies` | `string[]` | (10 fonts) | Font families listed in the font-family dropdown. |
729
- | `onChange` | `Function` | `null` | `(html: string) => void` — called on every change. |
730
- | `onFocus` | `Function` | `null` | `(context: Context) => void` |
731
- | `onBlur` | `Function` | `null` | `(context: Context) => void` |
732
- | `stickyToolbar` | `boolean` | `false` | Stick the toolbar to the viewport top when the page is scrolled. |
733
- | `stickyToolbarOffset` | `number` | `0` | Top offset in px for the sticky toolbar (e.g. height of a fixed navigation bar). |
734
- | `theme` | `string` | `'light'` | Colour theme: `'light'` or `'dark'`. |
735
- | `codeHighlight` | `boolean` | `false` | Auto-load Prism.js for syntax highlighting inside `<pre><code>` blocks. |
736
- | `codeHighlightCDN` | `string` | cdnjs Prism 1.29.0 | Base CDN URL used when auto-loading Prism assets. |
737
- | `markdownPaste` | `boolean` | `true` | Convert pasted Markdown text to HTML when no HTML is present in the clipboard. |
738
-
739
- ---
740
-
741
- ## Toolbar Customisation
387
+ ### Hiding the toolbar
742
388
 
743
- The `toolbar` option accepts an array of button groups. Each group is an array of button definition objects imported from `Buttons.js`:
389
+ Pass an empty array for a toolbar-less editor (keyboard shortcuts still work):
744
390
 
745
391
  ```js
746
- import AutumnNote from 'AutumnNote';
747
- import {
748
- boldBtn, italicBtn, underlineBtn,
749
- linkBtn, imageBtn, emojiBtn, iconBtn,
750
- codeviewBtn, fullscreenBtn,
751
- } from 'AutumnNote/src/js/module/Buttons.js';
752
-
753
- AutumnNote.create('#editor', {
754
- toolbar: [
755
- [boldBtn, italicBtn, underlineBtn],
756
- [linkBtn, imageBtn, emojiBtn, iconBtn],
757
- [codeviewBtn, fullscreenBtn],
758
- ],
759
- });
392
+ AutumnNote.create('#editor', { toolbar: [] });
760
393
  ```
761
394
 
762
- **Available buttons**
395
+ ### All available buttons
763
396
 
764
397
  | Export | Tooltip |
765
398
  |---|---|
@@ -771,16 +404,51 @@ AutumnNote.create('#editor', {
771
404
  | `superscriptBtn` / `subscriptBtn` | Super / Subscript |
772
405
  | `foreColorBtn` / `backColorBtn` | Text colour / Highlight colour |
773
406
  | `alignLeftBtn` / `alignCenterBtn` / `alignRightBtn` / `alignJustifyBtn` | Alignment |
774
- | `ulBtn` / `olBtn` / `indentBtn` / `outdentBtn` | Lists & indentation |
407
+ | `ulBtn` / `olBtn` / `checklistBtn` | Lists |
408
+ | `indentBtn` / `outdentBtn` | Indentation |
775
409
  | `hrBtn` | Horizontal Rule |
776
- | `linkBtn` | Insert Link |
410
+ | `linkBtn` | Insert / Edit Link |
777
411
  | `imageBtn` | Insert Image |
778
412
  | `videoBtn` | Insert Video |
779
413
  | `tableBtn` | Insert Table (grid picker) |
780
414
  | `emojiBtn` | Insert Emoji |
781
415
  | `iconBtn` | Insert FA Icon |
416
+ | `findBtn` | Find (Ctrl+F) |
417
+ | `findReplaceBtn` | Find & Replace (Ctrl+H) |
782
418
  | `codeviewBtn` | HTML Code View |
783
419
  | `fullscreenBtn` | Fullscreen |
420
+ | `shortcutsBtn` | Keyboard Shortcuts dialog |
421
+
422
+ ### Setting global defaults
423
+
424
+ ```js
425
+ Object.assign(AutumnNote.defaults, {
426
+ height: 400,
427
+ theme: 'dark',
428
+ fontFamilies: ['Inter', 'Roboto', 'Georgia', 'Courier New'],
429
+ colorSwatches: ['#e74c3c', '#f39c12', '#2ecc71', '#3498db'],
430
+ });
431
+ ```
432
+
433
+ ---
434
+
435
+ ## Keyboard Shortcuts
436
+
437
+ | Keys | Action |
438
+ |---|---|
439
+ | `Ctrl + Z` | Undo |
440
+ | `Ctrl + Shift + Z` / `Ctrl + Y` | Redo |
441
+ | `Ctrl + B` | Bold |
442
+ | `Ctrl + I` | Italic |
443
+ | `Ctrl + U` | Underline |
444
+ | `Ctrl + F` | Open Find dialog |
445
+ | `Ctrl + H` | Open Find & Replace dialog |
446
+ | `Shift + Enter` | Insert line break |
447
+ | `Tab` | Insert spaces / indent list item |
448
+ | `Shift + Tab` | Outdent list item |
449
+ | `Shift + ?` | Open Keyboard Shortcuts dialog |
450
+
451
+ > The number of spaces inserted by `Tab` is controlled by the `tabSize` option.
784
452
 
785
453
  ---
786
454
 
@@ -790,89 +458,115 @@ AutumnNote.create('#editor', {
790
458
  src/
791
459
  ├── js/
792
460
  │ ├── core/
793
- │ │ ├── dom.js DOM utilities
794
- │ │ ├── range.js Selection / Range API helpers
795
- │ │ ├── func.js General utility helpers
461
+ │ │ ├── dom.js DOM utilities (createElement, on, closest, ...)
462
+ │ │ ├── range.js Selection and Range API helpers
463
+ │ │ ├── func.js General helpers (mergeDeep, debounce, ...)
796
464
  │ │ ├── key.js Keyboard key constants
797
465
  │ │ ├── lists.js Array helpers
798
- │ │ ├── env.js Browser / platform detection
799
- │ │ ├── markdown.js Markdown HTML converter
466
+ │ │ ├── env.js Browser/platform detection
467
+ │ │ ├── markdown.js Lightweight Markdown to HTML converter
800
468
  │ │ └── sanitise.js DOM-based HTML and URL sanitiser
801
469
  │ ├── editing/
802
- │ │ ├── History.js Undo / redo stack
470
+ │ │ ├── History.js Undo/redo stack (configurable depth)
803
471
  │ │ ├── Style.js execCommand style wrappers
804
- │ │ ├── Table.js Table creation and manipulation
805
- │ │ └── Typing.js Tab / Enter key behaviour
472
+ │ │ ├── Table.js Table creation and cell manipulation
473
+ │ │ └── Typing.js Tab/Enter/ArrowKey behaviour and FA icon caret handling
806
474
  │ ├── module/
807
- │ │ ├── Editor.js Core editing commands + getHTML / setHTML
808
- │ │ ├── Toolbar.js Toolbar UI and button rendering
809
- │ │ ├── Buttons.js Button and dropdown definitions
810
- │ │ ├── Statusbar.js Status bar + drag-to-resize
811
- │ │ ├── Clipboard.js Paste sanitisation
812
- │ │ ├── ContextMenu.js Right-click context menu
813
- │ │ ├── Placeholder.js Placeholder text
814
- │ │ ├── Codeview.js HTML source view
475
+ │ │ ├── Editor.js Core editing commands, getHTML/setHTML, sanitiser
476
+ │ │ ├── Toolbar.js Toolbar UI, button rendering (SVG + FA), dropdowns, colour picker
477
+ │ │ ├── Buttons.js Button/dropdown/colorpicker definitions and defaultToolbar
478
+ │ │ ├── Statusbar.js Word and character count, drag-to-resize, limit warnings
479
+ │ │ ├── Clipboard.js Paste sanitisation (HTML clean, plain-text, Markdown modes)
480
+ │ │ ├── ContextMenu.js Right-click context menu with colour palette
481
+ │ │ ├── Placeholder.js CSS-based placeholder
482
+ │ │ ├── Codeview.js HTML source view toggle
815
483
  │ │ ├── Fullscreen.js Fullscreen mode
816
- │ │ ├── LinkDialog.js Link insert / edit dialog
817
- │ │ ├── LinkTooltip.js Inline link tooltip
818
- │ │ ├── ImageDialog.js Image insert dialog (URL + upload)
819
- │ │ ├── ImageTooltip.js Inline image tooltip
820
- │ │ ├── ImageResizer.js Drag-to-resize for images
821
- │ │ ├── VideoDialog.js Video embed dialog
822
- │ │ ├── VideoTooltip.js Inline video tooltip
823
- │ │ ├── VideoResizer.js Drag-to-resize for video embeds
824
- │ │ ├── TableTooltip.js Table context tooltip
825
- │ │ ├── CodeTooltip.js Code block context tooltip
826
- │ │ ├── EmojiDialog.js Unicode emoji picker
827
- │ │ ├── IconDialog.js FontAwesome icon picker
828
- │ │ └── ShortcutsDialog.js Keyboard shortcuts dialog
829
- │ ├── Context.js Editor instance hub
830
- ├── settings.js Default options
484
+ │ │ ├── FindReplace.js Find and Replace dialog (Ctrl+F / Ctrl+H)
485
+ │ │ ├── LinkDialog.js Link insert/edit dialog
486
+ │ │ ├── LinkTooltip.js Floating toolbar for links (open/edit/unlink)
487
+ │ │ ├── ImageDialog.js Image insert dialog (URL + file upload with MIME filtering)
488
+ │ │ ├── ImageTooltip.js Floating toolbar for images (edit/crop/delete)
489
+ │ │ ├── ImageResizer.js rAF-based drag handle to resize images
490
+ │ │ ├── ImageCropOverlay.js Inline crop tool (corner/edge handles, canvas export)
491
+ │ │ ├── VideoDialog.js Video embed dialog (YouTube, Vimeo, direct file)
492
+ │ │ ├── VideoTooltip.js Floating toolbar for video embeds (edit/delete)
493
+ │ │ ├── VideoResizer.js rAF-based drag handle to resize video embeds
494
+ │ │ ├── TableTooltip.js Floating toolbar for tables (row/col/merge/unmerge/select mode)
495
+ │ │ ├── CodeTooltip.js Floating toolbar for code blocks (copy/delete)
496
+ │ │ ├── EmojiDialog.js Unicode emoji picker (~380 emoji, 7 categories)
497
+ ├── IconDialog.js FontAwesome icon picker (FA 6 Free Solid, 8 categories)
498
+ │ └── ShortcutsDialog.js Keyboard shortcuts reference dialog (Shift+?)
499
+ │ ├── Context.js Editor instance hub: module registry and event bus
500
+ │ ├── settings.js Default options (AsnOptions)
831
501
  │ ├── renderer.js DOM layout builder
832
- │ └── index.js Public entry point
502
+ │ └── index.js Public entry point + AutumnNote factory
833
503
  └── styles/
834
- ├── _variables.scss SCSS design tokens
835
- └── AutumnNote.scss Main stylesheet
504
+ ├── _variables.scss SCSS design tokens (colours, spacing, radii, transitions)
505
+ └── autumnnote.scss Main stylesheet
836
506
  ```
837
507
 
838
- ---
839
-
840
- ## Development
508
+ ### Development commands
841
509
 
842
510
  ```bash
843
- # Install dependencies
844
- npm install
845
-
846
- # Start dev server (Vite)
847
- npm run dev
511
+ npm install # install dependencies
512
+ npm run dev # start Vite dev server with HMR
513
+ npm run build # build ES + UMD + CSS to dist/
514
+ npm test # run Vitest test suite once
515
+ npm run test:watch # run tests in watch mode
516
+ npm run lint # ESLint
517
+ npm run typecheck # TypeScript type check (tsconfig.json)
518
+ ```
848
519
 
849
- # Build library
850
- npm run build
520
+ Build output in `dist/`:
851
521
 
852
- # Run tests
853
- npm test
854
- ```
522
+ | File | Format | Use |
523
+ |---|---|---|
524
+ | `autumnnote.es.js` | ES Module | `import` in bundlers (tree-shakeable) |
525
+ | `autumnnote.umd.js` | UMD | `<script>` tag / CommonJS |
526
+ | `autumnnote.css` | CSS | Styles for both builds |
855
527
 
856
528
  ---
857
529
 
858
- ## Comparison with Summernote
859
-
860
- | Feature | Summernote | AutumnNote |
861
- |---|---|---|
862
- | jQuery required | Yes | No |
863
- | Bootstrap required | Optional | No |
864
- | Build system | Grunt | Vite |
865
- | Module format | IIFE | ES module + UMD |
866
- | Written in | ES5 / ES6 mix | ES2022 |
867
- | HTML sanitisation | Basic | DOM-based (strips scripts, XSS vectors) |
868
- | Emoji picker | No | Yes (~380 Unicode emoji) |
869
- | FA icon picker | No | Yes (FA 6 Free Solid, searchable) |
870
- | Video embeds | No | Yes (YouTube, Vimeo, direct) |
871
- | Image / video resize | No | Yes (drag handles) |
872
- | Inline tooltips | No | Yes (link, image, video, table, code) |
530
+ ## Comparison
531
+
532
+ The table below compares AutumnNote against editors commonly used in this category. Specific library names are omitted to avoid bias.
533
+
534
+ | Feature | Editor A | Editor B | Editor C | **AutumnNote** |
535
+ |---|---|---|---|---|
536
+ | jQuery dependency | Required | Required | Optional | **None** |
537
+ | Runtime dependencies | Several | Several | 1–2 | **Zero** |
538
+ | JavaScript standard | ES5 / legacy | ES6 mix | ES6 | **ES2022** |
539
+ | Module format | IIFE / AMD | IIFE | CommonJS + IIFE | **ES Module + UMD** |
540
+ | Build tool | Grunt | Gulp | Rollup | **Vite** |
541
+ | TypeScript definitions | External / partial | Partial | Yes | **Yes (bundled)** |
542
+ | HTML sanitisation | Basic | Whitelist-only | Moderate | **DOM-based (XSS-safe)** |
543
+ | Iframe support in setHTML | No | No | Restricted | **Yes (host-trusted)** |
544
+ | Dark theme | No | No | Yes | **Yes (built-in)** |
545
+ | RTL text direction | No | Partial | No | **Yes** |
546
+ | Built-in i18n locales | No | No | Partial | **Yes (8 languages)** |
547
+ | Custom locale object | No | No | No | **Yes** |
548
+ | Checklist (todo list) | No | No | No | **Yes** |
549
+ | Find and Replace | No | No | No | **Yes (Ctrl+F / Ctrl+H)** |
550
+ | Emoji picker | No | No | No | **Yes (~380 emoji)** |
551
+ | FA icon picker | No | No | No | **Yes (FA 6, searchable)** |
552
+ | Video embeds | No | No | No | **Yes (YouTube, Vimeo, direct)** |
553
+ | Image crop tool | No | No | No | **Yes (inline)** |
554
+ | Image / video resize | No | No | No | **Yes (drag handles)** |
555
+ | Inline tooltips | No | No | Partial | **Yes (link, image, video, table, code)** |
556
+ | Table cell merge/unmerge | No | No | Yes | **Yes** |
557
+ | Table cell selection mode | No | No | No | **Yes** |
558
+ | Context menu | No | No | No | **Yes (with colour palette)** |
559
+ | Sticky toolbar | No | No | No | **Yes** |
560
+ | Auto-save to localStorage | No | No | No | **Yes** |
561
+ | Character / word limits | No | No | No | **Yes** |
562
+ | Custom focus ring colour | No | No | No | **Yes** |
563
+ | Read-only mode | No | Partial | Yes | **Yes** |
564
+ | Custom colour swatches | No | No | No | **Yes** |
565
+ | Code view (HTML source) | No | Yes | Yes | **Yes (sanitised)** |
566
+ | Syntax highlighting | No | No | Partial | **Yes (Prism.js via CDN)** |
873
567
 
874
568
  ---
875
569
 
876
570
  ## License
877
571
 
878
- MIT
572
+ [MIT](LICENSE)