/** * RenderGate — single-source-of-truth render scheduler for the terminal * UI. Modeled on Vigil's render-on-commit pattern (custom React * reconciler with a dirty flag + double buffering): every state mutation * calls markDirty(); a fixed-rate tick commits at most once per frame * when state actually changed. The gate guarantees: * * 1. Animation-driven repaints (spinner, elapsed timer) happen every * frame regardless of whether stdin produced an event. The previous * design had multiple silent-drop paths inside the renderer * (renderThrottle / paste suppression / mode early-return); when ANY * of those flagged on, the screen froze until a keystroke fired. * * 2. State mutations are batched within a frame. Setting mode + * activity + status in the same JS turn results in ONE commit, not * three. * * 3. Re-entrant commits are impossible — the gate is locked while a * commit is in flight. * * 4. A best-effort defer hook (canCommitNow) lets the renderer ask the * gate to wait when an overlay rewrite would be unsafe (e.g. * mid-bracketed-paste). But deferral is hard-bounded: past * maxDeferMs the gate force-commits, so a stuck flag can never * freeze the UI again. * * This is purely additive: existing inline render calls (renderPrompt) * keep working. The gate's tick is the safety net that ensures the * screen stays in sync with state even when those inline calls early- * return for reasons that wouldn't survive scrutiny. */ export interface RenderGateOptions { /** Target frame rate. Vigil's frame budget is ~30fps. */ fps?: number; /** Run a single commit pass; called only when dirty. */ commit: () => void; /** * Optional pre-commit gate. Returning false defers this frame's * commit. The gate enforces a hard upper bound on consecutive defers * (maxDeferMs) so a stuck precondition can never freeze the UI. */ canCommitNow?: () => boolean; /** Hard upper bound on consecutive deferrals (default 1500 ms). */ maxDeferMs?: number; } export declare class RenderGate { private dirty; private rendering; private readonly targetFrameMs; private interval; private commitCount; private skipCount; private forceCommitCount; private deferStartedAt; private readonly commitFn; private readonly canCommitNow; private readonly maxDeferMs; private disposed; constructor(opts: RenderGateOptions); /** Start the frame-rate tick. Idempotent. */ start(): void; /** Stop the tick. Safe to call repeatedly. */ stop(): void; /** Mark state dirty. The next tick (or a flush()) will commit once. */ markDirty(): void; /** * Synchronous force-commit, ignoring deferral and the dirty flag. * Use sparingly — for transitions where the next thing to happen * (e.g. clearing the screen, reading user input) needs the new state * already on the wire. */ flush(): void; private tick; dispose(): void; /** Telemetry — useful for debugging stuck-state issues. */ stats(): { commitCount: number; skipCount: number; forceCommitCount: number; dirty: boolean; }; } //# sourceMappingURL=RenderGate.d.ts.map