or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

collaboration.mdcommands-and-editing.mdcursors-and-enhancements.mdhistory.mdindex.mdinput-and-keymaps.mdmarkdown.mdmenus-and-ui.mdmodel-and-schema.mdschema-definitions.mdstate-management.mdtables.mdtransformations.mdview-and-rendering.md

model-and-schema.mddocs/

0

# Document Model and Schema

1

2

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.

3

4

## Capabilities

5

6

### Schema Definition

7

8

Defines the structure and rules for documents, including what nodes and marks are allowed.

9

10

```typescript { .api }

11

/**

12

* Schema defines the structure of documents and available node/mark types

13

*/

14

class Schema {

15

/**

16

* Create a new schema from a specification

17

* @param spec - Schema specification defining nodes, marks, and rules

18

*/

19

constructor(spec: SchemaSpec);

20

21

/** The defined node types, keyed by name */

22

readonly nodes: { [name: string]: NodeType };

23

24

/** The defined mark types, keyed by name */

25

readonly marks: { [name: string]: MarkType };

26

27

/** The schema specification this was created from */

28

readonly spec: SchemaSpec;

29

30

/** Get a node type by name */

31

node(name: string): NodeType;

32

33

/** Get a mark type by name */

34

mark(name: string): MarkType;

35

36

/** Get the default top-level node type */

37

readonly topNodeType: NodeType;

38

39

/** Check if a given text can appear at the given position */

40

text(text: string, marks?: Mark[]): Node | null;

41

}

42

43

interface SchemaSpec {

44

/** Object mapping node names to node specs */

45

nodes: { [name: string]: NodeSpec };

46

47

/** Object mapping mark names to mark specs */

48

marks?: { [name: string]: MarkSpec };

49

50

/** The name of the default top-level node type */

51

topNode?: string;

52

}

53

```

54

55

**Usage Examples:**

56

57

```typescript

58

import { Schema } from "@tiptap/pm/model";

59

60

// Create a simple schema

61

const mySchema = new Schema({

62

nodes: {

63

doc: { content: "paragraph+" },

64

paragraph: { content: "text*", group: "block" },

65

text: { group: "inline" },

66

},

67

marks: {

68

strong: {},

69

em: {},

70

},

71

});

72

73

// Get node types

74

const docType = mySchema.nodes.doc;

75

const paragraphType = mySchema.nodes.paragraph;

76

```

77

78

### Node Types

79

80

Define the characteristics and behavior of different node types in the document.

81

82

```typescript { .api }

83

/**

84

* A node type represents a kind of node that may occur in a document

85

*/

86

class NodeType {

87

/** The name of this node type */

88

readonly name: string;

89

90

/** The schema this node type belongs to */

91

readonly schema: Schema;

92

93

/** The spec this node type was created from */

94

readonly spec: NodeSpec;

95

96

/** True if this node type is inline */

97

readonly isInline: boolean;

98

99

/** True if this node type is a block */

100

readonly isBlock: boolean;

101

102

/** True if this node is an atom (cannot have content edited directly) */

103

readonly isAtom: boolean;

104

105

/** True if this node type is the schema's top node type */

106

readonly isTop: boolean;

107

108

/** True if this node allows marks */

109

readonly allowsMarks: boolean;

110

111

/**

112

* Create a node of this type

113

* @param attrs - The node's attributes

114

* @param content - The node's content

115

* @param marks - The node's marks

116

*/

117

create(attrs?: Attrs, content?: Fragment | Node | Node[], marks?: Mark[]): Node;

118

119

/**

120

* Create a node with the given content and attributes, checking validity

121

*/

122

createChecked(attrs?: Attrs, content?: Fragment | Node | Node[], marks?: Mark[]): Node;

123

124

/**

125

* Create a node and fill any required children

126

*/

127

createAndFill(attrs?: Attrs, content?: Fragment | Node | Node[], marks?: Mark[]): Node | null;

128

}

129

130

interface NodeSpec {

131

/** Expression describing allowed content */

132

content?: string;

133

134

/** Expression describing allowed marks */

135

marks?: string;

136

137

/** Nodes group this node belongs to */

138

group?: string;

139

140

/** Whether this node type is inline */

141

inline?: boolean;

142

143

/** Whether this node is an atom */

144

atom?: boolean;

145

146

/** Whether this node is selectable */

147

selectable?: boolean;

148

149

/** Whether this node can be dragged */

150

draggable?: boolean;

151

152

/** Node's attributes */

153

attrs?: { [name: string]: AttributeSpec };

154

155

/** Rules for parsing DOM to this node */

156

parseDOM?: ParseRule[];

157

158

/** Function to serialize this node to DOM */

159

toDOM?: (node: Node) => DOMOutputSpec;

160

}

161

```

162

163

### Document Nodes

164

165

Represents individual nodes in the document tree structure.

166

167

```typescript { .api }

168

/**

169

* A node in a ProseMirror document

170

*/

171

class Node {

172

/** The node's type */

173

readonly type: NodeType;

174

175

/** The node's attributes */

176

readonly attrs: Attrs;

177

178

/** The node's content as a Fragment */

179

readonly content: Fragment;

180

181

/** The marks applied to this node */

182

readonly marks: Mark[];

183

184

/** The number of children this node has */

185

readonly childCount: number;

186

187

/** The size of this node */

188

readonly nodeSize: number;

189

190

/** True if this node is an atom */

191

readonly isAtom: boolean;

192

193

/** True if this node is inline */

194

readonly isInline: boolean;

195

196

/** True if this node is a block */

197

readonly isBlock: boolean;

198

199

/** True if this node is a text node */

200

readonly isText: boolean;

201

202

/** True if this node is a leaf (no children) */

203

readonly isLeaf: boolean;

204

205

/**

206

* Get the child node at the given index

207

*/

208

child(index: number): Node;

209

210

/**

211

* Get the child node at the given offset

212

*/

213

childAfter(pos: number): { node: Node; index: number; offset: number } | null;

214

215

/**

216

* Get the child node before the given offset

217

*/

218

childBefore(pos: number): { node: Node; index: number; offset: number } | null;

219

220

/**

221

* Call a function for each child node

222

*/

223

forEach(f: (node: Node, offset: number, index: number) => void): void;

224

225

/**

226

* Create a new node with the same type and attributes but different content

227

*/

228

copy(content?: Fragment): Node;

229

230

/**

231

* Check if this node has the same markup as another

232

*/

233

sameMarkup(other: Node): boolean;

234

235

/**

236

* Get a text representation of this node

237

*/

238

textContent: string;

239

240

/**

241

* Resolve a position in this node's content

242

*/

243

resolve(pos: number): ResolvedPos;

244

245

/**

246

* Find all positions in this node that match a predicate

247

*/

248

descendants(f: (node: Node, pos: number, parent: Node) => boolean | void): void;

249

}

250

```

251

252

### Document Fragments

253

254

Represents collections of nodes, used for document content.

255

256

```typescript { .api }

257

/**

258

* A fragment represents a node's collection of child nodes

259

*/

260

class Fragment {

261

/** The number of child nodes in this fragment */

262

readonly size: number;

263

264

/** The combined size of all nodes in this fragment */

265

readonly nodeSize: number;

266

267

/**

268

* Create a fragment from an array of nodes

269

*/

270

static from(nodes?: Node[] | Node | Fragment | null): Fragment;

271

272

/** The empty fragment */

273

static empty: Fragment;

274

275

/**

276

* Get the child node at the given index

277

*/

278

child(index: number): Node;

279

280

/**

281

* Call a function for each child node

282

*/

283

forEach(f: (node: Node, offset: number, index: number) => void): void;

284

285

/**

286

* Find the first position at which this fragment differs from another

287

*/

288

findDiffStart(other: Fragment, pos?: number): number | null;

289

290

/**

291

* Find the last position at which this fragment differs from another

292

*/

293

findDiffEnd(other: Fragment, pos?: number, otherPos?: number): { a: number; b: number } | null;

294

295

/**

296

* Create new fragment with nodes appended

297

*/

298

append(other: Fragment): Fragment;

299

300

/**

301

* Cut out the part of the fragment between the given positions

302

*/

303

cut(from: number, to?: number): Fragment;

304

305

/**

306

* Replace the part between from and to with the given fragment

307

*/

308

replaceChild(index: number, node: Node): Fragment;

309

310

/**

311

* Get the text content of the fragment

312

*/

313

readonly textContent: string;

314

315

/**

316

* Convert the fragment to a JSON representation

317

*/

318

toJSON(): any;

319

320

/**

321

* Create a fragment from a JSON representation

322

*/

323

static fromJSON(schema: Schema, json: any): Fragment;

324

}

325

```

326

327

### Mark Types and Marks

328

329

Define and represent text annotations like bold, italic, links, etc.

330

331

```typescript { .api }

332

/**

333

* A mark type represents a type of mark that can be applied to text

334

*/

335

class MarkType {

336

/** The name of this mark type */

337

readonly name: string;

338

339

/** The schema this mark type belongs to */

340

readonly schema: Schema;

341

342

/** The spec this mark type was created from */

343

readonly spec: MarkSpec;

344

345

/**

346

* Create a mark of this type

347

*/

348

create(attrs?: Attrs): Mark;

349

350

/**

351

* Check if the given set of marks has a mark of this type

352

*/

353

isInSet(set: Mark[]): Mark | null;

354

355

/**

356

* Remove marks of this type from the given set

357

*/

358

removeFromSet(set: Mark[]): Mark[];

359

}

360

361

/**

362

* A mark represents a piece of information attached to inline content

363

*/

364

class Mark {

365

/** The mark's type */

366

readonly type: MarkType;

367

368

/** The mark's attributes */

369

readonly attrs: Attrs;

370

371

/**

372

* Add this mark to a set of marks

373

*/

374

addToSet(set: Mark[]): Mark[];

375

376

/**

377

* Remove this mark from a set of marks

378

*/

379

removeFromSet(set: Mark[]): Mark[];

380

381

/**

382

* Check if this mark is in a set of marks

383

*/

384

isInSet(set: Mark[]): boolean;

385

386

/**

387

* Check if this mark has the same type and attributes as another

388

*/

389

eq(other: Mark): boolean;

390

391

/**

392

* Convert the mark to a JSON representation

393

*/

394

toJSON(): any;

395

396

/**

397

* Create a mark from a JSON representation

398

*/

399

static fromJSON(schema: Schema, json: any): Mark;

400

}

401

402

interface MarkSpec {

403

/** Mark's attributes */

404

attrs?: { [name: string]: AttributeSpec };

405

406

/** Whether this mark should be inclusive (continue when typing at boundaries) */

407

inclusive?: boolean;

408

409

/** Other mark types this mark excludes */

410

excludes?: string;

411

412

/** Group this mark belongs to */

413

group?: string;

414

415

/** Rules for parsing DOM to this mark */

416

parseDOM?: ParseRule[];

417

418

/** Function to serialize this mark to DOM */

419

toDOM?: (mark: Mark, inline: boolean) => DOMOutputSpec;

420

}

421

```

422

423

### Position Resolution

424

425

Provides detailed information about positions within documents.

426

427

```typescript { .api }

428

/**

429

* Represents a position in a document with context information

430

*/

431

class ResolvedPos {

432

/** The position this was resolved from */

433

readonly pos: number;

434

435

/** Path to this position */

436

readonly path: (Node | number)[];

437

438

/** The depth of this position */

439

readonly depth: number;

440

441

/** The parent node at the outermost level */

442

readonly parent: Node;

443

444

/** The document this position is in */

445

readonly doc: Node;

446

447

/**

448

* Get the parent node at the given depth

449

*/

450

node(depth?: number): Node;

451

452

/**

453

* Get the index at the given depth

454

*/

455

index(depth?: number): number;

456

457

/**

458

* Get the index after this position at the given depth

459

*/

460

indexAfter(depth?: number): number;

461

462

/**

463

* Get the start position of the parent at the given depth

464

*/

465

start(depth?: number): number;

466

467

/**

468

* Get the end position of the parent at the given depth

469

*/

470

end(depth?: number): number;

471

472

/**

473

* Get the position before this position

474

*/

475

before(depth?: number): number;

476

477

/**

478

* Get the position after this position

479

*/

480

after(depth?: number): number;

481

482

/**

483

* Get the text offset of this position

484

*/

485

textOffset: number;

486

487

/**

488

* Get the marks active at this position

489

*/

490

marks(): Mark[];

491

}

492

```

493

494

### DOM Parsing and Serialization

495

496

Convert between DOM and ProseMirror document structures.

497

498

```typescript { .api }

499

/**

500

* Parser for converting DOM content to ProseMirror documents

501

*/

502

class DOMParser {

503

/** The schema this parser uses */

504

readonly schema: Schema;

505

506

/** The parsing rules */

507

readonly rules: ParseRule[];

508

509

/**

510

* Create a DOM parser

511

*/

512

static fromSchema(schema: Schema): DOMParser;

513

514

/**

515

* Parse a DOM node to a ProseMirror document

516

*/

517

parse(dom: Element, options?: ParseOptions): Node;

518

519

/**

520

* Parse a DOM node to a ProseMirror fragment

521

*/

522

parseFragment(dom: Element, options?: ParseOptions): Fragment;

523

524

/**

525

* Parse content from a string

526

*/

527

parseSlice(dom: Element, options?: ParseOptions): Slice;

528

}

529

530

/**

531

* Serializer for converting ProseMirror documents to DOM

532

*/

533

class DOMSerializer {

534

/** Node serialization functions */

535

readonly nodes: { [name: string]: (node: Node) => DOMOutputSpec };

536

537

/** Mark serialization functions */

538

readonly marks: { [name: string]: (mark: Mark, inline: boolean) => DOMOutputSpec };

539

540

/**

541

* Create a DOM serializer from a schema

542

*/

543

static fromSchema(schema: Schema): DOMSerializer;

544

545

/**

546

* Serialize a fragment to DOM

547

*/

548

serializeFragment(fragment: Fragment, options?: { document?: Document }): DocumentFragment;

549

550

/**

551

* Serialize a node to DOM

552

*/

553

serializeNode(node: Node, options?: { document?: Document }): Element;

554

}

555

556

interface ParseRule {

557

/** Tag name to match */

558

tag?: string;

559

560

/** CSS selector to match */

561

match?: (node: Element) => boolean;

562

563

/** Priority of this rule */

564

priority?: number;

565

566

/** Node type to create */

567

node?: string;

568

569

/** Mark type to create */

570

mark?: string;

571

572

/** Whether to ignore content */

573

ignore?: boolean;

574

575

/** How to get attributes */

576

attrs?: Attrs | ((node: Element) => Attrs);

577

578

/** How to handle content */

579

contentElement?: string | ((node: Element) => Element);

580

}

581

582

interface ParseOptions {

583

/** Whether to preserve whitespace */

584

preserveWhitespace?: boolean | "full";

585

586

/** Node to find parsing position in */

587

findPositions?: { node: Element; offset: number }[];

588

589

/** Starting position */

590

from?: number;

591

592

/** Ending position */

593

to?: number;

594

}

595

596

type DOMOutputSpec = string | Element | [string, ...any[]];

597

```

598

599

## Types

600

601

```typescript { .api }

602

interface AttributeSpec {

603

/** Default value for this attribute */

604

default?: any;

605

606

/** Validation function */

607

validate?: (value: any) => boolean;

608

}

609

610

type Attrs = { [key: string]: any };

611

612

interface Slice {

613

/** The content of the slice */

614

content: Fragment;

615

616

/** Open depth at the start */

617

openStart: number;

618

619

/** Open depth at the end */

620

openEnd: number;

621

}

622

```