termbeam 1.11.1 → 1.12.1
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/package.json +4 -3
- package/public-react/assets/index-C6ZPMc5Z.js +108 -0
- package/public-react/assets/index-D7_i8rqw.css +32 -0
- package/public-react/index.html +27 -0
- package/public-react/manifest.webmanifest +1 -0
- package/public-react/registerSW.js +1 -0
- package/public-react/sw.js +2 -0
- package/src/auth.js +122 -7
- package/src/routes.js +24 -22
- package/src/sessions.js +31 -0
- package/src/websocket.js +101 -2
- package/public/css/themes.css +0 -217
- package/public/index.html +0 -1521
- package/public/js/keybar.js +0 -180
- package/public/js/search.js +0 -95
- package/public/js/shared.js +0 -39
- package/public/js/terminal-themes.js +0 -291
- package/public/js/themes.js +0 -54
- package/public/manifest.json +0 -14
- package/public/sw.js +0 -88
- package/public/terminal.html +0 -4837
- /package/{public → public-react}/icons/icon-192.png +0 -0
- /package/{public → public-react}/icons/icon-512.png +0 -0
- /package/{public → public-react}/icons/icon.svg +0 -0
package/src/websocket.js
CHANGED
|
@@ -6,8 +6,26 @@ const ACTIVE_THRESHOLD = 60000; // 60 seconds
|
|
|
6
6
|
// on replay: color queries trigger xterm.js to generate responses that echo
|
|
7
7
|
// through the PTY as visible text, accumulating on each refresh.
|
|
8
8
|
const OSC_COLOR_RE = /\x1b\](?:4;\d+|10|11|12);[^\x07\x1b]*(?:\x07|\x1b\\)/g;
|
|
9
|
+
|
|
10
|
+
// Alternate screen buffer sequences (DECSET/DECRST 1049, 1047, 47) cause
|
|
11
|
+
// screen wipes on replay: entering alt screen hides the normal buffer content
|
|
12
|
+
// and re-entering on replay makes the terminal appear blank/wiped.
|
|
13
|
+
// Matched enter+exit pairs are stripped along with their content (the alt
|
|
14
|
+
// screen output is no longer relevant after the program exits).
|
|
15
|
+
// Unmatched enters/exits are stripped as bare sequences.
|
|
16
|
+
const ALT_SCREEN_PAIR_RE = /\x1b\[\?(1049|1047|47)h[\s\S]*?\x1b\[\?\1l/g;
|
|
17
|
+
const ALT_SCREEN_BARE_RE = /\x1b\[\?(?:1049|1047|47)[hl]/g;
|
|
18
|
+
|
|
19
|
+
// Clear-scrollback (ESC[3J) is destructive on replay — it would wipe
|
|
20
|
+
// the xterm.js scrollback that the user might want to scroll through.
|
|
21
|
+
const CLEAR_SCROLLBACK_RE = /\x1b\[3J/g;
|
|
22
|
+
|
|
9
23
|
function sanitizeForReplay(buf) {
|
|
10
|
-
|
|
24
|
+
buf = buf.replace(OSC_COLOR_RE, '');
|
|
25
|
+
buf = buf.replace(ALT_SCREEN_PAIR_RE, '');
|
|
26
|
+
buf = buf.replace(ALT_SCREEN_BARE_RE, '');
|
|
27
|
+
buf = buf.replace(CLEAR_SCROLLBACK_RE, '');
|
|
28
|
+
return buf;
|
|
11
29
|
}
|
|
12
30
|
|
|
13
31
|
function recalcPtySize(session) {
|
|
@@ -137,6 +155,14 @@ function setupWebSocket(wss, { auth, sessions }) {
|
|
|
137
155
|
JSON.stringify({ type: 'output', data: sanitizeForReplay(session.scrollbackBuf) }),
|
|
138
156
|
);
|
|
139
157
|
}
|
|
158
|
+
// After replaying scrollback (which has alt-screen sequences
|
|
159
|
+
// stripped by sanitizeForReplay), re-enter alt-screen so xterm.js
|
|
160
|
+
// uses the correct buffer before SIGWINCH triggers the app to repaint.
|
|
161
|
+
if (session.inAltScreen) {
|
|
162
|
+
const mode = session.altScreenMode || '1049';
|
|
163
|
+
ws.send(JSON.stringify({ type: 'output', data: `\x1b[?${mode}h` }));
|
|
164
|
+
ws._needsRedraw = true;
|
|
165
|
+
}
|
|
140
166
|
}
|
|
141
167
|
ws.send(JSON.stringify({ type: 'attached', sessionId: msg.sessionId }));
|
|
142
168
|
log.info(`Client attached to session ${msg.sessionId}`);
|
|
@@ -176,9 +202,76 @@ function setupWebSocket(wss, { auth, sessions }) {
|
|
|
176
202
|
}),
|
|
177
203
|
);
|
|
178
204
|
}
|
|
205
|
+
// After replaying scrollback (which has alt-screen sequences
|
|
206
|
+
// stripped), re-enter alt-screen so xterm.js uses the correct
|
|
207
|
+
// buffer before SIGWINCH triggers the TUI to repaint.
|
|
208
|
+
if (attached.inAltScreen) {
|
|
209
|
+
const mode = attached.altScreenMode || '1049';
|
|
210
|
+
ws.send(JSON.stringify({ type: 'output', data: `\x1b[?${mode}h` }));
|
|
211
|
+
// Force SIGWINCH so the TUI repaints into the alt buffer
|
|
212
|
+
recalcPtySize(attached);
|
|
213
|
+
const targetCols = attached._lastCols || cols;
|
|
214
|
+
const targetRows = attached._lastRows || rows;
|
|
215
|
+
try {
|
|
216
|
+
const small = Math.min(500, targetCols === 1 ? 2 : targetCols - 1);
|
|
217
|
+
attached.pty.resize(small, targetRows);
|
|
218
|
+
attached._lastCols = small;
|
|
219
|
+
} catch {
|
|
220
|
+
// ignore — PTY may have exited
|
|
221
|
+
}
|
|
222
|
+
if (attached._resizeBounceTimer) {
|
|
223
|
+
clearTimeout(attached._resizeBounceTimer);
|
|
224
|
+
}
|
|
225
|
+
attached._resizeBounceTimer = setTimeout(() => {
|
|
226
|
+
attached._resizeBounceTimer = null;
|
|
227
|
+
try {
|
|
228
|
+
attached.pty.resize(targetCols, targetRows);
|
|
229
|
+
attached._lastCols = targetCols;
|
|
230
|
+
} catch {
|
|
231
|
+
// ignore — PTY may have exited
|
|
232
|
+
}
|
|
233
|
+
}, 50);
|
|
234
|
+
}
|
|
179
235
|
}
|
|
180
236
|
} else {
|
|
181
|
-
|
|
237
|
+
if (ws._needsRedraw) {
|
|
238
|
+
ws._needsRedraw = false;
|
|
239
|
+
// Force SIGWINCH so TUI apps (vim, copilot, htop) redraw
|
|
240
|
+
// after client reconnect. The ioctl only fires SIGWINCH when
|
|
241
|
+
// the size actually changes, and standard signals coalesce —
|
|
242
|
+
// so we must delay the restore to ensure the app queries
|
|
243
|
+
// the intermediate size before we resize back.
|
|
244
|
+
recalcPtySize(attached);
|
|
245
|
+
const targetCols = attached._lastCols || cols;
|
|
246
|
+
const targetRows = attached._lastRows || rows;
|
|
247
|
+
try {
|
|
248
|
+
const small = Math.min(500, targetCols === 1 ? 2 : targetCols - 1);
|
|
249
|
+
attached.pty.resize(small, targetRows);
|
|
250
|
+
attached._lastCols = small;
|
|
251
|
+
} catch {
|
|
252
|
+
// ignore — PTY may have exited
|
|
253
|
+
}
|
|
254
|
+
// Cancel any prior bounce timer to avoid stale restores
|
|
255
|
+
if (attached._resizeBounceTimer) {
|
|
256
|
+
clearTimeout(attached._resizeBounceTimer);
|
|
257
|
+
}
|
|
258
|
+
attached._resizeBounceTimer = setTimeout(() => {
|
|
259
|
+
attached._resizeBounceTimer = null;
|
|
260
|
+
try {
|
|
261
|
+
attached.pty.resize(targetCols, targetRows);
|
|
262
|
+
attached._lastCols = targetCols;
|
|
263
|
+
} catch {
|
|
264
|
+
// ignore — PTY may have exited
|
|
265
|
+
}
|
|
266
|
+
}, 50);
|
|
267
|
+
} else {
|
|
268
|
+
// Cancel any pending bounce timer from a prior redraw
|
|
269
|
+
if (attached._resizeBounceTimer) {
|
|
270
|
+
clearTimeout(attached._resizeBounceTimer);
|
|
271
|
+
attached._resizeBounceTimer = null;
|
|
272
|
+
}
|
|
273
|
+
recalcPtySize(attached);
|
|
274
|
+
}
|
|
182
275
|
}
|
|
183
276
|
}
|
|
184
277
|
}
|
|
@@ -190,6 +283,12 @@ function setupWebSocket(wss, { auth, sessions }) {
|
|
|
190
283
|
ws.on('close', () => {
|
|
191
284
|
clearInterval(pingInterval);
|
|
192
285
|
if (attached) {
|
|
286
|
+
if (attached._resizeBounceTimer) {
|
|
287
|
+
clearTimeout(attached._resizeBounceTimer);
|
|
288
|
+
attached._resizeBounceTimer = null;
|
|
289
|
+
// Restore PTY to correct size since the bounce was interrupted
|
|
290
|
+
recalcPtySize(attached);
|
|
291
|
+
}
|
|
193
292
|
attached.clients.delete(ws);
|
|
194
293
|
recalcPtySize(attached);
|
|
195
294
|
log.info('Client detached');
|
package/public/css/themes.css
DELETED
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
/* TermBeam shared theme variables — imported by index.html and terminal.html */
|
|
2
|
-
|
|
3
|
-
:root {
|
|
4
|
-
--bg: #1e1e1e;
|
|
5
|
-
--surface: #252526;
|
|
6
|
-
--border: #3c3c3c;
|
|
7
|
-
--border-subtle: #474747;
|
|
8
|
-
--text: #d4d4d4;
|
|
9
|
-
--text-secondary: #858585;
|
|
10
|
-
--text-dim: #6e6e6e;
|
|
11
|
-
--text-muted: #555555;
|
|
12
|
-
--accent: #0078d4;
|
|
13
|
-
--accent-hover: #1a8ae8;
|
|
14
|
-
--accent-active: #005a9e;
|
|
15
|
-
--danger: #f14c4c;
|
|
16
|
-
--danger-hover: #d73a3a;
|
|
17
|
-
--success: #89d185;
|
|
18
|
-
--overlay-bg: rgba(0, 0, 0, 0.85);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
[data-theme='light'] {
|
|
22
|
-
--bg: #ffffff;
|
|
23
|
-
--surface: #f3f3f3;
|
|
24
|
-
--border: #e0e0e0;
|
|
25
|
-
--border-subtle: #d0d0d0;
|
|
26
|
-
--text: #1e1e1e;
|
|
27
|
-
--text-secondary: #616161;
|
|
28
|
-
--text-dim: #767676;
|
|
29
|
-
--text-muted: #a0a0a0;
|
|
30
|
-
--accent: #0078d4;
|
|
31
|
-
--accent-hover: #106ebe;
|
|
32
|
-
--accent-active: #005a9e;
|
|
33
|
-
--danger: #e51400;
|
|
34
|
-
--danger-hover: #c20000;
|
|
35
|
-
--success: #16825d;
|
|
36
|
-
--overlay-bg: rgba(0, 0, 0, 0.5);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
[data-theme='monokai'] {
|
|
40
|
-
--bg: #272822;
|
|
41
|
-
--surface: #1e1f1c;
|
|
42
|
-
--border: #49483e;
|
|
43
|
-
--border-subtle: #5c5c4f;
|
|
44
|
-
--text: #f8f8f2;
|
|
45
|
-
--text-secondary: #a59f85;
|
|
46
|
-
--text-dim: #75715e;
|
|
47
|
-
--text-muted: #5a5854;
|
|
48
|
-
--accent: #a6e22e;
|
|
49
|
-
--accent-hover: #b8f53c;
|
|
50
|
-
--accent-active: #8acc16;
|
|
51
|
-
--danger: #f92672;
|
|
52
|
-
--danger-hover: #e0155d;
|
|
53
|
-
--success: #a6e22e;
|
|
54
|
-
--overlay-bg: rgba(0, 0, 0, 0.75);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
[data-theme='solarized-dark'] {
|
|
58
|
-
--bg: #002b36;
|
|
59
|
-
--surface: #073642;
|
|
60
|
-
--border: #586e75;
|
|
61
|
-
--border-subtle: #657b83;
|
|
62
|
-
--text: #839496;
|
|
63
|
-
--text-secondary: #657b83;
|
|
64
|
-
--text-dim: #586e75;
|
|
65
|
-
--text-muted: #4a5a62;
|
|
66
|
-
--accent: #268bd2;
|
|
67
|
-
--accent-hover: #379ce3;
|
|
68
|
-
--accent-active: #1a7abf;
|
|
69
|
-
--danger: #dc322f;
|
|
70
|
-
--danger-hover: #c8221f;
|
|
71
|
-
--success: #859900;
|
|
72
|
-
--overlay-bg: rgba(0, 0, 0, 0.75);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
[data-theme='solarized-light'] {
|
|
76
|
-
--bg: #fdf6e3;
|
|
77
|
-
--surface: #eee8d5;
|
|
78
|
-
--border: #93a1a1;
|
|
79
|
-
--border-subtle: #839496;
|
|
80
|
-
--text: #657b83;
|
|
81
|
-
--text-secondary: #93a1a1;
|
|
82
|
-
--text-dim: #a0a0a0;
|
|
83
|
-
--text-muted: #b0b0b0;
|
|
84
|
-
--accent: #268bd2;
|
|
85
|
-
--accent-hover: #379ce3;
|
|
86
|
-
--accent-active: #1a7abf;
|
|
87
|
-
--danger: #dc322f;
|
|
88
|
-
--danger-hover: #c8221f;
|
|
89
|
-
--success: #859900;
|
|
90
|
-
--overlay-bg: rgba(0, 0, 0, 0.4);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
[data-theme='nord'] {
|
|
94
|
-
--bg: #2e3440;
|
|
95
|
-
--surface: #3b4252;
|
|
96
|
-
--border: #434c5e;
|
|
97
|
-
--border-subtle: #4c566a;
|
|
98
|
-
--text: #d8dee9;
|
|
99
|
-
--text-secondary: #b0bac9;
|
|
100
|
-
--text-dim: #7b88a1;
|
|
101
|
-
--text-muted: #5c6a85;
|
|
102
|
-
--accent: #88c0d0;
|
|
103
|
-
--accent-hover: #9fd4e4;
|
|
104
|
-
--accent-active: #6aafbf;
|
|
105
|
-
--danger: #bf616a;
|
|
106
|
-
--danger-hover: #a84d57;
|
|
107
|
-
--success: #a3be8c;
|
|
108
|
-
--overlay-bg: rgba(0, 0, 0, 0.75);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
[data-theme='dracula'] {
|
|
112
|
-
--bg: #282a36;
|
|
113
|
-
--surface: #343746;
|
|
114
|
-
--border: #44475a;
|
|
115
|
-
--border-subtle: #525568;
|
|
116
|
-
--text: #f8f8f2;
|
|
117
|
-
--text-secondary: #c1c4d2;
|
|
118
|
-
--text-dim: #8e92a4;
|
|
119
|
-
--text-muted: #6272a4;
|
|
120
|
-
--accent: #bd93f9;
|
|
121
|
-
--accent-hover: #d0b0ff;
|
|
122
|
-
--accent-active: #a77de7;
|
|
123
|
-
--danger: #ff5555;
|
|
124
|
-
--danger-hover: #e03d3d;
|
|
125
|
-
--success: #50fa7b;
|
|
126
|
-
--overlay-bg: rgba(0, 0, 0, 0.75);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
[data-theme='github-dark'] {
|
|
130
|
-
--bg: #0d1117;
|
|
131
|
-
--surface: #161b22;
|
|
132
|
-
--border: #30363d;
|
|
133
|
-
--border-subtle: #3d444d;
|
|
134
|
-
--text: #c9d1d9;
|
|
135
|
-
--text-secondary: #8b949e;
|
|
136
|
-
--text-dim: #6e7681;
|
|
137
|
-
--text-muted: #484f58;
|
|
138
|
-
--accent: #58a6ff;
|
|
139
|
-
--accent-hover: #79b8ff;
|
|
140
|
-
--accent-active: #388bfd;
|
|
141
|
-
--danger: #f85149;
|
|
142
|
-
--danger-hover: #da3633;
|
|
143
|
-
--success: #3fb950;
|
|
144
|
-
--overlay-bg: rgba(0, 0, 0, 0.75);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
[data-theme='one-dark'] {
|
|
148
|
-
--bg: #282c34;
|
|
149
|
-
--surface: #21252b;
|
|
150
|
-
--border: #3e4452;
|
|
151
|
-
--border-subtle: #4b5263;
|
|
152
|
-
--text: #abb2bf;
|
|
153
|
-
--text-secondary: #7f848e;
|
|
154
|
-
--text-dim: #5c6370;
|
|
155
|
-
--text-muted: #4b5263;
|
|
156
|
-
--accent: #61afef;
|
|
157
|
-
--accent-hover: #7dc0ff;
|
|
158
|
-
--accent-active: #4d9ede;
|
|
159
|
-
--danger: #e06c75;
|
|
160
|
-
--danger-hover: #c95c67;
|
|
161
|
-
--success: #98c379;
|
|
162
|
-
--overlay-bg: rgba(0, 0, 0, 0.75);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
[data-theme='catppuccin'] {
|
|
166
|
-
--bg: #1e1e2e;
|
|
167
|
-
--surface: #313244;
|
|
168
|
-
--border: #45475a;
|
|
169
|
-
--border-subtle: #585b70;
|
|
170
|
-
--text: #cdd6f4;
|
|
171
|
-
--text-secondary: #a6adc8;
|
|
172
|
-
--text-dim: #7f849c;
|
|
173
|
-
--text-muted: #585b70;
|
|
174
|
-
--accent: #89b4fa;
|
|
175
|
-
--accent-hover: #b4d0ff;
|
|
176
|
-
--accent-active: #5c9de3;
|
|
177
|
-
--danger: #f38ba8;
|
|
178
|
-
--danger-hover: #eb7c9d;
|
|
179
|
-
--success: #a6e3a1;
|
|
180
|
-
--overlay-bg: rgba(0, 0, 0, 0.75);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
[data-theme='gruvbox'] {
|
|
184
|
-
--bg: #282828;
|
|
185
|
-
--surface: #3c3836;
|
|
186
|
-
--border: #504945;
|
|
187
|
-
--border-subtle: #665c54;
|
|
188
|
-
--text: #ebdbb2;
|
|
189
|
-
--text-secondary: #d5c4a1;
|
|
190
|
-
--text-dim: #a89984;
|
|
191
|
-
--text-muted: #7c6f64;
|
|
192
|
-
--accent: #83a598;
|
|
193
|
-
--accent-hover: #9dbfb4;
|
|
194
|
-
--accent-active: #6a8f8a;
|
|
195
|
-
--danger: #fb4934;
|
|
196
|
-
--danger-hover: #e33826;
|
|
197
|
-
--success: #b8bb26;
|
|
198
|
-
--overlay-bg: rgba(0, 0, 0, 0.75);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
[data-theme='night-owl'] {
|
|
202
|
-
--bg: #011627;
|
|
203
|
-
--surface: #0d2a45;
|
|
204
|
-
--border: #1d3b53;
|
|
205
|
-
--border-subtle: #264863;
|
|
206
|
-
--text: #d6deeb;
|
|
207
|
-
--text-secondary: #8badc1;
|
|
208
|
-
--text-dim: #5f7e97;
|
|
209
|
-
--text-muted: #3f5f7d;
|
|
210
|
-
--accent: #7fdbca;
|
|
211
|
-
--accent-hover: #9ff0e0;
|
|
212
|
-
--accent-active: #62c5b5;
|
|
213
|
-
--danger: #ef5350;
|
|
214
|
-
--danger-hover: #d83130;
|
|
215
|
-
--success: #addb67;
|
|
216
|
-
--overlay-bg: rgba(0, 0, 0, 0.75);
|
|
217
|
-
}
|