/** * Headless Undo/Redo Manager * * Manages undo/redo state for notebooks when operated via the headless backend (MCP). * This mirrors the frontend UndoRedoManager but uses NebulaCell types. * * Note: This is a simplified version of lib/undoRedoCore.ts adapted for the backend. * The core logic is intentionally similar to maintain feature parity. */ import { NebulaCell } from '../fs/types'; import { FilesystemService } from '../fs/fs-service'; export type EditSource = 'user' | 'ai' | 'mcp' | 'system' | 'error'; export type MetadataChanges = Record; /** Patch format for content updates (diff-match-patch compatible) */ export interface Patch { diffs: Array<[number, string]>; start1: number; start2: number; length1: number; length2: number; } export type UndoableOperation = { type: 'insertCell'; index: number; cell: NebulaCell; source?: EditSource; } | { type: 'deleteCell'; index: number; cell: NebulaCell; source?: EditSource; } | { type: 'moveCell'; fromIndex: number; toIndex: number; source?: EditSource; } | { type: 'updateContent'; cellId: string; oldContent: string; newContent: string; source?: EditSource; } | { type: 'updateMetadata'; cellId: string; changes: MetadataChanges; source?: EditSource; } | { type: 'batch'; operations: UndoableOperation[]; source?: EditSource; }; export type EventCategory = 'execution' | 'kernel' | 'system' | 'ui'; export type EventTarget = { cellId?: string; cellIndex?: number; }; export type EventOperation = { type: 'event'; category: EventCategory; name: string; target?: EventTarget; runId?: string; data?: Record; source?: EditSource; }; export type LegacyLogOperation = { type: 'runCell'; cellId: string; cellIndex: number; runId?: string; } | { type: 'runAllCells'; cellCount?: number; cellIds?: string[]; } | { type: 'interruptKernel'; } | { type: 'restartKernel'; } | { type: 'runCellComplete'; cellId: string; cellIndex: number; durationMs: number; success: boolean; output?: string; runId?: string; }; export type LogOperation = EventOperation | LegacyLogOperation; export interface SnapshotOperation { type: 'snapshot'; cells: NebulaCell[]; } export interface BaseOperation { timestamp: number; operationId?: string; isUndo?: boolean; undoesOperationId?: string; } export type TimestampedOperation = BaseOperation & (UndoableOperation | LogOperation | SnapshotOperation); export type Operation = UndoableOperation; export interface UndoRedoResult { success: boolean; affectedCellIds: string[]; operationType: string; error?: string; } /** Summary of an update for agent awareness */ export interface UpdateSummary { kind: 'edit' | 'event'; type: string; category?: EventCategory; name?: string; cellId?: string; cellIndex?: number; timestamp: number; description: string; source?: EditSource; runId?: string; data?: Record; } interface NotebookUndoState { undoStack: Operation[]; redoStack: Operation[]; fullHistory: TimestampedOperation[]; lastContent: Map; } /** * Manages undo/redo state for multiple notebooks in the headless backend. */ export declare class HeadlessUndoRedoManager { private states; private fsService; constructor(fsService: FilesystemService); /** * Get or initialize undo state for a notebook. */ getState(notebookPath: string, cells: NebulaCell[]): NotebookUndoState; /** * Preload a notebook's history without blocking the event loop. After this * resolves, getState is a pure in-memory hit — its loadHistorySync fallback * (a large journal read that stalls every in-flight request on a network * filesystem) never fires. No-op if state already exists. */ warm(notebookPath: string, cells: NebulaCell[]): Promise; private initState; /** * Rebuild undo stack from history. */ private rebuildUndoStack; /** * Record an operation (called by headless handler after executing an operation). */ recordOperation(notebookPath: string, cells: NebulaCell[], op: UndoableOperation): void; /** * Record a non-undoable log operation (history only). */ recordLogOperation(notebookPath: string, cells: NebulaCell[], op: LogOperation): void; /** * Undo the last operation. */ undo(notebookPath: string, cells: NebulaCell[]): { cells: NebulaCell[]; result: UndoRedoResult; }; /** * Redo the last undone operation. */ redo(notebookPath: string, cells: NebulaCell[]): { cells: NebulaCell[]; result: UndoRedoResult; }; private updateContentTrackingAfterUndo; private updateContentTrackingAfterRedo; /** * Check if undo is available. */ canUndo(notebookPath: string, cells: NebulaCell[]): boolean; /** * Check if redo is available. */ canRedo(notebookPath: string, cells: NebulaCell[]): boolean; /** * Get history for a notebook. */ getHistory(notebookPath: string, cells: NebulaCell[]): TimestampedOperation[]; /** * Clear state for a notebook (e.g., when closing). */ /** Drop in-memory state so the next access rebuilds from the persisted * history file (the file/history may have changed underneath us: UI save, * external-edit reconciliation). Omit the path to clear all. */ clearState(notebookPath?: string): void; /** * Get updates since a timestamp (for agent awareness). * Returns human-readable summaries of operations made between agent sessions. * Includes both edits and non-undoable events (no source filtering). */ getUpdatesSince(notebookPath: string, cells: NebulaCell[], sinceTimestamp: number): UpdateSummary[]; } export declare function getUndoRedoManager(fsService: FilesystemService): HeadlessUndoRedoManager; export {};