import type { Element } from "./elements.js";
import type { InputSource, KeyEvent } from "./input/types.js";
import type { OutputTarget } from "./output/types.js";
import { Frame } from "./frame.js";
export declare class Screen {
    private output;
    private input;
    private width;
    private height;
    constructor(opts: {
        output: OutputTarget;
        input: InputSource;
        width: number;
        height: number;
    });
    render(root: Element, label?: string): Frame;
    nextKey(): Promise<KeyEvent>;
    nextLine(prompt: string): Promise<string>;
    size(): {
        width: number;
        height: number;
    };
    runLoop<S>(opts: {
        initialState: S;
        render: (state: S) => Element | Promise<Element>;
        handleKey: (state: S, event: KeyEvent) => S | Promise<S>;
        isDone: (state: S) => boolean | Promise<boolean>;
        label?: string;
        /**
         * If set, the loop races each `nextKey()` against a `setTimeout`
         * of `tickMs` milliseconds. On a tick, it re-renders with the
         * current state (no transition). On a key, it runs `handleKey`
         * then re-renders. Used by `repl()` to keep a live status line
         * ticking while no keys arrive (e.g. during a long LLM call).
         *
         * When omitted, the loop is pure event-driven: it blocks on
         * `nextKey()` and re-renders only after each keypress.
         *
         * The tick-side branch reuses a single in-flight `nextKey()`
         * promise across iterations so a key the user types during a
         * tick race is consumed by the next race rather than queued
         * behind abandoned waiters.
         */
        tickMs?: number;
    }): Promise<S>;
    destroy(): void;
}
