Update voice demo chat id handling
This commit is contained in:
@@ -45,6 +45,7 @@ function defaultWsUrl() {
|
|||||||
const els = {
|
const els = {
|
||||||
url: document.getElementById("ws-url"),
|
url: document.getElementById("ws-url"),
|
||||||
chatId: document.getElementById("chat-id"),
|
chatId: document.getElementById("chat-id"),
|
||||||
|
copyChatIdBtn: document.getElementById("copy-chat-id-btn"),
|
||||||
connectBtn: document.getElementById("connect-btn"),
|
connectBtn: document.getElementById("connect-btn"),
|
||||||
statusDot: document.getElementById("status-dot"),
|
statusDot: document.getElementById("status-dot"),
|
||||||
statusText: document.getElementById("status-text"),
|
statusText: document.getElementById("status-text"),
|
||||||
@@ -70,9 +71,21 @@ const els = {
|
|||||||
sendBtn: document.getElementById("send-btn"),
|
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 rawUrl = (els.url.value || "").trim();
|
||||||
const chatId = (els.chatId.value || "").trim();
|
|
||||||
if (!rawUrl || !chatId) return rawUrl;
|
if (!rawUrl || !chatId) return rawUrl;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -89,6 +102,7 @@ const state = {
|
|||||||
ws: null,
|
ws: null,
|
||||||
connected: false,
|
connected: false,
|
||||||
connecting: false,
|
connecting: false,
|
||||||
|
chatId: "",
|
||||||
|
|
||||||
audioContext: null,
|
audioContext: null,
|
||||||
micStream: null,
|
micStream: null,
|
||||||
@@ -127,6 +141,7 @@ function setStatus(kind, text) {
|
|||||||
|
|
||||||
function setConnectButton() {
|
function setConnectButton() {
|
||||||
els.chatId.disabled = state.connected || state.connecting;
|
els.chatId.disabled = state.connected || state.connecting;
|
||||||
|
els.copyChatIdBtn.disabled = !state.connected || !state.chatId;
|
||||||
if (state.connecting) {
|
if (state.connecting) {
|
||||||
els.connectBtn.textContent = "Connecting…";
|
els.connectBtn.textContent = "Connecting…";
|
||||||
els.connectBtn.disabled = true;
|
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() {
|
function setMicButton() {
|
||||||
els.micBtn.disabled = !state.connected;
|
els.micBtn.disabled = !state.connected;
|
||||||
els.micBtn.setAttribute("aria-pressed", state.micEnabled ? "true" : "false");
|
els.micBtn.setAttribute("aria-pressed", state.micEnabled ? "true" : "false");
|
||||||
@@ -891,13 +926,17 @@ function handleEvent(event) {
|
|||||||
|
|
||||||
async function connect() {
|
async function connect() {
|
||||||
if (state.connected || state.connecting) return;
|
if (state.connected || state.connecting) return;
|
||||||
const url = wsUrlWithChatId();
|
const inputChatId = currentChatIdInput();
|
||||||
|
const chatId = inputChatId || generateChatId();
|
||||||
|
const url = wsUrlWithChatId(chatId);
|
||||||
if (!url) {
|
if (!url) {
|
||||||
setStatus("error", "Missing URL");
|
setStatus("error", "Missing URL");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.connecting = true;
|
state.connecting = true;
|
||||||
|
state.chatId = chatId;
|
||||||
|
els.chatId.value = chatId;
|
||||||
setStatus("connecting", "Connecting…");
|
setStatus("connecting", "Connecting…");
|
||||||
setConnectButton();
|
setConnectButton();
|
||||||
addWsLog("system", `connecting ${url}`);
|
addWsLog("system", `connecting ${url}`);
|
||||||
@@ -908,6 +947,8 @@ async function connect() {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("AudioContext failed", err);
|
console.error("AudioContext failed", err);
|
||||||
state.connecting = false;
|
state.connecting = false;
|
||||||
|
state.chatId = "";
|
||||||
|
if (!inputChatId) els.chatId.value = "";
|
||||||
setStatus("error", "Audio init failed");
|
setStatus("error", "Audio init failed");
|
||||||
setConnectButton();
|
setConnectButton();
|
||||||
addWsLog("error", `audio init failed: ${err.message || err}`, "error");
|
addWsLog("error", `audio init failed: ${err.message || err}`, "error");
|
||||||
@@ -920,6 +961,8 @@ async function connect() {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("WebSocket constructor failed", err);
|
console.error("WebSocket constructor failed", err);
|
||||||
state.connecting = false;
|
state.connecting = false;
|
||||||
|
state.chatId = "";
|
||||||
|
if (!inputChatId) els.chatId.value = "";
|
||||||
setStatus("error", "Bad URL");
|
setStatus("error", "Bad URL");
|
||||||
setConnectButton();
|
setConnectButton();
|
||||||
addWsLog("error", `bad websocket URL: ${err.message || err}`, "error");
|
addWsLog("error", `bad websocket URL: ${err.message || err}`, "error");
|
||||||
@@ -929,7 +972,6 @@ async function connect() {
|
|||||||
state.ws = ws;
|
state.ws = ws;
|
||||||
|
|
||||||
ws.addEventListener("open", () => {
|
ws.addEventListener("open", () => {
|
||||||
const chatId = (els.chatId.value || "").trim();
|
|
||||||
const startMessage = {
|
const startMessage = {
|
||||||
type: "session.start",
|
type: "session.start",
|
||||||
protocol: PROTOCOL,
|
protocol: PROTOCOL,
|
||||||
@@ -939,9 +981,7 @@ async function connect() {
|
|||||||
channels: CHANNELS,
|
channels: CHANNELS,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
if (chatId) {
|
startMessage.chatId = state.chatId;
|
||||||
startMessage.chatId = chatId;
|
|
||||||
}
|
|
||||||
|
|
||||||
state.connecting = false;
|
state.connecting = false;
|
||||||
state.connected = true;
|
state.connected = true;
|
||||||
@@ -995,6 +1035,7 @@ async function connect() {
|
|||||||
state.ws = null;
|
state.ws = null;
|
||||||
state.connected = false;
|
state.connected = false;
|
||||||
state.connecting = false;
|
state.connecting = false;
|
||||||
|
state.chatId = "";
|
||||||
setAssistantState("");
|
setAssistantState("");
|
||||||
if (state.micEnabled) stopMic();
|
if (state.micEnabled) stopMic();
|
||||||
stopPlaybackQueue();
|
stopPlaybackQueue();
|
||||||
@@ -1047,6 +1088,8 @@ els.connectBtn.addEventListener("click", () => {
|
|||||||
else connect();
|
else connect();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
els.copyChatIdBtn.addEventListener("click", copyChatId);
|
||||||
|
|
||||||
els.micBtn.addEventListener("click", async () => {
|
els.micBtn.addEventListener("click", async () => {
|
||||||
if (!state.connected) return;
|
if (!state.connected) return;
|
||||||
els.micBtn.disabled = true;
|
els.micBtn.disabled = true;
|
||||||
|
|||||||
@@ -27,13 +27,25 @@
|
|||||||
</label>
|
</label>
|
||||||
<label class="connection__field connection__field--chat">
|
<label class="connection__field connection__field--chat">
|
||||||
<span>Chat ID</span>
|
<span>Chat ID</span>
|
||||||
<input
|
<div class="chat-id-control">
|
||||||
id="chat-id"
|
<input
|
||||||
type="text"
|
id="chat-id"
|
||||||
placeholder="optional chatId"
|
type="text"
|
||||||
spellcheck="false"
|
placeholder="optional chatId"
|
||||||
autocomplete="off"
|
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>
|
</label>
|
||||||
<button id="connect-btn" class="btn btn--primary" type="button">
|
<button id="connect-btn" class="btn btn--primary" type="button">
|
||||||
Connect
|
Connect
|
||||||
|
|||||||
@@ -328,11 +328,50 @@ body {
|
|||||||
min-width: 0;
|
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 {
|
.connection__field input:focus {
|
||||||
border-color: var(--accent);
|
border-color: var(--accent);
|
||||||
box-shadow: 0 0 0 3px rgba(79, 140, 255, 0.18);
|
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 {
|
.status {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
Reference in New Issue
Block a user