Comprehensive wrapper around ProseMirror packages providing unified entry point for rich text editing functionality in Tiptap framework
—
The document model provides the core data structures for representing rich text documents and defining their allowed structure through schemas. This forms the foundation of ProseMirror's content management system.
Defines the structure and rules for documents, including what nodes and marks are allowed.
/**
* Schema defines the structure of documents and available node/mark types
*/
class Schema {
/**
* Create a new schema from a specification
* @param spec - Schema specification defining nodes, marks, and rules
*/
constructor(spec: SchemaSpec);
/** The defined node types, keyed by name */
readonly nodes: { [name: string]: NodeType };
/** The defined mark types, keyed by name */
readonly marks: { [name: string]: MarkType };
/** The schema specification this was created from */
readonly spec: SchemaSpec;
/** Get a node type by name */
node(name: string): NodeType;
/** Get a mark type by name */
mark(name: string): MarkType;
/** Get the default top-level node type */
readonly topNodeType: NodeType;
/** Check if a given text can appear at the given position */
text(text: string, marks?: Mark[]): Node | null;
}
interface SchemaSpec {
/** Object mapping node names to node specs */
nodes: { [name: string]: NodeSpec };
/** Object mapping mark names to mark specs */
marks?: { [name: string]: MarkSpec };
/** The name of the default top-level node type */
topNode?: string;
}Usage Examples:
import { Schema } from "@tiptap/pm/model";
// Create a simple schema
const mySchema = new Schema({
nodes: {
doc: { content: "paragraph+" },
paragraph: { content: "text*", group: "block" },
text: { group: "inline" },
},
marks: {
strong: {},
em: {},
},
});
// Get node types
const docType = mySchema.nodes.doc;
const paragraphType = mySchema.nodes.paragraph;Define the characteristics and behavior of different node types in the document.
/**
* A node type represents a kind of node that may occur in a document
*/
class NodeType {
/** The name of this node type */
readonly name: string;
/** The schema this node type belongs to */
readonly schema: Schema;
/** The spec this node type was created from */
readonly spec: NodeSpec;
/** True if this node type is inline */
readonly isInline: boolean;
/** True if this node type is a block */
readonly isBlock: boolean;
/** True if this node is an atom (cannot have content edited directly) */
readonly isAtom: boolean;
/** True if this node type is the schema's top node type */
readonly isTop: boolean;
/** True if this node allows marks */
readonly allowsMarks: boolean;
/**
* Create a node of this type
* @param attrs - The node's attributes
* @param content - The node's content
* @param marks - The node's marks
*/
create(attrs?: Attrs, content?: Fragment | Node | Node[], marks?: Mark[]): Node;
/**
* Create a node with the given content and attributes, checking validity
*/
createChecked(attrs?: Attrs, content?: Fragment | Node | Node[], marks?: Mark[]): Node;
/**
* Create a node and fill any required children
*/
createAndFill(attrs?: Attrs, content?: Fragment | Node | Node[], marks?: Mark[]): Node | null;
}
interface NodeSpec {
/** Expression describing allowed content */
content?: string;
/** Expression describing allowed marks */
marks?: string;
/** Nodes group this node belongs to */
group?: string;
/** Whether this node type is inline */
inline?: boolean;
/** Whether this node is an atom */
atom?: boolean;
/** Whether this node is selectable */
selectable?: boolean;
/** Whether this node can be dragged */
draggable?: boolean;
/** Node's attributes */
attrs?: { [name: string]: AttributeSpec };
/** Rules for parsing DOM to this node */
parseDOM?: ParseRule[];
/** Function to serialize this node to DOM */
toDOM?: (node: Node) => DOMOutputSpec;
}Represents individual nodes in the document tree structure.
/**
* A node in a ProseMirror document
*/
class Node {
/** The node's type */
readonly type: NodeType;
/** The node's attributes */
readonly attrs: Attrs;
/** The node's content as a Fragment */
readonly content: Fragment;
/** The marks applied to this node */
readonly marks: Mark[];
/** The number of children this node has */
readonly childCount: number;
/** The size of this node */
readonly nodeSize: number;
/** True if this node is an atom */
readonly isAtom: boolean;
/** True if this node is inline */
readonly isInline: boolean;
/** True if this node is a block */
readonly isBlock: boolean;
/** True if this node is a text node */
readonly isText: boolean;
/** True if this node is a leaf (no children) */
readonly isLeaf: boolean;
/**
* Get the child node at the given index
*/
child(index: number): Node;
/**
* Get the child node at the given offset
*/
childAfter(pos: number): { node: Node; index: number; offset: number } | null;
/**
* Get the child node before the given offset
*/
childBefore(pos: number): { node: Node; index: number; offset: number } | null;
/**
* Call a function for each child node
*/
forEach(f: (node: Node, offset: number, index: number) => void): void;
/**
* Create a new node with the same type and attributes but different content
*/
copy(content?: Fragment): Node;
/**
* Check if this node has the same markup as another
*/
sameMarkup(other: Node): boolean;
/**
* Get a text representation of this node
*/
textContent: string;
/**
* Resolve a position in this node's content
*/
resolve(pos: number): ResolvedPos;
/**
* Find all positions in this node that match a predicate
*/
descendants(f: (node: Node, pos: number, parent: Node) => boolean | void): void;
}Represents collections of nodes, used for document content.
/**
* A fragment represents a node's collection of child nodes
*/
class Fragment {
/** The number of child nodes in this fragment */
readonly size: number;
/** The combined size of all nodes in this fragment */
readonly nodeSize: number;
/**
* Create a fragment from an array of nodes
*/
static from(nodes?: Node[] | Node | Fragment | null): Fragment;
/** The empty fragment */
static empty: Fragment;
/**
* Get the child node at the given index
*/
child(index: number): Node;
/**
* Call a function for each child node
*/
forEach(f: (node: Node, offset: number, index: number) => void): void;
/**
* Find the first position at which this fragment differs from another
*/
findDiffStart(other: Fragment, pos?: number): number | null;
/**
* Find the last position at which this fragment differs from another
*/
findDiffEnd(other: Fragment, pos?: number, otherPos?: number): { a: number; b: number } | null;
/**
* Create new fragment with nodes appended
*/
append(other: Fragment): Fragment;
/**
* Cut out the part of the fragment between the given positions
*/
cut(from: number, to?: number): Fragment;
/**
* Replace the part between from and to with the given fragment
*/
replaceChild(index: number, node: Node): Fragment;
/**
* Get the text content of the fragment
*/
readonly textContent: string;
/**
* Convert the fragment to a JSON representation
*/
toJSON(): any;
/**
* Create a fragment from a JSON representation
*/
static fromJSON(schema: Schema, json: any): Fragment;
}Define and represent text annotations like bold, italic, links, etc.
/**
* A mark type represents a type of mark that can be applied to text
*/
class MarkType {
/** The name of this mark type */
readonly name: string;
/** The schema this mark type belongs to */
readonly schema: Schema;
/** The spec this mark type was created from */
readonly spec: MarkSpec;
/**
* Create a mark of this type
*/
create(attrs?: Attrs): Mark;
/**
* Check if the given set of marks has a mark of this type
*/
isInSet(set: Mark[]): Mark | null;
/**
* Remove marks of this type from the given set
*/
removeFromSet(set: Mark[]): Mark[];
}
/**
* A mark represents a piece of information attached to inline content
*/
class Mark {
/** The mark's type */
readonly type: MarkType;
/** The mark's attributes */
readonly attrs: Attrs;
/**
* Add this mark to a set of marks
*/
addToSet(set: Mark[]): Mark[];
/**
* Remove this mark from a set of marks
*/
removeFromSet(set: Mark[]): Mark[];
/**
* Check if this mark is in a set of marks
*/
isInSet(set: Mark[]): boolean;
/**
* Check if this mark has the same type and attributes as another
*/
eq(other: Mark): boolean;
/**
* Convert the mark to a JSON representation
*/
toJSON(): any;
/**
* Create a mark from a JSON representation
*/
static fromJSON(schema: Schema, json: any): Mark;
}
interface MarkSpec {
/** Mark's attributes */
attrs?: { [name: string]: AttributeSpec };
/** Whether this mark should be inclusive (continue when typing at boundaries) */
inclusive?: boolean;
/** Other mark types this mark excludes */
excludes?: string;
/** Group this mark belongs to */
group?: string;
/** Rules for parsing DOM to this mark */
parseDOM?: ParseRule[];
/** Function to serialize this mark to DOM */
toDOM?: (mark: Mark, inline: boolean) => DOMOutputSpec;
}Provides detailed information about positions within documents.
/**
* Represents a position in a document with context information
*/
class ResolvedPos {
/** The position this was resolved from */
readonly pos: number;
/** Path to this position */
readonly path: (Node | number)[];
/** The depth of this position */
readonly depth: number;
/** The parent node at the outermost level */
readonly parent: Node;
/** The document this position is in */
readonly doc: Node;
/**
* Get the parent node at the given depth
*/
node(depth?: number): Node;
/**
* Get the index at the given depth
*/
index(depth?: number): number;
/**
* Get the index after this position at the given depth
*/
indexAfter(depth?: number): number;
/**
* Get the start position of the parent at the given depth
*/
start(depth?: number): number;
/**
* Get the end position of the parent at the given depth
*/
end(depth?: number): number;
/**
* Get the position before this position
*/
before(depth?: number): number;
/**
* Get the position after this position
*/
after(depth?: number): number;
/**
* Get the text offset of this position
*/
textOffset: number;
/**
* Get the marks active at this position
*/
marks(): Mark[];
}Convert between DOM and ProseMirror document structures.
/**
* Parser for converting DOM content to ProseMirror documents
*/
class DOMParser {
/** The schema this parser uses */
readonly schema: Schema;
/** The parsing rules */
readonly rules: ParseRule[];
/**
* Create a DOM parser
*/
static fromSchema(schema: Schema): DOMParser;
/**
* Parse a DOM node to a ProseMirror document
*/
parse(dom: Element, options?: ParseOptions): Node;
/**
* Parse a DOM node to a ProseMirror fragment
*/
parseFragment(dom: Element, options?: ParseOptions): Fragment;
/**
* Parse content from a string
*/
parseSlice(dom: Element, options?: ParseOptions): Slice;
}
/**
* Serializer for converting ProseMirror documents to DOM
*/
class DOMSerializer {
/** Node serialization functions */
readonly nodes: { [name: string]: (node: Node) => DOMOutputSpec };
/** Mark serialization functions */
readonly marks: { [name: string]: (mark: Mark, inline: boolean) => DOMOutputSpec };
/**
* Create a DOM serializer from a schema
*/
static fromSchema(schema: Schema): DOMSerializer;
/**
* Serialize a fragment to DOM
*/
serializeFragment(fragment: Fragment, options?: { document?: Document }): DocumentFragment;
/**
* Serialize a node to DOM
*/
serializeNode(node: Node, options?: { document?: Document }): Element;
}
interface ParseRule {
/** Tag name to match */
tag?: string;
/** CSS selector to match */
match?: (node: Element) => boolean;
/** Priority of this rule */
priority?: number;
/** Node type to create */
node?: string;
/** Mark type to create */
mark?: string;
/** Whether to ignore content */
ignore?: boolean;
/** How to get attributes */
attrs?: Attrs | ((node: Element) => Attrs);
/** How to handle content */
contentElement?: string | ((node: Element) => Element);
}
interface ParseOptions {
/** Whether to preserve whitespace */
preserveWhitespace?: boolean | "full";
/** Node to find parsing position in */
findPositions?: { node: Element; offset: number }[];
/** Starting position */
from?: number;
/** Ending position */
to?: number;
}
type DOMOutputSpec = string | Element | [string, ...any[]];interface AttributeSpec {
/** Default value for this attribute */
default?: any;
/** Validation function */
validate?: (value: any) => boolean;
}
type Attrs = { [key: string]: any };
interface Slice {
/** The content of the slice */
content: Fragment;
/** Open depth at the start */
openStart: number;
/** Open depth at the end */
openEnd: number;
}Install with Tessl CLI
npx tessl i tessl/npm-tiptap--pm