Experimental: The AI extension and its component APIs may change in the future. Please give us feedback on your experience with them.
Demos#
Prompt input#
On desktop, Enter sends the message and Shift+Enter adds a new line. On touch-primary devices, Enter adds a new line and the send button submits.
<PromptInput characterCounter={111} />
Compact prompt input#
<PromptInput variant="compact" placeholder="Ask Aino" />
Inline compact prompt input#
Use dropShadow={false} for the flat inline surface.
<PromptInput variant="compact" dropShadow={false} placeholder="Ask Aino" />
Custom actions#
Compose attachment and microphone buttons with startActions and endActions. Here, attachment adds an example document and microphone inserts example dictated text. Connect these controls to your application's file picker and speech recognition.
const CustomActions = () => { const [value, setValue] = useState('') const [hasAttachment, setHasAttachment] = useState(false) return ( <PromptInput variant={variant} icon={null} value={value} onChange={({ value }) => setValue(value)} onSubmit={() => setValue('')} startActions={ <Button type="button" variant="tertiary" bounding icon={add} iconSize="default" aria-label="Add attachment" tooltip="Add attachment" disabled={hasAttachment} onClick={() => setHasAttachment(true)} /> } endActions={ <Button type="button" variant="tertiary" bounding icon={microphone} iconSize="default" aria-label="Use microphone" tooltip="Use microphone" onClick={() => setValue('Show my latest transactions')} /> } > {hasAttachment && ( <Button type="button" variant="tertiary" bounding icon={close} iconSize="default" text="Budget.pdf" aria-label="Remove Budget.pdf" onClick={() => setHasAttachment(false)} /> )} </PromptInput> ) } render(<CustomActions />)
const CustomActions = () => { const [value, setValue] = useState('') const [hasAttachment, setHasAttachment] = useState(false) return ( <PromptInput variant={variant} icon={null} value={value} onChange={({ value }) => setValue(value)} onSubmit={() => setValue('')} startActions={ <Button type="button" variant="tertiary" bounding icon={add} iconSize="default" aria-label="Add attachment" tooltip="Add attachment" disabled={hasAttachment} onClick={() => setHasAttachment(true)} /> } endActions={ <Button type="button" variant="tertiary" bounding icon={microphone} iconSize="default" aria-label="Use microphone" tooltip="Use microphone" onClick={() => setValue('Show my latest transactions')} /> } > {hasAttachment && ( <Button type="button" variant="tertiary" bounding icon={close} iconSize="default" text="Budget.pdf" aria-label="Remove Budget.pdf" onClick={() => setHasAttachment(false)} /> )} </PromptInput> ) } render(<CustomActions />)
Prompt input with context#
Pass your context as children. The application owns the reference and its removal. The custom action adds it again after removal.
const ContextChip = styled.div` display: flex; align-items: center; gap: 0.5rem; padding: 0.25rem 0.5rem; background: var(--token-color-background-neutral-subtle); border: 0.0625rem solid var(--token-color-stroke-neutral-subtle); border-radius: 0.5rem; ` const InputWithContext = () => { const [hasReference, setHasReference] = useState(true) const [value, setValue] = useState( 'Add the companies from the attached document to my watchlist.' ) return ( <PromptInput dropShadow value={value} onChange={({ value }) => setValue(value)} onSubmit={() => setValue('')} startActions={ <Button type="button" variant="tertiary" size="small" icon={add} iconSize="default" aria-label="Add reference" tooltip="Add reference" disabled={hasReference} onClick={() => setHasReference(true)} /> } > {hasReference && ( <ContextChip> <Avatar size="small" src={referenceAvatar} aria-hidden /> <span>dnb.no/...Insurance</span> <Button type="button" variant="tertiary" size="small" icon={close} iconSize="default" aria-label="Remove reference" onClick={() => setHasReference(false)} /> </ContextChip> )} </PromptInput> ) } render(<InputWithContext />)
Stop while streaming#
Set isBusy and pass onStop to show a stop button while the reply streams in.
const Chat = () => { const [isBusy, setIsBusy] = useState(false) useEffect(() => { if (!isBusy) { return // stop here } const timeout = setTimeout(() => setIsBusy(false), 4000) return () => clearTimeout(timeout) }, [isBusy]) return ( <PromptInput variant={variant} isBusy={isBusy} onSubmit={() => setIsBusy(true)} onStop={() => setIsBusy(false)} /> ) } render(<Chat />)
const Chat = () => { const [isBusy, setIsBusy] = useState(false) useEffect(() => { if (!isBusy) { return // stop here } const timeout = setTimeout(() => setIsBusy(false), 4000) return () => clearTimeout(timeout) }, [isBusy]) return ( <PromptInput variant={variant} isBusy={isBusy} onSubmit={() => setIsBusy(true)} onStop={() => setIsBusy(false)} /> ) } render(<Chat />)
Prompt input with an error#
<PromptInput textareaProps={{ status: 'The message could not be sent. Try again.', }} />
Disabled prompt input#
<PromptInput disabled value="Message unavailable" startActions={ <Button type="button" variant="tertiary" size="small" text="Add reference" disabled /> } />