/** * History Service for FlowDrop * * Provides undo/redo functionality for the workflow editor using snapshot-based history. * Supports debounced drag operations and configurable history limits. * * @module services/historyService */ import type { Workflow } from '../types/index.js'; /** * Represents a single entry in the history stack */ export interface HistoryEntry { /** Deep clone of the workflow state at this point */ snapshot: Workflow; /** Timestamp when the entry was created */ timestamp: number; /** Human-readable description of the change (e.g., "Add node", "Delete edge") */ description?: string; } /** * Current state of the history service */ export interface HistoryState { /** Whether undo is available */ canUndo: boolean; /** Whether redo is available */ canRedo: boolean; /** Current position in the history stack */ currentIndex: number; /** Total number of entries in the history stack */ historyLength: number; /** Whether a transaction is currently active */ isInTransaction: boolean; } /** * Options for pushing a new state to history */ export interface PushOptions { /** Description of the change for debugging/display */ description?: string; /** Whether to skip adding this to history (used internally for undo/redo) */ skipHistory?: boolean; } /** * Manages undo/redo history for workflow state * * Uses a snapshot-based approach where each history entry contains * a complete copy of the workflow state. This trades memory for simplicity * and reliability. * * @example * ```typescript * const historyService = new HistoryService(); * * // Initialize with current workflow * historyService.initialize(workflow); * * // Push state before making changes * historyService.push(workflow, { description: "Add node" }); * * // Undo/Redo * const previousState = historyService.undo(); * const nextState = historyService.redo(); * ``` */ export declare class HistoryService { /** Stack of history entries */ private undoStack; /** Stack of redo entries (cleared when new changes are made) */ private redoStack; /** Maximum number of entries to keep in history */ private maxEntries; /** Flag to track if we're in a transaction (batch operation) */ private inTransaction; /** Snapshot captured at transaction start */ private transactionSnapshot; /** Description for the transaction */ private transactionDescription; /** Callbacks to notify when history state changes */ private changeCallbacks; /** * Creates a new HistoryService instance * * @param maxEntries - Maximum number of history entries to keep (default: 50) */ constructor(maxEntries?: number); /** * Initialize the history with the current workflow state * * This clears any existing history and sets the initial state. * Should be called when a workflow is first loaded. * * @param workflow - The initial workflow state */ initialize(workflow: Workflow): void; /** * Push the current state to history before making changes * * Call this BEFORE modifying the workflow to capture the "before" state. * The redo stack is cleared when new changes are pushed. * * @param workflow - The current workflow state (before changes) * @param options - Options for this history entry */ push(workflow: Workflow, options?: PushOptions): void; /** * Undo the last change * * Returns the previous workflow state, or null if at the beginning of history. * * @returns The previous workflow state, or null if cannot undo */ undo(): Workflow | null; /** * Redo the last undone change * * Returns the next workflow state, or null if at the end of history. * * @returns The next workflow state, or null if cannot redo */ redo(): Workflow | null; /** * Start a transaction for grouping multiple changes * * All changes made during a transaction are combined into a single undo entry. * Useful for operations like "delete node" which also removes connected edges. * * @param workflow - The current workflow state (before changes) * @param description - Description for the combined change */ startTransaction(workflow: Workflow, description?: string): void; /** * Commit the current transaction * * Pushes the state captured at transaction start to the history stack. */ commitTransaction(): void; /** * Cancel the current transaction without committing * * Discards the transaction without adding to history. * Returns the snapshot captured at transaction start so the caller * can restore the store to its pre-transaction state. */ cancelTransaction(): Workflow | null; /** * Clear all history * * Resets the history stack. Optionally keeps the current state as initial. * * @param currentWorkflow - If provided, keeps this as the initial state */ clear(currentWorkflow?: Workflow): void; /** * Get the current history state * * @returns The current state of the history service */ getState(): HistoryState; /** * Check if undo is available */ canUndo(): boolean; /** * Check if redo is available */ canRedo(): boolean; /** * Subscribe to history state changes * * @param callback - Function to call when history state changes * @returns Unsubscribe function */ subscribe(callback: (state: HistoryState) => void): () => void; /** * Update the maximum number of history entries * * @param maxEntries - New maximum number of entries */ setMaxEntries(maxEntries: number): void; /** * Internal method to push a state to history */ private pushInternal; /** * Trim history to stay within maxEntries limit. * A maxEntries of 0 means unlimited (no trimming). */ private trimHistory; /** * Create a deep clone of a workflow * * Strips non-serializable values (e.g., onConfigOpen callbacks) before cloning * so structuredClone doesn't throw on function references. * Falls back to JSON round-trip if structuredClone still fails (e.g., Svelte proxies). */ private cloneWorkflow; /** * Notify all subscribers of state changes */ private notifyChange; } /** * Singleton instance of the history service * * Use this for the main workflow editor. For isolated instances, * create a new HistoryService directly. */ export declare const historyService: HistoryService;