lit-shell.js 0.1.4 → 1.2.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.
Files changed (48) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/README.md +189 -62
  3. package/dist/client/browser-bundle.js +301 -5
  4. package/dist/client/browser-bundle.js.map +3 -3
  5. package/dist/client/index.d.ts +2 -1
  6. package/dist/client/index.d.ts.map +1 -1
  7. package/dist/client/index.js +1 -0
  8. package/dist/client/index.js.map +1 -1
  9. package/dist/client/terminal-client.d.ts +97 -3
  10. package/dist/client/terminal-client.d.ts.map +1 -1
  11. package/dist/client/terminal-client.js +298 -4
  12. package/dist/client/terminal-client.js.map +1 -1
  13. package/dist/server/circular-buffer.d.ts +55 -0
  14. package/dist/server/circular-buffer.d.ts.map +1 -0
  15. package/dist/server/circular-buffer.js +91 -0
  16. package/dist/server/circular-buffer.js.map +1 -0
  17. package/dist/server/index.d.ts +5 -1
  18. package/dist/server/index.d.ts.map +1 -1
  19. package/dist/server/index.js +4 -0
  20. package/dist/server/index.js.map +1 -1
  21. package/dist/server/session-manager.d.ts +201 -0
  22. package/dist/server/session-manager.d.ts.map +1 -0
  23. package/dist/server/session-manager.js +458 -0
  24. package/dist/server/session-manager.js.map +1 -0
  25. package/dist/server/terminal-server.d.ts +75 -5
  26. package/dist/server/terminal-server.d.ts.map +1 -1
  27. package/dist/server/terminal-server.js +515 -79
  28. package/dist/server/terminal-server.js.map +1 -1
  29. package/dist/shared/types.d.ts +185 -2
  30. package/dist/shared/types.d.ts.map +1 -1
  31. package/dist/ui/browser-bundle.js +1853 -88
  32. package/dist/ui/browser-bundle.js.map +4 -4
  33. package/dist/ui/index.d.ts +1 -0
  34. package/dist/ui/index.d.ts.map +1 -1
  35. package/dist/ui/index.js +1 -0
  36. package/dist/ui/index.js.map +1 -1
  37. package/dist/ui/lit-shell-terminal.d.ts +225 -6
  38. package/dist/ui/lit-shell-terminal.d.ts.map +1 -1
  39. package/dist/ui/lit-shell-terminal.js +1605 -60
  40. package/dist/ui/lit-shell-terminal.js.map +1 -1
  41. package/dist/ui/styles.d.ts.map +1 -1
  42. package/dist/ui/styles.js +22 -0
  43. package/dist/ui/styles.js.map +1 -1
  44. package/dist/version.d.ts +6 -0
  45. package/dist/version.d.ts.map +1 -0
  46. package/dist/version.js +6 -0
  47. package/dist/version.js.map +1 -0
  48. package/package.json +9 -4
package/CHANGELOG.md ADDED
@@ -0,0 +1,69 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.2.0] - 2025-02-01
9
+
10
+ ### Added
11
+
12
+ - **Mobile Support**: Auto-detect mobile devices and show touch keyboard
13
+ - Termux-style layout: ESC, arrows, HOME/END, PGUP/PGDN, TAB
14
+ - Sticky CTRL and ALT modifiers
15
+ - Collapsible extra row with common control sequences (^C, ^D, ^Z, ^L, ^A, ^E, ^R)
16
+ - Hide/show toggle for entire keyboard
17
+ - Responsive viewport detection with media query listener
18
+ - **Reconnect Dialog**: After WebSocket reconnection, shows dialog to rejoin previous session
19
+ - Saves session ID on disconnect
20
+ - Checks if session still exists after reconnect
21
+ - Option to rejoin with history or start fresh
22
+ - **Session Persistence Options**: Connection panel now includes
23
+ - Configurable orphan timeout (1 min to 1 week)
24
+ - Tmux integration checkbox for permanent persistence
25
+ - **Updated Font Stack**: Terminal now uses Cascadia Mono as primary font for better cross-platform consistency
26
+
27
+ ### Changed
28
+
29
+ - Improved README documentation with all new features
30
+ - Updated feature list to include Docker, multiplexing, mobile, and persistence
31
+
32
+ ## [1.1.0] - 2025-01-09
33
+
34
+ ### Added
35
+
36
+ - **Tabbed Terminals**: New `show-tabs` attribute enables multiple terminal tabs in a single component
37
+ - Each tab has independent WebSocket connection and terminal session
38
+ - Tab bar with status indicators and add/close buttons
39
+ - Dynamic labels showing shell or container name
40
+ - `createTab()`, `switchTab()`, `closeTab()` methods
41
+ - **Join Existing Session**: Connection panel now shows "Join Existing Session" mode when sessions are available
42
+ - **Prompt Refresh on Join**: Joining a session now triggers a fresh prompt display
43
+
44
+ ### Fixed
45
+
46
+ - Fixed duplicate output when multiple tabs join the same session
47
+ - Fixed session list not updating after spawning a session
48
+ - Each client's data handler now correctly writes to its own tab's terminal
49
+
50
+ ## [1.0.0] - 2025-01-08
51
+
52
+ ### Added
53
+
54
+ - Initial release
55
+ - WebSocket-based terminal server with node-pty
56
+ - Lightweight client library with auto-reconnection
57
+ - `<lit-shell-terminal>` Lit web component with xterm.js
58
+ - Docker exec support for connecting to containers
59
+ - Docker attach mode for connecting to container's main process (PID 1)
60
+ - Session multiplexing - multiple clients sharing the same terminal
61
+ - Session persistence with configurable orphan timeout
62
+ - History replay for clients joining existing sessions
63
+ - Built-in connection panel with container/shell selector
64
+ - Settings dropdown (theme, font size)
65
+ - Status bar with connection info
66
+ - Dark/light/auto theme support
67
+ - Security features: shell, path, and container allowlists
68
+ - Python client bindings (`bindings/python/`)
69
+ - Example projects for Docker containers and multiplexing
package/README.md CHANGED
@@ -9,8 +9,13 @@ A plug-and-play terminal solution for web applications. Includes a server compon
9
9
  - **Server**: WebSocket server with node-pty for real shell sessions
10
10
  - **Client**: Lightweight WebSocket client with auto-reconnection
11
11
  - **UI**: `<lit-shell-terminal>` Lit web component with xterm.js
12
+ - **Docker Support**: Execute commands in Docker containers (exec or attach mode)
13
+ - **Session Multiplexing**: Multiple clients can share the same terminal session
14
+ - **Session Persistence**: Orphan timeout and tmux integration for long-running sessions
15
+ - **Mobile Support**: Touch keyboard with Termux-style layout for mobile devices
16
+ - **Multi-Tab**: Support for multiple terminal tabs in one component
12
17
  - **Themes**: Built-in dark/light/auto theme support
13
- - **Security**: Configurable shell and path allowlists
18
+ - **Security**: Configurable shell, path, and container allowlists
14
19
  - **Framework Agnostic**: Works with React, Vue, Angular, Svelte, or vanilla JS
15
20
 
16
21
  ## Installation
@@ -51,10 +56,7 @@ server.listen(3000, () => {
51
56
  ### Client Usage (Web Component)
52
57
 
53
58
  ```html
54
- <!-- Load xterm.js CSS -->
55
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@5.3.0/css/xterm.css">
56
-
57
- <!-- Load lit-shell.js UI bundle -->
59
+ <!-- Load lit-shell.js UI bundle (includes xterm.js) -->
58
60
  <script type="module" src="https://unpkg.com/lit-shell.js/dist/ui/browser-bundle.js"></script>
59
61
 
60
62
  <!-- Use the component -->
@@ -94,6 +96,97 @@ client.write('ls -la\n');
94
96
  client.resize(120, 40);
95
97
  ```
96
98
 
99
+ ## Docker Support
100
+
101
+ lit-shell supports executing commands inside Docker containers via two modes:
102
+
103
+ ### Docker Exec Mode
104
+
105
+ Spawns a new shell inside a running container:
106
+
107
+ ```javascript
108
+ const terminalServer = new TerminalServer({
109
+ allowDockerExec: true,
110
+ allowedContainerPatterns: ['my-app-.*', 'dev-container'],
111
+ defaultContainerShell: '/bin/bash',
112
+ });
113
+
114
+ // Client-side
115
+ await client.spawn({
116
+ container: 'my-container',
117
+ containerShell: '/bin/bash',
118
+ containerUser: 'node',
119
+ containerCwd: '/app',
120
+ });
121
+ ```
122
+
123
+ ### Docker Attach Mode
124
+
125
+ Attaches to a container's main process (stdin/stdout):
126
+
127
+ ```javascript
128
+ await client.spawn({
129
+ container: 'my-container',
130
+ attachMode: true,
131
+ });
132
+ ```
133
+
134
+ ## Session Multiplexing
135
+
136
+ Multiple clients can share the same terminal session:
137
+
138
+ ```javascript
139
+ // First client spawns a session
140
+ const session = await client1.spawn({ shell: '/bin/bash' });
141
+
142
+ // Second client joins the same session
143
+ const shared = await client2.join({
144
+ sessionId: session.sessionId,
145
+ requestHistory: true, // Replay terminal history
146
+ historyLimit: 10000,
147
+ });
148
+
149
+ // Both clients now see the same terminal
150
+ // Either can type, both see the output
151
+ ```
152
+
153
+ ### Session List
154
+
155
+ ```javascript
156
+ // Get available sessions
157
+ client.onSessionList((sessions) => {
158
+ sessions.forEach(s => {
159
+ console.log(`${s.sessionId}: ${s.shell} (${s.clientCount} clients)`);
160
+ });
161
+ });
162
+
163
+ client.requestSessionList();
164
+ ```
165
+
166
+ ## Session Persistence
167
+
168
+ ### Orphan Timeout
169
+
170
+ Sessions can persist after all clients disconnect:
171
+
172
+ ```javascript
173
+ await client.spawn({
174
+ shell: '/bin/bash',
175
+ orphanTimeout: 3600000, // Keep alive 1 hour after last client leaves
176
+ });
177
+ ```
178
+
179
+ ### Tmux Integration
180
+
181
+ For permanent session persistence:
182
+
183
+ ```javascript
184
+ await client.spawn({
185
+ shell: '/bin/bash',
186
+ useTmux: true, // Session persists indefinitely in tmux
187
+ });
188
+ ```
189
+
97
190
  ## API Reference
98
191
 
99
192
  ### Server
@@ -104,28 +197,31 @@ client.resize(120, 40);
104
197
  import { TerminalServer } from 'lit-shell.js/server';
105
198
 
106
199
  const server = new TerminalServer({
107
- // Allowed shells (empty = all allowed)
108
- allowedShells: ['/bin/bash', '/bin/zsh', 'cmd.exe'],
109
-
110
- // Allowed working directories (empty = all allowed)
111
- allowedPaths: ['/home/user', '/var/www'],
112
-
113
- // Default shell if not specified
200
+ // Shell configuration
201
+ allowedShells: ['/bin/bash', '/bin/zsh'],
114
202
  defaultShell: '/bin/bash',
115
203
 
116
- // Default working directory
204
+ // Path restrictions
205
+ allowedPaths: ['/home/user', '/var/www'],
117
206
  defaultCwd: '/home/user',
118
207
 
119
- // Max sessions per client (default: 5)
120
- maxSessionsPerClient: 5,
208
+ // Docker configuration
209
+ allowDockerExec: false,
210
+ allowedContainerPatterns: ['.*'],
211
+ defaultContainerShell: '/bin/sh',
121
212
 
122
- // Idle timeout in ms (default: 30 minutes, 0 = disabled)
213
+ // Session limits
214
+ maxSessionsPerClient: 5,
123
215
  idleTimeout: 30 * 60 * 1000,
124
216
 
125
- // WebSocket path (default: '/terminal')
217
+ // History
218
+ historyEnabled: true,
219
+ historySize: 50000,
220
+
221
+ // WebSocket path
126
222
  path: '/terminal',
127
223
 
128
- // Enable verbose logging
224
+ // Logging
129
225
  verbose: false,
130
226
  });
131
227
 
@@ -134,12 +230,6 @@ server.attach(httpServer);
134
230
 
135
231
  // Or start standalone
136
232
  server.listen(3001);
137
-
138
- // Get active sessions
139
- const sessions = server.getSessions();
140
-
141
- // Close server
142
- server.close();
143
233
  ```
144
234
 
145
235
  ### Client
@@ -151,48 +241,52 @@ import { TerminalClient } from 'lit-shell.js/client';
151
241
 
152
242
  const client = new TerminalClient({
153
243
  url: 'ws://localhost:3000/terminal',
154
- reconnect: true, // Auto-reconnect (default: true)
155
- maxReconnectAttempts: 10, // Max attempts (default: 10)
156
- reconnectDelay: 1000, // Initial delay ms (default: 1000)
244
+ reconnect: true,
245
+ maxReconnectAttempts: 10,
246
+ reconnectDelay: 1000,
157
247
  });
158
248
 
159
- // Connect to server
249
+ // Connect
160
250
  await client.connect();
161
251
 
162
- // Spawn terminal session
163
- const sessionInfo = await client.spawn({
252
+ // Spawn session
253
+ const session = await client.spawn({
164
254
  shell: '/bin/bash',
165
255
  cwd: '/home/user',
166
256
  env: { TERM: 'xterm-256color' },
167
257
  cols: 80,
168
258
  rows: 24,
259
+ container: 'optional-container-name',
260
+ orphanTimeout: 3600000,
261
+ useTmux: false,
262
+ });
263
+
264
+ // Join existing session
265
+ const shared = await client.join({
266
+ sessionId: 'session-id',
267
+ requestHistory: true,
268
+ historyLimit: 50000,
169
269
  });
170
270
 
171
- // Write to terminal
172
- client.write('echo "Hello World"\n');
271
+ // Leave session (without killing it)
272
+ client.leave(sessionId);
173
273
 
174
- // Resize terminal
274
+ // Terminal I/O
275
+ client.write('echo "Hello"\n');
175
276
  client.resize(120, 40);
176
-
177
- // Kill session
178
277
  client.kill();
179
278
 
180
- // Disconnect
181
- client.disconnect();
182
-
183
279
  // Event handlers
184
- client.onConnect(() => console.log('Connected'));
185
- client.onDisconnect(() => console.log('Disconnected'));
186
- client.onData((data) => console.log('Data:', data));
187
- client.onExit((code) => console.log('Exit:', code));
188
- client.onError((err) => console.log('Error:', err));
189
- client.onSpawned((info) => console.log('Spawned:', info));
190
-
191
- // State getters
192
- client.isConnected(); // boolean
193
- client.hasActiveSession(); // boolean
194
- client.getSessionId(); // string | null
195
- client.getSessionInfo(); // SessionInfo | null
280
+ client.onConnect(() => {});
281
+ client.onDisconnect(() => {});
282
+ client.onData((data) => {});
283
+ client.onExit((code) => {});
284
+ client.onError((err) => {});
285
+ client.onSpawned((info) => {});
286
+ client.onSessionList((sessions) => {});
287
+ client.onClientJoined((sessionId, count) => {});
288
+ client.onClientLeft((sessionId, count) => {});
289
+ client.onReconnectWithSession((sessionId) => {});
196
290
  ```
197
291
 
198
292
  ### UI Component
@@ -206,12 +300,14 @@ client.getSessionInfo(); // SessionInfo | null
206
300
  cwd="/home/user"
207
301
  theme="dark"
208
302
  font-size="14"
209
- font-family="Menlo, Monaco, monospace"
210
303
  cols="80"
211
304
  rows="24"
212
305
  auto-connect
213
306
  auto-spawn
214
- no-header
307
+ show-connection-panel
308
+ show-status-bar
309
+ show-tabs
310
+ show-settings
215
311
  ></lit-shell-terminal>
216
312
  ```
217
313
 
@@ -222,28 +318,40 @@ client.getSessionInfo(); // SessionInfo | null
222
318
  | `url` | string | `''` | WebSocket URL |
223
319
  | `shell` | string | `''` | Shell to use |
224
320
  | `cwd` | string | `''` | Working directory |
321
+ | `container` | string | `''` | Docker container name |
225
322
  | `theme` | `'dark'` \| `'light'` \| `'auto'` | `'dark'` | Color theme |
226
323
  | `font-size` | number | `14` | Terminal font size |
227
- | `font-family` | string | `'Menlo, Monaco, ...'` | Terminal font |
324
+ | `font-family` | string | `'Cascadia Mono, ...'` | Terminal font |
228
325
  | `cols` | number | `80` | Initial columns |
229
326
  | `rows` | number | `24` | Initial rows |
230
327
  | `auto-connect` | boolean | `false` | Connect on mount |
231
328
  | `auto-spawn` | boolean | `false` | Spawn on connect |
232
329
  | `no-header` | boolean | `false` | Hide header bar |
330
+ | `show-connection-panel` | boolean | `false` | Show connection options |
331
+ | `show-status-bar` | boolean | `false` | Show status bar |
332
+ | `show-tabs` | boolean | `false` | Enable multi-tab mode |
333
+ | `show-settings` | boolean | `false` | Show settings menu |
233
334
 
234
335
  **Methods:**
235
336
 
236
337
  ```javascript
237
338
  const terminal = document.querySelector('lit-shell-terminal');
238
339
 
239
- await terminal.connect(); // Connect to server
240
- terminal.disconnect(); // Disconnect
241
- await terminal.spawn(); // Spawn session
242
- terminal.kill(); // Kill session
243
- terminal.clear(); // Clear display
244
- terminal.write('text'); // Write to display
245
- terminal.writeln('line'); // Write line to display
246
- terminal.focus(); // Focus terminal
340
+ await terminal.connect();
341
+ terminal.disconnect();
342
+ await terminal.spawn(options);
343
+ await terminal.join(sessionId);
344
+ terminal.leave();
345
+ terminal.kill();
346
+ terminal.clear();
347
+ terminal.write('text');
348
+ terminal.writeln('line');
349
+ terminal.focus();
350
+
351
+ // Tab management (when show-tabs is enabled)
352
+ terminal.createTab('Label');
353
+ terminal.switchTab(tabId);
354
+ terminal.closeTab(tabId);
247
355
  ```
248
356
 
249
357
  **Events:**
@@ -254,8 +362,23 @@ terminal.addEventListener('disconnect', () => {});
254
362
  terminal.addEventListener('spawned', (e) => console.log(e.detail.session));
255
363
  terminal.addEventListener('exit', (e) => console.log(e.detail.exitCode));
256
364
  terminal.addEventListener('error', (e) => console.log(e.detail.error));
365
+ terminal.addEventListener('theme-change', (e) => console.log(e.detail.theme));
366
+ ```
367
+
368
+ ## Mobile Support
369
+
370
+ On mobile devices, lit-shell automatically shows a touch keyboard with common terminal keys:
371
+
372
+ ```
373
+ Row 1: [ESC] [/] [-] [HOME] [↑] [END] [PGUP]
374
+ Row 2: [TAB] [CTRL] [ALT] [←] [↓] [→] [PGDN]
375
+ Row 3: [^C] [^D] [^Z] [^L] [^A] [^E] [^R] (expandable)
257
376
  ```
258
377
 
378
+ - CTRL and ALT are sticky modifiers (tap to activate, applies to next key)
379
+ - Collapsible extra row for common control sequences
380
+ - Hide/show toggle for the entire keyboard
381
+
259
382
  ## Theming
260
383
 
261
384
  The component uses CSS custom properties for theming:
@@ -291,11 +414,15 @@ const server = new TerminalServer({
291
414
  // Restrict working directories
292
415
  allowedPaths: ['/home/app', '/var/www'],
293
416
 
417
+ // Restrict Docker containers
418
+ allowDockerExec: true,
419
+ allowedContainerPatterns: ['^myapp-'],
420
+
294
421
  // Limit sessions per client
295
422
  maxSessionsPerClient: 2,
296
423
 
297
424
  // Set idle timeout
298
- idleTimeout: 10 * 60 * 1000, // 10 minutes
425
+ idleTimeout: 10 * 60 * 1000,
299
426
  });
300
427
  ```
301
428