agent-relay 1.0.7 → 1.0.9

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 (140) hide show
  1. package/README.md +176 -6
  2. package/dist/bridge/config.d.ts +41 -0
  3. package/dist/bridge/config.d.ts.map +1 -0
  4. package/dist/bridge/config.js +143 -0
  5. package/dist/bridge/config.js.map +1 -0
  6. package/dist/bridge/index.d.ts +10 -0
  7. package/dist/bridge/index.d.ts.map +1 -0
  8. package/dist/bridge/index.js +10 -0
  9. package/dist/bridge/index.js.map +1 -0
  10. package/dist/bridge/multi-project-client.d.ts +99 -0
  11. package/dist/bridge/multi-project-client.d.ts.map +1 -0
  12. package/dist/bridge/multi-project-client.js +386 -0
  13. package/dist/bridge/multi-project-client.js.map +1 -0
  14. package/dist/bridge/spawner.d.ts +46 -0
  15. package/dist/bridge/spawner.d.ts.map +1 -0
  16. package/dist/bridge/spawner.js +223 -0
  17. package/dist/bridge/spawner.js.map +1 -0
  18. package/dist/bridge/types.d.ts +55 -0
  19. package/dist/bridge/types.d.ts.map +1 -0
  20. package/dist/bridge/types.js +6 -0
  21. package/dist/bridge/types.js.map +1 -0
  22. package/dist/bridge/utils.d.ts +30 -0
  23. package/dist/bridge/utils.d.ts.map +1 -0
  24. package/dist/bridge/utils.js +54 -0
  25. package/dist/bridge/utils.js.map +1 -0
  26. package/dist/cli/index.d.ts +2 -0
  27. package/dist/cli/index.d.ts.map +1 -1
  28. package/dist/cli/index.js +906 -6
  29. package/dist/cli/index.js.map +1 -1
  30. package/dist/daemon/agent-registry.d.ts +60 -0
  31. package/dist/daemon/agent-registry.d.ts.map +1 -0
  32. package/dist/daemon/agent-registry.js +163 -0
  33. package/dist/daemon/agent-registry.js.map +1 -0
  34. package/dist/daemon/connection.d.ts +33 -1
  35. package/dist/daemon/connection.d.ts.map +1 -1
  36. package/dist/daemon/connection.js +86 -11
  37. package/dist/daemon/connection.js.map +1 -1
  38. package/dist/daemon/index.d.ts +2 -0
  39. package/dist/daemon/index.d.ts.map +1 -1
  40. package/dist/daemon/index.js +2 -0
  41. package/dist/daemon/index.js.map +1 -1
  42. package/dist/daemon/registry.d.ts +9 -0
  43. package/dist/daemon/registry.d.ts.map +1 -0
  44. package/dist/daemon/registry.js +9 -0
  45. package/dist/daemon/registry.js.map +1 -0
  46. package/dist/daemon/router.d.ts +61 -2
  47. package/dist/daemon/router.d.ts.map +1 -1
  48. package/dist/daemon/router.js +219 -4
  49. package/dist/daemon/router.js.map +1 -1
  50. package/dist/daemon/server.d.ts +9 -0
  51. package/dist/daemon/server.d.ts.map +1 -1
  52. package/dist/daemon/server.js +135 -16
  53. package/dist/daemon/server.js.map +1 -1
  54. package/dist/dashboard/metrics.d.ts +105 -0
  55. package/dist/dashboard/metrics.d.ts.map +1 -0
  56. package/dist/dashboard/metrics.js +192 -0
  57. package/dist/dashboard/metrics.js.map +1 -0
  58. package/dist/dashboard/needs-attention.d.ts +24 -0
  59. package/dist/dashboard/needs-attention.d.ts.map +1 -0
  60. package/dist/dashboard/needs-attention.js +78 -0
  61. package/dist/dashboard/needs-attention.js.map +1 -0
  62. package/dist/dashboard/public/bridge.html +1272 -0
  63. package/dist/dashboard/public/index.html +2094 -347
  64. package/dist/dashboard/public/js/app.js +184 -0
  65. package/dist/dashboard/public/js/app.js.map +7 -0
  66. package/dist/dashboard/public/metrics.html +999 -0
  67. package/dist/dashboard/server.d.ts +14 -1
  68. package/dist/dashboard/server.d.ts.map +1 -1
  69. package/dist/dashboard/server.js +689 -16
  70. package/dist/dashboard/server.js.map +1 -1
  71. package/dist/dashboard/start.js +1 -1
  72. package/dist/dashboard/start.js.map +1 -1
  73. package/dist/dashboard-v2/index.d.ts +10 -0
  74. package/dist/dashboard-v2/index.d.ts.map +1 -0
  75. package/dist/dashboard-v2/index.js +54 -0
  76. package/dist/dashboard-v2/index.js.map +1 -0
  77. package/dist/dashboard-v2/lib/api.d.ts +95 -0
  78. package/dist/dashboard-v2/lib/api.d.ts.map +1 -0
  79. package/dist/dashboard-v2/lib/api.js +270 -0
  80. package/dist/dashboard-v2/lib/api.js.map +1 -0
  81. package/dist/dashboard-v2/lib/colors.d.ts +61 -0
  82. package/dist/dashboard-v2/lib/colors.d.ts.map +1 -0
  83. package/dist/dashboard-v2/lib/colors.js +198 -0
  84. package/dist/dashboard-v2/lib/colors.js.map +1 -0
  85. package/dist/dashboard-v2/lib/hierarchy.d.ts +74 -0
  86. package/dist/dashboard-v2/lib/hierarchy.d.ts.map +1 -0
  87. package/dist/dashboard-v2/lib/hierarchy.js +196 -0
  88. package/dist/dashboard-v2/lib/hierarchy.js.map +1 -0
  89. package/dist/dashboard-v2/types/index.d.ts +154 -0
  90. package/dist/dashboard-v2/types/index.d.ts.map +1 -0
  91. package/dist/dashboard-v2/types/index.js +6 -0
  92. package/dist/dashboard-v2/types/index.js.map +1 -0
  93. package/dist/index.d.ts +1 -0
  94. package/dist/index.d.ts.map +1 -1
  95. package/dist/protocol/types.d.ts +15 -1
  96. package/dist/protocol/types.d.ts.map +1 -1
  97. package/dist/storage/adapter.d.ts +74 -1
  98. package/dist/storage/adapter.d.ts.map +1 -1
  99. package/dist/storage/adapter.js +39 -0
  100. package/dist/storage/adapter.js.map +1 -1
  101. package/dist/storage/sqlite-adapter.d.ts +92 -1
  102. package/dist/storage/sqlite-adapter.d.ts.map +1 -1
  103. package/dist/storage/sqlite-adapter.js +615 -47
  104. package/dist/storage/sqlite-adapter.js.map +1 -1
  105. package/dist/utils/agent-config.d.ts +45 -0
  106. package/dist/utils/agent-config.d.ts.map +1 -0
  107. package/dist/utils/agent-config.js +118 -0
  108. package/dist/utils/agent-config.js.map +1 -0
  109. package/dist/utils/project-namespace.d.ts.map +1 -1
  110. package/dist/utils/project-namespace.js +22 -1
  111. package/dist/utils/project-namespace.js.map +1 -1
  112. package/dist/wrapper/client.d.ts +30 -3
  113. package/dist/wrapper/client.d.ts.map +1 -1
  114. package/dist/wrapper/client.js +85 -9
  115. package/dist/wrapper/client.js.map +1 -1
  116. package/dist/wrapper/parser.d.ts +127 -4
  117. package/dist/wrapper/parser.d.ts.map +1 -1
  118. package/dist/wrapper/parser.js +622 -86
  119. package/dist/wrapper/parser.js.map +1 -1
  120. package/dist/wrapper/tmux-wrapper.d.ts +136 -10
  121. package/dist/wrapper/tmux-wrapper.d.ts.map +1 -1
  122. package/dist/wrapper/tmux-wrapper.js +599 -79
  123. package/dist/wrapper/tmux-wrapper.js.map +1 -1
  124. package/docs/AGENTS.md +132 -27
  125. package/docs/ARCHITECTURE_DECISIONS.md +175 -0
  126. package/docs/CHANGELOG.md +1 -1
  127. package/docs/COMPETITIVE_ANALYSIS.md +897 -0
  128. package/docs/DESIGN_BRIDGE_STAFFING.md +878 -0
  129. package/docs/DESIGN_V2.md +1079 -0
  130. package/docs/INTEGRATION-GUIDE.md +926 -0
  131. package/docs/MONETIZATION.md +1679 -0
  132. package/docs/PROPOSAL-trajectories.md +1582 -0
  133. package/docs/PROTOCOL.md +3 -3
  134. package/docs/SCALING_ANALYSIS.md +280 -0
  135. package/docs/TMUX_IMPLEMENTATION_NOTES.md +9 -9
  136. package/docs/TMUX_IMPROVEMENTS.md +968 -0
  137. package/docs/agent-relay-snippet.md +61 -0
  138. package/docs/competitive-analysis-mcp-agent-mail.md +389 -0
  139. package/docs/dashboard-v2-plan.md +179 -0
  140. package/package.json +10 -3
@@ -1,515 +1,2262 @@
1
1
  <!DOCTYPE html>
2
- <html>
2
+ <html lang="en">
3
3
  <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
4
6
  <title>Agent Relay</title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com">
8
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
5
10
  <style>
11
+ /* ========================================
12
+ Design Tokens
13
+ ======================================== */
6
14
  :root {
7
- --bg-primary: #09090b;
8
- --bg-secondary: #18181b;
9
- --bg-card: #18181b;
10
- --bg-elevated: #27272a;
11
- --border-color: #27272a;
12
- --border-hover: #3f3f46;
13
- --primary: #3b82f6;
14
- --primary-muted: rgba(59, 130, 246, 0.15);
15
- --text: #fafafa;
16
- --text-secondary: #a1a1aa;
17
- --text-muted: #71717a;
18
- --success: #22c55e;
19
- --success-muted: rgba(34, 197, 94, 0.15);
20
- --error: #ef4444;
21
- --error-muted: rgba(239, 68, 68, 0.15);
22
- --warning: #f59e0b;
23
- --warning-muted: rgba(245, 158, 11, 0.15);
24
- }
25
-
26
- * { box-sizing: border-box; margin: 0; padding: 0; }
15
+ /* Background colors */
16
+ --bg-workspace: #1a1d21;
17
+ --bg-sidebar: #19171d;
18
+ --bg-channel-active: #1164a3;
19
+ --bg-channel-hover: rgba(255, 255, 255, 0.04);
20
+ --bg-main: #222529;
21
+ --bg-message-hover: rgba(255, 255, 255, 0.03);
22
+ --bg-composer: #222529;
23
+ --bg-input: #222529;
24
+ --bg-modal: #1a1d21;
25
+ --bg-tooltip: #1d1c21;
26
+
27
+ /* Text colors */
28
+ --text-primary: #d1d2d3;
29
+ --text-secondary: #ababad;
30
+ --text-muted: #8d8d8e;
31
+ --text-channel: #bcabbc;
32
+ --text-channel-active: #ffffff;
33
+ --text-link: #1d9bd1;
34
+
35
+ /* Accent colors */
36
+ --accent-primary: #1264a3;
37
+ --accent-green: #2bac76;
38
+ --accent-yellow: #e8a427;
39
+ --accent-red: #e01e5a;
40
+ --accent-purple: #7c3aed;
41
+
42
+ /* Status colors */
43
+ --status-online: #2bac76;
44
+ --status-away: #e8a427;
45
+ --status-offline: #616061;
46
+ --status-busy: #e01e5a;
47
+
48
+ /* Border colors */
49
+ --border-subtle: rgba(255, 255, 255, 0.1);
50
+ --border-divider: rgba(255, 255, 255, 0.06);
51
+
52
+ /* Badge colors */
53
+ --badge-bg: #e01e5a;
54
+ --badge-text: #ffffff;
55
+
56
+ /* Shadows */
57
+ --shadow-modal: 0 18px 50px rgba(0, 0, 0, 0.6);
58
+ --shadow-message: 0 1px 2px rgba(0, 0, 0, 0.1);
59
+
60
+ /* Dimensions */
61
+ --sidebar-width: 260px;
62
+ --header-height: 49px;
63
+ --composer-min-height: 44px;
64
+
65
+ /* Typography */
66
+ --font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
67
+ --font-mono: 'JetBrains Mono', ui-monospace, monospace;
68
+
69
+ /* Transitions */
70
+ --transition-fast: 0.1s ease;
71
+ --transition-normal: 0.2s ease;
72
+ }
73
+
74
+ /* ========================================
75
+ Reset & Base
76
+ ======================================== */
77
+ *, *::before, *::after {
78
+ box-sizing: border-box;
79
+ margin: 0;
80
+ padding: 0;
81
+ }
82
+
83
+ html, body {
84
+ height: 100%;
85
+ overflow: hidden;
86
+ }
27
87
 
28
88
  body {
29
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
30
- background: var(--bg-primary);
31
- color: var(--text);
32
- min-height: 100vh;
33
- overflow-x: hidden;
89
+ font-family: var(--font-family);
90
+ font-size: 15px;
91
+ line-height: 1.46668;
92
+ color: var(--text-primary);
93
+ background: var(--bg-workspace);
94
+ -webkit-font-smoothing: antialiased;
95
+ -moz-osx-font-smoothing: grayscale;
34
96
  }
35
97
 
36
- .container {
37
- max-width: 1400px;
38
- margin: 0 auto;
39
- display: grid;
40
- grid-template-columns: 320px 1fr;
41
- gap: 1px;
98
+ /* ========================================
99
+ Layout Structure
100
+ ======================================== */
101
+ .app-container {
102
+ display: flex;
42
103
  height: 100vh;
43
- background: var(--border-color);
104
+ width: 100vw;
44
105
  }
45
106
 
107
+ /* Sidebar */
46
108
  .sidebar {
109
+ width: var(--sidebar-width);
110
+ background: var(--bg-sidebar);
47
111
  display: flex;
48
112
  flex-direction: column;
49
- background: var(--bg-primary);
50
- overflow: hidden;
113
+ flex-shrink: 0;
114
+ border-right: 1px solid var(--border-divider);
51
115
  }
52
116
 
53
- .main-content {
117
+ /* Main Content */
118
+ .main-panel {
119
+ flex: 1;
54
120
  display: flex;
55
121
  flex-direction: column;
56
- background: var(--bg-primary);
57
- overflow: hidden;
122
+ min-width: 0;
123
+ background: var(--bg-main);
58
124
  }
59
125
 
60
- .header {
126
+ /* ========================================
127
+ Sidebar Components
128
+ ======================================== */
129
+
130
+ /* Workspace Header */
131
+ .workspace-header {
132
+ height: var(--header-height);
133
+ padding: 0 16px;
61
134
  display: flex;
62
- justify-content: space-between;
63
135
  align-items: center;
64
- padding: 16px 20px;
65
- border-bottom: 1px solid var(--border-color);
136
+ justify-content: space-between;
137
+ border-bottom: 1px solid var(--border-divider);
138
+ flex-shrink: 0;
66
139
  }
67
140
 
68
- .logo {
141
+ .workspace-name {
142
+ font-size: 18px;
143
+ font-weight: 700;
144
+ color: var(--text-primary);
69
145
  display: flex;
70
146
  align-items: center;
71
- gap: 10px;
147
+ gap: 6px;
72
148
  }
73
149
 
74
- .logo-icon {
75
- width: 32px;
76
- height: 32px;
77
- background: var(--primary);
78
- border-radius: 8px;
150
+ .workspace-name .status-dot {
151
+ width: 10px;
152
+ height: 10px;
153
+ border-radius: 50%;
154
+ background: var(--status-online);
155
+ flex-shrink: 0;
156
+ }
157
+
158
+ .workspace-name .status-dot.offline {
159
+ background: var(--status-offline);
160
+ }
161
+
162
+ .compose-new-btn {
163
+ width: 36px;
164
+ height: 36px;
165
+ border-radius: 50%;
166
+ background: rgba(255, 255, 255, 0.1);
167
+ border: none;
168
+ color: var(--text-primary);
169
+ cursor: pointer;
79
170
  display: flex;
80
171
  align-items: center;
81
172
  justify-content: center;
82
- color: white;
83
- font-weight: 600;
84
- font-size: 14px;
173
+ transition: background var(--transition-fast);
85
174
  }
86
175
 
87
- h1 {
88
- font-size: 1rem;
89
- font-weight: 600;
90
- color: var(--text);
176
+ .compose-new-btn:hover {
177
+ background: rgba(255, 255, 255, 0.2);
91
178
  }
92
179
 
93
- h2 {
94
- font-size: 0.875rem;
95
- font-weight: 500;
96
- color: var(--text-secondary);
180
+ /* Command Palette Trigger */
181
+ .search-bar {
182
+ margin: 12px 12px 8px;
183
+ padding: 6px 12px;
184
+ background: rgba(255, 255, 255, 0.06);
185
+ border: 1px solid transparent;
186
+ border-radius: 6px;
187
+ display: flex;
188
+ align-items: center;
189
+ gap: 8px;
190
+ cursor: pointer;
191
+ transition: all var(--transition-fast);
192
+ }
193
+
194
+ .search-bar:hover {
195
+ background: rgba(255, 255, 255, 0.1);
196
+ }
197
+
198
+ .search-bar svg {
199
+ width: 16px;
200
+ height: 16px;
201
+ color: var(--text-muted);
202
+ }
203
+
204
+ .search-bar span {
205
+ font-size: 13px;
206
+ color: var(--text-muted);
207
+ flex: 1;
208
+ }
209
+
210
+ .search-bar kbd {
211
+ font-family: var(--font-family);
212
+ font-size: 11px;
213
+ color: var(--text-muted);
214
+ background: rgba(255, 255, 255, 0.1);
215
+ padding: 2px 6px;
216
+ border-radius: 4px;
97
217
  }
98
218
 
99
- .agents-section {
219
+ /* Sidebar Sections */
220
+ .sidebar-content {
100
221
  flex: 1;
101
222
  overflow-y: auto;
102
- padding: 12px;
223
+ padding: 8px 0;
103
224
  }
104
225
 
105
- .agents-header {
106
- padding: 8px 8px 12px;
107
- font-size: 0.75rem;
108
- font-weight: 500;
226
+ .section-header {
227
+ padding: 8px 16px 4px;
228
+ display: flex;
229
+ align-items: center;
230
+ justify-content: space-between;
231
+ cursor: pointer;
232
+ }
233
+
234
+ .section-title {
235
+ font-size: 15px;
236
+ font-weight: 600;
237
+ color: var(--text-channel);
238
+ display: flex;
239
+ align-items: center;
240
+ gap: 4px;
241
+ }
242
+
243
+ .section-title svg {
244
+ width: 12px;
245
+ height: 12px;
246
+ transition: transform var(--transition-fast);
247
+ }
248
+
249
+ .section-header.collapsed .section-title svg {
250
+ transform: rotate(-90deg);
251
+ }
252
+
253
+ .section-add-btn {
254
+ width: 24px;
255
+ height: 24px;
256
+ border: none;
257
+ background: transparent;
109
258
  color: var(--text-muted);
110
- text-transform: uppercase;
111
- letter-spacing: 0.05em;
259
+ cursor: pointer;
260
+ border-radius: 4px;
261
+ display: flex;
262
+ align-items: center;
263
+ justify-content: center;
264
+ opacity: 0;
265
+ transition: all var(--transition-fast);
112
266
  }
113
267
 
114
- .agent-card {
115
- padding: 12px;
116
- border-radius: 8px;
117
- margin-bottom: 4px;
118
- transition: background 0.15s;
119
- cursor: default;
268
+ .section-header:hover .section-add-btn {
269
+ opacity: 1;
270
+ }
271
+
272
+ .section-add-btn:hover {
273
+ background: rgba(255, 255, 255, 0.1);
274
+ color: var(--text-primary);
120
275
  }
121
276
 
122
- .agent-card:hover {
123
- background: var(--bg-secondary);
277
+ /* Channel/Agent List */
278
+ .channel-list {
279
+ list-style: none;
124
280
  }
125
281
 
126
- .agent-card.active {
127
- background: var(--bg-elevated);
282
+ /* Sidebar Footer */
283
+ .sidebar-footer {
284
+ padding: 12px 16px;
285
+ border-top: 1px solid var(--border-divider);
128
286
  }
129
287
 
130
- .agent-header {
288
+ .nav-link {
131
289
  display: flex;
132
- justify-content: space-between;
133
290
  align-items: center;
134
- margin-bottom: 8px;
291
+ gap: 8px;
292
+ padding: 8px 12px;
293
+ color: var(--text-muted);
294
+ text-decoration: none;
295
+ font-size: 13px;
296
+ border-radius: 6px;
297
+ transition: all var(--transition-fast);
135
298
  }
136
299
 
137
- .agent-name {
138
- font-weight: 600;
139
- font-size: 0.875rem;
140
- color: var(--text);
300
+ .nav-link:hover {
301
+ background: var(--bg-channel-hover);
302
+ color: var(--text-primary);
303
+ }
304
+
305
+ .nav-link svg {
306
+ width: 16px;
307
+ height: 16px;
141
308
  }
142
309
 
143
- .agent-meta {
310
+ .channel-item {
144
311
  display: flex;
145
312
  align-items: center;
146
- gap: 6px;
147
- margin-bottom: 10px;
313
+ padding: 6px 20px 6px 16px;
314
+ cursor: pointer;
315
+ transition: background var(--transition-fast);
316
+ gap: 8px;
317
+ position: relative;
318
+ }
319
+
320
+ .channel-item:hover {
321
+ background: var(--bg-channel-hover);
322
+ }
323
+
324
+ .channel-item.active {
325
+ background: var(--bg-channel-active);
326
+ }
327
+
328
+ .channel-item.active .channel-name,
329
+ .channel-item.active .channel-prefix {
330
+ color: var(--text-channel-active);
148
331
  }
149
332
 
150
- .agent-role {
151
- font-size: 0.75rem;
333
+ .channel-prefix {
334
+ font-size: 15px;
152
335
  color: var(--text-muted);
336
+ width: 18px;
337
+ text-align: center;
338
+ flex-shrink: 0;
153
339
  }
154
340
 
155
- .cli-badge {
156
- background: var(--bg-elevated);
157
- color: var(--text-secondary);
158
- padding: 2px 6px;
159
- border-radius: 4px;
160
- font-size: 0.7rem;
161
- font-weight: 500;
341
+ .channel-name {
342
+ font-size: 15px;
343
+ color: var(--text-channel);
344
+ flex: 1;
345
+ white-space: nowrap;
346
+ overflow: hidden;
347
+ text-overflow: ellipsis;
348
+ }
349
+
350
+ .channel-item.unread .channel-name {
351
+ font-weight: 600;
352
+ color: var(--text-primary);
162
353
  }
163
354
 
164
- .agent-status {
355
+ .unread-badge {
356
+ min-width: 18px;
357
+ height: 18px;
358
+ padding: 0 5px;
359
+ background: var(--badge-bg);
360
+ color: var(--badge-text);
361
+ font-size: 12px;
362
+ font-weight: 600;
363
+ border-radius: 9px;
165
364
  display: flex;
166
365
  align-items: center;
167
- gap: 6px;
168
- font-size: 0.8rem;
169
- color: var(--text-secondary);
366
+ justify-content: center;
170
367
  }
171
368
 
172
- .status-dot {
173
- width: 6px;
174
- height: 6px;
369
+ /* Agent item with presence */
370
+ .agent-avatar {
371
+ width: 20px;
372
+ height: 20px;
373
+ border-radius: 4px;
374
+ background: var(--accent-purple);
375
+ color: white;
376
+ font-size: 10px;
377
+ font-weight: 600;
378
+ display: flex;
379
+ align-items: center;
380
+ justify-content: center;
381
+ position: relative;
382
+ flex-shrink: 0;
383
+ }
384
+
385
+ .presence-indicator {
386
+ position: absolute;
387
+ bottom: -2px;
388
+ right: -2px;
389
+ width: 10px;
390
+ height: 10px;
175
391
  border-radius: 50%;
176
- background: var(--success);
392
+ border: 2px solid var(--bg-sidebar);
393
+ background: var(--status-offline);
177
394
  }
178
395
 
179
- .badge {
180
- background: var(--primary-muted);
181
- color: var(--primary);
182
- padding: 2px 8px;
183
- border-radius: 10px;
184
- font-size: 0.7rem;
396
+ .presence-indicator.online {
397
+ background: var(--status-online);
398
+ }
399
+
400
+ .presence-indicator.away {
401
+ background: var(--status-away);
402
+ }
403
+
404
+ .presence-indicator.busy {
405
+ background: var(--status-busy);
406
+ }
407
+
408
+ /* Needs Attention Indicator */
409
+ .channel-item.needs-attention {
410
+ background: rgba(232, 164, 39, 0.1);
411
+ }
412
+
413
+ .channel-item.needs-attention::after {
414
+ content: '';
415
+ position: absolute;
416
+ top: 50%;
417
+ right: 12px;
418
+ transform: translateY(-50%);
419
+ width: 8px;
420
+ height: 8px;
421
+ background: var(--accent-yellow);
422
+ border-radius: 50%;
423
+ animation: attentionPulse 1.5s ease-in-out infinite;
424
+ }
425
+
426
+ .attention-badge {
427
+ display: inline-flex;
428
+ align-items: center;
429
+ gap: 4px;
430
+ padding: 2px 6px;
431
+ margin-left: 8px;
432
+ background: var(--accent-yellow);
433
+ color: #1a1d21;
434
+ font-size: 10px;
185
435
  font-weight: 600;
436
+ border-radius: 4px;
437
+ text-transform: uppercase;
438
+ letter-spacing: 0.02em;
439
+ }
440
+
441
+ @keyframes attentionPulse {
442
+ 0%, 100% {
443
+ opacity: 1;
444
+ transform: translateY(-50%) scale(1);
445
+ }
446
+ 50% {
447
+ opacity: 0.5;
448
+ transform: translateY(-50%) scale(1.2);
449
+ }
450
+ }
451
+
452
+ /* ========================================
453
+ Main Panel Components
454
+ ======================================== */
455
+
456
+ /* Channel Header */
457
+ .channel-header {
458
+ height: var(--header-height);
459
+ padding: 0 20px;
460
+ display: flex;
461
+ align-items: center;
462
+ border-bottom: 1px solid var(--border-divider);
463
+ flex-shrink: 0;
464
+ gap: 8px;
186
465
  }
187
466
 
188
- /* Activity Log */
189
- .activity-log {
467
+ .channel-header-name {
468
+ font-size: 18px;
469
+ font-weight: 700;
470
+ color: var(--text-primary);
190
471
  display: flex;
191
- flex-direction: column;
472
+ align-items: center;
473
+ gap: 6px;
474
+ }
475
+
476
+ .channel-header-name .prefix {
477
+ color: var(--text-muted);
478
+ font-weight: 500;
479
+ }
480
+
481
+ .header-divider {
482
+ width: 1px;
483
+ height: 20px;
484
+ background: var(--border-subtle);
485
+ margin: 0 8px;
486
+ }
487
+
488
+ .channel-topic {
489
+ font-size: 13px;
490
+ color: var(--text-muted);
192
491
  flex: 1;
492
+ white-space: nowrap;
193
493
  overflow: hidden;
494
+ text-overflow: ellipsis;
194
495
  }
195
496
 
196
- .log-header {
197
- padding: 16px 20px;
198
- border-bottom: 1px solid var(--border-color);
497
+ .header-actions {
199
498
  display: flex;
200
- justify-content: space-between;
201
499
  align-items: center;
500
+ gap: 4px;
202
501
  }
203
502
 
204
- .live-indicator {
503
+ .header-action-btn {
504
+ width: 28px;
505
+ height: 28px;
506
+ border: none;
507
+ background: transparent;
508
+ color: var(--text-muted);
509
+ border-radius: 4px;
510
+ cursor: pointer;
205
511
  display: flex;
206
512
  align-items: center;
207
- gap: 6px;
208
- font-size: 0.75rem;
513
+ justify-content: center;
514
+ transition: all var(--transition-fast);
515
+ }
516
+
517
+ .header-action-btn:hover {
518
+ background: rgba(255, 255, 255, 0.1);
519
+ color: var(--text-primary);
520
+ }
521
+
522
+ .online-count {
523
+ font-size: 13px;
209
524
  color: var(--text-muted);
525
+ display: flex;
526
+ align-items: center;
527
+ gap: 4px;
210
528
  }
211
529
 
212
- .live-dot {
213
- width: 6px;
214
- height: 6px;
530
+ .online-count .dot {
531
+ width: 8px;
532
+ height: 8px;
215
533
  border-radius: 50%;
216
- background: var(--success);
534
+ background: var(--status-online);
217
535
  }
218
536
 
219
- .log-content {
537
+ /* Message List */
538
+ .messages-container {
539
+ flex: 1;
220
540
  overflow-y: auto;
541
+ overflow-x: hidden;
542
+ display: flex;
543
+ flex-direction: column;
544
+ }
545
+
546
+ .messages-list {
221
547
  flex: 1;
548
+ padding: 16px 0;
549
+ }
550
+
551
+ /* Date Divider */
552
+ .date-divider {
222
553
  display: flex;
223
- flex-direction: column-reverse;
554
+ align-items: center;
555
+ margin: 16px 20px;
556
+ }
557
+
558
+ .date-divider::before,
559
+ .date-divider::after {
560
+ content: '';
561
+ flex: 1;
562
+ height: 1px;
563
+ background: var(--border-divider);
564
+ }
565
+
566
+ .date-divider-text {
567
+ padding: 4px 16px;
568
+ font-size: 13px;
569
+ font-weight: 600;
570
+ color: var(--text-secondary);
571
+ background: var(--bg-main);
572
+ border: 1px solid var(--border-divider);
573
+ border-radius: 24px;
224
574
  }
225
575
 
576
+ /* Message */
226
577
  .message {
227
- padding: 16px 20px;
228
- border-bottom: 1px solid var(--border-color);
578
+ padding: 8px 20px;
229
579
  display: flex;
230
- gap: 12px;
231
- transition: background 0.15s;
580
+ gap: 8px;
581
+ transition: background var(--transition-fast);
582
+ position: relative;
232
583
  }
233
584
 
234
585
  .message:hover {
235
- background: var(--bg-secondary);
586
+ background: var(--bg-message-hover);
236
587
  }
237
588
 
238
- .message:last-child { border-bottom: none; }
589
+ .message:hover .message-actions {
590
+ opacity: 1;
591
+ }
239
592
 
240
- .msg-avatar {
593
+ .message-avatar {
241
594
  width: 36px;
242
595
  height: 36px;
243
- border-radius: 8px;
244
- background: var(--bg-elevated);
245
- color: var(--text-secondary);
596
+ border-radius: 4px;
597
+ flex-shrink: 0;
246
598
  display: flex;
247
599
  align-items: center;
248
600
  justify-content: center;
249
601
  font-weight: 600;
250
- font-size: 0.8rem;
251
- flex-shrink: 0;
602
+ font-size: 14px;
603
+ color: white;
252
604
  }
253
605
 
254
- .msg-body { flex: 1; min-width: 0; }
606
+ .message-content {
607
+ flex: 1;
608
+ min-width: 0;
609
+ }
255
610
 
256
- .msg-meta {
611
+ .message-header {
257
612
  display: flex;
258
- align-items: center;
613
+ align-items: baseline;
259
614
  gap: 8px;
260
- margin-bottom: 6px;
615
+ margin-bottom: 4px;
261
616
  }
262
617
 
263
- .msg-sender {
264
- font-weight: 600;
265
- font-size: 0.875rem;
266
- color: var(--text);
618
+ .message-sender {
619
+ font-size: 15px;
620
+ font-weight: 700;
621
+ color: var(--text-primary);
267
622
  }
268
623
 
269
- .msg-arrow {
624
+ .message-recipient {
625
+ font-size: 13px;
270
626
  color: var(--text-muted);
271
- font-size: 0.75rem;
272
627
  }
273
628
 
274
- .msg-target {
275
- color: var(--primary);
276
- font-weight: 500;
277
- font-size: 0.875rem;
629
+ .message-recipient .target {
630
+ color: var(--text-link);
631
+ }
632
+
633
+ .project-badge {
634
+ display: inline-block;
635
+ background: var(--accent-primary);
636
+ color: white;
637
+ font-size: 10px;
638
+ font-weight: 600;
639
+ padding: 2px 6px;
640
+ border-radius: 4px;
641
+ margin-right: 4px;
642
+ text-transform: uppercase;
643
+ letter-spacing: 0.5px;
278
644
  }
279
645
 
280
- .msg-time {
646
+ .message-timestamp {
647
+ font-size: 12px;
281
648
  color: var(--text-muted);
282
- font-size: 0.75rem;
283
- margin-left: auto;
284
649
  }
285
650
 
286
- .msg-text {
287
- font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
288
- font-size: 0.8rem;
651
+ .message-body {
652
+ font-size: 15px;
653
+ line-height: 1.46668;
654
+ color: var(--text-primary);
655
+ word-wrap: break-word;
289
656
  white-space: pre-wrap;
290
- color: var(--text-secondary);
291
- background: var(--bg-secondary);
292
- padding: 12px;
293
- border-radius: 6px;
294
- line-height: 1.5;
295
657
  }
296
658
 
297
- /* Connection Status */
298
- #connection-status {
299
- font-size: 0.75rem;
300
- font-weight: 500;
301
- padding: 4px 10px;
302
- border-radius: 6px;
303
- background: var(--success-muted);
304
- color: var(--success);
305
- display: flex;
306
- align-items: center;
307
- gap: 6px;
308
- transition: all 0.15s;
659
+ .message-body code {
660
+ font-family: var(--font-mono);
661
+ font-size: 13px;
662
+ background: rgba(255, 255, 255, 0.06);
663
+ padding: 2px 4px;
664
+ border-radius: 3px;
309
665
  }
310
666
 
311
- #connection-status .dot {
312
- width: 6px;
313
- height: 6px;
314
- border-radius: 50%;
315
- background: var(--success);
667
+ .message-body pre {
668
+ font-family: var(--font-mono);
669
+ font-size: 13px;
670
+ background: rgba(0, 0, 0, 0.3);
671
+ padding: 12px;
672
+ border-radius: 4px;
673
+ margin: 8px 0;
674
+ overflow-x: auto;
675
+ white-space: pre-wrap;
676
+ word-wrap: break-word;
316
677
  }
317
678
 
318
- .disconnected {
319
- background: var(--error-muted) !important;
320
- color: var(--error) !important;
679
+ .message-body pre code {
680
+ background: transparent;
681
+ padding: 0;
682
+ border-radius: 0;
321
683
  }
322
684
 
323
- .disconnected .dot {
324
- background: var(--error) !important;
685
+ .message-body strong {
686
+ font-weight: 700;
687
+ color: var(--text-primary);
325
688
  }
326
689
 
327
- .empty-state {
328
- text-align: center;
329
- padding: 48px 24px;
330
- color: var(--text-muted);
690
+ .message-body em {
691
+ font-style: italic;
331
692
  }
332
693
 
333
- .empty-state-text {
334
- font-size: 0.875rem;
335
- line-height: 1.6;
694
+ /* Broadcast message styling */
695
+ .message.broadcast {
696
+ border-left: 3px solid var(--accent-yellow);
697
+ margin-left: 17px;
698
+ padding-left: 17px;
336
699
  }
337
700
 
338
- /* Scrollbar */
339
- ::-webkit-scrollbar { width: 6px; }
340
- ::-webkit-scrollbar-track { background: transparent; }
341
- ::-webkit-scrollbar-thumb {
342
- background: var(--border-hover);
343
- border-radius: 3px;
701
+ .message.broadcast .message-recipient .target {
702
+ color: var(--accent-yellow);
344
703
  }
345
- ::-webkit-scrollbar-thumb:hover {
346
- background: var(--text-muted);
704
+
705
+ /* Message Actions (hover toolbar) */
706
+ .message-actions {
707
+ position: absolute;
708
+ top: -16px;
709
+ right: 20px;
710
+ background: var(--bg-tooltip);
711
+ border: 1px solid var(--border-subtle);
712
+ border-radius: 6px;
713
+ padding: 4px;
714
+ display: flex;
715
+ gap: 2px;
716
+ opacity: 0;
717
+ transition: opacity var(--transition-fast);
718
+ box-shadow: var(--shadow-message);
347
719
  }
348
720
 
349
- .last-active {
350
- margin-top: 8px;
351
- font-size: 0.7rem;
721
+ .message-action-btn {
722
+ width: 28px;
723
+ height: 28px;
724
+ border: none;
725
+ background: transparent;
352
726
  color: var(--text-muted);
353
- }
727
+ border-radius: 4px;
728
+ cursor: pointer;
729
+ display: flex;
730
+ align-items: center;
731
+ justify-content: center;
732
+ transition: all var(--transition-fast);
733
+ }
734
+
735
+ .message-action-btn:hover {
736
+ background: rgba(255, 255, 255, 0.1);
737
+ color: var(--text-primary);
738
+ }
739
+
740
+ /* Thread Indicator */
741
+ .thread-indicator {
742
+ display: inline-flex;
743
+ align-items: center;
744
+ gap: 4px;
745
+ margin-top: 6px;
746
+ padding: 4px 8px;
747
+ background: rgba(255, 255, 255, 0.04);
748
+ border-radius: 4px;
749
+ font-size: 13px;
750
+ color: var(--text-link);
751
+ cursor: pointer;
752
+ transition: background var(--transition-fast);
753
+ }
754
+
755
+ .thread-indicator:hover {
756
+ background: rgba(255, 255, 255, 0.08);
757
+ }
758
+
759
+ .thread-indicator svg {
760
+ width: 14px;
761
+ height: 14px;
762
+ }
763
+
764
+ /* Typing Indicator */
765
+ .typing-indicator {
766
+ padding: 8px 20px;
767
+ font-size: 13px;
768
+ color: var(--text-muted);
769
+ display: none;
770
+ }
771
+
772
+ .typing-indicator.visible {
773
+ display: flex;
774
+ align-items: center;
775
+ gap: 8px;
776
+ }
777
+
778
+ .typing-dots {
779
+ display: flex;
780
+ gap: 3px;
781
+ }
782
+
783
+ .typing-dots span {
784
+ width: 6px;
785
+ height: 6px;
786
+ background: var(--text-muted);
787
+ border-radius: 50%;
788
+ animation: typingBounce 1.4s infinite;
789
+ }
790
+
791
+ .typing-dots span:nth-child(2) { animation-delay: 0.2s; }
792
+ .typing-dots span:nth-child(3) { animation-delay: 0.4s; }
793
+
794
+ @keyframes typingBounce {
795
+ 0%, 60%, 100% { transform: translateY(0); }
796
+ 30% { transform: translateY(-4px); }
797
+ }
798
+
799
+ /* ========================================
800
+ Composer
801
+ ======================================== */
802
+ .composer {
803
+ padding: 0 20px 20px;
804
+ flex-shrink: 0;
805
+ position: relative;
806
+ z-index: 10;
807
+ }
808
+
809
+ .composer-container {
810
+ background: var(--bg-input);
811
+ border: 1px solid var(--border-subtle);
812
+ border-radius: 8px;
813
+ overflow: visible;
814
+ transition: border-color var(--transition-fast);
815
+ position: relative;
816
+ }
817
+
818
+ .composer-container:focus-within {
819
+ border-color: var(--accent-primary);
820
+ }
821
+
822
+ .composer-input-row {
823
+ display: flex;
824
+ align-items: flex-end;
825
+ }
826
+
827
+ .composer-toolbar {
828
+ padding: 8px 12px;
829
+ display: flex;
830
+ align-items: center;
831
+ gap: 4px;
832
+ border-right: 1px solid var(--border-divider);
833
+ }
834
+
835
+ .toolbar-btn {
836
+ width: 28px;
837
+ height: 28px;
838
+ border: none;
839
+ background: transparent;
840
+ color: var(--text-muted);
841
+ border-radius: 4px;
842
+ cursor: pointer;
843
+ display: flex;
844
+ align-items: center;
845
+ justify-content: center;
846
+ transition: all var(--transition-fast);
847
+ }
848
+
849
+ .toolbar-btn:hover {
850
+ background: rgba(255, 255, 255, 0.1);
851
+ color: var(--text-primary);
852
+ }
853
+
854
+ .composer-input {
855
+ flex: 1;
856
+ padding: 12px;
857
+ background: transparent;
858
+ border: none;
859
+ color: var(--text-primary);
860
+ font-family: var(--font-family);
861
+ font-size: 15px;
862
+ line-height: 1.46668;
863
+ resize: none;
864
+ outline: none;
865
+ min-height: var(--composer-min-height);
866
+ max-height: 200px;
867
+ }
868
+
869
+ .composer-input::placeholder {
870
+ color: var(--text-muted);
871
+ }
872
+
873
+ .composer-send {
874
+ padding: 8px 12px;
875
+ display: flex;
876
+ align-items: center;
877
+ }
878
+
879
+ .send-btn {
880
+ width: 32px;
881
+ height: 32px;
882
+ border: none;
883
+ background: var(--accent-primary);
884
+ color: white;
885
+ border-radius: 4px;
886
+ cursor: pointer;
887
+ display: flex;
888
+ align-items: center;
889
+ justify-content: center;
890
+ transition: all var(--transition-fast);
891
+ }
892
+
893
+ .send-btn:hover {
894
+ background: #0b5d99;
895
+ }
896
+
897
+ .send-btn:disabled {
898
+ background: var(--border-subtle);
899
+ cursor: not-allowed;
900
+ }
901
+
902
+ .composer-hint {
903
+ padding: 8px 12px 0;
904
+ font-size: 12px;
905
+ color: var(--text-muted);
906
+ text-align: right;
907
+ }
908
+
909
+ .composer-hint kbd {
910
+ font-family: var(--font-family);
911
+ background: rgba(255, 255, 255, 0.06);
912
+ padding: 2px 5px;
913
+ border-radius: 3px;
914
+ font-size: 11px;
915
+ }
916
+
917
+ /* ========================================
918
+ @-Mention Autocomplete
919
+ ======================================== */
920
+ .mention-autocomplete {
921
+ position: absolute;
922
+ bottom: 100%;
923
+ left: 0;
924
+ right: 0;
925
+ margin-bottom: 8px;
926
+ background: var(--bg-modal);
927
+ border: 1px solid var(--border-subtle);
928
+ border-radius: 8px;
929
+ box-shadow: var(--shadow-modal);
930
+ max-height: 240px;
931
+ overflow-y: auto;
932
+ display: none;
933
+ z-index: 500;
934
+ }
935
+
936
+ .mention-autocomplete.visible {
937
+ display: block;
938
+ animation: autocompleteSlideIn 0.1s ease-out;
939
+ }
940
+
941
+ @keyframes autocompleteSlideIn {
942
+ from {
943
+ opacity: 0;
944
+ transform: translateY(4px);
945
+ }
946
+ to {
947
+ opacity: 1;
948
+ transform: translateY(0);
949
+ }
950
+ }
951
+
952
+ .mention-autocomplete-header {
953
+ padding: 8px 12px 4px;
954
+ font-size: 11px;
955
+ font-weight: 600;
956
+ color: var(--text-muted);
957
+ text-transform: uppercase;
958
+ letter-spacing: 0.05em;
959
+ border-bottom: 1px solid var(--border-divider);
960
+ }
961
+
962
+ .mention-autocomplete-item {
963
+ padding: 8px 12px;
964
+ display: flex;
965
+ align-items: center;
966
+ gap: 10px;
967
+ cursor: pointer;
968
+ transition: background var(--transition-fast);
969
+ }
970
+
971
+ .mention-autocomplete-item:hover,
972
+ .mention-autocomplete-item.selected {
973
+ background: rgba(255, 255, 255, 0.08);
974
+ }
975
+
976
+ .mention-autocomplete-item .agent-avatar {
977
+ width: 24px;
978
+ height: 24px;
979
+ font-size: 10px;
980
+ }
981
+
982
+ .mention-autocomplete-name {
983
+ font-size: 14px;
984
+ font-weight: 500;
985
+ color: var(--text-primary);
986
+ }
987
+
988
+ .mention-autocomplete-role {
989
+ font-size: 12px;
990
+ color: var(--text-muted);
991
+ margin-left: auto;
992
+ }
993
+
994
+ .mention-autocomplete-hint {
995
+ padding: 6px 12px;
996
+ font-size: 11px;
997
+ color: var(--text-muted);
998
+ border-top: 1px solid var(--border-divider);
999
+ display: flex;
1000
+ gap: 12px;
1001
+ }
1002
+
1003
+ .mention-autocomplete-hint kbd {
1004
+ font-family: var(--font-family);
1005
+ background: rgba(255, 255, 255, 0.1);
1006
+ padding: 1px 5px;
1007
+ border-radius: 3px;
1008
+ font-size: 10px;
1009
+ }
1010
+
1011
+ /* ========================================
1012
+ Command Palette
1013
+ ======================================== */
1014
+ .command-palette-overlay {
1015
+ position: fixed;
1016
+ inset: 0;
1017
+ background: rgba(0, 0, 0, 0.6);
1018
+ display: none;
1019
+ align-items: flex-start;
1020
+ justify-content: center;
1021
+ padding-top: 15vh;
1022
+ z-index: 1000;
1023
+ }
1024
+
1025
+ .command-palette-overlay.visible {
1026
+ display: flex;
1027
+ }
1028
+
1029
+ .command-palette {
1030
+ width: 600px;
1031
+ max-width: 90vw;
1032
+ background: var(--bg-modal);
1033
+ border-radius: 8px;
1034
+ box-shadow: var(--shadow-modal);
1035
+ overflow: hidden;
1036
+ animation: paletteSlideIn 0.15s ease-out;
1037
+ }
1038
+
1039
+ @keyframes paletteSlideIn {
1040
+ from {
1041
+ opacity: 0;
1042
+ transform: translateY(-10px) scale(0.98);
1043
+ }
1044
+ to {
1045
+ opacity: 1;
1046
+ transform: translateY(0) scale(1);
1047
+ }
1048
+ }
1049
+
1050
+ .palette-search {
1051
+ padding: 16px;
1052
+ border-bottom: 1px solid var(--border-divider);
1053
+ }
1054
+
1055
+ .palette-search-input {
1056
+ width: 100%;
1057
+ background: transparent;
1058
+ border: none;
1059
+ color: var(--text-primary);
1060
+ font-family: var(--font-family);
1061
+ font-size: 18px;
1062
+ outline: none;
1063
+ }
1064
+
1065
+ .palette-search-input::placeholder {
1066
+ color: var(--text-muted);
1067
+ }
1068
+
1069
+ .palette-results {
1070
+ max-height: 400px;
1071
+ overflow-y: auto;
1072
+ }
1073
+
1074
+ .palette-section {
1075
+ padding: 8px 0;
1076
+ }
1077
+
1078
+ .palette-section-title {
1079
+ padding: 8px 16px 4px;
1080
+ font-size: 12px;
1081
+ font-weight: 600;
1082
+ color: var(--text-muted);
1083
+ text-transform: uppercase;
1084
+ letter-spacing: 0.05em;
1085
+ }
1086
+
1087
+ .palette-item {
1088
+ padding: 10px 16px;
1089
+ display: flex;
1090
+ align-items: center;
1091
+ gap: 12px;
1092
+ cursor: pointer;
1093
+ transition: background var(--transition-fast);
1094
+ }
1095
+
1096
+ .palette-item:hover,
1097
+ .palette-item.selected {
1098
+ background: rgba(255, 255, 255, 0.06);
1099
+ }
1100
+
1101
+ .palette-item-icon {
1102
+ width: 20px;
1103
+ height: 20px;
1104
+ color: var(--text-muted);
1105
+ display: flex;
1106
+ align-items: center;
1107
+ justify-content: center;
1108
+ }
1109
+
1110
+ .palette-item-content {
1111
+ flex: 1;
1112
+ }
1113
+
1114
+ .palette-item-title {
1115
+ font-size: 14px;
1116
+ color: var(--text-primary);
1117
+ }
1118
+
1119
+ .palette-item-subtitle {
1120
+ font-size: 12px;
1121
+ color: var(--text-muted);
1122
+ }
1123
+
1124
+ .palette-item-shortcut {
1125
+ font-size: 12px;
1126
+ color: var(--text-muted);
1127
+ }
1128
+
1129
+ .palette-item-shortcut kbd {
1130
+ font-family: var(--font-family);
1131
+ background: rgba(255, 255, 255, 0.1);
1132
+ padding: 2px 6px;
1133
+ border-radius: 3px;
1134
+ margin-left: 4px;
1135
+ }
1136
+
1137
+ /* ========================================
1138
+ Thread Panel
1139
+ ======================================== */
1140
+ .thread-panel-overlay {
1141
+ position: fixed;
1142
+ top: 0;
1143
+ right: 0;
1144
+ bottom: 0;
1145
+ width: 400px;
1146
+ background: var(--bg-modal);
1147
+ border-left: 1px solid var(--border-divider);
1148
+ display: none;
1149
+ flex-direction: column;
1150
+ z-index: 100;
1151
+ box-shadow: -4px 0 20px rgba(0, 0, 0, 0.3);
1152
+ animation: threadSlideIn 0.2s ease-out;
1153
+ }
1154
+
1155
+ .thread-panel-overlay.visible {
1156
+ display: flex;
1157
+ }
1158
+
1159
+ @keyframes threadSlideIn {
1160
+ from {
1161
+ transform: translateX(100%);
1162
+ opacity: 0;
1163
+ }
1164
+ to {
1165
+ transform: translateX(0);
1166
+ opacity: 1;
1167
+ }
1168
+ }
1169
+
1170
+ .thread-panel {
1171
+ display: flex;
1172
+ flex-direction: column;
1173
+ height: 100%;
1174
+ }
1175
+
1176
+ .thread-panel-header {
1177
+ padding: 16px;
1178
+ border-bottom: 1px solid var(--border-divider);
1179
+ display: flex;
1180
+ align-items: center;
1181
+ justify-content: space-between;
1182
+ flex-shrink: 0;
1183
+ }
1184
+
1185
+ .thread-panel-title {
1186
+ display: flex;
1187
+ align-items: center;
1188
+ gap: 8px;
1189
+ font-size: 16px;
1190
+ font-weight: 600;
1191
+ color: var(--text-primary);
1192
+ }
1193
+
1194
+ .thread-panel-title .thread-id {
1195
+ font-size: 13px;
1196
+ font-weight: 500;
1197
+ color: var(--text-link);
1198
+ background: rgba(29, 155, 209, 0.1);
1199
+ padding: 2px 8px;
1200
+ border-radius: 4px;
1201
+ }
1202
+
1203
+ .thread-panel-close {
1204
+ width: 32px;
1205
+ height: 32px;
1206
+ border: none;
1207
+ background: transparent;
1208
+ color: var(--text-muted);
1209
+ border-radius: 4px;
1210
+ cursor: pointer;
1211
+ display: flex;
1212
+ align-items: center;
1213
+ justify-content: center;
1214
+ transition: all var(--transition-fast);
1215
+ }
1216
+
1217
+ .thread-panel-close:hover {
1218
+ background: rgba(255, 255, 255, 0.1);
1219
+ color: var(--text-primary);
1220
+ }
1221
+
1222
+ .thread-messages {
1223
+ flex: 1;
1224
+ overflow-y: auto;
1225
+ padding: 16px;
1226
+ }
1227
+
1228
+ .thread-message {
1229
+ padding: 12px;
1230
+ background: rgba(255, 255, 255, 0.02);
1231
+ border-radius: 8px;
1232
+ margin-bottom: 12px;
1233
+ }
1234
+
1235
+ .thread-message:last-child {
1236
+ margin-bottom: 0;
1237
+ }
1238
+
1239
+ .thread-message-header {
1240
+ display: flex;
1241
+ align-items: center;
1242
+ gap: 8px;
1243
+ margin-bottom: 6px;
1244
+ }
1245
+
1246
+ .thread-message-avatar {
1247
+ width: 28px;
1248
+ height: 28px;
1249
+ border-radius: 4px;
1250
+ display: flex;
1251
+ align-items: center;
1252
+ justify-content: center;
1253
+ font-weight: 600;
1254
+ font-size: 11px;
1255
+ color: white;
1256
+ }
1257
+
1258
+ .thread-message-sender {
1259
+ font-weight: 600;
1260
+ color: var(--text-primary);
1261
+ font-size: 14px;
1262
+ }
1263
+
1264
+ .thread-message-time {
1265
+ font-size: 12px;
1266
+ color: var(--text-muted);
1267
+ }
1268
+
1269
+ .thread-message-body {
1270
+ font-size: 14px;
1271
+ color: var(--text-primary);
1272
+ line-height: 1.4;
1273
+ word-wrap: break-word;
1274
+ white-space: pre-wrap;
1275
+ }
1276
+
1277
+ .thread-composer {
1278
+ padding: 16px;
1279
+ border-top: 1px solid var(--border-divider);
1280
+ display: flex;
1281
+ gap: 8px;
1282
+ align-items: flex-end;
1283
+ flex-shrink: 0;
1284
+ }
1285
+
1286
+ .thread-composer-input {
1287
+ flex: 1;
1288
+ padding: 10px 12px;
1289
+ background: var(--bg-input);
1290
+ border: 1px solid var(--border-subtle);
1291
+ border-radius: 6px;
1292
+ color: var(--text-primary);
1293
+ font-family: var(--font-family);
1294
+ font-size: 14px;
1295
+ resize: none;
1296
+ outline: none;
1297
+ min-height: 40px;
1298
+ max-height: 120px;
1299
+ }
1300
+
1301
+ .thread-composer-input:focus {
1302
+ border-color: var(--accent-primary);
1303
+ }
1304
+
1305
+ .thread-composer-input::placeholder {
1306
+ color: var(--text-muted);
1307
+ }
1308
+
1309
+ .thread-send-btn {
1310
+ width: 36px;
1311
+ height: 36px;
1312
+ border: none;
1313
+ background: var(--accent-primary);
1314
+ color: white;
1315
+ border-radius: 6px;
1316
+ cursor: pointer;
1317
+ display: flex;
1318
+ align-items: center;
1319
+ justify-content: center;
1320
+ transition: all var(--transition-fast);
1321
+ flex-shrink: 0;
1322
+ }
1323
+
1324
+ .thread-send-btn:hover {
1325
+ background: #0b5d99;
1326
+ }
1327
+
1328
+ .thread-empty {
1329
+ text-align: center;
1330
+ padding: 32px;
1331
+ color: var(--text-muted);
1332
+ }
1333
+
1334
+ /* Reply count badge on messages */
1335
+ .reply-count-badge {
1336
+ display: inline-flex;
1337
+ align-items: center;
1338
+ gap: 4px;
1339
+ margin-top: 8px;
1340
+ padding: 4px 8px;
1341
+ background: rgba(29, 155, 209, 0.1);
1342
+ border-radius: 4px;
1343
+ font-size: 12px;
1344
+ color: var(--text-link);
1345
+ cursor: pointer;
1346
+ transition: background var(--transition-fast);
1347
+ }
1348
+
1349
+ .reply-count-badge:hover {
1350
+ background: rgba(29, 155, 209, 0.2);
1351
+ }
1352
+
1353
+ .reply-count-badge svg {
1354
+ width: 14px;
1355
+ height: 14px;
1356
+ }
1357
+
1358
+ /* ========================================
1359
+ Empty States
1360
+ ======================================== */
1361
+ .empty-state {
1362
+ flex: 1;
1363
+ display: flex;
1364
+ flex-direction: column;
1365
+ align-items: center;
1366
+ justify-content: center;
1367
+ padding: 48px 24px;
1368
+ text-align: center;
1369
+ }
1370
+
1371
+ .empty-state-icon {
1372
+ width: 64px;
1373
+ height: 64px;
1374
+ margin-bottom: 16px;
1375
+ color: var(--text-muted);
1376
+ }
1377
+
1378
+ .empty-state-title {
1379
+ font-size: 18px;
1380
+ font-weight: 600;
1381
+ color: var(--text-primary);
1382
+ margin-bottom: 8px;
1383
+ }
1384
+
1385
+ .empty-state-text {
1386
+ font-size: 14px;
1387
+ color: var(--text-muted);
1388
+ max-width: 300px;
1389
+ }
1390
+
1391
+ /* ========================================
1392
+ Scrollbars
1393
+ ======================================== */
1394
+ ::-webkit-scrollbar {
1395
+ width: 8px;
1396
+ height: 8px;
1397
+ }
1398
+
1399
+ ::-webkit-scrollbar-track {
1400
+ background: transparent;
1401
+ }
1402
+
1403
+ ::-webkit-scrollbar-thumb {
1404
+ background: rgba(255, 255, 255, 0.2);
1405
+ border-radius: 4px;
1406
+ }
1407
+
1408
+ ::-webkit-scrollbar-thumb:hover {
1409
+ background: rgba(255, 255, 255, 0.3);
1410
+ }
1411
+
1412
+ /* ========================================
1413
+ Utilities
1414
+ ======================================== */
1415
+ .visually-hidden {
1416
+ position: absolute;
1417
+ width: 1px;
1418
+ height: 1px;
1419
+ padding: 0;
1420
+ margin: -1px;
1421
+ overflow: hidden;
1422
+ clip: rect(0, 0, 0, 0);
1423
+ border: 0;
1424
+ }
1425
+
1426
+ /* ========================================
1427
+ Spawn Modal
1428
+ ======================================== */
1429
+ .spawn-modal-overlay {
1430
+ position: fixed;
1431
+ inset: 0;
1432
+ background: rgba(0, 0, 0, 0.6);
1433
+ display: none;
1434
+ align-items: center;
1435
+ justify-content: center;
1436
+ z-index: 1000;
1437
+ }
1438
+
1439
+ .spawn-modal-overlay.visible {
1440
+ display: flex;
1441
+ }
1442
+
1443
+ .spawn-modal {
1444
+ width: 480px;
1445
+ max-width: 90vw;
1446
+ background: var(--bg-modal);
1447
+ border-radius: 12px;
1448
+ box-shadow: var(--shadow-modal);
1449
+ overflow: hidden;
1450
+ animation: modalSlideIn 0.2s ease-out;
1451
+ }
1452
+
1453
+ @keyframes modalSlideIn {
1454
+ from {
1455
+ opacity: 0;
1456
+ transform: translateY(-20px) scale(0.95);
1457
+ }
1458
+ to {
1459
+ opacity: 1;
1460
+ transform: translateY(0) scale(1);
1461
+ }
1462
+ }
1463
+
1464
+ .spawn-modal-header {
1465
+ padding: 16px 20px;
1466
+ border-bottom: 1px solid var(--border-divider);
1467
+ display: flex;
1468
+ align-items: center;
1469
+ justify-content: space-between;
1470
+ }
1471
+
1472
+ .spawn-modal-title {
1473
+ display: flex;
1474
+ align-items: center;
1475
+ gap: 10px;
1476
+ font-size: 18px;
1477
+ font-weight: 600;
1478
+ color: var(--text-primary);
1479
+ }
1480
+
1481
+ .spawn-modal-title svg {
1482
+ color: var(--accent-green);
1483
+ }
1484
+
1485
+ .spawn-modal-close {
1486
+ width: 32px;
1487
+ height: 32px;
1488
+ border: none;
1489
+ background: transparent;
1490
+ color: var(--text-muted);
1491
+ border-radius: 6px;
1492
+ cursor: pointer;
1493
+ display: flex;
1494
+ align-items: center;
1495
+ justify-content: center;
1496
+ transition: all var(--transition-fast);
1497
+ }
1498
+
1499
+ .spawn-modal-close:hover {
1500
+ background: rgba(255, 255, 255, 0.1);
1501
+ color: var(--text-primary);
1502
+ }
1503
+
1504
+ .spawn-modal-body {
1505
+ padding: 20px;
1506
+ }
1507
+
1508
+ .spawn-form-group {
1509
+ margin-bottom: 16px;
1510
+ }
1511
+
1512
+ .spawn-form-group:last-child {
1513
+ margin-bottom: 0;
1514
+ }
1515
+
1516
+ .spawn-form-group label {
1517
+ display: block;
1518
+ font-size: 13px;
1519
+ font-weight: 600;
1520
+ color: var(--text-secondary);
1521
+ margin-bottom: 6px;
1522
+ }
1523
+
1524
+ .spawn-input,
1525
+ .spawn-textarea {
1526
+ width: 100%;
1527
+ padding: 10px 12px;
1528
+ background: var(--bg-input);
1529
+ border: 1px solid var(--border-subtle);
1530
+ border-radius: 6px;
1531
+ color: var(--text-primary);
1532
+ font-family: var(--font-family);
1533
+ font-size: 14px;
1534
+ transition: border-color var(--transition-fast);
1535
+ }
1536
+
1537
+ .spawn-input:focus,
1538
+ .spawn-textarea:focus {
1539
+ outline: none;
1540
+ border-color: var(--accent-primary);
1541
+ }
1542
+
1543
+ .spawn-input::placeholder,
1544
+ .spawn-textarea::placeholder {
1545
+ color: var(--text-muted);
1546
+ }
1547
+
1548
+ .spawn-textarea {
1549
+ resize: vertical;
1550
+ min-height: 80px;
1551
+ line-height: 1.4;
1552
+ }
1553
+
1554
+ .spawn-hint {
1555
+ display: block;
1556
+ font-size: 12px;
1557
+ color: var(--text-muted);
1558
+ margin-top: 4px;
1559
+ }
1560
+
1561
+ .spawn-status {
1562
+ min-height: 20px;
1563
+ font-size: 13px;
1564
+ margin-top: 12px;
1565
+ }
1566
+
1567
+ .spawn-status.success {
1568
+ color: var(--accent-green);
1569
+ }
1570
+
1571
+ .spawn-status.error {
1572
+ color: var(--accent-red);
1573
+ }
1574
+
1575
+ .spawn-status.loading {
1576
+ color: var(--text-muted);
1577
+ }
1578
+
1579
+ .spawn-modal-footer {
1580
+ padding: 16px 20px;
1581
+ border-top: 1px solid var(--border-divider);
1582
+ display: flex;
1583
+ justify-content: flex-end;
1584
+ gap: 10px;
1585
+ }
1586
+
1587
+ .spawn-cancel-btn {
1588
+ padding: 8px 16px;
1589
+ background: transparent;
1590
+ border: 1px solid var(--border-subtle);
1591
+ border-radius: 6px;
1592
+ color: var(--text-secondary);
1593
+ font-size: 14px;
1594
+ font-weight: 500;
1595
+ cursor: pointer;
1596
+ transition: all var(--transition-fast);
1597
+ }
1598
+
1599
+ .spawn-cancel-btn:hover {
1600
+ background: rgba(255, 255, 255, 0.05);
1601
+ color: var(--text-primary);
1602
+ }
1603
+
1604
+ .spawn-submit-btn {
1605
+ display: flex;
1606
+ align-items: center;
1607
+ gap: 6px;
1608
+ padding: 8px 16px;
1609
+ background: var(--accent-green);
1610
+ border: none;
1611
+ border-radius: 6px;
1612
+ color: white;
1613
+ font-size: 14px;
1614
+ font-weight: 600;
1615
+ cursor: pointer;
1616
+ transition: all var(--transition-fast);
1617
+ }
1618
+
1619
+ .spawn-submit-btn:hover:not(:disabled) {
1620
+ background: #249966;
1621
+ }
1622
+
1623
+ .spawn-submit-btn:disabled {
1624
+ opacity: 0.5;
1625
+ cursor: not-allowed;
1626
+ }
1627
+
1628
+ /* Spawned Agent Indicator */
1629
+ .channel-item .spawned-icon {
1630
+ width: 14px;
1631
+ height: 14px;
1632
+ color: var(--accent-green);
1633
+ flex-shrink: 0;
1634
+ }
1635
+
1636
+ .channel-item .release-btn {
1637
+ opacity: 0;
1638
+ width: 18px;
1639
+ height: 18px;
1640
+ border: none;
1641
+ background: var(--accent-red);
1642
+ color: white;
1643
+ border-radius: 4px;
1644
+ cursor: pointer;
1645
+ display: flex;
1646
+ align-items: center;
1647
+ justify-content: center;
1648
+ transition: opacity var(--transition-fast);
1649
+ flex-shrink: 0;
1650
+ }
1651
+
1652
+ .channel-item:hover .release-btn {
1653
+ opacity: 1;
1654
+ }
1655
+
1656
+ .channel-item .release-btn:hover {
1657
+ background: #c41a4f;
1658
+ }
1659
+
1660
+ /* ========================================
1661
+ Fleet View Toggle
1662
+ ======================================== */
1663
+ .view-toggle {
1664
+ display: flex;
1665
+ background: rgba(255, 255, 255, 0.06);
1666
+ border-radius: 6px;
1667
+ padding: 2px;
1668
+ margin: 0 12px 8px;
1669
+ }
1670
+
1671
+ .view-toggle-btn {
1672
+ flex: 1;
1673
+ padding: 6px 12px;
1674
+ border: none;
1675
+ background: transparent;
1676
+ color: var(--text-muted);
1677
+ font-size: 12px;
1678
+ font-weight: 500;
1679
+ border-radius: 4px;
1680
+ cursor: pointer;
1681
+ transition: all var(--transition-fast);
1682
+ display: flex;
1683
+ align-items: center;
1684
+ justify-content: center;
1685
+ gap: 6px;
1686
+ }
1687
+
1688
+ .view-toggle-btn:hover {
1689
+ color: var(--text-secondary);
1690
+ }
1691
+
1692
+ .view-toggle-btn.active {
1693
+ background: var(--accent-primary);
1694
+ color: white;
1695
+ }
1696
+
1697
+ .view-toggle-btn svg {
1698
+ width: 14px;
1699
+ height: 14px;
1700
+ }
1701
+
1702
+ .view-toggle-btn .peer-count {
1703
+ font-size: 10px;
1704
+ background: rgba(255, 255, 255, 0.2);
1705
+ padding: 1px 5px;
1706
+ border-radius: 8px;
1707
+ margin-left: 4px;
1708
+ }
1709
+
1710
+ /* Server badge for fleet agents */
1711
+ .server-badge {
1712
+ display: inline-flex;
1713
+ align-items: center;
1714
+ gap: 3px;
1715
+ padding: 1px 6px;
1716
+ background: var(--accent-purple);
1717
+ color: white;
1718
+ font-size: 9px;
1719
+ font-weight: 600;
1720
+ border-radius: 3px;
1721
+ margin-right: 6px;
1722
+ text-transform: uppercase;
1723
+ letter-spacing: 0.3px;
1724
+ }
1725
+
1726
+ .server-badge.local {
1727
+ background: var(--accent-primary);
1728
+ }
1729
+
1730
+ .server-badge .server-dot {
1731
+ width: 5px;
1732
+ height: 5px;
1733
+ border-radius: 50%;
1734
+ background: var(--status-online);
1735
+ }
1736
+
1737
+ .server-badge .server-dot.offline {
1738
+ background: var(--status-offline);
1739
+ }
1740
+
1741
+ /* Peer status in header */
1742
+ .peer-status {
1743
+ display: flex;
1744
+ align-items: center;
1745
+ gap: 8px;
1746
+ padding: 4px 10px;
1747
+ background: rgba(124, 58, 237, 0.15);
1748
+ border-radius: 12px;
1749
+ font-size: 12px;
1750
+ color: var(--accent-purple);
1751
+ margin-left: 8px;
1752
+ }
1753
+
1754
+ .peer-status .peer-dot {
1755
+ width: 6px;
1756
+ height: 6px;
1757
+ border-radius: 50%;
1758
+ background: var(--accent-purple);
1759
+ }
1760
+
1761
+ .peer-status.connected .peer-dot {
1762
+ background: var(--status-online);
1763
+ }
1764
+
1765
+ .peer-status.disconnected {
1766
+ background: rgba(97, 96, 97, 0.15);
1767
+ color: var(--status-offline);
1768
+ }
1769
+
1770
+ .peer-status.disconnected .peer-dot {
1771
+ background: var(--status-offline);
1772
+ }
1773
+
1774
+ /* Fleet section in sidebar */
1775
+ .fleet-servers-list {
1776
+ list-style: none;
1777
+ padding: 0 8px;
1778
+ margin-bottom: 8px;
1779
+ }
1780
+
1781
+ .server-item {
1782
+ display: flex;
1783
+ align-items: center;
1784
+ gap: 8px;
1785
+ padding: 6px 8px;
1786
+ border-radius: 6px;
1787
+ font-size: 13px;
1788
+ color: var(--text-channel);
1789
+ cursor: pointer;
1790
+ transition: background var(--transition-fast);
1791
+ }
1792
+
1793
+ .server-item:hover {
1794
+ background: var(--bg-channel-hover);
1795
+ }
1796
+
1797
+ .server-item .server-icon {
1798
+ width: 20px;
1799
+ height: 20px;
1800
+ border-radius: 4px;
1801
+ background: var(--accent-purple);
1802
+ display: flex;
1803
+ align-items: center;
1804
+ justify-content: center;
1805
+ position: relative;
1806
+ }
1807
+
1808
+ .server-item .server-icon svg {
1809
+ width: 12px;
1810
+ height: 12px;
1811
+ color: white;
1812
+ }
1813
+
1814
+ .server-item .server-icon .status-dot {
1815
+ position: absolute;
1816
+ bottom: -2px;
1817
+ right: -2px;
1818
+ width: 8px;
1819
+ height: 8px;
1820
+ border-radius: 50%;
1821
+ background: var(--status-online);
1822
+ border: 2px solid var(--bg-sidebar);
1823
+ }
1824
+
1825
+ .server-item .server-icon .status-dot.offline {
1826
+ background: var(--status-offline);
1827
+ }
1828
+
1829
+ .server-item .server-name {
1830
+ flex: 1;
1831
+ }
1832
+
1833
+ .server-item .agent-count {
1834
+ font-size: 11px;
1835
+ color: var(--text-muted);
1836
+ font-family: var(--font-mono);
1837
+ }
1838
+
1839
+ /* Fleet agent avatar with server indicator */
1840
+ .channel-item .agent-avatar .server-indicator {
1841
+ position: absolute;
1842
+ top: -3px;
1843
+ right: -3px;
1844
+ width: 10px;
1845
+ height: 10px;
1846
+ border-radius: 2px;
1847
+ background: var(--accent-purple);
1848
+ border: 2px solid var(--bg-sidebar);
1849
+ display: flex;
1850
+ align-items: center;
1851
+ justify-content: center;
1852
+ }
1853
+
1854
+ .channel-item .agent-avatar .server-indicator.local {
1855
+ background: var(--accent-primary);
1856
+ }
354
1857
  </style>
355
1858
  </head>
356
1859
  <body>
357
- <div class="container">
358
- <div class="sidebar">
359
- <div class="header">
360
- <div class="logo">
361
- <div class="logo-icon">AR</div>
362
- <h1>Agent Relay</h1>
1860
+ <div class="app-container">
1861
+ <!-- Sidebar -->
1862
+ <aside class="sidebar">
1863
+ <div class="workspace-header">
1864
+ <div class="workspace-name">
1865
+ <span class="status-dot" id="connection-dot"></span>
1866
+ Agent Relay
1867
+ </div>
1868
+ <button class="compose-new-btn" title="New message">
1869
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1870
+ <path d="M12 20h9"/>
1871
+ <path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"/>
1872
+ </svg>
1873
+ </button>
1874
+ </div>
1875
+
1876
+ <div class="search-bar" id="search-trigger">
1877
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1878
+ <circle cx="11" cy="11" r="8"/>
1879
+ <path d="M21 21l-4.35-4.35"/>
1880
+ </svg>
1881
+ <span>Search</span>
1882
+ <kbd>Ctrl K</kbd>
1883
+ </div>
1884
+
1885
+ <div class="sidebar-content">
1886
+ <!-- View Toggle (Local/Fleet) -->
1887
+ <div class="view-toggle" id="view-toggle" style="display: none;">
1888
+ <button class="view-toggle-btn active" data-view="local">
1889
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1890
+ <rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
1891
+ <line x1="9" y1="9" x2="15" y2="15"/>
1892
+ <line x1="15" y1="9" x2="9" y2="15"/>
1893
+ </svg>
1894
+ Local
1895
+ </button>
1896
+ <button class="view-toggle-btn" data-view="fleet">
1897
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1898
+ <circle cx="12" cy="12" r="10"/>
1899
+ <circle cx="12" cy="12" r="4"/>
1900
+ <line x1="12" y1="2" x2="12" y2="6"/>
1901
+ <line x1="12" y1="18" x2="12" y2="22"/>
1902
+ <line x1="2" y1="12" x2="6" y2="12"/>
1903
+ <line x1="18" y1="12" x2="22" y2="12"/>
1904
+ </svg>
1905
+ Fleet
1906
+ <span class="peer-count" id="peer-count">0</span>
1907
+ </button>
1908
+ </div>
1909
+
1910
+ <!-- Peer Servers Section (shown in fleet mode) -->
1911
+ <div class="section" id="servers-section" style="display: none;">
1912
+ <div class="section-header">
1913
+ <div class="section-title">
1914
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1915
+ <path d="M6 9l6 6 6-6"/>
1916
+ </svg>
1917
+ Servers
363
1918
  </div>
364
- <div id="connection-status">
365
- <span class="dot"></span>
366
- <span>Connected</span>
1919
+ </div>
1920
+ <ul class="fleet-servers-list" id="servers-list">
1921
+ <!-- Servers injected here -->
1922
+ </ul>
1923
+ </div>
1924
+
1925
+ <!-- Channels Section -->
1926
+ <div class="section">
1927
+ <div class="section-header" id="channels-header">
1928
+ <div class="section-title">
1929
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1930
+ <path d="M6 9l6 6 6-6"/>
1931
+ </svg>
1932
+ Channels
367
1933
  </div>
1934
+ </div>
1935
+ <ul class="channel-list" id="channels-list">
1936
+ <li class="channel-item active" data-channel="general">
1937
+ <span class="channel-prefix">#</span>
1938
+ <span class="channel-name">general</span>
1939
+ </li>
1940
+ </ul>
368
1941
  </div>
369
- <div class="agents-section">
370
- <div class="agents-header">Agents</div>
371
- <div id="agents">
372
- <!-- Agents injected here -->
1942
+
1943
+ <!-- Agents Section -->
1944
+ <div class="section">
1945
+ <div class="section-header" id="agents-header">
1946
+ <div class="section-title">
1947
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1948
+ <path d="M6 9l6 6 6-6"/>
1949
+ </svg>
1950
+ Agents
373
1951
  </div>
1952
+ <button class="section-add-btn" id="spawn-btn" title="Spawn new agent" style="opacity: 1;">
1953
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1954
+ <line x1="12" y1="5" x2="12" y2="19"/>
1955
+ <line x1="5" y1="12" x2="19" y2="12"/>
1956
+ </svg>
1957
+ </button>
1958
+ </div>
1959
+ <ul class="channel-list" id="agents-list">
1960
+ <!-- Agents injected here -->
1961
+ </ul>
374
1962
  </div>
375
- </div>
376
1963
 
377
- <div class="main-content">
378
- <div class="activity-log">
379
- <div class="log-header">
380
- <h2>Activity</h2>
381
- <div class="live-indicator">
382
- <span class="live-dot"></span>
383
- <span>Live</span>
384
- </div>
1964
+ </div>
1965
+
1966
+ <div class="sidebar-footer">
1967
+ <a href="/metrics" class="nav-link">
1968
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1969
+ <path d="M3 3v18h18"/>
1970
+ <path d="M18 17V9"/>
1971
+ <path d="M13 17V5"/>
1972
+ <path d="M8 17v-3"/>
1973
+ </svg>
1974
+ Metrics
1975
+ </a>
1976
+ <a href="/bridge" class="nav-link">
1977
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1978
+ <rect x="3" y="3" width="7" height="7"/>
1979
+ <rect x="14" y="3" width="7" height="7"/>
1980
+ <rect x="14" y="14" width="7" height="7"/>
1981
+ <rect x="3" y="14" width="7" height="7"/>
1982
+ </svg>
1983
+ Bridge View
1984
+ </a>
1985
+ </div>
1986
+ </aside>
1987
+
1988
+ <!-- Main Panel -->
1989
+ <main class="main-panel">
1990
+ <header class="channel-header">
1991
+ <div class="channel-header-name">
1992
+ <span class="prefix">#</span>
1993
+ <span id="current-channel-name">general</span>
1994
+ </div>
1995
+ <div class="header-divider"></div>
1996
+ <div class="channel-topic" id="channel-topic">All agent communications</div>
1997
+ <div class="header-actions">
1998
+ <div class="online-count">
1999
+ <span class="dot"></span>
2000
+ <span id="online-count">0 online</span>
2001
+ </div>
2002
+ </div>
2003
+ </header>
2004
+
2005
+ <div class="messages-container">
2006
+ <div class="messages-list" id="messages-list">
2007
+ <!-- Messages injected here -->
2008
+ </div>
2009
+ <div class="typing-indicator" id="typing-indicator">
2010
+ <div class="typing-dots">
2011
+ <span></span>
2012
+ <span></span>
2013
+ <span></span>
2014
+ </div>
2015
+ <span id="typing-text">Someone is typing...</span>
2016
+ </div>
2017
+ </div>
2018
+
2019
+ <div class="composer">
2020
+ <div class="composer-container" style="position: relative;">
2021
+ <!-- @-Mention Autocomplete Dropdown -->
2022
+ <div class="mention-autocomplete" id="mention-autocomplete">
2023
+ <div class="mention-autocomplete-header">Agents</div>
2024
+ <div id="mention-autocomplete-list">
2025
+ <!-- Agent items will be injected here -->
2026
+ </div>
2027
+ <div class="mention-autocomplete-hint">
2028
+ <span><kbd>Tab</kbd> or <kbd>Enter</kbd> to select</span>
2029
+ <span><kbd>↑↓</kbd> to navigate</span>
2030
+ <span><kbd>Esc</kbd> to close</span>
385
2031
  </div>
386
- <div class="log-content" id="log">
387
- <!-- Messages injected here -->
2032
+ </div>
2033
+ <div class="composer-input-row">
2034
+ <div class="composer-toolbar">
2035
+ <button class="toolbar-btn" id="bold-btn" title="Bold (Ctrl+B)">
2036
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
2037
+ <path d="M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z"/>
2038
+ <path d="M6 12h9a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z"/>
2039
+ </svg>
2040
+ </button>
2041
+ <button class="toolbar-btn" id="emoji-btn" title="Add emoji">
2042
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
2043
+ <circle cx="12" cy="12" r="10"/>
2044
+ <path d="M8 14s1.5 2 4 2 4-2 4-2"/>
2045
+ <line x1="9" y1="9" x2="9.01" y2="9"/>
2046
+ <line x1="15" y1="9" x2="15.01" y2="9"/>
2047
+ </svg>
2048
+ </button>
388
2049
  </div>
2050
+ <textarea
2051
+ id="message-input"
2052
+ class="composer-input"
2053
+ placeholder="@AgentName message... (or @* to broadcast)"
2054
+ rows="1"
2055
+ ></textarea>
2056
+ <div class="composer-send">
2057
+ <button class="send-btn" id="send-btn" title="Send message">
2058
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
2059
+ <line x1="22" y1="2" x2="11" y2="13"/>
2060
+ <polygon points="22 2 15 22 11 13 2 9 22 2"/>
2061
+ </svg>
2062
+ </button>
2063
+ </div>
2064
+ </div>
389
2065
  </div>
390
- </div>
2066
+ <div class="composer-hint">
2067
+ <kbd>Ctrl</kbd> + <kbd>Enter</kbd> to send
2068
+ </div>
2069
+ </div>
2070
+ </main>
391
2071
  </div>
392
2072
 
393
- <script>
394
- const agentsContainer = document.getElementById('agents');
395
- const logContainer = document.getElementById('log');
396
- const statusDiv = document.getElementById('connection-status');
397
-
398
- // Track last data hash to prevent unnecessary re-renders
399
- let lastDataHash = '';
400
-
401
- function connect() {
402
- const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
403
- const ws = new WebSocket(`${protocol}//${window.location.host}/ws`);
404
-
405
- ws.onopen = () => {
406
- statusDiv.innerHTML = '<span class="dot"></span><span>Live</span>';
407
- statusDiv.classList.remove('disconnected');
408
- };
409
-
410
- ws.onclose = () => {
411
- statusDiv.innerHTML = '<span class="dot"></span><span>Offline</span>';
412
- statusDiv.classList.add('disconnected');
413
- setTimeout(connect, 3000);
414
- };
415
-
416
- ws.onmessage = (e) => {
417
- // Skip re-render if data hasn't changed
418
- const dataHash = e.data;
419
- if (dataHash === lastDataHash) return;
420
- lastDataHash = dataHash;
421
-
422
- const data = JSON.parse(e.data);
423
- render(data);
424
- };
425
- }
426
-
427
- function render(data) {
428
- // Render Agents
429
- if (data.agents && data.agents.length > 0) {
430
- const newAgentsHTML = data.agents.map((a, i) => `
431
- <div class="agent-card ${a.messageCount > 0 ? 'active' : ''}">
432
- <div class="agent-header">
433
- <div class="agent-name">${a.name}</div>
434
- ${a.messageCount > 0 ? `<span class="badge">${a.messageCount}</span>` : ''}
435
- </div>
436
- <div class="agent-meta">
437
- <span class="cli-badge">${a.cli}</span>
438
- </div>
439
- <div class="agent-status">
440
- <span class="status-dot"></span>
441
- ${a.status && a.status !== 'Idle' ? a.status : 'Ready'}
442
- </div>
443
- <div class="last-active">
444
- ${a.lastActive ? timeAgo(new Date(a.lastActive)) : 'No activity'}
445
- </div>
446
- </div>
447
- `).join('');
448
-
449
- if (agentsContainer.innerHTML !== newAgentsHTML) {
450
- agentsContainer.innerHTML = newAgentsHTML;
451
- }
452
- } else if (!agentsContainer.querySelector('.empty-state')) {
453
- agentsContainer.innerHTML = `
454
- <div class="empty-state">
455
- <div class="empty-state-text">Waiting for agents...</div>
456
- </div>
457
- `;
458
- }
459
-
460
- // Render Messages (Activity Log)
461
- if (data.messages && data.messages.length > 0) {
462
- const currentCount = logContainer.querySelectorAll('.message').length;
463
- const newCount = data.messages.length;
464
-
465
- if (currentCount !== newCount) {
466
- logContainer.innerHTML = data.messages.map(m => createMessageHTML(m)).join('');
467
- }
468
- } else if (!logContainer.querySelector('.empty-state')) {
469
- logContainer.innerHTML = `
470
- <div class="empty-state">
471
- <div class="empty-state-text">No messages yet</div>
472
- </div>
473
- `;
474
- }
475
- }
476
-
477
- function createMessageHTML(m) {
478
- const initials = m.from.substring(0, 2).toUpperCase();
479
- return `
480
- <div class="message">
481
- <div class="msg-avatar">${initials}</div>
482
- <div class="msg-body">
483
- <div class="msg-meta">
484
- <span class="msg-sender">${m.from}</span>
485
- <span class="msg-arrow">→</span>
486
- <span class="msg-target">${m.to}</span>
487
- <span class="msg-time">${new Date(m.timestamp).toLocaleTimeString()}</span>
488
- </div>
489
- <div class="msg-text">${escapeHtml(m.content)}</div>
490
- </div>
2073
+ <!-- Command Palette -->
2074
+ <div class="command-palette-overlay" id="command-palette-overlay">
2075
+ <div class="command-palette">
2076
+ <div class="palette-search">
2077
+ <input
2078
+ type="text"
2079
+ class="palette-search-input"
2080
+ id="palette-search"
2081
+ placeholder="Search messages, agents, or type a command..."
2082
+ autocomplete="off"
2083
+ >
2084
+ </div>
2085
+ <div class="palette-results" id="palette-results">
2086
+ <div class="palette-section">
2087
+ <div class="palette-section-title">Commands</div>
2088
+ <div class="palette-item" data-command="broadcast">
2089
+ <div class="palette-item-icon">
2090
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
2091
+ <polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
2092
+ <path d="M15.54 8.46a5 5 0 0 1 0 7.07"/>
2093
+ <path d="M19.07 4.93a10 10 0 0 1 0 14.14"/>
2094
+ </svg>
491
2095
  </div>
492
- `;
493
- }
2096
+ <div class="palette-item-content">
2097
+ <div class="palette-item-title">/broadcast</div>
2098
+ <div class="palette-item-subtitle">Send message to all agents</div>
2099
+ </div>
2100
+ </div>
2101
+ <div class="palette-item" data-command="status">
2102
+ <div class="palette-item-icon">
2103
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
2104
+ <circle cx="12" cy="12" r="10"/>
2105
+ <path d="M12 6v6l4 2"/>
2106
+ </svg>
2107
+ </div>
2108
+ <div class="palette-item-content">
2109
+ <div class="palette-item-title">/status</div>
2110
+ <div class="palette-item-subtitle">Set your status message</div>
2111
+ </div>
2112
+ </div>
2113
+ <div class="palette-item" data-command="clear">
2114
+ <div class="palette-item-icon">
2115
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
2116
+ <path d="M3 6h18"/>
2117
+ <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
2118
+ </svg>
2119
+ </div>
2120
+ <div class="palette-item-content">
2121
+ <div class="palette-item-title">/clear</div>
2122
+ <div class="palette-item-subtitle">Clear message view</div>
2123
+ </div>
2124
+ </div>
2125
+ </div>
2126
+ <div class="palette-section" id="palette-channels-section">
2127
+ <div class="palette-section-title">Channels</div>
2128
+ <div class="palette-item" data-jump-channel="general">
2129
+ <div class="palette-item-icon">
2130
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
2131
+ <path d="M4 9l4-4 4 4"/>
2132
+ <path d="M12 15l4 4 4-4"/>
2133
+ <line x1="8" y1="5" x2="8" y2="19"/>
2134
+ <line x1="16" y1="5" x2="16" y2="19"/>
2135
+ </svg>
2136
+ </div>
2137
+ <div class="palette-item-content">
2138
+ <div class="palette-item-title">#general</div>
2139
+ <div class="palette-item-subtitle">All agent communications</div>
2140
+ </div>
2141
+ </div>
2142
+ </div>
2143
+ <div class="palette-section" id="palette-agents-section">
2144
+ <div class="palette-section-title">Jump to Agent</div>
2145
+ <!-- Agents will be injected here -->
2146
+ </div>
2147
+ <div class="palette-section" id="palette-messages-section" style="display: none;">
2148
+ <div class="palette-section-title">Recent Messages</div>
2149
+ <!-- Filtered messages will be injected here -->
2150
+ </div>
2151
+ </div>
2152
+ </div>
2153
+ </div>
494
2154
 
495
- function escapeHtml(text) {
496
- if (!text) return '';
497
- const div = document.createElement('div');
498
- div.textContent = text;
499
- return div.innerHTML;
500
- }
2155
+ <!-- Thread Panel -->
2156
+ <div class="thread-panel-overlay" id="thread-panel-overlay">
2157
+ <div class="thread-panel">
2158
+ <div class="thread-panel-header">
2159
+ <div class="thread-panel-title">
2160
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
2161
+ <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
2162
+ </svg>
2163
+ <span>Thread</span>
2164
+ <span class="thread-id" id="thread-panel-id"></span>
2165
+ </div>
2166
+ <button class="thread-panel-close" id="thread-panel-close" title="Close thread">
2167
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
2168
+ <line x1="18" y1="6" x2="6" y2="18"/>
2169
+ <line x1="6" y1="6" x2="18" y2="18"/>
2170
+ </svg>
2171
+ </button>
2172
+ </div>
2173
+ <div class="thread-messages" id="thread-messages">
2174
+ <!-- Thread messages will be injected here -->
2175
+ </div>
2176
+ <div class="thread-composer">
2177
+ <textarea
2178
+ id="thread-message-input"
2179
+ class="thread-composer-input"
2180
+ placeholder="Reply in thread..."
2181
+ rows="1"
2182
+ ></textarea>
2183
+ <button class="thread-send-btn" id="thread-send-btn" title="Send reply">
2184
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
2185
+ <line x1="22" y1="2" x2="11" y2="13"/>
2186
+ <polygon points="22 2 15 22 11 13 2 9 22 2"/>
2187
+ </svg>
2188
+ </button>
2189
+ </div>
2190
+ </div>
2191
+ </div>
501
2192
 
502
- function timeAgo(date) {
503
- const seconds = Math.floor((new Date() - date) / 1000);
504
- if (seconds < 60) return 'just now';
505
- const minutes = Math.floor(seconds / 60);
506
- if (minutes < 60) return `${minutes}m ago`;
507
- const hours = Math.floor(minutes / 60);
508
- if (hours < 24) return `${hours}h ago`;
509
- return `${Math.floor(hours / 24)}d ago`;
510
- }
2193
+ <!-- Spawn Agent Modal -->
2194
+ <div class="spawn-modal-overlay" id="spawn-modal-overlay">
2195
+ <div class="spawn-modal">
2196
+ <div class="spawn-modal-header">
2197
+ <div class="spawn-modal-title">
2198
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
2199
+ <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/>
2200
+ <circle cx="12" cy="7" r="4"/>
2201
+ <line x1="12" y1="11" x2="12" y2="17"/>
2202
+ <line x1="9" y1="14" x2="15" y2="14"/>
2203
+ </svg>
2204
+ Spawn New Agent
2205
+ </div>
2206
+ <button class="spawn-modal-close" id="spawn-modal-close" title="Close">
2207
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
2208
+ <line x1="18" y1="6" x2="6" y2="18"/>
2209
+ <line x1="6" y1="6" x2="18" y2="18"/>
2210
+ </svg>
2211
+ </button>
2212
+ </div>
2213
+ <div class="spawn-modal-body">
2214
+ <div class="spawn-form-group">
2215
+ <label for="spawn-name-input">Agent Name</label>
2216
+ <input
2217
+ type="text"
2218
+ id="spawn-name-input"
2219
+ class="spawn-input"
2220
+ placeholder="e.g., Developer, Reviewer, Tester"
2221
+ autocomplete="off"
2222
+ />
2223
+ </div>
2224
+ <div class="spawn-form-group">
2225
+ <label for="spawn-cli-input">CLI Command</label>
2226
+ <input
2227
+ type="text"
2228
+ id="spawn-cli-input"
2229
+ class="spawn-input"
2230
+ value="claude"
2231
+ placeholder="e.g., claude, aider, cursor"
2232
+ />
2233
+ <span class="spawn-hint">The AI CLI tool to wrap</span>
2234
+ </div>
2235
+ <div class="spawn-form-group">
2236
+ <label for="spawn-task-input">Initial Task (optional)</label>
2237
+ <textarea
2238
+ id="spawn-task-input"
2239
+ class="spawn-textarea"
2240
+ placeholder="Enter an initial task to send to the agent..."
2241
+ rows="4"
2242
+ ></textarea>
2243
+ <span class="spawn-hint">This will be injected into the agent's terminal</span>
2244
+ </div>
2245
+ <div class="spawn-status" id="spawn-status"></div>
2246
+ </div>
2247
+ <div class="spawn-modal-footer">
2248
+ <button class="spawn-cancel-btn" id="spawn-cancel-btn">Cancel</button>
2249
+ <button class="spawn-submit-btn" id="spawn-submit-btn">
2250
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
2251
+ <polygon points="5 3 19 12 5 21 5 3"/>
2252
+ </svg>
2253
+ Spawn Agent
2254
+ </button>
2255
+ </div>
2256
+ </div>
2257
+ </div>
511
2258
 
512
- connect();
513
- </script>
2259
+ <!-- Dashboard Application (TypeScript bundled) -->
2260
+ <script type="module" src="/js/app.js"></script>
514
2261
  </body>
515
2262
  </html>