lit-shell.js 0.1.3

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 (39) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +304 -0
  3. package/dist/client/browser-bundle.js +314 -0
  4. package/dist/client/browser-bundle.js.map +7 -0
  5. package/dist/client/index.d.ts +7 -0
  6. package/dist/client/index.d.ts.map +1 -0
  7. package/dist/client/index.js +5 -0
  8. package/dist/client/index.js.map +1 -0
  9. package/dist/client/terminal-client.d.ts +122 -0
  10. package/dist/client/terminal-client.d.ts.map +1 -0
  11. package/dist/client/terminal-client.js +328 -0
  12. package/dist/client/terminal-client.js.map +1 -0
  13. package/dist/server/index.d.ts +7 -0
  14. package/dist/server/index.d.ts.map +1 -0
  15. package/dist/server/index.js +5 -0
  16. package/dist/server/index.js.map +1 -0
  17. package/dist/server/terminal-server.d.ts +109 -0
  18. package/dist/server/terminal-server.d.ts.map +1 -0
  19. package/dist/server/terminal-server.js +393 -0
  20. package/dist/server/terminal-server.js.map +1 -0
  21. package/dist/shared/types.d.ts +133 -0
  22. package/dist/shared/types.d.ts.map +1 -0
  23. package/dist/shared/types.js +5 -0
  24. package/dist/shared/types.js.map +1 -0
  25. package/dist/ui/browser-bundle.js +1678 -0
  26. package/dist/ui/browser-bundle.js.map +7 -0
  27. package/dist/ui/index.d.ts +6 -0
  28. package/dist/ui/index.d.ts.map +1 -0
  29. package/dist/ui/index.js +6 -0
  30. package/dist/ui/index.js.map +1 -0
  31. package/dist/ui/lit-shell-terminal.d.ts +105 -0
  32. package/dist/ui/lit-shell-terminal.d.ts.map +1 -0
  33. package/dist/ui/lit-shell-terminal.js +570 -0
  34. package/dist/ui/lit-shell-terminal.js.map +1 -0
  35. package/dist/ui/styles.d.ts +16 -0
  36. package/dist/ui/styles.d.ts.map +1 -0
  37. package/dist/ui/styles.js +125 -0
  38. package/dist/ui/styles.js.map +1 -0
  39. package/package.json +94 -0
@@ -0,0 +1,6 @@
1
+ /**
2
+ * lit-shell.js UI exports
3
+ */
4
+ export { LitShellTerminal } from './lit-shell-terminal.js';
5
+ export { sharedStyles, buttonStyles, themeStyles } from './styles.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * lit-shell.js UI exports
3
+ */
4
+ export { LitShellTerminal } from './lit-shell-terminal.js';
5
+ export { sharedStyles, buttonStyles, themeStyles } from './styles.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * lit-shell-terminal web component
3
+ *
4
+ * A ready-to-use terminal component that wraps xterm.js and
5
+ * connects to a lit-shell server via WebSocket.
6
+ *
7
+ * Usage:
8
+ * ```html
9
+ * <lit-shell-terminal
10
+ * url="ws://localhost:3000/terminal"
11
+ * shell="/bin/bash"
12
+ * cwd="/home/user"
13
+ * theme="dark"
14
+ * ></lit-shell-terminal>
15
+ * ```
16
+ */
17
+ import { LitElement } from 'lit';
18
+ import type { TerminalOptions } from '../shared/types.js';
19
+ export declare class LitShellTerminal extends LitElement {
20
+ static styles: import("lit").CSSResult[];
21
+ url: string;
22
+ shell: string;
23
+ cwd: string;
24
+ cols: number;
25
+ rows: number;
26
+ theme: 'dark' | 'light' | 'auto';
27
+ noHeader: boolean;
28
+ autoConnect: boolean;
29
+ autoSpawn: boolean;
30
+ fontSize: number;
31
+ fontFamily: string;
32
+ private client;
33
+ private terminal;
34
+ private fitAddon;
35
+ private connected;
36
+ private sessionActive;
37
+ private loading;
38
+ private error;
39
+ private sessionInfo;
40
+ private xtermModule;
41
+ private fitAddonModule;
42
+ private resizeObserver;
43
+ connectedCallback(): void;
44
+ disconnectedCallback(): void;
45
+ /**
46
+ * Load xterm.js dynamically
47
+ */
48
+ private loadXterm;
49
+ /**
50
+ * Load xterm.css into the shadow DOM
51
+ * This is necessary because CSS loaded in the main document doesn't apply inside shadow DOM
52
+ */
53
+ private loadXtermStyles;
54
+ /**
55
+ * Connect to the terminal server
56
+ */
57
+ connect(): Promise<void>;
58
+ /**
59
+ * Disconnect from the server
60
+ */
61
+ disconnect(): void;
62
+ /**
63
+ * Spawn a terminal session
64
+ */
65
+ spawn(options?: TerminalOptions): Promise<void>;
66
+ /**
67
+ * Initialize xterm.js UI
68
+ */
69
+ private initTerminalUI;
70
+ /**
71
+ * Get terminal theme based on component theme
72
+ */
73
+ private getTerminalTheme;
74
+ /**
75
+ * Kill the current session
76
+ */
77
+ kill(): void;
78
+ /**
79
+ * Clear the terminal
80
+ */
81
+ clear(): void;
82
+ /**
83
+ * Write data to the terminal (display only, not sent to server)
84
+ */
85
+ write(data: string): void;
86
+ /**
87
+ * Write line to the terminal (display only, not sent to server)
88
+ */
89
+ writeln(data: string): void;
90
+ /**
91
+ * Focus the terminal
92
+ */
93
+ focus(): void;
94
+ /**
95
+ * Cleanup resources
96
+ */
97
+ private cleanup;
98
+ render(): import("lit-html").TemplateResult<1>;
99
+ }
100
+ declare global {
101
+ interface HTMLElementTagNameMap {
102
+ 'lit-shell-terminal': LitShellTerminal;
103
+ }
104
+ }
105
+ //# sourceMappingURL=lit-shell-terminal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lit-shell-terminal.d.ts","sourceRoot":"","sources":["../../src/ui/lit-shell-terminal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,UAAU,EAAsB,MAAM,KAAK,CAAC;AAIrD,OAAO,KAAK,EAAE,eAAe,EAAe,MAAM,oBAAoB,CAAC;AAsBvE,qBACa,gBAAiB,SAAQ,UAAU;IAC9C,OAAgB,MAAM,4BAwGpB;IAG0B,GAAG,SAAM;IACT,KAAK,SAAM;IACX,GAAG,SAAM;IACT,IAAI,SAAM;IACV,IAAI,SAAM;IACK,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAU;IAChC,QAAQ,UAAS;IACd,WAAW,UAAS;IACtB,SAAS,UAAS;IAGpB,QAAQ,SAAM;IACZ,UAAU,SACpB;IAGnC,OAAO,CAAC,MAAM,CAA+B;IAC7C,OAAO,CAAC,QAAQ,CAA0B;IAC1C,OAAO,CAAC,QAAQ,CAA0B;IAC1C,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,WAAW,CAA4B;IAGxD,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,cAAc,CAA+B;IAE5C,iBAAiB;IAQjB,oBAAoB;IAK7B;;OAEG;YACW,SAAS;IAyBvB;;;OAGG;YACW,eAAe;IAyB7B;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAuE9B;;OAEG;IACH,UAAU,IAAI,IAAI;IASlB;;OAEG;IACG,KAAK,CAAC,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAoCrD;;OAEG;YACW,cAAc;IA4D5B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAoBxB;;OAEG;IACH,IAAI,IAAI,IAAI;IAQZ;;OAEG;IACH,KAAK,IAAI,IAAI;IAMb;;OAEG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAMzB;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAM3B;;OAEG;IACM,KAAK,IAAI,IAAI;IAMtB;;OAEG;IACH,OAAO,CAAC,OAAO;IAmBN,MAAM;CA0ChB;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,oBAAoB,EAAE,gBAAgB,CAAC;KACxC;CACF"}
@@ -0,0 +1,570 @@
1
+ /**
2
+ * lit-shell-terminal web component
3
+ *
4
+ * A ready-to-use terminal component that wraps xterm.js and
5
+ * connects to a lit-shell server via WebSocket.
6
+ *
7
+ * Usage:
8
+ * ```html
9
+ * <lit-shell-terminal
10
+ * url="ws://localhost:3000/terminal"
11
+ * shell="/bin/bash"
12
+ * cwd="/home/user"
13
+ * theme="dark"
14
+ * ></lit-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 LitShellTerminal = class LitShellTerminal 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
+ // Load xterm.css into shadow DOM (CSS doesn't penetrate shadow boundaries)
93
+ await this.loadXtermStyles();
94
+ }
95
+ /**
96
+ * Load xterm.css into the shadow DOM
97
+ * This is necessary because CSS loaded in the main document doesn't apply inside shadow DOM
98
+ */
99
+ async loadXtermStyles() {
100
+ if (!this.shadowRoot)
101
+ return;
102
+ // Check if styles are already loaded
103
+ if (this.shadowRoot.querySelector('#xterm-styles'))
104
+ return;
105
+ try {
106
+ // Fetch xterm.css from CDN
107
+ const response = await fetch('https://cdn.jsdelivr.net/npm/xterm@5.3.0/css/xterm.css');
108
+ if (!response.ok) {
109
+ throw new Error(`Failed to fetch xterm.css: ${response.status}`);
110
+ }
111
+ const cssText = await response.text();
112
+ // Create style element and inject into shadow DOM
113
+ const style = document.createElement('style');
114
+ style.id = 'xterm-styles';
115
+ style.textContent = cssText;
116
+ this.shadowRoot.appendChild(style);
117
+ }
118
+ catch (error) {
119
+ console.warn('[lit-shell] Failed to load xterm.css:', error);
120
+ // Terminal will still work but may have visual issues
121
+ }
122
+ }
123
+ /**
124
+ * Connect to the terminal server
125
+ */
126
+ async connect() {
127
+ if (!this.url) {
128
+ this.error = 'No URL specified';
129
+ return;
130
+ }
131
+ this.loading = true;
132
+ this.error = null;
133
+ try {
134
+ // Load xterm.js
135
+ await this.loadXterm();
136
+ // Create client
137
+ this.client = new TerminalClient({ url: this.url });
138
+ this.client.onConnect(() => {
139
+ this.connected = true;
140
+ this.dispatchEvent(new CustomEvent('connect', { bubbles: true, composed: true }));
141
+ if (this.autoSpawn) {
142
+ this.spawn();
143
+ }
144
+ });
145
+ this.client.onDisconnect(() => {
146
+ this.connected = false;
147
+ this.sessionActive = false;
148
+ this.dispatchEvent(new CustomEvent('disconnect', { bubbles: true, composed: true }));
149
+ });
150
+ this.client.onError((err) => {
151
+ this.error = err.message;
152
+ this.dispatchEvent(new CustomEvent('error', { detail: { error: err }, bubbles: true, composed: true }));
153
+ });
154
+ this.client.onData((data) => {
155
+ if (this.terminal) {
156
+ this.terminal.write(data);
157
+ }
158
+ });
159
+ this.client.onExit((code) => {
160
+ this.sessionActive = false;
161
+ this.sessionInfo = null;
162
+ if (this.terminal) {
163
+ this.terminal.writeln('');
164
+ this.terminal.writeln(`\x1b[1;33m[Process exited with code: ${code}]\x1b[0m`);
165
+ }
166
+ this.dispatchEvent(new CustomEvent('exit', { detail: { exitCode: code }, bubbles: true, composed: true }));
167
+ });
168
+ this.client.onSpawned((info) => {
169
+ this.sessionInfo = info;
170
+ this.dispatchEvent(new CustomEvent('spawned', { detail: { session: info }, bubbles: true, composed: true }));
171
+ });
172
+ await this.client.connect();
173
+ }
174
+ catch (err) {
175
+ this.error = err instanceof Error ? err.message : 'Connection failed';
176
+ }
177
+ finally {
178
+ this.loading = false;
179
+ }
180
+ }
181
+ /**
182
+ * Disconnect from the server
183
+ */
184
+ disconnect() {
185
+ if (this.client) {
186
+ this.client.disconnect();
187
+ this.client = null;
188
+ }
189
+ this.connected = false;
190
+ this.sessionActive = false;
191
+ }
192
+ /**
193
+ * Spawn a terminal session
194
+ */
195
+ async spawn(options) {
196
+ if (!this.client || !this.connected) {
197
+ throw new Error('Not connected to server');
198
+ }
199
+ this.loading = true;
200
+ this.error = null;
201
+ try {
202
+ // Initialize terminal UI if needed
203
+ await this.initTerminalUI();
204
+ // Spawn session
205
+ const spawnOptions = {
206
+ shell: options?.shell || this.shell || undefined,
207
+ cwd: options?.cwd || this.cwd || undefined,
208
+ cols: this.terminal?.cols || this.cols,
209
+ rows: this.terminal?.rows || this.rows,
210
+ env: options?.env,
211
+ };
212
+ const info = await this.client.spawn(spawnOptions);
213
+ this.sessionActive = true;
214
+ this.sessionInfo = info;
215
+ // Focus terminal
216
+ if (this.terminal) {
217
+ this.terminal.focus();
218
+ }
219
+ }
220
+ catch (err) {
221
+ this.error = err instanceof Error ? err.message : 'Failed to spawn session';
222
+ }
223
+ finally {
224
+ this.loading = false;
225
+ }
226
+ }
227
+ /**
228
+ * Initialize xterm.js UI
229
+ */
230
+ async initTerminalUI() {
231
+ if (this.terminal)
232
+ return;
233
+ await this.loadXterm();
234
+ await this.updateComplete;
235
+ const container = this.shadowRoot?.querySelector('.terminal-container');
236
+ if (!container)
237
+ return;
238
+ // Get theme colors
239
+ const terminalTheme = this.getTerminalTheme();
240
+ // Create terminal
241
+ const Terminal = this.xtermModule.Terminal;
242
+ const term = new Terminal({
243
+ cursorBlink: true,
244
+ fontSize: this.fontSize,
245
+ fontFamily: this.fontFamily,
246
+ theme: terminalTheme,
247
+ cols: this.cols,
248
+ rows: this.rows,
249
+ });
250
+ // Create fit addon
251
+ const FitAddon = this.fitAddonModule.FitAddon;
252
+ const fit = new FitAddon();
253
+ // Store references
254
+ this.terminal = term;
255
+ this.fitAddon = fit;
256
+ term.loadAddon(fit);
257
+ // Open terminal
258
+ term.open(container);
259
+ fit.fit();
260
+ // Handle user input
261
+ term.onData((data) => {
262
+ if (this.client && this.sessionActive) {
263
+ this.client.write(data);
264
+ }
265
+ });
266
+ // Handle resize
267
+ term.onResize(({ cols, rows }) => {
268
+ if (this.client && this.sessionActive) {
269
+ this.client.resize(cols, rows);
270
+ }
271
+ });
272
+ // Setup resize observer
273
+ this.resizeObserver = new ResizeObserver(() => {
274
+ if (this.fitAddon) {
275
+ this.fitAddon.fit();
276
+ }
277
+ });
278
+ this.resizeObserver.observe(container);
279
+ }
280
+ /**
281
+ * Get terminal theme based on component theme
282
+ */
283
+ getTerminalTheme() {
284
+ // These will be overridden by CSS variables in the actual implementation
285
+ // For now, provide sensible defaults based on theme attribute
286
+ if (this.theme === 'light') {
287
+ return {
288
+ background: '#ffffff',
289
+ foreground: '#1f2937',
290
+ cursor: '#1f2937',
291
+ selection: '#b4d5fe',
292
+ };
293
+ }
294
+ return {
295
+ background: '#1e1e1e',
296
+ foreground: '#cccccc',
297
+ cursor: '#ffffff',
298
+ selection: '#264f78',
299
+ };
300
+ }
301
+ /**
302
+ * Kill the current session
303
+ */
304
+ kill() {
305
+ if (this.client) {
306
+ this.client.kill();
307
+ }
308
+ this.sessionActive = false;
309
+ this.sessionInfo = null;
310
+ }
311
+ /**
312
+ * Clear the terminal
313
+ */
314
+ clear() {
315
+ if (this.terminal) {
316
+ this.terminal.clear();
317
+ }
318
+ }
319
+ /**
320
+ * Write data to the terminal (display only, not sent to server)
321
+ */
322
+ write(data) {
323
+ if (this.terminal) {
324
+ this.terminal.write(data);
325
+ }
326
+ }
327
+ /**
328
+ * Write line to the terminal (display only, not sent to server)
329
+ */
330
+ writeln(data) {
331
+ if (this.terminal) {
332
+ this.terminal.writeln(data);
333
+ }
334
+ }
335
+ /**
336
+ * Focus the terminal
337
+ */
338
+ focus() {
339
+ if (this.terminal) {
340
+ this.terminal.focus();
341
+ }
342
+ }
343
+ /**
344
+ * Cleanup resources
345
+ */
346
+ cleanup() {
347
+ if (this.resizeObserver) {
348
+ this.resizeObserver.disconnect();
349
+ this.resizeObserver = null;
350
+ }
351
+ if (this.terminal) {
352
+ this.terminal.dispose();
353
+ this.terminal = null;
354
+ }
355
+ if (this.client) {
356
+ this.client.disconnect();
357
+ this.client = null;
358
+ }
359
+ this.fitAddon = null;
360
+ }
361
+ render() {
362
+ return html `
363
+ ${this.noHeader
364
+ ? nothing
365
+ : html `
366
+ <div class="header">
367
+ <div class="header-title">
368
+ <span>Terminal</span>
369
+ ${this.sessionInfo
370
+ ? html `<span style="font-weight: normal; font-size: 12px; color: var(--ls-text-muted)">
371
+ ${this.sessionInfo.shell}
372
+ </span>`
373
+ : nothing}
374
+ </div>
375
+ <div class="header-actions">
376
+ ${!this.connected
377
+ ? html `<button @click=${this.connect} ?disabled=${this.loading}>
378
+ ${this.loading ? 'Connecting...' : 'Connect'}
379
+ </button>`
380
+ : !this.sessionActive
381
+ ? html `<button @click=${() => this.spawn()} ?disabled=${this.loading}>
382
+ ${this.loading ? 'Spawning...' : 'Start'}
383
+ </button>`
384
+ : html `<button @click=${this.kill}>Stop</button>`}
385
+ <button @click=${this.clear} ?disabled=${!this.sessionActive}>Clear</button>
386
+ <div class="status">
387
+ <span class="status-dot ${this.connected ? 'connected' : ''}"></span>
388
+ <span>${this.connected ? 'Connected' : 'Disconnected'}</span>
389
+ </div>
390
+ </div>
391
+ </div>
392
+ `}
393
+
394
+ <div class="terminal-container">
395
+ ${this.loading && !this.terminal
396
+ ? html `<div class="loading"><span class="loading-spinner">⏳</span> Loading...</div>`
397
+ : this.error && !this.terminal
398
+ ? html `<div class="error">❌ ${this.error}</div>`
399
+ : nothing}
400
+ </div>
401
+ `;
402
+ }
403
+ };
404
+ LitShellTerminal.styles = [
405
+ sharedStyles,
406
+ themeStyles,
407
+ buttonStyles,
408
+ css `
409
+ :host {
410
+ display: flex;
411
+ flex-direction: column;
412
+ height: 100%;
413
+ min-height: 200px;
414
+ border: 1px solid var(--ls-border);
415
+ border-radius: 4px;
416
+ overflow: hidden;
417
+ }
418
+
419
+ .header {
420
+ display: flex;
421
+ align-items: center;
422
+ justify-content: space-between;
423
+ padding: 8px 12px;
424
+ background: var(--ls-bg-header);
425
+ border-bottom: 1px solid var(--ls-border);
426
+ }
427
+
428
+ .header-title {
429
+ display: flex;
430
+ align-items: center;
431
+ gap: 8px;
432
+ font-weight: 600;
433
+ }
434
+
435
+ .header-actions {
436
+ display: flex;
437
+ gap: 8px;
438
+ }
439
+
440
+ .status {
441
+ display: flex;
442
+ align-items: center;
443
+ gap: 6px;
444
+ font-size: 12px;
445
+ color: var(--ls-text-muted);
446
+ }
447
+
448
+ .status-dot {
449
+ width: 8px;
450
+ height: 8px;
451
+ border-radius: 50%;
452
+ background: var(--ls-status-disconnected);
453
+ }
454
+
455
+ .status-dot.connected {
456
+ background: var(--ls-status-connected);
457
+ }
458
+
459
+ .terminal-container {
460
+ flex: 1;
461
+ padding: 4px;
462
+ background: var(--ls-terminal-bg);
463
+ overflow: hidden;
464
+ }
465
+
466
+ .terminal-container .xterm {
467
+ height: 100%;
468
+ }
469
+
470
+ .terminal-container .xterm-viewport {
471
+ overflow-y: auto;
472
+ }
473
+
474
+ .loading,
475
+ .error {
476
+ display: flex;
477
+ align-items: center;
478
+ justify-content: center;
479
+ height: 100%;
480
+ padding: 20px;
481
+ text-align: center;
482
+ color: var(--ls-text-muted);
483
+ }
484
+
485
+ .error {
486
+ color: #ef4444;
487
+ }
488
+
489
+ .loading-spinner {
490
+ animation: spin 1s linear infinite;
491
+ margin-right: 8px;
492
+ }
493
+
494
+ @keyframes spin {
495
+ from {
496
+ transform: rotate(0deg);
497
+ }
498
+ to {
499
+ transform: rotate(360deg);
500
+ }
501
+ }
502
+
503
+ /* Hide header if requested */
504
+ :host([no-header]) .header {
505
+ display: none;
506
+ }
507
+ `,
508
+ ];
509
+ __decorate([
510
+ property({ type: String })
511
+ ], LitShellTerminal.prototype, "url", void 0);
512
+ __decorate([
513
+ property({ type: String })
514
+ ], LitShellTerminal.prototype, "shell", void 0);
515
+ __decorate([
516
+ property({ type: String })
517
+ ], LitShellTerminal.prototype, "cwd", void 0);
518
+ __decorate([
519
+ property({ type: Number })
520
+ ], LitShellTerminal.prototype, "cols", void 0);
521
+ __decorate([
522
+ property({ type: Number })
523
+ ], LitShellTerminal.prototype, "rows", void 0);
524
+ __decorate([
525
+ property({ type: String, reflect: true })
526
+ ], LitShellTerminal.prototype, "theme", void 0);
527
+ __decorate([
528
+ property({ type: Boolean, attribute: 'no-header' })
529
+ ], LitShellTerminal.prototype, "noHeader", void 0);
530
+ __decorate([
531
+ property({ type: Boolean, attribute: 'auto-connect' })
532
+ ], LitShellTerminal.prototype, "autoConnect", void 0);
533
+ __decorate([
534
+ property({ type: Boolean, attribute: 'auto-spawn' })
535
+ ], LitShellTerminal.prototype, "autoSpawn", void 0);
536
+ __decorate([
537
+ property({ type: Number, attribute: 'font-size' })
538
+ ], LitShellTerminal.prototype, "fontSize", void 0);
539
+ __decorate([
540
+ property({ type: String, attribute: 'font-family' })
541
+ ], LitShellTerminal.prototype, "fontFamily", void 0);
542
+ __decorate([
543
+ state()
544
+ ], LitShellTerminal.prototype, "client", void 0);
545
+ __decorate([
546
+ state()
547
+ ], LitShellTerminal.prototype, "terminal", void 0);
548
+ __decorate([
549
+ state()
550
+ ], LitShellTerminal.prototype, "fitAddon", void 0);
551
+ __decorate([
552
+ state()
553
+ ], LitShellTerminal.prototype, "connected", void 0);
554
+ __decorate([
555
+ state()
556
+ ], LitShellTerminal.prototype, "sessionActive", void 0);
557
+ __decorate([
558
+ state()
559
+ ], LitShellTerminal.prototype, "loading", void 0);
560
+ __decorate([
561
+ state()
562
+ ], LitShellTerminal.prototype, "error", void 0);
563
+ __decorate([
564
+ state()
565
+ ], LitShellTerminal.prototype, "sessionInfo", void 0);
566
+ LitShellTerminal = __decorate([
567
+ customElement('lit-shell-terminal')
568
+ ], LitShellTerminal);
569
+ export { LitShellTerminal };
570
+ //# sourceMappingURL=lit-shell-terminal.js.map