feat: implement full UPDATE.md review — critical fixes, UI upgrade, infra hardening
Critical frontend bugs: - Add TrackSubscribed/attach() for agent audio playback - Fix decodeToString TypeError with TextDecoder - XSS fix: innerHTML -> textContent in addMessage - Fresh token on reconnect retry Agent fixes: - GemmaLLM subclass with reasoning_content fallback wrapper - Disable Gemma 4 thinking mode via chat_template_kwargs (6.8s -> 0.5s) - Remove duplicate session-level LLM - Replace global _active_session with closure-based handler - asyncio.create_task instead of deprecated get_event_loop - Explicit silero VAD, topic filter on voice-control Infra: - supervisord: all programs log to /dev/stdout - Dockerfile: uv sync --frozen with committed uv.lock - nginx config moved to real file, token_server.py no longer served - entrypoint.sh: cert persisted, only regenerated on IP change - compose: healthcheck + cert volume - token_server: CORS removed, room pinned to voice-room UI upgrade: - Orb UI with state machine (idle/connecting/listening/thinking/speaking) - Streaming transcripts via lk.transcription text streams - Barge-in hint, thinking chip, audio visualizer - Glassmorphism, chat bubbles, settings sheet, light mode - PWA manifest, favicon, wake-lock, safe-area insets - localStorage conversation history Docs: AGENTS.md drift fixed
This commit is contained in:
+37
-21
@@ -2,8 +2,11 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||||
<meta name="theme-color" content="#09090b">
|
||||
<title>Voice Assistant</title>
|
||||
<link rel="manifest" href="manifest.json">
|
||||
<link rel="icon" type="image/svg+xml" href="favicon.svg">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
@@ -13,38 +16,51 @@
|
||||
<p class="subtitle">Azure Speech + Gemma LLM, real-time conversation</p>
|
||||
</header>
|
||||
|
||||
<div class="orb-stage">
|
||||
<div class="orb" id="orb" data-state="idle">
|
||||
<div class="orb-core"></div>
|
||||
<div class="orb-ring"></div>
|
||||
</div>
|
||||
<div class="orb-labels">
|
||||
<span class="state-label" id="stateLabel">Idle</span>
|
||||
<span class="thinking-chip" id="thinkingChip" hidden>thinking…</span>
|
||||
<span class="barge-hint" id="bargeHint" hidden>tap or speak to interrupt</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<button id="startBtn" class="btn btn-primary">Start Conversation</button>
|
||||
<button id="stopBtn" class="btn btn-danger" disabled>Stop</button>
|
||||
</div>
|
||||
|
||||
<div class="voice-panel">
|
||||
<label for="voiceSelect">Voice:</label>
|
||||
<select id="voiceSelect">
|
||||
<option value="en-US-AvaNeural" selected>Ava (DragonHD)</option>
|
||||
<option value="en-US-JennyNeural">Jenny</option>
|
||||
<option value="en-US-GuyNeural">Guy</option>
|
||||
<option value="en-US-AndrewNeural">Andrew</option>
|
||||
<option value="en-US-AriaNeural">Aria</option>
|
||||
<option value="en-US-EmmaNeural">Emma</option>
|
||||
<option value="en-US-EricNeural">Eric</option>
|
||||
<option value="en-US-BrianNeural">Brian</option>
|
||||
<option value="en-US-AshleyNeural">Ashley</option>
|
||||
<option value="en-US-RichardNeural">Richard</option>
|
||||
<option value="en-US-TinaNeural">Tina</option>
|
||||
<option value="en-US-SteffanNeural">Steffan</option>
|
||||
</select>
|
||||
<button id="settingsBtn" class="btn btn-icon" aria-label="Voice settings" title="Voice settings">
|
||||
<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><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-.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-.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.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>
|
||||
</div>
|
||||
|
||||
<div class="status" id="status">Disconnected</div>
|
||||
<div class="status" id="micLevel" style="font-size: 0.8em;"></div>
|
||||
<div class="mic-meter" id="micLevel" aria-hidden="true"><span class="mic-meter-fill" id="micMeterFill"></span></div>
|
||||
|
||||
<div class="transcript" id="transcript">
|
||||
<div class="transcript-header">Conversation</div>
|
||||
<div class="transcript-header">
|
||||
<span>Conversation</span>
|
||||
<button id="clearBtn" class="btn btn-ghost" title="Clear conversation">Clear</button>
|
||||
</div>
|
||||
<div id="messages"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Settings sheet (voice picker) -->
|
||||
<div class="sheet-backdrop" id="sheetBackdrop" hidden></div>
|
||||
<div class="settings-sheet" id="settingsSheet" role="dialog" aria-modal="true" aria-label="Voice settings">
|
||||
<div class="sheet-handle"></div>
|
||||
<div class="sheet-header">
|
||||
<h2>Voice</h2>
|
||||
<button id="closeSheetBtn" class="btn btn-ghost" aria-label="Close">
|
||||
<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M18 6 6 18M6 6l12 12"></path></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="voice-list" id="voiceList"></div>
|
||||
</div>
|
||||
|
||||
<script src="livekit-client.umd.js"></script>
|
||||
<script src="app.js"></script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user