- 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
160 lines
7.1 KiB
Vue
160 lines
7.1 KiB
Vue
<template>
|
|
<div class="min-h-full bg-white text-gray-900 font-sans">
|
|
<nav class="border-b border-gray-200 bg-white sticky top-0 z-30">
|
|
<div class="max-w-6xl mx-auto px-6 py-4 flex items-center justify-between">
|
|
<div class="flex items-center gap-3">
|
|
<div class="w-8 h-8 rounded-lg bg-blue-600 flex items-center justify-center">
|
|
<span class="text-white text-sm font-bold">A</span>
|
|
</div>
|
|
<span class="text-lg font-semibold">Acme App</span>
|
|
</div>
|
|
<div class="flex items-center gap-6 text-sm text-gray-600">
|
|
<a href="#" class="hover:text-gray-900">Dashboard</a>
|
|
<a href="#" class="hover:text-gray-900">Projects</a>
|
|
<a href="#" class="hover:text-gray-900">Settings</a>
|
|
<RouterLink to="/" class="text-accent font-medium hover:underline">
|
|
← Back to AIUI
|
|
</RouterLink>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
<div class="max-w-6xl mx-auto px-6 py-12">
|
|
<div class="mb-12">
|
|
<h1 class="text-3xl font-bold mb-2">Widget Integration Demo</h1>
|
|
<p class="text-gray-500 max-w-2xl">
|
|
This page simulates a third-party web application with the AIUI chat widget embedded.
|
|
Click the floating button in the bottom corner to open the AI assistant.
|
|
You can also switch which side it appears on.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="grid md:grid-cols-2 lg:grid-cols-3 gap-6 mb-16">
|
|
<div v-for="n in 6" :key="n" class="bg-gray-50 rounded-2xl p-6 border border-gray-100">
|
|
<div class="w-10 h-10 rounded-xl bg-blue-100 text-blue-600 flex items-center justify-center mb-4">
|
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
|
</svg>
|
|
</div>
|
|
<h3 class="font-semibold mb-1">Project {{ n }}</h3>
|
|
<p class="text-sm text-gray-500 mb-3">
|
|
Sample content card demonstrating how the widget overlays existing UI without disruption.
|
|
</p>
|
|
<div class="flex items-center gap-2">
|
|
<span class="px-2.5 py-0.5 text-xs font-medium rounded-full bg-green-100 text-green-700">Active</span>
|
|
<span class="text-xs text-gray-400">Updated 2h ago</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bg-gray-50 rounded-2xl border border-gray-200 p-8 mb-16">
|
|
<h2 class="text-xl font-bold mb-4">How to embed AIUI in your app</h2>
|
|
<div class="space-y-6 text-sm text-gray-700">
|
|
<div>
|
|
<h3 class="font-semibold text-gray-900 mb-2">Option 1: Script Tag (simplest)</h3>
|
|
<pre class="bg-gray-900 text-gray-100 rounded-lg p-4 overflow-x-auto text-xs"><code><script src="https://cdn.example.com/aiui-widget.js"></script>
|
|
<script>
|
|
AIUI.init({
|
|
apiKey: 'your-openrouter-key',
|
|
position: 'bottom-right', // or 'bottom-left'
|
|
theme: 'dark',
|
|
})
|
|
</script></code></pre>
|
|
</div>
|
|
|
|
<div>
|
|
<h3 class="font-semibold text-gray-900 mb-2">Option 2: Web Component</h3>
|
|
<pre class="bg-gray-900 text-gray-100 rounded-lg p-4 overflow-x-auto text-xs"><code><!-- Load the custom element -->
|
|
<script type="module" src="https://cdn.example.com/aiui-element.js"></script>
|
|
|
|
<!-- Use anywhere in your HTML -->
|
|
<aiui-chat
|
|
api-key="your-key"
|
|
position="bottom-right"
|
|
theme="dark"
|
|
></aiui-chat></code></pre>
|
|
</div>
|
|
|
|
<div>
|
|
<h3 class="font-semibold text-gray-900 mb-2">Option 3: npm package (Vue/React/Svelte)</h3>
|
|
<pre class="bg-gray-900 text-gray-100 rounded-lg p-4 overflow-x-auto text-xs"><code>npm install @aiui/widget
|
|
|
|
// Vue 3
|
|
import { AIUIWidget } from '@aiui/widget'
|
|
app.use(AIUIWidget, {
|
|
apiKey: 'your-key',
|
|
position: 'bottom-right',
|
|
})
|
|
|
|
// React
|
|
import { AIUIProvider } from '@aiui/widget/react'
|
|
<AIUIProvider apiKey="your-key" position="bottom-right" /></code></pre>
|
|
</div>
|
|
|
|
<div>
|
|
<h3 class="font-semibold text-gray-900 mb-2">Option 4: iframe (maximum isolation)</h3>
|
|
<pre class="bg-gray-900 text-gray-100 rounded-lg p-4 overflow-x-auto text-xs"><code><iframe
|
|
src="https://app.aiui.dev/embed?key=your-key&theme=dark"
|
|
style="position:fixed;bottom:80px;right:24px;width:420px;height:640px;
|
|
border:none;border-radius:16px;z-index:9999;"
|
|
allow="microphone"
|
|
></iframe></code></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bg-gray-50 rounded-2xl border border-gray-200 p-8">
|
|
<h2 class="text-xl font-bold mb-4">Technical Architecture</h2>
|
|
<div class="grid md:grid-cols-2 gap-8 text-sm text-gray-700">
|
|
<div>
|
|
<h3 class="font-semibold text-gray-900 mb-2">Script Tag / npm</h3>
|
|
<ul class="space-y-1 list-disc list-inside">
|
|
<li>Injects a Vue micro-app into a shadow DOM container</li>
|
|
<li>Styles are encapsulated — no CSS conflicts with host</li>
|
|
<li>Communicates via CustomEvents or a global AIUI API object</li>
|
|
<li>Host app can pass context (current page, user info)</li>
|
|
<li>Smallest footprint: ~80KB gzipped</li>
|
|
</ul>
|
|
</div>
|
|
<div>
|
|
<h3 class="font-semibold text-gray-900 mb-2">Web Component</h3>
|
|
<ul class="space-y-1 list-disc list-inside">
|
|
<li>Framework-agnostic Custom Element (works in any HTML)</li>
|
|
<li>Shadow DOM encapsulation by default</li>
|
|
<li>Attributes for configuration, events for callbacks</li>
|
|
<li>Can be lazy-loaded with dynamic import()</li>
|
|
<li>Works in React, Angular, Svelte, plain HTML</li>
|
|
</ul>
|
|
</div>
|
|
<div>
|
|
<h3 class="font-semibold text-gray-900 mb-2">iframe</h3>
|
|
<ul class="space-y-1 list-disc list-inside">
|
|
<li>Maximum isolation — separate browsing context</li>
|
|
<li>Zero risk of CSS/JS conflicts</li>
|
|
<li>Communication via postMessage API</li>
|
|
<li>API key stays on embedded origin (more secure)</li>
|
|
<li>Slightly larger overhead, but simplest integration</li>
|
|
</ul>
|
|
</div>
|
|
<div>
|
|
<h3 class="font-semibold text-gray-900 mb-2">Host Communication</h3>
|
|
<ul class="space-y-1 list-disc list-inside">
|
|
<li>Host can send page context to AI for better answers</li>
|
|
<li>Widget can trigger actions in host app via callbacks</li>
|
|
<li>Conversation history syncs via encrypted IndexedDB</li>
|
|
<li>Deep-link support: <code class="bg-gray-200 px-1 rounded">?aiui=open&q=help</code></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<AIUIWidget />
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { RouterLink } from 'vue-router'
|
|
import AIUIWidget from '@/components/widget/AIUIWidget.vue'
|
|
</script>
|