shell-mirror 1.5.99 โ 1.5.100
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.js +4 -1
- package/package.json +1 -1
- package/public/app/dashboard.css +43 -0
- package/public/app/dashboard.js +13 -10
- package/public/app/terminal.html +1 -1
- package/public/app/terminal.js +40 -3
package/mac-agent/agent.js
CHANGED
|
@@ -169,11 +169,14 @@ class SessionManager {
|
|
|
169
169
|
this.maxSessions = 10;
|
|
170
170
|
this.defaultSessionTimeout = 24 * 60 * 60 * 1000; // 24 hours
|
|
171
171
|
this.clientSessions = {}; // Maps clientId to sessionId
|
|
172
|
+
this.sessionCounter = 0; // Incrementing counter for unique session names (never resets)
|
|
172
173
|
}
|
|
173
174
|
|
|
174
175
|
createSession(sessionName = null, clientId = null) {
|
|
175
176
|
const sessionId = `ses_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
176
|
-
|
|
177
|
+
// Use incrementing counter for unique names (doesn't reuse after deletion)
|
|
178
|
+
this.sessionCounter++;
|
|
179
|
+
const name = sessionName || `Session ${this.sessionCounter}`;
|
|
177
180
|
|
|
178
181
|
logToFile(`[SESSION] Creating new session: ${sessionId} (${name})`);
|
|
179
182
|
|
package/package.json
CHANGED
package/public/app/dashboard.css
CHANGED
|
@@ -225,6 +225,49 @@ body {
|
|
|
225
225
|
transform: scale(1.05);
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
+
/* Text Action Buttons (No Emoji) */
|
|
229
|
+
.btn-text-action {
|
|
230
|
+
background: #f8f9fa;
|
|
231
|
+
border: 1px solid #dee2e6;
|
|
232
|
+
border-radius: 6px;
|
|
233
|
+
color: #495057;
|
|
234
|
+
padding: 6px 12px;
|
|
235
|
+
font-size: 0.8rem;
|
|
236
|
+
font-weight: 500;
|
|
237
|
+
cursor: pointer;
|
|
238
|
+
transition: all 0.15s ease;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.btn-text-action:hover:not(:disabled) {
|
|
242
|
+
background: #e9ecef;
|
|
243
|
+
border-color: #ced4da;
|
|
244
|
+
color: #212529;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
.btn-text-action.loading {
|
|
248
|
+
opacity: 0.7;
|
|
249
|
+
cursor: not-allowed;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.btn-text-action.btn-cleanup {
|
|
253
|
+
background: #fff5f5;
|
|
254
|
+
border-color: #feb2b2;
|
|
255
|
+
color: #c53030;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.btn-text-action.btn-cleanup:hover:not(:disabled) {
|
|
259
|
+
background: #fed7d7;
|
|
260
|
+
border-color: #fc8181;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/* Refresh Time in Card Header */
|
|
264
|
+
.refresh-time {
|
|
265
|
+
font-size: 0.75rem;
|
|
266
|
+
color: #718096;
|
|
267
|
+
font-weight: 400;
|
|
268
|
+
margin-left: 8px;
|
|
269
|
+
}
|
|
270
|
+
|
|
228
271
|
.connection-status {
|
|
229
272
|
font-size: 0.8rem;
|
|
230
273
|
font-weight: 500;
|
package/public/app/dashboard.js
CHANGED
|
@@ -513,10 +513,9 @@ class ShellMirrorDashboard {
|
|
|
513
513
|
document.getElementById('user-section').innerHTML = `
|
|
514
514
|
<div class="dashboard-controls">
|
|
515
515
|
<span id="connection-status" class="connection-status" style="display: none;"></span>
|
|
516
|
-
<span id="refresh-status" class="refresh-status">Loading...</span>
|
|
517
516
|
</div>
|
|
518
517
|
<button class="help-button" onclick="dashboard.showAgentInstructions()" title="How to Use">
|
|
519
|
-
|
|
518
|
+
How to Use
|
|
520
519
|
</button>
|
|
521
520
|
<div class="user-info">
|
|
522
521
|
<span class="user-name">${this.user?.name || this.user?.email || 'User'}</span>
|
|
@@ -632,19 +631,23 @@ class ShellMirrorDashboard {
|
|
|
632
631
|
const offlineAgents = this.agents.filter(agent => agent.status === 'offline');
|
|
633
632
|
const showCleanup = offlineAgents.length > 0;
|
|
634
633
|
|
|
634
|
+
// Format last refresh time
|
|
635
|
+
const refreshTime = this.lastRefresh ? new Date(this.lastRefresh).toLocaleTimeString() : 'Loading...';
|
|
636
|
+
|
|
635
637
|
return `
|
|
636
638
|
<div class="dashboard-card">
|
|
637
639
|
<div class="card-header">
|
|
638
640
|
<div class="card-title-section">
|
|
639
|
-
<h2
|
|
640
|
-
<span class="agent-count">${agentCount}
|
|
641
|
+
<h2>Active Agents</h2>
|
|
642
|
+
<span class="agent-count">${agentCount}</span>
|
|
643
|
+
<span class="refresh-time">Updated ${refreshTime}</span>
|
|
641
644
|
</div>
|
|
642
645
|
<div class="agent-actions-header">
|
|
643
|
-
<button id="refresh-btn" class="
|
|
644
|
-
|
|
646
|
+
<button id="refresh-btn" class="btn-text-action" onclick="dashboard.manualRefresh()" title="Refresh agents">
|
|
647
|
+
Refresh
|
|
645
648
|
</button>
|
|
646
|
-
${showCleanup ? `<button class="
|
|
647
|
-
|
|
649
|
+
${showCleanup ? `<button class="btn-text-action btn-cleanup" onclick="dashboard.cleanupOfflineAgents()" title="Remove offline agents">
|
|
650
|
+
Clean
|
|
648
651
|
</button>` : ''}
|
|
649
652
|
</div>
|
|
650
653
|
</div>
|
|
@@ -659,7 +662,7 @@ class ShellMirrorDashboard {
|
|
|
659
662
|
return `
|
|
660
663
|
<div class="empty-agent-state">
|
|
661
664
|
<div class="empty-state-header">
|
|
662
|
-
<h3
|
|
665
|
+
<h3>Get Started with Shell Mirror</h3>
|
|
663
666
|
<p>Connect your Mac in 2 simple steps:</p>
|
|
664
667
|
</div>
|
|
665
668
|
|
|
@@ -688,7 +691,7 @@ class ShellMirrorDashboard {
|
|
|
688
691
|
</div>
|
|
689
692
|
|
|
690
693
|
<div class="empty-state-footer">
|
|
691
|
-
<p
|
|
694
|
+
<p>Your agent will appear here once connected</p>
|
|
692
695
|
</div>
|
|
693
696
|
</div>
|
|
694
697
|
`;
|
package/public/app/terminal.html
CHANGED
package/public/app/terminal.js
CHANGED
|
@@ -965,6 +965,28 @@ function updateSessionDisplay() {
|
|
|
965
965
|
}
|
|
966
966
|
}
|
|
967
967
|
|
|
968
|
+
// Session tab color palette (fixed colors by creation order)
|
|
969
|
+
const SESSION_TAB_COLORS = [
|
|
970
|
+
{ bg: '#e3f2fd', border: '#2196f3', text: '#1565c0' }, // Blue
|
|
971
|
+
{ bg: '#e8f5e9', border: '#4caf50', text: '#2e7d32' }, // Green
|
|
972
|
+
{ bg: '#fff3e0', border: '#ff9800', text: '#e65100' }, // Orange
|
|
973
|
+
{ bg: '#f3e5f5', border: '#9c27b0', text: '#6a1b9a' }, // Purple
|
|
974
|
+
{ bg: '#e0f7fa', border: '#00bcd4', text: '#00838f' }, // Teal
|
|
975
|
+
{ bg: '#fce4ec', border: '#e91e63', text: '#ad1457' }, // Pink
|
|
976
|
+
];
|
|
977
|
+
|
|
978
|
+
// Track color assignments by session ID (persists across renders)
|
|
979
|
+
const sessionColorMap = {};
|
|
980
|
+
let nextColorIndex = 0;
|
|
981
|
+
|
|
982
|
+
function getSessionColor(sessionId) {
|
|
983
|
+
if (!sessionColorMap[sessionId]) {
|
|
984
|
+
sessionColorMap[sessionId] = nextColorIndex;
|
|
985
|
+
nextColorIndex = (nextColorIndex + 1) % SESSION_TAB_COLORS.length;
|
|
986
|
+
}
|
|
987
|
+
return SESSION_TAB_COLORS[sessionColorMap[sessionId]];
|
|
988
|
+
}
|
|
989
|
+
|
|
968
990
|
function renderTabs() {
|
|
969
991
|
const tabBar = document.getElementById('session-tab-bar');
|
|
970
992
|
if (!tabBar) {
|
|
@@ -996,15 +1018,24 @@ function renderTabs() {
|
|
|
996
1018
|
tabsHTML = sessionsToRender.map(session => {
|
|
997
1019
|
const isActive = currentSession && session.id === currentSession.id;
|
|
998
1020
|
const displayName = session.name || 'Terminal Session';
|
|
1021
|
+
const color = getSessionColor(session.id);
|
|
1022
|
+
|
|
1023
|
+
// Active tabs get full color, inactive tabs get muted version
|
|
1024
|
+
const tabStyle = isActive
|
|
1025
|
+
? `background: ${color.bg}; border-color: ${color.border}; border-bottom: 3px solid ${color.border};`
|
|
1026
|
+
: `background: transparent; border-color: transparent; opacity: 0.7;`;
|
|
1027
|
+
const textStyle = isActive
|
|
1028
|
+
? `color: ${color.text}; font-weight: 600;`
|
|
1029
|
+
: `color: #888;`;
|
|
999
1030
|
|
|
1000
1031
|
return `
|
|
1001
|
-
<div class="session-tab ${isActive ? 'active' : ''}" title="${displayName}">
|
|
1032
|
+
<div class="session-tab ${isActive ? 'active' : ''}" style="${tabStyle}" title="${displayName}" data-color-index="${sessionColorMap[session.id]}">
|
|
1002
1033
|
<button class="session-tab-btn"
|
|
1003
1034
|
onclick="switchToSession('${session.id}')"
|
|
1004
|
-
${
|
|
1035
|
+
style="${textStyle}">
|
|
1005
1036
|
<span class="session-tab-name">${displayName}</span>
|
|
1006
1037
|
</button>
|
|
1007
|
-
<button class="session-tab-close" onclick="closeSession('${session.id}', event)" title="Close session">ร</button>
|
|
1038
|
+
<button class="session-tab-close" onclick="closeSession('${session.id}', event)" title="Close session" style="color: ${isActive ? color.text : '#888'}">ร</button>
|
|
1008
1039
|
</div>
|
|
1009
1040
|
`;
|
|
1010
1041
|
}).join('');
|
|
@@ -1192,6 +1223,9 @@ function handleSessionMessage(message) {
|
|
|
1192
1223
|
|
|
1193
1224
|
// Save to localStorage
|
|
1194
1225
|
saveSessionToLocalStorage(AGENT_ID, currentSession);
|
|
1226
|
+
|
|
1227
|
+
// Focus terminal for keyboard input
|
|
1228
|
+
term.focus();
|
|
1195
1229
|
break;
|
|
1196
1230
|
|
|
1197
1231
|
case 'session-switched':
|
|
@@ -1208,6 +1242,9 @@ function handleSessionMessage(message) {
|
|
|
1208
1242
|
|
|
1209
1243
|
// Save updated session info
|
|
1210
1244
|
saveSessionToLocalStorage(AGENT_ID, currentSession);
|
|
1245
|
+
|
|
1246
|
+
// Focus terminal for keyboard input
|
|
1247
|
+
term.focus();
|
|
1211
1248
|
break;
|
|
1212
1249
|
case 'session-ended':
|
|
1213
1250
|
term.write(`\r\n\x1b[31mโ Session ended: ${message.reason}\x1b[0m\r\n`);
|