O componente ChatTool renderiza um bloco recolhível que exibe o status de invocação de ferramentas de IA, como "Buscando componentes" ou "Lendo documentação". Quando um slot padrão é fornecido, ele se torna recolhível para revelar a saída da ferramenta.
$ pnpm run lint > eslint . ✔ No lint errors found.
Use a prop text para definir o texto de status da ferramenta.
<template>
<NChatTool text="Searched components" />
</template>
Use a prop suffix para exibir um texto secundário após o rótulo principal.
<template>
<NChatTool text="Reading component" suffix="Button" />
</template>
Use a prop streaming para indicar que a ferramenta está em execução. O texto exibe uma animação shimmer.
<template>
<NChatTool streaming text="Searching components..." />
</template>
isToolStreaming de @nitro/ui/utils/ai para determinar se um tool part ainda está em execução. Ele retorna false quando a ferramenta está aguardando uma aprovação do usuário.Durante o streaming, o rótulo do gatilho usa o componente ChatShimmer. Use a prop shimmer para personalizar sua duration e seu spread.
<template>
<NChatTool
streaming
text="Searching components..."
:shimmer="{
duration: 2,
spread: 2
}"
/>
</template>
Use a prop icon para exibir um componente Icon ao lado do gatilho.
<template>
<NChatTool icon="i-lucide-search" text="Searched components" />
</template>
Use a prop loading para exibir um indicador de carregamento. Use a prop loading-icon para personalizar o ícone de carregamento.
<template>
<NChatTool loading text="Searching components..." />
</template>
Use a prop loading-icon para personalizar o ícone de carregamento. O padrão é i-lucide-loader-circle.
<template>
<NChatTool loading loading-icon="i-lucide-loader" text="Searching components..." />
</template>
Use a prop chevron para alterar a posição do ícone de chevron.
chevron é definido como leading com um icon, o ícone troca de lugar com o chevron no hover e quando aberto.<template>
<NChatTool chevron="leading" icon="i-lucide-search" text="Searched components">
Tool output content
</NChatTool>
</template>
Use a prop chevron-icon para personalizar o Icon de chevron. O padrão é i-lucide-chevron-down.
<template>
<NChatTool chevron-icon="i-lucide-arrow-down" text="Searched components">
Tool output content
</NChatTool>
</template>
Use a prop variant para alterar o estilo visual. O padrão é inline.
<template>
<NChatTool variant="card" text="Searched components" icon="i-lucide-search" chevron="trailing">
Tool output content
</NChatTool>
</template>
Use a prop actions para exibir uma lista de Button abaixo do gatilho, útil para ferramentas que exigem uma confirmação do usuário antes de executar.
<template>
<NChatTool
:actions="[
{
label: 'Approve'
},
{
label: 'Deny',
color: 'neutral',
variant: 'soft'
}
]"
text="Run terminal command"
variant="card"
icon="i-lucide-terminal"
>
$ pnpm run lint
</NChatTool>
</template>
Use a prop actions para construir um fluxo de aprovação de ferramenta com o AI SDK. Quando um tool part está no estado approval-requested, exiba as ações de aprovar e negar e responda com addToolApprovalResponse.
$ pnpm run lint
isToolApprovalPending de @nitro/ui/utils/ai para detectar uma aprovação pendente; isToolStreaming retorna false nesse estado.<script setup lang="ts">
import { useChat } from '@ai-sdk/vue'
import { lastAssistantMessageIsCompleteWithApprovalResponses } from 'ai'
const { messages, addToolApprovalResponse } = useChat({
sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithApprovalResponses
})
</script>
<template>
<NChatTool
v-if="isToolUIPart(part)"
:text="getToolName(part)"
:streaming="isToolStreaming(part)"
:actions="part.state === 'approval-requested' ? [
{ label: 'Approve', onClick: () => addToolApprovalResponse({ id: part.approval.id, approved: true }) },
{ label: 'Deny', color: 'neutral', variant: 'ghost', onClick: () => addToolApprovalResponse({ id: part.approval.id, approved: false }) }
] : undefined"
/>
</template>
| Prop | Default | Type |
|---|---|---|
text | stringThe text content to display. | |
suffix | stringThe suffix text displayed after the main text. | |
icon | anyThe icon displayed next to the trigger. | |
loading | false | boolean Whether the tool is in a loading state. |
loadingIcon | appConfig.ui.icons.loading | anyThe icon displayed when loading. |
streaming | false | boolean Whether the tool content is currently streaming. |
variant | 'inline' | "inline" | "card"The visual variant of the tool display. |
chevron | 'trailing' | "leading" | "trailing"The position of the chevron icon. |
chevronIcon | appConfig.ui.icons.chevronDown | anyThe icon displayed as the chevron. |
shimmer | Partial<Omit<ChatShimmerProps, "text">>Customize the | |
actions | ButtonProps[]Display a list of actions below the trigger, useful for tool approval flows.
| |
disabled | boolean When | |
open | undefined | boolean The controlled open state of the collapsible. Can be binded with |
defaultOpen | boolean The open state of the collapsible when it is initially rendered. | |
unmountOnHide | false | boolean When |
ui | { root?: SlotClass; trigger?: SlotClass; leading?: SlotClass; leadingIcon?: SlotClass; chevronIcon?: SlotClass; label?: SlotClass; suffix?: SlotClass; trailingIcon?: SlotClass; content?: SlotClass; body?: SlotClass; actions?: SlotClass; } |
| Slot | Type |
|---|---|
default | { open: boolean; } |
actions | {} |
| Event | Type |
|---|---|
update:open | [value: boolean] |
export default defineAppConfig({
ui: {
chatTool: {
slots: {
root: '',
trigger: [
'group flex w-full items-center gap-1.5 text-muted text-sm disabled:cursor-default disabled:hover:text-muted hover:text-default min-w-0',
'transition-colors'
],
leading: 'relative size-4 shrink-0',
leadingIcon: 'size-4 shrink-0',
chevronIcon: 'size-4 shrink-0 group-data-[state=open]:rotate-180 transition-transform duration-200',
label: 'truncate',
suffix: 'text-dimmed ms-1',
trailingIcon: 'size-4 shrink-0 group-data-[state=open]:rotate-180 transition-transform duration-200',
content: 'data-[state=open]:animate-[collapsible-down_200ms_ease-out] data-[state=closed]:animate-[collapsible-up_200ms_ease-out] data-[state=closed]:overflow-hidden',
body: 'text-sm text-dimmed whitespace-pre-wrap',
actions: 'flex items-center justify-end gap-1.5'
},
variants: {
variant: {
inline: {
trigger: 'rounded-sm outline-primary/25 focus-visible:outline-3',
body: 'pt-2',
actions: 'pt-2'
},
card: {
root: 'rounded-md ring ring-default overflow-hidden outline-primary/25 has-focus-visible:outline-3 has-focus-visible:ring-primary',
trigger: 'px-2 py-1 focus:outline-none',
trailingIcon: 'ms-auto',
body: 'border-t border-default p-2 max-h-[200px] overflow-y-auto focus:outline-none',
actions: 'border-t border-default p-2'
}
},
chevron: {
leading: '',
trailing: ''
},
loading: {
true: {
leadingIcon: 'animate-spin'
}
},
alone: {
false: {
leadingIcon: [
'absolute inset-0 group-hover:opacity-0 group-data-[state=open]:opacity-0',
'transition-opacity duration-200'
],
chevronIcon: [
'absolute inset-0 opacity-0 group-hover:opacity-100 group-data-[state=open]:opacity-100',
'transition-[rotate,opacity] duration-200'
]
}
}
}
}
}
})
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nitro/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
ui: {
chatTool: {
slots: {
root: '',
trigger: [
'group flex w-full items-center gap-1.5 text-muted text-sm disabled:cursor-default disabled:hover:text-muted hover:text-default min-w-0',
'transition-colors'
],
leading: 'relative size-4 shrink-0',
leadingIcon: 'size-4 shrink-0',
chevronIcon: 'size-4 shrink-0 group-data-[state=open]:rotate-180 transition-transform duration-200',
label: 'truncate',
suffix: 'text-dimmed ms-1',
trailingIcon: 'size-4 shrink-0 group-data-[state=open]:rotate-180 transition-transform duration-200',
content: 'data-[state=open]:animate-[collapsible-down_200ms_ease-out] data-[state=closed]:animate-[collapsible-up_200ms_ease-out] data-[state=closed]:overflow-hidden',
body: 'text-sm text-dimmed whitespace-pre-wrap',
actions: 'flex items-center justify-end gap-1.5'
},
variants: {
variant: {
inline: {
trigger: 'rounded-sm outline-primary/25 focus-visible:outline-3',
body: 'pt-2',
actions: 'pt-2'
},
card: {
root: 'rounded-md ring ring-default overflow-hidden outline-primary/25 has-focus-visible:outline-3 has-focus-visible:ring-primary',
trigger: 'px-2 py-1 focus:outline-none',
trailingIcon: 'ms-auto',
body: 'border-t border-default p-2 max-h-[200px] overflow-y-auto focus:outline-none',
actions: 'border-t border-default p-2'
}
},
chevron: {
leading: '',
trailing: ''
},
loading: {
true: {
leadingIcon: 'animate-spin'
}
},
alone: {
false: {
leadingIcon: [
'absolute inset-0 group-hover:opacity-0 group-data-[state=open]:opacity-0',
'transition-opacity duration-200'
],
chevronIcon: [
'absolute inset-0 opacity-0 group-hover:opacity-100 group-data-[state=open]:opacity-100',
'transition-[rotate,opacity] duration-200'
]
}
}
}
}
}
})
]
})