shell-mirror 1.5.40 → 1.5.42
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/mac-agent/agent-debug.log +9 -94
- package/mac-agent/agent.js +363 -64
- package/package.json +1 -1
- package/public/app/dashboard.css +172 -0
- package/public/app/dashboard.js +159 -6
- package/public/app/terminal.html +118 -1
- package/public/app/terminal.js +212 -4
package/public/app/dashboard.css
CHANGED
|
@@ -235,6 +235,19 @@ body {
|
|
|
235
235
|
margin-top: 2px;
|
|
236
236
|
}
|
|
237
237
|
|
|
238
|
+
.agent-sessions {
|
|
239
|
+
font-size: 0.8rem;
|
|
240
|
+
color: #4285f4;
|
|
241
|
+
margin-top: 2px;
|
|
242
|
+
font-weight: 500;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.agent-actions {
|
|
246
|
+
display: flex;
|
|
247
|
+
gap: 8px;
|
|
248
|
+
align-items: center;
|
|
249
|
+
}
|
|
250
|
+
|
|
238
251
|
.btn-connect {
|
|
239
252
|
background: #4caf50;
|
|
240
253
|
color: white;
|
|
@@ -244,6 +257,7 @@ body {
|
|
|
244
257
|
font-weight: 500;
|
|
245
258
|
cursor: pointer;
|
|
246
259
|
transition: all 0.2s ease;
|
|
260
|
+
font-size: 0.9rem;
|
|
247
261
|
}
|
|
248
262
|
|
|
249
263
|
.btn-connect:hover {
|
|
@@ -251,6 +265,23 @@ body {
|
|
|
251
265
|
transform: translateY(-1px);
|
|
252
266
|
}
|
|
253
267
|
|
|
268
|
+
.btn-sessions {
|
|
269
|
+
background: #4285f4;
|
|
270
|
+
color: white;
|
|
271
|
+
border: none;
|
|
272
|
+
padding: 8px 16px;
|
|
273
|
+
border-radius: 6px;
|
|
274
|
+
font-weight: 500;
|
|
275
|
+
cursor: pointer;
|
|
276
|
+
transition: all 0.2s ease;
|
|
277
|
+
font-size: 0.9rem;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.btn-sessions:hover {
|
|
281
|
+
background: #3367d6;
|
|
282
|
+
transform: translateY(-1px);
|
|
283
|
+
}
|
|
284
|
+
|
|
254
285
|
/* Quick Actions */
|
|
255
286
|
.action-buttons {
|
|
256
287
|
display: flex;
|
|
@@ -522,4 +553,145 @@ body {
|
|
|
522
553
|
flex-direction: row;
|
|
523
554
|
gap: 12px;
|
|
524
555
|
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/* Modal Styles */
|
|
559
|
+
.modal-overlay {
|
|
560
|
+
position: fixed;
|
|
561
|
+
top: 0;
|
|
562
|
+
left: 0;
|
|
563
|
+
right: 0;
|
|
564
|
+
bottom: 0;
|
|
565
|
+
background: rgba(0, 0, 0, 0.5);
|
|
566
|
+
display: flex;
|
|
567
|
+
align-items: center;
|
|
568
|
+
justify-content: center;
|
|
569
|
+
z-index: 2000;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
.modal {
|
|
573
|
+
background: white;
|
|
574
|
+
border-radius: 12px;
|
|
575
|
+
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
|
|
576
|
+
max-width: 500px;
|
|
577
|
+
width: 90%;
|
|
578
|
+
max-height: 80vh;
|
|
579
|
+
overflow: hidden;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
.modal-header {
|
|
583
|
+
padding: 20px 24px;
|
|
584
|
+
border-bottom: 1px solid #eee;
|
|
585
|
+
display: flex;
|
|
586
|
+
justify-content: space-between;
|
|
587
|
+
align-items: center;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
.modal-header h3 {
|
|
591
|
+
margin: 0;
|
|
592
|
+
font-size: 1.2rem;
|
|
593
|
+
font-weight: 600;
|
|
594
|
+
color: #333;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
.modal-close {
|
|
598
|
+
background: none;
|
|
599
|
+
border: none;
|
|
600
|
+
font-size: 1.5rem;
|
|
601
|
+
cursor: pointer;
|
|
602
|
+
padding: 0;
|
|
603
|
+
width: 30px;
|
|
604
|
+
height: 30px;
|
|
605
|
+
display: flex;
|
|
606
|
+
align-items: center;
|
|
607
|
+
justify-content: center;
|
|
608
|
+
border-radius: 50%;
|
|
609
|
+
color: #666;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
.modal-close:hover {
|
|
613
|
+
background: #f5f5f5;
|
|
614
|
+
color: #333;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
.modal-body {
|
|
618
|
+
padding: 24px;
|
|
619
|
+
max-height: 60vh;
|
|
620
|
+
overflow-y: auto;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/* Session List Styles */
|
|
624
|
+
.sessions-modal-content p {
|
|
625
|
+
margin-top: 0;
|
|
626
|
+
margin-bottom: 16px;
|
|
627
|
+
color: #666;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
.session-list {
|
|
631
|
+
margin-bottom: 20px;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
.session-list-item {
|
|
635
|
+
display: flex;
|
|
636
|
+
justify-content: space-between;
|
|
637
|
+
align-items: center;
|
|
638
|
+
padding: 12px;
|
|
639
|
+
border: 1px solid #eee;
|
|
640
|
+
border-radius: 8px;
|
|
641
|
+
margin-bottom: 8px;
|
|
642
|
+
cursor: pointer;
|
|
643
|
+
transition: all 0.2s ease;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
.session-list-item:hover {
|
|
647
|
+
background: #f8f9fa;
|
|
648
|
+
border-color: #4285f4;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
.session-list-info {
|
|
652
|
+
flex: 1;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
.session-list-name {
|
|
656
|
+
font-weight: 600;
|
|
657
|
+
color: #333;
|
|
658
|
+
margin-bottom: 4px;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
.session-list-details {
|
|
662
|
+
display: flex;
|
|
663
|
+
gap: 12px;
|
|
664
|
+
font-size: 0.8rem;
|
|
665
|
+
color: #666;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
.session-list-id {
|
|
669
|
+
font-family: monospace;
|
|
670
|
+
background: #f5f5f5;
|
|
671
|
+
padding: 2px 6px;
|
|
672
|
+
border-radius: 4px;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
.session-list-status {
|
|
676
|
+
padding: 4px 8px;
|
|
677
|
+
border-radius: 12px;
|
|
678
|
+
font-size: 0.75rem;
|
|
679
|
+
font-weight: 500;
|
|
680
|
+
text-transform: uppercase;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
.session-list-status.active {
|
|
684
|
+
background: #e8f5e8;
|
|
685
|
+
color: #2e7d32;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
.session-list-status.crashed {
|
|
689
|
+
background: #ffebee;
|
|
690
|
+
color: #c62828;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
.sessions-modal-actions {
|
|
694
|
+
border-top: 1px solid #eee;
|
|
695
|
+
padding-top: 16px;
|
|
696
|
+
text-align: center;
|
|
525
697
|
}
|
package/public/app/dashboard.js
CHANGED
|
@@ -5,6 +5,7 @@ class ShellMirrorDashboard {
|
|
|
5
5
|
this.user = null;
|
|
6
6
|
this.agents = [];
|
|
7
7
|
this.sessions = [];
|
|
8
|
+
this.agentSessions = {}; // Maps agentId to sessions array
|
|
8
9
|
this.init();
|
|
9
10
|
}
|
|
10
11
|
|
|
@@ -80,6 +81,9 @@ class ShellMirrorDashboard {
|
|
|
80
81
|
|
|
81
82
|
if (agentsData.success && agentsData.data && agentsData.data.agents) {
|
|
82
83
|
this.agents = agentsData.data.agents;
|
|
84
|
+
|
|
85
|
+
// Load session data from localStorage (persisted from terminal connections)
|
|
86
|
+
this.loadSessionsFromStorage();
|
|
83
87
|
}
|
|
84
88
|
|
|
85
89
|
// TODO: Load session history when API is available
|
|
@@ -174,18 +178,29 @@ class ShellMirrorDashboard {
|
|
|
174
178
|
});
|
|
175
179
|
|
|
176
180
|
const agentCount = activeAgents.length;
|
|
177
|
-
const agentsHtml = activeAgents.map(agent =>
|
|
181
|
+
const agentsHtml = activeAgents.map(agent => {
|
|
182
|
+
const sessions = this.agentSessions[agent.agentId] || [];
|
|
183
|
+
const sessionCount = sessions.length;
|
|
184
|
+
|
|
185
|
+
return `
|
|
178
186
|
<div class="agent-item">
|
|
179
187
|
<div class="agent-info">
|
|
180
188
|
<div class="agent-name">${agent.machineName || agent.agentId}</div>
|
|
181
189
|
<div class="agent-status ${agent.onlineStatus}">${agent.onlineStatus}</div>
|
|
182
190
|
<div class="agent-last-seen">Last seen: ${this.formatLastSeen(agent.lastSeen)}</div>
|
|
191
|
+
${sessionCount > 0 ? `<div class="agent-sessions">${sessionCount} active session${sessionCount !== 1 ? 's' : ''}</div>` : ''}
|
|
192
|
+
</div>
|
|
193
|
+
<div class="agent-actions">
|
|
194
|
+
<button class="btn-connect" onclick="dashboard.connectToAgent('${agent.agentId}')">
|
|
195
|
+
${sessionCount > 0 ? 'Resume Session' : 'New Session'}
|
|
196
|
+
</button>
|
|
197
|
+
${sessionCount > 0 ? `<button class="btn-sessions" onclick="dashboard.showAgentSessions('${agent.agentId}')">
|
|
198
|
+
All Sessions
|
|
199
|
+
</button>` : ''}
|
|
183
200
|
</div>
|
|
184
|
-
<button class="btn-connect" onclick="dashboard.connectToAgent('${agent.agentId}')">
|
|
185
|
-
Connect
|
|
186
|
-
</button>
|
|
187
201
|
</div>
|
|
188
|
-
|
|
202
|
+
`;
|
|
203
|
+
}).join('');
|
|
189
204
|
|
|
190
205
|
return `
|
|
191
206
|
<div class="dashboard-card">
|
|
@@ -398,13 +413,151 @@ class ShellMirrorDashboard {
|
|
|
398
413
|
|
|
399
414
|
// Action handlers
|
|
400
415
|
async connectToAgent(agentId) {
|
|
416
|
+
// Check if there are existing sessions for this agent
|
|
417
|
+
const sessions = this.agentSessions[agentId] || [];
|
|
418
|
+
const activeSessions = sessions.filter(s => s.status === 'active');
|
|
419
|
+
|
|
420
|
+
if (activeSessions.length > 0) {
|
|
421
|
+
// Reconnect to the most recently active session
|
|
422
|
+
const mostRecentSession = activeSessions.reduce((latest, session) =>
|
|
423
|
+
session.lastActivity > latest.lastActivity ? session : latest
|
|
424
|
+
);
|
|
425
|
+
console.log(`[DASHBOARD] Reconnecting to existing session: ${mostRecentSession.id}`);
|
|
426
|
+
window.location.href = `/app/terminal.html?agent=${agentId}&session=${mostRecentSession.id}`;
|
|
427
|
+
} else {
|
|
428
|
+
// No existing sessions, create new one
|
|
429
|
+
console.log(`[DASHBOARD] Creating new session for agent: ${agentId}`);
|
|
430
|
+
window.location.href = `/app/terminal.html?agent=${agentId}`;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
async connectToSession(agentId, sessionId) {
|
|
435
|
+
window.location.href = `/app/terminal.html?agent=${agentId}&session=${sessionId}`;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
async createNewSession(agentId) {
|
|
439
|
+
// Force creation of new session by not passing session parameter
|
|
440
|
+
console.log(`[DASHBOARD] Creating new session for agent: ${agentId}`);
|
|
401
441
|
window.location.href = `/app/terminal.html?agent=${agentId}`;
|
|
402
442
|
}
|
|
403
443
|
|
|
404
444
|
startNewSession() {
|
|
405
|
-
|
|
445
|
+
// Get first available agent for new session
|
|
446
|
+
const activeAgents = this.agents.filter(agent => {
|
|
447
|
+
const timeSinceLastSeen = Date.now() / 1000 - agent.lastSeen;
|
|
448
|
+
return agent.onlineStatus === 'online' || timeSinceLastSeen < 300;
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
if (activeAgents.length > 0) {
|
|
452
|
+
this.connectToAgent(activeAgents[0].agentId);
|
|
453
|
+
} else {
|
|
454
|
+
alert('No active agents available. Please ensure an agent is running on your Mac.');
|
|
455
|
+
}
|
|
406
456
|
}
|
|
407
457
|
|
|
458
|
+
showAgentSessions(agentId) {
|
|
459
|
+
const sessions = this.agentSessions[agentId] || [];
|
|
460
|
+
const agent = this.agents.find(a => a.agentId === agentId);
|
|
461
|
+
const agentName = agent ? (agent.machineName || agent.agentId) : agentId;
|
|
462
|
+
|
|
463
|
+
if (sessions.length === 0) {
|
|
464
|
+
alert(`No active sessions found for ${agentName}`);
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// Create session list modal
|
|
469
|
+
const sessionsList = sessions.map(session => `
|
|
470
|
+
<div class="session-list-item" onclick="dashboard.connectToSession('${agentId}', '${session.id}')">
|
|
471
|
+
<div class="session-list-info">
|
|
472
|
+
<div class="session-list-name">${session.name}</div>
|
|
473
|
+
<div class="session-list-details">
|
|
474
|
+
<span class="session-list-id">${session.id.substring(0, 8)}...</span>
|
|
475
|
+
<span class="session-list-activity">Last activity: ${this.formatLastActivity(session.lastActivity)}</span>
|
|
476
|
+
</div>
|
|
477
|
+
</div>
|
|
478
|
+
<div class="session-list-status ${session.status}">${session.status}</div>
|
|
479
|
+
</div>
|
|
480
|
+
`).join('');
|
|
481
|
+
|
|
482
|
+
// Show modal with sessions
|
|
483
|
+
this.showModal(`Sessions on ${agentName}`, `
|
|
484
|
+
<div class="sessions-modal-content">
|
|
485
|
+
<p>Active terminal sessions (${sessions.length}):</p>
|
|
486
|
+
<div class="session-list">
|
|
487
|
+
${sessionsList}
|
|
488
|
+
</div>
|
|
489
|
+
<div class="sessions-modal-actions">
|
|
490
|
+
<button class="btn-primary" onclick="dashboard.createNewSession('${agentId}')">
|
|
491
|
+
+ Create New Session
|
|
492
|
+
</button>
|
|
493
|
+
</div>
|
|
494
|
+
</div>
|
|
495
|
+
`);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
showModal(title, content) {
|
|
499
|
+
// Create modal overlay
|
|
500
|
+
const modalOverlay = document.createElement('div');
|
|
501
|
+
modalOverlay.className = 'modal-overlay';
|
|
502
|
+
modalOverlay.onclick = () => document.body.removeChild(modalOverlay);
|
|
503
|
+
|
|
504
|
+
modalOverlay.innerHTML = `
|
|
505
|
+
<div class="modal" onclick="event.stopPropagation()">
|
|
506
|
+
<div class="modal-header">
|
|
507
|
+
<h3>${title}</h3>
|
|
508
|
+
<button class="modal-close" onclick="document.body.removeChild(this.closest('.modal-overlay'))">×</button>
|
|
509
|
+
</div>
|
|
510
|
+
<div class="modal-body">
|
|
511
|
+
${content}
|
|
512
|
+
</div>
|
|
513
|
+
</div>
|
|
514
|
+
`;
|
|
515
|
+
|
|
516
|
+
document.body.appendChild(modalOverlay);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
formatLastActivity(timestamp) {
|
|
520
|
+
const now = Date.now();
|
|
521
|
+
const diff = now - timestamp;
|
|
522
|
+
const minutes = Math.floor(diff / 60000);
|
|
523
|
+
const hours = Math.floor(diff / 3600000);
|
|
524
|
+
const days = Math.floor(diff / 86400000);
|
|
525
|
+
|
|
526
|
+
if (minutes < 1) return 'now';
|
|
527
|
+
if (minutes < 60) return `${minutes}m ago`;
|
|
528
|
+
if (hours < 24) return `${hours}h ago`;
|
|
529
|
+
return `${days}d ago`;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// Session storage management
|
|
533
|
+
loadSessionsFromStorage() {
|
|
534
|
+
try {
|
|
535
|
+
const storedSessions = localStorage.getItem('shell-mirror-sessions');
|
|
536
|
+
if (storedSessions) {
|
|
537
|
+
const sessionData = JSON.parse(storedSessions);
|
|
538
|
+
|
|
539
|
+
// Filter out old sessions (older than 24 hours)
|
|
540
|
+
const now = Date.now();
|
|
541
|
+
const maxAge = 24 * 60 * 60 * 1000; // 24 hours
|
|
542
|
+
|
|
543
|
+
Object.keys(sessionData).forEach(agentId => {
|
|
544
|
+
const validSessions = sessionData[agentId].filter(session =>
|
|
545
|
+
(now - session.lastActivity) < maxAge
|
|
546
|
+
);
|
|
547
|
+
|
|
548
|
+
if (validSessions.length > 0) {
|
|
549
|
+
this.agentSessions[agentId] = validSessions;
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
console.log('[DASHBOARD] Loaded sessions from storage:', this.agentSessions);
|
|
554
|
+
}
|
|
555
|
+
} catch (error) {
|
|
556
|
+
console.error('[DASHBOARD] Error loading sessions from storage:', error);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
|
|
408
561
|
showAgentInstructions() {
|
|
409
562
|
// TODO: Show modal with agent setup instructions
|
|
410
563
|
alert('Agent setup instructions coming soon!');
|
package/public/app/terminal.html
CHANGED
|
@@ -21,11 +21,109 @@
|
|
|
21
21
|
width: 100%;
|
|
22
22
|
background-color: #000000;
|
|
23
23
|
}
|
|
24
|
+
|
|
25
|
+
#terminal-container.show {
|
|
26
|
+
display: flex;
|
|
27
|
+
flex-direction: column;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* Session Header */
|
|
31
|
+
.session-header {
|
|
32
|
+
background: #2a2a2a;
|
|
33
|
+
color: #ccc;
|
|
34
|
+
padding: 8px 16px;
|
|
35
|
+
border-bottom: 1px solid #444;
|
|
36
|
+
display: flex;
|
|
37
|
+
align-items: center;
|
|
38
|
+
justify-content: space-between;
|
|
39
|
+
font-size: 0.9em;
|
|
40
|
+
z-index: 100;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.session-info {
|
|
44
|
+
display: flex;
|
|
45
|
+
align-items: center;
|
|
46
|
+
gap: 12px;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.session-name {
|
|
50
|
+
font-weight: bold;
|
|
51
|
+
color: #fff;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.session-controls {
|
|
55
|
+
display: flex;
|
|
56
|
+
align-items: center;
|
|
57
|
+
gap: 8px;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.session-dropdown {
|
|
61
|
+
position: relative;
|
|
62
|
+
display: inline-block;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.session-dropdown-btn {
|
|
66
|
+
background: #3a3a3a;
|
|
67
|
+
color: #ccc;
|
|
68
|
+
border: 1px solid #555;
|
|
69
|
+
padding: 4px 8px;
|
|
70
|
+
border-radius: 4px;
|
|
71
|
+
cursor: pointer;
|
|
72
|
+
font-size: 0.8em;
|
|
73
|
+
display: flex;
|
|
74
|
+
align-items: center;
|
|
75
|
+
gap: 4px;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.session-dropdown-btn:hover {
|
|
79
|
+
background: #4a4a4a;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.session-dropdown-content {
|
|
83
|
+
display: none;
|
|
84
|
+
position: absolute;
|
|
85
|
+
background: #2a2a2a;
|
|
86
|
+
border: 1px solid #555;
|
|
87
|
+
border-radius: 4px;
|
|
88
|
+
right: 0;
|
|
89
|
+
top: 100%;
|
|
90
|
+
min-width: 200px;
|
|
91
|
+
z-index: 1000;
|
|
92
|
+
box-shadow: 0 4px 8px rgba(0,0,0,0.3);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.session-dropdown-content.show {
|
|
96
|
+
display: block;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.session-dropdown-item {
|
|
100
|
+
padding: 8px 12px;
|
|
101
|
+
cursor: pointer;
|
|
102
|
+
border-bottom: 1px solid #444;
|
|
103
|
+
display: flex;
|
|
104
|
+
justify-content: space-between;
|
|
105
|
+
align-items: center;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.session-dropdown-item:last-child {
|
|
109
|
+
border-bottom: none;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.session-dropdown-item:hover {
|
|
113
|
+
background: #3a3a3a;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.session-dropdown-item.current {
|
|
117
|
+
background: #4285f4;
|
|
118
|
+
color: white;
|
|
119
|
+
}
|
|
120
|
+
|
|
24
121
|
#terminal {
|
|
25
122
|
padding: 8px; /* Mac Terminal.app padding */
|
|
26
123
|
background-color: #000000;
|
|
27
|
-
height: calc(100% - 16px);
|
|
124
|
+
height: calc(100% - 16px - 40px); /* Subtract session header height */
|
|
28
125
|
width: calc(100% - 16px);
|
|
126
|
+
flex: 1;
|
|
29
127
|
}
|
|
30
128
|
#connect-container { padding: 2em; text-align: center; }
|
|
31
129
|
#agent-id-input { font-size: 1.2em; padding: 8px; width: 400px; margin-bottom: 1em; }
|
|
@@ -95,6 +193,25 @@
|
|
|
95
193
|
<p>Connecting to terminal...</p>
|
|
96
194
|
</div>
|
|
97
195
|
<div id="terminal-container">
|
|
196
|
+
<div class="session-header" id="session-header" style="display: none;">
|
|
197
|
+
<div class="session-info">
|
|
198
|
+
<span class="session-name" id="session-name">Terminal Session</span>
|
|
199
|
+
<span class="session-id" id="session-id"></span>
|
|
200
|
+
</div>
|
|
201
|
+
<div class="session-controls">
|
|
202
|
+
<div class="session-dropdown">
|
|
203
|
+
<button class="session-dropdown-btn" id="session-dropdown-btn">
|
|
204
|
+
<span>Sessions</span>
|
|
205
|
+
<span>▼</span>
|
|
206
|
+
</button>
|
|
207
|
+
<div class="session-dropdown-content" id="session-dropdown-content">
|
|
208
|
+
<div class="session-dropdown-item" onclick="createNewSession()">
|
|
209
|
+
<span>+ New Session</span>
|
|
210
|
+
</div>
|
|
211
|
+
</div>
|
|
212
|
+
</div>
|
|
213
|
+
</div>
|
|
214
|
+
</div>
|
|
98
215
|
<div id="terminal"></div>
|
|
99
216
|
</div>
|
|
100
217
|
|