Files
archy/packages/app/src/components/chat/ChatWindow.vue
T
Dorian ece4b8256f feat(app): enhance theme support and improve PWA integration
- Updated the app to support light and dark themes with appropriate CSS classes.
- Enhanced PWA configuration with manifest details and caching strategies.
- Improved the chat UI with dynamic theme adjustments for various components.
- Added new meta tags for better mobile web app experience.
- Refactored environment variables to include new Anthropic token.
- Updated package dependencies for better compatibility and performance.

Made-with: Cursor
2026-03-02 16:34:44 +00:00

124 lines
3.0 KiB
Vue

<template>
<div class="flex flex-col h-full rounded-2xl overflow-hidden transition-all duration-300">
<ChatHeader
:title="title"
:conversation-id="displayId"
:side="side"
:show-close="showClose"
@switch-side="$emit('switchSide')"
@new-chat="handleNewChat"
@close="$emit('close')"
/>
<div
ref="messageListRef"
class="flex-1 overflow-y-auto scrollbar-hide p-4 space-y-3"
>
<div v-if="messages.length === 0" class="flex items-center justify-center h-full">
<div class="text-center space-y-3 animate-fade-up">
<div class="w-16 h-16 rounded-2xl glass flex items-center justify-center mx-auto">
<span class="text-2xl"></span>
</div>
<p class="text-sm" :class="isDark ? 'text-white/30' : 'text-gray-400'">
Start a conversation
</p>
</div>
</div>
<ChatMessage
v-for="(msg, i) in messages"
:key="msg.id"
:message="msg"
:index="i"
/>
<StreamingDots v-if="isStreaming && lastMessageEmpty" />
</div>
<ChatInput
:disabled="isStreaming"
:placeholder="isStreaming ? 'Waiting for response...' : 'Message AIUI...'"
@send="handleSend"
/>
</div>
</template>
<script setup lang="ts">
import { computed, ref, watch, nextTick } from 'vue'
import { useChatStore } from '@/stores/chat'
import { useAI } from '@/composables/useAI'
import { useTheme } from '@/composables/useTheme'
import ChatHeader from './ChatHeader.vue'
import ChatMessage from './ChatMessage.vue'
import ChatInput from './ChatInput.vue'
import StreamingDots from './StreamingDots.vue'
withDefaults(
defineProps<{
variant?: 'standalone' | 'modal'
side?: 'left' | 'right'
showClose?: boolean
}>(),
{
variant: 'standalone',
side: 'right',
showClose: false,
}
)
defineEmits<{
switchSide: []
close: []
}>()
const chatStore = useChatStore()
const { sendMessage } = useAI()
const { isDark } = useTheme()
const messageListRef = ref<HTMLElement | null>(null)
const messages = computed(() => chatStore.messages)
const isStreaming = computed(() => chatStore.isStreaming)
const title = computed(
() => chatStore.activeConversation?.title ?? 'New Chat'
)
const displayId = computed(
() => chatStore.activeConversationId?.slice(0, 8) ?? '—'
)
const lastMessageEmpty = computed(() => {
const msgs = messages.value
if (msgs.length === 0) return true
return msgs[msgs.length - 1].content === ''
})
function handleNewChat() {
chatStore.createConversation()
}
async function handleSend(text: string) {
await sendMessage(text)
}
watch(
() => messages.value.length,
() => {
nextTick(() => {
const el = messageListRef.value
if (el) el.scrollTop = el.scrollHeight
})
}
)
watch(
() => messages.value[messages.value.length - 1]?.content,
() => {
nextTick(() => {
const el = messageListRef.value
if (el) el.scrollTop = el.scrollHeight
})
}
)
</script>