x-shell.js 0.1.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 (38) hide show
  1. package/README.md +304 -0
  2. package/dist/client/browser-bundle.js +314 -0
  3. package/dist/client/browser-bundle.js.map +7 -0
  4. package/dist/client/index.d.ts +7 -0
  5. package/dist/client/index.d.ts.map +1 -0
  6. package/dist/client/index.js +5 -0
  7. package/dist/client/index.js.map +1 -0
  8. package/dist/client/terminal-client.d.ts +122 -0
  9. package/dist/client/terminal-client.d.ts.map +1 -0
  10. package/dist/client/terminal-client.js +328 -0
  11. package/dist/client/terminal-client.js.map +1 -0
  12. package/dist/server/index.d.ts +7 -0
  13. package/dist/server/index.d.ts.map +1 -0
  14. package/dist/server/index.js +5 -0
  15. package/dist/server/index.js.map +1 -0
  16. package/dist/server/terminal-server.d.ts +107 -0
  17. package/dist/server/terminal-server.d.ts.map +1 -0
  18. package/dist/server/terminal-server.js +392 -0
  19. package/dist/server/terminal-server.js.map +1 -0
  20. package/dist/shared/types.d.ts +133 -0
  21. package/dist/shared/types.d.ts.map +1 -0
  22. package/dist/shared/types.js +5 -0
  23. package/dist/shared/types.js.map +1 -0
  24. package/dist/ui/browser-bundle.js +1654 -0
  25. package/dist/ui/browser-bundle.js.map +7 -0
  26. package/dist/ui/index.d.ts +6 -0
  27. package/dist/ui/index.d.ts.map +1 -0
  28. package/dist/ui/index.js +6 -0
  29. package/dist/ui/index.js.map +1 -0
  30. package/dist/ui/styles.d.ts +16 -0
  31. package/dist/ui/styles.d.ts.map +1 -0
  32. package/dist/ui/styles.js +125 -0
  33. package/dist/ui/styles.js.map +1 -0
  34. package/dist/ui/x-shell-terminal.d.ts +100 -0
  35. package/dist/ui/x-shell-terminal.d.ts.map +1 -0
  36. package/dist/ui/x-shell-terminal.js +540 -0
  37. package/dist/ui/x-shell-terminal.js.map +1 -0
  38. package/package.json +94 -0
@@ -0,0 +1,540 @@
1
+ /**
2
+ * x-shell-terminal web component
3
+ *
4
+ * A ready-to-use terminal component that wraps xterm.js and
5
+ * connects to an x-shell server via WebSocket.
6
+ *
7
+ * Usage:
8
+ * ```html
9
+ * <x-shell-terminal
10
+ * url="ws://localhost:3000/terminal"
11
+ * shell="/bin/bash"
12
+ * cwd="/home/user"
13
+ * theme="dark"
14
+ * ></x-shell-terminal>
15
+ * ```
16
+ */
17
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
18
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
19
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
20
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
21
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
22
+ };
23
+ import { LitElement, html, css, nothing } from 'lit';
24
+ import { customElement, property, state } from 'lit/decorators.js';
25
+ import { sharedStyles, buttonStyles, themeStyles } from './styles.js';
26
+ import { TerminalClient } from '../client/terminal-client.js';
27
+ let XShellTerminal = class XShellTerminal extends LitElement {
28
+ constructor() {
29
+ super(...arguments);
30
+ // Connection properties
31
+ this.url = '';
32
+ this.shell = '';
33
+ this.cwd = '';
34
+ this.cols = 80;
35
+ this.rows = 24;
36
+ this.theme = 'dark';
37
+ this.noHeader = false;
38
+ this.autoConnect = false;
39
+ this.autoSpawn = false;
40
+ // Terminal appearance
41
+ this.fontSize = 14;
42
+ this.fontFamily = 'Menlo, Monaco, "Courier New", monospace';
43
+ // State
44
+ this.client = null;
45
+ this.terminal = null;
46
+ this.fitAddon = null;
47
+ this.connected = false;
48
+ this.sessionActive = false;
49
+ this.loading = false;
50
+ this.error = null;
51
+ this.sessionInfo = null;
52
+ // xterm.js module (loaded dynamically)
53
+ this.xtermModule = null;
54
+ this.fitAddonModule = null;
55
+ this.resizeObserver = null;
56
+ }
57
+ connectedCallback() {
58
+ super.connectedCallback();
59
+ if (this.autoConnect && this.url) {
60
+ this.connect();
61
+ }
62
+ }
63
+ disconnectedCallback() {
64
+ super.disconnectedCallback();
65
+ this.cleanup();
66
+ }
67
+ /**
68
+ * Load xterm.js dynamically
69
+ */
70
+ async loadXterm() {
71
+ if (this.xtermModule)
72
+ return;
73
+ try {
74
+ // Try to import from CDN
75
+ // @ts-ignore - Dynamic import from CDN
76
+ this.xtermModule = await import('https://cdn.jsdelivr.net/npm/xterm@5.3.0/+esm');
77
+ // @ts-ignore - Dynamic import from CDN
78
+ this.fitAddonModule = await import('https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.8.0/+esm');
79
+ }
80
+ catch (e) {
81
+ // Fallback to npm package if available
82
+ try {
83
+ // @ts-ignore - Optional peer dependency
84
+ this.xtermModule = await import('xterm');
85
+ // @ts-ignore - Optional peer dependency
86
+ this.fitAddonModule = await import('xterm-addon-fit');
87
+ }
88
+ catch {
89
+ throw new Error('Failed to load xterm.js. Make sure it is available.');
90
+ }
91
+ }
92
+ }
93
+ /**
94
+ * Connect to the terminal server
95
+ */
96
+ async connect() {
97
+ if (!this.url) {
98
+ this.error = 'No URL specified';
99
+ return;
100
+ }
101
+ this.loading = true;
102
+ this.error = null;
103
+ try {
104
+ // Load xterm.js
105
+ await this.loadXterm();
106
+ // Create client
107
+ this.client = new TerminalClient({ url: this.url });
108
+ this.client.onConnect(() => {
109
+ this.connected = true;
110
+ this.dispatchEvent(new CustomEvent('connect', { bubbles: true, composed: true }));
111
+ if (this.autoSpawn) {
112
+ this.spawn();
113
+ }
114
+ });
115
+ this.client.onDisconnect(() => {
116
+ this.connected = false;
117
+ this.sessionActive = false;
118
+ this.dispatchEvent(new CustomEvent('disconnect', { bubbles: true, composed: true }));
119
+ });
120
+ this.client.onError((err) => {
121
+ this.error = err.message;
122
+ this.dispatchEvent(new CustomEvent('error', { detail: { error: err }, bubbles: true, composed: true }));
123
+ });
124
+ this.client.onData((data) => {
125
+ if (this.terminal) {
126
+ this.terminal.write(data);
127
+ }
128
+ });
129
+ this.client.onExit((code) => {
130
+ this.sessionActive = false;
131
+ this.sessionInfo = null;
132
+ if (this.terminal) {
133
+ this.terminal.writeln('');
134
+ this.terminal.writeln(`\x1b[1;33m[Process exited with code: ${code}]\x1b[0m`);
135
+ }
136
+ this.dispatchEvent(new CustomEvent('exit', { detail: { exitCode: code }, bubbles: true, composed: true }));
137
+ });
138
+ this.client.onSpawned((info) => {
139
+ this.sessionInfo = info;
140
+ this.dispatchEvent(new CustomEvent('spawned', { detail: { session: info }, bubbles: true, composed: true }));
141
+ });
142
+ await this.client.connect();
143
+ }
144
+ catch (err) {
145
+ this.error = err instanceof Error ? err.message : 'Connection failed';
146
+ }
147
+ finally {
148
+ this.loading = false;
149
+ }
150
+ }
151
+ /**
152
+ * Disconnect from the server
153
+ */
154
+ disconnect() {
155
+ if (this.client) {
156
+ this.client.disconnect();
157
+ this.client = null;
158
+ }
159
+ this.connected = false;
160
+ this.sessionActive = false;
161
+ }
162
+ /**
163
+ * Spawn a terminal session
164
+ */
165
+ async spawn(options) {
166
+ if (!this.client || !this.connected) {
167
+ throw new Error('Not connected to server');
168
+ }
169
+ this.loading = true;
170
+ this.error = null;
171
+ try {
172
+ // Initialize terminal UI if needed
173
+ await this.initTerminalUI();
174
+ // Spawn session
175
+ const spawnOptions = {
176
+ shell: options?.shell || this.shell || undefined,
177
+ cwd: options?.cwd || this.cwd || undefined,
178
+ cols: this.terminal?.cols || this.cols,
179
+ rows: this.terminal?.rows || this.rows,
180
+ env: options?.env,
181
+ };
182
+ const info = await this.client.spawn(spawnOptions);
183
+ this.sessionActive = true;
184
+ this.sessionInfo = info;
185
+ // Focus terminal
186
+ if (this.terminal) {
187
+ this.terminal.focus();
188
+ }
189
+ }
190
+ catch (err) {
191
+ this.error = err instanceof Error ? err.message : 'Failed to spawn session';
192
+ }
193
+ finally {
194
+ this.loading = false;
195
+ }
196
+ }
197
+ /**
198
+ * Initialize xterm.js UI
199
+ */
200
+ async initTerminalUI() {
201
+ if (this.terminal)
202
+ return;
203
+ await this.loadXterm();
204
+ await this.updateComplete;
205
+ const container = this.shadowRoot?.querySelector('.terminal-container');
206
+ if (!container)
207
+ return;
208
+ // Get theme colors
209
+ const terminalTheme = this.getTerminalTheme();
210
+ // Create terminal
211
+ const Terminal = this.xtermModule.Terminal;
212
+ const term = new Terminal({
213
+ cursorBlink: true,
214
+ fontSize: this.fontSize,
215
+ fontFamily: this.fontFamily,
216
+ theme: terminalTheme,
217
+ cols: this.cols,
218
+ rows: this.rows,
219
+ });
220
+ // Create fit addon
221
+ const FitAddon = this.fitAddonModule.FitAddon;
222
+ const fit = new FitAddon();
223
+ // Store references
224
+ this.terminal = term;
225
+ this.fitAddon = fit;
226
+ term.loadAddon(fit);
227
+ // Open terminal
228
+ term.open(container);
229
+ fit.fit();
230
+ // Handle user input
231
+ term.onData((data) => {
232
+ if (this.client && this.sessionActive) {
233
+ this.client.write(data);
234
+ }
235
+ });
236
+ // Handle resize
237
+ term.onResize(({ cols, rows }) => {
238
+ if (this.client && this.sessionActive) {
239
+ this.client.resize(cols, rows);
240
+ }
241
+ });
242
+ // Setup resize observer
243
+ this.resizeObserver = new ResizeObserver(() => {
244
+ if (this.fitAddon) {
245
+ this.fitAddon.fit();
246
+ }
247
+ });
248
+ this.resizeObserver.observe(container);
249
+ }
250
+ /**
251
+ * Get terminal theme based on component theme
252
+ */
253
+ getTerminalTheme() {
254
+ // These will be overridden by CSS variables in the actual implementation
255
+ // For now, provide sensible defaults based on theme attribute
256
+ if (this.theme === 'light') {
257
+ return {
258
+ background: '#ffffff',
259
+ foreground: '#1f2937',
260
+ cursor: '#1f2937',
261
+ selection: '#b4d5fe',
262
+ };
263
+ }
264
+ return {
265
+ background: '#1e1e1e',
266
+ foreground: '#cccccc',
267
+ cursor: '#ffffff',
268
+ selection: '#264f78',
269
+ };
270
+ }
271
+ /**
272
+ * Kill the current session
273
+ */
274
+ kill() {
275
+ if (this.client) {
276
+ this.client.kill();
277
+ }
278
+ this.sessionActive = false;
279
+ this.sessionInfo = null;
280
+ }
281
+ /**
282
+ * Clear the terminal
283
+ */
284
+ clear() {
285
+ if (this.terminal) {
286
+ this.terminal.clear();
287
+ }
288
+ }
289
+ /**
290
+ * Write data to the terminal (display only, not sent to server)
291
+ */
292
+ write(data) {
293
+ if (this.terminal) {
294
+ this.terminal.write(data);
295
+ }
296
+ }
297
+ /**
298
+ * Write line to the terminal (display only, not sent to server)
299
+ */
300
+ writeln(data) {
301
+ if (this.terminal) {
302
+ this.terminal.writeln(data);
303
+ }
304
+ }
305
+ /**
306
+ * Focus the terminal
307
+ */
308
+ focus() {
309
+ if (this.terminal) {
310
+ this.terminal.focus();
311
+ }
312
+ }
313
+ /**
314
+ * Cleanup resources
315
+ */
316
+ cleanup() {
317
+ if (this.resizeObserver) {
318
+ this.resizeObserver.disconnect();
319
+ this.resizeObserver = null;
320
+ }
321
+ if (this.terminal) {
322
+ this.terminal.dispose();
323
+ this.terminal = null;
324
+ }
325
+ if (this.client) {
326
+ this.client.disconnect();
327
+ this.client = null;
328
+ }
329
+ this.fitAddon = null;
330
+ }
331
+ render() {
332
+ return html `
333
+ ${this.noHeader
334
+ ? nothing
335
+ : html `
336
+ <div class="header">
337
+ <div class="header-title">
338
+ <span>Terminal</span>
339
+ ${this.sessionInfo
340
+ ? html `<span style="font-weight: normal; font-size: 12px; color: var(--xs-text-muted)">
341
+ ${this.sessionInfo.shell}
342
+ </span>`
343
+ : nothing}
344
+ </div>
345
+ <div class="header-actions">
346
+ ${!this.connected
347
+ ? html `<button @click=${this.connect} ?disabled=${this.loading}>
348
+ ${this.loading ? 'Connecting...' : 'Connect'}
349
+ </button>`
350
+ : !this.sessionActive
351
+ ? html `<button @click=${() => this.spawn()} ?disabled=${this.loading}>
352
+ ${this.loading ? 'Spawning...' : 'Start'}
353
+ </button>`
354
+ : html `<button @click=${this.kill}>Stop</button>`}
355
+ <button @click=${this.clear} ?disabled=${!this.sessionActive}>Clear</button>
356
+ <div class="status">
357
+ <span class="status-dot ${this.connected ? 'connected' : ''}"></span>
358
+ <span>${this.connected ? 'Connected' : 'Disconnected'}</span>
359
+ </div>
360
+ </div>
361
+ </div>
362
+ `}
363
+
364
+ <div class="terminal-container">
365
+ ${this.loading && !this.terminal
366
+ ? html `<div class="loading"><span class="loading-spinner">⏳</span> Loading...</div>`
367
+ : this.error && !this.terminal
368
+ ? html `<div class="error">❌ ${this.error}</div>`
369
+ : nothing}
370
+ </div>
371
+ `;
372
+ }
373
+ };
374
+ XShellTerminal.styles = [
375
+ sharedStyles,
376
+ themeStyles,
377
+ buttonStyles,
378
+ css `
379
+ :host {
380
+ display: flex;
381
+ flex-direction: column;
382
+ height: 100%;
383
+ min-height: 200px;
384
+ border: 1px solid var(--xs-border);
385
+ border-radius: 4px;
386
+ overflow: hidden;
387
+ }
388
+
389
+ .header {
390
+ display: flex;
391
+ align-items: center;
392
+ justify-content: space-between;
393
+ padding: 8px 12px;
394
+ background: var(--xs-bg-header);
395
+ border-bottom: 1px solid var(--xs-border);
396
+ }
397
+
398
+ .header-title {
399
+ display: flex;
400
+ align-items: center;
401
+ gap: 8px;
402
+ font-weight: 600;
403
+ }
404
+
405
+ .header-actions {
406
+ display: flex;
407
+ gap: 8px;
408
+ }
409
+
410
+ .status {
411
+ display: flex;
412
+ align-items: center;
413
+ gap: 6px;
414
+ font-size: 12px;
415
+ color: var(--xs-text-muted);
416
+ }
417
+
418
+ .status-dot {
419
+ width: 8px;
420
+ height: 8px;
421
+ border-radius: 50%;
422
+ background: var(--xs-status-disconnected);
423
+ }
424
+
425
+ .status-dot.connected {
426
+ background: var(--xs-status-connected);
427
+ }
428
+
429
+ .terminal-container {
430
+ flex: 1;
431
+ padding: 4px;
432
+ background: var(--xs-terminal-bg);
433
+ overflow: hidden;
434
+ }
435
+
436
+ .terminal-container .xterm {
437
+ height: 100%;
438
+ }
439
+
440
+ .terminal-container .xterm-viewport {
441
+ overflow-y: auto;
442
+ }
443
+
444
+ .loading,
445
+ .error {
446
+ display: flex;
447
+ align-items: center;
448
+ justify-content: center;
449
+ height: 100%;
450
+ padding: 20px;
451
+ text-align: center;
452
+ color: var(--xs-text-muted);
453
+ }
454
+
455
+ .error {
456
+ color: #ef4444;
457
+ }
458
+
459
+ .loading-spinner {
460
+ animation: spin 1s linear infinite;
461
+ margin-right: 8px;
462
+ }
463
+
464
+ @keyframes spin {
465
+ from {
466
+ transform: rotate(0deg);
467
+ }
468
+ to {
469
+ transform: rotate(360deg);
470
+ }
471
+ }
472
+
473
+ /* Hide header if requested */
474
+ :host([no-header]) .header {
475
+ display: none;
476
+ }
477
+ `,
478
+ ];
479
+ __decorate([
480
+ property({ type: String })
481
+ ], XShellTerminal.prototype, "url", void 0);
482
+ __decorate([
483
+ property({ type: String })
484
+ ], XShellTerminal.prototype, "shell", void 0);
485
+ __decorate([
486
+ property({ type: String })
487
+ ], XShellTerminal.prototype, "cwd", void 0);
488
+ __decorate([
489
+ property({ type: Number })
490
+ ], XShellTerminal.prototype, "cols", void 0);
491
+ __decorate([
492
+ property({ type: Number })
493
+ ], XShellTerminal.prototype, "rows", void 0);
494
+ __decorate([
495
+ property({ type: String, reflect: true })
496
+ ], XShellTerminal.prototype, "theme", void 0);
497
+ __decorate([
498
+ property({ type: Boolean, attribute: 'no-header' })
499
+ ], XShellTerminal.prototype, "noHeader", void 0);
500
+ __decorate([
501
+ property({ type: Boolean, attribute: 'auto-connect' })
502
+ ], XShellTerminal.prototype, "autoConnect", void 0);
503
+ __decorate([
504
+ property({ type: Boolean, attribute: 'auto-spawn' })
505
+ ], XShellTerminal.prototype, "autoSpawn", void 0);
506
+ __decorate([
507
+ property({ type: Number, attribute: 'font-size' })
508
+ ], XShellTerminal.prototype, "fontSize", void 0);
509
+ __decorate([
510
+ property({ type: String, attribute: 'font-family' })
511
+ ], XShellTerminal.prototype, "fontFamily", void 0);
512
+ __decorate([
513
+ state()
514
+ ], XShellTerminal.prototype, "client", void 0);
515
+ __decorate([
516
+ state()
517
+ ], XShellTerminal.prototype, "terminal", void 0);
518
+ __decorate([
519
+ state()
520
+ ], XShellTerminal.prototype, "fitAddon", void 0);
521
+ __decorate([
522
+ state()
523
+ ], XShellTerminal.prototype, "connected", void 0);
524
+ __decorate([
525
+ state()
526
+ ], XShellTerminal.prototype, "sessionActive", void 0);
527
+ __decorate([
528
+ state()
529
+ ], XShellTerminal.prototype, "loading", void 0);
530
+ __decorate([
531
+ state()
532
+ ], XShellTerminal.prototype, "error", void 0);
533
+ __decorate([
534
+ state()
535
+ ], XShellTerminal.prototype, "sessionInfo", void 0);
536
+ XShellTerminal = __decorate([
537
+ customElement('x-shell-terminal')
538
+ ], XShellTerminal);
539
+ export { XShellTerminal };
540
+ //# sourceMappingURL=x-shell-terminal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"x-shell-terminal.js","sourceRoot":"","sources":["../../src/ui/x-shell-terminal.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,94 @@
1
+ {
2
+ "name": "x-shell.js",
3
+ "version": "0.1.0",
4
+ "description": "WebSocket-based terminal for Node.js - the truth is in your shell",
5
+ "type": "module",
6
+ "main": "dist/server/index.js",
7
+ "types": "dist/server/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/server/index.d.ts",
11
+ "import": "./dist/server/index.js"
12
+ },
13
+ "./server": {
14
+ "types": "./dist/server/index.d.ts",
15
+ "import": "./dist/server/index.js"
16
+ },
17
+ "./client": {
18
+ "types": "./dist/client/index.d.ts",
19
+ "import": "./dist/client/index.js"
20
+ },
21
+ "./client/browser": {
22
+ "types": "./dist/client/index.d.ts",
23
+ "import": "./dist/client/browser-bundle.js"
24
+ },
25
+ "./ui": {
26
+ "types": "./dist/ui/index.d.ts",
27
+ "import": "./dist/ui/index.js"
28
+ },
29
+ "./ui/browser": {
30
+ "types": "./dist/ui/index.d.ts",
31
+ "import": "./dist/ui/browser-bundle.js"
32
+ }
33
+ },
34
+ "files": [
35
+ "dist",
36
+ "README.md",
37
+ "LICENSE"
38
+ ],
39
+ "scripts": {
40
+ "build": "tsc && npm run build:browser",
41
+ "build:browser": "node scripts/build-browser.js",
42
+ "watch": "tsc --watch",
43
+ "clean": "rm -rf dist",
44
+ "prepublishOnly": "npm run clean && npm run build",
45
+ "test": "echo \"Tests coming soon\" && exit 0"
46
+ },
47
+ "keywords": [
48
+ "terminal",
49
+ "websocket",
50
+ "pty",
51
+ "xterm",
52
+ "shell",
53
+ "web-terminal",
54
+ "node-pty",
55
+ "lit",
56
+ "web-components",
57
+ "custom-elements"
58
+ ],
59
+ "author": "",
60
+ "license": "MIT",
61
+ "repository": {
62
+ "type": "git",
63
+ "url": "git+https://github.com/anthropics/x-shell.git"
64
+ },
65
+ "bugs": {
66
+ "url": "https://github.com/anthropics/x-shell/issues"
67
+ },
68
+ "homepage": "https://github.com/anthropics/x-shell#readme",
69
+ "engines": {
70
+ "node": ">=18.0.0"
71
+ },
72
+ "dependencies": {
73
+ "lit": "^3.3.2",
74
+ "ws": "^8.14.0"
75
+ },
76
+ "devDependencies": {
77
+ "@types/node": "^20.10.0",
78
+ "@types/ws": "^8.5.10",
79
+ "esbuild": "^0.19.0",
80
+ "typescript": "^5.3.0"
81
+ },
82
+ "peerDependencies": {
83
+ "express": "^4.18.0 || ^5.0.0",
84
+ "node-pty": "^1.0.0"
85
+ },
86
+ "peerDependenciesMeta": {
87
+ "express": {
88
+ "optional": true
89
+ },
90
+ "node-pty": {
91
+ "optional": true
92
+ }
93
+ }
94
+ }