チャットメッセージ

Vercel AI SDKとシームレスに連携するように設計されたチャットメッセージの一覧を表示します。

使用法

ChatMessagesコンポーネントはChatMessageコンポーネントのリストをデフォルトスロットまたはmessagesプロパティのいずれかを使用して表示します。

<template>
  <UChatMessages>
    <UChatMessage
      v-for="(message, index) in messages"
      :key="index"
      v-bind="message"
    />
  </UChatMessages>
</template>
このコンポーネントは、以下のような機能を備えたAIチャットボット専用に構築されています。
  • ロード時に最初にスクロールします(shouldScrollToBottom)。
  • 新しいメッセージが到着すると連続的にスクロールダウン(shouldAutoScroll)。
  • スクロールアップすると「自動スクロール」ボタンが表示され、最新のメッセージ(autoScroll)にジャンプできます。
  • Aアシスタントの処理中に読み込みインジケータが表示されますstatus。
  • Submittedメッセージはビューポートの上部までスクロールされ、最後のユーザーメッセージの高さは動的に調整されます。

メッセージ

messagesプロパティを使用して、チャットメッセージのリストを表示します。

Hello, how are you?
I am doing well, thank you for asking! How can I assist you today?
What is the current weather in Tokyo?
Based on the latest data, Tokyo is currently experiencing sunny weather with temperatures around 24°C (75°F). It's a beautiful day with clear skies.
<script setup lang="ts">
const messages = ref([
  {
    id: '6045235a-a435-46b8-989d-2df38ca2eb47',
    role: 'user',
    parts: [
      {
        type: 'text',
        text: 'Hello, how are you?'
      }
    ]
  },
  {
    id: '7a92b3c1-d5f8-4e76-b8a9-3c1e5fb2e0d8',
    role: 'assistant',
    parts: [
      {
        type: 'text',
        text: 'I am doing well, thank you for asking! How can I assist you today?'
      }
    ]
  },
  {
    id: '9c84d6a7-8b23-4f12-a1d5-e7f3b9c05e2a',
    role: 'user',
    parts: [
      {
        type: 'text',
        text: 'What is the current weather in Tokyo?'
      }
    ]
  },
  {
    id: 'b2e5f8c3-a1d9-4e67-b3f2-c9d8e7a6b5f4',
    role: 'assistant',
    parts: [
      {
        type: 'text',
        text: "Based on the latest data, Tokyo is currently experiencing sunny weather with temperatures around 24°C (75°F). It's a beautiful day with clear skies."
      }
    ]
  }
])
</script>

<template>
  <UChatMessages :messages="messages" />
</template>
<script setup lang="ts">
import { ref } from 'vue'

const messages = ref([
  {
    id: '6045235a-a435-46b8-989d-2df38ca2eb47',
    role: 'user',
    parts: [
      {
        type: 'text',
        text: 'Hello, how are you?'
      }
    ]
  },
  {
    id: '7a92b3c1-d5f8-4e76-b8a9-3c1e5fb2e0d8',
    role: 'assistant',
    parts: [
      {
        type: 'text',
        text: 'I am doing well, thank you for asking! How can I assist you today?'
      }
    ]
  },
  {
    id: '9c84d6a7-8b23-4f12-a1d5-e7f3b9c05e2a',
    role: 'user',
    parts: [
      {
        type: 'text',
        text: 'What is the current weather in Tokyo?'
      }
    ]
  },
  {
    id: 'b2e5f8c3-a1d9-4e67-b3f2-c9d8e7a6b5f4',
    role: 'assistant',
    parts: [
      {
        type: 'text',
        text: "Based on the latest data, Tokyo is currently experiencing sunny weather with temperatures around 24°C (75°F). It's a beautiful day with clear skies."
      }
    ]
  }
])
</script>

<template>
  <UChatMessages :messages="messages" />
</template>

Status

statusプロパティを使用して、アシスタントが処理中にビジュアルインジケータを表示します。

Hello, how are you?
<script setup lang="ts">
const messages = ref([
  {
    id: '6045235a-a435-46b8-989d-2df38ca2eb47',
    role: 'user',
    parts: [
      {
        type: 'text',
        text: 'Hello, how are you?'
      }
    ]
  }
])
</script>

<template>
  <UChatMessages status="submitted" :messages="messages" />
</template>
<script setup lang="ts">
import { ref } from 'vue'

const messages = ref([
  {
    id: '6045235a-a435-46b8-989d-2df38ca2eb47',
    role: 'user',
    parts: [
      {
        type: 'text',
        text: 'Hello, how are you?'
      }
    ]
  }
])
</script>

<template>
  <UChatMessages status="submitted" :messages="messages" />
</template>
AI SDK useChat composableの異なるステータスの詳細は次のとおりです。
  • submitted:メッセージがAPIに送信され、レスポンスストリームの開始を待っています。
  • streaming:レスポンスがAPIからアクティブにストリーミングされ、大量のデータを受信します。
  • ready:完全なレスポンスが受信され処理されました。新しいユーザーメッセージを送信できます。
  • error:APIリクエスト中にエラーが発生し、正常に完了できませんでした。

User

userプロパティを使用して、ChatMessageプロパティをuserメッセージに変更します。デフォルトは

  • side: 'right'
  • variant: 'soft'
Hello, how are you?
I am doing well, thank you for asking! How can I assist you today?
What is the current weather in Tokyo?
Based on the latest data, Tokyo is currently experiencing sunny weather with temperatures around 24°C (75°F). It's a beautiful day with clear skies.
<script setup lang="ts">
const messages = ref([
  {
    id: '6045235a-a435-46b8-989d-2df38ca2eb47',
    role: 'user',
    parts: [
      {
        type: 'text',
        text: 'Hello, how are you?'
      }
    ]
  },
  {
    id: '7a92b3c1-d5f8-4e76-b8a9-3c1e5fb2e0d8',
    role: 'assistant',
    parts: [
      {
        type: 'text',
        text: 'I am doing well, thank you for asking! How can I assist you today?'
      }
    ]
  },
  {
    id: '9c84d6a7-8b23-4f12-a1d5-e7f3b9c05e2a',
    role: 'user',
    parts: [
      {
        type: 'text',
        text: 'What is the current weather in Tokyo?'
      }
    ]
  },
  {
    id: 'b2e5f8c3-a1d9-4e67-b3f2-c9d8e7a6b5f4',
    role: 'assistant',
    parts: [
      {
        type: 'text',
        text: "Based on the latest data, Tokyo is currently experiencing sunny weather with temperatures around 24°C (75°F). It's a beautiful day with clear skies."
      }
    ]
  }
])
</script>

<template>
  <UChatMessages
    :user="{
      side: 'left',
      variant: 'solid',
      avatar: {
        src: 'https://github.com/benjamincanac.png',
        loading: 'lazy'
      }
    }"
    :messages="messages"
  />
</template>
<script setup lang="ts">
import { ref } from 'vue'

const messages = ref([
  {
    id: '6045235a-a435-46b8-989d-2df38ca2eb47',
    role: 'user',
    parts: [
      {
        type: 'text',
        text: 'Hello, how are you?'
      }
    ]
  },
  {
    id: '7a92b3c1-d5f8-4e76-b8a9-3c1e5fb2e0d8',
    role: 'assistant',
    parts: [
      {
        type: 'text',
        text: 'I am doing well, thank you for asking! How can I assist you today?'
      }
    ]
  },
  {
    id: '9c84d6a7-8b23-4f12-a1d5-e7f3b9c05e2a',
    role: 'user',
    parts: [
      {
        type: 'text',
        text: 'What is the current weather in Tokyo?'
      }
    ]
  },
  {
    id: 'b2e5f8c3-a1d9-4e67-b3f2-c9d8e7a6b5f4',
    role: 'assistant',
    parts: [
      {
        type: 'text',
        text: "Based on the latest data, Tokyo is currently experiencing sunny weather with temperatures around 24°C (75°F). It's a beautiful day with clear skies."
      }
    ]
  }
])
</script>

<template>
  <UChatMessages
    :user="{
      side: 'left',
      variant: 'solid',
      avatar: {
        src: 'https://github.com/benjamincanac.png',
        loading: 'lazy'
      }
    }"
    :messages="messages"
  />
</template>

Assistant

assistantプロパティを使用して、ChatMessageプロパティをassistantメッセージに変更します。デフォルトは

  • side: 'left'
  • variant: 'naked'
Hello, how are you?
I am doing well, thank you for asking! How can I assist you today?
What is the current weather in Tokyo?
Based on the latest data, Tokyo is currently experiencing sunny weather with temperatures around 24°C (75°F). It's a beautiful day with clear skies.
<script setup lang="ts">
const messages = ref([
  {
    id: '6045235a-a435-46b8-989d-2df38ca2eb47',
    role: 'user',
    parts: [
      {
        type: 'text',
        text: 'Hello, how are you?'
      }
    ]
  },
  {
    id: '7a92b3c1-d5f8-4e76-b8a9-3c1e5fb2e0d8',
    role: 'assistant',
    parts: [
      {
        type: 'text',
        text: 'I am doing well, thank you for asking! How can I assist you today?'
      }
    ]
  },
  {
    id: '9c84d6a7-8b23-4f12-a1d5-e7f3b9c05e2a',
    role: 'user',
    parts: [
      {
        type: 'text',
        text: 'What is the current weather in Tokyo?'
      }
    ]
  },
  {
    id: 'b2e5f8c3-a1d9-4e67-b3f2-c9d8e7a6b5f4',
    role: 'assistant',
    parts: [
      {
        type: 'text',
        text: "Based on the latest data, Tokyo is currently experiencing sunny weather with temperatures around 24°C (75°F). It's a beautiful day with clear skies."
      }
    ]
  }
])
</script>

<template>
  <UChatMessages
    :assistant="{
      side: 'left',
      variant: 'outline',
      avatar: {
        icon: 'i-lucide-bot'
      },
      actions: [
        {
          label: 'Copy to clipboard',
          icon: 'i-lucide-copy'
        }
      ]
    }"
    :messages="messages"
  />
</template>
<script setup lang="ts">
import { ref } from 'vue'

const messages = ref([
  {
    id: '6045235a-a435-46b8-989d-2df38ca2eb47',
    role: 'user',
    parts: [
      {
        type: 'text',
        text: 'Hello, how are you?'
      }
    ]
  },
  {
    id: '7a92b3c1-d5f8-4e76-b8a9-3c1e5fb2e0d8',
    role: 'assistant',
    parts: [
      {
        type: 'text',
        text: 'I am doing well, thank you for asking! How can I assist you today?'
      }
    ]
  },
  {
    id: '9c84d6a7-8b23-4f12-a1d5-e7f3b9c05e2a',
    role: 'user',
    parts: [
      {
        type: 'text',
        text: 'What is the current weather in Tokyo?'
      }
    ]
  },
  {
    id: 'b2e5f8c3-a1d9-4e67-b3f2-c9d8e7a6b5f4',
    role: 'assistant',
    parts: [
      {
        type: 'text',
        text: "Based on the latest data, Tokyo is currently experiencing sunny weather with temperatures around 24°C (75°F). It's a beautiful day with clear skies."
      }
    ]
  }
])
</script>

<template>
  <UChatMessages
    :assistant="{
      side: 'left',
      variant: 'outline',
      avatar: {
        icon: 'i-lucide-bot'
      },
      actions: [
        {
          label: 'Copy to clipboard',
          icon: 'i-lucide-copy'
        }
      ]
    }"
    :messages="messages"
  />
</template>

Autoスクロール

auto-scrollプロパティを使用して、チャットの先頭までスクロールするときに表示される自動スクロールボタン(false値)をカスタマイズまたは非表示にします。デフォルトは

  • color: 'neutral'
  • variant: 'outline'

Buttonコンポーネントの任意のプロパティを渡してカスタマイズできます。

Hello, how are you?
I am doing well, thank you for asking! How can I assist you today?
What is the current weather in Tokyo?
Based on the latest data, Tokyo is currently experiencing sunny weather with temperatures around 24°C (75°F). It's a beautiful day with clear skies. The forecast for the rest of the week shows a slight chance of rain on Thursday, with temperatures gradually rising to 28°C by the weekend. Humidity levels are moderate at around 65%, and wind speeds are light at 8 km/h from the southeast. Air quality is good with an index of 42. The UV index is high at 7, so it's recommended to wear sunscreen if you're planning to spend time outdoors. Sunrise was at 5:24 AM and sunset will be at 6:48 PM, giving Tokyo approximately 13 hours and 24 minutes of daylight today. The moon is currently in its waxing gibbous phase.
Can you recommend some popular tourist attractions in Kyoto?
Kyoto is known for its beautiful temples, traditional tea houses, and gardens. Some popular attractions include Kinkaku-ji (Golden Pavilion) with its stunning gold leaf exterior reflecting in the mirror pond, Fushimi Inari Shrine with its thousands of vermilion torii gates winding up the mountainside, Arashiyama Bamboo Grove where towering stalks create an otherworldly atmosphere, Kiyomizu-dera Temple perched on a hillside offering panoramic views of the city, and the historic Gion district where you might spot geisha hurrying to evening appointments through narrow stone-paved streets lined with traditional wooden machiya houses.
<script setup lang="ts">
const messages = ref([
  {
    id: '6045235a-a435-46b8-989d-2df38ca2eb47',
    role: 'user',
    parts: [
      {
        type: 'text',
        text: 'Hello, how are you?'
      }
    ]
  },
  {
    id: '7a92b3c1-d5f8-4e76-b8a9-3c1e5fb2e0d8',
    role: 'assistant',
    parts: [
      {
        type: 'text',
        text: 'I am doing well, thank you for asking! How can I assist you today?'
      }
    ]
  },
  {
    id: '9c84d6a7-8b23-4f12-a1d5-e7f3b9c05e2a',
    role: 'user',
    parts: [
      {
        type: 'text',
        text: 'What is the current weather in Tokyo?'
      }
    ]
  },
  {
    id: 'b2e5f8c3-a1d9-4e67-b3f2-c9d8e7a6b5f4',
    role: 'assistant',
    parts: [
      {
        type: 'text',
        text: "Based on the latest data, Tokyo is currently experiencing sunny weather with temperatures around 24°C (75°F). It's a beautiful day with clear skies. The forecast for the rest of the week shows a slight chance of rain on Thursday, with temperatures gradually rising to 28°C by the weekend. Humidity levels are moderate at around 65%, and wind speeds are light at 8 km/h from the southeast. Air quality is good with an index of 42. The UV index is high at 7, so it's recommended to wear sunscreen if you're planning to spend time outdoors. Sunrise was at 5:24 AM and sunset will be at 6:48 PM, giving Tokyo approximately 13 hours and 24 minutes of daylight today. The moon is currently in its waxing gibbous phase."
      }
    ]
  },
  {
    id: 'c3e5f8c3-a1d9-4e67-b3f2-c9d8e7a6b5f4',
    role: 'user',
    parts: [
      {
        type: 'text',
        text: 'Can you recommend some popular tourist attractions in Kyoto?'
      }
    ]
  },
  {
    id: 'd4f5g8c3-a1d9-4e67-b3f2-c9d8e7a6b5f4',
    role: 'assistant',
    parts: [
      {
        type: 'text',
        text: 'Kyoto is known for its beautiful temples, traditional tea houses, and gardens. Some popular attractions include Kinkaku-ji (Golden Pavilion) with its stunning gold leaf exterior reflecting in the mirror pond, Fushimi Inari Shrine with its thousands of vermilion torii gates winding up the mountainside, Arashiyama Bamboo Grove where towering stalks create an otherworldly atmosphere, Kiyomizu-dera Temple perched on a hillside offering panoramic views of the city, and the historic Gion district where you might spot geisha hurrying to evening appointments through narrow stone-paved streets lined with traditional wooden machiya houses.'
      }
    ]
  }
])
</script>

<template>
  <UChatMessages
    :auto-scroll="{
      color: 'neutral',
      variant: 'outline'
    }"
    :should-scroll-to-bottom="false"
    :messages="messages"
  />
</template>
<script setup lang="ts">
import { ref, h } from 'vue'

const messages = ref([
  {
    id: '6045235a-a435-46b8-989d-2df38ca2eb47',
    role: 'user',
    parts: [
      {
        type: 'text',
        text: 'Hello, how are you?'
      }
    ]
  },
  {
    id: '7a92b3c1-d5f8-4e76-b8a9-3c1e5fb2e0d8',
    role: 'assistant',
    parts: [
      {
        type: 'text',
        text: 'I am doing well, thank you for asking! How can I assist you today?'
      }
    ]
  },
  {
    id: '9c84d6a7-8b23-4f12-a1d5-e7f3b9c05e2a',
    role: 'user',
    parts: [
      {
        type: 'text',
        text: 'What is the current weather in Tokyo?'
      }
    ]
  },
  {
    id: 'b2e5f8c3-a1d9-4e67-b3f2-c9d8e7a6b5f4',
    role: 'assistant',
    parts: [
      {
        type: 'text',
        text: "Based on the latest data, Tokyo is currently experiencing sunny weather with temperatures around 24°C (75°F). It's a beautiful day with clear skies. The forecast for the rest of the week shows a slight chance of rain on Thursday, with temperatures gradually rising to 28°C by the weekend. Humidity levels are moderate at around 65%, and wind speeds are light at 8 km/h from the southeast. Air quality is good with an index of 42. The UV index is high at 7, so it's recommended to wear sunscreen if you're planning to spend time outdoors. Sunrise was at 5:24 AM and sunset will be at 6:48 PM, giving Tokyo approximately 13 hours and 24 minutes of daylight today. The moon is currently in its waxing gibbous phase."
      }
    ]
  },
  {
    id: 'c3e5f8c3-a1d9-4e67-b3f2-c9d8e7a6b5f4',
    role: 'user',
    parts: [
      {
        type: 'text',
        text: 'Can you recommend some popular tourist attractions in Kyoto?'
      }
    ]
  },
  {
    id: 'd4f5g8c3-a1d9-4e67-b3f2-c9d8e7a6b5f4',
    role: 'assistant',
    parts: [
      {
        type: 'text',
        text: 'Kyoto is known for its beautiful temples, traditional tea houses, and gardens. Some popular attractions include Kinkaku-ji (Golden Pavilion) with its stunning gold leaf exterior reflecting in the mirror pond, Fushimi Inari Shrine with its thousands of vermilion torii gates winding up the mountainside, Arashiyama Bamboo Grove where towering stalks create an otherworldly atmosphere, Kiyomizu-dera Temple perched on a hillside offering panoramic views of the city, and the historic Gion district where you might spot geisha hurrying to evening appointments through narrow stone-paved streets lined with traditional wooden machiya houses.'
      }
    ]
  }
])
</script>

<template>
  <UChatMessages
    :auto-scroll="{
      color: 'neutral',
      variant: 'outline'
    }"
    :should-scroll-to-bottom="false"
    :messages="messages"
  />
</template>

Autoスクロールアイコン

auto-scroll-iconプロパティを使用して、自動スクロールボタンIconをカスタマイズします。デフォルトはi-lucide-arrow-downです。

Hello, how are you?
I am doing well, thank you for asking! How can I assist you today?
What is the current weather in Tokyo?
Based on the latest data, Tokyo is currently experiencing sunny weather with temperatures around 24°C (75°F). It's a beautiful day with clear skies. The forecast for the rest of the week shows a slight chance of rain on Thursday, with temperatures gradually rising to 28°C by the weekend. Humidity levels are moderate at around 65%, and wind speeds are light at 8 km/h from the southeast. Air quality is good with an index of 42. The UV index is high at 7, so it's recommended to wear sunscreen if you're planning to spend time outdoors. Sunrise was at 5:24 AM and sunset will be at 6:48 PM, giving Tokyo approximately 13 hours and 24 minutes of daylight today. The moon is currently in its waxing gibbous phase.
Can you recommend some popular tourist attractions in Kyoto?
Kyoto is known for its beautiful temples, traditional tea houses, and gardens. Some popular attractions include Kinkaku-ji (Golden Pavilion) with its stunning gold leaf exterior reflecting in the mirror pond, Fushimi Inari Shrine with its thousands of vermilion torii gates winding up the mountainside, Arashiyama Bamboo Grove where towering stalks create an otherworldly atmosphere, Kiyomizu-dera Temple perched on a hillside offering panoramic views of the city, and the historic Gion district where you might spot geisha hurrying to evening appointments through narrow stone-paved streets lined with traditional wooden machiya houses.
<script setup lang="ts">
const messages = ref([
  {
    id: '6045235a-a435-46b8-989d-2df38ca2eb47',
    role: 'user',
    parts: [
      {
        type: 'text',
        text: 'Hello, how are you?'
      }
    ]
  },
  {
    id: '7a92b3c1-d5f8-4e76-b8a9-3c1e5fb2e0d8',
    role: 'assistant',
    parts: [
      {
        type: 'text',
        text: 'I am doing well, thank you for asking! How can I assist you today?'
      }
    ]
  },
  {
    id: '9c84d6a7-8b23-4f12-a1d5-e7f3b9c05e2a',
    role: 'user',
    parts: [
      {
        type: 'text',
        text: 'What is the current weather in Tokyo?'
      }
    ]
  },
  {
    id: 'b2e5f8c3-a1d9-4e67-b3f2-c9d8e7a6b5f4',
    role: 'assistant',
    parts: [
      {
        type: 'text',
        text: "Based on the latest data, Tokyo is currently experiencing sunny weather with temperatures around 24°C (75°F). It's a beautiful day with clear skies. The forecast for the rest of the week shows a slight chance of rain on Thursday, with temperatures gradually rising to 28°C by the weekend. Humidity levels are moderate at around 65%, and wind speeds are light at 8 km/h from the southeast. Air quality is good with an index of 42. The UV index is high at 7, so it's recommended to wear sunscreen if you're planning to spend time outdoors. Sunrise was at 5:24 AM and sunset will be at 6:48 PM, giving Tokyo approximately 13 hours and 24 minutes of daylight today. The moon is currently in its waxing gibbous phase."
      }
    ]
  },
  {
    id: 'c3e5f8c3-a1d9-4e67-b3f2-c9d8e7a6b5f4',
    role: 'user',
    parts: [
      {
        type: 'text',
        text: 'Can you recommend some popular tourist attractions in Kyoto?'
      }
    ]
  },
  {
    id: 'd4f5g8c3-a1d9-4e67-b3f2-c9d8e7a6b5f4',
    role: 'assistant',
    parts: [
      {
        type: 'text',
        text: 'Kyoto is known for its beautiful temples, traditional tea houses, and gardens. Some popular attractions include Kinkaku-ji (Golden Pavilion) with its stunning gold leaf exterior reflecting in the mirror pond, Fushimi Inari Shrine with its thousands of vermilion torii gates winding up the mountainside, Arashiyama Bamboo Grove where towering stalks create an otherworldly atmosphere, Kiyomizu-dera Temple perched on a hillside offering panoramic views of the city, and the historic Gion district where you might spot geisha hurrying to evening appointments through narrow stone-paved streets lined with traditional wooden machiya houses.'
      }
    ]
  }
])
</script>

<template>
  <UChatMessages
    auto-scroll-icon="i-lucide-chevron-down"
    :should-scroll-to-bottom="false"
    :messages="messages"
  />
</template>
<script setup lang="ts">
import { ref, h } from 'vue'

const messages = ref([
  {
    id: '6045235a-a435-46b8-989d-2df38ca2eb47',
    role: 'user',
    parts: [
      {
        type: 'text',
        text: 'Hello, how are you?'
      }
    ]
  },
  {
    id: '7a92b3c1-d5f8-4e76-b8a9-3c1e5fb2e0d8',
    role: 'assistant',
    parts: [
      {
        type: 'text',
        text: 'I am doing well, thank you for asking! How can I assist you today?'
      }
    ]
  },
  {
    id: '9c84d6a7-8b23-4f12-a1d5-e7f3b9c05e2a',
    role: 'user',
    parts: [
      {
        type: 'text',
        text: 'What is the current weather in Tokyo?'
      }
    ]
  },
  {
    id: 'b2e5f8c3-a1d9-4e67-b3f2-c9d8e7a6b5f4',
    role: 'assistant',
    parts: [
      {
        type: 'text',
        text: "Based on the latest data, Tokyo is currently experiencing sunny weather with temperatures around 24°C (75°F). It's a beautiful day with clear skies. The forecast for the rest of the week shows a slight chance of rain on Thursday, with temperatures gradually rising to 28°C by the weekend. Humidity levels are moderate at around 65%, and wind speeds are light at 8 km/h from the southeast. Air quality is good with an index of 42. The UV index is high at 7, so it's recommended to wear sunscreen if you're planning to spend time outdoors. Sunrise was at 5:24 AM and sunset will be at 6:48 PM, giving Tokyo approximately 13 hours and 24 minutes of daylight today. The moon is currently in its waxing gibbous phase."
      }
    ]
  },
  {
    id: 'c3e5f8c3-a1d9-4e67-b3f2-c9d8e7a6b5f4',
    role: 'user',
    parts: [
      {
        type: 'text',
        text: 'Can you recommend some popular tourist attractions in Kyoto?'
      }
    ]
  },
  {
    id: 'd4f5g8c3-a1d9-4e67-b3f2-c9d8e7a6b5f4',
    role: 'assistant',
    parts: [
      {
        type: 'text',
        text: 'Kyoto is known for its beautiful temples, traditional tea houses, and gardens. Some popular attractions include Kinkaku-ji (Golden Pavilion) with its stunning gold leaf exterior reflecting in the mirror pond, Fushimi Inari Shrine with its thousands of vermilion torii gates winding up the mountainside, Arashiyama Bamboo Grove where towering stalks create an otherworldly atmosphere, Kiyomizu-dera Temple perched on a hillside offering panoramic views of the city, and the historic Gion district where you might spot geisha hurrying to evening appointments through narrow stone-paved streets lined with traditional wooden machiya houses.'
      }
    ]
  }
])
</script>

<template>
  <UChatMessages
    auto-scroll-icon="i-lucide-chevron-down"
    :should-scroll-to-bottom="false"
    :messages="messages"
  />
</template>
このアイコンはapp.config.tsのui.icons.arrowDownキーでグローバルにカスタマイズできます。
このアイコンはvite.config.tsのui.icons.arrowDownキーでグローバルにカスタマイズできます。

自動スクロール

should-auto-scrollプロパティを使用して、メッセージのストリーミング中に連続自動スクロールを有効/無効にします。デフォルトはfalseです。

<template>
  <UChatMessages :messages="messages" should-auto-scroll />
</template>

下までスクロールしてください

should-scroll-to-bottomプロパティを使用して、コンポーネントがマウントされたときに下部の自動スクロールを有効/無効にします。デフォルトはtrueです。

<template>
  <UChatMessages :messages="messages" :should-scroll-to-bottom="false" />
</template>

サンプル

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

インジケータスロット付

#indicatorスロットを使用して、ChatShimmerエフェクトでロードインジケータをカスタマイズします。

Hello! Can you help me with something?
Searching...
<script setup lang="ts">
import type { UIMessage } from 'ai'
import { useChat } from '@ai-sdk/vue'

const initialMessages: UIMessage[] = [{
  id: '1',
  role: 'user',
  parts: [{ type: 'text', text: 'Hello! Can you help me with something?' }]
}]

const { messages } = useChat({
  messages: initialMessages
})

const size = 4
const gap = 2
const totalDots = size * size

const patterns = [
  [[0], [1], [2], [3], [7], [11], [15], [14], [13], [12], [8], [4], [5], [6], [10], [9]],
  [[0, 4, 8, 12], [1, 5, 9, 13], [2, 6, 10, 14], [3, 7, 11, 15]],
  [[5, 6, 9, 10], [1, 4, 7, 8, 11, 14], [0, 3, 12, 15], [1, 4, 7, 8, 11, 14], [5, 6, 9, 10]],
  [[0], [1, 4], [2, 5, 8], [3, 6, 9, 12], [7, 10, 13], [11, 14], [15]]
]

const activeDots = ref<Set<number>>(new Set())
let patternIndex = 0
let stepIndex = 0

function nextStep() {
  const pattern = patterns[patternIndex]
  if (!pattern) return

  activeDots.value = new Set(pattern[stepIndex])
  stepIndex++

  if (stepIndex >= pattern.length) {
    stepIndex = 0
    patternIndex = (patternIndex + 1) % patterns.length
  }
}

const statusMessages = ['Searching...', 'Reading...', 'Analyzing...', 'Thinking...']
const currentIndex = ref(0)
const displayedText = ref(statusMessages[0]!)
const chars = 'abcdefghijklmnopqrstuvwxyz'

function scramble(from: string, to: string) {
  const maxLength = Math.max(from.length, to.length)
  let frame = 0
  const totalFrames = 15

  const step = () => {
    frame++
    let result = ''
    const progress = (frame / totalFrames) * maxLength

    for (let i = 0; i < maxLength; i++) {
      if (i < progress - 2) {
        result += to[i] || ''
      } else if (i < progress) {
        result += chars[Math.floor(Math.random() * chars.length)]
      } else {
        result += from[i] || ''
      }
    }

    displayedText.value = result

    if (frame < totalFrames) {
      requestAnimationFrame(step)
    } else {
      displayedText.value = to
    }
  }

  requestAnimationFrame(step)
}

let matrixInterval: ReturnType<typeof setInterval> | undefined
let textInterval: ReturnType<typeof setInterval> | undefined

onMounted(() => {
  nextStep()
  matrixInterval = setInterval(nextStep, 120)
  textInterval = setInterval(() => {
    const prev = displayedText.value
    currentIndex.value = (currentIndex.value + 1) % statusMessages.length
    scramble(prev, statusMessages[currentIndex.value]!)
  }, 3000)
})

onUnmounted(() => {
  clearInterval(matrixInterval)
  clearInterval(textInterval)
})
</script>

<template>
  <UChatMessages
    :messages="messages"
    status="submitted"
    :should-scroll-to-bottom="false"
  >
    <template #indicator>
      <div class="flex items-center gap-2 text-muted overflow-hidden">
        <div
          class="shrink-0 grid size-4"
          :style="{
            gridTemplateColumns: `repeat(${size}, 1fr)`,
            gap: `${gap}px`
          }"
        >
          <span
            v-for="i in totalDots"
            :key="i"
            class="rounded-sm bg-current transition-opacity duration-100"
            :class="activeDots.has(i - 1) ? 'opacity-100' : 'opacity-20'"
          />
        </div>

        <UChatShimmer :text="displayedText" class="text-sm font-mono" />
      </div>
    </template>
  </UChatMessages>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import type { UIMessage } from 'ai'
import { useChat } from '@ai-sdk/vue'

const initialMessages: UIMessage[] = [{
  id: '1',
  role: 'user',
  parts: [{ type: 'text', text: 'Hello! Can you help me with something?' }]
}]

const { messages } = useChat({
  messages: initialMessages
})

const size = 4
const gap = 2
const totalDots = size * size

const patterns = [
  [[0], [1], [2], [3], [7], [11], [15], [14], [13], [12], [8], [4], [5], [6], [10], [9]],
  [[0, 4, 8, 12], [1, 5, 9, 13], [2, 6, 10, 14], [3, 7, 11, 15]],
  [[5, 6, 9, 10], [1, 4, 7, 8, 11, 14], [0, 3, 12, 15], [1, 4, 7, 8, 11, 14], [5, 6, 9, 10]],
  [[0], [1, 4], [2, 5, 8], [3, 6, 9, 12], [7, 10, 13], [11, 14], [15]]
]

const activeDots = ref<Set<number>>(new Set())
let patternIndex = 0
let stepIndex = 0

function nextStep() {
  const pattern = patterns[patternIndex]
  if (!pattern) return

  activeDots.value = new Set(pattern[stepIndex])
  stepIndex++

  if (stepIndex >= pattern.length) {
    stepIndex = 0
    patternIndex = (patternIndex + 1) % patterns.length
  }
}

const statusMessages = ['Searching...', 'Reading...', 'Analyzing...', 'Thinking...']
const currentIndex = ref(0)
const displayedText = ref(statusMessages[0]!)
const chars = 'abcdefghijklmnopqrstuvwxyz'

function scramble(from: string, to: string) {
  const maxLength = Math.max(from.length, to.length)
  let frame = 0
  const totalFrames = 15

  const step = () => {
    frame++
    let result = ''
    const progress = (frame / totalFrames) * maxLength

    for (let i = 0; i < maxLength; i++) {
      if (i < progress - 2) {
        result += to[i] || ''
      } else if (i < progress) {
        result += chars[Math.floor(Math.random() * chars.length)]
      } else {
        result += from[i] || ''
      }
    }

    displayedText.value = result

    if (frame < totalFrames) {
      requestAnimationFrame(step)
    } else {
      displayedText.value = to
    }
  }

  requestAnimationFrame(step)
}

let matrixInterval: ReturnType<typeof setInterval> | undefined
let textInterval: ReturnType<typeof setInterval> | undefined

onMounted(() => {
  nextStep()
  matrixInterval = setInterval(nextStep, 120)
  textInterval = setInterval(() => {
    const prev = displayedText.value
    currentIndex.value = (currentIndex.value + 1) % statusMessages.length
    scramble(prev, statusMessages[currentIndex.value]!)
  }, 3000)
})

onUnmounted(() => {
  clearInterval(matrixInterval)
  clearInterval(textInterval)
})
</script>

<template>
  <UChatMessages
    :messages="messages"
    status="submitted"
    :should-scroll-to-bottom="false"
  >
    <template #indicator>
      <div class="flex items-center gap-2 text-muted overflow-hidden">
        <div
          class="shrink-0 grid size-4"
          :style="{
            gridTemplateColumns: `repeat(${size}, 1fr)`,
            gap: `${gap}px`
          }"
        >
          <span
            v-for="i in totalDots"
            :key="i"
            class="rounded-sm bg-current transition-opacity duration-100"
            :class="activeDots.has(i - 1) ? 'opacity-100' : 'opacity-20'"
          />
        </div>

        <UChatShimmer :text="displayedText" class="text-sm font-mono" />
      </div>
    </template>
  </UChatMessages>
</template>

API

Props

Prop Default Type
messages
T
status
"submitted" | "streaming" | "ready" | "error"
shouldAutoScroll
false
boolean

Whether to automatically scroll to the bottom when a message is streaming.

shouldScrollToBottom
true
boolean

Whether to scroll to the bottom on mounted.

autoScroll
true
boolean | Omit<ButtonProps, LinkPropsKeys>

Display an auto scroll button. { size: 'md', color: 'neutral', variant: 'outline' }

autoScrollIcon
appConfig.ui.icons.arrowDown
any

The icon displayed in the auto scroll button.

user
Pick<PropsBase<T>, "actions" | "ui" | "color" | "variant" | "icon" | "avatar" | "side">

The user messages props. { side: 'right', variant: 'soft' }

assistant
Pick<PropsBase<T>, "actions" | "ui" | "color" | "variant" | "icon" | "avatar" | "side">

The assistant messages props. { side: 'left', variant: 'naked' }

compact
false
boolean

Render the messages in a compact style. This is done automatically when used inside a UChatPalette.

spacingOffset
0
number

The spacing offset for the last message in px. Can be useful when the prompt is sticky for example.

ui
{ root?: SlotClass; indicator?: SlotClass; viewport?: SlotClass; autoScroll?: SlotClass; }

スロット

Slot Type
default
{ registerMessageRef: (id: string, element: ComponentPublicInstance | null) => void; }
indicator
{ ui: object; }
viewport
{ ui: object; }
header
UIMessage<unknown, UIDataTypes, UITools> & { message: MessageBase<T>; }
leading
UIMessage<unknown, UIDataTypes, UITools> & { avatar: (AvatarProps & { [key: string]: any; }) | undefined; ui: object; } & { message: MessageBase<T>; }
files
Omit<UIMessage<unknown, UIDataTypes, UITools>, "parts"> & { parts: FileUIPart[]; } & { message: MessageBase<T>; }
body
UIMessage<unknown, UIDataTypes, UITools> & { message: MessageBase<T>; }
content
UIMessage<unknown, UIDataTypes, UITools> & { content?: string | undefined; } & { message: MessageBase<T>; }
actions
UIMessage<unknown, UIDataTypes, UITools> & { actions: (Omit<ButtonProps, "onClick"> & { onClick?: ((e: MouseEvent, message: UIMessage<unknown, UIDataTypes, UITools>) => void) | undefined; })[] | undefined; } & { message: MessageBase<T>; }
ChatMessageコンポーネントのすべてのスロットをChatMessages内で使用できます。messagesプロパティを使用するときに個々のメッセージをカスタマイズできるように自動的に転送されます。
<script setup lang="ts">
import { isTextUIPart } from 'ai'
</script>

<template>
  <UChatMessages :messages="messages" :status="status">
    <template #content="{ message }">
      <template
        v-for="(part, index) in message.parts"
        :key="`${message.id}-${part.type}-${index}`"
      >
        <p v-if="isTextUIPart(part)" class="whitespace-pre-wrap">
          {{ part.text }}
        </p>
      </template>
    </template>
  </UChatMessages>
</template>

Expose

テンプレート参照を介してコンポーネントにアクセスする場合、以下を使用できます:

名前タイプ
registerMessageRef(id: string, element: ComponentPublicInstance | null)void

Theme

app.config.ts
export default defineAppConfig({
  ui: {
    chatMessages: {
      slots: {
        root: 'w-full flex flex-col gap-1 flex-1 px-2.5 [&>article]:last-of-type:min-h-(--last-message-height)',
        indicator: 'h-6 flex items-center gap-1 py-3 *:size-2 *:rounded-full *:bg-elevated motion-safe:[&>*:nth-child(1)]:animate-[bounce_1s_infinite] motion-safe:[&>*:nth-child(2)]:animate-[bounce_1s_0.15s_infinite] motion-safe:[&>*:nth-child(3)]:animate-[bounce_1s_0.3s_infinite]',
        viewport: 'absolute inset-x-0 top-[86%] data-[state=open]:animate-[fade-in_200ms_var(--ease-out)] data-[state=closed]:animate-[fade-out_200ms_var(--ease-out)]',
        autoScroll: 'rounded-full absolute right-1/2 translate-x-1/2 bottom-0'
      },
      variants: {
        compact: {
          true: '',
          false: ''
        }
      }
    }
  }
})
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: {
        chatMessages: {
          slots: {
            root: 'w-full flex flex-col gap-1 flex-1 px-2.5 [&>article]:last-of-type:min-h-(--last-message-height)',
            indicator: 'h-6 flex items-center gap-1 py-3 *:size-2 *:rounded-full *:bg-elevated motion-safe:[&>*:nth-child(1)]:animate-[bounce_1s_infinite] motion-safe:[&>*:nth-child(2)]:animate-[bounce_1s_0.15s_infinite] motion-safe:[&>*:nth-child(3)]:animate-[bounce_1s_0.3s_infinite]',
            viewport: 'absolute inset-x-0 top-[86%] data-[state=open]:animate-[fade-in_200ms_var(--ease-out)] data-[state=closed]:animate-[fade-out_200ms_var(--ease-out)]',
            autoScroll: 'rounded-full absolute right-1/2 translate-x-1/2 bottom-0'
          },
          variants: {
            compact: {
              true: '',
              false: ''
            }
          }
        }
      }
    })
  ]
})

Changelog

No recent changes