feat(app): glassmorphism chat UI with widget embed system
- Glassmorphism chat window matching Proux design rules (glass layers, gradient backgrounds, glow effects, blur intensities, inner glows) - Conversation ID displayed in header, side-switching (left/right layout) - Chat store with Pinia: conversations, messages, streaming state - OpenRouter AI integration with SSE streaming (Llama 4 Maverick free model) - Chat components: ChatWindow, ChatHeader, ChatInput, ChatMessage, StreamingDots (typing indicator) - Embeddable widget system: floating action button (FAB) + modal popup with scale-in animation, side-aware positioning - Widget demo page (/widget-demo) showing AIUI embedded in a mock "Acme App" with documentation of 4 integration methods: script tag, web component, npm package, and iframe - Theme composable with dark/light mode, system preference detection - Custom CSS: glass variants (subtle/medium/strong/light/dark), glow effects, gradient text, animation keyframes, thin scrollbar - Responsive: mobile-first, 44px touch targets, dvh viewport Made-with: Cursor
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<div
|
||||
class="flex animate-fade-up"
|
||||
:class="isUser ? 'justify-end' : 'justify-start'"
|
||||
:style="{ animationDelay: `${index * 30}ms` }"
|
||||
>
|
||||
<div
|
||||
class="max-w-[85%] md:max-w-[70%] rounded-2xl px-4 py-3 transition-all duration-200"
|
||||
:class="bubbleClasses"
|
||||
>
|
||||
<p class="text-sm leading-relaxed whitespace-pre-wrap break-words">{{ message.content }}</p>
|
||||
<div class="flex items-center gap-2 mt-1.5">
|
||||
<span class="text-[10px] opacity-40 select-none">{{ formattedTime }}</span>
|
||||
<span v-if="isUser && message.status" class="text-[10px] opacity-30">
|
||||
{{ message.status === 'sent' ? '✓' : message.status === 'delivered' ? '✓✓' : '' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import type { Message } from '@aiui/core/types/message'
|
||||
|
||||
const props = defineProps<{
|
||||
message: Message
|
||||
index: number
|
||||
}>()
|
||||
|
||||
const isUser = computed(() => props.message.role === 'user')
|
||||
|
||||
const bubbleClasses = computed(() =>
|
||||
isUser.value
|
||||
? 'glass-strong text-white rounded-br-md'
|
||||
: 'glass-dark text-gray-100 rounded-bl-md'
|
||||
)
|
||||
|
||||
const formattedTime = computed(() => {
|
||||
const d = new Date(props.message.timestamp)
|
||||
return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
|
||||
})
|
||||
</script>
|
||||
Reference in New Issue
Block a user