/**
 * Logical actions a keyboard interaction can map to. Numeric values are
 * implementation detail; never serialise them.
 */
export declare enum LLSelectAction {
    /** Open the popup (no item activation). */
    Open = 0,
    /** Close the popup (no item activation). */
    Close = 1,
    /** Activate the currently focused option (select + maybe close). */
    Select = 2,
    /** Move focus to the next option (clamps at last). */
    Next = 3,
    /** Move focus to the previous option (clamps at first). */
    Previous = 4,
    /** Move focus to the first option. */
    GotoFirst = 5,
    /** Move focus to the last option. */
    GotoLast = 6,
    /** Jump focus down by a fixed page size. */
    PageDown = 7,
    /** Jump focus up by a fixed page size. */
    PageUp = 8
}
/**
 * Map a keydown event to a logical {@link LLSelectAction}, given whether the
 * popup is currently open.
 * - Returns `undefined` if the key should be left alone (no preventDefault,
 *   no library reaction).
 * - Maps the action keys of the ARIA APG combobox pattern.
 * - The pattern's printable-character typeahead is not mapped here - it needs
 *   the character, which an action enum cannot carry. The keydown handler
 *   runs {@link findTypeaheadIndex} before this mapping.
 *
 * @param inTextInput - true when focus is in the editable filter input. There,
 *   Space must type a space and Home/End must move the text caret, so those
 *   keys are NOT mapped to selection / first-last navigation. Selection is
 *   Enter only; option navigation is the arrow / page keys.
 */
export declare function getActionFromKey(ev: KeyboardEvent, isOpened: boolean, inTextInput?: boolean): LLSelectAction | undefined;
/**
 * Compute a new focused-option index after applying a navigation action.
 * Clamps to `[0, maxIndex]` (no wrap-around). Returns `-1` if there are no
 * options (`maxIndex < 0`). The page size for PageUp/PageDown is a fixed
 * constant.
 *
 * @param currentIndex - current focused index (`-1` for "none")
 * @param maxIndex - largest valid index (`options.length - 1`)
 */
export declare function getUpdatedIndex(currentIndex: number, maxIndex: number, action: LLSelectAction): number;
/**
 * Pause (in ms) after which the next typed character starts a new typeahead
 * buffer instead of extending the old one. Native `<select>` implementations
 * use 1 s (Blink / WebKit).
 */
export declare const TYPEAHEAD_TIMEOUT_MS = 1000;
/**
 * Compute the typeahead buffer after a newly typed character.
 * - If `elapsedMs` since the previous character exceeds
 *   {@link TYPEAHEAD_TIMEOUT_MS}, the character starts a fresh buffer.
 */
export declare function getUpdatedTypeaheadBuffer(buffer: string, char: string, elapsedMs: number): string;
/**
 * Resolve where prefix typeahead moves the active option; `-1` = no match.
 * Mirrors native `<select>` typeahead:
 * - An option matches when its text starts with `buffer`, case-insensitive.
 * - A one-character buffer searches from the option AFTER `currentIndex`, so
 *   repeated presses of one initial cycle through the options sharing it.
 * - A buffer of one repeated character (e.g. `"aa"`) behaves exactly like its
 *   single character - it keeps cycling. It is never matched literally (the
 *   W3C APG example tries the literal `"aa"` prefix first; native does not,
 *   and a text really starting `"aa"` is still reached by the cycle).
 * - Any other longer buffer searches from `currentIndex` itself, so extending
 *   the buffer stays on the current option while it still matches.
 * - The search wraps around the whole list - deliberately, unlike the clamped
 *   arrow navigation (see `docs/llm/A11Y.md`): a search means "anywhere", and
 *   cycling needs the wrap.
 * - Indexes where `textAt` returns `undefined` (disabled options) never match.
 * - `currentIndex` `-1` means no option is active; the search starts at 0.
 *
 * The repeated-character test compares CODE POINTS, each lower-cased on its
 * own: a key whose lower-case form expands to two code units (the Turkish
 * dotted capital I) still counts, astral-plane characters compare whole, and
 * a mid-repeat Shift ("aA") still cycles.
 *
 * @param textAt - match text of the option at an index, or `undefined` when
 *   that option must never match.
 */
export declare function findTypeaheadIndex(buffer: string, count: number, currentIndex: number, textAt: (index: number) => string | undefined): number;
/**
 * Scroll `scrollParent` just enough so `child` is fully visible. No-op if
 * `child` is already in view. Adjusts `scrollTop` directly rather than using
 * `scrollIntoView`, so the page (window) does not scroll alongside.
 *
 * Uses viewport-rect deltas, NOT `offsetTop`: `offsetTop` is relative to the
 * offset parent, which a theme could change by making a group container
 * `position: relative` (optgroup), silently breaking the math. Rect deltas are
 * correct regardless of nesting / theme CSS. `clientTop` / `clientHeight`
 * exclude the parent's border so a bordered list stays exact. Measured to cost
 * the same as the old `offsetTop` path (see docs/llm/DESIGN.md "Optgroup").
 */
export declare function ensureVisibleInScroll(child: HTMLElement, scrollParent: HTMLElement): void;
//# sourceMappingURL=keyboard.d.ts.map