termbeam 1.2.10 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -2
- package/bin/termbeam.js +26 -1
- package/package.json +3 -3
- package/public/index.html +326 -33
- package/public/terminal.html +770 -37
- package/src/auth.js +135 -22
- package/src/cli.js +10 -1
- package/src/server.js +3 -4
- package/src/service.js +731 -0
package/public/terminal.html
CHANGED
|
@@ -63,6 +63,216 @@
|
|
|
63
63
|
--key-special-bg: #adb5bd;
|
|
64
64
|
--overlay-bg: rgba(0, 0, 0, 0.5);
|
|
65
65
|
}
|
|
66
|
+
[data-theme='monokai'] {
|
|
67
|
+
--bg: #272822;
|
|
68
|
+
--surface: #1e1f1c;
|
|
69
|
+
--border: #49483e;
|
|
70
|
+
--border-subtle: #5c5c4f;
|
|
71
|
+
--text: #f8f8f2;
|
|
72
|
+
--text-secondary: #a59f85;
|
|
73
|
+
--text-dim: #75715e;
|
|
74
|
+
--text-muted: #5a5854;
|
|
75
|
+
--accent: #a6e22e;
|
|
76
|
+
--accent-hover: #b8f53c;
|
|
77
|
+
--accent-active: #8acc16;
|
|
78
|
+
--danger: #f92672;
|
|
79
|
+
--danger-hover: #e0155d;
|
|
80
|
+
--success: #a6e22e;
|
|
81
|
+
--key-bg: #49483e;
|
|
82
|
+
--key-border: #5c5c4f;
|
|
83
|
+
--key-shadow: rgba(0, 0, 0, 0.4);
|
|
84
|
+
--key-special-bg: #3e3d32;
|
|
85
|
+
--overlay-bg: rgba(0, 0, 0, 0.75);
|
|
86
|
+
}
|
|
87
|
+
[data-theme='solarized-dark'] {
|
|
88
|
+
--bg: #002b36;
|
|
89
|
+
--surface: #073642;
|
|
90
|
+
--border: #586e75;
|
|
91
|
+
--border-subtle: #657b83;
|
|
92
|
+
--text: #839496;
|
|
93
|
+
--text-secondary: #657b83;
|
|
94
|
+
--text-dim: #586e75;
|
|
95
|
+
--text-muted: #4a5a62;
|
|
96
|
+
--accent: #268bd2;
|
|
97
|
+
--accent-hover: #379ce3;
|
|
98
|
+
--accent-active: #1a7abf;
|
|
99
|
+
--danger: #dc322f;
|
|
100
|
+
--danger-hover: #c8221f;
|
|
101
|
+
--success: #859900;
|
|
102
|
+
--key-bg: #073642;
|
|
103
|
+
--key-border: #586e75;
|
|
104
|
+
--key-shadow: rgba(0, 0, 0, 0.3);
|
|
105
|
+
--key-special-bg: #002b36;
|
|
106
|
+
--overlay-bg: rgba(0, 0, 0, 0.75);
|
|
107
|
+
}
|
|
108
|
+
[data-theme='solarized-light'] {
|
|
109
|
+
--bg: #fdf6e3;
|
|
110
|
+
--surface: #eee8d5;
|
|
111
|
+
--border: #93a1a1;
|
|
112
|
+
--border-subtle: #839496;
|
|
113
|
+
--text: #657b83;
|
|
114
|
+
--text-secondary: #93a1a1;
|
|
115
|
+
--text-dim: #a0a0a0;
|
|
116
|
+
--text-muted: #b0b0b0;
|
|
117
|
+
--accent: #268bd2;
|
|
118
|
+
--accent-hover: #379ce3;
|
|
119
|
+
--accent-active: #1a7abf;
|
|
120
|
+
--danger: #dc322f;
|
|
121
|
+
--danger-hover: #c8221f;
|
|
122
|
+
--success: #859900;
|
|
123
|
+
--key-bg: #ffffff;
|
|
124
|
+
--key-border: #b5b5b5;
|
|
125
|
+
--key-shadow: rgba(0, 0, 0, 0.12);
|
|
126
|
+
--key-special-bg: #adb5bd;
|
|
127
|
+
--overlay-bg: rgba(0, 0, 0, 0.4);
|
|
128
|
+
}
|
|
129
|
+
[data-theme='nord'] {
|
|
130
|
+
--bg: #2e3440;
|
|
131
|
+
--surface: #3b4252;
|
|
132
|
+
--border: #434c5e;
|
|
133
|
+
--border-subtle: #4c566a;
|
|
134
|
+
--text: #d8dee9;
|
|
135
|
+
--text-secondary: #b0bac9;
|
|
136
|
+
--text-dim: #7b88a1;
|
|
137
|
+
--text-muted: #5c6a85;
|
|
138
|
+
--accent: #88c0d0;
|
|
139
|
+
--accent-hover: #9fd4e4;
|
|
140
|
+
--accent-active: #6aafbf;
|
|
141
|
+
--danger: #bf616a;
|
|
142
|
+
--danger-hover: #a84d57;
|
|
143
|
+
--success: #a3be8c;
|
|
144
|
+
--key-bg: #434c5e;
|
|
145
|
+
--key-border: #4c566a;
|
|
146
|
+
--key-shadow: rgba(0, 0, 0, 0.3);
|
|
147
|
+
--key-special-bg: #3b4252;
|
|
148
|
+
--overlay-bg: rgba(0, 0, 0, 0.75);
|
|
149
|
+
}
|
|
150
|
+
[data-theme='dracula'] {
|
|
151
|
+
--bg: #282a36;
|
|
152
|
+
--surface: #343746;
|
|
153
|
+
--border: #44475a;
|
|
154
|
+
--border-subtle: #525568;
|
|
155
|
+
--text: #f8f8f2;
|
|
156
|
+
--text-secondary: #c1c4d2;
|
|
157
|
+
--text-dim: #8e92a4;
|
|
158
|
+
--text-muted: #6272a4;
|
|
159
|
+
--accent: #bd93f9;
|
|
160
|
+
--accent-hover: #d0b0ff;
|
|
161
|
+
--accent-active: #a77de7;
|
|
162
|
+
--danger: #ff5555;
|
|
163
|
+
--danger-hover: #e03d3d;
|
|
164
|
+
--success: #50fa7b;
|
|
165
|
+
--key-bg: #44475a;
|
|
166
|
+
--key-border: #525568;
|
|
167
|
+
--key-shadow: rgba(0, 0, 0, 0.4);
|
|
168
|
+
--key-special-bg: #343746;
|
|
169
|
+
--overlay-bg: rgba(0, 0, 0, 0.75);
|
|
170
|
+
}
|
|
171
|
+
[data-theme='github-dark'] {
|
|
172
|
+
--bg: #0d1117;
|
|
173
|
+
--surface: #161b22;
|
|
174
|
+
--border: #30363d;
|
|
175
|
+
--border-subtle: #3d444d;
|
|
176
|
+
--text: #c9d1d9;
|
|
177
|
+
--text-secondary: #8b949e;
|
|
178
|
+
--text-dim: #6e7681;
|
|
179
|
+
--text-muted: #484f58;
|
|
180
|
+
--accent: #58a6ff;
|
|
181
|
+
--accent-hover: #79b8ff;
|
|
182
|
+
--accent-active: #388bfd;
|
|
183
|
+
--danger: #f85149;
|
|
184
|
+
--danger-hover: #da3633;
|
|
185
|
+
--success: #3fb950;
|
|
186
|
+
--key-bg: #161b22;
|
|
187
|
+
--key-border: #30363d;
|
|
188
|
+
--key-shadow: rgba(0, 0, 0, 0.4);
|
|
189
|
+
--key-special-bg: #0d1117;
|
|
190
|
+
--overlay-bg: rgba(0, 0, 0, 0.75);
|
|
191
|
+
}
|
|
192
|
+
[data-theme='one-dark'] {
|
|
193
|
+
--bg: #282c34;
|
|
194
|
+
--surface: #21252b;
|
|
195
|
+
--border: #3e4452;
|
|
196
|
+
--border-subtle: #4b5263;
|
|
197
|
+
--text: #abb2bf;
|
|
198
|
+
--text-secondary: #7f848e;
|
|
199
|
+
--text-dim: #5c6370;
|
|
200
|
+
--text-muted: #4b5263;
|
|
201
|
+
--accent: #61afef;
|
|
202
|
+
--accent-hover: #7dc0ff;
|
|
203
|
+
--accent-active: #4d9ede;
|
|
204
|
+
--danger: #e06c75;
|
|
205
|
+
--danger-hover: #c95c67;
|
|
206
|
+
--success: #98c379;
|
|
207
|
+
--key-bg: #3e4452;
|
|
208
|
+
--key-border: #4b5263;
|
|
209
|
+
--key-shadow: rgba(0, 0, 0, 0.3);
|
|
210
|
+
--key-special-bg: #21252b;
|
|
211
|
+
--overlay-bg: rgba(0, 0, 0, 0.75);
|
|
212
|
+
}
|
|
213
|
+
[data-theme='catppuccin'] {
|
|
214
|
+
--bg: #1e1e2e;
|
|
215
|
+
--surface: #313244;
|
|
216
|
+
--border: #45475a;
|
|
217
|
+
--border-subtle: #585b70;
|
|
218
|
+
--text: #cdd6f4;
|
|
219
|
+
--text-secondary: #a6adc8;
|
|
220
|
+
--text-dim: #7f849c;
|
|
221
|
+
--text-muted: #585b70;
|
|
222
|
+
--accent: #89b4fa;
|
|
223
|
+
--accent-hover: #b4d0ff;
|
|
224
|
+
--accent-active: #5c9de3;
|
|
225
|
+
--danger: #f38ba8;
|
|
226
|
+
--danger-hover: #eb7c9d;
|
|
227
|
+
--success: #a6e3a1;
|
|
228
|
+
--key-bg: #45475a;
|
|
229
|
+
--key-border: #585b70;
|
|
230
|
+
--key-shadow: rgba(0, 0, 0, 0.3);
|
|
231
|
+
--key-special-bg: #313244;
|
|
232
|
+
--overlay-bg: rgba(0, 0, 0, 0.75);
|
|
233
|
+
}
|
|
234
|
+
[data-theme='gruvbox'] {
|
|
235
|
+
--bg: #282828;
|
|
236
|
+
--surface: #3c3836;
|
|
237
|
+
--border: #504945;
|
|
238
|
+
--border-subtle: #665c54;
|
|
239
|
+
--text: #ebdbb2;
|
|
240
|
+
--text-secondary: #d5c4a1;
|
|
241
|
+
--text-dim: #a89984;
|
|
242
|
+
--text-muted: #7c6f64;
|
|
243
|
+
--accent: #83a598;
|
|
244
|
+
--accent-hover: #9dbfb4;
|
|
245
|
+
--accent-active: #6a8f8a;
|
|
246
|
+
--danger: #fb4934;
|
|
247
|
+
--danger-hover: #e33826;
|
|
248
|
+
--success: #b8bb26;
|
|
249
|
+
--key-bg: #504945;
|
|
250
|
+
--key-border: #665c54;
|
|
251
|
+
--key-shadow: rgba(0, 0, 0, 0.4);
|
|
252
|
+
--key-special-bg: #3c3836;
|
|
253
|
+
--overlay-bg: rgba(0, 0, 0, 0.75);
|
|
254
|
+
}
|
|
255
|
+
[data-theme='night-owl'] {
|
|
256
|
+
--bg: #011627;
|
|
257
|
+
--surface: #0d2a45;
|
|
258
|
+
--border: #1d3b53;
|
|
259
|
+
--border-subtle: #264863;
|
|
260
|
+
--text: #d6deeb;
|
|
261
|
+
--text-secondary: #8badc1;
|
|
262
|
+
--text-dim: #5f7e97;
|
|
263
|
+
--text-muted: #3f5f7d;
|
|
264
|
+
--accent: #7fdbca;
|
|
265
|
+
--accent-hover: #9ff0e0;
|
|
266
|
+
--accent-active: #62c5b5;
|
|
267
|
+
--danger: #ef5350;
|
|
268
|
+
--danger-hover: #d83130;
|
|
269
|
+
--success: #addb67;
|
|
270
|
+
--key-bg: #1d3b53;
|
|
271
|
+
--key-border: #264863;
|
|
272
|
+
--key-shadow: rgba(0, 0, 0, 0.4);
|
|
273
|
+
--key-special-bg: #0d2a45;
|
|
274
|
+
--overlay-bg: rgba(0, 0, 0, 0.75);
|
|
275
|
+
}
|
|
66
276
|
@font-face {
|
|
67
277
|
font-family: 'NerdFont';
|
|
68
278
|
src: url('https://cdn.jsdelivr.net/gh/ryanoasis/nerd-fonts@latest/patched-fonts/JetBrainsMono/Ligatures/Regular/JetBrainsMonoNerdFont-Regular.ttf')
|
|
@@ -248,6 +458,23 @@
|
|
|
248
458
|
.tab-close:hover {
|
|
249
459
|
color: var(--danger);
|
|
250
460
|
}
|
|
461
|
+
.tab-unread {
|
|
462
|
+
width: 7px;
|
|
463
|
+
height: 7px;
|
|
464
|
+
border-radius: 50%;
|
|
465
|
+
background: var(--accent);
|
|
466
|
+
flex-shrink: 0;
|
|
467
|
+
animation: tab-pulse 1.5s ease-in-out infinite;
|
|
468
|
+
}
|
|
469
|
+
@keyframes tab-pulse {
|
|
470
|
+
0%,
|
|
471
|
+
100% {
|
|
472
|
+
opacity: 1;
|
|
473
|
+
}
|
|
474
|
+
50% {
|
|
475
|
+
opacity: 0.4;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
251
478
|
/* ===== Tab Preview ===== */
|
|
252
479
|
#tab-preview {
|
|
253
480
|
display: none;
|
|
@@ -382,6 +609,52 @@
|
|
|
382
609
|
font-size: 14px;
|
|
383
610
|
font-weight: 600;
|
|
384
611
|
}
|
|
612
|
+
.theme-wrap {
|
|
613
|
+
position: relative;
|
|
614
|
+
display: flex;
|
|
615
|
+
align-items: center;
|
|
616
|
+
}
|
|
617
|
+
.theme-picker {
|
|
618
|
+
display: none;
|
|
619
|
+
position: absolute;
|
|
620
|
+
top: calc(100% + 4px);
|
|
621
|
+
right: 0;
|
|
622
|
+
background: var(--surface);
|
|
623
|
+
border: 1px solid var(--border);
|
|
624
|
+
border-radius: 8px;
|
|
625
|
+
min-width: 160px;
|
|
626
|
+
padding: 4px 0;
|
|
627
|
+
z-index: 200;
|
|
628
|
+
box-shadow: 0 4px 12px var(--shadow);
|
|
629
|
+
}
|
|
630
|
+
.theme-picker.open {
|
|
631
|
+
display: block;
|
|
632
|
+
}
|
|
633
|
+
.theme-option {
|
|
634
|
+
display: flex;
|
|
635
|
+
align-items: center;
|
|
636
|
+
gap: 8px;
|
|
637
|
+
padding: 7px 12px;
|
|
638
|
+
cursor: pointer;
|
|
639
|
+
font-size: 13px;
|
|
640
|
+
color: var(--text);
|
|
641
|
+
transition: background 0.1s;
|
|
642
|
+
white-space: nowrap;
|
|
643
|
+
}
|
|
644
|
+
.theme-option:hover {
|
|
645
|
+
background: var(--border);
|
|
646
|
+
}
|
|
647
|
+
.theme-option.active {
|
|
648
|
+
color: var(--accent);
|
|
649
|
+
}
|
|
650
|
+
.theme-swatch {
|
|
651
|
+
width: 14px;
|
|
652
|
+
height: 14px;
|
|
653
|
+
border-radius: 50%;
|
|
654
|
+
display: inline-block;
|
|
655
|
+
flex-shrink: 0;
|
|
656
|
+
border: 1px solid rgba(128, 128, 128, 0.3);
|
|
657
|
+
}
|
|
385
658
|
#stop-btn {
|
|
386
659
|
background: none;
|
|
387
660
|
border: none;
|
|
@@ -467,6 +740,9 @@
|
|
|
467
740
|
[data-theme='light'] #key-bar {
|
|
468
741
|
background: #d1d3d9;
|
|
469
742
|
}
|
|
743
|
+
[data-theme='solarized-light'] #key-bar {
|
|
744
|
+
background: #d1d3d9;
|
|
745
|
+
}
|
|
470
746
|
.key-row {
|
|
471
747
|
display: flex;
|
|
472
748
|
align-items: center;
|
|
@@ -502,6 +778,10 @@
|
|
|
502
778
|
color: #000;
|
|
503
779
|
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2);
|
|
504
780
|
}
|
|
781
|
+
[data-theme='solarized-light'] .key-btn {
|
|
782
|
+
color: #000;
|
|
783
|
+
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2);
|
|
784
|
+
}
|
|
505
785
|
.key-btn:active {
|
|
506
786
|
background: #6e6e72;
|
|
507
787
|
transform: scale(0.95);
|
|
@@ -510,6 +790,9 @@
|
|
|
510
790
|
[data-theme='light'] .key-btn:active {
|
|
511
791
|
background: #c8c8cc;
|
|
512
792
|
}
|
|
793
|
+
[data-theme='solarized-light'] .key-btn:active {
|
|
794
|
+
background: #c8c8cc;
|
|
795
|
+
}
|
|
513
796
|
.key-btn.flash {
|
|
514
797
|
background: #fff !important;
|
|
515
798
|
color: #000 !important;
|
|
@@ -519,6 +802,10 @@
|
|
|
519
802
|
background: #333 !important;
|
|
520
803
|
color: #fff !important;
|
|
521
804
|
}
|
|
805
|
+
[data-theme='solarized-light'] .key-btn.flash {
|
|
806
|
+
background: #333 !important;
|
|
807
|
+
color: #fff !important;
|
|
808
|
+
}
|
|
522
809
|
.key-btn.modifier,
|
|
523
810
|
.key-btn.special {
|
|
524
811
|
background: var(--key-special-bg);
|
|
@@ -530,6 +817,11 @@
|
|
|
530
817
|
background: var(--key-special-bg);
|
|
531
818
|
color: #000;
|
|
532
819
|
}
|
|
820
|
+
[data-theme='solarized-light'] .key-btn.modifier,
|
|
821
|
+
[data-theme='solarized-light'] .key-btn.special {
|
|
822
|
+
background: var(--key-special-bg);
|
|
823
|
+
color: #000;
|
|
824
|
+
}
|
|
533
825
|
.key-btn.modifier.active {
|
|
534
826
|
background: var(--accent);
|
|
535
827
|
color: #fff;
|
|
@@ -563,6 +855,10 @@
|
|
|
563
855
|
background: #fee2e2;
|
|
564
856
|
color: #dc2626;
|
|
565
857
|
}
|
|
858
|
+
[data-theme='solarized-light'] .key-btn.key-danger {
|
|
859
|
+
background: #fee2e2;
|
|
860
|
+
color: #dc2626;
|
|
861
|
+
}
|
|
566
862
|
.key-btn.key-danger:active {
|
|
567
863
|
background: var(--danger);
|
|
568
864
|
color: #fff;
|
|
@@ -1381,7 +1677,7 @@
|
|
|
1381
1677
|
#back-btn {
|
|
1382
1678
|
display: none;
|
|
1383
1679
|
}
|
|
1384
|
-
#theme-
|
|
1680
|
+
#theme-wrap {
|
|
1385
1681
|
display: flex;
|
|
1386
1682
|
}
|
|
1387
1683
|
#stop-btn {
|
|
@@ -1572,28 +1868,66 @@
|
|
|
1572
1868
|
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15" />
|
|
1573
1869
|
</svg>
|
|
1574
1870
|
</button>
|
|
1575
|
-
<
|
|
1576
|
-
<
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
</
|
|
1596
|
-
|
|
1871
|
+
<div class="theme-wrap" id="theme-wrap">
|
|
1872
|
+
<button class="bar-btn" id="theme-toggle" title="Switch theme">
|
|
1873
|
+
<svg
|
|
1874
|
+
width="16"
|
|
1875
|
+
height="16"
|
|
1876
|
+
viewBox="0 0 24 24"
|
|
1877
|
+
fill="none"
|
|
1878
|
+
stroke="currentColor"
|
|
1879
|
+
stroke-width="2"
|
|
1880
|
+
stroke-linecap="round"
|
|
1881
|
+
stroke-linejoin="round"
|
|
1882
|
+
>
|
|
1883
|
+
<circle cx="13.5" cy="6.5" r=".5" fill="currentColor" />
|
|
1884
|
+
<circle cx="17.5" cy="10.5" r=".5" fill="currentColor" />
|
|
1885
|
+
<circle cx="8.5" cy="7.5" r=".5" fill="currentColor" />
|
|
1886
|
+
<circle cx="6.5" cy="12.5" r=".5" fill="currentColor" />
|
|
1887
|
+
<path
|
|
1888
|
+
d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10c.926 0 1.648-.746 1.648-1.688 0-.437-.18-.835-.437-1.125-.29-.289-.438-.652-.438-1.125a1.64 1.64 0 0 1 1.668-1.668h1.996c3.051 0 5.555-2.503 5.555-5.554C21.965 6.012 17.461 2 12 2z"
|
|
1889
|
+
/>
|
|
1890
|
+
</svg>
|
|
1891
|
+
</button>
|
|
1892
|
+
<div class="theme-picker" id="theme-picker">
|
|
1893
|
+
<div class="theme-option" data-theme-option="dark">
|
|
1894
|
+
<span class="theme-swatch" style="background: #1e1e1e"></span>Dark
|
|
1895
|
+
</div>
|
|
1896
|
+
<div class="theme-option" data-theme-option="light">
|
|
1897
|
+
<span class="theme-swatch" style="background: #ffffff"></span>Light
|
|
1898
|
+
</div>
|
|
1899
|
+
<div class="theme-option" data-theme-option="monokai">
|
|
1900
|
+
<span class="theme-swatch" style="background: #272822"></span>Monokai
|
|
1901
|
+
</div>
|
|
1902
|
+
<div class="theme-option" data-theme-option="solarized-dark">
|
|
1903
|
+
<span class="theme-swatch" style="background: #002b36"></span>Solarized Dark
|
|
1904
|
+
</div>
|
|
1905
|
+
<div class="theme-option" data-theme-option="solarized-light">
|
|
1906
|
+
<span class="theme-swatch" style="background: #fdf6e3"></span>Solarized Light
|
|
1907
|
+
</div>
|
|
1908
|
+
<div class="theme-option" data-theme-option="nord">
|
|
1909
|
+
<span class="theme-swatch" style="background: #2e3440"></span>Nord
|
|
1910
|
+
</div>
|
|
1911
|
+
<div class="theme-option" data-theme-option="dracula">
|
|
1912
|
+
<span class="theme-swatch" style="background: #282a36"></span>Dracula
|
|
1913
|
+
</div>
|
|
1914
|
+
<div class="theme-option" data-theme-option="github-dark">
|
|
1915
|
+
<span class="theme-swatch" style="background: #0d1117"></span>GitHub Dark
|
|
1916
|
+
</div>
|
|
1917
|
+
<div class="theme-option" data-theme-option="one-dark">
|
|
1918
|
+
<span class="theme-swatch" style="background: #282c34"></span>One Dark
|
|
1919
|
+
</div>
|
|
1920
|
+
<div class="theme-option" data-theme-option="catppuccin">
|
|
1921
|
+
<span class="theme-swatch" style="background: #1e1e2e"></span>Catppuccin
|
|
1922
|
+
</div>
|
|
1923
|
+
<div class="theme-option" data-theme-option="gruvbox">
|
|
1924
|
+
<span class="theme-swatch" style="background: #282828"></span>Gruvbox
|
|
1925
|
+
</div>
|
|
1926
|
+
<div class="theme-option" data-theme-option="night-owl">
|
|
1927
|
+
<span class="theme-swatch" style="background: #011627"></span>Night Owl
|
|
1928
|
+
</div>
|
|
1929
|
+
</div>
|
|
1930
|
+
</div>
|
|
1597
1931
|
<button id="stop-btn" title="Stop session">
|
|
1598
1932
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor" stroke="none">
|
|
1599
1933
|
<rect x="6" y="6" width="12" height="12" rx="2" /></svg
|
|
@@ -1970,29 +2304,296 @@
|
|
|
1970
2304
|
brightCyan: '#0598bc',
|
|
1971
2305
|
brightWhite: '#a5a5a5',
|
|
1972
2306
|
};
|
|
2307
|
+
const monokaiTermTheme = {
|
|
2308
|
+
background: '#272822',
|
|
2309
|
+
foreground: '#f8f8f2',
|
|
2310
|
+
cursor: '#f8f8f0',
|
|
2311
|
+
cursorAccent: '#272822',
|
|
2312
|
+
selectionBackground: 'rgba(166, 226, 46, 0.3)',
|
|
2313
|
+
black: '#272822',
|
|
2314
|
+
red: '#f92672',
|
|
2315
|
+
green: '#a6e22e',
|
|
2316
|
+
yellow: '#f4bf75',
|
|
2317
|
+
blue: '#66d9e8',
|
|
2318
|
+
magenta: '#ae81ff',
|
|
2319
|
+
cyan: '#a1efe4',
|
|
2320
|
+
white: '#f8f8f2',
|
|
2321
|
+
brightBlack: '#75715e',
|
|
2322
|
+
brightRed: '#f92672',
|
|
2323
|
+
brightGreen: '#a6e22e',
|
|
2324
|
+
brightYellow: '#f4bf75',
|
|
2325
|
+
brightBlue: '#66d9e8',
|
|
2326
|
+
brightMagenta: '#ae81ff',
|
|
2327
|
+
brightCyan: '#a1efe4',
|
|
2328
|
+
brightWhite: '#f9f8f5',
|
|
2329
|
+
};
|
|
2330
|
+
const solarizedDarkTermTheme = {
|
|
2331
|
+
background: '#002b36',
|
|
2332
|
+
foreground: '#839496',
|
|
2333
|
+
cursor: '#839496',
|
|
2334
|
+
cursorAccent: '#002b36',
|
|
2335
|
+
selectionBackground: 'rgba(7, 54, 66, 0.8)',
|
|
2336
|
+
black: '#073642',
|
|
2337
|
+
red: '#dc322f',
|
|
2338
|
+
green: '#859900',
|
|
2339
|
+
yellow: '#b58900',
|
|
2340
|
+
blue: '#268bd2',
|
|
2341
|
+
magenta: '#d33682',
|
|
2342
|
+
cyan: '#2aa198',
|
|
2343
|
+
white: '#eee8d5',
|
|
2344
|
+
brightBlack: '#002b36',
|
|
2345
|
+
brightRed: '#cb4b16',
|
|
2346
|
+
brightGreen: '#586e75',
|
|
2347
|
+
brightYellow: '#657b83',
|
|
2348
|
+
brightBlue: '#839496',
|
|
2349
|
+
brightMagenta: '#6c71c4',
|
|
2350
|
+
brightCyan: '#93a1a1',
|
|
2351
|
+
brightWhite: '#fdf6e3',
|
|
2352
|
+
};
|
|
2353
|
+
const solarizedLightTermTheme = {
|
|
2354
|
+
background: '#fdf6e3',
|
|
2355
|
+
foreground: '#657b83',
|
|
2356
|
+
cursor: '#586e75',
|
|
2357
|
+
cursorAccent: '#fdf6e3',
|
|
2358
|
+
selectionBackground: 'rgba(238, 232, 213, 0.8)',
|
|
2359
|
+
black: '#073642',
|
|
2360
|
+
red: '#dc322f',
|
|
2361
|
+
green: '#859900',
|
|
2362
|
+
yellow: '#b58900',
|
|
2363
|
+
blue: '#268bd2',
|
|
2364
|
+
magenta: '#d33682',
|
|
2365
|
+
cyan: '#2aa198',
|
|
2366
|
+
white: '#eee8d5',
|
|
2367
|
+
brightBlack: '#002b36',
|
|
2368
|
+
brightRed: '#cb4b16',
|
|
2369
|
+
brightGreen: '#586e75',
|
|
2370
|
+
brightYellow: '#657b83',
|
|
2371
|
+
brightBlue: '#839496',
|
|
2372
|
+
brightMagenta: '#6c71c4',
|
|
2373
|
+
brightCyan: '#93a1a1',
|
|
2374
|
+
brightWhite: '#fdf6e3',
|
|
2375
|
+
};
|
|
2376
|
+
const nordTermTheme = {
|
|
2377
|
+
background: '#2e3440',
|
|
2378
|
+
foreground: '#d8dee9',
|
|
2379
|
+
cursor: '#d8dee9',
|
|
2380
|
+
cursorAccent: '#2e3440',
|
|
2381
|
+
selectionBackground: 'rgba(67, 76, 94, 0.5)',
|
|
2382
|
+
black: '#3b4252',
|
|
2383
|
+
red: '#bf616a',
|
|
2384
|
+
green: '#a3be8c',
|
|
2385
|
+
yellow: '#ebcb8b',
|
|
2386
|
+
blue: '#81a1c1',
|
|
2387
|
+
magenta: '#b48ead',
|
|
2388
|
+
cyan: '#88c0d0',
|
|
2389
|
+
white: '#e5e9f0',
|
|
2390
|
+
brightBlack: '#4c566a',
|
|
2391
|
+
brightRed: '#bf616a',
|
|
2392
|
+
brightGreen: '#a3be8c',
|
|
2393
|
+
brightYellow: '#ebcb8b',
|
|
2394
|
+
brightBlue: '#81a1c1',
|
|
2395
|
+
brightMagenta: '#b48ead',
|
|
2396
|
+
brightCyan: '#8fbcbb',
|
|
2397
|
+
brightWhite: '#eceff4',
|
|
2398
|
+
};
|
|
2399
|
+
const draculaTermTheme = {
|
|
2400
|
+
background: '#282a36',
|
|
2401
|
+
foreground: '#f8f8f2',
|
|
2402
|
+
cursor: '#f8f8f2',
|
|
2403
|
+
cursorAccent: '#282a36',
|
|
2404
|
+
selectionBackground: 'rgba(68, 71, 90, 0.7)',
|
|
2405
|
+
black: '#21222c',
|
|
2406
|
+
red: '#ff5555',
|
|
2407
|
+
green: '#50fa7b',
|
|
2408
|
+
yellow: '#f1fa8c',
|
|
2409
|
+
blue: '#bd93f9',
|
|
2410
|
+
magenta: '#ff79c6',
|
|
2411
|
+
cyan: '#8be9fd',
|
|
2412
|
+
white: '#f8f8f2',
|
|
2413
|
+
brightBlack: '#6272a4',
|
|
2414
|
+
brightRed: '#ff6e6e',
|
|
2415
|
+
brightGreen: '#69ff94',
|
|
2416
|
+
brightYellow: '#ffffa5',
|
|
2417
|
+
brightBlue: '#d6acff',
|
|
2418
|
+
brightMagenta: '#ff92df',
|
|
2419
|
+
brightCyan: '#a4ffff',
|
|
2420
|
+
brightWhite: '#ffffff',
|
|
2421
|
+
};
|
|
2422
|
+
const githubDarkTermTheme = {
|
|
2423
|
+
background: '#0d1117',
|
|
2424
|
+
foreground: '#c9d1d9',
|
|
2425
|
+
cursor: '#c9d1d9',
|
|
2426
|
+
cursorAccent: '#0d1117',
|
|
2427
|
+
selectionBackground: 'rgba(56, 139, 253, 0.3)',
|
|
2428
|
+
black: '#484f58',
|
|
2429
|
+
red: '#ff7b72',
|
|
2430
|
+
green: '#3fb950',
|
|
2431
|
+
yellow: '#d29922',
|
|
2432
|
+
blue: '#58a6ff',
|
|
2433
|
+
magenta: '#bc8cff',
|
|
2434
|
+
cyan: '#39c5cf',
|
|
2435
|
+
white: '#c9d1d9',
|
|
2436
|
+
brightBlack: '#6e7681',
|
|
2437
|
+
brightRed: '#ffa198',
|
|
2438
|
+
brightGreen: '#56d364',
|
|
2439
|
+
brightYellow: '#e3b341',
|
|
2440
|
+
brightBlue: '#79c0ff',
|
|
2441
|
+
brightMagenta: '#d2a8ff',
|
|
2442
|
+
brightCyan: '#56d4dd',
|
|
2443
|
+
brightWhite: '#f0f6fc',
|
|
2444
|
+
};
|
|
2445
|
+
const oneDarkTermTheme = {
|
|
2446
|
+
background: '#282c34',
|
|
2447
|
+
foreground: '#abb2bf',
|
|
2448
|
+
cursor: '#528bff',
|
|
2449
|
+
cursorAccent: '#282c34',
|
|
2450
|
+
selectionBackground: 'rgba(62, 68, 82, 0.7)',
|
|
2451
|
+
black: '#3f4451',
|
|
2452
|
+
red: '#e06c75',
|
|
2453
|
+
green: '#98c379',
|
|
2454
|
+
yellow: '#e5c07b',
|
|
2455
|
+
blue: '#61afef',
|
|
2456
|
+
magenta: '#c678dd',
|
|
2457
|
+
cyan: '#56b6c2',
|
|
2458
|
+
white: '#d7dae0',
|
|
2459
|
+
brightBlack: '#4f5666',
|
|
2460
|
+
brightRed: '#e06c75',
|
|
2461
|
+
brightGreen: '#98c379',
|
|
2462
|
+
brightYellow: '#e5c07b',
|
|
2463
|
+
brightBlue: '#61afef',
|
|
2464
|
+
brightMagenta: '#c678dd',
|
|
2465
|
+
brightCyan: '#56b6c2',
|
|
2466
|
+
brightWhite: '#ffffff',
|
|
2467
|
+
};
|
|
2468
|
+
const catppuccinTermTheme = {
|
|
2469
|
+
background: '#1e1e2e',
|
|
2470
|
+
foreground: '#cdd6f4',
|
|
2471
|
+
cursor: '#f5e0dc',
|
|
2472
|
+
cursorAccent: '#1e1e2e',
|
|
2473
|
+
selectionBackground: 'rgba(88, 91, 112, 0.5)',
|
|
2474
|
+
black: '#45475a',
|
|
2475
|
+
red: '#f38ba8',
|
|
2476
|
+
green: '#a6e3a1',
|
|
2477
|
+
yellow: '#f9e2af',
|
|
2478
|
+
blue: '#89b4fa',
|
|
2479
|
+
magenta: '#f5c2e7',
|
|
2480
|
+
cyan: '#94e2d5',
|
|
2481
|
+
white: '#bac2de',
|
|
2482
|
+
brightBlack: '#585b70',
|
|
2483
|
+
brightRed: '#f38ba8',
|
|
2484
|
+
brightGreen: '#a6e3a1',
|
|
2485
|
+
brightYellow: '#f9e2af',
|
|
2486
|
+
brightBlue: '#89b4fa',
|
|
2487
|
+
brightMagenta: '#f5c2e7',
|
|
2488
|
+
brightCyan: '#94e2d5',
|
|
2489
|
+
brightWhite: '#a6adc8',
|
|
2490
|
+
};
|
|
2491
|
+
const gruvboxTermTheme = {
|
|
2492
|
+
background: '#282828',
|
|
2493
|
+
foreground: '#ebdbb2',
|
|
2494
|
+
cursor: '#ebdbb2',
|
|
2495
|
+
cursorAccent: '#282828',
|
|
2496
|
+
selectionBackground: 'rgba(80, 73, 69, 0.7)',
|
|
2497
|
+
black: '#282828',
|
|
2498
|
+
red: '#cc241d',
|
|
2499
|
+
green: '#98971a',
|
|
2500
|
+
yellow: '#d79921',
|
|
2501
|
+
blue: '#458588',
|
|
2502
|
+
magenta: '#b16286',
|
|
2503
|
+
cyan: '#689d6a',
|
|
2504
|
+
white: '#a89984',
|
|
2505
|
+
brightBlack: '#928374',
|
|
2506
|
+
brightRed: '#fb4934',
|
|
2507
|
+
brightGreen: '#b8bb26',
|
|
2508
|
+
brightYellow: '#fabd2f',
|
|
2509
|
+
brightBlue: '#83a598',
|
|
2510
|
+
brightMagenta: '#d3869b',
|
|
2511
|
+
brightCyan: '#8ec07c',
|
|
2512
|
+
brightWhite: '#ebdbb2',
|
|
2513
|
+
};
|
|
2514
|
+
const nightOwlTermTheme = {
|
|
2515
|
+
background: '#011627',
|
|
2516
|
+
foreground: '#d6deeb',
|
|
2517
|
+
cursor: '#80a4c2',
|
|
2518
|
+
cursorAccent: '#011627',
|
|
2519
|
+
selectionBackground: 'rgba(29, 59, 83, 0.7)',
|
|
2520
|
+
black: '#010e1a',
|
|
2521
|
+
red: '#ef5350',
|
|
2522
|
+
green: '#22da6e',
|
|
2523
|
+
yellow: '#addb67',
|
|
2524
|
+
blue: '#82aaff',
|
|
2525
|
+
magenta: '#c792ea',
|
|
2526
|
+
cyan: '#21c7a8',
|
|
2527
|
+
white: '#d6deeb',
|
|
2528
|
+
brightBlack: '#575656',
|
|
2529
|
+
brightRed: '#ef5350',
|
|
2530
|
+
brightGreen: '#22da6e',
|
|
2531
|
+
brightYellow: '#ffeb95',
|
|
2532
|
+
brightBlue: '#82aaff',
|
|
2533
|
+
brightMagenta: '#c792ea',
|
|
2534
|
+
brightCyan: '#7fdbca',
|
|
2535
|
+
brightWhite: '#ffffff',
|
|
2536
|
+
};
|
|
2537
|
+
const TERM_THEMES = {
|
|
2538
|
+
dark: darkTermTheme,
|
|
2539
|
+
light: lightTermTheme,
|
|
2540
|
+
monokai: monokaiTermTheme,
|
|
2541
|
+
'solarized-dark': solarizedDarkTermTheme,
|
|
2542
|
+
'solarized-light': solarizedLightTermTheme,
|
|
2543
|
+
nord: nordTermTheme,
|
|
2544
|
+
dracula: draculaTermTheme,
|
|
2545
|
+
'github-dark': githubDarkTermTheme,
|
|
2546
|
+
'one-dark': oneDarkTermTheme,
|
|
2547
|
+
catppuccin: catppuccinTermTheme,
|
|
2548
|
+
gruvbox: gruvboxTermTheme,
|
|
2549
|
+
'night-owl': nightOwlTermTheme,
|
|
2550
|
+
};
|
|
1973
2551
|
|
|
1974
2552
|
// ===== Theme =====
|
|
2553
|
+
const THEMES = [
|
|
2554
|
+
{ id: 'dark', name: 'Dark', bg: '#1e1e1e' },
|
|
2555
|
+
{ id: 'light', name: 'Light', bg: '#f3f3f3' },
|
|
2556
|
+
{ id: 'monokai', name: 'Monokai', bg: '#272822' },
|
|
2557
|
+
{ id: 'solarized-dark', name: 'Solarized Dark', bg: '#002b36' },
|
|
2558
|
+
{ id: 'solarized-light', name: 'Solarized Light', bg: '#fdf6e3' },
|
|
2559
|
+
{ id: 'nord', name: 'Nord', bg: '#2e3440' },
|
|
2560
|
+
{ id: 'dracula', name: 'Dracula', bg: '#282a36' },
|
|
2561
|
+
{ id: 'github-dark', name: 'GitHub Dark', bg: '#0d1117' },
|
|
2562
|
+
{ id: 'one-dark', name: 'One Dark', bg: '#282c34' },
|
|
2563
|
+
{ id: 'catppuccin', name: 'Catppuccin', bg: '#1e1e2e' },
|
|
2564
|
+
{ id: 'gruvbox', name: 'Gruvbox', bg: '#282828' },
|
|
2565
|
+
{ id: 'night-owl', name: 'Night Owl', bg: '#011627' },
|
|
2566
|
+
];
|
|
1975
2567
|
function getTheme() {
|
|
1976
2568
|
return localStorage.getItem('termbeam-theme') || 'dark';
|
|
1977
2569
|
}
|
|
1978
2570
|
function applyTheme(theme) {
|
|
1979
2571
|
document.documentElement.setAttribute('data-theme', theme);
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
const btn = document.getElementById('theme-toggle');
|
|
1983
|
-
if (btn)
|
|
1984
|
-
btn.innerHTML =
|
|
1985
|
-
theme === 'light'
|
|
1986
|
-
? '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>'
|
|
1987
|
-
: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg>';
|
|
2572
|
+
const t = THEMES.find((x) => x.id === theme) || THEMES[0];
|
|
2573
|
+
document.querySelector('meta[name="theme-color"]').content = t.bg;
|
|
1988
2574
|
localStorage.setItem('termbeam-theme', theme);
|
|
2575
|
+
document.querySelectorAll('.theme-option').forEach((el) => {
|
|
2576
|
+
el.classList.toggle('active', el.dataset.themeOption === theme);
|
|
2577
|
+
});
|
|
2578
|
+
const termTheme = TERM_THEMES[theme] || darkTermTheme;
|
|
1989
2579
|
for (const [, ms] of managed) {
|
|
1990
|
-
ms.term.options.theme =
|
|
2580
|
+
ms.term.options.theme = termTheme;
|
|
1991
2581
|
}
|
|
1992
2582
|
}
|
|
1993
2583
|
applyTheme(getTheme());
|
|
1994
|
-
document.getElementById('theme-toggle').addEventListener('click', () => {
|
|
1995
|
-
|
|
2584
|
+
document.getElementById('theme-toggle').addEventListener('click', (e) => {
|
|
2585
|
+
e.stopPropagation();
|
|
2586
|
+
document.getElementById('theme-picker').classList.toggle('open');
|
|
2587
|
+
});
|
|
2588
|
+
document.addEventListener('click', () => {
|
|
2589
|
+
document.getElementById('theme-picker').classList.remove('open');
|
|
2590
|
+
});
|
|
2591
|
+
document.querySelectorAll('.theme-option').forEach((el) => {
|
|
2592
|
+
el.addEventListener('click', (e) => {
|
|
2593
|
+
e.stopPropagation();
|
|
2594
|
+
applyTheme(el.dataset.themeOption);
|
|
2595
|
+
document.getElementById('theme-picker').classList.remove('open');
|
|
2596
|
+
});
|
|
1996
2597
|
});
|
|
1997
2598
|
|
|
1998
2599
|
// ===== Font Loading (non-blocking) =====
|
|
@@ -2049,8 +2650,18 @@
|
|
|
2049
2650
|
|
|
2050
2651
|
// ===== Zoom =====
|
|
2051
2652
|
const MIN_FONT = 2,
|
|
2052
|
-
MAX_FONT =
|
|
2053
|
-
|
|
2653
|
+
MAX_FONT = 32;
|
|
2654
|
+
function defaultFontSize() {
|
|
2655
|
+
const w = window.innerWidth;
|
|
2656
|
+
if (w <= 480) return 12;
|
|
2657
|
+
if (w <= 768) return 13;
|
|
2658
|
+
if (w <= 1280) return 14;
|
|
2659
|
+
return 15;
|
|
2660
|
+
}
|
|
2661
|
+
let fontSize = parseInt(
|
|
2662
|
+
localStorage.getItem('termbeam-fontsize') || String(defaultFontSize()),
|
|
2663
|
+
10,
|
|
2664
|
+
);
|
|
2054
2665
|
|
|
2055
2666
|
function applyZoom(size) {
|
|
2056
2667
|
fontSize = Math.max(MIN_FONT, Math.min(MAX_FONT, size));
|
|
@@ -2126,6 +2737,62 @@
|
|
|
2126
2737
|
.getElementById('zoom-out')
|
|
2127
2738
|
.addEventListener('click', () => applyZoom(fontSize - 2));
|
|
2128
2739
|
|
|
2740
|
+
// Pinch-to-zoom
|
|
2741
|
+
(function setupPinchZoom() {
|
|
2742
|
+
const wrapper = document.getElementById('terminals-wrapper');
|
|
2743
|
+
let pinchStartDist = 0;
|
|
2744
|
+
let pinchStartFont = 0;
|
|
2745
|
+
let pinchActive = false;
|
|
2746
|
+
let pinchZoomTimer = null;
|
|
2747
|
+
|
|
2748
|
+
function touchDist(t) {
|
|
2749
|
+
const dx = t[0].clientX - t[1].clientX;
|
|
2750
|
+
const dy = t[0].clientY - t[1].clientY;
|
|
2751
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
2752
|
+
}
|
|
2753
|
+
|
|
2754
|
+
wrapper.addEventListener(
|
|
2755
|
+
'touchstart',
|
|
2756
|
+
function (e) {
|
|
2757
|
+
if (e.touches.length === 2) {
|
|
2758
|
+
pinchActive = true;
|
|
2759
|
+
pinchStartDist = touchDist(e.touches);
|
|
2760
|
+
pinchStartFont = fontSize;
|
|
2761
|
+
wrapper.style.touchAction = 'none';
|
|
2762
|
+
}
|
|
2763
|
+
},
|
|
2764
|
+
{ passive: true },
|
|
2765
|
+
);
|
|
2766
|
+
|
|
2767
|
+
wrapper.addEventListener(
|
|
2768
|
+
'touchmove',
|
|
2769
|
+
function (e) {
|
|
2770
|
+
if (!pinchActive || e.touches.length !== 2) return;
|
|
2771
|
+
e.preventDefault();
|
|
2772
|
+
const dist = touchDist(e.touches);
|
|
2773
|
+
const scale = dist / pinchStartDist;
|
|
2774
|
+
const newSize = Math.round(pinchStartFont * scale);
|
|
2775
|
+
if (newSize !== fontSize) {
|
|
2776
|
+
if (pinchZoomTimer) clearTimeout(pinchZoomTimer);
|
|
2777
|
+
pinchZoomTimer = setTimeout(function () {
|
|
2778
|
+
applyZoom(newSize);
|
|
2779
|
+
}, 50);
|
|
2780
|
+
}
|
|
2781
|
+
},
|
|
2782
|
+
{ passive: false },
|
|
2783
|
+
);
|
|
2784
|
+
|
|
2785
|
+
wrapper.addEventListener('touchend', function () {
|
|
2786
|
+
pinchActive = false;
|
|
2787
|
+
wrapper.style.touchAction = '';
|
|
2788
|
+
});
|
|
2789
|
+
|
|
2790
|
+
wrapper.addEventListener('touchcancel', function () {
|
|
2791
|
+
pinchActive = false;
|
|
2792
|
+
wrapper.style.touchAction = '';
|
|
2793
|
+
});
|
|
2794
|
+
})();
|
|
2795
|
+
|
|
2129
2796
|
// Resize
|
|
2130
2797
|
function doResize() {
|
|
2131
2798
|
for (const [, ms] of managed) {
|
|
@@ -2203,6 +2870,7 @@
|
|
|
2203
2870
|
// Scroll to bottom when returning from idle / tab switch
|
|
2204
2871
|
document.addEventListener('visibilitychange', () => {
|
|
2205
2872
|
if (!document.hidden && activeId) {
|
|
2873
|
+
clearUnreadIndicator();
|
|
2206
2874
|
const ms = managed.get(activeId);
|
|
2207
2875
|
if (ms) {
|
|
2208
2876
|
ms.term.scrollToBottom();
|
|
@@ -2265,7 +2933,7 @@
|
|
|
2265
2933
|
fontWeightBold: 'bold',
|
|
2266
2934
|
letterSpacing: 0,
|
|
2267
2935
|
lineHeight: 1.1,
|
|
2268
|
-
theme: getTheme()
|
|
2936
|
+
theme: TERM_THEMES[getTheme()] || darkTermTheme,
|
|
2269
2937
|
allowProposedApi: true,
|
|
2270
2938
|
scrollback: 10000,
|
|
2271
2939
|
scrollOnOutput: false,
|
|
@@ -2462,6 +3130,53 @@
|
|
|
2462
3130
|
return ms;
|
|
2463
3131
|
}
|
|
2464
3132
|
|
|
3133
|
+
// Tab title activity indicator for unread output
|
|
3134
|
+
const originalTitle = document.title;
|
|
3135
|
+
let hasUnread = false;
|
|
3136
|
+
|
|
3137
|
+
// Shared AudioContext — must be created/resumed on user gesture for mobile
|
|
3138
|
+
let notifAudioCtx = null;
|
|
3139
|
+
function ensureAudioContext() {
|
|
3140
|
+
if (!notifAudioCtx) {
|
|
3141
|
+
notifAudioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
|
3142
|
+
}
|
|
3143
|
+
if (notifAudioCtx.state === 'suspended') notifAudioCtx.resume();
|
|
3144
|
+
return notifAudioCtx;
|
|
3145
|
+
}
|
|
3146
|
+
document.addEventListener('click', ensureAudioContext, { once: true });
|
|
3147
|
+
document.addEventListener('touchstart', ensureAudioContext, { once: true });
|
|
3148
|
+
|
|
3149
|
+
function playNotificationSound() {
|
|
3150
|
+
try {
|
|
3151
|
+
const ctx = ensureAudioContext();
|
|
3152
|
+
const osc = ctx.createOscillator();
|
|
3153
|
+
const gain = ctx.createGain();
|
|
3154
|
+
osc.connect(gain);
|
|
3155
|
+
gain.connect(ctx.destination);
|
|
3156
|
+
osc.frequency.value = 440;
|
|
3157
|
+
osc.type = 'sine';
|
|
3158
|
+
gain.gain.setValueAtTime(0.15, ctx.currentTime);
|
|
3159
|
+
gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.15);
|
|
3160
|
+
osc.start(ctx.currentTime);
|
|
3161
|
+
osc.stop(ctx.currentTime + 0.15);
|
|
3162
|
+
} catch {
|
|
3163
|
+
// Audio not available
|
|
3164
|
+
}
|
|
3165
|
+
}
|
|
3166
|
+
|
|
3167
|
+
function markUnreadOutput() {
|
|
3168
|
+
if (!document.hidden || hasUnread) return;
|
|
3169
|
+
hasUnread = true;
|
|
3170
|
+
playNotificationSound();
|
|
3171
|
+
document.title = '(\u25CF) ' + originalTitle;
|
|
3172
|
+
}
|
|
3173
|
+
|
|
3174
|
+
function clearUnreadIndicator() {
|
|
3175
|
+
if (!hasUnread) return;
|
|
3176
|
+
hasUnread = false;
|
|
3177
|
+
document.title = originalTitle;
|
|
3178
|
+
}
|
|
3179
|
+
|
|
2465
3180
|
function connectSession(ms) {
|
|
2466
3181
|
if (ms.reconnectTimer) {
|
|
2467
3182
|
clearTimeout(ms.reconnectTimer);
|
|
@@ -2496,6 +3211,12 @@
|
|
|
2496
3211
|
if (msg.type === 'output') {
|
|
2497
3212
|
ms.coalescedWrite(msg.data);
|
|
2498
3213
|
ms.lastActivity = Date.now();
|
|
3214
|
+
markUnreadOutput();
|
|
3215
|
+
if (ms.id !== activeId && !ms.hasUnread) {
|
|
3216
|
+
ms.hasUnread = true;
|
|
3217
|
+
playNotificationSound();
|
|
3218
|
+
renderTabs();
|
|
3219
|
+
}
|
|
2499
3220
|
} else if (msg.type === 'attached') {
|
|
2500
3221
|
if (ms.container.classList.contains('visible')) {
|
|
2501
3222
|
sendResize(ms);
|
|
@@ -2557,6 +3278,8 @@
|
|
|
2557
3278
|
function activateSession(id) {
|
|
2558
3279
|
if (!managed.has(id)) return;
|
|
2559
3280
|
activeId = id;
|
|
3281
|
+
const ms = managed.get(id);
|
|
3282
|
+
if (ms) ms.hasUnread = false;
|
|
2560
3283
|
reconnectOverlay.classList.remove('visible');
|
|
2561
3284
|
|
|
2562
3285
|
// Show/hide panes
|
|
@@ -2671,6 +3394,7 @@
|
|
|
2671
3394
|
esc(ms.name) +
|
|
2672
3395
|
'</span>' +
|
|
2673
3396
|
(activity ? '<span class="tab-activity">' + activity + '</span>' : '') +
|
|
3397
|
+
(ms.hasUnread && !isActive ? '<span class="tab-unread"></span>' : '') +
|
|
2674
3398
|
'<span class="tab-status" style="background:' +
|
|
2675
3399
|
safeColor(statusColor) +
|
|
2676
3400
|
'"></span>' +
|
|
@@ -2760,6 +3484,14 @@
|
|
|
2760
3484
|
activateSession(tab.dataset.id);
|
|
2761
3485
|
});
|
|
2762
3486
|
|
|
3487
|
+
// Middle-click to close tab
|
|
3488
|
+
tab.addEventListener('auxclick', (e) => {
|
|
3489
|
+
if (e.button === 1) {
|
|
3490
|
+
e.preventDefault();
|
|
3491
|
+
if (confirm('Close this session?')) removeSession(tab.dataset.id);
|
|
3492
|
+
}
|
|
3493
|
+
});
|
|
3494
|
+
|
|
2763
3495
|
// Desktop: hover preview (non-active tabs only)
|
|
2764
3496
|
tab.addEventListener('mouseenter', () => {
|
|
2765
3497
|
if (tab.dataset.id === activeId) return;
|
|
@@ -2822,6 +3554,7 @@
|
|
|
2822
3554
|
'<span class="side-panel-card-name">' +
|
|
2823
3555
|
esc(ms.name) +
|
|
2824
3556
|
'</span>' +
|
|
3557
|
+
(ms.hasUnread && !isActive ? '<span class="tab-unread"></span>' : '') +
|
|
2825
3558
|
'<span class="side-panel-card-status" style="background:' +
|
|
2826
3559
|
safeColor(statusColor) +
|
|
2827
3560
|
'"></span>' +
|