create-claude-kanban 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.
@@ -0,0 +1,2462 @@
1
+ <!DOCTYPE html>
2
+ <html lang="ko">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>{{PROJECT_NAME}} Agent Kanban</title>
7
+ <link href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css" rel="stylesheet">
8
+ <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
9
+ <style>
10
+ :root {
11
+ --bg: #09090B;
12
+ --s1: #111113;
13
+ --s2: #18181B;
14
+ --s3: #1F1F23;
15
+ --b1: #27272A;
16
+ --b2: #3F3F46;
17
+ --t1: #FAFAFA;
18
+ --t2: #A1A1AA;
19
+ --t3: #71717A;
20
+ --t4: #52525B;
21
+ --ac: #3B82F6;
22
+ --ac2: #2563EB;
23
+ --gn: #22C55E;
24
+ --rd: #EF4444;
25
+ --am: #F59E0B;
26
+ --vl: #8B5CF6;
27
+ --r: 3px;
28
+ }
29
+ *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
30
+ body {
31
+ font-family: 'Pretendard Variable', Pretendard, -apple-system, sans-serif;
32
+ background: var(--bg);
33
+ color: var(--t1);
34
+ font-size: 13px;
35
+ line-height: 1.5;
36
+ -webkit-font-smoothing: antialiased;
37
+ }
38
+ code, .mono { font-family: 'JetBrains Mono', monospace; }
39
+
40
+ /* ── Header ── */
41
+ .header {
42
+ height: 52px;
43
+ padding: 0 24px;
44
+ border-bottom: 1px solid var(--b1);
45
+ display: flex;
46
+ align-items: center;
47
+ justify-content: space-between;
48
+ background: var(--s1);
49
+ }
50
+ .header-left { display: flex; align-items: center; gap: 12px; }
51
+ .logo {
52
+ font-size: 14px;
53
+ font-weight: 700;
54
+ letter-spacing: -0.02em;
55
+ color: var(--t1);
56
+ }
57
+ .logo-dot { color: var(--ac); }
58
+ .header-sub {
59
+ font-size: 11px;
60
+ color: var(--t3);
61
+ font-family: 'JetBrains Mono', monospace;
62
+ padding-left: 12px;
63
+ border-left: 1px solid var(--b1);
64
+ }
65
+ .stats { display: flex; gap: 2px; }
66
+ .stat {
67
+ padding: 6px 14px;
68
+ text-align: center;
69
+ background: var(--s2);
70
+ border: 1px solid var(--b1);
71
+ }
72
+ .stat:first-child { border-radius: var(--r) 0 0 var(--r); }
73
+ .stat:last-child { border-radius: 0 var(--r) var(--r) 0; }
74
+ .stat:not(:first-child) { border-left: none; }
75
+ .stat .num {
76
+ font-size: 16px;
77
+ font-weight: 700;
78
+ font-family: 'JetBrains Mono', monospace;
79
+ line-height: 1.2;
80
+ }
81
+ .stat .lbl {
82
+ font-size: 9px;
83
+ color: var(--t4);
84
+ text-transform: uppercase;
85
+ letter-spacing: 0.08em;
86
+ font-weight: 500;
87
+ }
88
+
89
+ /* ── Toolbar ── */
90
+ .toolbar {
91
+ height: 40px;
92
+ padding: 0 24px;
93
+ display: flex;
94
+ align-items: center;
95
+ gap: 1px;
96
+ border-bottom: 1px solid var(--b1);
97
+ background: var(--s1);
98
+ }
99
+ .toolbar button {
100
+ padding: 6px 12px;
101
+ border: none;
102
+ background: transparent;
103
+ cursor: pointer;
104
+ font-size: 11px;
105
+ font-weight: 500;
106
+ color: var(--t3);
107
+ font-family: inherit;
108
+ transition: color 0.15s;
109
+ border-radius: var(--r);
110
+ }
111
+ .toolbar button:hover { color: var(--t1); }
112
+ .toolbar button.active {
113
+ background: var(--s3);
114
+ color: var(--t1);
115
+ }
116
+ .toolbar .right { margin-left: auto; display: flex; align-items: center; gap: 6px; }
117
+ .toolbar .add-btn {
118
+ background: var(--t1);
119
+ color: var(--bg);
120
+ font-weight: 600;
121
+ padding: 5px 14px;
122
+ border-radius: var(--r);
123
+ font-size: 11px;
124
+ }
125
+ .toolbar .add-btn:hover { opacity: 0.9; color: var(--bg); }
126
+ .toolbar .clear-btn { color: var(--t4); font-size: 11px; }
127
+ .toolbar button[data-filter]:not([data-filter="all"]) { position: relative; }
128
+ .toolbar button[data-filter]:not([data-filter="all"])::after {
129
+ content: "i";
130
+ position: absolute;
131
+ top: -2px;
132
+ right: -2px;
133
+ width: 12px;
134
+ height: 12px;
135
+ border-radius: 50%;
136
+ background: var(--s3);
137
+ color: var(--t4);
138
+ font-size: 7px;
139
+ font-style: italic;
140
+ font-weight: 700;
141
+ display: none;
142
+ align-items: center;
143
+ justify-content: center;
144
+ line-height: 1;
145
+ }
146
+ .toolbar button[data-filter]:not([data-filter="all"]):hover::after { display: flex; }
147
+
148
+ /* ── Agent Prompt Panel ── */
149
+ .agent-overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.5); z-index: 500; }
150
+ .agent-overlay.show { display: block; }
151
+ .agent-panel {
152
+ position: fixed; top: 0; right: -700px; width: 700px; max-width: 90vw;
153
+ height: 100vh; background: var(--s1); border-left: 1px solid var(--b1);
154
+ z-index: 501; transition: right 0.2s ease;
155
+ display: flex; flex-direction: column;
156
+ }
157
+ .agent-panel.show { right: 0; }
158
+ .agent-panel .ap-header {
159
+ padding: 14px 20px; border-bottom: 1px solid var(--b1);
160
+ display: flex; align-items: center; justify-content: space-between; flex-shrink: 0;
161
+ }
162
+ .agent-panel .ap-header h3 { font-size: 14px; font-weight: 600; }
163
+ .agent-panel .ap-close {
164
+ background: none; border: none; color: var(--t4); cursor: pointer;
165
+ font-size: 18px; width: 28px; height: 28px; display: flex;
166
+ align-items: center; justify-content: center; border-radius: var(--r);
167
+ }
168
+ .agent-panel .ap-close:hover { color: var(--t1); background: var(--s3); }
169
+ .agent-tabs {
170
+ display: flex; gap: 0; border-bottom: 1px solid var(--b1);
171
+ overflow-x: auto; flex-shrink: 0; padding: 0 12px;
172
+ }
173
+ .agent-tab {
174
+ padding: 8px 14px; font-size: 11px; font-weight: 500;
175
+ color: var(--t3); cursor: pointer; white-space: nowrap;
176
+ border-bottom: 2px solid transparent; background: none; border-top: none;
177
+ border-left: none; border-right: none; font-family: inherit;
178
+ transition: color 0.15s;
179
+ }
180
+ .agent-tab:hover { color: var(--t1); }
181
+ .agent-tab.active { color: var(--t1); border-bottom-color: var(--ac); }
182
+ .agent-tab .tab-model {
183
+ font-size: 9px; color: var(--t4); margin-left: 4px;
184
+ padding: 1px 4px; background: var(--s3); border-radius: 2px;
185
+ }
186
+ .agent-prompt-view {
187
+ flex: 1; overflow-y: auto; padding: 20px;
188
+ }
189
+ .agent-prompt-view pre {
190
+ font-family: 'JetBrains Mono', monospace; font-size: 12px; line-height: 1.6;
191
+ color: var(--t2); white-space: pre-wrap; word-break: break-word;
192
+ margin: 0; padding: 0;
193
+ }
194
+ .agent-prompt-view .prompt-section {
195
+ margin-bottom: 20px; padding: 16px; background: var(--s2);
196
+ border: 1px solid var(--b1); border-radius: var(--r);
197
+ }
198
+ .agent-prompt-view .prompt-section-label {
199
+ font-size: 10px; font-weight: 600; color: var(--t4);
200
+ text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 10px;
201
+ }
202
+ .agent-prompt-view .agent-meta {
203
+ display: flex; gap: 12px; margin-bottom: 16px; flex-wrap: wrap;
204
+ }
205
+ .agent-prompt-view .agent-meta-item {
206
+ font-size: 11px; padding: 4px 10px; background: var(--s3);
207
+ border-radius: var(--r); color: var(--t2);
208
+ }
209
+ .agent-prompt-view .agent-meta-item strong { color: var(--t1); margin-right: 4px; }
210
+
211
+ /* ── Board ── */
212
+ .board {
213
+ display: grid;
214
+ grid-template-columns: repeat(4, 1fr);
215
+ gap: 1px;
216
+ padding: 0;
217
+ min-height: calc(100vh - 132px);
218
+ background: var(--b1);
219
+ }
220
+ @media(max-width: 900px) { .board { grid-template-columns: repeat(2, 1fr); } }
221
+ @media(max-width: 500px) { .board { grid-template-columns: 1fr; } }
222
+ .column {
223
+ background: var(--bg);
224
+ padding: 0;
225
+ min-height: 200px;
226
+ transition: background 0.1s;
227
+ }
228
+ .column.drag-over { background: var(--s1); }
229
+ .col-header {
230
+ display: flex;
231
+ align-items: center;
232
+ justify-content: space-between;
233
+ padding: 12px 16px 10px;
234
+ position: sticky;
235
+ top: 0;
236
+ background: var(--bg);
237
+ z-index: 2;
238
+ }
239
+ .column.drag-over .col-header { background: var(--s1); }
240
+ .col-header .name {
241
+ font-size: 11px;
242
+ font-weight: 600;
243
+ text-transform: uppercase;
244
+ letter-spacing: 0.06em;
245
+ color: var(--t3);
246
+ display: flex;
247
+ align-items: center;
248
+ gap: 6px;
249
+ }
250
+ .col-header .dot {
251
+ width: 6px;
252
+ height: 6px;
253
+ border-radius: 50%;
254
+ }
255
+ .col-header .count {
256
+ font-size: 10px;
257
+ font-family: 'JetBrains Mono', monospace;
258
+ color: var(--t4);
259
+ background: var(--s2);
260
+ padding: 1px 6px;
261
+ border-radius: var(--r);
262
+ }
263
+ .cards { display: flex; flex-direction: column; gap: 1px; padding: 0 8px 8px; }
264
+
265
+ /* ── Card ── */
266
+ .card {
267
+ background: var(--s1);
268
+ border-radius: var(--r);
269
+ padding: 10px 12px;
270
+ border-left: 2px solid var(--b1);
271
+ transition: background 0.1s, border-color 0.1s;
272
+ cursor: grab;
273
+ position: relative;
274
+ }
275
+ .card:hover { background: var(--s2); border-left-color: var(--t4); }
276
+ .card.dragging { opacity: 0.3; }
277
+ .card.blocked { opacity: 0.4; cursor: default; }
278
+ .card.readonly { cursor: default; }
279
+ .card .top {
280
+ display: flex;
281
+ justify-content: space-between;
282
+ align-items: center;
283
+ margin-bottom: 4px;
284
+ }
285
+ .card .id {
286
+ font-size: 10px;
287
+ font-family: 'JetBrains Mono', monospace;
288
+ color: var(--t4);
289
+ }
290
+ .card .badge {
291
+ font-size: 9px;
292
+ font-weight: 600;
293
+ padding: 1px 6px;
294
+ border-radius: 2px;
295
+ text-transform: uppercase;
296
+ letter-spacing: 0.03em;
297
+ font-family: 'JetBrains Mono', monospace;
298
+ }
299
+ .card .title {
300
+ font-size: 12px;
301
+ font-weight: 500;
302
+ color: var(--t1);
303
+ margin-bottom: 6px;
304
+ line-height: 1.5;
305
+ }
306
+ /* Collapsed by default — only top row + title visible */
307
+ .card .card-body { display: none; }
308
+ .card.expanded .card-body { display: block; }
309
+ /* Always show active-form spinner even when collapsed */
310
+ .card .active-form-inline {
311
+ font-size: 9px;
312
+ color: var(--am);
313
+ display: none;
314
+ align-items: center;
315
+ gap: 4px;
316
+ margin-top: 2px;
317
+ }
318
+ .card:not(.expanded) .active-form-inline { display: inline-flex; }
319
+ .card .active-form-inline .spinner {
320
+ width: 6px; height: 6px;
321
+ border: 1.5px solid var(--am);
322
+ border-top-color: transparent;
323
+ border-radius: 50%;
324
+ animation: spin 0.8s linear infinite;
325
+ flex-shrink: 0;
326
+ }
327
+ .card .active-form {
328
+ font-size: 10px;
329
+ color: var(--am);
330
+ display: flex;
331
+ align-items: center;
332
+ gap: 6px;
333
+ margin-bottom: 4px;
334
+ }
335
+ .card .active-form .spinner {
336
+ width: 8px; height: 8px;
337
+ border: 1.5px solid var(--am);
338
+ border-top-color: transparent;
339
+ border-radius: 50%;
340
+ animation: spin 0.8s linear infinite;
341
+ flex-shrink: 0;
342
+ }
343
+ @keyframes spin { to { transform: rotate(360deg); } }
344
+ .card .meta { display: flex; justify-content: space-between; align-items: center; }
345
+ .card .deps {
346
+ font-size: 9px;
347
+ font-family: 'JetBrains Mono', monospace;
348
+ color: var(--t4);
349
+ margin-top: 4px;
350
+ }
351
+ .card .time {
352
+ font-size: 9px;
353
+ font-family: 'JetBrains Mono', monospace;
354
+ color: var(--t4);
355
+ }
356
+ .card .session {
357
+ font-size: 9px;
358
+ font-family: 'JetBrains Mono', monospace;
359
+ color: var(--t4);
360
+ opacity: 0.6;
361
+ margin-top: 2px;
362
+ }
363
+ .card .source-badge {
364
+ font-size: 8px;
365
+ font-weight: 600;
366
+ padding: 1px 4px;
367
+ border-radius: 2px;
368
+ margin-left: 4px;
369
+ text-transform: uppercase;
370
+ letter-spacing: 0.04em;
371
+ }
372
+ .card .source-badge.manual { background: var(--s3); color: var(--t3); }
373
+ .card .source-badge:not(.manual) { background: var(--s3); color: var(--t4); }
374
+
375
+ .card .desc {
376
+ font-size: 11px;
377
+ color: var(--t3);
378
+ margin-top: 6px;
379
+ padding-top: 6px;
380
+ border-top: 1px solid var(--b1);
381
+ line-height: 1.5;
382
+ overflow: hidden;
383
+ }
384
+ .card .card-actions {
385
+ position: absolute;
386
+ top: 8px;
387
+ right: 8px;
388
+ display: flex;
389
+ gap: 2px;
390
+ opacity: 0;
391
+ transition: opacity 0.1s;
392
+ }
393
+ .card:hover .card-actions { opacity: 1; }
394
+ .card .card-actions button {
395
+ background: var(--s3);
396
+ border: 1px solid var(--b1);
397
+ border-radius: 2px;
398
+ color: var(--t3);
399
+ cursor: pointer;
400
+ font-size: 10px;
401
+ width: 22px;
402
+ height: 22px;
403
+ display: flex;
404
+ align-items: center;
405
+ justify-content: center;
406
+ transition: all 0.1s;
407
+ }
408
+ .card .card-actions button:hover { color: var(--t1); background: var(--b1); }
409
+ .card .card-actions button.del:hover { color: var(--rd); background: #EF444415; }
410
+
411
+ .badge-high { background: #7F1D1D; color: #FCA5A5; }
412
+ .badge-medium { background: #78350F; color: #FCD34D; }
413
+ .badge-low { background: #1E3A5F; color: #93C5FD; }
414
+
415
+ /* Parent / Subtask */
416
+ .card.parent { border-left-width: 3px; }
417
+ .card .subtask-bar {
418
+ margin-top: 6px;
419
+ height: 2px;
420
+ background: var(--b1);
421
+ border-radius: 1px;
422
+ overflow: hidden;
423
+ }
424
+ .card .subtask-bar .fill {
425
+ height: 100%;
426
+ border-radius: 1px;
427
+ transition: width 0.3s;
428
+ background: var(--gn);
429
+ }
430
+ .card .subtask-info {
431
+ font-size: 9px;
432
+ font-family: 'JetBrains Mono', monospace;
433
+ color: var(--t4);
434
+ margin-top: 4px;
435
+ display: flex;
436
+ justify-content: space-between;
437
+ }
438
+ .card.subtask {
439
+ margin-left: 12px;
440
+ border-left-width: 2px;
441
+ opacity: 0.85;
442
+ }
443
+ .card.subtask .title { font-size: 11px; }
444
+
445
+ /* Agent chip */
446
+ .card .agent-chip {
447
+ display: inline-flex;
448
+ align-items: center;
449
+ gap: 4px;
450
+ font-size: 9px;
451
+ font-weight: 500;
452
+ padding: 2px 7px;
453
+ border-radius: 2px;
454
+ background: var(--s3);
455
+ color: var(--t3);
456
+ margin-top: 4px;
457
+ }
458
+ .card .agent-chip .agent-dot {
459
+ width: 4px; height: 4px;
460
+ border-radius: 50%;
461
+ }
462
+
463
+ /* ── Modal ── */
464
+ .modal-overlay {
465
+ display: none;
466
+ position: fixed;
467
+ inset: 0;
468
+ background: rgba(0,0,0,0.7);
469
+ z-index: 200;
470
+ align-items: center;
471
+ justify-content: center;
472
+ backdrop-filter: blur(2px);
473
+ }
474
+ .modal-overlay.show { display: flex; }
475
+ .modal {
476
+ background: var(--s1);
477
+ border: 1px solid var(--b1);
478
+ border-radius: 4px;
479
+ width: 600px;
480
+ max-width: 92vw;
481
+ max-height: 90vh;
482
+ overflow: auto;
483
+ }
484
+ .modal-header {
485
+ padding: 16px 20px;
486
+ border-bottom: 1px solid var(--b1);
487
+ display: flex;
488
+ align-items: center;
489
+ justify-content: space-between;
490
+ }
491
+ .modal-header h2 { font-size: 14px; font-weight: 600; }
492
+ .modal-header .close-btn {
493
+ background: none;
494
+ border: none;
495
+ color: var(--t4);
496
+ cursor: pointer;
497
+ font-size: 18px;
498
+ line-height: 1;
499
+ width: 28px;
500
+ height: 28px;
501
+ display: flex;
502
+ align-items: center;
503
+ justify-content: center;
504
+ border-radius: var(--r);
505
+ }
506
+ .modal-header .close-btn:hover { color: var(--t1); background: var(--s3); }
507
+ .modal-body { padding: 20px; display: flex; flex-direction: column; gap: 16px; }
508
+ .field { display: flex; flex-direction: column; gap: 5px; }
509
+ .field label {
510
+ font-size: 11px;
511
+ font-weight: 600;
512
+ color: var(--t3);
513
+ letter-spacing: 0.02em;
514
+ }
515
+ .field input, .field textarea, .field select {
516
+ background: var(--bg);
517
+ border: 1px solid var(--b1);
518
+ border-radius: var(--r);
519
+ padding: 9px 12px;
520
+ color: var(--t1);
521
+ font-size: 13px;
522
+ font-family: inherit;
523
+ transition: border-color 0.15s;
524
+ }
525
+ .field input:focus, .field textarea:focus, .field select:focus {
526
+ outline: none;
527
+ border-color: var(--b2);
528
+ }
529
+ .field textarea { min-height: 80px; resize: vertical; line-height: 1.6; }
530
+ .desc-view {
531
+ background: var(--bg);
532
+ border: 1px solid var(--b1);
533
+ border-radius: var(--r);
534
+ padding: 12px 14px;
535
+ color: var(--t2);
536
+ font-size: 12.5px;
537
+ line-height: 1.75;
538
+ white-space: pre-wrap;
539
+ word-break: break-word;
540
+ max-height: 260px;
541
+ overflow-y: auto;
542
+ cursor: text;
543
+ }
544
+ .desc-view:hover { border-color: var(--b2); }
545
+ .desc-view strong, .desc-view b { color: var(--t1); font-weight: 600; }
546
+ .desc-view code { background: var(--s3); padding: 1px 5px; border-radius: 2px; font-size: 11.5px; color: var(--am); }
547
+ .desc-view a { color: var(--ac); text-decoration: underline; }
548
+ .desc-view ul, .desc-view ol { padding-left: 18px; margin: 4px 0; }
549
+ .desc-view li { margin: 2px 0; }
550
+ .desc-toggle {
551
+ font-size: 10px;
552
+ color: var(--t4);
553
+ background: none;
554
+ border: none;
555
+ cursor: pointer;
556
+ padding: 2px 0;
557
+ margin-left: auto;
558
+ }
559
+ .desc-toggle:hover { color: var(--t2); }
560
+ .field-label-row { display: flex; align-items: center; justify-content: space-between; }
561
+ .field .hint { font-size: 10px; color: var(--t4); }
562
+ .field-row { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 12px; }
563
+ .modal-footer {
564
+ padding: 14px 20px;
565
+ border-top: 1px solid var(--b1);
566
+ display: flex;
567
+ justify-content: flex-end;
568
+ gap: 8px;
569
+ }
570
+ .btn {
571
+ padding: 8px 18px;
572
+ border: 1px solid var(--b1);
573
+ border-radius: var(--r);
574
+ cursor: pointer;
575
+ font-size: 12px;
576
+ font-weight: 600;
577
+ font-family: inherit;
578
+ transition: all 0.1s;
579
+ }
580
+ .btn-ghost { background: transparent; color: var(--t3); }
581
+ .btn-ghost:hover { color: var(--t1); background: var(--s3); }
582
+ .btn-primary { background: var(--t1); color: var(--bg); border-color: var(--t1); }
583
+ .btn-primary:hover { opacity: 0.9; }
584
+ .btn-danger { background: transparent; color: var(--rd); border-color: #52525B; }
585
+ .btn-danger:hover { background: #EF444410; border-color: var(--rd); }
586
+
587
+ /* ── Live Feed ── */
588
+ .live-feed {
589
+ position: fixed;
590
+ bottom: 0;
591
+ left: 0;
592
+ right: 0;
593
+ background: var(--s1);
594
+ border-top: 1px solid var(--b1);
595
+ padding: 0 24px;
596
+ height: 32px;
597
+ display: flex;
598
+ align-items: center;
599
+ gap: 8px;
600
+ font-size: 11px;
601
+ z-index: 10;
602
+ }
603
+ .live-dot {
604
+ width: 5px; height: 5px;
605
+ border-radius: 50%;
606
+ background: var(--gn);
607
+ animation: pulse 2s infinite;
608
+ }
609
+ @keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.3; } }
610
+ .live-feed .msg { color: var(--t4); flex: 1; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
611
+ .live-feed .time { color: var(--t4); font-family: 'JetBrains Mono', monospace; font-size: 10px; }
612
+ .live-feed .conn { font-size: 10px; font-family: 'JetBrains Mono', monospace; }
613
+
614
+ .empty { text-align: center; padding: 80px 20px; color: var(--t4); font-size: 13px; grid-column: 1 / -1; background: var(--bg); }
615
+ .empty kbd { background: var(--s2); padding: 2px 8px; border-radius: 2px; border: 1px solid var(--b1); font-family: 'JetBrains Mono', monospace; font-size: 11px; }
616
+
617
+ /* ── Report Panel ── */
618
+ .report-panel {
619
+ position: fixed;
620
+ top: 0;
621
+ right: -620px;
622
+ width: 620px;
623
+ max-width: 100vw;
624
+ height: 100vh;
625
+ background: var(--s1);
626
+ border-left: 1px solid var(--b1);
627
+ z-index: 300;
628
+ transition: right 0.2s ease;
629
+ display: flex;
630
+ flex-direction: column;
631
+ }
632
+ .report-panel.open { right: 0; }
633
+ .report-panel .rp-header {
634
+ padding: 14px 20px;
635
+ border-bottom: 1px solid var(--b1);
636
+ display: flex;
637
+ align-items: center;
638
+ justify-content: space-between;
639
+ flex-shrink: 0;
640
+ }
641
+ .report-panel .rp-header h3 {
642
+ font-size: 13px;
643
+ font-weight: 600;
644
+ display: flex;
645
+ align-items: center;
646
+ gap: 8px;
647
+ }
648
+ .report-panel .rp-header .rp-badge {
649
+ font-size: 9px;
650
+ font-family: 'JetBrains Mono', monospace;
651
+ padding: 2px 8px;
652
+ border-radius: 2px;
653
+ background: var(--gn);
654
+ color: #000;
655
+ font-weight: 700;
656
+ letter-spacing: 0.04em;
657
+ }
658
+ .report-panel .rp-header .rp-close {
659
+ background: none;
660
+ border: 1px solid var(--b1);
661
+ border-radius: var(--r);
662
+ color: var(--t3);
663
+ cursor: pointer;
664
+ font-size: 14px;
665
+ width: 28px; height: 28px;
666
+ display: flex; align-items: center; justify-content: center;
667
+ }
668
+ .report-panel .rp-header .rp-close:hover { color: var(--t1); background: var(--s3); }
669
+ .report-panel .rp-meta {
670
+ padding: 10px 20px;
671
+ border-bottom: 1px solid var(--b1);
672
+ font-size: 11px;
673
+ font-family: 'JetBrains Mono', monospace;
674
+ color: var(--t4);
675
+ display: flex;
676
+ gap: 16px;
677
+ flex-shrink: 0;
678
+ }
679
+ .report-panel .rp-content { flex: 1; overflow-y: auto; padding: 20px; }
680
+ .report-panel .rp-content .rp-summary {
681
+ background: var(--bg);
682
+ border: 1px solid var(--b1);
683
+ border-radius: var(--r);
684
+ padding: 14px 16px;
685
+ margin-bottom: 20px;
686
+ font-size: 12px;
687
+ line-height: 1.7;
688
+ }
689
+ .report-panel .rp-content .rp-summary strong { color: var(--t1); }
690
+ .report-panel .rp-content .rp-md { font-size: 13px; line-height: 1.7; color: var(--t2); }
691
+ .report-panel .rp-content .rp-md h1 { font-size: 18px; font-weight: 700; margin: 24px 0 10px; color: var(--t1); border-bottom: 1px solid var(--b1); padding-bottom: 8px; }
692
+ .report-panel .rp-content .rp-md h2 { font-size: 15px; font-weight: 600; margin: 20px 0 8px; color: var(--t1); }
693
+ .report-panel .rp-content .rp-md h3 { font-size: 13px; font-weight: 600; margin: 16px 0 6px; color: var(--t2); }
694
+ .report-panel .rp-content .rp-md h4 { font-size: 12px; font-weight: 600; margin: 14px 0 4px; color: var(--t2); }
695
+ .report-panel .rp-content .rp-md p { margin: 8px 0; }
696
+ .report-panel .rp-content .rp-md ul, .rp-md ol { margin: 6px 0; padding-left: 20px; }
697
+ .report-panel .rp-content .rp-md li { margin: 3px 0; }
698
+ .report-panel .rp-content .rp-md code { background: var(--bg); padding: 1px 6px; border-radius: 2px; font-size: 12px; border: 1px solid var(--b1); color: var(--t1); }
699
+ .report-panel .rp-content .rp-md pre { background: var(--bg); padding: 14px; border-radius: var(--r); border: 1px solid var(--b1); overflow-x: auto; margin: 10px 0; }
700
+ .report-panel .rp-content .rp-md pre code { background: none; border: none; padding: 0; }
701
+ .report-panel .rp-content .rp-md table { width: 100%; border-collapse: collapse; margin: 10px 0; font-size: 12px; }
702
+ .report-panel .rp-content .rp-md th { background: var(--s2); text-align: left; padding: 8px 12px; border: 1px solid var(--b1); font-weight: 600; color: var(--t1); }
703
+ .report-panel .rp-content .rp-md td { padding: 6px 12px; border: 1px solid var(--b1); }
704
+ .report-panel .rp-content .rp-md tr:hover td { background: var(--s2); }
705
+ .report-panel .rp-content .rp-md blockquote { border-left: 2px solid var(--b2); padding: 4px 14px; margin: 10px 0; color: var(--t3); }
706
+ .report-panel .rp-content .rp-md strong { color: var(--t1); }
707
+ .report-panel .rp-content .rp-md hr { border: none; border-top: 1px solid var(--b1); margin: 20px 0; }
708
+ .rp-loading { text-align: center; padding: 40px; color: var(--t4); }
709
+ .rp-none { text-align: center; padding: 40px; color: var(--t4); font-size: 12px; }
710
+ .report-overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.4); z-index: 250; }
711
+ .report-overlay.open { display: block; }
712
+
713
+ /* Report button on card */
714
+ .card .report-btn {
715
+ font-size: 9px;
716
+ font-weight: 600;
717
+ padding: 3px 8px;
718
+ border-radius: 2px;
719
+ background: var(--s3);
720
+ color: var(--t2);
721
+ border: 1px solid var(--b1);
722
+ cursor: pointer;
723
+ margin-top: 6px;
724
+ display: inline-block;
725
+ font-family: inherit;
726
+ transition: all 0.1s;
727
+ }
728
+ .card .report-btn:hover { background: var(--b1); color: var(--t1); }
729
+
730
+ /* ── Activity Panel ── */
731
+ .activity-panel {
732
+ position: fixed;
733
+ top: 0;
734
+ right: -420px;
735
+ width: 420px;
736
+ max-width: 100vw;
737
+ height: 100vh;
738
+ background: var(--s1);
739
+ border-left: 1px solid var(--b1);
740
+ z-index: 300;
741
+ transition: right 0.2s ease;
742
+ display: flex;
743
+ flex-direction: column;
744
+ }
745
+ .activity-panel.open { right: 0; }
746
+ .activity-panel .ap-header {
747
+ padding: 14px 20px;
748
+ border-bottom: 1px solid var(--b1);
749
+ display: flex;
750
+ align-items: center;
751
+ justify-content: space-between;
752
+ flex-shrink: 0;
753
+ }
754
+ .activity-panel .ap-header h3 {
755
+ font-size: 13px;
756
+ font-weight: 600;
757
+ display: flex;
758
+ align-items: center;
759
+ gap: 8px;
760
+ }
761
+ .activity-panel .ap-header .ap-close {
762
+ background: none;
763
+ border: 1px solid var(--b1);
764
+ border-radius: var(--r);
765
+ color: var(--t3);
766
+ cursor: pointer;
767
+ font-size: 14px;
768
+ width: 28px; height: 28px;
769
+ display: flex; align-items: center; justify-content: center;
770
+ }
771
+ .activity-panel .ap-header .ap-close:hover { color: var(--t1); background: var(--s3); }
772
+ .activity-panel .ap-content {
773
+ flex: 1;
774
+ overflow-y: auto;
775
+ padding: 0;
776
+ }
777
+ .activity-panel .ap-empty {
778
+ text-align: center;
779
+ padding: 40px 20px;
780
+ color: var(--t4);
781
+ font-size: 12px;
782
+ }
783
+ .activity-item {
784
+ padding: 10px 20px;
785
+ border-bottom: 1px solid var(--b1);
786
+ transition: background 0.15s;
787
+ cursor: default;
788
+ }
789
+ .activity-item:hover { background: var(--s2); }
790
+ .activity-item.new-item {
791
+ animation: actSlideIn 0.3s ease;
792
+ }
793
+ @keyframes actSlideIn {
794
+ from { opacity: 0; transform: translateY(-8px); }
795
+ to { opacity: 1; transform: translateY(0); }
796
+ }
797
+ .activity-item .act-header {
798
+ display: flex;
799
+ align-items: center;
800
+ gap: 6px;
801
+ margin-bottom: 3px;
802
+ }
803
+ .activity-item .act-icon {
804
+ font-size: 12px;
805
+ flex-shrink: 0;
806
+ width: 18px;
807
+ text-align: center;
808
+ }
809
+ .activity-item .act-type {
810
+ font-size: 10px;
811
+ font-weight: 600;
812
+ font-family: 'JetBrains Mono', monospace;
813
+ text-transform: uppercase;
814
+ letter-spacing: 0.04em;
815
+ }
816
+ .activity-item .act-id {
817
+ font-size: 10px;
818
+ font-family: 'JetBrains Mono', monospace;
819
+ color: var(--ac);
820
+ cursor: pointer;
821
+ text-decoration: underline;
822
+ text-underline-offset: 2px;
823
+ }
824
+ .activity-item .act-id:hover { color: var(--t1); }
825
+ .activity-item .act-subject {
826
+ font-size: 12px;
827
+ color: var(--t2);
828
+ margin-left: 24px;
829
+ line-height: 1.4;
830
+ overflow: hidden;
831
+ text-overflow: ellipsis;
832
+ white-space: nowrap;
833
+ }
834
+ .activity-item .act-detail {
835
+ font-size: 11px;
836
+ color: var(--t4);
837
+ margin-left: 24px;
838
+ margin-top: 2px;
839
+ overflow: hidden;
840
+ text-overflow: ellipsis;
841
+ white-space: nowrap;
842
+ }
843
+ .activity-item .act-time {
844
+ font-size: 9px;
845
+ font-family: 'JetBrains Mono', monospace;
846
+ color: var(--t4);
847
+ margin-left: 24px;
848
+ margin-top: 3px;
849
+ }
850
+ .activity-item .act-agent {
851
+ font-size: 9px;
852
+ color: var(--t4);
853
+ margin-left: auto;
854
+ }
855
+ .act-type.created { color: var(--ac); }
856
+ .act-type.started { color: var(--am); }
857
+ .act-type.completed { color: var(--gn); }
858
+ .act-type.updated { color: var(--t3); }
859
+ .act-type.deleted { color: var(--rd); }
860
+
861
+ /* Activity toolbar button */
862
+ .activity-btn {
863
+ position: relative;
864
+ }
865
+ .activity-badge {
866
+ position: absolute;
867
+ top: 1px;
868
+ right: 2px;
869
+ min-width: 14px;
870
+ height: 14px;
871
+ background: var(--rd);
872
+ color: #fff;
873
+ font-size: 9px;
874
+ font-weight: 700;
875
+ font-family: 'JetBrains Mono', monospace;
876
+ border-radius: 7px;
877
+ display: none;
878
+ align-items: center;
879
+ justify-content: center;
880
+ padding: 0 3px;
881
+ line-height: 1;
882
+ }
883
+ .activity-badge.show { display: flex; }
884
+ .activity-overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.4); z-index: 250; }
885
+ .activity-overlay.open { display: block; }
886
+
887
+ /* Scrollbar */
888
+ ::-webkit-scrollbar { width: 6px; }
889
+ ::-webkit-scrollbar-track { background: transparent; }
890
+ ::-webkit-scrollbar-thumb { background: var(--b1); border-radius: 3px; }
891
+ ::-webkit-scrollbar-thumb:hover { background: var(--b2); }
892
+
893
+ /* Selection */
894
+ ::selection { background: var(--ac); color: #fff; }
895
+
896
+ /* ── Review Actions ── */
897
+ .card .review-actions {
898
+ display: flex;
899
+ gap: 6px;
900
+ margin-top: 8px;
901
+ padding-top: 8px;
902
+ border-top: 1px solid var(--b1);
903
+ }
904
+ .review-btn {
905
+ font-size: 11px;
906
+ font-weight: 600;
907
+ padding: 5px 14px;
908
+ border-radius: var(--r);
909
+ cursor: pointer;
910
+ font-family: inherit;
911
+ border: 1px solid var(--b1);
912
+ transition: all 0.1s;
913
+ }
914
+ .review-btn.approve {
915
+ background: var(--gn);
916
+ color: #000;
917
+ border-color: var(--gn);
918
+ }
919
+ .review-btn.approve:hover { opacity: 0.85; }
920
+ .review-btn.reject {
921
+ background: transparent;
922
+ color: var(--rd);
923
+ border-color: var(--rd);
924
+ }
925
+ .review-btn.reject:hover { background: #EF444415; }
926
+
927
+ /* ── Chat Panel ── */
928
+ .chat-panel {
929
+ position: fixed;
930
+ bottom: 32px;
931
+ right: 0;
932
+ width: 420px;
933
+ height: 0;
934
+ background: var(--s1);
935
+ border-left: 1px solid var(--b1);
936
+ border-top: 1px solid var(--b1);
937
+ z-index: 100;
938
+ display: flex;
939
+ flex-direction: column;
940
+ transition: height 0.2s ease, width 0.2s ease;
941
+ overflow: hidden;
942
+ border-radius: 8px 0 0 0;
943
+ }
944
+ .chat-panel.open { height: 50vh; }
945
+ .chat-panel.open.expanded { height: calc(100vh - 32px); width: 100%; border-radius: 0; }
946
+ .chat-resize-handle {
947
+ position: absolute;
948
+ top: 0;
949
+ left: 0;
950
+ right: 0;
951
+ height: 4px;
952
+ cursor: ns-resize;
953
+ z-index: 2;
954
+ }
955
+ .chat-resize-handle:hover,
956
+ .chat-resize-handle.active { background: var(--ac); opacity: 0.5; }
957
+ .chat-resize-handle-left {
958
+ position: absolute;
959
+ top: 0;
960
+ left: 0;
961
+ width: 4px;
962
+ bottom: 0;
963
+ cursor: ew-resize;
964
+ z-index: 2;
965
+ }
966
+ .chat-resize-handle-left:hover,
967
+ .chat-resize-handle-left.active { background: var(--ac); opacity: 0.5; }
968
+ .chat-header {
969
+ padding: 6px 12px;
970
+ border-bottom: 1px solid var(--b1);
971
+ display: flex;
972
+ align-items: center;
973
+ justify-content: space-between;
974
+ flex-shrink: 0;
975
+ background: var(--s2);
976
+ cursor: default;
977
+ user-select: none;
978
+ }
979
+ .chat-header .ch-title {
980
+ font-size: 11px;
981
+ font-weight: 600;
982
+ display: flex;
983
+ align-items: center;
984
+ gap: 6px;
985
+ color: var(--t2);
986
+ font-family: 'JetBrains Mono', monospace;
987
+ text-transform: uppercase;
988
+ letter-spacing: 0.05em;
989
+ }
990
+ .chat-header .ch-controls {
991
+ display: flex;
992
+ align-items: center;
993
+ gap: 4px;
994
+ }
995
+ .chat-header select {
996
+ background: var(--bg);
997
+ border: 1px solid var(--b1);
998
+ border-radius: var(--r);
999
+ color: var(--t2);
1000
+ font-size: 11px;
1001
+ font-family: 'JetBrains Mono', monospace;
1002
+ padding: 2px 6px;
1003
+ height: 24px;
1004
+ }
1005
+ .chat-header button {
1006
+ background: none;
1007
+ border: 1px solid transparent;
1008
+ border-radius: var(--r);
1009
+ color: var(--t4);
1010
+ cursor: pointer;
1011
+ font-size: 13px;
1012
+ width: 24px;
1013
+ height: 24px;
1014
+ display: flex;
1015
+ align-items: center;
1016
+ justify-content: center;
1017
+ transition: all 0.15s;
1018
+ }
1019
+ .chat-header button:hover { color: var(--t1); background: var(--s3); border-color: var(--b1); }
1020
+ .chat-messages {
1021
+ flex: 1;
1022
+ overflow-y: auto;
1023
+ padding: 16px;
1024
+ display: flex;
1025
+ flex-direction: column;
1026
+ gap: 16px;
1027
+ scroll-behavior: smooth;
1028
+ }
1029
+ .chat-messages::-webkit-scrollbar { width: 4px; }
1030
+ .chat-messages::-webkit-scrollbar-thumb { background: var(--b1); border-radius: 2px; }
1031
+ .chat-messages::-webkit-scrollbar-thumb:hover { background: var(--t4); }
1032
+ .chat-msg { display: flex; flex-direction: column; gap: 3px; max-width: 88%; }
1033
+ .chat-msg.user { align-self: flex-end; align-items: flex-end; }
1034
+ .chat-msg.assistant { align-self: flex-start; align-items: flex-start; }
1035
+ .chat-msg .cm-role {
1036
+ font-size: 9px;
1037
+ font-weight: 600;
1038
+ font-family: 'JetBrains Mono', monospace;
1039
+ text-transform: uppercase;
1040
+ letter-spacing: 0.06em;
1041
+ opacity: 0.6;
1042
+ }
1043
+ .chat-msg.user .cm-role { color: var(--ac); }
1044
+ .chat-msg.assistant .cm-role { color: var(--gn); }
1045
+ .chat-msg .cm-content {
1046
+ font-size: 13px;
1047
+ line-height: 1.65;
1048
+ color: var(--t2);
1049
+ word-break: break-word;
1050
+ }
1051
+ .chat-msg.user .cm-content {
1052
+ background: var(--ac);
1053
+ color: #fff;
1054
+ padding: 8px 14px;
1055
+ border-radius: 14px 14px 4px 14px;
1056
+ }
1057
+ .chat-msg.user .cm-content code,
1058
+ .chat-msg.user .cm-content strong {
1059
+ background: transparent;
1060
+ color: inherit;
1061
+ padding: 0;
1062
+ font-size: inherit;
1063
+ font-family: inherit;
1064
+ }
1065
+ .chat-msg.assistant .cm-content {
1066
+ background: var(--s2);
1067
+ padding: 10px 14px;
1068
+ border-radius: 14px 14px 14px 4px;
1069
+ border: 1px solid var(--b1);
1070
+ }
1071
+ .chat-msg .cm-content p { margin: 4px 0; }
1072
+ .chat-msg .cm-content p:first-child { margin-top: 0; }
1073
+ .chat-msg .cm-content p:last-child { margin-bottom: 0; }
1074
+ .chat-msg .cm-content code {
1075
+ background: var(--s3);
1076
+ padding: 1px 5px;
1077
+ border-radius: 3px;
1078
+ font-size: 11.5px;
1079
+ color: var(--t1);
1080
+ }
1081
+ .chat-msg .cm-content pre {
1082
+ background: var(--bg);
1083
+ padding: 10px 12px;
1084
+ border-radius: var(--r);
1085
+ border: 1px solid var(--b1);
1086
+ overflow-x: auto;
1087
+ margin: 8px 0;
1088
+ font-size: 11.5px;
1089
+ }
1090
+ .chat-msg .cm-content pre code { background: none; padding: 0; border: none; }
1091
+ .chat-msg .cm-content strong { color: var(--t1); }
1092
+ .chat-msg .cm-content ul, .chat-msg .cm-content ol { padding-left: 18px; margin: 4px 0; }
1093
+ .chat-msg .cm-content li { margin: 2px 0; }
1094
+ .chat-msg.streaming .cm-content::after {
1095
+ content: "";
1096
+ display: inline-block;
1097
+ width: 6px;
1098
+ height: 14px;
1099
+ background: var(--gn);
1100
+ margin-left: 2px;
1101
+ animation: blink 0.8s infinite;
1102
+ vertical-align: text-bottom;
1103
+ }
1104
+ @keyframes blink { 0%,100% { opacity: 1; } 50% { opacity: 0; } }
1105
+
1106
+ /* ── Tool Use Blocks ── */
1107
+ .chat-tool-block {
1108
+ margin: 6px 0;
1109
+ border: 1px solid var(--b1);
1110
+ border-radius: var(--r);
1111
+ overflow: hidden;
1112
+ font-size: 11px;
1113
+ }
1114
+ .tool-header {
1115
+ display: flex;
1116
+ align-items: center;
1117
+ gap: 6px;
1118
+ padding: 5px 10px;
1119
+ background: var(--bg);
1120
+ cursor: pointer;
1121
+ user-select: none;
1122
+ }
1123
+ .tool-header:hover { background: var(--s2); }
1124
+ .tool-icon {
1125
+ color: var(--am);
1126
+ font-family: 'JetBrains Mono', monospace;
1127
+ font-weight: 700;
1128
+ font-size: 10px;
1129
+ width: 16px;
1130
+ text-align: center;
1131
+ }
1132
+ .tool-name {
1133
+ font-weight: 600;
1134
+ color: var(--t2);
1135
+ font-family: 'JetBrains Mono', monospace;
1136
+ font-size: 10px;
1137
+ text-transform: uppercase;
1138
+ letter-spacing: 0.04em;
1139
+ }
1140
+ .tool-preview {
1141
+ color: var(--t4);
1142
+ font-family: 'JetBrains Mono', monospace;
1143
+ flex: 1;
1144
+ overflow: hidden;
1145
+ white-space: nowrap;
1146
+ text-overflow: ellipsis;
1147
+ }
1148
+ .tool-chevron {
1149
+ color: var(--t4);
1150
+ font-size: 8px;
1151
+ transition: transform 0.15s;
1152
+ }
1153
+ .tool-header.open .tool-chevron { transform: rotate(180deg); }
1154
+ .tool-detail { display: none; border-top: 1px solid var(--b1); }
1155
+ .tool-header.open + .tool-detail { display: block; }
1156
+ .tool-detail pre {
1157
+ margin: 0;
1158
+ padding: 8px 10px;
1159
+ font-size: 10.5px;
1160
+ line-height: 1.5;
1161
+ overflow-x: auto;
1162
+ max-height: 200px;
1163
+ overflow-y: auto;
1164
+ color: var(--t3);
1165
+ }
1166
+ .tool-detail .tool-output { border-top: 1px solid var(--b1); background: var(--s2); }
1167
+ .tool-spinner {
1168
+ width: 8px; height: 8px;
1169
+ border: 1.5px solid var(--am);
1170
+ border-top-color: transparent;
1171
+ border-radius: 50%;
1172
+ animation: spin 0.8s linear infinite;
1173
+ flex-shrink: 0;
1174
+ }
1175
+
1176
+ /* ── Action Result Badges ── */
1177
+ .chat-action-result {
1178
+ display: flex;
1179
+ align-items: center;
1180
+ gap: 6px;
1181
+ padding: 4px 10px;
1182
+ margin: 4px 0;
1183
+ background: var(--s3);
1184
+ border-radius: var(--r);
1185
+ font-size: 11px;
1186
+ font-family: 'JetBrains Mono', monospace;
1187
+ }
1188
+ .chat-action-result .action-icon { font-weight: 700; }
1189
+ .chat-action-result .action-text { color: var(--t2); }
1190
+ .chat-action-result .action-link {
1191
+ color: var(--ac);
1192
+ cursor: pointer;
1193
+ text-decoration: underline;
1194
+ margin-left: auto;
1195
+ }
1196
+
1197
+ /* ── Session indicator ── */
1198
+ .chat-session-badge {
1199
+ font-size: 9px;
1200
+ font-family: 'JetBrains Mono', monospace;
1201
+ color: var(--gn);
1202
+ padding: 1px 6px;
1203
+ background: rgba(34,197,94,0.1);
1204
+ border-radius: 3px;
1205
+ border: 1px solid rgba(34,197,94,0.2);
1206
+ }
1207
+
1208
+ /* ── Exec Queue indicator ── */
1209
+ .exec-queue-ind {
1210
+ font-size: 10px;
1211
+ font-family: 'JetBrains Mono', monospace;
1212
+ color: var(--am);
1213
+ margin-left: 6px;
1214
+ }
1215
+
1216
+ .chat-input-row {
1217
+ padding: 10px 12px;
1218
+ border-top: 1px solid var(--b1);
1219
+ display: flex;
1220
+ gap: 8px;
1221
+ flex-shrink: 0;
1222
+ background: var(--s1);
1223
+ }
1224
+ .chat-input-row textarea {
1225
+ flex: 1;
1226
+ background: var(--bg);
1227
+ border: 1px solid var(--b1);
1228
+ border-radius: 8px;
1229
+ color: var(--t1);
1230
+ font-size: 13px;
1231
+ font-family: inherit;
1232
+ padding: 10px 14px;
1233
+ transition: border-color 0.15s;
1234
+ resize: none;
1235
+ min-height: 40px;
1236
+ max-height: 120px;
1237
+ line-height: 1.4;
1238
+ }
1239
+ .chat-input-row textarea:focus { outline: none; border-color: var(--ac); box-shadow: 0 0 0 2px rgba(26,111,239,0.15); }
1240
+ .chat-input-row .send-btn {
1241
+ background: var(--ac);
1242
+ color: #fff;
1243
+ border: none;
1244
+ border-radius: 8px;
1245
+ padding: 10px 18px;
1246
+ font-size: 12px;
1247
+ font-weight: 600;
1248
+ font-family: inherit;
1249
+ cursor: pointer;
1250
+ white-space: nowrap;
1251
+ transition: background 0.15s;
1252
+ }
1253
+ .chat-input-row .send-btn:hover { background: var(--ac2); }
1254
+ .chat-input-row .send-btn:disabled { opacity: 0.4; cursor: not-allowed; }
1255
+ .chat-empty {
1256
+ flex: 1;
1257
+ display: flex;
1258
+ align-items: center;
1259
+ justify-content: center;
1260
+ color: var(--t4);
1261
+ font-size: 12px;
1262
+ }
1263
+
1264
+ /* ── Exec Indicator on Card ── */
1265
+ .card .exec-indicator {
1266
+ font-size: 10px;
1267
+ color: var(--gn);
1268
+ display: flex;
1269
+ align-items: center;
1270
+ gap: 6px;
1271
+ margin-top: 4px;
1272
+ font-family: 'JetBrains Mono', monospace;
1273
+ }
1274
+ .card .exec-indicator .spinner {
1275
+ width: 8px; height: 8px;
1276
+ border: 1.5px solid var(--gn);
1277
+ border-top-color: transparent;
1278
+ border-radius: 50%;
1279
+ animation: spin 0.8s linear infinite;
1280
+ }
1281
+ </style>
1282
+ </head>
1283
+ <body>
1284
+
1285
+ <div class="header">
1286
+ <div class="header-left">
1287
+ <div class="logo">{{PROJECT_NAME}}<span class="logo-dot">.</span>Kanban</div>
1288
+ <div class="header-sub" id="taskSource">~/.claude/tasks/</div>
1289
+ </div>
1290
+ <div class="stats">
1291
+ <div class="stat"><div class="num" id="s-total" style="color:var(--t3)">0</div><div class="lbl">Total</div></div>
1292
+ <div class="stat"><div class="num" id="s-active" style="color:var(--am)">0</div><div class="lbl">Active</div></div>
1293
+ <div class="stat"><div class="num" id="s-done" style="color:var(--gn)">0</div><div class="lbl">Done</div></div>
1294
+ <div class="stat"><div class="num" id="s-blocked" style="color:var(--rd)">0</div><div class="lbl">Blocked</div></div>
1295
+ </div>
1296
+ </div>
1297
+
1298
+ <div class="toolbar" id="toolbar">
1299
+ <span id="filterBtns"><button class="active" data-filter="all">All</button></span>
1300
+ <div class="right">
1301
+ <button onclick="toggleAgentPanel()">Agents</button>
1302
+ <button onclick="toggleChat()">Chat</button>
1303
+ <button class="activity-btn" onclick="toggleActivity()">Activity <span class="activity-badge" id="actBadge">0</span></button>
1304
+ <button class="add-btn" onclick="openModal()">New Task</button>
1305
+ <button class="clear-btn" onclick="clearDone()">Clear Done</button>
1306
+ </div>
1307
+ </div>
1308
+
1309
+ <div class="board" id="board"></div>
1310
+
1311
+ <div class="modal-overlay" id="modalOverlay">
1312
+ <div class="modal">
1313
+ <div class="modal-header">
1314
+ <h2 id="modalTitle">New Task</h2>
1315
+ <button class="close-btn" onclick="closeModal()">&times;</button>
1316
+ </div>
1317
+ <div class="modal-body">
1318
+ <div class="field">
1319
+ <label>Subject</label>
1320
+ <input type="text" id="fSubject" placeholder="What needs to be done?">
1321
+ </div>
1322
+ <div class="field">
1323
+ <div class="field-label-row">
1324
+ <label>Description</label>
1325
+ <button class="desc-toggle" id="descToggleBtn" onclick="toggleDescMode()">편집</button>
1326
+ </div>
1327
+ <div id="descViewWrap" class="desc-view" onclick="toggleDescMode()"></div>
1328
+ <textarea id="fDesc" placeholder="작업 지침, 상세 내용, 완료 기준..." style="display:none;min-height:140px;"></textarea>
1329
+ </div>
1330
+ <div class="field-row">
1331
+ <div class="field">
1332
+ <label>Priority</label>
1333
+ <select id="fPriority">
1334
+ <option value="high">P0 &mdash; Urgent</option>
1335
+ <option value="medium" selected>P1 &mdash; Normal</option>
1336
+ <option value="low">P2 &mdash; Low</option>
1337
+ </select>
1338
+ </div>
1339
+ <div class="field">
1340
+ <label>Agent</label>
1341
+ <select id="fAgent">
1342
+ <option value="">Auto</option>
1343
+ <option value="frontend">Frontend</option>
1344
+ <option value="backend">Backend</option>
1345
+ <option value="qa">QA</option>
1346
+ <option value="devops">DevOps</option>
1347
+ <option value="orchestrator">Orchestrator</option>
1348
+ <option value="researcher">Researcher</option>
1349
+ <option value="content">Content Planner</option>
1350
+ <option value="itemauthor">Item Author</option>
1351
+ </select>
1352
+ </div>
1353
+ <div class="field">
1354
+ <label>Status</label>
1355
+ <select id="fStatus">
1356
+ <option value="pending">To Do</option>
1357
+ <option value="in_progress">In Progress</option>
1358
+ <option value="in_review">In Review</option>
1359
+ <option value="completed">Done</option>
1360
+ </select>
1361
+ </div>
1362
+ </div>
1363
+ <div class="field-row" style="grid-template-columns:1fr 1fr;">
1364
+ <div class="field">
1365
+ <label>Parent Task ID</label>
1366
+ <input type="text" id="fParentId" placeholder="e.g. 1">
1367
+ <span class="hint">Makes this a subtask</span>
1368
+ </div>
1369
+ <div class="field">
1370
+ <label>Blocked By</label>
1371
+ <input type="text" id="fBlockedBy" placeholder="e.g. 1, 3">
1372
+ <span class="hint">Comma-separated IDs</span>
1373
+ </div>
1374
+ </div>
1375
+ </div>
1376
+ <div class="modal-footer">
1377
+ <button class="btn btn-danger" id="modalDeleteBtn" onclick="deleteFromModal()" style="display:none;margin-right:auto;">Delete</button>
1378
+ <button class="btn btn-ghost" onclick="closeModal()">Cancel</button>
1379
+ <button class="btn btn-primary" id="modalSubmitBtn" onclick="submitModal()">Create</button>
1380
+ </div>
1381
+ </div>
1382
+ </div>
1383
+
1384
+ <div class="report-overlay" id="reportOverlay" onclick="closeReport()"></div>
1385
+ <div class="report-panel" id="reportPanel">
1386
+ <div class="rp-header">
1387
+ <h3><span id="rpTitle">Report</span> <span class="rp-badge">DONE</span></h3>
1388
+ <button class="rp-close" onclick="closeReport()">&times;</button>
1389
+ </div>
1390
+ <div class="rp-meta">
1391
+ <span id="rpMeta"></span>
1392
+ </div>
1393
+ <div class="rp-content" id="rpContent">
1394
+ <div class="rp-none">No report available</div>
1395
+ </div>
1396
+ </div>
1397
+
1398
+ <div class="activity-overlay" id="actOverlay" onclick="closeActivity()"></div>
1399
+ <div class="activity-panel" id="actPanel">
1400
+ <div class="ap-header">
1401
+ <h3>Activity Timeline</h3>
1402
+ <button class="ap-close" onclick="closeActivity()">&times;</button>
1403
+ </div>
1404
+ <div class="ap-content" id="actContent">
1405
+ <div class="ap-empty">No activity yet</div>
1406
+ </div>
1407
+ </div>
1408
+
1409
+ <div class="agent-overlay" id="agentOverlay" onclick="closeAgentPanel()"></div>
1410
+ <div class="agent-panel" id="agentPanel">
1411
+ <div class="ap-header">
1412
+ <h3>Agent Prompts</h3>
1413
+ <button class="ap-close" onclick="closeAgentPanel()">&times;</button>
1414
+ </div>
1415
+ <div class="agent-tabs" id="agentTabs"></div>
1416
+ <div class="agent-prompt-view" id="agentPromptView">
1417
+ <div style="color:var(--t4);text-align:center;padding:40px">로딩 중...</div>
1418
+ </div>
1419
+ </div>
1420
+
1421
+ <div class="chat-panel" id="chatPanel">
1422
+ <div class="chat-resize-handle" id="chatResizeH"></div>
1423
+ <div class="chat-resize-handle-left" id="chatResizeW"></div>
1424
+ <div class="chat-header">
1425
+ <div class="ch-title">Chat <span class="chat-session-badge" id="chatSessionBadge" style="display:none">세션 유지</span></div>
1426
+ <div class="ch-controls">
1427
+ <select id="chatModel" onchange="onChatModelChange(this)">
1428
+ <option value="sonnet">Sonnet</option>
1429
+ <option value="opus">Opus</option>
1430
+ <option value="haiku">Haiku</option>
1431
+ </select>
1432
+ <button onclick="toggleChatExpand()" id="chatExpandBtn" title="확장">&#x26F6;</button>
1433
+ <button onclick="clearChat()" title="새 대화">&#8634;</button>
1434
+ <button onclick="closeChat()" title="닫기">&times;</button>
1435
+ </div>
1436
+ </div>
1437
+ <div class="chat-messages" id="chatMessages">
1438
+ <div class="chat-empty">태스크 생성, 보드 관리, 코드 질문 — 무엇이든 보내세요</div>
1439
+ </div>
1440
+ <div class="chat-input-row">
1441
+ <textarea id="chatInput" placeholder="태스크 생성, 보드 관리, 코드 질문..." rows="1" onkeydown="if(event.key==='Enter'&&!event.shiftKey){event.preventDefault();sendChat();}" oninput="autoResizeChatInput(this)"></textarea>
1442
+ <button class="send-btn" id="chatSendBtn" onclick="sendChat()">전송</button>
1443
+ </div>
1444
+ </div>
1445
+
1446
+ <div class="live-feed">
1447
+ <div class="live-dot" id="connDot"></div>
1448
+ <span class="msg" id="liveMsg">Waiting for agent activity...</span>
1449
+ <span class="exec-queue-ind" id="execQueueInd" style="display:none"></span>
1450
+ <span class="time" id="liveTime">--:--:--</span>
1451
+ <span class="conn" id="cliStatus" style="color:var(--t4)">CLI: --</span>
1452
+ <span style="color:var(--b1);margin:0 2px;">|</span>
1453
+ <span class="conn" id="connStatus" style="color:var(--gn)">SSE</span>
1454
+ </div>
1455
+
1456
+ <script>
1457
+ var AGENTS={orchestrator:{label:"Orchestrator",color:"var(--am)"},frontend:{label:"Frontend",color:"var(--ac)"},backend:{label:"Backend",color:"var(--gn)"},qa:{label:"QA",color:"var(--rd)"},devops:{label:"DevOps",color:"var(--t3)"},researcher:{label:"Researcher",color:"var(--vl)"},strategist:{label:"Strategist",color:"var(--t3)"},designer:{label:"Designer",color:"#EC4899"},content:{label:"Content",color:"#06B6D4"},itemauthor:{label:"Item Author",color:"#F97316"},claude:{label:"Claude",color:"#D4A574"},manual:{label:"Manual",color:"var(--t3)"},unknown:{label:"Unassigned",color:"var(--t4)"}};
1458
+ var COLUMNS=[{id:"pending",name:"To Do",dot:"var(--t4)"},{id:"in_progress",name:"In Progress",dot:"var(--am)"},{id:"in_review",name:"In Review",dot:"var(--vl)"},{id:"completed",name:"Done",dot:"var(--gn)"}];
1459
+ var allTasks=[], filter="all", prevDoneCount=0, editingId=null, dragId=null;
1460
+
1461
+ if("Notification" in window && Notification.permission==="default") Notification.requestPermission();
1462
+ function notify(t,b){if("Notification" in window && Notification.permission==="granted") new Notification(t,{body:b});}
1463
+
1464
+ function apiCreate(d){return fetch("/api/tasks",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(d)}).then(function(r){return r.json();});}
1465
+ function apiUpdate(id,d){return fetch("/api/tasks/"+id,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(d)}).then(function(r){return r.json();});}
1466
+ function apiDelete(id){return fetch("/api/tasks/"+id,{method:"DELETE"});}
1467
+
1468
+ // ── Agent Prompt Panel ──
1469
+ var agentData=null, activeAgentTab=null;
1470
+
1471
+ function toggleAgentPanel(){
1472
+ var panel=document.getElementById("agentPanel");
1473
+ var overlay=document.getElementById("agentOverlay");
1474
+ if(panel.classList.contains("show")){closeAgentPanel();return;}
1475
+ overlay.classList.add("show");
1476
+ panel.classList.add("show");
1477
+ if(!agentData) loadAgentData();
1478
+ }
1479
+ function closeAgentPanel(){
1480
+ document.getElementById("agentPanel").classList.remove("show");
1481
+ document.getElementById("agentOverlay").classList.remove("show");
1482
+ }
1483
+ function loadAgentData(){
1484
+ fetch("/api/agents").then(function(r){return r.json();}).then(function(data){
1485
+ agentData=data;
1486
+ renderAgentTabs("_project");
1487
+ });
1488
+ }
1489
+ function renderAgentTabs(selected){
1490
+ if(!agentData)return;
1491
+ var container=document.getElementById("agentTabs");
1492
+ var MODEL_LABELS={opus:"Opus",sonnet:"Sonnet",haiku:"Haiku"};
1493
+ var h='<button class="agent-tab'+(selected==="_project"?" active":"")+'" onclick="showAgentPrompt(\'_project\')">Project Context</button>';
1494
+ var order=["orchestrator","frontend","backend","designer","qa","devops","researcher","strategist","content-planner","item-author","doc-writer"];
1495
+ order.forEach(function(name){
1496
+ var agent=agentData.agents.find(function(a){return a.name===name;});
1497
+ if(!agent)return;
1498
+ var ml=MODEL_LABELS[agent.model]||agent.model;
1499
+ h+='<button class="agent-tab'+(selected===name?" active":"")+'" onclick="showAgentPrompt(\''+name+'\')">';
1500
+ h+=esc(agent.name)+'<span class="tab-model">'+ml+'</span></button>';
1501
+ });
1502
+ // Any remaining agents not in order
1503
+ agentData.agents.forEach(function(agent){
1504
+ if(order.indexOf(agent.name)>=0)return;
1505
+ var ml=MODEL_LABELS[agent.model]||agent.model;
1506
+ h+='<button class="agent-tab'+(selected===agent.name?" active":"")+'" onclick="showAgentPrompt(\''+agent.name+'\')">';
1507
+ h+=esc(agent.name)+'<span class="tab-model">'+ml+'</span></button>';
1508
+ });
1509
+ container.innerHTML=h;
1510
+ activeAgentTab=selected;
1511
+ showAgentPrompt(selected);
1512
+ }
1513
+ function showAgentPrompt(name){
1514
+ if(!agentData)return;
1515
+ activeAgentTab=name;
1516
+ // Update tab active state
1517
+ document.querySelectorAll(".agent-tab").forEach(function(t){t.classList.remove("active");});
1518
+ document.querySelectorAll(".agent-tab").forEach(function(t){
1519
+ if((name==="_project"&&t.textContent.indexOf("Project Context")>=0)||t.textContent.indexOf(name)===0) t.classList.add("active");
1520
+ });
1521
+ var view=document.getElementById("agentPromptView");
1522
+ if(name==="_project"){
1523
+ var ctx=agentData.projectContext||"(프로젝트 컨텍스트 없음)";
1524
+ view.innerHTML='<div class="agent-meta"><div class="agent-meta-item"><strong>파일:</strong>.claude/agents/_project-context.md</div><div class="agent-meta-item"><strong>용도:</strong>모든 에이전트에 주입되는 공유 컨텍스트</div></div><div class="prompt-section"><pre>'+esc(ctx)+'</pre></div>';
1525
+ return;
1526
+ }
1527
+ var agent=agentData.agents.find(function(a){return a.name===name;});
1528
+ if(!agent){view.innerHTML='<div style="color:var(--t4);padding:40px;text-align:center">에이전트를 찾을 수 없습니다</div>';return;}
1529
+ var MODEL_COLORS={opus:"var(--am)",sonnet:"var(--ac)",haiku:"var(--gn)"};
1530
+ var mc=MODEL_COLORS[agent.model]||"var(--t3)";
1531
+ var h='<div class="agent-meta">';
1532
+ h+='<div class="agent-meta-item"><strong>Agent:</strong>'+esc(agent.name)+'</div>';
1533
+ h+='<div class="agent-meta-item" style="border-left:2px solid '+mc+'"><strong>Model:</strong>'+esc(agent.model)+'</div>';
1534
+ h+='<div class="agent-meta-item"><strong>파일:</strong>.claude/agents/'+esc(agent.name)+'.md</div>';
1535
+ h+='</div>';
1536
+ h+='<div class="prompt-section"><div class="prompt-section-label">Agent Role Prompt</div><pre>'+esc(agent.prompt)+'</pre></div>';
1537
+ view.innerHTML=h;
1538
+ }
1539
+
1540
+ function simpleMarkdown(text){
1541
+ if(!text)return'<span style="color:var(--t4)">내용 없음 — 클릭하여 작성</span>';
1542
+ var s=text.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
1543
+ s=s.replace(/\*\*(.+?)\*\*/g,'<strong>$1</strong>');
1544
+ s=s.replace(/`([^`]+)`/g,'<code>$1</code>');
1545
+ s=s.replace(/(https?:\/\/[^\s<]+)/g,'<a href="$1" target="_blank" onclick="event.stopPropagation()">$1</a>');
1546
+ s=s.replace(/^(\d+)\.\s+(.*)$/gm,'<li>$2</li>');
1547
+ s=s.replace(/^[-•]\s+(.*)$/gm,'<li>$1</li>');
1548
+ s=s.replace(/(<li>.*<\/li>\n?)+/g,function(m){return'<ul>'+m+'</ul>';});
1549
+ return s;
1550
+ }
1551
+ var descEditMode=false;
1552
+ function setDescMode(edit){
1553
+ descEditMode=edit;
1554
+ var view=document.getElementById("descViewWrap");
1555
+ var ta=document.getElementById("fDesc");
1556
+ var btn=document.getElementById("descToggleBtn");
1557
+ if(edit){
1558
+ ta.value=ta.dataset.raw||ta.value;
1559
+ view.style.display="none";
1560
+ ta.style.display="block";
1561
+ btn.textContent="미리보기";
1562
+ ta.focus();
1563
+ }else{
1564
+ ta.dataset.raw=ta.value;
1565
+ view.innerHTML=simpleMarkdown(ta.value);
1566
+ view.style.display="block";
1567
+ ta.style.display="none";
1568
+ btn.textContent="편집";
1569
+ }
1570
+ }
1571
+ function toggleDescMode(){setDescMode(!descEditMode);}
1572
+
1573
+ function openModal(task){
1574
+ editingId=task?task.id:null;
1575
+ document.getElementById("modalTitle").textContent=task?"#"+task.id+" "+((task.subject||"").substring(0,40)):"New Task";
1576
+ document.getElementById("modalSubmitBtn").textContent=task?"Save":"Create";
1577
+ document.getElementById("modalDeleteBtn").style.display=task&&task._editable?"block":"none";
1578
+ document.getElementById("fSubject").value=task?(task.subject||""):"";
1579
+ document.getElementById("fDesc").value=task?(task.description||""):"";
1580
+ document.getElementById("fDesc").dataset.raw=task?(task.description||""):"";
1581
+ document.getElementById("fPriority").value=task?(task.priority||"medium"):"medium";
1582
+ document.getElementById("fAgent").value=task?(task.agent||""):"";
1583
+ document.getElementById("fStatus").value=task?getCol(task):"pending";
1584
+ document.getElementById("fBlockedBy").value=task&&task.blockedBy?task.blockedBy.join(", "):"";
1585
+ document.getElementById("fParentId").value=task&&task.parentId?task.parentId:"";
1586
+ // 기존 태스크: 뷰 모드(읽기), 새 태스크: 편집 모드
1587
+ if(task&&task.description){
1588
+ setDescMode(false);
1589
+ }else{
1590
+ setDescMode(true);
1591
+ }
1592
+ document.getElementById("modalOverlay").classList.add("show");
1593
+ setTimeout(function(){document.getElementById("fSubject").focus();},100);
1594
+ }
1595
+ function closeModal(){document.getElementById("modalOverlay").classList.remove("show");editingId=null;}
1596
+
1597
+ function submitModal(){
1598
+ var subject=document.getElementById("fSubject").value.trim();
1599
+ if(!subject){document.getElementById("fSubject").focus();return;}
1600
+ var data={
1601
+ subject:subject,
1602
+ description:(descEditMode?document.getElementById("fDesc").value:document.getElementById("fDesc").dataset.raw||document.getElementById("fDesc").value).trim(),
1603
+ priority:document.getElementById("fPriority").value,
1604
+ agent:document.getElementById("fAgent").value,
1605
+ status:document.getElementById("fStatus").value,
1606
+ blockedBy:document.getElementById("fBlockedBy").value.split(",").map(function(s){return s.trim();}).filter(Boolean),
1607
+ parentId:document.getElementById("fParentId").value.trim()||null
1608
+ };
1609
+ if(editingId){
1610
+ apiUpdate(editingId,data).then(function(){updateLive("Updated: "+subject);});
1611
+ }else{
1612
+ apiCreate(data).then(function(){updateLive("Created: "+subject);});
1613
+ }
1614
+ closeModal();
1615
+ }
1616
+
1617
+ function deleteFromModal(){
1618
+ if(!editingId)return;
1619
+ if(!confirm("Delete this task?"))return;
1620
+ apiDelete(editingId).then(function(){updateLive("Deleted #"+editingId);closeModal();});
1621
+ }
1622
+
1623
+ function onDragStart(e){
1624
+ var card=e.target.closest(".card");
1625
+ if(!card||card.classList.contains("readonly")){e.preventDefault();return;}
1626
+ dragId=card.dataset.id;
1627
+ card.classList.add("dragging");
1628
+ e.dataTransfer.effectAllowed="move";
1629
+ }
1630
+ function onDragEnd(e){
1631
+ var card=e.target.closest(".card");
1632
+ if(card)card.classList.remove("dragging");
1633
+ document.querySelectorAll(".column.drag-over").forEach(function(c){c.classList.remove("drag-over");});
1634
+ dragId=null;
1635
+ }
1636
+ function onDragOver(e){e.preventDefault();var col=e.target.closest(".column");if(col)col.classList.add("drag-over");}
1637
+ function onDragLeave(e){var col=e.target.closest(".column");if(col)col.classList.remove("drag-over");}
1638
+ function onDrop(e){
1639
+ e.preventDefault();
1640
+ document.querySelectorAll(".column.drag-over").forEach(function(c){c.classList.remove("drag-over");});
1641
+ if(!dragId)return;
1642
+ var col=e.target.closest(".column");
1643
+ if(!col)return;
1644
+ var newStatus=col.dataset.status;
1645
+ var task=allTasks.find(function(t){return String(t.id)===String(dragId);});
1646
+ if(!task)return;
1647
+ apiUpdate(dragId,{status:newStatus}).then(function(){updateLive((task.subject||"#"+dragId)+" -> "+newStatus);});
1648
+ dragId=null;
1649
+ }
1650
+
1651
+ function rebuildFilterButtons(){
1652
+ var counts={};
1653
+ allTasks.forEach(function(t){var a=guessAgent(t);counts[a]=(counts[a]||0)+1;});
1654
+ var container=document.getElementById("filterBtns");
1655
+ var html='<button class="'+(filter==="all"?"active":"")+'" data-filter="all">All ('+allTasks.length+')</button>';
1656
+ var order=["orchestrator","frontend","backend","qa","devops","researcher","strategist","designer","content","itemauthor","claude","manual","unknown"];
1657
+ order.forEach(function(key){
1658
+ if(!counts[key])return;
1659
+ var agent=AGENTS[key]||AGENTS.unknown;
1660
+ html+='<button class="'+(filter===key?"active":"")+'" data-filter="'+key+'" style="border-bottom:2px solid '+(filter===key?agent.color:"transparent")+'">'+agent.label+' ('+counts[key]+')</button>';
1661
+ });
1662
+ container.innerHTML=html;
1663
+ }
1664
+ function render(){
1665
+ rebuildFilterButtons();
1666
+ var filtered=filter==="all"?allTasks:allTasks.filter(function(t){return guessAgent(t)===filter;});
1667
+ var total=allTasks.length;
1668
+ var active=allTasks.filter(function(t){return getCol(t)==="in_progress";}).length;
1669
+ var done=allTasks.filter(function(t){return getCol(t)==="completed";}).length;
1670
+ var blocked=allTasks.filter(function(t){return isBlocked(t);}).length;
1671
+ document.getElementById("s-total").textContent=total;
1672
+ document.getElementById("s-active").textContent=active;
1673
+ document.getElementById("s-done").textContent=done;
1674
+ document.getElementById("s-blocked").textContent=blocked;
1675
+ if(done>prevDoneCount&&prevDoneCount>0){
1676
+ var nd=allTasks.filter(function(t){return getCol(t)==="completed";}).slice(-1)[0];
1677
+ if(nd)notify("Task Completed",nd.subject||"A task was completed");
1678
+ }
1679
+ prevDoneCount=done;
1680
+ var board=document.getElementById("board");
1681
+ if(allTasks.length===0){
1682
+ board.innerHTML='<div class="empty"><div style="font-size:20px;font-weight:700;margin-bottom:12px;color:var(--t3);">No tasks</div><div style="color:var(--t4)">Press <kbd>N</kbd> or click <strong style="color:var(--t1)">New Task</strong></div></div>';
1683
+ return;
1684
+ }
1685
+ board.innerHTML=COLUMNS.map(function(col){
1686
+ var colTasks=filtered.filter(function(t){return getCol(t)===col.id;});
1687
+ var parents=colTasks.filter(function(t){return !t.parentId;});
1688
+ var childMap={};
1689
+ allTasks.forEach(function(t){if(t.parentId){if(!childMap[t.parentId])childMap[t.parentId]=[];childMap[t.parentId].push(t);}});
1690
+ var cardsHtml="";
1691
+ parents.forEach(function(t){
1692
+ var children=childMap[t.id]||[];
1693
+ var isParent=children.length>0;
1694
+ cardsHtml+=renderCard(t,isParent,children);
1695
+ var colChildren=children.filter(function(c){return getCol(c)===col.id;});
1696
+ colChildren.forEach(function(c){cardsHtml+=renderCard(c,false,[],true);});
1697
+ });
1698
+ var orphanSubs=colTasks.filter(function(t){
1699
+ if(!t.parentId)return false;
1700
+ var p=allTasks.find(function(x){return String(x.id)===String(t.parentId);});
1701
+ return !p||getCol(p)!==col.id;
1702
+ });
1703
+ orphanSubs.forEach(function(t){cardsHtml+=renderCard(t,false,[],true);});
1704
+ return '<div class="column" data-status="'+col.id+'" ondragover="onDragOver(event)" ondragleave="onDragLeave(event)" ondrop="onDrop(event)"><div class="col-header"><span class="name"><span class="dot" style="background:'+col.dot+'"></span>'+col.name+'</span><span class="count">'+colTasks.length+'</span></div><div class="cards">'+cardsHtml+'</div></div>';
1705
+ }).join("");
1706
+ }
1707
+
1708
+ function renderCard(t,isParent,children,isSub){
1709
+ var agentKey=guessAgent(t),agent=AGENTS[agentKey]||AGENTS.unknown;
1710
+ var pri=t.priority||"medium",blocked=isBlocked(t);
1711
+ var id=t.id||t._file||"",title=t.subject||t.title||"Untitled";
1712
+ var desc=t.description||"",activeForm=t.activeForm||"";
1713
+ var owner=t.owner||t.agentName||t.teammate||t.assignee||"";
1714
+ var sessionShort=(t._session||"").slice(0,8),elapsed=getElapsed(t);
1715
+ var editable=t._editable,draggable=!blocked;
1716
+ children=children||[];
1717
+ var cls="card"+(blocked?" blocked":"")+(editable?"":" readonly")+(isParent?" parent":"")+(isSub?" subtask":"");
1718
+ var h='<div class="'+cls+'" data-id="'+esc(id)+'"'+(draggable?' draggable="true" ondragstart="onDragStart(event)" ondragend="onDragEnd(event)"':'')+' style="border-left-color:'+agent.color+'">';
1719
+ h+='<div class="card-actions">';
1720
+ if(editable) h+='<button title="Edit" onclick="event.stopPropagation();editTask(\''+esc(id)+'\')">&#9998;</button>';
1721
+ h+='<button class="del" title="Delete" onclick="event.stopPropagation();quickDelete(\''+esc(id)+'\')">&#10005;</button>';
1722
+ h+='</div>';
1723
+ h+='<div class="top"><span class="id">#'+esc(id);
1724
+ if(isSub&&t.parentId) h+=' <span style="color:var(--t4);font-size:8px;">&larr; #'+esc(String(t.parentId))+'</span>';
1725
+ h+=(editable?'<span class="source-badge manual">manual</span>':'<span class="source-badge">agent</span>')+'</span>';
1726
+ h+='<span class="badge badge-'+pri+'">'+(pri==="high"?"P0":pri==="medium"?"P1":"P2")+'</span></div>';
1727
+ h+='<div class="title">'+esc(title)+'</div>';
1728
+ // Inline spinner for collapsed state
1729
+ if(activeForm&&getCol(t)==="in_progress") h+='<div class="active-form-inline"><div class="spinner"></div>'+esc(activeForm)+'</div>';
1730
+ // Exec indicator
1731
+ if(getCol(t)==="in_progress"&&String(t.id)===String(currentExecId)) h+='<div class="exec-indicator"><div class="spinner"></div>Executing...</div>';
1732
+ // Review badge (collapsed)
1733
+ if(getCol(t)==="in_review") h+='<div style="font-size:10px;color:var(--vl);margin-top:2px;font-weight:600;">Awaiting Review</div>';
1734
+ // Expanded body
1735
+ h+='<div class="card-body">';
1736
+ if(activeForm&&getCol(t)==="in_progress") h+='<div class="active-form"><div class="spinner"></div>'+esc(activeForm)+'</div>';
1737
+ h+='<div class="agent-chip"><span class="agent-dot" style="background:'+agent.color+'"></span>'+esc(agent.label);
1738
+ if(owner) h+=' / '+esc(owner);
1739
+ if(blocked) h+=' / <span style="color:var(--rd)">blocked</span>';
1740
+ h+='</div>';
1741
+ if(elapsed) h+='<div class="time">'+elapsed+'</div>';
1742
+ if(isParent&&children.length>0){
1743
+ var doneCount=children.filter(function(c){return getCol(c)==="completed";}).length;
1744
+ var inpCount=children.filter(function(c){return getCol(c)==="in_progress";}).length;
1745
+ var pct=Math.round(doneCount/children.length*100);
1746
+ h+='<div class="subtask-bar"><div class="fill" style="width:'+pct+'%"></div></div>';
1747
+ h+='<div class="subtask-info"><span>'+doneCount+'/'+children.length+' subtasks</span><span>';
1748
+ if(inpCount>0) h+=inpCount+' active';
1749
+ h+='</span></div>';
1750
+ }
1751
+ if(t.blockedBy&&t.blockedBy.length) h+='<div class="deps">blocked by '+t.blockedBy.join(", ")+'</div>';
1752
+ if(t.blocks&&t.blocks.length) h+='<div class="deps" style="color:var(--am)">blocks '+t.blocks.join(", ")+'</div>';
1753
+ if(sessionShort&&!editable) h+='<div class="session">'+sessionShort+'</div>';
1754
+ if(desc) h+='<div class="desc" style="max-height:200px;">'+esc(desc)+'</div>';
1755
+ if(getCol(t)==="in_review"){
1756
+ if(t.reportSummary) h+='<div class="desc" style="max-height:120px;margin-top:6px;border-top:1px solid var(--b1);padding-top:6px;font-size:11px;color:var(--t3);">'+esc(t.reportSummary)+'</div>';
1757
+ h+='<div class="review-actions">';
1758
+ h+='<button class="review-btn approve" onclick="event.stopPropagation();approveTask(\''+esc(id)+'\')">Approve</button>';
1759
+ h+='<button class="review-btn reject" onclick="event.stopPropagation();rejectTask(\''+esc(id)+'\')">Reject</button>';
1760
+ if(t.reportPath||t.reportSummary) h+='<button class="report-btn" onclick="event.stopPropagation();openReport(\''+esc(id)+'\')">Report</button>';
1761
+ h+='</div>';
1762
+ }
1763
+ if(getCol(t)==="completed"&&(t.reportPath||t.reportSummary)){
1764
+ h+='<button class="report-btn" onclick="event.stopPropagation();openReport(\''+esc(id)+'\')">View Report</button>';
1765
+ }
1766
+ h+='</div>';
1767
+ h+='</div>';
1768
+ return h;
1769
+ }
1770
+
1771
+ function editTask(id){var t=allTasks.find(function(x){return String(x.id)===String(id);});if(t)openModal(t);}
1772
+ function quickDelete(id){
1773
+ var t=allTasks.find(function(x){return String(x.id)===String(id);});
1774
+ if(!t)return;
1775
+ if(!confirm("Delete #"+id+"?"))return;
1776
+ apiDelete(id).then(function(){updateLive("Deleted #"+id);});
1777
+ }
1778
+
1779
+ document.addEventListener("click",function(e){
1780
+ var card=e.target.closest(".card");
1781
+ if(!card||e.target.closest(".card-actions"))return;
1782
+ card.classList.toggle("expanded");
1783
+ });
1784
+ document.addEventListener("dblclick",function(e){
1785
+ var card=e.target.closest(".card");
1786
+ if(!card||e.target.closest(".card-actions"))return;
1787
+ var id=card.dataset.id;
1788
+ var t=allTasks.find(function(x){return String(x.id)===String(id);});
1789
+ if(t&&t._editable)openModal(t);
1790
+ });
1791
+
1792
+ function getCol(t){
1793
+ var s=(t.status||t.column||"pending").toLowerCase();
1794
+ if(s.indexOf("progress")>=0||s==="running"||s==="active")return "in_progress";
1795
+ if(s.indexOf("review")>=0)return "in_review";
1796
+ if(s==="done"||s==="completed"||s==="finished")return "completed";
1797
+ return "pending";
1798
+ }
1799
+ function isBlocked(t){return t.blockedBy&&t.blockedBy.length>0&&getCol(t)!=="completed";}
1800
+ function guessAgent(t){
1801
+ if(t.agent&&AGENTS[t.agent])return t.agent;
1802
+ if(t.agent==="claude"||(!t.agent&&t._session&&t._session!=="kanban"))return "claude";
1803
+ if(t.agent==="manual")return "manual";
1804
+ var name=(t.owner||t.agentName||t.teammate||t.assignee||"").toLowerCase();
1805
+ var subj=(t.subject||"").toLowerCase();
1806
+ var combined=name+" "+subj;
1807
+ if(name.indexOf("front")>=0||subj.indexOf("frontend")>=0||subj.indexOf("ui ")>=0||subj.indexOf("css")>=0||subj.indexOf("컴포넌트")>=0)return "frontend";
1808
+ if(name.indexOf("back")>=0||combined.indexOf("api")>=0||combined.indexOf("server")>=0||combined.indexOf("db")>=0||combined.indexOf("supabase")>=0||combined.indexOf("edge function")>=0||combined.indexOf("webhook")>=0)return "backend";
1809
+ if(name.indexOf("qa")>=0||combined.indexOf("test")>=0||combined.indexOf("테스트")>=0||combined.indexOf("e2e")>=0)return "qa";
1810
+ if(combined.indexOf("devops")>=0||combined.indexOf("infra")>=0||combined.indexOf("deploy")>=0||combined.indexOf("ci")>=0||combined.indexOf("배포")>=0)return "devops";
1811
+ if(combined.indexOf("strat")>=0||combined.indexOf("plan")>=0||combined.indexOf("arch")>=0||combined.indexOf("계획")>=0)return "strategist";
1812
+ if(combined.indexOf("research")>=0||combined.indexOf("explore")>=0||combined.indexOf("조사")>=0)return "researcher";
1813
+ if(name.indexOf("orch")>=0||name.indexOf("leader")>=0||name.indexOf("coord")>=0||combined.indexOf("점검")>=0||combined.indexOf("정리")>=0||combined.indexOf("공유")>=0)return "orchestrator";
1814
+ if(combined.indexOf("content")>=0||combined.indexOf("planner")>=0||combined.indexOf("curriculum")>=0||combined.indexOf("textbook")>=0||combined.indexOf("교재")>=0||combined.indexOf("콘텐츠")>=0||combined.indexOf("학습")>=0)return "content";
1815
+ if(combined.indexOf("item")>=0||combined.indexOf("author")>=0||combined.indexOf("출제")>=0||combined.indexOf("문항")>=0||combined.indexOf("rubric")>=0)return "itemauthor";
1816
+ if(combined.indexOf("design")>=0||combined.indexOf("ux")>=0||combined.indexOf("디자인")>=0||combined.indexOf("figma")>=0)return "designer";
1817
+ return "unknown";
1818
+ }
1819
+ function getElapsed(t){
1820
+ var start=t.startedAt||t.createdAt;if(!start)return "";
1821
+ var ms=(t.completedAt?new Date(t.completedAt):new Date())-new Date(start);
1822
+ if(ms<0)return "";var sec=Math.floor(ms/1000);
1823
+ if(sec<60)return sec+"s";var min=Math.floor(sec/60);
1824
+ if(min<60)return min+"m "+(sec%60)+"s";
1825
+ return Math.floor(min/60)+"h "+(min%60)+"m";
1826
+ }
1827
+ function esc(s){return String(s).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;");}
1828
+
1829
+ document.getElementById("toolbar").addEventListener("click",function(e){
1830
+ if(e.target.tagName!=="BUTTON"||!e.target.dataset.filter)return;
1831
+ filter=e.target.dataset.filter;
1832
+ document.querySelectorAll(".toolbar button[data-filter]").forEach(function(b){b.classList.remove("active");});
1833
+ e.target.classList.add("active");
1834
+ render();
1835
+ });
1836
+ // Right-click agent filter button → open agent prompt panel
1837
+ document.getElementById("toolbar").addEventListener("contextmenu",function(e){
1838
+ if(e.target.tagName!=="BUTTON"||!e.target.dataset.filter)return;
1839
+ var f=e.target.dataset.filter;
1840
+ if(f==="all")return;
1841
+ e.preventDefault();
1842
+ // Map filter key to agent file name
1843
+ var nameMap={content:"content-planner",itemauthor:"item-author"};
1844
+ var agentName=nameMap[f]||f;
1845
+ if(!agentData){
1846
+ fetch("/api/agents").then(function(r){return r.json();}).then(function(data){
1847
+ agentData=data;
1848
+ document.getElementById("agentOverlay").classList.add("show");
1849
+ document.getElementById("agentPanel").classList.add("show");
1850
+ renderAgentTabs(agentName);
1851
+ });
1852
+ } else {
1853
+ document.getElementById("agentOverlay").classList.add("show");
1854
+ document.getElementById("agentPanel").classList.add("show");
1855
+ renderAgentTabs(agentName);
1856
+ }
1857
+ });
1858
+
1859
+ function connectSSE(){
1860
+ var dot=document.getElementById("connDot"),status=document.getElementById("connStatus");
1861
+ var es=new EventSource("/events");
1862
+ es.onopen=function(){dot.style.background="var(--gn)";status.textContent="SSE";status.style.color="var(--gn)";};
1863
+ es.onmessage=function(e){
1864
+ try{
1865
+ var data=JSON.parse(e.data);
1866
+ if(data.type==="activity"&&data.event){
1867
+ onActivityEvent(data.event);
1868
+ } else if(data.type==="exec_start"){
1869
+ onExecStart(data);
1870
+ } else if(data.type==="exec"){
1871
+ onExecChunk(data);
1872
+ } else if(data.type==="exec_done"){
1873
+ onExecDone(data);
1874
+ } else if(data.type==="exec_error"){
1875
+ onExecError(data);
1876
+ } else if(data.type==="exec_queue"){
1877
+ var qi=document.getElementById("execQueueInd");
1878
+ if(qi){
1879
+ if(data.queued&&data.queued.length>0){
1880
+ qi.style.display="inline";
1881
+ qi.textContent="Q:"+data.queued.length;
1882
+ }else{
1883
+ qi.style.display="none";
1884
+ }
1885
+ }
1886
+ } else if(data.tasks){
1887
+ allTasks=data.tasks;
1888
+ var sessions=[];allTasks.forEach(function(t){if(sessions.indexOf(t._session)<0)sessions.push(t._session);});
1889
+ document.getElementById("taskSource").textContent=allTasks.length>0?allTasks.length+" tasks / "+sessions.length+" sessions":"~/.claude/tasks/";
1890
+ render();
1891
+ }
1892
+ }catch(ex){}
1893
+ };
1894
+ es.onerror=function(){dot.style.background="var(--rd)";status.textContent="reconnecting";status.style.color="var(--rd)";es.close();setTimeout(connectSSE,3000);};
1895
+ }
1896
+ function updateLive(msg){
1897
+ document.getElementById("liveMsg").textContent=msg;
1898
+ document.getElementById("liveTime").textContent=new Date().toLocaleTimeString("ko-KR",{hour:"2-digit",minute:"2-digit",second:"2-digit"});
1899
+ }
1900
+ function clearDone(){
1901
+ var done=allTasks.filter(function(t){return getCol(t)==="completed"&&t._editable;});
1902
+ if(!done.length)return;
1903
+ if(!confirm(done.length+" completed tasks will be deleted."))return;
1904
+ Promise.all(done.map(function(t){return apiDelete(t.id);})).then(function(){updateLive("Cleared "+done.length+" tasks");});
1905
+ }
1906
+
1907
+ document.addEventListener("keydown",function(e){
1908
+ // Global: Ctrl+Shift+K stops execution (works even in inputs)
1909
+ if(e.ctrlKey&&e.shiftKey&&(e.key==="K"||e.key==="k")){e.preventDefault();stopExecution();return;}
1910
+ if(e.target.closest("input,select,textarea"))return;
1911
+ if(e.key==="n"||e.key==="N")openModal();
1912
+ if(e.key==="Escape"){closeModal();closeReport();closeActivity();closeChat();}
1913
+ if(e.key==="a"||e.key==="A")toggleActivity();
1914
+ if(e.key==="c"||e.key==="C")toggleChat();
1915
+ });
1916
+ document.getElementById("modalOverlay").addEventListener("click",function(e){if(e.target===e.currentTarget)closeModal();});
1917
+ document.querySelector(".modal").addEventListener("keydown",function(e){if((e.ctrlKey||e.metaKey)&&e.key==="Enter")submitModal();});
1918
+ setInterval(function(){if(allTasks.some(function(t){return getCol(t)==="in_progress";}))render();},5000);
1919
+
1920
+ function openReport(id){
1921
+ var t=allTasks.find(function(x){return String(x.id)===String(id);});
1922
+ if(!t)return;
1923
+ document.getElementById("rpTitle").textContent="#"+t.id+" "+(t.subject||"Untitled");
1924
+ var meta="";
1925
+ if(t.completedAt)meta+=new Date(t.completedAt).toLocaleString("ko-KR");
1926
+ if(t.reportPath)meta+=" &middot; "+t.reportPath.split("/").pop();
1927
+ document.getElementById("rpMeta").innerHTML=meta;
1928
+ var content=document.getElementById("rpContent");
1929
+ if(t.reportPath){
1930
+ content.innerHTML='<div class="rp-loading">Loading...</div>';
1931
+ document.getElementById("reportPanel").classList.add("open");
1932
+ document.getElementById("reportOverlay").classList.add("open");
1933
+ fetch("/api/report?path="+encodeURIComponent(t.reportPath)).then(function(r){return r.json();}).then(function(data){
1934
+ if(data.error){content.innerHTML='<div class="rp-none">'+esc(data.error)+'</div>';return;}
1935
+ var html='';
1936
+ if(t.reportSummary){html+='<div class="rp-summary"><strong>Summary</strong><br>'+esc(t.reportSummary).replace(/\n/g,"<br>")+'</div>';}
1937
+ html+='<div class="rp-md">'+renderMarkdown(data.content)+'</div>';
1938
+ content.innerHTML=html;
1939
+ }).catch(function(){content.innerHTML='<div class="rp-none">Failed to load report</div>';});
1940
+ }else if(t.reportSummary){
1941
+ content.innerHTML='<div class="rp-summary"><strong>Summary</strong><br>'+esc(t.reportSummary).replace(/\n/g,"<br>")+'</div>';
1942
+ document.getElementById("reportPanel").classList.add("open");
1943
+ document.getElementById("reportOverlay").classList.add("open");
1944
+ }else{
1945
+ content.innerHTML='<div class="rp-none">No report attached</div>';
1946
+ document.getElementById("reportPanel").classList.add("open");
1947
+ document.getElementById("reportOverlay").classList.add("open");
1948
+ }
1949
+ }
1950
+ function closeReport(){
1951
+ document.getElementById("reportPanel").classList.remove("open");
1952
+ document.getElementById("reportOverlay").classList.remove("open");
1953
+ }
1954
+
1955
+ function renderMarkdown(md){
1956
+ var html=esc(md);
1957
+ html=html.replace(/```(\w*)\n([\s\S]*?)```/g,function(m,lang,code){return '<pre><code>'+code+'</code></pre>';});
1958
+ html=html.replace(/`([^`]+)`/g,'<code>$1</code>');
1959
+ html=html.replace(/((?:^\|.*\|$\n?)+)/gm,function(block){
1960
+ var rows=block.trim().split("\n");
1961
+ if(rows.length<2)return block;
1962
+ var out='<table>';
1963
+ rows.forEach(function(row,i){
1964
+ if(row.match(/^\|[\s\-:|]+\|$/))return;
1965
+ var cells=row.split("|").filter(function(c,j){return j>0&&j<row.split("|").length-1;});
1966
+ var tag=i===0?"th":"td";
1967
+ out+="<tr>"+cells.map(function(c){return "<"+tag+">"+c.trim()+"</"+tag+">";}).join("")+"</tr>";
1968
+ });
1969
+ out+='</table>';return out;
1970
+ });
1971
+ html=html.replace(/^#### (.+)$/gm,'<h4>$1</h4>');
1972
+ html=html.replace(/^### (.+)$/gm,'<h3>$1</h3>');
1973
+ html=html.replace(/^## (.+)$/gm,'<h2>$1</h2>');
1974
+ html=html.replace(/^# (.+)$/gm,'<h1>$1</h1>');
1975
+ html=html.replace(/^&gt; (.+)$/gm,'<blockquote>$1</blockquote>');
1976
+ html=html.replace(/\*\*(.+?)\*\*/g,'<strong>$1</strong>');
1977
+ html=html.replace(/\*(.+?)\*/g,'<em>$1</em>');
1978
+ html=html.replace(/^---$/gm,'<hr>');
1979
+ html=html.replace(/^- (.+)$/gm,'<li>$1</li>');
1980
+ html=html.replace(/((?:<li>.*<\/li>\n?)+)/g,'<ul>$1</ul>');
1981
+ html=html.replace(/\n\n/g,'</p><p>');
1982
+ html='<p>'+html+'</p>';
1983
+ html=html.replace(/<p><(h[1-4]|ul|ol|pre|table|blockquote|hr)/g,'<$1');
1984
+ html=html.replace(/<\/(h[1-4]|ul|ol|pre|table|blockquote)><\/p>/g,'</$1>');
1985
+ html=html.replace(/<hr><\/p>/g,'<hr>');
1986
+ html=html.replace(/<p><\/p>/g,'');
1987
+ return html;
1988
+ }
1989
+
1990
+ // ── Activity Panel ──
1991
+ var activityEvents=[], activityUnread=0, activityOpen=false;
1992
+ var ACT_ICONS={created:"🆕",started:"▶️",completed:"✅",updated:"📝",deleted:"🗑️"};
1993
+
1994
+ function toggleActivity(){
1995
+ if(activityOpen){closeActivity();}else{openActivity();}
1996
+ }
1997
+ function openActivity(){
1998
+ activityOpen=true;
1999
+ activityUnread=0;
2000
+ updateActBadge();
2001
+ document.getElementById("actPanel").classList.add("open");
2002
+ document.getElementById("actOverlay").classList.add("open");
2003
+ if(activityEvents.length===0)loadActivity();
2004
+ }
2005
+ function closeActivity(){
2006
+ activityOpen=false;
2007
+ document.getElementById("actPanel").classList.remove("open");
2008
+ document.getElementById("actOverlay").classList.remove("open");
2009
+ }
2010
+ function updateActBadge(){
2011
+ var badge=document.getElementById("actBadge");
2012
+ if(activityUnread>0){
2013
+ badge.textContent=activityUnread>99?"99+":activityUnread;
2014
+ badge.classList.add("show");
2015
+ }else{
2016
+ badge.classList.remove("show");
2017
+ }
2018
+ }
2019
+ function loadActivity(){
2020
+ fetch("/api/activity?limit=200").then(function(r){return r.json();}).then(function(events){
2021
+ activityEvents=events;
2022
+ renderActivity();
2023
+ }).catch(function(){});
2024
+ }
2025
+ function renderActivity(newEvent){
2026
+ var content=document.getElementById("actContent");
2027
+ if(activityEvents.length===0){
2028
+ content.innerHTML='<div class="ap-empty">No activity yet</div>';
2029
+ return;
2030
+ }
2031
+ var html=activityEvents.map(function(evt,i){
2032
+ return renderActivityItem(evt,newEvent&&i===0);
2033
+ }).join("");
2034
+ content.innerHTML=html;
2035
+ }
2036
+ function renderActivityItem(evt,isNew){
2037
+ var icon=ACT_ICONS[evt.type]||"📌";
2038
+ var h='<div class="activity-item'+(isNew?" new-item":"")+'">';
2039
+ h+='<div class="act-header">';
2040
+ h+='<span class="act-icon">'+icon+'</span>';
2041
+ h+='<span class="act-type '+esc(evt.type)+'">'+esc(evt.type)+'</span>';
2042
+ h+='<span class="act-id" onclick="scrollToCard(\''+esc(evt.taskId)+'\')">#'+esc(evt.taskId)+'</span>';
2043
+ if(evt.agent) h+='<span class="act-agent">'+esc(evt.agent)+'</span>';
2044
+ h+='</div>';
2045
+ h+='<div class="act-subject">'+esc(evt.subject||"")+'</div>';
2046
+ if(evt.detail) h+='<div class="act-detail">&gt; '+esc(evt.detail)+'</div>';
2047
+ h+='<div class="act-time">'+formatActTime(evt.ts)+'</div>';
2048
+ h+='</div>';
2049
+ return h;
2050
+ }
2051
+ function formatActTime(ts){
2052
+ if(!ts)return "";
2053
+ var d=new Date(ts);
2054
+ var now=new Date();
2055
+ var diff=now-d;
2056
+ var sec=Math.floor(diff/1000);
2057
+ var time=d.toLocaleTimeString("ko-KR",{hour:"2-digit",minute:"2-digit"});
2058
+ if(sec<60) return time+" · 방금 전";
2059
+ if(sec<3600) return time+" · "+Math.floor(sec/60)+"분 전";
2060
+ if(sec<86400) return time+" · "+Math.floor(sec/3600)+"시간 전";
2061
+ return d.toLocaleDateString("ko-KR",{month:"short",day:"numeric"})+" "+time;
2062
+ }
2063
+ function scrollToCard(id){
2064
+ closeActivity();
2065
+ var card=document.querySelector('.card[data-id="'+id+'"]');
2066
+ if(card){
2067
+ card.scrollIntoView({behavior:"smooth",block:"center"});
2068
+ card.style.outline="2px solid var(--ac)";
2069
+ card.classList.add("expanded");
2070
+ setTimeout(function(){card.style.outline="";},2000);
2071
+ }
2072
+ }
2073
+ function onActivityEvent(evt){
2074
+ // Prepend to events list
2075
+ activityEvents.unshift(evt);
2076
+ if(activityEvents.length>200) activityEvents=activityEvents.slice(0,200);
2077
+ if(activityOpen){
2078
+ renderActivity(true);
2079
+ }else{
2080
+ activityUnread++;
2081
+ updateActBadge();
2082
+ }
2083
+ // Update live feed
2084
+ var icon=ACT_ICONS[evt.type]||"";
2085
+ updateLive(icon+" #"+evt.taskId+" "+evt.type+": "+(evt.subject||""));
2086
+ }
2087
+ // ── Chat Panel ──
2088
+ var chatMessages=[], chatOpen=false, chatStreaming=false;
2089
+
2090
+ function toggleChat(){if(chatOpen)closeChat();else openChat();}
2091
+ function openChat(){
2092
+ chatOpen=true;
2093
+ var panel=document.getElementById("chatPanel");
2094
+ panel.classList.add("open");
2095
+ // restore saved size
2096
+ var savedH=localStorage.getItem("apex_chat_height");
2097
+ var savedW=localStorage.getItem("apex_chat_width");
2098
+ var savedExp=localStorage.getItem("apex_chat_expanded")==="1";
2099
+ if(savedExp){chatExpanded=true;panel.classList.add("expanded");}
2100
+ else{
2101
+ if(savedH)panel.style.height=savedH;
2102
+ if(savedW)panel.style.width=savedW;
2103
+ }
2104
+ loadChatHistory();
2105
+ setTimeout(function(){document.getElementById("chatInput").focus();},100);
2106
+ }
2107
+ function closeChat(){
2108
+ chatOpen=false;
2109
+ var panel=document.getElementById("chatPanel");
2110
+ panel.classList.remove("open","expanded");
2111
+ chatExpanded=false;
2112
+ }
2113
+ function clearChat(){
2114
+ chatMessages=[];
2115
+ renderChatMessages();
2116
+ fetch("/api/chat/history",{method:"DELETE"}).catch(function(){});
2117
+ fetch("/api/chat/session",{method:"DELETE"}).catch(function(){});
2118
+ var badge=document.getElementById("chatSessionBadge");
2119
+ if(badge)badge.style.display="none";
2120
+ }
2121
+ function saveChatHistory(){
2122
+ if(chatStreaming)return;
2123
+ fetch("/api/chat/history",{
2124
+ method:"PUT",
2125
+ headers:{"Content-Type":"application/json"},
2126
+ body:JSON.stringify({messages:chatMessages})
2127
+ }).catch(function(){});
2128
+ }
2129
+ var chatLoaded=false;
2130
+ function loadChatHistory(){
2131
+ if(chatLoaded)return;
2132
+ fetch("/api/chat/history").then(function(r){return r.json();}).then(function(msgs){
2133
+ if(msgs&&msgs.length>0&&chatMessages.length===0){
2134
+ chatMessages=msgs;
2135
+ renderChatMessages();
2136
+ }
2137
+ chatLoaded=true;
2138
+ }).catch(function(){chatLoaded=true;});
2139
+ var savedModel=localStorage.getItem("apex_chat_model");
2140
+ if(savedModel){var sel=document.getElementById("chatModel");if(sel)sel.value=savedModel;}
2141
+ }
2142
+ function onChatModelChange(el){
2143
+ localStorage.setItem("apex_chat_model",el.value);
2144
+ fetch("/api/chat/session",{method:"DELETE"}).catch(function(){});
2145
+ var badge=document.getElementById("chatSessionBadge");
2146
+ if(badge)badge.style.display="none";
2147
+ }
2148
+ function autoResizeChatInput(el){
2149
+ el.style.height="auto";
2150
+ el.style.height=Math.min(el.scrollHeight,120)+"px";
2151
+ }
2152
+
2153
+ // ── Chat Resize ──
2154
+ var chatExpanded=false;
2155
+ function toggleChatExpand(){
2156
+ var panel=document.getElementById("chatPanel");
2157
+ chatExpanded=!chatExpanded;
2158
+ panel.classList.toggle("expanded",chatExpanded);
2159
+ if(chatExpanded){panel.style.height="";panel.style.width="";}
2160
+ localStorage.setItem("apex_chat_expanded",chatExpanded?"1":"0");
2161
+ }
2162
+ (function(){
2163
+ var panel=document.getElementById("chatPanel");
2164
+ // height resize
2165
+ var hHandle=document.getElementById("chatResizeH");
2166
+ hHandle.addEventListener("mousedown",function(e){
2167
+ e.preventDefault();
2168
+ if(chatExpanded)return;
2169
+ hHandle.classList.add("active");
2170
+ panel.style.transition="none";
2171
+ var startY=e.clientY, startH=panel.offsetHeight;
2172
+ function onMove(ev){
2173
+ var newH=startH+(startY-ev.clientY);
2174
+ newH=Math.max(200,Math.min(window.innerHeight-60,newH));
2175
+ panel.style.height=newH+"px";
2176
+ }
2177
+ function onUp(){
2178
+ hHandle.classList.remove("active");
2179
+ panel.style.transition="";
2180
+ localStorage.setItem("apex_chat_height",panel.style.height);
2181
+ document.removeEventListener("mousemove",onMove);
2182
+ document.removeEventListener("mouseup",onUp);
2183
+ }
2184
+ document.addEventListener("mousemove",onMove);
2185
+ document.addEventListener("mouseup",onUp);
2186
+ });
2187
+ // width resize
2188
+ var wHandle=document.getElementById("chatResizeW");
2189
+ wHandle.addEventListener("mousedown",function(e){
2190
+ e.preventDefault();
2191
+ if(chatExpanded)return;
2192
+ wHandle.classList.add("active");
2193
+ panel.style.transition="none";
2194
+ var startX=e.clientX, startW=panel.offsetWidth;
2195
+ function onMove(ev){
2196
+ var newW=startW+(startX-ev.clientX);
2197
+ newW=Math.max(320,Math.min(window.innerWidth-100,newW));
2198
+ panel.style.width=newW+"px";
2199
+ }
2200
+ function onUp(){
2201
+ wHandle.classList.remove("active");
2202
+ panel.style.transition="";
2203
+ localStorage.setItem("apex_chat_width",panel.style.width);
2204
+ document.removeEventListener("mousemove",onMove);
2205
+ document.removeEventListener("mouseup",onUp);
2206
+ }
2207
+ document.addEventListener("mousemove",onMove);
2208
+ document.addEventListener("mouseup",onUp);
2209
+ });
2210
+ })();
2211
+ function getToolIcon(name){
2212
+ var icons={Bash:"$",Read:"\u{1F4C4}",Edit:"\u270E",Write:"\u270E",Grep:"\u{1F50D}",Glob:"\u{1F4C2}",WebSearch:"\u{1F310}",WebFetch:"\u{1F310}"};
2213
+ return icons[name]||"\u2699";
2214
+ }
2215
+ function stripActionBlocks(text){
2216
+ return (text||"").replace(/:::action\n[\s\S]*?\n:::/g,"").trim();
2217
+ }
2218
+ function renderActionResult(ar){
2219
+ if(ar.action==="task_create"&&ar.ok){
2220
+ return '<div class="chat-action-result"><span class="action-icon" style="color:var(--gn)">+</span><span class="action-text">\uD0DC\uC2A4\uD06C #'+esc(ar.task.id)+' \uC0DD\uC131: '+esc(ar.task.subject)+'</span><span class="action-link" onclick="scrollToCard(\''+esc(ar.task.id)+'\')">\uBCF4\uAE30</span></div>';
2221
+ }
2222
+ if(ar.action==="task_update"&&ar.ok&&ar.task){
2223
+ return '<div class="chat-action-result"><span class="action-icon" style="color:var(--am)">~</span><span class="action-text">\uD0DC\uC2A4\uD06C #'+esc(ar.task.id)+' \uC218\uC815\uB428</span></div>';
2224
+ }
2225
+ if(ar.action==="task_delete"&&ar.ok){
2226
+ return '<div class="chat-action-result"><span class="action-icon" style="color:var(--rd)">-</span><span class="action-text">\uD0DC\uC2A4\uD06C #'+esc(ar.id)+' \uC0AD\uC81C\uB428</span></div>';
2227
+ }
2228
+ if(ar.action==="task_execute"&&ar.ok){
2229
+ return '<div class="chat-action-result"><span class="action-icon" style="color:var(--am)">\u25B6</span><span class="action-text">\uD0DC\uC2A4\uD06C #'+esc(ar.id)+' \uC2E4\uD589 \uC2DC\uC791</span></div>';
2230
+ }
2231
+ if(ar.action==="task_execute_batch"&&ar.ok){
2232
+ return '<div class="chat-action-result"><span class="action-icon" style="color:var(--am)">\u25B6\u25B6</span><span class="action-text">\uD0DC\uC2A4\uD06C '+(ar.ids||[]).map(function(id){return"#"+id;}).join(", ")+' \uC77C\uAD04 \uC2E4\uD589</span></div>';
2233
+ }
2234
+ return "";
2235
+ }
2236
+ function renderChatMessages(){
2237
+ var container=document.getElementById("chatMessages");
2238
+ if(chatMessages.length===0){
2239
+ container.innerHTML='<div class="chat-empty">\uD0DC\uC2A4\uD06C \uC0DD\uC131, \uBCF4\uB4DC \uAD00\uB9AC, \uCF54\uB4DC \uC9C8\uBB38 \u2014 \uBB34\uC5C7\uC774\uB4E0 \uBCF4\uB0B4\uC138\uC694</div>';
2240
+ return;
2241
+ }
2242
+ container.innerHTML=chatMessages.map(function(msg,i){
2243
+ var cls=msg.role==="user"?"user":"assistant";
2244
+ var label=msg.role==="user"?"You":"Claude";
2245
+ var isLast=i===chatMessages.length-1;
2246
+ var streaming=isLast&&cls==="assistant"&&chatStreaming;
2247
+
2248
+ if(msg.role==="user"){
2249
+ return '<div class="chat-msg user"><div class="cm-role">'+label+'</div><div class="cm-content"><p>'+esc(msg.content)+'</p></div></div>';
2250
+ }
2251
+
2252
+ // Assistant message with tools and actions
2253
+ var h='<div class="chat-msg assistant'+(streaming?" streaming":"")+'">';
2254
+ h+='<div class="cm-role">'+label+'</div>';
2255
+ h+='<div class="cm-content">';
2256
+
2257
+ // Tool use blocks
2258
+ if(msg.tools&&msg.tools.length>0){
2259
+ for(var ti=0;ti<msg.tools.length;ti++){
2260
+ var tool=msg.tools[ti];
2261
+ var toolIcon=getToolIcon(tool.name);
2262
+ h+='<div class="chat-tool-block">';
2263
+ h+='<div class="tool-header" onclick="this.classList.toggle(\'open\')">';
2264
+ if(tool.status==="running"){
2265
+ h+='<div class="tool-spinner"></div>';
2266
+ }else{
2267
+ h+='<span class="tool-icon">'+toolIcon+'</span>';
2268
+ }
2269
+ h+='<span class="tool-name">'+esc(tool.name)+'</span>';
2270
+ h+='<span class="tool-preview">'+esc((tool.input||"").slice(0,60))+'</span>';
2271
+ h+='<span class="tool-chevron">&#x25BC;</span>';
2272
+ h+='</div>';
2273
+ h+='<div class="tool-detail">';
2274
+ if(tool.input) h+='<pre class="tool-input">'+esc(tool.input)+'</pre>';
2275
+ if(tool.output) h+='<pre class="tool-output">'+esc(tool.output.slice(0,2000))+'</pre>';
2276
+ h+='</div></div>';
2277
+ }
2278
+ }
2279
+
2280
+ // Text content (strip action blocks for display)
2281
+ var displayContent=stripActionBlocks(msg.content||(streaming?"":"..."));
2282
+ h+=renderMarkdown(displayContent);
2283
+
2284
+ // Action results
2285
+ if(msg.actions&&msg.actions.length>0){
2286
+ for(var ai=0;ai<msg.actions.length;ai++){
2287
+ h+=renderActionResult(msg.actions[ai]);
2288
+ }
2289
+ }
2290
+
2291
+ h+='</div></div>';
2292
+ return h;
2293
+ }).join("");
2294
+ container.scrollTop=container.scrollHeight;
2295
+ }
2296
+
2297
+ async function sendChat(){
2298
+ if(chatStreaming)return;
2299
+ var input=document.getElementById("chatInput");
2300
+ var message=input.value.trim();
2301
+ if(!message)return;
2302
+ input.value="";
2303
+ input.style.height="auto";
2304
+
2305
+ chatMessages.push({role:"user",content:message});
2306
+ chatMessages.push({role:"assistant",content:"",tools:[],actions:[]});
2307
+ chatStreaming=true;
2308
+ document.getElementById("chatSendBtn").disabled=true;
2309
+ renderChatMessages();
2310
+
2311
+ try{
2312
+ var model=document.getElementById("chatModel").value;
2313
+ var response=await fetch("/api/chat",{
2314
+ method:"POST",
2315
+ headers:{"Content-Type":"application/json"},
2316
+ body:JSON.stringify({message:message,model:model})
2317
+ });
2318
+ if(!response.ok){
2319
+ var err=await response.json().catch(function(){return{error:"Request failed"};});
2320
+ chatMessages[chatMessages.length-1].content="[Error: "+(err.error||"Unknown")+"]";
2321
+ renderChatMessages();
2322
+ chatStreaming=false;
2323
+ document.getElementById("chatSendBtn").disabled=false;
2324
+ return;
2325
+ }
2326
+ var reader=response.body.getReader();
2327
+ var decoder=new TextDecoder();
2328
+ var sseBuffer="";
2329
+ var currentMsg=chatMessages[chatMessages.length-1];
2330
+
2331
+ while(true){
2332
+ var result=await reader.read();
2333
+ if(result.done)break;
2334
+ sseBuffer+=decoder.decode(result.value,{stream:true});
2335
+ var sseLines=sseBuffer.split("\n");
2336
+ sseBuffer=sseLines.pop();
2337
+ for(var si=0;si<sseLines.length;si++){
2338
+ var sl=sseLines[si];
2339
+ if(!sl.startsWith("data: "))continue;
2340
+ try{
2341
+ var sd=JSON.parse(sl.slice(6));
2342
+
2343
+ if(sd.type==="chat"&&sd.chunk){
2344
+ currentMsg.content+=sd.chunk;
2345
+ renderChatMessages();
2346
+ }
2347
+ else if(sd.type==="chat_tool_use"){
2348
+ currentMsg.tools.push({name:sd.tool,input:sd.input,output:null,status:"running"});
2349
+ renderChatMessages();
2350
+ }
2351
+ else if(sd.type==="chat_tool_result"){
2352
+ var pendingTool=null;
2353
+ for(var pt=currentMsg.tools.length-1;pt>=0;pt--){
2354
+ if(currentMsg.tools[pt].status==="running"){pendingTool=currentMsg.tools[pt];break;}
2355
+ }
2356
+ if(pendingTool){pendingTool.output=sd.output;pendingTool.status="done";}
2357
+ renderChatMessages();
2358
+ }
2359
+ else if(sd.type==="chat_actions"){
2360
+ currentMsg.actions=sd.results||[];
2361
+ renderChatMessages();
2362
+ }
2363
+ else if(sd.type==="chat_session"){
2364
+ var badge=document.getElementById("chatSessionBadge");
2365
+ if(badge)badge.style.display="inline";
2366
+ }
2367
+ }catch(e){}
2368
+ }
2369
+ }
2370
+ }catch(e){
2371
+ chatMessages[chatMessages.length-1].content+="\n\n[Error: "+(e.message||"Connection failed")+"]";
2372
+ renderChatMessages();
2373
+ }
2374
+ chatStreaming=false;
2375
+ document.getElementById("chatSendBtn").disabled=false;
2376
+ saveChatHistory();
2377
+ input.focus();
2378
+ }
2379
+
2380
+ // ── Task Execution Tracking ──
2381
+ var currentExecId=null, execOutput={};
2382
+
2383
+ function onExecStart(data){
2384
+ currentExecId=data.taskId;
2385
+ execOutput[data.taskId]="";
2386
+ updateLive("Executing #"+data.taskId+": "+(data.subject||""));
2387
+ updateCliStatus();
2388
+ render();
2389
+ }
2390
+ function onExecChunk(data){
2391
+ if(execOutput[data.taskId]!==undefined) execOutput[data.taskId]+=data.chunk;
2392
+ var lines=(execOutput[data.taskId]||"").split("\n").filter(Boolean);
2393
+ if(lines.length>0) updateLive("Exec #"+data.taskId+": "+lines[lines.length-1].slice(0,80));
2394
+ }
2395
+ function onExecDone(data){
2396
+ currentExecId=null;
2397
+ delete execOutput[data.taskId];
2398
+ if(data.exitCode===0) updateLive("Completed #"+data.taskId);
2399
+ else if(data.stopped) updateLive("Stopped #"+data.taskId);
2400
+ else updateLive("Failed #"+data.taskId+" (exit "+data.exitCode+")");
2401
+ updateCliStatus();
2402
+ render();
2403
+ }
2404
+ function onExecError(data){
2405
+ // logged in live feed via chunk
2406
+ }
2407
+
2408
+ function approveTask(id){
2409
+ apiUpdate(id,{status:"completed"}).then(function(){updateLive("Approved #"+id);});
2410
+ }
2411
+ function rejectTask(id){
2412
+ apiUpdate(id,{status:"pending"}).then(function(){updateLive("Rejected #"+id+" — moved to To Do");});
2413
+ }
2414
+
2415
+ function stopExecution(){
2416
+ fetch("/api/exec/stop",{method:"POST"}).then(function(r){return r.json();}).then(function(data){
2417
+ if(data.stopped) updateLive("Execution stopped: #"+data.taskId);
2418
+ }).catch(function(){});
2419
+ }
2420
+
2421
+ function updateCliStatus(){
2422
+ var el=document.getElementById("cliStatus");
2423
+ if(!el)return;
2424
+ if(currentExecId){
2425
+ el.textContent="CLI: exec #"+currentExecId;
2426
+ el.style.color="var(--am)";
2427
+ }else{
2428
+ el.textContent="CLI: ready";
2429
+ el.style.color="var(--gn)";
2430
+ }
2431
+ }
2432
+
2433
+ function fetchCliStatus(){
2434
+ fetch("/api/cli-status").then(function(r){return r.json();}).then(function(data){
2435
+ var el=document.getElementById("cliStatus");
2436
+ if(!el)return;
2437
+ if(!data.available){
2438
+ el.textContent=data.nested?"CLI: nested":"CLI: unavailable";
2439
+ el.style.color="var(--rd)";
2440
+ document.getElementById("chatSendBtn").disabled=true;
2441
+ document.getElementById("chatInput").placeholder="CLI unavailable";
2442
+ }else if(data.executing){
2443
+ currentExecId=data.executing;
2444
+ el.textContent="CLI: exec #"+data.executing;
2445
+ el.style.color="var(--am)";
2446
+ }else{
2447
+ el.textContent="CLI: ready";
2448
+ el.style.color="var(--gn)";
2449
+ }
2450
+ }).catch(function(){});
2451
+ }
2452
+
2453
+ // Load activity on page load
2454
+ loadActivity();
2455
+ fetchCliStatus();
2456
+
2457
+ connectSSE();
2458
+ loadChatHistory();
2459
+ render();
2460
+ </script>
2461
+ </body>
2462
+ </html>