From 5c03cf2b1f89935d9156a85d6d54413cea5f19d6 Mon Sep 17 00:00:00 2001 From: Xin Wang Date: Fri, 6 Feb 2026 10:34:09 +0800 Subject: [PATCH] Update web client layout --- examples/web_client.html | 84 +++++++++++++++++++++++++++++++++++----- 1 file changed, 74 insertions(+), 10 deletions(-) diff --git a/examples/web_client.html b/examples/web_client.html index 05edb2a..02ab70e 100644 --- a/examples/web_client.html +++ b/examples/web_client.html @@ -192,7 +192,7 @@ } .log { - height: 520px; + height: 320px; overflow: auto; padding: 12px; background: #0d0d14; @@ -202,6 +202,33 @@ line-height: 1.4; } + .chat { + height: 260px; + overflow: auto; + padding: 12px; + background: #0d0d14; + border-radius: 12px; + border: 1px solid var(--grid); + font-size: 0.9rem; + line-height: 1.45; + } + + .chat-entry { + padding: 8px 10px; + margin-bottom: 8px; + border-radius: 10px; + background: rgba(255, 255, 255, 0.04); + border: 1px solid rgba(255, 255, 255, 0.06); + } + + .chat-entry.user { + border-left: 3px solid var(--accent-2); + } + + .chat-entry.ai { + border-left: 3px solid var(--good); + } + .log-entry { padding: 6px 8px; border-bottom: 1px dashed rgba(255, 255, 255, 0.06); @@ -256,6 +283,9 @@ .log { height: 360px; } + .chat { + height: 260px; + } } @@ -314,9 +344,15 @@ -
-

Event Log

-
+
+
+

Chat History

+
+
+
+

Event Log

+
+
@@ -342,6 +378,7 @@ const clearLogBtn = document.getElementById("clearLogBtn"); const chatInput = document.getElementById("chatInput"); const logEl = document.getElementById("log"); + const chatHistory = document.getElementById("chatHistory"); const statusDot = document.getElementById("statusDot"); const statusText = document.getElementById("statusText"); const statusSub = document.getElementById("statusSub"); @@ -381,12 +418,35 @@ logEl.scrollTop = logEl.scrollHeight; } + function addChat(role, text) { + const entry = document.createElement("div"); + entry.className = `chat-entry ${role === "AI" ? "ai" : "user"}`; + entry.textContent = `${role}: ${text}`; + chatHistory.appendChild(entry); + chatHistory.scrollTop = chatHistory.scrollHeight; + } + function setStatus(connected, detail) { statusDot.classList.toggle("on", connected); statusText.textContent = connected ? "Connected" : "Disconnected"; statusSub.textContent = detail || ""; } + async function ensureAudioContext() { + if (audioCtx) return; + audioCtx = new (window.AudioContext || window.webkitAudioContext)(); + playbackDest = audioCtx.createMediaStreamDestination(); + audioOut.srcObject = playbackDest.stream; + try { + await audioOut.play(); + } catch (err) { + logLine("sys", "Audio playback blocked (user gesture needed)", { err: String(err) }); + } + if (outputSelect.value) { + await setOutputDevice(outputSelect.value); + } + } + function downsampleBuffer(buffer, inRate, outRate) { if (outRate === inRate) return buffer; const ratio = inRate / outRate; @@ -443,6 +503,7 @@ ws.onopen = () => { setStatus(true, "Session open"); logLine("sys", "WebSocket connected"); + ensureAudioContext(); }; ws.onclose = () => { @@ -486,6 +547,12 @@ function handleEvent(event) { const type = event.event || "unknown"; logLine("event", type, event); + if (type === "transcript" && event.isFinal && event.text) { + addChat("You", event.text); + } + if (type === "llmResponse" && event.isFinal && event.text) { + addChat("AI", event.text); + } if (type === "trackStart") { discardAudio = false; playbackTime = audioCtx ? audioCtx.currentTime : 0; @@ -500,12 +567,7 @@ logLine("sys", "Connect before starting mic"); return; } - if (!audioCtx) { - audioCtx = new AudioContext(); - playbackDest = audioCtx.createMediaStreamDestination(); - audioOut.srcObject = playbackDest.stream; - await audioOut.play().catch(() => {}); - } + await ensureAudioContext(); const deviceId = inputSelect.value || undefined; micStream = await navigator.mediaDevices.getUserMedia({ audio: deviceId ? { deviceId: { exact: deviceId } } : true, @@ -595,6 +657,8 @@ sendChatBtn.addEventListener("click", () => { const text = chatInput.value.trim(); if (!text) return; + ensureAudioContext(); + addChat("You", text); sendCommand({ command: "chat", text }); chatInput.value = ""; });