create-claude-kanban 3.0.0 → 3.2.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.
- package/dist/index.js +1066 -379
- package/package.json +1 -1
- package/templates/kanban.cjs +531 -242
- package/templates/kanban.html +805 -636
package/templates/kanban.html
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>Agent Kanban</title>
|
|
6
|
+
<title>APEX Agent Kanban</title>
|
|
7
7
|
<link href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css" rel="stylesheet">
|
|
8
8
|
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
|
9
9
|
<style>
|
|
10
|
-
:root {
|
|
10
|
+
:root, [data-theme="dark"] {
|
|
11
11
|
--bg: #09090B;
|
|
12
12
|
--s1: #111113;
|
|
13
13
|
--s2: #18181B;
|
|
@@ -25,7 +25,60 @@
|
|
|
25
25
|
--am: #F59E0B;
|
|
26
26
|
--vl: #8B5CF6;
|
|
27
27
|
--r: 3px;
|
|
28
|
-
|
|
28
|
+
--card-bg: #1A1A1E;
|
|
29
|
+
--card-hover: #222226;
|
|
30
|
+
}
|
|
31
|
+
[data-theme="navy"] {
|
|
32
|
+
--bg: #050A12;
|
|
33
|
+
--s1: #0B1220;
|
|
34
|
+
--s2: #101828;
|
|
35
|
+
--s3: #162035;
|
|
36
|
+
--b1: #1E2D45;
|
|
37
|
+
--b2: #2A3F5F;
|
|
38
|
+
--t1: #DDE5F0;
|
|
39
|
+
--t2: #8899B0;
|
|
40
|
+
--t3: #607088;
|
|
41
|
+
--t4: #455570;
|
|
42
|
+
--ac: #1A6FEF;
|
|
43
|
+
--ac2: #1558C0;
|
|
44
|
+
--gn: #22C55E;
|
|
45
|
+
--rd: #EF4444;
|
|
46
|
+
--am: #C8A24A;
|
|
47
|
+
--vl: #8B5CF6;
|
|
48
|
+
--card-bg: #0F1B30;
|
|
49
|
+
--card-hover: #142440;
|
|
50
|
+
}
|
|
51
|
+
[data-theme="light"] {
|
|
52
|
+
--bg: #F4F5F7;
|
|
53
|
+
--s1: #FFFFFF;
|
|
54
|
+
--s2: #F0F1F3;
|
|
55
|
+
--s3: #E8E9EC;
|
|
56
|
+
--b1: #D1D5DB;
|
|
57
|
+
--b2: #B0B7C3;
|
|
58
|
+
--t1: #111827;
|
|
59
|
+
--t2: #4B5563;
|
|
60
|
+
--t3: #6B7280;
|
|
61
|
+
--t4: #9CA3AF;
|
|
62
|
+
--ac: #2563EB;
|
|
63
|
+
--ac2: #1D4ED8;
|
|
64
|
+
--gn: #16A34A;
|
|
65
|
+
--rd: #DC2626;
|
|
66
|
+
--am: #D97706;
|
|
67
|
+
--vl: #7C3AED;
|
|
68
|
+
--card-bg: #FFFFFF;
|
|
69
|
+
--card-hover: #F0F1F3;
|
|
70
|
+
}
|
|
71
|
+
/* theme switcher */
|
|
72
|
+
.theme-switcher { display: flex; gap: 2px; margin-left: 12px; padding-left: 12px; border-left: 1px solid var(--b1); }
|
|
73
|
+
.theme-btn {
|
|
74
|
+
width: 22px; height: 22px; border-radius: 50%; border: 2px solid var(--b2);
|
|
75
|
+
cursor: pointer; transition: border-color 0.15s, transform 0.15s; padding: 0;
|
|
76
|
+
}
|
|
77
|
+
.theme-btn:hover { transform: scale(1.15); }
|
|
78
|
+
.theme-btn.active { border-color: var(--ac); box-shadow: 0 0 0 2px var(--ac); }
|
|
79
|
+
.theme-btn[data-t="dark"] { background: #09090B; }
|
|
80
|
+
.theme-btn[data-t="navy"] { background: #0B1220; }
|
|
81
|
+
.theme-btn[data-t="light"] { background: #F4F5F7; }
|
|
29
82
|
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
|
|
30
83
|
body {
|
|
31
84
|
font-family: 'Pretendard Variable', Pretendard, -apple-system, sans-serif;
|
|
@@ -88,16 +141,18 @@
|
|
|
88
141
|
|
|
89
142
|
/* ── Toolbar ── */
|
|
90
143
|
.toolbar {
|
|
91
|
-
height:
|
|
92
|
-
|
|
144
|
+
height: auto;
|
|
145
|
+
min-height: 36px;
|
|
146
|
+
padding: 4px 24px;
|
|
93
147
|
display: flex;
|
|
94
148
|
align-items: center;
|
|
95
|
-
|
|
149
|
+
flex-wrap: wrap;
|
|
150
|
+
gap: 2px;
|
|
96
151
|
border-bottom: 1px solid var(--b1);
|
|
97
152
|
background: var(--s1);
|
|
98
153
|
}
|
|
99
154
|
.toolbar button {
|
|
100
|
-
padding:
|
|
155
|
+
padding: 4px 10px;
|
|
101
156
|
border: none;
|
|
102
157
|
background: transparent;
|
|
103
158
|
cursor: pointer;
|
|
@@ -223,6 +278,19 @@
|
|
|
223
278
|
align-items: center; justify-content: center; border-radius: var(--r);
|
|
224
279
|
}
|
|
225
280
|
.agent-panel .ap-close:hover { color: var(--t1); background: var(--s3); }
|
|
281
|
+
.ap-project-filter {
|
|
282
|
+
display: flex; gap: 0; padding: 0 12px;
|
|
283
|
+
border-bottom: 1px solid var(--b1); flex-shrink: 0;
|
|
284
|
+
overflow-x: auto;
|
|
285
|
+
}
|
|
286
|
+
.ap-project-btn {
|
|
287
|
+
padding: 6px 12px; font-size: 11px; font-weight: 500;
|
|
288
|
+
color: var(--t3); cursor: pointer; white-space: nowrap;
|
|
289
|
+
border: none; border-bottom: 2px solid transparent;
|
|
290
|
+
background: none; font-family: inherit;
|
|
291
|
+
}
|
|
292
|
+
.ap-project-btn:hover { color: var(--t1); }
|
|
293
|
+
.ap-project-btn.active { color: var(--t1); border-bottom-color: var(--ac); }
|
|
226
294
|
.agent-tabs {
|
|
227
295
|
display: flex; gap: 0; border-bottom: 1px solid var(--b1);
|
|
228
296
|
overflow-x: auto; flex-shrink: 0; padding: 0 12px;
|
|
@@ -240,6 +308,27 @@
|
|
|
240
308
|
font-size: 9px; color: var(--t4); margin-left: 4px;
|
|
241
309
|
padding: 1px 4px; background: var(--s3); border-radius: 2px;
|
|
242
310
|
}
|
|
311
|
+
/* Pipeline agent tabs — vertical card layout with role summary */
|
|
312
|
+
.agent-tabs:has(.agent-tab-pipeline) {
|
|
313
|
+
flex-direction: column; overflow-x: hidden; overflow-y: auto;
|
|
314
|
+
gap: 2px; padding: 8px 12px; max-height: 320px;
|
|
315
|
+
}
|
|
316
|
+
.agent-tab-pipeline {
|
|
317
|
+
display: flex; flex-wrap: wrap; align-items: center; gap: 4px;
|
|
318
|
+
white-space: normal; text-align: left; padding: 6px 10px;
|
|
319
|
+
border-bottom: none; border-left: 2px solid transparent;
|
|
320
|
+
border-radius: 4px;
|
|
321
|
+
}
|
|
322
|
+
.agent-tab-pipeline:hover { background: var(--s2); }
|
|
323
|
+
.agent-tab-pipeline.active { border-left-color: var(--ac); background: var(--s2); color: var(--t1); border-bottom: none; }
|
|
324
|
+
.agent-tab-order {
|
|
325
|
+
font-size: 9px; font-weight: 700; color: var(--ac); min-width: 18px;
|
|
326
|
+
}
|
|
327
|
+
.agent-tab-name { font-weight: 600; }
|
|
328
|
+
.agent-tab-role {
|
|
329
|
+
display: block; width: 100%; font-size: 10px; color: var(--t4);
|
|
330
|
+
line-height: 1.3; margin-top: 1px; padding-left: 22px;
|
|
331
|
+
}
|
|
243
332
|
.agent-prompt-view {
|
|
244
333
|
flex: 1; overflow-y: auto; padding: 20px;
|
|
245
334
|
}
|
|
@@ -269,9 +358,11 @@
|
|
|
269
358
|
.board {
|
|
270
359
|
display: grid;
|
|
271
360
|
grid-template-columns: repeat(4, 1fr);
|
|
361
|
+
grid-template-rows: 1fr;
|
|
272
362
|
gap: 1px;
|
|
273
363
|
padding: 0;
|
|
274
|
-
|
|
364
|
+
height: calc(100vh - var(--board-top, 168px));
|
|
365
|
+
overflow: hidden;
|
|
275
366
|
background: var(--b1);
|
|
276
367
|
}
|
|
277
368
|
@media(max-width: 900px) { .board { grid-template-columns: repeat(2, 1fr); } }
|
|
@@ -279,7 +370,11 @@
|
|
|
279
370
|
.column {
|
|
280
371
|
background: var(--bg);
|
|
281
372
|
padding: 0;
|
|
282
|
-
min-height:
|
|
373
|
+
min-height: 0;
|
|
374
|
+
height: 100%;
|
|
375
|
+
display: flex;
|
|
376
|
+
flex-direction: column;
|
|
377
|
+
overflow: hidden;
|
|
283
378
|
transition: background 0.1s;
|
|
284
379
|
}
|
|
285
380
|
.column.drag-over { background: var(--s1); }
|
|
@@ -288,8 +383,7 @@
|
|
|
288
383
|
align-items: center;
|
|
289
384
|
justify-content: space-between;
|
|
290
385
|
padding: 12px 16px 10px;
|
|
291
|
-
|
|
292
|
-
top: 0;
|
|
386
|
+
flex-shrink: 0;
|
|
293
387
|
background: var(--bg);
|
|
294
388
|
z-index: 2;
|
|
295
389
|
}
|
|
@@ -317,21 +411,21 @@
|
|
|
317
411
|
padding: 1px 6px;
|
|
318
412
|
border-radius: var(--r);
|
|
319
413
|
}
|
|
320
|
-
.cards { display: flex; flex-direction: column; gap: 1px; padding: 0 8px 8px; }
|
|
414
|
+
.cards { display: flex; flex-direction: column; gap: 1px; padding: 0 8px 8px; flex: 1; min-height: 0; overflow-y: auto; }
|
|
321
415
|
|
|
322
416
|
/* ── Card ── */
|
|
323
417
|
.card {
|
|
324
|
-
background: var(--
|
|
418
|
+
background: var(--card-bg, var(--s2));
|
|
325
419
|
border-radius: var(--r);
|
|
326
420
|
padding: 10px 12px;
|
|
327
|
-
border-left: 2px solid var(--
|
|
421
|
+
border-left: 2px solid var(--b2);
|
|
328
422
|
transition: background 0.1s, border-color 0.1s;
|
|
329
423
|
cursor: grab;
|
|
330
424
|
position: relative;
|
|
331
425
|
}
|
|
332
|
-
.card:hover { background: var(--
|
|
426
|
+
.card:hover { background: var(--card-hover, var(--s3)); border-left-color: var(--t3); }
|
|
333
427
|
.card.dragging { opacity: 0.3; }
|
|
334
|
-
.card.blocked { opacity: 0.
|
|
428
|
+
.card.blocked { opacity: 0.55; cursor: default; }
|
|
335
429
|
.card.readonly { cursor: default; }
|
|
336
430
|
.card .top {
|
|
337
431
|
display: flex;
|
|
@@ -342,7 +436,7 @@
|
|
|
342
436
|
.card .id {
|
|
343
437
|
font-size: 10px;
|
|
344
438
|
font-family: 'JetBrains Mono', monospace;
|
|
345
|
-
color: var(--
|
|
439
|
+
color: var(--t3);
|
|
346
440
|
}
|
|
347
441
|
.card .badge {
|
|
348
442
|
font-size: 9px;
|
|
@@ -402,13 +496,13 @@
|
|
|
402
496
|
.card .deps {
|
|
403
497
|
font-size: 9px;
|
|
404
498
|
font-family: 'JetBrains Mono', monospace;
|
|
405
|
-
color: var(--
|
|
499
|
+
color: var(--t3);
|
|
406
500
|
margin-top: 4px;
|
|
407
501
|
}
|
|
408
502
|
.card .time {
|
|
409
503
|
font-size: 9px;
|
|
410
504
|
font-family: 'JetBrains Mono', monospace;
|
|
411
|
-
color: var(--
|
|
505
|
+
color: var(--t3);
|
|
412
506
|
}
|
|
413
507
|
.card .session {
|
|
414
508
|
font-size: 9px;
|
|
@@ -431,13 +525,102 @@
|
|
|
431
525
|
|
|
432
526
|
.card .desc {
|
|
433
527
|
font-size: 11px;
|
|
434
|
-
color: var(--
|
|
528
|
+
color: var(--t2);
|
|
435
529
|
margin-top: 6px;
|
|
436
530
|
padding-top: 6px;
|
|
437
531
|
border-top: 1px solid var(--b1);
|
|
438
|
-
line-height: 1.
|
|
532
|
+
line-height: 1.6;
|
|
439
533
|
overflow: hidden;
|
|
440
534
|
}
|
|
535
|
+
/* ── Card detail sections ── */
|
|
536
|
+
.card-section {
|
|
537
|
+
margin-top: 8px;
|
|
538
|
+
padding: 8px 10px;
|
|
539
|
+
background: var(--s1);
|
|
540
|
+
border: 1px solid var(--b1);
|
|
541
|
+
border-radius: var(--r);
|
|
542
|
+
}
|
|
543
|
+
.card-section-label {
|
|
544
|
+
font-size: 9px;
|
|
545
|
+
font-weight: 700;
|
|
546
|
+
text-transform: uppercase;
|
|
547
|
+
letter-spacing: 0.06em;
|
|
548
|
+
color: var(--t3);
|
|
549
|
+
margin-bottom: 6px;
|
|
550
|
+
display: flex;
|
|
551
|
+
align-items: center;
|
|
552
|
+
gap: 5px;
|
|
553
|
+
}
|
|
554
|
+
.card-section-label .dot {
|
|
555
|
+
width: 5px; height: 5px;
|
|
556
|
+
border-radius: 50%;
|
|
557
|
+
display: inline-block;
|
|
558
|
+
}
|
|
559
|
+
.card-section-body {
|
|
560
|
+
font-size: 11px;
|
|
561
|
+
color: var(--t2);
|
|
562
|
+
line-height: 1.65;
|
|
563
|
+
}
|
|
564
|
+
.card-section-body ul {
|
|
565
|
+
margin: 0; padding-left: 16px;
|
|
566
|
+
list-style: none;
|
|
567
|
+
}
|
|
568
|
+
.card-section-body ul li {
|
|
569
|
+
position: relative;
|
|
570
|
+
padding-left: 2px;
|
|
571
|
+
margin-bottom: 3px;
|
|
572
|
+
}
|
|
573
|
+
.card-section-body ul li::before {
|
|
574
|
+
content: "▸";
|
|
575
|
+
position: absolute;
|
|
576
|
+
left: -14px;
|
|
577
|
+
color: var(--t4);
|
|
578
|
+
font-size: 9px;
|
|
579
|
+
top: 1px;
|
|
580
|
+
}
|
|
581
|
+
.card-section-body .line-item {
|
|
582
|
+
padding: 2px 0;
|
|
583
|
+
border-bottom: 1px solid var(--b1);
|
|
584
|
+
}
|
|
585
|
+
.card-section-body .line-item:last-child { border-bottom: none; }
|
|
586
|
+
.card-dep-chip {
|
|
587
|
+
display: inline-flex;
|
|
588
|
+
align-items: center;
|
|
589
|
+
gap: 4px;
|
|
590
|
+
font-size: 10px;
|
|
591
|
+
font-family: 'JetBrains Mono', monospace;
|
|
592
|
+
padding: 2px 8px;
|
|
593
|
+
border-radius: 2px;
|
|
594
|
+
background: var(--s3);
|
|
595
|
+
color: var(--t2);
|
|
596
|
+
margin: 2px 2px 2px 0;
|
|
597
|
+
cursor: pointer;
|
|
598
|
+
}
|
|
599
|
+
.card-dep-chip:hover { background: var(--b1); color: var(--t1); }
|
|
600
|
+
.card-dep-chip .dep-dot {
|
|
601
|
+
width: 5px; height: 5px;
|
|
602
|
+
border-radius: 50%;
|
|
603
|
+
display: inline-block;
|
|
604
|
+
}
|
|
605
|
+
.card-timeline {
|
|
606
|
+
display: flex;
|
|
607
|
+
flex-wrap: wrap;
|
|
608
|
+
gap: 6px 16px;
|
|
609
|
+
font-size: 9px;
|
|
610
|
+
font-family: 'JetBrains Mono', monospace;
|
|
611
|
+
color: var(--t3);
|
|
612
|
+
margin-top: 6px;
|
|
613
|
+
}
|
|
614
|
+
.card-timeline .tl-label { color: var(--t4); margin-right: 3px; }
|
|
615
|
+
.card-report-summary {
|
|
616
|
+
font-size: 11px;
|
|
617
|
+
color: var(--t2);
|
|
618
|
+
line-height: 1.6;
|
|
619
|
+
white-space: pre-wrap;
|
|
620
|
+
word-break: break-word;
|
|
621
|
+
}
|
|
622
|
+
.card-report-summary strong,
|
|
623
|
+
.card-report-summary b { color: var(--t1); font-weight: 600; }
|
|
441
624
|
.card .card-actions {
|
|
442
625
|
position: absolute;
|
|
443
626
|
top: 8px;
|
|
@@ -1060,6 +1243,174 @@
|
|
|
1060
1243
|
.act-type.completed { color: var(--gn); }
|
|
1061
1244
|
.act-type.updated { color: var(--t3); }
|
|
1062
1245
|
.act-type.deleted { color: var(--rd); }
|
|
1246
|
+
.act-type.archived { color: var(--vl); }
|
|
1247
|
+
|
|
1248
|
+
/* ── Archive Panel ── */
|
|
1249
|
+
.archive-panel {
|
|
1250
|
+
position: fixed;
|
|
1251
|
+
top: 0;
|
|
1252
|
+
right: -480px;
|
|
1253
|
+
width: 480px;
|
|
1254
|
+
max-width: 100vw;
|
|
1255
|
+
height: 100vh;
|
|
1256
|
+
background: var(--s1);
|
|
1257
|
+
border-left: 1px solid var(--b1);
|
|
1258
|
+
z-index: 310;
|
|
1259
|
+
transition: right 0.2s ease;
|
|
1260
|
+
display: flex;
|
|
1261
|
+
flex-direction: column;
|
|
1262
|
+
}
|
|
1263
|
+
.archive-panel.open { right: 0; }
|
|
1264
|
+
.archive-panel .arch-header {
|
|
1265
|
+
padding: 14px 20px;
|
|
1266
|
+
border-bottom: 1px solid var(--b1);
|
|
1267
|
+
display: flex;
|
|
1268
|
+
align-items: center;
|
|
1269
|
+
justify-content: space-between;
|
|
1270
|
+
flex-shrink: 0;
|
|
1271
|
+
}
|
|
1272
|
+
.archive-panel .arch-header h3 {
|
|
1273
|
+
font-size: 13px;
|
|
1274
|
+
font-weight: 600;
|
|
1275
|
+
display: flex;
|
|
1276
|
+
align-items: center;
|
|
1277
|
+
gap: 8px;
|
|
1278
|
+
}
|
|
1279
|
+
.archive-panel .arch-close {
|
|
1280
|
+
background: none;
|
|
1281
|
+
border: 1px solid var(--b1);
|
|
1282
|
+
border-radius: var(--r);
|
|
1283
|
+
color: var(--t3);
|
|
1284
|
+
cursor: pointer;
|
|
1285
|
+
font-size: 14px;
|
|
1286
|
+
width: 28px; height: 28px;
|
|
1287
|
+
display: flex; align-items: center; justify-content: center;
|
|
1288
|
+
}
|
|
1289
|
+
.archive-panel .arch-close:hover { color: var(--t1); background: var(--s3); }
|
|
1290
|
+
.arch-dates {
|
|
1291
|
+
display: flex;
|
|
1292
|
+
gap: 4px;
|
|
1293
|
+
padding: 10px 20px;
|
|
1294
|
+
border-bottom: 1px solid var(--b1);
|
|
1295
|
+
overflow-x: auto;
|
|
1296
|
+
flex-shrink: 0;
|
|
1297
|
+
}
|
|
1298
|
+
.arch-date-btn {
|
|
1299
|
+
padding: 5px 12px;
|
|
1300
|
+
border: 1px solid var(--b1);
|
|
1301
|
+
background: var(--s2);
|
|
1302
|
+
color: var(--t3);
|
|
1303
|
+
font-size: 11px;
|
|
1304
|
+
font-weight: 500;
|
|
1305
|
+
font-family: inherit;
|
|
1306
|
+
cursor: pointer;
|
|
1307
|
+
border-radius: var(--r);
|
|
1308
|
+
white-space: nowrap;
|
|
1309
|
+
transition: all 0.15s;
|
|
1310
|
+
}
|
|
1311
|
+
.arch-date-btn:hover { color: var(--t1); border-color: var(--b2); }
|
|
1312
|
+
.arch-date-btn.active { background: var(--ac); color: #fff; border-color: var(--ac); }
|
|
1313
|
+
.arch-date-btn .arch-count {
|
|
1314
|
+
font-size: 9px;
|
|
1315
|
+
font-family: 'JetBrains Mono', monospace;
|
|
1316
|
+
margin-left: 4px;
|
|
1317
|
+
opacity: 0.7;
|
|
1318
|
+
}
|
|
1319
|
+
.arch-content {
|
|
1320
|
+
flex: 1;
|
|
1321
|
+
overflow-y: auto;
|
|
1322
|
+
padding: 12px 20px;
|
|
1323
|
+
}
|
|
1324
|
+
.arch-empty {
|
|
1325
|
+
text-align: center;
|
|
1326
|
+
padding: 40px 20px;
|
|
1327
|
+
color: var(--t4);
|
|
1328
|
+
font-size: 12px;
|
|
1329
|
+
}
|
|
1330
|
+
.arch-stats {
|
|
1331
|
+
font-size: 11px;
|
|
1332
|
+
color: var(--t4);
|
|
1333
|
+
margin-bottom: 12px;
|
|
1334
|
+
padding-bottom: 8px;
|
|
1335
|
+
border-bottom: 1px solid var(--b1);
|
|
1336
|
+
}
|
|
1337
|
+
.arch-task {
|
|
1338
|
+
padding: 10px 12px;
|
|
1339
|
+
border: 1px solid var(--b1);
|
|
1340
|
+
border-radius: var(--r);
|
|
1341
|
+
margin-bottom: 6px;
|
|
1342
|
+
background: var(--s2);
|
|
1343
|
+
transition: background 0.15s;
|
|
1344
|
+
}
|
|
1345
|
+
.arch-task:hover { background: var(--s3); }
|
|
1346
|
+
.arch-task.parent { border-left: 3px solid var(--ac); }
|
|
1347
|
+
.arch-task.child { margin-left: 20px; border-left: 2px dashed var(--b2); }
|
|
1348
|
+
.arch-task.orphan { border-left: 2px dashed var(--am); }
|
|
1349
|
+
.arch-task-header {
|
|
1350
|
+
display: flex;
|
|
1351
|
+
align-items: center;
|
|
1352
|
+
gap: 6px;
|
|
1353
|
+
margin-bottom: 3px;
|
|
1354
|
+
}
|
|
1355
|
+
.arch-task-id {
|
|
1356
|
+
font-size: 10px;
|
|
1357
|
+
font-family: 'JetBrains Mono', monospace;
|
|
1358
|
+
color: var(--ac);
|
|
1359
|
+
font-weight: 600;
|
|
1360
|
+
}
|
|
1361
|
+
.arch-task-subject {
|
|
1362
|
+
font-size: 12px;
|
|
1363
|
+
font-weight: 600;
|
|
1364
|
+
color: var(--t1);
|
|
1365
|
+
flex: 1;
|
|
1366
|
+
overflow: hidden;
|
|
1367
|
+
text-overflow: ellipsis;
|
|
1368
|
+
white-space: nowrap;
|
|
1369
|
+
}
|
|
1370
|
+
.arch-task-priority {
|
|
1371
|
+
font-size: 9px;
|
|
1372
|
+
font-weight: 600;
|
|
1373
|
+
padding: 1px 6px;
|
|
1374
|
+
border-radius: 2px;
|
|
1375
|
+
text-transform: uppercase;
|
|
1376
|
+
font-family: 'JetBrains Mono', monospace;
|
|
1377
|
+
}
|
|
1378
|
+
.arch-task-priority.high { background: rgba(239,68,68,0.15); color: var(--rd); }
|
|
1379
|
+
.arch-task-priority.critical { background: rgba(239,68,68,0.25); color: var(--rd); }
|
|
1380
|
+
.arch-task-meta {
|
|
1381
|
+
display: flex;
|
|
1382
|
+
gap: 8px;
|
|
1383
|
+
font-size: 10px;
|
|
1384
|
+
color: var(--t4);
|
|
1385
|
+
margin-top: 4px;
|
|
1386
|
+
flex-wrap: wrap;
|
|
1387
|
+
}
|
|
1388
|
+
.arch-task-meta span { display: flex; align-items: center; gap: 3px; }
|
|
1389
|
+
.arch-task-desc {
|
|
1390
|
+
font-size: 11px;
|
|
1391
|
+
color: var(--t3);
|
|
1392
|
+
margin-top: 4px;
|
|
1393
|
+
overflow: hidden;
|
|
1394
|
+
text-overflow: ellipsis;
|
|
1395
|
+
white-space: nowrap;
|
|
1396
|
+
}
|
|
1397
|
+
.arch-task-parent-ref {
|
|
1398
|
+
font-size: 9px;
|
|
1399
|
+
color: var(--t4);
|
|
1400
|
+
font-family: 'JetBrains Mono', monospace;
|
|
1401
|
+
}
|
|
1402
|
+
.arch-section-label {
|
|
1403
|
+
font-size: 10px;
|
|
1404
|
+
font-weight: 600;
|
|
1405
|
+
color: var(--t4);
|
|
1406
|
+
text-transform: uppercase;
|
|
1407
|
+
letter-spacing: 0.5px;
|
|
1408
|
+
margin: 16px 0 8px;
|
|
1409
|
+
padding-bottom: 4px;
|
|
1410
|
+
border-bottom: 1px solid var(--b1);
|
|
1411
|
+
}
|
|
1412
|
+
.archive-overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.4); z-index: 260; }
|
|
1413
|
+
.archive-overlay.open { display: block; }
|
|
1063
1414
|
|
|
1064
1415
|
/* Activity toolbar button */
|
|
1065
1416
|
.activity-btn {
|
|
@@ -1481,175 +1832,27 @@
|
|
|
1481
1832
|
border-radius: 50%;
|
|
1482
1833
|
animation: spin 0.8s linear infinite;
|
|
1483
1834
|
}
|
|
1484
|
-
/* v3: Stale/Zombie */
|
|
1485
|
-
.stale-badge {
|
|
1486
|
-
font-size: 10px; font-weight: 700; color: var(--rd);
|
|
1487
|
-
font-family: 'JetBrains Mono', monospace;
|
|
1488
|
-
margin-top: 4px; padding: 2px 6px;
|
|
1489
|
-
background: rgba(239,68,68,0.1); border-radius: 2px;
|
|
1490
|
-
display: inline-flex; align-items: center; gap: 4px;
|
|
1491
|
-
animation: stalePulse 2s infinite;
|
|
1492
|
-
}
|
|
1493
|
-
.stale-badge .stale-reason { font-weight: 400; font-size: 9px; color: var(--t4); }
|
|
1494
|
-
@keyframes stalePulse { 0%,100%{opacity:1}50%{opacity:0.5} }
|
|
1495
|
-
.stale-actions { display: flex; gap: 6px; margin-top: 8px; }
|
|
1496
|
-
|
|
1497
|
-
/* v3: Agent Dashboard Panel */
|
|
1498
|
-
.agent-dash-overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.5); z-index: 500; }
|
|
1499
|
-
.agent-dash-overlay.open { display: block; }
|
|
1500
|
-
.agent-dash {
|
|
1501
|
-
position: fixed; top: 0; right: -640px; width: 640px; max-width: 100vw; height: 100vh;
|
|
1502
|
-
background: var(--s1); border-left: 1px solid var(--b1); z-index: 501;
|
|
1503
|
-
transition: right 0.25s ease; display: flex; flex-direction: column;
|
|
1504
|
-
}
|
|
1505
|
-
.agent-dash.open { right: 0; }
|
|
1506
|
-
.agent-dash .ad-header {
|
|
1507
|
-
padding: 14px 20px; border-bottom: 1px solid var(--b1);
|
|
1508
|
-
display: flex; align-items: center; justify-content: space-between; flex-shrink: 0;
|
|
1509
|
-
}
|
|
1510
|
-
.agent-dash .ad-header h3 { font-size: 14px; font-weight: 700; }
|
|
1511
|
-
.agent-dash .ad-close {
|
|
1512
|
-
background: none; border: 1px solid var(--b1); border-radius: var(--r);
|
|
1513
|
-
color: var(--t3); cursor: pointer; font-size: 14px;
|
|
1514
|
-
width: 28px; height: 28px; display: flex; align-items: center; justify-content: center;
|
|
1515
|
-
}
|
|
1516
|
-
.agent-dash .ad-close:hover { color: var(--t1); background: var(--s3); }
|
|
1517
|
-
.agent-dash .ad-body { flex: 1; overflow-y: auto; padding: 16px 20px; }
|
|
1518
|
-
.ad-card {
|
|
1519
|
-
background: var(--s2); border: 1px solid var(--b1); border-radius: 4px;
|
|
1520
|
-
padding: 14px 16px; margin-bottom: 10px;
|
|
1521
|
-
}
|
|
1522
|
-
.ad-card-header { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
|
|
1523
|
-
.ad-card-header .ad-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
|
|
1524
|
-
.ad-card-header .ad-name { font-size: 13px; font-weight: 600; color: var(--t1); }
|
|
1525
|
-
.ad-card-header .ad-status {
|
|
1526
|
-
font-size: 10px; font-family: 'JetBrains Mono', monospace; padding: 2px 8px;
|
|
1527
|
-
border-radius: 2px; font-weight: 600; margin-left: auto;
|
|
1528
|
-
}
|
|
1529
|
-
.ad-status-idle { background: var(--s3); color: var(--t3); }
|
|
1530
|
-
.ad-status-working { background: rgba(59,130,246,0.15); color: var(--ac); }
|
|
1531
|
-
.ad-status-stale { background: rgba(239,68,68,0.1); color: var(--rd); }
|
|
1532
|
-
.ad-stats {
|
|
1533
|
-
display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px;
|
|
1534
|
-
font-size: 11px; font-family: 'JetBrains Mono', monospace;
|
|
1535
|
-
}
|
|
1536
|
-
.ad-stat { text-align: center; }
|
|
1537
|
-
.ad-stat .ad-num { font-size: 16px; font-weight: 700; }
|
|
1538
|
-
.ad-stat .ad-lbl { font-size: 9px; color: var(--t4); text-transform: uppercase; letter-spacing: 0.04em; }
|
|
1539
|
-
.ad-current {
|
|
1540
|
-
font-size: 11px; color: var(--t2); margin-top: 8px;
|
|
1541
|
-
padding-top: 8px; border-top: 1px solid var(--b1);
|
|
1542
|
-
}
|
|
1543
|
-
.ad-current strong { color: var(--t1); }
|
|
1544
|
-
|
|
1545
|
-
/* v3: Filter Bar */
|
|
1546
|
-
.filter-bar {
|
|
1547
|
-
display: flex; align-items: center; gap: 8px; padding: 8px 16px;
|
|
1548
|
-
background: var(--s1); border-bottom: 1px solid var(--b1);
|
|
1549
|
-
}
|
|
1550
|
-
.filter-bar select, .filter-bar input {
|
|
1551
|
-
background: var(--bg); border: 1px solid var(--b1); border-radius: var(--r);
|
|
1552
|
-
padding: 5px 8px; color: var(--t1); font-size: 11px; font-family: inherit;
|
|
1553
|
-
}
|
|
1554
|
-
.filter-bar select { cursor: pointer; }
|
|
1555
|
-
.filter-bar input { width: 180px; }
|
|
1556
|
-
.filter-bar input::placeholder { color: var(--t4); }
|
|
1557
|
-
.filter-bar label {
|
|
1558
|
-
font-size: 10px; color: var(--t3); display: inline-flex; align-items: center; gap: 2px; cursor: pointer;
|
|
1559
|
-
font-family: 'JetBrains Mono', monospace; white-space: nowrap;
|
|
1560
|
-
}
|
|
1561
|
-
.filter-bar label input[type="checkbox"] { accent-color: var(--ac); margin: 0; width: 12px; height: 12px; }
|
|
1562
|
-
.filter-active { border-color: var(--ac) !important; }
|
|
1563
|
-
.card.filter-hidden { display: none; }
|
|
1564
|
-
|
|
1565
|
-
/* v3: Metrics Panel */
|
|
1566
|
-
.metrics-overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.55); z-index: 400; backdrop-filter: blur(3px); }
|
|
1567
|
-
.metrics-overlay.open { display: block; }
|
|
1568
|
-
.metrics-popup {
|
|
1569
|
-
display: none; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
|
|
1570
|
-
width: 720px; max-width: 94vw; max-height: 85vh;
|
|
1571
|
-
background: var(--s1); border: 1px solid var(--b1); border-radius: 6px;
|
|
1572
|
-
z-index: 410; flex-direction: column; box-shadow: 0 20px 60px rgba(0,0,0,0.5);
|
|
1573
|
-
}
|
|
1574
|
-
.metrics-popup.open { display: flex; }
|
|
1575
|
-
.metrics-header {
|
|
1576
|
-
padding: 16px 20px; border-bottom: 1px solid var(--b1);
|
|
1577
|
-
display: flex; align-items: center; justify-content: space-between; flex-shrink: 0;
|
|
1578
|
-
}
|
|
1579
|
-
.metrics-header h2 { font-size: 15px; font-weight: 700; }
|
|
1580
|
-
.metrics-body { flex: 1; overflow-y: auto; padding: 20px; }
|
|
1581
|
-
.m-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; margin-bottom: 20px; }
|
|
1582
|
-
.m-card {
|
|
1583
|
-
background: var(--s2); border: 1px solid var(--b1); border-radius: 4px;
|
|
1584
|
-
padding: 14px; text-align: center;
|
|
1585
|
-
}
|
|
1586
|
-
.m-card .m-num { font-size: 24px; font-weight: 700; font-family: 'JetBrains Mono', monospace; }
|
|
1587
|
-
.m-card .m-lbl { font-size: 10px; color: var(--t4); text-transform: uppercase; letter-spacing: 0.04em; margin-top: 2px; }
|
|
1588
|
-
.m-section { margin-bottom: 20px; }
|
|
1589
|
-
.m-section-title { font-size: 12px; font-weight: 600; color: var(--t2); margin-bottom: 10px; text-transform: uppercase; letter-spacing: 0.04em; }
|
|
1590
|
-
.m-bar-chart { display: flex; flex-direction: column; gap: 6px; }
|
|
1591
|
-
.m-bar-row { display: flex; align-items: center; gap: 8px; }
|
|
1592
|
-
.m-bar-label { font-size: 11px; font-family: 'JetBrains Mono', monospace; color: var(--t3); width: 80px; text-align: right; flex-shrink: 0; }
|
|
1593
|
-
.m-bar-track { flex: 1; height: 16px; background: var(--s3); border-radius: 2px; overflow: hidden; position: relative; }
|
|
1594
|
-
.m-bar-fill { height: 100%; border-radius: 2px; transition: width 0.3s; }
|
|
1595
|
-
.m-bar-val { font-size: 10px; font-family: 'JetBrains Mono', monospace; color: var(--t4); width: 30px; }
|
|
1596
|
-
.m-burndown { width: 100%; height: 120px; position: relative; }
|
|
1597
|
-
.m-burndown svg { width: 100%; height: 100%; }
|
|
1598
|
-
.m-progress-ring {
|
|
1599
|
-
width: 100px; height: 100px; margin: 0 auto 10px;
|
|
1600
|
-
}
|
|
1601
|
-
|
|
1602
|
-
/* v3: Dependency Graph */
|
|
1603
|
-
.dep-overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.55); z-index: 400; backdrop-filter: blur(3px); }
|
|
1604
|
-
.dep-overlay.open { display: block; }
|
|
1605
|
-
.dep-popup {
|
|
1606
|
-
display: none; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
|
|
1607
|
-
width: 860px; max-width: 96vw; max-height: 90vh;
|
|
1608
|
-
background: var(--s1); border: 1px solid var(--b1); border-radius: 6px;
|
|
1609
|
-
z-index: 410; flex-direction: column; box-shadow: 0 20px 60px rgba(0,0,0,0.5);
|
|
1610
|
-
}
|
|
1611
|
-
.dep-popup.open { display: flex; }
|
|
1612
|
-
.dep-header { padding: 14px 20px; border-bottom: 1px solid var(--b1); display: flex; align-items: center; justify-content: space-between; flex-shrink: 0; }
|
|
1613
|
-
.dep-header h2 { font-size: 15px; font-weight: 700; }
|
|
1614
|
-
.dep-body { flex: 1; overflow: auto; padding: 20px; }
|
|
1615
|
-
.dep-graph { position: relative; min-height: 200px; }
|
|
1616
|
-
.dep-node {
|
|
1617
|
-
display: inline-flex; align-items: center; gap: 6px;
|
|
1618
|
-
padding: 6px 10px; margin: 4px;
|
|
1619
|
-
background: var(--s2); border: 1px solid var(--b1); border-radius: 4px;
|
|
1620
|
-
font-size: 11px; cursor: pointer; transition: all 0.15s;
|
|
1621
|
-
}
|
|
1622
|
-
.dep-node:hover { border-color: var(--ac); background: var(--s3); }
|
|
1623
|
-
.dep-node .dn-id { font-family: 'JetBrains Mono', monospace; color: var(--t3); font-weight: 600; }
|
|
1624
|
-
.dep-node .dn-title { color: var(--t2); max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1625
|
-
.dep-node .dn-dot { width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0; }
|
|
1626
|
-
.dep-node[data-s="completed"] { opacity: 0.5; }
|
|
1627
|
-
.dep-node[data-s="completed"] .dn-dot { background: var(--gn); }
|
|
1628
|
-
.dep-node[data-s="in_progress"] .dn-dot { background: var(--ac); }
|
|
1629
|
-
.dep-node[data-s="in_review"] .dn-dot { background: var(--vl); }
|
|
1630
|
-
.dep-node[data-s="pending"] .dn-dot { background: var(--t4); }
|
|
1631
|
-
.dep-node.critical { border-color: var(--rd); box-shadow: 0 0 0 1px rgba(239,68,68,0.3); }
|
|
1632
|
-
.dep-tree { padding-left: 0; list-style: none; }
|
|
1633
|
-
.dep-tree ul { padding-left: 24px; list-style: none; border-left: 1px solid var(--b1); margin-left: 8px; }
|
|
1634
|
-
.dep-tree li { padding: 3px 0; }
|
|
1635
|
-
.dep-arrow { color: var(--t4); font-size: 10px; margin: 0 4px; }
|
|
1636
|
-
.dep-legend { display: flex; gap: 16px; margin-bottom: 16px; font-size: 10px; color: var(--t3); }
|
|
1637
|
-
.dep-legend span { display: flex; align-items: center; gap: 4px; }
|
|
1638
|
-
.dep-legend .dl-dot { width: 8px; height: 8px; border-radius: 50%; }
|
|
1639
1835
|
</style>
|
|
1640
1836
|
</head>
|
|
1641
1837
|
<body>
|
|
1642
1838
|
|
|
1643
1839
|
<div class="header">
|
|
1644
1840
|
<div class="header-left">
|
|
1645
|
-
<div class="logo">
|
|
1841
|
+
<div class="logo">APEX<span class="logo-dot">.</span>Kanban</div>
|
|
1646
1842
|
<div class="header-sub" id="taskSource">~/.claude/tasks/</div>
|
|
1647
1843
|
</div>
|
|
1648
|
-
<div
|
|
1649
|
-
<div class="
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1844
|
+
<div style="display:flex;align-items:center;gap:0">
|
|
1845
|
+
<div class="stats">
|
|
1846
|
+
<div class="stat"><div class="num" id="s-total" style="color:var(--t3)">0</div><div class="lbl">Total</div></div>
|
|
1847
|
+
<div class="stat"><div class="num" id="s-active" style="color:var(--am)">0</div><div class="lbl">Active</div></div>
|
|
1848
|
+
<div class="stat"><div class="num" id="s-done" style="color:var(--gn)">0</div><div class="lbl">Done</div></div>
|
|
1849
|
+
<div class="stat"><div class="num" id="s-blocked" style="color:var(--rd)">0</div><div class="lbl">Blocked</div></div>
|
|
1850
|
+
</div>
|
|
1851
|
+
<div class="theme-switcher">
|
|
1852
|
+
<button class="theme-btn active" data-t="dark" title="Dark"></button>
|
|
1853
|
+
<button class="theme-btn" data-t="navy" title="Navy"></button>
|
|
1854
|
+
<button class="theme-btn" data-t="light" title="Light"></button>
|
|
1855
|
+
</div>
|
|
1653
1856
|
</div>
|
|
1654
1857
|
</div>
|
|
1655
1858
|
|
|
@@ -1658,37 +1861,15 @@
|
|
|
1658
1861
|
<div class="toolbar" id="toolbar">
|
|
1659
1862
|
<span id="filterBtns"><button class="active" data-filter="all">All</button></span>
|
|
1660
1863
|
<div class="right">
|
|
1661
|
-
<button onclick="toggleDepGraph()">Deps</button>
|
|
1662
|
-
<button onclick="toggleMetrics()">Metrics</button>
|
|
1663
|
-
<button onclick="toggleAgentDash()">Dashboard</button>
|
|
1664
1864
|
<button onclick="toggleAgentPanel()">Agents</button>
|
|
1665
1865
|
<button onclick="toggleChat()">Chat</button>
|
|
1666
1866
|
<button class="activity-btn" onclick="toggleActivity()">Activity <span class="activity-badge" id="actBadge">0</span></button>
|
|
1867
|
+
<button onclick="toggleArchive()">Archive</button>
|
|
1667
1868
|
<button class="add-btn" onclick="openModal()">New Task</button>
|
|
1668
|
-
<button class="clear-btn" onclick="
|
|
1869
|
+
<button class="clear-btn" onclick="archiveDone()">Archive Done</button>
|
|
1669
1870
|
</div>
|
|
1670
1871
|
</div>
|
|
1671
1872
|
|
|
1672
|
-
<div class="filter-bar" id="filterBar">
|
|
1673
|
-
<input type="text" id="filterSearch" placeholder="Search tasks..." oninput="applyFilters()">
|
|
1674
|
-
<select id="filterAgent" onchange="applyFilters()"><option value="">All Agents</option></select>
|
|
1675
|
-
<select id="filterStatus" onchange="applyFilters()">
|
|
1676
|
-
<option value="">All Status</option>
|
|
1677
|
-
<option value="pending">Pending</option>
|
|
1678
|
-
<option value="in_progress">In Progress</option>
|
|
1679
|
-
<option value="in_review">In Review</option>
|
|
1680
|
-
<option value="completed">Completed</option>
|
|
1681
|
-
</select>
|
|
1682
|
-
<select id="filterPriority" onchange="applyFilters()">
|
|
1683
|
-
<option value="">All Priority</option>
|
|
1684
|
-
<option value="high">P0 Urgent</option>
|
|
1685
|
-
<option value="medium">P1 Normal</option>
|
|
1686
|
-
<option value="low">P2 Low</option>
|
|
1687
|
-
</select>
|
|
1688
|
-
<label><input type="checkbox" id="filterHideDone" onchange="applyFilters()"> Hide Done</label>
|
|
1689
|
-
<label><input type="checkbox" id="filterStaleOnly" onchange="applyFilters()"> Stale Only</label>
|
|
1690
|
-
</div>
|
|
1691
|
-
|
|
1692
1873
|
<div class="board" id="board"></div>
|
|
1693
1874
|
|
|
1694
1875
|
<div class="modal-overlay" id="modalOverlay">
|
|
@@ -1809,31 +1990,16 @@
|
|
|
1809
1990
|
</div>
|
|
1810
1991
|
</div>
|
|
1811
1992
|
|
|
1812
|
-
<div class="
|
|
1813
|
-
<div class="
|
|
1814
|
-
<div class="
|
|
1815
|
-
<
|
|
1816
|
-
<button class="
|
|
1993
|
+
<div class="archive-overlay" id="archOverlay" onclick="closeArchive()"></div>
|
|
1994
|
+
<div class="archive-panel" id="archPanel">
|
|
1995
|
+
<div class="arch-header">
|
|
1996
|
+
<h3>Archive</h3>
|
|
1997
|
+
<button class="arch-close" onclick="closeArchive()">×</button>
|
|
1817
1998
|
</div>
|
|
1818
|
-
<div class="
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
<div class="metrics-overlay" id="metricsOverlay" onclick="closeMetrics()"></div>
|
|
1822
|
-
<div class="metrics-popup" id="metricsPopup">
|
|
1823
|
-
<div class="metrics-header">
|
|
1824
|
-
<h2>Progress Metrics</h2>
|
|
1825
|
-
<button class="td-close" onclick="closeMetrics()">×</button>
|
|
1999
|
+
<div class="arch-dates" id="archDates"></div>
|
|
2000
|
+
<div class="arch-content" id="archContent">
|
|
2001
|
+
<div class="arch-empty">No archived tasks</div>
|
|
1826
2002
|
</div>
|
|
1827
|
-
<div class="metrics-body" id="metricsBody"><div style="color:var(--t4);text-align:center;padding:40px">Loading...</div></div>
|
|
1828
|
-
</div>
|
|
1829
|
-
|
|
1830
|
-
<div class="agent-dash-overlay" id="adOverlay" onclick="closeAgentDash()"></div>
|
|
1831
|
-
<div class="agent-dash" id="adPanel">
|
|
1832
|
-
<div class="ad-header">
|
|
1833
|
-
<h3>Agent Dashboard</h3>
|
|
1834
|
-
<button class="ad-close" onclick="closeAgentDash()">×</button>
|
|
1835
|
-
</div>
|
|
1836
|
-
<div class="ad-body" id="adBody"><div style="color:var(--t4);text-align:center;padding:40px">Loading...</div></div>
|
|
1837
2003
|
</div>
|
|
1838
2004
|
|
|
1839
2005
|
<div class="agent-overlay" id="agentOverlay" onclick="closeAgentPanel()"></div>
|
|
@@ -1842,6 +2008,7 @@
|
|
|
1842
2008
|
<h3>Agent Prompts</h3>
|
|
1843
2009
|
<button class="ap-close" onclick="closeAgentPanel()">×</button>
|
|
1844
2010
|
</div>
|
|
2011
|
+
<div class="ap-project-filter" id="apProjectFilter"></div>
|
|
1845
2012
|
<div class="agent-tabs" id="agentTabs"></div>
|
|
1846
2013
|
<div class="agent-prompt-view" id="agentPromptView">
|
|
1847
2014
|
<div style="color:var(--t4);text-align:center;padding:40px">로딩 중...</div>
|
|
@@ -1884,7 +2051,33 @@
|
|
|
1884
2051
|
</div>
|
|
1885
2052
|
|
|
1886
2053
|
<script>
|
|
2054
|
+
// ── Theme Switcher ──
|
|
2055
|
+
(function(){
|
|
2056
|
+
var saved = localStorage.getItem("kanban_theme") || "dark";
|
|
2057
|
+
document.documentElement.setAttribute("data-theme", saved);
|
|
2058
|
+
document.querySelectorAll(".theme-btn").forEach(function(btn){
|
|
2059
|
+
if(btn.getAttribute("data-t") === saved) btn.classList.add("active");
|
|
2060
|
+
else btn.classList.remove("active");
|
|
2061
|
+
btn.addEventListener("click", function(){
|
|
2062
|
+
var t = this.getAttribute("data-t");
|
|
2063
|
+
document.documentElement.setAttribute("data-theme", t);
|
|
2064
|
+
localStorage.setItem("kanban_theme", t);
|
|
2065
|
+
document.querySelectorAll(".theme-btn").forEach(function(b){ b.classList.remove("active"); });
|
|
2066
|
+
this.classList.add("active");
|
|
2067
|
+
});
|
|
2068
|
+
});
|
|
2069
|
+
})();
|
|
2070
|
+
|
|
1887
2071
|
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)"}};
|
|
2072
|
+
var DYNAMIC_COLORS=["#3B82F6","#10B981","#F59E0B","#EF4444","#8B5CF6","#EC4899","#06B6D4","#F97316","#14B8A6","#6366F1","#D946EF"];
|
|
2073
|
+
function getAgentEntry(key){
|
|
2074
|
+
if(AGENTS[key])return AGENTS[key];
|
|
2075
|
+
var hash=0;for(var i=0;i<key.length;i++)hash=((hash<<5)-hash)+key.charCodeAt(i);
|
|
2076
|
+
var color=DYNAMIC_COLORS[Math.abs(hash)%DYNAMIC_COLORS.length];
|
|
2077
|
+
var label=key.replace(/[-_]/g," ").replace(/\b\w/g,function(c){return c.toUpperCase();});
|
|
2078
|
+
AGENTS[key]={label:label,color:color};
|
|
2079
|
+
return AGENTS[key];
|
|
2080
|
+
}
|
|
1888
2081
|
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)"}];
|
|
1889
2082
|
var allTasks=[], filter="all", prevDoneCount=0, editingId=null, dragId=null;
|
|
1890
2083
|
var currentProject=localStorage.getItem("kanban_project")||null;
|
|
@@ -1923,6 +2116,7 @@ function switchProject(id){
|
|
|
1923
2116
|
localStorage.setItem("kanban_project",id);
|
|
1924
2117
|
renderProjectTabs();
|
|
1925
2118
|
render();
|
|
2119
|
+
if(archiveOpen)loadArchiveList();
|
|
1926
2120
|
}
|
|
1927
2121
|
|
|
1928
2122
|
function addProject(){
|
|
@@ -1949,315 +2143,53 @@ function apiUpdate(id,d){return fetch("/api/tasks/"+id,{method:"PUT",headers:{"C
|
|
|
1949
2143
|
function apiDelete(id){return fetch("/api/tasks/"+id,{method:"DELETE"});}
|
|
1950
2144
|
|
|
1951
2145
|
// ── Agent Prompt Panel ──
|
|
1952
|
-
var agentData=null, activeAgentTab=null;
|
|
1953
|
-
|
|
1954
|
-
// ── Dependency Graph (v3) ──
|
|
1955
|
-
function toggleDepGraph(){
|
|
1956
|
-
var p=document.getElementById("depPopup");
|
|
1957
|
-
if(p.classList.contains("open")){closeDepGraph();return;}
|
|
1958
|
-
document.getElementById("depOverlay").classList.add("open");
|
|
1959
|
-
p.classList.add("open");
|
|
1960
|
-
renderDepGraph();
|
|
1961
|
-
}
|
|
1962
|
-
function closeDepGraph(){
|
|
1963
|
-
document.getElementById("depPopup").classList.remove("open");
|
|
1964
|
-
document.getElementById("depOverlay").classList.remove("open");
|
|
1965
|
-
}
|
|
1966
|
-
function renderDepGraph(){
|
|
1967
|
-
var tasks=currentProject?allTasks.filter(function(t){return t.project===currentProject;}):allTasks;
|
|
1968
|
-
var byId={};
|
|
1969
|
-
tasks.forEach(function(t){byId[String(t.id)]=t;});
|
|
1970
|
-
// Find tasks with dependencies
|
|
1971
|
-
var hasDeps=tasks.filter(function(t){
|
|
1972
|
-
return (t.blockedBy&&t.blockedBy.length>0)||(t.parentId)||(t.subtasks&&t.subtasks.length>0);
|
|
1973
|
-
});
|
|
1974
|
-
// Build parent-child tree
|
|
1975
|
-
var childMap={};
|
|
1976
|
-
tasks.forEach(function(t){
|
|
1977
|
-
if(t.parentId){
|
|
1978
|
-
if(!childMap[String(t.parentId)])childMap[String(t.parentId)]=[];
|
|
1979
|
-
childMap[String(t.parentId)].push(t);
|
|
1980
|
-
}
|
|
1981
|
-
});
|
|
1982
|
-
// Find critical path: tasks that are blocked and blocking others
|
|
1983
|
-
var criticalIds={};
|
|
1984
|
-
tasks.forEach(function(t){
|
|
1985
|
-
if(t.blockedBy&&t.blockedBy.length>0&&getCol(t)!=="completed"){
|
|
1986
|
-
criticalIds[String(t.id)]=true;
|
|
1987
|
-
t.blockedBy.forEach(function(bid){if(byId[String(bid)]&&getCol(byId[String(bid)])!=="completed")criticalIds[String(bid)]=true;});
|
|
1988
|
-
}
|
|
1989
|
-
});
|
|
1990
|
-
var h='';
|
|
1991
|
-
// Legend
|
|
1992
|
-
h+='<div class="dep-legend">';
|
|
1993
|
-
h+='<span><span class="dl-dot" style="background:var(--gn)"></span>Done</span>';
|
|
1994
|
-
h+='<span><span class="dl-dot" style="background:var(--ac)"></span>Active</span>';
|
|
1995
|
-
h+='<span><span class="dl-dot" style="background:var(--vl)"></span>Review</span>';
|
|
1996
|
-
h+='<span><span class="dl-dot" style="background:var(--t4)"></span>Pending</span>';
|
|
1997
|
-
h+='<span><span class="dl-dot" style="background:var(--rd)"></span>Critical Path</span>';
|
|
1998
|
-
h+='</div>';
|
|
1999
|
-
// Dependency chains section
|
|
2000
|
-
var depsShown={};
|
|
2001
|
-
var chains=tasks.filter(function(t){return t.blockedBy&&t.blockedBy.length>0;});
|
|
2002
|
-
if(chains.length>0){
|
|
2003
|
-
h+='<div style="font-size:12px;font-weight:600;color:var(--t2);margin-bottom:8px">Dependency Chains</div>';
|
|
2004
|
-
chains.forEach(function(t){
|
|
2005
|
-
if(depsShown[t.id])return;
|
|
2006
|
-
depsShown[t.id]=true;
|
|
2007
|
-
t.blockedBy.forEach(function(bid){
|
|
2008
|
-
var blocker=byId[String(bid)];
|
|
2009
|
-
if(!blocker)return;
|
|
2010
|
-
h+=makeDepNode(blocker,criticalIds);
|
|
2011
|
-
h+='<span class="dep-arrow">→</span>';
|
|
2012
|
-
});
|
|
2013
|
-
h+=makeDepNode(t,criticalIds);
|
|
2014
|
-
h+='<br>';
|
|
2015
|
-
});
|
|
2016
|
-
h+='<hr style="border:none;border-top:1px solid var(--b1);margin:16px 0">';
|
|
2017
|
-
}
|
|
2018
|
-
// Parent-child trees
|
|
2019
|
-
var roots=tasks.filter(function(t){return !t.parentId&&childMap[String(t.id)];});
|
|
2020
|
-
if(roots.length>0){
|
|
2021
|
-
h+='<div style="font-size:12px;font-weight:600;color:var(--t2);margin-bottom:8px">Task Trees</div>';
|
|
2022
|
-
h+='<ul class="dep-tree">';
|
|
2023
|
-
roots.forEach(function(t){h+=renderDepTree(t,childMap,byId,criticalIds);});
|
|
2024
|
-
h+='</ul>';
|
|
2025
|
-
}
|
|
2026
|
-
if(!chains.length&&!roots.length){
|
|
2027
|
-
h+='<div style="color:var(--t4);text-align:center;padding:40px;font-size:12px">No dependencies found in current project</div>';
|
|
2028
|
-
}
|
|
2029
|
-
document.getElementById("depBody").innerHTML=h;
|
|
2030
|
-
}
|
|
2031
|
-
function makeDepNode(t,crit){
|
|
2032
|
-
var s=getCol(t);
|
|
2033
|
-
var cls="dep-node"+(crit[String(t.id)]?" critical":"");
|
|
2034
|
-
return '<span class="'+cls+'" data-s="'+s+'" onclick="closeDepGraph();openTaskDetail(\''+t.id+'\')">'+
|
|
2035
|
-
'<span class="dn-dot"></span><span class="dn-id">#'+esc(String(t.id))+'</span><span class="dn-title">'+esc((t.subject||"").slice(0,40))+'</span></span>';
|
|
2036
|
-
}
|
|
2037
|
-
function renderDepTree(t,childMap,byId,crit){
|
|
2038
|
-
var h='<li>'+makeDepNode(t,crit);
|
|
2039
|
-
var children=childMap[String(t.id)]||[];
|
|
2040
|
-
if(children.length>0){
|
|
2041
|
-
h+='<ul>';
|
|
2042
|
-
children.forEach(function(c){h+=renderDepTree(c,childMap,byId,crit);});
|
|
2043
|
-
h+='</ul>';
|
|
2044
|
-
}
|
|
2045
|
-
h+='</li>';
|
|
2046
|
-
return h;
|
|
2047
|
-
}
|
|
2048
|
-
|
|
2049
|
-
// ── Filter/Search (v3) ──
|
|
2050
|
-
function updateFilterAgentDropdown(){
|
|
2051
|
-
var sel=document.getElementById("filterAgent");
|
|
2052
|
-
var cur=sel.value;
|
|
2053
|
-
var agents={};
|
|
2054
|
-
allTasks.forEach(function(t){var a=guessAgent(t);agents[a]=true;});
|
|
2055
|
-
var opts='<option value="">All Agents</option>';
|
|
2056
|
-
Object.keys(agents).sort().forEach(function(a){
|
|
2057
|
-
opts+='<option value="'+esc(a)+'"'+(a===cur?' selected':'')+'>'+esc(a)+'</option>';
|
|
2058
|
-
});
|
|
2059
|
-
sel.innerHTML=opts;
|
|
2060
|
-
}
|
|
2061
|
-
function applyFilters(){
|
|
2062
|
-
var search=(document.getElementById("filterSearch").value||"").toLowerCase().trim();
|
|
2063
|
-
var agent=document.getElementById("filterAgent").value;
|
|
2064
|
-
var status=document.getElementById("filterStatus").value;
|
|
2065
|
-
var priority=document.getElementById("filterPriority").value;
|
|
2066
|
-
var hideDone=document.getElementById("filterHideDone").checked;
|
|
2067
|
-
var staleOnly=document.getElementById("filterStaleOnly").checked;
|
|
2068
|
-
var cards=document.querySelectorAll(".card");
|
|
2069
|
-
var shown=0,hidden=0;
|
|
2070
|
-
cards.forEach(function(card){
|
|
2071
|
-
var id=card.dataset.id||"";
|
|
2072
|
-
var cAgent=card.dataset.agent||"";
|
|
2073
|
-
var cStatus=card.dataset.status||"";
|
|
2074
|
-
var cPriority=card.dataset.priority||"";
|
|
2075
|
-
var cStale=card.dataset.stale==="1";
|
|
2076
|
-
var title=(card.querySelector(".title")||{}).textContent||"";
|
|
2077
|
-
var show=true;
|
|
2078
|
-
if(search&&title.toLowerCase().indexOf(search)<0&&id.indexOf(search)<0) show=false;
|
|
2079
|
-
if(agent&&cAgent!==agent) show=false;
|
|
2080
|
-
if(status&&cStatus!==status) show=false;
|
|
2081
|
-
if(priority&&cPriority!==priority) show=false;
|
|
2082
|
-
if(hideDone&&cStatus==="completed") show=false;
|
|
2083
|
-
if(staleOnly&&!cStale) show=false;
|
|
2084
|
-
card.classList.toggle("filter-hidden",!show);
|
|
2085
|
-
if(show) shown++; else hidden++;
|
|
2086
|
-
});
|
|
2087
|
-
// Update column counts
|
|
2088
|
-
document.querySelectorAll(".column").forEach(function(col){
|
|
2089
|
-
var cnt=col.querySelectorAll(".card:not(.filter-hidden)").length;
|
|
2090
|
-
var countEl=col.querySelector(".count");
|
|
2091
|
-
if(countEl) countEl.textContent=cnt;
|
|
2092
|
-
});
|
|
2093
|
-
}
|
|
2146
|
+
var agentData=null, activeAgentTab=null, agentPanelProject=null;
|
|
2147
|
+
var AGENT_FILE_MAP={frontend:"frontend",backend:"backend",qa:"qa",devops:"devops",orchestrator:"orchestrator",designer:"designer",researcher:"researcher",strategist:"strategist",content:"content-planner",itemauthor:"item-author"};
|
|
2094
2148
|
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
}
|
|
2103
|
-
function closeMetrics(){
|
|
2104
|
-
document.getElementById("metricsPopup").classList.remove("open");
|
|
2105
|
-
document.getElementById("metricsOverlay").classList.remove("open");
|
|
2106
|
-
}
|
|
2107
|
-
function loadMetrics(){
|
|
2108
|
-
var projectParam=currentProject?"?project="+encodeURIComponent(currentProject):"";
|
|
2109
|
-
fetch("/api/metrics"+projectParam).then(function(r){return r.json();}).then(renderMetrics).catch(function(){
|
|
2110
|
-
document.getElementById("metricsBody").innerHTML='<div style="color:var(--t4);text-align:center;padding:40px">Failed</div>';
|
|
2111
|
-
});
|
|
2112
|
-
}
|
|
2113
|
-
function renderMetrics(m){
|
|
2114
|
-
var h='';
|
|
2115
|
-
// Overview cards
|
|
2116
|
-
h+='<div class="m-grid">';
|
|
2117
|
-
h+='<div class="m-card"><div class="m-num" style="color:var(--t2)">'+m.total+'</div><div class="m-lbl">Total</div></div>';
|
|
2118
|
-
h+='<div class="m-card"><div class="m-num" style="color:var(--gn)">'+m.completed+'</div><div class="m-lbl">Done</div></div>';
|
|
2119
|
-
h+='<div class="m-card"><div class="m-num" style="color:var(--am)">'+m.active+'</div><div class="m-lbl">Active</div></div>';
|
|
2120
|
-
h+='<div class="m-card"><div class="m-num" style="color:var(--ac)">'+m.completionRate+'%</div><div class="m-lbl">Completion</div></div>';
|
|
2121
|
-
h+='</div>';
|
|
2122
|
-
// Second row
|
|
2123
|
-
h+='<div class="m-grid">';
|
|
2124
|
-
h+='<div class="m-card"><div class="m-num" style="color:var(--t3)">'+m.pending+'</div><div class="m-lbl">Pending</div></div>';
|
|
2125
|
-
h+='<div class="m-card"><div class="m-num" style="color:var(--vl)">'+m.review+'</div><div class="m-lbl">Review</div></div>';
|
|
2126
|
-
h+='<div class="m-card"><div class="m-num" style="color:var(--rd)">'+(m.blocked||0)+'</div><div class="m-lbl">Blocked</div></div>';
|
|
2127
|
-
h+='<div class="m-card"><div class="m-num" style="color:var(--rd)">'+(m.stale||0)+'</div><div class="m-lbl">Stale</div></div>';
|
|
2128
|
-
h+='</div>';
|
|
2129
|
-
// Completion progress ring
|
|
2130
|
-
h+='<div class="m-section"><div class="m-section-title">Completion</div>';
|
|
2131
|
-
h+='<div class="m-progress-ring"><svg viewBox="0 0 36 36"><path d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" fill="none" stroke="var(--s3)" stroke-width="3"/>';
|
|
2132
|
-
h+='<path d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" fill="none" stroke="var(--gn)" stroke-width="3" stroke-dasharray="'+m.completionRate+', 100" stroke-linecap="round"/>';
|
|
2133
|
-
h+='<text x="18" y="20.5" text-anchor="middle" fill="var(--t1)" font-size="8" font-weight="700" font-family="JetBrains Mono, monospace">'+m.completionRate+'%</text></svg></div></div>';
|
|
2134
|
-
// Agent bar chart
|
|
2135
|
-
var agentKeys=Object.keys(m.byAgent||{}).sort();
|
|
2136
|
-
if(agentKeys.length>0){
|
|
2137
|
-
var maxT=Math.max.apply(null,agentKeys.map(function(k){return m.byAgent[k].total;}));
|
|
2138
|
-
h+='<div class="m-section"><div class="m-section-title">By Agent</div><div class="m-bar-chart">';
|
|
2139
|
-
agentKeys.forEach(function(k){
|
|
2140
|
-
var s=m.byAgent[k];
|
|
2141
|
-
var pct=maxT>0?Math.round(s.completed/maxT*100):0;
|
|
2142
|
-
var pctA=maxT>0?Math.round(s.in_progress/maxT*100):0;
|
|
2143
|
-
var color=(AGENTS[k]||AGENTS.unknown).color;
|
|
2144
|
-
h+='<div class="m-bar-row"><span class="m-bar-label">'+esc(k)+'</span>';
|
|
2145
|
-
h+='<div class="m-bar-track"><div class="m-bar-fill" style="width:'+(pct+pctA)+'%;background:'+color+';opacity:0.3;position:absolute"></div>';
|
|
2146
|
-
h+='<div class="m-bar-fill" style="width:'+pct+'%;background:'+color+';position:relative;z-index:1"></div></div>';
|
|
2147
|
-
h+='<span class="m-bar-val">'+s.completed+'/'+s.total+'</span></div>';
|
|
2148
|
-
});
|
|
2149
|
-
h+='</div></div>';
|
|
2149
|
+
function updateBoardTop(){
|
|
2150
|
+
var board=document.querySelector(".board");
|
|
2151
|
+
if(board){
|
|
2152
|
+
var top=board.getBoundingClientRect().top;
|
|
2153
|
+
var feed=document.querySelector(".live-feed");
|
|
2154
|
+
var bottom=feed?feed.offsetHeight:0;
|
|
2155
|
+
board.style.height="calc(100vh - "+top+"px - "+bottom+"px)";
|
|
2150
2156
|
}
|
|
2151
|
-
// Daily completed trend
|
|
2152
|
-
var dailyKeys=Object.keys(m.dailyCompleted||{}).sort().slice(-14);
|
|
2153
|
-
if(dailyKeys.length>1){
|
|
2154
|
-
var maxD=Math.max.apply(null,dailyKeys.map(function(k){return m.dailyCompleted[k];}));
|
|
2155
|
-
h+='<div class="m-section"><div class="m-section-title">Daily Completed (last 14 days)</div><div class="m-bar-chart">';
|
|
2156
|
-
dailyKeys.forEach(function(k){
|
|
2157
|
-
var v=m.dailyCompleted[k];
|
|
2158
|
-
var pct=maxD>0?Math.round(v/maxD*100):0;
|
|
2159
|
-
h+='<div class="m-bar-row"><span class="m-bar-label" style="width:60px">'+k.slice(5)+'</span>';
|
|
2160
|
-
h+='<div class="m-bar-track"><div class="m-bar-fill" style="width:'+pct+'%;background:var(--gn)"></div></div>';
|
|
2161
|
-
h+='<span class="m-bar-val">'+v+'</span></div>';
|
|
2162
|
-
});
|
|
2163
|
-
h+='</div></div>';
|
|
2164
|
-
}
|
|
2165
|
-
// Priority breakdown
|
|
2166
|
-
h+='<div class="m-section"><div class="m-section-title">By Priority</div><div class="m-bar-chart">';
|
|
2167
|
-
[{k:"high",l:"P0 Urgent",c:"var(--rd)"},{k:"medium",l:"P1 Normal",c:"var(--am)"},{k:"low",l:"P2 Low",c:"var(--t3)"}].forEach(function(p){
|
|
2168
|
-
var v=m.byPriority[p.k]||0;
|
|
2169
|
-
var pct=m.total>0?Math.round(v/m.total*100):0;
|
|
2170
|
-
h+='<div class="m-bar-row"><span class="m-bar-label" style="width:70px">'+p.l+'</span>';
|
|
2171
|
-
h+='<div class="m-bar-track"><div class="m-bar-fill" style="width:'+pct+'%;background:'+p.c+'"></div></div>';
|
|
2172
|
-
h+='<span class="m-bar-val">'+v+'</span></div>';
|
|
2173
|
-
});
|
|
2174
|
-
h+='</div></div>';
|
|
2175
|
-
document.getElementById("metricsBody").innerHTML=h;
|
|
2176
2157
|
}
|
|
2158
|
+
window.addEventListener("resize",updateBoardTop);
|
|
2177
2159
|
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
loadAgentDash();
|
|
2185
|
-
}
|
|
2186
|
-
function closeAgentDash(){
|
|
2187
|
-
document.getElementById("adPanel").classList.remove("open");
|
|
2188
|
-
document.getElementById("adOverlay").classList.remove("open");
|
|
2189
|
-
}
|
|
2190
|
-
function loadAgentDash(){
|
|
2191
|
-
fetch("/api/agents/status").then(function(r){return r.json();}).then(function(data){
|
|
2192
|
-
renderAgentDash(data);
|
|
2193
|
-
}).catch(function(){
|
|
2194
|
-
document.getElementById("adBody").innerHTML='<div style="color:var(--t4);text-align:center;padding:40px">Failed to load</div>';
|
|
2160
|
+
function renderApProjectFilter(){
|
|
2161
|
+
var container=document.getElementById("apProjectFilter");
|
|
2162
|
+
if(!container||projects.length===0)return;
|
|
2163
|
+
var h='<button class="ap-project-btn'+(agentPanelProject===null?' active':'')+'" onclick="setApProject(null)">All</button>';
|
|
2164
|
+
projects.forEach(function(p){
|
|
2165
|
+
h+='<button class="ap-project-btn'+(agentPanelProject===p.id?' active':'')+'" onclick="setApProject(\''+esc(p.id)+'\')">'+esc(p.name)+'</button>';
|
|
2195
2166
|
});
|
|
2167
|
+
container.innerHTML=h;
|
|
2196
2168
|
}
|
|
2197
|
-
function
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
var agentCurrentTask={};
|
|
2203
|
-
Object.keys(tracked).forEach(function(tid){
|
|
2204
|
-
var t=tracked[tid];
|
|
2205
|
-
agentCurrentTask[t.agent]=agentCurrentTask[t.agent]||[];
|
|
2206
|
-
agentCurrentTask[t.agent].push({taskId:tid,subject:t.subject,alive:t.alive,lastActivity:t.lastActivity,startedAt:t.startedAt});
|
|
2207
|
-
});
|
|
2208
|
-
var keys=Object.keys(agents).filter(function(k){return k!=="unassigned";}).sort();
|
|
2209
|
-
if(agents["unassigned"])keys.push("unassigned");
|
|
2210
|
-
var html='';
|
|
2211
|
-
keys.forEach(function(agentName){
|
|
2212
|
-
var s=agents[agentName];
|
|
2213
|
-
var current=agentCurrentTask[agentName]||[];
|
|
2214
|
-
var status="idle";var statusCls="ad-status-idle";
|
|
2215
|
-
if(current.length>0){
|
|
2216
|
-
var hasStale=current.some(function(c){return !c.alive;});
|
|
2217
|
-
if(hasStale){status="STALE";statusCls="ad-status-stale";}
|
|
2218
|
-
else{status="WORKING";statusCls="ad-status-working";}
|
|
2219
|
-
}
|
|
2220
|
-
var color=(AGENTS[agentName]||AGENTS.unknown).color;
|
|
2221
|
-
html+='<div class="ad-card" style="border-left:3px solid '+color+'">';
|
|
2222
|
-
html+='<div class="ad-card-header">';
|
|
2223
|
-
html+='<span class="ad-dot" style="background:'+color+'"></span>';
|
|
2224
|
-
html+='<span class="ad-name">'+esc(agentName)+'</span>';
|
|
2225
|
-
html+='<span class="ad-status '+statusCls+'">'+status+'</span>';
|
|
2226
|
-
html+='</div>';
|
|
2227
|
-
html+='<div class="ad-stats">';
|
|
2228
|
-
html+='<div class="ad-stat"><div class="ad-num" style="color:var(--t2)">'+(s.total||0)+'</div><div class="ad-lbl">Total</div></div>';
|
|
2229
|
-
html+='<div class="ad-stat"><div class="ad-num" style="color:var(--am)">'+(s.in_progress||0)+'</div><div class="ad-lbl">Active</div></div>';
|
|
2230
|
-
html+='<div class="ad-stat"><div class="ad-num" style="color:var(--gn)">'+(s.completed||0)+'</div><div class="ad-lbl">Done</div></div>';
|
|
2231
|
-
html+='<div class="ad-stat"><div class="ad-num" style="color:var(--t3)">'+(s.pending||0)+'</div><div class="ad-lbl">Pending</div></div>';
|
|
2232
|
-
html+='</div>';
|
|
2233
|
-
if(current.length>0){
|
|
2234
|
-
current.forEach(function(c){
|
|
2235
|
-
var elapsed=Math.round((Date.now()-c.startedAt)/60000);
|
|
2236
|
-
html+='<div class="ad-current"><strong>#'+esc(c.taskId)+'</strong> '+esc(c.subject||"")+'<br>';
|
|
2237
|
-
html+='<span style="color:var(--t4)">'+elapsed+'min elapsed</span>';
|
|
2238
|
-
if(!c.alive) html+=' <span style="color:var(--rd);font-weight:600">DEAD</span>';
|
|
2239
|
-
html+='</div>';
|
|
2240
|
-
});
|
|
2241
|
-
}
|
|
2242
|
-
html+='</div>';
|
|
2243
|
-
});
|
|
2244
|
-
if(!html) html='<div style="color:var(--t4);text-align:center;padding:40px">No agents found</div>';
|
|
2245
|
-
document.getElementById("adBody").innerHTML=html;
|
|
2169
|
+
function setApProject(projectId){
|
|
2170
|
+
agentPanelProject=projectId;
|
|
2171
|
+
renderApProjectFilter();
|
|
2172
|
+
// Reload agents for the selected project
|
|
2173
|
+
loadAgentData(projectId);
|
|
2246
2174
|
}
|
|
2175
|
+
|
|
2247
2176
|
function toggleAgentPanel(){
|
|
2248
2177
|
var panel=document.getElementById("agentPanel");
|
|
2249
2178
|
var overlay=document.getElementById("agentOverlay");
|
|
2250
2179
|
if(panel.classList.contains("show")){closeAgentPanel();return;}
|
|
2251
2180
|
overlay.classList.add("show");
|
|
2252
2181
|
panel.classList.add("show");
|
|
2182
|
+
renderApProjectFilter();
|
|
2253
2183
|
if(!agentData) loadAgentData();
|
|
2254
2184
|
}
|
|
2255
2185
|
function closeAgentPanel(){
|
|
2256
2186
|
document.getElementById("agentPanel").classList.remove("show");
|
|
2257
2187
|
document.getElementById("agentOverlay").classList.remove("show");
|
|
2258
2188
|
}
|
|
2259
|
-
function loadAgentData(){
|
|
2260
|
-
|
|
2189
|
+
function loadAgentData(projectId){
|
|
2190
|
+
var url="/api/agents";
|
|
2191
|
+
if(projectId) url+="?project="+encodeURIComponent(projectId);
|
|
2192
|
+
fetch(url).then(function(r){return r.json();}).then(function(data){
|
|
2261
2193
|
agentData=data;
|
|
2262
2194
|
renderAgentTabs("_project");
|
|
2263
2195
|
});
|
|
@@ -2267,21 +2199,54 @@ function renderAgentTabs(selected){
|
|
|
2267
2199
|
var container=document.getElementById("agentTabs");
|
|
2268
2200
|
var MODEL_LABELS={opus:"Opus",sonnet:"Sonnet",haiku:"Haiku"};
|
|
2269
2201
|
var h='<button class="agent-tab'+(selected==="_project"?" active":"")+'" onclick="showAgentPrompt(\'_project\')">Project Context</button>';
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2202
|
+
|
|
2203
|
+
// When project filter is active AND the server returned project-specific agents,
|
|
2204
|
+
// skip the old allowedFiles filtering — just show all returned agents in order.
|
|
2205
|
+
var hasProjectAgents = agentPanelProject && agentData.agents.length > 0 && agentData.agents[0].project;
|
|
2206
|
+
|
|
2207
|
+
if (hasProjectAgents) {
|
|
2208
|
+
// Pipeline agents — show all in order with role summary
|
|
2209
|
+
agentData.agents.forEach(function(agent){
|
|
2210
|
+
var ml=MODEL_LABELS[agent.model]||agent.model;
|
|
2211
|
+
var roleSummary = (agent.role||'').substring(0, 60);
|
|
2212
|
+
if((agent.role||'').length > 60) roleSummary += '…';
|
|
2213
|
+
h+='<button class="agent-tab agent-tab-pipeline'+(selected===agent.name?" active":"")+'" data-agent="'+esc(agent.name)+'" onclick="showAgentPrompt(\''+esc(agent.name)+'\')">';
|
|
2214
|
+
h+='<span class="agent-tab-order">#'+esc(String(agent.order||0))+'</span>';
|
|
2215
|
+
h+='<span class="agent-tab-name">'+esc(agent.name)+'</span><span class="tab-model">'+ml+'</span>';
|
|
2216
|
+
if(roleSummary) h+='<span class="agent-tab-role">'+esc(roleSummary)+'</span>';
|
|
2217
|
+
h+='</button>';
|
|
2218
|
+
});
|
|
2219
|
+
} else {
|
|
2220
|
+
// Default: APEX .md agents with filtering
|
|
2221
|
+
var allowedFiles=null;
|
|
2222
|
+
if(agentPanelProject){
|
|
2223
|
+
var pTasks=allTasks.filter(function(t){return t.project===agentPanelProject;});
|
|
2224
|
+
var usedKeys={};
|
|
2225
|
+
pTasks.forEach(function(t){var k=guessAgent(t);usedKeys[k]=true;});
|
|
2226
|
+
allowedFiles={};
|
|
2227
|
+
Object.keys(usedKeys).forEach(function(k){
|
|
2228
|
+
var fname=AGENT_FILE_MAP[k];
|
|
2229
|
+
if(fname) allowedFiles[fname]=true;
|
|
2230
|
+
});
|
|
2231
|
+
}
|
|
2232
|
+
var order=["orchestrator","frontend","backend","designer","qa","devops","researcher","strategist","content-planner","item-author","doc-writer"];
|
|
2233
|
+
order.forEach(function(name){
|
|
2234
|
+
var agent=agentData.agents.find(function(a){return a.name===name;});
|
|
2235
|
+
if(!agent)return;
|
|
2236
|
+
if(allowedFiles&&!allowedFiles[name])return;
|
|
2237
|
+
var ml=MODEL_LABELS[agent.model]||agent.model;
|
|
2238
|
+
h+='<button class="agent-tab'+(selected===name?" active":"")+'" onclick="showAgentPrompt(\''+name+'\')">';
|
|
2239
|
+
h+=esc(agent.name)+'<span class="tab-model">'+ml+'</span></button>';
|
|
2240
|
+
});
|
|
2241
|
+
agentData.agents.forEach(function(agent){
|
|
2242
|
+
if(order.indexOf(agent.name)>=0)return;
|
|
2243
|
+
if(allowedFiles&&!allowedFiles[agent.name])return;
|
|
2244
|
+
var ml=MODEL_LABELS[agent.model]||agent.model;
|
|
2245
|
+
h+='<button class="agent-tab'+(selected===agent.name?" active":"")+'" onclick="showAgentPrompt(\''+agent.name+'\')">';
|
|
2246
|
+
h+=esc(agent.name)+'<span class="tab-model">'+ml+'</span></button>';
|
|
2247
|
+
});
|
|
2248
|
+
}
|
|
2249
|
+
|
|
2285
2250
|
container.innerHTML=h;
|
|
2286
2251
|
activeAgentTab=selected;
|
|
2287
2252
|
showAgentPrompt(selected);
|
|
@@ -2292,7 +2257,10 @@ function showAgentPrompt(name){
|
|
|
2292
2257
|
// Update tab active state
|
|
2293
2258
|
document.querySelectorAll(".agent-tab").forEach(function(t){t.classList.remove("active");});
|
|
2294
2259
|
document.querySelectorAll(".agent-tab").forEach(function(t){
|
|
2295
|
-
|
|
2260
|
+
var da=t.getAttribute("data-agent");
|
|
2261
|
+
if(da===name) { t.classList.add("active"); return; }
|
|
2262
|
+
if(name==="_project"&&t.textContent.indexOf("Project Context")>=0) t.classList.add("active");
|
|
2263
|
+
else if(!da&&t.textContent.indexOf(name)===0) t.classList.add("active");
|
|
2296
2264
|
});
|
|
2297
2265
|
var view=document.getElementById("agentPromptView");
|
|
2298
2266
|
if(name==="_project"){
|
|
@@ -2307,7 +2275,12 @@ function showAgentPrompt(name){
|
|
|
2307
2275
|
var h='<div class="agent-meta">';
|
|
2308
2276
|
h+='<div class="agent-meta-item"><strong>Agent:</strong>'+esc(agent.name)+'</div>';
|
|
2309
2277
|
h+='<div class="agent-meta-item" style="border-left:2px solid '+mc+'"><strong>Model:</strong>'+esc(agent.model)+'</div>';
|
|
2310
|
-
|
|
2278
|
+
if(agent.project){
|
|
2279
|
+
h+='<div class="agent-meta-item"><strong>Order:</strong>#'+esc(String(agent.order||0))+'</div>';
|
|
2280
|
+
if(agent.role) h+='<div class="agent-meta-item" style="grid-column:1/-1"><strong>Role:</strong>'+esc(agent.role)+'</div>';
|
|
2281
|
+
} else {
|
|
2282
|
+
h+='<div class="agent-meta-item"><strong>파일:</strong>.claude/agents/'+esc(agent.name)+'.md</div>';
|
|
2283
|
+
}
|
|
2311
2284
|
h+='</div>';
|
|
2312
2285
|
h+='<div class="prompt-section"><div class="prompt-section-label">Agent Role Prompt</div><pre>'+esc(agent.prompt)+'</pre></div>';
|
|
2313
2286
|
view.innerHTML=h;
|
|
@@ -2433,10 +2406,17 @@ function rebuildFilterButtons(){
|
|
|
2433
2406
|
pTasks.forEach(function(t){var a=guessAgent(t);counts[a]=(counts[a]||0)+1;});
|
|
2434
2407
|
var container=document.getElementById("filterBtns");
|
|
2435
2408
|
var html='<button class="'+(filter==="all"?"active":"")+'" data-filter="all">All ('+pTasks.length+')</button>';
|
|
2436
|
-
var
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
var
|
|
2409
|
+
var knownOrder=["orchestrator","frontend","backend","qa","devops","researcher","strategist","designer","content","itemauthor","claude","manual"];
|
|
2410
|
+
var keys=Object.keys(counts);
|
|
2411
|
+
keys.sort(function(a,b){
|
|
2412
|
+
var ia=knownOrder.indexOf(a),ib=knownOrder.indexOf(b);
|
|
2413
|
+
if(ia===-1&&ib===-1)return a.localeCompare(b);
|
|
2414
|
+
if(ia===-1)return 1;if(ib===-1)return -1;
|
|
2415
|
+
return ia-ib;
|
|
2416
|
+
});
|
|
2417
|
+
keys.forEach(function(key){
|
|
2418
|
+
if(key==="unknown"&&keys.length>1&&!counts[key])return;
|
|
2419
|
+
var agent=getAgentEntry(key);
|
|
2440
2420
|
html+='<button class="'+(filter===key?"active":"")+'" data-filter="'+key+'" style="border-bottom:2px solid '+(filter===key?agent.color:"transparent")+'">'+agent.label+' ('+counts[key]+')</button>';
|
|
2441
2421
|
});
|
|
2442
2422
|
container.innerHTML=html;
|
|
@@ -2445,6 +2425,7 @@ function render(){
|
|
|
2445
2425
|
renderProjectTabs();
|
|
2446
2426
|
rebuildFilterButtons();
|
|
2447
2427
|
var projectTasks=currentProject?allTasks.filter(function(t){return t.project===currentProject;}):allTasks;
|
|
2428
|
+
window._allTasks=projectTasks;
|
|
2448
2429
|
var filtered=filter==="all"?projectTasks:projectTasks.filter(function(t){return guessAgent(t)===filter;});
|
|
2449
2430
|
var total=projectTasks.length;
|
|
2450
2431
|
var active=projectTasks.filter(function(t){return getCol(t)==="in_progress";}).length;
|
|
@@ -2493,13 +2474,11 @@ function render(){
|
|
|
2493
2474
|
var c=board.querySelector('.card[data-id="'+eid+'"]');
|
|
2494
2475
|
if(c)c.classList.add("expanded");
|
|
2495
2476
|
});
|
|
2496
|
-
|
|
2497
|
-
updateFilterAgentDropdown();
|
|
2498
|
-
applyFilters();
|
|
2477
|
+
updateBoardTop();
|
|
2499
2478
|
}
|
|
2500
2479
|
|
|
2501
2480
|
function renderCard(t,isParent,children,isSub){
|
|
2502
|
-
var agentKey=guessAgent(t),agent=
|
|
2481
|
+
var agentKey=guessAgent(t),agent=getAgentEntry(agentKey);
|
|
2503
2482
|
var pri=t.priority||"medium",blocked=isBlocked(t);
|
|
2504
2483
|
var id=t.id||t._file||"",title=t.subject||t.title||"Untitled";
|
|
2505
2484
|
var desc=t.description||"",activeForm=t.activeForm||"";
|
|
@@ -2507,8 +2486,8 @@ function renderCard(t,isParent,children,isSub){
|
|
|
2507
2486
|
var sessionShort=(t._session||"").slice(0,8),elapsed=getElapsed(t);
|
|
2508
2487
|
var editable=t._editable,draggable=!blocked;
|
|
2509
2488
|
children=children||[];
|
|
2510
|
-
var cls="card"+(blocked?" blocked":"")+(editable?"":" readonly")+(isParent?" parent":"")+(isSub?" subtask":"")
|
|
2511
|
-
var h='<div class="'+cls+'" data-id="'+esc(id)+'"
|
|
2489
|
+
var cls="card"+(blocked?" blocked":"")+(editable?"":" readonly")+(isParent?" parent":"")+(isSub?" subtask":"");
|
|
2490
|
+
var h='<div class="'+cls+'" data-id="'+esc(id)+'"'+(draggable?' draggable="true" ondragstart="onDragStart(event)" ondragend="onDragEnd(event)"':'')+' style="border-left-color:'+agent.color+'">';
|
|
2512
2491
|
h+='<div class="card-actions">';
|
|
2513
2492
|
if(editable) h+='<button title="Edit" onclick="event.stopPropagation();editTask(\''+esc(id)+'\')">✎</button>';
|
|
2514
2493
|
h+='<button class="del" title="Delete" onclick="event.stopPropagation();quickDelete(\''+esc(id)+'\')">✕</button>';
|
|
@@ -2522,18 +2501,19 @@ function renderCard(t,isParent,children,isSub){
|
|
|
2522
2501
|
if(activeForm&&getCol(t)==="in_progress") h+='<div class="active-form-inline"><div class="spinner"></div>'+esc(activeForm)+'</div>';
|
|
2523
2502
|
// Exec indicator
|
|
2524
2503
|
if(getCol(t)==="in_progress"&&String(t.id)===String(currentExecId)) h+='<div class="exec-indicator"><div class="spinner"></div>Executing...</div>';
|
|
2525
|
-
// Zombie/stale indicator
|
|
2526
|
-
if(t._stale&&getCol(t)==="in_progress") h+='<div class="stale-badge">STALE<span class="stale-reason">'+(t._staleReason==="process_dead"?" — process dead":" — no activity 30min")+'</span></div>';
|
|
2527
2504
|
// Review badge (collapsed)
|
|
2528
2505
|
if(getCol(t)==="in_review") h+='<div style="font-size:10px;color:var(--vl);margin-top:2px;font-weight:600;">Awaiting Review</div>';
|
|
2529
2506
|
// Expanded body
|
|
2530
2507
|
h+='<div class="card-body">';
|
|
2531
2508
|
if(activeForm&&getCol(t)==="in_progress") h+='<div class="active-form"><div class="spinner"></div>'+esc(activeForm)+'</div>';
|
|
2509
|
+
|
|
2510
|
+
// ── Header row: agent + status ──
|
|
2532
2511
|
h+='<div class="agent-chip"><span class="agent-dot" style="background:'+agent.color+'"></span>'+esc(agent.label);
|
|
2533
2512
|
if(owner) h+=' / '+esc(owner);
|
|
2534
2513
|
if(blocked) h+=' / <span style="color:var(--rd)">blocked</span>';
|
|
2535
2514
|
h+='</div>';
|
|
2536
|
-
|
|
2515
|
+
|
|
2516
|
+
// ── Subtask bar ──
|
|
2537
2517
|
if(isParent&&children.length>0){
|
|
2538
2518
|
var doneCount=children.filter(function(c){return getCol(c)==="completed";}).length;
|
|
2539
2519
|
var inpCount=children.filter(function(c){return getCol(c)==="in_progress";}).length;
|
|
@@ -2543,12 +2523,67 @@ function renderCard(t,isParent,children,isSub){
|
|
|
2543
2523
|
if(inpCount>0) h+=inpCount+' active';
|
|
2544
2524
|
h+='</span></div>';
|
|
2545
2525
|
}
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
if(
|
|
2549
|
-
|
|
2526
|
+
|
|
2527
|
+
// ── Dependencies section ──
|
|
2528
|
+
if((t.blockedBy&&t.blockedBy.length)||(t.blocks&&t.blocks.length)){
|
|
2529
|
+
h+='<div class="card-section">';
|
|
2530
|
+
h+='<div class="card-section-label"><span class="dot" style="background:var(--rd)"></span>Dependencies</div>';
|
|
2531
|
+
h+='<div class="card-section-body">';
|
|
2532
|
+
if(t.blockedBy&&t.blockedBy.length){
|
|
2533
|
+
h+='<div style="margin-bottom:4px;font-size:9px;color:var(--t4);text-transform:uppercase">Blocked by</div>';
|
|
2534
|
+
var allT=window._allTasks||[];
|
|
2535
|
+
t.blockedBy.forEach(function(depId){
|
|
2536
|
+
var dep=allT.find(function(d){return String(d.id)===String(depId);});
|
|
2537
|
+
var depCol=dep?getCol(dep):"pending";
|
|
2538
|
+
var dotColor=depCol==="completed"?"var(--gn)":depCol==="in_progress"?"var(--am)":"var(--t4)";
|
|
2539
|
+
var depTitle=dep?(dep.subject||dep.title||""):"";
|
|
2540
|
+
if(depTitle.length>40)depTitle=depTitle.slice(0,40)+"…";
|
|
2541
|
+
h+='<span class="card-dep-chip" onclick="event.stopPropagation();openTaskDetail(\''+esc(String(depId))+'\')">';
|
|
2542
|
+
h+='<span class="dep-dot" style="background:'+dotColor+'"></span>#'+esc(String(depId));
|
|
2543
|
+
if(depTitle) h+=' '+esc(depTitle);
|
|
2544
|
+
h+='</span>';
|
|
2545
|
+
});
|
|
2546
|
+
}
|
|
2547
|
+
if(t.blocks&&t.blocks.length){
|
|
2548
|
+
h+='<div style="margin-top:4px;margin-bottom:4px;font-size:9px;color:var(--t4);text-transform:uppercase">Blocks</div>';
|
|
2549
|
+
t.blocks.forEach(function(bId){
|
|
2550
|
+
h+='<span class="card-dep-chip" onclick="event.stopPropagation();openTaskDetail(\''+esc(String(bId))+'\')">';
|
|
2551
|
+
h+='<span class="dep-dot" style="background:var(--am)"></span>#'+esc(String(bId));
|
|
2552
|
+
h+='</span>';
|
|
2553
|
+
});
|
|
2554
|
+
}
|
|
2555
|
+
h+='</div></div>';
|
|
2556
|
+
}
|
|
2557
|
+
|
|
2558
|
+
// ── Plan / Description section ──
|
|
2559
|
+
if(desc){
|
|
2560
|
+
h+='<div class="card-section">';
|
|
2561
|
+
h+='<div class="card-section-label"><span class="dot" style="background:var(--ac)"></span>작업 계획</div>';
|
|
2562
|
+
h+='<div class="card-section-body">'+formatDesc(desc)+'</div>';
|
|
2563
|
+
h+='</div>';
|
|
2564
|
+
}
|
|
2565
|
+
|
|
2566
|
+
// ── Report / Result section ──
|
|
2567
|
+
if(t.reportSummary){
|
|
2568
|
+
h+='<div class="card-section">';
|
|
2569
|
+
h+='<div class="card-section-label"><span class="dot" style="background:var(--gn)"></span>작업 결과</div>';
|
|
2570
|
+
h+='<div class="card-section-body"><div class="card-report-summary">'+formatDesc(t.reportSummary)+'</div></div>';
|
|
2571
|
+
h+='</div>';
|
|
2572
|
+
}
|
|
2573
|
+
|
|
2574
|
+
// ── Timeline ──
|
|
2575
|
+
var hasTimes=t.createdAt||t.startedAt||t.completedAt;
|
|
2576
|
+
if(hasTimes||elapsed){
|
|
2577
|
+
h+='<div class="card-timeline">';
|
|
2578
|
+
if(t.createdAt) h+='<span><span class="tl-label">Created</span>'+new Date(t.createdAt).toLocaleString("ko-KR",{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})+'</span>';
|
|
2579
|
+
if(t.startedAt) h+='<span><span class="tl-label">Started</span>'+new Date(t.startedAt).toLocaleString("ko-KR",{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})+'</span>';
|
|
2580
|
+
if(t.completedAt) h+='<span><span class="tl-label">Done</span>'+new Date(t.completedAt).toLocaleString("ko-KR",{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})+'</span>';
|
|
2581
|
+
if(elapsed) h+='<span><span class="tl-label">Elapsed</span>'+elapsed+'</span>';
|
|
2582
|
+
h+='</div>';
|
|
2583
|
+
}
|
|
2584
|
+
|
|
2585
|
+
// ── Review actions ──
|
|
2550
2586
|
if(getCol(t)==="in_review"){
|
|
2551
|
-
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>';
|
|
2552
2587
|
h+='<div class="review-actions">';
|
|
2553
2588
|
h+='<button class="review-btn approve" onclick="event.stopPropagation();approveTask(\''+esc(id)+'\')">Approve</button>';
|
|
2554
2589
|
h+='<button class="review-btn reject" onclick="event.stopPropagation();rejectTask(\''+esc(id)+'\')">Reject</button>';
|
|
@@ -2558,13 +2593,6 @@ function renderCard(t,isParent,children,isSub){
|
|
|
2558
2593
|
if(getCol(t)==="completed"&&(t.reportPath||t.reportSummary)){
|
|
2559
2594
|
h+='<button class="report-btn" onclick="event.stopPropagation();openReport(\''+esc(id)+'\')">View Report</button>';
|
|
2560
2595
|
}
|
|
2561
|
-
// Zombie recover button
|
|
2562
|
-
if(t._stale&&getCol(t)==="in_progress"){
|
|
2563
|
-
h+='<div class="stale-actions">';
|
|
2564
|
-
h+='<button class="review-btn reject" onclick="event.stopPropagation();recoverTask(\''+esc(id)+'\')">Recover (→ pending)</button>';
|
|
2565
|
-
h+='<button class="review-btn approve" onclick="event.stopPropagation();rerunTask(\''+esc(id)+'\')">Re-run</button>';
|
|
2566
|
-
h+='</div>';
|
|
2567
|
-
}
|
|
2568
2596
|
h+='</div>';
|
|
2569
2597
|
h+='</div>';
|
|
2570
2598
|
return h;
|
|
@@ -2577,14 +2605,6 @@ function quickDelete(id){
|
|
|
2577
2605
|
if(!confirm("Delete #"+id+"?"))return;
|
|
2578
2606
|
apiDelete(id).then(function(){updateLive("Deleted #"+id);});
|
|
2579
2607
|
}
|
|
2580
|
-
function recoverTask(id){
|
|
2581
|
-
fetch("/api/tasks/"+id+"/recover",{method:"POST"}).then(function(){updateLive("Recovered #"+id);});
|
|
2582
|
-
}
|
|
2583
|
-
function rerunTask(id){
|
|
2584
|
-
fetch("/api/tasks/"+id+"/recover",{method:"POST"}).then(function(){
|
|
2585
|
-
return fetch("/api/exec",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({id:id})});
|
|
2586
|
-
}).then(function(){updateLive("Re-running #"+id);});
|
|
2587
|
-
}
|
|
2588
2608
|
|
|
2589
2609
|
document.addEventListener("click",function(e){
|
|
2590
2610
|
var card=e.target.closest(".card");
|
|
@@ -2606,11 +2626,20 @@ function getCol(t){
|
|
|
2606
2626
|
if(s==="done"||s==="completed"||s==="finished")return "completed";
|
|
2607
2627
|
return "pending";
|
|
2608
2628
|
}
|
|
2609
|
-
function isBlocked(t){
|
|
2629
|
+
function isBlocked(t){
|
|
2630
|
+
if(!t.blockedBy||t.blockedBy.length===0||getCol(t)==="completed")return false;
|
|
2631
|
+
// Only blocked if any dependency is actually not completed
|
|
2632
|
+
var allTasks=window._allTasks||[];
|
|
2633
|
+
for(var i=0;i<t.blockedBy.length;i++){
|
|
2634
|
+
var depId=String(t.blockedBy[i]);
|
|
2635
|
+
var dep=allTasks.find(function(d){return String(d.id)===depId;});
|
|
2636
|
+
if(!dep||getCol(dep)!=="completed")return true;
|
|
2637
|
+
}
|
|
2638
|
+
return false;
|
|
2639
|
+
}
|
|
2610
2640
|
function guessAgent(t){
|
|
2611
|
-
if(t.agent&&
|
|
2612
|
-
if(
|
|
2613
|
-
if(t.agent==="manual")return "manual";
|
|
2641
|
+
if(t.agent&&t.agent.length>0)return t.agent;
|
|
2642
|
+
if(!t.agent&&t._session&&!t._editable)return "claude";
|
|
2614
2643
|
var name=(t.owner||t.agentName||t.teammate||t.assignee||"").toLowerCase();
|
|
2615
2644
|
var subj=(t.subject||"").toLowerCase();
|
|
2616
2645
|
var combined=name+" "+subj;
|
|
@@ -2636,6 +2665,53 @@ function getElapsed(t){
|
|
|
2636
2665
|
}
|
|
2637
2666
|
function esc(s){return String(s).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'");}
|
|
2638
2667
|
|
|
2668
|
+
function formatDesc(raw){
|
|
2669
|
+
var s=esc(raw);
|
|
2670
|
+
// Split by newlines
|
|
2671
|
+
var lines=s.split(/\n/);
|
|
2672
|
+
var html="",inList=false;
|
|
2673
|
+
for(var i=0;i<lines.length;i++){
|
|
2674
|
+
var line=lines[i].trim();
|
|
2675
|
+
if(!line){
|
|
2676
|
+
if(inList){html+="</ul>";inList=false;}
|
|
2677
|
+
continue;
|
|
2678
|
+
}
|
|
2679
|
+
// Numbered list: "1. xxx" or "1) xxx"
|
|
2680
|
+
var numMatch=line.match(/^(\d+)[.)]\s+(.+)/);
|
|
2681
|
+
// Bullet: "- xxx" or "• xxx" or "* xxx"
|
|
2682
|
+
var bulletMatch=!numMatch&&line.match(/^[-•*]\s+(.+)/);
|
|
2683
|
+
// Section header: ends with ":" or starts with "[" or "##" or all caps short line
|
|
2684
|
+
var isHeader=!numMatch&&!bulletMatch&&(
|
|
2685
|
+
(line.length<60&&line.match(/[:]$/)) ||
|
|
2686
|
+
line.match(/^#{1,3}\s/) ||
|
|
2687
|
+
line.match(/^\[.+\]$/)
|
|
2688
|
+
);
|
|
2689
|
+
// Key-value: "Key: Value" pattern
|
|
2690
|
+
var kvMatch=!numMatch&&!bulletMatch&&!isHeader&&line.match(/^([^:]{2,30}):\s+(.+)/);
|
|
2691
|
+
|
|
2692
|
+
if(numMatch||bulletMatch){
|
|
2693
|
+
if(!inList){html+="<ul>";inList=true;}
|
|
2694
|
+
var content=numMatch?numMatch[2]:bulletMatch[1];
|
|
2695
|
+
// Bold **text** patterns
|
|
2696
|
+
content=content.replace(/\*\*(.+?)\*\*/g,"<strong>$1</strong>");
|
|
2697
|
+
html+="<li>"+content+"</li>";
|
|
2698
|
+
} else {
|
|
2699
|
+
if(inList){html+="</ul>";inList=false;}
|
|
2700
|
+
if(isHeader){
|
|
2701
|
+
var hText=line.replace(/^#{1,3}\s/,"").replace(/[:]$/,"").replace(/^\[|\]$/g,"");
|
|
2702
|
+
html+='<div style="font-size:10px;font-weight:700;color:var(--t1);margin:8px 0 4px;text-transform:uppercase;letter-spacing:0.03em;">'+hText+'</div>';
|
|
2703
|
+
} else if(kvMatch){
|
|
2704
|
+
html+='<div class="line-item"><span style="color:var(--t3);font-size:10px;">'+kvMatch[1]+'</span> <span style="color:var(--t1);">'+kvMatch[2].replace(/\*\*(.+?)\*\*/g,"<strong>$1</strong>")+'</span></div>';
|
|
2705
|
+
} else {
|
|
2706
|
+
line=line.replace(/\*\*(.+?)\*\*/g,"<strong style='color:var(--t1)'>$1</strong>");
|
|
2707
|
+
html+="<div>"+line+"</div>";
|
|
2708
|
+
}
|
|
2709
|
+
}
|
|
2710
|
+
}
|
|
2711
|
+
if(inList) html+="</ul>";
|
|
2712
|
+
return html;
|
|
2713
|
+
}
|
|
2714
|
+
|
|
2639
2715
|
document.getElementById("toolbar").addEventListener("click",function(e){
|
|
2640
2716
|
if(e.target.tagName!=="BUTTON"||!e.target.dataset.filter)return;
|
|
2641
2717
|
filter=e.target.dataset.filter;
|
|
@@ -2675,8 +2751,6 @@ function connectSSE(){
|
|
|
2675
2751
|
var data=JSON.parse(e.data);
|
|
2676
2752
|
if(data.type==="activity"&&data.event){
|
|
2677
2753
|
onActivityEvent(data.event);
|
|
2678
|
-
} else if(data.type==="zombie"){
|
|
2679
|
-
updateLive("Zombie detected: #"+data.taskId);
|
|
2680
2754
|
} else if(data.type==="exec_start"){
|
|
2681
2755
|
onExecStart(data);
|
|
2682
2756
|
} else if(data.type==="exec"){
|
|
@@ -2710,12 +2784,17 @@ function updateLive(msg){
|
|
|
2710
2784
|
document.getElementById("liveMsg").textContent=msg;
|
|
2711
2785
|
document.getElementById("liveTime").textContent=new Date().toLocaleTimeString("ko-KR",{hour:"2-digit",minute:"2-digit",second:"2-digit"});
|
|
2712
2786
|
}
|
|
2713
|
-
function
|
|
2787
|
+
function archiveDone(){
|
|
2714
2788
|
var pTasks=getProjectTasks();
|
|
2715
2789
|
var done=pTasks.filter(function(t){return getCol(t)==="completed"&&t._editable;});
|
|
2716
|
-
if(!done.length)return;
|
|
2717
|
-
if(!confirm(done.length+" completed tasks will be
|
|
2718
|
-
|
|
2790
|
+
if(!done.length){updateLive("No completed tasks to archive");return;}
|
|
2791
|
+
if(!confirm(done.length+" completed tasks will be archived."))return;
|
|
2792
|
+
fetch("/api/archive",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({project:currentProject})}).then(function(r){return r.json();}).then(function(data){
|
|
2793
|
+
if(data.ok){
|
|
2794
|
+
updateLive("Archived "+data.count+" tasks → "+data.date);
|
|
2795
|
+
if(archiveOpen)loadArchiveList();
|
|
2796
|
+
}else{updateLive("Archive error: "+(data.error||"unknown"));}
|
|
2797
|
+
}).catch(function(e){updateLive("Archive failed: "+e.message);});
|
|
2719
2798
|
}
|
|
2720
2799
|
|
|
2721
2800
|
document.addEventListener("keydown",function(e){
|
|
@@ -2723,8 +2802,9 @@ document.addEventListener("keydown",function(e){
|
|
|
2723
2802
|
if(e.ctrlKey&&e.shiftKey&&(e.key==="K"||e.key==="k")){e.preventDefault();stopExecution();return;}
|
|
2724
2803
|
if(e.target.closest("input,select,textarea"))return;
|
|
2725
2804
|
if(e.key==="n"||e.key==="N")openModal();
|
|
2726
|
-
if(e.key==="Escape"){if(tdCurrentId){closeTaskDetail();return;}closeModal();closeReport();closeActivity();closeChat();}
|
|
2805
|
+
if(e.key==="Escape"){if(tdCurrentId){closeTaskDetail();return;}closeModal();closeReport();closeActivity();closeArchive();closeChat();}
|
|
2727
2806
|
if(e.key==="a"||e.key==="A")toggleActivity();
|
|
2807
|
+
if(e.key==="r"||e.key==="R")toggleArchive();
|
|
2728
2808
|
if(e.key==="c"||e.key==="C")toggleChat();
|
|
2729
2809
|
});
|
|
2730
2810
|
document.getElementById("modalOverlay").addEventListener("click",function(e){if(e.target===e.currentTarget)closeModal();});
|
|
@@ -2851,16 +2931,8 @@ function renderTaskDetail(t){
|
|
|
2851
2931
|
html+='<div style="font-size:11px;font-family:JetBrains Mono,monospace;color:var(--t4);line-height:1.8">'+times.join("<br>")+'</div>';
|
|
2852
2932
|
html+='</div>';
|
|
2853
2933
|
}
|
|
2854
|
-
// Comments section
|
|
2855
|
-
html+='<div class="td-section"><div class="td-section-label">Comments</div>';
|
|
2856
|
-
html+='<div id="tdComments"><div style="color:var(--t4);font-size:11px">Loading...</div></div>';
|
|
2857
|
-
html+='<div class="td-comment-form">';
|
|
2858
|
-
html+='<textarea id="tdCommentInput" placeholder="Add a comment..." rows="2" style="width:100%;background:var(--bg);border:1px solid var(--b1);border-radius:var(--r);padding:8px 10px;color:var(--t1);font-size:12px;font-family:inherit;resize:vertical"></textarea>';
|
|
2859
|
-
html+='<button class="btn btn-primary" style="margin-top:6px;font-size:11px;padding:5px 12px" onclick="addComment()">Post</button>';
|
|
2860
|
-
html+='</div></div>';
|
|
2861
2934
|
if(!html)html='<div class="td-empty">No details available</div>';
|
|
2862
2935
|
document.getElementById("tdBody").innerHTML=html;
|
|
2863
|
-
loadComments(String(t.id));
|
|
2864
2936
|
// Footer buttons
|
|
2865
2937
|
document.getElementById("tdEditBtn").style.display=t._editable!==false?"inline-flex":"none";
|
|
2866
2938
|
}
|
|
@@ -2869,29 +2941,6 @@ function closeTaskDetail(){
|
|
|
2869
2941
|
document.getElementById("tdPopup").classList.remove("open");
|
|
2870
2942
|
tdCurrentId=null;
|
|
2871
2943
|
}
|
|
2872
|
-
function loadComments(id){
|
|
2873
|
-
fetch("/api/tasks/"+id+"/comments").then(function(r){return r.json();}).then(function(comments){
|
|
2874
|
-
var el=document.getElementById("tdComments");
|
|
2875
|
-
if(!el)return;
|
|
2876
|
-
if(!comments||comments.length===0){el.innerHTML='<div style="color:var(--t4);font-size:11px">No comments yet</div>';return;}
|
|
2877
|
-
el.innerHTML=comments.map(function(c){
|
|
2878
|
-
return '<div style="padding:6px 0;border-bottom:1px solid var(--b1);font-size:12px;line-height:1.6">'+
|
|
2879
|
-
'<div style="display:flex;justify-content:space-between"><strong style="color:var(--t1);font-size:11px">'+esc(c.author)+'</strong>'+
|
|
2880
|
-
'<span style="font-size:9px;color:var(--t4);font-family:JetBrains Mono,monospace">'+new Date(c.createdAt).toLocaleString("ko-KR")+'</span></div>'+
|
|
2881
|
-
'<div style="color:var(--t2);margin-top:2px">'+renderMarkdown(c.text)+'</div></div>';
|
|
2882
|
-
}).join("");
|
|
2883
|
-
}).catch(function(){});
|
|
2884
|
-
}
|
|
2885
|
-
function addComment(){
|
|
2886
|
-
if(!tdCurrentId)return;
|
|
2887
|
-
var input=document.getElementById("tdCommentInput");
|
|
2888
|
-
var text=(input.value||"").trim();
|
|
2889
|
-
if(!text)return;
|
|
2890
|
-
fetch("/api/tasks/"+tdCurrentId+"/comments",{
|
|
2891
|
-
method:"POST",headers:{"Content-Type":"application/json"},
|
|
2892
|
-
body:JSON.stringify({author:"user",text:text})
|
|
2893
|
-
}).then(function(){input.value="";loadComments(tdCurrentId);}).catch(function(){});
|
|
2894
|
-
}
|
|
2895
2944
|
|
|
2896
2945
|
function renderMarkdown(md){
|
|
2897
2946
|
var html=esc(md);
|
|
@@ -2932,7 +2981,7 @@ function renderMarkdown(md){
|
|
|
2932
2981
|
|
|
2933
2982
|
// ── Activity Panel ──
|
|
2934
2983
|
var activityEvents=[], activityUnread=0, activityOpen=false;
|
|
2935
|
-
var ACT_ICONS={created:"🆕",started:"▶️",completed:"✅",updated:"📝",deleted:"🗑️"};
|
|
2984
|
+
var ACT_ICONS={created:"🆕",started:"▶️",completed:"✅",updated:"📝",deleted:"🗑️",archived:"📦"};
|
|
2936
2985
|
|
|
2937
2986
|
function toggleActivity(){
|
|
2938
2987
|
if(activityOpen){closeActivity();}else{openActivity();}
|
|
@@ -3027,6 +3076,140 @@ function onActivityEvent(evt){
|
|
|
3027
3076
|
var icon=ACT_ICONS[evt.type]||"";
|
|
3028
3077
|
updateLive(icon+" #"+evt.taskId+" "+evt.type+": "+(evt.subject||""));
|
|
3029
3078
|
}
|
|
3079
|
+
// ── Archive Panel ──
|
|
3080
|
+
var archiveOpen=false, archiveList=[], archiveSelectedDate=null, archiveData=null;
|
|
3081
|
+
|
|
3082
|
+
function toggleArchive(){if(archiveOpen)closeArchive();else openArchive();}
|
|
3083
|
+
function openArchive(){
|
|
3084
|
+
archiveOpen=true;
|
|
3085
|
+
document.getElementById("archPanel").classList.add("open");
|
|
3086
|
+
document.getElementById("archOverlay").classList.add("open");
|
|
3087
|
+
loadArchiveList();
|
|
3088
|
+
}
|
|
3089
|
+
function closeArchive(){
|
|
3090
|
+
archiveOpen=false;
|
|
3091
|
+
document.getElementById("archPanel").classList.remove("open");
|
|
3092
|
+
document.getElementById("archOverlay").classList.remove("open");
|
|
3093
|
+
}
|
|
3094
|
+
function loadArchiveList(){
|
|
3095
|
+
var proj=currentProject||(projects.length?projects[0].id:"apex");
|
|
3096
|
+
fetch("/api/archives?project="+encodeURIComponent(proj)).then(function(r){return r.json();}).then(function(list){
|
|
3097
|
+
archiveList=list;
|
|
3098
|
+
renderArchiveDates();
|
|
3099
|
+
if(list.length>0)loadArchiveDay(list[0].date);
|
|
3100
|
+
else{archiveData=null;archiveSelectedDate=null;renderArchiveContent();}
|
|
3101
|
+
}).catch(function(){});
|
|
3102
|
+
}
|
|
3103
|
+
function renderArchiveDates(){
|
|
3104
|
+
var container=document.getElementById("archDates");
|
|
3105
|
+
if(!archiveList.length){container.innerHTML='';return;}
|
|
3106
|
+
var today=new Date().toISOString().slice(0,10);
|
|
3107
|
+
var yesterday=new Date(Date.now()-86400000).toISOString().slice(0,10);
|
|
3108
|
+
container.innerHTML=archiveList.map(function(item){
|
|
3109
|
+
var label=item.date;
|
|
3110
|
+
if(item.date===today)label="Today";
|
|
3111
|
+
else if(item.date===yesterday)label="Yesterday";
|
|
3112
|
+
else{var d=new Date(item.date+"T00:00:00");label=(d.getMonth()+1)+"/"+ d.getDate();}
|
|
3113
|
+
var active=item.date===archiveSelectedDate?" active":"";
|
|
3114
|
+
return '<button class="arch-date-btn'+active+'" onclick="loadArchiveDay(\''+item.date+'\')">'+label+'<span class="arch-count">'+item.count+'</span></button>';
|
|
3115
|
+
}).join("");
|
|
3116
|
+
}
|
|
3117
|
+
function loadArchiveDay(date){
|
|
3118
|
+
archiveSelectedDate=date;
|
|
3119
|
+
renderArchiveDates();
|
|
3120
|
+
var proj=currentProject||(projects.length?projects[0].id:"apex");
|
|
3121
|
+
fetch("/api/archives/"+date+"?project="+encodeURIComponent(proj)).then(function(r){return r.json();}).then(function(data){
|
|
3122
|
+
archiveData=data;
|
|
3123
|
+
renderArchiveContent();
|
|
3124
|
+
}).catch(function(){});
|
|
3125
|
+
}
|
|
3126
|
+
function renderArchiveContent(){
|
|
3127
|
+
var container=document.getElementById("archContent");
|
|
3128
|
+
if(!archiveData||!archiveData.tasks||archiveData.tasks.length===0){
|
|
3129
|
+
container.innerHTML='<div class="arch-empty">No archived tasks</div>';
|
|
3130
|
+
return;
|
|
3131
|
+
}
|
|
3132
|
+
var tasks=archiveData.tasks;
|
|
3133
|
+
var taskMap={};
|
|
3134
|
+
tasks.forEach(function(t){taskMap[t.id]=t;});
|
|
3135
|
+
|
|
3136
|
+
// Categorize: parents, children, orphans, standalone
|
|
3137
|
+
var parents=[], children={}, orphans=[], standalone=[];
|
|
3138
|
+
tasks.forEach(function(t){
|
|
3139
|
+
if(t.children&&t.children.length>0){
|
|
3140
|
+
parents.push(t);
|
|
3141
|
+
}else if(t.parentId){
|
|
3142
|
+
if(taskMap[t.parentId]){
|
|
3143
|
+
if(!children[t.parentId])children[t.parentId]=[];
|
|
3144
|
+
children[t.parentId].push(t);
|
|
3145
|
+
}else{
|
|
3146
|
+
orphans.push(t);
|
|
3147
|
+
}
|
|
3148
|
+
}else{
|
|
3149
|
+
standalone.push(t);
|
|
3150
|
+
}
|
|
3151
|
+
});
|
|
3152
|
+
|
|
3153
|
+
var html='';
|
|
3154
|
+
// Stats
|
|
3155
|
+
html+='<div class="arch-stats">'+archiveData.tasks.length+' tasks';
|
|
3156
|
+
if(archiveData.stats&&archiveData.stats.agents&&archiveData.stats.agents.length){
|
|
3157
|
+
html+=' · agents: '+archiveData.stats.agents.join(", ");
|
|
3158
|
+
}
|
|
3159
|
+
if(archiveData.archivedAt){
|
|
3160
|
+
html+=' · '+new Date(archiveData.archivedAt).toLocaleString("ko-KR");
|
|
3161
|
+
}
|
|
3162
|
+
html+='</div>';
|
|
3163
|
+
|
|
3164
|
+
// Render parent trees
|
|
3165
|
+
parents.forEach(function(p){
|
|
3166
|
+
html+=renderArchiveTask(p,"parent");
|
|
3167
|
+
var kids=children[p.id]||[];
|
|
3168
|
+
kids.forEach(function(c){html+=renderArchiveTask(c,"child");});
|
|
3169
|
+
});
|
|
3170
|
+
|
|
3171
|
+
// Standalone
|
|
3172
|
+
standalone.forEach(function(t){html+=renderArchiveTask(t,"");});
|
|
3173
|
+
|
|
3174
|
+
// Orphans
|
|
3175
|
+
if(orphans.length>0){
|
|
3176
|
+
html+='<div class="arch-section-label">Orphan subtasks</div>';
|
|
3177
|
+
orphans.forEach(function(t){html+=renderArchiveTask(t,"orphan");});
|
|
3178
|
+
}
|
|
3179
|
+
|
|
3180
|
+
container.innerHTML=html;
|
|
3181
|
+
}
|
|
3182
|
+
function renderArchiveTask(t,type){
|
|
3183
|
+
var cls="arch-task";
|
|
3184
|
+
if(type)cls+=" "+type;
|
|
3185
|
+
var h='<div class="'+cls+'">';
|
|
3186
|
+
h+='<div class="arch-task-header">';
|
|
3187
|
+
h+='<span class="arch-task-id">#'+esc(t.id)+'</span>';
|
|
3188
|
+
h+='<span class="arch-task-subject">'+esc(t.subject)+'</span>';
|
|
3189
|
+
if(t.priority==="high"||t.priority==="critical"){
|
|
3190
|
+
h+='<span class="arch-task-priority '+esc(t.priority)+'">'+esc(t.priority)+'</span>';
|
|
3191
|
+
}
|
|
3192
|
+
h+='</div>';
|
|
3193
|
+
h+='<div class="arch-task-meta">';
|
|
3194
|
+
if(t.agent)h+='<span>'+esc(t.agent)+'</span>';
|
|
3195
|
+
if(t.completedAt)h+='<span>'+new Date(t.completedAt).toLocaleTimeString("ko-KR",{hour:"2-digit",minute:"2-digit"})+'</span>';
|
|
3196
|
+
if(t.elapsedSec!==null&&t.elapsedSec!==undefined){
|
|
3197
|
+
var dur=t.elapsedSec;
|
|
3198
|
+
var durStr;
|
|
3199
|
+
if(dur<60)durStr=dur+"s";
|
|
3200
|
+
else if(dur<3600)durStr=Math.floor(dur/60)+"m "+dur%60+"s";
|
|
3201
|
+
else durStr=Math.floor(dur/3600)+"h "+Math.floor((dur%3600)/60)+"m";
|
|
3202
|
+
h+='<span>'+durStr+'</span>';
|
|
3203
|
+
}
|
|
3204
|
+
if(type==="child"&&t.parentId)h+='<span class="arch-task-parent-ref">← #'+esc(t.parentId)+'</span>';
|
|
3205
|
+
if(type==="orphan"&&t.parentId)h+='<span class="arch-task-parent-ref">← #'+esc(t.parentId)+' (missing)</span>';
|
|
3206
|
+
h+='</div>';
|
|
3207
|
+
var desc=t.reportSummary||(t.description?t.description.split("\n")[0]:"");
|
|
3208
|
+
if(desc)h+='<div class="arch-task-desc">'+esc(desc)+'</div>';
|
|
3209
|
+
h+='</div>';
|
|
3210
|
+
return h;
|
|
3211
|
+
}
|
|
3212
|
+
|
|
3030
3213
|
// ── Chat Panel ──
|
|
3031
3214
|
var chatMessages=[], chatOpen=false, chatStreaming=false;
|
|
3032
3215
|
|
|
@@ -3036,9 +3219,9 @@ function openChat(){
|
|
|
3036
3219
|
var panel=document.getElementById("chatPanel");
|
|
3037
3220
|
panel.classList.add("open");
|
|
3038
3221
|
// restore saved size
|
|
3039
|
-
var savedH=localStorage.getItem("
|
|
3040
|
-
var savedW=localStorage.getItem("
|
|
3041
|
-
var savedExp=localStorage.getItem("
|
|
3222
|
+
var savedH=localStorage.getItem("apex_chat_height");
|
|
3223
|
+
var savedW=localStorage.getItem("apex_chat_width");
|
|
3224
|
+
var savedExp=localStorage.getItem("apex_chat_expanded")==="1";
|
|
3042
3225
|
if(savedExp){chatExpanded=true;panel.classList.add("expanded");}
|
|
3043
3226
|
else{
|
|
3044
3227
|
if(savedH)panel.style.height=savedH;
|
|
@@ -3079,11 +3262,11 @@ function loadChatHistory(){
|
|
|
3079
3262
|
}
|
|
3080
3263
|
chatLoaded=true;
|
|
3081
3264
|
}).catch(function(){chatLoaded=true;});
|
|
3082
|
-
var savedModel=localStorage.getItem("
|
|
3265
|
+
var savedModel=localStorage.getItem("apex_chat_model");
|
|
3083
3266
|
if(savedModel){var sel=document.getElementById("chatModel");if(sel)sel.value=savedModel;}
|
|
3084
3267
|
}
|
|
3085
3268
|
function onChatModelChange(el){
|
|
3086
|
-
localStorage.setItem("
|
|
3269
|
+
localStorage.setItem("apex_chat_model",el.value);
|
|
3087
3270
|
fetch("/api/chat/session",{method:"DELETE"}).catch(function(){});
|
|
3088
3271
|
var badge=document.getElementById("chatSessionBadge");
|
|
3089
3272
|
if(badge)badge.style.display="none";
|
|
@@ -3100,7 +3283,7 @@ function toggleChatExpand(){
|
|
|
3100
3283
|
chatExpanded=!chatExpanded;
|
|
3101
3284
|
panel.classList.toggle("expanded",chatExpanded);
|
|
3102
3285
|
if(chatExpanded){panel.style.height="";panel.style.width="";}
|
|
3103
|
-
localStorage.setItem("
|
|
3286
|
+
localStorage.setItem("apex_chat_expanded",chatExpanded?"1":"0");
|
|
3104
3287
|
}
|
|
3105
3288
|
(function(){
|
|
3106
3289
|
var panel=document.getElementById("chatPanel");
|
|
@@ -3120,7 +3303,7 @@ function toggleChatExpand(){
|
|
|
3120
3303
|
function onUp(){
|
|
3121
3304
|
hHandle.classList.remove("active");
|
|
3122
3305
|
panel.style.transition="";
|
|
3123
|
-
localStorage.setItem("
|
|
3306
|
+
localStorage.setItem("apex_chat_height",panel.style.height);
|
|
3124
3307
|
document.removeEventListener("mousemove",onMove);
|
|
3125
3308
|
document.removeEventListener("mouseup",onUp);
|
|
3126
3309
|
}
|
|
@@ -3143,7 +3326,7 @@ function toggleChatExpand(){
|
|
|
3143
3326
|
function onUp(){
|
|
3144
3327
|
wHandle.classList.remove("active");
|
|
3145
3328
|
panel.style.transition="";
|
|
3146
|
-
localStorage.setItem("
|
|
3329
|
+
localStorage.setItem("apex_chat_width",panel.style.width);
|
|
3147
3330
|
document.removeEventListener("mousemove",onMove);
|
|
3148
3331
|
document.removeEventListener("mouseup",onUp);
|
|
3149
3332
|
}
|
|
@@ -3353,24 +3536,10 @@ function onExecError(data){
|
|
|
3353
3536
|
}
|
|
3354
3537
|
|
|
3355
3538
|
function approveTask(id){
|
|
3356
|
-
|
|
3357
|
-
if(feedback===null)return; // cancelled
|
|
3358
|
-
var p=apiUpdate(id,{status:"completed"});
|
|
3359
|
-
if(feedback.trim()){
|
|
3360
|
-
p.then(function(){
|
|
3361
|
-
return fetch("/api/tasks/"+id+"/comments",{method:"POST",headers:{"Content-Type":"application/json"},
|
|
3362
|
-
body:JSON.stringify({author:"reviewer",text:"**Approved** — "+feedback.trim()})});
|
|
3363
|
-
});
|
|
3364
|
-
}
|
|
3365
|
-
p.then(function(){updateLive("Approved #"+id);});
|
|
3539
|
+
apiUpdate(id,{status:"completed"}).then(function(){updateLive("Approved #"+id);});
|
|
3366
3540
|
}
|
|
3367
3541
|
function rejectTask(id){
|
|
3368
|
-
|
|
3369
|
-
if(!feedback||!feedback.trim())return;
|
|
3370
|
-
apiUpdate(id,{status:"pending"}).then(function(){
|
|
3371
|
-
return fetch("/api/tasks/"+id+"/comments",{method:"POST",headers:{"Content-Type":"application/json"},
|
|
3372
|
-
body:JSON.stringify({author:"reviewer",text:"**Rejected** — "+feedback.trim()})});
|
|
3373
|
-
}).then(function(){updateLive("Rejected #"+id+" → pending with feedback");});
|
|
3542
|
+
apiUpdate(id,{status:"pending"}).then(function(){updateLive("Rejected #"+id+" — moved to To Do");});
|
|
3374
3543
|
}
|
|
3375
3544
|
|
|
3376
3545
|
function stopExecution(){
|