x-shell.js 0.1.1 → 1.0.0-rc.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.
@@ -748,6 +748,28 @@ var buttonStyles = i`
748
748
  opacity: 0.5;
749
749
  cursor: not-allowed;
750
750
  }
751
+
752
+ button.btn-primary,
753
+ .btn-primary {
754
+ background: var(--xs-status-connected);
755
+ color: #ffffff;
756
+ }
757
+
758
+ button.btn-primary:hover,
759
+ .btn-primary:hover {
760
+ background: #16a34a;
761
+ }
762
+
763
+ button.btn-danger,
764
+ .btn-danger {
765
+ background: var(--xs-status-disconnected);
766
+ color: #ffffff;
767
+ }
768
+
769
+ button.btn-danger:hover,
770
+ .btn-danger:hover {
771
+ background: #dc2626;
772
+ }
751
773
  `;
752
774
 
753
775
  // src/client/terminal-client.ts
@@ -757,6 +779,7 @@ var TerminalClient = class {
757
779
  this.state = "disconnected";
758
780
  this.sessionId = null;
759
781
  this.sessionInfo = null;
782
+ this.serverInfo = null;
760
783
  this.reconnectAttempts = 0;
761
784
  this.reconnectTimeout = null;
762
785
  // Event handlers
@@ -766,6 +789,8 @@ var TerminalClient = class {
766
789
  this.exitHandlers = [];
767
790
  this.errorHandlers = [];
768
791
  this.spawnedHandlers = [];
792
+ this.serverInfoHandlers = [];
793
+ this.containerListHandlers = [];
769
794
  // Promise resolvers for spawn
770
795
  this.spawnResolve = null;
771
796
  this.spawnReject = null;
@@ -875,7 +900,8 @@ var TerminalClient = class {
875
900
  cwd: message.cwd,
876
901
  cols: message.cols,
877
902
  rows: message.rows,
878
- createdAt: /* @__PURE__ */ new Date()
903
+ createdAt: /* @__PURE__ */ new Date(),
904
+ container: message.container
879
905
  };
880
906
  this.spawnedHandlers.forEach((handler) => handler(this.sessionInfo));
881
907
  if (this.spawnResolve) {
@@ -902,6 +928,13 @@ var TerminalClient = class {
902
928
  this.spawnReject = null;
903
929
  }
904
930
  break;
931
+ case "serverInfo":
932
+ this.serverInfo = message.info;
933
+ this.serverInfoHandlers.forEach((handler) => handler(message.info));
934
+ break;
935
+ case "containerList":
936
+ this.containerListHandlers.forEach((handler) => handler(message.containers));
937
+ break;
905
938
  }
906
939
  }
907
940
  /**
@@ -1026,6 +1059,31 @@ var TerminalClient = class {
1026
1059
  onSpawned(handler) {
1027
1060
  this.spawnedHandlers.push(handler);
1028
1061
  }
1062
+ /**
1063
+ * Called when server info is received
1064
+ */
1065
+ onServerInfo(handler) {
1066
+ this.serverInfoHandlers.push(handler);
1067
+ if (this.serverInfo) {
1068
+ handler(this.serverInfo);
1069
+ }
1070
+ }
1071
+ /**
1072
+ * Called when container list is received
1073
+ */
1074
+ onContainerList(handler) {
1075
+ this.containerListHandlers.push(handler);
1076
+ }
1077
+ /**
1078
+ * Request list of available containers
1079
+ */
1080
+ requestContainerList() {
1081
+ if (!this.ws || this.state !== "connected") {
1082
+ console.error("[x-shell] Cannot request containers: not connected");
1083
+ return;
1084
+ }
1085
+ this.ws.send(JSON.stringify({ type: "listContainers" }));
1086
+ }
1029
1087
  // ==========================================
1030
1088
  // Getters
1031
1089
  // ==========================================
@@ -1059,6 +1117,12 @@ var TerminalClient = class {
1059
1117
  hasActiveSession() {
1060
1118
  return this.sessionId !== null;
1061
1119
  }
1120
+ /**
1121
+ * Get server info
1122
+ */
1123
+ getServerInfo() {
1124
+ return this.serverInfo;
1125
+ }
1062
1126
  };
1063
1127
 
1064
1128
  // src/ui/x-shell-terminal.ts
@@ -1074,6 +1138,13 @@ var XShellTerminal = class extends i4 {
1074
1138
  this.noHeader = false;
1075
1139
  this.autoConnect = false;
1076
1140
  this.autoSpawn = false;
1141
+ this.container = "";
1142
+ this.containerShell = "";
1143
+ this.containerUser = "";
1144
+ this.containerCwd = "";
1145
+ this.showConnectionPanel = false;
1146
+ this.showSettings = false;
1147
+ this.showStatusBar = false;
1077
1148
  this.fontSize = 14;
1078
1149
  this.fontFamily = 'Menlo, Monaco, "Courier New", monospace';
1079
1150
  this.client = null;
@@ -1084,6 +1155,14 @@ var XShellTerminal = class extends i4 {
1084
1155
  this.loading = false;
1085
1156
  this.error = null;
1086
1157
  this.sessionInfo = null;
1158
+ this.containers = [];
1159
+ this.serverInfo = null;
1160
+ this.selectedContainer = "";
1161
+ this.selectedShell = "/bin/sh";
1162
+ this.connectionMode = "docker";
1163
+ this.settingsMenuOpen = false;
1164
+ this.statusMessage = "";
1165
+ this.statusType = "info";
1087
1166
  // xterm.js module (loaded dynamically)
1088
1167
  this.xtermModule = null;
1089
1168
  this.fitAddonModule = null;
@@ -1116,6 +1195,26 @@ var XShellTerminal = class extends i4 {
1116
1195
  throw new Error("Failed to load xterm.js. Make sure it is available.");
1117
1196
  }
1118
1197
  }
1198
+ await this.injectXtermCSS();
1199
+ }
1200
+ /**
1201
+ * Inject xterm.js CSS into shadow DOM
1202
+ */
1203
+ async injectXtermCSS() {
1204
+ if (!this.shadowRoot)
1205
+ return;
1206
+ if (this.shadowRoot.querySelector("#xterm-styles"))
1207
+ return;
1208
+ try {
1209
+ const response = await fetch("https://cdn.jsdelivr.net/npm/xterm@5.3.0/css/xterm.css");
1210
+ const css = await response.text();
1211
+ const style = document.createElement("style");
1212
+ style.id = "xterm-styles";
1213
+ style.textContent = css;
1214
+ this.shadowRoot.prepend(style);
1215
+ } catch (e5) {
1216
+ console.warn("[x-shell] Failed to load xterm CSS:", e5);
1217
+ }
1119
1218
  }
1120
1219
  /**
1121
1220
  * Connect to the terminal server
@@ -1144,6 +1243,7 @@ var XShellTerminal = class extends i4 {
1144
1243
  });
1145
1244
  this.client.onError((err) => {
1146
1245
  this.error = err.message;
1246
+ this.setStatus(err.message, "error");
1147
1247
  this.dispatchEvent(
1148
1248
  new CustomEvent("error", { detail: { error: err }, bubbles: true, composed: true })
1149
1249
  );
@@ -1166,10 +1266,25 @@ var XShellTerminal = class extends i4 {
1166
1266
  });
1167
1267
  this.client.onSpawned((info) => {
1168
1268
  this.sessionInfo = info;
1269
+ this.setStatus(`Session started: ${info.container || info.shell}`, "success");
1169
1270
  this.dispatchEvent(
1170
1271
  new CustomEvent("spawned", { detail: { session: info }, bubbles: true, composed: true })
1171
1272
  );
1172
1273
  });
1274
+ this.client.onServerInfo((info) => {
1275
+ this.serverInfo = info;
1276
+ if (info.dockerEnabled) {
1277
+ this.connectionMode = "docker";
1278
+ this.client?.requestContainerList();
1279
+ }
1280
+ this.selectedShell = info.defaultShell;
1281
+ });
1282
+ this.client.onContainerList((containers) => {
1283
+ this.containers = containers;
1284
+ if (containers.length > 0 && !this.selectedContainer) {
1285
+ this.selectedContainer = containers[0].name;
1286
+ }
1287
+ });
1173
1288
  await this.client.connect();
1174
1289
  } catch (err) {
1175
1290
  this.error = err instanceof Error ? err.message : "Connection failed";
@@ -1204,7 +1319,12 @@ var XShellTerminal = class extends i4 {
1204
1319
  cwd: options?.cwd || this.cwd || void 0,
1205
1320
  cols: this.terminal?.cols || this.cols,
1206
1321
  rows: this.terminal?.rows || this.rows,
1207
- env: options?.env
1322
+ env: options?.env,
1323
+ // Docker container options
1324
+ container: options?.container || this.container || void 0,
1325
+ containerShell: options?.containerShell || this.containerShell || void 0,
1326
+ containerUser: options?.containerUser || this.containerUser || void 0,
1327
+ containerCwd: options?.containerCwd || this.containerCwd || void 0
1208
1328
  };
1209
1329
  const info = await this.client.spawn(spawnOptions);
1210
1330
  this.sessionActive = true;
@@ -1212,8 +1332,10 @@ var XShellTerminal = class extends i4 {
1212
1332
  if (this.terminal) {
1213
1333
  this.terminal.focus();
1214
1334
  }
1335
+ return info;
1215
1336
  } catch (err) {
1216
1337
  this.error = err instanceof Error ? err.message : "Failed to spawn session";
1338
+ throw err;
1217
1339
  } finally {
1218
1340
  this.loading = false;
1219
1341
  }
@@ -1267,19 +1389,27 @@ var XShellTerminal = class extends i4 {
1267
1389
  * Get terminal theme based on component theme
1268
1390
  */
1269
1391
  getTerminalTheme() {
1270
- if (this.theme === "light") {
1392
+ let effectiveTheme = this.theme;
1393
+ if (this.theme === "auto") {
1394
+ effectiveTheme = window.matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark";
1395
+ }
1396
+ if (effectiveTheme === "light") {
1271
1397
  return {
1272
1398
  background: "#ffffff",
1273
1399
  foreground: "#1f2937",
1274
1400
  cursor: "#1f2937",
1275
- selection: "#b4d5fe"
1401
+ cursorAccent: "#ffffff",
1402
+ selection: "#b4d5fe",
1403
+ selectionForeground: "#1f2937"
1276
1404
  };
1277
1405
  }
1278
1406
  return {
1279
1407
  background: "#1e1e1e",
1280
1408
  foreground: "#cccccc",
1281
1409
  cursor: "#ffffff",
1282
- selection: "#264f78"
1410
+ cursorAccent: "#1e1e1e",
1411
+ selection: "#264f78",
1412
+ selectionForeground: "#ffffff"
1283
1413
  };
1284
1414
  }
1285
1415
  /**
@@ -1342,6 +1472,245 @@ var XShellTerminal = class extends i4 {
1342
1472
  }
1343
1473
  this.fitAddon = null;
1344
1474
  }
1475
+ /**
1476
+ * Set status message
1477
+ */
1478
+ setStatus(message, type = "info") {
1479
+ this.statusMessage = message;
1480
+ this.statusType = type;
1481
+ if (type !== "error") {
1482
+ setTimeout(() => {
1483
+ if (this.statusMessage === message) {
1484
+ this.statusMessage = "";
1485
+ }
1486
+ }, 5e3);
1487
+ }
1488
+ }
1489
+ /**
1490
+ * Clear status message
1491
+ */
1492
+ clearStatus() {
1493
+ this.statusMessage = "";
1494
+ this.statusType = "info";
1495
+ }
1496
+ /**
1497
+ * Handle theme change
1498
+ */
1499
+ handleThemeChange(e5) {
1500
+ const select = e5.target;
1501
+ this.theme = select.value;
1502
+ this.applyTerminalTheme();
1503
+ this.dispatchEvent(new CustomEvent("theme-change", {
1504
+ detail: { theme: this.theme },
1505
+ bubbles: true,
1506
+ composed: true
1507
+ }));
1508
+ }
1509
+ /**
1510
+ * Apply current theme to xterm.js terminal
1511
+ */
1512
+ applyTerminalTheme() {
1513
+ if (!this.terminal)
1514
+ return;
1515
+ const terminalTheme = this.getTerminalTheme();
1516
+ this.terminal.options.theme = terminalTheme;
1517
+ }
1518
+ /**
1519
+ * Apply current font size to xterm.js terminal
1520
+ */
1521
+ applyTerminalFontSize() {
1522
+ if (!this.terminal)
1523
+ return;
1524
+ this.terminal.options.fontSize = this.fontSize;
1525
+ if (this.fitAddon) {
1526
+ this.fitAddon.fit();
1527
+ }
1528
+ }
1529
+ /**
1530
+ * Handle connection mode change
1531
+ */
1532
+ handleModeChange(e5) {
1533
+ const select = e5.target;
1534
+ this.connectionMode = select.value;
1535
+ if (this.connectionMode === "docker" && this.client && this.connected) {
1536
+ this.client.requestContainerList();
1537
+ }
1538
+ }
1539
+ /**
1540
+ * Handle connect from connection panel
1541
+ */
1542
+ async handlePanelConnect() {
1543
+ if (!this.connected) {
1544
+ await this.connect();
1545
+ }
1546
+ if (this.connected) {
1547
+ const options = {};
1548
+ if (this.connectionMode === "docker" && this.selectedContainer) {
1549
+ options.container = this.selectedContainer;
1550
+ options.containerShell = this.selectedShell || "/bin/sh";
1551
+ } else {
1552
+ options.shell = this.selectedShell || void 0;
1553
+ }
1554
+ await this.spawn(options);
1555
+ }
1556
+ }
1557
+ /**
1558
+ * Toggle settings menu
1559
+ */
1560
+ toggleSettingsMenu() {
1561
+ this.settingsMenuOpen = !this.settingsMenuOpen;
1562
+ }
1563
+ /**
1564
+ * Render connection panel
1565
+ */
1566
+ renderConnectionPanel() {
1567
+ if (!this.showConnectionPanel)
1568
+ return A;
1569
+ const runningContainers = this.containers.filter((c4) => c4.state === "running");
1570
+ return b2`
1571
+ <div class="connection-panel">
1572
+ <div class="connection-panel-title">
1573
+ <span>Connection</span>
1574
+ ${this.serverInfo?.dockerEnabled ? b2`<span style="font-size: 11px; color: var(--xs-status-connected);">Docker enabled</span>` : A}
1575
+ </div>
1576
+ <div class="connection-form">
1577
+ <div class="form-group">
1578
+ <label>Mode</label>
1579
+ <select
1580
+ .value=${this.connectionMode}
1581
+ @change=${this.handleModeChange}
1582
+ ?disabled=${this.sessionActive}
1583
+ >
1584
+ <option value="local">Local Shell</option>
1585
+ ${this.serverInfo?.dockerEnabled ? b2`<option value="docker">Docker Container</option>` : A}
1586
+ </select>
1587
+ </div>
1588
+
1589
+ ${this.connectionMode === "docker" ? b2`
1590
+ <div class="form-group">
1591
+ <label>Container</label>
1592
+ <select
1593
+ .value=${this.selectedContainer}
1594
+ @change=${(e5) => this.selectedContainer = e5.target.value}
1595
+ ?disabled=${this.sessionActive}
1596
+ >
1597
+ ${runningContainers.length === 0 ? b2`<option value="">No containers running</option>` : runningContainers.map((c4) => b2`
1598
+ <option value=${c4.name}>${c4.name} (${c4.image})</option>
1599
+ `)}
1600
+ </select>
1601
+ </div>
1602
+ ` : A}
1603
+
1604
+ <div class="form-group">
1605
+ <label>Shell</label>
1606
+ <select
1607
+ .value=${this.selectedShell}
1608
+ @change=${(e5) => this.selectedShell = e5.target.value}
1609
+ ?disabled=${this.sessionActive}
1610
+ >
1611
+ ${this.serverInfo?.allowedShells.length ? this.serverInfo.allowedShells.map((s4) => b2`<option value=${s4}>${s4}</option>`) : b2`
1612
+ <option value="/bin/bash">/bin/bash</option>
1613
+ <option value="/bin/sh">/bin/sh</option>
1614
+ <option value="/bin/zsh">/bin/zsh</option>
1615
+ `}
1616
+ </select>
1617
+ </div>
1618
+
1619
+ <div class="form-group">
1620
+ ${!this.connected ? b2`<button class="btn-primary" @click=${this.handlePanelConnect} ?disabled=${this.loading}>
1621
+ ${this.loading ? "Connecting..." : "Connect"}
1622
+ </button>` : !this.sessionActive ? b2`<button class="btn-primary" @click=${this.handlePanelConnect} ?disabled=${this.loading}>
1623
+ ${this.loading ? "Starting..." : "Start Session"}
1624
+ </button>` : b2`<button class="btn-danger" @click=${this.kill}>
1625
+ Stop Session
1626
+ </button>`}
1627
+ </div>
1628
+ </div>
1629
+ </div>
1630
+ `;
1631
+ }
1632
+ /**
1633
+ * Render settings dropdown
1634
+ */
1635
+ renderSettingsDropdown() {
1636
+ if (!this.showSettings)
1637
+ return A;
1638
+ return b2`
1639
+ <div class="settings-dropdown">
1640
+ <button @click=${this.toggleSettingsMenu} title="Settings">
1641
+ ⚙️
1642
+ </button>
1643
+ ${this.settingsMenuOpen ? b2`
1644
+ <div class="settings-menu">
1645
+ <div class="settings-menu-item">
1646
+ <span>Theme</span>
1647
+ <select
1648
+ .value=${this.theme}
1649
+ @change=${this.handleThemeChange}
1650
+ >
1651
+ <option value="dark">Dark</option>
1652
+ <option value="light">Light</option>
1653
+ <option value="auto">Auto</option>
1654
+ </select>
1655
+ </div>
1656
+ <div class="settings-divider"></div>
1657
+ <div class="settings-menu-item">
1658
+ <span>Font Size</span>
1659
+ <select
1660
+ .value=${String(this.fontSize)}
1661
+ @change=${(e5) => {
1662
+ this.fontSize = parseInt(e5.target.value);
1663
+ this.applyTerminalFontSize();
1664
+ }}
1665
+ >
1666
+ <option value="12">12px</option>
1667
+ <option value="14">14px</option>
1668
+ <option value="16">16px</option>
1669
+ <option value="18">18px</option>
1670
+ </select>
1671
+ </div>
1672
+ <div class="settings-divider"></div>
1673
+ <div class="settings-menu-item" @click=${this.clear}>
1674
+ <span>Clear Terminal</span>
1675
+ </div>
1676
+ </div>
1677
+ ` : A}
1678
+ </div>
1679
+ `;
1680
+ }
1681
+ /**
1682
+ * Render status bar
1683
+ */
1684
+ renderStatusBar() {
1685
+ if (!this.showStatusBar)
1686
+ return A;
1687
+ return b2`
1688
+ <div class="status-bar">
1689
+ <div class="status-bar-left">
1690
+ <span class="status-dot ${this.connected ? "connected" : ""}"></span>
1691
+ <span>${this.connected ? this.sessionActive ? "Session active" : "Connected" : "Disconnected"}</span>
1692
+ ${this.sessionInfo ? b2`
1693
+ <span style="color: var(--xs-text-muted)">|</span>
1694
+ <span>${this.sessionInfo.container || this.sessionInfo.shell}</span>
1695
+ <span style="color: var(--xs-text-muted)">${this.sessionInfo.cols}x${this.sessionInfo.rows}</span>
1696
+ ` : A}
1697
+ </div>
1698
+ <div class="status-bar-right">
1699
+ ${this.statusMessage ? b2`
1700
+ <span class="${this.statusType === "error" ? "status-bar-error" : this.statusType === "success" ? "status-bar-success" : ""}">
1701
+ ${this.statusType === "error" ? "\u26A0\uFE0F" : this.statusType === "success" ? "\u2713" : ""}
1702
+ ${this.statusMessage}
1703
+ </span>
1704
+ <button
1705
+ style="background: none; border: none; cursor: pointer; padding: 0; font-size: 10px;"
1706
+ @click=${this.clearStatus}
1707
+ title="Dismiss"
1708
+ >✕</button>
1709
+ ` : A}
1710
+ </div>
1711
+ </div>
1712
+ `;
1713
+ }
1345
1714
  render() {
1346
1715
  return b2`
1347
1716
  ${this.noHeader ? A : b2`
@@ -1349,27 +1718,36 @@ var XShellTerminal = class extends i4 {
1349
1718
  <div class="header-title">
1350
1719
  <span>Terminal</span>
1351
1720
  ${this.sessionInfo ? b2`<span style="font-weight: normal; font-size: 12px; color: var(--xs-text-muted)">
1352
- ${this.sessionInfo.shell}
1721
+ ${this.sessionInfo.container ? `${this.sessionInfo.container} (${this.sessionInfo.shell})` : this.sessionInfo.shell}
1353
1722
  </span>` : A}
1354
1723
  </div>
1355
1724
  <div class="header-actions">
1356
- ${!this.connected ? b2`<button @click=${this.connect} ?disabled=${this.loading}>
1357
- ${this.loading ? "Connecting..." : "Connect"}
1358
- </button>` : !this.sessionActive ? b2`<button @click=${() => this.spawn()} ?disabled=${this.loading}>
1359
- ${this.loading ? "Spawning..." : "Start"}
1360
- </button>` : b2`<button @click=${this.kill}>Stop</button>`}
1725
+ ${!this.showConnectionPanel ? b2`
1726
+ ${!this.connected ? b2`<button @click=${this.connect} ?disabled=${this.loading}>
1727
+ ${this.loading ? "Connecting..." : "Connect"}
1728
+ </button>` : !this.sessionActive ? b2`<button @click=${() => this.spawn()} ?disabled=${this.loading}>
1729
+ ${this.loading ? "Spawning..." : "Start"}
1730
+ </button>` : b2`<button @click=${this.kill}>Stop</button>`}
1731
+ ` : A}
1361
1732
  <button @click=${this.clear} ?disabled=${!this.sessionActive}>Clear</button>
1362
- <div class="status">
1363
- <span class="status-dot ${this.connected ? "connected" : ""}"></span>
1364
- <span>${this.connected ? "Connected" : "Disconnected"}</span>
1365
- </div>
1733
+ ${this.renderSettingsDropdown()}
1734
+ ${!this.showStatusBar ? b2`
1735
+ <div class="status">
1736
+ <span class="status-dot ${this.connected ? "connected" : ""}"></span>
1737
+ <span>${this.connected ? "Connected" : "Disconnected"}</span>
1738
+ </div>
1739
+ ` : A}
1366
1740
  </div>
1367
1741
  </div>
1368
1742
  `}
1369
1743
 
1744
+ ${this.renderConnectionPanel()}
1745
+
1370
1746
  <div class="terminal-container">
1371
1747
  ${this.loading && !this.terminal ? b2`<div class="loading"><span class="loading-spinner">⏳</span> Loading...</div>` : this.error && !this.terminal ? b2`<div class="error">❌ ${this.error}</div>` : A}
1372
1748
  </div>
1749
+
1750
+ ${this.renderStatusBar()}
1373
1751
  `;
1374
1752
  }
1375
1753
  };
@@ -1476,6 +1854,139 @@ XShellTerminal.styles = [
1476
1854
  :host([no-header]) .header {
1477
1855
  display: none;
1478
1856
  }
1857
+
1858
+ /* Connection panel */
1859
+ .connection-panel {
1860
+ padding: 12px;
1861
+ background: var(--xs-bg-header);
1862
+ border-bottom: 1px solid var(--xs-border);
1863
+ }
1864
+
1865
+ .connection-panel-title {
1866
+ font-weight: 600;
1867
+ margin-bottom: 12px;
1868
+ display: flex;
1869
+ align-items: center;
1870
+ gap: 8px;
1871
+ }
1872
+
1873
+ .connection-form {
1874
+ display: grid;
1875
+ grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
1876
+ gap: 10px;
1877
+ align-items: end;
1878
+ }
1879
+
1880
+ .form-group {
1881
+ display: flex;
1882
+ flex-direction: column;
1883
+ gap: 4px;
1884
+ }
1885
+
1886
+ .form-group label {
1887
+ font-size: 11px;
1888
+ text-transform: uppercase;
1889
+ color: var(--xs-text-muted);
1890
+ letter-spacing: 0.5px;
1891
+ }
1892
+
1893
+ .form-group select,
1894
+ .form-group input {
1895
+ padding: 6px 10px;
1896
+ border: 1px solid var(--xs-border);
1897
+ border-radius: 4px;
1898
+ background: var(--xs-bg);
1899
+ color: var(--xs-text);
1900
+ font-size: 13px;
1901
+ }
1902
+
1903
+ .form-group select:focus,
1904
+ .form-group input:focus {
1905
+ outline: none;
1906
+ border-color: var(--xs-status-connected);
1907
+ }
1908
+
1909
+ /* Settings dropdown */
1910
+ .settings-dropdown {
1911
+ position: relative;
1912
+ }
1913
+
1914
+ .settings-menu {
1915
+ position: absolute;
1916
+ top: 100%;
1917
+ right: 0;
1918
+ margin-top: 4px;
1919
+ min-width: 180px;
1920
+ background: var(--xs-bg-header);
1921
+ border: 1px solid var(--xs-border);
1922
+ border-radius: 4px;
1923
+ box-shadow: 0 4px 12px rgba(0,0,0,0.3);
1924
+ z-index: 100;
1925
+ padding: 8px 0;
1926
+ }
1927
+
1928
+ .settings-menu-item {
1929
+ display: flex;
1930
+ align-items: center;
1931
+ justify-content: space-between;
1932
+ padding: 8px 12px;
1933
+ font-size: 13px;
1934
+ cursor: pointer;
1935
+ }
1936
+
1937
+ .settings-menu-item:hover {
1938
+ background: var(--xs-btn-hover);
1939
+ }
1940
+
1941
+ .settings-menu-item select {
1942
+ padding: 4px 8px;
1943
+ border: 1px solid var(--xs-border);
1944
+ border-radius: 3px;
1945
+ background: var(--xs-bg);
1946
+ color: var(--xs-text);
1947
+ font-size: 12px;
1948
+ }
1949
+
1950
+ .settings-divider {
1951
+ height: 1px;
1952
+ background: var(--xs-border);
1953
+ margin: 4px 0;
1954
+ }
1955
+
1956
+ /* Status bar */
1957
+ .status-bar {
1958
+ display: flex;
1959
+ align-items: center;
1960
+ justify-content: space-between;
1961
+ padding: 4px 12px;
1962
+ background: var(--xs-bg-header);
1963
+ border-top: 1px solid var(--xs-border);
1964
+ font-size: 12px;
1965
+ color: var(--xs-text-muted);
1966
+ }
1967
+
1968
+ .status-bar-left {
1969
+ display: flex;
1970
+ align-items: center;
1971
+ gap: 12px;
1972
+ }
1973
+
1974
+ .status-bar-right {
1975
+ display: flex;
1976
+ align-items: center;
1977
+ gap: 8px;
1978
+ }
1979
+
1980
+ .status-bar-error {
1981
+ color: #ef4444;
1982
+ display: flex;
1983
+ align-items: center;
1984
+ gap: 4px;
1985
+ }
1986
+
1987
+ .status-bar-success {
1988
+ color: var(--xs-status-connected);
1989
+ }
1479
1990
  `
1480
1991
  ];
1481
1992
  __decorateClass([
@@ -1505,6 +2016,27 @@ __decorateClass([
1505
2016
  __decorateClass([
1506
2017
  n4({ type: Boolean, attribute: "auto-spawn" })
1507
2018
  ], XShellTerminal.prototype, "autoSpawn", 2);
2019
+ __decorateClass([
2020
+ n4({ type: String })
2021
+ ], XShellTerminal.prototype, "container", 2);
2022
+ __decorateClass([
2023
+ n4({ type: String, attribute: "container-shell" })
2024
+ ], XShellTerminal.prototype, "containerShell", 2);
2025
+ __decorateClass([
2026
+ n4({ type: String, attribute: "container-user" })
2027
+ ], XShellTerminal.prototype, "containerUser", 2);
2028
+ __decorateClass([
2029
+ n4({ type: String, attribute: "container-cwd" })
2030
+ ], XShellTerminal.prototype, "containerCwd", 2);
2031
+ __decorateClass([
2032
+ n4({ type: Boolean, attribute: "show-connection-panel" })
2033
+ ], XShellTerminal.prototype, "showConnectionPanel", 2);
2034
+ __decorateClass([
2035
+ n4({ type: Boolean, attribute: "show-settings" })
2036
+ ], XShellTerminal.prototype, "showSettings", 2);
2037
+ __decorateClass([
2038
+ n4({ type: Boolean, attribute: "show-status-bar" })
2039
+ ], XShellTerminal.prototype, "showStatusBar", 2);
1508
2040
  __decorateClass([
1509
2041
  n4({ type: Number, attribute: "font-size" })
1510
2042
  ], XShellTerminal.prototype, "fontSize", 2);
@@ -1535,6 +2067,30 @@ __decorateClass([
1535
2067
  __decorateClass([
1536
2068
  r5()
1537
2069
  ], XShellTerminal.prototype, "sessionInfo", 2);
2070
+ __decorateClass([
2071
+ r5()
2072
+ ], XShellTerminal.prototype, "containers", 2);
2073
+ __decorateClass([
2074
+ r5()
2075
+ ], XShellTerminal.prototype, "serverInfo", 2);
2076
+ __decorateClass([
2077
+ r5()
2078
+ ], XShellTerminal.prototype, "selectedContainer", 2);
2079
+ __decorateClass([
2080
+ r5()
2081
+ ], XShellTerminal.prototype, "selectedShell", 2);
2082
+ __decorateClass([
2083
+ r5()
2084
+ ], XShellTerminal.prototype, "connectionMode", 2);
2085
+ __decorateClass([
2086
+ r5()
2087
+ ], XShellTerminal.prototype, "settingsMenuOpen", 2);
2088
+ __decorateClass([
2089
+ r5()
2090
+ ], XShellTerminal.prototype, "statusMessage", 2);
2091
+ __decorateClass([
2092
+ r5()
2093
+ ], XShellTerminal.prototype, "statusType", 2);
1538
2094
  XShellTerminal = __decorateClass([
1539
2095
  t3("x-shell-terminal")
1540
2096
  ], XShellTerminal);