API Reference
Autumn Note v3.1.0 — Fast. Lightweight. Reliable. Efficient.
Installation
Install via npm (or pnpm / yarn):
npm install autumnnote
Or use the CDN — no build step needed:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/autumnnote/dist/autumnnote.css">
<script src="https://cdn.jsdelivr.net/npm/autumnnote"></script>
Framework Wrappers v1.6.0
Official React and Vue 3 wrappers ship as separate packages in the same pnpm workspace.
Each wrapper is a thin lifecycle bridge — it mounts an editor instance on the host element and
exposes the Context object to the parent component via ref.
React — autumnnote-react
Install the wrapper alongside the core:
npm install autumnnote autumnnote-react
import { useRef } from 'react';
import AutumnNoteEditor from 'autumnnote-react';
import 'autumnnote/dist/autumnnote.css';
function MyEditor() {
const editorRef = useRef(null);
function handleSave() {
const html = editorRef.current.getHTML();
console.log(html);
}
return (
<>
<AutumnNoteEditor
ref={editorRef}
options={{
placeholder: 'Start typing…',
height: 300,
bubbleToolbar: true,
onChange: (html) => console.log(html),
}}
/>
<button onClick={handleSave}>Save</button>
</>
);
}
The component uses forwardRef + useImperativeHandle to expose the
underlying Context instance. All instance methods
(getHTML, setHTML, invoke, etc.) are accessible via the ref.
The editor initialises once on mount; to reinitialise with new options, pass a new key prop.
Props:
| Prop | Type | Description |
|---|---|---|
| options | AsnOptions | Editor configuration object. All AsnOptions fields are accepted. |
| className | string | CSS class applied to the container <div>. |
| style | CSSProperties | Inline styles applied to the container <div>. |
| ref | React.Ref<Context> | Forwarded ref — exposes the Context instance. |
Vue 3 — autumnnote-vue
Install the wrapper alongside the core:
npm install autumnnote autumnnote-vue
<script setup>
import { ref } from 'vue';
import AutumnNoteEditor from 'autumnnote-vue';
import 'autumnnote/dist/autumnnote.css';
const editorRef = ref(null);
function handleSave() {
const html = editorRef.value.editor.value.getHTML();
console.log(html);
}
</script>
<template>
<AutumnNoteEditor
ref="editorRef"
:options="{ placeholder: 'Start typing…', height: 300 }"
/>
<button @click="handleSave">Save</button>
</template>
The component uses defineExpose({ editor }) where editor is a Vue
ref<Context | null>. Access the instance via
editorRef.value.editor.value. The editor is created in onMounted
and destroyed in onUnmounted. Use v-if to force remount on options change.
Props:
| Prop | Type | Description |
|---|---|---|
| options | AsnOptions | Editor configuration object. All AsnOptions fields are accepted. |
Quick Start
Wrap any <textarea> or <div> in your HTML:
import AutumnNote from 'autumnnote';
import 'autumnnote/dist/autumnnote.css';
const editor = AutumnNote.create('#editor', {
placeholder: 'Start typing…',
height: 300,
bubbleToolbar: true,
onChange(html) {
console.log('content changed:', html);
},
});
Get and set content:
editor.setHTML('<p>Hello <strong>world</strong></p>');
const html = editor.getHTML();
const markdown = editor.getMarkdown();
editor.destroy(); // clean up when done
All Options 50+ options
Pass these to AutumnNote.create(selector, options) or AutumnNote.setDefaults(options).
| Option | Type | Default | Description |
|---|---|---|---|
| placeholder | string | '' | Placeholder text when editor is empty. |
| height | number | 200 | Editor minimum height in px. |
| minHeight | number | 100 | Minimum height of the editable area. |
| maxHeight | number | 0 | Maximum height in px. 0 = unlimited. |
| focus | boolean | false | Auto-focus editor on initialization. |
| resizable | boolean | true | Show drag-handle in statusbar to resize the editor. |
| statusbar | boolean | true | Show the statusbar (word/char count, resize handle). false hides it, e.g. for a read-only viewer; getWordCount() / getCharCount() keep working. |
| readOnly | boolean | false | Start in read-only (non-editable) mode. Toggle at runtime with setDisabled(). |
| spellcheck | boolean | true | Enable browser spell-check. |
| toolbar | Array<Array<…>> | defaultToolbar | 2D array of button groups. Each item is a built-in button name ('bold'), a name from buttons / registerButton(), or a ButtonDef object. |
| stickyToolbar | boolean | false | Stick toolbar to viewport top when page is scrolled. |
| stickyToolbarOffset | number | 0 | Top offset in px for sticky toolbar (e.g. fixed nav bar height). |
| toolbarOverflow | 'wrap' | 'scroll' | 'wrap' | Toolbar overflow strategy when buttons don't fit on one row. |
| bubbleToolbar | boolean | false | Show a mini floating toolbar above selected text. |
| bubbleToolbarItems | string[] | see below | Button names in the bubble toolbar. Default: ['bold','italic','underline','link','foreColor','hiliteColor','removeFormat']. |
| theme | 'light' | 'dark' | 'auto' | 'light' | Color theme. 'auto' follows the OS setting. Switch at runtime with editor.updateOptions({ theme: 'dark' }); colours are customisable through themeVars. |
| focusColor | string | null | null | Custom focus-ring colour (e.g. '#f97316'). |
| themeVars | object | null | null | Design-token overrides for this editor, e.g. { primary: '#f97316', radius: '10px' }. See Theming. |
| zIndexOffset | number | 0 | Added to the z-index of every floating layer (tooltips, popovers, dialogs, fullscreen) — e.g. to sit above a host modal. |
| popupContainer | string | Element | ShadowRoot | null | Where dialogs, tooltips and menus mount (default document.body). Use inside a focus-trapping modal or a shadow root. |
| direction | 'ltr' | 'rtl' | 'ltr' | Text direction for the editable area. |
| defaultFontFamily | string | 'Arial' | Default font family applied to the editor on init. |
| defaultFontSize | string | '14px' | Default font size. |
| fontFamilies | string[] | 10 fonts | Options shown in the font-family dropdown. |
| fontSizes | string[] | '8px'–'72px' | Sizes in the font-size dropdown. |
| lineHeights | string[] | '1.0'–'3.0' | Values in the line-height dropdown. |
| paragraphStyles | { value, label }[] | Normal, H1–H6, Quote, Code | Block formats in the paragraph-style dropdown (value is a block tag). |
| colorSwatches | string[] | [] | Custom swatches prepended to the colour palette (toolbar, bubble toolbar, context menu). |
| colorPalette | string[] | null | null | Replaces the built-in 24-colour palette in the toolbar, bubble toolbar and context menu. |
| tabSize | number | 4 | Spaces inserted per Tab key press (in code blocks). |
| maxChars | number | 0 | Max character count. 0 = unlimited. Shows warning in statusbar near limit. |
| maxWords | number | 0 | Max word count. 0 = unlimited. |
| historyLimit | number | 100 | Maximum undo/redo history steps. |
| pasteAsPlainText | boolean | false | Force plain-text paste — strip all HTML. |
| pasteCleanHTML | boolean | true | Sanitise HTML on paste (removes scripts, event handlers, etc.). |
| pasteStripAttributes | boolean | false | Strip class, style, and data-* attributes from pasted HTML. |
| maxPasteSize | number | 5242880 | Maximum paste size in bytes (default 5 MB, 0 = unlimited). |
| markdownPaste | boolean | true | Convert pasted Markdown to HTML. |
| markdownShortcuts | boolean | true | Inline Markdown triggers: **bold**, ## heading, - item, etc. |
| allowImageUpload | boolean | true | Allow file uploads in the image dialog. |
| maxImageSize | number | 5 | Maximum image upload size in MB. |
| linkDefaults | object | { openInNewTab: false, … } | Link dialog defaults: openInNewTab, rel (always includes noopener), defaultProtocol. Relative links are never prefixed. |
| videoProviders | array | null | null | Extra video sources: [{ name, match: RegExp, embed: (match, url) => embedUrl }], tried before YouTube/Vimeo. |
| iframeHosts | string[] | null | null | Extra hostnames whose iframes survive sanitisation (exact hostnames, HTTPS only). List only hosts you trust. |
| codeHighlight | boolean | true | Auto-load Prism.js for syntax highlighting in code blocks. |
| codeHighlightCDN | string | Prism 1.29 CDN | Prism CDN base URL (override to self-host). |
| autoSave | boolean | false | Save content to localStorage on every change. |
| autoSaveKey | string | 'autumnnote-autosave' | localStorage key for auto-save. |
| autoSaveRestore | boolean | false | Show restore banner when a draft exists (requires autoSave: true). |
| autoSaveRestoreTimeout | number | 7 | Max draft age in days for restore. 0 = no expiry. |
| useBootstrap | boolean | false | Apply Bootstrap button classes to toolbar buttons. |
| useFontAwesome | boolean | true | Render toolbar icons as Font Awesome glyphs (requires FA CSS). |
| fontAwesomeClass | string | 'fas' | FA prefix class ('fas' for v5, 'fa-solid' for v6). |
| icons | object | null | null | Per-editor icon overrides keyed by button or icon name — SVG markup, or a CSS class list. |
| buttons | object | array | null | null | Per-editor button definitions, usable by name in toolbar. |
| keyMap | object | null | null | Keyboard shortcut overrides merged over the defaults: false disables, a string rebinds to a command or button, a function handles it. |
| tableHeaderRow | boolean | false | Insert a <thead> row when creating tables. |
| mention | object | null | null | @mention autocomplete config. See mention options. |
| lang | string | object | 'en' | UI language. Built-in: 'en' 'vi' 'ja' 'zh' 'fr' 'de' 'es' 'ko'. Pass an object to override individual strings. |
Callback options
| Option | Signature | Description |
|---|---|---|
| onChange | (html: string) => void | Fires when content changes (debounced). |
| onFocus | (ctx: Context) => void | Fires when editor receives focus. |
| onBlur | (ctx: Context) => void | Fires when editor loses focus. |
| onInit | (ctx: Context) => void | Fires after all modules have initialised. |
| onDestroy | (ctx: Context) => void | Fires before the editor is destroyed. |
| onSelectionChange | (ctx: Context) => void | Fires on cursor or selection change. |
| onBeforeCommand | ({ name, value?, source }) => void | false | Fires before a toolbar, shortcut, context-menu or bubble-toolbar command. Return false to cancel. |
| onPaste | (data: {text, html}) => void | false | string | Fires on every paste, before insertion. Return false to cancel, or an HTML string to insert instead (sanitised). |
| onImageUpload | (files: File[]) => void | Custom image upload handler (replaces default inline embed). |
| onImageError | (err) => void | Fires when image upload fails. |
| onCharLimitReached | (ctx: Context) => void | Fires when maxChars is reached. |
| onWordLimitReached | (ctx: Context) => void | Fires when maxWords is reached. |
| onAutoSaveRestore | (html, ctx) => void | Fires after user restores a draft. |
Static Methods
Methods on the AutumnNote object itself (not on an editor instance).
AutumnNote.create(selector, options?)
Creates one or more editor instances. Returns a single Context when selector matches one element, or an array when it matches several. Caches instances in a WeakMap — calling create on the same element twice returns the cached instance.
const editor = AutumnNote.create('#my-editor', {
height: 350,
bubbleToolbar: true,
});
// Multiple elements
const editors = AutumnNote.create('.editor', { height: 200 });
AutumnNote.destroy(selector)
Destroys all editor instances matching selector and restores the original element.
AutumnNote.getInstance(selector)
Returns the Context for a given element, or null if none exists.
AutumnNote.setDefaults(options)
Merges options into the global defaults — applied to every future create() call.
AutumnNote.setDefaults({
theme: 'dark',
bubbleToolbar: true,
lang: 'vi',
});
AutumnNote.resetDefaults()
Restores global defaults to factory values.
AutumnNote.registerModule(name, ModuleClass)
Registers a custom module that will be included in every future editor instance. Modules receive the Context object in their constructor and must implement initialize() and optionally destroy().
AutumnNote.use(plugin, options?)
Installs a plugin globally. Plugin buttons are registered immediately (before create()); install() is called after all built-in modules initialise. See Plugin API.
AutumnNote.hasPlugin(name)
Returns true if a plugin with the given name has been registered globally.
AutumnNote.registerButton(btnDef)
Registers a single button definition in the global registry so it can be referenced by name string in toolbar config. Call ctx.invoke('toolbar.rebuild') after registering on an already-running editor.
AutumnNote.registerIcon(name, icon)
Sets the icon for a button or icon name in every editor: SVG markup, or a CSS class list such as 'bi bi-type-bold'. The per-editor icons option wins.
AutumnNote.version
String containing the library version, e.g. '3.0.0'.
Instance Methods
Methods on a Context object returned by AutumnNote.create().
Content
editor.getHTML()
Returns the current content as an HTML string.
editor.setHTML(html)
Sets the editor content. The HTML is sanitised before insertion.
editor.getText()
Returns the plain-text content (no HTML tags).
editor.setText(text)
Sets plain-text content (HTML-escaped, wraps in <p>).
editor.getMarkdown()
Converts current content to Markdown and returns the string.
editor.setMarkdown(md)
Converts Markdown to HTML and sets editor content.
editor.insertHTML(html)
Inserts HTML at the current cursor position.
editor.insertText(text)
Inserts plain text at the current cursor position.
editor.clear()
Clears all editor content.
editor.clearHistory()
Resets the undo/redo history stack.
editor.isEmpty()
Returns true when the editor has no meaningful content.
State & Info
editor.getWordCount()
Returns the current word count (CJK-aware via Intl.Segmenter).
editor.getCharCount()
Returns the current character count (excluding newlines).
editor.setDisabled(disabled)
Enables or disables the editor. true = read-only, toolbar hidden.
editor.updateOptions(options)
Merges options into a live editor. Theme, themeVars, focusColor, zIndexOffset, toolbar options, statusbar, resizable and popupContainer apply immediately; passing an unchanged value does nothing, so framework wrappers can call it on every render.
editor.downloadHTML(filename?) / .downloadText() / .downloadMarkdown()
Triggers a browser download of the content in the specified format. Default filenames: 'document.html', 'document.txt', 'document.md'.
editor.print(title?)
Opens the content in a new window and triggers the browser print dialog.
editor.invoke(path, ...args)
Invokes a method on a registered module. Format: 'moduleName.methodName'.
editor.invoke('toolbar.rebuild'); // Rebuild toolbar after button registration
editor.invoke('editor.bold'); // Apply bold
editor.invoke('findReplace.show'); // Open Find dialog
editor.invoke('fullscreen.toggle'); // Toggle fullscreen
Events
editor.on(eventName, handler)
Subscribes to an editor event. Returns an unsubscribe function.
const unsub = editor.on('change', (html) => {
console.log('changed:', html);
});
// Later:
unsub(); // unsubscribe
editor.off(eventName, handler)
Unsubscribes a specific handler from an event.
editor.registerModule(name, ModuleClass)
Registers and initialises a custom module on this specific instance only.
Plugin
editor.use(plugin, options?)
Installs a plugin on this instance only. Plugin buttons are registered immediately; if the toolbar is already rendered, call editor.invoke('toolbar.rebuild').
editor.getPlugin<T>(name)
Returns the public API returned by plugin.install(), or null.
editor.destroy()
Removes all editor DOM, fires onDestroy, calls plugin.uninstall() on all plugins, and restores the original element.
Callbacks & Events
Events can be subscribed in two ways: as option callbacks (passed to create()) or via editor.on().
on() listeners both fire for the same event. Use callbacks for one-time setup; use on() when you need to unsubscribe later.
// Option callback
const editor = AutumnNote.create('#editor', {
onChange(html) { /* ... */ },
onFocus(ctx) { /* ... */ },
onBlur(ctx) { /* ... */ },
onInit(ctx) { /* editor is ready */ },
onDestroy(ctx) { /* cleanup */ },
});
// Programmatic subscription
const unsub = editor.on('change', (html) => { /* ... */ });
editor.on('selectionChange', (ctx) => { /* cursor moved */ });
editor.on('focus', (ctx) => { /* ... */ });
editor.on('blur', (ctx) => { /* ... */ });
editor.on('beforeCommand', ({ name, source }) => name !== 'codeview'); // false cancels
editor.on('paste', ({ text }) => text.length > 10000 ? false : undefined);
Toolbar Configuration
The toolbar option is a 2D array — each inner array is a button group (separated by a divider). Items can be built-in button names, names from the buttons option or registerButton(), or ButtonDef objects imported from the library.
import AutumnNote, {
boldBtn, italicBtn, underlineBtn,
ulBtn, olBtn, linkBtn, imageBtn,
undoBtn, redoBtn,
defaultToolbar, // full default toolbar
} from 'autumnnote';
// By name — works from JSON, framework props or the UMD build
AutumnNote.create('#editor', {
toolbar: [['bold', 'italic', 'underline'], ['ul', 'ol'], ['link', 'image'], ['undo', 'redo']],
});
// Or with the exported definitions
const editor = AutumnNote.create('#editor', {
toolbar: [
[boldBtn, italicBtn, underlineBtn],
[ulBtn, olBtn],
[linkBtn, imageBtn],
[undoBtn, redoBtn],
],
});
Or mix named button objects with string names (after registering custom buttons):
AutumnNote.registerButton({
name: 'myButton',
icon: 'star', // SVG id or FontAwesome class name
tooltip: 'My Action',
action: (ctx) => { ctx.insertHTML('<mark>highlighted</mark>'); },
isActive: (ctx) => false,
});
const editor = AutumnNote.create('#editor', {
toolbar: [[boldBtn, italicBtn, 'myButton']],
});
All exported buttons
| Export | Tooltip | Type |
|---|---|---|
| boldBtn | Bold (Ctrl+B) | button |
| italicBtn | Italic (Ctrl+I) | button |
| underlineBtn | Underline (Ctrl+U) | button |
| strikeBtn | Strikethrough | button |
| inlineCodeBtn | Inline Code (Ctrl+`) | button |
| superscriptBtn | Superscript | button |
| subscriptBtn | Subscript | button |
| alignLeftBtn / alignCenterBtn / alignRightBtn / alignJustifyBtn | Alignment | button |
| ulBtn / olBtn / checklistBtn | Lists | button |
| indentBtn / outdentBtn | Indent / Outdent | button |
| undoBtn / redoBtn | Undo / Redo | button |
| hrBtn | Horizontal Rule | button |
| linkBtn / imageBtn / videoBtn | Insert dialogs | button |
| tableBtn | Insert Table | grid |
| emojiBtn / iconBtn | Emoji / FA Icon | button |
| foreColorBtn / backColorBtn | Text / Highlight Color | colorpicker |
| paragraphStyleBtn | Paragraph Style | select |
| fontFamilyBtn / fontSizeBtn / lineHeightBtn | Typography | select |
| removeFormatBtn / directionBtn | Utilities | button |
| codeviewBtn / fullscreenBtn | View | button |
| findBtn / findReplaceBtn / printBtn / shortcutsBtn | Tools | button |
| defaultToolbar | Full default toolbar array | array |
What's New v3.1.0
Version 3.1 replaces the browser's deprecated document.execCommand with the editor's own formatting engine. Bold, colours, fonts, links, headings, lists and indentation now produce the same markup and toolbar state on Chrome, Firefox and Safari.
Same result on every browser
Every command is a DOM transform the editor performs itself, tested on Chromium, Firefox and WebKit. Toolbar buttons read their state from the document, so they show exactly what the command toggles — including underline inside inline code, where the old API got it wrong.
Markup changes
- Strikethrough is written as
<s>(not<strike>). - Colour, highlight, font family and font size are
<span style>, never<font>. - Indenting a paragraph sets
margin-left: 40pxinstead of wrapping it in a borderless<blockquote>. - Existing content in the old forms is still read and edited correctly.
Copy and cut
The context-menu Copy/Cut and the copy buttons use the async Clipboard API, which browsers only provide on HTTPS and localhost. A cut never deletes text that did not reach the clipboard.
Fixes
- Inserting an emoji or icon could overwrite text selected elsewhere on the page.
setMarkdown(getMarkdown())now keeps underline, colour/font spans, superscript and subscript.- The character count no longer includes invisible caret anchors.
Full list in the CHANGELOG.
What's New v3.0.0
Version 3.0 is a customisation release: the look, shortcuts, icons, buttons and dropdown lists can all be changed per editor without forking or rebuilding the SCSS.
Theming with CSS custom properties
Every colour, radius and font token is a --an-* property. Theme every editor from CSS, per mode from .an-theme-dark, or one editor through themeVars — no Sass build. Themes can be switched at runtime and never touch <body>.
// Every editor, from CSS
.an-container, .an-portal { --an-primary: #f97316; --an-radius: 10px; }
// One editor
const editor = AutumnNote.create('#editor', {
theme: 'auto',
themeVars: { primary: '#f97316', radius: '10px' },
});
editor.updateOptions({ theme: 'dark' }); // switch at runtime
Per-editor portal
Each editor mounts its dialogs, tooltips and menus in its own .an-portal, which carries the editor's theme. popupContainer moves it into a modal or shadow root; zIndexOffset lifts every floating layer.
Icons, buttons, dropdowns and shortcuts
Toolbars accept built-in button names, buttons defines buttons for one editor, and icons / registerIcon() swap icons. keyMap disables or rebinds shortcuts — the shortcuts dialog follows it.
AutumnNote.create('#editor', {
toolbar: [['bold', 'italic', 'underline'], ['ul', 'ol'], ['link', 'save']],
buttons: {
save: { icon: '<svg viewBox="0 0 24 24">…</svg>', tooltip: 'Save', action: (ctx) => save(ctx.getHTML()) },
},
icons: { bold: 'bi bi-type-bold' },
fontSizes: ['12px', '14px', '18px', '24px'],
keyMap: {
'Mod+F': false, // give Ctrl+F back to the browser
'Mod+S': 'save', // bind to a toolbar button
'Mod+Shift+D': (ctx) => ctx.insertText(new Date().toLocaleDateString()),
},
onBeforeCommand: ({ name }) => name !== 'codeview' || user.isAdmin,
});
Integration hooks
beforeCommand can cancel any command; paste handlers can cancel or rewrite a paste; linkDefaults, videoProviders and iframeHosts tune links and embeds. statusbar: false hides the statusbar (#119).
Upgrading from 2.x
- Node 22 LTS (22.22.2+) or Node 24.15+ is required to install the package; Node 20 is no longer supported.
- Floating UI now lives inside
.an-portal— selectors such asbody > .an-dialog-overlayneed updating. <body>no longer getsan-theme-dark/an-theme-auto; key page CSS on the editor container instead.maxPasteSizeis in bytes, as documented — use5 * 1024 * 1024, not5.- Shortcuts match Shift and Alt exactly (
Ctrl+Shift+Bno longer bolds). Add extra combos throughkeyMap. onPastereturn values are used:falsecancels the paste, a string replaces it.
Full list in the CHANGELOG.
v1.8.0 highlights
Version 1.8.0 expands the public API with new Context methods, adds UMD button definitions, whole-word search, paste size limit, configurable image resize minimum, i18n improvements, and more.
Table Sort, CSV Export & Cell Padding
The Table Tooltip now includes three new data-management actions:
- Sort Ascending / Descending — sort all rows by the active column; numeric values are compared as numbers, text uses locale-aware comparison; merged-cell layouts are handled correctly.
- Export as CSV — downloads the full table as a UTF-8 (BOM)
.csvfile with proper quote-escaping for cells containing commas or newlines. - Cell Padding — open the size popover to apply a uniform padding (in px) to all selected cells.
A Toggle Header Row button also converts the first row between <thead><th> (header) and <tbody><td> (data), creating or removing the <thead> wrapper as needed.
Find & Replace — Regex Mode
A new .* toggle button in the Find & Replace panel enables JavaScript regular expression search. The compiled regex is cached across keystrokes for performance. Use cases: find all words matching a pattern, locate email addresses, replace formatted numbers, and so on. Works with case-sensitive mode; regex compilation errors are handled gracefully.
Code Block Line Numbers
The Code Tooltip now includes a Toggle Line Numbers button. When active, a CSS counter generates a numbered gutter on the left side of the code block — no JavaScript counting, no DOM changes to the content. The state syncs automatically when switching between code blocks.
Dark-Auto Theme
A new theme: 'auto' option makes the editor follow the operating system's dark-mode preference automatically:
AutumnNote.create('#editor', { theme: 'auto' });
The CSS @media (prefers-color-scheme: dark) activates the full dark palette when the OS is in dark mode; the editor stays light otherwise. theme: 'dark' and theme: 'light' continue to force a specific theme regardless of OS preference.
Undo / Redo Count API
Two new instance methods let host applications query the undo/redo stack depth:
editor.getUndoCount(); // → number of available undo steps
editor.getRedoCount(); // → number of available redo steps
Useful for disabling Undo/Redo toolbar buttons in custom UIs, or showing a step-counter badge in host applications.
Promise-Based @mention Search
The mention.onSearch callback now accepts both callback and Promise / async styles:
// Async / Promise style (new in 1.8.0)
AutumnNote.create('#editor', {
mention: {
onSearch: async (query) => {
const res = await fetch(`/api/users?q=${query}`);
return res.json(); // returns MentionItem[]
}
}
});
If the Promise rejects, the dropdown closes automatically. The original callback style remains fully supported.
Plugin API v1.4.1
The Plugin API lets you package editor extensions — custom modules, toolbar buttons, and event handlers — into a reusable, installable object. Plugins can be distributed as npm packages and installed in one line.
Plugin descriptor
A plugin is a plain object with the following fields:
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | ✓ | Unique plugin identifier. Used as key for getPlugin(name) and hasPlugin(name). |
| version | string | — | Semantic version string — informational only, not validated by the runtime. |
| buttons | ToolbarItemDef[] | — | Button definitions registered to the global button registry immediately when AutumnNote.use() is called — before any editor is created, so they are available by name string in toolbar config. |
| install | (ctx, opts) → T | — | Called after all 27 built-in modules have initialised. Receives the Context and the options object passed to use(). The return value becomes the plugin's public API accessible via ctx.getPlugin(name). |
| uninstall | (ctx) → void | — | Called when the editor instance is destroyed. Use this to release timers, remove external DOM, or disconnect external state. |
const MyPlugin = {
name: 'my-plugin', // required
version: '1.0.0', // optional
buttons: [ // registered BEFORE create() — usable by name in toolbar
{
name: 'highlight',
icon: 'highlighter', // SVG id or FontAwesome class suffix
tooltip: 'Highlight selection',
action: (ctx) => ctx.invoke('editor.hiliteColor', '#fef08a'),
isActive: (ctx) => false,
isDisabled: (ctx) => ctx.isEmpty(),
},
],
install(context, options) {
// Full context API available here — all modules are ready
context.on('change', (html) => { /* react to content changes */ });
context.registerModule('myModule', MyModule);
return { getLimit: () => options.limit ?? 0 }; // public API
},
uninstall(context) {
// Clean up side-effects (timers, external DOM, subscriptions…)
},
};
Inside install(context, options)
The context parameter is the full Context object. All methods documented in the Instance API section are available. The most useful inside plugins:
Event subscription
// Subscribe — returns an unsubscribe function
const unsub = context.on('change', (html) => {
console.log('word count:', context.getWordCount());
});
// Available event names:
// 'change' — content changed (debounced ~400 ms)
// 'focus' — editor gained focus
// 'blur' — editor lost focus
// Unsubscribe in uninstall() to avoid leaks
uninstall(ctx) { unsub(); }
Invoke built-in module methods
context.invoke('module.method', ...args) calls any public method on a registered module. Common paths:
| Path | Description |
|---|---|
| editor.bold / italic / underline / strikethrough | Toggle text formatting at cursor. |
| editor.inlineCode | Toggle inline <code> at cursor. |
| editor.foreColor / hiliteColor | Apply text / highlight colour. Pass colour string as 2nd arg: ctx.invoke('editor.foreColor', '#e11d48'). |
| editor.insertHTML | Insert HTML at current cursor position. |
| editor.insertText | Insert plain text at current cursor position. |
| editor.getHTML | Returns current HTML string. |
| editor.setHTML | Replace entire content. |
| editor.undo / redo | Step through history. |
| editor.focus | Focus the editable area. |
| editor.afterCommand | Snapshot undo history and trigger onChange. Call this after any direct DOM mutation. |
| toolbar.refresh | Re-evaluate isActive / isDisabled on all toolbar buttons (debounced via rAF). |
| toolbar.rebuild | Tear down and re-render the entire toolbar. Use after post-create button registration. |
| linkDialog.show | Open the Insert Link dialog. |
| imageDialog.show | Open the Insert Image dialog. |
| findReplace.show | Open the Find dialog. |
| fullscreen.toggle | Toggle fullscreen mode. |
| codeview.toggle | Toggle HTML code view. |
Register a custom module
A module is a class with initialize() (required) and destroy() (optional). It receives the Context in its constructor and can expose methods callable via context.invoke().
class WordBadgeModule {
constructor(context) {
this.context = context;
this._el = null;
this._unsub = null;
}
initialize() {
// Build UI
this._el = document.createElement('div');
this._el.className = 'word-badge';
this._el.textContent = '0 words';
this.context.layoutInfo.container.appendChild(this._el);
// React to content changes
this._unsub = this.context.on('change', () => this._update());
this._update();
return this;
}
// Public method — callable via ctx.invoke('wordBadge.getCount')
getCount() {
return this.context.getWordCount();
}
_update() {
const n = this.context.getWordCount();
this._el.textContent = `${n} word${n !== 1 ? 's' : ''}`;
}
destroy() {
this._unsub?.();
this._el?.remove();
this._el = null;
}
}
// Register inside install():
install(context) {
context.registerModule('wordBadge', WordBadgeModule);
// Now callable anywhere:
context.invoke('wordBadge.getCount'); // → number
}
Installation
// ── Global (applies to every future create() call) ─────────────────────────
AutumnNote.use(MyPlugin, { limit: 500 });
AutumnNote.hasPlugin('my-plugin'); // → true
const editor = AutumnNote.create('#editor', {
toolbar: [[boldBtn, 'highlight']], // 'highlight' resolved from plugin.buttons
});
editor.getPlugin('my-plugin').getLimit(); // → 500
// ── Per-instance (this editor only) ────────────────────────────────────────
const editor2 = AutumnNote.create('#editor2');
editor2.use(MyPlugin, { limit: 200 });
editor2.invoke('toolbar.rebuild'); // surface new buttons in already-rendered toolbar
// ── Standalone button registration ─────────────────────────────────────────
AutumnNote.registerButton({
name: 'timestamp',
icon: 'clock',
tooltip: 'Insert current timestamp',
action: (ctx) => ctx.invoke('editor.insertText', new Date().toLocaleString()),
});
// Then reference by name in any toolbar config:
// toolbar: [[boldBtn, 'timestamp']]
AutumnNote.use(plugin) registers plugin.buttons immediately (synchronously) so they are available when Toolbar builds during create(). plugin.install() is called after all 27 built-in modules are ready — safe to call ctx.invoke(), ctx.registerModule(), and any content methods.
Internationalisation
Eight locales ship with the package: en, vi, ja, zh, fr, de, es, ko. With a bundler only en is included by default — register the ones you need so unused languages stay out of your bundle. The UMD/CDN build pre-registers all eight.
import AutumnNote from 'autumnnote';
import { vi } from 'autumnnote/i18n/vi';
AutumnNote.registerLocale('vi', vi);
const editor = AutumnNote.create('#editor', { lang: 'vi' });
// Override specific strings (no registration needed)
const editor2 = AutumnNote.create('#editor2', {
lang: {
toolbar: { bold: 'Đậm (Ctrl+B)' },
findReplace: { noResults: 'Không tìm thấy' },
},
});
// Switch language at runtime (re-create editor)
const html = editor.getHTML();
editor.destroy();
const editor3 = AutumnNote.create('#editor', { lang: 'ja' });
editor3.setHTML(html);
TypeScript
Autumn Note ships bundled TypeScript definitions (types/index.d.ts). No @types package needed.
import AutumnNote, {
Context,
AsnOptions,
AsnPlugin,
ButtonDef,
ToolbarItemDef,
AsnLocale,
} from 'autumnnote';
// Typed options
const options: AsnOptions = {
height: 300,
bubbleToolbar: true,
onChange(html: string) { /* ... */ },
};
// Typed plugin
const myPlugin: AsnPlugin<{ version: string }> = {
name: 'typed-plugin',
install(ctx: Context, opts) {
return { version: '1.0.0' };
},
};
AutumnNote.use(myPlugin);
const editor = AutumnNote.create('#editor', options);
// Cast plugin API to typed interface
const api = editor.getPlugin<{ version: string }>('typed-plugin');
console.log(api?.version); // '1.0.0'