/**
* 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