agentdev-webui 1.0.0

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 (39) hide show
  1. package/lib/agent-api.js +530 -0
  2. package/lib/auth.js +127 -0
  3. package/lib/config.js +53 -0
  4. package/lib/database.js +762 -0
  5. package/lib/device-flow.js +257 -0
  6. package/lib/email.js +420 -0
  7. package/lib/encryption.js +112 -0
  8. package/lib/github.js +339 -0
  9. package/lib/history.js +143 -0
  10. package/lib/pwa.js +107 -0
  11. package/lib/redis-logs.js +226 -0
  12. package/lib/routes.js +680 -0
  13. package/migrations/000_create_database.sql +33 -0
  14. package/migrations/001_create_agentdev_schema.sql +135 -0
  15. package/migrations/001_create_agentdev_schema.sql.old +100 -0
  16. package/migrations/001_create_agentdev_schema_fixed.sql +135 -0
  17. package/migrations/002_add_github_token.sql +17 -0
  18. package/migrations/003_add_agent_logs_table.sql +23 -0
  19. package/migrations/004_remove_oauth_columns.sql +11 -0
  20. package/migrations/005_add_projects.sql +44 -0
  21. package/migrations/006_project_github_token.sql +7 -0
  22. package/migrations/007_project_repositories.sql +12 -0
  23. package/migrations/008_add_notifications.sql +20 -0
  24. package/migrations/009_unified_oauth.sql +153 -0
  25. package/migrations/README.md +97 -0
  26. package/package.json +37 -0
  27. package/public/css/styles.css +1140 -0
  28. package/public/device.html +384 -0
  29. package/public/docs.html +862 -0
  30. package/public/docs.md +697 -0
  31. package/public/favicon.svg +5 -0
  32. package/public/index.html +271 -0
  33. package/public/js/app.js +2379 -0
  34. package/public/login.html +224 -0
  35. package/public/profile.html +394 -0
  36. package/public/register.html +392 -0
  37. package/public/reset-password.html +349 -0
  38. package/public/verify-email.html +177 -0
  39. package/server.js +1450 -0
@@ -0,0 +1,1140 @@
1
+ :root {
2
+ --sidebar-width: 280px;
3
+ --touch-target: 44px;
4
+ --header-height: 60px;
5
+ --controls-height: 56px;
6
+ }
7
+ * { box-sizing: border-box; margin: 0; padding: 0; }
8
+ body { font-family: monospace; background: #1a1a2e; color: #eee; min-height: 100vh; min-height: 100dvh; display: flex; flex-direction: column; overflow: hidden; }
9
+
10
+ /* Offline banner */
11
+ .offline-banner {
12
+ display: none;
13
+ background: #ef4444;
14
+ color: #fff;
15
+ padding: 8px 16px;
16
+ text-align: center;
17
+ font-size: 12px;
18
+ position: fixed;
19
+ top: 0;
20
+ left: 0;
21
+ right: 0;
22
+ z-index: 2000;
23
+ }
24
+ .offline-banner.visible { display: block; }
25
+ body.offline { padding-top: 32px; }
26
+
27
+ .header { background: #16213e; padding: 12px 16px; border-bottom: 2px solid #0f3460; display: flex; justify-content: space-between; align-items: center; min-height: var(--header-height); flex-shrink: 0; }
28
+ .header h1 { color: #e94560; font-size: 1.1rem; }
29
+ .status { display: flex; align-items: center; gap: 8px; }
30
+ .status-dot { width: 12px; height: 12px; border-radius: 50%; background: #4ade80; flex-shrink: 0; }
31
+ .status-dot.running { background: #fbbf24; animation: pulse 1s infinite; }
32
+ @keyframes pulse { 50% { opacity: 0.5; } }
33
+
34
+ /* Hamburger menu button */
35
+ .hamburger {
36
+ display: none;
37
+ background: none;
38
+ border: none;
39
+ padding: 8px;
40
+ cursor: pointer;
41
+ flex-direction: column;
42
+ gap: 5px;
43
+ min-width: var(--touch-target);
44
+ min-height: var(--touch-target);
45
+ justify-content: center;
46
+ align-items: center;
47
+ }
48
+ .hamburger span {
49
+ display: block;
50
+ width: 24px;
51
+ height: 3px;
52
+ background: #e94560;
53
+ border-radius: 2px;
54
+ transition: transform 0.2s, opacity 0.2s;
55
+ }
56
+ .hamburger.active span:nth-child(1) { transform: translateY(8px) rotate(45deg); }
57
+ .hamburger.active span:nth-child(2) { opacity: 0; }
58
+ .hamburger.active span:nth-child(3) { transform: translateY(-8px) rotate(-45deg); }
59
+
60
+ /* Logout button */
61
+ .logout-btn {
62
+ background: none;
63
+ border: none;
64
+ padding: 8px;
65
+ cursor: pointer;
66
+ min-width: var(--touch-target);
67
+ min-height: var(--touch-target);
68
+ display: flex;
69
+ align-items: center;
70
+ justify-content: center;
71
+ color: #888;
72
+ transition: color 0.2s;
73
+ margin-left: 4px;
74
+ }
75
+ .logout-btn:hover { color: #ef4444; }
76
+
77
+ /* Project selector */
78
+ .project-group, .actions-group {
79
+ display: flex;
80
+ align-items: center;
81
+ gap: 4px;
82
+ }
83
+
84
+ .project-selector {
85
+ -webkit-appearance: none;
86
+ -moz-appearance: none;
87
+ appearance: none;
88
+ background: #0f3460 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23888'/%3E%3C/svg%3E") no-repeat right 8px center;
89
+ color: #eee;
90
+ border: 1px solid #333;
91
+ border-radius: 6px;
92
+ padding: 6px 28px 6px 10px;
93
+ font-size: 12px;
94
+ font-family: monospace;
95
+ cursor: pointer;
96
+ max-width: 180px;
97
+ min-height: var(--touch-target);
98
+ display: flex;
99
+ align-items: center;
100
+ }
101
+ .project-selector option {
102
+ background: #16213e;
103
+ color: #eee;
104
+ padding: 8px;
105
+ }
106
+ .project-selector:hover { border-color: #555; }
107
+ .project-selector:focus { outline: none; border-color: #e94560; box-shadow: 0 0 0 2px rgba(233, 69, 96, 0.2); }
108
+
109
+ /* Manage projects button */
110
+ .manage-projects-btn {
111
+ background: none;
112
+ border: 1px solid #333;
113
+ color: #888;
114
+ cursor: pointer;
115
+ padding: 4px 10px;
116
+ border-radius: 6px;
117
+ font-size: 14px;
118
+ font-weight: bold;
119
+ min-width: var(--touch-target);
120
+ min-height: var(--touch-target);
121
+ display: flex;
122
+ align-items: center;
123
+ justify-content: center;
124
+ transition: all 0.2s;
125
+ }
126
+ .manage-projects-btn:hover { border-color: #e94560; color: #e94560; }
127
+
128
+ /* Repo multi-select listbox */
129
+ .repo-multiselect {
130
+ width: 100%;
131
+ background: #16213e;
132
+ border: 1px solid #333;
133
+ color: #fff;
134
+ border-radius: 4px;
135
+ font-family: monospace;
136
+ font-size: 12px;
137
+ padding: 0;
138
+ }
139
+ .repo-multiselect option {
140
+ padding: 6px 10px;
141
+ cursor: pointer;
142
+ border-bottom: 1px solid #0f3460;
143
+ }
144
+ .repo-multiselect option:checked {
145
+ background: #e94560 linear-gradient(0deg, #e94560 0%, #e94560 100%);
146
+ color: #fff;
147
+ }
148
+ .repo-multiselect option:hover {
149
+ background: #0f3460;
150
+ }
151
+ .btn-fetch-repos {
152
+ background: #0f3460;
153
+ border: 1px solid #333;
154
+ color: #4ade80;
155
+ padding: 6px 14px;
156
+ border-radius: 4px;
157
+ cursor: pointer;
158
+ font-family: monospace;
159
+ font-size: 12px;
160
+ white-space: nowrap;
161
+ transition: all 0.2s;
162
+ }
163
+ .btn-fetch-repos:hover { border-color: #4ade80; background: #16213e; }
164
+ .btn-fetch-repos:disabled { opacity: 0.5; cursor: not-allowed; }
165
+
166
+ /* Status options table */
167
+ .status-options-table {
168
+ width: 100%;
169
+ border-collapse: collapse;
170
+ font-size: 12px;
171
+ font-family: monospace;
172
+ }
173
+ .status-options-table th {
174
+ text-align: left;
175
+ padding: 6px 8px;
176
+ color: #888;
177
+ border-bottom: 1px solid #333;
178
+ font-weight: normal;
179
+ font-size: 11px;
180
+ text-transform: uppercase;
181
+ letter-spacing: 0.5px;
182
+ }
183
+ .status-options-table td {
184
+ padding: 5px 8px;
185
+ border-bottom: 1px solid #0f3460;
186
+ color: #ccc;
187
+ vertical-align: middle;
188
+ }
189
+ .status-options-table select {
190
+ background: #0f3460;
191
+ border: 1px solid #333;
192
+ color: #fff;
193
+ padding: 4px 6px;
194
+ border-radius: 3px;
195
+ font-family: monospace;
196
+ font-size: 11px;
197
+ cursor: pointer;
198
+ width: 100%;
199
+ }
200
+ .status-options-table select:focus {
201
+ outline: none;
202
+ border-color: #e94560;
203
+ }
204
+ .status-options-table .col-id {
205
+ color: #666;
206
+ font-size: 10px;
207
+ max-width: 120px;
208
+ overflow: hidden;
209
+ text-overflow: ellipsis;
210
+ white-space: nowrap;
211
+ }
212
+ .status-options-table .col-actions {
213
+ text-align: center;
214
+ width: 30px;
215
+ }
216
+ .status-options-table .btn-remove-col {
217
+ background: none;
218
+ border: none;
219
+ color: #666;
220
+ cursor: pointer;
221
+ font-size: 14px;
222
+ padding: 2px 6px;
223
+ border-radius: 3px;
224
+ }
225
+ .status-options-table .btn-remove-col:hover { color: #ef4444; background: rgba(239,68,68,0.1); }
226
+ .status-options-table .btn-move {
227
+ background: none;
228
+ border: none;
229
+ color: #666;
230
+ cursor: pointer;
231
+ font-size: 12px;
232
+ padding: 1px 4px;
233
+ }
234
+ .status-options-table .btn-move:hover { color: #4ade80; }
235
+ .btn-add-column {
236
+ background: none;
237
+ border: 1px dashed #333;
238
+ color: #666;
239
+ padding: 6px 12px;
240
+ border-radius: 4px;
241
+ cursor: pointer;
242
+ font-family: monospace;
243
+ font-size: 11px;
244
+ margin-top: 6px;
245
+ width: 100%;
246
+ transition: all 0.2s;
247
+ }
248
+ .btn-add-column:hover { border-color: #4ade80; color: #4ade80; }
249
+
250
+ /* Notification bell */
251
+ .notification-bell {
252
+ background: none;
253
+ border: none;
254
+ padding: 8px;
255
+ cursor: pointer;
256
+ min-width: var(--touch-target);
257
+ min-height: var(--touch-target);
258
+ display: flex;
259
+ align-items: center;
260
+ justify-content: center;
261
+ position: relative;
262
+ margin-left: 8px;
263
+ }
264
+ .notification-bell svg {
265
+ width: 24px;
266
+ height: 24px;
267
+ fill: #888;
268
+ transition: fill 0.2s, transform 0.2s;
269
+ }
270
+ .notification-bell:hover svg { fill: #e94560; }
271
+ .notification-bell.enabled svg { fill: #4ade80; }
272
+ .notification-bell.ringing svg {
273
+ animation: ring 0.5s ease;
274
+ }
275
+ @keyframes ring {
276
+ 0%, 100% { transform: rotate(0); }
277
+ 20% { transform: rotate(15deg); }
278
+ 40% { transform: rotate(-15deg); }
279
+ 60% { transform: rotate(10deg); }
280
+ 80% { transform: rotate(-10deg); }
281
+ }
282
+ .notification-bell .badge {
283
+ position: absolute;
284
+ top: 4px;
285
+ right: 4px;
286
+ background: #e94560;
287
+ color: #fff;
288
+ font-size: 10px;
289
+ min-width: 16px;
290
+ height: 16px;
291
+ border-radius: 8px;
292
+ display: none;
293
+ align-items: center;
294
+ justify-content: center;
295
+ font-weight: bold;
296
+ }
297
+ .notification-bell .badge.visible { display: flex; }
298
+
299
+ /* Notification panel */
300
+ .notification-panel {
301
+ display: none;
302
+ position: absolute;
303
+ top: 100%;
304
+ right: 0;
305
+ width: 320px;
306
+ max-height: 400px;
307
+ background: #1e1e2e;
308
+ border: 1px solid #333;
309
+ border-radius: 8px;
310
+ box-shadow: 0 8px 24px rgba(0,0,0,0.4);
311
+ z-index: 1000;
312
+ overflow: hidden;
313
+ }
314
+ .notification-panel.open { display: block; }
315
+ .notification-panel-header {
316
+ display: flex;
317
+ justify-content: space-between;
318
+ align-items: center;
319
+ padding: 10px 14px;
320
+ border-bottom: 1px solid #333;
321
+ font-size: 13px;
322
+ font-weight: 600;
323
+ color: #ccc;
324
+ }
325
+ .notification-panel-header button {
326
+ background: none;
327
+ border: none;
328
+ color: #4ade80;
329
+ cursor: pointer;
330
+ font-size: 12px;
331
+ padding: 2px 8px;
332
+ border-radius: 4px;
333
+ }
334
+ .notification-panel-header button:hover { background: rgba(74,222,128,0.1); }
335
+ .notification-panel-body {
336
+ overflow-y: auto;
337
+ max-height: 340px;
338
+ }
339
+ .notification-item {
340
+ padding: 10px 14px;
341
+ border-bottom: 1px solid #2a2a3a;
342
+ cursor: default;
343
+ font-size: 12px;
344
+ }
345
+ .notification-item:hover { background: #252535; }
346
+ .notification-item .notif-title {
347
+ color: #e0e0e0;
348
+ font-weight: 500;
349
+ margin-bottom: 3px;
350
+ }
351
+ .notification-item .notif-message {
352
+ color: #888;
353
+ font-size: 11px;
354
+ }
355
+ .notification-item .notif-time {
356
+ color: #666;
357
+ font-size: 10px;
358
+ margin-top: 3px;
359
+ }
360
+ .notification-item.success .notif-title { color: #4ade80; }
361
+ .notification-item.failure .notif-title { color: #e94560; }
362
+ .notification-empty {
363
+ padding: 30px 14px;
364
+ text-align: center;
365
+ color: #666;
366
+ font-size: 13px;
367
+ }
368
+
369
+ /* Create ticket modal */
370
+ .modal-overlay {
371
+ display: none;
372
+ position: fixed;
373
+ top: 0;
374
+ left: 0;
375
+ right: 0;
376
+ bottom: 0;
377
+ background: rgba(0,0,0,0.7);
378
+ z-index: 2000;
379
+ justify-content: center;
380
+ align-items: center;
381
+ padding: 20px;
382
+ }
383
+ .modal-overlay.visible { display: flex; }
384
+ .modal {
385
+ background: #16213e;
386
+ border-radius: 12px;
387
+ width: 100%;
388
+ max-width: 500px;
389
+ max-height: 90vh;
390
+ overflow-y: auto;
391
+ box-shadow: 0 8px 32px rgba(0,0,0,0.5);
392
+ }
393
+ .modal-header {
394
+ display: flex;
395
+ justify-content: space-between;
396
+ align-items: center;
397
+ padding: 16px 20px;
398
+ border-bottom: 1px solid #0f3460;
399
+ }
400
+ .modal-header h2 { color: #e94560; font-size: 1.1rem; margin: 0; }
401
+ .modal-close {
402
+ background: none;
403
+ border: none;
404
+ color: #888;
405
+ font-size: 24px;
406
+ cursor: pointer;
407
+ padding: 4px 8px;
408
+ line-height: 1;
409
+ }
410
+ .modal-close:hover { color: #e94560; }
411
+ .modal-body { padding: 20px; }
412
+ .form-group { margin-bottom: 16px; }
413
+ .form-group label {
414
+ display: block;
415
+ color: #ccc;
416
+ font-size: 12px;
417
+ margin-bottom: 6px;
418
+ }
419
+ .form-group select,
420
+ .form-group textarea {
421
+ width: 100%;
422
+ background: #0f3460;
423
+ border: 1px solid #1a3a5c;
424
+ border-radius: 6px;
425
+ color: #eee;
426
+ font-family: monospace;
427
+ font-size: 13px;
428
+ padding: 10px 12px;
429
+ }
430
+ .form-group select:focus,
431
+ .form-group textarea:focus {
432
+ outline: none;
433
+ border-color: #e94560;
434
+ }
435
+ .form-group textarea {
436
+ min-height: 150px;
437
+ resize: vertical;
438
+ }
439
+ .form-group .hint {
440
+ font-size: 10px;
441
+ color: #666;
442
+ margin-top: 4px;
443
+ }
444
+ .modal-footer {
445
+ padding: 16px 20px;
446
+ border-top: 1px solid #0f3460;
447
+ display: flex;
448
+ gap: 10px;
449
+ justify-content: flex-end;
450
+ }
451
+ .modal-footer button {
452
+ min-width: 100px;
453
+ }
454
+ .btn-primary { background: #e94560; }
455
+ .btn-primary:hover { background: #c13350; }
456
+ .btn-primary:disabled { background: #666; }
457
+ .form-status {
458
+ padding: 10px 12px;
459
+ border-radius: 6px;
460
+ font-size: 12px;
461
+ margin-bottom: 16px;
462
+ display: none;
463
+ }
464
+ .form-status.visible { display: block; }
465
+ .form-status.success { background: #22c55e33; color: #4ade80; }
466
+ .form-status.error { background: #ef444433; color: #ef4444; }
467
+ .form-status a { color: inherit; }
468
+
469
+ /* Voice input */
470
+ .textarea-wrapper {
471
+ position: relative;
472
+ }
473
+ .textarea-wrapper textarea {
474
+ padding-right: 50px;
475
+ }
476
+ .voice-btn {
477
+ position: absolute;
478
+ right: 8px;
479
+ top: 8px;
480
+ background: #0f3460;
481
+ border: none;
482
+ border-radius: 50%;
483
+ width: 36px;
484
+ height: 36px;
485
+ cursor: pointer;
486
+ display: flex;
487
+ align-items: center;
488
+ justify-content: center;
489
+ transition: background 0.2s, transform 0.2s;
490
+ }
491
+ .voice-btn:hover { background: #1a3a5c; }
492
+ .voice-btn svg { width: 20px; height: 20px; fill: #888; }
493
+ .voice-btn:hover svg { fill: #e94560; }
494
+ .voice-btn.recording {
495
+ background: #e94560;
496
+ animation: pulse-recording 1s infinite;
497
+ }
498
+ .voice-btn.recording svg { fill: #fff; }
499
+ @keyframes pulse-recording {
500
+ 0%, 100% { transform: scale(1); }
501
+ 50% { transform: scale(1.1); }
502
+ }
503
+ .voice-btn.unsupported { display: none; }
504
+
505
+ /* Add comment button in logs panel */
506
+ .logs-panel-footer {
507
+ position: absolute;
508
+ bottom: 0;
509
+ left: 0;
510
+ right: 0;
511
+ padding: 12px 16px;
512
+ background: linear-gradient(transparent, #1a1a2e 30%);
513
+ display: none;
514
+ justify-content: center;
515
+ pointer-events: none;
516
+ }
517
+ .logs-panel-footer.visible { display: flex; }
518
+ .logs-panel-footer button {
519
+ pointer-events: auto;
520
+ background: #0f3460;
521
+ border: 1px solid #1a3a5c;
522
+ display: flex;
523
+ align-items: center;
524
+ gap: 8px;
525
+ padding: 10px 20px;
526
+ font-size: 13px;
527
+ }
528
+ .logs-panel-footer button:hover { background: #e94560; border-color: #e94560; }
529
+ .logs-panel-footer button svg { width: 16px; height: 16px; fill: currentColor; }
530
+ .logs-panel-footer .stop-btn { background: #dc2626; border-color: #b91c1c; color: #fff; }
531
+ .logs-panel-footer .stop-btn:hover { background: #b91c1c; border-color: #991b1b; }
532
+
533
+ /* Todo count badge in sidebar tab */
534
+ .todo-count { font-size: 10px; background: #e94560; color: #fff; border-radius: 8px; padding: 1px 6px; margin-left: 4px; display: none; }
535
+ .todo-count.visible { display: inline; }
536
+
537
+ .controls { display: flex; gap: 8px; padding: 10px 16px; background: #16213e; border-bottom: 1px solid #0f3460; align-items: center; flex-wrap: wrap; min-height: var(--controls-height); flex-shrink: 0; }
538
+ .agent-count { color: #60a5fa; margin-left: auto; font-size: 12px; }
539
+ button { background: #0f3460; color: #fff; border: none; padding: 10px 16px; border-radius: 5px; cursor: pointer; min-height: var(--touch-target); font-size: 13px; }
540
+ button:hover { background: #e94560; }
541
+ button:active { background: #c13350; }
542
+ button:disabled { opacity: 0.5; cursor: not-allowed; }
543
+
544
+ .main { display: flex; flex: 1; overflow: hidden; position: relative; }
545
+ .logs-container { position: absolute; top: 0; bottom: 0; left: 0; right: var(--sidebar-width); display: flex; flex-direction: column; transition: right 0.3s ease; }
546
+ .tabs { display: flex; background: #0f3460; border-bottom: 1px solid #0f3460; overflow-x: auto; flex-shrink: 0; -webkit-overflow-scrolling: touch; }
547
+ .tab { padding: 12px 16px; cursor: pointer; border-right: 1px solid #1a1a2e; display: flex; align-items: center; gap: 8px; white-space: nowrap; min-height: var(--touch-target); }
548
+ .tab:hover { background: #1a3a5c; }
549
+ .tab.active { background: #1a1a2e; color: #e94560; border-bottom: 2px solid #e94560; }
550
+ .tab .close { font-size: 16px; opacity: 0.6; padding: 4px 8px; border-radius: 3px; min-width: 28px; min-height: 28px; display: flex; align-items: center; justify-content: center; }
551
+ .tab .close:hover, .tab .close:active { opacity: 1; background: #e94560; }
552
+ .tab-content { flex: 1; position: relative; overflow: hidden; }
553
+ .logs-panel { position: absolute; top: 0; bottom: 0; left: 0; right: 0; padding: 16px; overflow-y: auto; display: none; -webkit-overflow-scrolling: touch; }
554
+ .logs-panel.active { display: block; }
555
+
556
+ /* View toggle */
557
+ .view-toggle {
558
+ display: flex;
559
+ border: 1px solid #333;
560
+ border-radius: 4px;
561
+ overflow: hidden;
562
+ margin-right: 4px;
563
+ }
564
+ .view-toggle .view-btn {
565
+ padding: 6px 12px;
566
+ font-size: 12px;
567
+ background: transparent;
568
+ color: #888;
569
+ border: none;
570
+ border-radius: 0;
571
+ cursor: pointer;
572
+ min-height: auto;
573
+ }
574
+ .view-toggle .view-btn:hover { background: #1a3a5c; color: #eee; }
575
+ .view-toggle .view-btn.active { background: #0f3460; color: #e94560; }
576
+
577
+ /* Board view */
578
+ .board-container {
579
+ display: none;
580
+ position: absolute;
581
+ top: 0;
582
+ bottom: 0;
583
+ left: 0;
584
+ right: var(--sidebar-width);
585
+ overflow-x: auto;
586
+ overflow-y: hidden;
587
+ padding: 16px;
588
+ background: #1a1a2e;
589
+ transition: right 0.3s ease;
590
+ }
591
+ .board-container.active { display: flex; }
592
+ .board {
593
+ display: flex;
594
+ gap: 12px;
595
+ height: 100%;
596
+ width: 100%;
597
+ }
598
+ .board-column {
599
+ flex: 1;
600
+ min-width: 200px;
601
+ display: flex;
602
+ flex-direction: column;
603
+ background: #16213e;
604
+ border-radius: 8px;
605
+ border: 1px solid #0f3460;
606
+ overflow: hidden;
607
+ }
608
+ .board-column-header {
609
+ padding: 12px 14px;
610
+ font-size: 13px;
611
+ font-weight: bold;
612
+ display: flex;
613
+ justify-content: space-between;
614
+ align-items: center;
615
+ border-bottom: 2px solid var(--col-color, #888);
616
+ flex-shrink: 0;
617
+ }
618
+ .board-column-header .col-title { color: var(--col-color, #888); }
619
+ .board-column-header .col-count {
620
+ background: var(--col-color, #888);
621
+ color: #000;
622
+ font-size: 11px;
623
+ padding: 2px 8px;
624
+ border-radius: 10px;
625
+ font-weight: bold;
626
+ }
627
+ .board-column-body {
628
+ flex: 1;
629
+ overflow-y: auto;
630
+ padding: 8px;
631
+ -webkit-overflow-scrolling: touch;
632
+ }
633
+ .board-card {
634
+ background: #0f3460;
635
+ border-radius: 6px;
636
+ padding: 10px 12px;
637
+ margin-bottom: 8px;
638
+ cursor: pointer;
639
+ transition: background 0.15s;
640
+ border-left: 3px solid var(--col-color, #888);
641
+ }
642
+ .board-card:hover { background: #1a3a5c; }
643
+ .board-card-header {
644
+ display: flex;
645
+ justify-content: space-between;
646
+ align-items: center;
647
+ margin-bottom: 6px;
648
+ }
649
+ .board-card-number {
650
+ color: #4ade80;
651
+ font-weight: bold;
652
+ font-size: 13px;
653
+ text-decoration: none;
654
+ }
655
+ .board-card-number:hover { text-decoration: underline; }
656
+ .board-card-repo {
657
+ font-size: 10px;
658
+ color: #888;
659
+ background: #1a1a2e;
660
+ padding: 2px 6px;
661
+ border-radius: 3px;
662
+ }
663
+ .board-card-title {
664
+ font-size: 12px;
665
+ color: #ccc;
666
+ line-height: 1.4;
667
+ margin-bottom: 8px;
668
+ display: -webkit-box;
669
+ -webkit-line-clamp: 3;
670
+ -webkit-box-orient: vertical;
671
+ overflow: hidden;
672
+ }
673
+ .board-card-footer {
674
+ display: flex;
675
+ justify-content: space-between;
676
+ align-items: center;
677
+ font-size: 10px;
678
+ color: #666;
679
+ }
680
+ .board-card-author {
681
+ display: flex;
682
+ align-items: center;
683
+ gap: 4px;
684
+ }
685
+ .board-card-author img {
686
+ width: 16px;
687
+ height: 16px;
688
+ border-radius: 50%;
689
+ }
690
+ .board-card-state {
691
+ font-size: 9px;
692
+ padding: 1px 5px;
693
+ border-radius: 3px;
694
+ text-transform: uppercase;
695
+ }
696
+ .board-card-state.open { background: #166534; color: #4ade80; }
697
+ .board-card-state.closed { background: #7f1d1d; color: #ef4444; }
698
+ .board-card-claude {
699
+ font-size: 9px;
700
+ padding: 1px 4px;
701
+ border-radius: 3px;
702
+ background: #7c3aed;
703
+ color: #fff;
704
+ font-weight: bold;
705
+ }
706
+ .board-card[draggable] { cursor: grab; }
707
+ .board-card[draggable]:active { cursor: grabbing; }
708
+ .board-card.dragging { opacity: 0.4; }
709
+ .board-column-body.drag-over {
710
+ background: rgba(233, 69, 96, 0.08);
711
+ border: 2px dashed rgba(233, 69, 96, 0.4);
712
+ border-radius: 6px;
713
+ }
714
+ .board-empty {
715
+ color: #555;
716
+ font-size: 12px;
717
+ text-align: center;
718
+ padding: 20px 10px;
719
+ }
720
+
721
+ /* Ticket detail panel */
722
+ .ticket-panel-overlay {
723
+ display: none;
724
+ position: fixed;
725
+ top: 0; left: 0; right: 0; bottom: 0;
726
+ background: rgba(0,0,0,0.5);
727
+ z-index: 2000;
728
+ }
729
+ .ticket-panel-overlay.visible { display: block; }
730
+ .ticket-panel {
731
+ position: fixed;
732
+ top: 0;
733
+ right: -600px;
734
+ width: 580px;
735
+ max-width: 90vw;
736
+ height: 100vh;
737
+ background: #16213e;
738
+ border-left: 2px solid #0f3460;
739
+ z-index: 2001;
740
+ display: flex;
741
+ flex-direction: column;
742
+ transition: right 0.3s ease;
743
+ }
744
+ .ticket-panel.open { right: 0; }
745
+ .ticket-panel-header {
746
+ display: flex;
747
+ justify-content: space-between;
748
+ align-items: flex-start;
749
+ padding: 16px 20px;
750
+ border-bottom: 1px solid #0f3460;
751
+ flex-shrink: 0;
752
+ }
753
+ .ticket-panel-title {
754
+ font-size: 16px;
755
+ font-weight: bold;
756
+ color: #eee;
757
+ line-height: 1.4;
758
+ flex: 1;
759
+ margin-right: 12px;
760
+ }
761
+ .ticket-panel-title a { color: #4ade80; text-decoration: none; }
762
+ .ticket-panel-title a:hover { text-decoration: underline; }
763
+ .ticket-panel-close {
764
+ background: none;
765
+ border: none;
766
+ color: #888;
767
+ font-size: 24px;
768
+ cursor: pointer;
769
+ padding: 0 4px;
770
+ line-height: 1;
771
+ min-width: auto;
772
+ min-height: auto;
773
+ }
774
+ .ticket-panel-close:hover { color: #e94560; background: none; }
775
+ .ticket-panel-body {
776
+ flex: 1;
777
+ overflow-y: auto;
778
+ padding: 16px 20px;
779
+ -webkit-overflow-scrolling: touch;
780
+ }
781
+ .ticket-meta {
782
+ display: flex;
783
+ gap: 8px;
784
+ align-items: center;
785
+ flex-wrap: wrap;
786
+ margin-bottom: 16px;
787
+ font-size: 12px;
788
+ }
789
+ .ticket-meta-item {
790
+ display: flex;
791
+ align-items: center;
792
+ gap: 4px;
793
+ color: #888;
794
+ }
795
+ .ticket-meta-item img { width: 18px; height: 18px; border-radius: 50%; }
796
+ .ticket-meta-badge {
797
+ padding: 2px 8px;
798
+ border-radius: 12px;
799
+ font-size: 11px;
800
+ font-weight: bold;
801
+ }
802
+ .ticket-meta-badge.open { background: #166534; color: #4ade80; }
803
+ .ticket-meta-badge.closed { background: #7f1d1d; color: #ef4444; }
804
+ .ticket-meta-badge.status { background: #0f3460; }
805
+ .ticket-body {
806
+ background: #1a1a2e;
807
+ border: 1px solid #0f3460;
808
+ border-radius: 8px;
809
+ padding: 16px;
810
+ margin-bottom: 20px;
811
+ font-size: 13px;
812
+ line-height: 1.6;
813
+ color: #ccc;
814
+ white-space: pre-wrap;
815
+ word-break: break-word;
816
+ }
817
+ .ticket-body a { color: #60a5fa; }
818
+ .ticket-comments-header {
819
+ font-size: 13px;
820
+ font-weight: bold;
821
+ color: #888;
822
+ margin-bottom: 12px;
823
+ padding-bottom: 8px;
824
+ border-bottom: 1px solid #0f3460;
825
+ }
826
+ .ticket-comment {
827
+ background: #1a1a2e;
828
+ border: 1px solid #0f3460;
829
+ border-radius: 8px;
830
+ margin-bottom: 12px;
831
+ overflow: hidden;
832
+ }
833
+ .ticket-comment-header {
834
+ display: flex;
835
+ align-items: center;
836
+ gap: 8px;
837
+ padding: 10px 14px;
838
+ background: #0f3460;
839
+ font-size: 12px;
840
+ }
841
+ .ticket-comment-header img { width: 20px; height: 20px; border-radius: 50%; }
842
+ .ticket-comment-header .author { color: #eee; font-weight: bold; }
843
+ .ticket-comment-header .date { color: #666; margin-left: auto; }
844
+ .ticket-comment-body {
845
+ padding: 12px 14px;
846
+ font-size: 13px;
847
+ line-height: 1.6;
848
+ color: #ccc;
849
+ white-space: pre-wrap;
850
+ word-break: break-word;
851
+ }
852
+ .ticket-comment-body a { color: #60a5fa; }
853
+ .ticket-no-comments {
854
+ color: #555;
855
+ font-size: 13px;
856
+ text-align: center;
857
+ padding: 20px;
858
+ }
859
+
860
+ /* Edit/Comment buttons */
861
+ .ticket-edit-btn {
862
+ background: none;
863
+ border: 1px solid #333;
864
+ color: #888;
865
+ cursor: pointer;
866
+ padding: 2px 8px;
867
+ border-radius: 4px;
868
+ font-size: 12px;
869
+ transition: color 0.15s, border-color 0.15s;
870
+ }
871
+ .ticket-edit-btn:hover { color: #60a5fa; border-color: #60a5fa; }
872
+ .ticket-edit-save, .ticket-edit-cancel {
873
+ padding: 4px 12px;
874
+ border: none;
875
+ border-radius: 4px;
876
+ cursor: pointer;
877
+ font-size: 12px;
878
+ font-family: monospace;
879
+ }
880
+ .ticket-edit-save { background: #22c55e; color: #fff; }
881
+ .ticket-edit-save:hover { background: #16a34a; }
882
+ .ticket-edit-cancel { background: #333; color: #ccc; }
883
+ .ticket-edit-cancel:hover { background: #444; }
884
+ .ticket-add-comment {
885
+ margin-top: 16px;
886
+ padding-top: 16px;
887
+ border-top: 1px solid #1a3a5c;
888
+ }
889
+ .ticket-add-comment textarea {
890
+ width: 100%;
891
+ padding: 10px;
892
+ background: #0f3460;
893
+ border: 1px solid #333;
894
+ color: #eee;
895
+ border-radius: 4px;
896
+ font-size: 13px;
897
+ font-family: monospace;
898
+ resize: vertical;
899
+ }
900
+ .ticket-add-comment textarea:focus { border-color: #60a5fa; outline: none; }
901
+ .ticket-comment-submit {
902
+ padding: 6px 16px;
903
+ background: #22c55e;
904
+ color: #fff;
905
+ border: none;
906
+ border-radius: 4px;
907
+ cursor: pointer;
908
+ font-size: 12px;
909
+ font-family: monospace;
910
+ }
911
+ .ticket-comment-submit:hover { background: #16a34a; }
912
+ .ticket-comment-submit:disabled { opacity: 0.5; cursor: not-allowed; }
913
+
914
+ /* Open/Close ticket button */
915
+ .ticket-state-btn {
916
+ padding: 4px 12px;
917
+ border: 1px solid;
918
+ border-radius: 4px;
919
+ cursor: pointer;
920
+ font-size: 12px;
921
+ font-family: monospace;
922
+ transition: opacity 0.15s;
923
+ }
924
+ .ticket-state-btn:hover { opacity: 0.8; }
925
+ .ticket-state-btn:disabled { opacity: 0.5; cursor: not-allowed; }
926
+ .ticket-state-btn.close-issue { background: #7c3aed22; color: #a78bfa; border-color: #7c3aed; }
927
+ .ticket-state-btn.reopen-issue { background: #22c55e22; color: #4ade80; border-color: #22c55e; }
928
+
929
+ /* Sidebar overlay */
930
+ .sidebar-overlay {
931
+ display: none;
932
+ position: fixed;
933
+ top: 0;
934
+ left: 0;
935
+ right: 0;
936
+ bottom: 0;
937
+ background: rgba(0,0,0,0.5);
938
+ z-index: 999;
939
+ opacity: 0;
940
+ transition: opacity 0.3s ease;
941
+ }
942
+ .sidebar-overlay.visible { opacity: 1; }
943
+
944
+ .sidebar { position: absolute; top: 0; bottom: 0; right: 0; width: var(--sidebar-width); display: flex; flex-direction: column; background: #16213e; border-left: 1px solid #0f3460; z-index: 1000; transition: transform 0.3s ease; }
945
+ .sidebar-tabs { display: flex; border-bottom: 1px solid #0f3460; }
946
+ .sidebar-tab { flex: 1; padding: 14px 12px; text-align: center; cursor: pointer; font-size: 12px; color: #888; min-height: var(--touch-target); }
947
+ .sidebar-tab:hover { background: #1a3a5c; }
948
+ .sidebar-tab.active { color: #e94560; border-bottom: 2px solid #e94560; background: #1a1a2e; }
949
+ .sidebar-content { flex: 1; overflow: hidden; position: relative; }
950
+ .agent-list { position: absolute; top: 0; bottom: 0; left: 0; right: 0; padding: 10px; overflow-y: auto; display: none; -webkit-overflow-scrolling: touch; }
951
+ .agent-list.active { display: block; }
952
+ .agent-item { padding: 12px 14px; margin: 6px 0; border-radius: 6px; background: #0f3460; cursor: pointer; font-size: 13px; position: relative; min-height: var(--touch-target); }
953
+ .agent-item:hover { background: #1a3a5c; }
954
+ .agent-item:active { background: #1a4a6c; }
955
+ .agent-item.active { border-left: 3px solid #e94560; }
956
+ .agent-item .ticket { color: #4ade80; }
957
+ .agent-item .view-btn { font-size: 11px; color: #60a5fa; margin-left: auto; padding: 4px 8px; background: rgba(96,165,250,0.1); border-radius: 4px; }
958
+ .agent-item .elapsed { font-size: 11px; color: #fbbf24; margin-top: 6px; }
959
+ .log-entry { padding: 8px 12px; margin: 3px 0; border-radius: 4px; white-space: pre-wrap; font-size: 12px; word-break: break-word; line-height: 1.4; }
960
+ .log-entry.ts { background: #0f3460; color: #e94560; font-weight: bold; margin-top: 15px; }
961
+ .log-entry.ok { color: #4ade80; }
962
+ .log-entry.err { color: #ef4444; }
963
+ .log-entry.tool { color: #fbbf24; }
964
+ .log-entry.phase { color: #60a5fa; font-weight: bold; background: rgba(96,165,250,0.1); }
965
+
966
+ /* Tooltip styles - desktop only */
967
+ .tooltip {
968
+ position: fixed;
969
+ z-index: 2010;
970
+ background: #16213e;
971
+ border: 1px solid #0f3460;
972
+ border-radius: 8px;
973
+ padding: 12px;
974
+ width: 320px;
975
+ max-width: 350px;
976
+ box-shadow: 0 4px 20px rgba(0,0,0,0.5);
977
+ pointer-events: none;
978
+ opacity: 0;
979
+ visibility: hidden;
980
+ transition: opacity 0.15s, visibility 0.15s;
981
+ }
982
+ .tooltip.visible { opacity: 1; visibility: visible; }
983
+ .tooltip-header { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; padding-bottom: 10px; border-bottom: 1px solid #0f3460; }
984
+ .tooltip-avatar { width: 36px; height: 36px; border-radius: 50%; background: #0f3460; }
985
+ .tooltip-author { font-size: 12px; }
986
+ .tooltip-author-name { color: #60a5fa; font-weight: bold; }
987
+ .tooltip-author-time { color: #888; font-size: 10px; }
988
+ .tooltip-title { font-size: 13px; font-weight: bold; color: #e94560; margin-bottom: 8px; line-height: 1.4; }
989
+ .tooltip-desc { font-size: 11px; color: #ccc; line-height: 1.5; margin-bottom: 10px; max-height: 80px; overflow: hidden; }
990
+ .tooltip-meta { display: flex; gap: 10px; flex-wrap: wrap; }
991
+ .tooltip-status { font-size: 10px; padding: 3px 8px; border-radius: 12px; }
992
+ .tooltip-status.open { background: #22c55e33; color: #4ade80; }
993
+ .tooltip-status.closed { background: #ef444433; color: #ef4444; }
994
+ .tooltip-status.in-progress { background: #fbbf2433; color: #fbbf24; }
995
+ .tooltip-elapsed { font-size: 10px; color: #fbbf24; background: #fbbf2422; padding: 3px 8px; border-radius: 12px; }
996
+ .tooltip-repo { font-size: 10px; color: #888; background: #0f3460; padding: 3px 8px; border-radius: 12px; }
997
+
998
+ /* Bottom sheet for mobile */
999
+ .bottom-sheet {
1000
+ position: fixed;
1001
+ bottom: 0;
1002
+ left: 0;
1003
+ right: 0;
1004
+ background: #16213e;
1005
+ border-top-left-radius: 16px;
1006
+ border-top-right-radius: 16px;
1007
+ box-shadow: 0 -4px 20px rgba(0,0,0,0.5);
1008
+ z-index: 1100;
1009
+ transform: translateY(100%);
1010
+ transition: transform 0.3s ease;
1011
+ max-height: 70vh;
1012
+ overflow-y: auto;
1013
+ -webkit-overflow-scrolling: touch;
1014
+ }
1015
+ .bottom-sheet.visible { transform: translateY(0); }
1016
+ .bottom-sheet-handle {
1017
+ width: 40px;
1018
+ height: 4px;
1019
+ background: #0f3460;
1020
+ border-radius: 2px;
1021
+ margin: 12px auto;
1022
+ }
1023
+ .bottom-sheet-content { padding: 0 20px 24px 20px; }
1024
+ .bottom-sheet .tooltip-header { margin-bottom: 16px; padding-bottom: 16px; }
1025
+ .bottom-sheet .tooltip-title { font-size: 16px; margin-bottom: 12px; }
1026
+ .bottom-sheet .tooltip-desc { font-size: 13px; max-height: none; margin-bottom: 16px; }
1027
+ .bottom-sheet .tooltip-meta { gap: 12px; }
1028
+ .bottom-sheet .tooltip-status,
1029
+ .bottom-sheet .tooltip-elapsed,
1030
+ .bottom-sheet .tooltip-repo { font-size: 12px; padding: 6px 12px; }
1031
+ .bottom-sheet .view-logs-btn {
1032
+ display: block;
1033
+ width: 100%;
1034
+ background: #e94560;
1035
+ color: #fff;
1036
+ border: none;
1037
+ padding: 14px;
1038
+ border-radius: 8px;
1039
+ font-size: 14px;
1040
+ font-weight: bold;
1041
+ margin-top: 20px;
1042
+ cursor: pointer;
1043
+ }
1044
+ .bottom-sheet .view-logs-btn:active { background: #c13350; }
1045
+
1046
+ /* Mobile sidebar tabs bar - hidden on desktop */
1047
+ .mobile-sidebar-tabs {
1048
+ display: none;
1049
+ background: #16213e;
1050
+ border-bottom: 1px solid #0f3460;
1051
+ flex-shrink: 0;
1052
+ }
1053
+ .mobile-sidebar-tab {
1054
+ flex: 1;
1055
+ padding: 10px 8px;
1056
+ background: none;
1057
+ border: none;
1058
+ border-bottom: 2px solid transparent;
1059
+ color: #888;
1060
+ font-size: 12px;
1061
+ font-family: monospace;
1062
+ cursor: pointer;
1063
+ min-height: 40px;
1064
+ position: relative;
1065
+ }
1066
+ .mobile-sidebar-tab:hover { color: #ccc; }
1067
+ .mobile-sidebar-tab:active { background: #1a3a5c; }
1068
+ .mobile-sidebar-tab.active { color: #e94560; border-bottom-color: #e94560; }
1069
+
1070
+ /* Mobile styles */
1071
+ @media (max-width: 768px) {
1072
+ :root {
1073
+ --sidebar-width: 85vw;
1074
+ }
1075
+ .header { flex-wrap: wrap; gap: 4px; padding: 8px 12px; }
1076
+ .header h1 { font-size: 1rem; }
1077
+ .hamburger { display: flex; }
1078
+ .mobile-sidebar-tabs { display: flex; }
1079
+ .logs-container { right: 0; }
1080
+ .board-container { right: 0; }
1081
+ .sidebar { transform: translateX(100%); }
1082
+ .sidebar.open { transform: translateX(0); }
1083
+ .sidebar-overlay.visible { display: block; }
1084
+ .tooltip { display: none !important; }
1085
+ .controls { gap: 6px; padding: 8px 12px; }
1086
+ .controls button { padding: 8px 12px; font-size: 12px; flex: 1; min-width: 0; }
1087
+ .agent-count { width: 100%; text-align: center; margin: 4px 0 0 0; }
1088
+ .logs-panel { padding: 12px; }
1089
+ .log-entry { font-size: 11px; padding: 6px 10px; }
1090
+ .scroll-to-bottom { right: 24px; }
1091
+ /* Hide less-important action buttons on mobile to keep logout visible */
1092
+ .actions-group .logout-btn:not(#logoutBtn) { display: none; }
1093
+ .project-selector { max-width: 120px; font-size: 11px; }
1094
+ .manage-projects-btn { min-width: 36px; min-height: 36px; padding: 2px 6px; }
1095
+ }
1096
+
1097
+ /* Scroll to bottom button */
1098
+ .scroll-to-bottom {
1099
+ position: fixed;
1100
+ bottom: 24px;
1101
+ right: calc(var(--sidebar-width) + 24px);
1102
+ width: 44px;
1103
+ height: 44px;
1104
+ background: #e94560;
1105
+ border: none;
1106
+ border-radius: 50%;
1107
+ cursor: pointer;
1108
+ display: flex;
1109
+ align-items: center;
1110
+ justify-content: center;
1111
+ box-shadow: 0 4px 12px rgba(233, 69, 96, 0.4);
1112
+ z-index: 100;
1113
+ opacity: 0;
1114
+ visibility: hidden;
1115
+ transform: translateY(20px);
1116
+ transition: opacity 0.2s, visibility 0.2s, transform 0.2s, background 0.2s;
1117
+ }
1118
+ .scroll-to-bottom.visible {
1119
+ opacity: 1;
1120
+ visibility: visible;
1121
+ transform: translateY(0);
1122
+ }
1123
+ .scroll-to-bottom:hover {
1124
+ background: #c13350;
1125
+ transform: scale(1.1);
1126
+ }
1127
+ .scroll-to-bottom:active {
1128
+ transform: scale(0.95);
1129
+ }
1130
+ .scroll-to-bottom svg {
1131
+ width: 24px;
1132
+ height: 24px;
1133
+ fill: #fff;
1134
+ }
1135
+
1136
+ /* Desktop styles */
1137
+ @media (min-width: 769px) {
1138
+ .bottom-sheet { display: none !important; }
1139
+ .sidebar-overlay { display: none !important; }
1140
+ }