ChatTool

CollapsibleGitLab
Exiba o status de invocação de uma ferramenta de IA de forma recolhível.

Uso

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.

Texto

Use a prop text para definir o texto de status da ferramenta.

<template>
  <NChatTool text="Searched components" />
</template>

Sufixo

Use a prop suffix para exibir um texto secundário após o rótulo principal.

<template>
  <NChatTool text="Reading component" suffix="Button" />
</template>

Streaming

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>
Use o utilitário 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.

Shimmer

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>

Ícone

Use a prop icon para exibir um componente Icon ao lado do gatilho.

<template>
  <NChatTool icon="i-lucide-search" text="Searched components" />
</template>

Carregando

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>

Ícone de carregamento

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>
Você pode personalizar esse ícone globalmente no seu app.config.ts na chave ui.icons.loading.
Você pode personalizar esse ícone globalmente no seu vite.config.ts na chave ui.icons.loading.

Chevron

Use a prop chevron para alterar a posição do ícone de chevron.

Quando 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>

Ícone de chevron

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>
Você pode personalizar esse ícone globalmente no seu app.config.ts na chave ui.icons.chevronDown.
Você pode personalizar esse ícone globalmente no seu vite.config.ts na chave ui.icons.chevronDown.

Variante

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>

Ações Soon

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.

$ pnpm run lint
<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>

Exemplos

Confira a página de visão geral do Chat para instruções de instalação, configuração do servidor e exemplos de uso.

Com fluxo de aprovação Soon

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
Use o utilitário 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>

API

Props

Prop Default Type
text string

The text content to display.

suffix string

The suffix text displayed after the main text.

iconany

The icon displayed next to the trigger.

loadingfalseboolean

Whether the tool is in a loading state.

loadingIconappConfig.ui.icons.loadingany

The icon displayed when loading.

streamingfalseboolean

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.

chevronIconappConfig.ui.icons.chevronDownany

The icon displayed as the chevron.

shimmer Partial<Omit<ChatShimmerProps, "text">>

Customize the ChatShimmer component when streaming.

actions ButtonProps[]

Display a list of actions below the trigger, useful for tool approval flows. { size: 'xs' }

disabledboolean

When true, prevents the user from interacting with the collapsible.

openundefinedboolean

The controlled open state of the collapsible. Can be binded with v-model.

defaultOpenboolean

The open state of the collapsible when it is initially rendered.
Use when you do not need to control its open state.

unmountOnHidefalseboolean

When true, the element will be unmounted on closed state.

ui { root?: SlotClass; trigger?: SlotClass; leading?: SlotClass; leadingIcon?: SlotClass; chevronIcon?: SlotClass; label?: SlotClass; suffix?: SlotClass; trailingIcon?: SlotClass; content?: SlotClass; body?: SlotClass; actions?: SlotClass; }

Slots

Slot Type
default{ open: boolean; }
actions{}

Emits

Event Type
update:open[value: boolean]

Tema

app.config.ts
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'
            ]
          }
        }
      }
    }
  }
})
vite.config.ts
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'
                ]
              }
            }
          }
        }
      }
    })
  ]
})

Changelog

No recent changes