diff --git a/examples/webpage/app.js b/examples/webpage/app.js index 2648e69..24337ec 100644 --- a/examples/webpage/app.js +++ b/examples/webpage/app.js @@ -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; diff --git a/examples/webpage/index.html b/examples/webpage/index.html index deb87f9..5364977 100644 --- a/examples/webpage/index.html +++ b/examples/webpage/index.html @@ -27,13 +27,25 @@