AI Chat Input
Compositeur d'invite pour interfaces conversationnelles avec texte d'aide, actions de barre d'outils et etats d'envoi. Installez AI Chat Input depuis le registre VLLNT UI avec la CLI shadcn.
Quand utiliser ceci dans une application IA
AI Chat Input is the message composer at the bottom of any chat or agent surface. It handles multi-line growth, attachments, and submit/streaming states, so you can wire it straight to your model or agent runtime.
Parcourir tous les composants pour agents IACompositeur d'invite pour interfaces conversationnelles avec texte d'aide, actions de barre d'outils et etats d'envoi. Fait partie de la famille ai dans VLLNT UI, il est publie comme entree de registre lisible par machine — copiez la source directement dans votre application avec la CLI shadcn et possedez-la, sans dependance d'execution a une bibliotheque de composants.
Apercu
Basculez entre clair et sombre pour inspecter l'apercu Storybook integre.
Installation
Ajoutez AI Chat Input a votre projet avec la CLI shadcn. La source arrive dans votre code, prete a etre adaptee :
pnpm dlx shadcn@latest add https://ui.vllnt.ai/r/ai-chat-input.jsonSource
import { cva } from "class-variance-authority";
import { LoaderCircle, SendHorizontal } from "lucide-react";
import { cn } from "../../lib/utils";
import { Button } from "../button/button";
import { Textarea } from "../textarea/textarea";
const formShellVariants = cva(
"rounded-2xl border border-border/70 bg-background shadow-sm",
);
function AIChatInputFooter({
currentLength,
helperText,
isSubmitDisabled,
isSubmitting,
maxLength,
status,
submitLabel,
}: {
currentLength: number;
helperText?: string;
isSubmitDisabled: boolean;
isSubmitting: boolean;
maxLength?: number;
status?: string;
submitLabel: string;
}) {
return (
<div className="flex flex-col gap-3 border-t border-border/60 pt-3 sm:flex-row sm:items-end sm:justify-between">
<div className="space-y-1 text-xs text-muted-foreground">
{helperText ? <p>{helperText}</p> : null}
<div className="flex flex-wrap items-center gap-2">
{status ? <span>{status}</span> : null}
{typeof maxLength === "number" ? (
<span>
{currentLength}/{maxLength}
</span>
) : null}
</div>
</div>
<Button
className="self-start rounded-full px-4 sm:self-auto"
disabled={isSubmitDisabled}
type="submit"
>
{isSubmitting ? (
<LoaderCircle className="mr-2 size-4 animate-spin" />
) : (
<SendHorizontal className="mr-2 size-4" />
)}
{submitLabel}
</Button>
</div>
);
}
export type AIChatInputProps = React.ComponentPropsWithoutRef<"form"> & {
/** Disables editing and submit actions. */
disabled?: boolean;
/** Optional helper text shown below the prompt field. */
helperText?: string;
/**
* Accessible name for the message textarea. Defaults to `"Chat message"`.
* Override `textareaProps.aria-label` / `aria-labelledby` for finer control.
*/
inputLabel?: string;
/** Whether the submit action is in progress. */
isSubmitting?: boolean;
/** Called whenever the textarea value changes. */
onValueChange?: (value: string) => void;
/** Optional status text shown beside helper copy. */
status?: string;
/** Label for the submit button. */
submitLabel?: string;
/** Props forwarded to the textarea primitive. */
textareaProps?: React.TextareaHTMLAttributes<HTMLTextAreaElement>;
/** Optional controls rendered above the footer row. */
toolbar?: React.ReactNode;
/** Controlled textarea value. */
value?: string;
};
const AIChatInput = ({
className,
disabled = false,
helperText,
inputLabel = "Chat message",
isSubmitting = false,
onSubmit,
onValueChange,
ref,
status,
submitLabel = "Send",
textareaProps,
toolbar,
value,
...props
}: AIChatInputProps & { ref?: React.Ref<HTMLFormElement> }) => {
const currentValue = value ?? "";
const maxLength = textareaProps?.maxLength;
const isSubmitDisabled =
disabled || isSubmitting || currentValue.trim().length === 0;
return (
<form
className={cn(formShellVariants(), "w-full p-3", className)}
onSubmit={onSubmit}
ref={ref}
{...props}
>
<div className="space-y-3">
<Textarea
aria-label={inputLabel}
className="min-h-[120px] resize-none rounded-xl border-0 bg-transparent p-1 shadow-none focus-visible:ring-0 focus-visible:ring-offset-0"
disabled={disabled}
onChange={(event) => {
textareaProps?.onChange?.(event);
onValueChange?.(event.target.value);
}}
placeholder="Ask a follow-up question, paste context, or describe what you need..."
value={value}
{...textareaProps}
/>
{toolbar ? <div className="flex flex-wrap gap-2">{toolbar}</div> : null}
<AIChatInputFooter
currentLength={currentValue.length}
helperText={helperText}
isSubmitDisabled={isSubmitDisabled}
isSubmitting={isSubmitting}
maxLength={maxLength}
status={status}
submitLabel={submitLabel}
/>
</div>
</form>
);
};
AIChatInput.displayName = "AIChatInput";
export { AIChatInput };
Stories
Explorez chaque variante et etat dans le Storybook interactif :
Apercu
Basculez entre clair et sombre pour inspecter l'apercu Storybook integre.