KDS
Chatbot

PromptInput

Input rico de prompt — textarea + toolbar + submit com lifecycle states (ready / streaming / stop).

Overview

PromptInput é o campo de entrada de um chat. Auto-resize na textarea, slot de toolbar pra ações (anexar, microfone, web search, modelo), e um Submit que muda de ícone conforme o estado da geração — ready mostra arrow, submitted mostra spinner, streaming mostra stop.

Construído sobre o InputGroup por baixo. Provider interno gerencia anexos (PromptInputProvider é opcional — útil quando você precisa controle externo dos files).

Preview
Textarea + Submit, sem toolbar.

Anatomy

<PromptInput onSubmit={...}>
  └─ <PromptInputBody>
       ├─ <PromptInputAttachments>
       │    └─ <PromptInputAttachment data={file} />
       ├─ <PromptInputHeader>           ← opcional, model selector
       ├─ <PromptInputTextarea />        ← auto-resize, ⌘+Enter envia
       └─ <PromptInputFooter>
            ├─ <PromptInputTools>        ← left: actions
            │    ├─ <PromptInputButton />
            │    ├─ <PromptInputActionAddAttachments />
            │    └─ <PromptInputActionMenu>
            └─ <PromptInputSubmit />     ← right
</PromptInput>

Usage

"use client";

import { useState } from "react";
import {
  PromptInput,
  PromptInputBody,
  PromptInputFooter,
  PromptInputSubmit,
  PromptInputTextarea
} from "@kalvner/kds/ai/prompt-input";

export function ChatBox() {
  const [status, setStatus] = useState<"ready" | "submitted" | "streaming">("ready");

  return (
    <PromptInput onSubmit={(message) => sendMessage(message.text, message.files)}>
      <PromptInputBody>
        <PromptInputTextarea placeholder="Pergunte qualquer coisa..." />
        <PromptInputFooter>
          <span />
          <PromptInputSubmit status={status} />
        </PromptInputFooter>
      </PromptInputBody>
    </PromptInput>
  );
}

Props

PromptInput (Root)

PropTipoDefaultDescrição
onSubmit(message: PromptInputMessage, e: FormEvent) => voidDisparado em ⌘+Enter ou click no Submit. message: { text, files }.
acceptstringMIME types aceitos no upload (passa pro <input type="file">).
maxFilesnumberLimite de anexos.
maxFileSizenumberBytes max por arquivo.
multiplebooleantruePermite múltiplos files.
controllerPromptInputControllerController externo (ver subcomponents).
globalDropbooleanfalseAceita drop em qualquer parte do componente, não só na zona dedicada.

Aceita HTMLAttributes<HTMLFormElement> (é um <form> por baixo).

PromptInputTextarea

PropTipoDefaultDescrição
placeholderstringSempre forneça — campos vazios sem hint matam UX.
disabledbooleanfalseBloqueia digitação (use durante submitted/streaming).
autoFocusbooleantrueFoco ao montar.

Auto-resize via field-sizing-content. Aceita HTMLAttributes<HTMLTextAreaElement>.

PromptInputSubmit

PropTipoDefaultDescrição
status'ready' | 'submitted' | 'streaming''ready'Controla o ícone exibido.
childrenReactNode(auto por status)Override do ícone.

Herda de Button. streaming desabilita o form-submit padrão e dispara onClick (consumidor interrompe).

PromptInputButton · PromptInputActionMenu

ComponenteFunção
PromptInputButtonBotão na Toolbar — herda de InputGroupButton.
PromptInputActionAddAttachmentsBotão pré-feito que abre file picker.
PromptInputActionMenu + Trigger/Content/ItemDropdownMenu wrapper pra agrupar actions secundárias.

PromptInputProvider · controller

HookRetorno
usePromptInputController()Controller — { attachments, addFile(s), removeFile(id), clear() }.
usePromptInputAttachments()Lista atual de attachments dentro do form.

Use <PromptInputProvider> no parent quando precisar controlar attachments fora do form (ex.: arrastar arquivo no Conversation inteiro pra adicionar ao prompt).

Subcomponents

  • PromptInput<form> raiz. Provider interno gerencia attachments e textarea state.
  • PromptInputBody — wrapper do InputGroup. Contém Attachments, Textarea, Header, Footer.
  • PromptInputHeader — slot superior — model selector, título de contexto, etc.
  • PromptInputTextarea — campo principal. Auto-resize, ⌘+Enter dispara submit.
  • PromptInputFooter — flex horizontal: tools à esquerda, Submit à direita.
  • PromptInputTools — wrapper que agrupa botões na toolbar.
  • PromptInputButton — botão genérico (anexar, web, mic).
  • PromptInputActionAddAttachments — botão pré-feito que abre file picker (acessibilidade + accept correto).
  • PromptInputActionMenu + Trigger/Content/Item — menu dropdown pra actions secundárias.
  • PromptInputAttachments + PromptInputAttachment — lista visível dos anexos (com X pra remover).
  • PromptInputSubmit — botão de envio com lifecycle.

States

StatusÍconeComportamento
readyArrowUpIconSubmit normal. Disabled se textarea vazia.
submittedLoader2 spinningAguardando resposta. Desabilitado.
streamingSquareIcon (stop)Permite cancelar a geração.
errorXIcon(custom — passe children)

Composition

Com tools + model selector

<PromptInput onSubmit={handle}>
  <PromptInputBody>
    <PromptInputHeader>
      <ModelSelector ... />
    </PromptInputHeader>
    <PromptInputTextarea placeholder="..." />
    <PromptInputFooter>
      <PromptInputTools>
        <PromptInputActionAddAttachments />
        <PromptInputButton><Mic /></PromptInputButton>
        <PromptInputButton><Globe />Web</PromptInputButton>
      </PromptInputTools>
      <PromptInputSubmit status={status} />
    </PromptInputFooter>
  </PromptInputBody>
</PromptInput>

Drag-to-drop global

<PromptInput globalDrop accept="image/*" maxFileSize={5_000_000}>
  ...
</PromptInput>

When to use

  • Campo de envio em qualquer chat de IA.
  • Surface de "compose" rica com upload, voice, web search.

When not to use

  • Input simples de texto sem upload — use Input ou Textarea direto.
  • Forms multi-campo (cadastro) — use Form.

Best practices

  • Sempre passe status ao Submit — sem isso, o ícone fica preso em ready durante streaming, confundindo o usuário.
  • Disabled em flight. Desabilite a textarea durante submitted/streaming pra evitar dupla submissão.
  • Placeholder ativo. "Pergunte qualquer coisa..." > "Mensagem".
  • maxFileSize + accept sempre — sem isso, usuário pode travar a UI com upload de 100MB.

Accessibility

ConcernComportamento
FormÉ um <form> real — Enter envia em browsers desktop, ⌘+Enter força submit.
TextareaAuto-focus ao montar (autoFocus); pode desativar pra fluxos sem chat aberto inicialmente.
Submitaria-label baseado no status ("Send message", "Stop generation").
AttachmentsMessageAttachment (no Message) e PromptInputAttachment (aqui) compartilham aria patterns.
Keyboard⌘/Ctrl+Enter envia; Escape limpa.

On this page