CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-lexical

Lexical is an extensible text editor framework that provides excellent reliability, accessible and performance.

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

caret-system.mddocs/

Caret System

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.

Capabilities

Core Caret Types

Base interfaces and types for representing different caret positions.

/**
 * Base interface for all caret types
 */
interface BaseCaret {
  type: CaretType;
}

/**
 * Type of caret positioning
 */
type CaretType = 'text-point' | 'sibling' | 'child';

/**
 * Direction for caret movement
 */
type CaretDirection = 'next' | 'previous';

/**
 * Union type for node-based carets
 */
type NodeCaret = SiblingCaret | ChildCaret;

/**
 * Union type for point-based carets
 */
type PointCaret = TextPointCaret | NodeCaret;

/**
 * Root mode for caret traversal
 */
type RootMode = 'root-boundary' | 'no-boundary';

Text Point Carets

Carets that point to specific positions within text nodes.

/**
 * Caret pointing to position within text node
 */
interface TextPointCaret extends BaseCaret {
  type: 'text-point';
  node: TextNode;
  offset: number;
}

/**
 * Slice of text point caret with additional metadata
 */
interface TextPointCaretSlice extends TextPointCaret {
  textContent: string;
  isFirst: boolean;
  isLast: boolean;
}

/**
 * Tuple type for text point caret slices
 */
type TextPointCaretSliceTuple = [TextPointCaretSlice, TextPointCaretSlice];

/**
 * Create text point caret
 * @param node - Text node to point to
 * @param offset - Character offset within node
 * @returns Text point caret
 */
function $getTextPointCaret(node: TextNode, offset: number): TextPointCaret;

/**
 * Create text point caret slice
 * @param node - Text node
 * @param startOffset - Start offset
 * @param endOffset - End offset
 * @returns Text point caret slice
 */
function $getTextPointCaretSlice(
  node: TextNode,
  startOffset: number,
  endOffset: number
): TextPointCaretSlice;

/**
 * Get text node offset for caret
 * @param caret - Text point caret
 * @returns Character offset
 */
function $getTextNodeOffset(caret: TextPointCaret): number;

/**
 * Check if caret is text point caret
 * @param caret - Caret to check
 * @returns True if text point caret
 */
function $isTextPointCaret(caret: unknown): caret is TextPointCaret;

/**
 * Check if caret is text point caret slice
 * @param caret - Caret to check
 * @returns True if text point caret slice
 */
function $isTextPointCaretSlice(caret: unknown): caret is TextPointCaretSlice;

Sibling Carets

Carets that point to positions relative to sibling nodes.

/**
 * Caret pointing to position relative to sibling
 */
interface SiblingCaret extends BaseCaret {
  type: 'sibling';
  node: LexicalNode;
  direction: CaretDirection;
}

/**
 * Create sibling caret
 * @param node - Reference node
 * @param direction - Direction relative to node
 * @returns Sibling caret
 */
function $getSiblingCaret(node: LexicalNode, direction: CaretDirection): SiblingCaret;

/**
 * Check if caret is sibling caret
 * @param caret - Caret to check
 * @returns True if sibling caret
 */
function $isSiblingCaret(caret: unknown): caret is SiblingCaret;

/**
 * Rewind sibling caret to previous position
 * @param caret - Sibling caret to rewind
 * @returns Rewound caret
 */
function $rewindSiblingCaret(caret: SiblingCaret): SiblingCaret;

Child Carets

Carets that point to positions within element nodes' children.

/**
 * Caret pointing to child position within element
 */
interface ChildCaret extends BaseCaret {
  type: 'child';
  node: ElementNode;
  offset: number;
}

/**
 * Create child caret
 * @param node - Parent element node
 * @param offset - Child index offset
 * @returns Child caret
 */
function $getChildCaret(node: ElementNode, offset: number): ChildCaret;

/**
 * Get child caret or self if not element
 * @param node - Node to get caret for
 * @param offset - Offset within node
 * @returns Child caret or appropriate caret type
 */
function $getChildCaretOrSelf(node: LexicalNode, offset: number): PointCaret;

/**
 * Get child caret at specific index
 * @param node - Parent element node
 * @param index - Child index
 * @returns Child caret
 */
function $getChildCaretAtIndex(node: ElementNode, index: number): ChildCaret;

/**
 * Get adjacent child caret
 * @param caret - Current child caret
 * @param direction - Direction to move
 * @returns Adjacent child caret or null
 */
function $getAdjacentChildCaret(
  caret: ChildCaret,
  direction: CaretDirection
): ChildCaret | null;

/**
 * Check if caret is child caret
 * @param caret - Caret to check
 * @returns True if child caret
 */
function $isChildCaret(caret: unknown): caret is ChildCaret;

/**
 * Check if caret is node-based caret
 * @param caret - Caret to check
 * @returns True if node caret
 */
function $isNodeCaret(caret: unknown): caret is NodeCaret;

Caret Ranges

Ranges defined by two carets for selection and text manipulation.

/**
 * Range defined by two carets
 */
interface CaretRange {
  start: PointCaret;
  end: PointCaret;
}

/**
 * Create caret range
 * @param start - Start caret
 * @param end - End caret
 * @returns Caret range
 */
function $getCaretRange(start: PointCaret, end: PointCaret): CaretRange;

/**
 * Create collapsed caret range
 * @param caret - Single caret for collapsed range
 * @returns Collapsed caret range
 */
function $getCollapsedCaretRange(caret: PointCaret): CaretRange;

/**
 * Extend caret to create range
 * @param caret - Starting caret
 * @param direction - Direction to extend
 * @param distance - Distance to extend
 * @returns Caret range
 */
function $extendCaretToRange(
  caret: PointCaret,
  direction: CaretDirection,
  distance: number
): CaretRange;

Caret Movement and Navigation

Functions for moving carets and navigating through the document.

/**
 * Get caret in specified direction
 * @param caret - Starting caret
 * @param direction - Direction to move
 * @param rootMode - Root boundary mode
 * @returns New caret position or null
 */
function $getCaretInDirection(
  caret: PointCaret,
  direction: CaretDirection,
  rootMode?: RootMode
): PointCaret | null;

/**
 * Get caret range in direction
 * @param range - Starting range
 * @param direction - Direction to move
 * @param distance - Distance to move
 * @returns New caret range
 */
function $getCaretRangeInDirection(
  range: CaretRange,
  direction: CaretDirection,
  distance: number
): CaretRange;

/**
 * Get adjacent sibling or parent sibling caret
 * @param caret - Starting caret
 * @param direction - Direction to search
 * @returns Adjacent caret or null
 */
function $getAdjacentSiblingOrParentSiblingCaret(
  caret: PointCaret,
  direction: CaretDirection
): PointCaret | null;

/**
 * Normalize caret to deepest equivalent position
 * @param caret - Caret to normalize
 * @returns Normalized caret
 */
function $normalizeCaret(caret: PointCaret): PointCaret;

/**
 * Check if text point caret is extendable
 * @param caret - Text point caret to check
 * @param direction - Direction to extend
 * @returns True if extendable
 */
function $isExtendableTextPointCaret(
  caret: TextPointCaret,
  direction: CaretDirection
): boolean;

Caret Comparison and Ordering

Functions for comparing and ordering carets.

/**
 * Compare point caret ordering
 * @param a - First caret
 * @param b - Second caret
 * @returns Comparison result (-1, 0, 1)
 */
function $comparePointCaretNext(a: PointCaret, b: PointCaret): -1 | 0 | 1;

/**
 * Function type for flipping direction
 */
type FlipDirection = (direction: CaretDirection) => CaretDirection;

/**
 * Flip caret direction
 * @param direction - Direction to flip
 * @returns Opposite direction
 */
function flipDirection(direction: CaretDirection): CaretDirection;

Common Ancestor Operations

Functions for finding common ancestors and analyzing relationships between carets.

/**
 * Result of common ancestor analysis
 */
type CommonAncestorResult = 
  | CommonAncestorResultSame
  | CommonAncestorResultAncestor
  | CommonAncestorResultDescendant
  | CommonAncestorResultBranch;

/**
 * Same node result
 */
interface CommonAncestorResultSame {
  type: 'same';
  node: LexicalNode;
}

/**
 * Ancestor relationship result
 */
interface CommonAncestorResultAncestor {
  type: 'ancestor';
  ancestor: LexicalNode;
  descendant: LexicalNode;
}

/**
 * Descendant relationship result
 */
interface CommonAncestorResultDescendant {
  type: 'descendant';
  ancestor: LexicalNode;
  descendant: LexicalNode;
}

/**
 * Branch relationship result
 */
interface CommonAncestorResultBranch {
  type: 'branch';
  commonAncestor: LexicalNode;
  branchA: LexicalNode;
  branchB: LexicalNode;
}

/**
 * Find common ancestor of two carets
 * @param a - First caret
 * @param b - Second caret
 * @returns Common ancestor analysis result
 */
function $getCommonAncestor(a: PointCaret, b: PointCaret): CommonAncestorResult;

/**
 * Get branch order in common ancestor result
 * @param result - Common ancestor result
 * @returns Branch ordering information
 */
function $getCommonAncestorResultBranchOrder(
  result: CommonAncestorResultBranch
): -1 | 0 | 1;

Selection Integration

Functions for converting between carets and editor selections.

/**
 * Create caret from selection point
 * @param point - Selection point
 * @returns Corresponding caret
 */
function $caretFromPoint(point: Point): PointCaret;

/**
 * Create caret range from selection
 * @param selection - Editor selection
 * @returns Corresponding caret range
 */
function $caretRangeFromSelection(selection: BaseSelection): CaretRange | null;

/**
 * Set selection point from caret
 * @param point - Selection point to update
 * @param caret - Caret to convert
 */
function $setPointFromCaret(point: Point, caret: PointCaret): void;

/**
 * Set editor selection from caret range
 * @param range - Caret range to convert
 */
function $setSelectionFromCaretRange(range: CaretRange): void;

/**
 * Update range selection from caret range
 * @param selection - Range selection to update
 * @param range - Caret range source
 */
function $updateRangeSelectionFromCaretRange(
  selection: RangeSelection,
  range: CaretRange
): void;

Text Manipulation with Carets

Functions for text operations using caret positioning.

/**
 * Remove text within caret range
 * @param range - Caret range defining text to remove
 */
function $removeTextFromCaretRange(range: CaretRange): void;

/**
 * Split at point caret next position
 * @param caret - Text point caret to split at
 * @param options - Split options
 * @returns Split result
 */
function $splitAtPointCaretNext(
  caret: TextPointCaret,
  options?: SplitAtPointCaretNextOptions
): [TextNode, TextNode];

/**
 * Options for splitting at point caret
 */
interface SplitAtPointCaretNextOptions {
  /** Whether to preserve formatting */
  preserveFormat?: boolean;
  /** Whether to update selection */
  updateSelection?: boolean;
}

Stepwise Iterator

Advanced iteration system for traversing carets.

/**
 * Configuration for stepwise iterator
 */
interface StepwiseIteratorConfig {
  /** Starting caret position */
  start: PointCaret;
  /** Ending caret position */
  end?: PointCaret;
  /** Direction of iteration */
  direction: CaretDirection;
  /** Root boundary mode */
  rootMode?: RootMode;
}

/**
 * Create stepwise iterator for caret traversal
 * @param config - Iterator configuration
 * @returns Iterator function
 */
function makeStepwiseIterator(
  config: StepwiseIteratorConfig
): () => PointCaret | null;

Usage Examples:

import { 
  $getTextPointCaret,
  $getSiblingCaret,
  $getCaretRange,
  $setSelectionFromCaretRange,
  $caretRangeFromSelection,
  $getCaretInDirection
} from "lexical";

// Working with text point carets
editor.update(() => {
  const textNode = $createTextNode('Hello, world!');
  const caret = $getTextPointCaret(textNode, 5); // Position after "Hello"
  
  // Move caret in direction
  const nextCaret = $getCaretInDirection(caret, 'next');
  
  // Create caret range
  const endCaret = $getTextPointCaret(textNode, 11); // Position after "world"
  const range = $getCaretRange(caret, endCaret);
  
  // Convert to selection
  $setSelectionFromCaretRange(range);
});

// Working with sibling carets
editor.update(() => {
  const paragraph = $createParagraphNode();
  const siblingCaret = $getSiblingCaret(paragraph, 'next');
  
  // Create range and select
  const range = $getCollapsedCaretRange(siblingCaret);
  $setSelectionFromCaretRange(range);
});

// Converting from selection to carets
editor.update(() => {
  const selection = $getSelection();
  if (selection) {
    const caretRange = $caretRangeFromSelection(selection);
    if (caretRange) {
      console.log('Selection as caret range:', caretRange);
      
      // Manipulate with caret precision
      $removeTextFromCaretRange(caretRange);
    }
  }
});

// Advanced caret iteration
const iteratorConfig = {
  start: $getTextPointCaret(textNode, 0),
  end: $getTextPointCaret(textNode, textNode.getTextContent().length),
  direction: 'next' as CaretDirection
};

const iterator = makeStepwiseIterator(iteratorConfig);
let currentCaret = iterator();

while (currentCaret) {
  console.log('Current caret position:', currentCaret);
  currentCaret = iterator();
}

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.

docs

caret-system.md

command-system.md

editor-management.md

index.md

node-system.md

selection-system.md

state-management.md

utilities-helpers.md

tile.json