チャット推論

折りたたみ式のAI推論や思考プロセスを表示します。

使用法

ChatReasoningコンポーネントは、AIの推論や思考コンテンツを表示する折りたたみ可能なブロックをレンダリングします。ストリーミング中は自動開き、ストリーミング後は自動閉じます。

<script setup lang="ts">
const streaming = ref(false)
const text = ref('')

async function simulateStreaming() {
  streaming.value = true
  text.value = ''

  const content =
    'The user is asking about Vue components. I should explain the Composition API pattern and how it relates to their question about reactive state management. Let me think about the best way to structure this response.\n\nFirst, I need to consider the key differences between the Options API and Composition API. The Composition API was introduced in Vue 3 to address limitations of the Options API when building large-scale applications.\n\nFor reactive state management specifically, the Composition API offers ref() for primitive values and reactive() for objects.'

  for (const char of content) {
    text.value += char
    await new Promise((resolve) => setTimeout(resolve, 10))
  }

  streaming.value = false
}

onMounted(simulateStreaming)
</script>

<template>
  <UChatReasoning :text="text" :streaming="streaming" class="w-60" />
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'

const streaming = ref(false)
const text = ref('')

async function simulateStreaming() {
  streaming.value = true
  text.value = ''

  const content =
    'The user is asking about Vue components. I should explain the Composition API pattern and how it relates to their question about reactive state management. Let me think about the best way to structure this response.\n\nFirst, I need to consider the key differences between the Options API and Composition API. The Composition API was introduced in Vue 3 to address limitations of the Options API when building large-scale applications.\n\nFor reactive state management specifically, the Composition API offers ref() for primitive values and reactive() for objects.'

  for (const char of content) {
    text.value += char
    await new Promise((resolve) => setTimeout(resolve, 10))
  }

  streaming.value = false
}

onMounted(simulateStreaming)
</script>

<template>
  <UChatReasoning :text="text" :streaming="streaming" class="w-60" />
</template>
ボディコンテンツはuseScrollShadowコンポーザーを使用してオーバーフロー時にフェードシャドウを適用します。

Text

textプロパティを使用して推論内容を設定します。テキストは折りたたみ可能なボディ内に表示されます。

<template>
  <UChatReasoning text="The user is asking about Vue components..." />
</template>

ストリーミング

streamingプロパティを使用してアクティブな推論を示します。コンポーネントはストリーミング開始時に自動的に開き、終了時に自動的に閉じます。

The user is asking about Vue components...
<template>
  <UChatReasoning streaming text="The user is asking about Vue components..." />
</template>
@nuxt/ui/utils/aiのisPartStreamingユーティリティを使用して、部品が現在ストリーミング中かどうかを判断します。

シマー

ストリーミング時、トリガーラベルはChatShimmerコンポーネントを使用します。shimmerプロパティを使用してdurationとspreadをカスタマイズします。

The user is asking about Vue components...
<template>
  <UChatReasoning
    streaming
    text="The user is asking about Vue components..."
    :shimmer="{
      duration: 2,
      spread: 2
    }"
  />
</template>

Icon

iconプロパティを使用して、Iconコンポーネントをトリガーの横に表示します。

<template>
  <UChatReasoning icon="i-lucide-brain" text="The user is asking about Vue components..." />
</template>

Chevron

chevronアイコンの位置を変更するには、chevronプロパティを使用します。

chevronがiconでleadingに設定されている場合、アイコンはホバーと開いたときにシェブロンと切り替わります。
<template>
  <UChatReasoning
    chevron="leading"
    icon="i-lucide-brain"
    text="The user is asking about Vue components..."
  />
</template>

Chevronアイコン

chevron-iconプロパティを使用して、シェブロンIconをカスタマイズします。デフォルトはi-lucide-chevron-downです。

<template>
  <UChatReasoning
    chevron-icon="i-lucide-arrow-down"
    text="The user is asking about Vue components..."
  />
</template>
このアイコンはapp.config.tsのui.icons.chevronDownキーでグローバルにカスタマイズできます。
このアイコンはvite.config.tsのui.icons.chevronDownキーでグローバルにカスタマイズできます。

例

インストール手順、サーバーのセットアップ、使用例については、Chatの概要ページをご覧ください。

API

Props

Prop Default Type
text
string

The reasoning text content to display.

streaming
false
boolean

Whether the reasoning content is currently streaming.

duration
number

The duration in seconds that the AI spent reasoning. If not provided, it will be calculated automatically based on streaming time.

icon
any

The icon displayed next to the trigger.

chevron
'trailing'
"leading" | "trailing"

The position of the chevron icon.

chevronIcon
appConfig.ui.icons.chevronDown
any

The icon displayed as the chevron.

autoCloseDelay
500
number

The delay in milliseconds before auto-closing when streaming ends. Set to 0 to disable auto-close.

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

Customize the ChatShimmer component when streaming.

disabled
boolean

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

open
undefined
boolean

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

defaultOpen
boolean

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

unmountOnHide
false
boolean

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

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

スロット

Slot Type
default
{ open: boolean; }

Emits

Event Type
update:open
[value: boolean]

Theme

app.config.ts
export default defineAppConfig({
  ui: {
    chatReasoning: {
      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 rounded-sm outline-primary/25 focus-visible:outline-3 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 ease-out motion-reduce:transition-none',
        label: 'truncate',
        trailingIcon: 'size-4 shrink-0 group-data-[state=open]:rotate-180 transition-transform duration-200 ease-out motion-reduce:transition-none',
        content: 'data-[state=open]:animate-[collapsible-down_200ms_var(--ease-out)] data-[state=closed]:animate-[collapsible-up_200ms_var(--ease-out)] data-[state=closed]:overflow-hidden rounded-sm outline-primary/25 has-focus-visible:outline-3',
        body: 'max-h-[200px] pt-2 overflow-y-auto text-sm text-dimmed whitespace-pre-wrap focus:outline-none'
      },
      variants: {
        chevron: {
          leading: {
            leadingIcon: 'group-hover:opacity-0'
          },
          trailing: ''
        },
        alone: {
          false: {
            leadingIcon: [
              'absolute inset-0 group-data-[state=open]:opacity-0',
              'transition-opacity duration-200 ease-out'
            ],
            chevronIcon: [
              'absolute inset-0 opacity-0 group-hover:opacity-100 group-data-[state=open]:opacity-100',
              'transition-[rotate,opacity] duration-200 ease-out motion-reduce:transition-none'
            ]
          }
        }
      }
    }
  }
})
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'

export default defineConfig({
  plugins: [
    vue(),
    ui({
      ui: {
        chatReasoning: {
          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 rounded-sm outline-primary/25 focus-visible:outline-3 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 ease-out motion-reduce:transition-none',
            label: 'truncate',
            trailingIcon: 'size-4 shrink-0 group-data-[state=open]:rotate-180 transition-transform duration-200 ease-out motion-reduce:transition-none',
            content: 'data-[state=open]:animate-[collapsible-down_200ms_var(--ease-out)] data-[state=closed]:animate-[collapsible-up_200ms_var(--ease-out)] data-[state=closed]:overflow-hidden rounded-sm outline-primary/25 has-focus-visible:outline-3',
            body: 'max-h-[200px] pt-2 overflow-y-auto text-sm text-dimmed whitespace-pre-wrap focus:outline-none'
          },
          variants: {
            chevron: {
              leading: {
                leadingIcon: 'group-hover:opacity-0'
              },
              trailing: ''
            },
            alone: {
              false: {
                leadingIcon: [
                  'absolute inset-0 group-data-[state=open]:opacity-0',
                  'transition-opacity duration-200 ease-out'
                ],
                chevronIcon: [
                  'absolute inset-0 opacity-0 group-hover:opacity-100 group-data-[state=open]:opacity-100',
                  'transition-[rotate,opacity] duration-200 ease-out motion-reduce:transition-none'
                ]
              }
            }
          }
        }
      }
    })
  ]
})

Changelog

No recent changes