taskplane 0.0.1 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +2 -20
  3. package/bin/taskplane.mjs +706 -0
  4. package/dashboard/public/app.js +900 -0
  5. package/dashboard/public/index.html +92 -0
  6. package/dashboard/public/style.css +924 -0
  7. package/dashboard/server.cjs +531 -0
  8. package/extensions/task-orchestrator.ts +28 -0
  9. package/extensions/task-runner.ts +1923 -0
  10. package/extensions/taskplane/abort.ts +466 -0
  11. package/extensions/taskplane/config.ts +102 -0
  12. package/extensions/taskplane/discovery.ts +988 -0
  13. package/extensions/taskplane/engine.ts +758 -0
  14. package/extensions/taskplane/execution.ts +1752 -0
  15. package/extensions/taskplane/extension.ts +577 -0
  16. package/extensions/taskplane/formatting.ts +718 -0
  17. package/extensions/taskplane/git.ts +38 -0
  18. package/extensions/taskplane/index.ts +22 -0
  19. package/extensions/taskplane/merge.ts +795 -0
  20. package/extensions/taskplane/messages.ts +134 -0
  21. package/extensions/taskplane/persistence.ts +1121 -0
  22. package/extensions/taskplane/resume.ts +1092 -0
  23. package/extensions/taskplane/sessions.ts +92 -0
  24. package/extensions/taskplane/types.ts +1514 -0
  25. package/extensions/taskplane/waves.ts +900 -0
  26. package/extensions/taskplane/worktree.ts +1624 -0
  27. package/package.json +50 -4
  28. package/skills/create-taskplane-task/SKILL.md +326 -0
  29. package/skills/create-taskplane-task/references/context-template.md +78 -0
  30. package/skills/create-taskplane-task/references/prompt-template.md +246 -0
  31. package/templates/agents/task-merger.md +256 -0
  32. package/templates/agents/task-reviewer.md +81 -0
  33. package/templates/agents/task-worker.md +140 -0
  34. package/templates/config/task-orchestrator.yaml +89 -0
  35. package/templates/config/task-runner.yaml +99 -0
  36. package/templates/tasks/CONTEXT.md +31 -0
  37. package/templates/tasks/EXAMPLE-001-hello-world/PROMPT.md +90 -0
  38. package/templates/tasks/EXAMPLE-001-hello-world/STATUS.md +73 -0
@@ -0,0 +1,924 @@
1
+ /* ─── Reset & Base ─────────────────────────────────────────────────────── */
2
+
3
+ *,
4
+ *::before,
5
+ *::after {
6
+ box-sizing: border-box;
7
+ margin: 0;
8
+ padding: 0;
9
+ }
10
+
11
+ :root {
12
+ --bg: #0d1117;
13
+ --bg-surface: #161b22;
14
+ --bg-surface-hover: #1c2129;
15
+ --bg-inset: #0d1117;
16
+ --border: #30363d;
17
+ --border-subtle: #21262d;
18
+ --text: #e6edf3;
19
+ --text-muted: #8b949e;
20
+ --text-faint: #484f58;
21
+ --accent: #58a6ff;
22
+ --accent-dim: #1f6feb;
23
+ --green: #3fb950;
24
+ --green-dim: #238636;
25
+ --red: #f85149;
26
+ --red-dim: #da3633;
27
+ --yellow: #d29922;
28
+ --yellow-dim: #9e6a03;
29
+ --cyan: #39d2c0;
30
+ --magenta: #bc8cff;
31
+ --font-mono: "JetBrains Mono", "Fira Code", "Cascadia Code", "SF Mono", Consolas, monospace;
32
+ --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
33
+ --radius: 8px;
34
+ --radius-sm: 4px;
35
+ }
36
+
37
+ html { font-size: 14px; }
38
+
39
+ body {
40
+ font-family: var(--font-sans);
41
+ background: var(--bg);
42
+ color: var(--text);
43
+ min-height: 100vh;
44
+ overflow-x: hidden;
45
+ }
46
+
47
+ /* ─── Layout Shell ─────────────────────────────────────────────────────── */
48
+
49
+ .dashboard {
50
+ display: grid;
51
+ grid-template-rows: auto auto 1fr auto;
52
+ min-height: 100vh;
53
+ padding: 16px 20px;
54
+ gap: 12px;
55
+ max-width: 1600px;
56
+ margin: 0 auto;
57
+ }
58
+
59
+ /* ─── Header ───────────────────────────────────────────────────────────── */
60
+
61
+ .header {
62
+ display: flex;
63
+ align-items: center;
64
+ gap: 16px;
65
+ flex-wrap: wrap;
66
+ }
67
+
68
+ .header-title {
69
+ font-size: 1.35rem;
70
+ font-weight: 700;
71
+ letter-spacing: -0.02em;
72
+ white-space: nowrap;
73
+ }
74
+
75
+ .header-badge {
76
+ display: inline-flex;
77
+ align-items: center;
78
+ gap: 6px;
79
+ font-family: var(--font-mono);
80
+ font-size: 0.8rem;
81
+ padding: 3px 10px;
82
+ border-radius: 12px;
83
+ font-weight: 500;
84
+ }
85
+
86
+ .badge-batch {
87
+ background: var(--bg-surface);
88
+ border: 1px solid var(--border);
89
+ color: var(--accent);
90
+ }
91
+
92
+ .badge-phase {
93
+ font-weight: 600;
94
+ text-transform: uppercase;
95
+ letter-spacing: 0.05em;
96
+ font-size: 0.7rem;
97
+ }
98
+
99
+ .phase-executing { background: var(--accent-dim); color: #fff; }
100
+ .phase-merging { background: #1a3a5c; color: var(--accent); }
101
+ .phase-completed { background: var(--green-dim); color: #fff; }
102
+ .phase-paused { background: var(--yellow-dim); color: #fff; }
103
+ .phase-stopped,
104
+ .phase-aborted { background: var(--red-dim); color: #fff; }
105
+ .phase-failed { background: var(--red-dim); color: #fff; }
106
+
107
+ .header-meta {
108
+ margin-left: auto;
109
+ display: flex;
110
+ align-items: center;
111
+ gap: 12px;
112
+ color: var(--text-muted);
113
+ font-size: 0.8rem;
114
+ }
115
+
116
+ .connection-dot {
117
+ width: 8px;
118
+ height: 8px;
119
+ border-radius: 50%;
120
+ display: inline-block;
121
+ }
122
+
123
+ .connection-dot.connected { background: var(--green); }
124
+ .connection-dot.disconnected { background: var(--red); }
125
+
126
+ /* ─── Summary Bar ──────────────────────────────────────────────────────── */
127
+
128
+ .summary-bar {
129
+ display: flex;
130
+ align-items: center;
131
+ gap: 16px;
132
+ padding: 12px 16px;
133
+ background: var(--bg-surface);
134
+ border: 1px solid var(--border);
135
+ border-radius: var(--radius);
136
+ flex-wrap: wrap;
137
+ }
138
+
139
+ .summary-progress {
140
+ display: flex;
141
+ align-items: center;
142
+ gap: 10px;
143
+ flex-shrink: 0;
144
+ }
145
+
146
+ .progress-bar-bg {
147
+ width: 280px;
148
+ height: 18px;
149
+ background: var(--bg-inset);
150
+ border-radius: 4px;
151
+ overflow: hidden;
152
+ display: flex;
153
+ position: relative;
154
+ }
155
+
156
+ /* Individual wave segment within the segmented bar */
157
+ .wave-seg {
158
+ position: relative;
159
+ height: 100%;
160
+ overflow: hidden;
161
+ border-right: 1px solid var(--bg-deeper, rgba(0,0,0,0.3));
162
+ }
163
+ .wave-seg:last-child { border-right: none; }
164
+
165
+ /* The filled portion within each segment */
166
+ .wave-seg-fill {
167
+ height: 100%;
168
+ transition: width 0.5s ease, background-color 0.5s ease;
169
+ }
170
+ .wave-seg-fill.pct-0 { background: transparent; }
171
+ .wave-seg-fill.pct-low { background: var(--yellow); }
172
+ .wave-seg-fill.pct-mid { background: var(--cyan); }
173
+ .wave-seg-fill.pct-hi { background: var(--green); }
174
+
175
+ /* Wave label centered in each segment */
176
+ .wave-seg-label {
177
+ position: absolute;
178
+ top: 0; left: 0; right: 0; bottom: 0;
179
+ display: flex;
180
+ align-items: center;
181
+ justify-content: center;
182
+ font-size: 0.6rem;
183
+ font-weight: 700;
184
+ letter-spacing: 0.03em;
185
+ color: rgba(255,255,255,0.85);
186
+ pointer-events: none;
187
+ text-shadow: 0 1px 2px rgba(0,0,0,0.6);
188
+ }
189
+
190
+ /* Highlight the active wave segment */
191
+ .wave-seg-current { box-shadow: inset 0 0 0 1px var(--accent); }
192
+
193
+ /* Future waves: visible but subdued */
194
+ .wave-seg-future {
195
+ background: rgba(139,148,158,0.25);
196
+ }
197
+ .wave-seg-future .wave-seg-label {
198
+ color: rgba(255,255,255,0.55);
199
+ text-shadow: none;
200
+ }
201
+
202
+ /* Legacy single-bar fill (still used by per-task progress bars) */
203
+ .progress-bar-fill {
204
+ height: 100%;
205
+ border-radius: 4px;
206
+ transition: width 0.5s ease, background-color 0.5s ease;
207
+ }
208
+ .progress-bar-fill.pct-0 { background: var(--text-faint); }
209
+ .progress-bar-fill.pct-low { background: var(--yellow); }
210
+ .progress-bar-fill.pct-mid { background: var(--cyan); }
211
+ .progress-bar-fill.pct-hi { background: var(--green); }
212
+
213
+ .summary-pct {
214
+ font-family: var(--font-mono);
215
+ font-size: 0.95rem;
216
+ font-weight: 600;
217
+ min-width: 38px;
218
+ }
219
+
220
+ .summary-counts {
221
+ display: flex;
222
+ gap: 10px;
223
+ font-family: var(--font-mono);
224
+ font-size: 0.85rem;
225
+ }
226
+
227
+ .count-chip {
228
+ display: inline-flex;
229
+ align-items: center;
230
+ gap: 4px;
231
+ padding: 2px 10px;
232
+ border-radius: 10px;
233
+ font-weight: 500;
234
+ margin-right: 4px;
235
+ }
236
+ .count-num { font-variant-numeric: tabular-nums; }
237
+ .count-icon { font-size: 0.85em; opacity: 0.9; }
238
+ .count-total { color: var(--text-faint); margin-left: 2px; }
239
+
240
+ .count-succeeded { background: rgba(63,185,80,0.15); color: var(--green); }
241
+ .count-running { background: rgba(88,166,255,0.15); color: var(--accent); }
242
+ .count-failed { background: rgba(248,81,73,0.15); color: var(--red); }
243
+ .count-stalled { background: rgba(210,153,34,0.15); color: var(--yellow); }
244
+ .count-pending { background: rgba(139,148,158,0.1); color: var(--text-muted); }
245
+
246
+ .summary-elapsed {
247
+ color: var(--text-muted);
248
+ font-size: 0.8rem;
249
+ font-family: var(--font-mono);
250
+ }
251
+
252
+ .summary-waves {
253
+ display: flex;
254
+ align-items: center;
255
+ gap: 6px;
256
+ font-size: 0.8rem;
257
+ }
258
+
259
+ .wave-chip {
260
+ padding: 2px 8px;
261
+ border-radius: 10px;
262
+ font-family: var(--font-mono);
263
+ font-size: 0.75rem;
264
+ font-weight: 500;
265
+ border: 1px solid var(--border);
266
+ color: var(--text-muted);
267
+ }
268
+
269
+ .wave-chip.current {
270
+ border-color: var(--accent-dim);
271
+ background: rgba(88,166,255,0.12);
272
+ color: var(--accent);
273
+ font-weight: 700;
274
+ }
275
+
276
+ .wave-chip.done {
277
+ border-color: var(--green-dim);
278
+ background: rgba(63,185,80,0.12);
279
+ color: var(--green);
280
+ }
281
+
282
+ /* ─── Content ──────────────────────────────────────────────────────────── */
283
+
284
+ .content {
285
+ display: flex;
286
+ flex-direction: column;
287
+ gap: 12px;
288
+ min-height: 0;
289
+ }
290
+
291
+ /* ─── Panels (shared) ──────────────────────────────────────────────────── */
292
+
293
+ .panel {
294
+ background: var(--bg-surface);
295
+ border: 1px solid var(--border);
296
+ border-radius: var(--radius);
297
+ overflow: hidden;
298
+ display: flex;
299
+ flex-direction: column;
300
+ }
301
+
302
+ .panel-header {
303
+ padding: 10px 14px;
304
+ font-size: 0.75rem;
305
+ font-weight: 600;
306
+ text-transform: uppercase;
307
+ letter-spacing: 0.06em;
308
+ color: var(--text-muted);
309
+ border-bottom: 1px solid var(--border);
310
+ background: var(--bg-surface);
311
+ }
312
+
313
+ .panel-body {
314
+ padding: 0;
315
+ flex: 1;
316
+ overflow-y: auto;
317
+ }
318
+
319
+ .empty-state {
320
+ padding: 24px;
321
+ text-align: center;
322
+ color: var(--text-faint);
323
+ font-size: 0.85rem;
324
+ }
325
+
326
+ /* ─── Lane Group (lane header + nested task rows) ──────────────────────── */
327
+
328
+ .lane-group {
329
+ border-bottom: 1px solid var(--border);
330
+ }
331
+
332
+ .lane-group:last-child { border-bottom: none; }
333
+
334
+ .lane-header {
335
+ display: grid;
336
+ grid-template-columns: 36px 1fr auto;
337
+ align-items: center;
338
+ gap: 12px;
339
+ padding: 10px 14px;
340
+ background: var(--bg-inset);
341
+ border-bottom: 1px solid var(--border-subtle);
342
+ }
343
+
344
+ .lane-num {
345
+ font-family: var(--font-mono);
346
+ font-size: 1rem;
347
+ font-weight: 700;
348
+ color: var(--text-muted);
349
+ text-align: center;
350
+ }
351
+
352
+ .lane-meta {
353
+ display: flex;
354
+ align-items: center;
355
+ gap: 10px;
356
+ min-width: 0;
357
+ }
358
+
359
+ .lane-session {
360
+ font-family: var(--font-mono);
361
+ font-size: 0.82rem;
362
+ color: var(--accent);
363
+ white-space: nowrap;
364
+ }
365
+
366
+ .lane-branch {
367
+ font-size: 0.72rem;
368
+ color: var(--text-muted);
369
+ white-space: nowrap;
370
+ overflow: hidden;
371
+ text-overflow: ellipsis;
372
+ }
373
+
374
+ .lane-right {
375
+ display: flex;
376
+ align-items: center;
377
+ gap: 10px;
378
+ }
379
+
380
+ .tmux-dot {
381
+ width: 8px;
382
+ height: 8px;
383
+ border-radius: 50%;
384
+ flex-shrink: 0;
385
+ }
386
+
387
+ .tmux-dot.alive { background: var(--green); }
388
+ .tmux-dot.dead { background: var(--red); opacity: 0.6; }
389
+
390
+ .tmux-view-btn {
391
+ font-family: var(--font-sans);
392
+ font-size: 0.72rem;
393
+ font-weight: 600;
394
+ padding: 3px 10px;
395
+ border-radius: var(--radius-sm);
396
+ background: var(--accent-dim);
397
+ border: 1px solid var(--accent-dim);
398
+ color: #fff;
399
+ cursor: pointer;
400
+ white-space: nowrap;
401
+ transition: background 0.2s, border-color 0.2s;
402
+ }
403
+
404
+ .tmux-view-btn:hover {
405
+ background: var(--accent);
406
+ border-color: var(--accent);
407
+ }
408
+
409
+ .tmux-cmd {
410
+ font-family: var(--font-mono);
411
+ font-size: 0.7rem;
412
+ padding: 3px 8px;
413
+ border-radius: var(--radius-sm);
414
+ background: var(--bg);
415
+ border: 1px solid var(--border);
416
+ color: var(--text);
417
+ cursor: pointer;
418
+ user-select: all;
419
+ white-space: nowrap;
420
+ transition: border-color 0.2s, color 0.2s;
421
+ }
422
+
423
+ .tmux-cmd:hover {
424
+ border-color: var(--accent-dim);
425
+ color: var(--accent);
426
+ }
427
+
428
+ .tmux-cmd.copied {
429
+ border-color: var(--green-dim);
430
+ color: var(--green);
431
+ }
432
+
433
+ .tmux-cmd.dead-session {
434
+ color: var(--text-muted);
435
+ cursor: default;
436
+ }
437
+
438
+ /* ─── Task Row (inside lane group) ─────────────────────────────────────── */
439
+
440
+ .task-row {
441
+ display: grid;
442
+ grid-template-columns: 36px 100px 90px 80px 200px 1fr;
443
+ align-items: center;
444
+ gap: 8px;
445
+ padding: 8px 14px;
446
+ border-bottom: 1px solid var(--border-subtle);
447
+ transition: background 0.15s;
448
+ }
449
+
450
+ .task-row:last-child { border-bottom: none; }
451
+ .task-row:hover { background: var(--bg-surface-hover); }
452
+
453
+ .task-icon {
454
+ text-align: center;
455
+ }
456
+
457
+ .task-id {
458
+ font-family: var(--font-mono);
459
+ font-weight: 600;
460
+ font-size: 0.85rem;
461
+ }
462
+
463
+ .task-duration {
464
+ font-family: var(--font-mono);
465
+ font-size: 0.8rem;
466
+ color: var(--text-muted);
467
+ }
468
+
469
+ .task-step {
470
+ font-size: 0.8rem;
471
+ color: var(--text-muted);
472
+ white-space: nowrap;
473
+ overflow: hidden;
474
+ text-overflow: ellipsis;
475
+ }
476
+
477
+ .task-iter {
478
+ font-family: var(--font-mono);
479
+ font-size: 0.7rem;
480
+ color: var(--text-faint);
481
+ margin-left: 6px;
482
+ }
483
+
484
+ /* Status badges */
485
+ .status-badge {
486
+ display: inline-flex;
487
+ align-items: center;
488
+ gap: 4px;
489
+ font-family: var(--font-mono);
490
+ font-size: 0.75rem;
491
+ font-weight: 600;
492
+ padding: 2px 8px;
493
+ border-radius: 10px;
494
+ }
495
+
496
+ .status-succeeded { background: rgba(63,185,80,0.15); color: var(--green); }
497
+ .status-running { background: rgba(88,166,255,0.15); color: var(--accent); }
498
+ .status-failed { background: rgba(248,81,73,0.15); color: var(--red); }
499
+ .status-stalled { background: rgba(210,153,34,0.15); color: var(--yellow); }
500
+ .status-pending { background: rgba(139,148,158,0.1); color: var(--text-muted); }
501
+ .status-skipped { background: rgba(139,148,158,0.1); color: var(--text-faint); }
502
+
503
+ .status-dot {
504
+ width: 6px;
505
+ height: 6px;
506
+ border-radius: 50%;
507
+ display: inline-block;
508
+ }
509
+
510
+ .status-dot.succeeded { background: var(--green); }
511
+ .status-dot.running { background: var(--accent); }
512
+ .status-dot.failed { background: var(--red); }
513
+ .status-dot.stalled { background: var(--yellow); }
514
+ .status-dot.pending { background: var(--text-faint); }
515
+ .status-dot.skipped { background: var(--text-faint); }
516
+
517
+ /* Task progress bar (inline) */
518
+ .task-progress {
519
+ display: flex;
520
+ align-items: center;
521
+ gap: 8px;
522
+ }
523
+
524
+ .task-progress-bar {
525
+ width: 80px;
526
+ height: 6px;
527
+ background: var(--bg-inset);
528
+ border-radius: 3px;
529
+ overflow: hidden;
530
+ flex-shrink: 0;
531
+ }
532
+
533
+ .task-progress-fill {
534
+ height: 100%;
535
+ border-radius: 3px;
536
+ transition: width 0.5s ease;
537
+ }
538
+
539
+ .task-progress-fill.pct-0 { background: var(--text-faint); }
540
+ .task-progress-fill.pct-low { background: var(--yellow); }
541
+ .task-progress-fill.pct-mid { background: var(--cyan); }
542
+ .task-progress-fill.pct-hi { background: var(--green); }
543
+
544
+ .task-progress-text {
545
+ font-family: var(--font-mono);
546
+ font-size: 0.75rem;
547
+ color: var(--text-muted);
548
+ min-width: 55px;
549
+ }
550
+
551
+ /* ─── Merge Agents Table ───────────────────────────────────────────────── */
552
+
553
+ .merge-table {
554
+ width: 100%;
555
+ border-collapse: collapse;
556
+ }
557
+
558
+ .merge-table th {
559
+ text-align: left;
560
+ padding: 8px 12px;
561
+ font-size: 0.7rem;
562
+ font-weight: 600;
563
+ text-transform: uppercase;
564
+ letter-spacing: 0.05em;
565
+ color: var(--text-faint);
566
+ border-bottom: 1px solid var(--border);
567
+ background: var(--bg-surface);
568
+ }
569
+
570
+ .merge-table td {
571
+ padding: 8px 12px;
572
+ border-bottom: 1px solid var(--border-subtle);
573
+ font-size: 0.85rem;
574
+ }
575
+
576
+ .merge-table tr:last-child td { border-bottom: none; }
577
+ .merge-table tr:hover td { background: var(--bg-surface-hover); }
578
+
579
+ /* ─── Terminal Panel ────────────────────────────────────────────────────── */
580
+
581
+ .terminal-panel .panel-header {
582
+ display: flex;
583
+ align-items: center;
584
+ justify-content: space-between;
585
+ }
586
+
587
+ .terminal-close {
588
+ background: none;
589
+ border: 1px solid var(--border);
590
+ color: var(--text-muted);
591
+ font-size: 0.85rem;
592
+ cursor: pointer;
593
+ padding: 2px 8px;
594
+ border-radius: var(--radius-sm);
595
+ transition: color 0.2s, border-color 0.2s;
596
+ }
597
+
598
+ .terminal-close:hover {
599
+ color: var(--red);
600
+ border-color: var(--red-dim);
601
+ }
602
+
603
+ .terminal-panel .panel-body {
604
+ max-height: 500px;
605
+ padding: 0;
606
+ background: var(--bg-inset);
607
+ overflow-y: auto;
608
+ }
609
+
610
+ /* ─── Worker Stats (inline in task row) ────────────────────────────────── */
611
+
612
+ .worker-stats {
613
+ display: flex;
614
+ align-items: center;
615
+ gap: 10px;
616
+ margin-top: 4px;
617
+ flex-wrap: wrap;
618
+ }
619
+
620
+ .worker-stat {
621
+ font-family: var(--font-mono);
622
+ font-size: 0.7rem;
623
+ color: var(--text-muted);
624
+ white-space: nowrap;
625
+ }
626
+
627
+ .worker-last-tool {
628
+ color: var(--text-muted);
629
+ max-width: 600px;
630
+ overflow: hidden;
631
+ text-overflow: ellipsis;
632
+ }
633
+
634
+ /* ─── Conversation Viewer ──────────────────────────────────────────────── */
635
+
636
+ .conv-stream {
637
+ padding: 12px 16px;
638
+ font-family: var(--font-mono);
639
+ font-size: 0.82rem;
640
+ line-height: 1.6;
641
+ white-space: pre-wrap;
642
+ word-break: break-word;
643
+ }
644
+
645
+ .conv-text {
646
+ color: var(--text);
647
+ }
648
+
649
+ .conv-thinking {
650
+ color: var(--text-faint);
651
+ font-style: italic;
652
+ }
653
+
654
+ .conv-tool-call {
655
+ margin: 8px 0 4px;
656
+ padding: 6px 10px;
657
+ background: rgba(88,166,255,0.08);
658
+ border-left: 3px solid var(--accent-dim);
659
+ border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
660
+ }
661
+
662
+ .conv-tool-name {
663
+ color: var(--accent);
664
+ font-weight: 600;
665
+ }
666
+
667
+ .conv-tool-args {
668
+ color: var(--text-muted);
669
+ font-size: 0.75rem;
670
+ }
671
+
672
+ .conv-tool-result {
673
+ margin: 0 0 8px;
674
+ padding: 6px 10px;
675
+ background: var(--bg-inset);
676
+ border-left: 3px solid var(--border);
677
+ border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
678
+ max-height: 200px;
679
+ overflow-y: auto;
680
+ }
681
+
682
+ .conv-tool-result pre {
683
+ margin: 0;
684
+ font-size: 0.75rem;
685
+ color: var(--text-muted);
686
+ white-space: pre-wrap;
687
+ }
688
+
689
+ .conv-usage {
690
+ margin: 6px 0;
691
+ padding: 4px 10px;
692
+ font-size: 0.7rem;
693
+ color: var(--text-faint);
694
+ border-top: 1px solid var(--border-subtle);
695
+ }
696
+
697
+ .conv-empty {
698
+ padding: 40px;
699
+ text-align: center;
700
+ color: var(--text-faint);
701
+ }
702
+
703
+ /* ─── Errors Panel ─────────────────────────────────────────────────────── */
704
+
705
+ .errors-panel .panel-header { color: var(--red); }
706
+
707
+ .error-item {
708
+ padding: 8px 14px;
709
+ font-family: var(--font-mono);
710
+ font-size: 0.78rem;
711
+ color: var(--red);
712
+ border-bottom: 1px solid var(--border-subtle);
713
+ display: flex;
714
+ align-items: flex-start;
715
+ gap: 8px;
716
+ }
717
+
718
+ .error-item:last-child { border-bottom: none; }
719
+ .error-bullet { flex-shrink: 0; margin-top: 2px; }
720
+ .error-text { word-break: break-word; }
721
+
722
+ /* ─── Footer ───────────────────────────────────────────────────────────── */
723
+
724
+ .footer {
725
+ display: flex;
726
+ align-items: center;
727
+ justify-content: space-between;
728
+ padding: 8px 4px;
729
+ font-size: 0.75rem;
730
+ color: var(--text-faint);
731
+ }
732
+
733
+ /* ─── No Batch State ───────────────────────────────────────────────────── */
734
+
735
+ .no-batch {
736
+ display: flex;
737
+ flex-direction: column;
738
+ align-items: center;
739
+ justify-content: center;
740
+ gap: 12px;
741
+ padding: 80px 20px;
742
+ text-align: center;
743
+ }
744
+
745
+ .no-batch-icon { font-size: 3rem; opacity: 0.3; }
746
+ .no-batch-title { font-size: 1.1rem; font-weight: 600; color: var(--text-muted); }
747
+ .no-batch-hint { font-size: 0.85rem; color: var(--text-faint); font-family: var(--font-mono); }
748
+
749
+ /* ─── Responsive ───────────────────────────────────────────────────────── */
750
+
751
+ @media (max-width: 900px) {
752
+ .task-row {
753
+ grid-template-columns: 36px 90px 80px 70px 1fr;
754
+ }
755
+ .task-row .task-step { display: none; }
756
+ .progress-bar-bg { width: 160px; }
757
+ }
758
+
759
+ /* ─── Scrollbar ────────────────────────────────────────────────────────── */
760
+
761
+ ::-webkit-scrollbar { width: 6px; }
762
+ ::-webkit-scrollbar-track { background: transparent; }
763
+ ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
764
+ ::-webkit-scrollbar-thumb:hover { background: var(--text-faint); }
765
+
766
+ /* ─── Pulse animation ──────────────────────────────────────────────────── */
767
+
768
+ @keyframes pulse {
769
+ 0%, 100% { opacity: 1; }
770
+ 50% { opacity: 0.5; }
771
+ }
772
+
773
+ .status-running .status-dot { animation: pulse 2s infinite; }
774
+
775
+ /* ─── Copy toast ───────────────────────────────────────────────────────── */
776
+
777
+ .copy-toast {
778
+ position: fixed;
779
+ bottom: 20px;
780
+ right: 20px;
781
+ background: var(--green-dim);
782
+ color: #fff;
783
+ padding: 8px 16px;
784
+ border-radius: var(--radius);
785
+ font-size: 0.8rem;
786
+ font-family: var(--font-mono);
787
+ opacity: 0;
788
+ transform: translateY(8px);
789
+ transition: opacity 0.2s, transform 0.2s;
790
+ pointer-events: none;
791
+ z-index: 100;
792
+ }
793
+
794
+ .copy-toast.visible {
795
+ opacity: 1;
796
+ transform: translateY(0);
797
+ }
798
+
799
+ /* ── History Dropdown ────────────────────────────────────────────────────── */
800
+
801
+ .history-select {
802
+ background: var(--bg-surface);
803
+ border: 1px solid var(--border);
804
+ color: var(--text-muted);
805
+ font-family: var(--font-mono);
806
+ font-size: 0.8rem;
807
+ padding: 3px 8px;
808
+ border-radius: 8px;
809
+ cursor: pointer;
810
+ max-width: 260px;
811
+ }
812
+ .history-select:hover {
813
+ border-color: var(--accent);
814
+ color: var(--text-primary);
815
+ }
816
+ .history-select:focus {
817
+ outline: none;
818
+ border-color: var(--accent);
819
+ }
820
+
821
+ /* ── History Summary Panel ───────────────────────────────────────────────── */
822
+
823
+ .history-panel .panel-body {
824
+ padding: 16px;
825
+ }
826
+
827
+ .history-header {
828
+ display: flex;
829
+ align-items: center;
830
+ gap: 16px;
831
+ margin-bottom: 16px;
832
+ flex-wrap: wrap;
833
+ }
834
+
835
+ .history-header .batch-id {
836
+ font-family: var(--font-mono);
837
+ font-size: 1.1rem;
838
+ font-weight: 700;
839
+ color: var(--accent);
840
+ }
841
+
842
+ .history-header .batch-status {
843
+ font-size: 0.85rem;
844
+ padding: 2px 10px;
845
+ border-radius: 12px;
846
+ font-weight: 600;
847
+ }
848
+
849
+ .history-header .batch-status.completed { background: rgba(63,185,80,0.15); color: var(--green); }
850
+ .history-header .batch-status.partial { background: rgba(210,153,34,0.15); color: var(--yellow); }
851
+ .history-header .batch-status.failed { background: rgba(248,81,73,0.15); color: var(--red); }
852
+ .history-header .batch-status.aborted { background: rgba(139,148,158,0.15); color: var(--text-muted); }
853
+
854
+ .history-header .batch-time {
855
+ color: var(--text-muted);
856
+ font-size: 0.85rem;
857
+ }
858
+
859
+ .history-stats {
860
+ display: grid;
861
+ grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
862
+ gap: 12px;
863
+ margin-bottom: 16px;
864
+ }
865
+
866
+ .stat-card {
867
+ background: var(--bg-surface);
868
+ border: 1px solid var(--border);
869
+ border-radius: 8px;
870
+ padding: 12px;
871
+ text-align: center;
872
+ }
873
+
874
+ .stat-card .stat-value {
875
+ font-family: var(--font-mono);
876
+ font-size: 1.3rem;
877
+ font-weight: 700;
878
+ color: var(--text-primary);
879
+ }
880
+
881
+ .stat-card .stat-label {
882
+ font-size: 0.75rem;
883
+ color: var(--text-muted);
884
+ margin-top: 2px;
885
+ }
886
+
887
+ .stat-card.stat-tokens .stat-value {
888
+ font-size: 0.95rem;
889
+ color: var(--accent);
890
+ }
891
+
892
+ .history-waves-table,
893
+ .history-tasks-table {
894
+ width: 100%;
895
+ border-collapse: collapse;
896
+ margin-bottom: 16px;
897
+ }
898
+
899
+ .history-waves-table th,
900
+ .history-tasks-table th {
901
+ text-align: left;
902
+ padding: 6px 10px;
903
+ border-bottom: 1px solid var(--border);
904
+ font-size: 0.75rem;
905
+ color: var(--text-muted);
906
+ text-transform: uppercase;
907
+ letter-spacing: 0.05em;
908
+ }
909
+
910
+ .history-waves-table td,
911
+ .history-tasks-table td {
912
+ padding: 6px 10px;
913
+ border-bottom: 1px solid rgba(48,54,61,0.3);
914
+ font-family: var(--font-mono);
915
+ font-size: 0.85rem;
916
+ color: var(--text-muted);
917
+ }
918
+
919
+ .history-section-title {
920
+ font-size: 0.9rem;
921
+ font-weight: 600;
922
+ color: var(--text-primary);
923
+ margin: 16px 0 8px;
924
+ }