/** * Shared per-user undo/redo for collaborative surfaces. * * Two primitives, one rule: undo NEVER reverts another participant's (human * or agent) work, and never restores whole-document snapshots that would * clobber concurrent edits. * * - `useCollabUndo` — Y.UndoManager lifecycle wrapper for Yjs-backed surfaces. * Tracks only transactions tagged with the local origin, coalesces rapid * edits, and is recreated/destroyed when the document changes (stale * managers hold Y.Doc references and grow unboundedly). * * - `useLocalOpUndo` / `createLocalOpUndoController` — inverse-operation undo * for op-based apps (slides decks, forms). Each local mutation registers * the granular ops that redo and undo it; Cmd+Z replays the inverse ops * through the app's normal granular mutation path, so concurrent edits by * other participants to OTHER items are never touched. */ import * as Y from "yjs"; export interface UndoKeyboardOptions { /** Bind Mod+Z / Shift+Mod+Z / Mod+Y on window while mounted. */ enableKeyboardShortcuts?: boolean; /** * Skip the shortcut when the event target is an input, textarea, or * contentEditable (those own their own undo). Default true. */ ignoreInputTargets?: boolean; } export type CollabUndoScope = Y.AbstractType | Y.AbstractType[] | ((doc: Y.Doc) => Y.AbstractType | Y.AbstractType[]); export interface UseCollabUndoOptions extends UndoKeyboardOptions { /** The collaborative document. Null/undefined while loading. */ ydoc: Y.Doc | null | undefined; /** * Shared type(s) to track, or a factory receiving the doc (evaluated when * the doc changes) — e.g. `(doc) => doc.getText("content")`. */ scope: CollabUndoScope; /** * Origins captured as undoable. Defaults to the hook's own `localOrigin`. * Transactions from remote peers ("remote") and the agent ("agent"/ * "server") are never captured. */ trackedOrigins?: unknown[]; /** Coalesce rapid edits into one undo step. Default 500ms. */ captureTimeout?: number; } export interface UseCollabUndoResult { /** Undo the local user's most recent edit. Returns true if applied. */ undo: () => boolean; /** Redo the local user's most recently undone edit. */ redo: () => boolean; canUndo: boolean; canRedo: boolean; /** * Tag local transactions with this origin so they're captured: * `ydoc.transact(() => { ... }, localOrigin)` — or use `transactLocal`. */ localOrigin: unknown; /** Run a mutation in a transaction tagged with the local origin. */ transactLocal: (fn: () => T) => T; /** The underlying manager (null while the doc is loading). */ undoManager: Y.UndoManager | null; } export declare function useCollabUndo(options: UseCollabUndoOptions): UseCollabUndoResult; export interface LocalOpUndoEntry { /** Granular ops that reverse the mutation. */ undo: TOp[]; /** Granular ops that re-apply the mutation. */ redo: TOp[]; /** Optional label for UI ("Delete slide 3"). */ label?: string; /** * Coalescing key: consecutive pushes with the same non-empty key within * `coalesceMs` merge into one entry (keeps the FIRST undo ops and the * LATEST redo ops) — e.g. per-keystroke text patches on one field. */ coalesceKey?: string; } export interface LocalOpUndoController { /** Record a local mutation. Clears the redo stack. */ push(entry: LocalOpUndoEntry): void; /** Apply the inverse ops of the most recent entry. */ undo(): Promise; /** Re-apply the most recently undone entry. */ redo(): Promise; canUndo(): boolean; canRedo(): boolean; clear(): void; /** Peek labels for UI (undo tooltip). */ peekUndoLabel(): string | undefined; peekRedoLabel(): string | undefined; } export interface CreateLocalOpUndoOptions { /** * Apply granular ops through the app's normal mutation path. MUST NOT * push back into this controller (re-entrant pushes are ignored). */ apply: (ops: TOp[], direction: "undo" | "redo", entry: LocalOpUndoEntry) => void | Promise; /** Max retained entries. Default 200. */ maxDepth?: number; /** Window for coalescing same-key pushes. Default 800ms. */ coalesceMs?: number; /** Called after any stack change (for canUndo/canRedo UI updates). */ onChange?: () => void; /** Clock override for tests. */ now?: () => number; } export declare function createLocalOpUndoController(options: CreateLocalOpUndoOptions): LocalOpUndoController; export interface UseLocalOpUndoOptions extends Omit, "onChange">, UndoKeyboardOptions { } export interface UseLocalOpUndoResult { push: (entry: LocalOpUndoEntry) => void; undo: () => Promise; redo: () => Promise; canUndo: boolean; canRedo: boolean; clear: () => void; controller: LocalOpUndoController; } export declare function useLocalOpUndo(options: UseLocalOpUndoOptions): UseLocalOpUndoResult; //# sourceMappingURL=undo.d.ts.map