shell-mirror 1.5.76 → 1.5.77

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shell-mirror",
3
- "version": "1.5.76",
3
+ "version": "1.5.77",
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": {
@@ -76,7 +76,7 @@
76
76
  flex-direction: column;
77
77
  }
78
78
 
79
- /* Session Header */
79
+ /* Session Header - Unified Design */
80
80
  .session-header {
81
81
  background: #2a2a2a;
82
82
  color: #ccc;
@@ -87,64 +87,157 @@
87
87
  justify-content: space-between;
88
88
  font-size: 0.9em;
89
89
  z-index: 100;
90
+ height: 40px;
90
91
  }
91
-
92
- .session-info {
92
+
93
+ .header-left {
93
94
  display: flex;
94
95
  align-items: center;
95
- gap: 12px;
96
+ gap: 10px;
97
+ position: relative;
96
98
  }
97
-
98
- .session-name {
99
- font-weight: bold;
100
- color: #fff;
99
+
100
+ .header-center {
101
+ flex: 1;
102
+ display: flex;
103
+ justify-content: center;
101
104
  }
102
-
103
- .session-controls {
105
+
106
+ .header-right {
104
107
  display: flex;
105
108
  align-items: center;
106
109
  gap: 8px;
107
110
  }
108
-
111
+
112
+ /* Session Info Button */
113
+ .session-info-btn {
114
+ background: transparent;
115
+ border: 1px solid #444;
116
+ color: #ccc;
117
+ padding: 4px 12px;
118
+ border-radius: 4px;
119
+ cursor: pointer;
120
+ display: flex;
121
+ align-items: center;
122
+ gap: 8px;
123
+ font-size: 0.9em;
124
+ transition: all 0.2s ease;
125
+ }
126
+
127
+ .session-info-btn:hover {
128
+ background: #3a3a3a;
129
+ border-color: #667eea;
130
+ }
131
+
132
+ .session-name {
133
+ font-weight: bold;
134
+ color: #fff;
135
+ }
136
+
137
+ .session-id {
138
+ color: #999;
139
+ font-size: 0.85em;
140
+ }
141
+
142
+ .dropdown-arrow {
143
+ color: #888;
144
+ font-size: 0.7em;
145
+ }
146
+
147
+ /* Session Info Dropdown */
148
+ .session-info-dropdown {
149
+ display: none;
150
+ position: absolute;
151
+ top: 100%;
152
+ left: 0;
153
+ margin-top: 8px;
154
+ background: #2a2a2a;
155
+ border: 1px solid #555;
156
+ border-radius: 4px;
157
+ min-width: 280px;
158
+ box-shadow: 0 4px 12px rgba(0,0,0,0.5);
159
+ z-index: 1000;
160
+ }
161
+
162
+ .session-info-dropdown.show {
163
+ display: block;
164
+ }
165
+
166
+ .dropdown-section {
167
+ padding: 12px 16px;
168
+ }
169
+
170
+ .dropdown-divider {
171
+ height: 1px;
172
+ background: #444;
173
+ }
174
+
175
+ .session-detail-name {
176
+ font-weight: bold;
177
+ color: #fff;
178
+ margin-bottom: 4px;
179
+ }
180
+
181
+ .session-detail-id {
182
+ color: #999;
183
+ font-size: 0.85em;
184
+ font-family: monospace;
185
+ }
186
+
187
+ .connection-detail {
188
+ color: #ccc;
189
+ font-size: 0.9em;
190
+ }
191
+
192
+ .version-info {
193
+ color: #888;
194
+ font-size: 0.85em;
195
+ }
196
+
197
+ /* Sessions Dropdown */
109
198
  .session-dropdown {
110
199
  position: relative;
111
200
  display: inline-block;
112
201
  }
113
-
202
+
114
203
  .session-dropdown-btn {
115
- background: #3a3a3a;
204
+ background: transparent;
116
205
  color: #ccc;
117
- border: 1px solid #555;
118
- padding: 4px 8px;
206
+ border: 1px solid #444;
207
+ padding: 4px 12px;
119
208
  border-radius: 4px;
120
209
  cursor: pointer;
121
- font-size: 0.8em;
210
+ font-size: 0.9em;
122
211
  display: flex;
123
212
  align-items: center;
124
213
  gap: 4px;
214
+ transition: all 0.2s ease;
125
215
  }
126
-
216
+
127
217
  .session-dropdown-btn:hover {
128
- background: #4a4a4a;
218
+ background: #3a3a3a;
219
+ border-color: #667eea;
129
220
  }
130
-
221
+
131
222
  .session-dropdown-content {
132
223
  display: none;
133
224
  position: absolute;
134
225
  background: #2a2a2a;
135
226
  border: 1px solid #555;
136
227
  border-radius: 4px;
137
- right: 0;
228
+ left: 50%;
229
+ transform: translateX(-50%);
138
230
  top: 100%;
231
+ margin-top: 8px;
139
232
  min-width: 200px;
140
233
  z-index: 1000;
141
234
  box-shadow: 0 4px 8px rgba(0,0,0,0.3);
142
235
  }
143
-
236
+
144
237
  .session-dropdown-content.show {
145
238
  display: block;
146
239
  }
147
-
240
+
148
241
  .session-dropdown-item {
149
242
  padding: 8px 12px;
150
243
  cursor: pointer;
@@ -153,82 +246,79 @@
153
246
  justify-content: space-between;
154
247
  align-items: center;
155
248
  }
156
-
249
+
157
250
  .session-dropdown-item:last-child {
158
251
  border-bottom: none;
159
252
  }
160
-
253
+
161
254
  .session-dropdown-item:hover {
162
255
  background: #3a3a3a;
163
256
  }
164
-
257
+
165
258
  .session-dropdown-item.current {
166
259
  background: #4285f4;
167
260
  color: white;
168
261
  }
169
-
170
- #terminal {
171
- padding: 8px; /* Mac Terminal.app padding */
172
- background-color: #000000;
173
- height: calc(100% - 16px - 40px); /* Subtract session header height */
174
- width: calc(100% - 16px);
175
- flex: 1;
176
- }
177
- #connect-container { padding: 2em; text-align: center; }
178
- #agent-id-input { font-size: 1.2em; padding: 8px; width: 400px; margin-bottom: 1em; }
179
- #connect-btn { font-size: 1.2em; padding: 10px 20px; }
180
-
181
- /* How to Use Button */
182
- .how-to-use-btn {
183
- position: fixed;
184
- top: 20px;
185
- left: 170px;
186
- background: rgba(102, 126, 234, 0.9);
187
- color: white;
188
- border: none;
189
- border-radius: 8px;
190
- padding: 10px 16px;
262
+
263
+ /* Header Buttons */
264
+ .header-btn {
265
+ background: transparent;
266
+ border: 1px solid #444;
267
+ color: #ccc;
268
+ padding: 6px 12px;
269
+ border-radius: 4px;
191
270
  cursor: pointer;
192
- font-size: 0.9em;
193
- font-weight: 500;
194
271
  display: flex;
195
272
  align-items: center;
196
273
  gap: 6px;
197
- z-index: 10000;
198
- backdrop-filter: blur(10px);
274
+ font-size: 0.9em;
275
+ text-decoration: none;
199
276
  transition: all 0.2s ease;
200
277
  }
201
278
 
202
- .how-to-use-btn:hover {
203
- background: rgba(102, 126, 234, 1);
204
- transform: translateY(-1px);
279
+ .header-btn:hover {
280
+ background: #3a3a3a;
281
+ border-color: #667eea;
205
282
  }
206
283
 
207
- /* Back to Dashboard Button */
208
- .back-to-dashboard {
209
- position: fixed;
210
- top: 20px;
211
- right: 20px;
212
- background: rgba(66, 133, 244, 0.9);
213
- color: white;
214
- border: none;
215
- padding: 12px 20px;
216
- border-radius: 8px;
217
- font-weight: 500;
218
- cursor: pointer;
219
- z-index: 1000;
220
- transition: all 0.2s ease;
221
- display: flex;
222
- align-items: center;
223
- gap: 8px;
224
- text-decoration: none;
284
+ .help-btn .btn-text {
285
+ display: none;
225
286
  }
226
-
227
- .back-to-dashboard:hover {
228
- background: rgba(51, 103, 214, 0.9);
229
- transform: translateY(-1px);
287
+
288
+ @media (min-width: 768px) {
289
+ .help-btn .btn-text {
290
+ display: inline;
291
+ }
292
+ }
293
+
294
+ /* Dashboard button with status dot */
295
+ .dashboard-btn {
296
+ position: relative;
230
297
  }
231
298
 
299
+ .dashboard-btn::before {
300
+ content: '';
301
+ position: absolute;
302
+ top: 4px;
303
+ right: 4px;
304
+ width: 8px;
305
+ height: 8px;
306
+ border-radius: 50%;
307
+ background: #44ff44;
308
+ box-shadow: 0 0 8px rgba(68, 255, 68, 0.5);
309
+ }
310
+
311
+ #terminal {
312
+ padding: 8px; /* Mac Terminal.app padding */
313
+ background-color: #000000;
314
+ height: calc(100% - 16px - 40px); /* Subtract session header height */
315
+ width: calc(100% - 16px);
316
+ flex: 1;
317
+ }
318
+ #connect-container { padding: 2em; text-align: center; }
319
+ #agent-id-input { font-size: 1.2em; padding: 8px; width: 400px; margin-bottom: 1em; }
320
+ #connect-btn { font-size: 1.2em; padding: 10px 20px; }
321
+
232
322
  /* Connection Status Indicator */
233
323
  .connection-status {
234
324
  width: 8px;
@@ -256,29 +346,40 @@
256
346
  </style>
257
347
  </head>
258
348
  <body>
259
- <!-- Back to Dashboard Button -->
260
- <a href="/app/dashboard.html" class="back-to-dashboard" id="dashboard-btn">
261
- <div class="connection-status" id="connection-status"></div>
262
- <span>←</span>
263
- <span>Dashboard</span>
264
- </a>
265
-
266
- <!-- How to Use Button -->
267
- <button class="how-to-use-btn" id="how-to-use-btn" onclick="showHelpModal()">
268
- 📖 How to Use
269
- </button>
270
-
271
349
  <div id="connect-container">
272
350
  <h2>Terminal Mirror</h2>
273
351
  <p>Connecting to terminal...</p>
274
352
  </div>
275
353
  <div id="terminal-container">
276
354
  <div class="session-header" id="session-header" style="display: none;">
277
- <div class="session-info">
278
- <span class="session-name" id="session-name">Terminal Session</span>
279
- <span class="session-id" id="session-id"></span>
355
+ <div class="header-left">
356
+ <div class="connection-status" id="connection-status"></div>
357
+ <button class="session-info-btn" id="session-info-btn">
358
+ <span class="session-name" id="session-name">Terminal Session</span>
359
+ <span class="session-id" id="session-id"></span>
360
+ <span class="dropdown-arrow">▼</span>
361
+ </button>
362
+
363
+ <!-- Session Info Dropdown -->
364
+ <div class="session-info-dropdown" id="session-info-dropdown">
365
+ <div class="dropdown-section">
366
+ <div class="session-detail-name" id="detail-name">Session 1</div>
367
+ <div class="session-detail-id" id="detail-id">ses_1764...</div>
368
+ </div>
369
+ <div class="dropdown-divider"></div>
370
+ <div class="dropdown-section">
371
+ <div class="connection-detail" id="connection-detail">
372
+ 🟢 Connected via WebRTC
373
+ </div>
374
+ </div>
375
+ <div class="dropdown-divider"></div>
376
+ <div class="dropdown-section version-info">
377
+ <div id="version-info-dropdown">v1.5.76 • Built Nov 26, 2025</div>
378
+ </div>
379
+ </div>
280
380
  </div>
281
- <div class="session-controls">
381
+
382
+ <div class="header-center">
282
383
  <div class="session-dropdown">
283
384
  <button class="session-dropdown-btn" id="session-dropdown-btn">
284
385
  <span>Sessions</span>
@@ -291,6 +392,16 @@
291
392
  </div>
292
393
  </div>
293
394
  </div>
395
+
396
+ <div class="header-right">
397
+ <button class="header-btn help-btn" onclick="showHelpModal()">
398
+ <span>📖</span>
399
+ <span class="btn-text">Help</span>
400
+ </button>
401
+ <a href="/app/dashboard.html" class="header-btn dashboard-btn">
402
+ <span>📊</span>
403
+ </a>
404
+ </div>
294
405
  </div>
295
406
  <div id="terminal"></div>
296
407
  </div>
@@ -340,13 +451,6 @@
340
451
  </div>
341
452
  </div>
342
453
 
343
- <!-- Version Footer -->
344
- <footer style="background: #ff6b35; color: white; text-align: center; padding: 10px 0; font-size: 0.8rem; position: fixed; bottom: 0; left: 0; right: 0; z-index: 1000;">
345
- <div style="max-width: 1200px; margin: 0 auto;">
346
- <p id="terminal-version-info">Terminal Mirror • Loading version...</p>
347
- </div>
348
- </footer>
349
-
350
454
  <script>
351
455
  // Help Modal Functions
352
456
  function showHelpModal() {
@@ -155,7 +155,7 @@ const chunkAssembler = {
155
155
  function updateConnectionStatus(status) {
156
156
  const statusElement = document.getElementById('connection-status');
157
157
  if (!statusElement) return;
158
-
158
+
159
159
  statusElement.className = 'connection-status';
160
160
  switch(status) {
161
161
  case 'connecting':
@@ -169,6 +169,9 @@ function updateConnectionStatus(status) {
169
169
  // Default red styling already applied
170
170
  break;
171
171
  }
172
+
173
+ // Update connection detail in dropdown
174
+ updateConnectionDetail(status);
172
175
  }
173
176
 
174
177
  // Cleanup timer for chunk assembler
@@ -217,27 +220,63 @@ window.addEventListener('load', () => {
217
220
  }
218
221
  });
219
222
 
220
- // Load version info for footer
223
+ // Load version info into dropdown
221
224
  async function loadVersionInfo() {
222
225
  try {
223
226
  const response = await fetch('/build-info.json');
224
227
  const buildInfo = await response.json();
225
- const versionElement = document.getElementById('terminal-version-info');
226
- const footerElement = versionElement?.parentElement?.parentElement; // Get the footer element
227
-
228
- if (versionElement && buildInfo) {
229
- const buildDateTime = new Date(buildInfo.buildTime).toLocaleString();
230
- versionElement.textContent = `Terminal Mirror v${buildInfo.version} • Built ${buildDateTime}`;
231
-
232
- // Apply random footer color from build info
233
- if (footerElement && buildInfo.footerColor) {
234
- footerElement.style.background = buildInfo.footerColor;
235
- console.log(`🎨 Applied footer color: ${buildInfo.footerColor}`);
228
+
229
+ if (buildInfo) {
230
+ const buildDateTime = new Date(buildInfo.buildTime).toLocaleString('en-US', {
231
+ month: 'short',
232
+ day: 'numeric',
233
+ year: 'numeric',
234
+ hour: '2-digit',
235
+ minute: '2-digit'
236
+ });
237
+
238
+ const versionElement = document.getElementById('version-info-dropdown');
239
+ if (versionElement) {
240
+ versionElement.textContent = `v${buildInfo.version} • Built ${buildDateTime}`;
236
241
  }
237
242
  }
238
243
  } catch (error) {
239
244
  console.log('Could not load build info for terminal:', error);
240
- // Keep default version and color if build-info.json not available
245
+ }
246
+ }
247
+
248
+ // Update connection detail in dropdown
249
+ function updateConnectionDetail(status, method = 'WebRTC') {
250
+ const detail = document.getElementById('connection-detail');
251
+ if (!detail) return;
252
+
253
+ const statusIcons = {
254
+ connected: '🟢',
255
+ connecting: '🟡',
256
+ disconnected: '🔴'
257
+ };
258
+
259
+ const statusTexts = {
260
+ connected: 'Connected',
261
+ connecting: 'Connecting',
262
+ disconnected: 'Disconnected'
263
+ };
264
+
265
+ detail.textContent = `${statusIcons[status] || '⚪'} ${statusTexts[status] || 'Unknown'} via ${method}`;
266
+ }
267
+
268
+ // Update session details in dropdown
269
+ function updateSessionDetails() {
270
+ if (currentSession) {
271
+ const detailName = document.getElementById('detail-name');
272
+ const detailId = document.getElementById('detail-id');
273
+
274
+ if (detailName) {
275
+ detailName.textContent = currentSession.name || 'Terminal Session';
276
+ }
277
+ if (detailId) {
278
+ detailId.textContent = `ID: ${currentSession.id}`;
279
+ }
241
280
  }
242
281
  }
243
282
 
@@ -920,15 +959,18 @@ function updateSessionDisplay() {
920
959
  const sessionHeader = document.getElementById('session-header');
921
960
  const sessionName = document.getElementById('session-name');
922
961
  const sessionId = document.getElementById('session-id');
923
-
962
+
924
963
  if (currentSession) {
925
964
  sessionHeader.style.display = 'flex';
926
965
  sessionName.textContent = currentSession.name;
927
966
  sessionId.textContent = `(${currentSession.id.substring(0, 8)}...)`;
928
-
967
+
929
968
  // Update available sessions dropdown
930
969
  updateSessionDropdown();
931
-
970
+
971
+ // Update session details in dropdown
972
+ updateSessionDetails();
973
+
932
974
  console.log('[CLIENT] 📋 Session display updated:', currentSession);
933
975
  }
934
976
  }
@@ -998,22 +1040,49 @@ function createNewSession() {
998
1040
  window.location.href = url.toString();
999
1041
  }
1000
1042
 
1001
- // Setup dropdown toggle
1043
+ // Setup dropdown toggles
1002
1044
  document.addEventListener('DOMContentLoaded', () => {
1045
+ // Session info dropdown
1046
+ const sessionInfoBtn = document.getElementById('session-info-btn');
1047
+ const sessionInfoDropdown = document.getElementById('session-info-dropdown');
1048
+
1049
+ // Sessions menu dropdown
1003
1050
  const dropdownBtn = document.getElementById('session-dropdown-btn');
1004
1051
  const dropdownContent = document.getElementById('session-dropdown-content');
1005
-
1052
+
1053
+ // Session info dropdown toggle
1054
+ if (sessionInfoBtn && sessionInfoDropdown) {
1055
+ sessionInfoBtn.onclick = (e) => {
1056
+ e.stopPropagation();
1057
+ sessionInfoDropdown.classList.toggle('show');
1058
+ // Close sessions dropdown if open
1059
+ if (dropdownContent) {
1060
+ dropdownContent.classList.remove('show');
1061
+ }
1062
+ };
1063
+ }
1064
+
1065
+ // Sessions menu dropdown toggle
1006
1066
  if (dropdownBtn && dropdownContent) {
1007
1067
  dropdownBtn.onclick = (e) => {
1008
1068
  e.stopPropagation();
1009
1069
  dropdownContent.classList.toggle('show');
1070
+ // Close session info dropdown if open
1071
+ if (sessionInfoDropdown) {
1072
+ sessionInfoDropdown.classList.remove('show');
1073
+ }
1010
1074
  };
1011
-
1012
- // Close dropdown when clicking outside
1013
- document.addEventListener('click', () => {
1014
- dropdownContent.classList.remove('show');
1015
- });
1016
1075
  }
1076
+
1077
+ // Close both dropdowns when clicking outside
1078
+ document.addEventListener('click', () => {
1079
+ if (dropdownContent) {
1080
+ dropdownContent.classList.remove('show');
1081
+ }
1082
+ if (sessionInfoDropdown) {
1083
+ sessionInfoDropdown.classList.remove('show');
1084
+ }
1085
+ });
1017
1086
  });
1018
1087
 
1019
1088
  // Handle session-related data channel messages