import type React from 'react';
import { type AutocompleteItem, type AutocompleteSource } from './model.js';
export interface UseComposerAutocompleteOptions {
    /** The composer's draft. Accepting an item rewrites it through `onValueChange`. */
    value: string;
    onValueChange: (next: string) => void;
    /**
     * What can be completed, and after which character. The composer knows nothing about what a
     * source offers — a skills list and an agent mention are the same shape.
     */
    sources: readonly AutocompleteSource[];
    /** A source that threw. The menu reports it; typing is unaffected either way. */
    onError?: (error: unknown, source: AutocompleteSource) => void;
    /** Tab accepts the highlighted item, as Enter does. Default `true`. */
    acceptOnTab?: boolean;
    /** Base for the generated `id`s the ARIA wiring points at. */
    id?: string;
}
export interface AutocompleteInputProps {
    ref: (element: HTMLTextAreaElement | HTMLInputElement | null) => void;
    onChange: (event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => void;
    onKeyDown: (event: React.KeyboardEvent<HTMLTextAreaElement | HTMLInputElement>) => void;
    onSelect: (event: React.SyntheticEvent<HTMLTextAreaElement | HTMLInputElement>) => void;
    onClick: (event: React.MouseEvent<HTMLTextAreaElement | HTMLInputElement>) => void;
    role: 'combobox';
    'aria-expanded': boolean;
    'aria-autocomplete': 'list';
    'aria-haspopup': 'listbox';
    'aria-controls'?: string;
    'aria-activedescendant'?: string;
}
export interface AutocompleteListboxProps {
    id: string;
    role: 'listbox';
    'aria-label': string;
}
export interface AutocompleteOptionProps {
    id: string;
    role: 'option';
    'aria-selected': boolean;
}
export interface ComposerAutocomplete {
    isOpen: boolean;
    /** The source behind the open menu, so a renderer can group or label by it. */
    source: AutocompleteSource | null;
    trigger: string | null;
    /** The text between the trigger and the caret. */
    query: string;
    items: AutocompleteItem[];
    activeIndex: number;
    activeItem: AutocompleteItem | null;
    /** An async source has been asked and has not answered yet. */
    isLoading: boolean;
    /** The message from a source that threw. The menu stays open and empty. */
    error: string | null;
    highlight: (index: number) => void;
    /** Wraps at both ends, so ↓ from the last item lands on the first. */
    moveHighlight: (delta: number) => void;
    /** Insert an item — the highlighted one by default. A click handler calls this directly. */
    accept: (item?: AutocompleteItem) => void;
    /** Close for the token being typed, without touching the text. */
    dismiss: () => void;
    getInputProps: () => AutocompleteInputProps;
    getListboxProps: () => AutocompleteListboxProps;
    getOptionProps: (index: number) => AutocompleteOptionProps;
}
/**
 * The state machine behind a `/`-style autocomplete in a chat composer: which trigger the caret is
 * inside, what the query is, which items answer it, which one is highlighted, and what accepting
 * one does to the text and the caret. It renders nothing and knows nothing about skills, agents or
 * threads — a host supplies sources, and the same machine drives `@` as drives `/`.
 *
 * The keys the open menu owns (↑ ↓ Enter Tab Escape) are consumed with `preventDefault`, which is
 * how a composer that sends on Enter knows to stand down: call this handler first and return early
 * when the event comes back `defaultPrevented`.
 */
export declare function useComposerAutocomplete(options: UseComposerAutocompleteOptions): ComposerAutocomplete;
//# sourceMappingURL=use-composer-autocomplete.d.ts.map