Update voice demo chat id handling

This commit is contained in:
Xin Wang
2026-06-01 09:44:44 +08:00
parent d62c564a0d
commit 0913f8e655
3 changed files with 108 additions and 14 deletions

View File

@@ -45,6 +45,7 @@ function defaultWsUrl() {
const els = {
url: document.getElementById("ws-url"),
chatId: document.getElementById("chat-id"),
copyChatIdBtn: document.getElementById("copy-chat-id-btn"),
connectBtn: document.getElementById("connect-btn"),
statusDot: document.getElementById("status-dot"),
statusText: document.getElementById("status-text"),
@@ -70,9 +71,21 @@ const els = {
sendBtn: document.getElementById("send-btn"),
};
function wsUrlWithChatId() {
function generateChatId() {
if (typeof crypto !== "undefined" && crypto.randomUUID) {
return `voice_${crypto.randomUUID().replaceAll("-", "").slice(0, 16)}`;
}
return `voice_${Date.now().toString(36)}${Math.random()
.toString(36)
.slice(2, 10)}`;
}
function currentChatIdInput() {
return (els.chatId.value || "").trim();
}
function wsUrlWithChatId(chatId) {
const rawUrl = (els.url.value || "").trim();
const chatId = (els.chatId.value || "").trim();
if (!rawUrl || !chatId) return rawUrl;
try {
@@ -89,6 +102,7 @@ const state = {
ws: null,
connected: false,
connecting: false,
chatId: "",
audioContext: null,
micStream: null,
@@ -127,6 +141,7 @@ function setStatus(kind, text) {
function setConnectButton() {
els.chatId.disabled = state.connected || state.connecting;
els.copyChatIdBtn.disabled = !state.connected || !state.chatId;
if (state.connecting) {
els.connectBtn.textContent = "Connecting…";
els.connectBtn.disabled = true;
@@ -142,6 +157,26 @@ function setConnectButton() {
}
}
async function copyChatId() {
if (!state.connected || !state.chatId) return;
try {
await navigator.clipboard.writeText(state.chatId);
} catch (_) {
const selectionStart = els.chatId.selectionStart;
const selectionEnd = els.chatId.selectionEnd;
els.chatId.disabled = false;
els.chatId.select();
document.execCommand("copy");
els.chatId.setSelectionRange(selectionStart, selectionEnd);
els.chatId.disabled = true;
}
els.copyChatIdBtn.textContent = "Copied";
window.setTimeout(() => {
els.copyChatIdBtn.textContent = "Copy";
}, 1200);
}
function setMicButton() {
els.micBtn.disabled = !state.connected;
els.micBtn.setAttribute("aria-pressed", state.micEnabled ? "true" : "false");
@@ -891,13 +926,17 @@ function handleEvent(event) {
async function connect() {
if (state.connected || state.connecting) return;
const url = wsUrlWithChatId();
const inputChatId = currentChatIdInput();
const chatId = inputChatId || generateChatId();
const url = wsUrlWithChatId(chatId);
if (!url) {
setStatus("error", "Missing URL");
return;
}
state.connecting = true;
state.chatId = chatId;
els.chatId.value = chatId;
setStatus("connecting", "Connecting…");
setConnectButton();
addWsLog("system", `connecting ${url}`);
@@ -908,6 +947,8 @@ async function connect() {
} catch (err) {
console.error("AudioContext failed", err);
state.connecting = false;
state.chatId = "";
if (!inputChatId) els.chatId.value = "";
setStatus("error", "Audio init failed");
setConnectButton();
addWsLog("error", `audio init failed: ${err.message || err}`, "error");
@@ -920,6 +961,8 @@ async function connect() {
} catch (err) {
console.error("WebSocket constructor failed", err);
state.connecting = false;
state.chatId = "";
if (!inputChatId) els.chatId.value = "";
setStatus("error", "Bad URL");
setConnectButton();
addWsLog("error", `bad websocket URL: ${err.message || err}`, "error");
@@ -929,7 +972,6 @@ async function connect() {
state.ws = ws;
ws.addEventListener("open", () => {
const chatId = (els.chatId.value || "").trim();
const startMessage = {
type: "session.start",
protocol: PROTOCOL,
@@ -939,9 +981,7 @@ async function connect() {
channels: CHANNELS,
},
};
if (chatId) {
startMessage.chatId = chatId;
}
startMessage.chatId = state.chatId;
state.connecting = false;
state.connected = true;
@@ -995,6 +1035,7 @@ async function connect() {
state.ws = null;
state.connected = false;
state.connecting = false;
state.chatId = "";
setAssistantState("");
if (state.micEnabled) stopMic();
stopPlaybackQueue();
@@ -1047,6 +1088,8 @@ els.connectBtn.addEventListener("click", () => {
else connect();
});
els.copyChatIdBtn.addEventListener("click", copyChatId);
els.micBtn.addEventListener("click", async () => {
if (!state.connected) return;
els.micBtn.disabled = true;

View File

@@ -27,13 +27,25 @@
</label>
<label class="connection__field connection__field--chat">
<span>Chat ID</span>
<input
id="chat-id"
type="text"
placeholder="optional chatId"
spellcheck="false"
autocomplete="off"
/>
<div class="chat-id-control">
<input
id="chat-id"
type="text"
placeholder="optional chatId"
spellcheck="false"
autocomplete="off"
/>
<button
id="copy-chat-id-btn"
class="chat-id-control__copy"
type="button"
disabled
title="Copy Chat ID"
aria-label="Copy Chat ID"
>
Copy
</button>
</div>
</label>
<button id="connect-btn" class="btn btn--primary" type="button">
Connect

View File

@@ -328,11 +328,50 @@ body {
min-width: 0;
}
.connection__field input:disabled {
color: var(--text-dim);
background: rgba(148, 163, 184, 0.12);
cursor: not-allowed;
}
.connection__field input:focus {
border-color: var(--accent);
box-shadow: 0 0 0 3px rgba(79, 140, 255, 0.18);
}
.chat-id-control {
display: flex;
min-width: 0;
}
.chat-id-control input {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.chat-id-control__copy {
flex: 0 0 auto;
border: 1px solid var(--border);
border-left: 0;
border-radius: 0 10px 10px 0;
background: var(--bg-soft);
color: var(--text-dim);
padding: 0 10px;
font: inherit;
font-size: 12px;
cursor: pointer;
}
.chat-id-control__copy:disabled {
opacity: 0.55;
cursor: not-allowed;
}
.chat-id-control__copy:not(:disabled):hover {
color: var(--text);
border-color: var(--accent);
}
.status {
display: flex;
align-items: center;