autumnnote 2.7.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -33,12 +33,13 @@ A **zero-dependency WYSIWYG rich-text editor** built with vanilla JavaScript (ES
33
33
  6. [Plugin API](#plugin-api)
34
34
  7. [API](#api)
35
35
  8. [Options](#options)
36
- 9. [Toolbar Customisation](#toolbar-customisation)
37
- 10. [Keyboard Shortcuts](#keyboard-shortcuts)
38
- 11. [Mentions](#mentions)
39
- 12. [Project Structure](#project-structure)
40
- 13. [Comparison](#comparison)
41
- 14. [License](#license)
36
+ 9. [Theming](#theming)
37
+ 10. [Toolbar Customisation](#toolbar-customisation)
38
+ 11. [Keyboard Shortcuts](#keyboard-shortcuts)
39
+ 12. [Mentions](#mentions)
40
+ 13. [Project Structure](#project-structure)
41
+ 14. [Comparison](#comparison)
42
+ 15. [License](#license)
42
43
 
43
44
  ---
44
45
 
@@ -359,12 +360,16 @@ const editor = AutumnNote.create('#my-editor', {
359
360
 
360
361
  ```js
361
362
  const editor = AutumnNote.create('#my-editor', { theme: 'dark' });
363
+ // or follow the OS setting, and switch later without re-creating
364
+ editor.updateOptions({ theme: 'auto' });
362
365
  ```
363
366
 
367
+ The theme applies to that editor only — a light and a dark editor can share a page. See [Theming](#theming) for colours.
368
+
364
369
  ### Read-only preview
365
370
 
366
371
  ```js
367
- const preview = AutumnNote.create('#preview', { readOnly: true });
372
+ const preview = AutumnNote.create('#preview', { readOnly: true, statusbar: false });
368
373
  preview.setHTML(savedHtml);
369
374
  ```
370
375
 
@@ -519,6 +524,7 @@ See the [full Plugin API docs →](https://autumn.konexforge.com/docs.html#plugi
519
524
  | `AutumnNote.registerLocale(code, locale)` | Registers a locale so `lang: '<code>'` can select it. Only English ships in the ESM build — see [Languages](#languages). |
520
525
  | `AutumnNote.registerModule(name, Class)` | Registers a custom module included in every future instance. |
521
526
  | `AutumnNote.registerButton(btnDef)` | Adds a button to the global registry. Call `editor.invoke('toolbar.rebuild')` afterwards to render it on existing instances. |
527
+ | `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'`. |
522
528
  | `AutumnNote.registerSlashCommand(command)` | Adds or replaces a slash-menu command for future instances. |
523
529
  | `AutumnNote.use(plugin, options?)` | Installs a plugin globally — see [Plugin API](#plugin-api). |
524
530
  | `AutumnNote.hasPlugin(name)` | Returns `true` if a plugin with that name is registered globally. |
@@ -560,20 +566,28 @@ See the [full Plugin API docs →](https://autumn.konexforge.com/docs.html#plugi
560
566
 
561
567
  ### Events
562
568
 
569
+ Every event reaches both `editor.on(name, fn)` listeners and the matching `on<Name>` option callback (e.g. `paste` → `onPaste`).
570
+
563
571
  | Name | Payload | Description |
564
572
  |---|---|---|
565
573
  | `change` | `html: string` | Fired after every content mutation. Debounced internally. |
566
574
  | `focus` | `context` | Editor gained focus. |
567
575
  | `blur` | `context` | Editor lost focus. |
568
576
  | `init` | `context` | Fired once after the editor has fully initialised. |
569
- | `imageUpload` | `files: FileList` | Fired when images are dropped or pasted (when `onImageUpload` is provided). |
570
577
  | `imageError` | `{ file, message, error?, retry? }` | Fired when an image is rejected (e.g. over `maxImageSize`) or an upload fails. `retry()` is present on upload failures and re-sends that one file. |
571
- | `paste` | `{ text, html }` | Fired after every paste event. |
578
+ | `paste` | `{ text, html }` | Fired on every paste, before the editor inserts anything. Return `false` to cancel the paste, or an HTML string to insert instead (it is sanitised). |
572
579
  | `pasteError` | `{ message, size?, maxBytes? }` | Fired when paste/drop exceeds `maxPasteSize` or a dropped Markdown file cannot be read. |
573
580
  | `selectionChange` | `context` | Fired when the cursor or selection changes. |
574
581
  | `destroy` | `context` | Fired just before the editor is destroyed. |
575
582
  | `charLimitReached` | `context` | Fired when `maxChars` is hit. |
576
583
  | `wordLimitReached` | `context` | Fired when `maxWords` is hit. |
584
+ | `autoSave` | `{ key, html, savedAt }` | Fired after an auto-save write. |
585
+ | `autoSaveError` | `{ key, error }` | Fired when an auto-save write fails. |
586
+ | `autoSaveRestore` | `html, context` | Fired after the user restores a draft from the restore banner. |
587
+ | `optionsChange` | `overrides` | Fired after `updateOptions()`. |
588
+ | `beforeCommand` | `{ name, value?, source, event? }` | Fired before a command runs from the toolbar, a shortcut, the context menu or the bubble toolbar (`source`). Return `false` to cancel it — e.g. to gate features by plan or log usage. |
589
+
590
+ Image uploads are not an event: they go to the `onImageUpload` handler, whose return value the editor uses.
577
591
 
578
592
  ---
579
593
 
@@ -587,6 +601,7 @@ See the [full Plugin API docs →](https://autumn.konexforge.com/docs.html#plugi
587
601
  | `maxHeight` | `number` | `0` | Maximum resizable height in pixels. `0` = unlimited. |
588
602
  | `focus` | `boolean` | `false` | Automatically focus the editor on creation. |
589
603
  | `resizable` | `boolean` | `true` | Show the resize handle at the bottom of the editor. |
604
+ | `statusbar` | `boolean` | `true` | Show the statusbar (word/char count, resize handle). Set `false` to hide it, e.g. for a read-only viewer. `getWordCount()`/`getCharCount()` keep working. |
590
605
  | `toolbar` | `Array[]` | all buttons | Toolbar layout. See [Toolbar Customisation](#toolbar-customisation). |
591
606
  | `toolbarOverflow` | `string` | `'wrap'` | Toolbar overflow strategy: `'wrap'` or `'scroll'`. |
592
607
  | `useBootstrap` | `boolean` | `false` | Apply Bootstrap CSS classes to toolbar buttons. |
@@ -606,9 +621,18 @@ See the [full Plugin API docs →](https://autumn.konexforge.com/docs.html#plugi
606
621
  | `defaultFontFamily` | `string` | `'Arial'` | Font shown by default in the font-family dropdown. |
607
622
  | `defaultFontSize` | `string` | `'14px'` | Default font size applied to new content. |
608
623
  | `fontFamilies` | `string[]` | 10 fonts | Font families available in the font-family dropdown. |
624
+ | `fontSizes` | `string[]` | `8px`–`72px` | Sizes in the font-size dropdown. |
625
+ | `lineHeights` | `string[]` | `'1.0'`–`'3.0'` | Values in the line-height dropdown. |
626
+ | `paragraphStyles` | `{ value, label }[]` | Normal, H1–H6, Quote, Code | Block formats in the paragraph-style dropdown (`value` is a block tag). |
627
+ | `icons` | `object` | `null` | Per-editor icon overrides keyed by button or icon name — SVG markup, or a CSS class list. See [Icons](#icons). |
628
+ | `buttons` | `object \| array` | `null` | Per-editor button definitions, usable by name in `toolbar`. |
629
+ | `keyMap` | `object` | `null` | Keyboard shortcut overrides merged over the defaults. See [Keyboard Shortcuts](#keyboard-shortcuts). |
609
630
  | `stickyToolbar` | `boolean` | `false` | Pin the toolbar to the viewport top when the page is scrolled. |
610
631
  | `stickyToolbarOffset` | `number` | `0` | Top offset in pixels for the sticky toolbar (e.g. height of a fixed nav bar). |
611
- | `theme` | `string` | `'light'` | Colour theme: `'light'`, `'dark'`, or `'auto'` (follows system preference). |
632
+ | `theme` | `string` | `'light'` | Colour theme: `'light'`, `'dark'`, or `'auto'` (follows system preference). Changeable at runtime. |
633
+ | `themeVars` | `object` | `null` | Design-token overrides for this editor, e.g. `{ primary: '#f97316', radius: '10px' }`. See [Theming](#theming). |
634
+ | `zIndexOffset` | `number` | `0` | Added to the z-index of every floating layer (tooltips, popovers, dialogs, fullscreen), e.g. to sit above a host modal. |
635
+ | `popupContainer` | `string \| Element \| ShadowRoot` | `null` | Where dialogs, tooltips and menus mount (default `document.body`). Use inside a focus-trapping modal or a shadow root. The element must not set `transform`, `filter` or `contain`. |
612
636
  | `readOnly` | `boolean` | `false` | Start the editor in non-editable (read-only) mode with toolbar hidden. |
613
637
  | `spellcheck` | `boolean` | `true` | Enable browser spellcheck in the editable area. |
614
638
  | `direction` | `string` | `'ltr'` | Text direction: `'ltr'` or `'rtl'`. |
@@ -623,7 +647,12 @@ See the [full Plugin API docs →](https://autumn.konexforge.com/docs.html#plugi
623
647
  | `cspNonce` | `string` | `''` | CSP nonce applied to dynamically injected Prism and Font Awesome assets. |
624
648
  | `externalAssetCrossOrigin` | `string` | `'anonymous'` | `crossorigin` value for optional external assets; set `''` to omit it. |
625
649
  | `externalAssetReferrerPolicy` | `string` | `'no-referrer'` | Referrer policy for optional external assets; set `''` to omit it. |
626
- | `colorSwatches` | `string[]` | `[]` | Custom brand colour swatches prepended to the colour picker palette. |
650
+ | `colorSwatches` | `string[]` | `[]` | Custom brand colour swatches prepended to the colour palette (toolbar, bubble toolbar, context menu). |
651
+ | `colorPalette` | `string[]` | `null` | Replaces the built-in 24-colour palette in the toolbar, bubble toolbar and context menu. |
652
+ | `linkDefaults` | `object` | `{ openInNewTab: false, rel: 'noopener noreferrer', defaultProtocol: 'https://' }` | Link dialog defaults. `openInNewTab` pre-ticks the checkbox; `rel` is used for new-tab links (`noopener` is always added); `defaultProtocol` prefixes bare domains (`''` disables). Relative links (`/path`, `#id`, `?q`) are never prefixed. |
653
+ | `videoProviders` | `array` | `null` | Extra video sources for the video dialog: `[{ name, match: RegExp, embed: (match, url) => embedUrl }]`, tried before YouTube/Vimeo. The embed host must be built in or in `iframeHosts`. |
654
+ | `iframeHosts` | `string[]` | `null` | Extra hostnames whose iframes survive sanitisation (exact hostnames, HTTPS only), e.g. `['player.twitch.tv']`. Every listed host can render arbitrary content in the document — list only hosts you trust. |
655
+ | `onBeforeCommand` | `function` | `null` | See the `beforeCommand` event. |
627
656
  | `focusColor` | `string` | `null` | Custom focus ring colour (any valid CSS colour). Overrides the default blue. |
628
657
  | `lang` | `string \| object` | `'en'` | UI display language. `'en'` is built in; other codes must be registered first — see [Languages](#languages). Pass a partial locale object for custom overrides. |
629
658
  | `markdownShortcuts` | `boolean` | `true` | Convert Markdown-style syntax typed in the editor to HTML in real time (block and inline rules). |
@@ -636,18 +665,21 @@ See the [full Plugin API docs →](https://autumn.konexforge.com/docs.html#plugi
636
665
  | `minImageSize` | `number` | `20` | Minimum image dimension in px during resize (width and height). |
637
666
  | `mention` | `object` | `null` | @mention configuration object. Set `mention.onSearch` to activate. See [Mentions](#mentions). |
638
667
  | `contextMenu` | `object` | `null` | Right-click menu override. `contextMenu.items` replaces the built-in list; see `ContextMenuItem` in the type definitions. |
668
+ | `slashMenu` | `boolean` | `true` | Show the `/` command palette when `/` is typed at the start of an empty line. |
639
669
  | `slashCommands` | `object[]` | `[]` | Extra entries appended to the `/` command palette. |
640
670
  | `historyMaxBytes` | `number` | `10485760` | Combined character budget for the undo/redo snapshot stack (default 10 MB). The oldest snapshots are dropped past it. |
641
671
  | `blockIds` | `boolean` | `false` | Add stable `data-an-block-id` attributes to top-level blocks, so an external adapter can address them. |
642
672
  | `documentAdapters` | `object` | `{}` | Import/export adapters keyed by format name. |
643
673
  | `collaborationAdapter` | `object` | `null` | Bridge notified of local HTML changes. |
674
+ | `autoSaveAdapter` | `object` | `null` | Async persistence for auto-save (`save`, `load`, `remove`) instead of `localStorage`. May be a class instance. |
675
+ | `imageProcessor` | `function` | `null` | `(file, { context }) => dataUrl` — transforms pasted/dropped images before insertion (e.g. compress in a Web Worker). |
644
676
  | `onChange` | `Function` | `null` | `(html: string) => void` — called on every content change. |
645
677
  | `onFocus` | `Function` | `null` | `(context) => void` — called when the editor gains focus. |
646
678
  | `onBlur` | `Function` | `null` | `(context) => void` — called when the editor loses focus. |
647
679
  | `onInit` | `Function` | `null` | `(context) => void` — called once after the editor is initialised. |
648
680
  | `onImageUpload` | `Function` | `null` | `(files, { context, setProgress }) => void \| string \| string[] \| Promise<…>` — upload handler; overrides the base64 embed. Return the uploaded URL(s) to have the editor insert them, or nothing to insert them yourself. |
649
681
  | `onImageError` | `Function` | `null` | `({ file, message }) => void` — called when an image is rejected. |
650
- | `onPaste` | `Function` | `null` | `({ text, html }) => void` — called after every paste event. |
682
+ | `onPaste` | `Function` | `null` | `({ text, html }) => void \| false \| string` — called on every paste, before insertion. Return `false` to cancel it or an HTML string to insert instead. See the `paste` event. |
651
683
  | `onPasteError` | `Function` | `null` | `({ message, size?, maxBytes? }) => void` — called when pasted or dropped content cannot be processed. |
652
684
  | `onSelectionChange` | `Function` | `null` | `(context) => void` — called when cursor or selection changes. |
653
685
  | `onDestroy` | `Function` | `null` | `(context) => void` — called just before the editor is destroyed. |
@@ -656,9 +688,108 @@ See the [full Plugin API docs →](https://autumn.konexforge.com/docs.html#plugi
656
688
 
657
689
  ---
658
690
 
691
+ ## Theming
692
+
693
+ Every colour, radius and font the editor uses is a CSS custom property with a built-in fallback, so you can theme it without a Sass build.
694
+
695
+ | Token | Light default | Used for |
696
+ |---|---|---|
697
+ | `--an-primary` | `#3b82f6` | Accent: active buttons, links, focus ring, selections |
698
+ | `--an-primary-hover` | `#2563eb` | Accent hover |
699
+ | `--an-border` | `#d1d5db` | Borders and separators |
700
+ | `--an-bg` | `#ffffff` | Editor and dialog background |
701
+ | `--an-bg-toolbar` | `#f9fafb` | Toolbar background |
702
+ | `--an-bg-btn-hover` | `#f3f4f6` | Button hover |
703
+ | `--an-bg-btn-active` | `#dbeafe` | Active (pressed) button |
704
+ | `--an-text` | `#111827` | Text |
705
+ | `--an-muted` | `#6b7280` | Secondary text, placeholders |
706
+ | `--an-statusbar-bg` | `#f9fafb` | Statusbar background |
707
+ | `--an-radius` / `--an-radius-sm` | `6px` / `4px` | Corner radii |
708
+ | `--an-font-family` / `--an-font-size` / `--an-line-height` | system UI / `14px` / `1.6` | Editor UI typography |
709
+ | `--an-focus-color` | `--an-primary` | Focus ring (also the `focusColor` option) |
710
+ | `--an-z-offset` | `0` | Added to floating z-indexes (also the `zIndexOffset` option) |
711
+
712
+ Three ways to set them, from broadest to narrowest:
713
+
714
+ ```css
715
+ /* 1. Every editor on the page */
716
+ :root { --an-primary: #f97316; --an-radius: 10px; }
717
+
718
+ /* 2. Dark mode only (theme: 'dark', or 'auto' when the OS is dark) */
719
+ .an-theme-dark { --an-bg: #0b1020; }
720
+ @media (prefers-color-scheme: dark) { .an-theme-auto { --an-bg: #0b1020; } }
721
+ ```
722
+
723
+ ```js
724
+ // 3. One editor, including its dialogs and tooltips; changeable at runtime
725
+ const editor = AutumnNote.create('#editor', {
726
+ themeVars: { primary: '#f97316', 'bg-toolbar': '#fff7ed' },
727
+ });
728
+ editor.updateOptions({ themeVars: null }); // drop all overrides
729
+ editor.updateOptions({ themeVars: { primary: '#10b981' } });
730
+ ```
731
+
732
+ Each editor mounts its dialogs, tooltips and menus in its own portal element (`.an-portal`), which carries the editor's theme class and `themeVars`. Use `popupContainer` to put that portal somewhere other than `document.body`.
733
+
659
734
  ## Toolbar Customisation
660
735
 
661
- The `toolbar` option accepts an array of **groups**. Each group is an array of button definition objects exported from the package:
736
+ The `toolbar` option accepts an array of **groups**. Each group lists buttons by **name** plain strings, so the config can come from JSON, framework props or the UMD build:
737
+
738
+ ```js
739
+ AutumnNote.create('#editor', {
740
+ toolbar: [
741
+ ['paragraphStyle', 'fontSize'],
742
+ ['bold', 'italic', 'underline', 'strikethrough'],
743
+ ['ul', 'ol', 'checklist'],
744
+ ['link', 'image', 'table'],
745
+ ['undo', 'redo'],
746
+ ],
747
+ });
748
+ ```
749
+
750
+ Built-in names: `paragraphStyle`, `fontFamily`, `fontSize`, `lineHeight`, `undo`, `redo`, `bold`, `italic`, `underline`, `strikethrough`, `inlineCode`, `superscript`, `subscript`, `foreColor`, `backColor`, `alignLeft`, `alignCenter`, `alignRight`, `alignJustify`, `ul`, `ol`, `checklist`, `indent`, `outdent`, `hr`, `link`, `image`, `video`, `table`, `emoji`, `icon`, `removeFormat`, `direction`, `codeview`, `fullscreen`, `find`, `findReplace`, `print`, `shortcuts`.
751
+
752
+ ### Buttons for one editor
753
+
754
+ `buttons` defines buttons for a single editor, without touching the global registry — two editors can each have their own `save`:
755
+
756
+ ```js
757
+ AutumnNote.create('#editor', {
758
+ toolbar: [['bold', 'italic'], ['save']],
759
+ buttons: {
760
+ save: { icon: '<svg viewBox="0 0 24 24">…</svg>', tooltip: 'Save', action: (ctx) => save(ctx.getHTML()) },
761
+ },
762
+ });
763
+ ```
764
+
765
+ ### Dropdown lists and colours
766
+
767
+ ```js
768
+ AutumnNote.create('#editor', {
769
+ fontSizes: ['12px', '14px', '16px', '20px'],
770
+ lineHeights: ['1.2', '1.5', '2'],
771
+ paragraphStyles: [{ value: 'p', label: 'Body' }, { value: 'h2', label: 'Title' }],
772
+ colorPalette: ['#0f172a', '#f97316', '#10b981'], // replaces the 24 defaults
773
+ colorSwatches: ['#7c3aed'], // prepended to the palette
774
+ });
775
+ ```
776
+
777
+ ### Icons
778
+
779
+ Buttons use built-in SVG icons (or Font Awesome when the page loads it). Override any of them per editor with `icons`, or for every editor with `AutumnNote.registerIcon()`. A value starting with `<` is markup; anything else is a CSS class list:
780
+
781
+ ```js
782
+ AutumnNote.registerIcon('bold', 'bi bi-type-bold'); // Bootstrap Icons, all editors
783
+ AutumnNote.create('#editor', {
784
+ icons: { italic: '<svg viewBox="0 0 24 24">…</svg>', table: 'ti ti-table' },
785
+ });
786
+ ```
787
+
788
+ Keys are button names (`bold`) or icon ids (`list-ul`). The per-editor option wins over `registerIcon()`.
789
+
790
+ ### Button objects
791
+
792
+ Button definition objects are also exported, for building your own or wrapping a built-in one:
662
793
 
663
794
  ```js
664
795
  import AutumnNote, {
@@ -763,12 +894,40 @@ Object.assign(AutumnNote.defaults, {
763
894
  | `Ctrl + B` | Bold |
764
895
  | `Ctrl + I` | Italic |
765
896
  | `Ctrl + U` | Underline |
897
+ | `Ctrl + K` | Insert / edit link |
898
+ | ``Ctrl + ` `` | Inline code |
899
+ | `Ctrl + Shift + V` | Paste as plain text |
766
900
  | `Ctrl + F` | Open Find dialog |
767
901
  | `Ctrl + H` | Open Find & Replace dialog |
902
+ | `Ctrl + Shift + /` | Open Keyboard Shortcuts dialog |
768
903
  | `Shift + Enter` | Insert line break |
769
904
  | `Tab` | Insert spaces / indent list item |
770
905
  | `Shift + Tab` | Outdent list item |
771
- | `Shift + ?` | Open Keyboard Shortcuts dialog |
906
+
907
+ `Ctrl` is `Cmd` on macOS.
908
+
909
+ ### Changing shortcuts
910
+
911
+ `keyMap` is merged over the defaults. Combos are written `Mod+B` (`Mod` = Ctrl or Cmd), `Ctrl+Alt+1`, `Mod+Shift+Z`; Shift and Alt must match exactly, so AltGr characters never trigger a Ctrl shortcut. A value can be:
912
+
913
+ - `false` — disable the default (e.g. give `Ctrl+F` back to the browser)
914
+ - a command name — `undo`, `redo`, `bold`, `italic`, `underline`, `inlineCode`, `link`, `find`, `findReplace`, `shortcuts`, `pastePlainText`, **or any toolbar button name**
915
+ - a function `(context, event) => {}` — return `false` to let the key through
916
+ - `{ run, description }` — a function plus the label the shortcuts dialog shows
917
+
918
+ ```js
919
+ AutumnNote.create('#editor', {
920
+ keyMap: {
921
+ 'Mod+F': false, // browser find
922
+ 'Mod+H': false,
923
+ 'Mod+Shift+F': 'find', // editor find moves here
924
+ 'Mod+Shift+X': 'strikethrough', // any toolbar button
925
+ 'Mod+Alt+1': { run: (ctx) => ctx.invoke('editor.formatBlock', 'h1'), description: 'Heading 1' },
926
+ },
927
+ });
928
+ ```
929
+
930
+ The shortcuts dialog (`Ctrl + Shift + /`) reflects the keyMap: disabled shortcuts disappear and custom ones are listed under *Custom*. Shortcuts need Ctrl, Cmd or Alt; plain keys belong to typing.
772
931
 
773
932
  > The number of spaces inserted by `Tab` is controlled by the `tabSize` option.
774
933
 
@@ -896,7 +1055,7 @@ autumn-note-ce/
896
1055
 
897
1056
  ### Development commands
898
1057
 
899
- Development and package usage require Node 20.19+ and pnpm 11.1.3.
1058
+ Development and package usage require Node 22 LTS (22.22.2+) or Node 24.15+ (newer releases included) and pnpm 11.1.3.
900
1059
 
901
1060
  ```bash
902
1061
  pnpm install # install all workspace packages