From faf4026cf461e58013f895276b86680c9327839c Mon Sep 17 00:00:00 2001 From: mattie ruth backman Date: Thu, 3 Jul 2025 11:12:10 -0700 Subject: [PATCH] Add device controls to the simple chatbot example --- .../client/javascript/index.html | 7 ++++ .../client/javascript/src/app.js | 37 ++++++++++++++++- .../client/javascript/src/style.css | 41 ++++++++++++++++++- 3 files changed, 81 insertions(+), 4 deletions(-) diff --git a/examples/simple-chatbot/client/javascript/index.html b/examples/simple-chatbot/client/javascript/index.html index d6f4bfcb1..6d3a18db4 100644 --- a/examples/simple-chatbot/client/javascript/index.html +++ b/examples/simple-chatbot/client/javascript/index.html @@ -27,6 +27,13 @@ +
+
+ + +
+
+

Debug Info

diff --git a/examples/simple-chatbot/client/javascript/src/app.js b/examples/simple-chatbot/client/javascript/src/app.js index f858749ed..6f33a015c 100644 --- a/examples/simple-chatbot/client/javascript/src/app.js +++ b/examples/simple-chatbot/client/javascript/src/app.js @@ -28,7 +28,6 @@ class ChatbotClient { // Initialize client state this.rtviClient = null; this.setupDOMElements(); - this.setupEventListeners(); this.initializeClientAndTransport(); } @@ -42,6 +41,7 @@ class ChatbotClient { this.statusSpan = document.getElementById('connection-status'); this.debugLog = document.getElementById('debug-log'); this.botVideoContainer = document.getElementById('bot-video-container'); + this.deviceSelector = document.getElementById('device-selector'); // Create an audio element for bot's voice output this.botAudio = document.createElement('audio'); @@ -56,12 +56,37 @@ class ChatbotClient { setupEventListeners() { this.connectBtn.addEventListener('click', () => this.connect()); this.disconnectBtn.addEventListener('click', () => this.disconnect()); + + // Populate device selector + this.rtviClient.getAllMics().then((mics) => { + console.log('Available mics:', mics); + mics.forEach((device) => { + const option = document.createElement('option'); + option.value = device.deviceId; + option.textContent = device.label || `Microphone ${device.deviceId}`; + this.deviceSelector.appendChild(option); + }); + }); + this.deviceSelector.addEventListener('change', (event) => { + const selectedDeviceId = event.target.value; + console.log('Selected device ID:', selectedDeviceId); + this.rtviClient.updateMic(selectedDeviceId); + }); + + // Handle mic mute/unmute toggle + const micToggleBtn = document.getElementById('mic-toggle-btn'); + + micToggleBtn.addEventListener('click', () => { + let micEnabled = this.rtviClient.isMicEnabled; + micToggleBtn.textContent = micEnabled ? 'Unmute Mic' : 'Mute Mic'; + this.rtviClient.enableMic(!micEnabled); + }); } /** * Set up the RTVI client and Daily transport */ - initializeClientAndTransport() { + async initializeClientAndTransport() { // Initialize the RTVI client with a DailyTransport and our configuration this.rtviClient = new RTVIClient({ transport: new DailyTransport(), @@ -121,14 +146,22 @@ class ChatbotClient { onMessageError: (error) => { console.log('Message error:', error); }, + onMicUpdated: (data) => { + console.log('Mic updated:', data); + this.deviceSelector.value = data.deviceId; + }, onError: (error) => { console.log('Error:', JSON.stringify(error)); }, }, }); + window.client = this; // Expose client globally for debugging // Set up listeners for media track events this.setupTrackListeners(); + + await this.rtviClient.initDevices(); + this.setupEventListeners(); } /** diff --git a/examples/simple-chatbot/client/javascript/src/style.css b/examples/simple-chatbot/client/javascript/src/style.css index a3cb55776..359dfa1a9 100644 --- a/examples/simple-chatbot/client/javascript/src/style.css +++ b/examples/simple-chatbot/client/javascript/src/style.css @@ -10,7 +10,8 @@ body { margin: 0 auto; } -.status-bar { +.status-bar, +.device-bar { display: flex; justify-content: space-between; align-items: center; @@ -20,7 +21,19 @@ body { margin-bottom: 20px; } -.controls button { +.controls, +.device-controls { + display: flex; + align-items: center; + gap: 10px; /* Adds spacing between elements */ +} + +.device-controls { + margin-left: auto; +} + +.controls button, +.device-controls button { padding: 8px 16px; margin-left: 10px; border: none; @@ -28,6 +41,27 @@ body { cursor: pointer; } +#bot-selector, +#device-selector { + padding: 8px 16px; + padding-right: 40px; + border: none; + border-radius: 4px; + background-color: #6c757d; /* Gray background */ + color: white; /* White text */ + cursor: pointer; + appearance: none; /* Removes default browser styling for dropdowns */ + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='white'%3E%3Cpath d='M7 10l5 5 5-5z'/%3E%3C/svg%3E"); /* Custom arrow */ + background-repeat: no-repeat; + background-position: right 8px center; /* Position the arrow */ +} + +#bot-selector:focus, +#device-selector:focus { + outline: none; + box-shadow: 0 0 4px rgba(0, 0, 0, 0.3); /* Add a subtle focus effect */ +} + #connect-btn { background-color: #4caf50; color: white; @@ -38,6 +72,9 @@ body { color: white; } +#mic-toggle-btn { +} + button:disabled { opacity: 0.5; cursor: not-allowed;