shell-mirror 1.5.111 โ†’ 1.5.113

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.111",
3
+ "version": "1.5.113",
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": {
@@ -23,11 +23,15 @@
23
23
  box-sizing: border-box;
24
24
  }
25
25
 
26
+ html {
27
+ background: #0a0b0d;
28
+ }
29
+
26
30
  body {
27
31
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
28
32
  line-height: 1.5;
29
33
  color: var(--text-primary);
30
- background: var(--bg-primary);
34
+ background: var(--bg-primary) !important;
31
35
  min-height: 100vh;
32
36
  }
33
37
 
@@ -194,12 +198,7 @@ body {
194
198
  }
195
199
 
196
200
  .agent-count {
197
- background: var(--accent);
198
- color: white;
199
- padding: 2px 8px;
200
- border-radius: 10px;
201
- font-size: 0.75rem;
202
- font-weight: 600;
201
+ display: none;
203
202
  }
204
203
 
205
204
  .refresh-time {
@@ -250,20 +249,58 @@ body {
250
249
  color: var(--text-primary);
251
250
  }
252
251
 
253
- .btn-delete-agent {
252
+ /* Agent Menu Dropdown */
253
+ .agent-menu {
254
+ position: relative;
255
+ }
256
+
257
+ .btn-agent-menu {
254
258
  background: none;
255
259
  border: none;
260
+ color: var(--text-muted);
261
+ font-size: 1.2rem;
256
262
  cursor: pointer;
257
- font-size: 0.8rem;
258
- opacity: 0.3;
259
- transition: opacity 0.15s ease;
260
- padding: 4px;
261
- color: var(--text-secondary);
263
+ padding: 4px 8px;
264
+ border-radius: 4px;
265
+ transition: all 0.15s ease;
266
+ }
267
+
268
+ .btn-agent-menu:hover {
269
+ color: var(--text-primary);
270
+ background: var(--bg-hover);
271
+ }
272
+
273
+ .agent-menu-dropdown {
274
+ display: none;
275
+ position: absolute;
276
+ right: 0;
277
+ top: calc(100% + 4px);
278
+ background: var(--bg-secondary);
279
+ border: 1px solid var(--border);
280
+ border-radius: 6px;
281
+ min-width: 160px;
282
+ z-index: 100;
283
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
262
284
  }
263
285
 
264
- .btn-delete-agent:hover {
265
- opacity: 1;
286
+ .agent-menu-dropdown.show {
287
+ display: block;
288
+ }
289
+
290
+ .agent-menu-dropdown button {
291
+ width: 100%;
292
+ padding: 10px 14px;
293
+ background: none;
294
+ border: none;
266
295
  color: var(--danger);
296
+ text-align: left;
297
+ cursor: pointer;
298
+ font-size: 0.85rem;
299
+ transition: background 0.15s ease;
300
+ }
301
+
302
+ .agent-menu-dropdown button:hover {
303
+ background: var(--bg-hover);
267
304
  }
268
305
 
269
306
  .agent-status {
@@ -276,7 +313,7 @@ body {
276
313
  }
277
314
 
278
315
  .agent-status.online {
279
- color: var(--success);
316
+ color: var(--text-secondary);
280
317
  background: none;
281
318
  padding: 0;
282
319
  }
@@ -290,17 +327,33 @@ body {
290
327
  }
291
328
 
292
329
  .agent-status.recent {
293
- color: var(--warning);
330
+ color: var(--text-secondary);
294
331
  background: none;
295
332
  padding: 0;
296
333
  }
297
334
 
335
+ .agent-status.recent::before {
336
+ content: '';
337
+ width: 8px;
338
+ height: 8px;
339
+ background: var(--warning);
340
+ border-radius: 50%;
341
+ }
342
+
298
343
  .agent-status.offline {
299
- color: var(--danger);
344
+ color: var(--text-secondary);
300
345
  background: none;
301
346
  padding: 0;
302
347
  }
303
348
 
349
+ .agent-status.offline::before {
350
+ content: '';
351
+ width: 8px;
352
+ height: 8px;
353
+ background: var(--danger);
354
+ border-radius: 50%;
355
+ }
356
+
304
357
  .agent-session-count {
305
358
  color: var(--text-muted);
306
359
  font-weight: 400;
@@ -556,7 +609,7 @@ body {
556
609
  .command-box code {
557
610
  font-family: 'Monaco', 'Menlo', monospace;
558
611
  font-size: 0.85rem;
559
- color: var(--success);
612
+ color: var(--text-primary);
560
613
  flex: 1;
561
614
  }
562
615
 
@@ -556,12 +556,6 @@ class ShellMirrorDashboard {
556
556
  const sessionCount = sessions.length;
557
557
 
558
558
  const isConnectable = agent.status === 'online' || agent.status === 'recent';
559
- const statusIcon = {
560
- 'online': '๐ŸŸข',
561
- 'recent': '๐ŸŸก',
562
- 'offline': '๐Ÿ”ด'
563
- }[agent.status] || 'โ“';
564
-
565
559
  const statusText = {
566
560
  'online': 'Live',
567
561
  'recent': 'Recent',
@@ -592,13 +586,15 @@ class ShellMirrorDashboard {
592
586
  <div class="agent-info">
593
587
  <div class="agent-name-row">
594
588
  <span class="agent-name">${agent.machineName || agent.agentId}</span>
595
- <button class="btn-delete-agent" onclick="dashboard.deleteAgent('${agent.agentId}')" title="Remove agent">
596
- ๐Ÿ—‘๏ธ
597
- </button>
589
+ <div class="agent-menu">
590
+ <button class="btn-agent-menu" onclick="event.stopPropagation(); dashboard.toggleAgentMenu('${agent.agentId}')">โ‹ฎ</button>
591
+ <div class="agent-menu-dropdown" id="agent-menu-${agent.agentId}">
592
+ <button onclick="dashboard.showShutdownConfirm('${agent.agentId}')">Shut down agent</button>
593
+ </div>
594
+ </div>
598
595
  </div>
599
596
  <div class="agent-status ${agent.status}">
600
- ${statusIcon} ${statusText}
601
- ${sessionCount > 0 ? ` ยท ${sessionCount} session${sessionCount !== 1 ? 's' : ''}` : ''}
597
+ ${statusText}${sessionCount > 0 ? ` ยท ${sessionCount} session${sessionCount !== 1 ? 's' : ''}` : ''}
602
598
  </div>
603
599
  </div>
604
600
  </div>
@@ -1072,13 +1068,66 @@ class ShellMirrorDashboard {
1072
1068
  return true;
1073
1069
  }
1074
1070
 
1075
- async deleteAgent(agentId) {
1071
+ toggleAgentMenu(agentId) {
1072
+ // Close all other menus first
1073
+ document.querySelectorAll('.agent-menu-dropdown.show').forEach(el => {
1074
+ el.classList.remove('show');
1075
+ });
1076
+ const menu = document.getElementById(`agent-menu-${agentId}`);
1077
+ if (menu) {
1078
+ menu.classList.toggle('show');
1079
+ }
1080
+ }
1081
+
1082
+ showShutdownConfirm(agentId) {
1083
+ // Close menu
1084
+ document.querySelectorAll('.agent-menu-dropdown.show').forEach(el => {
1085
+ el.classList.remove('show');
1086
+ });
1087
+ this.showShutdownModal(agentId);
1088
+ }
1089
+
1090
+ showShutdownModal(agentId) {
1076
1091
  const agent = this.agents.find(a => a.agentId === agentId);
1077
1092
  const agentName = agent ? (agent.machineName || agentId) : agentId;
1078
1093
 
1079
- if (!confirm(`Are you sure you want to remove "${agentName}" from the dashboard?\n\nThis will unregister the agent. If it's still running, it will re-register on next heartbeat.`)) {
1080
- return;
1081
- }
1094
+ const modalOverlay = document.createElement('div');
1095
+ modalOverlay.className = 'modal-overlay';
1096
+ modalOverlay.id = 'shutdown-modal';
1097
+ modalOverlay.onclick = (e) => {
1098
+ if (e.target === modalOverlay) {
1099
+ document.body.removeChild(modalOverlay);
1100
+ }
1101
+ };
1102
+
1103
+ modalOverlay.innerHTML = `
1104
+ <div class="modal" onclick="event.stopPropagation()">
1105
+ <div class="modal-header">
1106
+ <h3>Shut down agent</h3>
1107
+ <button class="modal-close" onclick="document.getElementById('shutdown-modal').remove()">ร—</button>
1108
+ </div>
1109
+ <div class="modal-body">
1110
+ <p style="margin-bottom: 12px; color: var(--text-secondary);">
1111
+ Are you sure you want to shut down <strong style="color: var(--text-primary);">${agentName}</strong>?
1112
+ </p>
1113
+ <p style="font-size: 0.85rem; color: var(--text-muted);">
1114
+ This will unregister the agent from the dashboard. If the agent is still running, it will re-register on next heartbeat.
1115
+ </p>
1116
+ </div>
1117
+ <div class="modal-footer" style="display: flex; justify-content: flex-end; gap: 10px; padding: 16px; border-top: 1px solid var(--border);">
1118
+ <button onclick="document.getElementById('shutdown-modal').remove()" style="padding: 8px 16px; background: var(--bg-tertiary); color: var(--text-secondary); border: 1px solid var(--border); border-radius: 6px; cursor: pointer;">Cancel</button>
1119
+ <button onclick="dashboard.shutdownAgent('${agentId}')" style="padding: 8px 16px; background: var(--danger); color: white; border: none; border-radius: 6px; cursor: pointer;">Shut down</button>
1120
+ </div>
1121
+ </div>
1122
+ `;
1123
+
1124
+ document.body.appendChild(modalOverlay);
1125
+ }
1126
+
1127
+ async shutdownAgent(agentId) {
1128
+ // Close modal
1129
+ const modal = document.getElementById('shutdown-modal');
1130
+ if (modal) modal.remove();
1082
1131
 
1083
1132
  try {
1084
1133
  const response = await fetch('/php-backend/api/delete-agent.php', {
@@ -1090,14 +1139,14 @@ class ShellMirrorDashboard {
1090
1139
 
1091
1140
  const data = await response.json();
1092
1141
  if (data.success) {
1093
- console.log('[DASHBOARD] Agent deleted:', agentId);
1142
+ console.log('[DASHBOARD] Agent shut down:', agentId);
1094
1143
  await this.refreshDashboardData();
1095
1144
  } else {
1096
- alert('Failed to remove agent: ' + (data.message || 'Unknown error'));
1145
+ alert('Failed to shut down agent: ' + (data.message || 'Unknown error'));
1097
1146
  }
1098
1147
  } catch (error) {
1099
- console.error('[DASHBOARD] Delete agent failed:', error);
1100
- alert('Failed to remove agent: ' + error.message);
1148
+ console.error('[DASHBOARD] Shutdown agent failed:', error);
1149
+ alert('Failed to shut down agent: ' + error.message);
1101
1150
  }
1102
1151
  }
1103
1152
 
@@ -1329,4 +1378,13 @@ window.addEventListener('beforeunload', () => {
1329
1378
  if (dashboard && dashboard.refreshInterval) {
1330
1379
  clearInterval(dashboard.refreshInterval);
1331
1380
  }
1381
+ });
1382
+
1383
+ // Close agent menu when clicking outside
1384
+ document.addEventListener('click', (e) => {
1385
+ if (!e.target.closest('.agent-menu')) {
1386
+ document.querySelectorAll('.agent-menu-dropdown.show').forEach(el => {
1387
+ el.classList.remove('show');
1388
+ });
1389
+ }
1332
1390
  });