@tiptap/core
Version:
headless rich text editor
1,506 lines (1,497 loc) • 164 kB
TypeScript
import { Transaction, EditorState, Plugin, Selection, NodeSelection, TextSelection, PluginKey } from '@tiptap/pm/state';
import { Node as Node$1, MarkType as MarkType$1, MarkSpec, Mark as Mark$1, DOMOutputSpec, NodeType as NodeType$1, NodeSpec, Slice, ParseOptions, Fragment as Fragment$1, Schema, ContentMatch, ResolvedPos, ParseRule } from '@tiptap/pm/model';
import { EditorProps, EditorView, Decoration, DecorationAttrs, ViewMutationRecord, NodeViewConstructor, NodeView as NodeView$1, MarkViewConstructor, MarkView as MarkView$1, DecorationSource } from '@tiptap/pm/view';
import { Transform, Mappable } from '@tiptap/pm/transform';
type StringKeyOf<T> = Extract<keyof T, string>;
type CallbackType<T extends Record<string, any>, EventName extends StringKeyOf<T>> = T[EventName] extends any[] ? T[EventName] : [T[EventName]];
type CallbackFunction<T extends Record<string, any>, EventName extends StringKeyOf<T>> = (...props: CallbackType<T, EventName>) => any;
declare class EventEmitter<T extends Record<string, any>> {
private callbacks;
on<EventName extends StringKeyOf<T>>(event: EventName, fn: CallbackFunction<T, EventName>): this;
emit<EventName extends StringKeyOf<T>>(event: EventName, ...args: CallbackType<T, EventName>): this;
off<EventName extends StringKeyOf<T>>(event: EventName, fn?: CallbackFunction<T, EventName>): this;
once<EventName extends StringKeyOf<T>>(event: EventName, fn: CallbackFunction<T, EventName>): this;
removeAllListeners(): void;
}
/**
* Returns a new `Transform` based on all steps of the passed transactions.
* @param oldDoc The Prosemirror node to start from
* @param transactions The transactions to combine
* @returns A new `Transform` with all steps of the passed transactions
*/
declare function combineTransactionSteps(oldDoc: Node$1, transactions: Transaction[]): Transform;
/**
* Takes a Transaction & Editor State and turns it into a chainable state object
* @param config The transaction and state to create the chainable state from
* @returns A chainable Editor state object
*/
declare function createChainableState(config: {
transaction: Transaction;
state: EditorState;
}): EditorState;
type InputRuleMatch = {
index: number;
text: string;
replaceWith?: string;
match?: RegExpMatchArray;
data?: Record<string, any>;
};
type InputRuleFinder = RegExp | ((text: string) => InputRuleMatch | null);
declare class InputRule {
find: InputRuleFinder;
handler: (props: {
state: EditorState;
range: Range;
match: ExtendedRegExpMatchArray;
commands: SingleCommands;
chain: () => ChainedCommands;
can: () => CanCommands;
}) => void | null;
undoable: boolean;
constructor(config: {
find: InputRuleFinder;
handler: (props: {
state: EditorState;
range: Range;
match: ExtendedRegExpMatchArray;
commands: SingleCommands;
chain: () => ChainedCommands;
can: () => CanCommands;
}) => void | null;
undoable?: boolean;
});
}
/**
* Create an input rules plugin. When enabled, it will cause text
* input that matches any of the given rules to trigger the rule’s
* action.
*/
declare function inputRulesPlugin(props: {
editor: Editor;
rules: InputRule[];
}): Plugin;
interface MarkConfig<Options = any, Storage = any> extends ExtendableConfig<Options, Storage, MarkConfig<Options, Storage>, MarkType$1> {
/**
* Mark View
*/
addMarkView?: ((this: {
name: string;
options: Options;
storage: Storage;
editor: Editor;
type: MarkType$1;
parent: ParentConfig<MarkConfig<Options, Storage>>['addMarkView'];
}) => MarkViewRenderer) | null;
/**
* Keep mark after split node
*/
keepOnSplit?: boolean | (() => boolean);
/**
* Inclusive
*/
inclusive?: MarkSpec['inclusive'] | ((this: {
name: string;
options: Options;
storage: Storage;
parent: ParentConfig<MarkConfig<Options, Storage>>['inclusive'];
editor?: Editor;
}) => MarkSpec['inclusive']);
/**
* Excludes
*/
excludes?: MarkSpec['excludes'] | ((this: {
name: string;
options: Options;
storage: Storage;
parent: ParentConfig<MarkConfig<Options, Storage>>['excludes'];
editor?: Editor;
}) => MarkSpec['excludes']);
/**
* Marks this Mark as exitable
*/
exitable?: boolean | (() => boolean);
/**
* Group
*/
group?: MarkSpec['group'] | ((this: {
name: string;
options: Options;
storage: Storage;
parent: ParentConfig<MarkConfig<Options, Storage>>['group'];
editor?: Editor;
}) => MarkSpec['group']);
/**
* Spanning
*/
spanning?: MarkSpec['spanning'] | ((this: {
name: string;
options: Options;
storage: Storage;
parent: ParentConfig<MarkConfig<Options, Storage>>['spanning'];
editor?: Editor;
}) => MarkSpec['spanning']);
/**
* Code
*/
code?: boolean | ((this: {
name: string;
options: Options;
storage: Storage;
parent: ParentConfig<MarkConfig<Options, Storage>>['code'];
editor?: Editor;
}) => boolean);
/**
* Parse HTML
*/
parseHTML?: (this: {
name: string;
options: Options;
storage: Storage;
parent: ParentConfig<MarkConfig<Options, Storage>>['parseHTML'];
editor?: Editor;
}) => MarkSpec['parseDOM'];
/**
* Render HTML
*/
renderHTML?: ((this: {
name: string;
options: Options;
storage: Storage;
parent: ParentConfig<MarkConfig<Options, Storage>>['renderHTML'];
editor?: Editor;
}, props: {
mark: Mark$1;
HTMLAttributes: Record<string, any>;
}) => DOMOutputSpec) | null;
/**
* Attributes
*/
addAttributes?: (this: {
name: string;
options: Options;
storage: Storage;
parent: ParentConfig<MarkConfig<Options, Storage>>['addAttributes'];
editor?: Editor;
}) => Attributes$1 | {};
}
/**
* The Mark class is used to create custom mark extensions.
* @see https://tiptap.dev/api/extensions#create-a-new-extension
*/
declare class Mark<Options = any, Storage = any> extends Extendable<Options, Storage, MarkConfig<Options, Storage>> {
type: string;
/**
* Create a new Mark instance
* @param config - Mark configuration object or a function that returns a configuration object
*/
static create<O = any, S = any>(config?: Partial<MarkConfig<O, S>> | (() => Partial<MarkConfig<O, S>>)): Mark<O, S>;
static handleExit({ editor, mark }: {
editor: Editor;
mark: Mark;
}): boolean;
configure(options?: Partial<Options>): Mark<Options, Storage>;
extend<ExtendedOptions = Options, ExtendedStorage = Storage, ExtendedConfig = MarkConfig<ExtendedOptions, ExtendedStorage>>(extendedConfig?: (() => Partial<ExtendedConfig>) | (Partial<ExtendedConfig> & ThisType<{
name: string;
options: ExtendedOptions;
storage: ExtendedStorage;
editor: Editor;
type: MarkType$1;
}>)): Mark<ExtendedOptions, ExtendedStorage>;
}
interface NodeConfig<Options = any, Storage = any> extends ExtendableConfig<Options, Storage, NodeConfig<Options, Storage>, NodeType$1> {
/**
* Node View
*/
addNodeView?: ((this: {
name: string;
options: Options;
storage: Storage;
editor: Editor;
type: NodeType$1;
parent: ParentConfig<NodeConfig<Options, Storage>>['addNodeView'];
}) => NodeViewRenderer | null) | null;
/**
* Defines if this node should be a top level node (doc)
* @default false
* @example true
*/
topNode?: boolean;
/**
* The content expression for this node, as described in the [schema
* guide](/docs/guide/#schema.content_expressions). When not given,
* the node does not allow any content.
*
* You can read more about it on the Prosemirror documentation here
* @see https://prosemirror.net/docs/guide/#schema.content_expressions
* @default undefined
* @example content: 'block+'
* @example content: 'headline paragraph block*'
*/
content?: NodeSpec['content'] | ((this: {
name: string;
options: Options;
storage: Storage;
parent: ParentConfig<NodeConfig<Options, Storage>>['content'];
editor?: Editor;
}) => NodeSpec['content']);
/**
* The marks that are allowed inside of this node. May be a
* space-separated string referring to mark names or groups, `"_"`
* to explicitly allow all marks, or `""` to disallow marks. When
* not given, nodes with inline content default to allowing all
* marks, other nodes default to not allowing marks.
*
* @example marks: 'strong em'
*/
marks?: NodeSpec['marks'] | ((this: {
name: string;
options: Options;
storage: Storage;
parent: ParentConfig<NodeConfig<Options, Storage>>['marks'];
editor?: Editor;
}) => NodeSpec['marks']);
/**
* The group or space-separated groups to which this node belongs,
* which can be referred to in the content expressions for the
* schema.
*
* By default Tiptap uses the groups 'block' and 'inline' for nodes. You
* can also use custom groups if you want to group specific nodes together
* and handle them in your schema.
* @example group: 'block'
* @example group: 'inline'
* @example group: 'customBlock' // this uses a custom group
*/
group?: NodeSpec['group'] | ((this: {
name: string;
options: Options;
storage: Storage;
parent: ParentConfig<NodeConfig<Options, Storage>>['group'];
editor?: Editor;
}) => NodeSpec['group']);
/**
* Should be set to true for inline nodes. (Implied for text nodes.)
*/
inline?: NodeSpec['inline'] | ((this: {
name: string;
options: Options;
storage: Storage;
parent: ParentConfig<NodeConfig<Options, Storage>>['inline'];
editor?: Editor;
}) => NodeSpec['inline']);
/**
* Can be set to true to indicate that, though this isn't a [leaf
* node](https://prosemirror.net/docs/ref/#model.NodeType.isLeaf), it doesn't have directly editable
* content and should be treated as a single unit in the view.
*
* @example atom: true
*/
atom?: NodeSpec['atom'] | ((this: {
name: string;
options: Options;
storage: Storage;
parent: ParentConfig<NodeConfig<Options, Storage>>['atom'];
editor?: Editor;
}) => NodeSpec['atom']);
/**
* Controls whether nodes of this type can be selected as a [node
* selection](https://prosemirror.net/docs/ref/#state.NodeSelection). Defaults to true for non-text
* nodes.
*
* @default true
* @example selectable: false
*/
selectable?: NodeSpec['selectable'] | ((this: {
name: string;
options: Options;
storage: Storage;
parent: ParentConfig<NodeConfig<Options, Storage>>['selectable'];
editor?: Editor;
}) => NodeSpec['selectable']);
/**
* Determines whether nodes of this type can be dragged without
* being selected. Defaults to false.
*
* @default: false
* @example: draggable: true
*/
draggable?: NodeSpec['draggable'] | ((this: {
name: string;
options: Options;
storage: Storage;
parent: ParentConfig<NodeConfig<Options, Storage>>['draggable'];
editor?: Editor;
}) => NodeSpec['draggable']);
/**
* Can be used to indicate that this node contains code, which
* causes some commands to behave differently.
*/
code?: NodeSpec['code'] | ((this: {
name: string;
options: Options;
storage: Storage;
parent: ParentConfig<NodeConfig<Options, Storage>>['code'];
editor?: Editor;
}) => NodeSpec['code']);
/**
* Controls way whitespace in this a node is parsed. The default is
* `"normal"`, which causes the [DOM parser](https://prosemirror.net/docs/ref/#model.DOMParser) to
* collapse whitespace in normal mode, and normalize it (replacing
* newlines and such with spaces) otherwise. `"pre"` causes the
* parser to preserve spaces inside the node. When this option isn't
* given, but [`code`](https://prosemirror.net/docs/ref/#model.NodeSpec.code) is true, `whitespace`
* will default to `"pre"`. Note that this option doesn't influence
* the way the node is rendered—that should be handled by `toDOM`
* and/or styling.
*/
whitespace?: NodeSpec['whitespace'] | ((this: {
name: string;
options: Options;
storage: Storage;
parent: ParentConfig<NodeConfig<Options, Storage>>['whitespace'];
editor?: Editor;
}) => NodeSpec['whitespace']);
/**
* Allows a **single** node to be set as linebreak equivalent (e.g. hardBreak).
* When converting between block types that have whitespace set to "pre"
* and don't support the linebreak node (e.g. codeBlock) and other block types
* that do support the linebreak node (e.g. paragraphs) - this node will be used
* as the linebreak instead of stripping the newline.
*
* See [linebreakReplacement](https://prosemirror.net/docs/ref/#model.NodeSpec.linebreakReplacement).
*/
linebreakReplacement?: NodeSpec['linebreakReplacement'] | ((this: {
name: string;
options: Options;
storage: Storage;
parent: ParentConfig<NodeConfig<Options, Storage>>['linebreakReplacement'];
editor?: Editor;
}) => NodeSpec['linebreakReplacement']);
/**
* When enabled, enables both
* [`definingAsContext`](https://prosemirror.net/docs/ref/#model.NodeSpec.definingAsContext) and
* [`definingForContent`](https://prosemirror.net/docs/ref/#model.NodeSpec.definingForContent).
*
* @default false
* @example isolating: true
*/
defining?: NodeSpec['defining'] | ((this: {
name: string;
options: Options;
storage: Storage;
parent: ParentConfig<NodeConfig<Options, Storage>>['defining'];
editor?: Editor;
}) => NodeSpec['defining']);
/**
* When enabled (default is false), the sides of nodes of this type
* count as boundaries that regular editing operations, like
* backspacing or lifting, won't cross. An example of a node that
* should probably have this enabled is a table cell.
*/
isolating?: NodeSpec['isolating'] | ((this: {
name: string;
options: Options;
storage: Storage;
parent: ParentConfig<NodeConfig<Options, Storage>>['isolating'];
editor?: Editor;
}) => NodeSpec['isolating']);
/**
* Associates DOM parser information with this node, which can be
* used by [`DOMParser.fromSchema`](https://prosemirror.net/docs/ref/#model.DOMParser^fromSchema) to
* automatically derive a parser. The `node` field in the rules is
* implied (the name of this node will be filled in automatically).
* If you supply your own parser, you do not need to also specify
* parsing rules in your schema.
*
* @example parseHTML: [{ tag: 'div', attrs: { 'data-id': 'my-block' } }]
*/
parseHTML?: (this: {
name: string;
options: Options;
storage: Storage;
parent: ParentConfig<NodeConfig<Options, Storage>>['parseHTML'];
editor?: Editor;
}) => NodeSpec['parseDOM'];
/**
* A description of a DOM structure. Can be either a string, which is
* interpreted as a text node, a DOM node, which is interpreted as
* itself, a `{dom, contentDOM}` object, or an array.
*
* An array describes a DOM element. The first value in the array
* should be a string—the name of the DOM element, optionally prefixed
* by a namespace URL and a space. If the second element is plain
* object, it is interpreted as a set of attributes for the element.
* Any elements after that (including the 2nd if it's not an attribute
* object) are interpreted as children of the DOM elements, and must
* either be valid `DOMOutputSpec` values, or the number zero.
*
* The number zero (pronounced “hole”) is used to indicate the place
* where a node's child nodes should be inserted. If it occurs in an
* output spec, it should be the only child element in its parent
* node.
*
* @example toDOM: ['div[data-id="my-block"]', { class: 'my-block' }, 0]
*/
renderHTML?: ((this: {
name: string;
options: Options;
storage: Storage;
parent: ParentConfig<NodeConfig<Options, Storage>>['renderHTML'];
editor?: Editor;
}, props: {
node: Node$1;
HTMLAttributes: Record<string, any>;
}) => DOMOutputSpec) | null;
/**
* renders the node as text
* @example renderText: () => 'foo
*/
renderText?: ((this: {
name: string;
options: Options;
storage: Storage;
parent: ParentConfig<NodeConfig<Options, Storage>>['renderText'];
editor?: Editor;
}, props: {
node: Node$1;
pos: number;
parent: Node$1;
index: number;
}) => string) | null;
/**
* Add attributes to the node
* @example addAttributes: () => ({ class: 'foo' })
*/
addAttributes?: (this: {
name: string;
options: Options;
storage: Storage;
parent: ParentConfig<NodeConfig<Options, Storage>>['addAttributes'];
editor?: Editor;
}) => Attributes$1 | {};
}
/**
* The Node class is used to create custom node extensions.
* @see https://tiptap.dev/api/extensions#create-a-new-extension
*/
declare class Node<Options = any, Storage = any> extends Extendable<Options, Storage, NodeConfig<Options, Storage>> {
type: string;
/**
* Create a new Node instance
* @param config - Node configuration object or a function that returns a configuration object
*/
static create<O = any, S = any>(config?: Partial<NodeConfig<O, S>> | (() => Partial<NodeConfig<O, S>>)): Node<O, S>;
configure(options?: Partial<Options>): Node<Options, Storage>;
extend<ExtendedOptions = Options, ExtendedStorage = Storage, ExtendedConfig = NodeConfig<ExtendedOptions, ExtendedStorage>>(extendedConfig?: (() => Partial<ExtendedConfig>) | (Partial<ExtendedConfig> & ThisType<{
name: string;
options: ExtendedOptions;
storage: ExtendedStorage;
editor: Editor;
type: NodeType$1;
}>)): Node<ExtendedOptions, ExtendedStorage>;
}
type PasteRuleMatch = {
index: number;
text: string;
replaceWith?: string;
match?: RegExpMatchArray;
data?: Record<string, any>;
};
type PasteRuleFinder = RegExp | ((text: string, event?: ClipboardEvent | null) => PasteRuleMatch[] | null | undefined);
/**
* Paste rules are used to react to pasted content.
* @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#paste-rules
*/
declare class PasteRule {
find: PasteRuleFinder;
handler: (props: {
state: EditorState;
range: Range;
match: ExtendedRegExpMatchArray;
commands: SingleCommands;
chain: () => ChainedCommands;
can: () => CanCommands;
pasteEvent: ClipboardEvent | null;
dropEvent: DragEvent | null;
}) => void | null;
constructor(config: {
find: PasteRuleFinder;
handler: (props: {
can: () => CanCommands;
chain: () => ChainedCommands;
commands: SingleCommands;
dropEvent: DragEvent | null;
match: ExtendedRegExpMatchArray;
pasteEvent: ClipboardEvent | null;
range: Range;
state: EditorState;
}) => void | null;
});
}
/**
* Create an paste rules plugin. When enabled, it will cause pasted
* text that matches any of the given rules to trigger the rule’s
* action.
*/
declare function pasteRulesPlugin(props: {
editor: Editor;
rules: PasteRule[];
}): Plugin[];
interface ExtendableConfig<Options = any, Storage = any, Config extends ExtensionConfig<Options, Storage> | NodeConfig<Options, Storage> | MarkConfig<Options, Storage> | ExtendableConfig<Options, Storage> = ExtendableConfig<Options, Storage, any, any>, PMType = any> {
/**
* The extension name - this must be unique.
* It will be used to identify the extension.
*
* @example 'myExtension'
*/
name: string;
/**
* The priority of your extension. The higher, the earlier it will be called
* and will take precedence over other extensions with a lower priority.
* @default 100
* @example 101
*/
priority?: number;
/**
* This method will add options to this extension
* @see https://tiptap.dev/docs/editor/guide/custom-extensions#settings
* @example
* addOptions() {
* return {
* myOption: 'foo',
* myOtherOption: 10,
* }
*/
addOptions?: (this: {
name: string;
parent: ParentConfig<Config>['addOptions'];
}) => Options;
/**
* The default storage this extension can save data to.
* @see https://tiptap.dev/docs/editor/guide/custom-extensions#storage
* @example
* defaultStorage: {
* prefetchedUsers: [],
* loading: false,
* }
*/
addStorage?: (this: {
name: string;
options: Options;
parent: ParentConfig<Config>['addStorage'];
}) => Storage;
/**
* This function adds globalAttributes to specific nodes.
* @see https://tiptap.dev/docs/editor/guide/custom-extensions#global-attributes
* @example
* addGlobalAttributes() {
* return [
* {
// Extend the following extensions
* types: [
* 'heading',
* 'paragraph',
* ],
* // … with those attributes
* attributes: {
* textAlign: {
* default: 'left',
* renderHTML: attributes => ({
* style: `text-align: ${attributes.textAlign}`,
* }),
* parseHTML: element => element.style.textAlign || 'left',
* },
* },
* },
* ]
* }
*/
addGlobalAttributes?: (this: {
name: string;
options: Options;
storage: Storage;
extensions: (Node | Mark)[];
parent: ParentConfig<Config>['addGlobalAttributes'];
}) => GlobalAttributes;
/**
* This function adds commands to the editor
* @see https://tiptap.dev/docs/editor/guide/custom-extensions#commands
* @example
* addCommands() {
* return {
* myCommand: () => ({ chain }) => chain().setMark('type', 'foo').run(),
* }
* }
*/
addCommands?: (this: {
name: string;
options: Options;
storage: Storage;
editor: Editor;
type: PMType;
parent: ParentConfig<Config>['addCommands'];
}) => Partial<RawCommands>;
/**
* This function registers keyboard shortcuts.
* @see https://tiptap.dev/docs/editor/guide/custom-extensions#keyboard-shortcuts
* @example
* addKeyboardShortcuts() {
* return {
* 'Mod-l': () => this.editor.commands.toggleBulletList(),
* }
* },
*/
addKeyboardShortcuts?: (this: {
name: string;
options: Options;
storage: Storage;
editor: Editor;
type: PMType;
parent: ParentConfig<Config>['addKeyboardShortcuts'];
}) => {
[key: string]: KeyboardShortcutCommand;
};
/**
* This function adds input rules to the editor.
* @see https://tiptap.dev/docs/editor/guide/custom-extensions#input-rules
* @example
* addInputRules() {
* return [
* markInputRule({
* find: inputRegex,
* type: this.type,
* }),
* ]
* },
*/
addInputRules?: (this: {
name: string;
options: Options;
storage: Storage;
editor: Editor;
type: PMType;
parent: ParentConfig<Config>['addInputRules'];
}) => InputRule[];
/**
* This function adds paste rules to the editor.
* @see https://tiptap.dev/docs/editor/guide/custom-extensions#paste-rules
* @example
* addPasteRules() {
* return [
* markPasteRule({
* find: pasteRegex,
* type: this.type,
* }),
* ]
* },
*/
addPasteRules?: (this: {
name: string;
options: Options;
storage: Storage;
editor: Editor;
type: PMType;
parent: ParentConfig<Config>['addPasteRules'];
}) => PasteRule[];
/**
* This function adds Prosemirror plugins to the editor
* @see https://tiptap.dev/docs/editor/guide/custom-extensions#prosemirror-plugins
* @example
* addProseMirrorPlugins() {
* return [
* customPlugin(),
* ]
* }
*/
addProseMirrorPlugins?: (this: {
name: string;
options: Options;
storage: Storage;
editor: Editor;
type: PMType;
parent: ParentConfig<Config>['addProseMirrorPlugins'];
}) => Plugin[];
/**
* This function adds additional extensions to the editor. This is useful for
* building extension kits.
* @example
* addExtensions() {
* return [
* BulletList,
* OrderedList,
* ListItem
* ]
* }
*/
addExtensions?: (this: {
name: string;
options: Options;
storage: Storage;
parent: ParentConfig<Config>['addExtensions'];
}) => Extensions;
/**
* The markdown token name
*
* This is the name of the token that this extension uses to parse and render markdown and comes from the Marked Lexer.
*
* @see https://github.com/markedjs/marked/blob/master/src/Tokens.ts
*
*/
markdownTokenName?: string;
/**
* The parse function used by the markdown parser to convert markdown tokens to ProseMirror nodes.
*/
parseMarkdown?: (token: MarkdownToken, helpers: MarkdownParseHelpers) => MarkdownParseResult;
/**
* The serializer function used by the markdown serializer to convert ProseMirror nodes to markdown tokens.
*/
renderMarkdown?: (node: JSONContent, helpers: MarkdownRendererHelpers, ctx: RenderContext) => string;
/**
* The markdown tokenizer responsible for turning a markdown string into tokens
*
* Custom tokenizers are only needed when you want to parse non-standard markdown token.
*/
markdownTokenizer?: MarkdownTokenizer;
/**
* Optional markdown options for indentation
*/
markdownOptions?: {
/**
* Defines if this markdown element should indent it's child elements
*/
indentsContent?: boolean;
};
/**
* This function extends the schema of the node.
* @example
* extendNodeSchema() {
* return {
* group: 'inline',
* selectable: false,
* }
* }
*/
extendNodeSchema?: ((this: {
name: string;
options: Options;
storage: Storage;
parent: ParentConfig<Config>['extendNodeSchema'];
}, extension: Node) => Record<string, any>) | null;
/**
* This function extends the schema of the mark.
* @example
* extendMarkSchema() {
* return {
* group: 'inline',
* selectable: false,
* }
* }
*/
extendMarkSchema?: ((this: {
name: string;
options: Options;
storage: Storage;
parent: ParentConfig<Config>['extendMarkSchema'];
}, extension: Mark) => Record<string, any>) | null;
/**
* The editor is not ready yet.
*/
onBeforeCreate?: ((this: {
name: string;
options: Options;
storage: Storage;
editor: Editor;
type: PMType;
parent: ParentConfig<Config>['onBeforeCreate'];
}, event: EditorEvents['beforeCreate']) => void) | null;
/**
* The editor is ready.
*/
onCreate?: ((this: {
name: string;
options: Options;
storage: Storage;
editor: Editor;
type: PMType;
parent: ParentConfig<Config>['onCreate'];
}, event: EditorEvents['create']) => void) | null;
/**
* The content has changed.
*/
onUpdate?: ((this: {
name: string;
options: Options;
storage: Storage;
editor: Editor;
type: PMType;
parent: ParentConfig<Config>['onUpdate'];
}, event: EditorEvents['update']) => void) | null;
/**
* The selection has changed.
*/
onSelectionUpdate?: ((this: {
name: string;
options: Options;
storage: Storage;
editor: Editor;
type: PMType;
parent: ParentConfig<Config>['onSelectionUpdate'];
}, event: EditorEvents['selectionUpdate']) => void) | null;
/**
* The editor state has changed.
*/
onTransaction?: ((this: {
name: string;
options: Options;
storage: Storage;
editor: Editor;
type: PMType;
parent: ParentConfig<Config>['onTransaction'];
}, event: EditorEvents['transaction']) => void) | null;
/**
* The editor is focused.
*/
onFocus?: ((this: {
name: string;
options: Options;
storage: Storage;
editor: Editor;
type: PMType;
parent: ParentConfig<Config>['onFocus'];
}, event: EditorEvents['focus']) => void) | null;
/**
* The editor isn’t focused anymore.
*/
onBlur?: ((this: {
name: string;
options: Options;
storage: Storage;
editor: Editor;
type: PMType;
parent: ParentConfig<Config>['onBlur'];
}, event: EditorEvents['blur']) => void) | null;
/**
* The editor is destroyed.
*/
onDestroy?: ((this: {
name: string;
options: Options;
storage: Storage;
editor: Editor;
type: PMType;
parent: ParentConfig<Config>['onDestroy'];
}, event: EditorEvents['destroy']) => void) | null;
}
declare class Extendable<Options = any, Storage = any, Config = ExtensionConfig<Options, Storage> | NodeConfig<Options, Storage> | MarkConfig<Options, Storage>> {
type: string;
parent: Extendable | null;
child: Extendable | null;
name: string;
config: Config;
constructor(config?: Partial<Config>);
get options(): Options;
get storage(): Readonly<Storage>;
configure(options?: Partial<Options>): Extendable<Options, Storage, ExtensionConfig<Options, Storage> | NodeConfig<Options, Storage> | MarkConfig<Options, Storage>>;
extend<ExtendedOptions = Options, ExtendedStorage = Storage, ExtendedConfig = ExtensionConfig<ExtendedOptions, ExtendedStorage> | NodeConfig<ExtendedOptions, ExtendedStorage> | MarkConfig<ExtendedOptions, ExtendedStorage>>(extendedConfig?: Partial<ExtendedConfig>): Extendable<ExtendedOptions, ExtendedStorage>;
}
type AnyConfig = ExtensionConfig | NodeConfig | MarkConfig;
type AnyExtension = Extendable;
type Extensions = AnyExtension[];
type ParentConfig<T> = Partial<{
[P in keyof T]: Required<T>[P] extends (...args: any) => any ? (...args: Parameters<Required<T>[P]>) => ReturnType<Required<T>[P]> : T[P];
}>;
type Primitive = null | undefined | string | number | boolean | symbol | bigint;
type RemoveThis<T> = T extends (...args: any) => any ? (...args: Parameters<T>) => ReturnType<T> : T;
type MaybeReturnType<T> = T extends (...args: any) => any ? ReturnType<T> : T;
type MaybeThisParameterType<T> = Exclude<T, Primitive> extends (...args: any) => any ? ThisParameterType<Exclude<T, Primitive>> : any;
interface EditorEvents {
mount: {
/**
* The editor instance
*/
editor: Editor;
};
unmount: {
/**
* The editor instance
*/
editor: Editor;
};
beforeCreate: {
/**
* The editor instance
*/
editor: Editor;
};
create: {
/**
* The editor instance
*/
editor: Editor;
};
contentError: {
/**
* The editor instance
*/
editor: Editor;
/**
* The error that occurred while parsing the content
*/
error: Error;
/**
* If called, will re-initialize the editor with the collaboration extension removed.
* This will prevent syncing back deletions of content not present in the current schema.
*/
disableCollaboration: () => void;
};
update: {
/**
* The editor instance
*/
editor: Editor;
/**
* The transaction that caused the update
*/
transaction: Transaction;
/**
* Appended transactions that were added to the initial transaction by plugins
*/
appendedTransactions: Transaction[];
};
selectionUpdate: {
/**
* The editor instance
*/
editor: Editor;
/**
* The transaction that caused the selection update
*/
transaction: Transaction;
};
beforeTransaction: {
/**
* The editor instance
*/
editor: Editor;
/**
* The transaction that will be applied
*/
transaction: Transaction;
/**
* The next state of the editor after the transaction is applied
*/
nextState: EditorState;
};
transaction: {
/**
* The editor instance
*/
editor: Editor;
/**
* The initial transaction
*/
transaction: Transaction;
/**
* Appended transactions that were added to the initial transaction by plugins
*/
appendedTransactions: Transaction[];
};
focus: {
/**
* The editor instance
*/
editor: Editor;
/**
* The focus event
*/
event: FocusEvent;
/**
* The transaction that caused the focus
*/
transaction: Transaction;
};
blur: {
/**
* The editor instance
*/
editor: Editor;
/**
* The focus event
*/
event: FocusEvent;
/**
* The transaction that caused the blur
*/
transaction: Transaction;
};
destroy: void;
paste: {
/**
* The editor instance
*/
editor: Editor;
/**
* The clipboard event
*/
event: ClipboardEvent;
/**
* The slice that was pasted
*/
slice: Slice;
};
drop: {
/**
* The editor instance
*/
editor: Editor;
/**
* The drag event
*/
event: DragEvent;
/**
* The slice that was dropped
*/
slice: Slice;
/**
* Whether the content was moved (true) or copied (false)
*/
moved: boolean;
};
delete: {
/**
* The editor instance
*/
editor: Editor;
/**
* The range of the deleted content (before the deletion)
*/
deletedRange: Range;
/**
* The new range of positions of where the deleted content was in the new document (after the deletion)
*/
newRange: Range;
/**
* The transaction that caused the deletion
*/
transaction: Transaction;
/**
* The combined transform (including all appended transactions) that caused the deletion
*/
combinedTransform: Transform;
/**
* Whether the deletion was partial (only a part of this content was deleted)
*/
partial: boolean;
/**
* This is the start position of the mark in the document (before the deletion)
*/
from: number;
/**
* This is the end position of the mark in the document (before the deletion)
*/
to: number;
} & ({
/**
* The content that was deleted
*/
type: 'node';
/**
* The node which the deletion occurred in
* @note This can be a parent node of the deleted content
*/
node: Node$1;
/**
* The new start position of the node in the document (after the deletion)
*/
newFrom: number;
/**
* The new end position of the node in the document (after the deletion)
*/
newTo: number;
} | {
/**
* The content that was deleted
*/
type: 'mark';
/**
* The mark that was deleted
*/
mark: Mark$1;
});
}
type EnableRules = (AnyExtension | string)[] | boolean;
interface EditorOptions {
/**
* The element to bind the editor to:
* - If an `Element` is passed, the editor will be mounted appended to that element
* - If `null` is passed, the editor will not be mounted automatically
* - If an object with a `mount` property is passed, the editor will be mounted to that element
* - If a function is passed, it will be called with the editor's element, which should place the editor within the document
*/
element: Element | {
mount: HTMLElement;
} | ((editor: HTMLElement) => void) | null;
/**
* The content of the editor (HTML, JSON, or a JSON array)
*/
content: Content;
/**
* The extensions to use
*/
extensions: Extensions;
/**
* Whether to inject base CSS styles
*/
injectCSS: boolean;
/**
* A nonce to use for CSP while injecting styles
*/
injectNonce: string | undefined;
/**
* The editor's initial focus position
*/
autofocus: FocusPosition;
/**
* Whether the editor is editable
*/
editable: boolean;
/**
* The editor's props
*/
editorProps: EditorProps;
/**
* The editor's content parser options
*/
parseOptions: ParseOptions;
/**
* The editor's core extension options
*/
coreExtensionOptions?: {
clipboardTextSerializer?: {
blockSeparator?: string;
};
delete?: {
/**
* Whether the `delete` extension should be called asynchronously to avoid blocking the editor while processing deletions
* @default true deletion events are called asynchronously
*/
async?: boolean;
/**
* Allows filtering the transactions that are processed by the `delete` extension.
* If the function returns `true`, the transaction will be ignored.
*/
filterTransaction?: (transaction: Transaction) => boolean;
};
};
/**
* Whether to enable input rules behavior
*/
enableInputRules: EnableRules;
/**
* Whether to enable paste rules behavior
*/
enablePasteRules: EnableRules;
/**
* Determines whether core extensions are enabled.
*
* If set to `false`, all core extensions will be disabled.
* To disable specific core extensions, provide an object where the keys are the extension names and the values are `false`.
* Extensions not listed in the object will remain enabled.
*
* @example
* // Disable all core extensions
* enabledCoreExtensions: false
*
* @example
* // Disable only the keymap core extension
* enabledCoreExtensions: { keymap: false }
*
* @default true
*/
enableCoreExtensions?: boolean | Partial<Record<'editable' | 'clipboardTextSerializer' | 'commands' | 'focusEvents' | 'keymap' | 'tabindex' | 'drop' | 'paste' | 'delete', false>>;
/**
* If `true`, the editor will check the content for errors on initialization.
* Emitting the `contentError` event if the content is invalid.
* Which can be used to show a warning or error message to the user.
* @default false
*/
enableContentCheck: boolean;
/**
* If `true`, the editor will emit the `contentError` event if invalid content is
* encountered but `enableContentCheck` is `false`. This lets you preserve the
* invalid editor content while still showing a warning or error message to
* the user.
*
* @default false
*/
emitContentError: boolean;
/**
* Called before the editor is constructed.
*/
onBeforeCreate: (props: EditorEvents['beforeCreate']) => void;
/**
* Called after the editor is constructed.
*/
onCreate: (props: EditorEvents['create']) => void;
/**
* Called when the editor is mounted.
*/
onMount: (props: EditorEvents['mount']) => void;
/**
* Called when the editor is unmounted.
*/
onUnmount: (props: EditorEvents['unmount']) => void;
/**
* Called when the editor encounters an error while parsing the content.
* Only enabled if `enableContentCheck` is `true`.
*/
onContentError: (props: EditorEvents['contentError']) => void;
/**
* Called when the editor's content is updated.
*/
onUpdate: (props: EditorEvents['update']) => void;
/**
* Called when the editor's selection is updated.
*/
onSelectionUpdate: (props: EditorEvents['selectionUpdate']) => void;
/**
* Called after a transaction is applied to the editor.
*/
onTransaction: (props: EditorEvents['transaction']) => void;
/**
* Called on focus events.
*/
onFocus: (props: EditorEvents['focus']) => void;
/**
* Called on blur events.
*/
onBlur: (props: EditorEvents['blur']) => void;
/**
* Called when the editor is destroyed.
*/
onDestroy: (props: EditorEvents['destroy']) => void;
/**
* Called when content is pasted into the editor.
*/
onPaste: (e: ClipboardEvent, slice: Slice) => void;
/**
* Called when content is dropped into the editor.
*/
onDrop: (e: DragEvent, slice: Slice, moved: boolean) => void;
/**
* Called when content is deleted from the editor.
*/
onDelete: (props: EditorEvents['delete']) => void;
}
/**
* The editor's content as HTML
*/
type HTMLContent = string;
/**
* Loosely describes a JSON representation of a Prosemirror document or node
*/
type JSONContent = {
type?: string;
attrs?: Record<string, any> | undefined;
content?: JSONContent[];
marks?: {
type: string;
attrs?: Record<string, any>;
[key: string]: any;
}[];
text?: string;
[key: string]: any;
};
/**
* A mark type is either a JSON representation of a mark or a Prosemirror mark instance
*/
type MarkType<Type extends string | {
name: string;
} = any, TAttributes extends undefined | Record<string, any> = any> = {
type: Type;
attrs: TAttributes;
};
/**
* A node type is either a JSON representation of a node or a Prosemirror node instance
*/
type NodeType<Type extends string | {
name: string;
} = any, TAttributes extends undefined | Record<string, any> = any, NodeMarkType extends MarkType = any, TContent extends (NodeType | TextType)[] = any> = {
type: Type;
attrs: TAttributes;
content?: TContent;
marks?: NodeMarkType[];
};
/**
* A node type is either a JSON representation of a doc node or a Prosemirror doc node instance
*/
type DocumentType<TDocAttributes extends Record<string, any> | undefined = Record<string, any>, TContentType extends NodeType[] = NodeType[]> = Omit<NodeType<'doc', TDocAttributes, never, TContentType>, 'marks' | 'content'> & {
content: TContentType;
};
/**
* A node type is either a JSON representation of a text node or a Prosemirror text node instance
*/
type TextType<TMarkType extends MarkType = MarkType> = {
type: 'text';
text: string;
marks: TMarkType[];
};
/**
* Describes the output of a `renderHTML` function in prosemirror
* @see https://prosemirror.net/docs/ref/#model.DOMOutputSpec
*/
type DOMOutputSpecArray$1 = [string] | [string, Record<string, any>] | [string, 0] | [string, Record<string, any>, 0] | [string, Record<string, any>, DOMOutputSpecArray$1 | 0] | [string, DOMOutputSpecArray$1];
type Content = HTMLContent | JSONContent | JSONContent[] | null;
type CommandProps = {
editor: Editor;
tr: Transaction;
commands: SingleCommands;
can: () => CanCommands;
chain: () => ChainedCommands;
state: EditorState;
view: EditorView;
dispatch: ((args?: any) => any) | undefined;
};
type Command = (props: CommandProps) => boolean;
type CommandSpec = (...args: any[]) => Command;
type KeyboardShortcutCommand = (props: {
editor: Editor;
}) => boolean;
type Attribute = {
default?: any;
validate?: string | ((value: any) => void);
rendered?: boolean;
renderHTML?: ((attributes: Record<string, any>) => Record<string, any> | null) | null;
parseHTML?: ((element: HTMLElement) => any | null) | null;
keepOnSplit?: boolean;
isRequired?: boolean;
};
type Attributes$1 = {
[key: string]: Attribute;
};
type ExtensionAttribute = {
type: string;
name: string;
attribute: Required<Omit<Attribute, 'validate'>> & Pick<Attribute, 'validate'>;
};
type GlobalAttributes = {
/**
* The node & mark types this attribute should be applied to.
*/
types: string[];
/**
* The attributes to add to the node or mark types.
*/
attributes: Record<string, Attribute | undefined>;
}[];
type PickValue<T, K extends keyof T> = T[K];
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
type Diff<T extends keyof any, U extends keyof any> = ({
[P in T]: P;
} & {
[P in U]: never;
} & {
[x: string]: never;
})[T];
type Overwrite<T, U> = Pick<T, Diff<keyof T, keyof U>> & U;
type ValuesOf<T> = T[keyof T];
type KeysWithTypeOf<T, Type> = {
[P in keyof T]: T[P] extends Type ? P : never;
}[keyof T];
type DOMNode = InstanceType<typeof window.Node>;
/**
* prosemirror-view does not export the `type` property of `Decoration`.
* So, this defines the `DecorationType` interface to include the `type` property.
*/
interface DecorationType {
spec: any;
map(mapping: Mappable, span: Decoration, offset: number, oldOffset: number): Decoration | null;
valid(node: Node, span: Decoration): boolean;
eq(other: DecorationType): boolean;
destroy(dom: DOMNode): void;
readonly attrs: DecorationAttrs;
}
/**
* prosemirror-view does not export the `type` property of `Decoration`.
* This adds the `type` property to the `Decoration` type.
*/
type DecorationWithType = Decoration & {
type: DecorationType;
};
interface NodeViewProps extends NodeViewRendererProps {
decorations: readonly DecorationWithType[];
selected: boolean;
updateAttributes: (attributes: Record<string, any>) => void;
deleteNode: () => void;
}
interface NodeViewRendererOptions {
stopEvent: ((props: {
event: Event;
}) => boolean) | null;
ignoreMutation: ((props: {
mutation: ViewMutationRecord;
}) => boolean) | null;
contentDOMElementTag: string;
}
interface NodeViewRendererProps {
/**
* The node that is being rendered.
*/
node: Parameters<NodeViewConstructor>[0];
/**
* The editor's view.
*/
view: Parameters<NodeViewConstructor>[1];
/**
* A function that can be called to get the node's current position in the document.
*/
getPos: Parameters<NodeViewConstructor>[2];
/**
* is an array of node or inline decorations that are active around the node.
* They are automatically drawn in the normal way, and you will usually just want to ignore this, but they can also be used as a way to provide context information to the node view without adding it to the document itself.
*/
decorations: Parameters<NodeViewConstructor>[3];
/**
* holds the decorations for the node's content. You can safely ignore this if your view has no content or a contentDOM property, since the editor will draw the decorations on the content.
* But if you, for example, want to create a nested editor with the content, it may make sense to provide it with the inner decorations.
*/
innerDecorations: Parameters<NodeViewConstructor>[4];
/**
* The editor instance.
*/
editor: Editor;
/**
* The extension that is responsible for the node.
*/
extension: Node;
/**
* The HTML attributes that should be added to the node's DOM element.
*/
HTMLAttributes: Record<string, any>;
}
type NodeViewRenderer = (props: NodeViewRendererProps) => NodeView$1;
interface MarkViewProps extends MarkViewRendererProps {
}
interface MarkViewRendererProps {
/**
* The node that is being rendered.
*/
mark: Parameters<MarkViewConstructor>[0];
/**
* The editor's view.
*/
view: Parameters<MarkViewConstructor>[1];
/**
* indicates whether the mark's content is inline
*/
inline: Parameters<MarkViewConstructor>[2];
/**
* The editor instance.
*/
editor: Editor;
/**
* The extension that is responsible for the mark.
*/
extension: Mark;
/**
* The HTML attribut