/** * AIInput — the composer a person types a prompt into. * * ```tsx * * * * } onPress={openSheet} /> * * } onPress={record} /> * * * * ``` * * ## The field grows, and then it stops * * It opens one line tall and follows what is typed into it up to `maxRows`, * after which it holds that height and scrolls its own content. Five rows is * the default because a composer that keeps growing eventually pushes its own * send button off the bottom of the screen, and the row that carries the * actions stays pinned to the bottom of the box either way. * * ## It does not record anything * * There is a recording state, and it draws a live meter, but nothing here * touches the microphone. The app owns the recorder — the permission prompt, * the session, the platform quirks — and passes back a `level` between 0 and 1 * along with a `status`. Pass a Reanimated shared value and a recorder * reporting every 30ms never re-renders anything above this component. * * That division is what keeps the composer free of an audio dependency. * * ## Glass is iOS 26 and above * * The surfaces ask for the system material and get it where it exists. Below * iOS 26, on Android, and for anyone with Reduce Transparency switched on, * they are drawn as solid token surfaces instead. Both are finished looks; see * `Glass` for why nothing is faked on the platforms without it. * * ## Where the props come from * * The root takes `ViewProps`. `Field` takes `TextInputProps`, so * `keyboardType`, `maxLength`, `autoFocus` and the rest work as they always * do. Every part takes `className`. */ import { type ReactNode } from 'react'; import { type TextInputProps, type ViewProps } from 'react-native'; import { type SharedValue } from 'react-native-reanimated'; import { type AIInputSize } from './ai-input-growth.js'; /** What the app is doing, which is what the trailing button offers to do next. */ export type AIInputStatus = 'ready' | 'recording' | 'submitted' | 'streaming'; export interface AIInputProps extends Omit { children?: ReactNode; className?: string; /** The prompt, when the app owns it. */ value?: string; /** Called on every keystroke. */ onValueChange?: (value: string) => void; /** The prompt to start with, when the composer owns it. */ defaultValue?: string; /** * What the app is doing. `ready` offers to send, `streaming` offers to stop, * and `recording` swaps the toolbar for the meter and its two decisions. */ status?: AIInputStatus; /** Called with the prompt when it is sent. The composer does not clear itself. */ onSubmit?: (value: string) => void; /** Called when the trailing button is pressed while `streaming`. */ onStop?: () => void; /** Called when the voice button is pressed on an empty composer. */ onVoice?: () => void; /** Called when a recording is thrown away. */ onRecordCancel?: () => void; /** Called when a recording is accepted. */ onRecordConfirm?: () => void; /** * Input level, 0–1, from the app's own recorder. Pass a shared value to keep * metering off the JS thread entirely. Omitted, the meter animates plausible * motion so a screen can be built before any audio exists. */ level?: number | SharedValue; /** Type scale and control size. */ size?: AIInputSize; /** Nothing can be typed, pressed or sent. */ disabled?: boolean; /** * Draw the toolbar's controls as the platform's own buttons, in the system * material — Liquid Glass on iOS 26, the platform's ordinary button style * below it and on Android. * * The platform owns their colour, metrics and shape when this is on, so * `className` and the theme tokens no longer reach them. The card behind * them is still ours, and still glass. Needs the optional `@expo/ui`; * without it the drawn controls are used and nothing breaks. */ native?: boolean; /** Rows the empty field is tall. */ minRows?: number; /** Rows the field grows to before it holds that height and scrolls. */ maxRows?: number; /** Lift the composer clear of the software keyboard. */ avoidKeyboard?: boolean; /** How far above the bottom edge the composer already sits. */ keyboardBottomInset?: number; /** * Gap to leave between the composer and the top of the keyboard. A composer * resting directly on the keys reads as part of them. */ keyboardGap?: number; } declare function AIInputRoot({ children, className, value: valueProp, onValueChange, defaultValue, status, onSubmit, onStop, onVoice, onRecordCancel, onRecordConfirm, level, size, disabled, native, minRows, maxRows, avoidKeyboard, keyboardBottomInset, keyboardGap, style, ...props }: AIInputProps): import("react").JSX.Element; declare namespace AIInputRoot { var displayName: string; } export interface AIInputFieldProps extends Omit { className?: string; } declare function AIInputField({ className, placeholder, onFocus, onBlur, style, ...props }: AIInputFieldProps): import("react").JSX.Element; declare namespace AIInputField { var displayName: string; } export interface AIInputRowProps extends ViewProps { className?: string; } /** * The field and its controls on a single line. * * The composer at its smallest: one pill above the keyboard with the field * between the controls rather than above them. It suits a bar that is always * on screen, where the stacked composer suits one that is the focus of it. * * The field still grows — it just grows the row. * * ```tsx * * * } /> * * } /> * * * * ``` */ declare function AIInputRow({ className, children, ...props }: AIInputRowProps): import("react").JSX.Element; declare namespace AIInputRow { var displayName: string; } export interface AIInputToolbarProps extends ViewProps { className?: string; } declare function AIInputToolbar({ className, children, style, ...props }: AIInputToolbarProps): import("react").JSX.Element; declare namespace AIInputToolbar { var displayName: string; } /** Pushes everything after it to the trailing end of the toolbar. */ declare function AIInputSpacer({ className, ...props }: ViewProps): import("react").JSX.Element; declare namespace AIInputSpacer { var displayName: string; } export interface AIInputActionProps extends Omit, Pick { className?: string; /** Names the control. It is a circle with a glyph in it; nothing else says what it does. */ label: string; icon: ReactNode; onPress?: () => void; disabled?: boolean; /** Control size. Inherited from the composer when it is inside one. */ size?: AIInputSize; /** * Draw it as the platform's own button, in the system material. Inherited * from the composer when it is inside one. The platform owns its colour and * shape, so `className` stops reaching it. */ native?: boolean; } declare function AIInputAction({ className, label, icon, onPress, disabled, size: sizeProp, native, ...props }: AIInputActionProps): import("react").JSX.Element; declare namespace AIInputAction { var displayName: string; } export interface AIInputPillProps extends Omit { className?: string; /** The current value — a model name, a mode, a project. */ label: ReactNode; /** A second, quieter value beside it. */ detail?: ReactNode; /** A glyph after the labels, for a pill that opens a list. */ indicator?: ReactNode; /** * The platform's name for `indicator`, used when the pill is handed over. * * A native button takes a glyph as a name from the system's symbol set, not * as an element — an element would have to be hosted, and a hosted view * inside a labelled button has no width anything can resolve. So the two are * separate props rather than one: `indicator` is what the drawn pill * renders, this is what the handed-over one asks the platform for. * * A pill with an `indicator` and no symbol is still handed over, and has no * glyph on it. */ indicatorSymbol?: string; onPress?: () => void; disabled?: boolean; /** Names the control when the label alone does not say what changing it does. */ accessibilityLabel?: string; /** Control size. Inherited from the composer when it is inside one. */ size?: AIInputSize; /** * Draw it as the platform's own button, in the system material. * * The platform is given text and a symbol name, never elements: a hosted * view inside a labelled native button has no width anything can resolve. So * a `label` or `detail` that is not a string is drawn here whatever this * says, and an `indicator` reaches the platform only through * `indicatorSymbol`. * * A handed-over pill is one label in the platform's own type, so `detail` * stops reading as the quieter half of the pair. */ native?: boolean; } declare function AIInputPill({ className, label, detail, indicator, indicatorSymbol, onPress, disabled, accessibilityLabel, size: sizeProp, native, ...props }: AIInputPillProps): import("react").JSX.Element; declare namespace AIInputPill { var displayName: string; } export interface AIInputSubmitProps extends Omit { className?: string; /** Names the button in its send state. */ sendLabel?: string; /** Names it in its voice state, which is what an empty composer offers. */ voiceLabel?: string; /** Names it while the model is answering. */ stopLabel?: string; /** Draw it as the platform's own button, in the system material. */ native?: boolean; } /** * The trailing button, which is three buttons wearing one shape. * * Empty and ready, it opens voice mode; with something typed, it sends; while * the model is answering, it stops. One control rather than three because only * one of them is ever the thing to do, and a row of dimmed siblings is a row * of things that look broken. */ declare function AIInputSubmit({ className, sendLabel, voiceLabel, stopLabel, native, ...props }: AIInputSubmitProps): import("react").JSX.Element; declare namespace AIInputSubmit { var displayName: string; } export interface AIInputRecordingProps extends Omit { className?: string; cancelLabel?: string; confirmLabel?: string; /** Draw the two decisions as the platform's own buttons. */ native?: boolean; } /** * What the toolbar becomes while a recording is running: throw it away, watch * it, or keep it. Render it in place of `AIInput.Toolbar` when `status` is * `recording`. */ declare function AIInputRecording({ className, cancelLabel, confirmLabel, native, ...props }: AIInputRecordingProps): import("react").JSX.Element; declare namespace AIInputRecording { var displayName: string; } export interface AIInputSheetScreenProps extends ViewProps { className?: string; /** Names this screen. `AIInput.Sheet.Row`'s `to` pushes the screen with this id. */ id: string; /** Centred in the header, and the first thing a screen reader reaches. */ title?: ReactNode; /** * A control at the trailing end of the header — a second action the screen * offers. The leading end is the sheet's, and is a close button on the root * screen and a back button on every screen pushed onto it. */ trailing?: ReactNode; } declare function AIInputSheetScreen({ className, id, title, trailing, children, ...props }: AIInputSheetScreenProps): import("react").JSX.Element; declare namespace AIInputSheetScreen { var displayName: string; } export interface AIInputSheetProps { children?: ReactNode; className?: string; open?: boolean; onOpenChange?: (open: boolean) => void; defaultOpen?: boolean; /** Which screen opens first. Defaults to the first one given. */ initialScreen?: string; /** Called whenever the screen on top changes, pushed or popped. */ onScreenChange?: (id: string) => void; /** Frost the screen behind the sheet instead of dimming it. */ blur?: boolean; /** How tall the sheet opens. `auto` sizes to the screen currently on top. */ size?: 'auto' | 'half' | 'full'; /** * Float the sheet clear of the screen edges instead of docking it to the * bottom. On by default: the surface is a material, and a material reads as * laid over the app when there is app visible around all four of its edges. * Docked, its bottom edge is the screen's, and there is nothing behind it * there to refract. */ detached?: boolean; } /** * The sheet the composer's controls open, and the screens it goes on to. * * A row that leads somewhere pushes a screen onto this sheet rather than * opening a second one over it. The sheet stays where it is, the header's * close button becomes a back button, and the body slides — which is what * makes going one level deeper feel like the same surface rather than another * one landing on top of it. * * ```tsx * * * * * * * * … * * ``` */ declare function AIInputSheet({ children, className, open: openProp, onOpenChange, defaultOpen, initialScreen, onScreenChange, blur, size, detached, }: AIInputSheetProps): import("react").JSX.Element | null; declare namespace AIInputSheet { var displayName: string; } export interface AIInputSheetGroupProps extends ViewProps { className?: string; /** A line under the group, for what the rows in it mean. */ footnote?: ReactNode; } /** A card of related rows, with a hairline between each pair. */ declare function AIInputSheetGroup({ className, footnote, children, ...props }: AIInputSheetGroupProps): import("react").JSX.Element; declare namespace AIInputSheetGroup { var displayName: string; } export interface AIInputSheetRowProps extends Omit { className?: string; label: ReactNode; /** A quieter line under the label. */ description?: ReactNode; /** A glyph at the leading end. */ icon?: ReactNode; /** The current setting, shown at the trailing end. */ value?: ReactNode; /** * Push the screen with this id when the row is pressed. A row with one shows * a chevron, because a row that leads somewhere should say so before it is * pressed. */ to?: string; onPress?: () => void; disabled?: boolean; } declare function AIInputSheetRow({ className, label, description, icon, value, to, onPress, disabled, ...props }: AIInputSheetRowProps): import("react").JSX.Element; declare namespace AIInputSheetRow { var displayName: string; } export interface AIInputSheetToggleProps extends Omit { className?: string; label: ReactNode; description?: ReactNode; icon?: ReactNode; value: boolean; onValueChange?: (value: boolean) => void; disabled?: boolean; } declare function AIInputSheetToggle({ className, label, description, icon, value, onValueChange, disabled, ...props }: AIInputSheetToggleProps): import("react").JSX.Element; declare namespace AIInputSheetToggle { var displayName: string; } export interface AIInputSheetChoiceProps extends Omit { className?: string; label: ReactNode; description?: ReactNode; /** A pill beside the label — what the choice costs, or what it needs. */ badge?: ReactNode; selected?: boolean; onPress?: () => void; disabled?: boolean; } /** One of a set, with a tick on the one in force. */ declare function AIInputSheetChoice({ className, label, description, badge, selected, onPress, disabled, ...props }: AIInputSheetChoiceProps): import("react").JSX.Element; declare namespace AIInputSheetChoice { var displayName: string; } export interface AIInputVoiceModeProps extends Omit { className?: string; /** The controls in the bottom row, before the close button. */ children?: ReactNode; /** What the app is doing, which is what the wave behind everything shows. */ state?: 'idle' | 'listening' | 'thinking' | 'speaking'; /** Input level, 0–1. A shared value keeps metering off the JS thread. */ level?: number | SharedValue; /** A line above the microphone — a greeting, or what it is waiting for. */ title?: ReactNode; onMicPress?: () => void; micLabel?: string; onClose?: () => void; closeLabel?: string; /** Type scale for the controls in the bottom row. */ size?: AIInputSize; /** Draw the bottom row's controls as the platform's own, in its material. */ native?: boolean; } /** * The screen a voice conversation happens on: no field, no keyboard, one * microphone and a way out. * * It is a surface rather than an overlay — render it as its own route or * inside a modal of your own. It provides the composer's context, so * `AIInput.Action` and `AIInput.Pill` work in the bottom row without an * `AIInput` around them. */ declare function AIInputVoiceMode({ className, children, state, level, title, onMicPress, micLabel, onClose, closeLabel, size, native, ...props }: AIInputVoiceModeProps): import("react").JSX.Element; declare namespace AIInputVoiceMode { var displayName: string; } export declare const AIInput: typeof AIInputRoot & { Field: typeof AIInputField; Row: typeof AIInputRow; Toolbar: typeof AIInputToolbar; Spacer: typeof AIInputSpacer; Action: typeof AIInputAction; Pill: typeof AIInputPill; Submit: typeof AIInputSubmit; Recording: typeof AIInputRecording; Sheet: typeof AIInputSheet & { Screen: typeof AIInputSheetScreen; Group: typeof AIInputSheetGroup; Row: typeof AIInputSheetRow; Toggle: typeof AIInputSheetToggle; Choice: typeof AIInputSheetChoice; }; VoiceMode: typeof AIInputVoiceMode; }; export {}; //# sourceMappingURL=index.d.ts.map