shell-mirror 1.5.39 → 1.5.41
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 +372 -64
- package/package.json +1 -1
- package/public/app/dashboard.css +172 -0
- package/public/app/dashboard.js +150 -6
- package/public/app/terminal.html +146 -3
- package/public/app/terminal.js +209 -5
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
|
|
|
@@ -20,6 +21,7 @@ class ShellMirrorDashboard {
|
|
|
20
21
|
this.user = authStatus.user;
|
|
21
22
|
await this.loadDashboardData();
|
|
22
23
|
this.renderAuthenticatedDashboard();
|
|
24
|
+
this.startAutoRefresh(); // Start auto-refresh for authenticated users
|
|
23
25
|
} else {
|
|
24
26
|
this.renderUnauthenticatedDashboard();
|
|
25
27
|
}
|
|
@@ -31,6 +33,24 @@ class ShellMirrorDashboard {
|
|
|
31
33
|
}
|
|
32
34
|
}
|
|
33
35
|
|
|
36
|
+
startAutoRefresh() {
|
|
37
|
+
// Refresh agent data every 30 seconds to detect disconnected agents
|
|
38
|
+
this.refreshInterval = setInterval(async () => {
|
|
39
|
+
if (this.isAuthenticated) {
|
|
40
|
+
await this.loadDashboardData();
|
|
41
|
+
// Only re-render the agents section to avoid full page flash
|
|
42
|
+
this.updateAgentsDisplay();
|
|
43
|
+
}
|
|
44
|
+
}, 30000);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
updateAgentsDisplay() {
|
|
48
|
+
const agentsCard = document.querySelector('.dashboard-card');
|
|
49
|
+
if (agentsCard) {
|
|
50
|
+
agentsCard.innerHTML = this.renderActiveAgents();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
34
54
|
showLoading() {
|
|
35
55
|
document.getElementById('loading-overlay').style.display = 'flex';
|
|
36
56
|
}
|
|
@@ -61,6 +81,31 @@ class ShellMirrorDashboard {
|
|
|
61
81
|
|
|
62
82
|
if (agentsData.success && agentsData.data && agentsData.data.agents) {
|
|
63
83
|
this.agents = agentsData.data.agents;
|
|
84
|
+
|
|
85
|
+
// Simulate session data for each agent
|
|
86
|
+
this.agents.forEach(agent => {
|
|
87
|
+
if (agent.onlineStatus === 'online') {
|
|
88
|
+
// Mock sessions for online agents
|
|
89
|
+
this.agentSessions[agent.agentId] = [
|
|
90
|
+
{
|
|
91
|
+
id: `ses_${Date.now()}_main`,
|
|
92
|
+
name: 'Main Terminal',
|
|
93
|
+
lastActivity: Date.now() - 5 * 60 * 1000, // 5 minutes ago
|
|
94
|
+
createdAt: Date.now() - 2 * 60 * 60 * 1000, // 2 hours ago
|
|
95
|
+
status: 'active',
|
|
96
|
+
connectedClients: 0
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
id: `ses_${Date.now()}_debug`,
|
|
100
|
+
name: 'Debug Server',
|
|
101
|
+
lastActivity: Date.now() - 15 * 60 * 1000, // 15 minutes ago
|
|
102
|
+
createdAt: Date.now() - 1 * 60 * 60 * 1000, // 1 hour ago
|
|
103
|
+
status: 'active',
|
|
104
|
+
connectedClients: 0
|
|
105
|
+
}
|
|
106
|
+
];
|
|
107
|
+
}
|
|
108
|
+
});
|
|
64
109
|
}
|
|
65
110
|
|
|
66
111
|
// TODO: Load session history when API is available
|
|
@@ -155,18 +200,29 @@ class ShellMirrorDashboard {
|
|
|
155
200
|
});
|
|
156
201
|
|
|
157
202
|
const agentCount = activeAgents.length;
|
|
158
|
-
const agentsHtml = activeAgents.map(agent =>
|
|
203
|
+
const agentsHtml = activeAgents.map(agent => {
|
|
204
|
+
const sessions = this.agentSessions[agent.agentId] || [];
|
|
205
|
+
const sessionCount = sessions.length;
|
|
206
|
+
|
|
207
|
+
return `
|
|
159
208
|
<div class="agent-item">
|
|
160
209
|
<div class="agent-info">
|
|
161
210
|
<div class="agent-name">${agent.machineName || agent.agentId}</div>
|
|
162
211
|
<div class="agent-status ${agent.onlineStatus}">${agent.onlineStatus}</div>
|
|
163
212
|
<div class="agent-last-seen">Last seen: ${this.formatLastSeen(agent.lastSeen)}</div>
|
|
213
|
+
${sessionCount > 0 ? `<div class="agent-sessions">${sessionCount} active session${sessionCount !== 1 ? 's' : ''}</div>` : ''}
|
|
214
|
+
</div>
|
|
215
|
+
<div class="agent-actions">
|
|
216
|
+
<button class="btn-connect" onclick="dashboard.connectToAgent('${agent.agentId}')">
|
|
217
|
+
New Session
|
|
218
|
+
</button>
|
|
219
|
+
${sessionCount > 0 ? `<button class="btn-sessions" onclick="dashboard.showAgentSessions('${agent.agentId}')">
|
|
220
|
+
View Sessions
|
|
221
|
+
</button>` : ''}
|
|
164
222
|
</div>
|
|
165
|
-
<button class="btn-connect" onclick="dashboard.connectToAgent('${agent.agentId}')">
|
|
166
|
-
Connect
|
|
167
|
-
</button>
|
|
168
223
|
</div>
|
|
169
|
-
|
|
224
|
+
`;
|
|
225
|
+
}).join('');
|
|
170
226
|
|
|
171
227
|
return `
|
|
172
228
|
<div class="dashboard-card">
|
|
@@ -382,8 +438,96 @@ class ShellMirrorDashboard {
|
|
|
382
438
|
window.location.href = `/app/terminal.html?agent=${agentId}`;
|
|
383
439
|
}
|
|
384
440
|
|
|
441
|
+
async connectToSession(agentId, sessionId) {
|
|
442
|
+
window.location.href = `/app/terminal.html?agent=${agentId}&session=${sessionId}`;
|
|
443
|
+
}
|
|
444
|
+
|
|
385
445
|
startNewSession() {
|
|
386
|
-
|
|
446
|
+
// Get first available agent for new session
|
|
447
|
+
const activeAgents = this.agents.filter(agent => {
|
|
448
|
+
const timeSinceLastSeen = Date.now() / 1000 - agent.lastSeen;
|
|
449
|
+
return agent.onlineStatus === 'online' || timeSinceLastSeen < 300;
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
if (activeAgents.length > 0) {
|
|
453
|
+
this.connectToAgent(activeAgents[0].agentId);
|
|
454
|
+
} else {
|
|
455
|
+
alert('No active agents available. Please ensure an agent is running on your Mac.');
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
showAgentSessions(agentId) {
|
|
460
|
+
const sessions = this.agentSessions[agentId] || [];
|
|
461
|
+
const agent = this.agents.find(a => a.agentId === agentId);
|
|
462
|
+
const agentName = agent ? (agent.machineName || agent.agentId) : agentId;
|
|
463
|
+
|
|
464
|
+
if (sessions.length === 0) {
|
|
465
|
+
alert(`No active sessions found for ${agentName}`);
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// Create session list modal
|
|
470
|
+
const sessionsList = sessions.map(session => `
|
|
471
|
+
<div class="session-list-item" onclick="dashboard.connectToSession('${agentId}', '${session.id}')">
|
|
472
|
+
<div class="session-list-info">
|
|
473
|
+
<div class="session-list-name">${session.name}</div>
|
|
474
|
+
<div class="session-list-details">
|
|
475
|
+
<span class="session-list-id">${session.id.substring(0, 8)}...</span>
|
|
476
|
+
<span class="session-list-activity">Last activity: ${this.formatLastActivity(session.lastActivity)}</span>
|
|
477
|
+
</div>
|
|
478
|
+
</div>
|
|
479
|
+
<div class="session-list-status ${session.status}">${session.status}</div>
|
|
480
|
+
</div>
|
|
481
|
+
`).join('');
|
|
482
|
+
|
|
483
|
+
// Show modal with sessions
|
|
484
|
+
this.showModal(`Sessions on ${agentName}`, `
|
|
485
|
+
<div class="sessions-modal-content">
|
|
486
|
+
<p>Active terminal sessions (${sessions.length}):</p>
|
|
487
|
+
<div class="session-list">
|
|
488
|
+
${sessionsList}
|
|
489
|
+
</div>
|
|
490
|
+
<div class="sessions-modal-actions">
|
|
491
|
+
<button class="btn-primary" onclick="dashboard.connectToAgent('${agentId}')">
|
|
492
|
+
+ Create New Session
|
|
493
|
+
</button>
|
|
494
|
+
</div>
|
|
495
|
+
</div>
|
|
496
|
+
`);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
showModal(title, content) {
|
|
500
|
+
// Create modal overlay
|
|
501
|
+
const modalOverlay = document.createElement('div');
|
|
502
|
+
modalOverlay.className = 'modal-overlay';
|
|
503
|
+
modalOverlay.onclick = () => document.body.removeChild(modalOverlay);
|
|
504
|
+
|
|
505
|
+
modalOverlay.innerHTML = `
|
|
506
|
+
<div class="modal" onclick="event.stopPropagation()">
|
|
507
|
+
<div class="modal-header">
|
|
508
|
+
<h3>${title}</h3>
|
|
509
|
+
<button class="modal-close" onclick="document.body.removeChild(this.closest('.modal-overlay'))">×</button>
|
|
510
|
+
</div>
|
|
511
|
+
<div class="modal-body">
|
|
512
|
+
${content}
|
|
513
|
+
</div>
|
|
514
|
+
</div>
|
|
515
|
+
`;
|
|
516
|
+
|
|
517
|
+
document.body.appendChild(modalOverlay);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
formatLastActivity(timestamp) {
|
|
521
|
+
const now = Date.now();
|
|
522
|
+
const diff = now - timestamp;
|
|
523
|
+
const minutes = Math.floor(diff / 60000);
|
|
524
|
+
const hours = Math.floor(diff / 3600000);
|
|
525
|
+
const days = Math.floor(diff / 86400000);
|
|
526
|
+
|
|
527
|
+
if (minutes < 1) return 'now';
|
|
528
|
+
if (minutes < 60) return `${minutes}m ago`;
|
|
529
|
+
if (hours < 24) return `${hours}h ago`;
|
|
530
|
+
return `${days}d ago`;
|
|
387
531
|
}
|
|
388
532
|
|
|
389
533
|
showAgentInstructions() {
|
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; }
|
|
@@ -35,7 +133,7 @@
|
|
|
35
133
|
.back-to-dashboard {
|
|
36
134
|
position: fixed;
|
|
37
135
|
top: 20px;
|
|
38
|
-
|
|
136
|
+
right: 20px;
|
|
39
137
|
background: rgba(66, 133, 244, 0.9);
|
|
40
138
|
color: white;
|
|
41
139
|
border: none;
|
|
@@ -55,11 +153,37 @@
|
|
|
55
153
|
background: rgba(51, 103, 214, 0.9);
|
|
56
154
|
transform: translateY(-1px);
|
|
57
155
|
}
|
|
156
|
+
|
|
157
|
+
/* Connection Status Indicator */
|
|
158
|
+
.connection-status {
|
|
159
|
+
width: 8px;
|
|
160
|
+
height: 8px;
|
|
161
|
+
border-radius: 50%;
|
|
162
|
+
background: #ff4444;
|
|
163
|
+
margin-right: 4px;
|
|
164
|
+
transition: all 0.3s ease;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.connection-status.connected {
|
|
168
|
+
background: #44ff44;
|
|
169
|
+
box-shadow: 0 0 8px rgba(68, 255, 68, 0.5);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.connection-status.connecting {
|
|
173
|
+
background: #ffaa44;
|
|
174
|
+
animation: pulse 1.5s ease-in-out infinite alternate;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
@keyframes pulse {
|
|
178
|
+
from { opacity: 1; }
|
|
179
|
+
to { opacity: 0.4; }
|
|
180
|
+
}
|
|
58
181
|
</style>
|
|
59
182
|
</head>
|
|
60
183
|
<body>
|
|
61
184
|
<!-- Back to Dashboard Button -->
|
|
62
|
-
<a href="/app/dashboard.html" class="back-to-dashboard">
|
|
185
|
+
<a href="/app/dashboard.html" class="back-to-dashboard" id="dashboard-btn">
|
|
186
|
+
<div class="connection-status" id="connection-status"></div>
|
|
63
187
|
<span>←</span>
|
|
64
188
|
<span>Dashboard</span>
|
|
65
189
|
</a>
|
|
@@ -69,6 +193,25 @@
|
|
|
69
193
|
<p>Connecting to terminal...</p>
|
|
70
194
|
</div>
|
|
71
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>
|
|
72
215
|
<div id="terminal"></div>
|
|
73
216
|
</div>
|
|
74
217
|
|