shell-mirror 1.5.80 → 1.5.82
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/public/app/terminal.html +86 -152
- package/public/app/terminal.js +80 -122
package/package.json
CHANGED
package/public/app/terminal.html
CHANGED
|
@@ -109,155 +109,121 @@
|
|
|
109
109
|
gap: 8px;
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
/*
|
|
113
|
-
.
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
display: flex;
|
|
121
|
-
align-items: center;
|
|
122
|
-
gap: 8px;
|
|
123
|
-
font-size: 0.9em;
|
|
124
|
-
transition: all 0.2s ease;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
.session-info-btn:hover {
|
|
128
|
-
background: #3a3a3a;
|
|
129
|
-
border-color: #667eea;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
.session-name {
|
|
133
|
-
font-weight: bold;
|
|
134
|
-
color: #fff;
|
|
112
|
+
/* Connection Status Indicator */
|
|
113
|
+
.connection-status {
|
|
114
|
+
width: 12px;
|
|
115
|
+
height: 12px;
|
|
116
|
+
border-radius: 50%;
|
|
117
|
+
background: #ff4444;
|
|
118
|
+
margin-right: 12px;
|
|
119
|
+
flex-shrink: 0;
|
|
135
120
|
}
|
|
136
121
|
|
|
137
|
-
.
|
|
138
|
-
|
|
139
|
-
|
|
122
|
+
.connection-status.connecting {
|
|
123
|
+
background: #ffaa44;
|
|
124
|
+
animation: pulse 1.5s ease-in-out infinite;
|
|
140
125
|
}
|
|
141
126
|
|
|
142
|
-
.
|
|
143
|
-
|
|
144
|
-
|
|
127
|
+
.connection-status.connected {
|
|
128
|
+
background: #44ff44;
|
|
129
|
+
box-shadow: 0 0 6px rgba(68, 255, 68, 0.5);
|
|
145
130
|
}
|
|
146
131
|
|
|
147
|
-
/* Session
|
|
148
|
-
.session-
|
|
149
|
-
display:
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
left: 0;
|
|
153
|
-
margin-top: 8px;
|
|
132
|
+
/* Session Tab Bar */
|
|
133
|
+
.session-tab-bar {
|
|
134
|
+
display: flex;
|
|
135
|
+
gap: 4px;
|
|
136
|
+
padding: 8px;
|
|
154
137
|
background: #2a2a2a;
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
z-index: 1000;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
.session-info-dropdown.show {
|
|
163
|
-
display: block;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
.dropdown-section {
|
|
167
|
-
padding: 12px 16px;
|
|
138
|
+
overflow-x: auto;
|
|
139
|
+
-webkit-overflow-scrolling: touch;
|
|
140
|
+
flex: 1;
|
|
141
|
+
align-items: center;
|
|
168
142
|
}
|
|
169
143
|
|
|
170
|
-
.
|
|
171
|
-
height:
|
|
172
|
-
background: #444;
|
|
144
|
+
.session-tab-bar::-webkit-scrollbar {
|
|
145
|
+
height: 4px;
|
|
173
146
|
}
|
|
174
147
|
|
|
175
|
-
.session-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
margin-bottom: 4px;
|
|
148
|
+
.session-tab-bar::-webkit-scrollbar-thumb {
|
|
149
|
+
background: #555;
|
|
150
|
+
border-radius: 2px;
|
|
179
151
|
}
|
|
180
152
|
|
|
181
|
-
.session-
|
|
153
|
+
.session-tab {
|
|
154
|
+
display: flex;
|
|
155
|
+
align-items: center;
|
|
156
|
+
gap: 6px;
|
|
157
|
+
padding: 8px 12px;
|
|
158
|
+
background: transparent;
|
|
159
|
+
border: none;
|
|
160
|
+
border-bottom: 3px solid transparent;
|
|
182
161
|
color: #999;
|
|
183
|
-
|
|
184
|
-
|
|
162
|
+
cursor: pointer;
|
|
163
|
+
min-width: 120px;
|
|
164
|
+
white-space: nowrap;
|
|
165
|
+
transition: all 0.2s ease;
|
|
166
|
+
flex-shrink: 0;
|
|
185
167
|
}
|
|
186
168
|
|
|
187
|
-
.
|
|
169
|
+
.session-tab:hover {
|
|
170
|
+
background: #3a3a3a;
|
|
188
171
|
color: #ccc;
|
|
189
|
-
font-size: 0.9em;
|
|
190
172
|
}
|
|
191
173
|
|
|
192
|
-
.
|
|
193
|
-
color: #
|
|
194
|
-
font-
|
|
174
|
+
.session-tab.active {
|
|
175
|
+
color: #fff;
|
|
176
|
+
font-weight: 600;
|
|
177
|
+
border-bottom-color: #667eea;
|
|
178
|
+
background: #1a1a1a;
|
|
195
179
|
}
|
|
196
180
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
181
|
+
.session-tab-status {
|
|
182
|
+
width: 8px;
|
|
183
|
+
height: 8px;
|
|
184
|
+
border-radius: 50%;
|
|
185
|
+
flex-shrink: 0;
|
|
201
186
|
}
|
|
202
187
|
|
|
203
|
-
.session-
|
|
204
|
-
background:
|
|
205
|
-
|
|
206
|
-
border: 1px solid #444;
|
|
207
|
-
padding: 4px 12px;
|
|
208
|
-
border-radius: 4px;
|
|
209
|
-
cursor: pointer;
|
|
210
|
-
font-size: 0.9em;
|
|
211
|
-
display: flex;
|
|
212
|
-
align-items: center;
|
|
213
|
-
gap: 4px;
|
|
214
|
-
transition: all 0.2s ease;
|
|
188
|
+
.session-tab-status.connected {
|
|
189
|
+
background: #44ff44;
|
|
190
|
+
box-shadow: 0 0 6px rgba(68, 255, 68, 0.5);
|
|
215
191
|
}
|
|
216
192
|
|
|
217
|
-
.session-
|
|
218
|
-
background: #
|
|
219
|
-
|
|
193
|
+
.session-tab-status.connecting {
|
|
194
|
+
background: #ffaa44;
|
|
195
|
+
animation: pulse 1.5s ease-in-out infinite;
|
|
220
196
|
}
|
|
221
197
|
|
|
222
|
-
.session-
|
|
223
|
-
|
|
224
|
-
position: absolute;
|
|
225
|
-
background: #2a2a2a;
|
|
226
|
-
border: 1px solid #555;
|
|
227
|
-
border-radius: 4px;
|
|
228
|
-
left: 50%;
|
|
229
|
-
transform: translateX(-50%);
|
|
230
|
-
top: 100%;
|
|
231
|
-
margin-top: 8px;
|
|
232
|
-
min-width: 200px;
|
|
233
|
-
z-index: 1000;
|
|
234
|
-
box-shadow: 0 4px 8px rgba(0,0,0,0.3);
|
|
198
|
+
.session-tab-status.disconnected {
|
|
199
|
+
background: #ff4444;
|
|
235
200
|
}
|
|
236
201
|
|
|
237
|
-
.session-
|
|
238
|
-
|
|
202
|
+
.session-tab-name {
|
|
203
|
+
overflow: hidden;
|
|
204
|
+
text-overflow: ellipsis;
|
|
239
205
|
}
|
|
240
206
|
|
|
241
|
-
.session-
|
|
242
|
-
padding:
|
|
207
|
+
.session-tab-new {
|
|
208
|
+
padding: 6px 12px;
|
|
209
|
+
background: #667eea;
|
|
210
|
+
border-radius: 4px;
|
|
211
|
+
color: white;
|
|
212
|
+
border: none;
|
|
243
213
|
cursor: pointer;
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
.session-dropdown-item:last-child {
|
|
251
|
-
border-bottom: none;
|
|
214
|
+
margin-left: 4px;
|
|
215
|
+
font-weight: 500;
|
|
216
|
+
transition: all 0.2s ease;
|
|
217
|
+
flex-shrink: 0;
|
|
252
218
|
}
|
|
253
219
|
|
|
254
|
-
.session-
|
|
255
|
-
background: #
|
|
220
|
+
.session-tab-new:hover {
|
|
221
|
+
background: #5568d3;
|
|
256
222
|
}
|
|
257
223
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
224
|
+
@keyframes pulse {
|
|
225
|
+
0%, 100% { opacity: 1; }
|
|
226
|
+
50% { opacity: 0.5; }
|
|
261
227
|
}
|
|
262
228
|
|
|
263
229
|
/* Header Buttons */
|
|
@@ -434,54 +400,22 @@
|
|
|
434
400
|
</div>
|
|
435
401
|
<div id="terminal-container">
|
|
436
402
|
<div class="session-header" id="session-header" style="display: none;">
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
<button class="session-info-btn" id="session-info-btn">
|
|
440
|
-
<span class="session-name" id="session-name">Terminal Session</span>
|
|
441
|
-
<span class="session-id" id="session-id"></span>
|
|
442
|
-
<span class="dropdown-arrow">▼</span>
|
|
443
|
-
</button>
|
|
444
|
-
|
|
445
|
-
<!-- Session Info Dropdown -->
|
|
446
|
-
<div class="session-info-dropdown" id="session-info-dropdown">
|
|
447
|
-
<div class="dropdown-section">
|
|
448
|
-
<div class="session-detail-name" id="detail-name">Session 1</div>
|
|
449
|
-
<div class="session-detail-id" id="detail-id">ses_1764...</div>
|
|
450
|
-
</div>
|
|
451
|
-
<div class="dropdown-divider"></div>
|
|
452
|
-
<div class="dropdown-section">
|
|
453
|
-
<div class="connection-detail" id="connection-detail">
|
|
454
|
-
🟢 Connected via WebRTC
|
|
455
|
-
</div>
|
|
456
|
-
</div>
|
|
457
|
-
<div class="dropdown-divider"></div>
|
|
458
|
-
<div class="dropdown-section version-info">
|
|
459
|
-
<div id="version-info-dropdown">v1.5.76 • Built Nov 26, 2025</div>
|
|
460
|
-
</div>
|
|
461
|
-
</div>
|
|
462
|
-
</div>
|
|
403
|
+
<!-- Connection Status Indicator -->
|
|
404
|
+
<div class="connection-status" id="connection-status"></div>
|
|
463
405
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
<span>Sessions</span>
|
|
468
|
-
<span>▼</span>
|
|
469
|
-
</button>
|
|
470
|
-
<div class="session-dropdown-content" id="session-dropdown-content">
|
|
471
|
-
<div class="session-dropdown-item" onclick="createNewSession()">
|
|
472
|
-
<span>+ New Session</span>
|
|
473
|
-
</div>
|
|
474
|
-
</div>
|
|
475
|
-
</div>
|
|
406
|
+
<!-- Session Tab Bar -->
|
|
407
|
+
<div class="session-tab-bar" id="session-tab-bar">
|
|
408
|
+
<!-- Tabs will be rendered here by JavaScript -->
|
|
476
409
|
</div>
|
|
477
410
|
|
|
411
|
+
<!-- Header Right Section -->
|
|
478
412
|
<div class="header-right">
|
|
479
413
|
<button class="header-btn help-btn" onclick="showHelpModal()">
|
|
480
414
|
<span>📖</span>
|
|
481
415
|
<span class="btn-text">Help</span>
|
|
482
416
|
</button>
|
|
483
417
|
<a href="/app/dashboard.html" class="header-btn dashboard-btn">
|
|
484
|
-
<span
|
|
418
|
+
<span class="btn-text">Dashboard</span>
|
|
485
419
|
</a>
|
|
486
420
|
</div>
|
|
487
421
|
</div>
|
package/public/app/terminal.js
CHANGED
|
@@ -170,8 +170,10 @@ function updateConnectionStatus(status) {
|
|
|
170
170
|
break;
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
// Update
|
|
174
|
-
|
|
173
|
+
// Update tab status indicators
|
|
174
|
+
if (currentSession) {
|
|
175
|
+
renderTabs();
|
|
176
|
+
}
|
|
175
177
|
}
|
|
176
178
|
|
|
177
179
|
// Cleanup timer for chunk assembler
|
|
@@ -246,42 +248,6 @@ async function loadVersionInfo() {
|
|
|
246
248
|
}
|
|
247
249
|
|
|
248
250
|
// Update connection detail in dropdown
|
|
249
|
-
function updateConnectionDetail(status, method = 'WebRTC') {
|
|
250
|
-
const detail = document.getElementById('connection-detail');
|
|
251
|
-
if (!detail) return;
|
|
252
|
-
|
|
253
|
-
const statusIcons = {
|
|
254
|
-
connected: '🟢',
|
|
255
|
-
connecting: '🟡',
|
|
256
|
-
disconnected: '🔴'
|
|
257
|
-
};
|
|
258
|
-
|
|
259
|
-
const statusTexts = {
|
|
260
|
-
connected: 'Connected',
|
|
261
|
-
connecting: 'Connecting',
|
|
262
|
-
disconnected: 'Disconnected'
|
|
263
|
-
};
|
|
264
|
-
|
|
265
|
-
detail.textContent = `${statusIcons[status] || '⚪'} ${statusTexts[status] || 'Unknown'} via ${method}`;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// Update session details in dropdown
|
|
269
|
-
function updateSessionDetails() {
|
|
270
|
-
if (currentSession) {
|
|
271
|
-
const detailName = document.getElementById('detail-name');
|
|
272
|
-
const detailId = document.getElementById('detail-id');
|
|
273
|
-
|
|
274
|
-
if (detailName) {
|
|
275
|
-
detailName.textContent = currentSession.name || 'Terminal Session';
|
|
276
|
-
}
|
|
277
|
-
if (detailId) {
|
|
278
|
-
detailId.textContent = `ID: ${currentSession.id}`;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
251
|
function startConnection() {
|
|
286
252
|
updateConnectionStatus('connecting');
|
|
287
253
|
connectContainer.style.display = 'none';
|
|
@@ -598,6 +564,11 @@ async function initializeWebRTCSignaling() {
|
|
|
598
564
|
if (nextData.availableSessions) {
|
|
599
565
|
availableSessions = nextData.availableSessions;
|
|
600
566
|
console.log('[CLIENT] 📚 Available sessions:', availableSessions);
|
|
567
|
+
|
|
568
|
+
// Re-render tabs with updated session list
|
|
569
|
+
if (currentSession) {
|
|
570
|
+
renderTabs();
|
|
571
|
+
}
|
|
601
572
|
}
|
|
602
573
|
|
|
603
574
|
console.log('[CLIENT] Received WebRTC offer from agent.');
|
|
@@ -957,50 +928,84 @@ function sendMessage(message) {
|
|
|
957
928
|
// Session Management Functions
|
|
958
929
|
function updateSessionDisplay() {
|
|
959
930
|
const sessionHeader = document.getElementById('session-header');
|
|
960
|
-
|
|
961
|
-
|
|
931
|
+
if (!sessionHeader) {
|
|
932
|
+
console.warn('[CLIENT] ⚠️ session-header element not found');
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
962
935
|
|
|
963
936
|
if (currentSession) {
|
|
964
937
|
sessionHeader.style.display = 'flex';
|
|
965
|
-
sessionName.textContent = currentSession.name;
|
|
966
|
-
sessionId.textContent = `(${currentSession.id.substring(0, 8)}...)`;
|
|
967
|
-
|
|
968
|
-
// Update available sessions dropdown
|
|
969
|
-
updateSessionDropdown();
|
|
970
938
|
|
|
971
|
-
// Update
|
|
972
|
-
|
|
939
|
+
// Update tabs
|
|
940
|
+
console.log('[CLIENT] 📋 Rendering tabs, availableSessions:', availableSessions);
|
|
941
|
+
renderTabs();
|
|
973
942
|
|
|
974
943
|
console.log('[CLIENT] 📋 Session display updated:', currentSession);
|
|
975
944
|
}
|
|
976
945
|
}
|
|
977
946
|
|
|
978
|
-
function
|
|
979
|
-
const
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
//
|
|
947
|
+
function renderTabs() {
|
|
948
|
+
const tabBar = document.getElementById('session-tab-bar');
|
|
949
|
+
if (!tabBar) {
|
|
950
|
+
console.warn('[CLIENT] ⚠️ session-tab-bar element not found');
|
|
951
|
+
return;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
// Get connection status
|
|
955
|
+
const connectionStatus = getConnectionStatus();
|
|
956
|
+
|
|
957
|
+
// Ensure we have sessions to display
|
|
958
|
+
let sessionsToRender = [];
|
|
959
|
+
|
|
989
960
|
if (availableSessions && availableSessions.length > 0) {
|
|
990
|
-
availableSessions
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
961
|
+
// Use availableSessions from agent
|
|
962
|
+
sessionsToRender = availableSessions;
|
|
963
|
+
} else if (currentSession) {
|
|
964
|
+
// Fallback: show at least the current session
|
|
965
|
+
sessionsToRender = [currentSession];
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
console.log('[CLIENT] 🎨 Rendering tabs:', {
|
|
969
|
+
sessionCount: sessionsToRender.length,
|
|
970
|
+
currentSession: currentSession?.id,
|
|
971
|
+
connectionStatus,
|
|
972
|
+
source: availableSessions?.length > 0 ? 'agent' : 'fallback'
|
|
973
|
+
});
|
|
974
|
+
|
|
975
|
+
// Build tabs HTML
|
|
976
|
+
let tabsHTML = '';
|
|
977
|
+
|
|
978
|
+
if (sessionsToRender.length > 0) {
|
|
979
|
+
tabsHTML = sessionsToRender.map(session => {
|
|
980
|
+
const isActive = currentSession && session.id === currentSession.id;
|
|
981
|
+
const displayName = session.name || 'Terminal Session';
|
|
982
|
+
|
|
983
|
+
return `
|
|
984
|
+
<button class="session-tab ${isActive ? 'active' : ''}"
|
|
985
|
+
onclick="switchToSession('${session.id}')"
|
|
986
|
+
${isActive ? 'disabled' : ''}
|
|
987
|
+
title="${displayName}">
|
|
988
|
+
<span class="session-tab-status ${connectionStatus}"></span>
|
|
989
|
+
<span class="session-tab-name">${displayName}</span>
|
|
990
|
+
</button>
|
|
1000
991
|
`;
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
992
|
+
}).join('');
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
// Add new session button
|
|
996
|
+
tabsHTML += '<button class="session-tab-new" onclick="createNewSession()" title="New Session">+</button>';
|
|
997
|
+
|
|
998
|
+
tabBar.innerHTML = tabsHTML;
|
|
999
|
+
console.log('[CLIENT] ✅ Tabs rendered:', sessionsToRender.length, 'tabs');
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
function getConnectionStatus() {
|
|
1003
|
+
if (dataChannel && dataChannel.readyState === 'open') {
|
|
1004
|
+
return 'connected';
|
|
1005
|
+
} else if (dataChannel && dataChannel.readyState === 'connecting') {
|
|
1006
|
+
return 'connecting';
|
|
1007
|
+
} else {
|
|
1008
|
+
return 'disconnected';
|
|
1004
1009
|
}
|
|
1005
1010
|
}
|
|
1006
1011
|
|
|
@@ -1022,15 +1027,12 @@ function switchToSession(sessionId) {
|
|
|
1022
1027
|
console.error('[CLIENT] ❌ Cannot switch session - data channel not open');
|
|
1023
1028
|
return;
|
|
1024
1029
|
}
|
|
1025
|
-
|
|
1030
|
+
|
|
1026
1031
|
console.log('[CLIENT] 🔄 Switching to session:', sessionId);
|
|
1027
|
-
dataChannel.send(JSON.stringify({
|
|
1028
|
-
type: 'session-switch',
|
|
1029
|
-
sessionId: sessionId
|
|
1032
|
+
dataChannel.send(JSON.stringify({
|
|
1033
|
+
type: 'session-switch',
|
|
1034
|
+
sessionId: sessionId
|
|
1030
1035
|
}));
|
|
1031
|
-
|
|
1032
|
-
// Hide dropdown
|
|
1033
|
-
document.getElementById('session-dropdown-content').classList.remove('show');
|
|
1034
1036
|
}
|
|
1035
1037
|
|
|
1036
1038
|
function createNewSession() {
|
|
@@ -1040,50 +1042,6 @@ function createNewSession() {
|
|
|
1040
1042
|
window.location.href = url.toString();
|
|
1041
1043
|
}
|
|
1042
1044
|
|
|
1043
|
-
// Setup dropdown toggles
|
|
1044
|
-
document.addEventListener('DOMContentLoaded', () => {
|
|
1045
|
-
// Session info dropdown
|
|
1046
|
-
const sessionInfoBtn = document.getElementById('session-info-btn');
|
|
1047
|
-
const sessionInfoDropdown = document.getElementById('session-info-dropdown');
|
|
1048
|
-
|
|
1049
|
-
// Sessions menu dropdown
|
|
1050
|
-
const dropdownBtn = document.getElementById('session-dropdown-btn');
|
|
1051
|
-
const dropdownContent = document.getElementById('session-dropdown-content');
|
|
1052
|
-
|
|
1053
|
-
// Session info dropdown toggle
|
|
1054
|
-
if (sessionInfoBtn && sessionInfoDropdown) {
|
|
1055
|
-
sessionInfoBtn.onclick = (e) => {
|
|
1056
|
-
e.stopPropagation();
|
|
1057
|
-
sessionInfoDropdown.classList.toggle('show');
|
|
1058
|
-
// Close sessions dropdown if open
|
|
1059
|
-
if (dropdownContent) {
|
|
1060
|
-
dropdownContent.classList.remove('show');
|
|
1061
|
-
}
|
|
1062
|
-
};
|
|
1063
|
-
}
|
|
1064
|
-
|
|
1065
|
-
// Sessions menu dropdown toggle
|
|
1066
|
-
if (dropdownBtn && dropdownContent) {
|
|
1067
|
-
dropdownBtn.onclick = (e) => {
|
|
1068
|
-
e.stopPropagation();
|
|
1069
|
-
dropdownContent.classList.toggle('show');
|
|
1070
|
-
// Close session info dropdown if open
|
|
1071
|
-
if (sessionInfoDropdown) {
|
|
1072
|
-
sessionInfoDropdown.classList.remove('show');
|
|
1073
|
-
}
|
|
1074
|
-
};
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
// Close both dropdowns when clicking outside
|
|
1078
|
-
document.addEventListener('click', () => {
|
|
1079
|
-
if (dropdownContent) {
|
|
1080
|
-
dropdownContent.classList.remove('show');
|
|
1081
|
-
}
|
|
1082
|
-
if (sessionInfoDropdown) {
|
|
1083
|
-
sessionInfoDropdown.classList.remove('show');
|
|
1084
|
-
}
|
|
1085
|
-
});
|
|
1086
|
-
});
|
|
1087
1045
|
|
|
1088
1046
|
// Handle session-related data channel messages
|
|
1089
1047
|
function handleSessionMessage(message) {
|