Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 18 additions & 14 deletions docs/app/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -58,24 +58,28 @@ provide('navigation', rootNavigation)
<Analytics />
<SpeedInsights />

<div :class="[route.path.startsWith('/docs/') && 'root']">
<template v-if="!route.path.startsWith('/examples')">
<!-- <Banner /> -->
<div class="flex">
<div class="flex-1" :class="[route.path.startsWith('/docs/') && 'root']">
<template v-if="!route.path.startsWith('/examples')">
<!-- <Banner /> -->

<Header />
</template>
<Header />
</template>

<NuxtLayout>
<NuxtPage />
</NuxtLayout>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>

<template v-if="!route.path.startsWith('/examples')">
<Footer />
<template v-if="!route.path.startsWith('/examples')">
<Footer />

<ClientOnly>
<Search :files="files" :navigation="navigationByFramework" />
</ClientOnly>
</template>
<ClientOnly>
<Search :files="files" :navigation="navigationByFramework" />
</ClientOnly>
</template>
</div>

<Chat />
</div>
</UApp>
</template>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import type { DefineComponent } from 'vue'
import { Chat } from '@ai-sdk/vue'
import type { UIMessage, UIToolInvocation } from 'ai'
import type { UIToolInvocation } from 'ai'
import { DefaultChatTransport } from 'ai'
import { splitByCase, upperFirst } from 'scule'
import ProseStreamPre from '../prose/PreStream.vue'
Expand All @@ -10,12 +10,7 @@ const components = {
pre: ProseStreamPre as unknown as DefineComponent
}

const messages = defineModel<UIMessage[]>('messages')
const fullscreen = defineModel<boolean>('fullscreen')

const emits = defineEmits<{
close: []
}>()
const { open, messages } = useChat()

const input = ref('')

Expand Down Expand Up @@ -54,13 +49,10 @@ function onSubmit() {
input.value = ''
}

function onClose(e: Event) {
e.preventDefault()

emits('close')
}
watch(messages, (newMessages) => {
if (newMessages === chat.messages) return

onMounted(() => {
chat.messages = newMessages
if (chat.lastMessage?.role === 'user') {
chat.regenerate()
}
Expand Down Expand Up @@ -99,56 +91,83 @@ const getCachedToolMessage = useMemoize((state: State, toolName: string, input:
</script>

<template>
<UChatPalette>
<UChatMessages
should-auto-scroll
:messages="chat.messages"
:status="chat.status"
:user="{ side: 'left', variant: 'naked', icon: 'i-lucide-user' }"
:assistant="{ icon: 'i-lucide-bot' }"
>
<template #content="{ message }">
<template v-for="(part, index) in message.parts" :key="`${message.id}-${part.type}-${index}${'state' in part ? `-${part.state}` : ''}`">
<MDCCached
v-if="part.type === 'text' && message.role === 'assistant'"
:value="part.text"
:cache-key="`${message.id}-${index}`"
:components="components"
:parser-options="{ highlight: false }"
class="[&_.my-5]:my-2.5 *:first:!mt-0 *:last:!mb-0 [&_.leading-7]:!leading-6"
/>
<p v-else-if="part.type === 'text' && message.role === 'user'" class="whitespace-pre-wrap">
{{ part.text }}
</p>

<p v-else-if="part.type === 'dynamic-tool'" class="text-muted text-sm leading-6 my-1.5">
{{ getCachedToolMessage(part.state, part.toolName, JSON.stringify(part.input || {})) }}
</p>
</template>
</template>
</UChatMessages>

<template #prompt>
<USidebar
v-model:open="open"
side="right"
collapsible="offcanvas"
title="AI Assistant"
close
close-icon="i-lucide-chevron-right"
:ui="{ footer: 'sm:px-4', container: 'sticky' }"
:style="{ '--sidebar-width': '20rem' }"
>
<template #body>
<UTheme
:ui="{
prose: {
p: { base: 'my-2.5 text-sm/6' },
li: { base: 'my-0.5 text-sm/6' },
ul: { base: 'my-2.5' },
ol: { base: 'my-2.5' },
h1: { base: 'text-xl mb-4' },
h2: { base: 'text-lg mt-6 mb-3' },
h3: { base: 'text-base mt-4 mb-2' },
h4: { base: 'text-sm mt-3 mb-1.5' },
code: { base: 'text-xs' },
pre: { root: 'my-2.5', base: 'text-xs/5' },
table: { root: 'my-2.5' },
hr: { base: 'my-5' }
}
}"
>
<UChatMessages
should-auto-scroll
:messages="chat.messages"
:status="chat.status"
compact
class="px-0"
:user="{ ui: { content: 'text-sm/6', container: 'pb-2.5' } }"
>
<template #content="{ message }">
<template v-for="(part, index) in message.parts" :key="`${message.id}-${part.type}-${index}${'state' in part ? `-${part.state}` : ''}`">
<MDCCached
v-if="part.type === 'text' && message.role === 'assistant'"
:value="part.text"
:cache-key="`${message.id}-${index}`"
:components="components"
:parser-options="{ highlight: false }"
class="*:first:mt-0! *:last:mb-0!"
/>
<p v-else-if="part.type === 'text' && message.role === 'user'" class="whitespace-pre-wrap">
{{ part.text }}
</p>

<p v-else-if="part.type === 'dynamic-tool'" class="text-muted text-sm leading-6 my-1.5">
{{ getCachedToolMessage(part.state, part.toolName, JSON.stringify(part.input || {})) }}
</p>
</template>
</template>
</UChatMessages>
</UTheme>
</template>

<template #footer>
<UChatPrompt
v-model="input"
icon="i-lucide-message-circle"
variant="naked"
:error="chat.error"
:ui="{ trailing: 'items-center' }"
size="sm"
:autoresize="open"
:ui="{ base: 'px-0' }"
@submit="onSubmit"
@close="onClose"
>
<template #trailing>
<UButton
:icon="fullscreen ? 'i-lucide-maximize' : 'i-lucide-minimize'"
color="neutral"
variant="ghost"
class="group"
:ui="{ leadingIcon: 'text-dimmed group-hover:text-muted transition' }"
@click="fullscreen = !fullscreen"
/>
</template>
<UChatPromptSubmit
color="neutral"
size="sm"
:status="chat.status"
@stop="chat.stop()"
@reload="chat.regenerate()"
/>
</UChatPrompt>
</template>
</UChatPalette>
</USidebar>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<script setup lang="ts">
import type { NavigationMenuItem } from '@nuxt/ui'

const open = ref(true)

const items: NavigationMenuItem[] = [{
label: 'Home',
icon: 'i-lucide-house',
active: true
}, {
label: 'Inbox',
icon: 'i-lucide-inbox',
badge: '4'
}, {
label: 'Contacts',
icon: 'i-lucide-users'
}]
</script>

<template>
<div class="flex h-full w-full [contain:paint]">
<USidebar
v-model:open="open"
collapsible="offcanvas"
title="Navigation"
close
:ui="{ container: 'absolute' }"
>
<UNavigationMenu
:items="items"
orientation="vertical"
/>
</USidebar>

<div class="flex-1 flex flex-col">
<div class="h-(--ui-header-height) shrink-0 flex items-center gap-2 px-4 border-b border-default">
<UButton
icon="i-lucide-panel-left"
color="neutral"
variant="ghost"
@click="open = !open"
/>

<span class="font-semibold text-sm">Page Title</span>
</div>

<div class="flex-1" />
</div>
</div>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<script setup lang="ts">
import type { NavigationMenuItem } from '@nuxt/ui'
const open = ref(true)
const items: NavigationMenuItem[] = [{
label: 'Home',
icon: 'i-lucide-house',
active: true
}, {
label: 'Inbox',
icon: 'i-lucide-inbox',
badge: '4'
}, {
label: 'Contacts',
icon: 'i-lucide-users'
}]
</script>

<template>
<div class="flex h-full w-full [contain:paint]">
<USidebar
v-model:open="open"
collapsible="offcanvas"
title="Navigation"
close
close-icon="i-lucide-arrow-left"
:ui="{ container: 'absolute' }"
>
<UNavigationMenu
:items="items"
orientation="vertical"
/>
</USidebar>

<div class="flex-1 flex flex-col">
<div class="h-(--ui-header-height) shrink-0 flex items-center gap-2 px-4 border-b border-default">
<UButton
icon="i-lucide-panel-left"
color="neutral"
variant="ghost"
@click="open = !open"
/>

<span class="font-semibold text-sm">Page Title</span>
</div>

<div class="flex-1" />
</div>
</div>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<script setup lang="ts">
import type { NavigationMenuItem } from '@nuxt/ui'

const open = ref(true)

const items: NavigationMenuItem[] = [{
label: 'Home',
icon: 'i-lucide-house',
active: true
}, {
label: 'Inbox',
icon: 'i-lucide-inbox',
badge: '4'
}, {
label: 'Contacts',
icon: 'i-lucide-users'
}]
</script>

<template>
<div class="flex h-full w-full [contain:paint]">
<USidebar
v-model:open="open"
collapsible="offcanvas"
title="Navigation"
description="Browse your workspace"
:ui="{ container: 'absolute' }"
>
<UNavigationMenu
:items="items"
orientation="vertical"
/>
</USidebar>

<div class="flex-1 flex flex-col">
<div class="h-(--ui-header-height) shrink-0 flex items-center gap-2 px-4 border-b border-default">
<UButton
icon="i-lucide-panel-left"
color="neutral"
variant="ghost"
@click="open = !open"
/>

<span class="font-semibold text-sm">Page Title</span>
</div>

<div class="flex-1" />
</div>
</div>
</template>
Loading
Loading