claude-code-templates 1.21.8 → 1.21.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,1406 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Claude Code Studio - AI Development Interface</title>
|
|
7
|
+
<meta name="description" content="Execute Claude Code locally or in cloud sandbox environments with real-time task management">
|
|
8
|
+
|
|
9
|
+
<link rel="stylesheet" href="css/styles.css">
|
|
10
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
11
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
12
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
|
13
|
+
|
|
14
|
+
<style>
|
|
15
|
+
/* Additional styles for sandbox interface */
|
|
16
|
+
.sandbox-interface {
|
|
17
|
+
min-height: 100vh;
|
|
18
|
+
background: var(--bg-primary);
|
|
19
|
+
color: var(--text-primary);
|
|
20
|
+
font-family: 'Inter', monospace;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.sandbox-header {
|
|
24
|
+
padding: 1.5rem 0 1rem;
|
|
25
|
+
border-bottom: 1px solid var(--border-secondary);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.ascii-title {
|
|
29
|
+
margin-bottom: 1rem;
|
|
30
|
+
overflow-x: hidden;
|
|
31
|
+
width: 100%;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.ascii-art {
|
|
35
|
+
font-size: 0.65rem;
|
|
36
|
+
line-height: 0.8;
|
|
37
|
+
color: var(--accent-color);
|
|
38
|
+
margin: 0;
|
|
39
|
+
text-align: center;
|
|
40
|
+
overflow: hidden;
|
|
41
|
+
white-space: pre;
|
|
42
|
+
width: 100%;
|
|
43
|
+
max-width: 100vw;
|
|
44
|
+
transform: scale(1);
|
|
45
|
+
transform-origin: center;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@media (max-width: 768px) {
|
|
49
|
+
.ascii-art {
|
|
50
|
+
font-size: 0.5rem;
|
|
51
|
+
transform: scale(0.85);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@media (max-width: 480px) {
|
|
56
|
+
.ascii-art {
|
|
57
|
+
font-size: 0.4rem;
|
|
58
|
+
transform: scale(0.7);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.header-nav {
|
|
63
|
+
display: flex;
|
|
64
|
+
flex-direction: column;
|
|
65
|
+
align-items: center;
|
|
66
|
+
gap: 0.75rem;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.nav-links {
|
|
70
|
+
display: flex;
|
|
71
|
+
align-items: center;
|
|
72
|
+
gap: 0.5rem;
|
|
73
|
+
flex-wrap: wrap;
|
|
74
|
+
justify-content: center;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.nav-link {
|
|
78
|
+
display: flex;
|
|
79
|
+
align-items: center;
|
|
80
|
+
padding: 0.375rem 0.75rem;
|
|
81
|
+
background: var(--bg-secondary);
|
|
82
|
+
border: 1px solid var(--border-primary);
|
|
83
|
+
border-radius: 6px;
|
|
84
|
+
color: var(--text-primary);
|
|
85
|
+
text-decoration: none;
|
|
86
|
+
font-size: 0.85rem;
|
|
87
|
+
font-weight: 500;
|
|
88
|
+
transition: all 0.2s ease;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.nav-link:hover {
|
|
92
|
+
background: var(--accent-color);
|
|
93
|
+
color: white;
|
|
94
|
+
border-color: var(--accent-color);
|
|
95
|
+
transform: translateY(-1px);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.nav-icon {
|
|
99
|
+
margin-right: 0.375rem;
|
|
100
|
+
font-size: 0.9rem;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.nav-separator {
|
|
104
|
+
color: var(--text-secondary);
|
|
105
|
+
margin: 0 0.25rem;
|
|
106
|
+
font-weight: 300;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.terminal-subtitle {
|
|
110
|
+
color: var(--text-secondary);
|
|
111
|
+
font-size: 0.9rem;
|
|
112
|
+
text-align: center;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.task-input-section {
|
|
116
|
+
padding: 2rem 0;
|
|
117
|
+
border-bottom: 1px solid var(--border-secondary);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.task-input-container {
|
|
121
|
+
max-width: 900px;
|
|
122
|
+
margin: 0 auto;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.main-input-container {
|
|
126
|
+
position: relative;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.bottom-controls {
|
|
130
|
+
display: flex;
|
|
131
|
+
justify-content: space-between;
|
|
132
|
+
align-items: center;
|
|
133
|
+
margin-top: 1rem;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.bottom-left {
|
|
137
|
+
display: flex;
|
|
138
|
+
align-items: center;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.bottom-right {
|
|
142
|
+
display: flex;
|
|
143
|
+
align-items: center;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.task-textarea {
|
|
147
|
+
width: 100%;
|
|
148
|
+
min-height: 200px;
|
|
149
|
+
background: var(--bg-secondary);
|
|
150
|
+
border: 1px solid var(--border-primary);
|
|
151
|
+
border-radius: 12px;
|
|
152
|
+
padding: 1.5rem;
|
|
153
|
+
color: var(--text-primary);
|
|
154
|
+
font-family: 'Inter', sans-serif;
|
|
155
|
+
font-size: 1rem;
|
|
156
|
+
line-height: 1.6;
|
|
157
|
+
resize: vertical;
|
|
158
|
+
margin-bottom: 0;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.task-textarea:focus {
|
|
162
|
+
outline: none;
|
|
163
|
+
border-color: var(--accent-color);
|
|
164
|
+
box-shadow: 0 0 0 2px rgba(255, 107, 107, 0.1);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.task-textarea::placeholder {
|
|
168
|
+
color: var(--text-secondary);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.code-btn {
|
|
172
|
+
background: var(--accent-color);
|
|
173
|
+
color: white;
|
|
174
|
+
border: none;
|
|
175
|
+
padding: 1rem 2.5rem;
|
|
176
|
+
border-radius: 8px;
|
|
177
|
+
font-weight: 600;
|
|
178
|
+
cursor: pointer;
|
|
179
|
+
transition: all 0.2s ease;
|
|
180
|
+
font-size: 1rem;
|
|
181
|
+
letter-spacing: 0.025em;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.code-btn:hover {
|
|
185
|
+
background: #e55555;
|
|
186
|
+
transform: translateY(-1px);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.code-btn:disabled {
|
|
190
|
+
background: var(--text-secondary);
|
|
191
|
+
cursor: not-allowed;
|
|
192
|
+
transform: none;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.tasks-section {
|
|
196
|
+
padding: 2rem 0;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.tasks-container {
|
|
200
|
+
max-width: 900px;
|
|
201
|
+
margin: 0 auto;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/* Tasks Tabs */
|
|
205
|
+
.tasks-tabs {
|
|
206
|
+
display: flex;
|
|
207
|
+
gap: 0;
|
|
208
|
+
margin-bottom: 2rem;
|
|
209
|
+
border-bottom: 1px solid var(--border-secondary);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.tab-btn {
|
|
213
|
+
background: none;
|
|
214
|
+
border: none;
|
|
215
|
+
padding: 1rem 2rem;
|
|
216
|
+
font-size: 1.1rem;
|
|
217
|
+
font-weight: 600;
|
|
218
|
+
color: var(--text-secondary);
|
|
219
|
+
cursor: pointer;
|
|
220
|
+
transition: all 0.2s ease;
|
|
221
|
+
border-bottom: 2px solid transparent;
|
|
222
|
+
display: flex;
|
|
223
|
+
align-items: center;
|
|
224
|
+
gap: 0.5rem;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.tab-btn:hover {
|
|
228
|
+
color: var(--text-primary);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.tab-btn.active {
|
|
232
|
+
color: var(--accent-color);
|
|
233
|
+
border-bottom-color: var(--accent-color);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.tab-count {
|
|
237
|
+
background: var(--bg-secondary);
|
|
238
|
+
color: var(--text-secondary);
|
|
239
|
+
padding: 0.2rem 0.5rem;
|
|
240
|
+
border-radius: 10px;
|
|
241
|
+
font-size: 0.8rem;
|
|
242
|
+
min-width: 20px;
|
|
243
|
+
text-align: center;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.tab-btn.active .tab-count {
|
|
247
|
+
background: var(--accent-color);
|
|
248
|
+
color: white;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/* Tab Content */
|
|
252
|
+
.tab-content {
|
|
253
|
+
display: none;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.tab-content.active {
|
|
257
|
+
display: block;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.task-list {
|
|
261
|
+
display: flex;
|
|
262
|
+
flex-direction: column;
|
|
263
|
+
gap: 1rem;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.task-item {
|
|
267
|
+
background: var(--bg-secondary);
|
|
268
|
+
border: 1px solid var(--border-primary);
|
|
269
|
+
border-radius: 8px;
|
|
270
|
+
padding: 1.5rem;
|
|
271
|
+
transition: all 0.2s ease;
|
|
272
|
+
cursor: pointer;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
.task-item:hover {
|
|
276
|
+
border-color: var(--accent-color);
|
|
277
|
+
transform: translateY(-1px);
|
|
278
|
+
box-shadow: 0 4px 12px rgba(255, 107, 107, 0.1);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.task-header {
|
|
282
|
+
display: flex;
|
|
283
|
+
justify-content: between;
|
|
284
|
+
align-items: flex-start;
|
|
285
|
+
margin-bottom: 1rem;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.task-info {
|
|
289
|
+
flex: 1;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.task-title {
|
|
293
|
+
font-size: 1.1rem;
|
|
294
|
+
font-weight: 600;
|
|
295
|
+
margin-bottom: 0.5rem;
|
|
296
|
+
color: var(--text-primary);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.task-meta {
|
|
300
|
+
display: flex;
|
|
301
|
+
gap: 1rem;
|
|
302
|
+
font-size: 0.85rem;
|
|
303
|
+
color: var(--text-secondary);
|
|
304
|
+
margin-bottom: 0.5rem;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
.task-status {
|
|
308
|
+
padding: 0.2rem 0.6rem;
|
|
309
|
+
border-radius: 12px;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.task-agent-label {
|
|
313
|
+
background: var(--accent-color);
|
|
314
|
+
color: white;
|
|
315
|
+
padding: 0.2rem 0.5rem;
|
|
316
|
+
border-radius: 12px;
|
|
317
|
+
font-size: 0.7rem;
|
|
318
|
+
font-weight: 600;
|
|
319
|
+
margin-left: 0.5rem;
|
|
320
|
+
display: inline-block;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
.status-running {
|
|
324
|
+
background: rgba(255, 193, 7, 0.2);
|
|
325
|
+
color: #ffc107;
|
|
326
|
+
border: 1px solid #ffc107;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.status-completed {
|
|
330
|
+
background: rgba(40, 167, 69, 0.2);
|
|
331
|
+
color: #28a745;
|
|
332
|
+
border: 1px solid #28a745;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.status-failed {
|
|
336
|
+
background: rgba(220, 53, 69, 0.2);
|
|
337
|
+
color: #dc3545;
|
|
338
|
+
border: 1px solid #dc3545;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
.task-progress {
|
|
342
|
+
background: var(--bg-primary);
|
|
343
|
+
border-radius: 4px;
|
|
344
|
+
height: 4px;
|
|
345
|
+
margin: 0.5rem 0;
|
|
346
|
+
overflow: hidden;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.progress-bar {
|
|
350
|
+
background: var(--accent-color);
|
|
351
|
+
height: 100%;
|
|
352
|
+
transition: width 0.3s ease;
|
|
353
|
+
animation: progressPulse 2s infinite;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
@keyframes progressPulse {
|
|
357
|
+
0%, 100% { opacity: 1; }
|
|
358
|
+
50% { opacity: 0.7; }
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.task-details {
|
|
362
|
+
margin-top: 1rem;
|
|
363
|
+
padding-top: 1rem;
|
|
364
|
+
border-top: 1px solid var(--border-secondary);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.task-output {
|
|
368
|
+
background: var(--bg-primary);
|
|
369
|
+
border: 1px solid var(--border-primary);
|
|
370
|
+
border-radius: 4px;
|
|
371
|
+
padding: 0.75rem;
|
|
372
|
+
font-family: 'Courier New', monospace;
|
|
373
|
+
font-size: 0.8rem;
|
|
374
|
+
color: var(--text-secondary);
|
|
375
|
+
max-height: 200px;
|
|
376
|
+
overflow-y: auto;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
.empty-state {
|
|
380
|
+
text-align: center;
|
|
381
|
+
padding: 3rem 1rem;
|
|
382
|
+
color: var(--text-secondary);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.empty-icon {
|
|
386
|
+
font-size: 3rem;
|
|
387
|
+
margin-bottom: 1rem;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
.floating-status {
|
|
391
|
+
position: fixed;
|
|
392
|
+
top: 2rem;
|
|
393
|
+
right: 2rem;
|
|
394
|
+
background: var(--bg-secondary);
|
|
395
|
+
border: 1px solid var(--border-primary);
|
|
396
|
+
border-radius: 8px;
|
|
397
|
+
padding: 1rem;
|
|
398
|
+
display: none;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/* Compact Execution Mode Toggle */
|
|
402
|
+
.compact-mode-selector {
|
|
403
|
+
display: flex;
|
|
404
|
+
align-items: center;
|
|
405
|
+
gap: 0.75rem;
|
|
406
|
+
margin-bottom: 1rem;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
.mode-toggle-group {
|
|
410
|
+
display: flex;
|
|
411
|
+
align-items: center;
|
|
412
|
+
gap: 0.5rem;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
.mode-info-icon {
|
|
416
|
+
width: 20px;
|
|
417
|
+
height: 20px;
|
|
418
|
+
border-radius: 50%;
|
|
419
|
+
background: var(--bg-secondary);
|
|
420
|
+
border: 1px solid var(--border-primary);
|
|
421
|
+
display: flex;
|
|
422
|
+
align-items: center;
|
|
423
|
+
justify-content: center;
|
|
424
|
+
cursor: help;
|
|
425
|
+
font-size: 0.7rem;
|
|
426
|
+
color: var(--text-secondary);
|
|
427
|
+
transition: all 0.2s ease;
|
|
428
|
+
position: relative;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
.mode-info-icon:hover {
|
|
432
|
+
background: var(--accent-color);
|
|
433
|
+
color: white;
|
|
434
|
+
border-color: var(--accent-color);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
.mode-info-tooltip {
|
|
438
|
+
position: absolute;
|
|
439
|
+
bottom: 30px;
|
|
440
|
+
left: 50%;
|
|
441
|
+
transform: translateX(-50%);
|
|
442
|
+
background: var(--bg-primary);
|
|
443
|
+
border: 1px solid var(--border-primary);
|
|
444
|
+
border-radius: 6px;
|
|
445
|
+
padding: 0.5rem;
|
|
446
|
+
font-size: 0.8rem;
|
|
447
|
+
color: var(--text-primary);
|
|
448
|
+
white-space: nowrap;
|
|
449
|
+
z-index: 1000;
|
|
450
|
+
opacity: 0;
|
|
451
|
+
visibility: hidden;
|
|
452
|
+
transition: all 0.2s ease;
|
|
453
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
.mode-info-icon:hover .mode-info-tooltip {
|
|
457
|
+
opacity: 1;
|
|
458
|
+
visibility: visible;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
.compact-toggle-switch {
|
|
462
|
+
position: relative;
|
|
463
|
+
display: inline-block;
|
|
464
|
+
width: 60px;
|
|
465
|
+
height: 28px;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
.compact-toggle-switch input {
|
|
469
|
+
opacity: 0;
|
|
470
|
+
width: 0;
|
|
471
|
+
height: 0;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
.compact-toggle-slider {
|
|
475
|
+
position: absolute;
|
|
476
|
+
cursor: pointer;
|
|
477
|
+
top: 0;
|
|
478
|
+
left: 0;
|
|
479
|
+
right: 0;
|
|
480
|
+
bottom: 0;
|
|
481
|
+
background: var(--bg-secondary);
|
|
482
|
+
border: 1px solid var(--border-primary);
|
|
483
|
+
transition: 0.3s;
|
|
484
|
+
border-radius: 14px;
|
|
485
|
+
display: flex;
|
|
486
|
+
align-items: center;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
.compact-toggle-slider:before {
|
|
490
|
+
position: absolute;
|
|
491
|
+
content: "";
|
|
492
|
+
height: 20px;
|
|
493
|
+
width: 20px;
|
|
494
|
+
left: 3px;
|
|
495
|
+
background: var(--text-secondary);
|
|
496
|
+
transition: 0.3s;
|
|
497
|
+
border-radius: 50%;
|
|
498
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
.compact-toggle-switch input:checked + .compact-toggle-slider {
|
|
502
|
+
background: rgba(255, 107, 107, 0.1);
|
|
503
|
+
border-color: var(--accent-color);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
.compact-toggle-switch input:checked + .compact-toggle-slider:before {
|
|
507
|
+
background: var(--accent-color);
|
|
508
|
+
transform: translateX(32px);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
.toggle-labels-compact {
|
|
512
|
+
display: flex;
|
|
513
|
+
align-items: center;
|
|
514
|
+
gap: 0.5rem;
|
|
515
|
+
font-size: 0.8rem;
|
|
516
|
+
color: var(--text-secondary);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
.toggle-label-active {
|
|
520
|
+
color: var(--text-primary);
|
|
521
|
+
font-weight: 500;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
.api-keys-notice.cloud-only {
|
|
525
|
+
display: block;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
.api-keys-notice.local-hidden {
|
|
529
|
+
display: none;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/* Agent Autocomplete Styles */
|
|
533
|
+
.autocomplete-container {
|
|
534
|
+
position: relative;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
.autocomplete-dropdown {
|
|
538
|
+
position: absolute;
|
|
539
|
+
top: 100%;
|
|
540
|
+
left: 0;
|
|
541
|
+
right: 0;
|
|
542
|
+
background: var(--bg-secondary);
|
|
543
|
+
border: 1px solid var(--border-primary);
|
|
544
|
+
border-radius: 8px;
|
|
545
|
+
max-height: 200px;
|
|
546
|
+
overflow-y: auto;
|
|
547
|
+
z-index: 1000;
|
|
548
|
+
display: none;
|
|
549
|
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
.autocomplete-item {
|
|
553
|
+
padding: 0.75rem 1rem;
|
|
554
|
+
cursor: pointer;
|
|
555
|
+
display: flex;
|
|
556
|
+
align-items: center;
|
|
557
|
+
border-bottom: 1px solid var(--border-secondary);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
.autocomplete-item:last-child {
|
|
561
|
+
border-bottom: none;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
.autocomplete-item:hover,
|
|
565
|
+
.autocomplete-item.selected {
|
|
566
|
+
background: var(--accent-color);
|
|
567
|
+
color: white;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
.autocomplete-item .agent-name {
|
|
571
|
+
font-weight: 600;
|
|
572
|
+
margin-right: 0.5rem;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
.autocomplete-item .agent-category {
|
|
576
|
+
font-size: 0.7rem;
|
|
577
|
+
color: var(--text-secondary);
|
|
578
|
+
margin-left: auto;
|
|
579
|
+
background: var(--bg-primary);
|
|
580
|
+
padding: 0.2rem 0.4rem;
|
|
581
|
+
border-radius: 4px;
|
|
582
|
+
border: 1px solid var(--border-secondary);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
.autocomplete-item:hover .agent-category,
|
|
586
|
+
.autocomplete-item.selected .agent-category {
|
|
587
|
+
color: rgba(255, 255, 255, 0.8);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/* Task Modal */
|
|
591
|
+
.modal {
|
|
592
|
+
display: none;
|
|
593
|
+
position: fixed;
|
|
594
|
+
top: 0;
|
|
595
|
+
left: 0;
|
|
596
|
+
width: 100%;
|
|
597
|
+
height: 100%;
|
|
598
|
+
background: rgba(0, 0, 0, 0.5);
|
|
599
|
+
z-index: 10000;
|
|
600
|
+
backdrop-filter: blur(4px);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
.modal.active {
|
|
604
|
+
display: flex;
|
|
605
|
+
align-items: center;
|
|
606
|
+
justify-content: center;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
.modal-content {
|
|
610
|
+
background: var(--bg-primary);
|
|
611
|
+
border: 1px solid var(--border-primary);
|
|
612
|
+
border-radius: 12px;
|
|
613
|
+
width: 90%;
|
|
614
|
+
max-width: 700px;
|
|
615
|
+
max-height: 80vh;
|
|
616
|
+
overflow: hidden;
|
|
617
|
+
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
|
|
618
|
+
animation: modalSlideIn 0.3s ease;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
@keyframes modalSlideIn {
|
|
622
|
+
from {
|
|
623
|
+
opacity: 0;
|
|
624
|
+
transform: scale(0.9) translateY(20px);
|
|
625
|
+
}
|
|
626
|
+
to {
|
|
627
|
+
opacity: 1;
|
|
628
|
+
transform: scale(1) translateY(0);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
.modal-header {
|
|
633
|
+
display: flex;
|
|
634
|
+
justify-content: space-between;
|
|
635
|
+
align-items: center;
|
|
636
|
+
padding: 1.5rem;
|
|
637
|
+
border-bottom: 1px solid var(--border-secondary);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
.modal-header h3 {
|
|
641
|
+
margin: 0;
|
|
642
|
+
font-size: 1.3rem;
|
|
643
|
+
font-weight: 600;
|
|
644
|
+
color: var(--text-primary);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
.modal-close {
|
|
648
|
+
background: none;
|
|
649
|
+
border: none;
|
|
650
|
+
font-size: 1.8rem;
|
|
651
|
+
color: var(--text-secondary);
|
|
652
|
+
cursor: pointer;
|
|
653
|
+
padding: 0.25rem;
|
|
654
|
+
border-radius: 4px;
|
|
655
|
+
transition: all 0.2s ease;
|
|
656
|
+
width: 32px;
|
|
657
|
+
height: 32px;
|
|
658
|
+
display: flex;
|
|
659
|
+
align-items: center;
|
|
660
|
+
justify-content: center;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
.modal-close:hover {
|
|
664
|
+
background: var(--bg-secondary);
|
|
665
|
+
color: var(--accent-color);
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
.modal-body {
|
|
669
|
+
padding: 1.5rem;
|
|
670
|
+
max-height: 60vh;
|
|
671
|
+
overflow-y: auto;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
.task-info-row {
|
|
675
|
+
display: flex;
|
|
676
|
+
align-items: center;
|
|
677
|
+
margin-bottom: 1rem;
|
|
678
|
+
gap: 1rem;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
.info-label {
|
|
682
|
+
font-weight: 600;
|
|
683
|
+
color: var(--text-secondary);
|
|
684
|
+
min-width: 80px;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
.logs-section {
|
|
688
|
+
margin-top: 2rem;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
.logs-section h4 {
|
|
692
|
+
margin: 0 0 1rem 0;
|
|
693
|
+
font-size: 1.1rem;
|
|
694
|
+
font-weight: 600;
|
|
695
|
+
color: var(--text-primary);
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
.logs-container {
|
|
699
|
+
background: var(--bg-secondary);
|
|
700
|
+
border: 1px solid var(--border-primary);
|
|
701
|
+
border-radius: 8px;
|
|
702
|
+
padding: 1rem;
|
|
703
|
+
max-height: 300px;
|
|
704
|
+
overflow-y: auto;
|
|
705
|
+
font-family: 'Courier New', monospace;
|
|
706
|
+
font-size: 0.85rem;
|
|
707
|
+
line-height: 1.5;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
.log-entry {
|
|
711
|
+
margin-bottom: 0.5rem;
|
|
712
|
+
color: var(--text-primary);
|
|
713
|
+
word-wrap: break-word;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
.log-entry:last-child {
|
|
717
|
+
margin-bottom: 0;
|
|
718
|
+
}
|
|
719
|
+
</style>
|
|
720
|
+
</head>
|
|
721
|
+
<body class="sandbox-interface">
|
|
722
|
+
<div class="container">
|
|
723
|
+
<header class="sandbox-header">
|
|
724
|
+
<div class="terminal-header">
|
|
725
|
+
<div class="ascii-title">
|
|
726
|
+
<pre class="ascii-art">
|
|
727
|
+
██████╗██╗ █████╗ ██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗ ██████╗ ███████╗ ███████╗████████╗██╗ ██╗██████╗ ██╗ ██████╗
|
|
728
|
+
██╔════╝██║ ██╔══██╗██║ ██║██╔══██╗██╔════╝ ██╔════╝██╔═══██╗██╔══██╗██╔════╝ ██╔════╝╚══██╔══╝██║ ██║██╔══██╗██║██╔═══██╗
|
|
729
|
+
██║ ██║ ███████║██║ ██║██║ ██║█████╗ ██║ ██║ ██║██║ ██║█████╗ ███████╗ ██║ ██║ ██║██║ ██║██║██║ ██║
|
|
730
|
+
██║ ██║ ██╔══██║██║ ██║██║ ██║██╔══╝ ██║ ██║ ██║██║ ██║██╔══╝ ╚════██║ ██║ ██║ ██║██║ ██║██║██║ ██║
|
|
731
|
+
╚██████╗███████╗██║ ██║╚██████╔╝██████╔╝███████╗ ╚██████╗╚██████╔╝██████╔╝███████╗ ███████║ ██║ ╚██████╔╝██████╔╝██║╚██████╔╝
|
|
732
|
+
╚═════╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚══════╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═════╝ </pre>
|
|
733
|
+
</div>
|
|
734
|
+
<div class="header-nav">
|
|
735
|
+
<div class="nav-links">
|
|
736
|
+
<a href="https://www.anthropic.com/claude-code?ref=aitmpl.com" target="_blank" class="nav-link">
|
|
737
|
+
Claude Code
|
|
738
|
+
</a>
|
|
739
|
+
<span class="nav-separator">|</span>
|
|
740
|
+
<a href="https://aitmpl.com" target="_blank" class="nav-link">
|
|
741
|
+
Templates
|
|
742
|
+
</a>
|
|
743
|
+
<span class="nav-separator">|</span>
|
|
744
|
+
<a href="https://github.com/davila7/claude-code-templates" target="_blank" class="nav-link">
|
|
745
|
+
GitHub
|
|
746
|
+
</a>
|
|
747
|
+
</div>
|
|
748
|
+
</div>
|
|
749
|
+
</div>
|
|
750
|
+
</header>
|
|
751
|
+
|
|
752
|
+
<section class="task-input-section">
|
|
753
|
+
<div class="task-input-container">
|
|
754
|
+
<!-- Main Title -->
|
|
755
|
+
<div style="text-align: center; margin-bottom: 3rem;">
|
|
756
|
+
<h1 style="margin: 0; font-size: 2.5rem; font-weight: 800; color: var(--text-primary); letter-spacing: -0.02em;">
|
|
757
|
+
What are we coding next?
|
|
758
|
+
</h1>
|
|
759
|
+
</div>
|
|
760
|
+
|
|
761
|
+
|
|
762
|
+
<!-- Main Input Container -->
|
|
763
|
+
<div class="main-input-container">
|
|
764
|
+
<div class="autocomplete-container">
|
|
765
|
+
<textarea
|
|
766
|
+
id="taskInput"
|
|
767
|
+
class="task-textarea"
|
|
768
|
+
placeholder="Describe what you want to build..."></textarea>
|
|
769
|
+
|
|
770
|
+
<div id="autocompleteDropdown" class="autocomplete-dropdown">
|
|
771
|
+
<!-- Agent suggestions will be populated here -->
|
|
772
|
+
</div>
|
|
773
|
+
</div>
|
|
774
|
+
|
|
775
|
+
<!-- Bottom Controls -->
|
|
776
|
+
<div class="bottom-controls">
|
|
777
|
+
<!-- Left: Execution Mode Toggle -->
|
|
778
|
+
<div class="bottom-left">
|
|
779
|
+
<div class="compact-mode-selector">
|
|
780
|
+
<div class="mode-toggle-group">
|
|
781
|
+
<div class="toggle-labels-compact">
|
|
782
|
+
<span id="localLabel" class="toggle-label-active">Local</span>
|
|
783
|
+
</div>
|
|
784
|
+
<label class="compact-toggle-switch">
|
|
785
|
+
<input type="checkbox" id="executionModeToggle" onchange="toggleExecutionMode()">
|
|
786
|
+
<span class="compact-toggle-slider"></span>
|
|
787
|
+
</label>
|
|
788
|
+
<div class="toggle-labels-compact">
|
|
789
|
+
<span id="cloudLabel">Cloud</span>
|
|
790
|
+
</div>
|
|
791
|
+
</div>
|
|
792
|
+
<div class="mode-info-icon">
|
|
793
|
+
<span>i</span>
|
|
794
|
+
<div class="mode-info-tooltip">
|
|
795
|
+
Switch between Local (Claude Code CLI) and Cloud (E2B Sandbox) execution
|
|
796
|
+
</div>
|
|
797
|
+
</div>
|
|
798
|
+
</div>
|
|
799
|
+
</div>
|
|
800
|
+
|
|
801
|
+
<!-- Right: Code Button -->
|
|
802
|
+
<div class="bottom-right">
|
|
803
|
+
<button id="codeBtn" class="code-btn" onclick="executeTask()">
|
|
804
|
+
Code
|
|
805
|
+
</button>
|
|
806
|
+
</div>
|
|
807
|
+
</div>
|
|
808
|
+
</div>
|
|
809
|
+
</div>
|
|
810
|
+
</section>
|
|
811
|
+
|
|
812
|
+
<section class="tasks-section">
|
|
813
|
+
<div class="tasks-container">
|
|
814
|
+
<!-- Tasks Tabs -->
|
|
815
|
+
<div class="tasks-tabs">
|
|
816
|
+
<button class="tab-btn active" id="tasksTab" onclick="switchTab('tasks')">
|
|
817
|
+
Tasks
|
|
818
|
+
<span class="tab-count" id="tasksCount">0</span>
|
|
819
|
+
</button>
|
|
820
|
+
<button class="tab-btn" id="archiveTab" onclick="switchTab('archive')">
|
|
821
|
+
Archive
|
|
822
|
+
<span class="tab-count" id="archiveCount">0</span>
|
|
823
|
+
</button>
|
|
824
|
+
</div>
|
|
825
|
+
|
|
826
|
+
<!-- Tasks Content -->
|
|
827
|
+
<div class="tab-content active" id="tasksContent">
|
|
828
|
+
<div class="task-list" id="runningTasks">
|
|
829
|
+
<div class="empty-state">
|
|
830
|
+
<div class="empty-icon">⚡</div>
|
|
831
|
+
<p>No active tasks</p>
|
|
832
|
+
</div>
|
|
833
|
+
</div>
|
|
834
|
+
</div>
|
|
835
|
+
|
|
836
|
+
<!-- Archive Content -->
|
|
837
|
+
<div class="tab-content" id="archiveContent">
|
|
838
|
+
<div class="task-list" id="completedTasks">
|
|
839
|
+
<div class="empty-state">
|
|
840
|
+
<div class="empty-icon">📁</div>
|
|
841
|
+
<p>No archived tasks</p>
|
|
842
|
+
</div>
|
|
843
|
+
</div>
|
|
844
|
+
</div>
|
|
845
|
+
</div>
|
|
846
|
+
</section>
|
|
847
|
+
|
|
848
|
+
<!-- Task Modal -->
|
|
849
|
+
<div id="taskModal" class="modal" onclick="closeTaskModal(event)">
|
|
850
|
+
<div class="modal-content" onclick="event.stopPropagation()">
|
|
851
|
+
<div class="modal-header">
|
|
852
|
+
<h3 id="modalTaskTitle">Task Details</h3>
|
|
853
|
+
<button class="modal-close" onclick="closeTaskModal()">×</button>
|
|
854
|
+
</div>
|
|
855
|
+
<div class="modal-body">
|
|
856
|
+
<div class="task-info-row">
|
|
857
|
+
<span class="info-label">Status:</span>
|
|
858
|
+
<span id="modalTaskStatus" class="task-status">running</span>
|
|
859
|
+
</div>
|
|
860
|
+
<div class="task-info-row">
|
|
861
|
+
<span class="info-label">Agent:</span>
|
|
862
|
+
<span id="modalTaskAgent">frontend-developer</span>
|
|
863
|
+
</div>
|
|
864
|
+
<div class="task-info-row">
|
|
865
|
+
<span class="info-label">Mode:</span>
|
|
866
|
+
<span id="modalTaskMode">local</span>
|
|
867
|
+
</div>
|
|
868
|
+
<div class="task-info-row">
|
|
869
|
+
<span class="info-label">Started:</span>
|
|
870
|
+
<span id="modalTaskTime">--</span>
|
|
871
|
+
</div>
|
|
872
|
+
<div class="logs-section">
|
|
873
|
+
<h4>Execution Logs</h4>
|
|
874
|
+
<div class="logs-container" id="modalTaskLogs">
|
|
875
|
+
<div class="log-entry">Initializing task...</div>
|
|
876
|
+
</div>
|
|
877
|
+
</div>
|
|
878
|
+
</div>
|
|
879
|
+
</div>
|
|
880
|
+
</div>
|
|
881
|
+
</div>
|
|
882
|
+
|
|
883
|
+
<!-- Floating status indicator -->
|
|
884
|
+
<div class="floating-status" id="floatingStatus">
|
|
885
|
+
<div>Status: <span id="statusText">Ready</span></div>
|
|
886
|
+
</div>
|
|
887
|
+
|
|
888
|
+
<script>
|
|
889
|
+
let tasks = [];
|
|
890
|
+
let taskIdCounter = 1;
|
|
891
|
+
let executionMode = 'local'; // Default to local mode
|
|
892
|
+
let agents = []; // Will be populated from components.json
|
|
893
|
+
let selectedAgentIndex = -1;
|
|
894
|
+
|
|
895
|
+
function generateTaskId() {
|
|
896
|
+
return `task-${Date.now()}-${taskIdCounter++}`;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
function toggleExecutionMode() {
|
|
900
|
+
const toggle = document.getElementById('executionModeToggle');
|
|
901
|
+
const localLabel = document.getElementById('localLabel');
|
|
902
|
+
const cloudLabel = document.getElementById('cloudLabel');
|
|
903
|
+
|
|
904
|
+
executionMode = toggle.checked ? 'cloud' : 'local';
|
|
905
|
+
|
|
906
|
+
// Update label highlighting
|
|
907
|
+
if (executionMode === 'cloud') {
|
|
908
|
+
localLabel.classList.remove('toggle-label-active');
|
|
909
|
+
cloudLabel.classList.add('toggle-label-active');
|
|
910
|
+
} else {
|
|
911
|
+
localLabel.classList.add('toggle-label-active');
|
|
912
|
+
cloudLabel.classList.remove('toggle-label-active');
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
// Execution mode changed - no additional UI updates needed
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
function extractAgentFromPrompt(prompt) {
|
|
919
|
+
// Look for @agent-name pattern at the start or after whitespace
|
|
920
|
+
const agentMatch = prompt.match(/(?:^|\s)@([a-zA-Z0-9-_]+)/);
|
|
921
|
+
if (agentMatch) {
|
|
922
|
+
const shortName = agentMatch[1];
|
|
923
|
+
const cleanPrompt = prompt.replace(agentMatch[0], '').trim();
|
|
924
|
+
|
|
925
|
+
// Find the full agent path from the agents list
|
|
926
|
+
const agent = agents.find(a => a.name === shortName);
|
|
927
|
+
const fullAgentPath = agent ? `${agent.category}/${agent.name}` : shortName;
|
|
928
|
+
|
|
929
|
+
return { agentName: fullAgentPath, cleanPrompt };
|
|
930
|
+
}
|
|
931
|
+
return { agentName: null, cleanPrompt: prompt };
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
async function executeTask() {
|
|
935
|
+
const taskInput = document.getElementById('taskInput');
|
|
936
|
+
const prompt = taskInput.value.trim();
|
|
937
|
+
const codeBtn = document.getElementById('codeBtn');
|
|
938
|
+
|
|
939
|
+
if (!prompt) {
|
|
940
|
+
alert('Please describe what you want to create');
|
|
941
|
+
return;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
if (prompt.length < 10) {
|
|
945
|
+
alert('Please provide a more detailed description (at least 10 characters)');
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
// Extract agent from prompt if specified
|
|
950
|
+
const { agentName, cleanPrompt } = extractAgentFromPrompt(prompt);
|
|
951
|
+
|
|
952
|
+
// Disable button during execution
|
|
953
|
+
codeBtn.disabled = true;
|
|
954
|
+
codeBtn.textContent = 'Starting...';
|
|
955
|
+
|
|
956
|
+
try {
|
|
957
|
+
// Send task to server with execution mode and selected agent
|
|
958
|
+
const response = await fetch('/api/execute', {
|
|
959
|
+
method: 'POST',
|
|
960
|
+
headers: {
|
|
961
|
+
'Content-Type': 'application/json',
|
|
962
|
+
},
|
|
963
|
+
body: JSON.stringify({
|
|
964
|
+
prompt: cleanPrompt,
|
|
965
|
+
mode: executionMode,
|
|
966
|
+
agent: agentName || 'development-team/frontend-developer'
|
|
967
|
+
})
|
|
968
|
+
});
|
|
969
|
+
|
|
970
|
+
const result = await response.json();
|
|
971
|
+
|
|
972
|
+
if (result.success) {
|
|
973
|
+
// Clear input and re-enable button
|
|
974
|
+
taskInput.value = '';
|
|
975
|
+
codeBtn.disabled = false;
|
|
976
|
+
codeBtn.innerHTML = `
|
|
977
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="margin-right: 0.5rem;">
|
|
978
|
+
<path d="M8,3A2,2 0 0,0 6,5V9A2,2 0 0,1 4,11H3V13H4A2,2 0 0,1 6,15V19A2,2 0 0,0 8,21H10V19H8V14A2,2 0 0,0 6,12A2,2 0 0,0 8,10V5H10V3M16,3A2,2 0 0,1 18,5V9A2,2 0 0,0 20,11H21V13H20A2,2 0 0,0 18,15V19A2,2 0 0,1 16,21H14V19H16V14A2,2 0 0,1 18,12A2,2 0 0,1 16,10V5H14V3H16Z"/>
|
|
979
|
+
</svg>
|
|
980
|
+
Code
|
|
981
|
+
`;
|
|
982
|
+
|
|
983
|
+
// Start polling for task updates
|
|
984
|
+
startTaskPolling(result.taskId);
|
|
985
|
+
} else {
|
|
986
|
+
alert('Failed to start task: ' + result.error);
|
|
987
|
+
codeBtn.disabled = false;
|
|
988
|
+
codeBtn.textContent = 'Code';
|
|
989
|
+
}
|
|
990
|
+
} catch (error) {
|
|
991
|
+
alert('Error starting task: ' + error.message);
|
|
992
|
+
codeBtn.disabled = false;
|
|
993
|
+
codeBtn.textContent = 'Code';
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
function startTaskPolling(taskId) {
|
|
998
|
+
// Add task to local tasks array for immediate display
|
|
999
|
+
const newTask = {
|
|
1000
|
+
id: taskId,
|
|
1001
|
+
title: 'Loading...',
|
|
1002
|
+
status: 'running',
|
|
1003
|
+
progress: 0,
|
|
1004
|
+
output: 'Initializing task...',
|
|
1005
|
+
startTime: new Date(),
|
|
1006
|
+
sandboxId: 'pending'
|
|
1007
|
+
};
|
|
1008
|
+
tasks.unshift(newTask);
|
|
1009
|
+
updateTaskLists();
|
|
1010
|
+
|
|
1011
|
+
// Start polling for updates
|
|
1012
|
+
const pollInterval = setInterval(async () => {
|
|
1013
|
+
try {
|
|
1014
|
+
const response = await fetch(`/api/task/${taskId}`);
|
|
1015
|
+
const result = await response.json();
|
|
1016
|
+
|
|
1017
|
+
if (result.success) {
|
|
1018
|
+
const taskIndex = tasks.findIndex(t => t.id === taskId);
|
|
1019
|
+
if (taskIndex !== -1) {
|
|
1020
|
+
tasks[taskIndex] = {
|
|
1021
|
+
id: result.task.id,
|
|
1022
|
+
title: result.task.title,
|
|
1023
|
+
status: result.task.status,
|
|
1024
|
+
progress: result.task.progress,
|
|
1025
|
+
output: result.task.output,
|
|
1026
|
+
startTime: new Date(result.task.startTime),
|
|
1027
|
+
endTime: result.task.endTime ? new Date(result.task.endTime) : null,
|
|
1028
|
+
sandboxId: result.task.sandboxId || 'pending'
|
|
1029
|
+
};
|
|
1030
|
+
updateTaskLists();
|
|
1031
|
+
|
|
1032
|
+
// Stop polling if task is completed or failed
|
|
1033
|
+
if (result.task.status === 'completed' || result.task.status === 'failed') {
|
|
1034
|
+
clearInterval(pollInterval);
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
} catch (error) {
|
|
1039
|
+
console.error('Polling error:', error);
|
|
1040
|
+
}
|
|
1041
|
+
}, 2000); // Poll every 2 seconds
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
async function loadAllTasks() {
|
|
1045
|
+
try {
|
|
1046
|
+
const response = await fetch('/api/tasks');
|
|
1047
|
+
const result = await response.json();
|
|
1048
|
+
|
|
1049
|
+
if (result.success) {
|
|
1050
|
+
tasks = result.tasks.map(task => ({
|
|
1051
|
+
id: task.id,
|
|
1052
|
+
title: task.title,
|
|
1053
|
+
status: task.status,
|
|
1054
|
+
progress: task.progress,
|
|
1055
|
+
output: task.output || '',
|
|
1056
|
+
startTime: new Date(task.startTime),
|
|
1057
|
+
endTime: task.endTime ? new Date(task.endTime) : null,
|
|
1058
|
+
sandboxId: task.sandboxId || 'unknown'
|
|
1059
|
+
}));
|
|
1060
|
+
updateTaskLists();
|
|
1061
|
+
}
|
|
1062
|
+
} catch (error) {
|
|
1063
|
+
console.error('Failed to load tasks:', error);
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
// Tab functionality
|
|
1068
|
+
let currentTab = 'tasks';
|
|
1069
|
+
|
|
1070
|
+
function switchTab(tabName) {
|
|
1071
|
+
const tasksTab = document.getElementById('tasksTab');
|
|
1072
|
+
const archiveTab = document.getElementById('archiveTab');
|
|
1073
|
+
const tasksContent = document.getElementById('tasksContent');
|
|
1074
|
+
const archiveContent = document.getElementById('archiveContent');
|
|
1075
|
+
|
|
1076
|
+
// Update tab buttons
|
|
1077
|
+
tasksTab.classList.toggle('active', tabName === 'tasks');
|
|
1078
|
+
archiveTab.classList.toggle('active', tabName === 'archive');
|
|
1079
|
+
|
|
1080
|
+
// Update content visibility
|
|
1081
|
+
tasksContent.classList.toggle('active', tabName === 'tasks');
|
|
1082
|
+
archiveContent.classList.toggle('active', tabName === 'archive');
|
|
1083
|
+
|
|
1084
|
+
currentTab = tabName;
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
function updateTaskLists() {
|
|
1088
|
+
const runningTasks = tasks.filter(t => t.status === 'running');
|
|
1089
|
+
const completedTasks = tasks.filter(t => t.status === 'completed' || t.status === 'failed');
|
|
1090
|
+
|
|
1091
|
+
updateTaskSection('runningTasks', 'tasksCount', runningTasks);
|
|
1092
|
+
updateTaskSection('completedTasks', 'archiveCount', completedTasks);
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
function updateTaskSection(containerId, countId, taskList) {
|
|
1096
|
+
const container = document.getElementById(containerId);
|
|
1097
|
+
const countElement = document.getElementById(countId);
|
|
1098
|
+
|
|
1099
|
+
countElement.textContent = taskList.length;
|
|
1100
|
+
|
|
1101
|
+
if (taskList.length === 0) {
|
|
1102
|
+
const emptyIcon = containerId === 'runningTasks' ? '⚡' : '📁';
|
|
1103
|
+
const emptyText = containerId === 'runningTasks' ? 'No running tasks' : 'No completed tasks';
|
|
1104
|
+
|
|
1105
|
+
container.innerHTML = `
|
|
1106
|
+
<div class="empty-state">
|
|
1107
|
+
<div class="empty-icon">${emptyIcon}</div>
|
|
1108
|
+
<p>${emptyText}</p>
|
|
1109
|
+
</div>
|
|
1110
|
+
`;
|
|
1111
|
+
return;
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
container.innerHTML = taskList.map(task => createTaskHTML(task)).join('');
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
// Modal functionality
|
|
1118
|
+
function openTaskModal(taskId) {
|
|
1119
|
+
const task = tasks.find(t => t.id === taskId);
|
|
1120
|
+
if (!task) return;
|
|
1121
|
+
|
|
1122
|
+
// Update modal content
|
|
1123
|
+
document.getElementById('modalTaskTitle').textContent = task.title;
|
|
1124
|
+
document.getElementById('modalTaskStatus').textContent = task.status;
|
|
1125
|
+
document.getElementById('modalTaskStatus').className = `task-status status-${task.status}`;
|
|
1126
|
+
document.getElementById('modalTaskAgent').textContent = task.agent || 'default';
|
|
1127
|
+
document.getElementById('modalTaskMode').textContent = task.mode || 'local';
|
|
1128
|
+
document.getElementById('modalTaskTime').textContent = task.startTime.toLocaleString();
|
|
1129
|
+
|
|
1130
|
+
// Update logs
|
|
1131
|
+
const logsContainer = document.getElementById('modalTaskLogs');
|
|
1132
|
+
if (task.output) {
|
|
1133
|
+
if (Array.isArray(task.output)) {
|
|
1134
|
+
// If output is an array, map each line
|
|
1135
|
+
logsContainer.innerHTML = task.output.map(line =>
|
|
1136
|
+
`<div class="log-entry">${line}</div>`
|
|
1137
|
+
).join('');
|
|
1138
|
+
} else if (typeof task.output === 'string') {
|
|
1139
|
+
// If output is a string, split by lines and handle \n literals
|
|
1140
|
+
const cleanOutput = task.output.replace(/\\n/g, '\n'); // Convert literal \n to actual newlines
|
|
1141
|
+
const lines = cleanOutput.split('\n').filter(line => line.trim());
|
|
1142
|
+
logsContainer.innerHTML = lines.map(line =>
|
|
1143
|
+
`<div class="log-entry">${line}</div>`
|
|
1144
|
+
).join('');
|
|
1145
|
+
} else {
|
|
1146
|
+
// If output is something else, convert to string and handle \n
|
|
1147
|
+
const cleanOutput = String(task.output).replace(/\\n/g, '\n');
|
|
1148
|
+
const lines = cleanOutput.split('\n').filter(line => line.trim());
|
|
1149
|
+
logsContainer.innerHTML = lines.map(line =>
|
|
1150
|
+
`<div class="log-entry">${line}</div>`
|
|
1151
|
+
).join('');
|
|
1152
|
+
}
|
|
1153
|
+
} else {
|
|
1154
|
+
logsContainer.innerHTML = '<div class="log-entry">No logs available</div>';
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
// Show modal
|
|
1158
|
+
document.getElementById('taskModal').classList.add('active');
|
|
1159
|
+
document.body.style.overflow = 'hidden';
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
function closeTaskModal(event) {
|
|
1163
|
+
if (event && event.target !== event.currentTarget) return;
|
|
1164
|
+
|
|
1165
|
+
document.getElementById('taskModal').classList.remove('active');
|
|
1166
|
+
document.body.style.overflow = 'auto';
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
function createTaskHTML(task) {
|
|
1170
|
+
const duration = task.endTime ?
|
|
1171
|
+
Math.round((task.endTime - task.startTime) / 1000) :
|
|
1172
|
+
Math.round((new Date() - task.startTime) / 1000);
|
|
1173
|
+
|
|
1174
|
+
// Create title with agent prefix and truncate
|
|
1175
|
+
let displayTitle = task.title;
|
|
1176
|
+
if (task.agent && task.agent !== 'development-team/frontend-developer') {
|
|
1177
|
+
const agentName = task.agent.split('/').pop(); // Get agent name without category
|
|
1178
|
+
displayTitle = `@${agentName} ${task.title}`;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
// Truncate title to max 60 characters
|
|
1182
|
+
const truncatedTitle = displayTitle.length > 60 ?
|
|
1183
|
+
displayTitle.substring(0, 57) + '...' : displayTitle;
|
|
1184
|
+
|
|
1185
|
+
// Create agent label if agent is specified
|
|
1186
|
+
const agentLabel = (task.agent && task.agent !== 'development-team/frontend-developer') ?
|
|
1187
|
+
`<span class="task-agent-label">@${task.agent.split('/').pop()}</span>` : '';
|
|
1188
|
+
|
|
1189
|
+
return `
|
|
1190
|
+
<div class="task-item" onclick="openTaskModal('${task.id}')">
|
|
1191
|
+
<div class="task-header">
|
|
1192
|
+
<div class="task-info">
|
|
1193
|
+
<div class="task-title">${truncatedTitle} ${agentLabel}</div>
|
|
1194
|
+
<div class="task-meta">
|
|
1195
|
+
<span>Mode: ${task.mode || 'local'}</span>
|
|
1196
|
+
<span>Duration: ${duration}s</span>
|
|
1197
|
+
<span>Started: ${task.startTime.toLocaleTimeString()}</span>
|
|
1198
|
+
</div>
|
|
1199
|
+
<div class="task-status status-${task.status}">${task.status}</div>
|
|
1200
|
+
</div>
|
|
1201
|
+
</div>
|
|
1202
|
+
${task.status === 'running' ? `
|
|
1203
|
+
<div class="task-progress">
|
|
1204
|
+
<div class="progress-bar" style="width: ${task.progress}%"></div>
|
|
1205
|
+
</div>
|
|
1206
|
+
` : ''}
|
|
1207
|
+
</div>
|
|
1208
|
+
`;
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
function truncateText(text, maxLength) {
|
|
1212
|
+
return text.length > maxLength ? text.substring(0, maxLength) + '...' : text;
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
// Agent Autocomplete Functionality
|
|
1216
|
+
async function loadAgents() {
|
|
1217
|
+
try {
|
|
1218
|
+
const response = await fetch('/components.json');
|
|
1219
|
+
const data = await response.json();
|
|
1220
|
+
agents = data.agents || [];
|
|
1221
|
+
} catch (error) {
|
|
1222
|
+
console.warn('Could not load agents list:', error);
|
|
1223
|
+
agents = [];
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
function getCaretPosition(textarea) {
|
|
1228
|
+
return textarea.selectionStart;
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
function setCaretPosition(textarea, pos) {
|
|
1232
|
+
textarea.setSelectionRange(pos, pos);
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
function findAtSymbolPosition(text, caretPos) {
|
|
1236
|
+
// Find the last @ symbol before the caret position
|
|
1237
|
+
for (let i = caretPos - 1; i >= 0; i--) {
|
|
1238
|
+
if (text[i] === '@') {
|
|
1239
|
+
// Check if this @ is at the start or preceded by whitespace
|
|
1240
|
+
if (i === 0 || /\s/.test(text[i - 1])) {
|
|
1241
|
+
return i;
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
// If we hit whitespace before finding @, stop searching
|
|
1245
|
+
if (/\s/.test(text[i])) {
|
|
1246
|
+
break;
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
return -1;
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
function extractAgentQuery(text, atPos, caretPos) {
|
|
1253
|
+
// Extract the text between @ and caret position
|
|
1254
|
+
return text.substring(atPos + 1, caretPos);
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
function filterAgents(query) {
|
|
1258
|
+
if (!query) return agents.slice(0, 8); // Show first 8 agents if no query
|
|
1259
|
+
|
|
1260
|
+
const lowerQuery = query.toLowerCase();
|
|
1261
|
+
return agents.filter(agent =>
|
|
1262
|
+
agent.name.toLowerCase().includes(lowerQuery) ||
|
|
1263
|
+
agent.category.toLowerCase().includes(lowerQuery)
|
|
1264
|
+
).slice(0, 8); // Limit to 8 results
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
function showAutocomplete(filteredAgents, query) {
|
|
1268
|
+
const dropdown = document.getElementById('autocompleteDropdown');
|
|
1269
|
+
|
|
1270
|
+
if (filteredAgents.length === 0) {
|
|
1271
|
+
dropdown.style.display = 'none';
|
|
1272
|
+
return;
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
dropdown.innerHTML = filteredAgents.map((agent, index) => `
|
|
1276
|
+
<div class="autocomplete-item" data-index="${index}" onclick="selectAgent('${agent.name}')">
|
|
1277
|
+
<span class="agent-name">@${agent.name}</span>
|
|
1278
|
+
<span class="agent-category">[${agent.category}]</span>
|
|
1279
|
+
</div>
|
|
1280
|
+
`).join('');
|
|
1281
|
+
|
|
1282
|
+
dropdown.style.display = 'block';
|
|
1283
|
+
selectedAgentIndex = -1; // Reset selection
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
function hideAutocomplete() {
|
|
1287
|
+
const dropdown = document.getElementById('autocompleteDropdown');
|
|
1288
|
+
dropdown.style.display = 'none';
|
|
1289
|
+
selectedAgentIndex = -1;
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
function selectAgent(agentName) {
|
|
1293
|
+
const textarea = document.getElementById('taskInput');
|
|
1294
|
+
const text = textarea.value;
|
|
1295
|
+
const caretPos = getCaretPosition(textarea);
|
|
1296
|
+
const atPos = findAtSymbolPosition(text, caretPos);
|
|
1297
|
+
|
|
1298
|
+
if (atPos !== -1) {
|
|
1299
|
+
// Replace the @query with the selected agent name
|
|
1300
|
+
const beforeAt = text.substring(0, atPos);
|
|
1301
|
+
const afterCaret = text.substring(caretPos);
|
|
1302
|
+
const newText = beforeAt + '@' + agentName + ' ' + afterCaret;
|
|
1303
|
+
|
|
1304
|
+
textarea.value = newText;
|
|
1305
|
+
|
|
1306
|
+
// Position caret after the inserted agent name
|
|
1307
|
+
const newCaretPos = atPos + agentName.length + 2; // +2 for @ and space
|
|
1308
|
+
setCaretPosition(textarea, newCaretPos);
|
|
1309
|
+
textarea.focus();
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
hideAutocomplete();
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
function handleAutocomplete() {
|
|
1316
|
+
const textarea = document.getElementById('taskInput');
|
|
1317
|
+
const text = textarea.value;
|
|
1318
|
+
const caretPos = getCaretPosition(textarea);
|
|
1319
|
+
const atPos = findAtSymbolPosition(text, caretPos);
|
|
1320
|
+
|
|
1321
|
+
if (atPos !== -1) {
|
|
1322
|
+
const query = extractAgentQuery(text, atPos, caretPos);
|
|
1323
|
+
const filteredAgents = filterAgents(query);
|
|
1324
|
+
showAutocomplete(filteredAgents, query);
|
|
1325
|
+
} else {
|
|
1326
|
+
hideAutocomplete();
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
function handleKeyNavigation(event) {
|
|
1331
|
+
const dropdown = document.getElementById('autocompleteDropdown');
|
|
1332
|
+
|
|
1333
|
+
if (dropdown.style.display !== 'block') return;
|
|
1334
|
+
|
|
1335
|
+
const items = dropdown.querySelectorAll('.autocomplete-item');
|
|
1336
|
+
|
|
1337
|
+
if (event.key === 'ArrowDown') {
|
|
1338
|
+
event.preventDefault();
|
|
1339
|
+
selectedAgentIndex = Math.min(selectedAgentIndex + 1, items.length - 1);
|
|
1340
|
+
updateSelection(items);
|
|
1341
|
+
} else if (event.key === 'ArrowUp') {
|
|
1342
|
+
event.preventDefault();
|
|
1343
|
+
selectedAgentIndex = Math.max(selectedAgentIndex - 1, -1);
|
|
1344
|
+
updateSelection(items);
|
|
1345
|
+
} else if (event.key === 'Enter' && selectedAgentIndex >= 0) {
|
|
1346
|
+
event.preventDefault();
|
|
1347
|
+
const selectedItem = items[selectedAgentIndex];
|
|
1348
|
+
const agentName = selectedItem.querySelector('.agent-name').textContent.slice(1); // Remove @
|
|
1349
|
+
selectAgent(agentName);
|
|
1350
|
+
} else if (event.key === 'Escape') {
|
|
1351
|
+
event.preventDefault();
|
|
1352
|
+
hideAutocomplete();
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
function updateSelection(items) {
|
|
1357
|
+
items.forEach((item, index) => {
|
|
1358
|
+
if (index === selectedAgentIndex) {
|
|
1359
|
+
item.classList.add('selected');
|
|
1360
|
+
} else {
|
|
1361
|
+
item.classList.remove('selected');
|
|
1362
|
+
}
|
|
1363
|
+
});
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
// Initialize the interface
|
|
1367
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
1368
|
+
// Load existing tasks from server
|
|
1369
|
+
loadAllTasks();
|
|
1370
|
+
|
|
1371
|
+
// Load agents for autocomplete
|
|
1372
|
+
loadAgents();
|
|
1373
|
+
|
|
1374
|
+
// Auto-resize textarea and handle autocomplete
|
|
1375
|
+
const textarea = document.getElementById('taskInput');
|
|
1376
|
+
textarea.addEventListener('input', function() {
|
|
1377
|
+
this.style.height = 'auto';
|
|
1378
|
+
this.style.height = Math.max(200, this.scrollHeight) + 'px';
|
|
1379
|
+
|
|
1380
|
+
// Handle agent autocomplete
|
|
1381
|
+
handleAutocomplete();
|
|
1382
|
+
});
|
|
1383
|
+
|
|
1384
|
+
// Handle keyboard navigation for autocomplete
|
|
1385
|
+
textarea.addEventListener('keydown', handleKeyNavigation);
|
|
1386
|
+
|
|
1387
|
+
// Hide autocomplete when clicking outside
|
|
1388
|
+
document.addEventListener('click', function(event) {
|
|
1389
|
+
if (!event.target.closest('.autocomplete-container')) {
|
|
1390
|
+
hideAutocomplete();
|
|
1391
|
+
}
|
|
1392
|
+
});
|
|
1393
|
+
|
|
1394
|
+
// Periodically refresh tasks
|
|
1395
|
+
setInterval(loadAllTasks, 10000); // Refresh every 10 seconds
|
|
1396
|
+
});
|
|
1397
|
+
|
|
1398
|
+
// Keyboard shortcuts
|
|
1399
|
+
document.addEventListener('keydown', function(event) {
|
|
1400
|
+
if ((event.ctrlKey || event.metaKey) && event.key === 'Enter') {
|
|
1401
|
+
executeTask();
|
|
1402
|
+
}
|
|
1403
|
+
});
|
|
1404
|
+
</script>
|
|
1405
|
+
</body>
|
|
1406
|
+
</html>
|