Spaces:
Running
Running
mute button
Browse files- index.html +98 -0
index.html
CHANGED
|
@@ -218,6 +218,23 @@
|
|
| 218 |
cursor: not-allowed;
|
| 219 |
}
|
| 220 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 221 |
/* Panels */
|
| 222 |
.panel {
|
| 223 |
background: var(--pollen-card);
|
|
@@ -491,6 +508,22 @@
|
|
| 491 |
<button class="btn btn-secondary" id="connectBtn" onclick="connectSignaling()">Connect</button>
|
| 492 |
<button class="btn btn-primary" id="startBtn" onclick="startStream()" disabled>Start</button>
|
| 493 |
<button class="btn btn-danger" id="stopBtn" onclick="stopStream()" disabled>Stop</button>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 494 |
</div>
|
| 495 |
</div>
|
| 496 |
</div>
|
|
@@ -587,6 +620,10 @@
|
|
| 587 |
// Latency monitor
|
| 588 |
let latencyMonitorId = null;
|
| 589 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 590 |
// Export functions
|
| 591 |
window.loginToHuggingFace = loginToHuggingFace;
|
| 592 |
window.logout = logout;
|
|
@@ -595,6 +632,7 @@
|
|
| 595 |
window.stopStream = stopStream;
|
| 596 |
window.playSound = playSound;
|
| 597 |
window.playSoundPreset = playSoundPreset;
|
|
|
|
| 598 |
|
| 599 |
document.addEventListener('DOMContentLoaded', () => {
|
| 600 |
initAuth();
|
|
@@ -775,10 +813,31 @@
|
|
| 775 |
if (!selectedProducerId) return;
|
| 776 |
updateStatus('connecting', 'Connecting...');
|
| 777 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 778 |
peerConnection = new RTCPeerConnection({
|
| 779 |
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
|
| 780 |
});
|
| 781 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 782 |
peerConnection.ontrack = (e) => {
|
| 783 |
if (e.track.kind === 'video') {
|
| 784 |
const video = document.getElementById('remoteVideo');
|
|
@@ -876,12 +935,22 @@
|
|
| 876 |
|
| 877 |
if (peerConnection) peerConnection.close();
|
| 878 |
if (dataChannel) dataChannel.close();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 879 |
peerConnection = null;
|
| 880 |
dataChannel = null;
|
| 881 |
currentSessionId = null;
|
| 882 |
document.getElementById('remoteVideo').srcObject = null;
|
| 883 |
document.getElementById('startBtn').disabled = !selectedProducerId;
|
| 884 |
document.getElementById('stopBtn').disabled = true;
|
|
|
|
| 885 |
document.getElementById('robotSelector').classList.remove('hidden');
|
| 886 |
enableControls(false);
|
| 887 |
updateStatus('connected', 'Connected');
|
|
@@ -889,6 +958,35 @@
|
|
| 889 |
|
| 890 |
function enableControls(enabled) {
|
| 891 |
document.getElementById('btnPlaySound').disabled = !enabled;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 892 |
}
|
| 893 |
|
| 894 |
// ===================== Robot State =====================
|
|
|
|
| 218 |
cursor: not-allowed;
|
| 219 |
}
|
| 220 |
|
| 221 |
+
.btn-mute {
|
| 222 |
+
background: rgba(255,255,255,0.15);
|
| 223 |
+
color: white;
|
| 224 |
+
display: flex;
|
| 225 |
+
align-items: center;
|
| 226 |
+
gap: 6px;
|
| 227 |
+
}
|
| 228 |
+
|
| 229 |
+
.btn-mute.muted {
|
| 230 |
+
background: var(--danger);
|
| 231 |
+
}
|
| 232 |
+
|
| 233 |
+
.btn-mute svg {
|
| 234 |
+
width: 16px;
|
| 235 |
+
height: 16px;
|
| 236 |
+
}
|
| 237 |
+
|
| 238 |
/* Panels */
|
| 239 |
.panel {
|
| 240 |
background: var(--pollen-card);
|
|
|
|
| 508 |
<button class="btn btn-secondary" id="connectBtn" onclick="connectSignaling()">Connect</button>
|
| 509 |
<button class="btn btn-primary" id="startBtn" onclick="startStream()" disabled>Start</button>
|
| 510 |
<button class="btn btn-danger" id="stopBtn" onclick="stopStream()" disabled>Stop</button>
|
| 511 |
+
<button class="btn btn-mute muted" id="muteBtn" onclick="toggleMute()" disabled>
|
| 512 |
+
<svg id="micOffIcon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
| 513 |
+
<line x1="1" y1="1" x2="23" y2="23"></line>
|
| 514 |
+
<path d="M9 9v3a3 3 0 0 0 5.12 2.12M15 9.34V4a3 3 0 0 0-5.94-.6"></path>
|
| 515 |
+
<path d="M17 16.95A7 7 0 0 1 5 12v-2m14 0v2a7 7 0 0 1-.11 1.23"></path>
|
| 516 |
+
<line x1="12" y1="19" x2="12" y2="23"></line>
|
| 517 |
+
<line x1="8" y1="23" x2="16" y2="23"></line>
|
| 518 |
+
</svg>
|
| 519 |
+
<svg id="micOnIcon" class="hidden" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
| 520 |
+
<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"></path>
|
| 521 |
+
<path d="M19 10v2a7 7 0 0 1-14 0v-2"></path>
|
| 522 |
+
<line x1="12" y1="19" x2="12" y2="23"></line>
|
| 523 |
+
<line x1="8" y1="23" x2="16" y2="23"></line>
|
| 524 |
+
</svg>
|
| 525 |
+
<span id="muteText">Unmute</span>
|
| 526 |
+
</button>
|
| 527 |
</div>
|
| 528 |
</div>
|
| 529 |
</div>
|
|
|
|
| 620 |
// Latency monitor
|
| 621 |
let latencyMonitorId = null;
|
| 622 |
|
| 623 |
+
// Microphone state
|
| 624 |
+
let localStream = null;
|
| 625 |
+
let isMuted = true; // Default to muted
|
| 626 |
+
|
| 627 |
// Export functions
|
| 628 |
window.loginToHuggingFace = loginToHuggingFace;
|
| 629 |
window.logout = logout;
|
|
|
|
| 632 |
window.stopStream = stopStream;
|
| 633 |
window.playSound = playSound;
|
| 634 |
window.playSoundPreset = playSoundPreset;
|
| 635 |
+
window.toggleMute = toggleMute;
|
| 636 |
|
| 637 |
document.addEventListener('DOMContentLoaded', () => {
|
| 638 |
initAuth();
|
|
|
|
| 813 |
if (!selectedProducerId) return;
|
| 814 |
updateStatus('connecting', 'Connecting...');
|
| 815 |
|
| 816 |
+
// Get microphone access
|
| 817 |
+
try {
|
| 818 |
+
localStream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
| 819 |
+
// Start muted by default
|
| 820 |
+
localStream.getAudioTracks().forEach(track => {
|
| 821 |
+
track.enabled = false;
|
| 822 |
+
});
|
| 823 |
+
isMuted = true;
|
| 824 |
+
updateMuteButton();
|
| 825 |
+
} catch (e) {
|
| 826 |
+
console.warn('Could not access microphone:', e);
|
| 827 |
+
localStream = null;
|
| 828 |
+
}
|
| 829 |
+
|
| 830 |
peerConnection = new RTCPeerConnection({
|
| 831 |
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
|
| 832 |
});
|
| 833 |
|
| 834 |
+
// Add microphone track to peer connection
|
| 835 |
+
if (localStream) {
|
| 836 |
+
localStream.getAudioTracks().forEach(track => {
|
| 837 |
+
peerConnection.addTrack(track, localStream);
|
| 838 |
+
});
|
| 839 |
+
}
|
| 840 |
+
|
| 841 |
peerConnection.ontrack = (e) => {
|
| 842 |
if (e.track.kind === 'video') {
|
| 843 |
const video = document.getElementById('remoteVideo');
|
|
|
|
| 935 |
|
| 936 |
if (peerConnection) peerConnection.close();
|
| 937 |
if (dataChannel) dataChannel.close();
|
| 938 |
+
|
| 939 |
+
// Stop microphone
|
| 940 |
+
if (localStream) {
|
| 941 |
+
localStream.getTracks().forEach(track => track.stop());
|
| 942 |
+
localStream = null;
|
| 943 |
+
}
|
| 944 |
+
isMuted = true;
|
| 945 |
+
updateMuteButton();
|
| 946 |
+
|
| 947 |
peerConnection = null;
|
| 948 |
dataChannel = null;
|
| 949 |
currentSessionId = null;
|
| 950 |
document.getElementById('remoteVideo').srcObject = null;
|
| 951 |
document.getElementById('startBtn').disabled = !selectedProducerId;
|
| 952 |
document.getElementById('stopBtn').disabled = true;
|
| 953 |
+
document.getElementById('muteBtn').disabled = true;
|
| 954 |
document.getElementById('robotSelector').classList.remove('hidden');
|
| 955 |
enableControls(false);
|
| 956 |
updateStatus('connected', 'Connected');
|
|
|
|
| 958 |
|
| 959 |
function enableControls(enabled) {
|
| 960 |
document.getElementById('btnPlaySound').disabled = !enabled;
|
| 961 |
+
document.getElementById('muteBtn').disabled = !enabled || !localStream;
|
| 962 |
+
}
|
| 963 |
+
|
| 964 |
+
function toggleMute() {
|
| 965 |
+
if (!localStream) return;
|
| 966 |
+
isMuted = !isMuted;
|
| 967 |
+
localStream.getAudioTracks().forEach(track => {
|
| 968 |
+
track.enabled = !isMuted;
|
| 969 |
+
});
|
| 970 |
+
updateMuteButton();
|
| 971 |
+
}
|
| 972 |
+
|
| 973 |
+
function updateMuteButton() {
|
| 974 |
+
const btn = document.getElementById('muteBtn');
|
| 975 |
+
const micOffIcon = document.getElementById('micOffIcon');
|
| 976 |
+
const micOnIcon = document.getElementById('micOnIcon');
|
| 977 |
+
const muteText = document.getElementById('muteText');
|
| 978 |
+
|
| 979 |
+
if (isMuted) {
|
| 980 |
+
btn.classList.add('muted');
|
| 981 |
+
micOffIcon.classList.remove('hidden');
|
| 982 |
+
micOnIcon.classList.add('hidden');
|
| 983 |
+
muteText.textContent = 'Unmute';
|
| 984 |
+
} else {
|
| 985 |
+
btn.classList.remove('muted');
|
| 986 |
+
micOffIcon.classList.add('hidden');
|
| 987 |
+
micOnIcon.classList.remove('hidden');
|
| 988 |
+
muteText.textContent = 'Mute';
|
| 989 |
+
}
|
| 990 |
}
|
| 991 |
|
| 992 |
// ===================== Robot State =====================
|