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()">&times;</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>