ai-agent-session-center 2.0.2 → 2.0.3

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.
Files changed (58) hide show
  1. package/README.md +484 -429
  2. package/docs/3D/ADAPTATION_GUIDE.md +592 -0
  3. package/docs/3D/index.html +754 -0
  4. package/docs/AGENT_TEAM_TASKS.md +716 -0
  5. package/docs/CYBERDROME_V2_SPEC.md +531 -0
  6. package/docs/ERROR_185_ANALYSIS.md +263 -0
  7. package/docs/PLATFORM_FEATURES_PROMPT.md +296 -0
  8. package/docs/SESSION_DETAIL_FEATURES.md +98 -0
  9. package/docs/_3d_multimedia_features.md +1080 -0
  10. package/docs/_frontend_features.md +1057 -0
  11. package/docs/_server_features.md +1077 -0
  12. package/docs/session-duplication-fixes.md +271 -0
  13. package/docs/session-terminal-linkage.md +412 -0
  14. package/package.json +63 -5
  15. package/public/apple-touch-icon.svg +21 -0
  16. package/public/css/dashboard.css +0 -161
  17. package/public/css/detail-panel.css +25 -0
  18. package/public/css/layout.css +18 -1
  19. package/public/css/modals.css +0 -26
  20. package/public/css/settings.css +0 -150
  21. package/public/css/terminal.css +34 -0
  22. package/public/favicon.svg +18 -0
  23. package/public/index.html +6 -26
  24. package/public/js/alarmManager.js +0 -21
  25. package/public/js/app.js +21 -7
  26. package/public/js/detailPanel.js +63 -64
  27. package/public/js/historyPanel.js +61 -55
  28. package/public/js/quickActions.js +132 -48
  29. package/public/js/sessionCard.js +5 -20
  30. package/public/js/sessionControls.js +8 -0
  31. package/public/js/settingsManager.js +0 -142
  32. package/server/apiRouter.js +60 -15
  33. package/server/apiRouter.ts +774 -0
  34. package/server/approvalDetector.ts +94 -0
  35. package/server/authManager.ts +144 -0
  36. package/server/autoIdleManager.ts +110 -0
  37. package/server/config.ts +121 -0
  38. package/server/constants.ts +150 -0
  39. package/server/db.ts +475 -0
  40. package/server/hookInstaller.d.ts +3 -0
  41. package/server/hookProcessor.ts +108 -0
  42. package/server/hookRouter.ts +18 -0
  43. package/server/hookStats.ts +116 -0
  44. package/server/index.js +15 -1
  45. package/server/index.ts +230 -0
  46. package/server/logger.ts +75 -0
  47. package/server/mqReader.ts +349 -0
  48. package/server/portManager.ts +55 -0
  49. package/server/processMonitor.ts +239 -0
  50. package/server/serverConfig.ts +29 -0
  51. package/server/sessionMatcher.js +17 -6
  52. package/server/sessionMatcher.ts +403 -0
  53. package/server/sessionStore.js +109 -3
  54. package/server/sessionStore.ts +1145 -0
  55. package/server/sshManager.js +167 -24
  56. package/server/sshManager.ts +671 -0
  57. package/server/teamManager.ts +289 -0
  58. package/server/wsManager.ts +200 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-agent-session-center",
3
- "version": "2.0.2",
3
+ "version": "2.0.3",
4
4
  "description": "A real-time dashboard for monitoring AI agent sessions (Claude Code, Gemini CLI, Codex) with 3D visualization",
5
5
  "type": "module",
6
6
  "main": "server/index.js",
@@ -8,16 +8,25 @@
8
8
  "ai-agent-session-center": "bin/cli.js"
9
9
  },
10
10
  "scripts": {
11
+ "dev": "concurrently \"vite\" \"tsx watch server/index.js\"",
12
+ "build": "vite build",
11
13
  "start": "node server/index.js",
12
14
  "start:no-open": "node server/index.js --no-open",
15
+ "preview": "vite preview",
13
16
  "setup": "npm install --ignore-scripts && node hooks/setup-wizard.js && node server/index.js",
14
17
  "debug": "node server/index.js --debug",
15
18
  "reset": "node hooks/reset.js",
16
19
  "install-hooks": "node hooks/install-hooks.js",
17
20
  "uninstall-hooks": "node hooks/install-hooks.js --uninstall",
18
21
  "postinstall": "chmod +x node_modules/node-pty/prebuilds/darwin-*/spawn-helper 2>/dev/null || true",
19
- "test": "node --test test/**/*.test.js",
20
- "test:watch": "node --test --watch test/**/*.test.js"
22
+ "typecheck": "tsc --noEmit",
23
+ "lint": "eslint src/",
24
+ "format": "prettier --write \"src/**/*.{ts,tsx,css}\"",
25
+ "test": "vitest run",
26
+ "test:legacy": "node --test test/**/*.test.js",
27
+ "test:watch": "vitest",
28
+ "test:e2e": "npx playwright test",
29
+ "test:coverage": "vitest run --coverage"
21
30
  },
22
31
  "keywords": [
23
32
  "claude-code",
@@ -40,10 +49,30 @@
40
49
  "node": ">=18.0.0"
41
50
  },
42
51
  "dependencies": {
52
+ "@dnd-kit/core": "^6.3.1",
53
+ "@dnd-kit/sortable": "^10.0.0",
54
+ "@dnd-kit/utilities": "^3.2.2",
55
+ "@react-three/drei": "^10.7.7",
56
+ "@react-three/fiber": "^9.5.0",
57
+ "@tanstack/react-query": "^5.90.21",
58
+ "@xterm/addon-fit": "^0.11.0",
59
+ "@xterm/addon-unicode11": "^0.9.0",
60
+ "@xterm/addon-web-links": "^0.12.0",
43
61
  "better-sqlite3": "^12.6.2",
62
+ "dexie": "^4.3.0",
44
63
  "express": "^5.0.0",
45
64
  "node-pty": "^1.1.0",
46
- "ws": "^8.18.0"
65
+ "react": "^19.2.4",
66
+ "react-dom": "^19.2.4",
67
+ "react-hook-form": "^7.71.1",
68
+ "react-is": "^19.2.4",
69
+ "react-router": "^7.13.0",
70
+ "recharts": "^3.7.0",
71
+ "three": "^0.182.0",
72
+ "ws": "^8.18.0",
73
+ "xterm": "^5.3.0",
74
+ "zod": "^4.3.6",
75
+ "zustand": "^5.0.11"
47
76
  },
48
77
  "files": [
49
78
  "server/**/*",
@@ -53,5 +82,34 @@
53
82
  "types/**/*",
54
83
  "docs/**/*",
55
84
  "data/.gitkeep"
56
- ]
85
+ ],
86
+ "devDependencies": {
87
+ "@eslint/js": "^9.0.0",
88
+ "@playwright/test": "^1.58.2",
89
+ "@testing-library/dom": "^10.4.1",
90
+ "@testing-library/jest-dom": "^6.9.1",
91
+ "@testing-library/react": "^16.3.2",
92
+ "@testing-library/user-event": "^14.6.1",
93
+ "@types/better-sqlite3": "^7.6.13",
94
+ "@types/express": "^5.0.6",
95
+ "@types/node": "^25.2.3",
96
+ "@types/react": "^19.2.14",
97
+ "@types/react-dom": "^19.2.3",
98
+ "@types/three": "^0.182.0",
99
+ "@types/ws": "^8.18.1",
100
+ "@vitejs/plugin-react": "^5.1.4",
101
+ "@vitest/coverage-v8": "^4.0.18",
102
+ "concurrently": "^9.2.1",
103
+ "eslint": "^9.0.0",
104
+ "eslint-plugin-react-hooks": "^7.0.1",
105
+ "eslint-plugin-react-refresh": "^0.5.0",
106
+ "globals": "^17.3.0",
107
+ "jsdom": "^28.1.0",
108
+ "prettier": "^3.8.1",
109
+ "tsx": "^4.21.0",
110
+ "typescript": "^5.9.3",
111
+ "typescript-eslint": "^8.56.0",
112
+ "vite": "^7.3.1",
113
+ "vitest": "^4.0.18"
114
+ }
57
115
  }
@@ -0,0 +1,21 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 180 180">
2
+ <rect width="180" height="180" rx="36" fill="#0a0a1a"/>
3
+ <!-- Robot head -->
4
+ <circle cx="90" cy="72" r="40" fill="none" stroke="#00e5ff" stroke-width="5"/>
5
+ <!-- Eyes -->
6
+ <circle cx="76" cy="67" r="8" fill="#00e5ff"/>
7
+ <circle cx="104" cy="67" r="8" fill="#00e5ff"/>
8
+ <!-- Eye glints -->
9
+ <circle cx="78" cy="65" r="2.5" fill="#0a0a1a"/>
10
+ <circle cx="106" cy="65" r="2.5" fill="#0a0a1a"/>
11
+ <!-- Smile -->
12
+ <path d="M73 88 Q90 106 107 88" fill="none" stroke="#00e5ff" stroke-width="3.5" stroke-linecap="round"/>
13
+ <!-- Antenna -->
14
+ <rect x="82" y="22" width="16" height="16" rx="5" fill="#00e5ff"/>
15
+ <line x1="90" y1="38" x2="90" y2="32" stroke="#00e5ff" stroke-width="4"/>
16
+ <!-- Terminal/keyboard -->
17
+ <rect x="48" y="125" width="84" height="34" rx="8" fill="none" stroke="#ff9100" stroke-width="4"/>
18
+ <line x1="66" y1="135" x2="66" y2="149" stroke="#ff9100" stroke-width="3" stroke-linecap="round"/>
19
+ <line x1="90" y1="135" x2="90" y2="149" stroke="#ff9100" stroke-width="3" stroke-linecap="round"/>
20
+ <line x1="114" y1="135" x2="114" y2="149" stroke="#ff9100" stroke-width="3" stroke-linecap="round"/>
21
+ </svg>
@@ -3200,32 +3200,6 @@ body.move-mode .session-card.move-source {
3200
3200
  background: rgba(0, 229, 255, 0.12) !important;
3201
3201
  }
3202
3202
 
3203
- .qa-btn-oneoff {
3204
- color: #ff9100 !important;
3205
- border-color: rgba(255, 145, 0, 0.4) !important;
3206
- }
3207
- .qa-btn-oneoff:hover {
3208
- background: rgba(255, 145, 0, 0.12) !important;
3209
- }
3210
-
3211
- .qa-btn-heavy {
3212
- color: #ff3355 !important;
3213
- border-color: rgba(255, 51, 85, 0.5) !important;
3214
- font-weight: 700 !important;
3215
- }
3216
- .qa-btn-heavy:hover {
3217
- background: rgba(255, 51, 85, 0.15) !important;
3218
- }
3219
-
3220
- .qa-btn-important {
3221
- color: #aa66ff !important;
3222
- border-color: rgba(170, 102, 255, 0.5) !important;
3223
- font-weight: 700 !important;
3224
- }
3225
- .qa-btn-important:hover {
3226
- background: rgba(170, 102, 255, 0.15) !important;
3227
- }
3228
-
3229
3203
  /* ---- Quick Session Modal ---- */
3230
3204
 
3231
3205
  .quick-session-panel {
@@ -3968,141 +3942,6 @@ body.move-mode .session-card.move-source {
3968
3942
  background: var(--bg-accent);
3969
3943
  }
3970
3944
 
3971
- /* ---- Label Settings Grid ---- */
3972
-
3973
- .label-settings-grid {
3974
- display: flex;
3975
- flex-direction: column;
3976
- gap: 12px;
3977
- margin-top: 12px;
3978
- }
3979
-
3980
- .label-config-card {
3981
- background: var(--bg-card);
3982
- border: 2px solid var(--label-color, var(--border-subtle));
3983
- border-radius: 8px;
3984
- padding: 12px 14px;
3985
- box-shadow: 0 0 8px color-mix(in srgb, var(--label-color) 20%, transparent);
3986
- position: relative;
3987
- overflow: visible;
3988
- }
3989
- /* Live frame preview on config cards */
3990
- .label-config-card[data-frame]:not([data-frame="none"]) {
3991
- border: none;
3992
- z-index: 1;
3993
- }
3994
- .label-config-card[data-frame]:not([data-frame="none"])::before,
3995
- .label-config-card[data-frame]:not([data-frame="none"])::after {
3996
- content: '';
3997
- position: absolute;
3998
- border-radius: 10px;
3999
- z-index: -1;
4000
- pointer-events: none;
4001
- }
4002
- .label-config-card[data-frame]:not([data-frame="none"])::before {
4003
- inset: -3px;
4004
- }
4005
- .label-config-card[data-frame]:not([data-frame="none"])::after {
4006
- inset: 0;
4007
- border-radius: 8px;
4008
- background: var(--bg-card);
4009
- }
4010
- .label-config-card[data-frame="fire"]::before {
4011
- background: conic-gradient(from var(--frame-angle, 0deg), #ff2200, #ff6600, #ffaa00, #ffdd00, #ffaa00, #ff6600, #ff2200, #ff4400, #ff8800, #ffcc00, #ff8800, #ff4400, #ff2200);
4012
- animation: frame-rotate 3s linear infinite, fire-flicker 0.15s ease-in-out infinite alternate;
4013
- filter: blur(1px);
4014
- box-shadow: 0 0 12px rgba(255, 68, 0, 0.4);
4015
- }
4016
- .label-config-card[data-frame="electric"]::before {
4017
- background: conic-gradient(from var(--frame-angle, 0deg), #00ccff, #0044ff, #00eeff 15%, transparent 25%, #0088ff 35%, #00ccff 45%, transparent 55%, #00aaff 65%, #00eeff 75%, transparent 85%, #0066ff 95%, #00ccff);
4018
- animation: frame-rotate 2s linear infinite, electric-spark 0.08s steps(2) infinite;
4019
- filter: blur(0.5px);
4020
- box-shadow: 0 0 12px rgba(0, 200, 255, 0.35);
4021
- }
4022
- .label-config-card[data-frame="chains"]::before {
4023
- background: repeating-conic-gradient(from var(--frame-angle, 0deg), #c8962e 0deg, #f5d37a 10deg, #a07828 20deg, #f5d37a 30deg, transparent 30deg, transparent 40deg);
4024
- animation: frame-rotate 6s linear infinite;
4025
- filter: blur(0.5px);
4026
- box-shadow: 0 0 12px rgba(200, 150, 46, 0.3);
4027
- }
4028
- .label-config-card[data-frame="liquid"]::before {
4029
- background: conic-gradient(from var(--frame-angle, 0deg), #aa44ff, #ff44aa, #44aaff, #44ffaa, #ffaa44, #aa44ff);
4030
- animation: frame-rotate 4s linear infinite, liquid-morph 3s ease-in-out infinite alternate;
4031
- filter: blur(2px);
4032
- box-shadow: 0 0 14px rgba(170, 68, 255, 0.35);
4033
- }
4034
- .label-config-card[data-frame="plasma"]::before {
4035
- background: conic-gradient(from var(--frame-angle, 0deg), #ff0080, #ff00ff, #8000ff, #0040ff, #00ffff, #00ff80, #80ff00, #ffff00, #ff8000, #ff0080);
4036
- animation: frame-rotate 2.5s linear infinite;
4037
- filter: blur(1.5px) saturate(1.5);
4038
- box-shadow: 0 0 14px rgba(255, 0, 128, 0.35);
4039
- }
4040
-
4041
- .label-config-header {
4042
- display: flex;
4043
- align-items: center;
4044
- gap: 8px;
4045
- margin-bottom: 10px;
4046
- padding-bottom: 8px;
4047
- border-bottom: 1px solid var(--border-subtle);
4048
- }
4049
-
4050
- .label-config-icon {
4051
- font-size: 16px;
4052
- }
4053
-
4054
- .label-config-name {
4055
- font-size: 13px;
4056
- font-weight: 800;
4057
- letter-spacing: 1px;
4058
- color: var(--label-color, var(--text-primary));
4059
- text-transform: uppercase;
4060
- }
4061
-
4062
- .label-config-row {
4063
- display: flex;
4064
- align-items: center;
4065
- gap: 8px;
4066
- margin-bottom: 6px;
4067
- }
4068
-
4069
- .label-config-row:last-child {
4070
- margin-bottom: 0;
4071
- }
4072
-
4073
- .label-config-field {
4074
- font-size: 11px;
4075
- color: var(--text-secondary);
4076
- width: 70px;
4077
- flex-shrink: 0;
4078
- }
4079
-
4080
- .label-config-select {
4081
- flex: 1;
4082
- background: var(--bg-panel);
4083
- border: 1px solid var(--border-subtle);
4084
- color: var(--text-primary);
4085
- font-family: var(--font-mono);
4086
- font-size: 11px;
4087
- padding: 4px 8px;
4088
- border-radius: 4px;
4089
- cursor: pointer;
4090
- }
4091
-
4092
- .label-config-select:focus {
4093
- border-color: var(--label-color, var(--accent-cyan));
4094
- outline: none;
4095
- }
4096
-
4097
- .label-config-select option {
4098
- background-color: var(--bg-card, #0d1b2a);
4099
- color: var(--text-primary, #ccd6f6);
4100
- }
4101
-
4102
- .label-preview-btn {
4103
- flex-shrink: 0;
4104
- }
4105
-
4106
3945
  /* ============================================================
4107
3946
  Settings Tabs
4108
3947
  ============================================================ */
@@ -252,6 +252,31 @@
252
252
  transition: border-color 0.2s, background 0.2s;
253
253
  }
254
254
 
255
+ .detail-title-edit-btn {
256
+ flex-shrink: 0;
257
+ background: none;
258
+ border: none;
259
+ color: var(--text-muted, #555);
260
+ cursor: pointer;
261
+ padding: 2px;
262
+ border-radius: 3px;
263
+ display: flex;
264
+ align-items: center;
265
+ justify-content: center;
266
+ opacity: 0.4;
267
+ transition: opacity 0.2s, color 0.2s;
268
+ }
269
+
270
+ .detail-title-edit-btn:hover {
271
+ opacity: 1;
272
+ color: var(--accent-cyan);
273
+ }
274
+
275
+ .detail-title-row:focus-within .detail-title-edit-btn {
276
+ opacity: 0;
277
+ pointer-events: none;
278
+ }
279
+
255
280
  .detail-header .detail-title-input:hover {
256
281
  border-color: var(--border-subtle);
257
282
  background: var(--border-faint);
@@ -295,7 +295,7 @@
295
295
 
296
296
  .history-row {
297
297
  display: grid;
298
- grid-template-columns: 1.5fr 1fr auto auto auto auto auto auto 24px;
298
+ grid-template-columns: 1.5fr 1fr auto auto auto auto auto auto 24px 24px;
299
299
  gap: 12px;
300
300
  padding: 12px 16px;
301
301
  background: var(--bg-card);
@@ -382,6 +382,23 @@
382
382
  font-size: 10px;
383
383
  }
384
384
 
385
+ .history-resume {
386
+ background: none;
387
+ border: 1px solid var(--accent-green);
388
+ color: var(--accent-green);
389
+ font-size: 10px;
390
+ cursor: pointer;
391
+ padding: 2px 6px;
392
+ border-radius: 3px;
393
+ line-height: 1;
394
+ transition: background 0.2s, color 0.2s;
395
+ }
396
+
397
+ .history-resume:hover {
398
+ background: var(--accent-green);
399
+ color: var(--bg-card);
400
+ }
401
+
385
402
  .history-delete {
386
403
  background: none;
387
404
  border: none;
@@ -174,32 +174,6 @@
174
174
  background: rgba(0, 229, 255, 0.12) !important;
175
175
  }
176
176
 
177
- .qa-btn-oneoff {
178
- color: #ff9100 !important;
179
- border-color: rgba(255, 145, 0, 0.4) !important;
180
- }
181
- .qa-btn-oneoff:hover {
182
- background: rgba(255, 145, 0, 0.12) !important;
183
- }
184
-
185
- .qa-btn-heavy {
186
- color: #ff3355 !important;
187
- border-color: rgba(255, 51, 85, 0.5) !important;
188
- font-weight: 700 !important;
189
- }
190
- .qa-btn-heavy:hover {
191
- background: rgba(255, 51, 85, 0.15) !important;
192
- }
193
-
194
- .qa-btn-important {
195
- color: #aa66ff !important;
196
- border-color: rgba(170, 102, 255, 0.5) !important;
197
- font-weight: 700 !important;
198
- }
199
- .qa-btn-important:hover {
200
- background: rgba(170, 102, 255, 0.15) !important;
201
- }
202
-
203
177
  /* ---- Quick Session Modal ---- */
204
178
 
205
179
  .quick-session-panel {
@@ -416,141 +416,6 @@
416
416
  background: var(--bg-accent);
417
417
  }
418
418
 
419
- /* ---- Label Settings Grid ---- */
420
-
421
- .label-settings-grid {
422
- display: flex;
423
- flex-direction: column;
424
- gap: 12px;
425
- margin-top: 12px;
426
- }
427
-
428
- .label-config-card {
429
- background: var(--bg-card);
430
- border: 2px solid var(--label-color, var(--border-subtle));
431
- border-radius: 8px;
432
- padding: 12px 14px;
433
- box-shadow: 0 0 8px color-mix(in srgb, var(--label-color) 20%, transparent);
434
- position: relative;
435
- overflow: visible;
436
- }
437
- /* Live frame preview on config cards */
438
- .label-config-card[data-frame]:not([data-frame="none"]) {
439
- border: none;
440
- z-index: 1;
441
- }
442
- .label-config-card[data-frame]:not([data-frame="none"])::before,
443
- .label-config-card[data-frame]:not([data-frame="none"])::after {
444
- content: '';
445
- position: absolute;
446
- border-radius: 10px;
447
- z-index: -1;
448
- pointer-events: none;
449
- }
450
- .label-config-card[data-frame]:not([data-frame="none"])::before {
451
- inset: -3px;
452
- }
453
- .label-config-card[data-frame]:not([data-frame="none"])::after {
454
- inset: 0;
455
- border-radius: 8px;
456
- background: var(--bg-card);
457
- }
458
- .label-config-card[data-frame="fire"]::before {
459
- background: conic-gradient(from var(--frame-angle, 0deg), #ff2200, #ff6600, #ffaa00, #ffdd00, #ffaa00, #ff6600, #ff2200, #ff4400, #ff8800, #ffcc00, #ff8800, #ff4400, #ff2200);
460
- animation: frame-rotate 3s linear infinite, fire-flicker 0.15s ease-in-out infinite alternate;
461
- filter: blur(1px);
462
- box-shadow: 0 0 12px rgba(255, 68, 0, 0.4);
463
- }
464
- .label-config-card[data-frame="electric"]::before {
465
- background: conic-gradient(from var(--frame-angle, 0deg), #00ccff, #0044ff, #00eeff 15%, transparent 25%, #0088ff 35%, #00ccff 45%, transparent 55%, #00aaff 65%, #00eeff 75%, transparent 85%, #0066ff 95%, #00ccff);
466
- animation: frame-rotate 2s linear infinite, electric-spark 0.08s steps(2) infinite;
467
- filter: blur(0.5px);
468
- box-shadow: 0 0 12px rgba(0, 200, 255, 0.35);
469
- }
470
- .label-config-card[data-frame="chains"]::before {
471
- background: repeating-conic-gradient(from var(--frame-angle, 0deg), #c8962e 0deg, #f5d37a 10deg, #a07828 20deg, #f5d37a 30deg, transparent 30deg, transparent 40deg);
472
- animation: frame-rotate 6s linear infinite;
473
- filter: blur(0.5px);
474
- box-shadow: 0 0 12px rgba(200, 150, 46, 0.3);
475
- }
476
- .label-config-card[data-frame="liquid"]::before {
477
- background: conic-gradient(from var(--frame-angle, 0deg), #aa44ff, #ff44aa, #44aaff, #44ffaa, #ffaa44, #aa44ff);
478
- animation: frame-rotate 4s linear infinite, liquid-morph 3s ease-in-out infinite alternate;
479
- filter: blur(2px);
480
- box-shadow: 0 0 14px rgba(170, 68, 255, 0.35);
481
- }
482
- .label-config-card[data-frame="plasma"]::before {
483
- background: conic-gradient(from var(--frame-angle, 0deg), #ff0080, #ff00ff, #8000ff, #0040ff, #00ffff, #00ff80, #80ff00, #ffff00, #ff8000, #ff0080);
484
- animation: frame-rotate 2.5s linear infinite;
485
- filter: blur(1.5px) saturate(1.5);
486
- box-shadow: 0 0 14px rgba(255, 0, 128, 0.35);
487
- }
488
-
489
- .label-config-header {
490
- display: flex;
491
- align-items: center;
492
- gap: 8px;
493
- margin-bottom: 10px;
494
- padding-bottom: 8px;
495
- border-bottom: 1px solid var(--border-subtle);
496
- }
497
-
498
- .label-config-icon {
499
- font-size: 16px;
500
- }
501
-
502
- .label-config-name {
503
- font-size: 13px;
504
- font-weight: 800;
505
- letter-spacing: 1px;
506
- color: var(--label-color, var(--text-primary));
507
- text-transform: uppercase;
508
- }
509
-
510
- .label-config-row {
511
- display: flex;
512
- align-items: center;
513
- gap: 8px;
514
- margin-bottom: 6px;
515
- }
516
-
517
- .label-config-row:last-child {
518
- margin-bottom: 0;
519
- }
520
-
521
- .label-config-field {
522
- font-size: 11px;
523
- color: var(--text-secondary);
524
- width: 70px;
525
- flex-shrink: 0;
526
- }
527
-
528
- .label-config-select {
529
- flex: 1;
530
- background: var(--bg-panel);
531
- border: 1px solid var(--border-subtle);
532
- color: var(--text-primary);
533
- font-family: var(--font-mono);
534
- font-size: 11px;
535
- padding: 4px 8px;
536
- border-radius: 4px;
537
- cursor: pointer;
538
- }
539
-
540
- .label-config-select:focus {
541
- border-color: var(--label-color, var(--accent-cyan));
542
- outline: none;
543
- }
544
-
545
- .label-config-select option {
546
- background-color: var(--bg-card, #0d1b2a);
547
- color: var(--text-primary, #ccd6f6);
548
- }
549
-
550
- .label-preview-btn {
551
- flex-shrink: 0;
552
- }
553
-
554
419
  /* ============================================================
555
420
  Settings Tabs
556
421
  ============================================================ */
@@ -1132,21 +997,6 @@ body.no-scanlines::after {
1132
997
  font-size: 16px; /* prevents iOS zoom */
1133
998
  }
1134
999
 
1135
- /* Label config cards: full width, touch-friendly selects */
1136
- .label-config-row {
1137
- flex-wrap: wrap;
1138
- }
1139
-
1140
- .label-config-field {
1141
- width: 100%;
1142
- margin-bottom: 2px;
1143
- }
1144
-
1145
- .label-config-select {
1146
- min-height: 44px;
1147
- font-size: 16px; /* prevents iOS zoom */
1148
- width: 100%;
1149
- }
1150
1000
 
1151
1001
  .label-preview-btn {
1152
1002
  min-width: 44px;
@@ -68,6 +68,40 @@
68
68
  height: 100%;
69
69
  }
70
70
 
71
+ /* External source hint shown when session has no managed terminal */
72
+ .terminal-external-hint {
73
+ display: flex;
74
+ flex-direction: column;
75
+ align-items: center;
76
+ justify-content: center;
77
+ height: 100%;
78
+ min-height: 200px;
79
+ padding: 2rem;
80
+ text-align: center;
81
+ color: rgba(255, 255, 255, 0.5);
82
+ gap: 0.75rem;
83
+ }
84
+ .terminal-external-hint-icon {
85
+ font-size: 2.5rem;
86
+ opacity: 0.6;
87
+ }
88
+ .terminal-external-hint-title {
89
+ font-size: 1rem;
90
+ font-weight: 600;
91
+ letter-spacing: 1px;
92
+ text-transform: uppercase;
93
+ color: rgba(255, 255, 255, 0.7);
94
+ }
95
+ .terminal-external-hint-text {
96
+ font-size: 0.8rem;
97
+ line-height: 1.6;
98
+ max-width: 360px;
99
+ color: rgba(255, 255, 255, 0.4);
100
+ }
101
+ .terminal-external-hint-text strong {
102
+ color: var(--accent-cyan, #00e5ff);
103
+ }
104
+
71
105
  #terminal-container .xterm-viewport {
72
106
  overflow-y: auto !important;
73
107
  }
@@ -0,0 +1,18 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
2
+ <rect width="32" height="32" rx="6" fill="#0a0a1a"/>
3
+ <!-- Robot head -->
4
+ <circle cx="16" cy="13" r="7" fill="none" stroke="#00e5ff" stroke-width="2"/>
5
+ <!-- Eyes -->
6
+ <circle cx="13.5" cy="12" r="1.5" fill="#00e5ff"/>
7
+ <circle cx="18.5" cy="12" r="1.5" fill="#00e5ff"/>
8
+ <!-- Smile -->
9
+ <path d="M13 16 Q16 19 19 16" fill="none" stroke="#00e5ff" stroke-width="1" stroke-linecap="round"/>
10
+ <!-- Antenna -->
11
+ <rect x="14.5" y="4" width="3" height="3" rx="1" fill="#00e5ff"/>
12
+ <line x1="16" y1="7" x2="16" y2="6" stroke="#00e5ff" stroke-width="1.5"/>
13
+ <!-- Terminal/keyboard -->
14
+ <rect x="9" y="22" width="14" height="6" rx="2" fill="none" stroke="#ff9100" stroke-width="1.5"/>
15
+ <line x1="12" y1="24" x2="12" y2="26" stroke="#ff9100" stroke-width="1" stroke-linecap="round"/>
16
+ <line x1="16" y1="24" x2="16" y2="26" stroke="#ff9100" stroke-width="1" stroke-linecap="round"/>
17
+ <line x1="20" y1="24" x2="20" y2="26" stroke="#ff9100" stroke-width="1" stroke-linecap="round"/>
18
+ </svg>