gemma-avatar / index.html
bep40's picture
Upload index.html
032c98c verified
Raw
History Blame Contribute Delete
21.7 kB
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<!-- Page Title (keyword-rich, action-oriented) -->
<title>Gemma Avatar + V.AI STUDIO -- Real-Time 3D Chat &amp; 8000+ Products</title>
<!-- SEO: Canonical URL -->
<link rel="canonical" href="https://huggingface.co/spaces/bep40/gemma-avatar" />
<!-- SEO: Meta Description (155 chars, unique, actionable) -->
<meta name="description" content="Gemma Avatar + V.AI STUDIO: Talk to Gemma 4 with a 3D talking-head avatar and real-time lip-sync. Search 8000+ kitchen appliances &amp; smart locks from Malloca, Eurogold, Grob, Canzy, Demax." />
<!-- SEO: Keywords -->
<meta name="keywords" content="Gemma Avatar, V.AI STUDIO, 3D avatar, lip-sync, talking head, voice AI, Gemma 4, real-time chat, kitchen appliances, smart locks, Malloca, Eurogold, Grob, Canzy, Demax" />
<!-- SEO: Author & Publisher -->
<meta name="author" content="bep40" />
<meta name="publisher" content="Hugging Face" />
<!-- Open Graph (Facebook, LinkedIn, Discord, WhatsApp) -->
<meta property="og:type" content="website" />
<meta property="og:locale" content="en_US" />
<meta property="og:locale:alternate" content="vi_VN" />
<meta property="og:site_name" content="Hugging Face Spaces" />
<meta property="og:title" content="Gemma Avatar + V.AI STUDIO -- 3D Talking Avatar &amp; 8000+ Products" />
<meta property="og:description" content="Talk to Gemma 4 face-to-face with a 3D talking-head avatar and real-time lip-sync. Search 8000+ kitchen appliances &amp; smart locks from Malloca, Eurogold, Grob, Canzy, Demax." />
<meta property="og:url" content="https://huggingface.co/spaces/bep40/gemma-avatar" />
<meta property="og:image" content="https://huggingface.co/spaces/bep40/gemma-avatar/resolve/main/thumbnail.webp" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:image:alt" content="Gemma Avatar + V.AI STUDIO -- Real-Time 3D Talking Avatar and 8000+ Product Search" />
<meta property="og:image:type" content="image/webp" />
<!-- Twitter Card (summary_large_image) -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@huggingface" />
<meta name="twitter:creator" content="@bep40" />
<meta name="twitter:title" content="Gemma Avatar + V.AI STUDIO -- 3D Talking Avatar &amp; 8000+ Products" />
<meta name="twitter:description" content="Talk to Gemma 4 face-to-face with a 3D talking-head avatar and real-time lip-sync. Search 8000+ kitchen appliances &amp; smart locks." />
<meta name="twitter:image" content="https://huggingface.co/spaces/bep40/gemma-avatar/resolve/main/thumbnail.webp" />
<meta name="twitter:image:alt" content="Gemma Avatar + V.AI STUDIO -- Real-Time 3D Talking Avatar and 8000+ Product Search" />
<!-- SEO: Robots -->
<meta name="robots" content="index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1" />
<!-- SEO: Language -->
<link rel="alternate" hreflang="en" href="https://huggingface.co/spaces/bep40/gemma-avatar" />
<link rel="alternate" hreflang="vi" href="https://huggingface.co/spaces/bep40/gemma-avatar" />
<meta name="language" content="English, Vietnamese" />
<!-- SEO: Theme & App -->
<meta name="theme-color" content="#4f46e5" />
<meta name="msapplication-TileColor" content="#4f46e5" />
<meta name="apple-mobile-web-app-title" content="Gemma Avatar" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Geist+Mono:wght@500&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" />
<link rel="stylesheet" href="./src/style.css" />
</head>
<body>
<main id="app">
<div id="stage" aria-label="3D avatar"></div>
<div id="loading">Loading avatar…</div>
<header id="topbar">
<div id="identity">
<h1>Gemma&nbsp;Avatar + V.AI STUDIO</h1>
<p>Chat or talk to <strong>Gemma&nbsp;4</strong>. Text → Qwen3-TTS → lip-sync avatar. Control V.AI STUDIO catalog.</p>
</div>
<button id="settings-btn" class="icon-btn" aria-label="Settings" title="Settings">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<circle cx="12" cy="12" r="3"></circle>
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06.06a2 2 0 1 1-2.83 2.83l-.06.06a2 2 0 1 1-2.83 2.83l-.06.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83 2.83l-.06.06a2 2 0 1 1-2.83 2.83l-.06.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06.06a2 2 0 1 1-2.83 2.83l-.06.06a2 2 0 1 1-2.83 2.83l-.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06.06a2 2 0 1 1-2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0-1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"></path>
</svg>
</button>
</header>
<div id="subtitles" aria-live="polite"></div>
<div id="text-chat" hidden>
<div id="chat-header">
<span id="chat-title">Text Chat</span>
<div id="chat-header-actions">
<select id="chat-avatar-select" title="Switch avatar"></select>
<button id="chat-close-btn" class="icon-btn small" aria-label="Close text chat" title="Close">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
</button>
</div>
</div>
<div id="chat-messages"></div>
<div id="chat-input-container">
<input id="chat-input" type="text" placeholder="Type a message..." autocomplete="off" />
<button id="chat-send-btn" class="icon-btn" aria-label="Send message">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>
</svg>
</button>
</div>
<div id="chat-resize-handle"></div>
</div>
<footer id="controls">
<div id="caption">LOADING…</div>
<div id="buttons">
<button id="main-btn" disabled><span id="main-btn-label">Loading…</span></button>
<button id="mute-btn" class="icon-btn" hidden aria-label="Mute microphone" title="Mute microphone">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"></path>
<path d="M19 10v2a7 7 0 0 1-14 0v-2"></path><line x1="12" y1="19" x2="12" y2="23"></line><line x1="8" y1="23" x2="16" y2="23"></line>
</svg>
</button>
<button id="text-mode-btn" class="icon-btn" hidden aria-label="Text chat mode" title="Text chat mode">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
</svg>
</button>
</div>
</footer>
<dialog id="settings">
<form method="dialog">
<h2>Settings</h2>
<label>Voice<select id="voice"></select></label>
<label>Avatar model<select id="settings-avatar-select"></select></label>
<label class="check-row"><input type="checkbox" id="subtitles-toggle" />Show subtitles while she speaks</label>
<label>Extra instructions<textarea id="instructions" rows="4" placeholder="Anything you want her to know or do (persona stays)."></textarea></label>
<label id="direct-url-row" hidden>Direct server URL<input id="direct-url" type="url" placeholder="ws://localhost:8080/v1/realtime" spellcheck="false" /></label>
<div class="dialog-actions"><button value="close" class="primary">Done</button></div>
</form>
</dialog>
</main>
<!-- V.AISTUDIO floating button -->
<button id="vaistudio-toggle" aria-label="Open V.AI STUDIO catalog" title="Open V.AI STUDIO catalog" style="position:fixed;bottom:24px;right:24px;z-index:180;width:56px;height:56px;border-radius:50%;background:linear-gradient(135deg,#003f62,#0077b6);color:#fff;border:none;cursor:pointer;box-shadow:0 4px 20px rgba(0,63,98,0.4);display:flex;align-items:center;justify-content:center;font-size:1.4rem;transition:all 0.25s ease" onmouseover="this.style.transform='scale(1.1)';this.style.boxShadow='0 6px 28px rgba(0,63,98,0.5)'" onmouseout="this.style.transform='scale(1)';this.style.boxShadow='0 4px 20px rgba(0,63,98,0.4)'">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path><polyline points="17 8 12 13 7 8"></polyline></svg>
</button>
<!-- V.AISTUDIO Panel -->
<div id="vaistudio-panel" role="dialog" aria-label="V.AI STUDIO Catalog" style="position:fixed;bottom:0;right:0;width:400px;max-width:100vw;height:560px;max-height:calc(100vh - 48px);background:#fff;border-radius:20px 20px 0 0;box-shadow:0 8px 40px rgba(0,0,0,0.18);z-index:160;display:none;flex-direction:column;overflow:hidden">
<div id="vaistudio-header" style="background:linear-gradient(135deg,#003f62,#0077b6);color:#fff;padding:14px 16px;display:flex;align-items:center;gap:12px;flex-shrink:0">
<div style="width:38px;height:38px;border-radius:12px;background:rgba(255,255,255,0.15);display:flex;align-items:center;justify-content:center;font-size:1.1rem;flex-shrink:0"><i class="fas fa-store"></i></div>
<div style="flex:1;min-width:0">
<h4 style="font-size:0.9rem;font-weight:700;margin:0">V.AI STUDIO</h4>
<span id="vaistudio-count" style="font-size:0.68rem;opacity:0.75">Đang tải sản phẩm…</span>
</div>
<button id="vaistudio-close" aria-label="Close" style="width:40px;height:40px;min-width:40px;min-height:40px;border-radius:50%;background:rgba(255,255,255,0.25);border:none;color:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:1.1rem;transition:all 0.2s" onmouseover="this.style.background='rgba(255,255,255,0.4)'" onmouseout="this.style.background='rgba(255,255,255,0.25)'">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
</button>
</div>
<!-- Search + Suggestions dropdown -->
<div style="padding:8px 12px 4px;flex-shrink:0;background:#f8fafc;border-bottom:1px solid #e2e8f0;position:relative">
<input id="vaix-search" type="text" placeholder="🔍 Tìm sản phẩm..." autocomplete="off" style="width:100%;padding:10px 14px;border:1px solid #e2e8f0;border-radius:10px;font-size:0.85rem;outline:none;background:#fff;box-sizing:border-box" onfocus="this.style.borderColor='#0077b6'" onblur="this.style.borderColor='#e2e8f0'" />
<div id="vaix-suggestions" style="position:absolute;top:100%;left:12px;right:12px;z-index:50;background:#fff;border:1px solid #e2e8f0;border-radius:10px;box-shadow:0 8px 24px rgba(0,0,0,0.12);display:none;max-height:360px;overflow-y:auto"></div>
</div>
<div id="vaix-category-chips" style="padding:4px 12px 8px;flex-shrink:0;background:#f8fafc;border-bottom:1px solid #e2e8f0;display:none;flex-wrap:wrap;gap:4px;overflow-x:auto"></div>
<div id="vaistudio-body" style="flex:1;overflow:hidden;position:relative;background:#f8fafc">
<div id="vaistudio-loading" style="position:absolute;inset:0;background:#fff;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;z-index:10">
<div style="width:32px;height:32px;border:3px solid #e2e8f0;border-top-color:#003f62;border-radius:50%;animation:spin 1s linear infinite"></div>
<div style="font-size:0.85rem;color:#64748b">Đang tải sản phẩm V.AI STUDIO…</div>
</div>
<div id="vaistudio-products" style="overflow-y:auto;height:100%;padding:12px;display:none"></div>
<div id="vaistudio-error" style="position:absolute;inset:0;background:#fff;display:none;flex-direction:column;align-items:center;justify-content:center;gap:12px;padding:24px;text-align:center">
<div style="font-size:2rem;margin-bottom:8px">⚠️</div>
<div id="vaistudio-error-text" style="font-size:0.9rem;color:#64748b">Không thể tải sản phẩm</div>
<button id="vaistudio-retry-btn" style="padding:8px 20px;background:#003f62;color:#fff;border:none;border-radius:8px;cursor:pointer;font-size:0.85rem">Thử lại</button>
</div>
</div>
</div>
<!-- Product Detail Modal -->
<div id="vaistudio-detail-overlay" style="position:fixed;inset:0;background:rgba(0,0,0,0.5);z-index:200;display:none;align-items:center;justify-content:center;padding:20px" onclick="if(event.target===this)this.style.display='none'">
<div id="vaistudio-detail-modal" style="background:#fff;border-radius:20px;max-width:480px;width:100%;max-height:85vh;overflow-y:auto;padding:0;box-shadow:0 20px 60px rgba(0,0,0,0.25);animation:modalIn 0.2s ease">
<div style="position:sticky;top:0;z-index:2;display:flex;justify-content:flex-end;padding:12px" onclick="document.getElementById('vaistudio-detail-overlay').style.display='none'">
<button style="width:36px;height:36px;border-radius:50%;border:none;background:rgba(0,0,0,0.06);cursor:pointer;font-size:1.1rem;display:flex;align-items:center;justify-content:center"></button>
</div>
<div id="detail-images" style="display:flex;gap:8px;overflow-x:auto;padding:0 16px 12px;scroll-snap-type:x mandatory;-webkit-overflow-scrolling:touch"></div>
<div style="padding:0 16px 12px;border-bottom:1px solid #e2e8f0">
<h2 id="detail-title" style="font-size:1.05rem;font-weight:700;color:#1e293b;margin:0 0 6px;line-height:1.4"></h2>
<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap">
<span id="detail-brand" style="font-size:0.75rem;color:#64748b;background:#f1f5f9;padding:3px 10px;border-radius:8px"></span>
<span id="detail-model" style="font-size:0.7rem;color:#94a3b8"></span>
</div>
<div id="detail-price" style="font-size:1.2rem;font-weight:800;color:#c2410c;margin-top:8px"></div>
</div>
<div id="detail-summary" style="padding:12px 16px;font-size:0.82rem;color:#475569;line-height:1.6;border-bottom:1px solid #f1f5f9;display:none"></div>
<div id="detail-features" style="padding:12px 16px;border-bottom:1px solid #f1f5f9;display:none">
<h3 style="font-size:0.8rem;font-weight:700;color:#003f62;margin:0 0 8px;text-transform:uppercase;letter-spacing:0.04em">Tính năng nổi bật</h3>
<ul id="detail-features-list" style="margin:0;padding:0;list-style:none"></ul>
</div>
<div id="detail-specs" style="padding:12px 16px 20px;display:none">
<h3 style="font-size:0.8rem;font-weight:700;color:#003f62;margin:0 0 8px;text-transform:uppercase;letter-spacing:0.04em">Thông số kỹ thuật</h3>
<table id="detail-specs-table" style="width:100%;border-collapse:collapse;font-size:0.78rem"><tbody></tbody></table>
</div>
<!-- Section sản phẩm tương tự và share button được render động bởi vaix-rag.js -->
</div>
</div>
<style>
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes modalIn { from { opacity:0;transform:translateY(20px) scale(0.96) } to { opacity:1;transform:translateY(0) scale(1) } }
#vaistudio-panel.open { display: flex !important; }
#vaistudio-loading.hidden { display: none !important; }
@media (max-width:768px) { #vaistudio-toggle { bottom:16px;right:16px;width:50px;height:50px } #vaistudio-panel { width:100%;max-width:100%;height:100%;max-height:100vh;border-radius:0 } }
.product-card { display:flex;gap:12px;padding:12px;background:#fff;border-radius:12px;margin-bottom:8px;border:1px solid #e2e8f0;cursor:pointer;transition:all 0.2s }
.product-card:hover { border-color:#0077b6;box-shadow:0 2px 8px rgba(0,63,98,0.12) }
.product-card-img { width:80px;height:80px;border-radius:8px;object-fit:cover;background:#f1f5f9;flex-shrink:0 }
.product-card-info { flex:1;min-width:0 }
.product-card-title { font-size:0.85rem;font-weight:600;color:#1e293b;margin:0 0 4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap }
.product-card-brand { font-size:0.75rem;color:#64748b;margin:0 0 4px }
.product-card-price { font-size:0.85rem;font-weight:700;color:#003f62;margin:0 }
.product-card-cat { font-size:0.7rem;color:#94a3b8;background:#f1f5f9;padding:2px 8px;border-radius:10px;display:inline-block;margin-top:4px }
/* ── Search suggestions ── */
.vaix-suggestion-item { display:flex;align-items:center;gap:10px;padding:10px 12px;cursor:pointer;border-bottom:1px solid #f1f5f9;transition:background 0.15s }
.vaix-suggestion-item:last-child { border-bottom:none }
.vaix-suggestion-item:hover { background:#f0f9ff }
#vaix-suggestions::-webkit-scrollbar { width:5px }
#vaix-suggestions::-webkit-scrollbar-thumb { background:#e2e8f0;border-radius:3px }
/* ── Category chips ── */
.vaix-category-chip { display:inline-flex;align-items:center;padding:4px 10px;background:#eef2ff;color:#003f62;border:1px solid #d0dfed;border-radius:14px;font-size:0.7rem;cursor:pointer;white-space:nowrap;transition:all 0.2s;font-family:inherit }
.vaix-category-chip:hover { background:#dbeafe;border-color:#0077b6;transform:translateY(-1px) }
/* ── Chat Product Cards ── */
.chat-product-cards { display:flex;flex-direction:column;gap:8px;margin:6px 0;max-width:100% }
.chat-product-card { background:#1e293b;border:1px solid #334155;border-radius:12px;overflow:hidden;transition:all 0.2s }
.chat-product-card:hover { border-color:#0077b6;box-shadow:0 2px 8px rgba(0,119,182,0.15) }
.chat-product-card-inner { display:flex;gap:10px;padding:10px;cursor:pointer }
.chat-product-card-img-wrap { width:64px;height:64px;border-radius:8px;overflow:hidden;flex-shrink:0;background:#1a1f2e }
.chat-product-card-img { width:100%;height:100%;object-fit:cover }
.chat-product-card-img-placeholder { width:100%;height:100%;display:flex;align-items:center;justify-content:center;font-size:1.3rem }
.chat-product-card-body { flex:1;min-width:0;display:flex;flex-direction:column;gap:2px }
.chat-product-card-name { font-size:0.8rem;font-weight:600;color:#e2e8f0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.3 }
.chat-product-card-meta { display:flex;align-items:center;gap:6px;flex-wrap:wrap }
.chat-product-card-brand { font-size:0.68rem;color:#94a3b8 }
.chat-product-card-stars { font-size:0.6rem;color:#f59e0b;letter-spacing:1px }
.chat-product-card-price { font-size:0.82rem;font-weight:700;color:#22d3ee;margin-top:1px }
.chat-product-card-cat { font-size:0.6rem;color:#64748b;background:#2d3748;padding:1px 6px;border-radius:4px;display:inline-block;width:fit-content;margin-top:1px }
.chat-product-card-actions { display:flex;gap:0;border-top:1px solid #334155 }
.chat-product-card-btn { flex:1;padding:7px 8px;background:transparent;border:none;color:#94a3b8;cursor:pointer;font-size:0.7rem;font-family:inherit;transition:all 0.15s;display:flex;align-items:center;justify-content:center;gap:4px }
.chat-product-card-btn:hover { background:#2d3748;color:#e2e8f0 }
.chat-product-card-btn:first-child { border-right:1px solid #334155 }
.chat-product-card-btn.detail-btn:hover { color:#22d3ee }
.chat-product-card-btn.share-btn:hover { color:#f59e0b }
.chat-message.assistant .chat-product-card { background:#1e293b;border:1px solid #334155;margin:4px 0 }
/* ── Share button inline style overrides for dark theme ── */
#detail-share-btn { display:inline-flex!important }
</style>
<!-- V.AI STUDIO RAG Module - loads products + integrates search/showProduct -->
<script src="./src/vaix-rag.js"></script>
<script type="module" src="./src/app.js"></script>
</body>
</html>