消息

一套用于显示聊天消息的综合组件,包括消息渲染、分支、操作和 Markdown 响应。

Message 组件套件提供了一套完整的工具来构建聊天界面。它包括用于显示来自用户和 AI 助手的消息、管理多个响应分支、添加操作按钮和渲染 Markdown 内容的组件。

使用 CLI 安装

AI Elements Vue
shadcn-vue CLI
npx ai-elements-vue@latest add message

手动安装

将以下代码复制并粘贴到同一文件夹中。

Message.vue
MessageContent.vue
MessageActions.vue
MessageAction.vue
MessageBranch.vue
MessageBranchContent.vue
MessageBranchSelector.vue
MessageBranchPrevious.vue
MessageBranchNext.vue
MessageBranchPage.vue
MessageResponse.vue
MessageAttachments.vue
MessageAttachment.vue
MessageToolbar.vue
context.ts
index.ts
<script setup lang="ts">
import type { UIMessage } from 'ai'
import type { HTMLAttributes } from 'vue'
import { cn } from '@repo/shadcn-vue/lib/utils'

interface Props {
  from: UIMessage['role']
  class?: HTMLAttributes['class']
}

const props = defineProps<Props>()
</script>

<template>
  <div
    :class="
      cn(
        'group flex w-full max-w-[80%] gap-2',
        props.from === 'user' ? 'is-user ml-auto justify-end' : 'is-assistant',
        props.class,
      )
    "
    v-bind="$attrs"
  >
    <slot />
  </div>
</template>

与 AI SDK 一起使用

构建一个简单的聊天 UI,用户可以复制或重新生成最近的消息。

将以下组件添加到你的前端:

pages/index.vue
<script setup lang="ts">
import { useChat } from '@ai-sdk/vue'
import { CopyIcon, RefreshCcwIcon } from 'lucide-vue-next'
import { ref } from 'vue'
import {
  Conversation,
  ConversationContent,
  ConversationScrollButton,
} from '@/components/ai-elements/conversation'
import {
  Message,
  MessageAction,
  MessageActions,
  MessageContent,
  MessageResponse,
} from '@/components/ai-elements/message'
import {
  PromptInput,
  PromptInputSubmit,
  PromptInputTextarea,
} from '@/components/ai-elements/prompt-input'

const input = ref('')

const { messages, sendMessage, status, regenerate } = useChat()

function handleSubmit() {
  if (input.value.trim()) {
    sendMessage({ text: input.value })
    input.value = ''
  }
}
</script>

<template>
  <div
    class="max-w-4xl mx-auto p-6 relative size-full rounded-lg border h-[600px]"
  >
    <div class="flex flex-col h-full">
      <Conversation>
        <ConversationContent>
          <template
            v-for="(message, messageIndex) in messages"
            :key="message.id"
          >
            <template v-for="(part, i) in message.parts" :key="`${message.id}-${i}`">
              <template v-if="part.type === 'text'">
                <Message :from="message.role">
                  <MessageContent>
                    <MessageResponse :content="part.text" />
                  </MessageContent>
                </Message>

                <MessageActions
                  v-if="
                    message.role === 'assistant'
                      && messageIndex === messages.length - 1
                  "
                >
                  <MessageAction label="Retry" @click="regenerate()">
                    <RefreshCcwIcon class="size-3" />
                  </MessageAction>
                  <MessageAction
                    label="Copy"
                    @click="navigator.clipboard.writeText(part.text)"
                  >
                    <CopyIcon class="size-3" />
                  </MessageAction>
                </MessageActions>
              </template>
            </template>
          </template>
        </ConversationContent>
        <ConversationScrollButton />
      </Conversation>

      <PromptInput
        class="mt-4 w-full max-w-2xl mx-auto relative"
        @submit.prevent="handleSubmit"
      >
        <PromptInputTextarea
          v-model="input"
          placeholder="Say something..."
          class="pr-12"
        />
        <PromptInputSubmit
          :status="status === 'streaming' ? 'streaming' : 'ready'"
          :disabled="!input.trim()"
          class="absolute bottom-1 right-1"
        />
      </PromptInput>
    </div>
  </div>
</template>

特性

  • 以不同的样式和自动对齐显示来自用户和 AI 助手的消息
  • 简约的扁平设计,用户消息使用次要背景,助手消息全宽
  • 响应分支,带有导航控件以在多个 AI 响应版本之间切换
  • Markdown 渲染,支持 GFM(表格、任务列表、删除线)、数学公式和智能流式传输
  • 操作按钮,用于常见操作(重试、喜欢、不喜欢、复制、分享),带有工具提示和状态管理
  • 文件附件显示,支持图片和通用文件,具有预览和删除功能
  • 代码块,具有语法高亮和复制到剪贴板功能
  • 键盘可访问,具有适当的 ARIA 标签
  • 响应式设计,适应不同的屏幕尺寸
  • 无缝的浅色/深色主题集成

与 AI SDK 一起使用

构建一个简单的聊天 UI,用户可以复制或重新生成最近的消息。

将以下组件添加到你的前端:

pages/index.vue
<script setup lang="ts">
import { useChat } from '@ai-sdk/vue'
import { CopyIcon, RefreshCcwIcon } from 'lucide-vue-next'
import { ref } from 'vue'

import {
  Conversation,
  ConversationContent,
  ConversationScrollButton,
} from '@/components/ai-elements/conversation'
import { Message, MessageAction, MessageActions, MessageContent, MessageResponse } from '@/components/ai-elements/message'
import {
  PromptInput,
  PromptInputSubmit,
  PromptInputTextarea,
} from '@/components/ai-elements/prompt-input'

const { messages, append, status, reload } = useChat()
const input = ref('')

function handleSubmit() {
  if (input.value.trim()) {
    append({ role: 'user', content: input.value })
    input.value = ''
  }
}

function copyToClipboard(text: string) {
  navigator.clipboard.writeText(text)
}
</script>

<template>
  <div class="max-w-4xl mx-auto p-6 relative size-full rounded-lg border h-[600px]">
    <div class="flex flex-col h-full">
      <Conversation>
        <ConversationContent>
          <template v-for="(message, messageIndex) in messages" :key="message.id">
            <template v-if="message.parts">
              <template v-for="(part, i) in message.parts" :key="`${message.id}-${i}`">
                <template v-if="part.type === 'text'">
                  <Message :from="message.role">
                    <MessageContent>
                      <MessageResponse>{{ part.text }}</MessageResponse>
                    </MessageContent>
                  </Message>

                  <MessageActions
                    v-if="message.role === 'assistant' && messageIndex === messages.length - 1"
                  >
                    <MessageAction label="Retry" @click="reload()">
                      <RefreshCcwIcon class="size-3" />
                    </MessageAction>
                    <MessageAction label="Copy" @click="copyToClipboard(part.text)">
                      <CopyIcon class="size-3" />
                    </MessageAction>
                  </MessageActions>
                </template>
              </template>
            </template>

            <template v-else>
              <Message :from="message.role">
                <MessageContent>
                  <MessageResponse>{{ message.content }}</MessageResponse>
                </MessageContent>
              </Message>

              <MessageActions
                v-if="message.role === 'assistant' && messageIndex === messages.length - 1"
              >
                <MessageAction label="Retry" @click="reload()">
                  <RefreshCcwIcon class="size-3" />
                </MessageAction>
                <MessageAction label="Copy" @click="copyToClipboard(message.content)">
                  <CopyIcon class="size-3" />
                </MessageAction>
              </MessageActions>
            </template>
          </template>
        </ConversationContent>
        <ConversationScrollButton />
      </Conversation>

      <PromptInput
        class="mt-4 w-full max-w-2xl mx-auto relative bg-transparent border-0"
        @submit="handleSubmit"
      >
        <PromptInputTextarea
          v-model="input"
          placeholder="Say something..."
          class="pr-12"
        />
        <PromptInputSubmit
          :status="status === 'streaming' ? 'streaming' : 'ready'"
          :disabled="!input.trim()"
          class="absolute bottom-1 right-1"
        />
      </PromptInput>
    </div>
  </div>
</template>

Props

<Message />

fromrequiredUIMessage['role']
消息发送者的角色("user"、"assistant" 或 "system")。
classstring
''
应用于组件的额外类。

<MessageContent />

classstring
''
Additional classes applied to the component.

<MessageActions />

classstring
''
Additional classes applied to the component.

<MessageAction />

tooltipstring
''
悬停时显示的可选工具提示文本。
labelstring
''
屏幕阅读器的可访问标签。如果未提供工具提示,也用作后备。
variantButtonVariants['variant']
'ghost'
可选按钮变体。
sizeButtonVariants['size']
'icon-sm'
可选按钮大小。

<MessageBranch />

defaultBranchnumber
0
默认显示的分支索引。
classstring
''
Additional classes applied to the component.

<MessageBranchContent />

classstring
''
Additional classes applied to the component.

<MessageBranchSelector />

fromrequiredUIMessage['role']
The role of the message sender ("user", "assistant", or "system").

<MessageBranchPage />

classstring
''
Additional classes applied to the component.

<MessageResponse />

contentstring
''
消息的内容。
classstring
''
Additional classes applied to the component.
vue-stream-markdown props
来自 vue-stream-markdown 的额外 props

<MessageAttachments />

classstring
''
Additional classes applied to the component.

Example:

<MessageAttachments class="mb-2">
  <MessageAttachment
    v-for="attachment in files"
    :key="attachment.url"
    :data="attachment"
  />
</MessageAttachments>

<MessageAttachment />

datarequiredFileUIPart
要显示的文件数据。必须包含 url 和 mediaType。
classstring
''
Additional classes applied to the component.

Example:

<MessageAttachment
  data="{
    type: 'file',
    url: 'https://example.com/image.jpg',
    mediaType: 'image/jpeg',
    filename: 'image.jpg'
  }"
  @remove="() => console.log('Remove clicked')"
/>

<MessageToolbar />

classstring
''
Additional classes applied to the component.

Emits

<MessageBranch />

branchChangefunction
分支更改时发出。

<MessageAttachment />

removefunction
附件被移除时发出。