API Reference

AutumnNote v1.8.0 — Fast. Lightweight. Reliable. Efficient.

Installation

Install via npm (or pnpm / yarn):

npm install autumnnote

Or use the CDN  Eno 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:

PropTypeDescription
optionsAsnOptionsEditor configuration object. All AsnOptions fields are accepted.
classNamestringCSS class applied to the container <div>.
styleCSSPropertiesInline styles applied to the container <div>.
refReact.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:

PropTypeDescription
optionsAsnOptionsEditor 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).

OptionTypeDefaultDescription
placeholderstring''Placeholder text when editor is empty.
heightnumber200Editor minimum height in px.
minHeightnumber100Minimum height of the editable area.
maxHeightnumber0Maximum height in px. 0 = unlimited.
focusbooleanfalseAuto-focus editor on initialization.
resizablebooleantrueShow drag-handle in statusbar to resize the editor.
readOnlybooleanfalseStart in read-only (non-editable) mode. Toggle at runtime with setDisabled().
spellcheckbooleantrueEnable browser spell-check.
toolbarArray<Array<…>>defaultToolbar2D array of button groups. Each item is a ButtonDef object or a registered button name string.
stickyToolbarbooleanfalseStick toolbar to viewport top when page is scrolled.
stickyToolbarOffsetnumber0Top 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.
bubbleToolbarbooleanfalseShow a mini floating toolbar above selected text.
bubbleToolbarItemsstring[]see belowButton names in the bubble toolbar. Default: ['bold','italic','underline','link','foreColor','hiliteColor','removeFormat'].
theme'light' | 'dark''light'Color theme. Use 'dark' or toggle via container.classList.add('an-theme-dark').
focusColorstring | nullnullCustom focus-ring colour (e.g. '#f97316').
direction'ltr' | 'rtl''ltr'Text direction for the editable area.
defaultFontFamilystring'Arial'Default font family applied to the editor on init.
defaultFontSizestring'14px'Default font size.
fontFamiliesstring[]10 fontsOptions shown in the font-family dropdown.
colorSwatchesstring[][]Custom hex swatches prepended to all colour pickers.
tabSizenumber4Spaces inserted per Tab key press (in code blocks).
maxCharsnumber0Max character count. 0 = unlimited. Shows warning in statusbar near limit.
maxWordsnumber0Max word count. 0 = unlimited.
historyLimitnumber100Maximum undo/redo history steps.
pasteAsPlainTextbooleanfalseForce plain-text paste  Estrip all HTML.
pasteCleanHTMLbooleantrueSanitise HTML on paste (removes scripts, event handlers, etc.).
pasteStripAttributesbooleanfalseStrip class, style, and data-* attributes from pasted HTML.
markdownPastebooleantrueConvert pasted Markdown to HTML.
markdownShortcutsbooleantrueInline Markdown triggers: **bold**, ## heading, - item, etc.
allowImageUploadbooleantrueAllow file uploads in the image dialog.
maxImageSizenumber5Maximum image upload size in MB.
codeHighlightbooleantrueAuto-load Prism.js for syntax highlighting in code blocks.
codeHighlightCDNstringPrism 1.29 CDNPrism CDN base URL (override to self-host).
autoSavebooleanfalseSave content to localStorage on every change.
autoSaveKeystring'autumnnote-autosave'localStorage key for auto-save.
autoSaveRestorebooleanfalseShow restore banner when a draft exists (requires autoSave: true).
autoSaveRestoreTimeoutnumber7Max draft age in days for restore. 0 = no expiry.
useBootstrapbooleanfalseApply Bootstrap button classes to toolbar buttons.
useFontAwesomebooleantrueRender toolbar icons as Font Awesome glyphs (requires FA CSS).
fontAwesomeClassstring'fas'FA prefix class ('fas' for v5, 'fa-solid' for v6).
tableHeaderRowbooleanfalseInsert a <thead> row when creating tables.
mentionobject | nullnull@mention autocomplete config. See mention options.
langstring | object'en'UI language. Built-in: 'en' 'vi' 'ja' 'zh' 'fr' 'de' 'es' 'ko'. Pass an object to override individual strings.

Callback options

OptionSignatureDescription
onChange(html: string) => voidFires when content changes (debounced).
onFocus(ctx: Context) => voidFires when editor receives focus.
onBlur(ctx: Context) => voidFires when editor loses focus.
onInit(ctx: Context) => voidFires after all modules have initialised.
onDestroy(ctx: Context) => voidFires before the editor is destroyed.
onSelectionChange(ctx: Context) => voidFires on cursor or selection change.
onPaste(data: {text, html}) => voidFires after every paste event.
onImageUpload(files: File[]) => voidCustom image upload handler (replaces default inline embed).
onImageError(err) => voidFires when image upload fails.
onCharLimitReached(ctx: Context) => voidFires when maxChars is reached.
onWordLimitReached(ctx: Context) => voidFires when maxWords is reached.
onAutoSaveRestore(html, ctx) => voidFires 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  Ecalling 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  Eapplied 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.version

String containing the library version, e.g. '1.3.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.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().

Tip: Option callbacks and 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) => { /* ... */ });

Toolbar Configuration

The toolbar option is a 2D array  Eeach inner array is a button group (separated by a divider). Items can be ButtonDef objects imported from the library, or string names of registered buttons.

import AutumnNote, {
  boldBtn, italicBtn, underlineBtn,
  ulBtn, olBtn, linkBtn, imageBtn,
  undoBtn, redoBtn,
  defaultToolbar, // full default toolbar
} from 'autumnnote';

// Custom toolbar
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

ExportTooltipType
boldBtnBold (Ctrl+B)button
italicBtnItalic (Ctrl+I)button
underlineBtnUnderline (Ctrl+U)button
strikeBtnStrikethroughbutton
inlineCodeBtnInline Code (Ctrl+`)button
superscriptBtnSuperscriptbutton
subscriptBtnSubscriptbutton
alignLeftBtn / alignCenterBtn / alignRightBtn / alignJustifyBtnAlignmentbutton
ulBtn / olBtn / checklistBtnListsbutton
indentBtn / outdentBtnIndent / Outdentbutton
undoBtn / redoBtnUndo / Redobutton
hrBtnHorizontal Rulebutton
linkBtn / imageBtn / videoBtnInsert dialogsbutton
tableBtnInsert Tablegrid
emojiBtn / iconBtnEmoji / FA Iconbutton
foreColorBtn / backColorBtnText / Highlight Colorcolorpicker
paragraphStyleBtnParagraph Styleselect
fontFamilyBtn / fontSizeBtn / lineHeightBtnTypographyselect
removeFormatBtn / directionBtnUtilitiesbutton
codeviewBtn / fullscreenBtnViewbutton
findBtn / findReplaceBtn / printBtn / shortcutsBtnToolsbutton
defaultToolbarFull default toolbar arrayarray

What's New v1.8.0

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) .csv file 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  Ecustom modules, toolbar buttons, and event handlers  Einto 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:

FieldTypeRequiredDescription
namestring✁E/td>Unique plugin identifier. Used as key for getPlugin(name) and hasPlugin(name).
versionstring E/td>Semantic version string  Einformational only, not validated by the runtime.
buttonsToolbarItemDef[] E/td>Button definitions registered to the global button registry immediately when AutumnNote.use() is called  Ebefore any editor is created, so they are available by name string in toolbar config.
install(ctx, opts) ↁET E/td>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) ↁEvoid E/td>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()  Eusable 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  Eall 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  Ereturns an unsubscribe function
const unsub = context.on('change', (html) => {
  console.log('word count:', context.getWordCount());
});

// Available event names:
// 'change'           Econtent changed (debounced ~400 ms)
// 'focus'            Eeditor gained focus
// 'blur'             Eeditor 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:

PathDescription
editor.bold / italic / underline / strikethroughToggle text formatting at cursor.
editor.inlineCodeToggle inline <code> at cursor.
editor.foreColor / hiliteColorApply text / highlight colour. Pass colour string as 2nd arg: ctx.invoke('editor.foreColor', '#e11d48').
editor.insertHTMLInsert HTML at current cursor position.
editor.insertTextInsert plain text at current cursor position.
editor.getHTMLReturns current HTML string.
editor.setHTMLReplace entire content.
editor.undo / redoStep through history.
editor.focusFocus the editable area.
editor.afterCommandSnapshot undo history and trigger onChange. Call this after any direct DOM mutation.
toolbar.refreshRe-evaluate isActive / isDisabled on all toolbar buttons (debounced via rAF).
toolbar.rebuildTear down and re-render the entire toolbar. Use after post-create button registration.
linkDialog.showOpen the Insert Link dialog.
imageDialog.showOpen the Insert Image dialog.
findReplace.showOpen the Find dialog.
fullscreen.toggleToggle fullscreen mode.
codeview.toggleToggle 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  Ecallable 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'); // ↁEnumber
}

Installation

// ── Global (applies to every future create() call) ─────────────────────────
AutumnNote.use(MyPlugin, { limit: 500 });
AutumnNote.hasPlugin('my-plugin'); // ↁEtrue

const editor = AutumnNote.create('#editor', {
  toolbar: [[boldBtn, 'highlight']], // 'highlight' resolved from plugin.buttons
});

editor.getPlugin('my-plugin').getLimit(); // ↁE500

// ── 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']]
Timing: 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  Esafe to call ctx.invoke(), ctx.registerModule(), and any content methods.

Internationalisation

Pass a language code to the lang option. Built-in locales: en (default), vi, ja, zh, fr, de, es, ko.

const editor = AutumnNote.create('#editor', { lang: 'vi' });

// Override specific strings
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

AutumnNote 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'