CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-slate-history

An operation-based history implementation for Slate editors providing undo/redo functionality.

Pending
Overview
Eval results
Files

history-editor.mddocs/

History Editor

The HistoryEditor interface extends Slate's base editor with history-specific methods and provides utilities for controlling history behavior, managing operation batching, and accessing history state.

Capabilities

HistoryEditor Interface

Extended editor interface that includes history functionality and state management methods.

interface HistoryEditor extends BaseEditor {
  /** History object containing undo and redo stacks */
  history: History;
  /** Undo the last batch of operations */
  undo: () => void;
  /** Redo the last undone batch of operations */
  redo: () => void;
  /** Write a batch of operations to the specified history stack */
  writeHistory: (stack: 'undos' | 'redos', batch: any) => void;
}

Type Guards

Check if an editor has history functionality.

/**
 * Check if a value is a HistoryEditor (has history functionality)
 * @param value - Value to check
 * @returns True if value is a HistoryEditor
 */
function isHistoryEditor(value: any): value is HistoryEditor;

Usage Example:

import { HistoryEditor } from "slate-history";

if (HistoryEditor.isHistoryEditor(editor)) {
  // Safe to use history methods
  editor.undo();
  editor.redo();
  console.log(editor.history.undos.length);
}

Undo and Redo Operations

Static methods for performing undo and redo operations.

/**
 * Undo to the previous saved state
 * @param editor - HistoryEditor instance
 */
function undo(editor: HistoryEditor): void;

/**
 * Redo to the next saved state
 * @param editor - HistoryEditor instance
 */
function redo(editor: HistoryEditor): void;

Usage Examples:

import { HistoryEditor } from "slate-history";

// Using static methods
HistoryEditor.undo(editor);
HistoryEditor.redo(editor);

// Using instance methods (equivalent)
editor.undo();
editor.redo();

State Management Utilities

Methods for checking and controlling editor history state flags.

/**
 * Get the current merging flag value
 * @param editor - HistoryEditor instance
 * @returns Current merging state (undefined, true, or false)
 */
function isMerging(editor: HistoryEditor): boolean | undefined;

/**
 * Get the current saving flag value
 * @param editor - HistoryEditor instance
 * @returns Current saving state (undefined, true, or false)
 */
function isSaving(editor: HistoryEditor): boolean | undefined;

/**
 * Get the current splitting once flag value
 * @param editor - HistoryEditor instance
 * @returns Current splitting state (undefined, true, or false)
 */
function isSplittingOnce(editor: HistoryEditor): boolean | undefined;

/**
 * Set the splitting once flag value
 * @param editor - HistoryEditor instance
 * @param value - New splitting state
 */
function setSplittingOnce(editor: HistoryEditor, value: boolean | undefined): void;

Operation Control Methods

Methods for controlling how operations are batched and saved to history.

/**
 * Apply operations inside a function with merging enabled
 * Operations will be merged into the previous history batch
 * @param editor - HistoryEditor instance
 * @param fn - Function to execute with merging enabled
 */
function withMerging(editor: HistoryEditor, fn: () => void): void;

/**
 * Apply operations ensuring the first operation starts a new batch
 * Subsequent operations will be merged as usual
 * @param editor - HistoryEditor instance
 * @param fn - Function to execute with new batch behavior
 */
function withNewBatch(editor: HistoryEditor, fn: () => void): void;

/**
 * Apply operations without merging into previous save point
 * Each operation will create its own history batch
 * @param editor - HistoryEditor instance
 * @param fn - Function to execute without merging
 */
function withoutMerging(editor: HistoryEditor, fn: () => void): void;

/**
 * Apply operations without saving to history
 * Operations will not be recorded for undo/redo
 * @param editor - HistoryEditor instance
 * @param fn - Function to execute without saving
 */
function withoutSaving(editor: HistoryEditor, fn: () => void): void;

Usage Examples:

import { HistoryEditor } from "slate-history";

// Merge multiple operations into one undo batch
HistoryEditor.withMerging(editor, () => {
  editor.insertText("Hello");
  editor.insertText(" ");
  editor.insertText("World");
  // All three insertions will be undone together
});

// Create a new batch even if operations would normally merge
HistoryEditor.withNewBatch(editor, () => {
  editor.insertText("New batch");
  editor.insertText(" continues"); // This will merge with above
});

// Prevent operations from merging
HistoryEditor.withoutMerging(editor, () => {
  editor.insertText("Each");
  editor.insertText("word"); // Each insertion is separate undo batch
});

// Perform operations without recording in history
HistoryEditor.withoutSaving(editor, () => {
  editor.insertText("Temporary change");
  // This won't be undoable
});

WeakMap State Storage

The HistoryEditor uses WeakMaps for internal state management:

/** WeakMap for attaching History objects to editors */
const HISTORY: WeakMap<Editor, History>;

/** WeakMap for tracking saving state */
const SAVING: WeakMap<Editor, boolean | undefined>;

/** WeakMap for tracking merging state */
const MERGING: WeakMap<Editor, boolean | undefined>;

/** WeakMap for tracking splitting once state */
const SPLITTING_ONCE: WeakMap<Editor, boolean | undefined>;

These WeakMaps are used internally by the history system and are exported for advanced use cases, but typical usage should rely on the provided static methods for state management.

State Management Patterns

Checking State:

// Check if operations are currently being merged
if (HistoryEditor.isMerging(editor)) {
  console.log("Operations will be merged");
}

// Check if operations are being saved to history
if (HistoryEditor.isSaving(editor) !== false) {
  console.log("Operations will be saved");
}

Nested State Control:

// State control methods properly handle nesting
HistoryEditor.withoutSaving(editor, () => {
  HistoryEditor.withMerging(editor, () => {
    // Operations here are merged but not saved
    editor.insertText("temp");
  });
});

Install with Tessl CLI

npx tessl i tessl/npm-slate-history

docs

history-editor.md

history.md

index.md

with-history.md

tile.json