@domternal/core 0.6.2 → 0.7.1

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/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { Schema, Node as Node$1, DOMOutputSpec, NodeType, Mark as Mark$1, MarkType, Attrs, DOMParser, ResolvedPos, ContentMatch, NodeSpec, MarkSpec } from '@domternal/pm/model';
1
+ import { Schema, Node as Node$1, DOMOutputSpec, NodeType, Mark as Mark$1, MarkType, Attrs, ResolvedPos, DOMParser, ContentMatch, NodeSpec, MarkSpec } from '@domternal/pm/model';
2
2
  import { Transaction, EditorState, Plugin, PluginKey, Selection as Selection$1 } from '@domternal/pm/state';
3
3
  export { PluginKey } from '@domternal/pm/state';
4
4
  import { InputRule } from '@domternal/pm/inputrules';
5
- import { EditorView, NodeViewConstructor } from '@domternal/pm/view';
5
+ import { NodeViewConstructor, EditorView } from '@domternal/pm/view';
6
6
  import { Placement } from '@floating-ui/dom';
7
7
 
8
8
  /**
@@ -131,8 +131,7 @@ interface ErrorEventProps$1 {
131
131
  context: string;
132
132
  }
133
133
  /**
134
- * All editor events with their payload types
135
- * Used by EventEmitter for type-safe event handling
134
+ * All editor events with their payload types.
136
135
  */
137
136
  interface EditorEvents {
138
137
  /** Fired before editor is created - can modify options */
@@ -169,6 +168,10 @@ interface EditorEvents {
169
168
  linkEdit: {
170
169
  anchorElement?: HTMLElement;
171
170
  };
171
+ /** Fired when the Notion color picker should open, with the trigger as anchor. */
172
+ notionColorOpen: {
173
+ anchorElement?: HTMLElement | null;
174
+ };
172
175
  }
173
176
  /**
174
177
  * Event names as a type
@@ -212,11 +215,9 @@ type FocusPosition = boolean | 'start' | 'end' | 'all' | number | null;
212
215
  */
213
216
  interface EditorOptions {
214
217
  /**
215
- * ProseMirror Schema for the editor
216
- *
217
- * Step 1.3: Required (no extensions system yet)
218
- * Step 2+: Optional if extensions are provided (schema built from extensions)
218
+ * ProseMirror Schema for the editor.
219
219
  *
220
+ * Optional when `extensions` is provided (schema is built from them).
220
221
  * The schema must contain at least 'doc' and 'text' nodes.
221
222
  */
222
223
  schema?: Schema;
@@ -354,12 +355,12 @@ type Command = (props: CommandProps) => boolean;
354
355
  * return true;
355
356
  * };
356
357
  */
357
- type CommandSpec$1<Args extends unknown[] = []> = (...args: Args) => Command;
358
+ type CommandSpec<Args extends unknown[] = []> = (...args: Args) => Command;
358
359
  /**
359
360
  * Internal command storage used by CommandManager and ExtensionManager.
360
361
  * Holds commands as a generic record for dynamic runtime collection.
361
362
  */
362
- type CommandMap = Record<string, CommandSpec$1<unknown[]>>;
363
+ type CommandMap = Record<string, CommandSpec<unknown[]>>;
363
364
  /**
364
365
  * Typed command interface for the public API.
365
366
  *
@@ -384,7 +385,7 @@ interface RawCommands {
384
385
  * These are accessed via editor.commands.commandName()
385
386
  */
386
387
  type SingleCommands = {
387
- [K in keyof RawCommands]: RawCommands[K] extends CommandSpec$1<infer Args> ? (...args: Args) => boolean : never;
388
+ [K in keyof RawCommands]: RawCommands[K] extends CommandSpec<infer Args> ? (...args: Args) => boolean : never;
388
389
  };
389
390
  /**
390
391
  * Information about a command chain failure
@@ -402,7 +403,7 @@ interface ChainFailure {
402
403
  * These are accessed via editor.chain().commandName().run()
403
404
  */
404
405
  type ChainedCommands = {
405
- [K in keyof RawCommands]: RawCommands[K] extends CommandSpec$1<infer Args> ? (...args: Args) => ChainedCommands : never;
406
+ [K in keyof RawCommands]: RawCommands[K] extends CommandSpec<infer Args> ? (...args: Args) => ChainedCommands : never;
406
407
  } & {
407
408
  /** Execute the command chain */
408
409
  run: () => boolean;
@@ -414,7 +415,7 @@ type ChainedCommands = {
414
415
  * These are accessed via editor.can().commandName()
415
416
  */
416
417
  type CanCommands = {
417
- [K in keyof RawCommands]: RawCommands[K] extends CommandSpec$1<infer Args> ? (...args: Args) => boolean : never;
418
+ [K in keyof RawCommands]: RawCommands[K] extends CommandSpec<infer Args> ? (...args: Args) => boolean : never;
418
419
  } & {
419
420
  /** Start a chain check */
420
421
  chain: () => CanChainedCommands;
@@ -423,7 +424,7 @@ type CanCommands = {
423
424
  * Chained commands for dry-run checking
424
425
  */
425
426
  type CanChainedCommands = {
426
- [K in keyof RawCommands]: RawCommands[K] extends CommandSpec$1<infer Args> ? (...args: Args) => CanChainedCommands : never;
427
+ [K in keyof RawCommands]: RawCommands[K] extends CommandSpec<infer Args> ? (...args: Args) => CanChainedCommands : never;
427
428
  } & {
428
429
  /** Check if the entire chain can be executed */
429
430
  run: () => boolean;
@@ -480,7 +481,7 @@ interface ToolbarButton {
480
481
  * How to check if this button is active.
481
482
  * - string: extension name passed to `editor.isActive(name)`
482
483
  * - object: `{ name, attributes }` passed to `editor.isActive(name, attributes)`
483
- * - array: OR-check active if ANY entry matches (useful for attributes on multiple node types)
484
+ * - array: OR-check - active if ANY entry matches (useful for attributes on multiple node types)
484
485
  * - undefined: button has no active state (e.g. undo/redo)
485
486
  */
486
487
  isActive?: string | {
@@ -602,1485 +603,1600 @@ interface ToolbarLayoutDropdown {
602
603
  /**
603
604
  * A single entry in a toolbar layout array.
604
605
  *
605
- * - `string` item name (e.g. `'bold'`) or separator (`'|'`)
606
- * - `ToolbarLayoutDropdown` custom dropdown grouping
606
+ * - `string` - item name (e.g. `'bold'`) or separator (`'|'`)
607
+ * - `ToolbarLayoutDropdown` - custom dropdown grouping
607
608
  */
608
609
  type ToolbarLayoutEntry = string | ToolbarLayoutDropdown;
609
610
 
610
611
  /**
611
- * Extension configuration types
612
+ * Callback type for event handlers
613
+ * - Events with payload: (data: T) => void
614
+ * - Events without payload (undefined): () => void
615
+ */
616
+ type EventCallback<T> = T extends undefined ? () => void : (data: T) => void;
617
+ /**
618
+ * Generic, type-safe event emitter
612
619
  *
613
- * These types define the configuration object passed to Extension.create(),
614
- * Node.create(), and Mark.create() factory methods.
620
+ * @example
621
+ * ```typescript
622
+ * interface MyEvents {
623
+ * update: { value: number };
624
+ * destroy: undefined;
625
+ * }
626
+ *
627
+ * const emitter = new EventEmitter<MyEvents>();
628
+ * emitter.on('update', ({ value }) => console.log(value));
629
+ * emitter.on('destroy', () => console.log('destroyed'));
630
+ * ```
615
631
  */
632
+ declare class EventEmitter<Events extends {
633
+ [K in keyof Events]: unknown;
634
+ } = Record<string, never>> {
635
+ protected callbacks: Map<keyof Events, Set<(data: unknown) => void>>;
636
+ /**
637
+ * Register an event listener
638
+ */
639
+ on<E extends keyof Events>(event: E, callback: EventCallback<Events[E]>): this;
640
+ /**
641
+ * Remove an event listener, or all listeners for an event if no callback specified
642
+ */
643
+ off<E extends keyof Events>(event: E, callback?: EventCallback<Events[E]>): this;
644
+ /**
645
+ * Emit an event to all registered listeners
646
+ * Uses .call(this) to preserve context for callbacks
647
+ */
648
+ emit<E extends keyof Events>(event: E, ...args: Events[E] extends undefined ? [] : [Events[E]]): this;
649
+ /**
650
+ * Register an event listener that fires only once
651
+ */
652
+ once<E extends keyof Events>(event: E, callback: EventCallback<Events[E]>): this;
653
+ /**
654
+ * Remove all listeners for a specific event, or all events if no event specified
655
+ */
656
+ removeAllListeners(event?: keyof Events): this;
657
+ /**
658
+ * Get the number of listeners for a specific event
659
+ */
660
+ listenerCount(event: keyof Events): number;
661
+ /**
662
+ * Get all event names that have listeners
663
+ */
664
+ eventNames(): (keyof Events)[];
665
+ }
616
666
 
617
667
  /**
618
- * Editor instance type (forward declaration)
668
+ * ExtensionManager - manages extensions and schema.
669
+ *
670
+ * Handles:
671
+ * - Extension lifecycle (flatten, resolve, bind)
672
+ * - Schema building from Node/Mark extensions
673
+ * - Plugin collection from all extensions
674
+ * - Extension storage management
675
+ * - Conflict detection (duplicate extension names)
619
676
  */
620
- interface ExtensionEditor {
621
- readonly state: EditorState;
622
- readonly view: EditorView;
623
- readonly schema: unknown;
624
- readonly commands: SingleCommands;
677
+
678
+ /**
679
+ * Error event props for safeCall
680
+ */
681
+ interface ErrorEventProps {
682
+ error: Error;
683
+ context: string;
625
684
  }
626
685
  /**
627
- * Any extension type (forward declaration)
686
+ * Editor interface for ExtensionManager
687
+ * Forward declaration to avoid circular dependency
628
688
  */
629
- interface AnyExtensionConfig {
630
- name: string;
631
- type?: 'extension' | 'node' | 'mark';
689
+ interface ExtensionManagerEditor {
690
+ readonly schema: Schema;
691
+ emit?(event: 'error', props: ErrorEventProps): void;
632
692
  }
633
693
  /**
634
- * Global attribute specification for injecting attributes into multiple node/mark types.
635
- * Used by extensions like TextAlign to add alignment to heading, paragraph, etc.
694
+ * Context attached to node view constructors for framework wrappers.
695
+ * Accessible via `(constructor as any).__domternalContext`.
636
696
  */
637
- interface GlobalAttributeSpec {
697
+ interface NodeViewContext {
698
+ editor: ExtensionManagerEditor;
699
+ extension: {
700
+ name: string;
701
+ options: Record<string, unknown>;
702
+ };
703
+ }
704
+ /**
705
+ * Options for ExtensionManager constructor
706
+ */
707
+ interface ExtensionManagerOptions {
638
708
  /**
639
- * Default value for the attribute.
709
+ * Extensions to process
710
+ * If provided, schema is built from extensions
640
711
  */
641
- default?: unknown;
712
+ extensions?: AnyExtension[] | undefined;
642
713
  /**
643
- * Parse attribute value from HTML element.
644
- * @param element - The DOM element to parse from
645
- * @returns The parsed attribute value
714
+ * Direct schema. If provided, extensions are ignored for schema building.
646
715
  */
647
- parseHTML?: (element: HTMLElement) => unknown;
716
+ schema?: Schema | undefined;
717
+ }
718
+ declare class ExtensionManager {
648
719
  /**
649
- * Render attribute value to HTML attributes.
650
- * @param attributes - The node/mark attributes
651
- * @returns Object of HTML attributes to set, or null/empty to skip
720
+ * Processed extensions (flattened, sorted by priority)
652
721
  */
653
- renderHTML?: (attributes: Record<string, unknown>) => Record<string, string> | null;
654
- }
655
- /**
656
- * Global attributes definition for injecting into node/mark types.
657
- */
658
- interface GlobalAttributes {
722
+ private readonly _extensions;
659
723
  /**
660
- * Node or mark type names to add these attributes to.
661
- * @example ['heading', 'paragraph']
724
+ * ProseMirror schema (built from extensions or passed directly)
662
725
  */
663
- types: string[];
726
+ private readonly _schema;
664
727
  /**
665
- * Attribute specifications to add.
666
- * @example { textAlign: { default: 'left', parseHTML: (el) => el.style.textAlign } }
728
+ * Reference to the editor instance
667
729
  */
668
- attributes: Record<string, GlobalAttributeSpec>;
669
- }
670
- /**
671
- * Context interface that describes what `this` will be in config methods.
672
- * This enables proper typing of `this.options`, `this.editor`, etc.
673
- * without creating circular dependencies.
674
- *
675
- * @typeParam Options - Extension options type
676
- * @typeParam Storage - Extension storage type
677
- */
678
- interface ExtensionContext<Options = unknown, Storage = unknown> {
679
- /** Extension type identifier */
680
- readonly type: 'extension' | 'node' | 'mark';
681
- /** Unique extension name */
682
- readonly name: string;
683
- /** Extension options (immutable after creation) */
684
- readonly options: Options;
685
- /** Extension storage (mutable state) */
686
- storage: Storage;
687
- /** Editor instance (null until bound by ExtensionManager) */
688
- editor: ExtensionEditor | null;
730
+ readonly editor: ExtensionManagerEditor;
689
731
  /**
690
- * Reference to the parent config method when using extend().
691
- * Available only inside overridden config methods.
692
- * Use `this.parent?.()` to call the parent's version of the current method.
732
+ * Extension storage (keyed by extension name)
693
733
  */
694
- parent?: ((...args: unknown[]) => unknown) | undefined;
695
- }
696
- /**
697
- * Base configuration properties for all extension types.
698
- * This interface contains just the properties without ThisType,
699
- * allowing Node and Mark configs to use their own context types.
700
- *
701
- * @typeParam Options - Extension options type
702
- * @typeParam Storage - Extension storage type
703
- */
704
- interface ExtensionConfigBase<Options = unknown, Storage = unknown> {
734
+ private readonly _storage;
705
735
  /**
706
- * Unique extension name
707
- * Used for identification, storage access, and error messages
736
+ * Whether the manager has been destroyed
708
737
  */
709
- name: string;
738
+ private isDestroyed;
710
739
  /**
711
- * Extension priority (higher = loaded first)
712
- *
713
- * Reserved ranges:
714
- * - 900-1000: Core nodes (Document, Text, Paragraph)
715
- * - 500-899: Standard extensions (Heading, Bold, etc.)
716
- * - 100-499: Default range for user extensions
717
- * - 0-99: Low priority extensions (run after everything)
718
- *
719
- * @default 100
740
+ * Cached plugins (built lazily)
720
741
  */
721
- priority?: number;
742
+ private _plugins;
722
743
  /**
723
- * Required extensions that must be present
724
- * ExtensionManager throws if any dependency is missing
725
- *
726
- * @example
727
- * dependencies: ['bulletList', 'orderedList']
744
+ * Cached commands (collected lazily)
728
745
  */
729
- dependencies?: string[];
746
+ private _commands;
730
747
  /**
731
- * Default options for this extension
732
- * Called during extension creation with `this` bound to the extension
748
+ * Cached toolbar items (collected lazily)
733
749
  */
734
- addOptions?: () => Options;
750
+ private _toolbarItems;
735
751
  /**
736
- * Initial storage state for this extension
737
- * Storage is mutable and accessible via editor.storage[extensionName]
752
+ * Cached floating-menu items (collected lazily)
738
753
  */
739
- addStorage?: () => Storage;
754
+ private _floatingMenuItems;
740
755
  /**
741
- * Commands this extension provides
742
- * Commands are accessible via editor.commands.commandName()
743
- *
744
- * @example
745
- * addCommands() {
746
- * return {
747
- * toggleBold: () => ({ commands }) => commands.toggleMark('bold'),
748
- * };
749
- * }
756
+ * Cached node views (collected lazily)
750
757
  */
751
- addCommands?: () => Record<string, (...args: never[]) => Command>;
758
+ private _nodeViews;
752
759
  /**
753
- * Keyboard shortcuts for this extension
754
- * Keys are shortcut strings (e.g., 'Mod-b'), values are handler functions
760
+ * Creates a new ExtensionManager
755
761
  *
756
- * @example
757
- * addKeyboardShortcuts() {
758
- * return {
759
- * 'Mod-b': () => this.editor.commands.toggleBold(),
760
- * };
761
- * }
762
+ * @param options - Extensions or direct schema
763
+ * @param editor - Editor instance
762
764
  */
763
- addKeyboardShortcuts?: () => Record<string, KeyboardShortcutCommand>;
765
+ constructor(options: ExtensionManagerOptions, editor: ExtensionManagerEditor);
764
766
  /**
765
- * Input rules (markdown-style shortcuts)
766
- * Triggered when user types matching patterns
767
- *
768
- * @example
769
- * addInputRules() {
770
- * return [
771
- * textblockTypeInputRule(/^## $/, this.type),
772
- * ];
773
- * }
767
+ * Gets the processed extensions array
774
768
  */
775
- addInputRules?: () => InputRule[];
769
+ get extensions(): readonly AnyExtension[];
776
770
  /**
777
- * ProseMirror plugins for this extension
771
+ * Gets the ProseMirror schema
778
772
  */
779
- addProseMirrorPlugins?: () => Plugin[];
773
+ get schema(): Schema;
780
774
  /**
781
- * Nested extensions (for extension bundles like StarterKit)
782
- * These extensions are flattened and processed like top-level extensions
775
+ * Gets extension storage (accessed via editor.storage)
783
776
  */
784
- addExtensions?: () => AnyExtensionConfig[];
777
+ get storage(): Record<string, unknown>;
785
778
  /**
786
- * Global attributes to add to multiple node/mark types.
787
- * Useful for extensions like TextAlign that need to add attributes
788
- * to several nodes (heading, paragraph, etc.) without modifying each.
789
- *
790
- * @example
791
- * addGlobalAttributes() {
792
- * return [{
793
- * types: ['heading', 'paragraph'],
794
- * attributes: {
795
- * textAlign: {
796
- * default: 'left',
797
- * parseHTML: (element) => element.style.textAlign || 'left',
798
- * renderHTML: (attrs) => attrs.textAlign !== 'left'
799
- * ? { style: `text-align: ${attrs.textAlign}` }
800
- * : null,
801
- * },
802
- * },
803
- * }];
804
- * }
805
- */
806
- addGlobalAttributes?: () => GlobalAttributes[];
807
- /**
808
- * Toolbar items this extension contributes.
809
- * Framework toolbar components read these to auto-generate buttons.
810
- *
811
- * @example
812
- * addToolbarItems() {
813
- * return [{
814
- * type: 'button',
815
- * name: 'bold',
816
- * command: 'toggleBold',
817
- * isActive: 'bold',
818
- * icon: 'textB',
819
- * label: 'Bold',
820
- * shortcut: 'Mod-b',
821
- * group: 'format',
822
- * }];
823
- * }
779
+ * Gets plugins from all extensions
780
+ * Cached after first call
824
781
  */
825
- addToolbarItems?: () => ToolbarItem[];
782
+ get plugins(): Plugin[];
826
783
  /**
827
- * Called before editor is created
828
- * Can be used to modify editor options
784
+ * Gets commands from all extensions
829
785
  */
830
- onBeforeCreate?: () => void;
786
+ get commands(): CommandMap;
831
787
  /**
832
- * Called when editor is fully initialized and ready
788
+ * Gets toolbar items from all extensions
789
+ * Cached after first call
833
790
  */
834
- onCreate?: () => void;
791
+ get toolbarItems(): ToolbarItem[];
835
792
  /**
836
- * Called when document content changes
793
+ * Gets floating-menu items from all extensions
794
+ * Cached after first call
837
795
  */
838
- onUpdate?: () => void;
796
+ get floatingMenuItems(): FloatingMenuItem[];
839
797
  /**
840
- * Called when selection changes (without content change)
798
+ * Gets node views from all Node extensions that define addNodeView
841
799
  */
842
- onSelectionUpdate?: () => void;
800
+ get nodeViews(): Record<string, NodeViewConstructor>;
843
801
  /**
844
- * Called on every transaction
845
- * Can be used to intercept or modify transactions
802
+ * Clears all caches (plugins, commands)
803
+ * Call when extensions change dynamically
846
804
  */
847
- onTransaction?: (props: {
848
- transaction: Transaction;
849
- }) => void;
805
+ clearAllCaches(): void;
850
806
  /**
851
- * Called when editor receives focus
807
+ * Recursively flattens extensions by expanding addExtensions()
808
+ * This allows extension bundles like StarterKit to work
852
809
  */
853
- onFocus?: (props: {
854
- event: FocusEvent;
855
- }) => void;
810
+ private flattenExtensions;
856
811
  /**
857
- * Called when editor loses focus
812
+ * Removes duplicate extensions by name, keeping the last occurrence.
813
+ * This allows parent extensions to auto-include children via addExtensions()
814
+ * while letting users override with explicitly configured versions.
858
815
  */
859
- onBlur?: (props: {
860
- event: FocusEvent;
861
- }) => void;
816
+ private deduplicateExtensions;
862
817
  /**
863
- * Called when editor is being destroyed
864
- * Use for cleanup (remove event listeners, etc.)
818
+ * Sorts extensions by priority (higher priority first)
819
+ * Default priority is 100
865
820
  */
866
- onDestroy?: () => void;
867
- }
868
- /**
869
- * Full configuration type for Extension.create()
870
- * Combines base properties with ThisType for proper `this` typing.
871
- *
872
- * @typeParam Options - Extension options type
873
- * @typeParam Storage - Extension storage type
874
- */
875
- type ExtensionConfig<Options = unknown, Storage = unknown> = ExtensionConfigBase<Options, Storage> & ThisType<ExtensionContext<Options, Storage>>;
876
-
877
- /**
878
- * Attribute specification for Node and Mark extensions
879
- *
880
- * Used by addAttributes() to define node/mark attributes
881
- * with parsing, rendering, and validation rules.
882
- *
883
- * @example
884
- * addAttributes() {
885
- * return {
886
- * level: {
887
- * default: 1,
888
- * parseHTML: (element) => parseInt(element.tagName.charAt(1), 10),
889
- * renderHTML: (attributes) => ({ 'data-level': attributes.level }),
890
- * },
891
- * };
892
- * }
893
- */
894
- /**
895
- * Specification for a single attribute
896
- */
897
- interface AttributeSpec {
821
+ private resolveExtensions;
898
822
  /**
899
- * Default value for the attribute
900
- * Used when the attribute is not explicitly set
823
+ * Detects duplicate extension names.
824
+ * @throws Error if duplicate names found
901
825
  */
902
- default?: unknown;
826
+ private detectConflicts;
903
827
  /**
904
- * Whether this attribute is rendered to the DOM
905
- * @default true
828
+ * Validates that all extension dependencies are present
829
+ * @throws Error if required dependency is missing
906
830
  */
907
- rendered?: boolean;
831
+ private checkDependencies;
908
832
  /**
909
- * Keep this attribute when splitting the node
910
- * For example, heading level should be kept when pressing Enter
911
- * @default true
833
+ * Sets editor reference on all extensions
912
834
  */
913
- keepOnSplit?: boolean;
835
+ private bindEditorToExtensions;
914
836
  /**
915
- * Validate attribute value (ProseMirror 1.22.0+).
916
- * When a string, a `|`-separated list of primitive types
917
- * (`"number"`, `"string"`, `"boolean"`, `"null"`, `"undefined"`).
918
- * When a function, it should throw if the value is invalid.
919
- *
920
- * @example
921
- * validate: 'number'
922
- * validate: 'string|null'
923
- * validate: (value) => { if (typeof value !== 'number') throw new Error('expected number'); }
837
+ * Collects global attributes from all extensions.
838
+ * Returns a map of type name -> attribute specs to merge.
924
839
  */
925
- validate?: string | ((value: unknown) => void);
840
+ private collectGlobalAttributes;
926
841
  /**
927
- * Parse attribute value from HTML element
928
- * Called during HTML parsing to extract attribute value
929
- *
930
- * @param element - The HTML element being parsed
931
- * @returns The attribute value
932
- *
933
- * @example
934
- * parseHTML: (element) => element.getAttribute('data-level')
842
+ * Applies global attributes to a node or mark spec.
843
+ * Merges extra attrs into spec.attrs, wraps parseDOM getAttrs to parse
844
+ * global attributes from DOM elements, and wraps toDOM to inject rendered
845
+ * global HTML attributes into the output.
935
846
  */
936
- parseHTML?: (element: HTMLElement) => unknown;
847
+ private applyGlobalAttributes;
937
848
  /**
938
- * Render attribute to HTML attributes object
939
- * Called during HTML serialization
940
- *
941
- * @param attributes - All attributes of the node/mark
942
- * @returns HTML attributes object or null to skip
943
- *
944
- * @example
945
- * renderHTML: (attributes) => ({ 'data-level': attributes.level })
849
+ * Builds ProseMirror Schema from Node and Mark extensions
946
850
  */
947
- renderHTML?: (attributes: Record<string, unknown>) => Record<string, string | number | boolean | null | undefined> | null;
948
- }
949
- /**
950
- * Collection of attribute specifications
951
- * Keyed by attribute name
952
- */
953
- type AttributeSpecs = Record<string, AttributeSpec>;
954
-
955
- /**
956
- * Node configuration types
957
- *
958
- * These types define the configuration object passed to Node.create()
959
- * for creating ProseMirror node extensions.
960
- */
961
-
962
- /**
963
- * Editor interface for Node context
964
- * Includes schema with nodes for NodeType getter
965
- */
966
- interface NodeEditorContext {
967
- readonly state: EditorState;
968
- readonly view: EditorView;
969
- readonly schema: {
970
- nodes: Record<string, NodeType>;
971
- };
972
- readonly commands: Record<string, (...args: unknown[]) => boolean>;
973
- }
974
- /**
975
- * Context interface for Node config methods.
976
- * Extends ExtensionContext with node-specific properties.
977
- * This enables proper typing of `this.options`, `this.nodeType`, etc.
978
- *
979
- * @typeParam Options - Node options type
980
- * @typeParam Storage - Node storage type
981
- */
982
- interface NodeContext<Options = unknown, Storage = unknown> extends Omit<ExtensionContext<Options, Storage>, 'editor' | 'type'> {
983
- /** Node type identifier */
984
- readonly type: 'node';
985
- /** Editor instance with schema access */
986
- editor: NodeEditorContext | null;
987
- /** ProseMirror NodeType (null until editor is initialized) */
988
- readonly nodeType: NodeType | null;
989
- }
990
- /**
991
- * Parse rule for converting HTML to ProseMirror node
992
- * Simplified version of ProseMirror's ParseRule
993
- */
994
- interface NodeParseRule {
851
+ private buildSchema;
995
852
  /**
996
- * CSS selector or tag name to match
997
- * @example 'p', 'h1', 'div.my-class'
853
+ * Initializes storage for all extensions
998
854
  */
999
- tag?: string;
855
+ private initializeStorage;
1000
856
  /**
1001
- * Match by CSS style
1002
- * @example 'font-weight'
857
+ * Builds all ProseMirror plugins from extensions
1003
858
  */
1004
- style?: string;
859
+ private buildPlugins;
1005
860
  /**
1006
- * Priority for this rule (higher = checked first)
1007
- * @default 50
861
+ * Collects keyboard shortcuts from all extensions
862
+ * Returns ProseMirror-compatible commands for keymap plugin
863
+ *
864
+ * Note: Extensions should return PM-compatible commands from addKeyboardShortcuts()
1008
865
  */
1009
- priority?: number;
866
+ private collectKeyboardShortcuts;
1010
867
  /**
1011
- * Whether to consume the matched element
1012
- * @default true
868
+ * Collects input rules from all extensions
1013
869
  */
1014
- consuming?: boolean;
870
+ private collectInputRules;
1015
871
  /**
1016
- * Context required for this rule to match
1017
- * ProseMirror content expression
872
+ * Collects commands from all extensions
873
+ *
874
+ * Note: Commands with the same name will be overwritten by later extensions
875
+ * (lower priority extensions override higher priority). This is intentional
876
+ * to allow customization of built-in commands.
1018
877
  */
1019
- context?: string;
878
+ private collectCommands;
1020
879
  /**
1021
- * Get attributes from the matched element
1022
- * Return null/undefined to skip this rule
880
+ * Collects toolbar items from all extensions
1023
881
  */
1024
- getAttrs?: ((node: HTMLElement) => Record<string, unknown> | null | undefined) | null;
882
+ private collectToolbarItems;
1025
883
  /**
1026
- * Get content from the matched element
1027
- * Return false to use default content parsing
884
+ * Collects floating-menu items from all extensions via `addFloatingMenuItems()`.
1028
885
  */
1029
- getContent?: (node: HTMLElement, schema: unknown) => unknown;
886
+ private collectFloatingMenuItems;
1030
887
  /**
1031
- * How to preserve whitespace
1032
- */
1033
- preserveWhitespace?: boolean | 'full';
1034
- }
1035
- /**
1036
- * Props passed to renderHTML function
1037
- */
1038
- interface NodeRenderHTMLProps {
1039
- /**
1040
- * The ProseMirror node being rendered
1041
- */
1042
- node: Node$1;
1043
- /**
1044
- * Merged HTML attributes from all sources
1045
- */
1046
- HTMLAttributes: Record<string, unknown>;
1047
- }
1048
- /**
1049
- * Node-specific configuration properties (schema-related)
1050
- */
1051
- interface NodeSchemaProperties {
1052
- /**
1053
- * Node group(s) this node belongs to
1054
- * Used in content expressions
888
+ * Collects node views from all Node extensions.
889
+ * Returns a map of node name to NodeViewConstructor for EditorView.
1055
890
  *
1056
- * @example 'block', 'inline', 'block list'
1057
- * Can be a function that returns the group string (useful for dynamic group based on options)
891
+ * Each constructor is annotated with `__domternalContext` containing
892
+ * the editor and extension metadata so framework wrappers (React, Vue)
893
+ * can access them without changing the ProseMirror calling convention.
1058
894
  */
1059
- group?: string | (() => string);
895
+ private collectNodeViews;
1060
896
  /**
1061
- * Content expression defining allowed children
1062
- * Uses ProseMirror content expression syntax
1063
- *
1064
- * @example 'inline*', 'block+', 'paragraph block*'
897
+ * Validates that the schema has required nodes
898
+ * @throws Error if schema is missing 'doc' or 'text' nodes
1065
899
  */
1066
- content?: string;
900
+ validateSchema(): void;
1067
901
  /**
1068
- * Whether this is an inline node
1069
- * Can be a function for dynamic inline based on options
1070
- * @default false
902
+ * Cleans up the extension manager
903
+ * Calls onDestroy on all extensions and clears all caches
1071
904
  */
1072
- inline?: boolean | (() => boolean);
905
+ destroy(): void;
1073
906
  /**
1074
- * Whether this node is an atom (no direct content editing)
1075
- * Cursor moves around atoms, not into them
907
+ * Safely executes a function, catching and reporting errors
908
+ * Prevents a single extension error from crashing the entire editor
1076
909
  *
1077
- * @example true for images, mentions, emoji
1078
- */
1079
- atom?: boolean;
1080
- /**
1081
- * Whether the node can be selected as a whole
1082
- * @default true for leaf nodes, false for others
1083
- */
1084
- selectable?: boolean;
1085
- /**
1086
- * Whether the node can be dragged
1087
- * @default false
1088
- */
1089
- draggable?: boolean;
1090
- /**
1091
- * Whether this node represents code
1092
- * Affects text input handling (disables smart quotes, etc.)
1093
- */
1094
- code?: boolean;
1095
- /**
1096
- * How whitespace is handled in this node
1097
- * - 'pre': preserve whitespace (like <pre>)
1098
- * - 'normal': collapse whitespace (default)
910
+ * Handles both synchronous errors and async promise rejections.
911
+ *
912
+ * @param fn - Function to execute
913
+ * @param context - Context for error reporting (e.g., 'Bold.onUpdate')
914
+ * @returns The function result, or undefined if an error occurred
1099
915
  */
1100
- whitespace?: 'pre' | 'normal';
916
+ safeCall<T>(fn: () => T, context: string): T | undefined;
1101
917
  /**
1102
- * Whether this node isolates marks at its boundaries
1103
- * Marks don't extend across isolating boundaries
918
+ * Calls onBeforeCreate on all extensions
1104
919
  */
1105
- isolating?: boolean;
920
+ callOnBeforeCreate(): void;
1106
921
  /**
1107
- * Whether a gap cursor is allowed inside this node.
1108
- * Set to false to prevent gapcursor from appearing inside this node.
1109
- * Set to true to force gapcursor even if the default heuristic disallows it.
1110
- * When undefined, uses ProseMirror's default heuristic.
922
+ * Calls onCreate on all extensions
1111
923
  */
1112
- allowGapCursor?: boolean;
924
+ callOnCreate(): void;
1113
925
  /**
1114
- * Table role for prosemirror-tables integration.
1115
- * Nodes with a tableRole are discovered by prosemirror-tables via spec.tableRole.
1116
- * One of: 'table', 'row', 'cell', 'header_cell'
926
+ * Calls onUpdate on all extensions
1117
927
  */
1118
- tableRole?: 'table' | 'row' | 'cell' | 'header_cell';
928
+ callOnUpdate(): void;
1119
929
  /**
1120
- * Whether this is a top-level node (document root)
1121
- * Only one node should have this set to true
930
+ * Calls onSelectionUpdate on all extensions
1122
931
  */
1123
- topNode?: boolean;
932
+ callOnSelectionUpdate(): void;
1124
933
  /**
1125
- * Whether this node defines its own scope
1126
- * Content and marks don't leak out of defining nodes
934
+ * Calls onTransaction on all extensions
935
+ * @param props - Transaction props
1127
936
  */
1128
- defining?: boolean;
937
+ callOnTransaction(props: {
938
+ transaction: Transaction;
939
+ }): void;
1129
940
  /**
1130
- * Which marks are allowed in this node
1131
- * Empty string means no marks allowed
1132
- *
1133
- * @example '', '_', 'bold italic'
941
+ * Calls onFocus on all extensions
942
+ * @param props - Focus event props
1134
943
  */
1135
- marks?: string;
944
+ callOnFocus(props: {
945
+ event: FocusEvent;
946
+ }): void;
1136
947
  /**
1137
- * Custom text for leaf nodes
1138
- * Used by getText() and textContent
1139
- *
1140
- * @example '\n' for hard break
948
+ * Calls onBlur on all extensions
949
+ * @param props - Blur event props
1141
950
  */
1142
- leafText?: string | ((node: Node$1) => string);
951
+ callOnBlur(props: {
952
+ event: FocusEvent;
953
+ }): void;
954
+ }
955
+
956
+ /**
957
+ * Inline Styles Utility
958
+ *
959
+ * Applies inline CSS styles to serialized HTML so it renders correctly
960
+ * when pasted outside the editor (email clients, CMS, Google Docs, etc.).
961
+ *
962
+ * Uses hardcoded light-theme defaults (same approach as Google Docs, Notion,
963
+ * TinyMCE). Optionally accepts overrides for custom styling.
964
+ *
965
+ * Only structural styles are inlined (borders, padding, margins, fonts).
966
+ * Colors are NOT inlined - explicit colors (TextColor, Highlight, cell bg)
967
+ * are already inline from renderHTML, and default text color is browser default.
968
+ */
969
+ interface InlineStyleOverrides {
970
+ blockquoteBorder?: string;
971
+ blockquoteColor?: string;
972
+ tableBorder?: string;
973
+ tableHeaderBg?: string;
974
+ codeBg?: string;
975
+ codeFont?: string;
976
+ codeBorder?: string;
977
+ codeBlockBg?: string;
978
+ codeBlockFont?: string;
979
+ hrBorder?: string;
980
+ linkColor?: string;
981
+ detailsBorder?: string;
982
+ detailsBg?: string;
1143
983
  /**
1144
- * Define node attributes
1145
- * Returns attribute specifications
1146
- *
1147
- * @example
1148
- * addAttributes() {
1149
- * return {
1150
- * level: { default: 1 },
1151
- * };
1152
- * }
984
+ * How to export table column widths from `data-colwidth` attributes.
985
+ * - `'percent'` (default): convert to percentage widths on first-row cells
986
+ * - `'pixel'`: convert to pixel widths on first-row cells, table gets fixed width
987
+ * - `'none'`: leave `data-colwidth` as-is, no width styles applied
1153
988
  */
1154
- addAttributes?: () => AttributeSpecs;
989
+ tableColumnWidths?: 'percent' | 'pixel' | 'none';
1155
990
  /**
1156
- * Parse rules for converting HTML to this node
1157
- * Each rule defines how to match and parse HTML elements
991
+ * Optional callback to syntax-highlight code blocks.
992
+ * Receives the raw text content and optional language, returns highlighted HTML
993
+ * with `<span class="hljs-*">` markup (or any spans with inline styles).
1158
994
  *
1159
995
  * @example
1160
- * parseHTML() {
1161
- * return [
1162
- * { tag: 'p' },
1163
- * { tag: 'div', priority: 10 },
1164
- * ];
1165
- * }
1166
- */
1167
- parseHTML?: () => NodeParseRule[];
1168
- /**
1169
- * Render this node to DOM
1170
- * Returns DOMOutputSpec (tag, attributes, children)
996
+ * ```ts
997
+ * import { createLowlight, common } from 'lowlight';
998
+ * import { toHtml } from 'hast-util-to-html';
999
+ * const lowlight = createLowlight(common);
1171
1000
  *
1172
- * @example
1173
- * renderHTML({ node, HTMLAttributes }) {
1174
- * return ['p', HTMLAttributes, 0];
1175
- * }
1176
- */
1177
- renderHTML?: (props: NodeRenderHTMLProps) => DOMOutputSpec;
1178
- /**
1179
- * Custom node view constructor
1180
- * For complex interactive nodes
1181
- */
1182
- addNodeView?: () => NodeViewConstructor;
1183
- /**
1184
- * Additional ProseMirror plugins for this node
1185
- * Called during plugin collection
1001
+ * inlineStyles(html, {
1002
+ * codeHighlighter: (code, language) => {
1003
+ * if (language && lowlight.registered(language)) {
1004
+ * return toHtml(lowlight.highlight(language, code));
1005
+ * }
1006
+ * return null; // no highlighting
1007
+ * },
1008
+ * });
1009
+ * ```
1186
1010
  */
1187
- addProseMirrorPlugins?: () => Plugin[];
1011
+ codeHighlighter?: (code: string, language: string | null) => string | null;
1188
1012
  }
1189
1013
  /**
1190
- * Configuration for Node extensions
1191
- * Combines ExtensionConfig base with node-specific schema properties.
1192
- * Uses ThisType<NodeContext> to provide proper typing for `this` in config methods.
1014
+ * Applies inline styles to all elements in a container.
1015
+ * Exported for use in clipboardSerializer (operates on DOM directly).
1016
+ */
1017
+ declare function applyInlineStyles(container: HTMLElement, overrides?: InlineStyleOverrides): void;
1018
+ /**
1019
+ * Takes an HTML string and returns it with inline CSS styles applied
1020
+ * to all elements, so it renders correctly outside the editor.
1193
1021
  *
1194
- * @typeParam Options - Node options type
1195
- * @typeParam Storage - Node storage type
1022
+ * @param html - Serialized HTML string from editor.getHTML()
1023
+ * @param overrides - Optional style overrides for custom theming
1196
1024
  *
1197
1025
  * @example
1198
- * const Paragraph = Node.create({
1199
- * name: 'paragraph',
1200
- * group: 'block',
1201
- * content: 'inline*',
1202
- * parseHTML() {
1203
- * // `this` is properly typed here!
1204
- * return [{ tag: 'p' }];
1205
- * },
1206
- * renderHTML({ HTMLAttributes }) {
1207
- * return ['p', HTMLAttributes, 0];
1208
- * },
1026
+ * ```ts
1027
+ * // Default light-theme styles
1028
+ * const styled = inlineStyles(editor.getHTML());
1029
+ *
1030
+ * // With custom overrides
1031
+ * const styled = inlineStyles(editor.getHTML(), {
1032
+ * blockquoteBorder: '5px solid red',
1033
+ * linkColor: '#ff6600',
1209
1034
  * });
1035
+ * ```
1210
1036
  */
1211
- type NodeConfig<Options = unknown, Storage = unknown> = ExtensionConfigBase<Options, Storage> & NodeSchemaProperties & ThisType<NodeContext<Options, Storage>>;
1037
+ declare function inlineStyles(html: string, overrides?: InlineStyleOverrides): string;
1212
1038
 
1213
1039
  /**
1214
- * Mark configuration types
1040
+ * Editor - Main editor class wrapping ProseMirror
1215
1041
  *
1216
- * These types define the configuration object passed to Mark.create()
1217
- * for creating ProseMirror mark extensions.
1042
+ * Manages extensions, schema, commands, and the ProseMirror EditorView/State.
1218
1043
  */
1219
1044
 
1220
1045
  /**
1221
- * Editor interface for Mark context
1222
- * Includes schema with marks for MarkType getter
1223
- */
1224
- interface MarkEditorContext {
1225
- readonly state: EditorState;
1226
- readonly view: EditorView;
1227
- readonly schema: {
1228
- marks: Record<string, MarkType>;
1229
- };
1230
- readonly commands: Record<string, (...args: unknown[]) => boolean>;
1231
- }
1232
- /**
1233
- * Context interface for Mark config methods.
1234
- * Extends ExtensionContext with mark-specific properties.
1235
- * This enables proper typing of `this.options`, `this.markType`, etc.
1046
+ * Main editor class
1236
1047
  *
1237
- * @typeParam Options - Mark options type
1238
- * @typeParam Storage - Mark storage type
1239
- */
1240
- interface MarkContext<Options = unknown, Storage = unknown> extends Omit<ExtensionContext<Options, Storage>, 'editor' | 'type'> {
1241
- /** Mark type identifier */
1242
- readonly type: 'mark';
1243
- /** Editor instance with schema access */
1244
- editor: MarkEditorContext | null;
1245
- /** ProseMirror MarkType (null until editor is initialized) */
1246
- readonly markType: MarkType | null;
1247
- }
1248
- /**
1249
- * Parse rule for converting HTML to ProseMirror mark
1250
- * Simplified version of ProseMirror's ParseRule for marks
1048
+ * Wraps ProseMirror's EditorView and EditorState with a cleaner API.
1049
+ *
1050
+ * @example
1051
+ * ```ts
1052
+ * import { Editor } from '@domternal/core';
1053
+ * import { Schema } from '@domternal/pm/model';
1054
+ *
1055
+ * const schema = new Schema({
1056
+ * nodes: { doc: { content: 'paragraph+' }, paragraph: { content: 'text*' }, text: {} }
1057
+ * });
1058
+ *
1059
+ * const editor = new Editor({
1060
+ * schema,
1061
+ * element: document.getElementById('editor'),
1062
+ * content: '<p>Hello world</p>',
1063
+ * });
1064
+ *
1065
+ * // Get content
1066
+ * const json = editor.getJSON();
1067
+ * const html = editor.getHTML();
1068
+ *
1069
+ * // Set content
1070
+ * editor.commands.setContent('<p>New content</p>');
1071
+ *
1072
+ * // Cleanup
1073
+ * editor.destroy();
1074
+ * ```
1251
1075
  */
1252
- interface MarkParseRule {
1076
+ declare class Editor extends EventEmitter<EditorEvents> {
1253
1077
  /**
1254
- * CSS selector or tag name to match
1255
- * @example 'strong', 'b', 'span.bold'
1078
+ * Editor configuration options
1256
1079
  */
1257
- tag?: string;
1080
+ private options;
1258
1081
  /**
1259
- * Match by CSS style property
1260
- * Can include expected value after '='
1261
- * @example 'font-weight', 'font-weight=bold'
1082
+ * Manages extensions and schema
1083
+ * @internal Exposed for CommandManager, not for public use
1262
1084
  */
1263
- style?: string;
1085
+ private _extensionManager;
1264
1086
  /**
1265
- * Priority for this rule (higher = checked first)
1266
- * @default 50
1087
+ * Gets the extension manager
1088
+ * @internal For CommandManager use only
1267
1089
  */
1268
- priority?: number;
1090
+ get extensionManager(): ExtensionManager;
1269
1091
  /**
1270
- * Whether to consume the matched element
1271
- * @default true
1092
+ * Manages commands
1272
1093
  */
1273
- consuming?: boolean;
1094
+ private commandManager;
1274
1095
  /**
1275
- * Get attributes from the matched element
1276
- * Return null/undefined to skip this rule
1277
- * Return false to explicitly not match
1278
- *
1279
- * For style rules, receives the style value as string
1096
+ * ProseMirror EditorView instance
1280
1097
  */
1281
- getAttrs?: ((node: HTMLElement | string) => Record<string, unknown> | false | null) | null;
1282
- }
1283
- /**
1284
- * Props passed to renderHTML function for marks
1285
- */
1286
- interface MarkRenderHTMLProps {
1098
+ view: EditorView;
1287
1099
  /**
1288
- * The ProseMirror mark being rendered
1100
+ * Whether the editor has been destroyed
1289
1101
  */
1290
- mark: Mark$1;
1102
+ private _isDestroyed;
1291
1103
  /**
1292
- * Merged HTML attributes from all sources
1104
+ * Timer for autofocus (cleared on destroy to prevent memory leaks)
1293
1105
  */
1294
- HTMLAttributes: Record<string, unknown>;
1295
- }
1296
- /**
1297
- * Mark-specific configuration properties (schema-related)
1298
- */
1299
- interface MarkSchemaProperties {
1106
+ private _autofocusTimer;
1300
1107
  /**
1301
- * Whether this mark should be active when the cursor
1302
- * is at its end (or beginning for marks that open).
1303
- *
1304
- * When true, typing at the mark's boundary continues the mark.
1305
- * When false, typing creates unmarked content.
1108
+ * Creates a new Editor instance
1306
1109
  *
1307
- * @default true
1110
+ * @param options - Editor configuration
1111
+ * @throws Error if running in SSR environment (no window)
1112
+ * @throws Error if schema is not provided
1308
1113
  */
1309
- inclusive?: boolean | (() => boolean);
1114
+ constructor(options: EditorOptions);
1310
1115
  /**
1311
- * Marks that this mark excludes (cannot coexist with)
1312
- *
1313
- * - '_' excludes all marks
1314
- * - Space-separated mark names exclude specific marks
1315
- * - Empty string or undefined means no exclusions
1316
- *
1317
- * @example 'code' - excludes code mark
1318
- * @example 'bold italic' - excludes bold and italic
1319
- * @example '_' - excludes all other marks
1116
+ * Gets the current EditorState
1320
1117
  */
1321
- excludes?: string;
1118
+ get state(): EditorState;
1322
1119
  /**
1323
- * Mark group(s) this mark belongs to
1324
- * Used in node's marks property
1325
- *
1326
- * @example 'formatting', 'inline'
1120
+ * Gets the ProseMirror schema
1327
1121
  */
1328
- group?: string;
1122
+ get schema(): Schema;
1329
1123
  /**
1330
- * Whether this mark can span multiple nodes
1331
- *
1332
- * When true (default), the mark persists across inline nodes.
1333
- * When false, the mark only applies within a single text node.
1334
- *
1335
- * @default true
1124
+ * Checks if the editor is editable
1336
1125
  */
1337
- spanning?: boolean;
1126
+ get isEditable(): boolean;
1338
1127
  /**
1339
- * Whether this mark represents visual formatting.
1128
+ * Checks if the editor content is empty
1129
+ */
1130
+ get isEmpty(): boolean;
1131
+ /**
1132
+ * Checks if the editor has focus
1133
+ */
1134
+ get isFocused(): boolean;
1135
+ /**
1136
+ * Checks if the editor has been destroyed
1137
+ */
1138
+ get isDestroyed(): boolean;
1139
+ /**
1140
+ * Gets single commands for immediate execution
1141
+ * @example editor.commands.focus('end')
1142
+ */
1143
+ get commands(): SingleCommands;
1144
+ /**
1145
+ * Creates a command chain for batched execution
1146
+ * @example editor.chain().focus().insertText('Hello').run()
1147
+ */
1148
+ chain(): ChainedCommands;
1149
+ /**
1150
+ * Checks if commands can be executed (dry-run)
1151
+ * @example if (editor.can().toggleBold()) { ... }
1152
+ */
1153
+ can(): CanCommands;
1154
+ /**
1155
+ * Gets extension storage
1156
+ * Access via: editor.storage.extensionName.propertyName
1157
+ */
1158
+ get storage(): Record<string, unknown>;
1159
+ /**
1160
+ * Toolbar items registered by all extensions.
1161
+ */
1162
+ get toolbarItems(): ToolbarItem[];
1163
+ /**
1164
+ * Floating-menu items registered by all extensions, rendered as the
1165
+ * block-insert menu shown on empty paragraphs.
1166
+ */
1167
+ get floatingMenuItems(): FloatingMenuItem[];
1168
+ /**
1169
+ * Checks if a node or mark is currently active
1340
1170
  *
1341
- * Used by `unsetAllMarks` to decide which marks to remove.
1342
- * Marks with `isFormatting: false` survive clear formatting
1343
- * (e.g., links, comments, annotations).
1171
+ * For toolbar button states - returns true if:
1172
+ * - For marks: the current selection has the mark applied
1173
+ * - For nodes: the cursor is inside that node type
1344
1174
  *
1345
- * Can be overridden via `.configure()`:
1346
- * ```ts
1347
- * Link.configure({ isFormatting: true }) // make links clearable
1348
- * ```
1175
+ * @param nameOrAttributes - Extension name, or object with name and attributes
1176
+ * @param attributes - Optional attributes to match (for node/mark specific states)
1349
1177
  *
1350
- * @default true
1178
+ * @example
1179
+ * editor.isActive('bold') // → true if bold mark is active
1180
+ * editor.isActive('heading', { level: 2 }) // → true if in h2
1181
+ * editor.isActive({ name: 'textAlign', attributes: { align: 'center' } })
1351
1182
  */
1352
- isFormatting?: boolean;
1183
+ isActive(nameOrAttributes: string | {
1184
+ name: string;
1185
+ attributes?: Record<string, unknown>;
1186
+ }, attributes?: Record<string, unknown>): boolean;
1353
1187
  /**
1354
- * Define mark attributes
1355
- * Returns attribute specifications
1188
+ * Gets attributes of the currently active node or mark
1189
+ *
1190
+ * Returns empty object if the node/mark is not found or not active.
1191
+ *
1192
+ * @param name - Extension name (node or mark)
1356
1193
  *
1357
1194
  * @example
1358
- * addAttributes() {
1359
- * return {
1360
- * color: { default: null },
1361
- * };
1362
- * }
1195
+ * editor.getAttributes('heading') // → { level: 2 }
1196
+ * editor.getAttributes('link') // → { href: 'https://...', target: '_blank' }
1363
1197
  */
1364
- addAttributes?: () => AttributeSpecs;
1198
+ getAttributes(name: string): Record<string, unknown>;
1365
1199
  /**
1366
- * Parse rules for converting HTML to this mark
1367
- * Each rule defines how to match and parse HTML elements
1200
+ * Helper to match attributes
1201
+ * Returns true if target contains all key/value pairs from source
1202
+ */
1203
+ private matchAttributes;
1204
+ /**
1205
+ * Gets the document content as JSON
1206
+ */
1207
+ getJSON(): JSONContent;
1208
+ /**
1209
+ * Gets the document content as HTML string
1368
1210
  *
1369
- * @example
1370
- * parseHTML() {
1371
- * return [
1372
- * { tag: 'strong' },
1373
- * { tag: 'b' },
1374
- * { style: 'font-weight', getAttrs: (value) => /bold|[5-9]\d{2}/.test(value) && null },
1375
- * ];
1376
- * }
1211
+ * @param options - Optional settings
1212
+ * @param options.styled - When true (or an override object), applies inline CSS
1213
+ * styles so the HTML renders correctly outside the editor (email, CMS, etc.)
1377
1214
  */
1378
- parseHTML?: () => MarkParseRule[];
1215
+ getHTML(options?: {
1216
+ styled?: boolean | InlineStyleOverrides;
1217
+ }): string;
1379
1218
  /**
1380
- * Render this mark to DOM
1381
- * Returns DOMOutputSpec (tag, attributes, hole)
1219
+ * Gets the document content as plain text
1382
1220
  *
1383
- * The 0 in the return array indicates where child content goes.
1221
+ * @param options - Options for text extraction
1222
+ * @param options.blockSeparator - String to insert between blocks (default: '\n\n')
1223
+ */
1224
+ getText(options?: {
1225
+ blockSeparator?: string;
1226
+ }): string;
1227
+ /**
1228
+ * Executes a command with proper CommandProps
1229
+ * @internal
1230
+ */
1231
+ private runCommand;
1232
+ /**
1233
+ * Sets the editor content
1384
1234
  *
1385
- * @example
1386
- * renderHTML({ mark, HTMLAttributes }) {
1387
- * return ['strong', HTMLAttributes, 0];
1388
- * }
1235
+ * @param content - JSON or HTML content
1236
+ * @param emitUpdate - Whether to emit update event (default: true)
1237
+ * @returns true if content was set successfully, false if content was invalid
1389
1238
  */
1390
- renderHTML?: (props: MarkRenderHTMLProps) => DOMOutputSpec;
1239
+ setContent(content: Content, emitUpdate?: boolean): boolean;
1240
+ /**
1241
+ * Clears the editor content
1242
+ *
1243
+ * @param emitUpdate - Whether to emit update event (default: true)
1244
+ * @returns true if content was cleared successfully
1245
+ */
1246
+ clearContent(emitUpdate?: boolean): boolean;
1247
+ /**
1248
+ * Sets whether the editor is editable
1249
+ *
1250
+ * @param editable - Whether the editor should be editable
1251
+ */
1252
+ setEditable(editable: boolean): this;
1253
+ /**
1254
+ * Focuses the editor
1255
+ *
1256
+ * @param position - Where to place cursor (default: null = just focus)
1257
+ */
1258
+ focus(position?: FocusPosition): this;
1259
+ /**
1260
+ * Removes focus from the editor
1261
+ */
1262
+ blur(): this;
1263
+ /**
1264
+ * Registers a ProseMirror plugin dynamically at runtime, after the editor
1265
+ * has been created. Safe to call repeatedly with the same plugin key.
1266
+ */
1267
+ registerPlugin(plugin: Plugin): void;
1268
+ /**
1269
+ * Unregisters a ProseMirror plugin by its PluginKey.
1270
+ * Uses PluginKey.get() to identify the plugin to remove.
1271
+ */
1272
+ unregisterPlugin(key: PluginKey): void;
1273
+ /**
1274
+ * Destroys the editor and cleans up resources
1275
+ *
1276
+ * After calling destroy(), the editor instance should not be used.
1277
+ */
1278
+ destroy(): void;
1279
+ /**
1280
+ * Builds a clipboardSerializer that applies a transform function to HTML on copy/cut.
1281
+ */
1282
+ private buildClipboardSerializer;
1283
+ /**
1284
+ * Creates the editor instance
1285
+ */
1286
+ private createEditor;
1287
+ /**
1288
+ * Handles ProseMirror transactions
1289
+ */
1290
+ private dispatchTransaction;
1291
+ /**
1292
+ * Emit method - needed for CommandManager interface
1293
+ */
1294
+ emit<E extends keyof EditorEvents>(event: E, ...args: EditorEvents[E] extends undefined ? [] : [EditorEvents[E]]): this;
1391
1295
  }
1296
+
1392
1297
  /**
1393
- * Configuration for Mark extensions
1394
- * Combines ExtensionConfigBase with mark-specific schema properties.
1395
- * Uses ThisType<MarkContext> to provide proper typing for `this` in config methods.
1298
+ * FloatingMenu configuration types
1396
1299
  *
1397
- * @typeParam Options - Mark options type
1398
- * @typeParam Storage - Mark storage type
1399
- *
1400
- * @example
1401
- * const Bold = Mark.create({
1402
- * name: 'bold',
1403
- * parseHTML() {
1404
- * // `this` is properly typed here!
1405
- * return [{ tag: 'strong' }, { tag: 'b' }];
1406
- * },
1407
- * renderHTML({ HTMLAttributes }) {
1408
- * return ['strong', HTMLAttributes, 0];
1409
- * },
1410
- * });
1300
+ * Types for block-insert items contributed by extensions via the
1301
+ * `addFloatingMenuItems()` hook. Framework wrappers read these items
1302
+ * and render a WAI-ARIA menu shown on empty paragraphs.
1411
1303
  */
1412
- type MarkConfig<Options = unknown, Storage = unknown> = ExtensionConfigBase<Options, Storage> & MarkSchemaProperties & ThisType<MarkContext<Options, Storage>>;
1413
1304
 
1414
1305
  /**
1415
- * Callback type for event handlers
1416
- * - Events with payload: (data: T) => void
1417
- * - Events without payload (undefined): () => void
1418
- */
1419
- type EventCallback<T> = T extends undefined ? () => void : (data: T) => void;
1420
- /**
1421
- * Generic, type-safe event emitter
1306
+ * A single entry in the floating menu.
1307
+ *
1308
+ * Unlike `ToolbarButton`, floating-menu items represent one-shot insert
1309
+ * actions (no toggle / active state), carry an optional `description` for
1310
+ * Notion-style two-line rendering, and expose `keywords` for future
1311
+ * slash-command filtering.
1422
1312
  *
1423
1313
  * @example
1424
- * ```typescript
1425
- * interface MyEvents {
1426
- * update: { value: number };
1427
- * destroy: undefined;
1314
+ * {
1315
+ * name: 'heading-1',
1316
+ * label: 'Heading 1',
1317
+ * description: 'Big section heading',
1318
+ * icon: 'textHOne',
1319
+ * group: 'Basic',
1320
+ * shortcut: '# ',
1321
+ * command: 'toggleHeading',
1322
+ * commandArgs: [{ level: 1 }],
1428
1323
  * }
1429
- *
1430
- * const emitter = new EventEmitter<MyEvents>();
1431
- * emitter.on('update', ({ value }) => console.log(value));
1432
- * emitter.on('destroy', () => console.log('destroyed'));
1433
- * ```
1434
1324
  */
1435
- declare class EventEmitter<Events extends {
1436
- [K in keyof Events]: unknown;
1437
- } = Record<string, never>> {
1438
- protected callbacks: Map<keyof Events, Set<(data: unknown) => void>>;
1439
- /**
1440
- * Register an event listener
1441
- */
1442
- on<E extends keyof Events>(event: E, callback: EventCallback<Events[E]>): this;
1443
- /**
1444
- * Remove an event listener, or all listeners for an event if no callback specified
1445
- */
1446
- off<E extends keyof Events>(event: E, callback?: EventCallback<Events[E]>): this;
1447
- /**
1448
- * Emit an event to all registered listeners
1449
- * Uses .call(this) to preserve context for callbacks
1450
- */
1451
- emit<E extends keyof Events>(event: E, ...args: Events[E] extends undefined ? [] : [Events[E]]): this;
1325
+ interface FloatingMenuItem {
1326
+ /** Unique identifier (used as React key, aria id, and dedup key when merging). */
1327
+ name: string;
1328
+ /** Primary label shown to the user. */
1329
+ label: string;
1330
+ /** Optional secondary line shown under the label. */
1331
+ description?: string;
1332
+ /** Icon key resolved against the editor's IconSet. Optional. */
1333
+ icon?: string;
1452
1334
  /**
1453
- * Register an event listener that fires only once
1335
+ * Group heading. Items with the same group render under a shared
1336
+ * `<div role="group" aria-label={group}>` section.
1337
+ * @default '' (no group heading)
1454
1338
  */
1455
- once<E extends keyof Events>(event: E, callback: EventCallback<Events[E]>): this;
1339
+ group?: string;
1340
+ /** Sort order within group (higher first). @default 100 */
1341
+ priority?: number;
1342
+ /** Keywords for future slash-command fuzzy filtering. */
1343
+ keywords?: string[];
1344
+ /** Visible keyboard-shortcut hint (e.g. `# `, `Mod-Alt-1`). */
1345
+ shortcut?: string;
1456
1346
  /**
1457
- * Remove all listeners for a specific event, or all events if no event specified
1347
+ * Command to execute on activation.
1348
+ *
1349
+ * - String: key of `editor.commands`; invoked with `commandArgs`.
1350
+ * - Function: arbitrary callback receiving the editor. Use when the
1351
+ * action is not a registered command (e.g. opening a popover).
1458
1352
  */
1459
- removeAllListeners(event?: keyof Events): this;
1353
+ command: string | ((editor: Editor) => void);
1354
+ /** Arguments for string commands. Ignored when `command` is a function. */
1355
+ commandArgs?: unknown[];
1460
1356
  /**
1461
- * Get the number of listeners for a specific event
1357
+ * Custom disabled predicate. When omitted, the controller dry-runs the
1358
+ * command via `editor.can()` (string commands only) to detect availability.
1462
1359
  */
1463
- listenerCount(event: keyof Events): number;
1360
+ isDisabled?: (editor: Editor) => boolean;
1464
1361
  /**
1465
- * Get all event names that have listeners
1362
+ * Node-type names that, when present as ancestors of the cursor, cause
1363
+ * the slash menu to hide this item. Useful for list/task-list entries:
1364
+ * e.g. `['bulletList']` removes "Bulleted list" from the menu while the
1365
+ * cursor is already inside a bullet list, so picking it doesn't lift
1366
+ * the user out of the list unexpectedly.
1466
1367
  */
1467
- eventNames(): (keyof Events)[];
1368
+ hideWhenInside?: string[];
1468
1369
  }
1469
-
1470
1370
  /**
1471
- * ExtensionManager - Manages extensions and schema
1371
+ * Override form accepted by `FloatingMenu.configure({ items })`.
1472
1372
  *
1473
- * Handles:
1474
- * - Extension lifecycle (flatten, resolve, bind)
1475
- * - Schema building from Node/Mark extensions
1476
- * - Plugin collection from all extensions
1477
- * - Extension storage management
1478
- * - Conflict detection (AD-7)
1373
+ * - Array: replaces the collected defaults entirely.
1374
+ * - Function: transforms the defaults (filter / reorder / extend).
1479
1375
  */
1376
+ type FloatingMenuItemsOverride = FloatingMenuItem[] | ((defaults: FloatingMenuItem[], editor: Editor) => FloatingMenuItem[]);
1480
1377
 
1481
1378
  /**
1482
- * Error event props for safeCall
1379
+ * Extension configuration types
1380
+ *
1381
+ * These types define the configuration object passed to Extension.create(),
1382
+ * Node.create(), and Mark.create() factory methods.
1483
1383
  */
1484
- interface ErrorEventProps {
1485
- error: Error;
1486
- context: string;
1487
- }
1384
+
1488
1385
  /**
1489
- * Editor interface for ExtensionManager
1490
- * Forward declaration to avoid circular dependency
1386
+ * Editor instance type (forward declaration)
1491
1387
  */
1492
- interface ExtensionManagerEditor {
1493
- readonly schema: Schema;
1494
- emit?(event: 'error', props: ErrorEventProps): void;
1388
+ interface ExtensionEditor {
1389
+ readonly state: EditorState;
1390
+ readonly view: EditorView;
1391
+ readonly schema: unknown;
1392
+ readonly commands: SingleCommands;
1495
1393
  }
1496
1394
  /**
1497
- * Context attached to node view constructors for framework wrappers.
1498
- * Accessible via `(constructor as any).__domternalContext`.
1395
+ * Any extension type (forward declaration)
1499
1396
  */
1500
- interface NodeViewContext {
1501
- editor: ExtensionManagerEditor;
1502
- extension: {
1503
- name: string;
1504
- options: Record<string, unknown>;
1505
- };
1397
+ interface AnyExtensionConfig {
1398
+ name: string;
1399
+ type?: 'extension' | 'node' | 'mark';
1506
1400
  }
1507
1401
  /**
1508
- * Options for ExtensionManager constructor
1402
+ * Global attribute specification for injecting attributes into multiple node/mark types.
1403
+ * Used by extensions like TextAlign to add alignment to heading, paragraph, etc.
1509
1404
  */
1510
- interface ExtensionManagerOptions {
1511
- /**
1512
- * Extensions to process
1513
- * If provided, schema is built from extensions
1514
- */
1515
- extensions?: AnyExtension[] | undefined;
1516
- /**
1517
- * Direct schema (backward compatibility with Step 1.3)
1518
- * If provided, extensions are ignored for schema building
1519
- */
1520
- schema?: Schema | undefined;
1521
- }
1522
- declare class ExtensionManager {
1405
+ interface GlobalAttributeSpec {
1523
1406
  /**
1524
- * Processed extensions (flattened, sorted by priority)
1407
+ * Default value for the attribute.
1525
1408
  */
1526
- private readonly _extensions;
1409
+ default?: unknown;
1527
1410
  /**
1528
- * ProseMirror schema (built from extensions or passed directly)
1411
+ * Parse attribute value from HTML element.
1412
+ * @param element - The DOM element to parse from
1413
+ * @returns The parsed attribute value
1529
1414
  */
1530
- private readonly _schema;
1415
+ parseHTML?: (element: HTMLElement) => unknown;
1531
1416
  /**
1532
- * Reference to the editor instance
1417
+ * Render attribute value to HTML attributes.
1418
+ * @param attributes - The node/mark attributes
1419
+ * @returns Object of HTML attributes to set, or null/empty to skip
1533
1420
  */
1534
- readonly editor: ExtensionManagerEditor;
1421
+ renderHTML?: (attributes: Record<string, unknown>) => Record<string, string> | null;
1422
+ }
1423
+ /**
1424
+ * Global attributes definition for injecting into node/mark types.
1425
+ */
1426
+ interface GlobalAttributes {
1535
1427
  /**
1536
- * Extension storage (keyed by extension name)
1428
+ * Node or mark type names to add these attributes to.
1429
+ * @example ['heading', 'paragraph']
1537
1430
  */
1538
- private readonly _storage;
1431
+ types: string[];
1539
1432
  /**
1540
- * Whether the manager has been destroyed
1433
+ * Attribute specifications to add.
1434
+ * @example { textAlign: { default: 'left', parseHTML: (el) => el.style.textAlign } }
1541
1435
  */
1542
- private isDestroyed;
1436
+ attributes: Record<string, GlobalAttributeSpec>;
1437
+ }
1438
+ /**
1439
+ * Context interface that describes what `this` will be in config methods.
1440
+ * This enables proper typing of `this.options`, `this.editor`, etc.
1441
+ * without creating circular dependencies.
1442
+ *
1443
+ * @typeParam Options - Extension options type
1444
+ * @typeParam Storage - Extension storage type
1445
+ */
1446
+ interface ExtensionContext<Options = unknown, Storage = unknown> {
1447
+ /** Extension type identifier */
1448
+ readonly type: 'extension' | 'node' | 'mark';
1449
+ /** Unique extension name */
1450
+ readonly name: string;
1451
+ /** Extension options (immutable after creation) */
1452
+ readonly options: Options;
1453
+ /** Extension storage (mutable state) */
1454
+ storage: Storage;
1455
+ /** Editor instance (null until bound by ExtensionManager) */
1456
+ editor: ExtensionEditor | null;
1543
1457
  /**
1544
- * Cached plugins (built lazily)
1458
+ * Reference to the parent config method when using extend().
1459
+ * Available only inside overridden config methods.
1460
+ * Use `this.parent?.()` to call the parent's version of the current method.
1545
1461
  */
1546
- private _plugins;
1547
- /**
1548
- * Cached commands (collected lazily)
1549
- */
1550
- private _commands;
1462
+ parent?: ((...args: unknown[]) => unknown) | undefined;
1463
+ }
1464
+ /**
1465
+ * Base configuration properties for all extension types.
1466
+ * This interface contains just the properties without ThisType,
1467
+ * allowing Node and Mark configs to use their own context types.
1468
+ *
1469
+ * @typeParam Options - Extension options type
1470
+ * @typeParam Storage - Extension storage type
1471
+ */
1472
+ interface ExtensionConfigBase<Options = unknown, Storage = unknown> {
1551
1473
  /**
1552
- * Cached toolbar items (collected lazily)
1474
+ * Unique extension name
1475
+ * Used for identification, storage access, and error messages
1553
1476
  */
1554
- private _toolbarItems;
1477
+ name: string;
1555
1478
  /**
1556
- * Cached node views (collected lazily)
1479
+ * Extension priority (higher = loaded first)
1480
+ *
1481
+ * Reserved ranges:
1482
+ * - 900-1000: Core nodes (Document, Text, Paragraph)
1483
+ * - 500-899: Standard extensions (Heading, Bold, etc.)
1484
+ * - 100-499: Default range for user extensions
1485
+ * - 0-99: Low priority extensions (run after everything)
1486
+ *
1487
+ * @default 100
1557
1488
  */
1558
- private _nodeViews;
1489
+ priority?: number;
1559
1490
  /**
1560
- * Creates a new ExtensionManager
1491
+ * Required extensions that must be present
1492
+ * ExtensionManager throws if any dependency is missing
1561
1493
  *
1562
- * @param options - Extensions or direct schema
1563
- * @param editor - Editor instance
1494
+ * @example
1495
+ * dependencies: ['bulletList', 'orderedList']
1564
1496
  */
1565
- constructor(options: ExtensionManagerOptions, editor: ExtensionManagerEditor);
1497
+ dependencies?: string[];
1566
1498
  /**
1567
- * Gets the processed extensions array
1499
+ * Default options for this extension
1500
+ * Called during extension creation with `this` bound to the extension
1568
1501
  */
1569
- get extensions(): readonly AnyExtension[];
1502
+ addOptions?: () => Options;
1570
1503
  /**
1571
- * Gets the ProseMirror schema
1504
+ * Initial storage state for this extension
1505
+ * Storage is mutable and accessible via editor.storage[extensionName]
1572
1506
  */
1573
- get schema(): Schema;
1507
+ addStorage?: () => Storage;
1574
1508
  /**
1575
- * Gets extension storage (accessed via editor.storage)
1509
+ * Commands this extension provides
1510
+ * Commands are accessible via editor.commands.commandName()
1511
+ *
1512
+ * @example
1513
+ * addCommands() {
1514
+ * return {
1515
+ * toggleBold: () => ({ commands }) => commands.toggleMark('bold'),
1516
+ * };
1517
+ * }
1576
1518
  */
1577
- get storage(): Record<string, unknown>;
1519
+ addCommands?: () => Record<string, (...args: never[]) => Command>;
1578
1520
  /**
1579
- * Gets plugins from all extensions
1580
- * Cached after first call
1521
+ * Keyboard shortcuts for this extension
1522
+ * Keys are shortcut strings (e.g., 'Mod-b'), values are handler functions
1523
+ *
1524
+ * @example
1525
+ * addKeyboardShortcuts() {
1526
+ * return {
1527
+ * 'Mod-b': () => this.editor.commands.toggleBold(),
1528
+ * };
1529
+ * }
1581
1530
  */
1582
- get plugins(): Plugin[];
1531
+ addKeyboardShortcuts?: () => Record<string, KeyboardShortcutCommand>;
1583
1532
  /**
1584
- * Gets commands from all extensions
1533
+ * Input rules (markdown-style shortcuts)
1534
+ * Triggered when user types matching patterns
1535
+ *
1536
+ * @example
1537
+ * addInputRules() {
1538
+ * return [
1539
+ * textblockTypeInputRule(/^## $/, this.type),
1540
+ * ];
1541
+ * }
1585
1542
  */
1586
- get commands(): CommandMap;
1543
+ addInputRules?: () => InputRule[];
1587
1544
  /**
1588
- * Gets toolbar items from all extensions
1589
- * Cached after first call
1545
+ * ProseMirror plugins for this extension
1590
1546
  */
1591
- get toolbarItems(): ToolbarItem[];
1547
+ addProseMirrorPlugins?: () => Plugin[];
1592
1548
  /**
1593
- * Gets node views from all Node extensions that define addNodeView
1549
+ * Nested extensions (for extension bundles like StarterKit)
1550
+ * These extensions are flattened and processed like top-level extensions
1594
1551
  */
1595
- get nodeViews(): Record<string, NodeViewConstructor>;
1552
+ addExtensions?: () => AnyExtensionConfig[];
1596
1553
  /**
1597
- * Clears all caches (plugins, commands)
1598
- * Call when extensions change dynamically
1554
+ * Global attributes to add to multiple node/mark types.
1555
+ * Useful for extensions like TextAlign that need to add attributes
1556
+ * to several nodes (heading, paragraph, etc.) without modifying each.
1557
+ *
1558
+ * @example
1559
+ * addGlobalAttributes() {
1560
+ * return [{
1561
+ * types: ['heading', 'paragraph'],
1562
+ * attributes: {
1563
+ * textAlign: {
1564
+ * default: 'left',
1565
+ * parseHTML: (element) => element.style.textAlign || 'left',
1566
+ * renderHTML: (attrs) => attrs.textAlign !== 'left'
1567
+ * ? { style: `text-align: ${attrs.textAlign}` }
1568
+ * : null,
1569
+ * },
1570
+ * },
1571
+ * }];
1572
+ * }
1599
1573
  */
1600
- clearAllCaches(): void;
1574
+ addGlobalAttributes?: () => GlobalAttributes[];
1601
1575
  /**
1602
- * Recursively flattens extensions by expanding addExtensions()
1603
- * This allows extension bundles like StarterKit to work
1576
+ * Toolbar items this extension contributes.
1577
+ * Framework toolbar components read these to auto-generate buttons.
1578
+ *
1579
+ * @example
1580
+ * addToolbarItems() {
1581
+ * return [{
1582
+ * type: 'button',
1583
+ * name: 'bold',
1584
+ * command: 'toggleBold',
1585
+ * isActive: 'bold',
1586
+ * icon: 'textB',
1587
+ * label: 'Bold',
1588
+ * shortcut: 'Mod-b',
1589
+ * group: 'format',
1590
+ * }];
1591
+ * }
1604
1592
  */
1605
- private flattenExtensions;
1593
+ addToolbarItems?: () => ToolbarItem[];
1606
1594
  /**
1607
- * Removes duplicate extensions by name, keeping the last occurrence.
1608
- * This allows parent extensions to auto-include children via addExtensions()
1609
- * while letting users override with explicitly configured versions.
1595
+ * Floating-menu items this extension contributes.
1596
+ * Block-insert menu shown on empty paragraphs aggregates these items
1597
+ * across all extensions. Framework wrappers render them as a WAI-ARIA
1598
+ * menu with groups, arrow-key navigation, and Enter to execute.
1599
+ *
1600
+ * @example
1601
+ * addFloatingMenuItems() {
1602
+ * return [{
1603
+ * name: 'heading-1',
1604
+ * label: 'Heading 1',
1605
+ * description: 'Big section heading',
1606
+ * icon: 'textHOne',
1607
+ * group: 'Basic',
1608
+ * command: 'toggleHeading',
1609
+ * commandArgs: [{ level: 1 }],
1610
+ * }];
1611
+ * }
1610
1612
  */
1611
- private deduplicateExtensions;
1613
+ addFloatingMenuItems?: () => FloatingMenuItem[];
1612
1614
  /**
1613
- * Sorts extensions by priority (higher priority first)
1614
- * Default priority is 100
1615
+ * Called before editor is created
1616
+ * Can be used to modify editor options
1615
1617
  */
1616
- private resolveExtensions;
1618
+ onBeforeCreate?: () => void;
1617
1619
  /**
1618
- * Detects duplicate extension names (AD-7: Schema Conflict Detection)
1619
- * @throws Error if duplicate names found
1620
+ * Called when editor is fully initialized and ready
1620
1621
  */
1621
- private detectConflicts;
1622
+ onCreate?: () => void;
1622
1623
  /**
1623
- * Validates that all extension dependencies are present
1624
- * @throws Error if required dependency is missing
1624
+ * Called when document content changes
1625
1625
  */
1626
- private checkDependencies;
1626
+ onUpdate?: () => void;
1627
1627
  /**
1628
- * Sets editor reference on all extensions
1628
+ * Called when selection changes (without content change)
1629
1629
  */
1630
- private bindEditorToExtensions;
1630
+ onSelectionUpdate?: () => void;
1631
1631
  /**
1632
- * Collects global attributes from all extensions.
1633
- * Returns a map of type name -> attribute specs to merge.
1632
+ * Called on every transaction
1633
+ * Can be used to intercept or modify transactions
1634
1634
  */
1635
- private collectGlobalAttributes;
1635
+ onTransaction?: (props: {
1636
+ transaction: Transaction;
1637
+ }) => void;
1636
1638
  /**
1637
- * Applies global attributes to a node or mark spec.
1638
- * Merges extra attrs into spec.attrs, wraps parseDOM getAttrs to parse
1639
- * global attributes from DOM elements, and wraps toDOM to inject rendered
1640
- * global HTML attributes into the output.
1639
+ * Called when editor receives focus
1641
1640
  */
1642
- private applyGlobalAttributes;
1641
+ onFocus?: (props: {
1642
+ event: FocusEvent;
1643
+ }) => void;
1643
1644
  /**
1644
- * Builds ProseMirror Schema from Node and Mark extensions
1645
+ * Called when editor loses focus
1645
1646
  */
1646
- private buildSchema;
1647
+ onBlur?: (props: {
1648
+ event: FocusEvent;
1649
+ }) => void;
1647
1650
  /**
1648
- * Initializes storage for all extensions
1651
+ * Called when editor is being destroyed
1652
+ * Use for cleanup (remove event listeners, etc.)
1649
1653
  */
1650
- private initializeStorage;
1654
+ onDestroy?: () => void;
1655
+ }
1656
+ /**
1657
+ * Full configuration type for Extension.create()
1658
+ * Combines base properties with ThisType for proper `this` typing.
1659
+ *
1660
+ * @typeParam Options - Extension options type
1661
+ * @typeParam Storage - Extension storage type
1662
+ */
1663
+ type ExtensionConfig<Options = unknown, Storage = unknown> = ExtensionConfigBase<Options, Storage> & ThisType<ExtensionContext<Options, Storage>>;
1664
+
1665
+ /**
1666
+ * Attribute specification for Node and Mark extensions
1667
+ *
1668
+ * Used by addAttributes() to define node/mark attributes
1669
+ * with parsing, rendering, and validation rules.
1670
+ *
1671
+ * @example
1672
+ * addAttributes() {
1673
+ * return {
1674
+ * level: {
1675
+ * default: 1,
1676
+ * parseHTML: (element) => parseInt(element.tagName.charAt(1), 10),
1677
+ * renderHTML: (attributes) => ({ 'data-level': attributes.level }),
1678
+ * },
1679
+ * };
1680
+ * }
1681
+ */
1682
+ /**
1683
+ * Specification for a single attribute
1684
+ */
1685
+ interface AttributeSpec {
1651
1686
  /**
1652
- * Builds all ProseMirror plugins from extensions
1687
+ * Default value for the attribute
1688
+ * Used when the attribute is not explicitly set
1653
1689
  */
1654
- private buildPlugins;
1690
+ default?: unknown;
1655
1691
  /**
1656
- * Collects keyboard shortcuts from all extensions
1657
- * Returns ProseMirror-compatible commands for keymap plugin
1658
- *
1659
- * Note: Extensions should return PM-compatible commands from addKeyboardShortcuts()
1692
+ * Whether this attribute is rendered to the DOM
1693
+ * @default true
1660
1694
  */
1661
- private collectKeyboardShortcuts;
1695
+ rendered?: boolean;
1662
1696
  /**
1663
- * Collects input rules from all extensions
1697
+ * Keep this attribute when splitting the node
1698
+ * For example, heading level should be kept when pressing Enter
1699
+ * @default true
1664
1700
  */
1665
- private collectInputRules;
1701
+ keepOnSplit?: boolean;
1666
1702
  /**
1667
- * Collects commands from all extensions
1703
+ * Validate attribute value (ProseMirror 1.22.0+).
1704
+ * When a string, a `|`-separated list of primitive types
1705
+ * (`"number"`, `"string"`, `"boolean"`, `"null"`, `"undefined"`).
1706
+ * When a function, it should throw if the value is invalid.
1668
1707
  *
1669
- * Note: Commands with the same name will be overwritten by later extensions
1670
- * (lower priority extensions override higher priority). This is intentional
1671
- * to allow customization of built-in commands.
1672
- */
1673
- private collectCommands;
1674
- /**
1675
- * Collects toolbar items from all extensions
1708
+ * @example
1709
+ * validate: 'number'
1710
+ * validate: 'string|null'
1711
+ * validate: (value) => { if (typeof value !== 'number') throw new Error('expected number'); }
1676
1712
  */
1677
- private collectToolbarItems;
1713
+ validate?: string | ((value: unknown) => void);
1678
1714
  /**
1679
- * Collects node views from all Node extensions.
1680
- * Returns a map of node name to NodeViewConstructor for EditorView.
1715
+ * Parse attribute value from HTML element
1716
+ * Called during HTML parsing to extract attribute value
1681
1717
  *
1682
- * Each constructor is annotated with `__domternalContext` containing
1683
- * the editor and extension metadata so framework wrappers (React, Vue)
1684
- * can access them without changing the ProseMirror calling convention.
1685
- */
1686
- private collectNodeViews;
1687
- /**
1688
- * Validates that the schema has required nodes
1689
- * @throws Error if schema is missing 'doc' or 'text' nodes
1690
- */
1691
- validateSchema(): void;
1692
- /**
1693
- * Cleans up the extension manager
1694
- * Calls onDestroy on all extensions and clears all caches
1718
+ * @param element - The HTML element being parsed
1719
+ * @returns The attribute value
1720
+ *
1721
+ * @example
1722
+ * parseHTML: (element) => element.getAttribute('data-level')
1695
1723
  */
1696
- destroy(): void;
1724
+ parseHTML?: (element: HTMLElement) => unknown;
1697
1725
  /**
1698
- * Safely executes a function, catching and reporting errors
1699
- * Prevents a single extension error from crashing the entire editor
1726
+ * Render attribute to HTML attributes object
1727
+ * Called during HTML serialization
1700
1728
  *
1701
- * Handles both synchronous errors and async promise rejections.
1729
+ * @param attributes - All attributes of the node/mark
1730
+ * @returns HTML attributes object or null to skip
1702
1731
  *
1703
- * @param fn - Function to execute
1704
- * @param context - Context for error reporting (e.g., 'Bold.onUpdate')
1705
- * @returns The function result, or undefined if an error occurred
1706
- */
1707
- safeCall<T>(fn: () => T, context: string): T | undefined;
1708
- /**
1709
- * Calls onBeforeCreate on all extensions
1732
+ * @example
1733
+ * renderHTML: (attributes) => ({ 'data-level': attributes.level })
1710
1734
  */
1711
- callOnBeforeCreate(): void;
1735
+ renderHTML?: (attributes: Record<string, unknown>) => Record<string, string | number | boolean | null | undefined> | null;
1736
+ }
1737
+ /**
1738
+ * Collection of attribute specifications
1739
+ * Keyed by attribute name
1740
+ */
1741
+ type AttributeSpecs = Record<string, AttributeSpec>;
1742
+
1743
+ /**
1744
+ * Node configuration types
1745
+ *
1746
+ * These types define the configuration object passed to Node.create()
1747
+ * for creating ProseMirror node extensions.
1748
+ */
1749
+
1750
+ /**
1751
+ * Editor interface for Node context
1752
+ * Includes schema with nodes for NodeType getter
1753
+ */
1754
+ interface NodeEditorContext {
1755
+ readonly state: EditorState;
1756
+ readonly view: EditorView;
1757
+ readonly schema: {
1758
+ nodes: Record<string, NodeType>;
1759
+ };
1760
+ readonly commands: Record<string, (...args: unknown[]) => boolean>;
1761
+ }
1762
+ /**
1763
+ * Context interface for Node config methods.
1764
+ * Extends ExtensionContext with node-specific properties.
1765
+ * This enables proper typing of `this.options`, `this.nodeType`, etc.
1766
+ *
1767
+ * @typeParam Options - Node options type
1768
+ * @typeParam Storage - Node storage type
1769
+ */
1770
+ interface NodeContext<Options = unknown, Storage = unknown> extends Omit<ExtensionContext<Options, Storage>, 'editor' | 'type'> {
1771
+ /** Node type identifier */
1772
+ readonly type: 'node';
1773
+ /** Editor instance with schema access */
1774
+ editor: NodeEditorContext | null;
1775
+ /** ProseMirror NodeType (null until editor is initialized) */
1776
+ readonly nodeType: NodeType | null;
1777
+ }
1778
+ /**
1779
+ * Parse rule for converting HTML to ProseMirror node
1780
+ * Simplified version of ProseMirror's ParseRule
1781
+ */
1782
+ interface NodeParseRule {
1712
1783
  /**
1713
- * Calls onCreate on all extensions
1784
+ * CSS selector or tag name to match
1785
+ * @example 'p', 'h1', 'div.my-class'
1714
1786
  */
1715
- callOnCreate(): void;
1787
+ tag?: string;
1716
1788
  /**
1717
- * Calls onUpdate on all extensions
1789
+ * Match by CSS style
1790
+ * @example 'font-weight'
1718
1791
  */
1719
- callOnUpdate(): void;
1792
+ style?: string;
1720
1793
  /**
1721
- * Calls onSelectionUpdate on all extensions
1794
+ * Priority for this rule (higher = checked first)
1795
+ * @default 50
1722
1796
  */
1723
- callOnSelectionUpdate(): void;
1797
+ priority?: number;
1724
1798
  /**
1725
- * Calls onTransaction on all extensions
1726
- * @param props - Transaction props
1799
+ * Whether to consume the matched element
1800
+ * @default true
1727
1801
  */
1728
- callOnTransaction(props: {
1729
- transaction: Transaction;
1730
- }): void;
1802
+ consuming?: boolean;
1731
1803
  /**
1732
- * Calls onFocus on all extensions
1733
- * @param props - Focus event props
1804
+ * Context required for this rule to match
1805
+ * ProseMirror content expression
1734
1806
  */
1735
- callOnFocus(props: {
1736
- event: FocusEvent;
1737
- }): void;
1807
+ context?: string;
1738
1808
  /**
1739
- * Calls onBlur on all extensions
1740
- * @param props - Blur event props
1809
+ * Get attributes from the matched element
1810
+ * Return null/undefined to skip this rule
1741
1811
  */
1742
- callOnBlur(props: {
1743
- event: FocusEvent;
1744
- }): void;
1745
- }
1746
-
1747
- /**
1748
- * Inline Styles Utility
1749
- *
1750
- * Applies inline CSS styles to serialized HTML so it renders correctly
1751
- * when pasted outside the editor (email clients, CMS, Google Docs, etc.).
1752
- *
1753
- * Uses hardcoded light-theme defaults (same approach as Google Docs, Notion,
1754
- * TinyMCE). Optionally accepts overrides for custom styling.
1755
- *
1756
- * Only structural styles are inlined (borders, padding, margins, fonts).
1757
- * Colors are NOT inlined — explicit colors (TextColor, Highlight, cell bg)
1758
- * are already inline from renderHTML, and default text color is browser default.
1759
- */
1760
- interface InlineStyleOverrides {
1761
- blockquoteBorder?: string;
1762
- blockquoteColor?: string;
1763
- tableBorder?: string;
1764
- tableHeaderBg?: string;
1765
- codeBg?: string;
1766
- codeFont?: string;
1767
- codeBorder?: string;
1768
- codeBlockBg?: string;
1769
- codeBlockFont?: string;
1770
- hrBorder?: string;
1771
- linkColor?: string;
1772
- detailsBorder?: string;
1773
- detailsBg?: string;
1812
+ getAttrs?: ((node: HTMLElement) => Record<string, unknown> | null | undefined) | null;
1774
1813
  /**
1775
- * How to export table column widths from `data-colwidth` attributes.
1776
- * - `'percent'` (default): convert to percentage widths on first-row cells
1777
- * - `'pixel'`: convert to pixel widths on first-row cells, table gets fixed width
1778
- * - `'none'`: leave `data-colwidth` as-is, no width styles applied
1814
+ * Get content from the matched element
1815
+ * Return false to use default content parsing
1779
1816
  */
1780
- tableColumnWidths?: 'percent' | 'pixel' | 'none';
1817
+ getContent?: (node: HTMLElement, schema: unknown) => unknown;
1781
1818
  /**
1782
- * Optional callback to syntax-highlight code blocks.
1783
- * Receives the raw text content and optional language, returns highlighted HTML
1784
- * with `<span class="hljs-*">` markup (or any spans with inline styles).
1785
- *
1786
- * @example
1787
- * ```ts
1788
- * import { createLowlight, common } from 'lowlight';
1789
- * import { toHtml } from 'hast-util-to-html';
1790
- * const lowlight = createLowlight(common);
1791
- *
1792
- * inlineStyles(html, {
1793
- * codeHighlighter: (code, language) => {
1794
- * if (language && lowlight.registered(language)) {
1795
- * return toHtml(lowlight.highlight(language, code));
1796
- * }
1797
- * return null; // no highlighting
1798
- * },
1799
- * });
1800
- * ```
1819
+ * How to preserve whitespace
1801
1820
  */
1802
- codeHighlighter?: (code: string, language: string | null) => string | null;
1821
+ preserveWhitespace?: boolean | 'full';
1803
1822
  }
1804
1823
  /**
1805
- * Applies inline styles to all elements in a container.
1806
- * Exported for use in clipboardSerializer (operates on DOM directly).
1807
- */
1808
- declare function applyInlineStyles(container: HTMLElement, overrides?: InlineStyleOverrides): void;
1809
- /**
1810
- * Takes an HTML string and returns it with inline CSS styles applied
1811
- * to all elements, so it renders correctly outside the editor.
1812
- *
1813
- * @param html - Serialized HTML string from editor.getHTML()
1814
- * @param overrides - Optional style overrides for custom theming
1815
- *
1816
- * @example
1817
- * ```ts
1818
- * // Default light-theme styles
1819
- * const styled = inlineStyles(editor.getHTML());
1820
- *
1821
- * // With custom overrides
1822
- * const styled = inlineStyles(editor.getHTML(), {
1823
- * blockquoteBorder: '5px solid red',
1824
- * linkColor: '#ff6600',
1825
- * });
1826
- * ```
1827
- */
1828
- declare function inlineStyles(html: string, overrides?: InlineStyleOverrides): string;
1829
-
1830
- /**
1831
- * Editor - Main editor class wrapping ProseMirror
1832
- *
1833
- * Manages extensions, schema, commands, and the ProseMirror EditorView/State.
1834
- */
1835
-
1836
- /**
1837
- * Main editor class
1838
- *
1839
- * Wraps ProseMirror's EditorView and EditorState with a cleaner API.
1840
- *
1841
- * @example
1842
- * ```ts
1843
- * import { Editor } from '@domternal/core';
1844
- * import { Schema } from '@domternal/pm/model';
1845
- *
1846
- * const schema = new Schema({
1847
- * nodes: { doc: { content: 'paragraph+' }, paragraph: { content: 'text*' }, text: {} }
1848
- * });
1849
- *
1850
- * const editor = new Editor({
1851
- * schema,
1852
- * element: document.getElementById('editor'),
1853
- * content: '<p>Hello world</p>',
1854
- * });
1855
- *
1856
- * // Get content
1857
- * const json = editor.getJSON();
1858
- * const html = editor.getHTML();
1859
- *
1860
- * // Set content
1861
- * editor.commands.setContent('<p>New content</p>');
1862
- *
1863
- * // Cleanup
1864
- * editor.destroy();
1865
- * ```
1824
+ * Props passed to renderHTML function
1866
1825
  */
1867
- declare class Editor extends EventEmitter<EditorEvents> {
1868
- /**
1869
- * Editor configuration options
1870
- */
1871
- private options;
1872
- /**
1873
- * Manages extensions and schema
1874
- * @internal Exposed for CommandManager, not for public use
1875
- */
1876
- private _extensionManager;
1826
+ interface NodeRenderHTMLProps {
1877
1827
  /**
1878
- * Gets the extension manager
1879
- * @internal For CommandManager use only
1828
+ * The ProseMirror node being rendered
1880
1829
  */
1881
- get extensionManager(): ExtensionManager;
1830
+ node: Node$1;
1882
1831
  /**
1883
- * Manages commands
1832
+ * Merged HTML attributes from all sources
1884
1833
  */
1885
- private commandManager;
1834
+ HTMLAttributes: Record<string, unknown>;
1835
+ }
1836
+ /**
1837
+ * Node-specific configuration properties (schema-related)
1838
+ */
1839
+ interface NodeSchemaProperties {
1886
1840
  /**
1887
- * ProseMirror EditorView instance
1841
+ * Node group(s) this node belongs to
1842
+ * Used in content expressions
1843
+ *
1844
+ * @example 'block', 'inline', 'block list'
1845
+ * Can be a function that returns the group string (useful for dynamic group based on options)
1888
1846
  */
1889
- view: EditorView;
1847
+ group?: string | (() => string);
1890
1848
  /**
1891
- * Whether the editor has been destroyed
1849
+ * Content expression defining allowed children
1850
+ * Uses ProseMirror content expression syntax
1851
+ *
1852
+ * @example 'inline*', 'block+', 'paragraph block*'
1892
1853
  */
1893
- private _isDestroyed;
1854
+ content?: string;
1894
1855
  /**
1895
- * Timer for autofocus (cleared on destroy to prevent memory leaks)
1856
+ * Whether this is an inline node
1857
+ * Can be a function for dynamic inline based on options
1858
+ * @default false
1896
1859
  */
1897
- private _autofocusTimer;
1860
+ inline?: boolean | (() => boolean);
1898
1861
  /**
1899
- * Creates a new Editor instance
1862
+ * Whether this node is an atom (no direct content editing)
1863
+ * Cursor moves around atoms, not into them
1900
1864
  *
1901
- * @param options - Editor configuration
1902
- * @throws Error if running in SSR environment (no window)
1903
- * @throws Error if schema is not provided
1865
+ * @example true for images, mentions, emoji
1904
1866
  */
1905
- constructor(options: EditorOptions);
1867
+ atom?: boolean;
1906
1868
  /**
1907
- * Gets the current EditorState
1869
+ * Whether the node can be selected as a whole
1870
+ * @default true for leaf nodes, false for others
1908
1871
  */
1909
- get state(): EditorState;
1872
+ selectable?: boolean;
1910
1873
  /**
1911
- * Gets the ProseMirror schema
1874
+ * Whether the node can be dragged
1875
+ * @default false
1912
1876
  */
1913
- get schema(): Schema;
1877
+ draggable?: boolean;
1914
1878
  /**
1915
- * Checks if the editor is editable
1879
+ * Whether this node represents code
1880
+ * Affects text input handling (disables smart quotes, etc.)
1916
1881
  */
1917
- get isEditable(): boolean;
1882
+ code?: boolean;
1918
1883
  /**
1919
- * Checks if the editor content is empty
1884
+ * How whitespace is handled in this node
1885
+ * - 'pre': preserve whitespace (like <pre>)
1886
+ * - 'normal': collapse whitespace (default)
1920
1887
  */
1921
- get isEmpty(): boolean;
1888
+ whitespace?: 'pre' | 'normal';
1922
1889
  /**
1923
- * Checks if the editor has focus
1890
+ * Whether this node isolates marks at its boundaries
1891
+ * Marks don't extend across isolating boundaries
1924
1892
  */
1925
- get isFocused(): boolean;
1893
+ isolating?: boolean;
1926
1894
  /**
1927
- * Checks if the editor has been destroyed
1895
+ * Whether a gap cursor is allowed inside this node.
1896
+ * Set to false to prevent gapcursor from appearing inside this node.
1897
+ * Set to true to force gapcursor even if the default heuristic disallows it.
1898
+ * When undefined, uses ProseMirror's default heuristic.
1928
1899
  */
1929
- get isDestroyed(): boolean;
1900
+ allowGapCursor?: boolean;
1930
1901
  /**
1931
- * Gets single commands for immediate execution
1932
- * @example editor.commands.focus('end')
1902
+ * Table role for prosemirror-tables integration.
1903
+ * Nodes with a tableRole are discovered by prosemirror-tables via spec.tableRole.
1904
+ * One of: 'table', 'row', 'cell', 'header_cell'
1933
1905
  */
1934
- get commands(): SingleCommands;
1906
+ tableRole?: 'table' | 'row' | 'cell' | 'header_cell';
1935
1907
  /**
1936
- * Creates a command chain for batched execution
1937
- * @example editor.chain().focus().insertText('Hello').run()
1908
+ * Whether this is a top-level node (document root)
1909
+ * Only one node should have this set to true
1938
1910
  */
1939
- chain(): ChainedCommands;
1911
+ topNode?: boolean;
1940
1912
  /**
1941
- * Checks if commands can be executed (dry-run)
1942
- * @example if (editor.can().toggleBold()) { ... }
1913
+ * Whether this node defines its own scope
1914
+ * Content and marks don't leak out of defining nodes
1943
1915
  */
1944
- can(): CanCommands;
1916
+ defining?: boolean;
1945
1917
  /**
1946
- * Gets extension storage
1947
- * Access via: editor.storage.extensionName.propertyName
1918
+ * Which marks are allowed in this node
1919
+ * Empty string means no marks allowed
1920
+ *
1921
+ * @example '', '_', 'bold italic'
1948
1922
  */
1949
- get storage(): Record<string, unknown>;
1923
+ marks?: string;
1950
1924
  /**
1951
- * Gets toolbar items registered by all extensions.
1952
- * Used by framework toolbar components to auto-generate UI.
1925
+ * Custom text for leaf nodes returned by `getText()` / `textContent`.
1926
+ *
1927
+ * @example '\n' for hard break
1953
1928
  */
1954
- get toolbarItems(): ToolbarItem[];
1929
+ leafText?: string | ((node: Node$1) => string);
1955
1930
  /**
1956
- * Checks if a node or mark is currently active
1957
- *
1958
- * For toolbar button states - returns true if:
1959
- * - For marks: the current selection has the mark applied
1960
- * - For nodes: the cursor is inside that node type
1961
- *
1962
- * @param nameOrAttributes - Extension name, or object with name and attributes
1963
- * @param attributes - Optional attributes to match (for node/mark specific states)
1931
+ * Define node attributes
1932
+ * Returns attribute specifications
1964
1933
  *
1965
1934
  * @example
1966
- * editor.isActive('bold') // → true if bold mark is active
1967
- * editor.isActive('heading', { level: 2 }) // → true if in h2
1968
- * editor.isActive({ name: 'textAlign', attributes: { align: 'center' } })
1935
+ * addAttributes() {
1936
+ * return {
1937
+ * level: { default: 1 },
1938
+ * };
1939
+ * }
1969
1940
  */
1970
- isActive(nameOrAttributes: string | {
1971
- name: string;
1972
- attributes?: Record<string, unknown>;
1973
- }, attributes?: Record<string, unknown>): boolean;
1941
+ addAttributes?: () => AttributeSpecs;
1974
1942
  /**
1975
- * Gets attributes of the currently active node or mark
1976
- *
1977
- * Returns empty object if the node/mark is not found or not active.
1943
+ * Parse rules for converting HTML to this node
1944
+ * Each rule defines how to match and parse HTML elements
1978
1945
  *
1979
- * @param name - Extension name (node or mark)
1946
+ * @example
1947
+ * parseHTML() {
1948
+ * return [
1949
+ * { tag: 'p' },
1950
+ * { tag: 'div', priority: 10 },
1951
+ * ];
1952
+ * }
1953
+ */
1954
+ parseHTML?: () => NodeParseRule[];
1955
+ /**
1956
+ * Render this node to DOM
1957
+ * Returns DOMOutputSpec (tag, attributes, children)
1980
1958
  *
1981
1959
  * @example
1982
- * editor.getAttributes('heading') // → { level: 2 }
1983
- * editor.getAttributes('link') // → { href: 'https://...', target: '_blank' }
1960
+ * renderHTML({ node, HTMLAttributes }) {
1961
+ * return ['p', HTMLAttributes, 0];
1962
+ * }
1984
1963
  */
1985
- getAttributes(name: string): Record<string, unknown>;
1964
+ renderHTML?: (props: NodeRenderHTMLProps) => DOMOutputSpec;
1986
1965
  /**
1987
- * Helper to match attributes
1988
- * Returns true if target contains all key/value pairs from source
1966
+ * Custom node view constructor
1967
+ * For complex interactive nodes
1989
1968
  */
1990
- private matchAttributes;
1969
+ addNodeView?: () => NodeViewConstructor;
1991
1970
  /**
1992
- * Gets the document content as JSON
1971
+ * Additional ProseMirror plugins for this node
1972
+ * Called during plugin collection
1993
1973
  */
1994
- getJSON(): JSONContent;
1974
+ addProseMirrorPlugins?: () => Plugin[];
1975
+ }
1976
+ /**
1977
+ * Configuration for Node extensions
1978
+ * Combines ExtensionConfig base with node-specific schema properties.
1979
+ * Uses ThisType<NodeContext> to provide proper typing for `this` in config methods.
1980
+ *
1981
+ * @typeParam Options - Node options type
1982
+ * @typeParam Storage - Node storage type
1983
+ *
1984
+ * @example
1985
+ * const Paragraph = Node.create({
1986
+ * name: 'paragraph',
1987
+ * group: 'block',
1988
+ * content: 'inline*',
1989
+ * parseHTML() {
1990
+ * // `this` is properly typed here!
1991
+ * return [{ tag: 'p' }];
1992
+ * },
1993
+ * renderHTML({ HTMLAttributes }) {
1994
+ * return ['p', HTMLAttributes, 0];
1995
+ * },
1996
+ * });
1997
+ */
1998
+ type NodeConfig<Options = unknown, Storage = unknown> = ExtensionConfigBase<Options, Storage> & NodeSchemaProperties & ThisType<NodeContext<Options, Storage>>;
1999
+
2000
+ /**
2001
+ * Mark configuration types
2002
+ *
2003
+ * These types define the configuration object passed to Mark.create()
2004
+ * for creating ProseMirror mark extensions.
2005
+ */
2006
+
2007
+ /**
2008
+ * Editor interface for Mark context
2009
+ * Includes schema with marks for MarkType getter
2010
+ */
2011
+ interface MarkEditorContext {
2012
+ readonly state: EditorState;
2013
+ readonly view: EditorView;
2014
+ readonly schema: {
2015
+ marks: Record<string, MarkType>;
2016
+ };
2017
+ readonly commands: Record<string, (...args: unknown[]) => boolean>;
2018
+ }
2019
+ /**
2020
+ * Context interface for Mark config methods.
2021
+ * Extends ExtensionContext with mark-specific properties.
2022
+ * This enables proper typing of `this.options`, `this.markType`, etc.
2023
+ *
2024
+ * @typeParam Options - Mark options type
2025
+ * @typeParam Storage - Mark storage type
2026
+ */
2027
+ interface MarkContext<Options = unknown, Storage = unknown> extends Omit<ExtensionContext<Options, Storage>, 'editor' | 'type'> {
2028
+ /** Mark type identifier */
2029
+ readonly type: 'mark';
2030
+ /** Editor instance with schema access */
2031
+ editor: MarkEditorContext | null;
2032
+ /** ProseMirror MarkType (null until editor is initialized) */
2033
+ readonly markType: MarkType | null;
2034
+ }
2035
+ /**
2036
+ * Parse rule for converting HTML to ProseMirror mark
2037
+ * Simplified version of ProseMirror's ParseRule for marks
2038
+ */
2039
+ interface MarkParseRule {
1995
2040
  /**
1996
- * Gets the document content as HTML string
1997
- *
1998
- * @param options - Optional settings
1999
- * @param options.styled - When true (or an override object), applies inline CSS
2000
- * styles so the HTML renders correctly outside the editor (email, CMS, etc.)
2041
+ * CSS selector or tag name to match
2042
+ * @example 'strong', 'b', 'span.bold'
2001
2043
  */
2002
- getHTML(options?: {
2003
- styled?: boolean | InlineStyleOverrides;
2004
- }): string;
2044
+ tag?: string;
2005
2045
  /**
2006
- * Gets the document content as plain text
2007
- *
2008
- * @param options - Options for text extraction
2009
- * @param options.blockSeparator - String to insert between blocks (default: '\n\n')
2046
+ * Match by CSS style property
2047
+ * Can include expected value after '='
2048
+ * @example 'font-weight', 'font-weight=bold'
2010
2049
  */
2011
- getText(options?: {
2012
- blockSeparator?: string;
2013
- }): string;
2050
+ style?: string;
2014
2051
  /**
2015
- * Executes a command with proper CommandProps
2016
- * @internal
2052
+ * Priority for this rule (higher = checked first)
2053
+ * @default 50
2017
2054
  */
2018
- private runCommand;
2055
+ priority?: number;
2019
2056
  /**
2020
- * Sets the editor content
2021
- *
2022
- * @param content - JSON or HTML content
2023
- * @param emitUpdate - Whether to emit update event (default: true)
2024
- * @returns true if content was set successfully, false if content was invalid
2057
+ * Whether to consume the matched element
2058
+ * @default true
2025
2059
  */
2026
- setContent(content: Content, emitUpdate?: boolean): boolean;
2060
+ consuming?: boolean;
2027
2061
  /**
2028
- * Clears the editor content
2062
+ * Get attributes from the matched element
2063
+ * Return null/undefined to skip this rule
2064
+ * Return false to explicitly not match
2029
2065
  *
2030
- * @param emitUpdate - Whether to emit update event (default: true)
2031
- * @returns true if content was cleared successfully
2066
+ * For style rules, receives the style value as string
2032
2067
  */
2033
- clearContent(emitUpdate?: boolean): boolean;
2068
+ getAttrs?: ((node: HTMLElement | string) => Record<string, unknown> | false | null) | null;
2069
+ }
2070
+ /**
2071
+ * Props passed to renderHTML function for marks
2072
+ */
2073
+ interface MarkRenderHTMLProps {
2034
2074
  /**
2035
- * Sets whether the editor is editable
2036
- *
2037
- * @param editable - Whether the editor should be editable
2075
+ * The ProseMirror mark being rendered
2038
2076
  */
2039
- setEditable(editable: boolean): this;
2077
+ mark: Mark$1;
2040
2078
  /**
2041
- * Focuses the editor
2042
- *
2043
- * @param position - Where to place cursor (default: null = just focus)
2079
+ * Merged HTML attributes from all sources
2044
2080
  */
2045
- focus(position?: FocusPosition): this;
2081
+ HTMLAttributes: Record<string, unknown>;
2082
+ }
2083
+ /**
2084
+ * Mark-specific configuration properties (schema-related)
2085
+ */
2086
+ interface MarkSchemaProperties {
2046
2087
  /**
2047
- * Removes focus from the editor
2088
+ * Whether this mark should be active when the cursor
2089
+ * is at its end (or beginning for marks that open).
2090
+ *
2091
+ * When true, typing at the mark's boundary continues the mark.
2092
+ * When false, typing creates unmarked content.
2093
+ *
2094
+ * @default true
2048
2095
  */
2049
- blur(): this;
2096
+ inclusive?: boolean | (() => boolean);
2050
2097
  /**
2051
- * Registers a ProseMirror plugin dynamically at runtime.
2052
- * Used by framework wrappers (e.g. Angular BubbleMenu component) to add
2053
- * plugins after the editor is created.
2098
+ * Marks that this mark excludes (cannot coexist with)
2099
+ *
2100
+ * - '_' excludes all marks
2101
+ * - Space-separated mark names exclude specific marks
2102
+ * - Empty string or undefined means no exclusions
2103
+ *
2104
+ * @example 'code' - excludes code mark
2105
+ * @example 'bold italic' - excludes bold and italic
2106
+ * @example '_' - excludes all other marks
2054
2107
  */
2055
- registerPlugin(plugin: Plugin): void;
2108
+ excludes?: string;
2056
2109
  /**
2057
- * Unregisters a ProseMirror plugin by its PluginKey.
2058
- * Uses PluginKey.get() to identify the plugin to remove.
2110
+ * Mark group(s) this mark belongs to
2111
+ * Used in node's marks property
2112
+ *
2113
+ * @example 'formatting', 'inline'
2059
2114
  */
2060
- unregisterPlugin(key: PluginKey): void;
2115
+ group?: string;
2061
2116
  /**
2062
- * Destroys the editor and cleans up resources
2117
+ * Whether this mark can span multiple nodes
2063
2118
  *
2064
- * After calling destroy(), the editor instance should not be used.
2119
+ * When true (default), the mark persists across inline nodes.
2120
+ * When false, the mark only applies within a single text node.
2121
+ *
2122
+ * @default true
2065
2123
  */
2066
- destroy(): void;
2124
+ spanning?: boolean;
2067
2125
  /**
2068
- * Builds a clipboardSerializer that applies a transform function to HTML on copy/cut.
2126
+ * Whether this mark represents visual formatting.
2127
+ *
2128
+ * Used by `unsetAllMarks` to decide which marks to remove.
2129
+ * Marks with `isFormatting: false` survive clear formatting
2130
+ * (e.g., links, comments, annotations).
2131
+ *
2132
+ * Can be overridden via `.configure()`:
2133
+ * ```ts
2134
+ * Link.configure({ isFormatting: true }) // make links clearable
2135
+ * ```
2136
+ *
2137
+ * @default true
2069
2138
  */
2070
- private buildClipboardSerializer;
2139
+ isFormatting?: boolean;
2071
2140
  /**
2072
- * Creates the editor instance
2141
+ * Define mark attributes
2142
+ * Returns attribute specifications
2143
+ *
2144
+ * @example
2145
+ * addAttributes() {
2146
+ * return {
2147
+ * color: { default: null },
2148
+ * };
2149
+ * }
2073
2150
  */
2074
- private createEditor;
2151
+ addAttributes?: () => AttributeSpecs;
2075
2152
  /**
2076
- * Handles ProseMirror transactions
2153
+ * Parse rules for converting HTML to this mark
2154
+ * Each rule defines how to match and parse HTML elements
2155
+ *
2156
+ * @example
2157
+ * parseHTML() {
2158
+ * return [
2159
+ * { tag: 'strong' },
2160
+ * { tag: 'b' },
2161
+ * { style: 'font-weight', getAttrs: (value) => /bold|[5-9]\d{2}/.test(value) && null },
2162
+ * ];
2163
+ * }
2077
2164
  */
2078
- private dispatchTransaction;
2165
+ parseHTML?: () => MarkParseRule[];
2079
2166
  /**
2080
- * Emit method - needed for CommandManager interface
2167
+ * Render this mark to DOM
2168
+ * Returns DOMOutputSpec (tag, attributes, hole)
2169
+ *
2170
+ * The 0 in the return array indicates where child content goes.
2171
+ *
2172
+ * @example
2173
+ * renderHTML({ mark, HTMLAttributes }) {
2174
+ * return ['strong', HTMLAttributes, 0];
2175
+ * }
2081
2176
  */
2082
- emit<E extends keyof EditorEvents>(event: E, ...args: EditorEvents[E] extends undefined ? [] : [EditorEvents[E]]): this;
2177
+ renderHTML?: (props: MarkRenderHTMLProps) => DOMOutputSpec;
2083
2178
  }
2179
+ /**
2180
+ * Configuration for Mark extensions
2181
+ * Combines ExtensionConfigBase with mark-specific schema properties.
2182
+ * Uses ThisType<MarkContext> to provide proper typing for `this` in config methods.
2183
+ *
2184
+ * @typeParam Options - Mark options type
2185
+ * @typeParam Storage - Mark storage type
2186
+ *
2187
+ * @example
2188
+ * const Bold = Mark.create({
2189
+ * name: 'bold',
2190
+ * parseHTML() {
2191
+ * // `this` is properly typed here!
2192
+ * return [{ tag: 'strong' }, { tag: 'b' }];
2193
+ * },
2194
+ * renderHTML({ HTMLAttributes }) {
2195
+ * return ['strong', HTMLAttributes, 0];
2196
+ * },
2197
+ * });
2198
+ */
2199
+ type MarkConfig<Options = unknown, Storage = unknown> = ExtensionConfigBase<Options, Storage> & MarkSchemaProperties & ThisType<MarkContext<Options, Storage>>;
2084
2200
 
2085
2201
  /**
2086
2202
  * Focus command - focuses the editor at the specified position
@@ -2092,30 +2208,30 @@ declare class Editor extends EventEmitter<EditorEvents> {
2092
2208
  * - number: Specific position
2093
2209
  * - null/false: Just focus without changing selection
2094
2210
  */
2095
- declare const focus: CommandSpec$1<[position?: FocusPosition]>;
2211
+ declare const focus: CommandSpec<[position?: FocusPosition]>;
2096
2212
  /**
2097
2213
  * Blur command - removes focus from the editor
2098
2214
  */
2099
- declare const blur: CommandSpec$1;
2215
+ declare const blur: CommandSpec;
2100
2216
  /**
2101
2217
  * SelectAll command - selects all content in the editor
2102
2218
  *
2103
2219
  * Uses AllSelection to select the entire document.
2104
2220
  * Uses tr.doc to support chain context.
2105
2221
  */
2106
- declare const selectAll: CommandSpec$1;
2222
+ declare const selectAll: CommandSpec;
2107
2223
  /**
2108
2224
  * SelectNodeBackward command - selects the node before the cursor
2109
2225
  *
2110
2226
  * When the cursor is at the start of a textblock, this selects the node before it.
2111
2227
  */
2112
- declare const selectNodeBackward: CommandSpec$1;
2228
+ declare const selectNodeBackward: CommandSpec;
2113
2229
  /**
2114
2230
  * DeleteSelection command - deletes the current selection
2115
2231
  *
2116
2232
  * Uses tr.selection to support chain context.
2117
2233
  */
2118
- declare const deleteSelection: CommandSpec$1;
2234
+ declare const deleteSelection: CommandSpec;
2119
2235
 
2120
2236
  /**
2121
2237
  * Options for setContent command
@@ -2147,28 +2263,28 @@ interface ClearContentOptions {
2147
2263
  * @param content - JSON or HTML content
2148
2264
  * @param options - Options for setting content
2149
2265
  */
2150
- declare const setContent: CommandSpec$1<[content: Content, options?: SetContentOptions]>;
2266
+ declare const setContent: CommandSpec<[content: Content, options?: SetContentOptions]>;
2151
2267
  /**
2152
2268
  * ClearContent command - clears the editor content to empty state
2153
2269
  *
2154
2270
  * @param options - Options for clearing content
2155
2271
  */
2156
- declare const clearContent: CommandSpec$1<[options?: ClearContentOptions]>;
2272
+ declare const clearContent: CommandSpec<[options?: ClearContentOptions]>;
2157
2273
  /**
2158
2274
  * InsertText command - inserts text at the current selection
2159
2275
  *
2160
2276
  * @param text - Text to insert
2161
2277
  */
2162
- declare const insertText: CommandSpec$1<[text: string]>;
2278
+ declare const insertText: CommandSpec<[text: string]>;
2163
2279
  /**
2164
2280
  * InsertContent command - inserts content at the current selection
2165
2281
  *
2166
2282
  * @param content - The content to insert (JSON object, array, or HTML string)
2167
2283
  */
2168
- declare const insertContent: CommandSpec$1<[content: Content]>;
2284
+ declare const insertContent: CommandSpec<[content: Content]>;
2169
2285
 
2170
2286
  /**
2171
- * Mark commands toggleMark, setMark, unsetMark, unsetAllMarks
2287
+ * Mark commands - toggleMark, setMark, unsetMark, unsetAllMarks
2172
2288
  */
2173
2289
 
2174
2290
  /**
@@ -2181,20 +2297,20 @@ declare const insertContent: CommandSpec$1<[content: Content]>;
2181
2297
  * @param markName - The name of the mark to toggle
2182
2298
  * @param attributes - Optional attributes for the mark
2183
2299
  */
2184
- declare const toggleMark: CommandSpec$1<[markName: string, attributes?: Attrs]>;
2300
+ declare const toggleMark: CommandSpec<[markName: string, attributes?: Attrs]>;
2185
2301
  /**
2186
2302
  * SetMark command - adds a mark to the current selection
2187
2303
  *
2188
2304
  * @param markName - The name of the mark to set
2189
2305
  * @param attributes - Optional attributes for the mark
2190
2306
  */
2191
- declare const setMark: CommandSpec$1<[markName: string, attributes?: Attrs]>;
2307
+ declare const setMark: CommandSpec<[markName: string, attributes?: Attrs]>;
2192
2308
  /**
2193
2309
  * UnsetMark command - removes a mark from the current selection
2194
2310
  *
2195
2311
  * @param markName - The name of the mark to remove
2196
2312
  */
2197
- declare const unsetMark: CommandSpec$1<[markName: string]>;
2313
+ declare const unsetMark: CommandSpec<[markName: string]>;
2198
2314
  /**
2199
2315
  * UnsetAllMarks command - removes all formatting marks from the current selection
2200
2316
  *
@@ -2204,7 +2320,7 @@ declare const unsetMark: CommandSpec$1<[markName: string]>;
2204
2320
  *
2205
2321
  * Returns false for empty selections (no range to clear).
2206
2322
  */
2207
- declare const unsetAllMarks: CommandSpec$1;
2323
+ declare const unsetAllMarks: CommandSpec;
2208
2324
 
2209
2325
  /**
2210
2326
  * SetBlockType command - changes the block type of the selection
@@ -2216,7 +2332,7 @@ declare const unsetAllMarks: CommandSpec$1;
2216
2332
  * @param nodeName - The name of the node type to set
2217
2333
  * @param attributes - Optional attributes for the node
2218
2334
  */
2219
- declare const setBlockType: CommandSpec$1<[nodeName: string, attributes?: Attrs]>;
2335
+ declare const setBlockType: CommandSpec<[nodeName: string, attributes?: Attrs]>;
2220
2336
  /**
2221
2337
  * ToggleBlockType command - toggles between a block type and a default type
2222
2338
  *
@@ -2228,7 +2344,7 @@ declare const setBlockType: CommandSpec$1<[nodeName: string, attributes?: Attrs]
2228
2344
  * @param defaultNodeName - The name of the default node type (usually 'paragraph')
2229
2345
  * @param attributes - Optional attributes for the node
2230
2346
  */
2231
- declare const toggleBlockType: CommandSpec$1<[nodeName: string, defaultNodeName: string, attributes?: Attrs]>;
2347
+ declare const toggleBlockType: CommandSpec<[nodeName: string, defaultNodeName: string, attributes?: Attrs]>;
2232
2348
  /**
2233
2349
  * WrapIn command - wraps the selection in a node type
2234
2350
  *
@@ -2237,7 +2353,7 @@ declare const toggleBlockType: CommandSpec$1<[nodeName: string, defaultNodeName:
2237
2353
  * @param nodeName - The name of the wrapping node type
2238
2354
  * @param attributes - Optional attributes for the node
2239
2355
  */
2240
- declare const wrapIn: CommandSpec$1<[nodeName: string, attributes?: Attrs]>;
2356
+ declare const wrapIn: CommandSpec<[nodeName: string, attributes?: Attrs]>;
2241
2357
  /**
2242
2358
  * ToggleWrap command - toggles wrapping of the selection in a node type
2243
2359
  *
@@ -2248,14 +2364,14 @@ declare const wrapIn: CommandSpec$1<[nodeName: string, attributes?: Attrs]>;
2248
2364
  * @param nodeName - The name of the wrapping node type
2249
2365
  * @param attributes - Optional attributes for the node
2250
2366
  */
2251
- declare const toggleWrap: CommandSpec$1<[nodeName: string, attributes?: Attrs]>;
2367
+ declare const toggleWrap: CommandSpec<[nodeName: string, attributes?: Attrs]>;
2252
2368
  /**
2253
2369
  * Lift command - lifts the current block out of its parent wrapper
2254
2370
  *
2255
2371
  * Uses tr.doc/tr.selection for chain compatibility.
2256
2372
  * For example, lifts a paragraph out of a blockquote.
2257
2373
  */
2258
- declare const lift: CommandSpec$1;
2374
+ declare const lift: CommandSpec;
2259
2375
 
2260
2376
  /**
2261
2377
  * ToggleList command - toggles a list type on the current selection
@@ -2268,10 +2384,10 @@ declare const lift: CommandSpec$1;
2268
2384
  * @param listItemNodeName - The name of the list item node type (usually 'listItem')
2269
2385
  * @param attributes - Optional attributes for the list node
2270
2386
  */
2271
- declare const toggleList: CommandSpec$1<[listNodeName: string, listItemNodeName: string, attributes?: Attrs]>;
2387
+ declare const toggleList: CommandSpec<[listNodeName: string, listItemNodeName: string, attributes?: Attrs]>;
2272
2388
 
2273
2389
  /**
2274
- * Attribute commands updateAttributes, resetAttributes
2390
+ * Attribute commands - updateAttributes, resetAttributes
2275
2391
  */
2276
2392
 
2277
2393
  /**
@@ -2282,7 +2398,7 @@ declare const toggleList: CommandSpec$1<[listNodeName: string, listItemNodeName:
2282
2398
  * @param typeOrName - The node type name or NodeType to update
2283
2399
  * @param attributes - The attributes to merge into existing attributes
2284
2400
  */
2285
- declare const updateAttributes: CommandSpec$1<[typeOrName: string, attributes: Record<string, unknown>]>;
2401
+ declare const updateAttributes: CommandSpec<[typeOrName: string, attributes: Record<string, unknown>]>;
2286
2402
  /**
2287
2403
  * ResetAttributes command - resets an attribute to its default value
2288
2404
  *
@@ -2292,7 +2408,7 @@ declare const updateAttributes: CommandSpec$1<[typeOrName: string, attributes: R
2292
2408
  * @param typeOrName - The node type name to update
2293
2409
  * @param attributeName - The name of the attribute to reset
2294
2410
  */
2295
- declare const resetAttributes: CommandSpec$1<[typeOrName: string, attributeName: string]>;
2411
+ declare const resetAttributes: CommandSpec<[typeOrName: string, attributeName: string]>;
2296
2412
 
2297
2413
  /**
2298
2414
  * Built-in commands for @domternal/core
@@ -2370,8 +2486,8 @@ declare class CommandManager {
2370
2486
  */
2371
2487
  private buildCommandProps;
2372
2488
  /**
2373
- * Single commands that execute immediately
2374
- * Uses Proxy to dynamically generate command methods (ID-1)
2489
+ * Single commands that execute immediately.
2490
+ * Uses a Proxy to dynamically generate command methods.
2375
2491
  *
2376
2492
  * @example
2377
2493
  * editor.commands.focus('end');
@@ -2430,61 +2546,252 @@ interface PositionFloatingOptions {
2430
2546
  trackScroll?: boolean;
2431
2547
  }
2432
2548
  /**
2433
- * Positions a floating element relative to a reference element or virtual rect,
2434
- * and keeps it positioned on scroll, resize, and layout shifts.
2435
- *
2436
- * Uses `autoUpdate` from floating-ui with `animationFrame` polling for
2437
- * jitter-free scroll tracking (rAF syncs with browser paint).
2438
- *
2439
- * Includes `hide` middleware when the reference element is scrolled out of
2440
- * view, the floating element is hidden via `visibility: hidden`.
2441
- *
2442
- * The floating element must have `position: fixed`.
2443
- *
2444
- * Returns a cleanup function. **Always call it** when hiding or destroying
2445
- * the floating element to stop listeners and prevent memory leaks.
2446
- *
2447
- * @example
2448
- * ```ts
2449
- * // Start auto-positioning (follows scroll/resize)
2450
- * const cleanup = positionFloating(buttonEl, dropdownEl, {
2451
- * placement: 'bottom-start',
2452
- * });
2453
- *
2454
- * // Virtual reference (e.g. cursor position must return fresh coords)
2455
- * const virtualEl = {
2456
- * getBoundingClientRect: () => {
2457
- * const coords = view.coordsAtPos(pos);
2458
- * return new DOMRect(coords.left, coords.top, 0, coords.bottom - coords.top);
2459
- * },
2460
- * };
2461
- * const cleanup = positionFloating(virtualEl, tooltipEl, { placement: 'top' });
2549
+ * Positions a floating element relative to a reference element or virtual rect,
2550
+ * and keeps it positioned on scroll, resize, and layout shifts.
2551
+ *
2552
+ * Uses `autoUpdate` from floating-ui with `animationFrame` polling for
2553
+ * jitter-free scroll tracking (rAF syncs with browser paint).
2554
+ *
2555
+ * Includes `hide` middleware - when the reference element is scrolled out of
2556
+ * view, the floating element is hidden via `visibility: hidden`.
2557
+ *
2558
+ * The floating element must have `position: fixed`.
2559
+ *
2560
+ * Returns a cleanup function. **Always call it** when hiding or destroying
2561
+ * the floating element to stop listeners and prevent memory leaks.
2562
+ *
2563
+ * @example
2564
+ * ```ts
2565
+ * // Start auto-positioning (follows scroll/resize)
2566
+ * const cleanup = positionFloating(buttonEl, dropdownEl, {
2567
+ * placement: 'bottom-start',
2568
+ * });
2569
+ *
2570
+ * // Virtual reference (e.g. cursor position - must return fresh coords)
2571
+ * const virtualEl = {
2572
+ * getBoundingClientRect: () => {
2573
+ * const coords = view.coordsAtPos(pos);
2574
+ * return new DOMRect(coords.left, coords.top, 0, coords.bottom - coords.top);
2575
+ * },
2576
+ * };
2577
+ * const cleanup = positionFloating(virtualEl, tooltipEl, { placement: 'top' });
2578
+ *
2579
+ * // Stop when done
2580
+ * cleanup();
2581
+ * ```
2582
+ */
2583
+ declare function positionFloating(reference: Element | {
2584
+ getBoundingClientRect: () => DOMRect;
2585
+ }, floating: HTMLElement, options?: PositionFloatingOptions): () => void;
2586
+ /**
2587
+ * Positions a floating element using `strategy: 'absolute'` so it scrolls
2588
+ * together with its offsetParent - zero jitter by design.
2589
+ *
2590
+ * Ideal for dropdowns inside scroll containers (e.g. emoji suggestion inside
2591
+ * `.dm-editor`) and toolbar dropdowns. The absolute coordinates are stable
2592
+ * across scrolls - only `flip`/`shift` decisions change on scroll, producing
2593
+ * a discrete jump rather than continuous jitter.
2594
+ *
2595
+ * The floating element must have `position: absolute` and its offsetParent
2596
+ * must have `position: relative`.
2597
+ *
2598
+ * Returns a cleanup function - call it when hiding or destroying the
2599
+ * floating element.
2600
+ */
2601
+ declare function positionFloatingOnce(reference: Element | {
2602
+ getBoundingClientRect: () => DOMRect;
2603
+ }, floating: HTMLElement, options?: PositionFloatingOptions): () => void;
2604
+
2605
+ /**
2606
+ * Clipboard helper - writes plain text to the system clipboard.
2607
+ *
2608
+ * Tries the modern async Clipboard API first; if unavailable or denied
2609
+ * (Safari private mode, insecure context, missing user gesture), falls
2610
+ * back to a hidden textarea + `document.execCommand('copy')`.
2611
+ *
2612
+ * Returns `true` on success, `false` on failure. Never throws - callers
2613
+ * decide how to surface failure (toast, re-throw, silent).
2614
+ *
2615
+ * @example
2616
+ * ```ts
2617
+ * const ok = await writeToClipboard('https://example.com#block-abc123');
2618
+ * if (!ok) showError('Copy failed');
2619
+ * ```
2620
+ */
2621
+ declare function writeToClipboard(text: string): Promise<boolean>;
2622
+
2623
+ /**
2624
+ * Copies `dm-theme-*` classes from the editor's host element onto a
2625
+ * floating element that lives OUTSIDE `.dm-editor` (typically portaled
2626
+ * to `document.body` to escape `overflow: hidden`). Without the copy
2627
+ * the `.dm-theme-dark` cascade does not reach the floating element and
2628
+ * it renders with light-theme defaults on a dark page.
2629
+ *
2630
+ * Call this whenever the floating element is shown - cheap and
2631
+ * idempotent. Removes stale theme classes from the target first so
2632
+ * runtime theme toggles propagate on the next show.
2633
+ */
2634
+ declare function copyThemeClass(view: {
2635
+ dom: Element;
2636
+ }, target: Element): void;
2637
+
2638
+ /**
2639
+ * Default `contexts` map for a bubble menu when the consumer has not
2640
+ * supplied one. Returns a richer item set when the editor (or any
2641
+ * ancestor) carries the `.dm-notion-mode` class - that class is the
2642
+ * project-wide signal that the host is rendering Notion-style UX, so
2643
+ * the bubble menu mirrors it by including `link` and `textAlign`.
2644
+ *
2645
+ * Consumers can always override by passing their own `contexts` prop.
2646
+ */
2647
+ declare function defaultBubbleContexts(editor: Editor): Record<string, string[]>;
2648
+
2649
+ interface InsertAsListItemChildArgs {
2650
+ /** Existing transaction to mutate. Caller dispatches. */
2651
+ tr: Transaction;
2652
+ /** Position of the list wrapper node (bulletList / orderedList / taskList) in `tr.doc`. */
2653
+ wrapperPos: number;
2654
+ /**
2655
+ * Position of the target list item node (= position right BEFORE the
2656
+ * item) in `tr.doc`. When omitted, the wrapper's LAST child is used,
2657
+ * matching the Tab keyboard behaviour ("indent into last item").
2658
+ */
2659
+ targetItemPos?: number;
2660
+ /** Block node to append as the LAST child of the target item. */
2661
+ blockNode: Node$1;
2662
+ /**
2663
+ * Optional source range to delete in the SAME transaction (when
2664
+ * MOVING an existing block instead of creating a new one). Position
2665
+ * math handles source-before-vs-after-target ordering automatically.
2666
+ */
2667
+ sourceRange?: {
2668
+ from: number;
2669
+ to: number;
2670
+ };
2671
+ }
2672
+ interface InsertAsListItemChildResult {
2673
+ /** True when schema accepted the insertion and `tr` was mutated. */
2674
+ ok: boolean;
2675
+ /**
2676
+ * Position right BEFORE the inserted block in the resulting doc.
2677
+ * Caller can resolve `insertedAt + 1` to place the caret inside the
2678
+ * inserted block. Only present when `ok === true`.
2679
+ */
2680
+ insertedAt?: number;
2681
+ }
2682
+ /**
2683
+ * Insert `blockNode` as the LAST child of a list item (target item or, when
2684
+ * omitted, the wrapper's last item). When `sourceRange` is set, the source
2685
+ * range is removed in the same transaction so the op is a clean MOVE.
2686
+ * Returns `{ ok: false }` WITHOUT mutating `tr` on schema reject so callers
2687
+ * can fall through to a sibling-mode fallback.
2688
+ */
2689
+ declare function insertAsListItemChild(args: InsertAsListItemChildArgs): InsertAsListItemChildResult;
2690
+
2691
+ /**
2692
+ * Cursor context relative to the INNERMOST containing list/task item,
2693
+ * resolved only when the cursor's parent paragraph is a DIRECT child of
2694
+ * that item. Innermost so nested lists hand control to the deepest item;
2695
+ * direct-child so paragraphs inside blockquote/table-cell/etc. inside a
2696
+ * list item fall through to the container's own Enter/Backspace.
2697
+ */
2698
+ interface ListItemCursorContext {
2699
+ /** Depth of the containing list item ancestor in the resolved pos. */
2700
+ itemDepth: number;
2701
+ /** Position right BEFORE the containing list item in the doc. */
2702
+ itemPos: number;
2703
+ /** Position right BEFORE the containing list wrapper (one depth above the item). */
2704
+ wrapperPos: number;
2705
+ /** `true` when the parent paragraph is not the first child of the list item. */
2706
+ isInChildrenZone: boolean;
2707
+ /** `true` when the parent paragraph has no inline content. */
2708
+ paragraphIsEmpty: boolean;
2709
+ /** `true` when the parent paragraph is the LAST child of the list item. */
2710
+ isLastChild: boolean;
2711
+ /** Index of the parent paragraph within the list item (0 = label). */
2712
+ childIndex: number;
2713
+ /** Index of the containing list item within its wrapper. */
2714
+ itemIndexInWrapper: number;
2715
+ }
2716
+ /**
2717
+ * Resolve list-item cursor context. Returns `null` when the cursor's
2718
+ * shape does not match `(wrapper > item > paragraph)`; callers should
2719
+ * fall through to default Enter/Backspace in that case.
2720
+ */
2721
+ declare function getListItemCursorContext($from: ResolvedPos): ListItemCursorContext | null;
2722
+
2723
+ /**
2724
+ * Lift an empty children-zone paragraph out of its list/task item as a
2725
+ * top-level sibling. PM's `liftTarget` covers the common case; we fall
2726
+ * back to manual delete-and-insert when it returns null (empty p sandwiched
2727
+ * before a non-paragraph would leave the after-cut half violating the
2728
+ * `paragraph block*` schema).
2729
+ */
2730
+ declare function liftEmptyChildrenZoneParagraph(state: EditorState, dispatch: ((tr: Transaction) => void) | undefined, ctx: ListItemCursorContext): boolean;
2731
+
2732
+ /**
2733
+ * Insert an empty paragraph as next sibling inside the same list/task item,
2734
+ * keeping the cursor in the children-zone so Enter accumulates blocks at the
2735
+ * same nesting level (Notion semantics). Exit paths are Backspace on empty,
2736
+ * Shift+Tab, or Enter on the empty label paragraph.
2737
+ */
2738
+ declare function insertChildrenZoneSibling(state: EditorState, dispatch: ((tr: Transaction) => void) | undefined, ctx: ListItemCursorContext): boolean;
2739
+
2740
+ /**
2741
+ * Activation conditions (single-cursor only):
2742
+ * - selection is empty (single caret)
2743
+ * - cursor's nearest list-item ancestor exists
2744
+ * - cursor's containing block is at index 0 (label slot) of that item
2745
+ * - `tr` has no prior steps (lift steps captured from a fresh state must
2746
+ * replay against the same starting state as `tr`)
2747
+ *
2748
+ * Returns true if the lift was applied to `tr`. The caller can keep
2749
+ * chaining work on `tr` (e.g. `setBlockType`, `wrapIn`) on the new
2750
+ * top-level paragraph.
2751
+ */
2752
+ declare function liftCurrentListItem(state: EditorState, tr: Transaction): boolean;
2753
+
2754
+ interface SplitListForInsertRange {
2755
+ from: number;
2756
+ to: number;
2757
+ }
2758
+ /**
2759
+ * Activation conditions (single-cursor only):
2760
+ * - selection is empty
2761
+ * - cursor's nearest list-item ancestor exists
2762
+ * - cursor's containing block is at index 0 (label slot) of that item
2763
+ * - `tr` has no prior steps
2462
2764
  *
2463
- * // Stop when done
2464
- * cleanup();
2465
- * ```
2765
+ * @returns A range where the caller should call `tr.replaceWith(from, to,
2766
+ * [block, optionalTrailingParagraph])`, or `null` if the cursor isn't
2767
+ * in a list-item label.
2466
2768
  */
2467
- declare function positionFloating(reference: Element | {
2468
- getBoundingClientRect: () => DOMRect;
2469
- }, floating: HTMLElement, options?: PositionFloatingOptions): () => void;
2769
+ declare function splitListForInsert(state: EditorState, tr: Transaction): SplitListForInsertRange | null;
2770
+
2470
2771
  /**
2471
- * Positions a floating element using `strategy: 'absolute'` so it scrolls
2472
- * together with its offsetParent zero jitter by design.
2473
- *
2474
- * Ideal for dropdowns inside scroll containers (e.g. emoji suggestion inside
2475
- * `.dm-editor`) and toolbar dropdowns. The absolute coordinates are stable
2476
- * across scrolls — only `flip`/`shift` decisions change on scroll, producing
2477
- * a discrete jump rather than continuous jitter.
2478
- *
2479
- * The floating element must have `position: absolute` and its offsetParent
2480
- * must have `position: relative`.
2772
+ * Shared helpers for locating a list/task item ancestor of a resolved
2773
+ * position. Centralises the `LIST_ITEM_TYPE_NAMES` constant + ancestor
2774
+ * walk so the four+ consumers (liftCurrentListItem, splitListForInsert,
2775
+ * TaskItem keymap, Details proactive lift, slash menu filtering) stay
2776
+ * in sync.
2777
+ */
2778
+
2779
+ declare const LIST_ITEM_TYPE_NAMES: Set<string>;
2780
+ /**
2781
+ * Returns the depth at which the nearest list/task item ancestor sits,
2782
+ * or -1 if none.
2481
2783
  *
2482
- * Returns a cleanup function call it when hiding or destroying the
2483
- * floating element.
2784
+ * Walks UP from `$pos.depth` toward 1 (we skip depth 0 = doc itself).
2484
2785
  */
2485
- declare function positionFloatingOnce(reference: Element | {
2486
- getBoundingClientRect: () => DOMRect;
2487
- }, floating: HTMLElement, options?: PositionFloatingOptions): () => void;
2786
+ declare function findListItemAncestorDepth($pos: ResolvedPos): number;
2787
+ /** Convenience: true when the cursor has a list/task item ancestor. */
2788
+ declare function isInsideListItem($pos: ResolvedPos): boolean;
2789
+ /**
2790
+ * True when the cursor sits in the LABEL paragraph (index 0) of its
2791
+ * nearest list/task item ancestor. Used to gate Notion-style "operate
2792
+ * on the label" commands (slash menu CONVERT, INSERT, etc.).
2793
+ */
2794
+ declare function isInListItemLabel($pos: ResolvedPos): boolean;
2488
2795
 
2489
2796
  /**
2490
2797
  * Create a ProseMirror document from content
@@ -3160,7 +3467,7 @@ declare class Extension<Options = unknown, Storage = unknown> {
3160
3467
  * // Shallow merge behavior with nested objects:
3161
3468
  * // Given: options = { nested: { a: 1, b: 2 } }
3162
3469
  * // configure({ nested: { b: 3 } })
3163
- * // Result: { nested: { b: 3 } } 'a' is lost!
3470
+ * // Result: { nested: { b: 3 } } - 'a' is lost!
3164
3471
  * // To preserve nested values, spread manually:
3165
3472
  * // configure({ nested: { ...original.options.nested, b: 3 } })
3166
3473
  */
@@ -3289,25 +3596,12 @@ declare class Node<Options = unknown, Storage = unknown> extends Extension<Optio
3289
3596
  */
3290
3597
  static create<O = unknown, S = unknown>(config: NodeConfig<O, S>): Node<O, S>;
3291
3598
  /**
3292
- * Creates a new node with merged options
3293
- * Original node is not modified
3294
- *
3295
- * **Note:** Options are merged shallowly using object spread (`...`).
3296
- * Nested objects are replaced entirely, not deeply merged.
3297
- *
3298
- * @param options - Options to merge with existing options
3299
- * @returns New node instance with merged options
3599
+ * Creates a new node with merged options. Original node is not modified.
3600
+ * Options merge shallowly (object spread); see {@link Extension.configure}
3601
+ * for the nested-object gotcha and a workaround.
3300
3602
  *
3301
3603
  * @example
3302
3604
  * const CustomParagraph = Paragraph.configure({ HTMLAttributes: { class: 'custom' } });
3303
- *
3304
- * @example
3305
- * // Shallow merge behavior with nested objects:
3306
- * // Given: options = { HTMLAttributes: { class: 'a', id: 'b' } }
3307
- * // configure({ HTMLAttributes: { class: 'c' } })
3308
- * // Result: { HTMLAttributes: { class: 'c' } } — 'id' is lost!
3309
- * // To preserve nested values, spread manually:
3310
- * // configure({ HTMLAttributes: { ...original.options.HTMLAttributes, class: 'c' } })
3311
3605
  */
3312
3606
  configure(options: Partial<Options>): Node<Options, Storage>;
3313
3607
  /**
@@ -3351,7 +3645,7 @@ declare class Node<Options = unknown, Storage = unknown> extends Extension<Optio
3351
3645
  }
3352
3646
 
3353
3647
  /**
3354
- * ToolbarController Headless, framework-agnostic toolbar state machine
3648
+ * ToolbarController - Headless, framework-agnostic toolbar state machine
3355
3649
  *
3356
3650
  * Manages toolbar item collection, grouping, active state tracking,
3357
3651
  * dropdown state, and keyboard navigation. Framework wrappers (Angular,
@@ -3359,7 +3653,7 @@ declare class Node<Options = unknown, Storage = unknown> extends Extension<Optio
3359
3653
  *
3360
3654
  * @example
3361
3655
  * const controller = new ToolbarController(editor, () => {
3362
- * // Called on every state change trigger framework re-render
3656
+ * // Called on every state change - trigger framework re-render
3363
3657
  * });
3364
3658
  * controller.subscribe();
3365
3659
  * // ... use controller.groups, controller.activeMap, etc.
@@ -3419,7 +3713,7 @@ declare class ToolbarController {
3419
3713
  private _activeMap;
3420
3714
  /** Disabled state for each button (keyed by item.name) */
3421
3715
  private _disabledMap;
3422
- /** Expanded state for emitEvent buttons true when their panel is open */
3716
+ /** Expanded state for emitEvent buttons - true when their panel is open */
3423
3717
  private _expandedMap;
3424
3718
  /** Currently open dropdown name (null = none) */
3425
3719
  private _openDropdown;
@@ -3518,6 +3812,124 @@ declare class ToolbarController {
3518
3812
  private checkButtonExpanded;
3519
3813
  }
3520
3814
 
3815
+ /**
3816
+ * A group of floating-menu items sharing the same `group` value.
3817
+ */
3818
+ interface FloatingMenuGroup {
3819
+ name: string;
3820
+ items: FloatingMenuItem[];
3821
+ }
3822
+ /**
3823
+ * Groups items by their `group` property, preserving extension insertion
3824
+ * order of groups. Within each group, items are sorted by `priority`
3825
+ * descending (higher first, default 100).
3826
+ *
3827
+ * Shared between `FloatingMenuController` (which renders grouped item lists
3828
+ * for FloatingMenu + framework wrappers) and `createSlashSuggestionRenderer`
3829
+ * (the popup shown by SlashCommand). Having one implementation keeps visual
3830
+ * grouping consistent across every trigger mechanism.
3831
+ */
3832
+ declare function groupFloatingMenuItems(items: FloatingMenuItem[]): FloatingMenuGroup[];
3833
+
3834
+ /**
3835
+ * FloatingMenuController - Headless, framework-agnostic state machine for the
3836
+ * block-insert floating menu.
3837
+ *
3838
+ * Mirrors the shape of ToolbarController: collects items from extensions,
3839
+ * groups them, tracks focused index for roving-tabindex keyboard navigation,
3840
+ * and executes commands. Framework wrappers (Angular, React, Vue) bind their
3841
+ * templates to this controller.
3842
+ *
3843
+ * @example
3844
+ * const controller = new FloatingMenuController(editor, () => {
3845
+ * // onChange - re-render
3846
+ * });
3847
+ * controller.subscribe();
3848
+ * // ... controller.groups, controller.focusedIndex, controller.execute(item)
3849
+ * controller.destroy();
3850
+ */
3851
+
3852
+ /** -1 means no item is focused (menu not entered via keyboard). */
3853
+ declare const FLOATING_MENU_NO_FOCUS = -1;
3854
+ declare class FloatingMenuController {
3855
+ /**
3856
+ * Resolves an `items` option (array | function) against the editor's
3857
+ * default floating-menu items. Exposed as static so the plugin can
3858
+ * resolve once at init without constructing a controller.
3859
+ */
3860
+ static resolveItems(editor: Editor, override?: FloatingMenuItemsOverride): FloatingMenuItem[];
3861
+ /**
3862
+ * Executes a floating-menu item's command on the editor.
3863
+ * String commands are dispatched via `editor.commands[name](...args)`;
3864
+ * function commands are called directly.
3865
+ */
3866
+ static executeItem(editor: Editor, item: FloatingMenuItem): void;
3867
+ private editor;
3868
+ private onChange;
3869
+ private override;
3870
+ private transactionHandler;
3871
+ private _groups;
3872
+ private _flatItems;
3873
+ private _disabledMap;
3874
+ private _focusedIndex;
3875
+ constructor(editor: Editor, onChange: () => void, override?: FloatingMenuItemsOverride);
3876
+ get groups(): FloatingMenuGroup[];
3877
+ get flatItems(): FloatingMenuItem[];
3878
+ get disabledMap(): ReadonlyMap<string, boolean>;
3879
+ get focusedIndex(): number;
3880
+ /** True when keyboard focus is inside the menu (at least one item focused). */
3881
+ get isEntered(): boolean;
3882
+ get itemCount(): number;
3883
+ isDisabled(item: FloatingMenuItem): boolean;
3884
+ /**
3885
+ * Executes an item's command, then closes the menu keyboard focus.
3886
+ * Callers should refocus the editor after calling this.
3887
+ */
3888
+ execute(item: FloatingMenuItem): void;
3889
+ /**
3890
+ * Rebuilds items from the editor. Call when the editor's extensions
3891
+ * change (rare) or on explicit refresh. Notification is delegated to
3892
+ * `updateDisabledStates` which fires `onChange` only when a disabled
3893
+ * state flipped - wrappers that need to react to pure group-structure
3894
+ * changes do so by bumping their own render signal after constructing
3895
+ * / re-using the controller (see framework wrapper usage).
3896
+ */
3897
+ rebuild(): void;
3898
+ /** Enter keyboard focus on the menu (first item). Called by the plugin's keymap. */
3899
+ enterMenu(): number;
3900
+ /** Leave keyboard focus (Escape). Refocus of the editor is the caller's job. */
3901
+ leaveMenu(): void;
3902
+ /** ArrowDown - wrap to first at end. */
3903
+ next(): number;
3904
+ /** ArrowUp - wrap to last at start. */
3905
+ prev(): number;
3906
+ first(): number;
3907
+ last(): number;
3908
+ /** Set focused index directly (e.g. on pointer hover). */
3909
+ setFocusedIndex(index: number): void;
3910
+ /** Get focused item (or null). */
3911
+ focusedItem(): FloatingMenuItem | null;
3912
+ /** Get flat index of item by name (for wrappers binding roving tabindex). */
3913
+ getFlatIndex(name: string): number;
3914
+ /** Subscribes to editor transactions for disabled-state tracking. */
3915
+ subscribe(): void;
3916
+ /** Unsubscribes and clears internal state. */
3917
+ destroy(): void;
3918
+ /**
3919
+ * Groups items by `group` preserving insertion order, then sorts by
3920
+ * priority (higher first) within each group. Delegates to the shared
3921
+ * utility so `SlashCommand`'s renderer and this controller always
3922
+ * produce identical ordering.
3923
+ */
3924
+ private groupItems;
3925
+ /**
3926
+ * Updates disabled state for each item. Uses custom predicate when
3927
+ * provided; otherwise tries a dry-run against `editor.can()[command]`.
3928
+ * Only notifies on change to avoid noisy re-renders.
3929
+ */
3930
+ private updateDisabledStates;
3931
+ }
3932
+
3521
3933
  declare const defaultIcons: IconSet;
3522
3934
 
3523
3935
  /**
@@ -3619,25 +4031,12 @@ declare class Mark<Options = unknown, Storage = unknown> extends Extension<Optio
3619
4031
  */
3620
4032
  static create<O = unknown, S = unknown>(config: MarkConfig<O, S>): Mark<O, S>;
3621
4033
  /**
3622
- * Creates a new mark with merged options
3623
- * Original mark is not modified
3624
- *
3625
- * **Note:** Options are merged shallowly using object spread (`...`).
3626
- * Nested objects are replaced entirely, not deeply merged.
3627
- *
3628
- * @param options - Options to merge with existing options
3629
- * @returns New mark instance with merged options
4034
+ * Creates a new mark with merged options. Original mark is not modified.
4035
+ * Options merge shallowly (object spread); see {@link Extension.configure}
4036
+ * for the nested-object gotcha and a workaround.
3630
4037
  *
3631
4038
  * @example
3632
4039
  * const CustomBold = Bold.configure({ HTMLAttributes: { class: 'custom-bold' } });
3633
- *
3634
- * @example
3635
- * // Shallow merge behavior with nested objects:
3636
- * // Given: options = { HTMLAttributes: { class: 'a', id: 'b' } }
3637
- * // configure({ HTMLAttributes: { class: 'c' } })
3638
- * // Result: { HTMLAttributes: { class: 'c' } } — 'id' is lost!
3639
- * // To preserve nested values, spread manually:
3640
- * // configure({ HTMLAttributes: { ...original.options.HTMLAttributes, class: 'c' } })
3641
4040
  */
3642
4041
  configure(options: Partial<Options> & {
3643
4042
  isFormatting?: boolean;
@@ -4086,14 +4485,19 @@ interface OrderedListOptions {
4086
4485
  declare const OrderedList: Node<OrderedListOptions, unknown>;
4087
4486
 
4088
4487
  /**
4089
- * ListItem Node
4488
+ * Enter / Backspace state machine for listItem (cursor's parent must be a paragraph):
4090
4489
  *
4091
- * Individual list item that can contain paragraphs and nested blocks.
4092
- * Used by BulletList and OrderedList.
4490
+ * LABEL paragraph (childIndex 0):
4491
+ * Enter, empty -> liftListItem (exit empty bullet)
4492
+ * Enter, non-empty -> splitListItem (new sibling listItem)
4493
+ * Backspace, empty -> falls through to ListKeymap (liftListItem)
4093
4494
  *
4094
- * Keyboard shortcuts:
4095
- * - Enter: Split list item at cursor, or lift out of list if item is empty
4096
- * - Tab/Shift-Tab: Handled by ListKeymap extension (included via addExtensions)
4495
+ * CHILDREN-ZONE paragraph (childIndex > 0):
4496
+ * Enter, empty -> insertChildrenZoneSibling (accumulate inside the item)
4497
+ * Enter, non-empty -> splitBlock (both halves stay inside the same item)
4498
+ * Backspace, empty -> liftEmptyChildrenZoneParagraph (exit as top-level)
4499
+ *
4500
+ * Tab / Shift-Tab handled by the ListKeymap extension.
4097
4501
  */
4098
4502
 
4099
4503
  interface ListItemOptions {
@@ -4155,17 +4559,21 @@ interface TaskListOptions {
4155
4559
  declare const TaskList: Node<TaskListOptions, unknown>;
4156
4560
 
4157
4561
  /**
4158
- * TaskItem Node
4562
+ * Enter / Backspace state machine for taskItem (cursor's parent must be a paragraph):
4159
4563
  *
4160
- * Individual task/checkbox item that can contain paragraphs and nested blocks.
4161
- * Used by TaskList.
4564
+ * LABEL paragraph (childIndex 0):
4565
+ * Enter, empty -> splitListItem fall-through, then taskItem-in-listItem
4566
+ * promotion (nested orderedList > listItem > taskList > taskItem
4567
+ * context), else liftListItem.
4568
+ * Enter, non-empty -> splitListItem (new sibling taskItem)
4569
+ * Backspace, empty -> liftListItem (exit empty checkbox)
4162
4570
  *
4163
- * Keyboard shortcuts:
4164
- * - Enter: Split task item at cursor
4165
- * - Tab: Sink (indent) task item
4166
- * - Shift-Tab: Lift (outdent) task item
4167
- * - Backspace: Lift task item when at start of empty-ish item (converts to paragraph)
4168
- * - Mod-Enter: Toggle task checked state
4571
+ * CHILDREN-ZONE paragraph (childIndex > 0):
4572
+ * Enter, empty -> insertChildrenZoneSibling (accumulate inside the item)
4573
+ * Enter, non-empty -> splitBlock (both halves stay inside the same item)
4574
+ * Backspace, empty -> liftEmptyChildrenZoneParagraph (exit as top-level)
4575
+ *
4576
+ * Tab / Shift-Tab sink / lift the item; Mod-Enter toggles checked state.
4169
4577
  */
4170
4578
 
4171
4579
  declare module '@domternal/core' {
@@ -4376,9 +4784,6 @@ declare module '@domternal/core' {
4376
4784
  }
4377
4785
  }
4378
4786
 
4379
- /**
4380
- * Options for the Link mark
4381
- */
4382
4787
  interface LinkOptions {
4383
4788
  /**
4384
4789
  * HTML attributes to add to the rendered element
@@ -4882,6 +5287,61 @@ interface ListKeymapOptions {
4882
5287
  }
4883
5288
  declare const ListKeymap: Extension<ListKeymapOptions, unknown>;
4884
5289
 
5290
+ /**
5291
+ * Keyboard indent across list boundaries. `Tab` on a top-level block whose
5292
+ * previous sibling is a list moves the block INTO the last item as a nested
5293
+ * child; `Shift-Tab` reverses for a block sitting as the last child of the
5294
+ * last item.
5295
+ *
5296
+ * Trigger is intentionally narrow so ListKeymap retains in-list Tab/Shift-Tab
5297
+ * (`sinkListItem` / `liftListItem`). Schema is validated via `canReplaceWith`;
5298
+ * invalid placements are a clean no-op so the keymap chain falls through.
5299
+ *
5300
+ * Intentional design restrictions (NOT bugs to "fix"):
5301
+ * - Tab indents into the IMMEDIATE last item only, not recursively into a
5302
+ * deeper "deepest last item". Users get deeper nesting via repeated Tab
5303
+ * inside the now-nested context (then ListKeymap takes over).
5304
+ * - Tab only fires for cursors in TOP-LEVEL blocks (depth === 1). Cursors
5305
+ * inside other containers (blockquote, table cell) fall through.
5306
+ * - Shift-Tab only fires when the block is BOTH the last child of its
5307
+ * item AND the item is the last in its wrapper. Mid-position outdent
5308
+ * would require splitting the list item, which is deferred.
5309
+ *
5310
+ * Registration order: must come AFTER ListKeymap so this extension's keymap
5311
+ * runs FIRST and can defer to ListKeymap for in-list flows.
5312
+ */
5313
+
5314
+ /**
5315
+ * `Tab` handler: indent a top-level block as the last child of the
5316
+ * last item of the immediately-preceding list wrapper. Returns true
5317
+ * when the operation succeeded (and dispatched, if `dispatch` is
5318
+ * provided), false when any precondition fails so the keymap chain
5319
+ * can fall through to the next handler.
5320
+ */
5321
+ declare function indentBlockAsListChild(state: EditorState, dispatch?: (tr: Transaction) => void): boolean;
5322
+ /**
5323
+ * `Shift-Tab` handler: lift a non-label nested block out of its list
5324
+ * item to become a top-level sibling AFTER the list wrapper. Only
5325
+ * fires for the strict end-of-end case (last child of last item) so
5326
+ * the lift is deterministic and never needs to split a list item.
5327
+ *
5328
+ * Precondition table:
5329
+ * - cursor empty
5330
+ * - cursor textblock parent is NOT a paragraph (so we never
5331
+ * accidentally outdent the label; ListKeymap.Shift-Tab handles
5332
+ * in-label cases via liftListItem)
5333
+ * - the cursor's enclosing list item exists in the ancestry
5334
+ * - the cursor's containing block is a DIRECT child of the list
5335
+ * item (depth = listItemDepth + 1) - keeps the math local to
5336
+ * immediate li children rather than reaching deeper into nested
5337
+ * containers
5338
+ * - the block is the LAST child of the list item
5339
+ * - the list item is the LAST child of its wrapper
5340
+ * - the wrapper's parent accepts the block as a sibling (schema)
5341
+ */
5342
+ declare function outdentBlockFromListItem(state: EditorState, dispatch?: (tr: Transaction) => void): boolean;
5343
+ declare const ListIndent: Extension<unknown, unknown>;
5344
+
4885
5345
  /**
4886
5346
  * CharacterCount Extension
4887
5347
  *
@@ -5113,25 +5573,8 @@ interface LineHeightOptions {
5113
5573
  declare const LineHeight: Extension<LineHeightOptions, unknown>;
5114
5574
 
5115
5575
  /**
5116
- * UniqueID Extension
5117
- *
5118
- * Automatically assigns unique IDs to specified node types.
5119
- * Useful for collaborative editing, linking, and history tracking.
5120
- *
5121
- * @example
5122
- * ```ts
5123
- * import { UniqueID } from '@domternal/core';
5124
- *
5125
- * const editor = new Editor({
5126
- * extensions: [
5127
- * // ... other extensions
5128
- * UniqueID.configure({
5129
- * types: ['paragraph', 'heading'],
5130
- * attributeName: 'id',
5131
- * }),
5132
- * ],
5133
- * });
5134
- * ```
5576
+ * Canonical block-id system. Assigns ids to configured node types and is
5577
+ * read by TableOfContents and BlockContextMenu.
5135
5578
  */
5136
5579
 
5137
5580
  declare const uniqueIDPluginKey: PluginKey<any>;
@@ -5159,6 +5602,70 @@ interface UniqueIDOptions {
5159
5602
  }
5160
5603
  declare const UniqueID: Extension<UniqueIDOptions, unknown>;
5161
5604
 
5605
+ /**
5606
+ * Tints whole blocks via `bgColor` / `textColor` attributes rendered as
5607
+ * `data-bg-color` / `data-text-color`. Theme's `_block-colors.scss` maps
5608
+ * names to CSS custom properties with light/dark variants. `null` clears
5609
+ * the attribute; unknown palette names are rejected by commands.
5610
+ */
5611
+
5612
+ declare module '@domternal/core' {
5613
+ interface RawCommands {
5614
+ setBlockBgColor: CommandSpec<[color: string | null]>;
5615
+ setBlockTextColor: CommandSpec<[color: string | null]>;
5616
+ unsetBlockColors: CommandSpec;
5617
+ }
5618
+ }
5619
+ /**
5620
+ * Notion's public palette. Names are semantic (not tied to specific hex);
5621
+ * the stylesheet owns the actual colors so themes can customize them.
5622
+ * `'default'` is implicit - represented by `null` (no data attribute).
5623
+ */
5624
+ declare const DEFAULT_BLOCK_COLORS: string[];
5625
+ /**
5626
+ * Default set of block types that receive the color attributes. `codeBlock`
5627
+ * is intentionally excluded - `<pre><code>` already has its own background
5628
+ * that would conflict visually. Similarly, details-content blocks have
5629
+ * their own affordance. Callers can extend via `types` option.
5630
+ */
5631
+ declare const DEFAULT_BLOCK_COLOR_TYPES: string[];
5632
+ interface BlockColorOptions {
5633
+ /**
5634
+ * Node types that receive `bgColor` / `textColor` attributes.
5635
+ * @default DEFAULT_BLOCK_COLOR_TYPES
5636
+ */
5637
+ types: string[];
5638
+ /**
5639
+ * Palette used by the `setBlockBgColor` command. Commands reject values
5640
+ * outside this list (no-op + false return) so host apps can curate
5641
+ * what's selectable.
5642
+ * @default DEFAULT_BLOCK_COLORS
5643
+ */
5644
+ bgColors: string[];
5645
+ /**
5646
+ * Palette used by the `setBlockTextColor` command.
5647
+ * @default DEFAULT_BLOCK_COLORS
5648
+ */
5649
+ textColors: string[];
5650
+ }
5651
+ /**
5652
+ * Strip any inline `textStyle` marks inside [from, to] that carry the inline
5653
+ * counterpart of a block-level color attribute. Mutates the transaction in
5654
+ * place. This makes "last action wins": applying a block color erases
5655
+ * conflicting inline colors so the new block tint isn't visually hidden by
5656
+ * old span overrides.
5657
+ *
5658
+ * `which` may be 'text', 'bg', or 'both' - 'both' handles the unset case
5659
+ * in a single pass so the two strip operations don't step on each other's
5660
+ * replaced mark instances.
5661
+ *
5662
+ * Exported so `BlockContextMenu` (which writes node attrs directly via
5663
+ * `setNodeMarkup` instead of going through `setBlockBgColor` /
5664
+ * `setBlockTextColor`) shares the same conflict-stripping behavior.
5665
+ */
5666
+ declare function stripInlineColorConflicts(tr: Transaction, state: EditorState, from: number, to: number, which: 'text' | 'bg' | 'both'): void;
5667
+ declare const BlockColor: Extension<BlockColorOptions, unknown>;
5668
+
5162
5669
  /**
5163
5670
  * Selection Extension
5164
5671
  *
@@ -5237,7 +5744,7 @@ declare const Selection: Extension<SelectionOptions, SelectionStorage>;
5237
5744
  * clicks outside the editor (approach A - same as Google Docs / Notion).
5238
5745
  *
5239
5746
  * Toolbar and bubble-menu buttons call `event.preventDefault()` on
5240
- * `mousedown`, so they never trigger blur the selection stays intact
5747
+ * `mousedown`, so they never trigger blur - the selection stays intact
5241
5748
  * while the user interacts with editor UI.
5242
5749
  */
5243
5750
 
@@ -5358,6 +5865,8 @@ declare module '@domternal/core' {
5358
5865
  interface RawCommands {
5359
5866
  setTextColor: CommandSpec<[color: string]>;
5360
5867
  unsetTextColor: CommandSpec;
5868
+ setTextColorToken: CommandSpec<[token: string | null]>;
5869
+ unsetTextColorToken: CommandSpec;
5361
5870
  }
5362
5871
  }
5363
5872
  /**
@@ -5419,6 +5928,8 @@ declare module '@domternal/core' {
5419
5928
  toggleHighlight: CommandSpec<[attributes?: {
5420
5929
  color?: string;
5421
5930
  }]>;
5931
+ setBackgroundColorToken: CommandSpec<[token: string | null]>;
5932
+ unsetBackgroundColorToken: CommandSpec;
5422
5933
  }
5423
5934
  }
5424
5935
  /**
@@ -5534,6 +6045,59 @@ interface FontSizeOptions {
5534
6045
  }
5535
6046
  declare const FontSize: Extension<FontSizeOptions, unknown>;
5536
6047
 
6048
+ /**
6049
+ * NotionColorPicker Extension
6050
+ *
6051
+ * Provides toolbar registration for a Notion-style inline color picker that
6052
+ * drives the `colorToken` / `backgroundColorToken` attributes on the textStyle
6053
+ * mark (added by TextColor and Highlight).
6054
+ *
6055
+ * Owns:
6056
+ * - The shared named-token palette (default: 9 Notion-style colors).
6057
+ * - A bubble-menu-only toolbar item (`notionColor`) that emits a custom
6058
+ * `notionColorOpen` event when clicked. Framework wrappers listen for the
6059
+ * event and render the actual picker panel.
6060
+ *
6061
+ * The panel renders the full palette every time it opens; no MRU/recent
6062
+ * tracking is kept because all 18 swatches (9 text + 9 bg) fit in one view.
6063
+ *
6064
+ * @example
6065
+ * ```ts
6066
+ * import { TextStyle, TextColor, Highlight, NotionColorPicker } from '@domternal/core';
6067
+ *
6068
+ * const editor = new Editor({
6069
+ * extensions: [
6070
+ * TextStyle,
6071
+ * TextColor,
6072
+ * Highlight,
6073
+ * NotionColorPicker,
6074
+ * ],
6075
+ * });
6076
+ * ```
6077
+ */
6078
+
6079
+ /**
6080
+ * Default named tokens. Match the BlockColor palette + theme tokens
6081
+ * (`--dm-block-text-<token>`, `--dm-block-bg-<token>`) so light and dark
6082
+ * themes render the right hex without any extra CSS work.
6083
+ */
6084
+ declare const DEFAULT_NOTION_COLOR_PALETTE: readonly string[];
6085
+ interface NotionColorPickerOptions {
6086
+ /**
6087
+ * Named tokens shown in the picker. Each token must have matching
6088
+ * `--dm-block-text-<token>` and `--dm-block-bg-<token>` CSS variables in
6089
+ * the active theme; tokens with no theme support render as transparent
6090
+ * swatches.
6091
+ * @default DEFAULT_NOTION_COLOR_PALETTE
6092
+ */
6093
+ palette: readonly string[];
6094
+ }
6095
+ interface NotionColorPickerStorage {
6096
+ /** Whether the picker panel is currently open. UI sets this. */
6097
+ isOpen: boolean;
6098
+ }
6099
+ declare const NotionColorPicker: Extension<NotionColorPickerOptions, NotionColorPickerStorage>;
6100
+
5537
6101
  /**
5538
6102
  * ClearFormatting Extension
5539
6103
  *
@@ -5569,33 +6133,7 @@ interface LinkPopoverOptions {
5569
6133
  declare const LinkPopover: Extension<LinkPopoverOptions, unknown>;
5570
6134
 
5571
6135
  /**
5572
- * BubbleMenu Extension
5573
- *
5574
- * Shows a floating menu when text is selected in the editor.
5575
- * Useful for formatting toolbars that appear contextually.
5576
- *
5577
- * Styles are included automatically via `@domternal/theme` (`_bubble-menu.scss`).
5578
- *
5579
- * @example
5580
- * ```ts
5581
- * import { BubbleMenu } from '@domternal/core';
5582
- *
5583
- * // Create menu element
5584
- * const menuElement = document.getElementById('bubble-menu');
5585
- *
5586
- * const editor = new Editor({
5587
- * extensions: [
5588
- * // ... other extensions
5589
- * BubbleMenu.configure({
5590
- * element: menuElement,
5591
- * shouldShow: ({ editor, state, from, to }) => {
5592
- * // Only show for text selections
5593
- * return !state.selection.empty;
5594
- * },
5595
- * }),
5596
- * ],
5597
- * });
5598
- * ```
6136
+ * Floating menu shown when text is selected. Contextual formatting toolbar.
5599
6137
  */
5600
6138
 
5601
6139
  declare const bubbleMenuPluginKey: PluginKey<any>;
@@ -5649,76 +6187,6 @@ interface CreateBubbleMenuPluginOptions {
5649
6187
  declare function createBubbleMenuPlugin(options: CreateBubbleMenuPluginOptions): Plugin;
5650
6188
  declare const BubbleMenu: Extension<BubbleMenuOptions, unknown>;
5651
6189
 
5652
- /**
5653
- * FloatingMenu Extension
5654
- *
5655
- * Shows a floating menu when the cursor is in an empty paragraph.
5656
- * Useful for showing block-level insertion options.
5657
- *
5658
- * @example
5659
- * ```ts
5660
- * import { FloatingMenu } from '@domternal/core';
5661
- *
5662
- * // Create menu element
5663
- * const menuElement = document.getElementById('floating-menu');
5664
- *
5665
- * const editor = new Editor({
5666
- * extensions: [
5667
- * // ... other extensions
5668
- * FloatingMenu.configure({
5669
- * element: menuElement,
5670
- * shouldShow: ({ editor, state }) => {
5671
- * const { $from, empty } = state.selection;
5672
- * // Show in empty paragraphs
5673
- * return empty &&
5674
- * $from.parent.type.name === 'paragraph' &&
5675
- * $from.parent.content.size === 0;
5676
- * },
5677
- * }),
5678
- * ],
5679
- * });
5680
- * ```
5681
- *
5682
- * Styles are included automatically via `@domternal/theme` (`_floating-menu.scss`).
5683
- */
5684
-
5685
- declare const floatingMenuPluginKey: PluginKey<any>;
5686
- interface FloatingMenuOptions {
5687
- /**
5688
- * The HTML element that contains the menu.
5689
- * Must be provided by the user.
5690
- */
5691
- element: HTMLElement | null;
5692
- /**
5693
- * Function to determine if the menu should be shown.
5694
- * By default, shows when the cursor is in an empty paragraph.
5695
- */
5696
- shouldShow: (props: {
5697
- editor: Editor;
5698
- view: EditorView;
5699
- state: EditorState;
5700
- }) => boolean;
5701
- /**
5702
- * Offset in pixels from the cursor position.
5703
- * @default 0
5704
- */
5705
- offset: number;
5706
- }
5707
- interface CreateFloatingMenuPluginOptions {
5708
- pluginKey: PluginKey;
5709
- editor: Editor;
5710
- element: HTMLElement;
5711
- shouldShow?: FloatingMenuOptions['shouldShow'];
5712
- offset?: number;
5713
- }
5714
- /**
5715
- * Creates a standalone FloatingMenu ProseMirror plugin.
5716
- * Can be used by framework wrappers (Angular, React, Vue) to create the plugin
5717
- * independently of the extension system.
5718
- */
5719
- declare function createFloatingMenuPlugin(options: CreateFloatingMenuPluginOptions): Plugin;
5720
- declare const FloatingMenu: Extension<FloatingMenuOptions, unknown>;
5721
-
5722
6190
  /**
5723
6191
  * StarterKit Extension
5724
6192
  *
@@ -5827,6 +6295,13 @@ interface StarterKitOptions {
5827
6295
  * Set to false to disable the ListKeymap extension, or pass options to configure it.
5828
6296
  */
5829
6297
  listKeymap?: false | Partial<ListKeymapOptions>;
6298
+ /**
6299
+ * Set to false to disable the ListIndent extension. ListIndent adds
6300
+ * Tab / Shift-Tab keymap that indents a top-level block under the
6301
+ * previous list (and outdents back). Registered AFTER ListKeymap so
6302
+ * the in-list-item Tab/Shift-Tab keep priority.
6303
+ */
6304
+ listIndent?: false;
5830
6305
  /**
5831
6306
  * Set to false to disable the LinkPopover extension, or pass options to configure it.
5832
6307
  */
@@ -5846,4 +6321,4 @@ declare const StarterKit: Extension<StarterKitOptions, unknown>;
5846
6321
  */
5847
6322
  declare const VERSION = "0.1.0";
5848
6323
 
5849
- export { type AnyExtension, type AnyExtensionConfig, type AttributeSpec, type AttributeSpecs, type AutolinkPluginOptions, BaseKeymap, type BaseKeymapOptions, Blockquote, type BlockquoteOptions, Bold, type BoldOptions, BubbleMenu, type BubbleMenuOptions, type BuildCommandPropsOptions, BulletList, type BulletListOptions, type CanChainedCommands, CanChecker, type CanCheckerEditor, type CanCheckerOptions, type CanCommands, ChainBuilder, type ChainBuilderEditor, type ChainBuilderOptions, type ChainFailure, type ChainedCommands, CharacterCount, type CharacterCountOptions, type CharacterCountStorage, type ClearContentOptions, ClearFormatting, Code, CodeBlock, type CodeBlockOptions, type CodeOptions, type Command, type CommandEditor, CommandManager, type CommandManagerEditor, type CommandMap, type CommandProps, type CommandPropsEditor, type CommandSpec$1 as CommandSpec, type Content, type ContentErrorProps, type CreateBubbleMenuPluginOptions, type CreateDocumentOptions, type CreateEventProps, type CreateFloatingMenuPluginOptions, DEFAULT_HIGHLIGHT_COLORS, DEFAULT_TEXT_COLORS, type DeleteEventProps, Document$1 as Document, type DropEventProps, Dropcursor, type DropcursorOptions, Editor, type EditorEventName, type EditorEvents, type EditorInstance, type EditorOptions, EventEmitter, Extension, type ExtensionConfig, type ExtensionEditor, ExtensionManager, type ExtensionManagerEditor, type ExtensionManagerOptions, type FindChildResult, type FindParentNodeResult, FloatingMenu, type FloatingMenuOptions, Focus, type FocusEventProps, type FocusOptions, type FocusPosition, FontFamily, type FontFamilyOptions, FontSize, type FontSizeOptions, Gapcursor, type GenerateHTMLOptions, type GenerateJSONOptions, type GenerateTextOptions, type GlobalAttributeSpec, type GlobalAttributes, HardBreak, type HardBreakOptions, Heading, type HeadingOptions, Highlight, type HighlightOptions, History, type HistoryOptions, HorizontalRule, type HorizontalRuleOptions, type IconSet, type InlineStyleOverrides, InvisibleChars, type InvisibleCharsOptions, type InvisibleCharsStorage, type IsNodeEmptyOptions, type IsValidUrlOptions, Italic, type ItalicOptions, type JSONAttribute, type JSONContent, type JSONMark, type KeyboardShortcutCommand, LineHeight, type LineHeightOptions, Link, type LinkAttributes, type LinkClickPluginOptions, type LinkExitPluginOptions, type LinkOptions, type LinkPastePluginOptions, LinkPopover, type LinkPopoverOptions, ListItem, type ListItemOptions, ListKeymap, type ListKeymapOptions, Mark, type MarkConfig, type MarkInputRuleOptions, type MarkParseRule, type MarkRange, type MarkRenderHTMLProps, type MountEventProps, Node, type NodeConfig, type NodeInputRuleOptions, type NodeParseRule, type NodeRenderHTMLProps, type NodeViewContext, OrderedList, type OrderedListOptions, Paragraph, type ParagraphOptions, type PasteEventProps, Placeholder, type PlaceholderOptions, type PositionFloatingOptions, type Range, type RawCommands, Selection, SelectionDecoration, type SelectionDecorationOptions, type SelectionOptions, type SelectionStorage, type SetContentOptions, type SingleCommands, StarterKit, type StarterKitOptions, Strike, type StrikeOptions, Subscript, type SubscriptOptions, Superscript, type SuperscriptOptions, TaskItem, type TaskItemOptions, TaskList, type TaskListOptions, Text, TextAlign, type TextAlignOptions, TextColor, type TextColorOptions, type TextInputRuleOptions, TextStyle, type TextStyleOptions, type TextblockTypeInputRuleOptions, type ToolbarButton, ToolbarController, type ToolbarControllerEditor, type ToolbarDropdown, type ToolbarGroup, type ToolbarItem, type ToolbarLayoutDropdown, type ToolbarLayoutEntry, type ToolbarSeparator, TrailingNode, type TrailingNodeOptions, type TransactionEventProps, Typography, type TypographyOptions, Underline, type UnderlineOptions, UniqueID, type UniqueIDOptions, VERSION, type WrappingInputRuleOptions, applyInlineStyles, autolinkPlugin, autolinkPluginKey, blur, bubbleMenuPluginKey, buildCommandProps, builtInCommands, callOrReturn, characterCountPluginKey, clearContent, createAccumulatingDispatch, createBubbleMenuPlugin, createCanChecker, createChainBuilder, createDocument, createFloatingMenuPlugin, defaultBlockAt, defaultIcons, deleteSelection, findChildren, findParentNode, floatingMenuPluginKey, focus, focusPluginKey, generateHTML, generateJSON, generateText, getMarkRange, inlineStyles, insertContent, insertText, invisibleCharsPluginKey, isDocumentEmpty, isNodeEmpty, isValidUrl, lift, linkClickPlugin, linkClickPluginKey, linkExitPlugin, linkExitPluginKey, linkPastePlugin, linkPastePluginKey, markInputRule, markInputRulePatterns, nodeInputRule, placeholderPluginKey, positionFloating, positionFloatingOnce, resetAttributes, selectAll, selectNodeBackward, selectionDecorationPluginKey, setBlockType, setContent, setMark, textInputRule, textblockTypeInputRule, toggleBlockType, toggleList, toggleMark, toggleWrap, uniqueIDPluginKey, unsetAllMarks, unsetMark, updateAttributes, wrapIn, wrappingInputRule };
6324
+ export { type AnyExtension, type AnyExtensionConfig, type AttributeSpec, type AttributeSpecs, type AutolinkPluginOptions, BaseKeymap, type BaseKeymapOptions, BlockColor, type BlockColorOptions, Blockquote, type BlockquoteOptions, Bold, type BoldOptions, BubbleMenu, type BubbleMenuOptions, type BuildCommandPropsOptions, BulletList, type BulletListOptions, type CanChainedCommands, CanChecker, type CanCheckerEditor, type CanCheckerOptions, type CanCommands, ChainBuilder, type ChainBuilderEditor, type ChainBuilderOptions, type ChainFailure, type ChainedCommands, CharacterCount, type CharacterCountOptions, type CharacterCountStorage, type ClearContentOptions, ClearFormatting, Code, CodeBlock, type CodeBlockOptions, type CodeOptions, type Command, type CommandEditor, CommandManager, type CommandManagerEditor, type CommandMap, type CommandProps, type CommandPropsEditor, type CommandSpec, type Content, type ContentErrorProps, type CreateBubbleMenuPluginOptions, type CreateDocumentOptions, type CreateEventProps, DEFAULT_BLOCK_COLORS, DEFAULT_BLOCK_COLOR_TYPES, DEFAULT_HIGHLIGHT_COLORS, DEFAULT_NOTION_COLOR_PALETTE, DEFAULT_TEXT_COLORS, type DeleteEventProps, Document$1 as Document, type DropEventProps, Dropcursor, type DropcursorOptions, Editor, type EditorEventName, type EditorEvents, type EditorInstance, type EditorOptions, EventEmitter, Extension, type ExtensionConfig, type ExtensionEditor, ExtensionManager, type ExtensionManagerEditor, type ExtensionManagerOptions, FLOATING_MENU_NO_FOCUS, type FindChildResult, type FindParentNodeResult, FloatingMenuController, type FloatingMenuGroup, type FloatingMenuItem, type FloatingMenuItemsOverride, Focus, type FocusEventProps, type FocusOptions, type FocusPosition, FontFamily, type FontFamilyOptions, FontSize, type FontSizeOptions, Gapcursor, type GenerateHTMLOptions, type GenerateJSONOptions, type GenerateTextOptions, type GlobalAttributeSpec, type GlobalAttributes, HardBreak, type HardBreakOptions, Heading, type HeadingOptions, Highlight, type HighlightOptions, History, type HistoryOptions, HorizontalRule, type HorizontalRuleOptions, type IconSet, type InlineStyleOverrides, type InsertAsListItemChildArgs, type InsertAsListItemChildResult, InvisibleChars, type InvisibleCharsOptions, type InvisibleCharsStorage, type IsNodeEmptyOptions, type IsValidUrlOptions, Italic, type ItalicOptions, type JSONAttribute, type JSONContent, type JSONMark, type KeyboardShortcutCommand, LIST_ITEM_TYPE_NAMES, LineHeight, type LineHeightOptions, Link, type LinkAttributes, type LinkClickPluginOptions, type LinkExitPluginOptions, type LinkOptions, type LinkPastePluginOptions, LinkPopover, type LinkPopoverOptions, ListIndent, ListItem, type ListItemCursorContext, type ListItemOptions, ListKeymap, type ListKeymapOptions, Mark, type MarkConfig, type MarkInputRuleOptions, type MarkParseRule, type MarkRange, type MarkRenderHTMLProps, type MountEventProps, Node, type NodeConfig, type NodeInputRuleOptions, type NodeParseRule, type NodeRenderHTMLProps, type NodeViewContext, NotionColorPicker, type NotionColorPickerOptions, type NotionColorPickerStorage, OrderedList, type OrderedListOptions, Paragraph, type ParagraphOptions, type PasteEventProps, Placeholder, type PlaceholderOptions, type PositionFloatingOptions, type Range, type RawCommands, Selection, SelectionDecoration, type SelectionDecorationOptions, type SelectionOptions, type SelectionStorage, type SetContentOptions, type SingleCommands, type SplitListForInsertRange, StarterKit, type StarterKitOptions, Strike, type StrikeOptions, Subscript, type SubscriptOptions, Superscript, type SuperscriptOptions, TaskItem, type TaskItemOptions, TaskList, type TaskListOptions, Text, TextAlign, type TextAlignOptions, TextColor, type TextColorOptions, type TextInputRuleOptions, TextStyle, type TextStyleOptions, type TextblockTypeInputRuleOptions, type ToolbarButton, ToolbarController, type ToolbarControllerEditor, type ToolbarDropdown, type ToolbarGroup, type ToolbarItem, type ToolbarLayoutDropdown, type ToolbarLayoutEntry, type ToolbarSeparator, TrailingNode, type TrailingNodeOptions, type TransactionEventProps, Typography, type TypographyOptions, Underline, type UnderlineOptions, UniqueID, type UniqueIDOptions, VERSION, type WrappingInputRuleOptions, applyInlineStyles, autolinkPlugin, autolinkPluginKey, blur, bubbleMenuPluginKey, buildCommandProps, builtInCommands, callOrReturn, characterCountPluginKey, clearContent, copyThemeClass, createAccumulatingDispatch, createBubbleMenuPlugin, createCanChecker, createChainBuilder, createDocument, defaultBlockAt, defaultBubbleContexts, defaultIcons, deleteSelection, findChildren, findListItemAncestorDepth, findParentNode, focus, focusPluginKey, generateHTML, generateJSON, generateText, getListItemCursorContext, getMarkRange, groupFloatingMenuItems, indentBlockAsListChild, inlineStyles, insertAsListItemChild, insertChildrenZoneSibling, insertContent, insertText, invisibleCharsPluginKey, isDocumentEmpty, isInListItemLabel, isInsideListItem, isNodeEmpty, isValidUrl, lift, liftCurrentListItem, liftEmptyChildrenZoneParagraph, linkClickPlugin, linkClickPluginKey, linkExitPlugin, linkExitPluginKey, linkPastePlugin, linkPastePluginKey, markInputRule, markInputRulePatterns, nodeInputRule, outdentBlockFromListItem, placeholderPluginKey, positionFloating, positionFloatingOnce, resetAttributes, selectAll, selectNodeBackward, selectionDecorationPluginKey, setBlockType, setContent, setMark, splitListForInsert, stripInlineColorConflicts, textInputRule, textblockTypeInputRule, toggleBlockType, toggleList, toggleMark, toggleWrap, uniqueIDPluginKey, unsetAllMarks, unsetMark, updateAttributes, wrapIn, wrappingInputRule, writeToClipboard };