export type ChangeDecision = "pending" | "accepted" | "rejected"; /** Review bookkeeping (counter, keep-all, undo, commit gate) without a surface: the host draws what its * screen needs. Decisions are stored as overrides against a default, never seeded from `ids` — `ids` is * usually a per-render memo, so a seeding effect would loop ("Maximum update depth exceeded"); stored * exceptions keep the selection derived. */ export interface ChangeSet { status: (id: Id) => ChangeDecision; accept: (id: Id) => void; reject: (id: Id) => void; /** Back to the default — a decided row's Undo. */ undo: (id: Id) => void; acceptAll: () => void; rejectAll: () => void; reset: () => void; accepted: readonly Id[]; rejected: readonly Id[]; pending: readonly Id[]; keptCount: number; total: number; /** Nothing undecided — the usual commit gate. */ settled: boolean; } export interface UseChangeSetOptions { /** What an untouched proposal counts as. Default `accepted`: the operator drops exceptions instead of * approving eight identical lines; `pending` when each change deserves a verdict, gating on `settled`. * A map when kinds differ in safe default — filling a blank `accepted`, overwriting a human value * `rejected` — with unnamed ids `accepted`; calling `reject()` from a result handler would record * overrides nobody made and break `undo`. A value, not a predicate, so it is a dependency: an inline * predicate is new each render and could only be honoured by hiding it from dependencies, freezing * `status` for memoizing screens. */ initial?: ChangeDecision | ReadonlyMap; } export declare function useChangeSet(ids: readonly Id[], options?: UseChangeSetOptions): ChangeSet;