termbeam 1.5.0 → 1.7.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 +2 -32
- package/package.json +1 -1
- package/public/css/themes.css +217 -0
- package/public/index.html +42 -242
- package/public/js/keybar.js +180 -0
- package/public/js/search.js +95 -0
- package/public/js/shared.js +39 -0
- package/public/js/terminal-themes.js +291 -0
- package/public/js/themes.js +54 -0
- package/public/terminal.html +74 -873
- package/src/git.js +125 -0
- package/src/sessions.js +86 -1
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
TermBeam lets you access your terminal from a phone, tablet, or any browser — no SSH, no port forwarding, no config files. Run one command and scan the QR code.
|
|
17
17
|
|
|
18
|
-
I built this because I kept needing to run quick commands on my dev machine while away from my desk, and SSH on a phone is painful. TermBeam gives you a real terminal with a touch-
|
|
18
|
+
I built this because I kept needing to run quick commands on my dev machine while away from my desk, and SSH on a phone is painful. TermBeam gives you a real terminal with a touch-optimized UI — key bar, swipe scroll, pinch zoom — that actually works on small screens. You get multi-session tabs with split view, terminal search, a command palette, 12 themes, and secure remote access out of the box.
|
|
19
19
|
|
|
20
20
|
[Full documentation](https://dorlugasigal.github.io/TermBeam/) · [Website](https://termbeam.pages.dev)
|
|
21
21
|
|
|
@@ -26,8 +26,8 @@ https://github.com/user-attachments/assets/9dd4f3d7-f017-4314-9b3a-f6a5688e3671
|
|
|
26
26
|
<table align="center">
|
|
27
27
|
<tr>
|
|
28
28
|
<td align="center"><img src="docs/assets/screenshots/mobile-session-hub.jpeg" alt="Session hub on mobile" width="250" /></td>
|
|
29
|
-
<td align="center"><img src="docs/assets/screenshots/mobile-session-preview.jpeg" alt="Session preview on mobile" width="250" /></td>
|
|
30
29
|
<td align="center"><img src="docs/assets/screenshots/mobile-terminal.jpeg" alt="Terminal on mobile" width="250" /></td>
|
|
30
|
+
<td align="center"><img src="docs/assets/screenshots/mobile-session-preview.jpeg" alt="Session preview on mobile" width="250" /></td>
|
|
31
31
|
</tr>
|
|
32
32
|
</table>
|
|
33
33
|
|
|
@@ -57,36 +57,6 @@ termbeam --no-tunnel # LAN-only (no tunnel)
|
|
|
57
57
|
termbeam --no-password # disable password protection
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
-
## Features
|
|
61
|
-
|
|
62
|
-
- **Mobile-first UI** with on-screen touch bar (arrow keys, Tab, Enter, Ctrl shortcuts, Esc) and touch-optimized controls
|
|
63
|
-
- **Copy/paste support** — Copy button opens text overlay for finger-selectable terminal content; Paste button with clipboard API + fallback modal
|
|
64
|
-
- **Image paste** — paste images from clipboard, uploaded to server
|
|
65
|
-
- **Tabbed multi-session terminal** — open, switch, and manage multiple sessions from a single tab bar with drag-to-reorder
|
|
66
|
-
- **Split view** — view two sessions side-by-side (horizontal on desktop, vertical on mobile)
|
|
67
|
-
- **Session colors** — assign a color to each session for quick identification
|
|
68
|
-
- **Activity indicators** — see how recently each session had output (e.g. "3s ago", "5m ago")
|
|
69
|
-
- **Tab previews** — hover (desktop) or long-press (mobile) a tab to preview the last few lines of output
|
|
70
|
-
- **Side panel** (mobile) — slide-out session list with output previews for quick switching
|
|
71
|
-
- **Create sessions anywhere** — new session modal available from both the hub page and the terminal page
|
|
72
|
-
- **Touch scrolling** — swipe to scroll through terminal history
|
|
73
|
-
- **Share button** — share the TermBeam URL via Web Share API, clipboard, or legacy copy fallback (works over HTTP); each share gets a fresh auto-login link with a 5-minute share token
|
|
74
|
-
- **QR code auto-login** — scan the QR code to log in automatically without typing the password (share token, 5-minute expiry)
|
|
75
|
-
- **Refresh button** — clear PWA/service worker cache and reload to get the latest version
|
|
76
|
-
- **iPhone PWA safe area** — full support for `viewport-fit=cover` and safe area insets on notched devices
|
|
77
|
-
- **Password auth** with token-based cookies and rate-limited login
|
|
78
|
-
- **Folder browser** to pick working directories without typing paths
|
|
79
|
-
- **Initial command** — optionally launch a session straight into `htop`, `vim`, or any command
|
|
80
|
-
- **Shell detection** — auto-detects your shell on all platforms (PowerShell, cmd, bash, zsh, Git Bash, WSL)
|
|
81
|
-
- **QR code on startup** for instant phone connection
|
|
82
|
-
- **Command completion notifications** — get browser notifications when a command finishes in a background tab; toggle with the bell icon in the toolbar (opt-in, requires browser permission)
|
|
83
|
-
- **Terminal search** — press <kbd>Ctrl+F</kbd> / <kbd>Cmd+F</kbd> to open a search overlay with regex support, powered by xterm.js SearchAddon
|
|
84
|
-
- **Command palette** — press <kbd>Ctrl+K</kbd> / <kbd>Cmd+K</kbd> (or tap the ⚙️ button) to open a slide-out tool panel with categorized actions: Session, Search, View, Share, Notifications, and System
|
|
85
|
-
- **Light/dark theme** with persistent preference
|
|
86
|
-
- **Adjustable font size** via status bar controls, saved across sessions
|
|
87
|
-
- **Port preview** — reverse-proxy a single local web server port and preview it in the browser (HTTP only; no WebSocket/HMR; best with server-rendered apps)
|
|
88
|
-
- **Remote access via [DevTunnel](#remote-access)** — ephemeral or persisted public URLs
|
|
89
|
-
|
|
90
60
|
## Remote Access
|
|
91
61
|
|
|
92
62
|
```bash
|
package/package.json
CHANGED
|
@@ -0,0 +1,217 @@
|
|
|
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
|
+
}
|
package/public/index.html
CHANGED
|
@@ -15,235 +15,56 @@
|
|
|
15
15
|
/>
|
|
16
16
|
<link rel="manifest" href="/manifest.json" />
|
|
17
17
|
<link rel="apple-touch-icon" href="/icons/icon-192.png" />
|
|
18
|
+
<link rel="stylesheet" href="/css/themes.css" />
|
|
18
19
|
<title>TermBeam — Beam Your Terminal to Any Device</title>
|
|
19
20
|
<style>
|
|
20
21
|
:root {
|
|
21
|
-
--bg: #1e1e1e;
|
|
22
|
-
--surface: #252526;
|
|
23
|
-
--border: #3c3c3c;
|
|
24
|
-
--border-subtle: #474747;
|
|
25
|
-
--text: #d4d4d4;
|
|
26
|
-
--text-secondary: #858585;
|
|
27
|
-
--text-dim: #6e6e6e;
|
|
28
|
-
--text-muted: #5a5a5a;
|
|
29
|
-
--accent: #0078d4;
|
|
30
|
-
--accent-hover: #1a8ae8;
|
|
31
|
-
--accent-active: #005a9e;
|
|
32
|
-
--danger: #f14c4c;
|
|
33
|
-
--danger-hover: #d73a3a;
|
|
34
|
-
--success: #89d185;
|
|
35
22
|
--info: #b0b0b0;
|
|
36
23
|
--shadow: rgba(0, 0, 0, 0.15);
|
|
37
|
-
--overlay-bg: rgba(0, 0, 0, 0.7);
|
|
38
24
|
}
|
|
39
25
|
[data-theme='light'] {
|
|
40
|
-
--bg: #ffffff;
|
|
41
|
-
--surface: #f3f3f3;
|
|
42
|
-
--border: #e0e0e0;
|
|
43
|
-
--border-subtle: #d0d0d0;
|
|
44
|
-
--text: #1e1e1e;
|
|
45
|
-
--text-secondary: #616161;
|
|
46
|
-
--text-dim: #767676;
|
|
47
|
-
--text-muted: #a0a0a0;
|
|
48
|
-
--accent: #0078d4;
|
|
49
|
-
--accent-hover: #106ebe;
|
|
50
|
-
--accent-active: #005a9e;
|
|
51
|
-
--danger: #e51400;
|
|
52
|
-
--danger-hover: #c20000;
|
|
53
|
-
--success: #16825d;
|
|
54
26
|
--info: #616161;
|
|
55
27
|
--shadow: rgba(0, 0, 0, 0.06);
|
|
56
|
-
--overlay-bg: rgba(0, 0, 0, 0.4);
|
|
57
28
|
}
|
|
58
29
|
[data-theme='monokai'] {
|
|
59
|
-
--bg: #272822;
|
|
60
|
-
--surface: #1e1f1c;
|
|
61
|
-
--border: #49483e;
|
|
62
|
-
--border-subtle: #5c5c4f;
|
|
63
|
-
--text: #f8f8f2;
|
|
64
|
-
--text-secondary: #a59f85;
|
|
65
|
-
--text-dim: #75715e;
|
|
66
|
-
--text-muted: #5a5854;
|
|
67
|
-
--accent: #a6e22e;
|
|
68
|
-
--accent-hover: #b8f53c;
|
|
69
|
-
--accent-active: #8acc16;
|
|
70
|
-
--danger: #f92672;
|
|
71
|
-
--danger-hover: #e0155d;
|
|
72
|
-
--success: #a6e22e;
|
|
73
30
|
--info: #a59f85;
|
|
74
31
|
--shadow: rgba(0, 0, 0, 0.3);
|
|
75
|
-
--overlay-bg: rgba(0, 0, 0, 0.75);
|
|
76
32
|
}
|
|
77
33
|
[data-theme='solarized-dark'] {
|
|
78
|
-
--bg: #002b36;
|
|
79
|
-
--surface: #073642;
|
|
80
|
-
--border: #586e75;
|
|
81
|
-
--border-subtle: #657b83;
|
|
82
|
-
--text: #839496;
|
|
83
|
-
--text-secondary: #657b83;
|
|
84
|
-
--text-dim: #586e75;
|
|
85
|
-
--text-muted: #4a5a62;
|
|
86
|
-
--accent: #268bd2;
|
|
87
|
-
--accent-hover: #379ce3;
|
|
88
|
-
--accent-active: #1a7abf;
|
|
89
|
-
--danger: #dc322f;
|
|
90
|
-
--danger-hover: #c8221f;
|
|
91
|
-
--success: #859900;
|
|
92
34
|
--info: #657b83;
|
|
93
35
|
--shadow: rgba(0, 0, 0, 0.25);
|
|
94
|
-
--overlay-bg: rgba(0, 0, 0, 0.75);
|
|
95
36
|
}
|
|
96
37
|
[data-theme='solarized-light'] {
|
|
97
|
-
--bg: #fdf6e3;
|
|
98
|
-
--surface: #eee8d5;
|
|
99
|
-
--border: #93a1a1;
|
|
100
|
-
--border-subtle: #839496;
|
|
101
|
-
--text: #657b83;
|
|
102
|
-
--text-secondary: #93a1a1;
|
|
103
|
-
--text-dim: #a0a0a0;
|
|
104
|
-
--text-muted: #b0b0b0;
|
|
105
|
-
--accent: #268bd2;
|
|
106
|
-
--accent-hover: #379ce3;
|
|
107
|
-
--accent-active: #1a7abf;
|
|
108
|
-
--danger: #dc322f;
|
|
109
|
-
--danger-hover: #c8221f;
|
|
110
|
-
--success: #859900;
|
|
111
38
|
--info: #93a1a1;
|
|
112
39
|
--shadow: rgba(0, 0, 0, 0.08);
|
|
113
|
-
--overlay-bg: rgba(0, 0, 0, 0.4);
|
|
114
40
|
}
|
|
115
41
|
[data-theme='nord'] {
|
|
116
|
-
--bg: #2e3440;
|
|
117
|
-
--surface: #3b4252;
|
|
118
|
-
--border: #434c5e;
|
|
119
|
-
--border-subtle: #4c566a;
|
|
120
|
-
--text: #d8dee9;
|
|
121
|
-
--text-secondary: #b0bac9;
|
|
122
|
-
--text-dim: #7b88a1;
|
|
123
|
-
--text-muted: #5c6a85;
|
|
124
|
-
--accent: #88c0d0;
|
|
125
|
-
--accent-hover: #9fd4e4;
|
|
126
|
-
--accent-active: #6aafbf;
|
|
127
|
-
--danger: #bf616a;
|
|
128
|
-
--danger-hover: #a84d57;
|
|
129
|
-
--success: #a3be8c;
|
|
130
42
|
--info: #b0bac9;
|
|
131
43
|
--shadow: rgba(0, 0, 0, 0.2);
|
|
132
|
-
--overlay-bg: rgba(0, 0, 0, 0.75);
|
|
133
44
|
}
|
|
134
45
|
[data-theme='dracula'] {
|
|
135
|
-
--bg: #282a36;
|
|
136
|
-
--surface: #343746;
|
|
137
|
-
--border: #44475a;
|
|
138
|
-
--border-subtle: #525568;
|
|
139
|
-
--text: #f8f8f2;
|
|
140
|
-
--text-secondary: #c1c4d2;
|
|
141
|
-
--text-dim: #8e92a4;
|
|
142
|
-
--text-muted: #6272a4;
|
|
143
|
-
--accent: #bd93f9;
|
|
144
|
-
--accent-hover: #d0b0ff;
|
|
145
|
-
--accent-active: #a77de7;
|
|
146
|
-
--danger: #ff5555;
|
|
147
|
-
--danger-hover: #e03d3d;
|
|
148
|
-
--success: #50fa7b;
|
|
149
46
|
--info: #c1c4d2;
|
|
150
47
|
--shadow: rgba(0, 0, 0, 0.25);
|
|
151
|
-
--overlay-bg: rgba(0, 0, 0, 0.75);
|
|
152
48
|
}
|
|
153
49
|
[data-theme='github-dark'] {
|
|
154
|
-
--bg: #0d1117;
|
|
155
|
-
--surface: #161b22;
|
|
156
|
-
--border: #30363d;
|
|
157
|
-
--border-subtle: #3d444d;
|
|
158
|
-
--text: #c9d1d9;
|
|
159
|
-
--text-secondary: #8b949e;
|
|
160
|
-
--text-dim: #6e7681;
|
|
161
|
-
--text-muted: #484f58;
|
|
162
|
-
--accent: #58a6ff;
|
|
163
|
-
--accent-hover: #79b8ff;
|
|
164
|
-
--accent-active: #388bfd;
|
|
165
|
-
--danger: #f85149;
|
|
166
|
-
--danger-hover: #da3633;
|
|
167
|
-
--success: #3fb950;
|
|
168
50
|
--info: #8b949e;
|
|
169
51
|
--shadow: rgba(0, 0, 0, 0.3);
|
|
170
|
-
--overlay-bg: rgba(0, 0, 0, 0.75);
|
|
171
52
|
}
|
|
172
53
|
[data-theme='one-dark'] {
|
|
173
|
-
--bg: #282c34;
|
|
174
|
-
--surface: #21252b;
|
|
175
|
-
--border: #3e4452;
|
|
176
|
-
--border-subtle: #4b5263;
|
|
177
|
-
--text: #abb2bf;
|
|
178
|
-
--text-secondary: #7f848e;
|
|
179
|
-
--text-dim: #5c6370;
|
|
180
|
-
--text-muted: #4b5263;
|
|
181
|
-
--accent: #61afef;
|
|
182
|
-
--accent-hover: #7dc0ff;
|
|
183
|
-
--accent-active: #4d9ede;
|
|
184
|
-
--danger: #e06c75;
|
|
185
|
-
--danger-hover: #c95c67;
|
|
186
|
-
--success: #98c379;
|
|
187
54
|
--info: #7f848e;
|
|
188
55
|
--shadow: rgba(0, 0, 0, 0.25);
|
|
189
|
-
--overlay-bg: rgba(0, 0, 0, 0.75);
|
|
190
56
|
}
|
|
191
57
|
[data-theme='catppuccin'] {
|
|
192
|
-
--bg: #1e1e2e;
|
|
193
|
-
--surface: #313244;
|
|
194
|
-
--border: #45475a;
|
|
195
|
-
--border-subtle: #585b70;
|
|
196
|
-
--text: #cdd6f4;
|
|
197
|
-
--text-secondary: #a6adc8;
|
|
198
|
-
--text-dim: #7f849c;
|
|
199
|
-
--text-muted: #585b70;
|
|
200
|
-
--accent: #89b4fa;
|
|
201
|
-
--accent-hover: #b4d0ff;
|
|
202
|
-
--accent-active: #5c9de3;
|
|
203
|
-
--danger: #f38ba8;
|
|
204
|
-
--danger-hover: #eb7c9d;
|
|
205
|
-
--success: #a6e3a1;
|
|
206
58
|
--info: #a6adc8;
|
|
207
59
|
--shadow: rgba(0, 0, 0, 0.2);
|
|
208
|
-
--overlay-bg: rgba(0, 0, 0, 0.75);
|
|
209
60
|
}
|
|
210
61
|
[data-theme='gruvbox'] {
|
|
211
|
-
--bg: #282828;
|
|
212
|
-
--surface: #3c3836;
|
|
213
|
-
--border: #504945;
|
|
214
|
-
--border-subtle: #665c54;
|
|
215
|
-
--text: #ebdbb2;
|
|
216
|
-
--text-secondary: #d5c4a1;
|
|
217
|
-
--text-dim: #a89984;
|
|
218
|
-
--text-muted: #7c6f64;
|
|
219
|
-
--accent: #83a598;
|
|
220
|
-
--accent-hover: #9dbfb4;
|
|
221
|
-
--accent-active: #6a8f8a;
|
|
222
|
-
--danger: #fb4934;
|
|
223
|
-
--danger-hover: #e33826;
|
|
224
|
-
--success: #b8bb26;
|
|
225
62
|
--info: #d5c4a1;
|
|
226
63
|
--shadow: rgba(0, 0, 0, 0.25);
|
|
227
|
-
--overlay-bg: rgba(0, 0, 0, 0.75);
|
|
228
64
|
}
|
|
229
65
|
[data-theme='night-owl'] {
|
|
230
|
-
--bg: #011627;
|
|
231
|
-
--surface: #0d2a45;
|
|
232
|
-
--border: #1d3b53;
|
|
233
|
-
--border-subtle: #264863;
|
|
234
|
-
--text: #d6deeb;
|
|
235
|
-
--text-secondary: #8badc1;
|
|
236
|
-
--text-dim: #5f7e97;
|
|
237
|
-
--text-muted: #3f5f7d;
|
|
238
|
-
--accent: #7fdbca;
|
|
239
|
-
--accent-hover: #9ff0e0;
|
|
240
|
-
--accent-active: #62c5b5;
|
|
241
|
-
--danger: #ef5350;
|
|
242
|
-
--danger-hover: #d83130;
|
|
243
|
-
--success: #addb67;
|
|
244
66
|
--info: #8badc1;
|
|
245
67
|
--shadow: rgba(0, 0, 0, 0.3);
|
|
246
|
-
--overlay-bg: rgba(0, 0, 0, 0.75);
|
|
247
68
|
}
|
|
248
69
|
* {
|
|
249
70
|
margin: 0;
|
|
@@ -507,6 +328,32 @@
|
|
|
507
328
|
transform: scale(0.95);
|
|
508
329
|
}
|
|
509
330
|
|
|
331
|
+
.session-card .git-info {
|
|
332
|
+
display: flex;
|
|
333
|
+
flex-wrap: wrap;
|
|
334
|
+
gap: 4px 10px;
|
|
335
|
+
font-size: 12px;
|
|
336
|
+
color: var(--text-secondary);
|
|
337
|
+
align-items: center;
|
|
338
|
+
}
|
|
339
|
+
.session-card .git-info .git-badge {
|
|
340
|
+
display: inline-flex;
|
|
341
|
+
align-items: center;
|
|
342
|
+
gap: 4px;
|
|
343
|
+
background: var(--bg);
|
|
344
|
+
padding: 2px 8px;
|
|
345
|
+
border-radius: 4px;
|
|
346
|
+
transition:
|
|
347
|
+
background 0.3s,
|
|
348
|
+
color 0.3s;
|
|
349
|
+
}
|
|
350
|
+
.session-card .git-info .git-status-clean {
|
|
351
|
+
color: var(--success);
|
|
352
|
+
}
|
|
353
|
+
.session-card .git-info .git-status-dirty {
|
|
354
|
+
color: var(--warning, #fbbf24);
|
|
355
|
+
}
|
|
356
|
+
|
|
510
357
|
.new-session {
|
|
511
358
|
position: fixed;
|
|
512
359
|
bottom: calc(16px + env(safe-area-inset-bottom, 0px));
|
|
@@ -1121,46 +968,12 @@
|
|
|
1121
968
|
</div>
|
|
1122
969
|
</div>
|
|
1123
970
|
|
|
971
|
+
<script src="/js/shared.js"></script>
|
|
972
|
+
<script src="/js/themes.js"></script>
|
|
1124
973
|
<script>
|
|
1125
|
-
//
|
|
1126
|
-
const THEMES = [
|
|
1127
|
-
{ id: 'dark', name: 'Dark', bg: '#1e1e1e' },
|
|
1128
|
-
{ id: 'light', name: 'Light', bg: '#f3f3f3' },
|
|
1129
|
-
{ id: 'monokai', name: 'Monokai', bg: '#272822' },
|
|
1130
|
-
{ id: 'solarized-dark', name: 'Solarized Dark', bg: '#002b36' },
|
|
1131
|
-
{ id: 'solarized-light', name: 'Solarized Light', bg: '#fdf6e3' },
|
|
1132
|
-
{ id: 'nord', name: 'Nord', bg: '#2e3440' },
|
|
1133
|
-
{ id: 'dracula', name: 'Dracula', bg: '#282a36' },
|
|
1134
|
-
{ id: 'github-dark', name: 'GitHub Dark', bg: '#0d1117' },
|
|
1135
|
-
{ id: 'one-dark', name: 'One Dark', bg: '#282c34' },
|
|
1136
|
-
{ id: 'catppuccin', name: 'Catppuccin', bg: '#1e1e2e' },
|
|
1137
|
-
{ id: 'gruvbox', name: 'Gruvbox', bg: '#282828' },
|
|
1138
|
-
{ id: 'night-owl', name: 'Night Owl', bg: '#011627' },
|
|
1139
|
-
];
|
|
1140
|
-
function getTheme() {
|
|
1141
|
-
return localStorage.getItem('termbeam-theme') || 'dark';
|
|
1142
|
-
}
|
|
1143
|
-
function applyTheme(theme) {
|
|
1144
|
-
document.documentElement.setAttribute('data-theme', theme);
|
|
1145
|
-
const t = THEMES.find((x) => x.id === theme) || THEMES[0];
|
|
1146
|
-
document.querySelector('meta[name="theme-color"]').content = t.bg;
|
|
1147
|
-
localStorage.setItem('termbeam-theme', theme);
|
|
1148
|
-
document.querySelectorAll('.theme-option').forEach((el) => {
|
|
1149
|
-
el.classList.toggle('active', el.dataset.themeOption === theme);
|
|
1150
|
-
});
|
|
1151
|
-
}
|
|
1152
|
-
applyTheme(getTheme());
|
|
1153
|
-
document.getElementById('theme-toggle').addEventListener('click', (e) => {
|
|
1154
|
-
e.stopPropagation();
|
|
1155
|
-
document.getElementById('theme-picker').classList.toggle('open');
|
|
1156
|
-
});
|
|
1157
|
-
document.addEventListener('click', () => {
|
|
1158
|
-
document.getElementById('theme-picker').classList.remove('open');
|
|
1159
|
-
});
|
|
974
|
+
// Close theme picker when option is selected (index-specific behavior)
|
|
1160
975
|
document.querySelectorAll('.theme-option').forEach((el) => {
|
|
1161
|
-
el.addEventListener('click', (
|
|
1162
|
-
e.stopPropagation();
|
|
1163
|
-
applyTheme(el.dataset.themeOption);
|
|
976
|
+
el.addEventListener('click', () => {
|
|
1164
977
|
document.getElementById('theme-picker').classList.remove('open');
|
|
1165
978
|
});
|
|
1166
979
|
});
|
|
@@ -1204,6 +1017,16 @@
|
|
|
1204
1017
|
<span><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg> ${s.clients} connected</span>
|
|
1205
1018
|
<span title="Last activity">${getActivityLabel(s.lastActivity)}</span>
|
|
1206
1019
|
</div>
|
|
1020
|
+
${
|
|
1021
|
+
s.git
|
|
1022
|
+
? `<div class="git-info">
|
|
1023
|
+
<span class="git-badge"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="6" y1="3" x2="6" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/></svg> ${esc(s.git.branch || 'detached')}</span>
|
|
1024
|
+
${s.git.provider ? `<span class="git-badge">${esc(s.git.provider)}</span>` : ''}
|
|
1025
|
+
${s.git.repoName ? `<span class="git-badge">${esc(s.git.repoName)}</span>` : ''}
|
|
1026
|
+
${s.git.status ? `<span class="git-badge ${s.git.status.clean ? 'git-status-clean' : 'git-status-dirty'}">${s.git.status.clean ? '✓ clean' : esc(s.git.status.summary)}</span>` : ''}
|
|
1027
|
+
</div>`
|
|
1028
|
+
: ''
|
|
1029
|
+
}
|
|
1207
1030
|
<button class="connect-btn">Connect →</button>
|
|
1208
1031
|
</div>
|
|
1209
1032
|
</div>
|
|
@@ -1226,12 +1049,6 @@
|
|
|
1226
1049
|
});
|
|
1227
1050
|
}
|
|
1228
1051
|
|
|
1229
|
-
function esc(str) {
|
|
1230
|
-
const d = document.createElement('div');
|
|
1231
|
-
d.textContent = str;
|
|
1232
|
-
return d.innerHTML;
|
|
1233
|
-
}
|
|
1234
|
-
|
|
1235
1052
|
document.getElementById('new-session-btn').addEventListener('click', () => {
|
|
1236
1053
|
loadShells();
|
|
1237
1054
|
modal.classList.add('visible');
|
|
@@ -1504,21 +1321,6 @@
|
|
|
1504
1321
|
setInterval(loadSessions, 3000);
|
|
1505
1322
|
|
|
1506
1323
|
// Share button
|
|
1507
|
-
function copyToClipboardFallback(text) {
|
|
1508
|
-
const ta = document.createElement('textarea');
|
|
1509
|
-
ta.value = text;
|
|
1510
|
-
ta.style.cssText = 'position:fixed;left:-9999px;top:-9999px';
|
|
1511
|
-
document.body.appendChild(ta);
|
|
1512
|
-
ta.focus();
|
|
1513
|
-
ta.select();
|
|
1514
|
-
let ok = false;
|
|
1515
|
-
try {
|
|
1516
|
-
ok = document.execCommand('copy');
|
|
1517
|
-
} catch {}
|
|
1518
|
-
document.body.removeChild(ta);
|
|
1519
|
-
return ok;
|
|
1520
|
-
}
|
|
1521
|
-
|
|
1522
1324
|
function showShareToast(msg, duration) {
|
|
1523
1325
|
const toast = document.createElement('div');
|
|
1524
1326
|
toast.textContent = msg;
|
|
@@ -1602,9 +1404,7 @@
|
|
|
1602
1404
|
location.reload();
|
|
1603
1405
|
});
|
|
1604
1406
|
|
|
1605
|
-
|
|
1606
|
-
navigator.serviceWorker.register('/sw.js').catch(() => {});
|
|
1607
|
-
}
|
|
1407
|
+
registerServiceWorker();
|
|
1608
1408
|
</script>
|
|
1609
1409
|
</body>
|
|
1610
1410
|
</html>
|