ChatReasoning
Usage
The ChatReasoning component renders a collapsible block that displays AI reasoning or thinking content. It auto-opens during streaming and auto-closes after.
<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>
<PChatReasoning :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>
<PChatReasoning :text="text" :streaming="streaming" class="w-60" />
</template>Text
Use the text prop to set the reasoning content. The text is displayed inside the collapsible body.
<template>
<PChatReasoning text="The user is asking about Vue components..." />
</template>Streaming
Use the streaming prop to indicate active reasoning. The component auto-opens when streaming starts and auto-closes when it ends.
<template>
<PChatReasoning streaming text="The user is asking about Vue components..." />
</template>auto-open prop to false to keep the component closed when streaming starts, and auto-close-delay to 0 to keep it open when streaming ends. If the user toggles it while streaming, it stays as they left it.isPartStreaming utility from pohon-ui/utils/ai to determine if a part is currently being streamed.Shimmer
When streaming, the trigger label uses the ChatShimmer component. Use the shimmer prop to customize its duration and spread.
<template>
<PChatReasoning
streaming
text="The user is asking about Vue components..."
:shimmer="{
duration: 2,
spread: 2
}"
/>
</template>Icon
Use the icon prop to display an Icon component next to the trigger.
<template>
<PChatReasoning icon="i-lucide-brain" text="The user is asking about Vue components..." />
</template>Chevron
Use the chevron prop to change the position of the chevron icon.
chevron is set to leading with an icon, the icon swaps with the chevron on hover and when open.<template>
<PChatReasoning
chevron="leading"
icon="i-lucide-brain"
text="The user is asking about Vue components..."
/>
</template>Chevron Icon
Use the chevron-icon prop to customize the chevron Icon. Defaults to i-lucide-chevron-down.
<template>
<PChatReasoning
chevron-icon="i-lucide-arrow-down"
text="The user is asking about Vue components..."
/>
</template>Examples
API
Props
| Prop | Default | Type |
|---|---|---|
text | stringThe reasoning text content to display. | |
streaming | false | boolean Whether the reasoning content is currently streaming. |
duration | numberThe duration in seconds that the AI spent reasoning. If not provided, it will be calculated automatically based on streaming time. | |
icon | anyThe icon displayed next to the trigger. | |
chevron | 'trailing' | "leading" | "trailing"The position of the chevron icon. |
chevronIcon | appConfig.ui.icons.chevronDown | anyThe icon displayed as the chevron. |
autoOpen | true | boolean Whether to automatically open when streaming starts. |
autoCloseDelay | 500 | numberThe delay in milliseconds before auto-closing when streaming ends.
Set to |
shimmer | Partial<Omit<ChatShimmerProps, "text">>Customize the | |
defaultOpen | boolean The open state of the collapsible when it is initially rendered. | |
open | boolean The controlled open state of the collapsible. Can be binded with | |
disabled | boolean When | |
unmountOnHide | boolean When | |
ui | { root?: SlotClass; trigger?: SlotClass; leading?: SlotClass; leadingIcon?: SlotClass; chevronIcon?: SlotClass; label?: SlotClass; trailingIcon?: SlotClass; content?: SlotClass; body?: SlotClass; } |
Slots
| Slot | Type |
|---|---|
default | { open: boolean; } |
Emits
| Event | Type |
|---|---|
update:open | [value: boolean] |
Theme
export default defineAppConfig({
ui: {
chatReasoning: {
slots: {
root: '',
trigger: [
'group flex w-full items-center gap-1.5 color-text-muted text-sm disabled:cursor-default disabled:hover:color-text-muted hover:color-text 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: 'shrink-0 size-4 transition-transform-280 ease-out group-data-[state=open]:rotate-180 motion-reduce:transition-none',
label: 'truncate',
trailingIcon: 'shrink-0 size-4 transition-transform-280 ease-out group-data-[state=open]:rotate-180 motion-reduce:transition-none',
content: 'outline-primary/25 data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down rounded-sm has-focus-visible:outline-3 data-[state=closed]:overflow-hidden',
body: 'max-h-[200px] pt-2 overflow-y-auto text-sm color-text-dimmed whitespace-pre-wrap focus:outline-none'
},
variants: {
chevron: {
leading: {
leadingIcon: 'group-hover:opacity-0'
},
trailing: ''
},
alone: {
false: {
leadingIcon: [
'inset-0 absolute group-data-[state=open]:opacity-0',
'transition-opacity-280 ease-out'
],
chevronIcon: [
'opacity-0 inset-0 absolute group-data-[state=open]:opacity-100 group-hover:opacity-100',
'transition-[transform,opacity]-280 ease-out motion-reduce:transition-none'
]
}
}
}
}
}
})import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from 'pohon-ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
ui: {
chatReasoning: {
slots: {
root: '',
trigger: [
'group flex w-full items-center gap-1.5 color-text-muted text-sm disabled:cursor-default disabled:hover:color-text-muted hover:color-text 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: 'shrink-0 size-4 transition-transform-280 ease-out group-data-[state=open]:rotate-180 motion-reduce:transition-none',
label: 'truncate',
trailingIcon: 'shrink-0 size-4 transition-transform-280 ease-out group-data-[state=open]:rotate-180 motion-reduce:transition-none',
content: 'outline-primary/25 data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down rounded-sm has-focus-visible:outline-3 data-[state=closed]:overflow-hidden',
body: 'max-h-[200px] pt-2 overflow-y-auto text-sm color-text-dimmed whitespace-pre-wrap focus:outline-none'
},
variants: {
chevron: {
leading: {
leadingIcon: 'group-hover:opacity-0'
},
trailing: ''
},
alone: {
false: {
leadingIcon: [
'inset-0 absolute group-data-[state=open]:opacity-0',
'transition-opacity-280 ease-out'
],
chevronIcon: [
'opacity-0 inset-0 absolute group-data-[state=open]:opacity-100 group-hover:opacity-100',
'transition-[transform,opacity]-280 ease-out motion-reduce:transition-none'
]
}
}
}
}
}
})
]
})