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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autumnnote",
3
- "version": "2.7.1",
3
+ "version": "3.0.0",
4
4
  "description": "WYSIWYG rich-text editor built with vanilla JavaScript — zero dependencies, no jQuery. Dark mode, @mention, markdown shortcuts, bubble toolbar. React and Vue 3 wrappers included.",
5
5
  "type": "module",
6
6
  "main": "dist/autumnnote.cjs",
@@ -130,7 +130,7 @@
130
130
  },
131
131
  "packageManager": "pnpm@11.1.3",
132
132
  "engines": {
133
- "node": ">=20.19.0"
133
+ "node": "^22.22.2 || >=24.15.0"
134
134
  },
135
135
  "browserslist": [
136
136
  "last 2 versions",
@@ -143,12 +143,38 @@ export function mergeDeep(target, source) {
143
143
  }
144
144
 
145
145
  /**
146
- * Checks if value is a plain object.
146
+ * Structural equality for option values: plain objects and arrays compare by
147
+ * content, everything else (functions, DOM nodes, class instances) by identity.
148
+ * @param {*} a
149
+ * @param {*} b
150
+ * @returns {boolean}
151
+ */
152
+ export function deepEqual(a, b) {
153
+ if (Object.is(a, b)) return true;
154
+ if (Array.isArray(a) && Array.isArray(b)) {
155
+ return a.length === b.length && a.every((item, i) => deepEqual(item, b[i]));
156
+ }
157
+ if (isPlainObject(a) && isPlainObject(b)) {
158
+ const keys = Object.keys(a);
159
+ return keys.length === Object.keys(b).length
160
+ && keys.every((key) => Object.hasOwn(b, key) && deepEqual(a[key], b[key]));
161
+ }
162
+ return false;
163
+ }
164
+
165
+ /**
166
+ * Checks if value is a plain object (an object literal or Object.create(null)).
167
+ *
168
+ * DOM nodes and class instances are not: mergeDeep() used to recurse into
169
+ * them, which turned a `popupContainer` element into a detached copy and
170
+ * stripped the prototype methods off class-based adapters.
147
171
  * @param {*} val
148
172
  * @returns {boolean}
149
173
  */
150
174
  export function isPlainObject(val) {
151
- return val !== null && typeof val === 'object' && !Array.isArray(val);
175
+ if (val === null || typeof val !== 'object') return false;
176
+ const proto = Object.getPrototypeOf(val);
177
+ return proto === Object.prototype || proto === null;
152
178
  }
153
179
 
154
180
  /**
package/src/js/i18n/de.js CHANGED
@@ -159,6 +159,8 @@ export const de = {
159
159
  replaceBtn: 'Ersetzen',
160
160
  noResults: 'Keine Ergebnisse',
161
161
  useRegex: 'Regulären Ausdruck verwenden',
162
+ prevMatch: 'Zurück (Umschalt+Eingabe)',
163
+ nextMatch: 'Weiter (Eingabe)',
162
164
  replaceAllBtn: 'Alle ersetzen',
163
165
  close: '×',
164
166
  },
@@ -167,6 +169,7 @@ export const de = {
167
169
  title: 'Tastenkürzel',
168
170
  ariaLabel: 'Tastenkürzel',
169
171
  close: 'Schließen',
172
+ customCategory: 'Benutzerdefiniert',
170
173
  shortcuts: [
171
174
  {
172
175
  category: 'Textformatierung',
@@ -244,6 +247,13 @@ export const de = {
244
247
  charsLimit: (n, max) => `Zeichen: ${n}/${max}`,
245
248
  },
246
249
 
250
+ a11y: {
251
+ editor: 'Rich-Text-Editor',
252
+ toolbar: 'Editor-Werkzeugleiste',
253
+ statistics: 'Editor-Statistik',
254
+ tableSize: 'Tabellengröße wählen',
255
+ },
256
+
247
257
  tooltips: {
248
258
  link: {
249
259
  ariaLabel: 'Link-Aktionen',
package/src/js/i18n/en.js CHANGED
@@ -162,6 +162,8 @@ export const en = {
162
162
  replaceAllBtn: 'Replace All',
163
163
  noResults: 'No results',
164
164
  useRegex: 'Use Regular Expression',
165
+ prevMatch: 'Previous (Shift+Enter)',
166
+ nextMatch: 'Next (Enter)',
165
167
  close: '\u00d7',
166
168
  },
167
169
 
@@ -176,6 +178,7 @@ export const en = {
176
178
  title: 'Keyboard Shortcuts',
177
179
  ariaLabel: 'Keyboard Shortcuts',
178
180
  close: 'Close',
181
+ customCategory: 'Custom',
179
182
  shortcuts: [
180
183
  {
181
184
  category: 'Text Formatting',
@@ -257,6 +260,13 @@ export const en = {
257
260
  charsLimit: (n, max) => `Chars: ${n}/${max}`,
258
261
  },
259
262
 
263
+ a11y: {
264
+ editor: 'Rich text editor',
265
+ toolbar: 'Editor toolbar',
266
+ statistics: 'Editor statistics',
267
+ tableSize: 'Select table size',
268
+ },
269
+
260
270
  tooltips: {
261
271
  link: {
262
272
  ariaLabel: 'Link actions',
package/src/js/i18n/es.js CHANGED
@@ -159,6 +159,8 @@ export const es = {
159
159
  replaceBtn: 'Reemplazar',
160
160
  noResults: 'Sin resultados',
161
161
  useRegex: 'Usar expresión regular',
162
+ prevMatch: 'Anterior (Mayús+Intro)',
163
+ nextMatch: 'Siguiente (Intro)',
162
164
  replaceAllBtn: 'Reemplazar todo',
163
165
  close: '×',
164
166
  },
@@ -167,6 +169,7 @@ export const es = {
167
169
  title: 'Atajos de teclado',
168
170
  ariaLabel: 'Atajos de teclado',
169
171
  close: 'Cerrar',
172
+ customCategory: 'Personalizados',
170
173
  shortcuts: [
171
174
  {
172
175
  category: 'Formato de texto',
@@ -244,6 +247,13 @@ export const es = {
244
247
  charsLimit: (n, max) => `Caracteres: ${n}/${max}`,
245
248
  },
246
249
 
250
+ a11y: {
251
+ editor: 'Editor de texto enriquecido',
252
+ toolbar: 'Barra de herramientas del editor',
253
+ statistics: 'Estadísticas del editor',
254
+ tableSize: 'Seleccionar tamaño de tabla',
255
+ },
256
+
247
257
  tooltips: {
248
258
  link: {
249
259
  ariaLabel: 'Acciones de enlace',
package/src/js/i18n/fr.js CHANGED
@@ -160,6 +160,8 @@ export const fr = {
160
160
  replaceBtn: 'Remplacer',
161
161
  noResults: 'Aucun résultat',
162
162
  useRegex: 'Utiliser une expression régulière',
163
+ prevMatch: 'Précédent (Maj+Entrée)',
164
+ nextMatch: 'Suivant (Entrée)',
163
165
  replaceAllBtn: 'Tout remplacer',
164
166
  close: '\u00d7',
165
167
  },
@@ -168,6 +170,7 @@ export const fr = {
168
170
  title: 'Raccourcis clavier',
169
171
  ariaLabel: 'Raccourcis clavier',
170
172
  close: 'Fermer',
173
+ customCategory: 'Personnalisés',
171
174
  shortcuts: [
172
175
  {
173
176
  category: 'Mise en forme du texte',
@@ -245,6 +248,13 @@ export const fr = {
245
248
  charsLimit: (n, max) => `Caractères\u00a0: ${n}/${max}`,
246
249
  },
247
250
 
251
+ a11y: {
252
+ editor: 'Éditeur de texte enrichi',
253
+ toolbar: 'Barre d\'outils de l\'éditeur',
254
+ statistics: 'Statistiques de l\'éditeur',
255
+ tableSize: 'Choisir la taille du tableau',
256
+ },
257
+
248
258
  tooltips: {
249
259
  link: {
250
260
  ariaLabel: 'Actions du lien',
package/src/js/i18n/ja.js CHANGED
@@ -160,6 +160,8 @@ export const ja = {
160
160
  replaceBtn: '置換',
161
161
  noResults: '結果なし',
162
162
  useRegex: '正規表現を使用',
163
+ prevMatch: '前へ (Shift+Enter)',
164
+ nextMatch: '次へ (Enter)',
163
165
  replaceAllBtn: 'すべて置換',
164
166
  close: '\u00d7',
165
167
  },
@@ -168,6 +170,7 @@ export const ja = {
168
170
  title: 'キーボードショートカット',
169
171
  ariaLabel: 'キーボードショートカット',
170
172
  close: '閉じる',
173
+ customCategory: 'カスタム',
171
174
  shortcuts: [
172
175
  {
173
176
  category: 'テキスト書式',
@@ -245,6 +248,13 @@ export const ja = {
245
248
  charsLimit: (n, max) => `文字数: ${n}/${max}`,
246
249
  },
247
250
 
251
+ a11y: {
252
+ editor: 'リッチテキストエディター',
253
+ toolbar: 'エディターツールバー',
254
+ statistics: 'エディターの統計',
255
+ tableSize: '表のサイズを選択',
256
+ },
257
+
248
258
  tooltips: {
249
259
  link: {
250
260
  ariaLabel: 'リンク操作',
package/src/js/i18n/ko.js CHANGED
@@ -159,6 +159,8 @@ export const ko = {
159
159
  replaceBtn: '바꾸기',
160
160
  noResults: '결과 없음',
161
161
  useRegex: '정규식 사용',
162
+ prevMatch: '이전 (Shift+Enter)',
163
+ nextMatch: '다음 (Enter)',
162
164
  replaceAllBtn: '모두 바꾸기',
163
165
  close: '×',
164
166
  },
@@ -167,6 +169,7 @@ export const ko = {
167
169
  title: '키보드 단축키',
168
170
  ariaLabel: '키보드 단축키',
169
171
  close: '닫기',
172
+ customCategory: '사용자 지정',
170
173
  shortcuts: [
171
174
  {
172
175
  category: '텍스트 서식',
@@ -244,6 +247,13 @@ export const ko = {
244
247
  charsLimit: (n, max) => `글자: ${n}/${max}`,
245
248
  },
246
249
 
250
+ a11y: {
251
+ editor: '서식 있는 텍스트 편집기',
252
+ toolbar: '편집기 도구 모음',
253
+ statistics: '편집기 통계',
254
+ tableSize: '표 크기 선택',
255
+ },
256
+
247
257
  tooltips: {
248
258
  link: {
249
259
  ariaLabel: '링크 작업',
package/src/js/i18n/vi.js CHANGED
@@ -161,6 +161,8 @@ export const vi = {
161
161
  replaceAllBtn: 'Thay thế tất cả',
162
162
  noResults: 'Không có kết quả',
163
163
  useRegex: 'Dùng biểu thức chính quy',
164
+ prevMatch: 'Trước (Shift+Enter)',
165
+ nextMatch: 'Tiếp (Enter)',
164
166
  close: '\u00d7',
165
167
  },
166
168
 
@@ -168,6 +170,7 @@ export const vi = {
168
170
  title: 'Phím tắt bàn phím',
169
171
  ariaLabel: 'Phím tắt bàn phím',
170
172
  close: 'Đóng',
173
+ customCategory: 'Tùy chỉnh',
171
174
  shortcuts: [
172
175
  {
173
176
  category: 'Định dạng văn bản',
@@ -245,6 +248,13 @@ export const vi = {
245
248
  charsLimit: (n, max) => `Ký tự: ${n}/${max}`,
246
249
  },
247
250
 
251
+ a11y: {
252
+ editor: 'Trình soạn thảo văn bản',
253
+ toolbar: 'Thanh công cụ soạn thảo',
254
+ statistics: 'Thống kê văn bản',
255
+ tableSize: 'Chọn kích thước bảng',
256
+ },
257
+
248
258
  tooltips: {
249
259
  link: {
250
260
  ariaLabel: 'Hành động liên kết',
package/src/js/i18n/zh.js CHANGED
@@ -160,6 +160,8 @@ export const zh = {
160
160
  replaceBtn: '替换',
161
161
  noResults: '没有结果',
162
162
  useRegex: '使用正则表达式',
163
+ prevMatch: '上一个 (Shift+Enter)',
164
+ nextMatch: '下一个 (Enter)',
163
165
  replaceAllBtn: '全部替换',
164
166
  close: '\u00d7',
165
167
  },
@@ -168,6 +170,7 @@ export const zh = {
168
170
  title: '键盘快捷键',
169
171
  ariaLabel: '键盘快捷键',
170
172
  close: '关闭',
173
+ customCategory: '自定义',
171
174
  shortcuts: [
172
175
  {
173
176
  category: '文字格式',
@@ -245,6 +248,13 @@ export const zh = {
245
248
  charsLimit: (n, max) => `字符数: ${n}/${max}`,
246
249
  },
247
250
 
251
+ a11y: {
252
+ editor: '富文本编辑器',
253
+ toolbar: '编辑器工具栏',
254
+ statistics: '编辑器统计',
255
+ tableSize: '选择表格大小',
256
+ },
257
+
248
258
  tooltips: {
249
259
  link: {
250
260
  ariaLabel: '链接操作',
package/types/index.d.ts CHANGED
@@ -79,6 +79,38 @@ export interface CollaborationAdapter {
79
79
  onLocalChange?(html: string, context: Context): void | Promise<void>;
80
80
  }
81
81
 
82
+ /** Built-in design tokens accepted by `themeVars` (any `--an-*` key also passes through). */
83
+ export interface AsnThemeVars {
84
+ primary?: string;
85
+ 'primary-hover'?: string;
86
+ border?: string;
87
+ bg?: string;
88
+ 'bg-toolbar'?: string;
89
+ 'bg-btn-hover'?: string;
90
+ 'bg-btn-active'?: string;
91
+ text?: string;
92
+ muted?: string;
93
+ 'statusbar-bg'?: string;
94
+ radius?: string;
95
+ 'radius-sm'?: string;
96
+ 'font-family'?: string;
97
+ 'font-size'?: string;
98
+ 'line-height'?: string;
99
+ [customProperty: `--an-${string}`]: string | number | undefined;
100
+ }
101
+
102
+ /**
103
+ * A keyMap value. Built-in commands: undo, redo, bold, italic, underline,
104
+ * inlineCode, link, find, findReplace, shortcuts, pastePlainText — or any
105
+ * toolbar button name. A handler returning `false` lets the key through.
106
+ */
107
+ export type AsnKeyBinding =
108
+ | false
109
+ | null
110
+ | string
111
+ | ((context: Context, event: KeyboardEvent) => void | false)
112
+ | { run: (context: Context, event: KeyboardEvent) => void | false; description?: string };
113
+
82
114
  export interface AsnOptions {
83
115
  /** Placeholder text when the editor is empty. */
84
116
  placeholder?: string;
@@ -92,6 +124,12 @@ export interface AsnOptions {
92
124
  focus?: boolean;
93
125
  /** Show the resize handle in the statusbar. */
94
126
  resizable?: boolean;
127
+ /**
128
+ * Show the statusbar (word/char count and resize handle). Default `true`.
129
+ * When `false` the bar is hidden, but `getWordCount()`/`getCharCount()`
130
+ * keep working. Can be toggled at runtime with `updateOptions()`.
131
+ */
132
+ statusbar?: boolean;
95
133
  /** Toolbar button group configuration. */
96
134
  toolbar?: Array<Array<ToolbarItemDef | string>>;
97
135
  /** Use Bootstrap button classes on toolbar buttons. */
@@ -132,6 +170,16 @@ export interface AsnOptions {
132
170
  onInit?: (context: Context) => void;
133
171
  /** Callback fired just before the editor instance is destroyed. */
134
172
  onDestroy?: (context: Context) => void;
173
+ /**
174
+ * Fired before a command runs from the toolbar, a keyboard shortcut, the
175
+ * context menu or the bubble toolbar. Return `false` to cancel it.
176
+ */
177
+ onBeforeCommand?: (data: {
178
+ name: string;
179
+ value?: unknown;
180
+ source: 'toolbar' | 'shortcut' | 'contextMenu' | 'bubbleToolbar';
181
+ event?: KeyboardEvent;
182
+ }) => void | false;
135
183
  /** Callback fired whenever the selection changes inside the editor. */
136
184
  onSelectionChange?: (context: Context) => void;
137
185
  /** Callback fired when the character limit is reached. */
@@ -174,8 +222,29 @@ export interface AsnOptions {
174
222
  stickyToolbar?: boolean;
175
223
  /** Top offset in px for sticky toolbar (e.g. height of a fixed nav bar). */
176
224
  stickyToolbarOffset?: number;
177
- /** Colour theme: 'light' (default) | 'dark'. */
225
+ /**
226
+ * Colour theme: 'light' (default), 'dark', or 'auto' (follows the OS
227
+ * `prefers-color-scheme`). Can be changed at runtime with `updateOptions()`.
228
+ */
178
229
  theme?: 'light' | 'dark' | 'auto';
230
+ /**
231
+ * Design-token overrides applied to this editor and its floating UI, e.g.
232
+ * `{ primary: '#f97316', radius: '10px' }`. Keys are token names (`primary`)
233
+ * or full custom properties (`--an-primary`). See README "Theming".
234
+ */
235
+ themeVars?: AsnThemeVars | null;
236
+ /**
237
+ * Added to the z-index of every floating layer (tooltips, popovers, dialogs,
238
+ * fullscreen). Use it to lift the editor's UI above a host modal. Default 0.
239
+ */
240
+ zIndexOffset?: number;
241
+ /**
242
+ * Where the editor mounts its floating UI: a selector, an element or a
243
+ * ShadowRoot. Default `document.body`. Use it inside a modal that traps
244
+ * focus, or a shadow root. The element must not have `transform`, `filter`
245
+ * or `contain` set, because floating UI is positioned against the viewport.
246
+ */
247
+ popupContainer?: string | Element | ShadowRoot | null;
179
248
  /** Auto-load Prism.js for syntax highlighting inside code blocks. */
180
249
  codeHighlight?: boolean;
181
250
  /** CDN base URL for Prism assets. */
@@ -199,6 +268,56 @@ export interface AsnOptions {
199
268
  defaultFontSize?: string;
200
269
  /** Font families shown in the font-family toolbar dropdown. */
201
270
  fontFamilies?: string[];
271
+ /** Sizes offered by the font-size dropdown (CSS lengths). Default 8px–72px. */
272
+ fontSizes?: string[];
273
+ /** Values offered by the line-height dropdown. Default '1.0'–'3.0'. */
274
+ lineHeights?: string[];
275
+ /** Block formats offered by the paragraph-style dropdown; `value` is a block tag name. */
276
+ paragraphStyles?: Array<{ value: string; label: string }>;
277
+ /** Replaces the built-in colour swatches (toolbar, bubble toolbar, context menu). `colorSwatches` are still prepended. */
278
+ colorPalette?: string[] | null;
279
+ /**
280
+ * Per-editor icon overrides keyed by button name (`bold`) or icon id
281
+ * (`list-ul`). A value starting with `<` is markup (e.g. an SVG); anything
282
+ * else is a CSS class list rendered as `<i class="...">`.
283
+ */
284
+ icons?: Record<string, string> | null;
285
+ /**
286
+ * Per-editor button definitions, usable by name in `toolbar` without the
287
+ * global registry: `{ save: { icon: 'save', tooltip: 'Save', action } }`.
288
+ */
289
+ buttons?: Record<string, Omit<ButtonDef, 'name'> | Omit<DropdownDef, 'name'>> | ToolbarItemDef[] | null;
290
+ /**
291
+ * Keyboard shortcuts merged over the defaults. Keys are combos (`Mod+B`,
292
+ * `Ctrl+Alt+1`; `Mod` = Ctrl or Cmd). Values: `false` to disable, a
293
+ * built-in command or toolbar button name, a handler, or `{ run, description }`.
294
+ */
295
+ keyMap?: Record<string, AsnKeyBinding> | null;
296
+ /** Link dialog defaults. */
297
+ linkDefaults?: {
298
+ /** Pre-tick "open in new tab" for new links. Default false. */
299
+ openInNewTab?: boolean;
300
+ /** `rel` for new-tab links; `noopener` is always added. Default 'noopener noreferrer'. */
301
+ rel?: string;
302
+ /** Prefix for bare domains ('example.com'); '' disables. Relative links are never prefixed. Default 'https://'. */
303
+ defaultProtocol?: string;
304
+ } | null;
305
+ /**
306
+ * Extra video providers for the video dialog, tried before the built-in
307
+ * YouTube/Vimeo rules. The embed URL's host must be built in or listed in
308
+ * `iframeHosts`, otherwise the provider is ignored.
309
+ */
310
+ videoProviders?: Array<{
311
+ name: string;
312
+ match: RegExp;
313
+ embed: (match: RegExpExecArray, url: string) => string;
314
+ }> | null;
315
+ /**
316
+ * Extra hostnames whose iframes survive sanitisation (exact hostnames,
317
+ * HTTPS only), e.g. `['player.twitch.tv']`. Every listed host can render
318
+ * arbitrary content inside the document — list only hosts you trust.
319
+ */
320
+ iframeHosts?: string[] | null;
202
321
  /** Start the editor in read-only (non-editable) mode. */
203
322
  readOnly?: boolean;
204
323
  /** Enable browser spellcheck in the editable area (default: true). */
@@ -222,7 +341,11 @@ export interface AsnOptions {
222
341
  /** Insert a header row (<thead>) when creating new tables. */
223
342
  tableHeaderRow?: boolean;
224
343
  /** Callback fired after every paste event. */
225
- onPaste?: (data: { text: string; html: string | null }) => void;
344
+ /**
345
+ * Fired on every paste before anything is inserted. Return `false` to cancel
346
+ * the paste, or an HTML string to insert instead (it is sanitised).
347
+ */
348
+ onPaste?: (data: { text: string; html: string | null }) => void | false | string;
226
349
  /** Callback fired when pasted or dropped content cannot be processed. */
227
350
  onPasteError?: (error: PasteErrorData) => void;
228
351
  /** Additional color swatches shown at the top of the color picker. */
@@ -249,7 +372,7 @@ export interface AsnOptions {
249
372
  autoSaveRestoreTimeout?: number;
250
373
  /** Callback fired after the user chooses to restore a draft. */
251
374
  onAutoSaveRestore?: (html: string, context: Context) => void;
252
- /** Maximum paste size in MB before paste is silently dropped (0 = unlimited). Default: 5. */
375
+ /** Maximum paste size in bytes; larger pastes are dropped and `pasteError` fires (0 = unlimited). Default: 5242880 (5 MB). */
253
376
  maxPasteSize?: number;
254
377
  /** Minimum image dimension in px during resize (width and height). Default: 20. */
255
378
  minImageSize?: number;
@@ -289,6 +412,7 @@ export interface AsnOptions {
289
412
  documentAdapters?: Record<string, DocumentAdapter>;
290
413
  /** Optional external image processor, such as a Web Worker bridge. */
291
414
  imageProcessor?: (file: File, helpers: { context: Context }) => string | Promise<string>;
415
+ /** Bridge notified with local HTML changes. */
292
416
  collaborationAdapter?: CollaborationAdapter | null;
293
417
  /** Adds stable IDs to top-level blocks for external collaboration adapters. */
294
418
  blockIds?: boolean;
@@ -446,6 +570,8 @@ export interface AsnLocale {
446
570
  title: string;
447
571
  ariaLabel: string;
448
572
  close: string;
573
+ /** Heading of the section listing `keyMap` shortcuts. */
574
+ customCategory: string;
449
575
  shortcuts: Array<{ category: string; items: Array<{ keys: string; action: string }> }>;
450
576
  };
451
577
  contextMenu: Record<string, string>;
@@ -456,6 +582,13 @@ export interface AsnLocale {
456
582
  chars: (n: number) => string;
457
583
  charsLimit: (n: number, max: number) => string;
458
584
  };
585
+ /** Accessible names for landmarks that have no visible label. */
586
+ a11y: {
587
+ editor: string;
588
+ toolbar: string;
589
+ statistics: string;
590
+ tableSize: string;
591
+ };
459
592
  tooltips: {
460
593
  link: Record<string, string>;
461
594
  image: Record<string, string>;
@@ -549,13 +682,14 @@ export declare class Context {
549
682
  invoke(path: string, ...args: unknown[]): unknown;
550
683
 
551
684
  /** Subscribes to an editor event. Returns an unsubscribe function. */
552
- on(eventName: string, handler: (...args: unknown[]) => void): () => void;
685
+ on(eventName: string, handler: (...args: any[]) => unknown): () => void;
553
686
 
554
687
  /** Unsubscribes from an editor event. */
555
688
  off(eventName: string, handler: (...args: unknown[]) => void): void;
556
689
 
557
690
  /** Triggers an editor event. */
558
- triggerEvent(eventName: string, ...args: unknown[]): void;
691
+ /** Fires an event; returns the last value a handler returned (other than undefined). */
692
+ triggerEvent(eventName: string, ...args: unknown[]): unknown;
559
693
 
560
694
  /** Applies runtime-safe option changes without recreating the editor. */
561
695
  updateOptions(overrides: Partial<AsnOptions>): this;
@@ -818,6 +952,13 @@ export interface AutumnNoteStatic {
818
952
  * referenced by string name in toolbar configuration.
819
953
  */
820
954
  registerButton(btnDef: ToolbarItemDef): this;
955
+
956
+ /**
957
+ * Registers an icon for every editor, keyed by button name or icon id.
958
+ * `icon` is SVG/HTML markup, or a CSS class list such as `'bi bi-type-bold'`.
959
+ * The per-editor `icons` option takes precedence.
960
+ */
961
+ registerIcon(name: string, icon: string): this;
821
962
  registerSlashCommand(command: SlashCommand): this;
822
963
 
823
964
  /**