or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

command-system.mddocument-helpers.mdeditor-core.mdextension-system.mdindex.mdrule-systems.mdutilities.md

document-helpers.mddocs/

0

# Document Helpers

1

2

@tiptap/core provides a comprehensive set of helper functions for document manipulation, content generation, querying, and state inspection. These functions enable advanced document processing and analysis.

3

4

## Capabilities

5

6

### Content Generation

7

8

Functions for converting content between different formats and creating documents.

9

10

```typescript { .api }

11

/**

12

* Generate HTML string from JSON document content

13

* @param doc - JSON document content

14

* @param extensions - Array of extensions for schema generation

15

* @returns HTML string representation

16

*/

17

function generateHTML(doc: JSONContent, extensions: Extensions): string;

18

19

/**

20

* Generate JSON document from HTML string

21

* @param content - HTML string to parse

22

* @param extensions - Array of extensions for schema generation

23

* @returns JSONContent representation

24

*/

25

function generateJSON(content: string, extensions: Extensions): JSONContent;

26

27

/**

28

* Generate plain text from JSON document content

29

* @param doc - JSON document or ProseMirror Node

30

* @param options - Text generation options

31

* @returns Plain text string

32

*/

33

function generateText(

34

doc: JSONContent | ProseMirrorNode,

35

options?: GenerateTextOptions

36

): string;

37

38

interface GenerateTextOptions {

39

/** Separator between block elements */

40

blockSeparator?: string;

41

/** Include text from specific range */

42

from?: number;

43

to?: number;

44

}

45

```

46

47

**Usage Examples:**

48

49

```typescript

50

import { generateHTML, generateJSON, generateText } from '@tiptap/core';

51

52

// Convert JSON to HTML

53

const jsonDoc = {

54

type: 'doc',

55

content: [

56

{

57

type: 'paragraph',

58

content: [

59

{ type: 'text', text: 'Hello ' },

60

{ type: 'text', text: 'World', marks: [{ type: 'bold' }] }

61

]

62

}

63

]

64

};

65

66

const html = generateHTML(jsonDoc, extensions);

67

// '<p>Hello <strong>World</strong></p>'

68

69

// Convert HTML to JSON

70

const htmlContent = '<h1>Title</h1><p>Paragraph with <em>emphasis</em></p>';

71

const json = generateJSON(htmlContent, extensions);

72

73

// Extract plain text

74

const plainText = generateText(jsonDoc);

75

// 'Hello World'

76

77

const textWithSeparator = generateText(jsonDoc, {

78

blockSeparator: ' | '

79

});

80

// 'Hello World |'

81

82

// From ProseMirror node

83

const text = generateText(editor.state.doc, {

84

from: 0,

85

to: 100

86

});

87

```

88

89

### Document Creation

90

91

Functions for creating ProseMirror documents and nodes from various content sources.

92

93

```typescript { .api }

94

/**

95

* Create a ProseMirror document from content

96

* @param content - Content to convert to document

97

* @param schema - ProseMirror schema to use

98

* @param parseOptions - Options for parsing content

99

* @returns ProseMirror document node

100

*/

101

function createDocument(

102

content: Content,

103

schema: Schema,

104

parseOptions?: ParseOptions

105

): ProseMirrorNode;

106

107

/**

108

* Create ProseMirror node(s) from content

109

* @param content - Content to convert

110

* @param schema - ProseMirror schema to use

111

* @param options - Creation options

112

* @returns Single node or array of nodes

113

*/

114

function createNodeFromContent(

115

content: Content,

116

schema: Schema,

117

options?: CreateNodeFromContentOptions

118

): ProseMirrorNode | ProseMirrorNode[];

119

120

interface ParseOptions {

121

/** Preserve whitespace */

122

preserveWhitespace?: boolean | 'full';

123

/** Parse context */

124

context?: ResolvedPos;

125

/** Parse rules to use */

126

ruleFromNode?: (node: ProseMirrorNode) => ParseRule | null;

127

/** Top node name */

128

topNode?: string;

129

}

130

131

interface CreateNodeFromContentOptions extends ParseOptions {

132

/** Slice content to fragment */

133

slice?: boolean;

134

/** Parse as single node */

135

parseOptions?: ParseOptions;

136

}

137

138

type Content =

139

| string

140

| JSONContent

141

| JSONContent[]

142

| ProseMirrorNode

143

| ProseMirrorNode[]

144

| ProseMirrorFragment;

145

```

146

147

**Usage Examples:**

148

149

```typescript

150

import { createDocument, createNodeFromContent } from '@tiptap/core';

151

152

// Create document from HTML

153

const doc = createDocument(

154

'<h1>Title</h1><p>Content</p>',

155

schema

156

);

157

158

// Create document from JSON

159

const jsonContent = {

160

type: 'doc',

161

content: [

162

{ type: 'heading', attrs: { level: 1 }, content: [{ type: 'text', text: 'Title' }] }

163

]

164

};

165

const docFromJson = createDocument(jsonContent, schema);

166

167

// Create nodes from content

168

const nodes = createNodeFromContent(

169

'<p>Paragraph 1</p><p>Paragraph 2</p>',

170

schema

171

);

172

173

// Create with options

174

const nodeWithOptions = createNodeFromContent(

175

'<pre> Code with spaces </pre>',

176

schema,

177

{ parseOptions: { preserveWhitespace: 'full' } }

178

);

179

180

// Create fragment

181

const fragment = createNodeFromContent(

182

[

183

{ type: 'paragraph', content: [{ type: 'text', text: 'First' }] },

184

{ type: 'paragraph', content: [{ type: 'text', text: 'Second' }] }

185

],

186

schema,

187

{ slice: true }

188

);

189

```

190

191

### Content Queries

192

193

Functions for finding and extracting content from documents.

194

195

```typescript { .api }

196

/**

197

* Find child nodes matching a predicate

198

* @param node - Parent node to search in

199

* @param predicate - Function to test each child

200

* @param descend - Whether to search recursively

201

* @returns Array of nodes with their positions

202

*/

203

function findChildren(

204

node: ProseMirrorNode,

205

predicate: (child: ProseMirrorNode) => boolean,

206

descend?: boolean

207

): NodeWithPos[];

208

209

/**

210

* Find child nodes in a specific range

211

* @param node - Parent node to search in

212

* @param range - Range to search within

213

* @param predicate - Function to test each child

214

* @param descend - Whether to search recursively

215

* @returns Array of nodes with their positions

216

*/

217

function findChildrenInRange(

218

node: ProseMirrorNode,

219

range: { from: number; to: number },

220

predicate: (child: ProseMirrorNode) => boolean,

221

descend?: boolean

222

): NodeWithPos[];

223

224

/**

225

* Find parent node matching predicate

226

* @param predicate - Function to test parent nodes

227

* @returns Function that takes selection and returns parent info

228

*/

229

function findParentNode(

230

predicate: (node: ProseMirrorNode) => boolean

231

): (selection: Selection) => NodeWithPos | null;

232

233

/**

234

* Find parent node closest to a position

235

* @param $pos - Resolved position

236

* @param predicate - Function to test parent nodes

237

* @returns Parent node info if found

238

*/

239

function findParentNodeClosestToPos(

240

$pos: ResolvedPos,

241

predicate: (node: ProseMirrorNode) => boolean

242

): NodeWithPos | null;

243

244

/**

245

* Get node at specific position

246

* @param state - Editor state

247

* @param pos - Document position

248

* @returns Node information at position

249

*/

250

function getNodeAtPosition(

251

state: EditorState,

252

pos: number

253

): { node: ProseMirrorNode; pos: number; depth: number };

254

255

/**

256

* Get text content between positions

257

* @param node - Node to extract text from

258

* @param from - Start position

259

* @param to - End position

260

* @param options - Extraction options

261

* @returns Text content

262

*/

263

function getTextBetween(

264

node: ProseMirrorNode,

265

from: number,

266

to: number,

267

options?: GetTextBetweenOptions

268

): string;

269

270

interface NodeWithPos {

271

node: ProseMirrorNode;

272

pos: number;

273

}

274

275

interface GetTextBetweenOptions {

276

/** Block separator string */

277

blockSeparator?: string;

278

/** Text serializers for custom nodes */

279

textSerializers?: Record<string, (props: { node: ProseMirrorNode }) => string>;

280

}

281

```

282

283

**Usage Examples:**

284

285

```typescript

286

import {

287

findChildren,

288

findChildrenInRange,

289

findParentNode,

290

getTextBetween

291

} from '@tiptap/core';

292

293

// Find all headings in document

294

const headings = findChildren(

295

editor.state.doc,

296

node => node.type.name === 'heading'

297

);

298

299

headings.forEach(({ node, pos }) => {

300

console.log(`H${node.attrs.level}: ${node.textContent} at ${pos}`);

301

});

302

303

// Find images in a specific range

304

const images = findChildrenInRange(

305

editor.state.doc,

306

{ from: 100, to: 200 },

307

node => node.type.name === 'image'

308

);

309

310

// Find all paragraphs recursively

311

const allParagraphs = findChildren(

312

editor.state.doc,

313

node => node.type.name === 'paragraph',

314

true // descend into nested structures

315

);

316

317

// Find parent list item

318

const findListItem = findParentNode(node => node.type.name === 'listItem');

319

const listItemInfo = findListItem(editor.state.selection);

320

321

if (listItemInfo) {

322

console.log('Inside list item at position:', listItemInfo.pos);

323

}

324

325

// Get text between positions

326

const textContent = getTextBetween(

327

editor.state.doc,

328

0,

329

editor.state.doc.content.size

330

);

331

332

// Custom text extraction

333

const textWithCustomSeparators = getTextBetween(

334

editor.state.doc,

335

0,

336

100,

337

{

338

blockSeparator: '\n\n',

339

textSerializers: {

340

image: ({ node }) => `[Image: ${node.attrs.alt || 'Untitled'}]`,

341

mention: ({ node }) => `@${node.attrs.label}`

342

}

343

}

344

);

345

```

346

347

### State Inspection

348

349

Functions for analyzing editor state and selection properties.

350

351

```typescript { .api }

352

/**

353

* Check if node or mark is active

354

* @param state - Editor state

355

* @param name - Node/mark name or type

356

* @param attributes - Optional attributes to match

357

* @returns Whether the node/mark is active

358

*/

359

function isActive(

360

state: EditorState,

361

name?: string | NodeType | MarkType,

362

attributes?: Record<string, any>

363

): boolean;

364

365

/**

366

* Check if a mark is active

367

* @param state - Editor state

368

* @param type - Mark type or name

369

* @param attributes - Optional attributes to match

370

* @returns Whether the mark is active

371

*/

372

function isMarkActive(

373

state: EditorState,

374

type: MarkType | string,

375

attributes?: Record<string, any>

376

): boolean;

377

378

/**

379

* Check if a node is active

380

* @param state - Editor state

381

* @param type - Node type or name

382

* @param attributes - Optional attributes to match

383

* @returns Whether the node is active

384

*/

385

function isNodeActive(

386

state: EditorState,

387

type: NodeType | string,

388

attributes?: Record<string, any>

389

): boolean;

390

391

/**

392

* Check if node is empty

393

* @param node - Node to check

394

* @param options - Check options

395

* @returns Whether the node is considered empty

396

*/

397

function isNodeEmpty(

398

node: ProseMirrorNode,

399

options?: { ignoreWhitespace?: boolean; checkChildren?: boolean }

400

): boolean;

401

402

/**

403

* Check if selection is text selection

404

* @param selection - Selection to check

405

* @returns Whether selection is TextSelection

406

*/

407

function isTextSelection(selection: Selection): selection is TextSelection;

408

409

/**

410

* Check if selection is node selection

411

* @param selection - Selection to check

412

* @returns Whether selection is NodeSelection

413

*/

414

function isNodeSelection(selection: Selection): selection is NodeSelection;

415

416

/**

417

* Check if cursor is at start of node

418

* @param state - Editor state

419

* @param types - Optional node types to check

420

* @returns Whether cursor is at start of specified node types

421

*/

422

function isAtStartOfNode(

423

state: EditorState,

424

types?: string[] | string

425

): boolean;

426

427

/**

428

* Check if cursor is at end of node

429

* @param state - Editor state

430

* @param types - Optional node types to check

431

* @returns Whether cursor is at end of specified node types

432

*/

433

function isAtEndOfNode(

434

state: EditorState,

435

types?: string[] | string

436

): boolean;

437

```

438

439

**Usage Examples:**

440

441

```typescript

442

import {

443

isActive,

444

isMarkActive,

445

isNodeActive,

446

isNodeEmpty,

447

isTextSelection,

448

isAtStartOfNode

449

} from '@tiptap/core';

450

451

// Check active formatting

452

const isBold = isMarkActive(editor.state, 'bold');

453

const isItalic = isMarkActive(editor.state, 'italic');

454

const isLink = isMarkActive(editor.state, 'link');

455

456

// Check active nodes

457

const isHeading = isNodeActive(editor.state, 'heading');

458

const isH1 = isNodeActive(editor.state, 'heading', { level: 1 });

459

const isBlockquote = isNodeActive(editor.state, 'blockquote');

460

461

// Generic active check

462

const isHeadingActive = isActive(editor.state, 'heading');

463

const isSpecificHeading = isActive(editor.state, 'heading', { level: 2 });

464

465

// Check if nodes are empty

466

const isEmpty = isNodeEmpty(editor.state.doc);

467

const isEmptyIgnoreSpaces = isNodeEmpty(editor.state.doc, {

468

ignoreWhitespace: true

469

});

470

471

// Check selection type

472

if (isTextSelection(editor.state.selection)) {

473

console.log('Text is selected');

474

const { from, to } = editor.state.selection;

475

console.log(`Selection from ${from} to ${to}`);

476

}

477

478

// Check cursor position

479

const atStart = isAtStartOfNode(editor.state);

480

const atStartOfParagraph = isAtStartOfNode(editor.state, 'paragraph');

481

const atStartOfMultiple = isAtStartOfNode(editor.state, ['paragraph', 'heading']);

482

483

// UI state management

484

function getToolbarState() {

485

const state = editor.state;

486

487

return {

488

bold: isMarkActive(state, 'bold'),

489

italic: isMarkActive(state, 'italic'),

490

heading: isNodeActive(state, 'heading'),

491

canIndent: !isAtStartOfNode(state, 'listItem'),

492

canOutdent: isNodeActive(state, 'listItem')

493

};

494

}

495

```

496

497

### Attributes and Schema

498

499

Functions for working with node and mark attributes and schema information.

500

501

```typescript { .api }

502

/**

503

* Get attributes from current selection

504

* @param state - Editor state

505

* @param nameOrType - Node/mark name or type

506

* @returns Attributes object

507

*/

508

function getAttributes(

509

state: EditorState,

510

nameOrType: string | NodeType | MarkType

511

): Record<string, any>;

512

513

/**

514

* Get node attributes from selection

515

* @param state - Editor state

516

* @param typeOrName - Node type or name

517

* @returns Node attributes

518

*/

519

function getNodeAttributes(

520

state: EditorState,

521

typeOrName: NodeType | string

522

): Record<string, any>;

523

524

/**

525

* Get mark attributes from selection

526

* @param state - Editor state

527

* @param typeOrName - Mark type or name

528

* @returns Mark attributes

529

*/

530

function getMarkAttributes(

531

state: EditorState,

532

typeOrName: MarkType | string

533

): Record<string, any>;

534

535

/**

536

* Generate ProseMirror schema from extensions

537

* @param extensions - Array of extensions

538

* @returns ProseMirror schema

539

*/

540

function getSchema(extensions: Extensions): Schema;

541

542

/**

543

* Get schema from resolved extensions

544

* @param extensions - Resolved extensions array

545

* @returns ProseMirror schema

546

*/

547

function getSchemaByResolvedExtensions(

548

extensions: AnyExtension[]

549

): Schema;

550

551

/**

552

* Split attributes by extension type

553

* @param extensionAttributes - Extension attributes

554

* @param typeName - Type name to match

555

* @param attributes - Attributes to split

556

* @returns Object with extension and node/mark attributes

557

*/

558

function getSplittedAttributes(

559

extensionAttributes: ExtensionAttribute[],

560

typeName: string,

561

attributes: Record<string, any>

562

): {

563

extensionAttributes: Record<string, any>;

564

nodeAttributes: Record<string, any>;

565

};

566

```

567

568

**Usage Examples:**

569

570

```typescript

571

import {

572

getAttributes,

573

getNodeAttributes,

574

getMarkAttributes,

575

getSchema

576

} from '@tiptap/core';

577

578

// Get current attributes

579

const headingAttrs = getNodeAttributes(editor.state, 'heading');

580

// { level: 1, id: 'intro' }

581

582

const linkAttrs = getMarkAttributes(editor.state, 'link');

583

// { href: 'https://example.com', target: '_blank' }

584

585

// Generic attribute getting

586

const attrs = getAttributes(editor.state, 'image');

587

// { src: 'image.jpg', alt: 'Description', width: 500 }

588

589

// Build schema from extensions

590

const customSchema = getSchema([

591

// your extensions

592

]);

593

594

// Use attributes in UI

595

function LinkDialog() {

596

const linkAttrs = getMarkAttributes(editor.state, 'link');

597

const [url, setUrl] = useState(linkAttrs.href || '');

598

const [target, setTarget] = useState(linkAttrs.target || '');

599

600

const applyLink = () => {

601

editor.commands.setMark('link', { href: url, target });

602

};

603

604

return (

605

<div>

606

<input value={url} onChange={e => setUrl(e.target.value)} />

607

<select value={target} onChange={e => setTarget(e.target.value)}>

608

<option value="">Same window</option>

609

<option value="_blank">New window</option>

610

</select>

611

<button onClick={applyLink}>Apply</button>

612

</div>

613

);

614

}

615

616

// Dynamic attribute handling

617

function updateNodeAttributes(nodeType: string, newAttributes: Record<string, any>) {

618

const currentAttrs = getNodeAttributes(editor.state, nodeType);

619

const mergedAttrs = { ...currentAttrs, ...newAttributes };

620

621

editor.commands.updateAttributes(nodeType, mergedAttrs);

622

}

623

```

624

625

### Utility Functions

626

627

Additional helper functions for document processing and analysis.

628

629

```typescript { .api }

630

/**

631

* Combine transaction steps from multiple transactions

632

* @param oldTr - Original transaction

633

* @param newTr - New transaction to combine

634

* @returns Combined transaction

635

*/

636

function combineTransactionSteps(

637

oldTr: Transaction,

638

newTr: Transaction

639

): Transaction;

640

641

/**

642

* Create chainable state for command chaining

643

* @param options - State and transaction

644

* @returns Chainable state object

645

*/

646

function createChainableState(options: {

647

state: EditorState;

648

transaction: Transaction;

649

}): EditorState;

650

651

/**

652

* Get ranges that were changed by a transaction

653

* @param tr - Transaction to analyze

654

* @returns Array of changed ranges

655

*/

656

function getChangedRanges(tr: Transaction): {

657

from: number;

658

to: number;

659

newFrom: number;

660

newTo: number;

661

}[];

662

663

/**

664

* Get debug representation of document

665

* @param doc - Document to debug

666

* @param schema - Schema for type information

667

* @returns Debug object with structure info

668

*/

669

function getDebugJSON(

670

doc: ProseMirrorNode,

671

schema: Schema

672

): Record<string, any>;

673

674

/**

675

* Convert document fragment to HTML

676

* @param fragment - ProseMirror fragment

677

* @param schema - Schema for serialization

678

* @returns HTML string

679

*/

680

function getHTMLFromFragment(

681

fragment: ProseMirrorFragment,

682

schema: Schema

683

): string;

684

685

/**

686

* Get DOM rectangle for position range

687

* @param view - Editor view

688

* @param from - Start position

689

* @param to - End position

690

* @returns DOM rectangle

691

*/

692

function posToDOMRect(

693

view: EditorView,

694

from: number,

695

to: number

696

): DOMRect;

697

```

698

699

**Usage Examples:**

700

701

```typescript

702

import {

703

getChangedRanges,

704

getDebugJSON,

705

posToDOMRect

706

} from '@tiptap/core';

707

708

// Track changes in transactions

709

editor.on('transaction', ({ transaction }) => {

710

const changes = getChangedRanges(transaction);

711

712

changes.forEach(range => {

713

console.log(`Changed: ${range.from}-${range.to} → ${range.newFrom}-${range.newTo}`);

714

});

715

});

716

717

// Debug document structure

718

const debugInfo = getDebugJSON(editor.state.doc, editor.schema);

719

console.log('Document structure:', debugInfo);

720

721

// Get position of selection for UI positioning

722

function positionTooltip() {

723

const { from, to } = editor.state.selection;

724

const rect = posToDOMRect(editor.view, from, to);

725

726

const tooltip = document.getElementById('tooltip');

727

if (tooltip && rect) {

728

tooltip.style.left = `${rect.left}px`;

729

tooltip.style.top = `${rect.bottom + 10}px`;

730

}

731

}

732

733

// Use in selection change handler

734

editor.on('selectionUpdate', () => {

735

if (!editor.state.selection.empty) {

736

positionTooltip();

737

}

738

});

739

```