lit-shell.js 1.1.0 → 1.2.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.
Files changed (43) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +438 -9
  3. package/dist/client/browser-bundle.js +61 -1
  4. package/dist/client/browser-bundle.js.map +3 -3
  5. package/dist/client/index.d.ts +1 -0
  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 +19 -0
  10. package/dist/client/terminal-client.d.ts.map +1 -1
  11. package/dist/client/terminal-client.js +61 -0
  12. package/dist/client/terminal-client.js.map +1 -1
  13. package/dist/server/index.d.ts +1 -0
  14. package/dist/server/index.d.ts.map +1 -1
  15. package/dist/server/index.js +1 -0
  16. package/dist/server/index.js.map +1 -1
  17. package/dist/server/session-manager.d.ts +6 -0
  18. package/dist/server/session-manager.d.ts.map +1 -1
  19. package/dist/server/session-manager.js +12 -2
  20. package/dist/server/session-manager.js.map +1 -1
  21. package/dist/server/terminal-server.d.ts.map +1 -1
  22. package/dist/server/terminal-server.js +23 -4
  23. package/dist/server/terminal-server.js.map +1 -1
  24. package/dist/shared/types.d.ts +6 -0
  25. package/dist/shared/types.d.ts.map +1 -1
  26. package/dist/ui/browser-bundle.js +1625 -96
  27. package/dist/ui/browser-bundle.js.map +4 -4
  28. package/dist/ui/index.d.ts +1 -0
  29. package/dist/ui/index.d.ts.map +1 -1
  30. package/dist/ui/index.js +1 -0
  31. package/dist/ui/index.js.map +1 -1
  32. package/dist/ui/lit-shell-terminal.d.ts +225 -6
  33. package/dist/ui/lit-shell-terminal.d.ts.map +1 -1
  34. package/dist/ui/lit-shell-terminal.js +1605 -60
  35. package/dist/ui/lit-shell-terminal.js.map +1 -1
  36. package/dist/ui/styles.d.ts.map +1 -1
  37. package/dist/ui/styles.js +22 -0
  38. package/dist/ui/styles.js.map +1 -1
  39. package/dist/version.d.ts +6 -0
  40. package/dist/version.d.ts.map +1 -0
  41. package/dist/version.js +6 -0
  42. package/dist/version.js.map +1 -0
  43. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,42 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.2.1] - 2025-02-01
9
+
10
+ ### Changed
11
+
12
+ - Comprehensive README update with full documentation
13
+ - Added Tabbed Terminals section with Tab API and use cases
14
+ - Added Built-in Connection Panel section
15
+ - Added Session Multiplexing diagram
16
+ - Added Docker Compose quick start instructions
17
+ - Added Examples section with local development guide
18
+ - Added Client-Side CDN loading instructions
19
+
20
+ ## [1.2.0] - 2025-02-01
21
+
22
+ ### Added
23
+
24
+ - **Mobile Support**: Auto-detect mobile devices and show touch keyboard
25
+ - Termux-style layout: ESC, arrows, HOME/END, PGUP/PGDN, TAB
26
+ - Sticky CTRL and ALT modifiers
27
+ - Collapsible extra row with common control sequences (^C, ^D, ^Z, ^L, ^A, ^E, ^R)
28
+ - Hide/show toggle for entire keyboard
29
+ - Responsive viewport detection with media query listener
30
+ - **Reconnect Dialog**: After WebSocket reconnection, shows dialog to rejoin previous session
31
+ - Saves session ID on disconnect
32
+ - Checks if session still exists after reconnect
33
+ - Option to rejoin with history or start fresh
34
+ - **Session Persistence Options**: Connection panel now includes
35
+ - Configurable orphan timeout (1 min to 1 week)
36
+ - Tmux integration checkbox for permanent persistence
37
+ - **Updated Font Stack**: Terminal now uses Cascadia Mono as primary font for better cross-platform consistency
38
+
39
+ ### Changed
40
+
41
+ - Improved README documentation with all new features
42
+ - Updated feature list to include Docker, multiplexing, mobile, and persistence
43
+
8
44
  ## [1.1.0] - 2025-01-09
9
45
 
10
46
  ### Added
package/README.md CHANGED
@@ -9,17 +9,65 @@ 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
+ - **Tabbed Terminals**: Multiple terminal tabs in a single component
13
+ - **Docker Exec**: Connect to Docker containers via `docker exec`
14
+ - **Docker Attach**: Connect to a container's main process (PID 1)
15
+ - **Session Multiplexing**: Multiple clients can share the same terminal session
16
+ - **Session Persistence**: Sessions survive client disconnects with configurable timeout
17
+ - **History Replay**: New clients receive recent terminal output when joining
18
+ - **Mobile Support**: Touch keyboard with Termux-style layout for mobile devices
12
19
  - **Themes**: Built-in dark/light/auto theme support
13
- - **Security**: Configurable shell and path allowlists
20
+ - **Security**: Configurable shell, path, and container allowlists
14
21
  - **Framework Agnostic**: Works with React, Vue, Angular, Svelte, or vanilla JS
15
22
 
16
23
  ## Installation
17
24
 
18
25
  ```bash
19
- npm install lit-shell.js node-pty
26
+ npm install lit-shell.js
20
27
  ```
21
28
 
22
- Note: `node-pty` requires native compilation. See [node-pty docs](https://github.com/microsoft/node-pty) for platform-specific requirements.
29
+ ### Server-Side Requirements (node-pty)
30
+
31
+ The server component requires `node-pty` for spawning terminal processes. Install it as a dev dependency:
32
+
33
+ ```bash
34
+ npm install node-pty --save-dev
35
+ ```
36
+
37
+ **Important:** `node-pty` requires native compilation. If you encounter installation issues:
38
+
39
+ ```bash
40
+ # Linux - install build essentials
41
+ sudo apt-get install build-essential python3
42
+
43
+ # macOS - install Xcode command line tools
44
+ xcode-select --install
45
+
46
+ # If npm install fails, try:
47
+ npm install node-pty --save-dev --legacy-peer-deps
48
+
49
+ # Or rebuild native modules:
50
+ npm rebuild node-pty
51
+ ```
52
+
53
+ See [node-pty docs](https://github.com/microsoft/node-pty) for platform-specific requirements.
54
+
55
+ ### Client-Side (Browser)
56
+
57
+ The UI component can be loaded directly from a CDN - no build step required:
58
+
59
+ ```html
60
+ <!-- Using unpkg -->
61
+ <script type="module" src="https://unpkg.com/lit-shell.js/dist/ui/browser-bundle.js"></script>
62
+
63
+ <!-- Or using jsDelivr -->
64
+ <script type="module" src="https://cdn.jsdelivr.net/npm/lit-shell.js/dist/ui/browser-bundle.js"></script>
65
+
66
+ <!-- Pin to a specific version -->
67
+ <script type="module" src="https://unpkg.com/lit-shell.js@1.2.0/dist/ui/browser-bundle.js"></script>
68
+ ```
69
+
70
+ The bundle includes the `<lit-shell-terminal>` web component with xterm.js built-in.
23
71
 
24
72
  ## Quick Start
25
73
 
@@ -51,9 +99,6 @@ server.listen(3000, () => {
51
99
  ### Client Usage (Web Component)
52
100
 
53
101
  ```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
102
  <!-- Load lit-shell.js UI bundle -->
58
103
  <script type="module" src="https://unpkg.com/lit-shell.js/dist/ui/browser-bundle.js"></script>
59
104
 
@@ -94,6 +139,94 @@ client.write('ls -la\n');
94
139
  client.resize(120, 40);
95
140
  ```
96
141
 
142
+ ## Tabbed Terminals
143
+
144
+ Enable multiple terminal tabs within a single component using the `show-tabs` attribute:
145
+
146
+ ```html
147
+ <lit-shell-terminal
148
+ url="ws://localhost:3000/terminal"
149
+ show-tabs
150
+ show-connection-panel
151
+ show-settings
152
+ show-status-bar
153
+ ></lit-shell-terminal>
154
+ ```
155
+
156
+ ### Features
157
+
158
+ - **Independent Sessions**: Each tab has its own WebSocket connection and terminal session
159
+ - **Tab Bar**: Shows all open tabs with status indicators
160
+ - **Dynamic Labels**: Tabs automatically update their label to show the shell or container name
161
+ - **Session Joining**: Create a tab and join an existing session from another tab
162
+ - **Easy Management**: Click "+" to add tabs, "×" to close, click tab to switch
163
+
164
+ ### Tab API
165
+
166
+ ```javascript
167
+ const terminal = document.querySelector('lit-shell-terminal');
168
+
169
+ // Create a new tab
170
+ const tab = terminal.createTab('My Terminal');
171
+ // Returns: { id: 'tab-1', label: 'My Terminal', ... }
172
+
173
+ // Switch to a specific tab
174
+ terminal.switchTab('tab-1');
175
+
176
+ // Close a tab (resources are cleaned up automatically)
177
+ terminal.closeTab('tab-1');
178
+
179
+ // Access tab state
180
+ // Each tab maintains its own: client, terminal, sessionInfo, etc.
181
+ ```
182
+
183
+ ### Use Cases
184
+
185
+ **1. Multi-Environment Development**
186
+ ```html
187
+ <!-- Open tabs for different containers -->
188
+ <lit-shell-terminal show-tabs show-connection-panel></lit-shell-terminal>
189
+ ```
190
+ - Tab 1: Local shell for git operations
191
+ - Tab 2: Docker container for backend
192
+ - Tab 3: Docker container for frontend
193
+
194
+ **2. Session Sharing**
195
+ - Create a session in Tab 1
196
+ - Create Tab 2, select "Join Existing Session"
197
+ - Both tabs now mirror the same terminal
198
+
199
+ **3. Monitoring Multiple Processes**
200
+ - Open multiple tabs
201
+ - Each tab connects to a different running session
202
+ - Monitor all processes from a single interface
203
+
204
+ ## Built-in Connection Panel
205
+
206
+ When `show-connection-panel` is enabled, the terminal component provides a built-in UI for:
207
+
208
+ - **Mode Selection**: Switch between local shell, Docker exec, Docker attach, and join existing session modes
209
+ - **Container Picker**: Dropdown of running containers (when Docker is enabled on server)
210
+ - **Shell Selection**: Choose from server-allowed shells
211
+ - **Session Timeout**: Configure orphan timeout (1 min to 1 week)
212
+ - **Tmux Integration**: Option for permanent session persistence
213
+ - **Connect/Disconnect**: One-click session management
214
+
215
+ The connection panel automatically queries the server for:
216
+ - Docker availability and allowed containers
217
+ - Allowed shells and default configuration
218
+ - Available sessions for joining
219
+
220
+ ```html
221
+ <!-- Full-featured terminal with all UI panels -->
222
+ <lit-shell-terminal
223
+ url="ws://localhost:3000/terminal"
224
+ show-connection-panel
225
+ show-settings
226
+ show-status-bar
227
+ ></lit-shell-terminal>
228
+ ```
229
+
97
230
  ## API Reference
98
231
 
99
232
  ### Server
@@ -127,6 +260,18 @@ const server = new TerminalServer({
127
260
 
128
261
  // Enable verbose logging
129
262
  verbose: false,
263
+
264
+ // Session multiplexing options
265
+ maxClientsPerSession: 10, // Max clients per session (default: 10)
266
+ orphanTimeout: 60000, // Ms before orphaned sessions close (default: 60000)
267
+ historySize: 50000, // History buffer size in chars (default: 50000)
268
+ historyEnabled: true, // Enable history replay (default: true)
269
+ maxSessionsTotal: 100, // Max concurrent sessions (default: 100)
270
+
271
+ // Docker configuration
272
+ allowDockerExec: false,
273
+ allowedContainerPatterns: ['.*'],
274
+ defaultContainerShell: '/bin/sh',
130
275
  });
131
276
 
132
277
  // Attach to HTTP server
@@ -138,6 +283,10 @@ server.listen(3001);
138
283
  // Get active sessions
139
284
  const sessions = server.getSessions();
140
285
 
286
+ // Get session statistics
287
+ const stats = server.getStats();
288
+ // { sessionCount: 5, clientCount: 12, orphanedCount: 1 }
289
+
141
290
  // Close server
142
291
  server.close();
143
292
  ```
@@ -166,6 +315,11 @@ const sessionInfo = await client.spawn({
166
315
  env: { TERM: 'xterm-256color' },
167
316
  cols: 80,
168
317
  rows: 24,
318
+ container: 'optional-container-name',
319
+ orphanTimeout: 3600000,
320
+ useTmux: false,
321
+ label: 'my-session', // Optional label for identification
322
+ allowJoin: true, // Allow others to join (default: true)
169
323
  });
170
324
 
171
325
  // Write to terminal
@@ -193,6 +347,26 @@ client.isConnected(); // boolean
193
347
  client.hasActiveSession(); // boolean
194
348
  client.getSessionId(); // string | null
195
349
  client.getSessionInfo(); // SessionInfo | null
350
+
351
+ // Session multiplexing
352
+ const sessions = await client.listSessions(); // List all sessions
353
+ const session = await client.join({ // Join existing session
354
+ sessionId: 'term-123...',
355
+ requestHistory: true,
356
+ historyLimit: 50000,
357
+ });
358
+ client.leave(sessionId); // Leave without killing
359
+
360
+ // Multiplexing event handlers
361
+ client.onClientJoined((sessionId, count) => console.log(`${count} clients`));
362
+ client.onClientLeft((sessionId, count) => console.log(`${count} clients`));
363
+ client.onSessionClosed((sessionId, reason) => console.log(reason));
364
+ // reason: 'orphan_timeout' | 'owner_closed' | 'process_exit' | 'error'
365
+
366
+ // Reconnection with session recovery
367
+ client.onReconnectWithSession((sessionId) => {
368
+ // Previous session is still available after reconnect
369
+ });
196
370
  ```
197
371
 
198
372
  ### UI Component
@@ -206,12 +380,16 @@ client.getSessionInfo(); // SessionInfo | null
206
380
  cwd="/home/user"
207
381
  theme="dark"
208
382
  font-size="14"
209
- font-family="Menlo, Monaco, monospace"
383
+ font-family="Cascadia Mono, Consolas, monospace"
210
384
  cols="80"
211
385
  rows="24"
212
386
  auto-connect
213
387
  auto-spawn
214
388
  no-header
389
+ show-connection-panel
390
+ show-settings
391
+ show-status-bar
392
+ show-tabs
215
393
  ></lit-shell-terminal>
216
394
  ```
217
395
 
@@ -222,14 +400,22 @@ client.getSessionInfo(); // SessionInfo | null
222
400
  | `url` | string | `''` | WebSocket URL |
223
401
  | `shell` | string | `''` | Shell to use |
224
402
  | `cwd` | string | `''` | Working directory |
403
+ | `container` | string | `''` | Docker container name |
404
+ | `container-shell` | string | `''` | Shell inside container |
405
+ | `container-user` | string | `''` | User in container |
406
+ | `container-cwd` | string | `''` | Working directory in container |
225
407
  | `theme` | `'dark'` \| `'light'` \| `'auto'` | `'dark'` | Color theme |
226
408
  | `font-size` | number | `14` | Terminal font size |
227
- | `font-family` | string | `'Menlo, Monaco, ...'` | Terminal font |
409
+ | `font-family` | string | `'Cascadia Mono, ...'` | Terminal font |
228
410
  | `cols` | number | `80` | Initial columns |
229
411
  | `rows` | number | `24` | Initial rows |
230
412
  | `auto-connect` | boolean | `false` | Connect on mount |
231
413
  | `auto-spawn` | boolean | `false` | Spawn on connect |
232
414
  | `no-header` | boolean | `false` | Hide header bar |
415
+ | `show-connection-panel` | boolean | `false` | Show connection panel with container/shell selector |
416
+ | `show-settings` | boolean | `false` | Show settings dropdown (theme, font size) |
417
+ | `show-status-bar` | boolean | `false` | Show status bar with connection info and errors |
418
+ | `show-tabs` | boolean | `false` | Enable tabbed terminal interface |
233
419
 
234
420
  **Methods:**
235
421
 
@@ -244,6 +430,15 @@ terminal.clear(); // Clear display
244
430
  terminal.write('text'); // Write to display
245
431
  terminal.writeln('line'); // Write line to display
246
432
  terminal.focus(); // Focus terminal
433
+
434
+ // Session multiplexing
435
+ await terminal.join(sessionId); // Join existing session
436
+ terminal.leave(); // Leave without killing
437
+
438
+ // Tab methods (when show-tabs is enabled)
439
+ terminal.createTab('label'); // Create new tab
440
+ terminal.switchTab('tab-id'); // Switch to tab
441
+ terminal.closeTab('tab-id'); // Close tab
247
442
  ```
248
443
 
249
444
  **Events:**
@@ -254,8 +449,184 @@ terminal.addEventListener('disconnect', () => {});
254
449
  terminal.addEventListener('spawned', (e) => console.log(e.detail.session));
255
450
  terminal.addEventListener('exit', (e) => console.log(e.detail.exitCode));
256
451
  terminal.addEventListener('error', (e) => console.log(e.detail.error));
452
+ terminal.addEventListener('theme-change', (e) => console.log(e.detail.theme));
453
+ ```
454
+
455
+ ## Docker Container Support
456
+
457
+ lit-shell.js can connect to Docker containers, allowing you to exec into running containers directly from the browser.
458
+
459
+ ### Server Configuration
460
+
461
+ ```javascript
462
+ const server = new TerminalServer({
463
+ // Enable Docker exec feature
464
+ allowDockerExec: true,
465
+
466
+ // Restrict which containers can be accessed (regex patterns)
467
+ allowedContainerPatterns: [
468
+ '^myapp-', // Containers starting with 'myapp-'
469
+ '^dev-container$', // Exact match
470
+ 'backend', // Contains 'backend'
471
+ ],
472
+
473
+ // Default shell for containers
474
+ defaultContainerShell: '/bin/bash',
475
+
476
+ // Path to Docker CLI (default: 'docker')
477
+ dockerPath: '/usr/bin/docker',
478
+
479
+ verbose: true,
480
+ });
257
481
  ```
258
482
 
483
+ ### Client Usage
484
+
485
+ ```javascript
486
+ // Connect to a Docker container
487
+ await client.spawn({
488
+ container: 'my-container-name', // Container ID or name
489
+ containerShell: '/bin/sh', // Shell inside container
490
+ containerUser: 'root', // User to run as
491
+ containerCwd: '/app', // Working directory in container
492
+ env: { DEBUG: 'true' }, // Environment variables
493
+ });
494
+ ```
495
+
496
+ ### Web Component
497
+
498
+ ```html
499
+ <lit-shell-terminal
500
+ url="ws://localhost:3000/terminal"
501
+ container="my-container-name"
502
+ container-shell="/bin/bash"
503
+ container-user="node"
504
+ container-cwd="/app"
505
+ theme="dark"
506
+ auto-connect
507
+ auto-spawn
508
+ ></lit-shell-terminal>
509
+ ```
510
+
511
+ ### Docker Attach Mode
512
+
513
+ Docker attach connects to a container's main process (PID 1) instead of spawning a new shell. This is useful for:
514
+ - Interacting with interactive containers started with `docker run -it`
515
+ - Debugging container startup issues
516
+ - Sharing a session with `docker attach` from another terminal
517
+
518
+ ```javascript
519
+ // Client: Attach to container's main process
520
+ await client.spawn({
521
+ container: 'my-container',
522
+ attachMode: true, // Use docker attach instead of docker exec
523
+ });
524
+ ```
525
+
526
+ **Important:** Docker attach connects to whatever is running as PID 1. If the container was started with a non-interactive command (like a web server), attach may not provide useful interaction.
527
+
528
+ ## Session Multiplexing
529
+
530
+ Session multiplexing allows multiple clients to connect to the same terminal session. This enables:
531
+ - **Collaboration**: Multiple users can share a terminal
532
+ - **Session Persistence**: Sessions survive client disconnects
533
+ - **History Replay**: New clients receive recent output when joining
534
+ - **Monitoring**: Watch others' terminal sessions in real-time
535
+
536
+ ### How It Works
537
+
538
+ ```
539
+ ┌──────────┐ ┌─────────────────────────────────────────┐ ┌──────────┐
540
+ │ Client A │◄────┤ SessionManager ├────►│ PTY │
541
+ └──────────┘ │ ┌─────────────────────────────────────┐ │ │ Process │
542
+ │ │ SharedSession │ │ └──────────┘
543
+ ┌──────────┐ │ │ - clients: [A, B, C] │ │
544
+ │ Client B │◄────┼──┤ - historyBuffer (50KB) │ │
545
+ └──────────┘ │ │ - orphanedAt: null │ │
546
+ │ └─────────────────────────────────────┘ │
547
+ ┌──────────┐ │ │
548
+ │ Client C │◄────┼─────────────────────────────────────────┘
549
+ └──────────┘ (broadcast output)
550
+ ```
551
+
552
+ ### Client API
553
+
554
+ ```javascript
555
+ // List available sessions
556
+ const sessions = await client.listSessions();
557
+
558
+ // Create a shareable session
559
+ await client.spawn({
560
+ shell: '/bin/bash',
561
+ label: 'dev-session', // Optional label for identification
562
+ allowJoin: true, // Allow others to join (default: true)
563
+ orphanTimeout: 3600000, // Keep alive 1 hour after last client leaves
564
+ });
565
+
566
+ // Join an existing session
567
+ const session = await client.join({
568
+ sessionId: 'term-abc123...',
569
+ requestHistory: true, // Request output history
570
+ historyLimit: 50000, // Max history chars to receive
571
+ });
572
+ // session.history contains recent output
573
+
574
+ // Leave session without killing it
575
+ client.leave(sessionId);
576
+ // Session survives if other clients connected
577
+ // Or waits orphanTimeout before closing
578
+
579
+ // Kill session
580
+ client.kill();
581
+ ```
582
+
583
+ ### Use Cases
584
+
585
+ **1. Pair Programming**
586
+ ```javascript
587
+ // Developer A creates session
588
+ await client.spawn({ label: 'pair-session' });
589
+ // Share session ID with Developer B
590
+ // Developer B joins with history
591
+ await client.join({ sessionId, requestHistory: true });
592
+ ```
593
+
594
+ **2. Session Persistence**
595
+ ```javascript
596
+ // Start long-running task
597
+ await client.spawn({ shell: '/bin/bash', orphanTimeout: 86400000 });
598
+ client.write('npm run build\n');
599
+ client.disconnect(); // Session survives!
600
+
601
+ // Later, reconnect
602
+ await client.connect();
603
+ const sessions = await client.listSessions();
604
+ await client.join({ sessionId: sessions[0].sessionId, requestHistory: true });
605
+ // See build output that happened while disconnected
606
+ ```
607
+
608
+ **3. Monitoring**
609
+ ```javascript
610
+ // Admin joins session in read-only mode
611
+ await client.join({ sessionId, requestHistory: true });
612
+ // Watch activity without interfering
613
+ ```
614
+
615
+ ## Mobile Support
616
+
617
+ On mobile devices, lit-shell automatically shows a touch keyboard with common terminal keys:
618
+
619
+ ```
620
+ Row 1: [ESC] [/] [-] [HOME] [↑] [END] [PGUP]
621
+ Row 2: [TAB] [CTRL] [ALT] [←] [↓] [→] [PGDN]
622
+ Row 3: [^C] [^D] [^Z] [^L] [^A] [^E] [^R] (expandable)
623
+ ```
624
+
625
+ - **Auto-detection**: Detects mobile via touch capability + viewport size
626
+ - **Sticky modifiers**: CTRL and ALT are toggle keys (tap to activate, applies to next key)
627
+ - **Collapsible**: Extra row can be expanded/collapsed for more screen space
628
+ - **Hide/show toggle**: Entire keyboard can be hidden when not needed
629
+
259
630
  ## Theming
260
631
 
261
632
  The component uses CSS custom properties for theming:
@@ -291,14 +662,72 @@ const server = new TerminalServer({
291
662
  // Restrict working directories
292
663
  allowedPaths: ['/home/app', '/var/www'],
293
664
 
665
+ // Restrict Docker containers
666
+ allowDockerExec: true,
667
+ allowedContainerPatterns: ['^myapp-'],
668
+
294
669
  // Limit sessions per client
295
670
  maxSessionsPerClient: 2,
296
671
 
297
672
  // Set idle timeout
298
- idleTimeout: 10 * 60 * 1000, // 10 minutes
673
+ idleTimeout: 10 * 60 * 1000,
299
674
  });
300
675
  ```
301
676
 
677
+ ## Examples
678
+
679
+ See the [examples](./examples) directory for complete working examples:
680
+
681
+ - [**docker-container**](./examples/docker-container) - Connect to Docker containers from the browser
682
+ - [**multiplexing**](./examples/multiplexing) - Session multiplexing with multiple clients sharing terminals
683
+
684
+ ### Running Locally (Development)
685
+
686
+ ```bash
687
+ # Clone the repository
688
+ git clone https://github.com/lsadehaan/lit-shell.git
689
+ cd lit-shell
690
+
691
+ # Install dependencies (including node-pty)
692
+ npm install
693
+ npm install node-pty --save-dev --legacy-peer-deps
694
+
695
+ # Build the project
696
+ npm run build
697
+
698
+ # Start a test container (optional, for Docker exec testing)
699
+ docker run -d --name test-container alpine sleep infinity
700
+
701
+ # Run the example server
702
+ node examples/docker-container/server.js
703
+
704
+ # Open http://localhost:3000 in your browser
705
+ ```
706
+
707
+ ### Quick Start with Docker Compose
708
+
709
+ Run the full demo with Docker Compose (no local node-pty installation required):
710
+
711
+ ```bash
712
+ cd docker
713
+ docker compose up -d
714
+ ```
715
+
716
+ This starts:
717
+ - lit-shell server on http://localhost:3000
718
+ - Two test containers (Alpine and Ubuntu) to exec into
719
+
720
+ Open http://localhost:3000 and use the connection panel to:
721
+ 1. Select "Docker Container" mode
722
+ 2. Choose a container from the dropdown
723
+ 3. Click "Start Session"
724
+
725
+ Stop the demo:
726
+
727
+ ```bash
728
+ docker compose down
729
+ ```
730
+
302
731
  ## License
303
732
 
304
733
  MIT
@@ -1,3 +1,6 @@
1
+ // src/version.ts
2
+ var VERSION = "1.2.1";
3
+
1
4
  // src/client/terminal-client.ts
2
5
  var TerminalClient = class {
3
6
  constructor(config) {
@@ -8,6 +11,8 @@ var TerminalClient = class {
8
11
  this.serverInfo = null;
9
12
  this.reconnectAttempts = 0;
10
13
  this.reconnectTimeout = null;
14
+ this.previousSessionId = null;
15
+ this.isReconnecting = false;
11
16
  // Event handlers
12
17
  this.connectHandlers = [];
13
18
  this.disconnectHandlers = [];
@@ -24,6 +29,7 @@ var TerminalClient = class {
24
29
  this.clientJoinedHandlers = [];
25
30
  this.clientLeftHandlers = [];
26
31
  this.sessionClosedHandlers = [];
32
+ this.reconnectWithSessionHandlers = [];
27
33
  // Promise resolvers for spawn/join
28
34
  this.spawnResolve = null;
29
35
  this.spawnReject = null;
@@ -58,17 +64,25 @@ var TerminalClient = class {
58
64
  this.state = "connected";
59
65
  this.reconnectAttempts = 0;
60
66
  this.connectHandlers.forEach((handler) => handler());
67
+ if (this.isReconnecting && this.previousSessionId) {
68
+ this.checkPreviousSessionAndNotify();
69
+ }
70
+ this.isReconnecting = false;
61
71
  resolve();
62
72
  };
63
73
  this.ws.onclose = () => {
64
74
  const wasConnected = this.state === "connected";
65
75
  this.state = "disconnected";
76
+ if (this.sessionId) {
77
+ this.previousSessionId = this.sessionId;
78
+ }
66
79
  this.sessionId = null;
67
80
  this.sessionInfo = null;
68
81
  if (wasConnected) {
69
82
  this.disconnectHandlers.forEach((handler) => handler());
70
83
  }
71
84
  if (this.config.reconnect && this.reconnectAttempts < this.config.maxReconnectAttempts) {
85
+ this.isReconnecting = true;
72
86
  this.scheduleReconnect();
73
87
  }
74
88
  };
@@ -543,8 +557,54 @@ var TerminalClient = class {
543
557
  getServerInfo() {
544
558
  return this.serverInfo;
545
559
  }
560
+ /**
561
+ * Get previous session ID (available after disconnect)
562
+ */
563
+ getPreviousSessionId() {
564
+ return this.previousSessionId;
565
+ }
566
+ /**
567
+ * Clear previous session ID (call after user declines to rejoin)
568
+ */
569
+ clearPreviousSessionId() {
570
+ this.previousSessionId = null;
571
+ }
572
+ /**
573
+ * Called when reconnected and previous session is available
574
+ */
575
+ onReconnectWithSession(handler) {
576
+ this.reconnectWithSessionHandlers.push(handler);
577
+ }
578
+ /**
579
+ * Check if previous session exists and notify handlers
580
+ */
581
+ async checkPreviousSessionAndNotify() {
582
+ if (!this.previousSessionId)
583
+ return;
584
+ try {
585
+ const sessions = await this.listSessions();
586
+ const previousSession = sessions.find(
587
+ (s) => s.sessionId === this.previousSessionId
588
+ );
589
+ if (previousSession && previousSession.accepting) {
590
+ this.reconnectWithSessionHandlers.forEach((handler) => {
591
+ try {
592
+ handler(this.previousSessionId);
593
+ } catch (e) {
594
+ console.error("[lit-shell] Error in reconnectWithSession handler:", e);
595
+ }
596
+ });
597
+ } else {
598
+ this.previousSessionId = null;
599
+ }
600
+ } catch (e) {
601
+ console.error("[lit-shell] Failed to check previous session:", e);
602
+ this.previousSessionId = null;
603
+ }
604
+ }
546
605
  };
547
606
  export {
548
- TerminalClient
607
+ TerminalClient,
608
+ VERSION
549
609
  };
550
610
  //# sourceMappingURL=browser-bundle.js.map