From 6c1b8e95c851c29022c8d256e632ccd9d5cb3596 Mon Sep 17 00:00:00 2001 From: imbytecat Date: Mon, 2 Mar 2026 05:23:47 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=8E=86=E5=8F=B2=E5=88=97=E8=A1=A8?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BA=A4=E9=94=99=E6=BB=91=E5=85=A5=E5=8A=A8?= =?UTF-8?q?=E7=94=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - renderHistory 改为索引循环,设置 CSS 变量 --i 实现交错延迟 - 最大交错层级限制为 10(400ms),避免过长等待 --- web/app.ts | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/web/app.ts b/web/app.ts index 55f4b28..a05ccc2 100644 --- a/web/app.ts +++ b/web/app.ts @@ -128,19 +128,19 @@ function connectWS(): void { micBtn.disabled = false; }; ws.onmessage = (e: MessageEvent) => handleServerMsg(e.data); -ws.onclose = () => { - state.connected = false; - state.ws = null; - micBtn.disabled = true; - if (state.recording) stopRecording(); - if (state.pendingStart) { - state.abortController?.abort(); - state.pendingStart = false; - micBtn.classList.remove("recording"); - } - setStatus("disconnected", "已断开"); - scheduleReconnect(); -}; + ws.onclose = () => { + state.connected = false; + state.ws = null; + micBtn.disabled = true; + if (state.recording) stopRecording(); + if (state.pendingStart) { + state.abortController?.abort(); + state.pendingStart = false; + micBtn.classList.remove("recording"); + } + setStatus("disconnected", "已断开"); + scheduleReconnect(); + }; ws.onerror = () => ws.close(); state.ws = ws; } @@ -201,11 +201,14 @@ function showToast(msg: string): void { const toast = q("#toast"); toast.textContent = msg; toast.classList.add("show"); - const timer = (toast as HTMLElement & { _timer?: ReturnType })._timer; + const timer = ( + toast as HTMLElement & { _timer?: ReturnType } + )._timer; if (timer) clearTimeout(timer); - (toast as HTMLElement & { _timer?: ReturnType })._timer = setTimeout(() => { - toast.classList.remove("show"); - }, 2000); + (toast as HTMLElement & { _timer?: ReturnType })._timer = + setTimeout(() => { + toast.classList.remove("show"); + }, 2000); } // ── Audio pipeline ── async function initAudio(): Promise { @@ -331,8 +334,10 @@ function renderHistory(): void { return; } (historyEmpty as HTMLElement).style.display = "none"; - for (const item of items) { + for (let i = 0; i < items.length; i++) { + const item = items[i]; const li = document.createElement("li"); + li.style.setProperty("--i", String(Math.min(i, 10))); li.innerHTML = `${escapeHtml(item.text)}` + `${formatTime(item.ts)}`;