Update web client layout
This commit is contained in:
@@ -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">
|
||||
<h2>Event Log</h2>
|
||||
<div class="log" id="log"></div>
|
||||
<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 = "";
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user