Skip to content

Commit c73b063

Browse files
committed
UI imprvements for browser bridge demo.
1 parent cf322ba commit c73b063

File tree

1 file changed

+143
-71
lines changed

1 file changed

+143
-71
lines changed

examples/BrowserBridge/wwwroot/index.html

Lines changed: 143 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -38,29 +38,45 @@
3838
border-radius: 4px;
3939
}
4040
.controls button {
41-
padding: 0.75rem;
42-
font-size: 1rem;
41+
padding: 1rem 2rem;
42+
font-size: 1.25rem;
4343
border: none;
4444
border-radius: 5px;
45-
background: #4CAF50; /* Modern green */
46-
color: #fff;
4745
cursor: pointer;
48-
transition: background 0.2s;
49-
}
50-
.controls button:hover:enabled {
51-
background: #388E3C; /* Slightly darker green for hover */
5246
}
53-
.controls button.close-btn {
54-
background: #F44336; /* Modern red */
47+
.btn-success {
48+
background: #28a745;
5549
color: #fff;
5650
}
57-
.controls button.close-btn:hover:enabled {
58-
background: #C62828; /* Slightly darker red for hover */
51+
.close-btn {
52+
background: #dc3545;
53+
color: #fff;
5954
}
6055
.controls button:disabled {
6156
opacity: 0.6;
6257
cursor: not-allowed;
6358
}
59+
.button-row {
60+
display: flex;
61+
flex-direction: row;
62+
gap: 1rem;
63+
width: 100%;
64+
justify-content: center;
65+
}
66+
.diagnostics-panel {
67+
width: 100%;
68+
min-height: 160px;
69+
max-height: 640px;
70+
margin-top: 1.5rem;
71+
background: #222;
72+
color: #fff;
73+
font-size: 0.95rem;
74+
border-radius: 6px;
75+
padding: 0.75rem 1rem;
76+
overflow-y: auto;
77+
font-family: monospace;
78+
box-sizing: border-box;
79+
}
6480
@media (max-width: 600px) {
6581
.container {
6682
margin: 0;
@@ -70,6 +86,16 @@
7086
audio {
7187
max-width: 100%;
7288
}
89+
.button-row {
90+
flex-direction: column;
91+
gap: 0.75rem;
92+
align-items: center;
93+
}
94+
.controls button {
95+
width: 100%;
96+
font-size: 1.1rem;
97+
padding: 1rem;
98+
}
7399
}
74100
</style>
75101
<script type="text/javascript">
@@ -88,79 +114,121 @@
88114
return `${protocol}//${host}${path}`;
89115
}
90116

91-
async function start() {
92-
document.getElementById('startBtn').disabled = true;
93-
document.getElementById('closeBtn').disabled = false;
94-
pc = new RTCPeerConnection({
95-
iceServers: [
96-
{
97-
urls: STUN_URL
98-
}
99-
]
100-
});
101-
102-
localStream = await navigator.mediaDevices.getUserMedia({ video: false, audio: true });
117+
function logDiag(msg) {
118+
const panel = document.getElementById('diagnosticsPanel');
119+
if (!panel) return; // Prevent error if panel not yet in DOM
120+
const now = new Date().toLocaleTimeString();
121+
const entry = document.createElement('div');
122+
entry.textContent = `[${now}] ${msg}`;
123+
if (panel.firstChild) {
124+
panel.insertBefore(entry, panel.firstChild);
125+
} else {
126+
panel.appendChild(entry);
127+
}
128+
}
103129

104-
localStream.getTracks().forEach(track => {
105-
console.log('add local track ' + track.kind + ' to peer connection.');
106-
console.log(track);
107-
pc.addTrack(track, localStream);
108-
});
130+
function setButtonStates(startEnabled, closeEnabled) {
131+
document.getElementById('startBtn').disabled = !startEnabled;
132+
document.getElementById('closeBtn').disabled = !closeEnabled;
133+
logDiag(`Button states updated: Start ${startEnabled ? 'ENABLED' : 'DISABLED'}, Close ${closeEnabled ? 'ENABLED' : 'DISABLED'}`);
134+
}
109135

110-
pc.ontrack = evt => {
111-
console.log("Adding track to audio control.");
112-
document.querySelector('#audioCtl').srcObject = evt.streams[0];
136+
async function start() {
137+
logDiag('Start button clicked. Attempting to start peer connection.');
138+
setButtonStates(false, true);
139+
try {
140+
pc = new RTCPeerConnection({
141+
iceServers: [
142+
{ urls: STUN_URL }
143+
]
144+
});
145+
logDiag('RTCPeerConnection created.');
146+
147+
localStream = await navigator.mediaDevices.getUserMedia({ video: false, audio: true });
148+
logDiag('Got local audio stream.');
149+
150+
localStream.getTracks().forEach(track => {
151+
logDiag('Adding local track: ' + track.kind);
152+
pc.addTrack(track, localStream);
153+
});
154+
155+
pc.ontrack = evt => {
156+
logDiag('Received remote track.');
157+
document.querySelector('#audioCtl').srcObject = evt.streams[0];
158+
}
113159

114-
evt.streams[0].onunmute = () => {
115-
console.log("Adding track to audio control.");
160+
pc.onicecandidate = evt => {
161+
if (evt.candidate) {
162+
logDiag('ICE candidate generated. Sending to server.');
163+
ws.send(JSON.stringify(evt.candidate));
164+
}
116165
};
117166

118-
evt.streams[0].onended = () => {
119-
console.log("Track ended.");
167+
pc.onconnectionstatechange = () => {
168+
logDiag('Peer connection state: ' + pc.connectionState);
169+
if (pc.connectionState === 'connected' || pc.connectionState === 'connecting') {
170+
setButtonStates(false, true);
171+
} else {
172+
setButtonStates(true, false);
173+
}
120174
};
121-
}
122-
123-
pc.onicecandidate = evt => evt.candidate && ws.send(JSON.stringify(evt.candidate));
124175

125-
pc.onclose = () => {
126-
console.log("pc close");
127-
};
128-
129-
ws = new WebSocket(document.querySelector('#websockurl').value, []);
130-
131-
ws.onmessage = async function (evt) {
132-
console.log("WebSocket message received:", evt.data);
133-
var obj = JSON.parse(evt.data);
134-
if (obj?.candidate) {
135-
pc.addIceCandidate(obj);
136-
}
137-
else if (obj?.sdp) {
138-
await pc.setRemoteDescription(new RTCSessionDescription(obj));
139-
pc.createAnswer()
140-
.then((answer) => pc.setLocalDescription(answer))
141-
.then(() => ws.send(JSON.stringify(pc.localDescription)));
142-
}
143-
};
176+
pc.onclose = () => {
177+
logDiag('Peer connection closed.');
178+
};
144179

145-
ws.onclose = function (evt) {
146-
console.log("WebSocket closed, code: " + evt.code + ", reason: " + evt.reason);
147-
};
180+
const wsUrl = document.querySelector('#websockurl').value;
181+
logDiag('Opening WebSocket: ' + wsUrl);
182+
ws = new WebSocket(wsUrl, []);
148183

149-
ws.onerror = function (evt) {
150-
console.error("WebSocket error:", evt);
151-
};
184+
ws.onopen = function () {
185+
logDiag('WebSocket connection opened.');
186+
};
187+
ws.onmessage = async function (evt) {
188+
logDiag('WebSocket message received: ' + evt.data);
189+
var obj = JSON.parse(evt.data);
190+
if (obj?.candidate) {
191+
logDiag('Received ICE candidate from server.');
192+
pc.addIceCandidate(obj);
193+
}
194+
else if (obj?.sdp) {
195+
logDiag('Received SDP from server. Setting remote description.');
196+
await pc.setRemoteDescription(new RTCSessionDescription(obj));
197+
pc.createAnswer()
198+
.then((answer) => pc.setLocalDescription(answer))
199+
.then(() => {
200+
logDiag('Sending local SDP answer.');
201+
ws.send(JSON.stringify(pc.localDescription));
202+
});
203+
}
204+
};
205+
ws.onclose = function (evt) {
206+
logDiag('WebSocket closed. Code: ' + evt.code + ', Reason: ' + evt.reason);
207+
};
208+
ws.onerror = function (evt) {
209+
logDiag('WebSocket error: ' + JSON.stringify(evt));
210+
};
211+
} catch (err) {
212+
logDiag('Error in start(): ' + err);
213+
setButtonStates(true, false);
214+
}
152215
};
153216

154217
async function closePeer() {
155-
document.getElementById('startBtn').disabled = false;
156-
document.getElementById('closeBtn').disabled = true;
157-
await pc?.close();
158-
await ws?.close();
159-
218+
logDiag('Close button clicked. Closing peer connection and WebSocket.');
219+
setButtonStates(true, false);
220+
try {
221+
await pc?.close();
222+
await ws?.close();
223+
logDiag('Peer connection and WebSocket closed.');
224+
} catch (err) {
225+
logDiag('Error closing connections: ' + err);
226+
}
160227
// stop and release the mic
161228
if (localStream) {
162229
localStream.getAudioTracks().forEach(t => t.stop());
163230
localStream = null;
231+
logDiag('Local audio tracks stopped.');
164232
}
165233
};
166234
</script>
@@ -170,12 +238,16 @@
170238
<audio controls autoplay id="audioCtl"></audio>
171239
<div class="controls">
172240
<input type="text" id="websockurl" size="40" />
173-
<button type="button" class="btn btn-success" id="startBtn" onclick="start();">Start</button>
174-
<button type="button" class="btn close-btn" id="closeBtn" onclick="closePeer();" disabled>Close</button>
241+
<div class="button-row">
242+
<button type="button" class="btn btn-success" id="startBtn" onclick="start();">Start</button>
243+
<button type="button" class="btn close-btn" id="closeBtn" onclick="closePeer();" disabled>Close</button>
244+
</div>
175245
</div>
246+
<div id="diagnosticsPanel" class="diagnostics-panel"></div>
176247
</div>
177248
</body>
178249

179250
<script>
180251
document.querySelector('#websockurl').value = getWebSocketUrl();
252+
logDiag('Diagnostics panel initialized. User agent: ' + navigator.userAgent);
181253
</script>

0 commit comments

Comments
 (0)