Update web client layout

This commit is contained in:
Xin Wang
2026-02-06 10:34:09 +08:00
parent 876ca8221c
commit 5c03cf2b1f

View File

@@ -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;
}
}
</style>
</head>
@@ -314,9 +344,15 @@
</div>
</section>
<section class="panel stack">
<section class="stack">
<div class="panel stack">
<h2>Chat History</h2>
<div class="chat" id="chatHistory"></div>
</div>
<div class="panel stack">
<h2>Event Log</h2>
<div class="log" id="log"></div>
</div>
</section>
</main>
@@ -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 = "";
});