claude-controller 0.1.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +2 -2
  2. package/bin/autoloop.sh +382 -0
  3. package/bin/ctl +1189 -0
  4. package/bin/native-app.py +6 -3
  5. package/bin/watchdog.sh +357 -0
  6. package/cognitive/__init__.py +14 -0
  7. package/cognitive/__pycache__/__init__.cpython-314.pyc +0 -0
  8. package/cognitive/__pycache__/dispatcher.cpython-314.pyc +0 -0
  9. package/cognitive/__pycache__/evaluator.cpython-314.pyc +0 -0
  10. package/cognitive/__pycache__/goal_engine.cpython-314.pyc +0 -0
  11. package/cognitive/__pycache__/learning.cpython-314.pyc +0 -0
  12. package/cognitive/__pycache__/orchestrator.cpython-314.pyc +0 -0
  13. package/cognitive/__pycache__/planner.cpython-314.pyc +0 -0
  14. package/cognitive/dispatcher.py +192 -0
  15. package/cognitive/evaluator.py +289 -0
  16. package/cognitive/goal_engine.py +232 -0
  17. package/cognitive/learning.py +189 -0
  18. package/cognitive/orchestrator.py +303 -0
  19. package/cognitive/planner.py +207 -0
  20. package/cognitive/prompts/analyst.md +31 -0
  21. package/cognitive/prompts/coder.md +22 -0
  22. package/cognitive/prompts/reviewer.md +33 -0
  23. package/cognitive/prompts/tester.md +21 -0
  24. package/cognitive/prompts/writer.md +25 -0
  25. package/config.sh +6 -1
  26. package/dag/__init__.py +5 -0
  27. package/dag/__pycache__/__init__.cpython-314.pyc +0 -0
  28. package/dag/__pycache__/graph.cpython-314.pyc +0 -0
  29. package/dag/graph.py +222 -0
  30. package/lib/jobs.sh +12 -1
  31. package/package.json +11 -5
  32. package/postinstall.sh +1 -1
  33. package/service/controller.sh +43 -11
  34. package/web/audit.py +122 -0
  35. package/web/checkpoint.py +80 -0
  36. package/web/config.py +2 -5
  37. package/web/handler.py +634 -473
  38. package/web/handler_fs.py +153 -0
  39. package/web/handler_goals.py +203 -0
  40. package/web/handler_jobs.py +372 -0
  41. package/web/handler_memory.py +203 -0
  42. package/web/handler_sessions.py +132 -0
  43. package/web/jobs.py +585 -13
  44. package/web/personas.py +419 -0
  45. package/web/pipeline.py +981 -0
  46. package/web/presets.py +506 -0
  47. package/web/projects.py +246 -0
  48. package/web/static/api.js +141 -0
  49. package/web/static/app.js +25 -1937
  50. package/web/static/attachments.js +144 -0
  51. package/web/static/base.css +497 -0
  52. package/web/static/context.js +204 -0
  53. package/web/static/dirs.js +246 -0
  54. package/web/static/form.css +763 -0
  55. package/web/static/goals.css +363 -0
  56. package/web/static/goals.js +300 -0
  57. package/web/static/i18n.js +625 -0
  58. package/web/static/index.html +215 -13
  59. package/web/static/{styles.css → jobs.css} +746 -1141
  60. package/web/static/jobs.js +1270 -0
  61. package/web/static/memoryview.js +117 -0
  62. package/web/static/personas.js +228 -0
  63. package/web/static/pipeline.css +338 -0
  64. package/web/static/pipelines.js +487 -0
  65. package/web/static/presets.js +244 -0
  66. package/web/static/send.js +135 -0
  67. package/web/static/settings-style.css +291 -0
  68. package/web/static/settings.js +81 -0
  69. package/web/static/stream.js +534 -0
  70. package/web/static/utils.js +131 -0
  71. package/web/webhook.py +210 -0
@@ -1,1087 +1,482 @@
1
- *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
2
-
3
- :root {
4
- --bg: #0f1117;
5
- --surface: #1a1d27;
6
- --surface-hover: #222531;
7
- --surface-active: #2a2d3a;
8
- --border: #2e3140;
9
- --border-light: #3a3d4a;
10
- --text: #e4e6ed;
11
- --text-secondary: #8b8fa3;
12
- --text-muted: #5c5f73;
13
- --accent: #6c8cff;
14
- --accent-hover: #8aa4ff;
15
- --accent-glow: rgba(108, 140, 255, 0.15);
16
- --green: #34d399;
17
- --green-dim: rgba(52, 211, 153, 0.15);
18
- --red: #f87171;
19
- --red-dim: rgba(248, 113, 113, 0.15);
20
- --yellow: #fbbf24;
21
- --yellow-dim: rgba(251, 191, 36, 0.15);
22
- --blue: #60a5fa;
23
- --blue-dim: rgba(96, 165, 250, 0.15);
24
- --radius: 8px;
25
- --radius-lg: 12px;
26
- --font: 'Noto Sans KR', system-ui, -apple-system, sans-serif;
27
- --font-mono: 'JetBrains Mono', 'SF Mono', 'Consolas', monospace;
28
- --transition: 0.2s ease;
29
- --shadow: 0 2px 8px rgba(0,0,0,0.3);
30
- }
31
-
32
- html { font-size: 14px; }
33
-
34
- body {
35
- font-family: var(--font);
1
+ /* ── Jobs & Stream ── */
2
+
3
+ /* ── Stats Bar ── */
4
+ .stats-bar {
5
+ display: flex;
6
+ align-items: center;
7
+ gap: 12px;
8
+ padding: 8px 18px;
9
+ border-bottom: 1px solid var(--border);
36
10
  background: var(--bg);
37
- color: var(--text);
38
- min-height: 100vh;
39
- line-height: 1.6;
40
- -webkit-font-smoothing: antialiased;
41
- max-width: 100vw;
42
- overflow-x: hidden;
11
+ font-size: 0.72rem;
43
12
  }
44
13
 
45
- /* ── Scrollbar ── */
46
- ::-webkit-scrollbar { width: 6px; height: 6px; }
47
- ::-webkit-scrollbar-track { background: transparent; }
48
- ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
49
- ::-webkit-scrollbar-thumb:hover { background: var(--border-light); }
14
+ .stats-period-btns {
15
+ display: flex;
16
+ gap: 2px;
17
+ flex-shrink: 0;
18
+ }
50
19
 
51
- /* ── Buttons ── */
52
- button {
20
+ .stats-period-btn {
21
+ padding: 3px 8px;
22
+ border-radius: var(--radius);
23
+ font-size: 0.68rem;
24
+ font-weight: 600;
53
25
  font-family: var(--font);
26
+ background: transparent;
27
+ color: var(--text-muted);
28
+ border: 1px solid transparent;
54
29
  cursor: pointer;
55
- border: none;
56
- outline: none;
57
- transition: all var(--transition);
30
+ transition: all 0.15s ease;
58
31
  }
59
32
 
60
- .btn {
61
- display: inline-flex;
62
- align-items: center;
63
- gap: 6px;
64
- padding: 7px 14px;
65
- border-radius: var(--radius);
66
- font-size: 0.82rem;
67
- font-weight: 500;
33
+ .stats-period-btn:hover {
68
34
  background: var(--surface-hover);
69
35
  color: var(--text-secondary);
70
- border: 1px solid var(--border);
71
- }
72
-
73
- .btn:hover {
74
- background: var(--surface-active);
75
- color: var(--text);
76
- border-color: var(--border-light);
77
36
  }
78
37
 
79
- .btn:active { transform: scale(0.97); }
80
-
81
- .btn-primary {
82
- background: var(--accent);
83
- color: #fff;
38
+ .stats-period-btn.active {
39
+ background: var(--accent-glow);
40
+ color: var(--accent);
84
41
  border-color: var(--accent);
85
42
  }
86
43
 
87
- .btn-primary:hover {
88
- background: var(--accent-hover);
89
- border-color: var(--accent-hover);
90
- box-shadow: 0 0 20px var(--accent-glow);
91
- }
92
-
93
- .btn-danger {
94
- color: var(--red);
44
+ .stats-metrics {
45
+ display: flex;
46
+ align-items: center;
47
+ gap: 16px;
48
+ margin-left: auto;
49
+ font-family: var(--font-mono, monospace);
50
+ font-size: 0.72rem;
51
+ color: var(--text-secondary);
95
52
  }
96
53
 
97
- .btn-danger:hover {
98
- background: var(--red-dim);
99
- border-color: var(--red);
54
+ .stats-metric {
55
+ white-space: nowrap;
100
56
  }
101
57
 
102
- .btn-success {
103
- color: var(--green);
104
- }
58
+ .stats-metric.stats-success { color: var(--success, #22c55e); }
59
+ .stats-metric.stats-duration { color: var(--text-muted); }
105
60
 
106
- .btn-success:hover {
107
- background: var(--green-dim);
108
- border-color: var(--green);
61
+ /* ── Project Strip ── */
62
+ .project-strip {
63
+ display: flex;
64
+ align-items: center;
65
+ gap: 6px;
66
+ padding: 10px 0 10px 18px;
67
+ border-bottom: 1px solid var(--border);
68
+ overflow-x: auto;
69
+ -webkit-overflow-scrolling: touch;
70
+ scrollbar-width: none;
109
71
  }
110
-
111
- .btn-sm {
112
- padding: 4px 10px;
113
- font-size: 0.75rem;
72
+ .project-strip::after {
73
+ content: '';
74
+ flex-shrink: 0;
75
+ width: 18px;
114
76
  }
77
+ .project-strip::-webkit-scrollbar { display: none; }
78
+ .project-strip:empty { display: none; }
115
79
 
116
- .btn-icon {
117
- padding: 7px;
80
+ .project-chip {
118
81
  display: inline-flex;
119
82
  align-items: center;
120
- justify-content: center;
83
+ gap: 6px;
84
+ padding: 6px 14px;
85
+ border-radius: 20px;
86
+ border: 1px solid var(--border);
87
+ background: var(--surface);
88
+ color: var(--text-secondary);
89
+ font-size: 0.74rem;
90
+ font-weight: 500;
91
+ cursor: pointer;
92
+ white-space: nowrap;
93
+ transition: all 0.15s ease;
94
+ flex-shrink: 0;
95
+ line-height: 1;
121
96
  }
122
97
 
123
- /* ── Context Toolbar ── */
124
- .ctx-toolbar-wrap {
125
- position: relative;
126
- flex-shrink: 0;
98
+ .project-chip:hover {
99
+ background: var(--surface-hover);
100
+ border-color: var(--text-muted);
127
101
  }
128
102
 
129
- .ctx-toolbar {
130
- display: flex;
131
- align-items: center;
132
- gap: 2px;
103
+ .project-chip.active {
104
+ background: var(--accent-glow);
105
+ border-color: var(--accent);
106
+ color: var(--accent);
107
+ }
108
+
109
+ .pchip-icon {
133
110
  flex-shrink: 0;
111
+ opacity: 0.5;
134
112
  }
135
113
 
136
- .ctx-btn {
137
- padding: 4px 10px;
138
- border-radius: var(--radius);
139
- font-size: 0.72rem;
140
- font-weight: 600;
141
- font-family: var(--font-mono);
142
- background: transparent;
143
- color: var(--text-muted);
144
- border: 1px solid transparent;
145
- letter-spacing: 0.02em;
146
- text-transform: lowercase;
114
+ .project-chip.active .pchip-icon {
115
+ opacity: 1;
147
116
  }
148
117
 
149
- .ctx-btn:hover {
150
- background: var(--surface-hover);
151
- color: var(--text-secondary);
152
- border-color: var(--border);
118
+ .pchip-dot {
119
+ width: 6px;
120
+ height: 6px;
121
+ border-radius: 50%;
122
+ background: var(--blue);
123
+ animation: pulse 1.5s ease-in-out infinite;
124
+ flex-shrink: 0;
153
125
  }
154
126
 
155
- .ctx-btn.active {
156
- background: var(--accent-glow);
157
- color: var(--accent);
158
- border-color: var(--accent);
127
+ .pchip-name {
128
+ font-family: var(--font-mono);
159
129
  }
160
130
 
161
- .ctx-btn-x {
162
- padding: 4px 6px;
163
- border-radius: var(--radius);
164
- font-size: 0.78rem;
165
- font-weight: 500;
166
- background: transparent;
131
+ .pchip-count {
132
+ font-size: 0.66rem;
133
+ font-family: var(--font-mono);
134
+ padding: 2px 7px;
135
+ border-radius: 10px;
136
+ background: var(--bg);
167
137
  color: var(--text-muted);
168
- border: 1px solid transparent;
138
+ font-weight: 600;
139
+ min-width: 18px;
140
+ text-align: center;
169
141
  line-height: 1;
170
142
  display: inline-flex;
171
143
  align-items: center;
172
144
  justify-content: center;
173
145
  }
174
146
 
175
- .ctx-btn-x:hover {
176
- background: var(--red-dim);
177
- color: var(--red);
178
- border-color: var(--red);
147
+ .project-chip.active .pchip-count {
148
+ background: var(--accent);
149
+ color: var(--surface);
179
150
  }
180
151
 
181
- .ctx-session-label {
182
- display: none;
183
- padding: 2px 8px;
184
- border-radius: 12px;
185
- font-size: 0.7rem;
152
+ .pchip-stat {
153
+ font-size: 0.62rem;
186
154
  font-family: var(--font-mono);
187
- background: var(--blue-dim);
188
- color: var(--blue);
189
- overflow: hidden;
190
- text-overflow: ellipsis;
191
- white-space: nowrap;
192
- line-height: 1.6;
193
- min-width: 0;
155
+ font-weight: 700;
156
+ padding: 2px 5px;
157
+ border-radius: 8px;
158
+ min-width: 14px;
159
+ text-align: center;
160
+ line-height: 1;
161
+ display: inline-flex;
162
+ align-items: center;
163
+ justify-content: center;
194
164
  }
195
165
 
196
- .ctx-session-label.visible {
197
- display: inline;
166
+ .pchip-stat-running {
167
+ background: var(--blue-dim);
168
+ color: var(--blue);
198
169
  }
199
170
 
200
- .session-picker {
201
- display: none;
202
- flex-direction: column;
203
- position: absolute;
204
- top: 100%;
205
- right: 0;
206
- margin-top: 4px;
207
- width: 520px;
208
- max-width: calc(100vw - 32px);
209
- max-height: 420px;
210
- background: var(--surface);
211
- border: 1px solid var(--border);
212
- border-radius: var(--radius-lg);
213
- box-shadow: 0 8px 32px rgba(0,0,0,0.4);
214
- z-index: 200;
171
+ .pchip-stat-done {
172
+ background: var(--green-dim);
173
+ color: var(--green);
215
174
  }
216
175
 
217
- .session-picker.open { display: flex; }
218
-
219
- .session-picker-header {
220
- display: flex;
221
- align-items: center;
222
- justify-content: space-between;
223
- padding: 10px 14px;
224
- border-bottom: 1px solid var(--border);
225
- flex-shrink: 0;
176
+ .pchip-stat-failed {
177
+ background: var(--red-dim);
178
+ color: var(--red);
226
179
  }
227
180
 
228
- .session-picker-title {
229
- font-size: 0.78rem;
230
- font-weight: 600;
231
- color: var(--text-secondary);
181
+ /* ── Registered Project Badge ── */
182
+ .project-chip.registered {
183
+ border-style: solid;
184
+ border-width: 1.5px;
232
185
  }
233
186
 
234
- .session-picker-search {
235
- padding: 8px 14px;
236
- border-bottom: 1px solid var(--border);
187
+ .pchip-reg {
188
+ width: 4px;
189
+ height: 4px;
190
+ border-radius: 50%;
191
+ background: var(--accent);
237
192
  flex-shrink: 0;
238
193
  }
239
194
 
240
- .session-picker-search input {
241
- width: 100%;
242
- padding: 6px 10px;
243
- border: 1px solid var(--border);
244
- border-radius: var(--radius);
245
- background: var(--bg);
246
- color: var(--text);
247
- font-size: 0.78rem;
248
- font-family: var(--font);
249
- outline: none;
250
- transition: border-color var(--transition);
251
- }
252
-
253
- .session-picker-search input:focus {
254
- border-color: var(--accent);
255
- }
256
-
257
- .session-picker-search input::placeholder {
258
- color: var(--text-muted);
195
+ .project-chip.registered .pchip-icon {
196
+ opacity: 0.8;
259
197
  }
260
198
 
261
- .session-picker-list {
262
- overflow-y: auto;
263
- flex: 1;
199
+ /* ── Project Detail Panel ── */
200
+ .project-detail {
201
+ padding: 14px 18px 12px;
202
+ border-bottom: 1px solid var(--border);
203
+ background: var(--surface);
264
204
  }
205
+ .project-detail:empty { display: none; }
265
206
 
266
- .session-item {
207
+ .pd-header {
267
208
  display: flex;
268
209
  flex-direction: column;
269
210
  gap: 4px;
270
- padding: 10px 14px;
271
- cursor: pointer;
272
- border-bottom: 1px solid var(--border);
273
- transition: background var(--transition);
274
211
  }
275
212
 
276
- .session-item:hover { background: var(--surface-hover); }
277
- .session-item:last-child { border-bottom: none; }
278
-
279
- .session-item-row {
213
+ .pd-title-row {
280
214
  display: flex;
281
215
  align-items: center;
282
216
  gap: 8px;
283
217
  }
284
218
 
285
- .session-item-status {
286
- display: inline-block;
287
- width: 7px;
288
- height: 7px;
289
- border-radius: 50%;
219
+ .pd-icon {
290
220
  flex-shrink: 0;
291
- }
292
- .session-item-status.done { background: var(--green); }
293
- .session-item-status.failed { background: var(--red); }
294
- .session-item-status.running { background: var(--yellow); animation: pulse 1.5s infinite; }
295
- .session-item-status.unknown { background: var(--text-muted); }
296
-
297
- @keyframes pulse {
298
- 0%, 100% { opacity: 1; }
299
- 50% { opacity: 0.4; }
300
- }
301
-
302
- .session-item-id {
303
- font-family: var(--font-mono);
304
- font-size: 0.7rem;
305
221
  color: var(--accent);
306
- flex-shrink: 0;
307
- }
308
-
309
- .session-item-slug {
310
- font-size: 0.7rem;
311
- color: var(--text-muted);
312
- flex-shrink: 0;
313
- font-style: italic;
314
- }
315
-
316
- .session-item-job {
317
- font-family: var(--font-mono);
318
- font-size: 0.65rem;
319
- color: var(--text-muted);
320
- flex-shrink: 0;
321
222
  }
322
223
 
323
- .session-item-prompt {
324
- font-size: 0.78rem;
224
+ .pd-name {
225
+ font-size: 0.88rem;
226
+ font-weight: 600;
325
227
  color: var(--text);
326
- flex: 1;
327
- overflow: hidden;
328
- text-overflow: ellipsis;
329
- white-space: nowrap;
330
- }
331
-
332
- .session-item-meta {
333
- display: flex;
334
- align-items: center;
335
- gap: 12px;
336
- padding-left: 15px;
337
- }
338
-
339
- .session-item-time {
340
- font-size: 0.68rem;
341
- color: var(--text-muted);
342
- flex-shrink: 0;
343
- }
344
-
345
- .session-item-cwd {
346
- font-size: 0.65rem;
347
- color: var(--text-muted);
348
- font-family: var(--font-mono);
349
- overflow: hidden;
350
- text-overflow: ellipsis;
351
- white-space: nowrap;
352
228
  }
353
229
 
354
- .session-item-cost {
355
- font-size: 0.65rem;
356
- color: var(--green);
357
- font-family: var(--font-mono);
358
- flex-shrink: 0;
230
+ .pd-badge {
231
+ font-size: 0.58rem;
232
+ font-weight: 700;
233
+ padding: 2px 7px;
234
+ border-radius: 8px;
235
+ background: var(--accent-glow);
236
+ color: var(--accent);
237
+ text-transform: uppercase;
238
+ letter-spacing: 0.04em;
359
239
  }
360
240
 
361
- .session-filter-bar {
241
+ .pd-actions {
242
+ margin-left: auto;
362
243
  display: flex;
363
244
  align-items: center;
364
245
  gap: 6px;
365
- padding: 6px 14px;
366
- border-bottom: 1px solid var(--border);
367
- flex-shrink: 0;
368
246
  }
369
247
 
370
- .session-filter-toggle {
371
- display: inline-flex;
248
+ .pd-close {
249
+ display: flex;
372
250
  align-items: center;
373
- gap: 4px;
374
- padding: 3px 8px;
251
+ justify-content: center;
252
+ width: 28px;
253
+ height: 28px;
254
+ border-radius: 6px;
375
255
  border: 1px solid var(--border);
376
- border-radius: 4px;
377
256
  background: transparent;
378
257
  color: var(--text-muted);
379
- font-size: 0.68rem;
380
- font-family: var(--font);
381
258
  cursor: pointer;
382
- transition: all var(--transition);
383
- white-space: nowrap;
384
- }
385
-
386
- .session-filter-toggle:hover { border-color: var(--accent); color: var(--text-secondary); }
387
- .session-filter-toggle.active { background: var(--accent-glow); border-color: var(--accent); color: var(--accent); }
388
-
389
- .session-filter-project {
390
- font-size: 0.68rem;
391
- color: var(--text-muted);
392
- overflow: hidden;
393
- text-overflow: ellipsis;
394
- white-space: nowrap;
395
- flex: 1;
396
- }
397
-
398
- .session-group-header {
399
- display: flex;
400
- align-items: center;
401
- gap: 6px;
402
- padding: 6px 14px;
403
- background: var(--bg);
404
- font-size: 0.68rem;
405
- font-weight: 600;
406
- color: var(--text-secondary);
407
- border-bottom: 1px solid var(--border);
408
- position: sticky;
409
- top: 0;
410
- z-index: 1;
411
- }
412
-
413
- .session-group-header svg {
414
- width: 12px;
415
- height: 12px;
416
- flex-shrink: 0;
417
- opacity: 0.6;
418
- }
419
-
420
- .session-group-count {
421
- font-weight: 400;
422
- color: var(--text-muted);
423
- margin-left: auto;
424
- }
425
-
426
- .session-empty {
427
- padding: 24px 14px;
428
- text-align: center;
429
- font-size: 0.78rem;
430
- color: var(--text-muted);
431
- }
432
-
433
- .session-empty-icon {
434
- font-size: 1.5rem;
435
- margin-bottom: 8px;
436
- opacity: 0.5;
437
- }
438
-
439
- /* ── Layout ── */
440
- .layout {
441
- min-height: 100vh;
442
- }
443
-
444
- .main {
445
- padding: 8px 0;
446
- overflow-x: hidden;
447
- }
448
-
449
- /* ── CWD badge ── */
450
- .cwd-badge {
451
- display: inline-flex;
452
- align-items: center;
453
- gap: 4px;
454
- padding: 2px 8px;
455
- border-radius: 12px;
456
- font-size: 0.7rem;
457
- font-weight: 500;
458
- background: var(--accent-glow);
459
- color: var(--accent);
460
- margin-left: 8px;
461
- max-width: 180px;
462
- overflow: hidden;
463
- text-overflow: ellipsis;
464
- white-space: nowrap;
465
- vertical-align: middle;
466
- }
467
-
468
- .cwd-badge:empty { display: none; }
469
-
470
- /* ── Directory Picker ── */
471
- .dir-picker {
472
- display: flex;
473
- align-items: center;
474
- gap: 8px;
475
- }
476
-
477
- .dir-picker-display {
478
- flex: 1;
479
- display: flex;
480
- align-items: center;
481
- gap: 8px;
482
- padding: 8px 12px;
483
- border-radius: var(--radius);
484
- background: var(--bg);
485
- border: 1px solid var(--border);
486
- color: var(--text-muted);
487
- font-size: 0.85rem;
488
- font-family: var(--font-mono);
489
- min-height: 38px;
490
- overflow: hidden;
491
- text-overflow: ellipsis;
492
- white-space: nowrap;
493
- cursor: pointer;
494
- transition: all var(--transition);
495
- }
496
-
497
- .dir-picker-display:hover {
498
- border-color: var(--border-light);
499
- background: var(--surface);
500
- }
501
-
502
- .dir-picker-display.has-value {
503
- color: var(--text);
504
- }
505
-
506
- .dir-picker-display .dir-icon {
507
- flex-shrink: 0;
508
- color: var(--text-muted);
509
- }
510
-
511
- .dir-picker-clear {
512
- flex-shrink: 0;
513
- padding: 4px;
514
- border-radius: 4px;
515
- background: transparent;
516
- color: var(--text-muted);
517
- border: none;
518
- cursor: pointer;
519
- display: none;
520
- }
521
-
522
- .dir-picker-clear:hover {
523
- background: var(--red-dim);
524
- color: var(--red);
525
- }
526
-
527
- .dir-picker-clear.visible { display: inline-flex; }
528
-
529
- /* ── Recent Directory Chips ── */
530
- .recent-dirs {
531
- display: flex;
532
- align-items: center;
533
- gap: 6px;
534
- flex-wrap: wrap;
535
- margin-bottom: 10px;
536
- }
537
-
538
- .recent-dirs:empty { display: none; }
539
-
540
- .recent-dirs-label {
541
- font-size: 0.72rem;
542
- color: var(--text-muted);
543
- font-weight: 500;
544
- flex-shrink: 0;
545
- margin-right: 2px;
546
- }
547
-
548
- .recent-chip {
549
- display: inline-flex;
550
- align-items: center;
551
- gap: 4px;
552
- padding: 3px 10px;
553
- border-radius: 14px;
554
- background: var(--surface);
555
- border: 1px solid var(--border);
556
- color: var(--text-secondary);
557
- font-size: 0.75rem;
558
- font-family: var(--font-mono);
559
- cursor: pointer;
560
- transition: all var(--transition);
561
- max-width: 200px;
562
- }
563
-
564
- .recent-chip:hover {
565
- border-color: var(--accent);
566
- color: var(--accent);
567
- background: var(--accent-glow);
568
- }
569
-
570
- .recent-chip.active {
571
- border-color: var(--accent);
572
- color: var(--accent);
573
- background: var(--accent-glow);
574
- font-weight: 500;
575
- }
576
-
577
- .recent-chip-name {
578
- overflow: hidden;
579
- text-overflow: ellipsis;
580
- white-space: nowrap;
581
- }
582
-
583
- .recent-chip-remove {
584
- flex-shrink: 0;
585
- width: 14px;
586
- height: 14px;
587
- padding: 0;
588
- margin-left: 2px;
589
- border: none;
590
- background: transparent;
591
- color: var(--text-muted);
592
- cursor: pointer;
593
- border-radius: 50%;
594
- display: inline-flex;
595
- align-items: center;
596
- justify-content: center;
597
- transition: all var(--transition);
598
- }
599
-
600
- .recent-chip-remove:hover {
601
- background: var(--red-dim);
602
- color: var(--red);
603
- }
604
-
605
- /* ── Inline Directory Browser Panel ── */
606
- .dir-browser-panel {
607
- display: none;
608
- border: 1px solid var(--border);
609
- border-radius: var(--radius);
610
- background: var(--surface);
611
- margin-top: 8px;
612
- overflow: hidden;
613
- animation: panel-slide 0.15s ease-out;
614
- }
615
-
616
- .dir-browser-panel.open { display: block; }
617
-
618
- @keyframes panel-slide {
619
- from { opacity: 0; transform: translateY(-4px); }
620
- to { opacity: 1; transform: translateY(0); }
621
- }
622
-
623
- .dir-modal-header {
624
- display: flex;
625
- align-items: center;
626
- justify-content: space-between;
627
- padding: 10px 12px;
628
- border-bottom: 1px solid var(--border);
629
- }
630
-
631
- .dir-modal-title {
632
- font-size: 0.82rem;
633
- font-weight: 600;
634
- color: var(--text);
635
- }
636
-
637
- .dir-modal-close {
638
- padding: 4px;
639
- border-radius: 4px;
640
- background: transparent;
641
- color: var(--text-muted);
642
- border: none;
643
- cursor: pointer;
644
- }
645
-
646
- .dir-modal-close:hover { background: var(--surface-active); color: var(--text); }
647
-
648
- .dir-modal-path {
649
- display: flex;
650
- align-items: center;
651
- gap: 4px;
652
- padding: 8px 12px;
653
- background: var(--bg);
654
- border-bottom: 1px solid var(--border);
655
- font-family: var(--font-mono);
656
- font-size: 0.75rem;
657
- color: var(--text-secondary);
658
- overflow-x: auto;
659
- white-space: nowrap;
660
- }
661
-
662
- .dir-modal-path .breadcrumb-seg {
663
- padding: 2px 6px;
664
- border-radius: 4px;
665
- cursor: pointer;
666
- color: var(--accent);
667
- transition: background var(--transition);
668
- }
669
-
670
- .dir-modal-path .breadcrumb-seg:hover {
671
- background: var(--accent-glow);
672
- }
673
-
674
- .dir-modal-path .breadcrumb-sep {
675
- color: var(--text-muted);
676
- }
677
-
678
- .dir-modal-list {
679
- flex: 1;
680
- overflow-y: auto;
681
- padding: 4px 0;
682
- max-height: 240px;
683
- }
684
-
685
- .dir-item {
686
- display: flex;
687
- align-items: center;
688
- gap: 10px;
689
- padding: 8px 16px;
690
- cursor: pointer;
691
- font-size: 0.85rem;
692
- color: var(--text);
693
- transition: background var(--transition);
694
- }
695
-
696
- .dir-item:hover { background: var(--surface-hover); }
697
-
698
- .dir-item-icon {
699
- flex-shrink: 0;
700
- width: 18px;
701
- height: 18px;
702
- color: var(--text-muted);
703
- }
704
-
705
- .dir-item-icon.is-dir { color: var(--yellow); }
706
- .dir-item-icon.is-parent { color: var(--accent); }
707
-
708
- .dir-item-name {
709
- flex: 1;
710
- overflow: hidden;
711
- text-overflow: ellipsis;
712
- white-space: nowrap;
713
- }
714
-
715
- .dir-item-name.is-dir { font-weight: 500; }
716
-
717
- .dir-modal-footer {
718
- display: flex;
719
- align-items: center;
720
- justify-content: space-between;
721
- padding: 10px 12px;
722
- border-top: 1px solid var(--border);
723
- gap: 8px;
724
- }
725
-
726
- .dir-modal-current {
727
- flex: 1;
728
- font-family: var(--font-mono);
729
- font-size: 0.78rem;
730
- color: var(--text-secondary);
731
- overflow: hidden;
732
- text-overflow: ellipsis;
733
- white-space: nowrap;
734
- }
735
-
736
- .dir-modal-loading {
737
- display: flex;
738
- align-items: center;
739
- justify-content: center;
740
- padding: 32px;
741
- color: var(--text-muted);
742
- font-size: 0.85rem;
743
- gap: 8px;
744
- }
745
-
746
- /* ── Cards / Sections ── */
747
- .section {
748
- margin-bottom: 24px;
749
- animation: fadeIn 0.3s ease forwards;
750
- animation-iteration-count: 1;
751
- }
752
-
753
- @keyframes fadeIn {
754
- from { opacity: 0; transform: translateY(8px); }
755
- to { opacity: 1; transform: translateY(0); }
756
- }
757
-
758
- .card {
759
- background: var(--surface);
760
- border: 1px solid var(--border);
761
- border-left: none;
762
- border-right: none;
763
- border-radius: 0;
764
- overflow: visible;
765
- }
766
-
767
- .card-header {
768
- display: flex;
769
- align-items: center;
770
- justify-content: space-between;
771
- padding: 14px 18px;
772
- border-bottom: 1px solid var(--border);
773
- gap: 8px;
774
- flex-wrap: wrap;
775
- }
776
-
777
- .card-title {
778
- font-size: 0.92rem;
779
- font-weight: 600;
780
- color: var(--text);
781
- display: flex;
782
- align-items: center;
783
- gap: 8px;
784
- min-width: 0;
785
- flex: 1 1 0;
786
- }
787
-
788
- .card-title svg { width: 16px; height: 16px; flex-shrink: 0; color: var(--accent); }
789
-
790
- .card-header-actions {
791
- display: flex;
792
- align-items: center;
793
- gap: 8px;
794
- flex-shrink: 0;
795
- }
796
-
797
- .card-body { padding: 18px; }
798
-
799
- /* ── Send Task Form ── */
800
- .form-group { margin-bottom: 14px; }
801
-
802
- .form-label {
803
- display: flex;
804
- align-items: center;
805
- font-size: 0.78rem;
806
- font-weight: 500;
807
- color: var(--text-secondary);
808
- margin-bottom: 6px;
809
- }
810
-
811
- textarea, input[type="text"] {
812
- width: 100%;
813
- padding: 10px 14px;
814
- background: var(--bg);
815
- border: 1px solid var(--border);
816
- border-radius: var(--radius);
817
- color: var(--text);
818
- font-family: var(--font);
819
- font-size: 0.85rem;
820
- line-height: 1.6;
821
- resize: vertical;
822
- transition: border-color var(--transition);
259
+ transition: all 0.15s ease;
823
260
  }
824
261
 
825
- textarea:focus, input[type="text"]:focus {
826
- outline: none;
827
- border-color: var(--accent);
828
- box-shadow: 0 0 0 3px var(--accent-glow);
262
+ .pd-close:hover {
263
+ background: var(--surface-hover);
264
+ color: var(--text);
265
+ border-color: var(--text-muted);
829
266
  }
830
267
 
831
- textarea { min-height: unset; position: relative; }
832
-
833
- /* ── Prompt Input + Mirror (color-only highlight, no text replacement) ── */
834
- #promptInput {
835
- color: transparent;
836
- caret-color: var(--text);
837
- padding-right: 44px;
268
+ .pd-meta {
269
+ font-size: 0.72rem;
270
+ color: var(--text-muted);
271
+ font-family: var(--font-mono);
272
+ padding-left: 24px;
273
+ display: flex;
274
+ align-items: baseline;
275
+ flex-wrap: wrap;
276
+ row-gap: 2px;
838
277
  }
839
278
 
840
- #promptInput::selection {
841
- background: rgba(108, 140, 255, 0.3);
842
- color: transparent;
279
+ .pd-sep {
280
+ color: var(--border);
281
+ margin: 0 3px;
282
+ flex-shrink: 0;
843
283
  }
844
284
 
845
- .prompt-mirror {
846
- position: absolute;
847
- top: 0;
848
- left: 0;
849
- right: 0;
850
- bottom: 0;
851
- padding: 10px 14px;
852
- padding-right: 44px;
853
- font-family: var(--font);
854
- font-size: 0.85rem;
855
- line-height: 1.6;
856
- color: var(--text);
857
- white-space: pre-wrap;
858
- word-wrap: break-word;
285
+ .pd-path-text {
286
+ color: var(--text-muted);
859
287
  overflow: hidden;
860
- pointer-events: none;
288
+ text-overflow: ellipsis;
289
+ white-space: nowrap;
290
+ max-width: min(100%, 360px);
291
+ flex-shrink: 1;
861
292
  }
862
293
 
863
- .prompt-at-ref {
294
+ .pd-branch {
864
295
  color: var(--green);
865
- font-weight: 500;
296
+ white-space: nowrap;
297
+ flex-shrink: 0;
866
298
  }
867
299
 
868
- /* ── Image Attachments ── */
869
- .prompt-wrapper {
870
- position: relative;
300
+ .pd-branch::before {
301
+ content: '';
302
+ display: inline-block;
303
+ width: 8px;
304
+ height: 8px;
305
+ margin-right: 3px;
306
+ background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' fill='none' stroke='%2322c55e' stroke-width='2.5' xmlns='http://www.w3.org/2000/svg'%3E%3Cline x1='6' y1='3' x2='6' y2='15'/%3E%3Ccircle cx='18' cy='6' r='3'/%3E%3Ccircle cx='6' cy='18' r='3'/%3E%3Cpath d='M18 9a9 9 0 0 1-9 9'/%3E%3C/svg%3E") no-repeat center / contain;
307
+ vertical-align: middle;
871
308
  }
872
309
 
873
- .prompt-wrapper.drag-over textarea {
874
- border-color: var(--accent);
875
- box-shadow: 0 0 0 3px var(--accent-glow);
310
+ .pd-remote {
311
+ color: var(--text-muted);
312
+ opacity: 0.75;
313
+ white-space: nowrap;
314
+ flex-shrink: 0;
315
+ overflow: hidden;
316
+ text-overflow: ellipsis;
317
+ max-width: 200px;
876
318
  }
877
319
 
878
- .drop-overlay {
879
- display: none;
880
- position: absolute;
881
- inset: 0;
882
- background: rgba(108, 140, 255, 0.08);
883
- border: 2px dashed var(--accent);
884
- border-radius: var(--radius);
885
- z-index: 10;
886
- pointer-events: none;
887
- align-items: center;
888
- justify-content: center;
889
- font-size: 0.85rem;
890
- font-weight: 600;
891
- color: var(--accent);
320
+ .pd-stats {
321
+ display: flex;
892
322
  gap: 8px;
323
+ padding: 10px 0 2px 24px;
324
+ flex-wrap: wrap;
893
325
  }
894
326
 
895
- .prompt-wrapper.drag-over .drop-overlay {
327
+ .pd-stat {
896
328
  display: flex;
329
+ flex-direction: column;
330
+ align-items: center;
331
+ padding: 5px 12px;
332
+ border-radius: 8px;
333
+ background: var(--bg);
334
+ min-width: 50px;
897
335
  }
898
336
 
899
- .image-previews {
900
- display: flex;
901
- flex-wrap: wrap;
902
- gap: 8px;
903
- margin-top: 8px;
337
+ .pd-stat-val {
338
+ font-size: 0.84rem;
339
+ font-weight: 700;
340
+ color: var(--text);
341
+ font-family: var(--font-mono);
342
+ line-height: 1.2;
904
343
  }
905
344
 
906
- .image-previews:empty { display: none; }
907
-
908
- .img-thumb {
909
- position: relative;
910
- width: 72px;
911
- height: 72px;
912
- border-radius: 6px;
913
- border: 1px solid var(--border);
914
- overflow: hidden;
915
- background: var(--bg);
916
- flex-shrink: 0;
917
- animation: fadeIn 0.2s ease;
345
+ .pd-stat-label {
346
+ font-size: 0.58rem;
347
+ color: var(--text-muted);
348
+ font-weight: 500;
349
+ margin-top: 1px;
918
350
  }
919
351
 
920
- .img-thumb img {
921
- width: 100%;
922
- height: 100%;
923
- object-fit: cover;
924
- cursor: pointer;
925
- }
352
+ .pd-running { background: var(--blue-dim); }
353
+ .pd-running .pd-stat-val { color: var(--blue); }
354
+ .pd-done { background: var(--green-dim); }
355
+ .pd-done .pd-stat-val { color: var(--green); }
356
+ .pd-failed { background: var(--red-dim); }
357
+ .pd-failed .pd-stat-val { color: var(--red); }
358
+ .pd-rate-ok .pd-stat-val { color: var(--green); }
359
+ .pd-rate-warn .pd-stat-val { color: var(--red); }
926
360
 
927
- .img-thumb .img-remove {
928
- position: absolute;
929
- top: 2px;
930
- right: 2px;
931
- width: 18px;
932
- height: 18px;
933
- border-radius: 50%;
934
- background: rgba(0,0,0,0.7);
935
- color: #fff;
936
- font-size: 11px;
361
+ /* ── Job Filter Bar ── */
362
+ .job-filter-bar {
937
363
  display: flex;
938
364
  align-items: center;
939
- justify-content: center;
940
- cursor: pointer;
941
- opacity: 0;
942
- transition: opacity 0.15s;
943
- border: none;
944
- padding: 0;
945
- line-height: 1;
365
+ gap: 10px;
366
+ padding: 8px 18px;
367
+ border-bottom: 1px solid var(--border);
368
+ background: var(--bg);
946
369
  }
947
370
 
948
- .img-thumb:hover .img-remove {
949
- opacity: 1;
371
+ .job-filter-btns {
372
+ display: flex;
373
+ gap: 2px;
374
+ flex-shrink: 0;
950
375
  }
951
376
 
952
- .img-thumb.uploading {
953
- opacity: 0.5;
377
+ .job-filter-btn {
378
+ padding: 4px 10px;
379
+ border-radius: var(--radius);
380
+ font-size: 0.72rem;
381
+ font-weight: 600;
382
+ font-family: var(--font);
383
+ background: transparent;
384
+ color: var(--text-muted);
385
+ border: 1px solid transparent;
386
+ transition: all 0.15s ease;
954
387
  }
955
388
 
956
- .img-thumb.uploading::after {
957
- content: '';
958
- position: absolute;
959
- inset: 0;
960
- display: flex;
961
- align-items: center;
962
- justify-content: center;
963
- background: rgba(0,0,0,0.3);
389
+ .job-filter-btn:hover {
390
+ background: var(--surface-hover);
391
+ color: var(--text-secondary);
964
392
  }
965
393
 
966
- .img-count-badge {
967
- display: inline-flex;
968
- align-items: center;
969
- gap: 4px;
970
- padding: 2px 8px;
971
- border-radius: 12px;
972
- font-size: 0.7rem;
973
- font-weight: 600;
394
+ .job-filter-btn.active {
974
395
  background: var(--accent-glow);
975
396
  color: var(--accent);
976
- margin-left: 8px;
397
+ border-color: var(--accent);
977
398
  }
978
399
 
979
- .img-count-badge:empty { display: none; }
400
+ .job-filter-btn[data-filter="running"].active { background: var(--blue-dim); color: var(--blue); border-color: var(--blue); }
401
+ .job-filter-btn[data-filter="done"].active { background: var(--green-dim); color: var(--green); border-color: var(--green); }
402
+ .job-filter-btn[data-filter="failed"].active { background: var(--red-dim); color: var(--red); border-color: var(--red); }
980
403
 
981
- /* ── File Attachments (non-image) ── */
982
- .file-thumb {
983
- position: relative;
984
- display: inline-flex;
404
+ /* ── Project Filter Dropdown ── */
405
+ .job-project-filter {
406
+ display: flex;
985
407
  align-items: center;
986
- gap: 6px;
987
- padding: 6px 10px 6px 8px;
988
- border-radius: 6px;
989
- border: 1px solid var(--border);
990
- background: var(--surface);
991
- max-width: 200px;
992
- animation: fadeIn 0.2s ease;
408
+ gap: 5px;
993
409
  flex-shrink: 0;
994
410
  }
995
- .file-thumb .file-icon {
996
- width: 28px;
997
- height: 28px;
998
- display: flex;
999
- align-items: center;
1000
- justify-content: center;
1001
- border-radius: 4px;
1002
- background: var(--surface-active);
411
+
412
+ .job-project-filter svg {
1003
413
  color: var(--text-muted);
1004
414
  flex-shrink: 0;
1005
- font-size: 0.6rem;
1006
- font-weight: 700;
1007
- font-family: var(--font-mono);
1008
- text-transform: uppercase;
1009
- letter-spacing: -0.02em;
1010
- }
1011
- .file-thumb .file-info {
1012
- display: flex;
1013
- flex-direction: column;
1014
- gap: 1px;
1015
- overflow: hidden;
1016
- min-width: 0;
1017
415
  }
1018
- .file-thumb .file-name {
1019
- font-size: 0.75rem;
416
+
417
+ .job-project-filter select {
418
+ padding: 4px 24px 4px 8px;
419
+ border-radius: var(--radius);
420
+ border: 1px solid var(--border);
421
+ background: var(--surface);
422
+ color: var(--text-secondary);
423
+ font-size: 0.72rem;
1020
424
  font-weight: 500;
1021
- color: var(--text);
1022
- white-space: nowrap;
1023
- overflow: hidden;
1024
- text-overflow: ellipsis;
425
+ font-family: var(--font-mono);
426
+ cursor: pointer;
427
+ outline: none;
428
+ appearance: none;
429
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
430
+ background-repeat: no-repeat;
431
+ background-position: right 6px center;
432
+ max-width: 180px;
433
+ transition: border-color var(--transition);
1025
434
  }
1026
- .file-thumb .file-size {
1027
- font-size: 0.65rem;
1028
- color: var(--text-muted);
435
+
436
+ .job-project-filter select:focus {
437
+ border-color: var(--accent);
1029
438
  }
1030
- .file-thumb .file-remove {
1031
- position: absolute;
1032
- top: -4px;
1033
- right: -4px;
1034
- width: 16px;
1035
- height: 16px;
1036
- border-radius: 50%;
1037
- background: rgba(0,0,0,0.7);
1038
- color: #fff;
1039
- font-size: 10px;
1040
- display: flex;
1041
- align-items: center;
1042
- justify-content: center;
1043
- cursor: pointer;
1044
- opacity: 0;
1045
- transition: opacity 0.15s;
1046
- border: none;
1047
- padding: 0;
1048
- line-height: 1;
439
+
440
+ .job-project-filter select:hover {
441
+ background-color: var(--surface-hover);
1049
442
  }
1050
- .file-thumb:hover .file-remove { opacity: 1; }
1051
- .file-thumb.uploading { opacity: 0.5; }
1052
443
 
1053
- /* ── Attach Button (inside prompt-wrapper) ── */
1054
- .attach-float-btn {
1055
- position: absolute;
1056
- bottom: 12px;
1057
- right: 10px;
1058
- width: 28px;
1059
- height: 28px;
1060
- border-radius: 6px;
1061
- background: var(--surface-hover);
1062
- border: 1px solid var(--border);
1063
- color: var(--text-muted);
444
+ .job-search-wrap {
1064
445
  display: flex;
1065
446
  align-items: center;
1066
- justify-content: center;
1067
- cursor: pointer;
1068
- transition: all var(--transition);
1069
- z-index: 5;
447
+ gap: 6px;
448
+ flex: 1;
449
+ max-width: 280px;
450
+ padding: 4px 10px;
451
+ border: 1px solid var(--border);
452
+ border-radius: var(--radius);
453
+ background: var(--surface);
454
+ transition: border-color var(--transition);
1070
455
  }
1071
- .attach-float-btn:hover {
1072
- background: var(--surface-active);
1073
- color: var(--accent);
456
+
457
+ .job-search-wrap:focus-within {
1074
458
  border-color: var(--accent);
1075
459
  }
1076
460
 
1077
- textarea::placeholder, input::placeholder {
461
+ .job-search-wrap svg {
1078
462
  color: var(--text-muted);
463
+ flex-shrink: 0;
1079
464
  }
1080
465
 
1081
- .form-actions {
1082
- display: flex;
1083
- justify-content: flex-end;
1084
- gap: 8px;
466
+ .job-search-input {
467
+ flex: 1;
468
+ border: none !important;
469
+ background: transparent !important;
470
+ padding: 0 !important;
471
+ font-size: 0.78rem !important;
472
+ color: var(--text) !important;
473
+ outline: none !important;
474
+ box-shadow: none !important;
475
+ min-width: 0;
476
+ }
477
+
478
+ .job-search-input::placeholder {
479
+ color: var(--text-muted);
1085
480
  }
1086
481
 
1087
482
  /* ── Job Table ── */
@@ -1160,6 +555,22 @@ thead tr { cursor: default; }
1160
555
  color: var(--yellow);
1161
556
  }
1162
557
 
558
+ .badge-zombie {
559
+ background: var(--red-dim);
560
+ color: var(--red);
561
+ font-size: 0.65rem;
562
+ padding: 2px 6px;
563
+ animation: pulse 1.5s ease-in-out infinite;
564
+ }
565
+
566
+ .badge-dep {
567
+ background: var(--blue-dim, rgba(59, 130, 246, 0.1));
568
+ color: var(--blue, #3b82f6);
569
+ font-size: 0.6rem;
570
+ padding: 2px 5px;
571
+ letter-spacing: -0.02em;
572
+ }
573
+
1163
574
  .job-id {
1164
575
  font-family: var(--font-mono);
1165
576
  font-size: 0.78rem;
@@ -1281,7 +692,7 @@ thead tr { cursor: default; }
1281
692
  max-height: 400px;
1282
693
  overflow-y: auto;
1283
694
  padding: 14px 18px;
1284
- background: #080a0e;
695
+ background: var(--stream-bg, #080a0e);
1285
696
  font-family: var(--font-mono);
1286
697
  font-size: 0.78rem;
1287
698
  line-height: 1.7;
@@ -1300,7 +711,7 @@ thead tr { cursor: default; }
1300
711
  }
1301
712
 
1302
713
  .stream-event-text {
1303
- color: #e4e6ed;
714
+ color: var(--text);
1304
715
  white-space: pre-wrap;
1305
716
  }
1306
717
 
@@ -1332,7 +743,7 @@ thead tr { cursor: default; }
1332
743
  }
1333
744
 
1334
745
  .stream-tool-input {
1335
- color: #d4d4d4;
746
+ color: var(--text-secondary);
1336
747
  white-space: pre-wrap;
1337
748
  flex: 1;
1338
749
  min-width: 0;
@@ -1385,6 +796,42 @@ thead tr { cursor: default; }
1385
796
  font-size: 0.8rem;
1386
797
  }
1387
798
 
799
+ .stream-no-output {
800
+ color: var(--text-muted);
801
+ text-align: center;
802
+ padding: 24px;
803
+ font-size: 0.8rem;
804
+ font-style: italic;
805
+ opacity: 0.7;
806
+ }
807
+
808
+ .stream-error-state {
809
+ display: flex;
810
+ flex-direction: column;
811
+ align-items: center;
812
+ gap: 8px;
813
+ padding: 24px;
814
+ color: var(--text-muted);
815
+ font-size: 0.8rem;
816
+ }
817
+ .stream-error-state svg { opacity: 0.5; }
818
+ .stream-error-state .btn { margin-top: 4px; }
819
+
820
+ .stream-poll-warning {
821
+ display: flex;
822
+ align-items: center;
823
+ gap: 6px;
824
+ padding: 6px 12px;
825
+ background: var(--yellow-dim, rgba(234, 179, 8, 0.1));
826
+ color: var(--yellow, #eab308);
827
+ font-size: 0.75rem;
828
+ font-weight: 500;
829
+ border-bottom: 1px solid rgba(234, 179, 8, 0.15);
830
+ animation: pollWarnIn 0.3s ease;
831
+ }
832
+ .stream-poll-warning svg { flex-shrink: 0; opacity: 0.7; }
833
+ @keyframes pollWarnIn { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: translateY(0); } }
834
+
1388
835
  .stream-done-banner {
1389
836
  display: flex;
1390
837
  align-items: center;
@@ -1404,6 +851,75 @@ thead tr { cursor: default; }
1404
851
  border-top-color: rgba(248, 113, 113, 0.2);
1405
852
  }
1406
853
 
854
+ /* ── User Error Card ── */
855
+ .user-error-card {
856
+ margin: 0;
857
+ padding: 12px 18px;
858
+ background: var(--red-dim);
859
+ border-top: 1px solid rgba(248, 113, 113, 0.15);
860
+ font-size: 0.78rem;
861
+ line-height: 1.5;
862
+ }
863
+ .user-error-summary {
864
+ font-weight: 700;
865
+ color: var(--red);
866
+ margin-bottom: 4px;
867
+ }
868
+ .user-error-cause {
869
+ color: var(--text-secondary, var(--text-muted));
870
+ margin-bottom: 6px;
871
+ }
872
+ .user-error-steps {
873
+ margin: 0 0 6px 0;
874
+ padding-left: 18px;
875
+ color: var(--text);
876
+ }
877
+ .user-error-steps li {
878
+ margin-bottom: 2px;
879
+ }
880
+ .user-error-details {
881
+ margin-top: 6px;
882
+ }
883
+ .user-error-details summary {
884
+ cursor: pointer;
885
+ font-size: 0.7rem;
886
+ color: var(--text-muted);
887
+ user-select: none;
888
+ }
889
+ .user-error-details summary:hover {
890
+ color: var(--text);
891
+ }
892
+ .user-error-raw {
893
+ margin-top: 6px;
894
+ padding: 8px 10px;
895
+ background: var(--bg-secondary, rgba(0,0,0,0.15));
896
+ border-radius: var(--radius);
897
+ font-size: 0.68rem;
898
+ color: var(--text-muted);
899
+ white-space: pre-wrap;
900
+ word-break: break-word;
901
+ max-height: 200px;
902
+ overflow-y: auto;
903
+ }
904
+
905
+ .stream-event-result-error .stream-result-icon {
906
+ color: var(--red);
907
+ }
908
+ .stream-event-result-error .stream-result-text {
909
+ color: var(--red);
910
+ }
911
+
912
+ /* ── Job Meta Info (duration) ── */
913
+ .job-meta-info {
914
+ display: inline-block;
915
+ margin-left: 6px;
916
+ font-size: 0.65rem;
917
+ color: var(--text-muted);
918
+ font-weight: 400;
919
+ font-variant-numeric: tabular-nums;
920
+ vertical-align: middle;
921
+ }
922
+
1407
923
  /* ── Stream Action Buttons ── */
1408
924
  .stream-actions {
1409
925
  display: flex;
@@ -1577,6 +1093,37 @@ thead tr { cursor: default; }
1577
1093
  background: var(--green-dim) !important;
1578
1094
  }
1579
1095
 
1096
+ /* ── Connection Lost Banner ── */
1097
+ .conn-lost-banner {
1098
+ position: fixed;
1099
+ top: 0;
1100
+ left: 0;
1101
+ right: 0;
1102
+ z-index: 1100;
1103
+ display: flex;
1104
+ align-items: center;
1105
+ justify-content: center;
1106
+ gap: 8px;
1107
+ padding: 8px 16px;
1108
+ background: var(--red-dim);
1109
+ border-bottom: 1px solid var(--red);
1110
+ color: var(--red);
1111
+ font-size: 0.78rem;
1112
+ font-weight: 500;
1113
+ transform: translateY(-100%);
1114
+ opacity: 0;
1115
+ transition: transform 0.3s ease, opacity 0.3s ease;
1116
+ pointer-events: none;
1117
+ }
1118
+
1119
+ .conn-lost-banner.visible {
1120
+ transform: translateY(0);
1121
+ opacity: 1;
1122
+ pointer-events: auto;
1123
+ }
1124
+
1125
+ .conn-lost-banner svg { flex-shrink: 0; }
1126
+
1580
1127
  /* ── Toast ── */
1581
1128
  .toast-container {
1582
1129
  position: fixed;
@@ -1589,6 +1136,7 @@ thead tr { cursor: default; }
1589
1136
  }
1590
1137
 
1591
1138
  .toast {
1139
+ --toast-duration: 3000ms;
1592
1140
  padding: 10px 16px;
1593
1141
  border-radius: var(--radius);
1594
1142
  font-size: 0.82rem;
@@ -1597,11 +1145,21 @@ thead tr { cursor: default; }
1597
1145
  border: 1px solid var(--border);
1598
1146
  color: var(--text);
1599
1147
  box-shadow: var(--shadow);
1600
- animation: toastIn 0.3s ease, toastOut 0.3s ease 2.7s forwards;
1148
+ animation: toastIn 0.3s ease;
1601
1149
  display: flex;
1602
1150
  align-items: center;
1603
1151
  gap: 8px;
1152
+ cursor: pointer;
1153
+ }
1154
+
1155
+ .toast .toast-msg { flex: 1; }
1156
+ .toast .toast-close {
1157
+ font-size: 1.1rem;
1158
+ opacity: 0.4;
1159
+ margin-left: 4px;
1160
+ line-height: 1;
1604
1161
  }
1162
+ .toast:hover .toast-close { opacity: 0.8; }
1605
1163
 
1606
1164
  .toast.success { border-color: var(--green); color: var(--green); }
1607
1165
  .toast.error { border-color: var(--red); color: var(--red); }
@@ -1652,271 +1210,318 @@ thead tr { cursor: default; }
1652
1210
  flex-shrink: 0;
1653
1211
  }
1654
1212
 
1655
- @media (max-width: 900px) {
1656
- th, td { padding: 10px 10px; font-size: 0.78rem; }
1657
- }
1213
+ /* ── Job Group View ── */
1214
+ .job-group-row { cursor: pointer; }
1215
+ .job-group-row:hover td { background: var(--surface-hover); }
1658
1216
 
1659
- @media (max-width: 600px) {
1660
- .main { padding: 4px 0; }
1661
- .card-body { padding: 14px; }
1662
- th, td { padding: 8px 6px; font-size: 0.74rem; }
1663
- .stream-content { max-height: 300px; }
1217
+ .job-group-cell {
1218
+ padding: 12px 18px !important;
1219
+ background: var(--surface) !important;
1220
+ border-bottom: 1px solid var(--border) !important;
1664
1221
  }
1665
1222
 
1666
- /* ── Settings Panel ── */
1667
- .settings-fab {
1668
- position: fixed;
1669
- bottom: 24px;
1670
- right: 24px;
1671
- z-index: 300;
1672
- width: 44px;
1673
- height: 44px;
1674
- border-radius: 50%;
1675
- background: var(--surface);
1676
- border: 1px solid var(--border);
1677
- color: var(--text-secondary);
1223
+ .job-group-content {
1678
1224
  display: flex;
1679
1225
  align-items: center;
1680
- justify-content: center;
1681
- cursor: pointer;
1682
- box-shadow: 0 4px 16px rgba(0,0,0,0.4);
1683
- transition: all var(--transition);
1226
+ gap: 10px;
1684
1227
  }
1685
- .settings-fab:hover {
1686
- background: var(--surface-hover);
1228
+
1229
+ .job-group-chevron {
1230
+ transition: transform 0.2s ease;
1231
+ color: var(--text-muted);
1232
+ flex-shrink: 0;
1233
+ }
1234
+
1235
+ .job-group-chevron.collapsed { transform: rotate(-90deg); }
1236
+
1237
+ .job-group-folder {
1687
1238
  color: var(--accent);
1688
- border-color: var(--accent);
1689
- transform: rotate(30deg);
1239
+ flex-shrink: 0;
1690
1240
  }
1691
1241
 
1692
- .settings-overlay {
1693
- display: none;
1694
- position: fixed;
1695
- inset: 0;
1696
- z-index: 400;
1697
- background: rgba(0,0,0,0.5);
1698
- backdrop-filter: blur(4px);
1699
- animation: fadeIn 0.2s ease;
1700
- }
1701
- .settings-overlay.open { display: flex; align-items: center; justify-content: center; }
1702
-
1703
- .settings-panel {
1704
- width: 520px;
1705
- max-width: 90vw;
1706
- max-height: 85vh;
1707
- background: var(--surface);
1708
- border: 1px solid var(--border);
1709
- border-radius: var(--radius-lg);
1710
- box-shadow: 0 16px 48px rgba(0,0,0,0.5);
1711
- display: flex;
1712
- flex-direction: column;
1713
- overflow: hidden;
1714
- animation: panel-slide 0.2s ease-out;
1242
+ .job-group-name {
1243
+ font-weight: 600;
1244
+ font-size: 0.82rem;
1245
+ color: var(--text);
1246
+ font-family: var(--font-mono);
1247
+ }
1248
+
1249
+ .job-group-count {
1250
+ font-size: 0.68rem;
1251
+ padding: 2px 8px;
1252
+ border-radius: 10px;
1253
+ background: var(--bg);
1254
+ color: var(--text-muted);
1255
+ font-weight: 600;
1256
+ font-family: var(--font-mono);
1715
1257
  }
1716
1258
 
1717
- .settings-header {
1259
+ .job-group-stats {
1718
1260
  display: flex;
1719
1261
  align-items: center;
1720
- justify-content: space-between;
1721
- padding: 16px 20px;
1722
- border-bottom: 1px solid var(--border);
1262
+ gap: 6px;
1263
+ margin-left: auto;
1723
1264
  }
1724
- .settings-title {
1725
- font-size: 1rem;
1265
+
1266
+ .grp-stat {
1267
+ display: inline-flex;
1268
+ align-items: center;
1269
+ gap: 4px;
1270
+ font-size: 0.68rem;
1726
1271
  font-weight: 700;
1727
- color: var(--text);
1272
+ padding: 2px 8px;
1273
+ border-radius: 10px;
1274
+ }
1275
+
1276
+ .grp-stat-running { background: var(--blue-dim); color: var(--blue); }
1277
+ .grp-stat-done { background: var(--green-dim); color: var(--green); }
1278
+ .grp-stat-failed { background: var(--red-dim); color: var(--red); }
1279
+
1280
+ .grp-dot {
1281
+ width: 5px;
1282
+ height: 5px;
1283
+ border-radius: 50%;
1284
+ background: var(--blue);
1285
+ animation: pulse 1.5s ease-in-out infinite;
1286
+ }
1287
+
1288
+ .job-group-meta {
1728
1289
  display: flex;
1729
1290
  align-items: center;
1730
1291
  gap: 8px;
1292
+ margin-left: 8px;
1293
+ padding-left: 8px;
1294
+ border-left: 1px solid var(--border);
1731
1295
  }
1732
- .settings-title svg { width: 18px; height: 18px; color: var(--accent); }
1733
- .settings-close {
1734
- padding: 6px;
1735
- border-radius: var(--radius);
1736
- background: transparent;
1737
- color: var(--text-muted);
1738
- border: none;
1739
- cursor: pointer;
1740
- }
1741
- .settings-close:hover { background: var(--surface-active); color: var(--text); }
1742
1296
 
1743
- .settings-body {
1744
- flex: 1;
1745
- overflow-y: auto;
1746
- padding: 20px;
1297
+ .grp-meta {
1298
+ font-size: 0.66rem;
1299
+ font-weight: 600;
1300
+ font-family: var(--font-mono);
1301
+ white-space: nowrap;
1747
1302
  }
1748
1303
 
1749
- .settings-section {
1750
- margin-bottom: 20px;
1751
- }
1752
- .settings-section-title {
1753
- font-size: 0.72rem;
1754
- font-weight: 600;
1755
- color: var(--text-muted);
1756
- text-transform: uppercase;
1757
- letter-spacing: 0.06em;
1758
- margin-bottom: 12px;
1304
+ .grp-meta-dur { color: var(--text-muted); }
1305
+ .grp-meta-rate-ok { color: var(--green); }
1306
+ .grp-meta-rate-warn { color: var(--red); }
1307
+
1308
+ .job-group-item td:first-child { padding-left: 36px; }
1309
+
1310
+ #btnViewMode.active {
1311
+ background: var(--accent-glow);
1312
+ color: var(--accent);
1313
+ border-color: var(--accent);
1759
1314
  }
1760
1315
 
1761
- .setting-row {
1762
- display: flex;
1763
- align-items: flex-start;
1764
- justify-content: space-between;
1765
- padding: 10px 0;
1766
- border-bottom: 1px solid var(--border);
1767
- gap: 16px;
1316
+ @media (max-width: 900px) {
1317
+ th, td { padding: 10px 10px; font-size: 0.78rem; }
1318
+ .project-detail { padding: 12px 14px 10px; }
1319
+ .pd-meta { padding-left: 0; }
1320
+ .pd-path-text { max-width: min(100%, 260px); }
1321
+ .pd-stats { padding-left: 0; gap: 6px; }
1322
+ .pd-stat { padding: 4px 10px; min-width: 44px; }
1768
1323
  }
1769
- .setting-row:last-child { border-bottom: none; }
1770
1324
 
1771
- .setting-info { flex: 1; min-width: 0; }
1772
- .setting-label {
1773
- font-size: 0.85rem;
1774
- font-weight: 500;
1775
- color: var(--text);
1776
- margin-bottom: 2px;
1325
+ /* ── Checkpoint Diff Viewer ── */
1326
+
1327
+ .ckpt-panel {
1328
+ border-top: 1px solid var(--border, #2a2a2a);
1329
+ padding: 12px 0 0;
1330
+ margin-top: 10px;
1777
1331
  }
1778
- .setting-desc {
1332
+ .ckpt-loading, .ckpt-empty {
1333
+ color: var(--text-muted, #888);
1334
+ font-size: 0.8rem;
1335
+ text-align: center;
1336
+ padding: 16px 0;
1337
+ }
1338
+ .ckpt-hint {
1339
+ color: var(--text-muted, #888);
1779
1340
  font-size: 0.72rem;
1780
- color: var(--text-muted);
1781
- line-height: 1.4;
1341
+ margin: 4px 0 8px;
1782
1342
  }
1783
-
1784
- .setting-control { flex-shrink: 0; }
1785
-
1786
- /* Toggle Switch */
1787
- .toggle {
1788
- position: relative;
1789
- width: 40px;
1790
- height: 22px;
1791
- cursor: pointer;
1343
+ .ckpt-selector {
1344
+ display: flex;
1345
+ align-items: center;
1346
+ gap: 8px;
1347
+ flex-wrap: wrap;
1792
1348
  }
1793
- .toggle input { display: none; }
1794
- .toggle-track {
1795
- position: absolute;
1796
- inset: 0;
1797
- background: var(--border);
1798
- border-radius: 11px;
1799
- transition: background var(--transition);
1349
+ .ckpt-select-group {
1350
+ display: flex;
1351
+ flex-direction: column;
1352
+ gap: 2px;
1800
1353
  }
1801
- .toggle input:checked + .toggle-track { background: var(--accent); }
1802
- .toggle-thumb {
1803
- position: absolute;
1804
- top: 2px;
1805
- left: 2px;
1806
- width: 18px;
1807
- height: 18px;
1808
- border-radius: 50%;
1809
- background: #fff;
1810
- transition: transform var(--transition);
1811
- box-shadow: 0 1px 3px rgba(0,0,0,0.3);
1354
+ .ckpt-select-group label {
1355
+ font-size: 0.68rem;
1356
+ color: var(--text-muted, #888);
1357
+ font-weight: 600;
1358
+ text-transform: uppercase;
1359
+ letter-spacing: 0.04em;
1812
1360
  }
1813
- .toggle input:checked ~ .toggle-thumb { transform: translateX(18px); }
1814
-
1815
- .setting-input {
1816
- width: 100%;
1817
- padding: 7px 10px;
1818
- background: var(--bg);
1819
- border: 1px solid var(--border);
1820
- border-radius: var(--radius);
1821
- color: var(--text);
1822
- font-family: var(--font-mono);
1823
- font-size: 0.8rem;
1824
- transition: border-color var(--transition);
1361
+ .ckpt-select-group select {
1362
+ background: var(--bg-secondary, #1a1a1a);
1363
+ color: var(--text, #e0e0e0);
1364
+ border: 1px solid var(--border, #2a2a2a);
1365
+ border-radius: 4px;
1366
+ padding: 4px 8px;
1367
+ font-size: 0.78rem;
1368
+ font-family: inherit;
1369
+ min-width: 180px;
1825
1370
  }
1826
- .setting-input:focus {
1827
- outline: none;
1828
- border-color: var(--accent);
1829
- box-shadow: 0 0 0 3px var(--accent-glow);
1371
+ .ckpt-arrow {
1372
+ color: var(--text-muted, #888);
1373
+ font-size: 1.1rem;
1374
+ margin-top: 14px;
1830
1375
  }
1831
- .setting-input-sm { width: 80px; text-align: center; }
1832
1376
 
1833
- .settings-footer {
1377
+ /* diff summary */
1378
+ .diff-summary {
1834
1379
  display: flex;
1835
- align-items: center;
1836
- justify-content: flex-end;
1837
- gap: 8px;
1838
- padding: 14px 20px;
1839
- border-top: 1px solid var(--border);
1380
+ gap: 12px;
1381
+ font-size: 0.78rem;
1382
+ padding: 8px 0;
1383
+ border-bottom: 1px solid var(--border, #2a2a2a);
1384
+ margin-bottom: 8px;
1840
1385
  }
1386
+ .diff-stat-files { color: var(--text, #e0e0e0); }
1387
+ .diff-stat-add { color: #4ade80; }
1388
+ .diff-stat-del { color: #f87171; }
1841
1389
 
1842
- .setting-restart-hint {
1843
- font-size: 0.7rem;
1844
- color: var(--yellow);
1845
- margin-right: auto;
1390
+ /* diff file blocks */
1391
+ .diff-file {
1392
+ margin-bottom: 6px;
1393
+ border: 1px solid var(--border, #2a2a2a);
1394
+ border-radius: 6px;
1395
+ overflow: hidden;
1396
+ }
1397
+ .diff-file.collapsed .diff-file-body { display: none; }
1398
+ .diff-file-header {
1846
1399
  display: flex;
1400
+ justify-content: space-between;
1847
1401
  align-items: center;
1848
- gap: 4px;
1402
+ padding: 6px 10px;
1403
+ background: var(--bg-secondary, #1a1a1a);
1404
+ cursor: pointer;
1405
+ font-size: 0.78rem;
1406
+ user-select: none;
1849
1407
  }
1850
-
1851
- /* ── Connection Modal ── */
1852
- .conn-message {
1853
- font-size: 0.8rem;
1854
- color: var(--red);
1855
- background: var(--red-dim);
1856
- border: 1px solid rgba(248, 113, 113, 0.25);
1857
- border-radius: var(--radius);
1858
- padding: 8px 12px;
1408
+ .diff-file-header:hover { background: var(--bg-hover, #222); }
1409
+ .diff-file-name {
1410
+ font-family: 'SF Mono', 'Fira Code', monospace;
1411
+ color: var(--text, #e0e0e0);
1412
+ font-weight: 500;
1859
1413
  }
1860
-
1861
- /* ── Running Job Preview Row ── */
1862
- .preview-row {
1863
- cursor: default !important;
1414
+ .diff-file-stats {
1415
+ display: flex;
1416
+ gap: 8px;
1417
+ font-size: 0.72rem;
1418
+ font-weight: 600;
1864
1419
  }
1865
- .preview-row,
1866
- .preview-row:hover {
1867
- background: transparent !important;
1420
+ .diff-file-body {
1421
+ overflow-x: auto;
1868
1422
  }
1869
- .preview-row td {
1870
- padding: 0 !important;
1871
- border-bottom: 1px solid var(--border) !important;
1423
+ .diff-code {
1424
+ margin: 0;
1425
+ padding: 0;
1426
+ font-family: 'SF Mono', 'Fira Code', monospace;
1427
+ font-size: 0.74rem;
1428
+ line-height: 1.45;
1429
+ tab-size: 4;
1872
1430
  }
1873
1431
 
1874
- .job-preview {
1875
- padding: 6px 14px;
1876
- background: var(--bg);
1877
- font-family: var(--font-mono);
1878
- font-size: 0.78rem;
1879
- line-height: 1.7;
1880
- color: var(--text-muted);
1881
- max-height: calc(0.78rem * 1.7 * 2 + 6px + 6px);
1882
- overflow: hidden;
1883
- box-sizing: content-box;
1432
+ /* diff lines */
1433
+ .diff-line {
1434
+ padding: 0 10px;
1435
+ white-space: pre;
1436
+ min-height: 1.45em;
1884
1437
  }
1885
- .preview-lines {
1886
- min-width: 0;
1887
- overflow: hidden;
1438
+ .diff-add {
1439
+ background: rgba(74, 222, 128, 0.1);
1440
+ color: #4ade80;
1888
1441
  }
1889
- .preview-line {
1890
- padding: 2px 0;
1891
- word-break: break-word;
1442
+ .diff-del {
1443
+ background: rgba(248, 113, 113, 0.1);
1444
+ color: #f87171;
1892
1445
  }
1893
- .preview-line + .preview-line {
1894
- margin-top: 1px;
1446
+ .diff-hunk {
1447
+ background: rgba(96, 165, 250, 0.08);
1448
+ color: #60a5fa;
1449
+ font-style: italic;
1450
+ padding-top: 4px;
1451
+ padding-bottom: 4px;
1452
+ margin-top: 2px;
1895
1453
  }
1896
- .preview-tool {
1897
- display: inline-flex;
1454
+ .diff-meta {
1455
+ color: var(--text-muted, #888);
1456
+ font-style: italic;
1457
+ }
1458
+
1459
+ .diff-result {
1460
+ max-height: 500px;
1461
+ overflow-y: auto;
1462
+ }
1463
+
1464
+ @media (max-width: 600px) {
1465
+ .main { padding: 4px 0; }
1466
+ .card-body { padding: 14px; }
1467
+ th, td { padding: 8px 6px; font-size: 0.74rem; }
1468
+ .stream-content { max-height: 300px; }
1469
+ .ckpt-selector { flex-direction: column; align-items: stretch; }
1470
+ .ckpt-arrow { display: none; }
1471
+ .diff-result { max-height: 350px; }
1472
+
1473
+ .project-detail { padding: 10px 12px 8px; }
1474
+ .pd-title-row { flex-wrap: wrap; gap: 6px; }
1475
+ .pd-meta { padding-left: 0; font-size: 0.66rem; }
1476
+ .pd-path-text { max-width: 100%; }
1477
+ .pd-remote { max-width: 150px; }
1478
+ .pd-stats { padding: 8px 0 2px 0; gap: 5px; }
1479
+ .pd-stat { padding: 4px 8px; min-width: 40px; }
1480
+ .pd-stat-val { font-size: 0.78rem; }
1481
+ .pd-stat-label { font-size: 0.54rem; }
1482
+ .pd-actions .btn { font-size: 0.68rem; padding: 4px 8px; }
1483
+ }
1484
+
1485
+ /* ── Pagination ── */
1486
+ .job-pagination {
1487
+ display: flex;
1898
1488
  align-items: center;
1489
+ justify-content: center;
1899
1490
  gap: 4px;
1900
- padding: 2px 8px;
1901
- border-radius: 4px;
1902
- background: var(--yellow-dim);
1903
- color: var(--yellow);
1904
- font-size: 0.7rem;
1905
- font-weight: 600;
1906
- white-space: nowrap;
1907
- margin-right: 4px;
1491
+ padding: 10px 0 6px;
1908
1492
  }
1909
- .preview-tool::before {
1910
- content: '>';
1911
- opacity: 0.5;
1493
+ .pg-btn {
1494
+ min-width: 32px;
1495
+ height: 32px;
1496
+ border: 1px solid var(--border);
1497
+ border-radius: 6px;
1498
+ background: var(--bg);
1499
+ color: var(--text);
1500
+ font-size: 0.82rem;
1501
+ cursor: pointer;
1502
+ transition: background 0.15s, border-color 0.15s;
1912
1503
  }
1913
- .preview-result {
1914
- color: var(--green);
1504
+ .pg-btn:hover:not(:disabled):not(.active) {
1505
+ background: var(--hover);
1506
+ border-color: var(--accent);
1915
1507
  }
1916
- .preview-result::before {
1917
- content: '✓ ';
1918
- font-weight: 700;
1508
+ .pg-btn.active {
1509
+ background: var(--accent);
1510
+ color: #fff;
1511
+ border-color: var(--accent);
1512
+ font-weight: 600;
1919
1513
  }
1920
- .preview-text {
1921
- color: var(--text-muted);
1514
+ .pg-btn:disabled {
1515
+ opacity: 0.35;
1516
+ cursor: default;
1517
+ }
1518
+ .pg-ellipsis {
1519
+ padding: 0 4px;
1520
+ color: var(--text-secondary);
1521
+ font-size: 0.82rem;
1522
+ }
1523
+ .pg-info {
1524
+ margin-left: 10px;
1525
+ font-size: 0.78rem;
1526
+ color: var(--text-secondary);
1922
1527
  }