11<script setup lang="ts">
22import type { UIMessage } from ' ai'
33import { DefaultChatTransport , isToolUIPart , isReasoningUIPart , isTextUIPart , getToolName } from ' ai'
4- import { Chat } from ' @ai-sdk/vue'
4+ import { useChat } from ' @ai-sdk/vue'
55import { isPartStreaming , isToolStreaming } from ' @nuxt/ui/utils/ai'
66
77useSeoMeta ({
@@ -41,13 +41,13 @@ const input = ref('')
4141const storedMessages = useLocalStorage <UIMessage []>(' volta-chat-messages' , [])
4242const wasStreaming = useLocalStorage (' volta-chat-streaming' , false )
4343
44- const chat = new Chat ({
44+ const { messages, status, error, sendMessage, stop, regenerate } = useChat ({
4545 messages: storedMessages .value ,
4646 transport: new DefaultChatTransport ({
4747 api: ' /api/chat'
4848 }),
49- onError : (error ) => {
50- let message = error .message
49+ onError : (err ) => {
50+ let message = err .message
5151 if (typeof message === ' string' && message [0 ] === ' {' ) {
5252 try {
5353 message = JSON .parse (message ).message || message
@@ -70,35 +70,35 @@ const chat = new Chat({
7070 }
7171})
7272
73- watch (() => chat . status , (status ) => {
74- wasStreaming .value = status === ' streaming'
73+ watch (status , (value ) => {
74+ wasStreaming .value = value === ' streaming'
7575})
7676
7777onBeforeUnmount (() => {
78- if (chat . status === ' streaming' ) {
79- chat . stop ()
78+ if (status . value === ' streaming' ) {
79+ stop ()
8080 }
8181})
8282
8383onMounted (() => {
84- if (wasStreaming .value && chat . messages .length ) {
84+ if (wasStreaming .value && messages . value .length ) {
8585 wasStreaming .value = false
86- chat . regenerate ()
86+ regenerate ()
8787 }
8888})
8989
9090function onSubmit() {
9191 if (! input .value .trim ()) return
9292
93- const isFirstMessage = ! chat . messages .length
93+ const isFirstMessage = ! messages . value .length
9494
9595 if (isFirstMessage && document .startViewTransition ) {
9696 document .startViewTransition (() => {
97- chat . sendMessage ({ text: input .value })
97+ sendMessage ({ text: input .value })
9898 input .value = ' '
9999 })
100100 } else {
101- chat . sendMessage ({ text: input .value })
101+ sendMessage ({ text: input .value })
102102 input .value = ' '
103103 }
104104}
@@ -108,10 +108,10 @@ function askQuestion(question: string) {
108108 onSubmit ()
109109}
110110
111- watch (() => chat . messages , (messages ) => {
112- storedMessages .value = messages
111+ watch (messages , (value ) => {
112+ storedMessages .value = [ ... value ]
113113
114- const last = messages [ messages .length - 1 ]
114+ const last = value [ value .length - 1 ]
115115 if (last ?.role === ' assistant' ) {
116116 const hasCompletedFavoriteTool = last .parts .some (
117117 p => isToolUIPart (p ) && getToolName (p as any ) === ' manageFavorites' && ! isToolStreaming (p )
@@ -123,10 +123,10 @@ watch(() => chat.messages, (messages) => {
123123}, { deep: true })
124124
125125function clearMessages() {
126- if (chat . status === ' streaming' ) {
127- chat . stop ()
126+ if (status . value === ' streaming' ) {
127+ stop ()
128128 }
129- chat . messages = []
129+ messages . value = []
130130}
131131
132132const toolLabels: Record <string , { searching: string , searched: string , icon: string }> = {
@@ -160,7 +160,7 @@ function getToolIcon(part: { state: string, toolName?: string, toolCallId?: stri
160160 >
161161 <template #header >
162162 <UDashboardNavbar class="border-b-0 backdrop-blur lg:backdrop-blur-none absolute inset-x-0 z-5">
163- <template v-if =" chat . messages .length " #right >
163+ <template v-if =" messages .length " #right >
164164 <UTooltip text="Clear conversation">
165165 <UButton
166166 icon="i-lucide-list-x"
@@ -175,7 +175,7 @@ function getToolIcon(part: { state: string, toolName?: string, toolCallId?: stri
175175
176176 <template #body >
177177 <!-- Empty state: welcome screen -->
178- <div v-if =" !chat. messages.length" class =" flex-1 flex flex-col items-center justify-center gap-8 p-8" >
178+ <div v-if =" !messages.length" class =" flex-1 flex flex-col items-center justify-center gap-8 p-8" >
179179 <div class =" flex flex-col items-center gap-4" >
180180 <AppIconAnimated :size =" 340 " class="absolute -translate-y-1/2 top-1/2 opacity-15 text-highlighted -z-1 -mt-18" />
181181
@@ -192,13 +192,13 @@ function getToolIcon(part: { state: string, toolName?: string, toolCallId?: stri
192192 <div class =" w-full max-w-xl flex flex-col gap-6" >
193193 <ChatPrompt
194194 v-model =" input "
195- :status =" chat . status "
195+ :status =" status "
196196 :selected-model =" selectedModel "
197197 :disabled =" ! input .trim ()"
198198 class="[view-transition-name:chat-prompt ] p-2"
199199 @submit =" onSubmit "
200- @stop =" chat . stop ()"
201- @reload =" chat . regenerate ()"
200+ @stop =" stop ()"
201+ @reload =" regenerate ()"
202202 @update :selected-model =" onModelChange "
203203 />
204204
@@ -227,8 +227,8 @@ function getToolIcon(part: { state: string, toolName?: string, toolCallId?: stri
227227 } "
228228 >
229229 <UChatMessages
230- :messages =" chat . messages "
231- :status =" chat . status "
230+ :messages =" messages "
231+ :status =" status "
232232 should-auto-scroll
233233 :spacing-offset =" 160 "
234234 class="pt-(--ui-header-height) pb-4 sm:pb-6 px-2"
@@ -284,14 +284,14 @@ function getToolIcon(part: { state: string, toolName?: string, toolCallId?: stri
284284
285285 <ChatPrompt
286286 v-model =" input "
287- :status =" chat . status "
288- :error =" chat . error "
287+ :status =" status "
288+ :error =" error "
289289 :selected-model =" selectedModel "
290290 :disabled =" ! input .trim ()"
291291 class="sticky bottom-0 [view-transition-name:chat-prompt ] rounded-b-none z-10 p-2"
292292 @submit =" onSubmit "
293- @stop =" chat . stop ()"
294- @reload =" chat . regenerate ()"
293+ @stop =" stop ()"
294+ @reload =" regenerate ()"
295295 @update :selected-model =" onModelChange "
296296 />
297297 </UTheme >
0 commit comments