import { RefObject } from 'react'; import { PopupModal } from '../schema/index.ts'; /** * Undo/redo for the editor. * * Every edit in the builder funnels through one `patch` in FormEditor, so * history is a stack of whole-form snapshots taken there rather than a log of * reversible commands. A form is small and the editors already rebuild it * wholesale on each edit, so snapshots cost little and cannot drift out of sync * the way hand-written inverse operations do. * * The work is in deciding what counts as *one* step. Typing a label fires an * edit per keystroke; an undo per letter would be useless. So consecutive edits * that touch the same thing within a short window fold into a single entry, * while anything structural — adding, deleting, reordering, resizing — always * starts its own. */ /** How long consecutive edits to the same thing keep folding into one step. */ export declare const COALESCE_MS = 600; /** What an edit touched, and whether it changed the shape of the form. */ export interface Change { /** * Identity of what was edited. Two edits with the same tag are the "same * thing" being changed again, which is what makes typing fold into one step. */ tag: string; /** * Items added, removed, reordered or resized. Never folded into the previous * step: these are the edits an author most expects to undo one at a time. */ structural: boolean; } /** * Describe an edit by what it touched. Top-level fields are compared by name; * content items by id, so editing one field's label reads differently from * editing another's and the two don't fold together. */ export declare function describeChange(prev: PopupModal, next: PopupModal): Change; /** * Whether an edit continues the step already on the stack rather than starting * a new one. Pure and exported because this single decision is what stands * between "undo reverts the label I just typed" and "undo reverts one letter". */ export declare function foldsIntoPrevious(change: Change, last: { tag: string | null; at: number; }, now: number, hasPast: boolean): boolean; export interface History { /** Record `next` as the new present, folding into the last step where it fits. */ record: (prev: PopupModal, next: PopupModal) => void; /** Step back from `current`, or null when there's nothing to go back to. */ undo: (current: PopupModal) => PopupModal | null; /** Step forward from `current`, or null when there's nothing to go forward to. */ redo: (current: PopupModal) => PopupModal | null; /** Throw the stacks away — a different form has been loaded. */ reset: () => void; canUndo: boolean; canRedo: boolean; } /** * The two stacks. The present is deliberately *not* held here: the caller * already owns the live form and passes it in, so there is no second copy to * fall out of step with it. */ export declare function useHistory(): History; /** * Undo/redo keystrokes: the platform's own — cmd+Z / cmd+shift+Z on a Mac, * ctrl+Z / ctrl+Y elsewhere, both accepted either way round. * * Bound to the document rather than an element because the canvas has no focus * to speak of: clicking a field selects it without focusing anything, so most of * the time the keystroke arrives at the body. To avoid an embedded builder * stealing undo from the page around it, the handler only fires when the last * pointer landed inside the editor — `composedPath` so it still reads true * through the shadow root the editor ships in. */ export declare function useUndoKeys(rootRef: RefObject, onUndo: () => void, onRedo: () => void): void;