shell-mirror 1.5.109 → 1.5.111
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/dashboard.css +587 -1104
- package/public/app/dashboard.html +2 -5
- package/public/app/dashboard.js +24 -78
- package/public/app/terminal.html +16 -4
- package/public/app/terminal.js +4 -1
|
@@ -60,7 +60,6 @@
|
|
|
60
60
|
<div class="logo">
|
|
61
61
|
<a href="/" style="text-decoration: none; color: inherit;">
|
|
62
62
|
<h1>Shell Mirror</h1>
|
|
63
|
-
<span class="subtitle">Dashboard</span>
|
|
64
63
|
</a>
|
|
65
64
|
</div>
|
|
66
65
|
<div class="header-right">
|
|
@@ -98,10 +97,8 @@
|
|
|
98
97
|
</div>
|
|
99
98
|
|
|
100
99
|
<!-- Version Footer -->
|
|
101
|
-
<footer style="background: #
|
|
102
|
-
<
|
|
103
|
-
<p id="dashboard-version-info">Shell Mirror Dashboard • Loading version...</p>
|
|
104
|
-
</div>
|
|
100
|
+
<footer id="version-footer" style="background: var(--bg-secondary, #141519); color: var(--text-secondary, #8a8f98); text-align: center; padding: 8px 0; font-size: 0.75rem; position: fixed; bottom: 0; left: 0; right: 0; z-index: 1000; border-top: 1px solid var(--border, #2a2b30);">
|
|
101
|
+
<p id="dashboard-version-info" style="margin: 0;">Shell Mirror • Loading...</p>
|
|
105
102
|
</footer>
|
|
106
103
|
|
|
107
104
|
<script src="dashboard.js"></script>
|
package/public/app/dashboard.js
CHANGED
|
@@ -31,13 +31,13 @@ class ShellMirrorDashboard {
|
|
|
31
31
|
async init() {
|
|
32
32
|
this.showLoading();
|
|
33
33
|
this.loadVersionInfo(); // Load version info immediately
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
// Wait for GA script to load and send page view
|
|
36
36
|
setTimeout(() => {
|
|
37
37
|
console.log('🔍 [DASHBOARD DEBUG] Checking Google Analytics setup...');
|
|
38
38
|
console.log('🔍 [DASHBOARD DEBUG] gtag function type:', typeof gtag);
|
|
39
39
|
console.log('🔍 [DASHBOARD DEBUG] gtagLoaded flag:', window.gtagLoaded);
|
|
40
|
-
|
|
40
|
+
|
|
41
41
|
// Send dashboard page view event
|
|
42
42
|
if (typeof sendGAEvent === 'function') {
|
|
43
43
|
sendGAEvent('page_view', {
|
|
@@ -48,10 +48,10 @@ class ShellMirrorDashboard {
|
|
|
48
48
|
console.warn('❌ [DASHBOARD DEBUG] sendGAEvent function not available');
|
|
49
49
|
}
|
|
50
50
|
}, 1000);
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
try {
|
|
53
53
|
const authStatus = await this.checkAuthStatus();
|
|
54
|
-
|
|
54
|
+
|
|
55
55
|
if (authStatus.isAuthenticated) {
|
|
56
56
|
this.isAuthenticated = true;
|
|
57
57
|
this.user = authStatus.user;
|
|
@@ -501,14 +501,6 @@ class ShellMirrorDashboard {
|
|
|
501
501
|
<div class="dashboard-card">
|
|
502
502
|
${this.renderActiveAgentsPreview()}
|
|
503
503
|
</div>
|
|
504
|
-
|
|
505
|
-
<div class="dashboard-card">
|
|
506
|
-
${this.renderQuickActions()}
|
|
507
|
-
</div>
|
|
508
|
-
|
|
509
|
-
<div class="dashboard-card full-width">
|
|
510
|
-
${this.renderRecentSessionsPreview()}
|
|
511
|
-
</div>
|
|
512
504
|
</div>
|
|
513
505
|
`;
|
|
514
506
|
|
|
@@ -520,32 +512,26 @@ class ShellMirrorDashboard {
|
|
|
520
512
|
}
|
|
521
513
|
|
|
522
514
|
renderAuthenticatedDashboard() {
|
|
523
|
-
// Update user section -
|
|
515
|
+
// Update user section - minimal design
|
|
524
516
|
document.getElementById('user-section').innerHTML = `
|
|
525
|
-
<
|
|
526
|
-
<span id="connection-status" class="connection-status" style="display: none;"></span>
|
|
527
|
-
</div>
|
|
528
|
-
<button class="help-button" onclick="dashboard.showAgentInstructions()" title="How to Use">
|
|
529
|
-
How to Use
|
|
530
|
-
</button>
|
|
517
|
+
<span id="connection-status" class="connection-status" style="display: none;"></span>
|
|
531
518
|
<div class="user-info">
|
|
532
519
|
<span class="user-name">${this.user?.name || this.user?.email || 'User'}</span>
|
|
533
520
|
<div class="user-dropdown">
|
|
534
521
|
<button class="dropdown-btn">⚙️</button>
|
|
535
522
|
<div class="dropdown-content">
|
|
536
|
-
<a href="#" onclick="dashboard.
|
|
523
|
+
<a href="#" onclick="dashboard.showAgentInstructions()">Help</a>
|
|
537
524
|
<a href="/">Home</a>
|
|
525
|
+
<a href="#" onclick="dashboard.logout()">Logout</a>
|
|
538
526
|
</div>
|
|
539
527
|
</div>
|
|
540
528
|
</div>
|
|
541
529
|
`;
|
|
542
530
|
|
|
543
|
-
// Render main dashboard content
|
|
531
|
+
// Render main dashboard content - agents only
|
|
544
532
|
document.getElementById('dashboard-main').innerHTML = `
|
|
545
533
|
<div class="dashboard-grid">
|
|
546
534
|
${this.renderActiveAgents()}
|
|
547
|
-
${this.renderQuickActions()}
|
|
548
|
-
${this.renderRecentSessions()}
|
|
549
535
|
</div>
|
|
550
536
|
`;
|
|
551
537
|
|
|
@@ -588,14 +574,11 @@ class ShellMirrorDashboard {
|
|
|
588
574
|
|
|
589
575
|
// Build inline session list with colors
|
|
590
576
|
const sessionsHtml = sessions.map((session, index) => {
|
|
591
|
-
const sessionStatus = session.status === 'active' ? 'active' : 'crashed';
|
|
592
|
-
const activityText = this.formatLastActivity(session.lastActivity);
|
|
593
577
|
const color = SESSION_COLORS[index % SESSION_COLORS.length];
|
|
594
578
|
return `
|
|
595
579
|
<div class="inline-session-item" style="border-left: 3px solid ${color.border};">
|
|
596
580
|
<span class="session-status-dot" style="background-color: ${color.border};"></span>
|
|
597
|
-
<span class="session-name" style="color: ${color.
|
|
598
|
-
<span class="session-activity">${activityText}</span>
|
|
581
|
+
<span class="session-name" style="color: ${color.border};">${session.name}</span>
|
|
599
582
|
<button class="btn-session-connect" onclick="dashboard.connectToSession('${agent.agentId}', '${session.id}')" style="background-color: ${color.border};">
|
|
600
583
|
Connect
|
|
601
584
|
</button>
|
|
@@ -615,15 +598,13 @@ class ShellMirrorDashboard {
|
|
|
615
598
|
</div>
|
|
616
599
|
<div class="agent-status ${agent.status}">
|
|
617
600
|
${statusIcon} ${statusText}
|
|
618
|
-
${sessionCount > 0 ?
|
|
601
|
+
${sessionCount > 0 ? ` · ${sessionCount} session${sessionCount !== 1 ? 's' : ''}` : ''}
|
|
619
602
|
</div>
|
|
620
|
-
<div class="agent-last-seen">Last seen: ${lastSeenText}</div>
|
|
621
603
|
</div>
|
|
622
604
|
</div>
|
|
623
605
|
${isConnectable ? `
|
|
624
606
|
<div class="agent-sessions-inline">
|
|
625
607
|
${sessionCount > 0 ? `
|
|
626
|
-
<div class="sessions-label">Active Sessions</div>
|
|
627
608
|
<div class="sessions-list">
|
|
628
609
|
${sessionsHtml}
|
|
629
610
|
</div>
|
|
@@ -639,31 +620,11 @@ class ShellMirrorDashboard {
|
|
|
639
620
|
`;
|
|
640
621
|
}).join('');
|
|
641
622
|
|
|
642
|
-
// Check if there are any offline agents to show cleanup option
|
|
643
|
-
const offlineAgents = this.agents.filter(agent => agent.status === 'offline');
|
|
644
|
-
const showCleanup = offlineAgents.length > 0;
|
|
645
|
-
|
|
646
|
-
// Format last refresh time
|
|
647
|
-
const refreshTime = this.lastRefresh
|
|
648
|
-
? new Date(this.lastRefresh).toLocaleTimeString()
|
|
649
|
-
: '<span class="loading-dots"><span>.</span><span>.</span><span>.</span></span>';
|
|
650
|
-
|
|
651
623
|
return `
|
|
652
624
|
<div class="dashboard-card">
|
|
653
625
|
<div class="card-header">
|
|
654
|
-
<
|
|
655
|
-
|
|
656
|
-
<span class="agent-count">${agentCount}</span>
|
|
657
|
-
<span class="refresh-time">${this.lastRefresh ? 'Updated ' : ''}${refreshTime}</span>
|
|
658
|
-
</div>
|
|
659
|
-
<div class="agent-actions-header">
|
|
660
|
-
<button id="refresh-btn" class="btn-text-action" onclick="dashboard.manualRefresh()" title="Refresh agents">
|
|
661
|
-
Refresh
|
|
662
|
-
</button>
|
|
663
|
-
${showCleanup ? `<button class="btn-text-action btn-cleanup" onclick="dashboard.cleanupOfflineAgents()" title="Remove offline agents">
|
|
664
|
-
Clean
|
|
665
|
-
</button>` : ''}
|
|
666
|
-
</div>
|
|
626
|
+
<h2>Agents</h2>
|
|
627
|
+
${agentCount > 0 ? `<span class="agent-count">${agentCount}</span>` : ''}
|
|
667
628
|
</div>
|
|
668
629
|
<div class="card-content">
|
|
669
630
|
${agentCount > 0 ? agentsHtml : this.renderEmptyAgentState()}
|
|
@@ -675,38 +636,23 @@ class ShellMirrorDashboard {
|
|
|
675
636
|
renderEmptyAgentState() {
|
|
676
637
|
return `
|
|
677
638
|
<div class="empty-agent-state">
|
|
678
|
-
<
|
|
679
|
-
<h3>Get Started with Shell Mirror</h3>
|
|
680
|
-
<p>Connect your Mac in 2 simple steps:</p>
|
|
681
|
-
</div>
|
|
682
|
-
|
|
639
|
+
<p class="empty-state-title">No agents connected</p>
|
|
683
640
|
<div class="empty-state-steps">
|
|
684
|
-
<div class="
|
|
685
|
-
<
|
|
686
|
-
<div class="
|
|
687
|
-
<
|
|
688
|
-
<
|
|
689
|
-
<code>npm install -g shell-mirror</code>
|
|
690
|
-
<button class="inline-copy-btn" onclick="navigator.clipboard.writeText('npm install -g shell-mirror'); this.textContent = 'Copied!'; setTimeout(() => this.textContent = 'Copy', 2000)">Copy</button>
|
|
691
|
-
</div>
|
|
641
|
+
<div class="command-step">
|
|
642
|
+
<span class="step-label">1. Install</span>
|
|
643
|
+
<div class="command-box">
|
|
644
|
+
<code>npm install -g shell-mirror</code>
|
|
645
|
+
<button class="copy-btn" onclick="navigator.clipboard.writeText('npm install -g shell-mirror'); this.textContent = '✓'; setTimeout(() => this.textContent = 'Copy', 1500)">Copy</button>
|
|
692
646
|
</div>
|
|
693
647
|
</div>
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
<div class="
|
|
697
|
-
|
|
698
|
-
<
|
|
699
|
-
<div class="inline-command-box">
|
|
700
|
-
<code>shell-mirror</code>
|
|
701
|
-
<button class="inline-copy-btn" onclick="navigator.clipboard.writeText('shell-mirror'); this.textContent = 'Copied!'; setTimeout(() => this.textContent = 'Copy', 2000)">Copy</button>
|
|
702
|
-
</div>
|
|
648
|
+
<div class="command-step">
|
|
649
|
+
<span class="step-label">2. Run</span>
|
|
650
|
+
<div class="command-box">
|
|
651
|
+
<code>shell-mirror</code>
|
|
652
|
+
<button class="copy-btn" onclick="navigator.clipboard.writeText('shell-mirror'); this.textContent = '✓'; setTimeout(() => this.textContent = 'Copy', 1500)">Copy</button>
|
|
703
653
|
</div>
|
|
704
654
|
</div>
|
|
705
655
|
</div>
|
|
706
|
-
|
|
707
|
-
<div class="empty-state-footer">
|
|
708
|
-
<p>Your agent will appear here once connected</p>
|
|
709
|
-
</div>
|
|
710
656
|
</div>
|
|
711
657
|
`;
|
|
712
658
|
}
|
package/public/app/terminal.html
CHANGED
|
@@ -510,10 +510,10 @@
|
|
|
510
510
|
|
|
511
511
|
<!-- Content -->
|
|
512
512
|
<div style="padding: 24px;">
|
|
513
|
-
<div style="display: flex; align-items: center; gap: 16px; margin-bottom: 20px;">
|
|
513
|
+
<div id="close-session-info" style="display: flex; align-items: center; gap: 16px; margin-bottom: 20px; padding-left: 16px; border-left: 4px solid #888;">
|
|
514
514
|
<div style="font-size: 2.5rem;">🗑️</div>
|
|
515
515
|
<div>
|
|
516
|
-
<div id="close-session-name" style="font-size: 1.1rem;
|
|
516
|
+
<div id="close-session-name" style="font-size: 1.1rem; font-weight: 500; margin-bottom: 4px;">Session 1</div>
|
|
517
517
|
<div id="close-session-duration" style="font-size: 0.85rem; color: #888;">Duration: 5 minutes</div>
|
|
518
518
|
</div>
|
|
519
519
|
</div>
|
|
@@ -535,7 +535,7 @@
|
|
|
535
535
|
// Close Session Modal Functions
|
|
536
536
|
let pendingCloseSessionId = null;
|
|
537
537
|
|
|
538
|
-
function showCloseSessionModal(sessionId, sessionName, createdAt) {
|
|
538
|
+
function showCloseSessionModal(sessionId, sessionName, createdAt, sessionColor) {
|
|
539
539
|
pendingCloseSessionId = sessionId;
|
|
540
540
|
|
|
541
541
|
// Calculate duration
|
|
@@ -543,6 +543,18 @@
|
|
|
543
543
|
|
|
544
544
|
document.getElementById('close-session-name').textContent = sessionName || 'Session';
|
|
545
545
|
document.getElementById('close-session-duration').textContent = `Duration: ${duration}`;
|
|
546
|
+
|
|
547
|
+
// Apply session color to border and name
|
|
548
|
+
const infoDiv = document.getElementById('close-session-info');
|
|
549
|
+
const nameDiv = document.getElementById('close-session-name');
|
|
550
|
+
if (sessionColor) {
|
|
551
|
+
infoDiv.style.borderLeftColor = sessionColor.border;
|
|
552
|
+
nameDiv.style.color = sessionColor.border;
|
|
553
|
+
} else {
|
|
554
|
+
infoDiv.style.borderLeftColor = '#888';
|
|
555
|
+
nameDiv.style.color = '#fff';
|
|
556
|
+
}
|
|
557
|
+
|
|
546
558
|
document.getElementById('close-session-modal').style.display = 'flex';
|
|
547
559
|
}
|
|
548
560
|
|
|
@@ -572,6 +584,6 @@
|
|
|
572
584
|
}
|
|
573
585
|
</script>
|
|
574
586
|
|
|
575
|
-
<script src="/app/terminal.js?v=1.5.
|
|
587
|
+
<script src="/app/terminal.js?v=1.5.94"></script>
|
|
576
588
|
</body>
|
|
577
589
|
</html>
|
package/public/app/terminal.js
CHANGED
|
@@ -1061,9 +1061,12 @@ function closeSession(sessionId, event) {
|
|
|
1061
1061
|
const sessionName = session?.name || 'this session';
|
|
1062
1062
|
const createdAt = session?.createdAt || null;
|
|
1063
1063
|
|
|
1064
|
+
// Get session color for modal
|
|
1065
|
+
const sessionColor = getSessionColor(sessionId);
|
|
1066
|
+
|
|
1064
1067
|
// Show custom modal instead of browser confirm()
|
|
1065
1068
|
if (typeof showCloseSessionModal === 'function') {
|
|
1066
|
-
showCloseSessionModal(sessionId, sessionName, createdAt);
|
|
1069
|
+
showCloseSessionModal(sessionId, sessionName, createdAt, sessionColor);
|
|
1067
1070
|
} else {
|
|
1068
1071
|
// Fallback to native confirm if modal not available
|
|
1069
1072
|
if (confirm(`Close "${sessionName}"?\n\nThis will terminate the terminal session.`)) {
|