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.
@@ -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
- const name = sessionName || `Session ${Object.keys(this.sessions).length + 1}`;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shell-mirror",
3
- "version": "1.5.99",
3
+ "version": "1.5.100",
4
4
  "description": "Access your Mac shell from any device securely. Perfect for mobile coding with Claude Code CLI, Gemini CLI, and any shell tool.",
5
5
  "main": "server.js",
6
6
  "bin": {
@@ -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;
@@ -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
- ๐Ÿ“– How to Use
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>๐Ÿ–ฅ๏ธ Active Agents</h2>
640
- <span class="agent-count">${agentCount} agent${agentCount !== 1 ? 's' : ''}</span>
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="refresh-btn-inline" onclick="dashboard.manualRefresh()" title="Refresh agents">
644
- <span class="refresh-icon">๐Ÿ”„</span>
646
+ <button id="refresh-btn" class="btn-text-action" onclick="dashboard.manualRefresh()" title="Refresh agents">
647
+ Refresh
645
648
  </button>
646
- ${showCleanup ? `<button class="cleanup-btn-inline" onclick="dashboard.cleanupOfflineAgents()" title="Remove offline agents">
647
- <span>๐Ÿงน</span>
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>๐Ÿš€ Get Started with Shell Mirror</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>โœจ Your agent will appear here once connected</p>
694
+ <p>Your agent will appear here once connected</p>
692
695
  </div>
693
696
  </div>
694
697
  `;
@@ -579,6 +579,6 @@
579
579
  }
580
580
  </script>
581
581
 
582
- <script src="/app/terminal.js?v=1.5.88"></script>
582
+ <script src="/app/terminal.js?v=1.5.89"></script>
583
583
  </body>
584
584
  </html>
@@ -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
- ${isActive ? '' : ''}>
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`);