or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

caret-system.mdcommand-system.mdeditor-management.mdindex.mdnode-system.mdselection-system.mdstate-management.mdutilities-helpers.md
tile.json

caret-system.mddocs/

0

# Caret System

1

2

Advanced caret positioning and navigation system for precise text manipulation and cursor movement. Lexical's caret system provides fine-grained control over cursor positioning that goes beyond standard DOM selection capabilities.

3

4

## Capabilities

5

6

### Core Caret Types

7

8

Base interfaces and types for representing different caret positions.

9

10

```typescript { .api }

11

/**

12

* Base interface for all caret types

13

*/

14

interface BaseCaret {

15

type: CaretType;

16

}

17

18

/**

19

* Type of caret positioning

20

*/

21

type CaretType = 'text-point' | 'sibling' | 'child';

22

23

/**

24

* Direction for caret movement

25

*/

26

type CaretDirection = 'next' | 'previous';

27

28

/**

29

* Union type for node-based carets

30

*/

31

type NodeCaret = SiblingCaret | ChildCaret;

32

33

/**

34

* Union type for point-based carets

35

*/

36

type PointCaret = TextPointCaret | NodeCaret;

37

38

/**

39

* Root mode for caret traversal

40

*/

41

type RootMode = 'root-boundary' | 'no-boundary';

42

```

43

44

### Text Point Carets

45

46

Carets that point to specific positions within text nodes.

47

48

```typescript { .api }

49

/**

50

* Caret pointing to position within text node

51

*/

52

interface TextPointCaret extends BaseCaret {

53

type: 'text-point';

54

node: TextNode;

55

offset: number;

56

}

57

58

/**

59

* Slice of text point caret with additional metadata

60

*/

61

interface TextPointCaretSlice extends TextPointCaret {

62

textContent: string;

63

isFirst: boolean;

64

isLast: boolean;

65

}

66

67

/**

68

* Tuple type for text point caret slices

69

*/

70

type TextPointCaretSliceTuple = [TextPointCaretSlice, TextPointCaretSlice];

71

72

/**

73

* Create text point caret

74

* @param node - Text node to point to

75

* @param offset - Character offset within node

76

* @returns Text point caret

77

*/

78

function $getTextPointCaret(node: TextNode, offset: number): TextPointCaret;

79

80

/**

81

* Create text point caret slice

82

* @param node - Text node

83

* @param startOffset - Start offset

84

* @param endOffset - End offset

85

* @returns Text point caret slice

86

*/

87

function $getTextPointCaretSlice(

88

node: TextNode,

89

startOffset: number,

90

endOffset: number

91

): TextPointCaretSlice;

92

93

/**

94

* Get text node offset for caret

95

* @param caret - Text point caret

96

* @returns Character offset

97

*/

98

function $getTextNodeOffset(caret: TextPointCaret): number;

99

100

/**

101

* Check if caret is text point caret

102

* @param caret - Caret to check

103

* @returns True if text point caret

104

*/

105

function $isTextPointCaret(caret: unknown): caret is TextPointCaret;

106

107

/**

108

* Check if caret is text point caret slice

109

* @param caret - Caret to check

110

* @returns True if text point caret slice

111

*/

112

function $isTextPointCaretSlice(caret: unknown): caret is TextPointCaretSlice;

113

```

114

115

### Sibling Carets

116

117

Carets that point to positions relative to sibling nodes.

118

119

```typescript { .api }

120

/**

121

* Caret pointing to position relative to sibling

122

*/

123

interface SiblingCaret extends BaseCaret {

124

type: 'sibling';

125

node: LexicalNode;

126

direction: CaretDirection;

127

}

128

129

/**

130

* Create sibling caret

131

* @param node - Reference node

132

* @param direction - Direction relative to node

133

* @returns Sibling caret

134

*/

135

function $getSiblingCaret(node: LexicalNode, direction: CaretDirection): SiblingCaret;

136

137

/**

138

* Check if caret is sibling caret

139

* @param caret - Caret to check

140

* @returns True if sibling caret

141

*/

142

function $isSiblingCaret(caret: unknown): caret is SiblingCaret;

143

144

/**

145

* Rewind sibling caret to previous position

146

* @param caret - Sibling caret to rewind

147

* @returns Rewound caret

148

*/

149

function $rewindSiblingCaret(caret: SiblingCaret): SiblingCaret;

150

```

151

152

### Child Carets

153

154

Carets that point to positions within element nodes' children.

155

156

```typescript { .api }

157

/**

158

* Caret pointing to child position within element

159

*/

160

interface ChildCaret extends BaseCaret {

161

type: 'child';

162

node: ElementNode;

163

offset: number;

164

}

165

166

/**

167

* Create child caret

168

* @param node - Parent element node

169

* @param offset - Child index offset

170

* @returns Child caret

171

*/

172

function $getChildCaret(node: ElementNode, offset: number): ChildCaret;

173

174

/**

175

* Get child caret or self if not element

176

* @param node - Node to get caret for

177

* @param offset - Offset within node

178

* @returns Child caret or appropriate caret type

179

*/

180

function $getChildCaretOrSelf(node: LexicalNode, offset: number): PointCaret;

181

182

/**

183

* Get child caret at specific index

184

* @param node - Parent element node

185

* @param index - Child index

186

* @returns Child caret

187

*/

188

function $getChildCaretAtIndex(node: ElementNode, index: number): ChildCaret;

189

190

/**

191

* Get adjacent child caret

192

* @param caret - Current child caret

193

* @param direction - Direction to move

194

* @returns Adjacent child caret or null

195

*/

196

function $getAdjacentChildCaret(

197

caret: ChildCaret,

198

direction: CaretDirection

199

): ChildCaret | null;

200

201

/**

202

* Check if caret is child caret

203

* @param caret - Caret to check

204

* @returns True if child caret

205

*/

206

function $isChildCaret(caret: unknown): caret is ChildCaret;

207

208

/**

209

* Check if caret is node-based caret

210

* @param caret - Caret to check

211

* @returns True if node caret

212

*/

213

function $isNodeCaret(caret: unknown): caret is NodeCaret;

214

```

215

216

### Caret Ranges

217

218

Ranges defined by two carets for selection and text manipulation.

219

220

```typescript { .api }

221

/**

222

* Range defined by two carets

223

*/

224

interface CaretRange {

225

start: PointCaret;

226

end: PointCaret;

227

}

228

229

/**

230

* Create caret range

231

* @param start - Start caret

232

* @param end - End caret

233

* @returns Caret range

234

*/

235

function $getCaretRange(start: PointCaret, end: PointCaret): CaretRange;

236

237

/**

238

* Create collapsed caret range

239

* @param caret - Single caret for collapsed range

240

* @returns Collapsed caret range

241

*/

242

function $getCollapsedCaretRange(caret: PointCaret): CaretRange;

243

244

/**

245

* Extend caret to create range

246

* @param caret - Starting caret

247

* @param direction - Direction to extend

248

* @param distance - Distance to extend

249

* @returns Caret range

250

*/

251

function $extendCaretToRange(

252

caret: PointCaret,

253

direction: CaretDirection,

254

distance: number

255

): CaretRange;

256

```

257

258

### Caret Movement and Navigation

259

260

Functions for moving carets and navigating through the document.

261

262

```typescript { .api }

263

/**

264

* Get caret in specified direction

265

* @param caret - Starting caret

266

* @param direction - Direction to move

267

* @param rootMode - Root boundary mode

268

* @returns New caret position or null

269

*/

270

function $getCaretInDirection(

271

caret: PointCaret,

272

direction: CaretDirection,

273

rootMode?: RootMode

274

): PointCaret | null;

275

276

/**

277

* Get caret range in direction

278

* @param range - Starting range

279

* @param direction - Direction to move

280

* @param distance - Distance to move

281

* @returns New caret range

282

*/

283

function $getCaretRangeInDirection(

284

range: CaretRange,

285

direction: CaretDirection,

286

distance: number

287

): CaretRange;

288

289

/**

290

* Get adjacent sibling or parent sibling caret

291

* @param caret - Starting caret

292

* @param direction - Direction to search

293

* @returns Adjacent caret or null

294

*/

295

function $getAdjacentSiblingOrParentSiblingCaret(

296

caret: PointCaret,

297

direction: CaretDirection

298

): PointCaret | null;

299

300

/**

301

* Normalize caret to deepest equivalent position

302

* @param caret - Caret to normalize

303

* @returns Normalized caret

304

*/

305

function $normalizeCaret(caret: PointCaret): PointCaret;

306

307

/**

308

* Check if text point caret is extendable

309

* @param caret - Text point caret to check

310

* @param direction - Direction to extend

311

* @returns True if extendable

312

*/

313

function $isExtendableTextPointCaret(

314

caret: TextPointCaret,

315

direction: CaretDirection

316

): boolean;

317

```

318

319

### Caret Comparison and Ordering

320

321

Functions for comparing and ordering carets.

322

323

```typescript { .api }

324

/**

325

* Compare point caret ordering

326

* @param a - First caret

327

* @param b - Second caret

328

* @returns Comparison result (-1, 0, 1)

329

*/

330

function $comparePointCaretNext(a: PointCaret, b: PointCaret): -1 | 0 | 1;

331

332

/**

333

* Function type for flipping direction

334

*/

335

type FlipDirection = (direction: CaretDirection) => CaretDirection;

336

337

/**

338

* Flip caret direction

339

* @param direction - Direction to flip

340

* @returns Opposite direction

341

*/

342

function flipDirection(direction: CaretDirection): CaretDirection;

343

```

344

345

### Common Ancestor Operations

346

347

Functions for finding common ancestors and analyzing relationships between carets.

348

349

```typescript { .api }

350

/**

351

* Result of common ancestor analysis

352

*/

353

type CommonAncestorResult =

354

| CommonAncestorResultSame

355

| CommonAncestorResultAncestor

356

| CommonAncestorResultDescendant

357

| CommonAncestorResultBranch;

358

359

/**

360

* Same node result

361

*/

362

interface CommonAncestorResultSame {

363

type: 'same';

364

node: LexicalNode;

365

}

366

367

/**

368

* Ancestor relationship result

369

*/

370

interface CommonAncestorResultAncestor {

371

type: 'ancestor';

372

ancestor: LexicalNode;

373

descendant: LexicalNode;

374

}

375

376

/**

377

* Descendant relationship result

378

*/

379

interface CommonAncestorResultDescendant {

380

type: 'descendant';

381

ancestor: LexicalNode;

382

descendant: LexicalNode;

383

}

384

385

/**

386

* Branch relationship result

387

*/

388

interface CommonAncestorResultBranch {

389

type: 'branch';

390

commonAncestor: LexicalNode;

391

branchA: LexicalNode;

392

branchB: LexicalNode;

393

}

394

395

/**

396

* Find common ancestor of two carets

397

* @param a - First caret

398

* @param b - Second caret

399

* @returns Common ancestor analysis result

400

*/

401

function $getCommonAncestor(a: PointCaret, b: PointCaret): CommonAncestorResult;

402

403

/**

404

* Get branch order in common ancestor result

405

* @param result - Common ancestor result

406

* @returns Branch ordering information

407

*/

408

function $getCommonAncestorResultBranchOrder(

409

result: CommonAncestorResultBranch

410

): -1 | 0 | 1;

411

```

412

413

### Selection Integration

414

415

Functions for converting between carets and editor selections.

416

417

```typescript { .api }

418

/**

419

* Create caret from selection point

420

* @param point - Selection point

421

* @returns Corresponding caret

422

*/

423

function $caretFromPoint(point: Point): PointCaret;

424

425

/**

426

* Create caret range from selection

427

* @param selection - Editor selection

428

* @returns Corresponding caret range

429

*/

430

function $caretRangeFromSelection(selection: BaseSelection): CaretRange | null;

431

432

/**

433

* Set selection point from caret

434

* @param point - Selection point to update

435

* @param caret - Caret to convert

436

*/

437

function $setPointFromCaret(point: Point, caret: PointCaret): void;

438

439

/**

440

* Set editor selection from caret range

441

* @param range - Caret range to convert

442

*/

443

function $setSelectionFromCaretRange(range: CaretRange): void;

444

445

/**

446

* Update range selection from caret range

447

* @param selection - Range selection to update

448

* @param range - Caret range source

449

*/

450

function $updateRangeSelectionFromCaretRange(

451

selection: RangeSelection,

452

range: CaretRange

453

): void;

454

```

455

456

### Text Manipulation with Carets

457

458

Functions for text operations using caret positioning.

459

460

```typescript { .api }

461

/**

462

* Remove text within caret range

463

* @param range - Caret range defining text to remove

464

*/

465

function $removeTextFromCaretRange(range: CaretRange): void;

466

467

/**

468

* Split at point caret next position

469

* @param caret - Text point caret to split at

470

* @param options - Split options

471

* @returns Split result

472

*/

473

function $splitAtPointCaretNext(

474

caret: TextPointCaret,

475

options?: SplitAtPointCaretNextOptions

476

): [TextNode, TextNode];

477

478

/**

479

* Options for splitting at point caret

480

*/

481

interface SplitAtPointCaretNextOptions {

482

/** Whether to preserve formatting */

483

preserveFormat?: boolean;

484

/** Whether to update selection */

485

updateSelection?: boolean;

486

}

487

```

488

489

### Stepwise Iterator

490

491

Advanced iteration system for traversing carets.

492

493

```typescript { .api }

494

/**

495

* Configuration for stepwise iterator

496

*/

497

interface StepwiseIteratorConfig {

498

/** Starting caret position */

499

start: PointCaret;

500

/** Ending caret position */

501

end?: PointCaret;

502

/** Direction of iteration */

503

direction: CaretDirection;

504

/** Root boundary mode */

505

rootMode?: RootMode;

506

}

507

508

/**

509

* Create stepwise iterator for caret traversal

510

* @param config - Iterator configuration

511

* @returns Iterator function

512

*/

513

function makeStepwiseIterator(

514

config: StepwiseIteratorConfig

515

): () => PointCaret | null;

516

```

517

518

**Usage Examples:**

519

520

```typescript

521

import {

522

$getTextPointCaret,

523

$getSiblingCaret,

524

$getCaretRange,

525

$setSelectionFromCaretRange,

526

$caretRangeFromSelection,

527

$getCaretInDirection

528

} from "lexical";

529

530

// Working with text point carets

531

editor.update(() => {

532

const textNode = $createTextNode('Hello, world!');

533

const caret = $getTextPointCaret(textNode, 5); // Position after "Hello"

534

535

// Move caret in direction

536

const nextCaret = $getCaretInDirection(caret, 'next');

537

538

// Create caret range

539

const endCaret = $getTextPointCaret(textNode, 11); // Position after "world"

540

const range = $getCaretRange(caret, endCaret);

541

542

// Convert to selection

543

$setSelectionFromCaretRange(range);

544

});

545

546

// Working with sibling carets

547

editor.update(() => {

548

const paragraph = $createParagraphNode();

549

const siblingCaret = $getSiblingCaret(paragraph, 'next');

550

551

// Create range and select

552

const range = $getCollapsedCaretRange(siblingCaret);

553

$setSelectionFromCaretRange(range);

554

});

555

556

// Converting from selection to carets

557

editor.update(() => {

558

const selection = $getSelection();

559

if (selection) {

560

const caretRange = $caretRangeFromSelection(selection);

561

if (caretRange) {

562

console.log('Selection as caret range:', caretRange);

563

564

// Manipulate with caret precision

565

$removeTextFromCaretRange(caretRange);

566

}

567

}

568

});

569

570

// Advanced caret iteration

571

const iteratorConfig = {

572

start: $getTextPointCaret(textNode, 0),

573

end: $getTextPointCaret(textNode, textNode.getTextContent().length),

574

direction: 'next' as CaretDirection

575

};

576

577

const iterator = makeStepwiseIterator(iteratorConfig);

578

let currentCaret = iterator();

579

580

while (currentCaret) {

581

console.log('Current caret position:', currentCaret);

582

currentCaret = iterator();

583

}

584

```

585

586

The caret system provides unprecedented precision in cursor positioning and text manipulation, enabling advanced text editing features that go beyond standard DOM selection capabilities. It's particularly useful for complex text editing scenarios, custom selection behaviors, and precise cursor movement operations.