ChatMessage 대화
Usage
ChatMessage 구성 요소는 user 또는 assistant 채팅 메시지에 대한 <article> 요소를 렌더링합니다.
Parts 부품
parts prop를 사용하여 AI SDK 형식을 사용하여 메시지 내용을 표시합니다.
<template>
<UChatMessage
:parts="[
{
type: 'text',
id: '1',
text: 'Hello! Tell me more about building AI chatbots with Nuxt UI.'
}
]"
role="user"
id="1"
/>
</template>parts prop은 AI SDK에 권장되는 형식입니다. 각 부분에는 type(예: 'text')와 해당 내용이 있습니다. ChatMessage 구성 요소는 이전 버전과의 호환성을 위해 더 이상 사용되지 않는 content prop도 지원합니다.Side 사이드
side prop을 사용하여 메시지를 왼쪽이나 오른쪽에 표시합니다.
<template>
<UChatMessage
side="right"
:parts="[
{
type: 'text',
id: '1',
text: 'Hello! Tell me more about building AI chatbots with Nuxt UI.'
}
]"
role="user"
id="1"
/>
</template>변형
variant prop을 사용하여 메시지의 스타일을 변경합니다.
<template>
<UChatMessage
variant="soft"
:parts="[
{
type: 'text',
id: '1',
text: 'Hello! Tell me more about building AI chatbots with Nuxt UI.'
}
]"
role="user"
id="1"
/>
</template>Color : badge{label="4.8+" class="align-text-top"}
color prop을 사용하여 메시지 색상을 변경합니다.
<template>
<UChatMessage
variant="soft"
color="primary"
:parts="[
{
type: 'text',
id: '1',
text: 'Hello! Tell me more about building AI chatbots with Nuxt UI.'
}
]"
role="user"
id="1"
/>
</template>Icon
icon prop을 사용하여 메시지 옆에 Icon 구성 요소를 표시합니다.
<template>
<UChatMessage
icon="i-lucide-user"
variant="soft"
side="right"
:parts="[
{
type: 'text',
id: '1',
text: 'Hello! Tell me more about building AI chatbots with Nuxt UI.'
}
]"
role="user"
id="1"
/>
</template>avatar 이미지
avatar prop을 사용하여 메시지 옆에 Avatar 구성 요소를 표시합니다.
<template>
<UChatMessage
:avatar="{
src: 'https://github.com/benjamincanac.png',
loading: 'lazy'
}"
variant="soft"
side="right"
:parts="[
{
type: 'text',
id: '1',
text: 'Hello! Tell me more about building AI chatbots with Nuxt UI.'
}
]"
role="user"
id="1"
/>
</template>avatar.icon prop을 사용하여 아이콘을 아바타로 표시할 수도 있습니다.
<template>
<UChatMessage
:avatar="{
icon: 'i-lucide-bot'
}"
:parts="[
{
type: 'text',
id: '1',
text: 'Nuxt UI offers several features for building AI chatbots including the ChatMessage, ChatMessages, and ChatPrompt components. Best practices include using the Chat class from AI SDK, implementing proper message styling with variants, and utilizing the built-in actions for message interactions. The components are fully customizable with theming support and responsive design.'
}
]"
role="assistant"
id="1"
/>
</template>Actions (### 액션)
actions 소품을 사용하여 메시지 위에 마우스를 놓을 때 표시될 메시지 아래에 액션을 표시합니다.
<script setup lang="ts">
import type { ButtonProps } from '@nuxt/ui'
const actions = ref<ButtonProps[]>([
{
label: 'Copy to clipboard',
icon: 'i-lucide-copy'
}
])
</script>
<template>
<UChatMessage
:actions="actions"
:parts="[
{
type: 'text',
id: '1',
text: 'Nuxt UI offers several features for building AI chatbots including the ChatMessage, ChatMessages, and ChatPrompt components. Best practices include using the Chat class from AI SDK, implementing proper message styling with variants, and utilizing the built-in actions for message interactions. The components are fully customizable with theming support and responsive design.'
}
]"
role="user"
id="1"
/>
</template><script setup lang="ts">
import { ref } from 'vue'
import type { ButtonProps } from '@nuxt/ui'
const actions = ref<ButtonProps[]>([
{
label: 'Copy to clipboard',
icon: 'i-lucide-copy'
}
])
</script>
<template>
<UChatMessage
:actions="actions"
:parts="[
{
type: 'text',
id: '1',
text: 'Nuxt UI offers several features for building AI chatbots including the ChatMessage, ChatMessages, and ChatPrompt components. Best practices include using the Chat class from AI SDK, implementing proper message styling with variants, and utilizing the built-in actions for message interactions. The components are fully customizable with theming support and responsive design.'
}
]"
role="user"
id="1"
/>
</template>예제
API 파일
Props (### Props)
| Prop | Default | Type |
|---|---|---|
as | 'article' | anyThe element or component this component should render as. |
id | stringA unique identifier for the message. | |
role | "system" | "user" | "assistant"The role of the message. | |
parts | UIMessagePart<TDataParts, TTools>[]The parts of the message. Use this for rendering the message in the UI. System messages should be avoided (set the system prompt on the server instead). They can have text parts. User messages can have text parts and file parts. Assistant messages can have text, reasoning, tool invocation, and file parts. | |
icon | any | |
avatar | AvatarProps & { [key: string]: any; } | |
variant | 'naked' | "solid" | "outline" | "soft" | "subtle" | "naked" |
color | 'neutral' | "primary" | "secondary" | "success" | "info" | "warning" | "error" | "neutral" |
side | 'left' | "left" | "right" |
actions | (Omit<ButtonProps, "onClick"> & { onClick?: ((e: MouseEvent, message: UIMessage<TMetadata, TDataParts, TTools>) => void) | undefined; })[]Display a list of actions under the message.
The | |
compact | false | boolean Render the message in a compact style.
This is done automatically when used inside a |
content | string | |
metadata | TMetadataThe metadata of the message. | |
ui | { root?: SlotClass; header?: SlotClass; container?: SlotClass; body?: SlotClass; leading?: SlotClass; leadingIcon?: SlotClass; leadingAvatar?: SlotClass; leadingAvatarSize?: SlotClass; files?: SlotClass; content?: SlotClass; actions?: SlotClass; } |
슬롯
| Slot | Type |
|---|---|
header | UIMessage<TMetadata, TDataParts, TTools> |
leading | UIMessage<TMetadata, TDataParts, TTools> & { avatar: (AvatarProps & { [key: string]: any; }) | undefined; ui: object; } |
files | Omit<UIMessage<TMetadata, TDataParts, TTools>, "parts"> & { parts: FileUIPart[]; } |
body | UIMessage<TMetadata, TDataParts, TTools> |
content | UIMessage<TMetadata, TDataParts, TTools> & { content?: string | undefined; } |
actions | UIMessage<TMetadata, TDataParts, TTools> & { actions: (Omit<ButtonProps, "onClick"> & { onClick?: ((e: MouseEvent, message: UIMessage<TMetadata, TDataParts, TTools>) => void) | undefined; })[] | undefined; } |
Theme 테마
export default defineAppConfig({
ui: {
chatMessage: {
slots: {
root: 'group/message relative w-full',
header: 'flex mb-1.5',
container: 'relative flex items-start',
body: 'min-w-0',
leading: 'inline-flex items-center justify-center min-h-6',
leadingIcon: 'shrink-0',
leadingAvatar: 'shrink-0',
leadingAvatarSize: '',
files: 'flex items-center gap-1.5',
content: 'relative text-pretty wrap-break-word *:first:mt-0 *:last:mb-0',
actions: [
'[@media(hover:hover)]:opacity-0 group-hover/message:opacity-100 absolute bottom-0 flex items-center',
'transition-opacity ease-out'
]
},
variants: {
variant: {
solid: '',
outline: '',
soft: '',
subtle: '',
naked: ''
},
color: {
primary: '',
secondary: '',
success: '',
info: '',
warning: '',
error: '',
neutral: ''
},
side: {
left: {},
right: {
container: 'justify-end ms-auto max-w-[75%]',
header: 'justify-end',
actions: 'right-0'
}
},
leading: {
true: ''
},
actions: {
true: ''
},
compact: {
true: {
root: 'scroll-mt-3',
container: 'gap-1.5 pb-3',
content: 'space-y-2',
leadingIcon: 'size-5',
leadingAvatarSize: '2xs'
},
false: {
root: 'scroll-mt-4 sm:scroll-mt-6',
container: 'gap-3 pb-8',
content: 'space-y-4',
leadingIcon: 'size-8',
leadingAvatarSize: 'md'
}
}
},
compoundVariants: [
{
compact: true,
actions: true,
class: {
container: 'pb-8'
}
},
{
variant: [
'solid',
'outline',
'soft',
'subtle'
],
compact: false,
class: {
content: 'px-4 py-3 rounded-lg min-h-12',
leading: 'mt-2'
}
},
{
variant: [
'solid',
'outline',
'soft',
'subtle'
],
compact: true,
class: {
content: 'px-2 py-1 rounded-lg min-h-8',
leading: 'mt-1'
}
},
{
variant: 'naked',
side: 'left',
class: {
body: 'w-full',
content: 'w-full'
}
},
{
color: 'primary',
variant: 'solid',
class: {
content: 'bg-primary text-inverted'
}
},
{
color: 'primary',
variant: 'outline',
class: {
content: 'text-primary ring ring-primary/25'
}
},
{
color: 'primary',
variant: 'soft',
class: {
content: 'bg-primary/10 text-primary'
}
},
{
color: 'primary',
variant: 'subtle',
class: {
content: 'bg-primary/10 text-primary ring ring-primary/25'
}
},
{
color: 'primary',
variant: 'naked',
class: {
content: 'text-primary'
}
},
{
color: 'neutral',
variant: 'solid',
class: {
content: 'bg-inverted text-inverted'
}
},
{
color: 'neutral',
variant: 'outline',
class: {
content: 'bg-default ring ring-default'
}
},
{
color: 'neutral',
variant: 'soft',
class: {
content: 'bg-elevated/50'
}
},
{
color: 'neutral',
variant: 'subtle',
class: {
content: 'bg-elevated/50 ring ring-default'
}
}
],
defaultVariants: {
side: 'left',
variant: 'naked',
color: 'neutral'
}
}
}
})import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
ui: {
chatMessage: {
slots: {
root: 'group/message relative w-full',
header: 'flex mb-1.5',
container: 'relative flex items-start',
body: 'min-w-0',
leading: 'inline-flex items-center justify-center min-h-6',
leadingIcon: 'shrink-0',
leadingAvatar: 'shrink-0',
leadingAvatarSize: '',
files: 'flex items-center gap-1.5',
content: 'relative text-pretty wrap-break-word *:first:mt-0 *:last:mb-0',
actions: [
'[@media(hover:hover)]:opacity-0 group-hover/message:opacity-100 absolute bottom-0 flex items-center',
'transition-opacity ease-out'
]
},
variants: {
variant: {
solid: '',
outline: '',
soft: '',
subtle: '',
naked: ''
},
color: {
primary: '',
secondary: '',
success: '',
info: '',
warning: '',
error: '',
neutral: ''
},
side: {
left: {},
right: {
container: 'justify-end ms-auto max-w-[75%]',
header: 'justify-end',
actions: 'right-0'
}
},
leading: {
true: ''
},
actions: {
true: ''
},
compact: {
true: {
root: 'scroll-mt-3',
container: 'gap-1.5 pb-3',
content: 'space-y-2',
leadingIcon: 'size-5',
leadingAvatarSize: '2xs'
},
false: {
root: 'scroll-mt-4 sm:scroll-mt-6',
container: 'gap-3 pb-8',
content: 'space-y-4',
leadingIcon: 'size-8',
leadingAvatarSize: 'md'
}
}
},
compoundVariants: [
{
compact: true,
actions: true,
class: {
container: 'pb-8'
}
},
{
variant: [
'solid',
'outline',
'soft',
'subtle'
],
compact: false,
class: {
content: 'px-4 py-3 rounded-lg min-h-12',
leading: 'mt-2'
}
},
{
variant: [
'solid',
'outline',
'soft',
'subtle'
],
compact: true,
class: {
content: 'px-2 py-1 rounded-lg min-h-8',
leading: 'mt-1'
}
},
{
variant: 'naked',
side: 'left',
class: {
body: 'w-full',
content: 'w-full'
}
},
{
color: 'primary',
variant: 'solid',
class: {
content: 'bg-primary text-inverted'
}
},
{
color: 'primary',
variant: 'outline',
class: {
content: 'text-primary ring ring-primary/25'
}
},
{
color: 'primary',
variant: 'soft',
class: {
content: 'bg-primary/10 text-primary'
}
},
{
color: 'primary',
variant: 'subtle',
class: {
content: 'bg-primary/10 text-primary ring ring-primary/25'
}
},
{
color: 'primary',
variant: 'naked',
class: {
content: 'text-primary'
}
},
{
color: 'neutral',
variant: 'solid',
class: {
content: 'bg-inverted text-inverted'
}
},
{
color: 'neutral',
variant: 'outline',
class: {
content: 'bg-default ring ring-default'
}
},
{
color: 'neutral',
variant: 'soft',
class: {
content: 'bg-elevated/50'
}
},
{
color: 'neutral',
variant: 'subtle',
class: {
content: 'bg-elevated/50 ring ring-default'
}
}
],
defaultVariants: {
side: 'left',
variant: 'naked',
color: 'neutral'
}
}
}
})
]
})