import type { Component } from "@earendil-works/pi-tui";
/**
 * Row-intent discriminated union. `kind` is the single discriminator —
 * pre-1.0.3 boolean flags have been removed (see `banned-flags.test.ts`).
 * Modeled after `QuestionnaireAction` (`key-router.ts:13-32`) and `Effect`
 * (`state-reducer.ts:26-32`) — pure literal-tagged variants, no shared base,
 * exhaustive-`switch` enforcement via non-`void` returns.
 *
 * Variant semantics:
 * - `option`: a regular author-defined option row.
 * - `other`: the inline free-text input row appended to single-select questions
 *   (label is "Type something."). Renders as inline `Input` when active.
 * - `chat`: the abandon-questionnaire escape-hatch row (label is "Chat about this").
 * - `next`: the explicit commit-and-advance row appended to multi-select questions
 *   (label is "Next"). Renders without a number / checkbox.
 */
export type WrappingSelectItem = {
    kind: "option";
    label: string;
    description?: string;
} | {
    kind: "other";
    label: string;
    description?: string;
} | {
    kind: "chat";
    label: string;
    description?: string;
} | {
    kind: "next";
    label: string;
    description?: string;
};
export interface WrappingSelectTheme {
    selectedText: (text: string) => string;
    description: (text: string) => string;
    scrollInfo: (text: string) => string;
}
/**
 * Numbering controls.
 *
 * Use `numberStartOffset` + `totalItemsForNumbering` when a list is logically a slice of a
 * larger numbered sequence — e.g. the chat row lives in its own WrappingSelect but should
 * render as `(N+1).` where N is the previous list's item count, with the column padded as
 * if both lists were one continuous numbered sequence.
 */
export interface WrappingSelectOptions {
    /** Start numbering at this offset + 1 (default 0 → rows labeled 1, 2, 3 …). */
    numberStartOffset?: number;
    /** Override the total used to pad the number column (useful when items span multiple lists). */
    totalItemsForNumbering?: number;
}
export declare class WrappingSelect implements Component {
    private static readonly ACTIVE_POINTER;
    private static readonly INACTIVE_POINTER;
    private static readonly NUMBER_SEPARATOR;
    private static readonly CURSOR_INVERSE_START;
    private static readonly CURSOR_INVERSE_END;
    private static readonly CONFIRMED_MARK;
    private static readonly MIN_CONTENT_WIDTH;
    private readonly items;
    private readonly maxVisible;
    private readonly theme;
    private numberStartOffset;
    private totalItemsForNumbering;
    private selectedIndex;
    private focused;
    private inputBuffer;
    private inputCursor;
    /**
     * Index of the row that was previously confirmed for this list (e.g. the user's prior
     * answer when re-entering a multi-question tab). Renders `<label> ✔` in the active-row
     * styling but WITHOUT the `❯` pointer — pointer is reserved for the live cursor. When
     * `selectedIndex === confirmedIndex && focused`, the active rendering wins (no double-mark).
     */
    private confirmedIndex;
    /**
     * When set together with `confirmedIndex`, replaces the row's static label at render time.
     * Used for the `kind: "other"` sentinel — its label is "Type something." but if the user's
     * prior answer was custom text, we render that text instead (e.g. `4. Hello ✔`).
     */
    private confirmedLabelOverride;
    constructor(items: readonly WrappingSelectItem[], maxVisible: number, theme: WrappingSelectTheme, options?: WrappingSelectOptions);
    /**
     * Update the numbering offset + total padding width without rebuilding the component.
     * Used by the host to keep the chat-row WrappingSelect's number aligned with the active tab's
     * options list when the user switches tabs (each tab can have a different items count).
     */
    setNumbering(numberStartOffset: number, totalItemsForNumbering: number): void;
    setSelectedIndex(index: number): void;
    setFocused(focused: boolean): void;
    /**
     * Mark a previously-confirmed row. Pass `undefined` to clear. `labelOverride` replaces
     * the row's static `item.label` at render time — used for the `kind: "other"` sentinel so
     * the row reads `Hello ✔` instead of `Type something. ✔` when the prior answer was custom
     * text.
     */
    setConfirmedIndex(index: number | undefined, labelOverride?: string): void;
    setInputBuffer(text: string): void;
    setInputCursor(cursor: number): void;
    /** Intentionally empty — input is routed at the container level. */
    handleInput(_data: string): void;
    invalidate(): void;
    render(width: number): string[];
    private computeVisibleWindow;
    private hasItemsOutsideWindow;
    private renderItem;
    private buildRowPrefix;
    private shouldRenderAsInlineInput;
    /**
     * Render the inline input row across one or more lines, wrapping at `contentWidth`
     * so long input doesn't run off the right edge or trip the parent renderer's
     * width invariant. Mirrors `renderLabelBlock`'s contract: first line carries
     * `rowPrefix`, continuation lines carry `continuationPrefix` (spaces), and every
     * emitted line passes through `theme.selectedText`. The cursor mirrors the
     * main chat editor: inverse-video over the character at the caret, or an
     * inverse-video space at end-of-line.
     */
    private renderInlineInputRow;
    private inputTextWithCursor;
    private clampInputCursor;
    private renderLabelBlock;
    private renderDescriptionBlock;
}
//# sourceMappingURL=wrapping-select.d.ts.map