@cydm/magic-shell-agent-node 0.1.1 → 0.1.2

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,3827 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
6
+ <title>Magic Shell - Retro Terminal</title>
7
+ <!-- xterm.js CSS -->
8
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@5.3.0/css/xterm.css">
9
+ <!-- Retro Font -->
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=VT323&display=swap" rel="stylesheet">
13
+ <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
14
+ <style>
15
+ :root {
16
+ /* Chassis Colors - Olive Green */
17
+ --chassis-main: #8C8C70;
18
+ --chassis-light: #A8A88A;
19
+ --chassis-dark: #6B6B54;
20
+ --chassis-deep: #4A4A38;
21
+
22
+ /* Screen Colors */
23
+ --screen-bg: #11214a;
24
+ --screen-glow: rgba(146, 214, 255, 0.16);
25
+ --screen-curvature: rgba(0, 0, 0, 0.18);
26
+
27
+ /* Text Colors */
28
+ --text-primary: #F5F8FF;
29
+ --text-dim: #AAB8CC;
30
+ --text-amber: #FFB000;
31
+ --text-green: #5CF29A;
32
+
33
+ /* Accent */
34
+ --accent-led: #FF3333;
35
+ --accent-led-off: #330000;
36
+
37
+ /* Keys */
38
+ --key-face: #96967A;
39
+ --key-side: #5A5C45;
40
+ --key-text: #1a1a15;
41
+ --key-nav: #333333;
42
+ --key-nav-text: #cccccc;
43
+
44
+ /* Effects */
45
+ --bevel-light: rgba(255,255,255,0.4);
46
+ --bevel-dark: rgba(0,0,0,0.4);
47
+
48
+ /* UI Controls */
49
+ --btn-radius: 6px;
50
+ --btn-height-sm: 34px;
51
+ --btn-height-md: 38px;
52
+ --btn-padding-x-sm: 10px;
53
+ --btn-padding-x-md: 12px;
54
+ }
55
+
56
+ * {
57
+ box-sizing: border-box;
58
+ user-select: none;
59
+ -webkit-tap-highlight-color: transparent;
60
+ }
61
+
62
+ .xterm,
63
+ .xterm *,
64
+ .node-chat-thread,
65
+ .node-chat-thread *,
66
+ .worker-detail-panel,
67
+ .worker-detail-panel *,
68
+ .runtime-feed,
69
+ .runtime-feed *,
70
+ .runtime-summary,
71
+ .runtime-summary *,
72
+ .current-worker-bar,
73
+ .current-worker-bar * {
74
+ user-select: text;
75
+ }
76
+
77
+ body, html {
78
+ margin: 0;
79
+ padding: 0;
80
+ height: 100%;
81
+ width: 100%;
82
+ overflow: hidden;
83
+ background-color: #111;
84
+ font-family: 'JetBrains Mono', monospace;
85
+ -webkit-font-smoothing: antialiased;
86
+ -moz-osx-font-smoothing: grayscale;
87
+ text-rendering: optimizeLegibility;
88
+ }
89
+
90
+ /* Device Casing - Full Screen */
91
+ .device-casing {
92
+ width: 100%;
93
+ height: 100%;
94
+ background-color: var(--chassis-main);
95
+ display: flex;
96
+ flex-direction: column;
97
+ padding: 16px;
98
+ position: relative;
99
+ box-shadow: inset 0 0 40px rgba(0,0,0,0.5);
100
+ }
101
+
102
+ /* Noise Texture Overlay */
103
+ .device-casing::before {
104
+ content: "";
105
+ position: absolute;
106
+ top: 0; left: 0; width: 100%; height: 100%;
107
+ background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)' opacity='0.08'/%3E%3C/svg%3E");
108
+ pointer-events: none;
109
+ opacity: 0.4;
110
+ mix-blend-mode: multiply;
111
+ z-index: 1;
112
+ }
113
+
114
+ /* Top Bezel with Brand and Status */
115
+ .bezel-top {
116
+ display: flex;
117
+ justify-content: space-between;
118
+ align-items: center;
119
+ padding-bottom: 12px;
120
+ border-bottom: 2px solid var(--chassis-dark);
121
+ margin-bottom: 12px;
122
+ position: relative;
123
+ z-index: 2;
124
+ }
125
+
126
+ .brand-badge {
127
+ font-size: 1.4rem;
128
+ color: var(--chassis-deep);
129
+ font-weight: bold;
130
+ text-shadow: 0 1px 0 rgba(255,255,255,0.2);
131
+ border: 2px solid var(--chassis-deep);
132
+ padding: 2px 12px;
133
+ background: rgba(0,0,0,0.05);
134
+ box-shadow: inset 1px 1px 2px rgba(0,0,0,0.2);
135
+ font-family: 'VT323', monospace;
136
+ letter-spacing: 2px;
137
+ }
138
+
139
+ .power-indicator {
140
+ display: flex;
141
+ align-items: center;
142
+ gap: 8px;
143
+ font-size: 1rem;
144
+ color: var(--chassis-deep);
145
+ font-weight: bold;
146
+ text-shadow: 0 1px 0 rgba(255,255,255,0.1);
147
+ }
148
+
149
+ .led {
150
+ width: 14px;
151
+ height: 14px;
152
+ background-color: var(--accent-led-off);
153
+ border-radius: 2px;
154
+ border: 1px solid rgba(0,0,0,0.5);
155
+ box-shadow: inset 1px 1px 2px rgba(0,0,0,0.8);
156
+ transition: all 0.3s ease;
157
+ }
158
+
159
+ .led.active {
160
+ background-color: var(--accent-led);
161
+ box-shadow: 0 0 8px var(--accent-led), inset 1px 1px 1px rgba(255,255,255,0.5);
162
+ }
163
+
164
+ /* CRT Container */
165
+ .crt-container {
166
+ flex: 1;
167
+ background-color: var(--screen-bg);
168
+ border-radius: 12px;
169
+ position: relative;
170
+ overflow: hidden;
171
+ border: 6px solid;
172
+ border-color: var(--chassis-dark) var(--chassis-light) var(--chassis-light) var(--chassis-dark);
173
+ box-shadow: inset 0 0 18px rgba(0,0,0,0.45);
174
+ padding: 16px;
175
+ z-index: 2;
176
+ }
177
+
178
+ .screen-layout {
179
+ position: relative;
180
+ z-index: 5;
181
+ display: flex;
182
+ height: 100%;
183
+ gap: 12px;
184
+ }
185
+
186
+ .worker-sidebar {
187
+ width: 220px;
188
+ background: rgba(0,0,0,0.25);
189
+ border: 1px solid rgba(255,255,255,0.08);
190
+ border-radius: 8px;
191
+ padding: 10px;
192
+ display: none;
193
+ flex-direction: column;
194
+ gap: 10px;
195
+ }
196
+
197
+ .mobile-sidebar-backdrop {
198
+ display: none;
199
+ }
200
+
201
+ .mobile-sessions-toggle {
202
+ display: none;
203
+ }
204
+
205
+ .worker-sidebar.active {
206
+ display: flex;
207
+ }
208
+
209
+ .worker-header {
210
+ display: flex;
211
+ justify-content: space-between;
212
+ align-items: center;
213
+ color: var(--text-primary);
214
+ font-size: 1.1rem;
215
+ }
216
+
217
+ .session-nav {
218
+ display: flex;
219
+ flex-direction: column;
220
+ gap: 8px;
221
+ min-height: 0;
222
+ overflow: auto;
223
+ }
224
+
225
+ .node-nav-card,
226
+ .worker-item {
227
+ background: rgba(255,255,255,0.04);
228
+ border: 1px solid rgba(255,255,255,0.08);
229
+ border-radius: 6px;
230
+ padding: 8px;
231
+ cursor: pointer;
232
+ color: var(--text-primary);
233
+ }
234
+
235
+ .node-nav-card.active,
236
+ .worker-item.active {
237
+ border-color: var(--text-green);
238
+ box-shadow: 0 0 10px rgba(74, 222, 128, 0.15);
239
+ }
240
+
241
+ .node-nav-card {
242
+ margin-bottom: 2px;
243
+ }
244
+
245
+ .node-nav-title {
246
+ color: var(--text-green);
247
+ font-size: 1rem;
248
+ }
249
+
250
+ .node-nav-meta {
251
+ font-size: 0.85rem;
252
+ color: var(--text-dim);
253
+ margin-top: 4px;
254
+ line-height: 1.25;
255
+ }
256
+
257
+ .node-nav-badge {
258
+ display: inline-flex;
259
+ align-items: center;
260
+ gap: 6px;
261
+ margin-top: 6px;
262
+ padding: 2px 6px;
263
+ border-radius: 999px;
264
+ font-size: 0.78rem;
265
+ border: 1px solid rgba(255,255,255,0.12);
266
+ color: var(--text-amber);
267
+ background: rgba(255,255,255,0.04);
268
+ }
269
+
270
+ .worker-spawn-form {
271
+ display: flex;
272
+ flex-direction: column;
273
+ gap: 7px;
274
+ padding: 8px;
275
+ border: 1px solid rgba(255,255,255,0.08);
276
+ border-radius: 6px;
277
+ background: rgba(255,255,255,0.04);
278
+ }
279
+
280
+ .worker-spawn-topbar {
281
+ display: flex;
282
+ gap: 6px;
283
+ align-items: stretch;
284
+ height: 30px;
285
+ }
286
+
287
+ .worker-spawn-topbar > * ,
288
+ .worker-spawn-actions > * {
289
+ flex: 1 1 0;
290
+ min-width: 0;
291
+ }
292
+
293
+ .worker-select-wrap {
294
+ position: relative;
295
+ flex: 1 1 auto;
296
+ min-width: 0;
297
+ height: 100%;
298
+ }
299
+
300
+ .worker-select-wrap::after {
301
+ content: "▾";
302
+ position: absolute;
303
+ right: 10px;
304
+ top: 50%;
305
+ transform: translateY(-52%);
306
+ color: var(--text-dim);
307
+ font-size: 0.72rem;
308
+ line-height: 1;
309
+ pointer-events: none;
310
+ }
311
+
312
+ .worker-spawn-topbar .worker-spawn-input,
313
+ .worker-spawn-row .worker-spawn-input {
314
+ flex: 1 1 auto;
315
+ min-width: 0;
316
+ }
317
+
318
+ .worker-spawn-field {
319
+ display: flex;
320
+ flex-direction: column;
321
+ gap: 6px;
322
+ }
323
+
324
+ .worker-spawn-actions {
325
+ display: flex;
326
+ gap: 6px;
327
+ }
328
+
329
+ .worker-shortcuts-wrap {
330
+ display: flex;
331
+ flex-direction: column;
332
+ }
333
+
334
+ .worker-spawn-label {
335
+ color: var(--text-dim);
336
+ font-size: 0.74rem;
337
+ margin-bottom: 3px;
338
+ }
339
+
340
+ .worker-spawn-input {
341
+ width: 100%;
342
+ padding: 0 8px;
343
+ border: 1px solid rgba(255,255,255,0.12);
344
+ box-sizing: border-box;
345
+ border-radius: 4px;
346
+ background: rgba(0,0,0,0.28);
347
+ color: var(--text-primary);
348
+ font-family: 'VT323', monospace;
349
+ font-size: 0.95rem;
350
+ outline: none;
351
+ min-height: 30px;
352
+ height: 30px;
353
+ line-height: 1;
354
+ appearance: none;
355
+ }
356
+
357
+ .worker-select-wrap .worker-spawn-input {
358
+ padding-right: 24px;
359
+ }
360
+
361
+ .worker-spawn-input:focus {
362
+ border-color: rgba(74, 222, 128, 0.4);
363
+ }
364
+
365
+ .worker-shortcuts {
366
+ display: flex;
367
+ flex-wrap: wrap;
368
+ gap: 4px;
369
+ max-height: calc(22px * 2 + 4px);
370
+ overflow: hidden;
371
+ }
372
+
373
+ .worker-shortcut {
374
+ background: rgba(255,255,255,0.06);
375
+ color: var(--text-dim);
376
+ border: 1px solid rgba(255,255,255,0.10);
377
+ border-radius: 999px;
378
+ font-family: 'VT323', monospace;
379
+ font-size: 0.72rem;
380
+ min-height: 24px;
381
+ padding: 0 7px;
382
+ display: inline-flex;
383
+ align-items: center;
384
+ justify-content: center;
385
+ cursor: pointer;
386
+ max-width: 100%;
387
+ line-height: 1;
388
+ white-space: nowrap;
389
+ }
390
+
391
+ .worker-spawn-row {
392
+ display: flex;
393
+ gap: 6px;
394
+ align-items: center;
395
+ }
396
+
397
+ .worker-browse-btn {
398
+ background: rgba(255,255,255,0.08);
399
+ color: var(--text-primary);
400
+ border: 1px solid rgba(255,255,255,0.12);
401
+ box-sizing: border-box;
402
+ border-radius: var(--btn-radius);
403
+ font-family: 'VT323', monospace;
404
+ font-size: 0.84rem;
405
+ min-height: 24px;
406
+ height: 24px;
407
+ padding: 0 10px;
408
+ display: inline-flex;
409
+ align-items: center;
410
+ justify-content: center;
411
+ cursor: pointer;
412
+ flex-shrink: 0;
413
+ touch-action: manipulation;
414
+ line-height: 1;
415
+ }
416
+
417
+ .worker-browse-btn:disabled {
418
+ opacity: 0.45;
419
+ cursor: not-allowed;
420
+ }
421
+
422
+ .worker-add-btn {
423
+ flex: 0 0 44px;
424
+ width: 44px;
425
+ min-height: 30px;
426
+ height: 30px;
427
+ padding: 0;
428
+ box-sizing: border-box;
429
+ border-radius: 4px;
430
+ appearance: none;
431
+ -webkit-appearance: none;
432
+ margin: 0;
433
+ }
434
+
435
+ .worker-dir-browser {
436
+ display: none;
437
+ flex-direction: column;
438
+ gap: 4px;
439
+ padding: 6px;
440
+ border: 1px solid rgba(255,255,255,0.08);
441
+ border-radius: 6px;
442
+ background: rgba(0,0,0,0.22);
443
+ }
444
+
445
+ .worker-dir-browser.active {
446
+ display: flex;
447
+ }
448
+
449
+ .worker-dir-path {
450
+ color: var(--text-amber);
451
+ font-size: 0.74rem;
452
+ word-break: break-word;
453
+ display: flex;
454
+ flex-wrap: wrap;
455
+ gap: 3px;
456
+ align-items: center;
457
+ }
458
+
459
+ .worker-dir-crumb {
460
+ cursor: pointer;
461
+ color: var(--text-amber);
462
+ text-decoration: underline;
463
+ text-underline-offset: 2px;
464
+ }
465
+
466
+ .worker-dir-sep {
467
+ color: var(--text-dim);
468
+ }
469
+
470
+ .worker-dir-list {
471
+ display: flex;
472
+ flex-direction: column;
473
+ gap: 3px;
474
+ max-height: 180px;
475
+ overflow: auto;
476
+ }
477
+
478
+ .worker-dir-item {
479
+ background: rgba(255,255,255,0.04);
480
+ border: 1px solid rgba(255,255,255,0.08);
481
+ border-radius: 4px;
482
+ padding: 0 8px;
483
+ min-height: 24px;
484
+ height: 24px;
485
+ display: flex;
486
+ align-items: center;
487
+ font-size: 0.82rem;
488
+ line-height: 1;
489
+ color: var(--text-primary);
490
+ cursor: pointer;
491
+ white-space: nowrap;
492
+ overflow: hidden;
493
+ text-overflow: ellipsis;
494
+ }
495
+
496
+ .worker-dir-empty {
497
+ color: var(--text-dim);
498
+ font-size: 0.74rem;
499
+ }
500
+
501
+ .current-worker-bar {
502
+ display: none;
503
+ align-items: flex-start;
504
+ justify-content: space-between;
505
+ gap: 12px;
506
+ margin-bottom: 8px;
507
+ padding: 8px 10px;
508
+ background: rgba(255,255,255,0.05);
509
+ border: 1px solid rgba(255,255,255,0.08);
510
+ border-radius: 6px;
511
+ color: var(--text-primary);
512
+ }
513
+
514
+ .current-worker-bar.active {
515
+ display: flex;
516
+ }
517
+
518
+ .current-worker-main {
519
+ min-width: 0;
520
+ }
521
+
522
+ .current-worker-title {
523
+ color: var(--text-green);
524
+ font-size: 1rem;
525
+ }
526
+
527
+ .current-worker-subtitle {
528
+ color: var(--text-dim);
529
+ font-size: 0.85rem;
530
+ white-space: nowrap;
531
+ overflow: hidden;
532
+ text-overflow: ellipsis;
533
+ }
534
+
535
+ .current-worker-stop {
536
+ background: rgba(255, 51, 51, 0.14);
537
+ color: #ffd4d4;
538
+ border: 1px solid rgba(255, 51, 51, 0.35);
539
+ border-radius: var(--btn-radius);
540
+ font-family: 'VT323', monospace;
541
+ font-size: 0.95rem;
542
+ min-height: var(--btn-height-md);
543
+ padding: 0 var(--btn-padding-x-md);
544
+ display: inline-flex;
545
+ align-items: center;
546
+ justify-content: center;
547
+ cursor: pointer;
548
+ flex-shrink: 0;
549
+ line-height: 1;
550
+ }
551
+
552
+ .current-worker-actions {
553
+ display: flex;
554
+ gap: 8px;
555
+ flex-shrink: 0;
556
+ }
557
+
558
+ .current-worker-action {
559
+ background: rgba(255,255,255,0.08);
560
+ color: var(--text-primary);
561
+ border: 1px solid rgba(255,255,255,0.12);
562
+ border-radius: var(--btn-radius);
563
+ font-family: 'VT323', monospace;
564
+ font-size: 0.95rem;
565
+ min-height: var(--btn-height-md);
566
+ padding: 0 var(--btn-padding-x-md);
567
+ display: inline-flex;
568
+ align-items: center;
569
+ justify-content: center;
570
+ cursor: pointer;
571
+ touch-action: manipulation;
572
+ line-height: 1;
573
+ }
574
+
575
+ .worker-mobile-action {
576
+ display: none;
577
+ }
578
+
579
+ .current-worker-action:disabled {
580
+ opacity: 0.45;
581
+ cursor: not-allowed;
582
+ }
583
+
584
+ .current-worker-stop:disabled {
585
+ opacity: 0.45;
586
+ cursor: not-allowed;
587
+ }
588
+
589
+ .worker-add-btn {
590
+ background: rgba(255,255,255,0.08);
591
+ color: var(--text-green);
592
+ border: 1px solid rgba(255,255,255,0.12);
593
+ border-radius: var(--btn-radius);
594
+ font-family: 'VT323', monospace;
595
+ font-size: 0.95rem;
596
+ min-height: 30px;
597
+ height: 30px;
598
+ padding: 0 10px;
599
+ display: inline-flex;
600
+ align-items: center;
601
+ justify-content: center;
602
+ cursor: pointer;
603
+ touch-action: manipulation;
604
+ line-height: 1;
605
+ }
606
+
607
+ .worker-add-btn:disabled {
608
+ opacity: 0.55;
609
+ cursor: not-allowed;
610
+ }
611
+
612
+ .worker-close-btn {
613
+ display: none;
614
+ background: rgba(255,255,255,0.08);
615
+ color: var(--text-dim);
616
+ border: 1px solid rgba(255,255,255,0.12);
617
+ border-radius: var(--btn-radius);
618
+ font-family: 'VT323', monospace;
619
+ font-size: 1rem;
620
+ min-height: var(--btn-height-md);
621
+ padding: 0 var(--btn-padding-x-md);
622
+ cursor: pointer;
623
+ touch-action: manipulation;
624
+ line-height: 1;
625
+ }
626
+
627
+ .worker-list {
628
+ display: flex;
629
+ flex-direction: column;
630
+ gap: 8px;
631
+ overflow: auto;
632
+ min-height: 0;
633
+ }
634
+
635
+ .worker-section {
636
+ display: flex;
637
+ flex-direction: column;
638
+ gap: 8px;
639
+ }
640
+
641
+ .worker-section-toggle {
642
+ display: inline-flex;
643
+ align-items: center;
644
+ justify-content: space-between;
645
+ gap: 8px;
646
+ min-height: var(--btn-height-sm);
647
+ padding: 0 10px;
648
+ border-radius: var(--btn-radius);
649
+ border: 1px solid rgba(255,255,255,0.08);
650
+ background: rgba(255,255,255,0.03);
651
+ color: var(--text-dim);
652
+ font-family: 'VT323', monospace;
653
+ font-size: 0.9rem;
654
+ cursor: pointer;
655
+ line-height: 1;
656
+ }
657
+
658
+ .worker-section-toggle:hover {
659
+ border-color: rgba(255,255,255,0.16);
660
+ color: var(--text-primary);
661
+ }
662
+
663
+ .worker-section-list {
664
+ display: flex;
665
+ flex-direction: column;
666
+ gap: 8px;
667
+ }
668
+
669
+ .worker-item.inactive {
670
+ opacity: 0.6;
671
+ cursor: default;
672
+ }
673
+
674
+ .worker-item.running .worker-type,
675
+ .worker-item.starting .worker-type {
676
+ color: var(--text-green);
677
+ }
678
+
679
+ .worker-item.blocked .worker-type {
680
+ color: var(--text-amber);
681
+ }
682
+
683
+ .worker-item.needs-attention {
684
+ border-color: rgba(255, 176, 0, 0.35);
685
+ box-shadow: 0 0 12px rgba(255, 176, 0, 0.12);
686
+ }
687
+
688
+ .worker-item.stopping .worker-type,
689
+ .worker-item.stopped .worker-type,
690
+ .worker-item.failed .worker-type {
691
+ color: #ff8f8f;
692
+ }
693
+
694
+ .worker-type {
695
+ font-size: 1rem;
696
+ color: var(--text-green);
697
+ }
698
+
699
+ .worker-title-row {
700
+ display: flex;
701
+ justify-content: space-between;
702
+ align-items: baseline;
703
+ gap: 8px;
704
+ }
705
+
706
+ .worker-label {
707
+ font-size: 0.82rem;
708
+ color: var(--text-amber);
709
+ flex-shrink: 0;
710
+ }
711
+
712
+ .worker-meta {
713
+ font-size: 0.85rem;
714
+ color: var(--text-dim);
715
+ margin-top: 4px;
716
+ line-height: 1.2;
717
+ }
718
+
719
+ .worker-session {
720
+ font-size: 0.8rem;
721
+ color: var(--text-amber);
722
+ margin-top: 4px;
723
+ }
724
+
725
+ .worker-event {
726
+ font-size: 0.8rem;
727
+ color: rgba(245, 248, 255, 0.78);
728
+ margin-top: 4px;
729
+ line-height: 1.2;
730
+ }
731
+
732
+ .worker-badge {
733
+ display: inline-flex;
734
+ align-items: center;
735
+ gap: 6px;
736
+ margin-top: 6px;
737
+ padding: 2px 6px;
738
+ border-radius: 999px;
739
+ font-size: 0.78rem;
740
+ border: 1px solid rgba(255,255,255,0.12);
741
+ color: var(--text-dim);
742
+ background: rgba(255,255,255,0.04);
743
+ }
744
+
745
+ .worker-empty {
746
+ color: var(--text-dim);
747
+ font-size: 0.9rem;
748
+ padding: 6px 2px;
749
+ }
750
+
751
+ .runtime-feed {
752
+ margin-bottom: 14px;
753
+ padding: 10px;
754
+ border-radius: 8px;
755
+ border: 1px solid rgba(255,255,255,0.08);
756
+ background: rgba(255,255,255,0.03);
757
+ }
758
+
759
+ .runtime-feed-title {
760
+ font-size: 0.88rem;
761
+ color: var(--text-amber);
762
+ letter-spacing: 0.04em;
763
+ margin-bottom: 8px;
764
+ }
765
+
766
+ .runtime-feed-empty {
767
+ font-size: 0.82rem;
768
+ color: var(--text-dim);
769
+ line-height: 1.35;
770
+ }
771
+
772
+ .runtime-feed-list {
773
+ display: flex;
774
+ flex-direction: column;
775
+ gap: 6px;
776
+ }
777
+
778
+ .runtime-feed-item {
779
+ padding-left: 8px;
780
+ border-left: 2px solid rgba(255,255,255,0.08);
781
+ }
782
+
783
+ .runtime-feed-head {
784
+ display: flex;
785
+ justify-content: space-between;
786
+ gap: 8px;
787
+ font-size: 0.78rem;
788
+ color: var(--text-dim);
789
+ }
790
+
791
+ .runtime-feed-body {
792
+ font-size: 0.82rem;
793
+ color: rgba(245, 248, 255, 0.88);
794
+ line-height: 1.3;
795
+ margin-top: 2px;
796
+ }
797
+ .runtime-level {
798
+ display: inline-flex;
799
+ align-items: center;
800
+ padding: 1px 6px;
801
+ border-radius: 999px;
802
+ font-size: 0.68rem;
803
+ border: 1px solid rgba(255,255,255,0.12);
804
+ text-transform: uppercase;
805
+ letter-spacing: 0.05em;
806
+ }
807
+ .runtime-level.control {
808
+ color: var(--text-cyan);
809
+ border-color: rgba(107, 231, 255, 0.22);
810
+ }
811
+ .runtime-level.warning {
812
+ color: var(--text-amber);
813
+ border-color: rgba(255, 176, 0, 0.24);
814
+ }
815
+ .runtime-level.error {
816
+ color: #ff8c8c;
817
+ border-color: rgba(255, 108, 108, 0.22);
818
+ }
819
+
820
+ .runtime-summary {
821
+ margin-bottom: 10px;
822
+ padding: 10px;
823
+ border-radius: 8px;
824
+ border: 1px solid rgba(255,255,255,0.08);
825
+ background: rgba(255,255,255,0.03);
826
+ }
827
+
828
+ .runtime-summary-title {
829
+ font-size: 0.88rem;
830
+ color: var(--text-amber);
831
+ letter-spacing: 0.04em;
832
+ margin-bottom: 8px;
833
+ }
834
+
835
+ .runtime-summary-grid {
836
+ display: grid;
837
+ grid-template-columns: repeat(3, minmax(0, 1fr));
838
+ gap: 8px;
839
+ }
840
+
841
+ .runtime-summary-stat {
842
+ padding: 8px;
843
+ border-radius: 6px;
844
+ background: rgba(255,255,255,0.04);
845
+ border: 1px solid rgba(255,255,255,0.06);
846
+ }
847
+
848
+ .runtime-summary-label {
849
+ font-size: 0.72rem;
850
+ color: var(--text-dim);
851
+ letter-spacing: 0.05em;
852
+ }
853
+
854
+ .runtime-summary-value {
855
+ font-size: 1rem;
856
+ color: var(--text-primary);
857
+ margin-top: 4px;
858
+ }
859
+
860
+ .runtime-focus {
861
+ margin-bottom: 10px;
862
+ padding: 10px;
863
+ border-radius: 8px;
864
+ border: 1px solid rgba(255,255,255,0.08);
865
+ background: rgba(255,255,255,0.03);
866
+ }
867
+
868
+ .runtime-focus-title {
869
+ font-size: 0.88rem;
870
+ color: var(--text-amber);
871
+ letter-spacing: 0.04em;
872
+ margin-bottom: 8px;
873
+ }
874
+
875
+ .runtime-focus-empty {
876
+ font-size: 0.82rem;
877
+ color: var(--text-dim);
878
+ line-height: 1.35;
879
+ }
880
+
881
+ .runtime-focus-list {
882
+ display: flex;
883
+ flex-direction: column;
884
+ gap: 6px;
885
+ }
886
+
887
+ .runtime-focus-item {
888
+ padding: 8px;
889
+ border-radius: 6px;
890
+ background: rgba(255,255,255,0.04);
891
+ border: 1px solid rgba(255,255,255,0.06);
892
+ cursor: pointer;
893
+ }
894
+
895
+ .runtime-focus-item:hover {
896
+ border-color: rgba(255,255,255,0.16);
897
+ background: rgba(255,255,255,0.06);
898
+ }
899
+
900
+ .runtime-focus-head {
901
+ display: flex;
902
+ justify-content: space-between;
903
+ gap: 8px;
904
+ font-size: 0.78rem;
905
+ color: var(--text-dim);
906
+ }
907
+
908
+ .runtime-focus-body {
909
+ font-size: 0.82rem;
910
+ color: rgba(245, 248, 255, 0.88);
911
+ line-height: 1.3;
912
+ margin-top: 3px;
913
+ }
914
+
915
+ .worker-detail-panel {
916
+ display: none;
917
+ position: absolute;
918
+ top: 66px;
919
+ right: 10px;
920
+ width: min(820px, calc(100% - 20px));
921
+ max-height: min(68vh, 640px);
922
+ padding: 10px;
923
+ border-radius: 8px;
924
+ border: 1px solid rgba(255,255,255,0.10);
925
+ background: rgba(22, 36, 75, 0.96);
926
+ backdrop-filter: blur(6px);
927
+ box-shadow: 0 18px 48px rgba(0,0,0,0.35);
928
+ color: var(--text-primary);
929
+ overflow: auto;
930
+ z-index: 20;
931
+ }
932
+
933
+ .worker-detail-panel.active {
934
+ display: block;
935
+ }
936
+
937
+ .worker-detail-header {
938
+ display: flex;
939
+ align-items: center;
940
+ justify-content: space-between;
941
+ gap: 12px;
942
+ margin-bottom: 6px;
943
+ position: sticky;
944
+ top: 0;
945
+ padding-bottom: 6px;
946
+ background: rgba(22, 36, 75, 0.96);
947
+ z-index: 1;
948
+ }
949
+
950
+ .worker-detail-title {
951
+ color: var(--text-amber);
952
+ font-size: 0.95rem;
953
+ margin-bottom: 0;
954
+ }
955
+
956
+ .worker-detail-close {
957
+ border: 1px solid rgba(255,255,255,0.10);
958
+ background: rgba(255,255,255,0.05);
959
+ color: var(--text-dim);
960
+ border-radius: var(--btn-radius);
961
+ min-height: var(--btn-height-sm);
962
+ padding: 0 10px;
963
+ display: inline-flex;
964
+ align-items: center;
965
+ justify-content: center;
966
+ font-size: 0.74rem;
967
+ cursor: pointer;
968
+ line-height: 1;
969
+ }
970
+
971
+ .worker-detail-close:hover {
972
+ color: var(--text-primary);
973
+ border-color: rgba(255,255,255,0.22);
974
+ }
975
+
976
+ .worker-detail-meta {
977
+ color: var(--text-dim);
978
+ font-size: 0.82rem;
979
+ line-height: 1.4;
980
+ white-space: pre-wrap;
981
+ }
982
+
983
+ .worker-detail-events {
984
+ margin-top: 8px;
985
+ display: flex;
986
+ flex-direction: column;
987
+ gap: 4px;
988
+ max-height: 140px;
989
+ overflow: auto;
990
+ }
991
+
992
+ .worker-detail-event {
993
+ font-size: 0.8rem;
994
+ color: var(--text-dim);
995
+ border-left: 2px solid rgba(255,255,255,0.08);
996
+ padding-left: 8px;
997
+ }
998
+
999
+ .worker-detail-output {
1000
+ margin-top: 8px;
1001
+ padding: 8px 10px;
1002
+ border-radius: 6px;
1003
+ border: 1px solid rgba(255,255,255,0.08);
1004
+ background: rgba(10, 18, 38, 0.42);
1005
+ color: var(--text-primary);
1006
+ font-size: 0.8rem;
1007
+ line-height: 1.45;
1008
+ white-space: pre-wrap;
1009
+ max-height: 120px;
1010
+ overflow: auto;
1011
+ }
1012
+
1013
+ .worker-detail-snapshots {
1014
+ margin-top: 8px;
1015
+ display: flex;
1016
+ flex-direction: column;
1017
+ gap: 4px;
1018
+ max-height: 120px;
1019
+ overflow: auto;
1020
+ }
1021
+
1022
+ .worker-detail-snapshot {
1023
+ font-size: 0.8rem;
1024
+ color: rgba(245, 248, 255, 0.82);
1025
+ border-left: 2px solid rgba(92,242,154,0.25);
1026
+ padding-left: 8px;
1027
+ }
1028
+
1029
+ .workspace-panel {
1030
+ position: relative;
1031
+ flex: 1;
1032
+ display: flex;
1033
+ flex-direction: column;
1034
+ min-width: 0;
1035
+ }
1036
+
1037
+ .node-home-view,
1038
+ .worker-view {
1039
+ display: none;
1040
+ flex: 1;
1041
+ min-height: 0;
1042
+ }
1043
+
1044
+ .node-home-view.active,
1045
+ .worker-view.active {
1046
+ display: flex;
1047
+ }
1048
+
1049
+ .node-home-view {
1050
+ flex-direction: column;
1051
+ gap: 10px;
1052
+ min-height: 0;
1053
+ }
1054
+
1055
+ .node-home-header {
1056
+ display: flex;
1057
+ align-items: center;
1058
+ justify-content: space-between;
1059
+ gap: 12px;
1060
+ padding: 4px 2px 8px;
1061
+ }
1062
+
1063
+ .node-home-title {
1064
+ color: var(--text-green);
1065
+ font-size: 1.12rem;
1066
+ }
1067
+
1068
+ .node-home-subtitle {
1069
+ color: var(--text-dim);
1070
+ font-size: 0.82rem;
1071
+ line-height: 1.35;
1072
+ margin-top: 4px;
1073
+ }
1074
+
1075
+ .node-home-main {
1076
+ display: flex;
1077
+ flex-direction: column;
1078
+ min-height: 0;
1079
+ flex: 1;
1080
+ }
1081
+
1082
+ .node-coordination-state {
1083
+ display: none;
1084
+ align-items: center;
1085
+ gap: 10px;
1086
+ padding: 8px 12px;
1087
+ border-radius: 10px;
1088
+ border: 1px solid rgba(92, 242, 154, 0.18);
1089
+ background: rgba(92, 242, 154, 0.06);
1090
+ color: var(--text-primary);
1091
+ font-size: 0.86rem;
1092
+ line-height: 1.35;
1093
+ }
1094
+
1095
+ .node-coordination-state.active {
1096
+ display: flex;
1097
+ }
1098
+
1099
+ .node-coordination-dot {
1100
+ width: 9px;
1101
+ height: 9px;
1102
+ border-radius: 999px;
1103
+ background: var(--text-green);
1104
+ box-shadow: 0 0 0 4px rgba(92, 242, 154, 0.10);
1105
+ flex: 0 0 auto;
1106
+ }
1107
+
1108
+ .node-coordination-state.waiting .node-coordination-dot {
1109
+ background: var(--text-amber);
1110
+ box-shadow: 0 0 0 4px rgba(255, 176, 0, 0.10);
1111
+ }
1112
+
1113
+ .node-chat-card {
1114
+ display: flex;
1115
+ flex-direction: column;
1116
+ flex: 1;
1117
+ min-height: 0;
1118
+ border-radius: 8px;
1119
+ border: 1px solid rgba(255,255,255,0.08);
1120
+ background: rgba(255,255,255,0.025);
1121
+ overflow: hidden;
1122
+ }
1123
+
1124
+ .node-chat-thread {
1125
+ flex: 1;
1126
+ min-height: 0;
1127
+ overflow: auto;
1128
+ padding: 18px 18px 12px;
1129
+ display: flex;
1130
+ flex-direction: column;
1131
+ gap: 12px;
1132
+ align-items: stretch;
1133
+ }
1134
+
1135
+ .node-chat-empty {
1136
+ color: var(--text-dim);
1137
+ font-size: 0.92rem;
1138
+ line-height: 1.45;
1139
+ }
1140
+
1141
+ .node-turn {
1142
+ width: fit-content;
1143
+ max-width: min(72ch, 92%);
1144
+ padding: 12px 14px;
1145
+ border-radius: 14px;
1146
+ border: 1px solid rgba(255,255,255,0.08);
1147
+ line-height: 1.45;
1148
+ font-size: 0.92rem;
1149
+ white-space: pre-wrap;
1150
+ }
1151
+
1152
+ .node-turn.user {
1153
+ align-self: flex-end;
1154
+ background: rgba(92, 242, 154, 0.10);
1155
+ border-color: rgba(92, 242, 154, 0.20);
1156
+ color: var(--text-primary);
1157
+ }
1158
+
1159
+ .node-turn.assistant {
1160
+ align-self: flex-start;
1161
+ background: rgba(255,255,255,0.04);
1162
+ color: var(--text-primary);
1163
+ }
1164
+
1165
+ .node-turn-meta {
1166
+ font-size: 0.74rem;
1167
+ color: var(--text-dim);
1168
+ margin-top: 6px;
1169
+ }
1170
+
1171
+ .node-turn.pending {
1172
+ border-style: dashed;
1173
+ opacity: 0.92;
1174
+ }
1175
+
1176
+ .node-turn-status {
1177
+ margin-top: 10px;
1178
+ color: var(--text-amber);
1179
+ font-size: 0.84rem;
1180
+ }
1181
+
1182
+ .node-turn-action-row {
1183
+ margin-top: 10px;
1184
+ display: flex;
1185
+ justify-content: flex-start;
1186
+ }
1187
+
1188
+ .node-turn-action {
1189
+ background: rgba(255,255,255,0.08);
1190
+ color: var(--text-green);
1191
+ border: 1px solid rgba(92, 242, 154, 0.24);
1192
+ border-radius: 999px;
1193
+ font-family: 'VT323', monospace;
1194
+ font-size: 0.9rem;
1195
+ min-height: 30px;
1196
+ padding: 0 10px;
1197
+ display: inline-flex;
1198
+ align-items: center;
1199
+ justify-content: center;
1200
+ cursor: pointer;
1201
+ line-height: 1;
1202
+ }
1203
+
1204
+ .crt-container.disconnected .screen-layout {
1205
+ filter: blur(1px);
1206
+ opacity: 0.28;
1207
+ pointer-events: none;
1208
+ }
1209
+
1210
+ .node-compose {
1211
+ display: flex;
1212
+ flex-direction: column;
1213
+ gap: 8px;
1214
+ padding: 14px 16px 16px;
1215
+ border-top: 1px solid rgba(255,255,255,0.08);
1216
+ background: rgba(7, 11, 25, 0.16);
1217
+ }
1218
+
1219
+ .node-compose-input {
1220
+ width: 100%;
1221
+ min-height: 64px;
1222
+ max-height: 180px;
1223
+ resize: vertical;
1224
+ padding: 10px 12px;
1225
+ border: 1px solid rgba(255,255,255,0.12);
1226
+ border-radius: 8px;
1227
+ background: rgba(0,0,0,0.24);
1228
+ color: var(--text-primary);
1229
+ font-family: 'JetBrains Mono', monospace;
1230
+ font-size: 0.9rem;
1231
+ outline: none;
1232
+ }
1233
+
1234
+ .node-compose-input:focus {
1235
+ border-color: rgba(92, 242, 154, 0.35);
1236
+ }
1237
+
1238
+ .node-compose-input:disabled {
1239
+ opacity: 0.6;
1240
+ cursor: not-allowed;
1241
+ }
1242
+
1243
+ .node-compose-row {
1244
+ display: flex;
1245
+ align-items: center;
1246
+ justify-content: space-between;
1247
+ gap: 10px;
1248
+ }
1249
+
1250
+ .node-compose-hint {
1251
+ color: var(--text-dim);
1252
+ font-size: 0.78rem;
1253
+ line-height: 1.35;
1254
+ }
1255
+
1256
+ .node-compose-send {
1257
+ background: rgba(92, 242, 154, 0.12);
1258
+ color: var(--text-green);
1259
+ border: 1px solid rgba(92, 242, 154, 0.24);
1260
+ border-radius: var(--btn-radius);
1261
+ font-family: 'VT323', monospace;
1262
+ font-size: 1rem;
1263
+ min-height: var(--btn-height-md);
1264
+ padding: 0 16px;
1265
+ display: inline-flex;
1266
+ align-items: center;
1267
+ justify-content: center;
1268
+ cursor: pointer;
1269
+ flex-shrink: 0;
1270
+ line-height: 1;
1271
+ }
1272
+
1273
+ .node-compose-send:disabled {
1274
+ opacity: 0.45;
1275
+ cursor: not-allowed;
1276
+ }
1277
+
1278
+ .node-home-actions {
1279
+ display: flex;
1280
+ align-items: center;
1281
+ gap: 8px;
1282
+ }
1283
+
1284
+ .node-home-action {
1285
+ background: rgba(255,255,255,0.08);
1286
+ color: var(--text-primary);
1287
+ border: 1px solid rgba(255,255,255,0.12);
1288
+ border-radius: var(--btn-radius);
1289
+ font-family: 'VT323', monospace;
1290
+ font-size: 0.95rem;
1291
+ min-height: var(--btn-height-md);
1292
+ padding: 0 var(--btn-padding-x-md);
1293
+ display: inline-flex;
1294
+ align-items: center;
1295
+ justify-content: center;
1296
+ cursor: pointer;
1297
+ touch-action: manipulation;
1298
+ line-height: 1;
1299
+ }
1300
+
1301
+ .node-home-action:hover {
1302
+ border-color: rgba(255,255,255,0.2);
1303
+ }
1304
+
1305
+ .node-home-badge {
1306
+ display: inline-flex;
1307
+ align-items: center;
1308
+ gap: 6px;
1309
+ padding: 3px 8px;
1310
+ border-radius: 999px;
1311
+ font-size: 0.8rem;
1312
+ border: 1px solid rgba(255,255,255,0.12);
1313
+ color: var(--text-amber);
1314
+ background: rgba(255,255,255,0.04);
1315
+ }
1316
+
1317
+ .node-detail-panel {
1318
+ display: none;
1319
+ position: absolute;
1320
+ top: 10px;
1321
+ right: 10px;
1322
+ width: min(420px, calc(100% - 20px));
1323
+ max-height: calc(100% - 20px);
1324
+ padding: 10px;
1325
+ border-radius: 8px;
1326
+ border: 1px solid rgba(255,255,255,0.10);
1327
+ background: rgba(22, 36, 75, 0.97);
1328
+ backdrop-filter: blur(6px);
1329
+ box-shadow: 0 18px 48px rgba(0,0,0,0.35);
1330
+ color: var(--text-primary);
1331
+ overflow: auto;
1332
+ z-index: 20;
1333
+ }
1334
+
1335
+ .node-detail-panel.active {
1336
+ display: block;
1337
+ }
1338
+
1339
+ .node-detail-header {
1340
+ display: flex;
1341
+ align-items: center;
1342
+ justify-content: space-between;
1343
+ gap: 12px;
1344
+ margin-bottom: 6px;
1345
+ position: sticky;
1346
+ top: 0;
1347
+ padding-bottom: 6px;
1348
+ background: rgba(22, 36, 75, 0.97);
1349
+ z-index: 1;
1350
+ }
1351
+
1352
+ .node-detail-title {
1353
+ color: var(--text-amber);
1354
+ font-size: 0.95rem;
1355
+ }
1356
+
1357
+ .node-detail-close {
1358
+ border: 1px solid rgba(255,255,255,0.10);
1359
+ background: rgba(255,255,255,0.05);
1360
+ color: var(--text-dim);
1361
+ border-radius: var(--btn-radius);
1362
+ min-height: var(--btn-height-sm);
1363
+ padding: 0 10px;
1364
+ display: inline-flex;
1365
+ align-items: center;
1366
+ justify-content: center;
1367
+ font-size: 0.74rem;
1368
+ cursor: pointer;
1369
+ line-height: 1;
1370
+ }
1371
+
1372
+ .node-detail-close:hover {
1373
+ color: var(--text-primary);
1374
+ border-color: rgba(255,255,255,0.22);
1375
+ }
1376
+
1377
+ .worker-view {
1378
+ position: relative;
1379
+ flex-direction: column;
1380
+ min-height: 0;
1381
+ }
1382
+
1383
+ .worker-view .terminal-content {
1384
+ display: block;
1385
+ flex: 1;
1386
+ min-height: 0;
1387
+ }
1388
+
1389
+ /* CRT Glow Effect */
1390
+ .crt-glow {
1391
+ position: absolute;
1392
+ top: 0; left: 0; right: 0; bottom: 0;
1393
+ background: radial-gradient(circle at center, rgba(150,220,255,0.10) 0%, rgba(17,33,74,0.18) 72%, rgba(0,0,0,0.24) 100%);
1394
+ pointer-events: none;
1395
+ z-index: 10;
1396
+ }
1397
+
1398
+ /* Screen Curvature */
1399
+ .crt-curvature {
1400
+ position: absolute;
1401
+ top: 0; left: 0; right: 0; bottom: 0;
1402
+ background: radial-gradient(ellipse at center, transparent 60%, var(--screen-curvature) 100%);
1403
+ pointer-events: none;
1404
+ z-index: 11;
1405
+ border-radius: 8px;
1406
+ }
1407
+
1408
+ /* Scanlines */
1409
+ .scanlines {
1410
+ position: absolute;
1411
+ top: 0; left: 0; right: 0; bottom: 0;
1412
+ background: linear-gradient(
1413
+ to bottom,
1414
+ rgba(255,255,255,0),
1415
+ rgba(255,255,255,0) 50%,
1416
+ rgba(0,0,0,0.08) 50%,
1417
+ rgba(0,0,0,0.08)
1418
+ );
1419
+ background-size: 100% 4px;
1420
+ pointer-events: none;
1421
+ z-index: 12;
1422
+ opacity: 0.28;
1423
+ }
1424
+
1425
+ /* Connection Overlay */
1426
+ .connection-overlay {
1427
+ position: absolute;
1428
+ top: 0; left: 0; right: 0; bottom: 0;
1429
+ display: flex;
1430
+ flex-direction: column;
1431
+ justify-content: center;
1432
+ align-items: center;
1433
+ z-index: 20;
1434
+ padding: 40px;
1435
+ background: linear-gradient(180deg, rgba(8, 14, 30, 0.96), rgba(10, 18, 38, 0.98));
1436
+ backdrop-filter: blur(10px);
1437
+ }
1438
+
1439
+ .connection-overlay.hidden {
1440
+ display: none;
1441
+ }
1442
+
1443
+ .connection-form {
1444
+ background: rgba(0,0,0,0.3);
1445
+ border: 2px solid var(--text-dim);
1446
+ border-radius: 8px;
1447
+ padding: 30px;
1448
+ max-width: 400px;
1449
+ width: 100%;
1450
+ }
1451
+
1452
+ .form-title {
1453
+ color: var(--text-primary);
1454
+ font-size: 1.6rem;
1455
+ text-align: center;
1456
+ margin-bottom: 20px;
1457
+ text-shadow: 0 0 8px rgba(200,200,255,0.3);
1458
+ }
1459
+
1460
+ .form-group {
1461
+ margin-bottom: 16px;
1462
+ }
1463
+
1464
+ .form-label {
1465
+ display: block;
1466
+ color: var(--text-dim);
1467
+ font-size: 0.9rem;
1468
+ margin-bottom: 4px;
1469
+ }
1470
+
1471
+ .form-input {
1472
+ width: 100%;
1473
+ background: rgba(0,0,0,0.4);
1474
+ border: 1px solid var(--text-dim);
1475
+ border-radius: 4px;
1476
+ padding: 8px 12px;
1477
+ color: var(--text-primary);
1478
+ font-family: 'VT323', monospace;
1479
+ font-size: 1.1rem;
1480
+ }
1481
+
1482
+ .form-input:focus {
1483
+ outline: none;
1484
+ border-color: var(--text-green);
1485
+ box-shadow: 0 0 8px rgba(74, 222, 128, 0.3);
1486
+ }
1487
+
1488
+ .connect-btn {
1489
+ width: 100%;
1490
+ background: var(--chassis-main);
1491
+ border: 2px solid var(--chassis-deep);
1492
+ border-bottom-width: 4px;
1493
+ border-radius: var(--btn-radius);
1494
+ min-height: 48px;
1495
+ padding: 0 14px;
1496
+ color: var(--key-text);
1497
+ font-family: 'VT323', monospace;
1498
+ font-size: 1.2rem;
1499
+ cursor: pointer;
1500
+ margin-top: 10px;
1501
+ line-height: 1;
1502
+ }
1503
+
1504
+ .connect-btn:active {
1505
+ border-bottom-width: 2px;
1506
+ transform: translateY(2px);
1507
+ }
1508
+
1509
+ .connect-btn:disabled {
1510
+ opacity: 0.5;
1511
+ cursor: not-allowed;
1512
+ }
1513
+
1514
+ /* Terminal Content Area */
1515
+ .terminal-content {
1516
+ width: 100%;
1517
+ height: 100%;
1518
+ position: relative;
1519
+ z-index: 5;
1520
+ display: none;
1521
+ }
1522
+
1523
+ .terminal-content.active {
1524
+ display: block;
1525
+ }
1526
+
1527
+ .terminal-pane {
1528
+ position: absolute;
1529
+ inset: 0;
1530
+ display: none;
1531
+ }
1532
+
1533
+ .terminal-pane.active {
1534
+ display: block;
1535
+ }
1536
+
1537
+ /* xterm.js Customization */
1538
+ .xterm {
1539
+ height: 100% !important;
1540
+ font-variant-ligatures: none;
1541
+ }
1542
+
1543
+ .xterm .xterm-rows,
1544
+ .xterm .xterm-helpers,
1545
+ .xterm .xterm-screen {
1546
+ font-family: 'JetBrains Mono', monospace !important;
1547
+ font-weight: 500;
1548
+ -webkit-font-smoothing: antialiased;
1549
+ -moz-osx-font-smoothing: grayscale;
1550
+ text-rendering: geometricPrecision;
1551
+ }
1552
+
1553
+ /* Control Deck */
1554
+ .control-deck {
1555
+ margin-top: 16px;
1556
+ background-color: rgba(0,0,0,0.1);
1557
+ border-radius: 8px;
1558
+ padding: 12px;
1559
+ border: 2px solid rgba(255,255,255,0.1);
1560
+ border-bottom-color: rgba(0,0,0,0.2);
1561
+ border-right-color: rgba(0,0,0,0.2);
1562
+ z-index: 2;
1563
+ }
1564
+
1565
+ .key-grid {
1566
+ display: grid;
1567
+ grid-template-columns: repeat(6, 1fr);
1568
+ gap: 10px;
1569
+ max-width: 600px;
1570
+ margin: 0 auto;
1571
+ }
1572
+
1573
+ /* Retro 3D Keys */
1574
+ .key {
1575
+ position: relative;
1576
+ background-color: var(--key-face);
1577
+ height: 44px;
1578
+ border-radius: 6px;
1579
+ border-top: 2px solid var(--bevel-light);
1580
+ border-left: 2px solid rgba(255,255,255,0.2);
1581
+ border-right: 3px solid rgba(0,0,0,0.3);
1582
+ border-bottom: 5px solid var(--bevel-dark);
1583
+ color: var(--key-text);
1584
+ font-size: 1rem;
1585
+ font-weight: bold;
1586
+ display: flex;
1587
+ align-items: center;
1588
+ justify-content: center;
1589
+ text-transform: uppercase;
1590
+ cursor: pointer;
1591
+ transition: all 0.05s ease-out;
1592
+ box-shadow: 0 4px 6px rgba(0,0,0,0.3);
1593
+ font-family: 'VT323', monospace;
1594
+ }
1595
+
1596
+ .key::after {
1597
+ content: '';
1598
+ position: absolute;
1599
+ top: 4px; left: 4px; right: 4px; bottom: 4px;
1600
+ background: linear-gradient(135deg, rgba(0,0,0,0.05), rgba(255,255,255,0.1));
1601
+ border-radius: 3px;
1602
+ pointer-events: none;
1603
+ }
1604
+
1605
+ .key:active {
1606
+ transform: translateY(3px);
1607
+ border-bottom-width: 2px;
1608
+ box-shadow: 0 1px 2px rgba(0,0,0,0.2);
1609
+ background-color: #8A8A6E;
1610
+ }
1611
+
1612
+ .key.nav {
1613
+ background-color: var(--key-nav);
1614
+ color: var(--key-nav-text);
1615
+ border-top-color: #555;
1616
+ border-left-color: #444;
1617
+ border-right-color: #111;
1618
+ border-bottom-color: #000;
1619
+ }
1620
+
1621
+ .key.danger {
1622
+ background-color: #9e4a4a;
1623
+ color: #ffe0e0;
1624
+ border-bottom-color: rgba(0,0,0,0.6);
1625
+ }
1626
+
1627
+ .key.danger:active {
1628
+ background-color: #8a4040;
1629
+ }
1630
+
1631
+ .key.accent {
1632
+ background-color: #846b28;
1633
+ color: #fff4cf;
1634
+ border-bottom-color: rgba(0,0,0,0.55);
1635
+ }
1636
+
1637
+ .key.accent:active {
1638
+ background-color: #755d21;
1639
+ }
1640
+
1641
+ .key.wide {
1642
+ min-width: 0;
1643
+ }
1644
+
1645
+ .mobile-control-cluster {
1646
+ display: none;
1647
+ flex-direction: column;
1648
+ gap: 6px;
1649
+ max-width: 420px;
1650
+ margin: 0 auto;
1651
+ }
1652
+
1653
+ .mobile-key-row {
1654
+ display: grid;
1655
+ gap: 6px;
1656
+ grid-template-columns: repeat(5, minmax(0, 1fr));
1657
+ }
1658
+
1659
+ .mobile-key-row.top {
1660
+ align-items: stretch;
1661
+ }
1662
+
1663
+ .mobile-key-split {
1664
+ display: grid;
1665
+ grid-template-columns: repeat(2, minmax(0, 1fr));
1666
+ gap: 4px;
1667
+ height: 100%;
1668
+ }
1669
+
1670
+ .mobile-key-split .key {
1671
+ height: 100%;
1672
+ font-size: 0.8rem;
1673
+ }
1674
+
1675
+ /* Status Bar at Bottom */
1676
+ .status-bar {
1677
+ display: flex;
1678
+ justify-content: space-between;
1679
+ align-items: center;
1680
+ margin-top: 12px;
1681
+ padding-top: 12px;
1682
+ border-top: 1px solid var(--chassis-dark);
1683
+ font-size: 0.9rem;
1684
+ color: var(--chassis-deep);
1685
+ z-index: 2;
1686
+ }
1687
+
1688
+ .status-left, .status-right {
1689
+ display: flex;
1690
+ gap: 16px;
1691
+ }
1692
+
1693
+ .status-item {
1694
+ display: flex;
1695
+ align-items: center;
1696
+ gap: 6px;
1697
+ }
1698
+
1699
+ .status-value {
1700
+ font-family: 'VT323', monospace;
1701
+ background: rgba(0,0,0,0.15);
1702
+ padding: 2px 8px;
1703
+ border-radius: 3px;
1704
+ border: 1px inset rgba(0,0,0,0.2);
1705
+ color: var(--chassis-deep);
1706
+ }
1707
+
1708
+ /* Responsive */
1709
+ @media (max-width: 600px) {
1710
+ .key-grid {
1711
+ grid-template-columns: repeat(4, 1fr);
1712
+ gap: 8px;
1713
+ }
1714
+ .key {
1715
+ height: 40px;
1716
+ font-size: 0.9rem;
1717
+ }
1718
+ .brand-badge {
1719
+ font-size: 1.1rem;
1720
+ }
1721
+ .connection-form {
1722
+ padding: 20px;
1723
+ }
1724
+ }
1725
+
1726
+ @media (max-width: 820px) {
1727
+ body, html {
1728
+ overflow: hidden;
1729
+ }
1730
+
1731
+ .device-casing {
1732
+ padding: max(8px, env(safe-area-inset-top)) 8px max(8px, env(safe-area-inset-bottom)) 8px;
1733
+ }
1734
+
1735
+ .bezel-top {
1736
+ padding-bottom: 8px;
1737
+ margin-bottom: 8px;
1738
+ gap: 8px;
1739
+ flex-wrap: wrap;
1740
+ }
1741
+
1742
+ .brand-badge {
1743
+ font-size: 1.08rem;
1744
+ padding: 2px 8px;
1745
+ letter-spacing: 1px;
1746
+ }
1747
+
1748
+ .power-indicator {
1749
+ font-size: 0.84rem;
1750
+ gap: 6px;
1751
+ }
1752
+
1753
+ .crt-container {
1754
+ padding: 10px;
1755
+ border-width: 4px;
1756
+ }
1757
+
1758
+ .screen-layout {
1759
+ gap: 0;
1760
+ }
1761
+
1762
+ .mobile-sidebar-backdrop {
1763
+ display: block;
1764
+ position: absolute;
1765
+ inset: 0;
1766
+ background: rgba(6, 10, 24, 0.16);
1767
+ z-index: 12;
1768
+ opacity: 0;
1769
+ pointer-events: none;
1770
+ transition: opacity 0.2s ease;
1771
+ }
1772
+
1773
+ .mobile-sidebar-backdrop.active {
1774
+ opacity: 1;
1775
+ pointer-events: none;
1776
+ }
1777
+
1778
+ .worker-sidebar {
1779
+ position: absolute;
1780
+ top: 0;
1781
+ left: 0;
1782
+ bottom: 0;
1783
+ width: calc(100% - 8px);
1784
+ max-width: calc(100% - 8px);
1785
+ display: flex;
1786
+ flex: none;
1787
+ z-index: 25;
1788
+ transform: translateX(calc(-100% - 14px));
1789
+ opacity: 0;
1790
+ pointer-events: none;
1791
+ transition: transform 0.2s ease, opacity 0.2s ease;
1792
+ box-shadow: 0 18px 42px rgba(0,0,0,0.42);
1793
+ background: linear-gradient(180deg, rgba(11, 24, 63, 0.98), rgba(9, 20, 51, 0.98));
1794
+ }
1795
+
1796
+ .worker-sidebar.mobile-open {
1797
+ transform: translateX(0);
1798
+ opacity: 1;
1799
+ pointer-events: auto;
1800
+ }
1801
+
1802
+ .workspace-panel {
1803
+ width: 100%;
1804
+ }
1805
+
1806
+ .workspace-panel.mobile-muted {
1807
+ opacity: 0.08;
1808
+ pointer-events: none;
1809
+ filter: saturate(0.85);
1810
+ }
1811
+
1812
+ .node-home-header {
1813
+ flex-direction: row;
1814
+ align-items: center;
1815
+ justify-content: space-between;
1816
+ padding: 2px 0 8px;
1817
+ }
1818
+
1819
+ .worker-sidebar {
1820
+ padding: 8px;
1821
+ gap: 8px;
1822
+ }
1823
+
1824
+ .worker-header {
1825
+ font-size: 0.98rem;
1826
+ }
1827
+
1828
+ .worker-spawn-form {
1829
+ gap: 6px;
1830
+ padding: 7px;
1831
+ }
1832
+
1833
+ .worker-spawn-topbar {
1834
+ gap: 5px;
1835
+ height: 28px;
1836
+ }
1837
+
1838
+ .worker-select-wrap::after {
1839
+ right: 9px;
1840
+ font-size: 0.68rem;
1841
+ }
1842
+
1843
+ .worker-spawn-field {
1844
+ gap: 5px;
1845
+ }
1846
+
1847
+ .worker-shortcuts-wrap {
1848
+ gap: 0;
1849
+ }
1850
+
1851
+ .worker-spawn-label {
1852
+ font-size: 0.72rem;
1853
+ margin-bottom: 2px;
1854
+ }
1855
+
1856
+ .worker-spawn-row {
1857
+ gap: 6px;
1858
+ }
1859
+
1860
+ .worker-spawn-input {
1861
+ padding: 0 7px;
1862
+ font-size: 0.92rem;
1863
+ min-height: 28px;
1864
+ height: 28px;
1865
+ }
1866
+
1867
+ .worker-shortcuts {
1868
+ gap: 4px;
1869
+ max-height: calc(22px * 2 + 4px);
1870
+ }
1871
+
1872
+ .worker-shortcut {
1873
+ min-height: 22px;
1874
+ padding: 0 6px;
1875
+ font-size: 0.68rem;
1876
+ }
1877
+
1878
+ .worker-browse-btn {
1879
+ min-height: 22px;
1880
+ height: 22px;
1881
+ font-size: 0.76rem;
1882
+ }
1883
+
1884
+ .worker-add-btn {
1885
+ flex-basis: 40px;
1886
+ width: 40px;
1887
+ min-height: 28px;
1888
+ height: 28px;
1889
+ }
1890
+
1891
+ .worker-dir-browser {
1892
+ padding: 6px;
1893
+ gap: 5px;
1894
+ }
1895
+
1896
+ .worker-dir-item {
1897
+ min-height: 22px;
1898
+ height: 22px;
1899
+ }
1900
+
1901
+ .worker-dir-list {
1902
+ max-height: 132px;
1903
+ }
1904
+
1905
+ .session-nav {
1906
+ gap: 6px;
1907
+ }
1908
+
1909
+ .node-nav-card,
1910
+ .worker-item {
1911
+ padding: 6px 7px;
1912
+ }
1913
+
1914
+ .node-nav-title {
1915
+ font-size: 0.92rem;
1916
+ }
1917
+
1918
+ .node-nav-meta {
1919
+ display: none;
1920
+ }
1921
+
1922
+ .node-nav-badge {
1923
+ margin-top: 4px;
1924
+ padding: 1px 5px;
1925
+ font-size: 0.72rem;
1926
+ }
1927
+
1928
+ .worker-meta,
1929
+ .worker-event,
1930
+ .worker-session,
1931
+ .worker-badge {
1932
+ font-size: 0.74rem;
1933
+ line-height: 1.12;
1934
+ }
1935
+
1936
+ .worker-title-row {
1937
+ margin-bottom: 2px;
1938
+ }
1939
+
1940
+ .node-home-subtitle,
1941
+ .node-compose-hint {
1942
+ display: none;
1943
+ }
1944
+
1945
+ .node-chat-thread {
1946
+ padding: 12px 12px 10px;
1947
+ gap: 10px;
1948
+ align-items: stretch;
1949
+ }
1950
+
1951
+ .node-turn {
1952
+ width: 100%;
1953
+ max-width: 100%;
1954
+ padding: 10px 12px;
1955
+ font-size: 0.88rem;
1956
+ }
1957
+
1958
+ .node-compose {
1959
+ padding: 10px 10px 12px;
1960
+ gap: 10px;
1961
+ }
1962
+
1963
+ .node-compose-input {
1964
+ min-height: 92px;
1965
+ max-height: 200px;
1966
+ font-size: 0.94rem;
1967
+ }
1968
+
1969
+ .node-compose-row {
1970
+ flex-direction: column;
1971
+ align-items: stretch;
1972
+ }
1973
+
1974
+ .node-compose-send {
1975
+ width: 100%;
1976
+ min-height: 42px;
1977
+ font-size: 1.06rem;
1978
+ }
1979
+
1980
+ .current-worker-bar {
1981
+ flex-direction: column;
1982
+ align-items: stretch;
1983
+ padding: 7px 8px;
1984
+ gap: 8px;
1985
+ }
1986
+
1987
+ .current-worker-title {
1988
+ font-size: 0.92rem;
1989
+ white-space: nowrap;
1990
+ overflow: hidden;
1991
+ text-overflow: ellipsis;
1992
+ }
1993
+
1994
+ .current-worker-subtitle {
1995
+ font-size: 0.76rem;
1996
+ line-height: 1.15;
1997
+ white-space: nowrap;
1998
+ overflow: hidden;
1999
+ text-overflow: ellipsis;
2000
+ }
2001
+
2002
+ .current-worker-actions {
2003
+ display: grid;
2004
+ grid-template-columns: repeat(5, minmax(0, 1fr));
2005
+ width: 100%;
2006
+ }
2007
+
2008
+ .mobile-sessions-toggle {
2009
+ display: inline-flex;
2010
+ align-items: center;
2011
+ justify-content: center;
2012
+ min-height: 40px;
2013
+ }
2014
+
2015
+ .worker-close-btn {
2016
+ display: inline-flex;
2017
+ align-items: center;
2018
+ justify-content: center;
2019
+ min-height: 40px;
2020
+ }
2021
+
2022
+ .worker-mobile-action {
2023
+ display: inline-flex;
2024
+ align-items: center;
2025
+ justify-content: center;
2026
+ min-height: 40px;
2027
+ }
2028
+
2029
+ .current-worker-action,
2030
+ .current-worker-stop {
2031
+ width: 100%;
2032
+ min-height: 40px;
2033
+ display: flex;
2034
+ align-items: center;
2035
+ justify-content: center;
2036
+ }
2037
+
2038
+ .worker-browse-btn,
2039
+ .node-home-action {
2040
+ min-height: 40px;
2041
+ }
2042
+
2043
+ .worker-detail-panel,
2044
+ .node-detail-panel {
2045
+ top: 0;
2046
+ right: 0;
2047
+ left: 0;
2048
+ width: 100%;
2049
+ max-height: 100%;
2050
+ border-radius: 10px;
2051
+ }
2052
+
2053
+ .control-deck {
2054
+ margin-top: 8px;
2055
+ padding: 8px;
2056
+ }
2057
+
2058
+ .key-grid.desktop-key-grid {
2059
+ display: none;
2060
+ }
2061
+
2062
+ .mobile-control-cluster {
2063
+ display: flex;
2064
+ }
2065
+
2066
+ .key-grid {
2067
+ grid-template-columns: repeat(6, minmax(0, 1fr));
2068
+ gap: 6px;
2069
+ }
2070
+
2071
+ .key {
2072
+ height: 36px;
2073
+ font-size: 0.82rem;
2074
+ }
2075
+
2076
+ .status-bar {
2077
+ margin-top: 8px;
2078
+ padding-top: 8px;
2079
+ flex-wrap: wrap;
2080
+ gap: 8px;
2081
+ font-size: 0.8rem;
2082
+ }
2083
+
2084
+ .status-left,
2085
+ .status-right {
2086
+ gap: 8px;
2087
+ flex-wrap: wrap;
2088
+ }
2089
+ }
2090
+
2091
+ /* xterm scrollbar styling */
2092
+ .xterm-viewport::-webkit-scrollbar {
2093
+ width: 8px;
2094
+ }
2095
+
2096
+ .xterm-viewport::-webkit-scrollbar-track {
2097
+ background: rgba(0,0,0,0.2);
2098
+ }
2099
+
2100
+ .xterm-viewport::-webkit-scrollbar-thumb {
2101
+ background: var(--chassis-dark);
2102
+ border-radius: 4px;
2103
+ }
2104
+ </style>
2105
+ </head>
2106
+ <body>
2107
+ <!-- Main Device Casing -->
2108
+ <div class="device-casing">
2109
+ <!-- Top Bezel -->
2110
+ <div class="bezel-top">
2111
+ <div class="brand-badge">◈ MAGIC-SHELL ◈</div>
2112
+ <div class="power-indicator">
2113
+ <span id="status-text">STANDBY</span>
2114
+ <div id="status-led" class="led"></div>
2115
+ </div>
2116
+ </div>
2117
+
2118
+ <!-- CRT Screen Container -->
2119
+ <div class="crt-container">
2120
+ <!-- CRT Effects -->
2121
+ <div class="scanlines"></div>
2122
+ <div class="crt-glow"></div>
2123
+ <div class="crt-curvature"></div>
2124
+ <div id="mobile-sidebar-backdrop" class="mobile-sidebar-backdrop" onclick="closeWorkerSidebar()"></div>
2125
+
2126
+ <!-- Connection Overlay -->
2127
+ <div id="connection-overlay" class="connection-overlay">
2128
+ <div class="connection-form">
2129
+ <div class="form-title">[ CONNECTION ]</div>
2130
+
2131
+ <div class="form-group">
2132
+ <label class="form-label">RELAY URL</label>
2133
+ <input type="text" id="relayUrl" class="form-input" value="" placeholder="wss://magicshell.ai/ws">
2134
+ </div>
2135
+
2136
+ <div class="form-group">
2137
+ <label class="form-label">NODE ID</label>
2138
+ <input type="text" id="nodeId" class="form-input" placeholder="Enter node ID">
2139
+ </div>
2140
+
2141
+ <div class="form-group">
2142
+ <label class="form-label">PASSWORD</label>
2143
+ <input type="password" id="password" class="form-input" placeholder="Enter password">
2144
+ </div>
2145
+
2146
+ <button id="connectBtn" class="connect-btn" onclick="connect()">ESTABLISH LINK</button>
2147
+ </div>
2148
+ </div>
2149
+
2150
+ <div class="screen-layout">
2151
+ <div id="worker-sidebar" class="worker-sidebar">
2152
+ <div class="worker-header">
2153
+ <span id="worker-header-label">SESSIONS</span>
2154
+ <div style="display:flex; gap:8px; align-items:center;">
2155
+ <button class="worker-close-btn" onclick="closeWorkerSidebar()">CLOSE</button>
2156
+ </div>
2157
+ </div>
2158
+ <div class="worker-spawn-form">
2159
+ <div class="worker-spawn-topbar">
2160
+ <div class="worker-select-wrap">
2161
+ <select id="spawnPlugin" class="worker-spawn-input">
2162
+ <option value="pie">PIE</option>
2163
+ <option value="claude-code">CLAUDE</option>
2164
+ <option value="codex">CODEX</option>
2165
+ </select>
2166
+ </div>
2167
+ <button class="worker-add-btn" onclick="spawnPieWorker()">+ PIE</button>
2168
+ </div>
2169
+ <div class="worker-spawn-field">
2170
+ <div class="worker-spawn-row">
2171
+ <input id="spawnCwd" class="worker-spawn-input" type="text" placeholder="/path/to/project">
2172
+ </div>
2173
+ <div class="worker-spawn-actions">
2174
+ <button id="browseCwdBtn" class="worker-browse-btn" onclick="toggleDirBrowser()">BROWSE</button>
2175
+ <button id="useCwdBtn" class="worker-browse-btn" onclick="useBrowsedDir()" disabled>SELECT</button>
2176
+ </div>
2177
+ </div>
2178
+ <div class="worker-shortcuts-wrap">
2179
+ <div id="cwd-shortcuts" class="worker-shortcuts"></div>
2180
+ </div>
2181
+ <div id="dir-browser" class="worker-dir-browser">
2182
+ <div id="dir-browser-path" class="worker-dir-path">Loading...</div>
2183
+ <div id="dir-browser-list" class="worker-dir-list"></div>
2184
+ </div>
2185
+ </div>
2186
+ <div class="session-nav">
2187
+ <div id="node-nav-card" class="node-nav-card" onclick="selectNodeHome()">
2188
+ <div id="node-nav-title" class="node-nav-title">Agent</div>
2189
+ <div id="node-nav-meta" class="node-nav-meta">Chat with your node and supervise workers.</div>
2190
+ <div id="node-nav-badge" class="node-nav-badge">idle</div>
2191
+ </div>
2192
+ <div id="worker-list" class="worker-list">
2193
+ <div class="worker-empty">No workers yet.</div>
2194
+ </div>
2195
+ </div>
2196
+ </div>
2197
+
2198
+ <div class="workspace-panel">
2199
+ <div id="node-home-view" class="node-home-view active">
2200
+ <div class="node-home-header">
2201
+ <div>
2202
+ <div id="node-home-title" class="node-home-title">Agent</div>
2203
+ <div class="node-home-subtitle">Chat with the primary agent here. Open a worker only when you want to inspect or take over.</div>
2204
+ </div>
2205
+ <div class="node-home-actions">
2206
+ <div id="node-home-badge" class="node-home-badge">idle</div>
2207
+ <button id="node-detail-toggle" class="node-home-action" onclick="toggleNodeDetail()">OVERVIEW</button>
2208
+ <button id="mobile-sessions-toggle" class="node-home-action mobile-sessions-toggle" onclick="toggleWorkerSidebar()">SESSIONS</button>
2209
+ </div>
2210
+ </div>
2211
+ <div class="node-home-main">
2212
+ <div id="node-coordination-state" class="node-coordination-state">
2213
+ <div class="node-coordination-dot"></div>
2214
+ <div id="node-coordination-text">Primary coordination idle.</div>
2215
+ </div>
2216
+ <div class="node-chat-card">
2217
+ <div id="node-chat-thread" class="node-chat-thread">
2218
+ <div class="node-chat-empty">Primary agent is ready.</div>
2219
+ </div>
2220
+ <div class="node-compose">
2221
+ <textarea id="node-compose-input" class="node-compose-input" placeholder="Ask the primary agent to coordinate work."></textarea>
2222
+ <div class="node-compose-row">
2223
+ <div class="node-compose-hint">Chat with the primary agent here. Attach a worker only when you want to inspect or directly steer execution.</div>
2224
+ <button id="node-compose-send" class="node-compose-send" onclick="sendNodeMessage()">SEND</button>
2225
+ </div>
2226
+ </div>
2227
+ </div>
2228
+ </div>
2229
+ <div id="node-detail-panel" class="node-detail-panel">
2230
+ <div class="node-detail-header">
2231
+ <div class="node-detail-title">Primary Overview</div>
2232
+ <button class="node-detail-close" onclick="closeNodeDetail()">CLOSE</button>
2233
+ </div>
2234
+ <div class="runtime-summary">
2235
+ <div class="runtime-summary-title">PRIMARY OVERVIEW</div>
2236
+ <div id="runtime-summary-grid" class="runtime-summary-grid"></div>
2237
+ </div>
2238
+ <div class="runtime-focus">
2239
+ <div class="runtime-focus-title">NEXT ACTIONS</div>
2240
+ <div id="runtime-focus-list" class="runtime-focus-empty">No immediate actions.</div>
2241
+ </div>
2242
+ <div class="runtime-feed">
2243
+ <div class="runtime-feed-title">RUNTIME ACTIVITY</div>
2244
+ <div id="runtime-feed-list" class="runtime-feed-empty">No worker activity yet.</div>
2245
+ </div>
2246
+ </div>
2247
+ </div>
2248
+
2249
+ <div id="worker-view" class="worker-view">
2250
+ <div id="current-worker-bar" class="current-worker-bar">
2251
+ <div class="current-worker-main">
2252
+ <div id="current-worker-title" class="current-worker-title">No worker attached</div>
2253
+ <div id="current-worker-subtitle" class="current-worker-subtitle">Attach a worker to start</div>
2254
+ </div>
2255
+ <div class="current-worker-actions">
2256
+ <button class="current-worker-action worker-mobile-action" onclick="selectNodeHome()">CHAT</button>
2257
+ <button class="current-worker-action worker-mobile-action" onclick="toggleWorkerSidebar()">SESSIONS</button>
2258
+ <button id="current-worker-detail-btn" class="current-worker-action" onclick="inspectCurrentWorker()">DETAILS</button>
2259
+ <button id="current-worker-restart" class="current-worker-action" onclick="restartCurrentWorker()">RESTART</button>
2260
+ <button id="current-worker-stop" class="current-worker-stop" onclick="stopCurrentWorker()">STOP</button>
2261
+ </div>
2262
+ </div>
2263
+ <div id="worker-detail-panel" class="worker-detail-panel">
2264
+ <div class="worker-detail-header">
2265
+ <div id="worker-detail-title" class="worker-detail-title">Worker details</div>
2266
+ <button class="worker-detail-close" onclick="closeWorkerDetail()">CLOSE</button>
2267
+ </div>
2268
+ <div id="worker-detail-meta" class="worker-detail-meta"></div>
2269
+ <div id="worker-detail-snapshots" class="worker-detail-snapshots"></div>
2270
+ <div id="worker-detail-events" class="worker-detail-events"></div>
2271
+ <div id="worker-detail-output" class="worker-detail-output"></div>
2272
+ </div>
2273
+
2274
+ <!-- Terminal -->
2275
+ <div id="terminal" class="terminal-content"></div>
2276
+ </div>
2277
+ </div>
2278
+ </div>
2279
+ </div>
2280
+
2281
+ <!-- Control Deck -->
2282
+ <div class="control-deck">
2283
+ <div class="key-grid desktop-key-grid">
2284
+ <button class="key" onclick="sendCommand('clear')" title="Clear Screen">CLS</button>
2285
+ <button class="key" onclick="zoomIn()" title="Zoom In">ZOOM+</button>
2286
+ <button class="key" onclick="zoomOut()" title="Zoom Out">ZOOM-</button>
2287
+ <button class="key" onclick="fitTerminal()" title="Fit Screen">FIT</button>
2288
+ <button class="key nav" onclick="disconnect()" title="Disconnect">DISC</button>
2289
+ <button class="key danger" onclick="resetTerminal()" title="Reset">RST</button>
2290
+ </div>
2291
+ <div class="mobile-control-cluster">
2292
+ <div class="mobile-key-row top">
2293
+ <button class="key accent" onclick="sendTerminalControl('escape')" title="Escape">ESC</button>
2294
+ <button class="key nav" onclick="sendTerminalControl('up')" title="Arrow Up">UP</button>
2295
+ <button class="key" onclick="sendTerminalControl('enter')" title="Enter">ENTER</button>
2296
+ <button class="key" onclick="openMenuCommand()" title="Open Slash Command">/</button>
2297
+ <button class="key danger" onclick="sendTerminalControl('backspace')" title="Backspace">←</button>
2298
+ </div>
2299
+ <div class="mobile-key-row middle">
2300
+ <button class="key nav" onclick="sendTerminalControl('left')" title="Arrow Left">LEFT</button>
2301
+ <button class="key nav" onclick="sendTerminalControl('down')" title="Arrow Down">DOWN</button>
2302
+ <button class="key nav" onclick="sendTerminalControl('right')" title="Arrow Right">RIGHT</button>
2303
+ <button class="key" onclick="zoomOut()" title="Zoom Out">-</button>
2304
+ <button class="key" onclick="zoomIn()" title="Zoom In">+</button>
2305
+ </div>
2306
+ </div>
2307
+ </div>
2308
+
2309
+ <!-- Status Bar -->
2310
+ <div class="status-bar">
2311
+ <div class="status-left">
2312
+ <div class="status-item">
2313
+ <span>SES:</span>
2314
+ <span id="session-display" class="status-value">----</span>
2315
+ </div>
2316
+ <div class="status-item">
2317
+ <span>DIM:</span>
2318
+ <span id="terminal-size" class="status-value">--×--</span>
2319
+ </div>
2320
+ </div>
2321
+ <div class="status-right">
2322
+ <div class="status-item">
2323
+ <span>MODE:</span>
2324
+ <span id="mode-display" class="status-value">STANDBY</span>
2325
+ </div>
2326
+ </div>
2327
+ </div>
2328
+ </div>
2329
+
2330
+ <!-- xterm.js -->
2331
+ <script src="https://cdn.jsdelivr.net/npm/xterm@5.3.0/lib/xterm.min.js"></script>
2332
+ <script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.min.js"></script>
2333
+ <script src="./runtime-control.js"></script>
2334
+ <script src="./runtime-transport.js"></script>
2335
+ <script src="./runtime-messages.js"></script>
2336
+ <script src="./runtime-render.js"></script>
2337
+ <script src="./runtime-worker-ui.js"></script>
2338
+ <script src="./runtime-node-ui.js"></script>
2339
+ <script src="./runtime-terminal.js"></script>
2340
+
2341
+ <script>
2342
+ const terminalContainer = document.getElementById('terminal');
2343
+ const terminalTheme = {
2344
+ background: '#11214a',
2345
+ foreground: '#f5f8ff',
2346
+ cursor: '#5cf29a',
2347
+ selection: '#294b89',
2348
+ black: '#11214a',
2349
+ red: '#ff6b6b',
2350
+ green: '#5cf29a',
2351
+ yellow: '#FFB000',
2352
+ blue: '#77b6ff',
2353
+ magenta: '#ff89d8',
2354
+ cyan: '#6be7ff',
2355
+ white: '#f5f8ff',
2356
+ brightBlack: '#5b6f96',
2357
+ brightRed: '#ff8c8c',
2358
+ brightGreen: '#8dffbc',
2359
+ brightYellow: '#ffd76b',
2360
+ brightBlue: '#9ccaff',
2361
+ brightMagenta: '#fface6',
2362
+ brightCyan: '#9af0ff',
2363
+ brightWhite: '#ffffff'
2364
+ };
2365
+
2366
+ // WebSocket
2367
+ let ws = null;
2368
+ let transport = null;
2369
+ let sessionId = null;
2370
+ let selectedViewId = localStorage.getItem('selectedViewId') || 'node';
2371
+ let activeTerminalId = null;
2372
+ let workers = [];
2373
+ let preferredSessionId = localStorage.getItem('preferredSessionId');
2374
+ let workerPollTimer = null;
2375
+ let selectedSpawnPlugin = readStoredSpawnPlugin();
2376
+ let defaultSpawnCwd = localStorage.getItem('spawnCwd') || '';
2377
+ let recentCwds = [];
2378
+ let pendingSpawnSessionId = null;
2379
+ let pendingSpawnAutoAttach = true;
2380
+ let pendingStopSessionId = null;
2381
+ let pendingRestartSessionId = null;
2382
+ let terminalFontSize = 14;
2383
+ let dirBrowserPath = null;
2384
+ let dirBrowserParentPath = null;
2385
+ let dirBrowserRepoRoot = null;
2386
+ let dirBrowserEntries = [];
2387
+ let showClosedWorkers = localStorage.getItem('showClosedWorkers') === '1';
2388
+ let workerDetail = null;
2389
+ let workerDetailOpen = false;
2390
+ let nodeDetailOpen = false;
2391
+ let runtimeSummary = null;
2392
+ let runtimeFocus = [];
2393
+ let primaryAgent = null;
2394
+ let pendingNodeReply = false;
2395
+ let nodeTranscript = [{
2396
+ id: `node-welcome-${Date.now()}`,
2397
+ role: 'assistant',
2398
+ text: 'Magic Shell is online. Use the runtime views to inspect, spawn, attach, and supervise workers.',
2399
+ timestamp: Date.now(),
2400
+ }];
2401
+ let terminalManager = null;
2402
+ let mobileSidebarOpen = false;
2403
+ let mobileControlMode = 'terminal';
2404
+ let mobileGestureState = null;
2405
+
2406
+ function escapeHtml(value) {
2407
+ return String(value ?? '')
2408
+ .replaceAll('&', '&amp;')
2409
+ .replaceAll('<', '&lt;')
2410
+ .replaceAll('>', '&gt;')
2411
+ .replaceAll('"', '&quot;')
2412
+ .replaceAll("'", '&#39;');
2413
+ }
2414
+
2415
+ function pathBaseName(value) {
2416
+ if (!value) return '.';
2417
+ const normalized = String(value).replace(/[\\/]+$/, '');
2418
+ if (!normalized) return value;
2419
+ const parts = normalized.split(/[\\/]/).filter(Boolean);
2420
+ return parts[parts.length - 1] || normalized;
2421
+ }
2422
+
2423
+ function readCookie(name) {
2424
+ const match = document.cookie.match(new RegExp(`(?:^|; )${name}=([^;]*)`));
2425
+ return match ? decodeURIComponent(match[1]) : '';
2426
+ }
2427
+
2428
+ function writeCookie(name, value) {
2429
+ document.cookie = `${name}=${encodeURIComponent(value)}; path=/; max-age=${60 * 60 * 24 * 365}`;
2430
+ }
2431
+
2432
+ function readStoredSpawnPlugin() {
2433
+ const cookieValue = readCookie('spawnPlugin');
2434
+ const saved = cookieValue || localStorage.getItem('spawnPlugin') || 'pie';
2435
+ return saved === 'claude-code' || saved === 'codex' ? saved : 'pie';
2436
+ }
2437
+
2438
+ function persistSpawnPlugin(pluginName) {
2439
+ selectedSpawnPlugin = pluginName === 'claude-code' || pluginName === 'codex' ? pluginName : 'pie';
2440
+ localStorage.setItem('spawnPlugin', selectedSpawnPlugin);
2441
+ writeCookie('spawnPlugin', selectedSpawnPlugin);
2442
+ }
2443
+
2444
+ function loadRecentCwds() {
2445
+ try {
2446
+ const stored = readCookie('recentCwds') || localStorage.getItem('recentCwds') || '[]';
2447
+ const parsed = JSON.parse(stored);
2448
+ return Array.isArray(parsed) ? parsed.filter((item) => typeof item === 'string') : [];
2449
+ } catch {
2450
+ return [];
2451
+ }
2452
+ }
2453
+
2454
+ function saveRecentCwds() {
2455
+ const serialized = JSON.stringify(recentCwds.slice(0, 8));
2456
+ localStorage.setItem('recentCwds', serialized);
2457
+ writeCookie('recentCwds', serialized);
2458
+ }
2459
+
2460
+ function rememberCwd(cwd) {
2461
+ if (!cwd) return;
2462
+ recentCwds = [cwd, ...recentCwds.filter((item) => item !== cwd)].slice(0, 8);
2463
+ saveRecentCwds();
2464
+ renderCwdShortcuts();
2465
+ }
2466
+
2467
+ function getTerminalManager() {
2468
+ if (!terminalManager) {
2469
+ terminalManager = window.MagicShellRuntimeTerminal.createTerminalManager({
2470
+ container: terminalContainer,
2471
+ theme: terminalTheme,
2472
+ getFontSize() {
2473
+ return terminalFontSize;
2474
+ },
2475
+ isConnected() {
2476
+ return !!ws && ws.readyState === WebSocket.OPEN;
2477
+ },
2478
+ sendInput(targetSessionId, data) {
2479
+ if (!ws || ws.readyState !== WebSocket.OPEN) return;
2480
+ ws.send(JSON.stringify({
2481
+ type: 'input',
2482
+ sessionId: targetSessionId,
2483
+ data,
2484
+ }));
2485
+ },
2486
+ sendResize(targetSessionId, cols, rows) {
2487
+ if (!ws || ws.readyState !== WebSocket.OPEN) return;
2488
+ ws.send(JSON.stringify({
2489
+ type: 'resize',
2490
+ sessionId: targetSessionId,
2491
+ cols,
2492
+ rows,
2493
+ }));
2494
+ },
2495
+ setTerminalSizeText(value) {
2496
+ document.getElementById('terminal-size').textContent = value;
2497
+ },
2498
+ onActiveChange(value) {
2499
+ activeTerminalId = value;
2500
+ },
2501
+ });
2502
+ }
2503
+ return terminalManager;
2504
+ }
2505
+
2506
+ function createTerminalView(id) {
2507
+ return getTerminalManager().createTerminalView(id);
2508
+ }
2509
+
2510
+ function ensureTerminalView(id = '__system__') {
2511
+ return getTerminalManager().ensureView(id);
2512
+ }
2513
+
2514
+ function getActiveTerminalView() {
2515
+ return getTerminalManager().getActiveView();
2516
+ }
2517
+
2518
+ function setActiveTerminal(id) {
2519
+ getTerminalManager().setActive(id);
2520
+ }
2521
+
2522
+ function fitTerminalView(view) {
2523
+ getTerminalManager().fitView(view);
2524
+ }
2525
+
2526
+ function fitAllTerminalViews() {
2527
+ getTerminalManager().fitAll();
2528
+ }
2529
+
2530
+ function writeTerminal(id, data) {
2531
+ return getTerminalManager().writeToView(id, data);
2532
+ }
2533
+
2534
+ function writeTerminalLine(id, data) {
2535
+ return getTerminalManager().writelnToView(id, data);
2536
+ }
2537
+
2538
+ function destroyTerminalViews() {
2539
+ getTerminalManager().destroyAll();
2540
+ }
2541
+
2542
+ function destroyTerminalView(id) {
2543
+ getTerminalManager().destroyView(id);
2544
+ }
2545
+
2546
+ recentCwds = loadRecentCwds();
2547
+
2548
+ // Load saved credentials
2549
+ document.getElementById('relayUrl').value = localStorage.getItem('relayUrl') || document.getElementById('relayUrl').value;
2550
+ document.getElementById('nodeId').value = localStorage.getItem('nodeId') || '';
2551
+ document.getElementById('password').value = localStorage.getItem('password') || '';
2552
+ document.getElementById('spawnCwd').value = defaultSpawnCwd;
2553
+ document.getElementById('spawnPlugin').value = selectedSpawnPlugin;
2554
+ document.getElementById('spawnPlugin').addEventListener('change', (event) => {
2555
+ persistSpawnPlugin(event.target.value || 'pie');
2556
+ resetSpawnButton();
2557
+ syncWorkerPluginPreference();
2558
+ });
2559
+ resetSpawnButton();
2560
+
2561
+ function renderCwdShortcuts() {
2562
+ const container = document.getElementById('cwd-shortcuts');
2563
+ const workerCwds = Array.from(new Set(
2564
+ workers
2565
+ .map((worker) => worker.cwd)
2566
+ .filter((cwd) => typeof cwd === 'string' && cwd.length > 0)
2567
+ ));
2568
+ const shortcuts = Array.from(new Set([
2569
+ ...recentCwds,
2570
+ ...workerCwds,
2571
+ ])).slice(0, 8);
2572
+
2573
+ if (!shortcuts.length) {
2574
+ container.innerHTML = '';
2575
+ return;
2576
+ }
2577
+
2578
+ container.innerHTML = shortcuts.map((cwd) => {
2579
+ const label = pathBaseName(cwd);
2580
+ return `<button class="worker-shortcut" onclick="applyCwdShortcut(decodeURIComponent('${encodeURIComponent(cwd)}'))" title="${escapeHtml(cwd)}">${escapeHtml(label)}</button>`;
2581
+ }).join('');
2582
+ }
2583
+
2584
+ function applyCwdShortcut(cwd) {
2585
+ const input = document.getElementById('spawnCwd');
2586
+ input.value = cwd;
2587
+ defaultSpawnCwd = cwd;
2588
+ localStorage.setItem('spawnCwd', defaultSpawnCwd);
2589
+ if (cwd) {
2590
+ rememberCwd(cwd);
2591
+ }
2592
+ }
2593
+
2594
+ const searchParams = new URLSearchParams(window.location.search);
2595
+ const inferredRelayUrl = inferDefaultRelayUrl();
2596
+ document.getElementById('relayUrl').value = inferredRelayUrl;
2597
+ const relayParam = searchParams.get('relay');
2598
+ const nodeParam = searchParams.get('node');
2599
+ const passwordParam = searchParams.get('pwd');
2600
+ const autoConnectParam = searchParams.get('autoconnect');
2601
+ if (relayParam) {
2602
+ document.getElementById('relayUrl').value = relayParam;
2603
+ }
2604
+ if (nodeParam) {
2605
+ document.getElementById('nodeId').value = nodeParam;
2606
+ }
2607
+ if (passwordParam) {
2608
+ document.getElementById('password').value = passwordParam;
2609
+ }
2610
+ if (autoConnectParam === '1' || autoConnectParam === 'true') {
2611
+ queueMicrotask(() => {
2612
+ const relayUrl = document.getElementById('relayUrl').value.trim();
2613
+ const nodeId = document.getElementById('nodeId').value.trim();
2614
+ const password = document.getElementById('password').value.trim();
2615
+ if (relayUrl && nodeId && password) {
2616
+ connect();
2617
+ }
2618
+ });
2619
+ }
2620
+ document.getElementById('node-compose-input').addEventListener('keydown', (event) => {
2621
+ if (event.key === 'Enter' && !event.shiftKey && !event.altKey && !event.ctrlKey && !event.metaKey) {
2622
+ event.preventDefault();
2623
+ sendNodeMessage();
2624
+ }
2625
+ });
2626
+
2627
+ function updateStatus(status, color = '#eaeaea') {
2628
+ document.getElementById('status-text').textContent = status;
2629
+ document.getElementById('mode-display').textContent = status;
2630
+ const led = document.getElementById('status-led');
2631
+ if (status === 'ONLINE') {
2632
+ led.classList.add('active');
2633
+ } else {
2634
+ led.classList.remove('active');
2635
+ }
2636
+ }
2637
+
2638
+ function inferDefaultRelayUrl() {
2639
+ const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
2640
+ return `${protocol}//${window.location.host}/ws`;
2641
+ }
2642
+
2643
+ function setDisconnectedState(disconnected) {
2644
+ document.querySelector('.crt-container')?.classList.toggle('disconnected', disconnected);
2645
+ }
2646
+
2647
+ function isNodeViewSelected() {
2648
+ return selectedViewId === 'node';
2649
+ }
2650
+
2651
+ function pushNodeTranscript(role, text, extra = {}) {
2652
+ nodeTranscript = [...nodeTranscript, {
2653
+ id: `${role}-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
2654
+ role,
2655
+ text,
2656
+ timestamp: Date.now(),
2657
+ actionSessionId: extra.actionSessionId || null,
2658
+ actionLabel: extra.actionLabel || '',
2659
+ actionType: extra.actionType || '',
2660
+ actionTaskSummary: extra.actionTaskSummary || '',
2661
+ }].slice(-40);
2662
+ renderNodeHome();
2663
+ }
2664
+
2665
+ function syncNodeTranscriptFromServer(history) {
2666
+ if (!Array.isArray(history) || !history.length) return;
2667
+ nodeTranscript = history.slice(-40).map((entry, index) => ({
2668
+ id: `node-sync-${entry.timestamp || Date.now()}-${index}`,
2669
+ role: entry.role === 'user' ? 'user' : 'assistant',
2670
+ text: String(entry.text || ''),
2671
+ timestamp: entry.timestamp || Date.now(),
2672
+ actionSessionId: entry.actionSessionId || null,
2673
+ actionLabel: entry.actionLabel || '',
2674
+ actionType: entry.actionType || '',
2675
+ actionTaskSummary: entry.actionTaskSummary || '',
2676
+ }));
2677
+ renderNodeHome();
2678
+ }
2679
+
2680
+ function sendControl(controlKind, controlName, payload = {}, target = {}) {
2681
+ return transport?.sendControl(controlKind, controlName, payload, target) || null;
2682
+ }
2683
+
2684
+ function syncWorkerPluginPreference() {
2685
+ if (!ws || ws.readyState !== WebSocket.OPEN) return null;
2686
+ return sendControl('command', 'set_worker_plugin_preference', {
2687
+ pluginName: selectedSpawnPlugin,
2688
+ });
2689
+ }
2690
+
2691
+ function selectNodeHome() {
2692
+ selectedViewId = 'node';
2693
+ localStorage.setItem('selectedViewId', selectedViewId);
2694
+ workerDetailOpen = false;
2695
+ if (isMobileViewport()) {
2696
+ closeWorkerSidebar();
2697
+ }
2698
+ updateWorkspaceView();
2699
+ renderWorkers();
2700
+ }
2701
+
2702
+ function returnToPrimaryChat({ clearSession = false } = {}) {
2703
+ if (clearSession) {
2704
+ sessionId = null;
2705
+ document.getElementById('session-display').textContent = '----';
2706
+ }
2707
+ selectedViewId = 'node';
2708
+ localStorage.setItem('selectedViewId', selectedViewId);
2709
+ workerDetail = null;
2710
+ workerDetailOpen = false;
2711
+ nodeDetailOpen = false;
2712
+ mobileSidebarOpen = false;
2713
+ setActiveTerminal('__system__');
2714
+ setMobileControlMode('terminal');
2715
+ updateWorkspaceView();
2716
+ renderWorkers();
2717
+ requestAnimationFrame(() => {
2718
+ document.getElementById('node-compose-input')?.focus?.();
2719
+ });
2720
+ }
2721
+
2722
+ function isMobileViewport() {
2723
+ return (window.innerWidth || 0) <= 820;
2724
+ }
2725
+
2726
+ function syncMobileWorkbench() {
2727
+ const sidebar = document.getElementById('worker-sidebar');
2728
+ const backdrop = document.getElementById('mobile-sidebar-backdrop');
2729
+ const sessionsBtn = document.getElementById('mobile-sessions-toggle');
2730
+ const controlDeck = document.querySelector('.control-deck');
2731
+ const statusBar = document.querySelector('.status-bar');
2732
+ const brandBadge = document.querySelector('.brand-badge');
2733
+ const bezelTop = document.querySelector('.bezel-top');
2734
+ const workspacePanel = document.querySelector('.workspace-panel');
2735
+ const isMobile = isMobileViewport();
2736
+ if (!isMobile) {
2737
+ mobileSidebarOpen = false;
2738
+ }
2739
+ document.body.classList.toggle('compact-layout', isMobile);
2740
+ if (sidebar) {
2741
+ if (isMobile) {
2742
+ sidebar.style.position = 'absolute';
2743
+ sidebar.style.top = '0';
2744
+ sidebar.style.left = '0';
2745
+ sidebar.style.bottom = '0';
2746
+ sidebar.style.width = 'calc(100% - 8px)';
2747
+ sidebar.style.maxWidth = 'calc(100% - 8px)';
2748
+ sidebar.style.flex = 'none';
2749
+ sidebar.style.zIndex = '25';
2750
+ sidebar.style.opacity = mobileSidebarOpen ? '1' : '0';
2751
+ sidebar.style.pointerEvents = mobileSidebarOpen ? 'auto' : 'none';
2752
+ sidebar.style.transform = mobileSidebarOpen ? 'translateX(0)' : 'translateX(calc(-100% - 14px))';
2753
+ sidebar.style.boxShadow = '0 18px 42px rgba(0,0,0,0.42)';
2754
+ sidebar.style.background = 'linear-gradient(180deg, rgba(11, 24, 63, 0.98), rgba(9, 20, 51, 0.98))';
2755
+ } else {
2756
+ sidebar.style.position = '';
2757
+ sidebar.style.top = '';
2758
+ sidebar.style.left = '';
2759
+ sidebar.style.bottom = '';
2760
+ sidebar.style.width = '';
2761
+ sidebar.style.maxWidth = '';
2762
+ sidebar.style.flex = '';
2763
+ sidebar.style.zIndex = '';
2764
+ sidebar.style.opacity = '';
2765
+ sidebar.style.pointerEvents = '';
2766
+ sidebar.style.transform = '';
2767
+ sidebar.style.boxShadow = '';
2768
+ sidebar.style.background = '';
2769
+ }
2770
+ }
2771
+ if (bezelTop) {
2772
+ bezelTop.style.flexWrap = isMobile ? 'wrap' : '';
2773
+ bezelTop.style.gap = isMobile ? '8px' : '';
2774
+ bezelTop.style.paddingBottom = isMobile ? '8px' : '';
2775
+ bezelTop.style.marginBottom = isMobile ? '8px' : '';
2776
+ }
2777
+ if (brandBadge) {
2778
+ brandBadge.style.fontSize = isMobile ? '1.08rem' : '';
2779
+ brandBadge.style.padding = isMobile ? '2px 8px' : '';
2780
+ brandBadge.style.letterSpacing = isMobile ? '1px' : '';
2781
+ }
2782
+ if (controlDeck) {
2783
+ controlDeck.style.marginTop = isMobile ? '8px' : '';
2784
+ controlDeck.style.padding = isMobile ? '8px' : '';
2785
+ }
2786
+ if (statusBar) {
2787
+ statusBar.style.marginTop = isMobile ? '8px' : '';
2788
+ statusBar.style.paddingTop = isMobile ? '8px' : '';
2789
+ statusBar.style.fontSize = isMobile ? '0.8rem' : '';
2790
+ statusBar.style.flexWrap = isMobile ? 'wrap' : '';
2791
+ statusBar.style.gap = isMobile ? '8px' : '';
2792
+ }
2793
+ if (workspacePanel) {
2794
+ workspacePanel.classList.toggle('mobile-muted', isMobile && mobileSidebarOpen);
2795
+ }
2796
+ sidebar.classList.toggle('mobile-open', isMobile && mobileSidebarOpen);
2797
+ backdrop.classList.toggle('active', isMobile && mobileSidebarOpen);
2798
+ if (sessionsBtn) {
2799
+ sessionsBtn.classList.toggle('active', isMobile && mobileSidebarOpen);
2800
+ }
2801
+ if (!isMobile && mobileControlMode !== 'terminal') {
2802
+ setMobileControlMode('terminal');
2803
+ }
2804
+ renderMobileControlMode();
2805
+ }
2806
+
2807
+ function closeWorkerSidebar() {
2808
+ mobileSidebarOpen = false;
2809
+ syncMobileWorkbench();
2810
+ }
2811
+
2812
+ function renderMobileControlMode() {
2813
+ const badge = document.getElementById('control-mode-display');
2814
+ if (!badge) return;
2815
+ const inMenuNav = mobileControlMode === 'menu_nav';
2816
+ badge.textContent = inMenuNav ? 'MENU NAV' : 'TERMINAL';
2817
+ badge.classList.toggle('menu-nav', inMenuNav);
2818
+ }
2819
+
2820
+ function setMobileControlMode(nextMode) {
2821
+ mobileControlMode = nextMode === 'menu_nav' ? 'menu_nav' : 'terminal';
2822
+ renderMobileControlMode();
2823
+ }
2824
+
2825
+ function toggleMenuNavMode() {
2826
+ if (!isMobileViewport()) return;
2827
+ setMobileControlMode(mobileControlMode === 'menu_nav' ? 'terminal' : 'menu_nav');
2828
+ getActiveTerminalView()?.term?.focus?.();
2829
+ }
2830
+
2831
+ function openMenuCommand() {
2832
+ if (!isMobileViewport()) return;
2833
+ sendRawTerminalInput('/');
2834
+ setMobileControlMode('menu_nav');
2835
+ }
2836
+
2837
+ function getActiveTerminalTargetSessionId() {
2838
+ const activeId = getTerminalManager().getActiveId() || '__system__';
2839
+ return activeId === '__system__' ? null : activeId;
2840
+ }
2841
+
2842
+ function sendRawTerminalInput(data) {
2843
+ if (!ws || ws.readyState !== WebSocket.OPEN) return false;
2844
+ ws.send(JSON.stringify({
2845
+ type: 'input',
2846
+ sessionId: getActiveTerminalTargetSessionId(),
2847
+ data,
2848
+ }));
2849
+ getActiveTerminalView()?.term?.focus?.();
2850
+ return true;
2851
+ }
2852
+
2853
+ function sendTerminalControl(action) {
2854
+ const data = {
2855
+ up: '\u001b[A',
2856
+ down: '\u001b[B',
2857
+ left: '\u001b[D',
2858
+ right: '\u001b[C',
2859
+ enter: '\r',
2860
+ escape: '\u001b',
2861
+ backspace: '\u007f',
2862
+ ctrl_c: '\u0003',
2863
+ }[action];
2864
+ if (!data) return false;
2865
+ return sendRawTerminalInput(data);
2866
+ }
2867
+
2868
+ function installMobileSidebarGuards() {
2869
+ const sidebar = document.getElementById('worker-sidebar');
2870
+ if (!sidebar || sidebar.dataset.guardsInstalled === '1') {
2871
+ return;
2872
+ }
2873
+ sidebar.dataset.guardsInstalled = '1';
2874
+ }
2875
+
2876
+ function toggleWorkerSidebar() {
2877
+ if (!isMobileViewport()) return;
2878
+ mobileSidebarOpen = !mobileSidebarOpen;
2879
+ syncMobileWorkbench();
2880
+ }
2881
+
2882
+ function focusWorkerView() {
2883
+ if (sessionId) {
2884
+ selectedViewId = sessionId;
2885
+ localStorage.setItem('selectedViewId', selectedViewId);
2886
+ updateWorkspaceView();
2887
+ renderWorkers();
2888
+ } else {
2889
+ selectNodeHome();
2890
+ }
2891
+ if (isMobileViewport()) {
2892
+ closeWorkerSidebar();
2893
+ }
2894
+ }
2895
+
2896
+ function toggleNodeDetail() {
2897
+ nodeDetailOpen = !nodeDetailOpen;
2898
+ renderNodeDetailPanel();
2899
+ }
2900
+
2901
+ function closeNodeDetail() {
2902
+ nodeDetailOpen = false;
2903
+ renderNodeDetailPanel();
2904
+ }
2905
+
2906
+ function renderNodeDetailPanel() {
2907
+ window.MagicShellRuntimeNodeUI.renderNodeDetailPanel({
2908
+ nodeDetailOpen,
2909
+ isNodeViewSelected,
2910
+ getPanelNode() {
2911
+ return document.getElementById('node-detail-panel');
2912
+ },
2913
+ getToggleNode() {
2914
+ return document.getElementById('node-detail-toggle');
2915
+ },
2916
+ });
2917
+ }
2918
+
2919
+ function updateWorkspaceView() {
2920
+ const nodeHome = document.getElementById('node-home-view');
2921
+ const workerView = document.getElementById('worker-view');
2922
+ const controlDeck = document.querySelector('.control-deck');
2923
+ const isNode = isNodeViewSelected();
2924
+ nodeHome.classList.toggle('active', isNode);
2925
+ workerView.classList.toggle('active', !isNode);
2926
+ if (controlDeck) {
2927
+ controlDeck.style.display = isNode ? 'none' : '';
2928
+ }
2929
+ document.getElementById('mode-display').textContent = isNode ? 'PRIMARY' : 'WORKER';
2930
+ syncMobileWorkbench();
2931
+ if (isNode) {
2932
+ if (mobileControlMode !== 'terminal') {
2933
+ setMobileControlMode('terminal');
2934
+ }
2935
+ renderNodeHome();
2936
+ renderNodeDetailPanel();
2937
+ } else {
2938
+ nodeDetailOpen = false;
2939
+ renderNodeDetailPanel();
2940
+ updateCurrentWorkerBar();
2941
+ }
2942
+ }
2943
+
2944
+ function getRuntimeControlContext() {
2945
+ return {
2946
+ get workers() { return workers; },
2947
+ set workers(value) { workers = value; },
2948
+ get selectedViewId() { return selectedViewId; },
2949
+ get runtimeSummary() { return runtimeSummary; },
2950
+ set runtimeSummary(value) { runtimeSummary = value; },
2951
+ get runtimeFocus() { return runtimeFocus; },
2952
+ set runtimeFocus(value) { runtimeFocus = value; },
2953
+ get primaryAgent() { return primaryAgent; },
2954
+ set primaryAgent(value) { primaryAgent = value; },
2955
+ get pendingNodeReply() { return pendingNodeReply; },
2956
+ set pendingNodeReply(value) { pendingNodeReply = value; },
2957
+ get sessionId() { return sessionId; },
2958
+ set sessionId(value) { sessionId = value; },
2959
+ get workerDetail() { return workerDetail; },
2960
+ set workerDetail(value) { workerDetail = value; },
2961
+ get workerDetailOpen() { return workerDetailOpen; },
2962
+ get preferredSessionId() { return preferredSessionId; },
2963
+ get pendingSpawnSessionId() { return pendingSpawnSessionId; },
2964
+ set pendingSpawnSessionId(value) { pendingSpawnSessionId = value; },
2965
+ get pendingSpawnAutoAttach() { return pendingSpawnAutoAttach; },
2966
+ set pendingSpawnAutoAttach(value) { pendingSpawnAutoAttach = value; },
2967
+ get pendingStopSessionId() { return pendingStopSessionId; },
2968
+ set pendingStopSessionId(value) { pendingStopSessionId = value; },
2969
+ get pendingRestartSessionId() { return pendingRestartSessionId; },
2970
+ set pendingRestartSessionId(value) { pendingRestartSessionId = value; },
2971
+ get dirBrowserPath() { return dirBrowserPath; },
2972
+ set dirBrowserPath(value) { dirBrowserPath = value; },
2973
+ get dirBrowserParentPath() { return dirBrowserParentPath; },
2974
+ set dirBrowserParentPath(value) { dirBrowserParentPath = value; },
2975
+ get dirBrowserRepoRoot() { return dirBrowserRepoRoot; },
2976
+ set dirBrowserRepoRoot(value) { dirBrowserRepoRoot = value; },
2977
+ get dirBrowserEntries() { return dirBrowserEntries; },
2978
+ set dirBrowserEntries(value) { dirBrowserEntries = value; },
2979
+ syncNodeTranscriptFromServer,
2980
+ destroyTerminalView,
2981
+ attachWorker,
2982
+ pushNodeTranscript,
2983
+ renderNodeHome,
2984
+ selectNodeHome,
2985
+ returnToPrimaryChat,
2986
+ resetSpawnButton,
2987
+ renderWorkers,
2988
+ inspectCurrentWorker,
2989
+ refreshCurrentWorkerDetail,
2990
+ renderWorkerDetailPanel,
2991
+ renderDirBrowser,
2992
+ isNodeViewSelected,
2993
+ writeSystemNotice(text) {
2994
+ writeTerminalLine('__system__', `\r\n[ ${text} ]\r\n`);
2995
+ },
2996
+ clearPreferredSession() {
2997
+ preferredSessionId = null;
2998
+ localStorage.removeItem('preferredSessionId');
2999
+ },
3000
+ };
3001
+ }
3002
+
3003
+ function renderNodeHome() {
3004
+ window.MagicShellRuntimeNodeUI.renderNodeHome({
3005
+ workers,
3006
+ runtimeSummary,
3007
+ runtimeFocus,
3008
+ primaryAgent,
3009
+ pendingNodeReply,
3010
+ nodeTranscript,
3011
+ escapeHtml,
3012
+ formatRelativeTime,
3013
+ isConnected() {
3014
+ return !!ws && ws.readyState === WebSocket.OPEN;
3015
+ },
3016
+ getThreadNode() {
3017
+ return document.getElementById('node-chat-thread');
3018
+ },
3019
+ getCoordinationNode() {
3020
+ return document.getElementById('node-coordination-state');
3021
+ },
3022
+ getCoordinationTextNode() {
3023
+ return document.getElementById('node-coordination-text');
3024
+ },
3025
+ getInputNode() {
3026
+ return document.getElementById('node-compose-input');
3027
+ },
3028
+ setNodeNavTitle(value) {
3029
+ document.getElementById('node-nav-title').textContent = value;
3030
+ },
3031
+ setNodeHomeTitle(value) {
3032
+ document.getElementById('node-home-title').textContent = value;
3033
+ },
3034
+ setNodeNavMeta(value) {
3035
+ document.getElementById('node-nav-meta').textContent = value;
3036
+ },
3037
+ setNodeNavBadge(value) {
3038
+ document.getElementById('node-nav-badge').textContent = value;
3039
+ },
3040
+ setNodeHomeBadge(value) {
3041
+ document.getElementById('node-home-badge').textContent = value;
3042
+ },
3043
+ });
3044
+ }
3045
+
3046
+ function sendNodeMessage() {
3047
+ const input = document.getElementById('node-compose-input');
3048
+ const text = input.value.trim();
3049
+ if (!text || pendingNodeReply || !ws || ws.readyState !== WebSocket.OPEN || !window.MagicShellRuntimeControl.hasUsablePrimaryAgent(primaryAgent)) return;
3050
+ pushNodeTranscript('user', text);
3051
+ input.value = '';
3052
+ pendingNodeReply = true;
3053
+ renderNodeHome();
3054
+ const requestId = sendControl('command', 'send_primary_message', { text });
3055
+ if (!requestId) {
3056
+ pendingNodeReply = false;
3057
+ pushNodeTranscript('assistant', 'Primary agent unavailable. Try reconnecting or use the runtime controls directly.');
3058
+ renderNodeHome();
3059
+ }
3060
+ }
3061
+
3062
+ function runNodeAction(entryId) {
3063
+ const entry = nodeTranscript.find((item) => item.id === entryId);
3064
+ if (!entry) return;
3065
+ if (entry.actionType === 'attach' && entry.actionSessionId) {
3066
+ attachWorker(entry.actionSessionId);
3067
+ return;
3068
+ }
3069
+ if (entry.actionType === 'spawn') {
3070
+ const taskSummary = entry.actionTaskSummary || undefined;
3071
+ spawnPieWorkerInternal({ taskSummary, autoAttach: false });
3072
+ }
3073
+ }
3074
+
3075
+ function getSpawnPluginLabel(pluginName) {
3076
+ if (pluginName === 'claude-code') return 'CLAUDE';
3077
+ if (pluginName === 'codex') return 'CODEX';
3078
+ return 'PIE';
3079
+ }
3080
+
3081
+ function isControlNoise(summary) {
3082
+ if (!summary) return true;
3083
+ const normalized = String(summary).toLowerCase();
3084
+ return normalized.includes('detail requested')
3085
+ || normalized.includes('attached from browser')
3086
+ || normalized.includes('control snapshot')
3087
+ || normalized.includes('attached ')
3088
+ || normalized === 'attached';
3089
+ }
3090
+
3091
+ function getWorkerPreviewLine(worker) {
3092
+ if (worker.recommendedActionReason) return worker.recommendedActionReason;
3093
+ if (worker.interventionReason) return worker.interventionReason;
3094
+ if (worker.attentionReason) return worker.attentionReason;
3095
+ if (worker.lastEventSummary && !isControlNoise(worker.lastEventSummary)) return worker.lastEventSummary;
3096
+ return worker.cwd || '.';
3097
+ }
3098
+
3099
+ function renderWorkers() {
3100
+ window.MagicShellRuntimeRender.renderWorkers({
3101
+ workers,
3102
+ runtimeSummary,
3103
+ runtimeFocus,
3104
+ showClosedWorkers,
3105
+ selectedViewId,
3106
+ sessionId,
3107
+ escapeHtml,
3108
+ pathBaseName,
3109
+ formatLastOutput,
3110
+ formatRelativeTime,
3111
+ formatWorkerBadge,
3112
+ formatRecommendedAction,
3113
+ formatFocusAction,
3114
+ formatBytes,
3115
+ getWorkerPreviewLine,
3116
+ workerNeedsAttention,
3117
+ isControlNoise,
3118
+ renderNodeHome,
3119
+ selectNodeHome,
3120
+ returnToPrimaryChat,
3121
+ renderCwdShortcuts,
3122
+ updateWorkspaceView,
3123
+ isNodeViewSelected,
3124
+ getWorkerListNode() {
3125
+ return document.getElementById('worker-list');
3126
+ },
3127
+ getNodeNavCard() {
3128
+ return document.getElementById('node-nav-card');
3129
+ },
3130
+ getRuntimeSummaryNode() {
3131
+ return document.getElementById('runtime-summary-grid');
3132
+ },
3133
+ getRuntimeFocusNode() {
3134
+ return document.getElementById('runtime-focus-list');
3135
+ },
3136
+ getRuntimeFeedNode() {
3137
+ return document.getElementById('runtime-feed-list');
3138
+ },
3139
+ setWorkerHeaderLabel(value) {
3140
+ document.getElementById('worker-header-label').textContent = value;
3141
+ },
3142
+ });
3143
+ }
3144
+
3145
+ function renderRuntimeSummary() {
3146
+ window.MagicShellRuntimeRender.renderRuntimeSummary({
3147
+ workers,
3148
+ runtimeSummary,
3149
+ escapeHtml,
3150
+ formatBytes,
3151
+ workerNeedsAttention,
3152
+ getRuntimeSummaryNode() {
3153
+ return document.getElementById('runtime-summary-grid');
3154
+ },
3155
+ });
3156
+ }
3157
+
3158
+ function renderRuntimeFocus() {
3159
+ window.MagicShellRuntimeRender.renderRuntimeFocus({
3160
+ runtimeFocus,
3161
+ escapeHtml,
3162
+ formatFocusAction,
3163
+ formatRelativeTime,
3164
+ getRuntimeFocusNode() {
3165
+ return document.getElementById('runtime-focus-list');
3166
+ },
3167
+ });
3168
+ }
3169
+
3170
+ function renderRuntimeFeed() {
3171
+ window.MagicShellRuntimeRender.renderRuntimeFeed({
3172
+ workers,
3173
+ escapeHtml,
3174
+ formatRelativeTime,
3175
+ isControlNoise,
3176
+ getRuntimeFeedNode() {
3177
+ return document.getElementById('runtime-feed-list');
3178
+ },
3179
+ });
3180
+ }
3181
+
3182
+ function toggleClosedWorkers() {
3183
+ showClosedWorkers = !showClosedWorkers;
3184
+ localStorage.setItem('showClosedWorkers', showClosedWorkers ? '1' : '0');
3185
+ renderWorkers();
3186
+ }
3187
+
3188
+ function updateCurrentWorkerBar() {
3189
+ window.MagicShellRuntimeWorkerUI.renderCurrentWorkerBar({
3190
+ workers,
3191
+ sessionId,
3192
+ pendingStopSessionId,
3193
+ pendingRestartSessionId,
3194
+ pathBaseName,
3195
+ formatWorkerBadge,
3196
+ formatLastOutput,
3197
+ getWorkerPreviewLine,
3198
+ isNodeViewSelected,
3199
+ setActiveTerminal,
3200
+ renderWorkerDetailPanel,
3201
+ getBarNode() {
3202
+ return document.getElementById('current-worker-bar');
3203
+ },
3204
+ getTitleNode() {
3205
+ return document.getElementById('current-worker-title');
3206
+ },
3207
+ getSubtitleNode() {
3208
+ return document.getElementById('current-worker-subtitle');
3209
+ },
3210
+ getStopButton() {
3211
+ return document.getElementById('current-worker-stop');
3212
+ },
3213
+ getRestartButton() {
3214
+ return document.getElementById('current-worker-restart');
3215
+ },
3216
+ getDetailButton() {
3217
+ return document.getElementById('current-worker-detail-btn');
3218
+ },
3219
+ });
3220
+ }
3221
+
3222
+ function closeWorkerDetail() {
3223
+ workerDetailOpen = false;
3224
+ renderWorkerDetailPanel();
3225
+ }
3226
+
3227
+ function renderWorkerDetailPanel() {
3228
+ window.MagicShellRuntimeWorkerUI.renderWorkerDetailPanel({
3229
+ workerDetailOpen,
3230
+ workerDetail,
3231
+ sessionId,
3232
+ escapeHtml,
3233
+ formatEventTime,
3234
+ formatRelativeTime,
3235
+ formatDuration,
3236
+ formatBytes,
3237
+ stripAnsi,
3238
+ getPanelNode() {
3239
+ return document.getElementById('worker-detail-panel');
3240
+ },
3241
+ getTitleNode() {
3242
+ return document.getElementById('worker-detail-title');
3243
+ },
3244
+ getMetaNode() {
3245
+ return document.getElementById('worker-detail-meta');
3246
+ },
3247
+ getSnapshotsNode() {
3248
+ return document.getElementById('worker-detail-snapshots');
3249
+ },
3250
+ getEventsNode() {
3251
+ return document.getElementById('worker-detail-events');
3252
+ },
3253
+ getOutputNode() {
3254
+ return document.getElementById('worker-detail-output');
3255
+ },
3256
+ });
3257
+ }
3258
+
3259
+ function formatEventTime(timestamp) {
3260
+ const date = new Date(timestamp);
3261
+ return `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}:${String(date.getSeconds()).padStart(2, '0')}`;
3262
+ }
3263
+
3264
+ function formatLastOutput(timestamp) {
3265
+ if (!timestamp) return 'idle';
3266
+ const seconds = Math.max(0, Math.floor((Date.now() - timestamp) / 1000));
3267
+ if (seconds < 5) return 'active now';
3268
+ if (seconds < 60) return `${seconds}s ago`;
3269
+ const minutes = Math.floor(seconds / 60);
3270
+ if (minutes < 60) return `${minutes}m ago`;
3271
+ const hours = Math.floor(minutes / 60);
3272
+ return `${hours}h ago`;
3273
+ }
3274
+
3275
+ function formatRelativeTime(timestamp) {
3276
+ if (!timestamp) return 'just now';
3277
+ const seconds = Math.max(0, Math.floor((Date.now() - timestamp) / 1000));
3278
+ if (seconds < 5) return 'just now';
3279
+ if (seconds < 60) return `${seconds}s ago`;
3280
+ const minutes = Math.floor(seconds / 60);
3281
+ if (minutes < 60) return `${minutes}m ago`;
3282
+ const hours = Math.floor(minutes / 60);
3283
+ if (hours < 24) return `${hours}h ago`;
3284
+ const days = Math.floor(hours / 24);
3285
+ return `${days}d ago`;
3286
+ }
3287
+
3288
+ function formatDuration(durationMs) {
3289
+ const totalSeconds = Math.max(0, Math.floor(durationMs / 1000));
3290
+ if (totalSeconds < 60) return `${totalSeconds}s`;
3291
+ const minutes = Math.floor(totalSeconds / 60);
3292
+ if (minutes < 60) return `${minutes}m ${totalSeconds % 60}s`;
3293
+ const hours = Math.floor(minutes / 60);
3294
+ if (hours < 24) return `${hours}h ${minutes % 60}m`;
3295
+ const days = Math.floor(hours / 24);
3296
+ return `${days}d ${hours % 24}h`;
3297
+ }
3298
+
3299
+ function formatBytes(byteCount) {
3300
+ const value = Math.max(0, byteCount || 0);
3301
+ if (value < 1024) return `${value} B`;
3302
+ if (value < 1024 * 1024) return `${(value / 1024).toFixed(1)} KB`;
3303
+ return `${(value / (1024 * 1024)).toFixed(1)} MB`;
3304
+ }
3305
+
3306
+ function stripAnsi(value) {
3307
+ return String(value || '')
3308
+ .replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g, '')
3309
+ .replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, '')
3310
+ .replace(/\x1b[@-_]/g, '');
3311
+ }
3312
+
3313
+ function formatWorkerBadge(worker) {
3314
+ if (pendingSpawnSessionId === worker.sessionId) return 'spawning';
3315
+ if (pendingStopSessionId === worker.sessionId) return 'stopping';
3316
+ if (pendingRestartSessionId === worker.sessionId) return 'restarting';
3317
+ if (worker.status === 'starting') return 'booting';
3318
+ if (worker.status === 'blocked') return 'needs help';
3319
+ if (worker.status === 'stopped') return 'closed';
3320
+ if (worker.status === 'failed') return 'failed';
3321
+ if (worker.interventionLevel === 'required') return 'intervene';
3322
+ if (worker.interventionLevel === 'suggested') return 'check in';
3323
+ if (worker.interventionLevel === 'watch') return 'watch';
3324
+ if (worker.phase === 'attention') return 'needs help';
3325
+ if (worker.phase === 'quiet') return 'quiet';
3326
+ if (worker.activityState === 'busy') {
3327
+ return workerNeedsAttention(worker) ? 'needs help' : 'thinking';
3328
+ }
3329
+ if (worker.activityState === 'ready') {
3330
+ const outputAgeSeconds = getAgeSeconds(worker.lastOutputAt);
3331
+ if (outputAgeSeconds !== null && outputAgeSeconds > 600) return 'quiet';
3332
+ return 'waiting';
3333
+ }
3334
+ if (worker.status === 'running' && worker.lastOutputAt) {
3335
+ const ageSeconds = getAgeSeconds(worker.lastOutputAt) ?? 0;
3336
+ if (ageSeconds < 5) return 'live';
3337
+ if (ageSeconds < 45) return 'active';
3338
+ if (ageSeconds < 180) return 'idle';
3339
+ return 'stale';
3340
+ }
3341
+ return 'idle';
3342
+ }
3343
+
3344
+ function formatRecommendedAction(worker) {
3345
+ if (!worker.recommendedAction || worker.recommendedAction === 'none') return '';
3346
+ if (worker.recommendedAction === 'attach') return 'attach next';
3347
+ if (worker.recommendedAction === 'observe') return 'observe';
3348
+ if (worker.recommendedAction === 'restart') return 'restart';
3349
+ if (worker.recommendedAction === 'close') return 'safe to close';
3350
+ return worker.recommendedAction;
3351
+ }
3352
+
3353
+ function formatFocusAction(item) {
3354
+ if (!item.recommendedAction || item.recommendedAction === 'none') return 'observe';
3355
+ if (item.recommendedAction === 'attach') return 'attach next';
3356
+ if (item.recommendedAction === 'observe') return 'observe';
3357
+ if (item.recommendedAction === 'restart') return 'restart';
3358
+ if (item.recommendedAction === 'close') return 'safe to close';
3359
+ return item.recommendedAction;
3360
+ }
3361
+
3362
+ function getAgeSeconds(timestamp) {
3363
+ if (!timestamp) return null;
3364
+ return Math.max(0, Math.floor((Date.now() - timestamp) / 1000));
3365
+ }
3366
+
3367
+ function workerNeedsAttention(worker) {
3368
+ if (worker.status === 'blocked' || worker.status === 'failed') return true;
3369
+ if (worker.activityState !== 'busy') return false;
3370
+ const activityAgeSeconds = getAgeSeconds(worker.activityUpdatedAt);
3371
+ const outputAgeSeconds = getAgeSeconds(worker.lastOutputAt);
3372
+ return activityAgeSeconds !== null
3373
+ && outputAgeSeconds !== null
3374
+ && activityAgeSeconds > 90
3375
+ && outputAgeSeconds > 45;
3376
+ }
3377
+
3378
+ function requestWorkerList() {
3379
+ sendControl('query', 'get_runtime_summary');
3380
+ }
3381
+
3382
+ function startWorkerPolling() {
3383
+ stopWorkerPolling();
3384
+ workerPollTimer = setInterval(() => {
3385
+ requestWorkerList();
3386
+ }, 5000);
3387
+ }
3388
+
3389
+ function stopWorkerPolling() {
3390
+ if (workerPollTimer) {
3391
+ clearInterval(workerPollTimer);
3392
+ workerPollTimer = null;
3393
+ }
3394
+ }
3395
+
3396
+ function spawnPieWorker() {
3397
+ return spawnPieWorkerInternal({ pluginName: selectedSpawnPlugin, autoAttach: !isMobileViewport() });
3398
+ }
3399
+
3400
+ function spawnPieWorkerInternal({ taskSummary, autoAttach = true, pluginName = selectedSpawnPlugin } = {}) {
3401
+ return spawnWorkerInternal({ pluginName, taskSummary, autoAttach });
3402
+ }
3403
+
3404
+ function spawnWorkerInternal({ pluginName = 'pie', taskSummary, autoAttach = true } = {}) {
3405
+ if (!ws || ws.readyState !== WebSocket.OPEN || pendingSpawnSessionId) return;
3406
+ const nextSessionId = 'session-' + Date.now();
3407
+ const cwdInput = document.getElementById('spawnCwd');
3408
+ const pluginInput = document.getElementById('spawnPlugin');
3409
+ const cwd = cwdInput.value.trim();
3410
+ const spawnButton = document.querySelector('.worker-add-btn');
3411
+ const normalizedPlugin = pluginName || pluginInput?.value || 'pie';
3412
+ persistSpawnPlugin(normalizedPlugin);
3413
+ if (pluginInput) {
3414
+ pluginInput.value = selectedSpawnPlugin;
3415
+ }
3416
+ const pluginLabel = getSpawnPluginLabel(selectedSpawnPlugin);
3417
+ defaultSpawnCwd = cwd;
3418
+ localStorage.setItem('spawnCwd', defaultSpawnCwd);
3419
+ if (cwd) {
3420
+ rememberCwd(cwd);
3421
+ }
3422
+ pendingSpawnSessionId = nextSessionId;
3423
+ pendingSpawnAutoAttach = autoAttach;
3424
+ if (spawnButton) {
3425
+ spawnButton.disabled = true;
3426
+ spawnButton.textContent = `SPAWNING ${pluginLabel}`;
3427
+ }
3428
+ writeTerminalLine('__system__', `\r\n[ SPAWNING ${pluginLabel} WORKER: S:${nextSessionId.slice(-6)} ]\r\n`);
3429
+ sendControl('command', 'spawn_worker', {
3430
+ sessionId: nextSessionId,
3431
+ pluginName: selectedSpawnPlugin,
3432
+ taskSummary: taskSummary || undefined,
3433
+ cwd: cwd || undefined
3434
+ }, {
3435
+ sessionId: nextSessionId
3436
+ });
3437
+ if (autoAttach) {
3438
+ sessionId = nextSessionId;
3439
+ selectedViewId = nextSessionId;
3440
+ localStorage.setItem('selectedViewId', selectedViewId);
3441
+ preferredSessionId = nextSessionId;
3442
+ localStorage.setItem('preferredSessionId', nextSessionId);
3443
+ document.getElementById('session-display').textContent = nextSessionId.slice(-8);
3444
+ ensureTerminalView(nextSessionId);
3445
+ setActiveTerminal(nextSessionId);
3446
+ updateWorkspaceView();
3447
+ }
3448
+ renderWorkers();
3449
+ }
3450
+
3451
+ function attachWorker(targetSessionId, sendMessage = true) {
3452
+ sessionId = targetSessionId;
3453
+ selectedViewId = targetSessionId;
3454
+ localStorage.setItem('selectedViewId', selectedViewId);
3455
+ ensureTerminalView(targetSessionId);
3456
+ setActiveTerminal(targetSessionId);
3457
+ preferredSessionId = targetSessionId;
3458
+ localStorage.setItem('preferredSessionId', targetSessionId);
3459
+ document.getElementById('session-display').textContent = targetSessionId.slice(-8);
3460
+ updateWorkspaceView();
3461
+ renderWorkers();
3462
+ if (isMobileViewport()) {
3463
+ closeWorkerSidebar();
3464
+ }
3465
+ if (sendMessage && ws && ws.readyState === WebSocket.OPEN) {
3466
+ sendControl('command', 'attach_session', { sessionId: targetSessionId }, { sessionId: targetSessionId });
3467
+ }
3468
+ const activeView = getActiveTerminalView();
3469
+ if (ws && ws.readyState === WebSocket.OPEN) {
3470
+ ws.send(JSON.stringify({
3471
+ type: 'resize',
3472
+ sessionId: targetSessionId,
3473
+ cols: activeView.term.cols,
3474
+ rows: activeView.term.rows
3475
+ }));
3476
+ requestWorkerList();
3477
+ }
3478
+ activeView.term.focus();
3479
+ }
3480
+
3481
+ function stopCurrentWorker() {
3482
+ if (!ws || ws.readyState !== WebSocket.OPEN || !sessionId || pendingStopSessionId === sessionId) return;
3483
+ const targetSessionId = sessionId;
3484
+ pendingStopSessionId = targetSessionId;
3485
+ updateCurrentWorkerBar();
3486
+ sendControl('command', 'stop_worker', { sessionId: targetSessionId }, { sessionId: targetSessionId });
3487
+ if (isMobileViewport()) {
3488
+ returnToPrimaryChat({ clearSession: true });
3489
+ }
3490
+ }
3491
+
3492
+ function restartCurrentWorker() {
3493
+ if (!ws || ws.readyState !== WebSocket.OPEN || !sessionId) return;
3494
+ pendingRestartSessionId = sessionId;
3495
+ updateCurrentWorkerBar();
3496
+ sendControl('command', 'restart_worker', { sessionId }, { sessionId });
3497
+ }
3498
+
3499
+ function inspectCurrentWorker() {
3500
+ if (!ws || ws.readyState !== WebSocket.OPEN || !sessionId) return;
3501
+ if (workerDetailOpen && workerDetail && workerDetail.worker?.sessionId === sessionId) {
3502
+ closeWorkerDetail();
3503
+ return;
3504
+ }
3505
+ workerDetailOpen = true;
3506
+ sendControl('query', 'get_worker_detail', { sessionId }, { sessionId });
3507
+ }
3508
+
3509
+ function refreshCurrentWorkerDetail() {
3510
+ if (!ws || ws.readyState !== WebSocket.OPEN || !sessionId || !workerDetailOpen) return;
3511
+ sendControl('query', 'get_worker_detail', { sessionId }, { sessionId });
3512
+ }
3513
+
3514
+ function resetSpawnButton() {
3515
+ const spawnButton = document.querySelector('.worker-add-btn');
3516
+ if (spawnButton) {
3517
+ spawnButton.disabled = false;
3518
+ spawnButton.textContent = '+';
3519
+ }
3520
+ }
3521
+
3522
+ function requestDirBrowse(path) {
3523
+ sendControl('query', 'browse_directories', { path: path || undefined }, path ? { path } : {});
3524
+ }
3525
+
3526
+ function renderDirBrowser() {
3527
+ const browser = document.getElementById('dir-browser');
3528
+ const pathNode = document.getElementById('dir-browser-path');
3529
+ const listNode = document.getElementById('dir-browser-list');
3530
+ const useButton = document.getElementById('useCwdBtn');
3531
+ if (!browser.classList.contains('active')) return;
3532
+ renderDirBreadcrumbs(pathNode, dirBrowserPath);
3533
+ if (useButton) {
3534
+ useButton.disabled = !dirBrowserPath;
3535
+ }
3536
+ if (!dirBrowserEntries.length) {
3537
+ listNode.innerHTML = '<div class="worker-dir-empty">No subdirectories here.</div>';
3538
+ return;
3539
+ }
3540
+ listNode.innerHTML = dirBrowserEntries.map((entry) => `
3541
+ <div class="worker-dir-item" onclick="browseDir(decodeURIComponent('${encodeURIComponent(entry.path)}'))">${escapeHtml(entry.name)}</div>
3542
+ `).join('');
3543
+ }
3544
+
3545
+ function renderDirBreadcrumbs(node, targetPath) {
3546
+ if (!targetPath) {
3547
+ node.textContent = 'Loading...';
3548
+ return;
3549
+ }
3550
+ const parts = targetPath.split('/').filter(Boolean);
3551
+ const crumbs = [];
3552
+ let current = targetPath.startsWith('/') ? '/' : '';
3553
+ if (targetPath.startsWith('/')) {
3554
+ crumbs.push({ label: '/', path: '/' });
3555
+ }
3556
+ for (const part of parts) {
3557
+ current = current === '/' ? `/${part}` : `${current}/${part}`;
3558
+ crumbs.push({ label: part, path: current });
3559
+ }
3560
+ node.innerHTML = crumbs.map((crumb, index) => `
3561
+ <span class="worker-dir-crumb" onclick="browseDir(decodeURIComponent('${encodeURIComponent(crumb.path)}'))">${escapeHtml(crumb.label)}</span>${index < crumbs.length - 1 ? '<span class="worker-dir-sep">/</span>' : ''}
3562
+ `).join('');
3563
+ }
3564
+
3565
+ function toggleDirBrowser() {
3566
+ const browser = document.getElementById('dir-browser');
3567
+ const useButton = document.getElementById('useCwdBtn');
3568
+ const willOpen = !browser.classList.contains('active');
3569
+ browser.classList.toggle('active', willOpen);
3570
+ if (useButton) {
3571
+ useButton.disabled = !willOpen || !dirBrowserPath;
3572
+ }
3573
+ if (willOpen) {
3574
+ requestDirBrowse(document.getElementById('spawnCwd').value.trim() || undefined);
3575
+ }
3576
+ }
3577
+
3578
+ function browseDir(targetPath) {
3579
+ requestDirBrowse(targetPath);
3580
+ }
3581
+
3582
+ function useBrowsedDir() {
3583
+ if (!dirBrowserPath) return;
3584
+ const input = document.getElementById('spawnCwd');
3585
+ const browser = document.getElementById('dir-browser');
3586
+ const useButton = document.getElementById('useCwdBtn');
3587
+ input.value = dirBrowserPath;
3588
+ defaultSpawnCwd = dirBrowserPath;
3589
+ localStorage.setItem('spawnCwd', defaultSpawnCwd);
3590
+ rememberCwd(dirBrowserPath);
3591
+ if (browser) {
3592
+ browser.classList.remove('active');
3593
+ }
3594
+ if (useButton) {
3595
+ useButton.disabled = true;
3596
+ }
3597
+ }
3598
+
3599
+ function connect() {
3600
+ const relayUrl = document.getElementById('relayUrl').value;
3601
+ const nodeId = document.getElementById('nodeId').value;
3602
+ const password = document.getElementById('password').value;
3603
+
3604
+ // Save credentials
3605
+ localStorage.setItem('relayUrl', relayUrl);
3606
+ localStorage.setItem('nodeId', nodeId);
3607
+ localStorage.setItem('password', password);
3608
+
3609
+ updateStatus('CONNECTING...', '#ff0');
3610
+
3611
+ destroyTerminalViews();
3612
+ ensureTerminalView('__system__');
3613
+ setActiveTerminal('__system__');
3614
+
3615
+ if (!transport) {
3616
+ transport = window.MagicShellTransport.createWorkbenchTransport({
3617
+ onSocketChange(nextSocket) {
3618
+ ws = nextSocket;
3619
+ },
3620
+ onOpen() {
3621
+ updateStatus('ONLINE', '#0f0');
3622
+ setDisconnectedState(false);
3623
+ setMobileControlMode('terminal');
3624
+ sessionId = null;
3625
+ document.getElementById('connectBtn').disabled = true;
3626
+ document.getElementById('connection-overlay').classList.add('hidden');
3627
+ document.getElementById('terminal').classList.add('active');
3628
+ document.getElementById('worker-sidebar').classList.add('active');
3629
+ fitAllTerminalViews();
3630
+ setTimeout(() => fitAllTerminalViews(), 50);
3631
+ startWorkerPolling();
3632
+ syncWorkerPluginPreference();
3633
+ syncMobileWorkbench();
3634
+ },
3635
+ onMessage(msg) {
3636
+ window.MagicShellRuntimeMessages.handleWorkbenchMessage(msg, {
3637
+ get sessionId() { return sessionId; },
3638
+ set sessionId(value) { sessionId = value; },
3639
+ get activeTerminalId() { return activeTerminalId; },
3640
+ get pendingNodeReply() { return pendingNodeReply; },
3641
+ set pendingNodeReply(value) { pendingNodeReply = value; },
3642
+ get workerDetail() { return workerDetail; },
3643
+ set workerDetail(value) { workerDetail = value; },
3644
+ get dirBrowserPath() { return dirBrowserPath; },
3645
+ set dirBrowserPath(value) { dirBrowserPath = value; },
3646
+ get dirBrowserParentPath() { return dirBrowserParentPath; },
3647
+ set dirBrowserParentPath(value) { dirBrowserParentPath = value; },
3648
+ get dirBrowserRepoRoot() { return dirBrowserRepoRoot; },
3649
+ set dirBrowserRepoRoot(value) { dirBrowserRepoRoot = value; },
3650
+ get dirBrowserEntries() { return dirBrowserEntries; },
3651
+ set dirBrowserEntries(value) { dirBrowserEntries = value; },
3652
+ ensureTerminalView,
3653
+ writeTerminal,
3654
+ writeTerminalLine,
3655
+ renderDirBrowser,
3656
+ requestWorkerList,
3657
+ renderWorkerDetailPanel,
3658
+ pushNodeTranscript,
3659
+ renderWorkers,
3660
+ syncWorkerPluginPreference,
3661
+ setSessionDisplay(value) {
3662
+ document.getElementById('session-display').textContent = value;
3663
+ },
3664
+ getRuntimeControlContext,
3665
+ });
3666
+ },
3667
+ onClose() {
3668
+ sessionId = null;
3669
+ workerDetail = null;
3670
+ preferredSessionId = localStorage.getItem('preferredSessionId');
3671
+ setMobileControlMode('terminal');
3672
+ pendingNodeReply = false;
3673
+ pendingSpawnSessionId = null;
3674
+ pendingStopSessionId = null;
3675
+ pendingRestartSessionId = null;
3676
+ resetSpawnButton();
3677
+ updateStatus('STANDBY', '#eaeaea');
3678
+ setDisconnectedState(true);
3679
+ writeTerminalLine('__system__', '\r\n[ CONNECTION CLOSED ]\r\n');
3680
+ renderNodeHome();
3681
+ stopWorkerPolling();
3682
+ document.getElementById('connectBtn').disabled = false;
3683
+ document.getElementById('connection-overlay').classList.remove('hidden');
3684
+ document.getElementById('terminal').classList.remove('active');
3685
+ document.getElementById('worker-sidebar').classList.remove('active');
3686
+ mobileSidebarOpen = false;
3687
+ workers = [];
3688
+ renderWorkers();
3689
+ document.getElementById('current-worker-bar').classList.remove('active');
3690
+ syncMobileWorkbench();
3691
+ },
3692
+ onError() {
3693
+ updateStatus('ERROR', '#f00');
3694
+ writeTerminalLine(activeTerminalId || '__system__', '\r\n[ CONNECTION ERROR ]\r\n');
3695
+ },
3696
+ });
3697
+ }
3698
+
3699
+ try {
3700
+ transport.connect({ relayUrl, nodeId, password });
3701
+ } catch (error) {
3702
+ alert(error instanceof Error ? error.message : String(error));
3703
+ updateStatus('STANDBY', '#eaeaea');
3704
+ }
3705
+ }
3706
+
3707
+ function disconnect() {
3708
+ if (transport) {
3709
+ transport.disconnect();
3710
+ }
3711
+ setMobileControlMode('terminal');
3712
+ updateStatus('STANDBY', '#eaeaea');
3713
+ setDisconnectedState(true);
3714
+ writeTerminalLine('__system__', '\r\n[ DISCONNECTED ]\r\n');
3715
+ }
3716
+
3717
+ function sendCommand(cmd) {
3718
+ const activeView = getActiveTerminalView();
3719
+ if (cmd === 'clear') {
3720
+ activeView.term.clear();
3721
+ }
3722
+ }
3723
+
3724
+ function zoomIn() {
3725
+ terminalFontSize += 2;
3726
+ getTerminalManager().updateFontSize(terminalFontSize);
3727
+ }
3728
+
3729
+ function zoomOut() {
3730
+ terminalFontSize = Math.max(10, terminalFontSize - 2);
3731
+ getTerminalManager().updateFontSize(terminalFontSize);
3732
+ }
3733
+
3734
+ function fitTerminal() {
3735
+ fitAllTerminalViews();
3736
+ }
3737
+
3738
+ function resetTerminal() {
3739
+ const activeView = getActiveTerminalView();
3740
+ activeView.term.reset();
3741
+ writeTerminalLine(getTerminalManager().getActiveId() || '__system__', '[ TERMINAL RESET ]\r\n');
3742
+ }
3743
+
3744
+ function installMobileTerminalGestures() {
3745
+ const terminal = document.getElementById('terminal');
3746
+ if (!terminal || terminal.dataset.mobileGesturesInstalled === '1') {
3747
+ return;
3748
+ }
3749
+ terminal.dataset.mobileGesturesInstalled = '1';
3750
+ const minDistance = 30;
3751
+
3752
+ terminal.addEventListener('touchstart', (event) => {
3753
+ if (!isMobileViewport() || mobileControlMode !== 'menu_nav' || event.touches.length !== 1) {
3754
+ mobileGestureState = null;
3755
+ return;
3756
+ }
3757
+ const touch = event.touches[0];
3758
+ mobileGestureState = {
3759
+ startX: touch.clientX,
3760
+ startY: touch.clientY,
3761
+ handled: false,
3762
+ };
3763
+ }, { passive: true });
3764
+
3765
+ terminal.addEventListener('touchmove', (event) => {
3766
+ if (!isMobileViewport() || mobileControlMode !== 'menu_nav' || !mobileGestureState || mobileGestureState.handled || event.touches.length !== 1) {
3767
+ return;
3768
+ }
3769
+ const touch = event.touches[0];
3770
+ const dx = touch.clientX - mobileGestureState.startX;
3771
+ const dy = touch.clientY - mobileGestureState.startY;
3772
+ if (Math.abs(dy) < minDistance || Math.abs(dy) <= Math.abs(dx)) {
3773
+ return;
3774
+ }
3775
+ mobileGestureState.handled = true;
3776
+ event.preventDefault();
3777
+ sendTerminalControl(dy < 0 ? 'up' : 'down');
3778
+ }, { passive: false });
3779
+
3780
+ terminal.addEventListener('touchend', () => {
3781
+ mobileGestureState = null;
3782
+ }, { passive: true });
3783
+
3784
+ terminal.addEventListener('touchcancel', () => {
3785
+ mobileGestureState = null;
3786
+ }, { passive: true });
3787
+ }
3788
+
3789
+ document.getElementById('spawnCwd').addEventListener('keydown', (event) => {
3790
+ if (event.key === 'Enter') {
3791
+ event.preventDefault();
3792
+ spawnPieWorker();
3793
+ }
3794
+ });
3795
+
3796
+ window.addEventListener('resize', () => {
3797
+ fitAllTerminalViews();
3798
+ syncMobileWorkbench();
3799
+ });
3800
+
3801
+ if (document.fonts && document.fonts.ready) {
3802
+ document.fonts.ready.then(() => {
3803
+ fitAllTerminalViews();
3804
+ }).catch(() => {});
3805
+ }
3806
+
3807
+ syncMobileWorkbench();
3808
+ installMobileSidebarGuards();
3809
+ installMobileTerminalGestures();
3810
+ renderMobileControlMode();
3811
+
3812
+ if (autoConnectParam === '1' || autoConnectParam === 'true') {
3813
+ requestAnimationFrame(() => {
3814
+ const relayUrl = document.getElementById('relayUrl').value.trim();
3815
+ const nodeId = document.getElementById('nodeId').value.trim();
3816
+ const password = document.getElementById('password').value.trim();
3817
+ if (relayUrl && nodeId && password) {
3818
+ connect();
3819
+ }
3820
+ });
3821
+ }
3822
+
3823
+ // Initial message in overlay
3824
+ console.log('[Magic Shell] Ready to connect');
3825
+ </script>
3826
+ </body>
3827
+ </html>