tmex-cli 0.14.0 → 0.15.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 (75) hide show
  1. package/CHANGELOG.md +12 -24
  2. package/dist/runtime/server.js +158 -14
  3. package/package.json +1 -1
  4. package/resources/fe-dist/assets/{DevicePage-Ctq31eWb.js → DevicePage-kndrXVHE.js} +1 -1
  5. package/resources/fe-dist/assets/{DevicesPage-DGo44NW0.js → DevicesPage-CZLzOS04.js} +1 -1
  6. package/resources/fe-dist/assets/{FilePage-BpubOqjN.js → FilePage-CxVqdlbt.js} +1 -1
  7. package/resources/fe-dist/assets/{SettingsPage-QT6fy6XA.js → SettingsPage-C50JRe91.js} +1 -1
  8. package/resources/fe-dist/assets/{agent-tab-A2FeMPwL.js → agent-tab-BVWxou-d.js} +1 -1
  9. package/resources/fe-dist/assets/{api-CGfOwFKm.js → api-CrHrQflz.js} +1 -1
  10. package/resources/fe-dist/assets/{arc-B2dphCVi.js → arc-fsY_kDTa.js} +1 -1
  11. package/resources/fe-dist/assets/{architectureDiagram-3BPJPVTR-BpvEhd8M.js → architectureDiagram-3BPJPVTR-Vsg8Cd3e.js} +1 -1
  12. package/resources/fe-dist/assets/{blockDiagram-GPEHLZMM-CI_NnIJz.js → blockDiagram-GPEHLZMM-G7sC3DZO.js} +1 -1
  13. package/resources/fe-dist/assets/{c4Diagram-AAUBKEIU-Y6RrOc7Y.js → c4Diagram-AAUBKEIU-C1AfLOva.js} +1 -1
  14. package/resources/fe-dist/assets/{card-CydUB3N3.js → card-CXa-2u8i.js} +1 -1
  15. package/resources/fe-dist/assets/channel-DS4kWkSb.js +1 -0
  16. package/resources/fe-dist/assets/{chunk-2J33WTMH-BQN_fYaa.js → chunk-2J33WTMH-C1p02eGI.js} +1 -1
  17. package/resources/fe-dist/assets/{chunk-4BX2VUAB-BExarxjk.js → chunk-4BX2VUAB-C8n2FURp.js} +1 -1
  18. package/resources/fe-dist/assets/{chunk-55IACEB6-DeOEgcn_.js → chunk-55IACEB6-Da0nuNCD.js} +1 -1
  19. package/resources/fe-dist/assets/{chunk-727SXJPM-CwkxCtPA.js → chunk-727SXJPM-C1TXyDEt.js} +1 -1
  20. package/resources/fe-dist/assets/{chunk-AQP2D5EJ-_IoTtvOb.js → chunk-AQP2D5EJ-CnRKlbXo.js} +1 -1
  21. package/resources/fe-dist/assets/{chunk-FMBD7UC4-6pvmQ3Lj.js → chunk-FMBD7UC4-B8HGrJ3t.js} +1 -1
  22. package/resources/fe-dist/assets/{chunk-ND2GUHAM-CxpGf5zM.js → chunk-ND2GUHAM-CcN2Ou-o.js} +1 -1
  23. package/resources/fe-dist/assets/{chunk-QZHKN3VN-DOMH0v_e.js → chunk-QZHKN3VN-DfWJ_tl5.js} +1 -1
  24. package/resources/fe-dist/assets/classDiagram-4FO5ZUOK-BfsrnUi9.js +1 -0
  25. package/resources/fe-dist/assets/classDiagram-v2-Q7XG4LA2-BfsrnUi9.js +1 -0
  26. package/resources/fe-dist/assets/{copy-DnlW35bA.js → copy-BveLgJe5.js} +1 -1
  27. package/resources/fe-dist/assets/{cose-bilkent-S5V4N54A-D03NElzH.js → cose-bilkent-S5V4N54A-BkmsTzwU.js} +1 -1
  28. package/resources/fe-dist/assets/{dagre-BM42HDAG-CVOxLhKQ.js → dagre-BM42HDAG-pGgtc4EE.js} +1 -1
  29. package/resources/fe-dist/assets/{diagram-2AECGRRQ-p564GAo-.js → diagram-2AECGRRQ-DJxixZvm.js} +1 -1
  30. package/resources/fe-dist/assets/{diagram-5GNKFQAL-C_pgnQLt.js → diagram-5GNKFQAL-2ITViev7.js} +1 -1
  31. package/resources/fe-dist/assets/{diagram-KO2AKTUF-B36jioML.js → diagram-KO2AKTUF-EMcadaeo.js} +1 -1
  32. package/resources/fe-dist/assets/{diagram-LMA3HP47-B8P4WWeq.js → diagram-LMA3HP47-w3sLU7zY.js} +1 -1
  33. package/resources/fe-dist/assets/{diagram-OG6HWLK6-CTL4zJlz.js → diagram-OG6HWLK6-B4KGMQr7.js} +1 -1
  34. package/resources/fe-dist/assets/{en_US-DRPd4vPi.js → en_US-Chxeay8F.js} +1 -1
  35. package/resources/fe-dist/assets/{erDiagram-TEJ5UH35-DbKPL3Fa.js → erDiagram-TEJ5UH35-CB0P-Wz7.js} +1 -1
  36. package/resources/fe-dist/assets/{files-tab-CeUM00x8.js → files-tab-lNenDPFy.js} +1 -1
  37. package/resources/fe-dist/assets/{flowDiagram-I6XJVG4X-DvOU6wtN.js → flowDiagram-I6XJVG4X-CmBc7GAX.js} +1 -1
  38. package/resources/fe-dist/assets/{ganttDiagram-6RSMTGT7-CwgNky8L.js → ganttDiagram-6RSMTGT7-CHNf7S_K.js} +1 -1
  39. package/resources/fe-dist/assets/{gitGraphDiagram-PVQCEYII-B3U-hNGe.js → gitGraphDiagram-PVQCEYII-CabspIF1.js} +1 -1
  40. package/resources/fe-dist/assets/{index-DBHh6Xu1.js → index-BQbq6q_-.js} +1 -1
  41. package/resources/fe-dist/assets/{index-DAElYydw.js → index-DOaZjm8K.js} +79 -79
  42. package/resources/fe-dist/assets/index-DaOR3c0u.css +1 -0
  43. package/resources/fe-dist/assets/{infoDiagram-5YYISTIA-BmcWF7v1.js → infoDiagram-5YYISTIA-ny6wqvHH.js} +1 -1
  44. package/resources/fe-dist/assets/{ishikawaDiagram-YF4QCWOH-Dr8C0MtS.js → ishikawaDiagram-YF4QCWOH-EFCdGVsV.js} +1 -1
  45. package/resources/fe-dist/assets/{ja_JP-DHREHMZZ.js → ja_JP-BI-C8I9X.js} +1 -1
  46. package/resources/fe-dist/assets/{journeyDiagram-JHISSGLW-DzlvXb4K.js → journeyDiagram-JHISSGLW-Cvi8jO9a.js} +1 -1
  47. package/resources/fe-dist/assets/{kanban-definition-UN3LZRKU-4aBWZlcm.js → kanban-definition-UN3LZRKU-nKej8tSo.js} +1 -1
  48. package/resources/fe-dist/assets/{linear-_wXLepjd.js → linear-leOPyu29.js} +1 -1
  49. package/resources/fe-dist/assets/{markdown-preview-BX0o83rG.js → markdown-preview-rqUSs5w8.js} +3 -3
  50. package/resources/fe-dist/assets/{mermaid.core-DoE3dx_X.js → mermaid.core-QvhJBi8f.js} +5 -5
  51. package/resources/fe-dist/assets/{mindmap-definition-RKZ34NQL-CKpq2s43.js → mindmap-definition-RKZ34NQL-xWje0B_-.js} +1 -1
  52. package/resources/fe-dist/assets/{pieDiagram-4H26LBE5-DlW1TLza.js → pieDiagram-4H26LBE5-DT1i53ec.js} +1 -1
  53. package/resources/fe-dist/assets/{quadrantDiagram-W4KKPZXB-NEwxpMVe.js → quadrantDiagram-W4KKPZXB-C1zbS4Tx.js} +1 -1
  54. package/resources/fe-dist/assets/{requirementDiagram-4Y6WPE33-CGysr6uH.js → requirementDiagram-4Y6WPE33-By_eNRll.js} +1 -1
  55. package/resources/fe-dist/assets/{sankeyDiagram-5OEKKPKP-j8yXOgys.js → sankeyDiagram-5OEKKPKP-CG7jC05N.js} +1 -1
  56. package/resources/fe-dist/assets/{send-BtOPYsPT.js → send-CcFyyX2G.js} +1 -1
  57. package/resources/fe-dist/assets/{sequenceDiagram-3UESZ5HK-BRPWK9ZU.js → sequenceDiagram-3UESZ5HK-C9yK_EKN.js} +1 -1
  58. package/resources/fe-dist/assets/{stateDiagram-AJRCARHV-C8ckQlA7.js → stateDiagram-AJRCARHV-B-ROUwGP.js} +1 -1
  59. package/resources/fe-dist/assets/stateDiagram-v2-BHNVJYJU-iopOcale.js +1 -0
  60. package/resources/fe-dist/assets/{terminal-settings-panel-ZnL_cBER.js → terminal-settings-panel-CNwWqOMV.js} +1 -1
  61. package/resources/fe-dist/assets/{timeline-definition-PNZ67QCA-BXMFPa99.js → timeline-definition-PNZ67QCA-yMM8VB4U.js} +1 -1
  62. package/resources/fe-dist/assets/{transfer-toast-CUkPc73G.js → transfer-toast-mQs0hgyg.js} +1 -1
  63. package/resources/fe-dist/assets/{triangle-alert-DyM9tU7d.js → triangle-alert-DMp7kMmJ.js} +1 -1
  64. package/resources/fe-dist/assets/{vennDiagram-CIIHVFJN-Bk8oPY9E.js → vennDiagram-CIIHVFJN-CE08llZ3.js} +1 -1
  65. package/resources/fe-dist/assets/{wardley-L42UT6IY-Bi_GTP2_.js → wardley-L42UT6IY-BKLGCmLJ.js} +1 -1
  66. package/resources/fe-dist/assets/{wardleyDiagram-YWT4CUSO-CaPDmxFM.js → wardleyDiagram-YWT4CUSO-BsjwfO3P.js} +1 -1
  67. package/resources/fe-dist/assets/{xychartDiagram-2RQKCTM6-EHogasXy.js → xychartDiagram-2RQKCTM6-Bhz6AI7w.js} +1 -1
  68. package/resources/fe-dist/assets/{zap-CBgYyGBV.js → zap-DPwn8lbv.js} +1 -1
  69. package/resources/fe-dist/assets/{zh_CN-DMEKXuFd.js → zh_CN-DE-BaQ3P.js} +1 -1
  70. package/resources/fe-dist/index.html +2 -2
  71. package/resources/fe-dist/assets/channel-Ce3VMo9B.js +0 -1
  72. package/resources/fe-dist/assets/classDiagram-4FO5ZUOK-BjK-bnh-.js +0 -1
  73. package/resources/fe-dist/assets/classDiagram-v2-Q7XG4LA2-BjK-bnh-.js +0 -1
  74. package/resources/fe-dist/assets/index-D-q7dOhH.css +0 -1
  75. package/resources/fe-dist/assets/stateDiagram-v2-BHNVJYJU-Cm0vyyJy.js +0 -1
package/CHANGELOG.md CHANGED
@@ -1,41 +1,29 @@
1
- # 0.14.0
1
+ # 0.15.0
2
2
 
3
3
  _2026-06-29_
4
4
 
5
5
  ## English
6
6
 
7
- ### New
7
+ ### Bug Fixes
8
8
 
9
- - Clipboard support for terminal programs: apps like vim, Claude Code, and other TUI tools can now copy text to your system clipboard via the terminal (OSC 52). A brief toast confirms each copy.
10
- - Configurable default working directory: set a default path for new terminal windows per device — new windows and panes open there instead of the home directory. Changes take effect immediately without disconnecting.
11
- - Installer now guides you through installing missing dependencies (tmux, bun) during setup, with distro-specific commands for common Linux distributions.
9
+ - Fix iOS PWA losing WebSocket connection permanently after backgrounding. The client now detects zombie connections via pong timeout and automatically reconnects when the page returns to foreground. (`42c8249`)
10
+ - Fix stdin heartbeat and `%pause` handling in control mode; improve pump recovery on unexpected disconnects. (`a208371`)
12
11
 
13
12
  ### Improvements
14
13
 
15
- - Notification toasts now show the terminal name or running command (e.g. "vim", "make") instead of a numeric pane index, making it easier to identify which terminal triggered an alert.
16
- - `tmex doctor` suggests `tmex doctor --fix` when it finds fixable issues, and `--fix --no-interactive` works fully unattended for scripted installs.
17
- - The installer detects whether systemd is available on Linux before proceeding, instead of failing midway on container or WSL environments without it.
18
-
19
- ### Fixes
20
-
21
- - Fixed non-ASCII filenames (Chinese, Japanese, Korean, etc.) showing as garbled escape sequences in the file browser on Linux servers.
14
+ - Add a floating connection status indicator (bottom-right corner) that shows reconnecting state and provides a manual reconnect button when all retries are exhausted. Replaces the previous unhelpful error toast.
15
+ - Display WebSocket latency next to the dark mode toggle in the sidebar. Values >= 150 ms are highlighted in red.
22
16
 
23
17
  ---
24
18
 
25
19
  ## 中文
26
20
 
27
- ### 新增
28
-
29
- - 终端程序剪贴板支持:vim、Claude Code 等 TUI 程序现在可以通过终端直接复制文本到系统剪贴板(OSC 52),复制成功时会显示提示。
30
- - 可配置默认工作目录:可为每台设备设置新终端窗口的默认路径,新窗口将在该目录下打开而非主目录。修改后立即生效,无需断开连接。
31
- - 安装器现在会在安装过程中引导用户安装缺失的依赖(tmux、bun),并为常见 Linux 发行版提供专属的安装命令。
32
-
33
- ### 改进
21
+ ### 问题修复
34
22
 
35
- - 通知提示现在显示终端名称或正在运行的命令(如"vim"、"make"),而非数字索引,更容易识别是哪个终端触发了提醒。
36
- - `tmex doctor` 检测到可修复的问题时会提示使用 `tmex doctor --fix`,且 `--fix --no-interactive` 支持完全无人值守的脚本化安装。
37
- - 安装器在 Linux 上会先检测 systemd 是否可用,在容器或无 systemd 的 WSL 环境中提前给出明确错误,而非中途失败。
23
+ - 修复 iOS PWA 后台化后 WebSocket 永久断连的问题。客户端现在通过 pong 超时检测僵尸连接,页面回到前台时自动恢复连接。(`42c8249`)
24
+ - 修复控制模式下 stdin 心跳和 `%pause` 处理问题,改善意外断连时的 pump 恢复。(`a208371`)
38
25
 
39
- ### 修复
26
+ ### 体验优化
40
27
 
41
- - 修复了 Linux 服务器文件浏览器中中日韩等非 ASCII 文件名显示为乱码转义序列的问题。
28
+ - 新增浮动连接状态指示器(右下角),显示重连状态,重试耗尽后提供手动重连按钮。替换了之前不友好的错误 toast。
29
+ - 在侧边栏深色模式开关旁显示 WebSocket 延迟,延迟 >= 150 ms 时标红。
@@ -23122,7 +23122,9 @@ Time: {{time}}`,
23122
23122
  error: "WebSocket connection error",
23123
23123
  checkGateway: "Please check Gateway status",
23124
23124
  upgradeFailed: "Upgrade failed",
23125
- invalidMessage: "Invalid message format"
23125
+ invalidMessage: "Invalid message format",
23126
+ reconnecting: "Reconnecting",
23127
+ reconnect: "Reconnect"
23126
23128
  },
23127
23129
  wsError: {
23128
23130
  checkGateway: "Please check Gateway status"
@@ -24118,7 +24120,9 @@ Bot\uFF1A{{botName}}
24118
24120
  error: "WebSocket \u8FDE\u63A5\u9519\u8BEF",
24119
24121
  checkGateway: "\u8BF7\u68C0\u67E5 Gateway \u72B6\u6001",
24120
24122
  upgradeFailed: "Upgrade failed",
24121
- invalidMessage: "Invalid message format"
24123
+ invalidMessage: "Invalid message format",
24124
+ reconnecting: "\u91CD\u8FDE\u4E2D",
24125
+ reconnect: "\u91CD\u65B0\u8FDE\u63A5"
24122
24126
  },
24123
24127
  wsError: {
24124
24128
  checkGateway: "\u8BF7\u68C0\u67E5 Gateway \u72B6\u6001"
@@ -25114,7 +25118,9 @@ Bot\uFF1A{{botName}}
25114
25118
  error: "WebSocket \u63A5\u7D9A\u30A8\u30E9\u30FC",
25115
25119
  checkGateway: "Gateway \u72B6\u614B\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044",
25116
25120
  upgradeFailed: "\u30A2\u30C3\u30D7\u30B0\u30EC\u30FC\u30C9\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
25117
- invalidMessage: "\u7121\u52B9\u306A\u30E1\u30C3\u30BB\u30FC\u30B8\u5F62\u5F0F"
25121
+ invalidMessage: "\u7121\u52B9\u306A\u30E1\u30C3\u30BB\u30FC\u30B8\u5F62\u5F0F",
25122
+ reconnecting: "\u518D\u63A5\u7D9A\u4E2D",
25123
+ reconnect: "\u518D\u63A5\u7D9A"
25118
25124
  },
25119
25125
  wsError: {
25120
25126
  checkGateway: "Gateway \u72B6\u614B\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044"
@@ -98613,6 +98619,11 @@ function createControlModeSubscription(callbacks) {
98613
98619
  if (STRUCTURE_NOTIFICATION_TYPES.has(notification.type)) {
98614
98620
  scheduleStructureChanged();
98615
98621
  }
98622
+ if (notification.type === "pause") {
98623
+ callbacks.onPause?.(notification.args.trim());
98624
+ } else if (notification.type === "continue") {
98625
+ callbacks.onContinue?.(notification.args.trim());
98626
+ }
98616
98627
  }
98617
98628
  const parser = createControlModeParser({
98618
98629
  onOutput: (paneId, data) => {
@@ -98888,6 +98899,8 @@ var CONTROL_RESTART_DELAY_MS = 500;
98888
98899
  var CONTROL_STABLE_RESET_MS = 1e4;
98889
98900
  var CONTROL_STDERR_TAIL_LIMIT = 2048;
98890
98901
  var CONTROL_ATTACH_READY_TIMEOUT_MS = 3000;
98902
+ var HEARTBEAT_INTERVAL_MS = 30000;
98903
+ var HEARTBEAT_TIMEOUT_MS = 1e4;
98891
98904
  var PARKING_WINDOW_NAME = "tmex-park";
98892
98905
  function hasRenderableTerminalContent(value) {
98893
98906
  return value.trim().length > 0;
@@ -98946,6 +98959,11 @@ function defaultSpawnControlClient(argv) {
98946
98959
  stdin?.end();
98947
98960
  } catch {}
98948
98961
  subprocess.kill();
98962
+ },
98963
+ write: (data) => {
98964
+ try {
98965
+ stdin?.write(data);
98966
+ } catch {}
98949
98967
  }
98950
98968
  };
98951
98969
  }
@@ -98973,6 +98991,9 @@ class LocalExternalTmuxConnection {
98973
98991
  controlRestartCount = 0;
98974
98992
  controlStderrTail = "";
98975
98993
  spawnUnavailableNotified = false;
98994
+ heartbeatTimer = null;
98995
+ heartbeatPending = false;
98996
+ heartbeatTimeoutTimer = null;
98976
98997
  constructor(options, inputDeps = {}) {
98977
98998
  this.deviceId = options.deviceId;
98978
98999
  this.callbacks = options;
@@ -99305,6 +99326,7 @@ class LocalExternalTmuxConnection {
99305
99326
  await this.runTmuxAllowFailure(["kill-window", "-t", windowId]);
99306
99327
  }
99307
99328
  async startControlClient() {
99329
+ this.stopHeartbeat();
99308
99330
  let attachReadyResolve = null;
99309
99331
  const attachReady = new Promise((resolve3) => {
99310
99332
  attachReadyResolve = resolve3;
@@ -99328,6 +99350,7 @@ class LocalExternalTmuxConnection {
99328
99350
  console.warn(`[local] tmux control client died during attach on ${this.deviceId}: ${message}`);
99329
99351
  throw new Error(message);
99330
99352
  }
99353
+ this.startHeartbeat();
99331
99354
  }
99332
99355
  spawnControlClientProcess(onAttachReady) {
99333
99356
  const subscription = createControlModeSubscription({
@@ -99353,9 +99376,16 @@ class LocalExternalTmuxConnection {
99353
99376
  onStructureChanged: () => {
99354
99377
  this.requestSnapshot();
99355
99378
  },
99379
+ onPause: (paneId) => {
99380
+ this.controlProcess?.write("refresh-client -A " + paneId + `:continue
99381
+ `);
99382
+ },
99356
99383
  onExit: () => {},
99357
- onBlockEnd: () => {
99384
+ onBlockEnd: (block) => {
99358
99385
  onAttachReady();
99386
+ if (!block.isError && block.lines.length === 1 && block.lines[0] === "tmex-hb") {
99387
+ this.onHeartbeatResponse();
99388
+ }
99359
99389
  }
99360
99390
  });
99361
99391
  const proc = this.deps.spawnControlClient([
@@ -99395,6 +99425,10 @@ class LocalExternalTmuxConnection {
99395
99425
  }
99396
99426
  }
99397
99427
  subscription.end();
99428
+ if (this.controlProcess === proc) {
99429
+ console.warn("[local] control client stdout ended unexpectedly on " + this.deviceId + ", killing process");
99430
+ proc.kill();
99431
+ }
99398
99432
  }
99399
99433
  async pumpControlStderr(proc) {
99400
99434
  const reader = proc.stderr.getReader();
@@ -99412,12 +99446,55 @@ class LocalExternalTmuxConnection {
99412
99446
  } catch {}
99413
99447
  }
99414
99448
  stopControlClient() {
99449
+ this.stopHeartbeat();
99415
99450
  const proc = this.controlProcess;
99416
99451
  this.controlProcess = null;
99417
99452
  this.controlSubscription?.dispose();
99418
99453
  this.controlSubscription = null;
99419
99454
  proc?.kill();
99420
99455
  }
99456
+ startHeartbeat() {
99457
+ this.stopHeartbeat();
99458
+ this.heartbeatTimer = setInterval(() => {
99459
+ this.sendHeartbeat();
99460
+ }, HEARTBEAT_INTERVAL_MS);
99461
+ }
99462
+ stopHeartbeat() {
99463
+ if (this.heartbeatTimer) {
99464
+ clearInterval(this.heartbeatTimer);
99465
+ this.heartbeatTimer = null;
99466
+ }
99467
+ if (this.heartbeatTimeoutTimer) {
99468
+ clearTimeout(this.heartbeatTimeoutTimer);
99469
+ this.heartbeatTimeoutTimer = null;
99470
+ }
99471
+ this.heartbeatPending = false;
99472
+ }
99473
+ sendHeartbeat() {
99474
+ if (!this.controlProcess || this.heartbeatPending || !this.connected || this.manualDisconnect) {
99475
+ return;
99476
+ }
99477
+ this.heartbeatPending = true;
99478
+ this.controlProcess.write(`display-message -p "tmex-hb"
99479
+ `);
99480
+ this.heartbeatTimeoutTimer = setTimeout(() => {
99481
+ if (!this.heartbeatPending || !this.connected || this.manualDisconnect) {
99482
+ return;
99483
+ }
99484
+ console.warn(`[local] tmux control client heartbeat timeout on ${this.deviceId}, killing stalled process`);
99485
+ this.controlProcess?.kill();
99486
+ }, HEARTBEAT_TIMEOUT_MS);
99487
+ }
99488
+ onHeartbeatResponse() {
99489
+ if (!this.heartbeatPending) {
99490
+ return;
99491
+ }
99492
+ this.heartbeatPending = false;
99493
+ if (this.heartbeatTimeoutTimer) {
99494
+ clearTimeout(this.heartbeatTimeoutTimer);
99495
+ this.heartbeatTimeoutTimer = null;
99496
+ }
99497
+ }
99421
99498
  handleControlClientExit(proc, exitCode) {
99422
99499
  if (this.controlProcess !== proc) {
99423
99500
  return;
@@ -100197,6 +100274,8 @@ var CONTROL_RESTART_DELAY_MS2 = 500;
100197
100274
  var CONTROL_STABLE_RESET_MS2 = 1e4;
100198
100275
  var CONTROL_STDERR_TAIL_LIMIT2 = 2048;
100199
100276
  var CONTROL_ATTACH_READY_TIMEOUT_MS2 = 3000;
100277
+ var HEARTBEAT_INTERVAL_MS2 = 30000;
100278
+ var HEARTBEAT_TIMEOUT_MS2 = 1e4;
100200
100279
  var PARKING_WINDOW_NAME2 = "tmex-park";
100201
100280
 
100202
100281
  class SshExternalTmuxConnection {
@@ -100220,6 +100299,9 @@ class SshExternalTmuxConnection {
100220
100299
  controlStartedAt = 0;
100221
100300
  controlRestartCount = 0;
100222
100301
  controlStderrTail = "";
100302
+ heartbeatTimer = null;
100303
+ heartbeatPending = false;
100304
+ heartbeatTimeoutTimer = null;
100223
100305
  sshClient = null;
100224
100306
  commandStream = null;
100225
100307
  commandStdoutBuffer = "";
@@ -100641,6 +100723,7 @@ class SshExternalTmuxConnection {
100641
100723
  await this.runTmuxAllowFailure(["kill-window", "-t", windowId]);
100642
100724
  }
100643
100725
  async startControlClient() {
100726
+ this.stopHeartbeat();
100644
100727
  let attachReadyResolve = null;
100645
100728
  const attachReady = new Promise((resolve3) => {
100646
100729
  attachReadyResolve = resolve3;
@@ -100662,6 +100745,7 @@ class SshExternalTmuxConnection {
100662
100745
  if (this.controlChannel !== handle) {
100663
100746
  throw new Error(this.controlStderrTail.trim() || "tmux control client channel closed during attach");
100664
100747
  }
100748
+ this.startHeartbeat();
100665
100749
  }
100666
100750
  async openControlChannel(onAttachReady) {
100667
100751
  const subscription = createControlModeSubscription({
@@ -100688,16 +100772,25 @@ class SshExternalTmuxConnection {
100688
100772
  this.requestSnapshot();
100689
100773
  },
100690
100774
  onExit: () => {},
100691
- onBlockEnd: () => {
100775
+ onPause: (paneId) => {
100776
+ if (this.controlChannel === handle) {
100777
+ handle.write("refresh-client -A " + paneId + ":continue" + `
100778
+ `);
100779
+ }
100780
+ },
100781
+ onBlockEnd: (block) => {
100692
100782
  onAttachReady();
100783
+ if (!block.isError && block.lines.length === 1 && block.lines[0] === "tmex-hb") {
100784
+ this.onHeartbeatResponse();
100785
+ }
100693
100786
  }
100694
100787
  });
100695
- const handle = { stop: () => {} };
100788
+ const handle = { stop: () => {}, write: () => {} };
100696
100789
  this.controlChannel = handle;
100697
100790
  this.controlSubscription = subscription;
100698
100791
  this.controlStartedAt = Date.now();
100699
100792
  this.controlStderrTail = "";
100700
- const stopReader = await this.openReaderChannel(`exec ${quoteShellArg(this.tmuxBin)} -C attach-session -t ${quoteShellArg(this.sessionName)}`, {
100793
+ const reader = await this.openReaderChannel(`exec ${quoteShellArg(this.tmuxBin)} -C attach-session -t ${quoteShellArg(this.sessionName)}`, {
100701
100794
  onData: (data) => {
100702
100795
  if (this.controlChannel === handle) {
100703
100796
  subscription.push(new Uint8Array(data.buffer, data.byteOffset, data.byteLength));
@@ -100712,16 +100805,60 @@ class SshExternalTmuxConnection {
100712
100805
  this.handleControlChannelClose(handle);
100713
100806
  }
100714
100807
  });
100715
- handle.stop = stopReader;
100808
+ handle.stop = reader.stop;
100809
+ handle.write = reader.write;
100716
100810
  return handle;
100717
100811
  }
100718
100812
  stopControlClient() {
100813
+ this.stopHeartbeat();
100719
100814
  const handle = this.controlChannel;
100720
100815
  this.controlChannel = null;
100721
100816
  this.controlSubscription?.dispose();
100722
100817
  this.controlSubscription = null;
100723
100818
  handle?.stop();
100724
100819
  }
100820
+ startHeartbeat() {
100821
+ if (this.heartbeatTimer) {
100822
+ clearInterval(this.heartbeatTimer);
100823
+ }
100824
+ if (this.heartbeatTimeoutTimer) {
100825
+ clearTimeout(this.heartbeatTimeoutTimer);
100826
+ }
100827
+ this.heartbeatPending = false;
100828
+ this.heartbeatTimer = setInterval(() => this.sendHeartbeat(), HEARTBEAT_INTERVAL_MS2);
100829
+ }
100830
+ stopHeartbeat() {
100831
+ if (this.heartbeatTimer) {
100832
+ clearInterval(this.heartbeatTimer);
100833
+ this.heartbeatTimer = null;
100834
+ }
100835
+ if (this.heartbeatTimeoutTimer) {
100836
+ clearTimeout(this.heartbeatTimeoutTimer);
100837
+ this.heartbeatTimeoutTimer = null;
100838
+ }
100839
+ this.heartbeatPending = false;
100840
+ }
100841
+ sendHeartbeat() {
100842
+ if (!this.controlChannel || this.heartbeatPending || !this.connected || this.manualDisconnect) {
100843
+ return;
100844
+ }
100845
+ this.heartbeatPending = true;
100846
+ this.controlChannel.write('display-message -p "tmex-hb"' + `
100847
+ `);
100848
+ this.heartbeatTimeoutTimer = setTimeout(() => {
100849
+ if (this.heartbeatPending && this.connected && !this.manualDisconnect) {
100850
+ console.warn(`[ssh] tmux control client heartbeat timeout on ${this.deviceId}, killing stalled channel`);
100851
+ this.controlChannel?.stop();
100852
+ }
100853
+ }, HEARTBEAT_TIMEOUT_MS2);
100854
+ }
100855
+ onHeartbeatResponse() {
100856
+ this.heartbeatPending = false;
100857
+ if (this.heartbeatTimeoutTimer) {
100858
+ clearTimeout(this.heartbeatTimeoutTimer);
100859
+ this.heartbeatTimeoutTimer = null;
100860
+ }
100861
+ }
100725
100862
  handleControlChannelClose(handle) {
100726
100863
  if (this.controlChannel !== handle) {
100727
100864
  return;
@@ -101172,10 +101309,17 @@ printf '\\036TMEX_END %s %d\\036\\n' ${quoteShellArg(commandId)} $?
101172
101309
  });
101173
101310
  stream.write(`${command}
101174
101311
  `);
101175
- return () => {
101176
- stream.end();
101177
- stream.close();
101178
- stream.destroy();
101312
+ return {
101313
+ stop: () => {
101314
+ stream.end();
101315
+ stream.close();
101316
+ stream.destroy();
101317
+ },
101318
+ write: (data) => {
101319
+ try {
101320
+ stream.write(data);
101321
+ } catch {}
101322
+ }
101179
101323
  };
101180
101324
  }
101181
101325
  isTmuxServerGoneMessage(message) {
@@ -106601,8 +106745,8 @@ function getBaseVersion() {
106601
106745
  if (cachedBase !== undefined)
106602
106746
  return cachedBase;
106603
106747
  let base = null;
106604
- if ("0.14.0") {
106605
- base = "0.14.0";
106748
+ if ("0.15.0") {
106749
+ base = "0.15.0";
106606
106750
  }
106607
106751
  if (!base && config.isProd) {
106608
106752
  base = readInstallMeta()?.cliVersion ?? null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tmex-cli",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "tmex": "./bin/tmex.js",
@@ -1,4 +1,4 @@
1
- import{Y as Fe,u as je,j as o,$ as tt,a0 as nt,a1 as rt,a2 as st,a3 as ot,a4 as it,r,d as H,a5 as J,a6 as Xe,a7 as Ke,a8 as ct,a9 as at,aa as lt,ab as Oe,o as xe,ac as ut,ad as Be,ae as dt,b as Ge,af as We,ag as ft,c as $e,ah as Ve,ai as Ee,aj as mt,ak as ht,al as ze,y as wt,am as pt,B as me,T as xt,an as vt,ao as gt,ap as St,aq as bt,A as Et,f as Rt,g as Tt,i as yt,k as jt,l as It,m as Ct,n as Nt}from"./index-DAElYydw.js";import{T as kt,C as Mt,c as Pt,F as At,a as Qe,R as Lt,A as _t,f as zt,t as Dt,S as Ft}from"./terminal-settings-panel-ZnL_cBER.js";import{w as Bt}from"./selection-clipboard-Dq6Zemfd.js";import{C as Wt}from"./copy-DnlW35bA.js";import{S as qe,K as $t}from"./send-BtOPYsPT.js";/**
1
+ import{Y as Fe,u as je,j as o,$ as tt,a0 as nt,a1 as rt,a2 as st,a3 as ot,a4 as it,r,d as H,a5 as J,a6 as Xe,a7 as Ke,a8 as ct,a9 as at,aa as lt,ab as Oe,o as xe,ac as ut,ad as Be,ae as dt,b as Ge,af as We,ag as ft,c as $e,ah as Ve,ai as Ee,aj as mt,ak as ht,al as ze,y as wt,am as pt,B as me,T as xt,an as vt,ao as gt,ap as St,aq as bt,A as Et,f as Rt,g as Tt,i as yt,k as jt,l as It,m as Ct,n as Nt}from"./index-DOaZjm8K.js";import{T as kt,C as Mt,c as Pt,F as At,a as Qe,R as Lt,A as _t,f as zt,t as Dt,S as Ft}from"./terminal-settings-panel-CNwWqOMV.js";import{w as Bt}from"./selection-clipboard-Dq6Zemfd.js";import{C as Wt}from"./copy-BveLgJe5.js";import{S as qe,K as $t}from"./send-CcFyyX2G.js";/**
2
2
  * @license lucide-react v0.564.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1 +1 @@
1
- import{u as b,r as v,a as _,b as ie,c as re,d as ne,e as I,t as oe,j as s,M as X,B as C,P as Y,A as ce,f as de,g as le,h as ue,T as ee,i as me,k as he,l as pe,m as xe,n as ve,G as fe,o as f,D as ge,p as ye,E as je,q as we,s as $,v as Ne,w as De,x as Q,y as Ce,S as be,L as Se,z as ke,C as Ee,F as Me,H as Pe,I as Ie,J as Ke,K as x,N as z,O as V,Q as U,R as J,U as w,V as Re,W as $e}from"./index-DAElYydw.js";import{C as M,a as P,b as Ae,c as Te,d as Fe}from"./card-CydUB3N3.js";import{Z as We}from"./zap-CBgYyGBV.js";function g(e){const o=e.trim();return o||void 0}function qe(e){return e?{name:e.name,type:e.type,host:e.host??"",port:e.port??22,username:e.username??"",sshConfigRef:e.sshConfigRef??"",session:e.session??"tmex",defaultWorkingDir:e.defaultWorkingDir??"",authMode:e.type==="local"?"auto":e.authMode,password:"",privateKey:"",privateKeyPassphrase:""}:{name:"",type:"local",host:"",port:22,username:"root",sshConfigRef:"",session:"tmex",defaultWorkingDir:"",authMode:"auto",password:"",privateKey:"",privateKeyPassphrase:""}}function Le(e){if(e.type==="local")return{name:e.name.trim(),type:"local",session:g(e.session)??"tmex",defaultWorkingDir:g(e.defaultWorkingDir),authMode:"auto"};const o={name:e.name.trim(),type:"ssh",host:e.host.trim(),port:e.port,username:e.username.trim(),session:g(e.session)??"tmex",defaultWorkingDir:g(e.defaultWorkingDir),authMode:e.authMode};return e.authMode==="configRef"&&(o.sshConfigRef=e.sshConfigRef.trim()),e.authMode==="password"&&(o.password=e.password),e.authMode==="key"&&(o.privateKey=e.privateKey,o.privateKeyPassphrase=e.privateKeyPassphrase||void 0),o}function He(e){if(e.type==="local")return{name:e.name.trim(),session:g(e.session)??"tmex",defaultWorkingDir:g(e.defaultWorkingDir)??"",authMode:"auto"};const o={name:e.name.trim(),host:e.host.trim(),port:e.port,username:e.username.trim(),sshConfigRef:e.authMode==="configRef"?e.sshConfigRef.trim():"",session:g(e.session)??"tmex",defaultWorkingDir:g(e.defaultWorkingDir)??"",authMode:e.authMode};return e.authMode==="password"&&e.password&&(o.password=e.password),e.authMode==="key"&&e.privateKey&&(o.privateKey=e.privateKey,o.privateKeyPassphrase=e.privateKeyPassphrase||void 0),o}function se(e){return Number.isInteger(e)&&e>=1&&e<=65535}function Oe(e){return e.type!=="ssh"?null:e.host.trim()?se(e.port)?e.username.trim()?e.authMode==="configRef"&&!e.sshConfigRef.trim()?"validation.sshConfigRequired":null:"validation.usernameRequired":"validation.portRequired":"validation.hostRequired"}async function G(e,o){try{return(await e.json()).error??o}catch{return o}}function Ue(){const{t:e}=b(),[o,m]=v.useState(!1),[t,y]=v.useState(null),[r,d]=v.useState(null),l=_(),h=ie(n=>{var p;return((p=n.settings)==null?void 0:p.language)??"en_US"});v.useEffect(()=>{const n=()=>m(!0);return window.addEventListener("tmex:open-add-device",n),()=>window.removeEventListener("tmex:open-add-device",n)},[]);const{data:c,isLoading:K,isError:N}=re({queryKey:["devices"],queryFn:async()=>{const n=await fetch("/api/devices");if(!n.ok)throw new Error(e("device.loadFailed"));return n.json()},throwOnError:!1}),S=ne(n=>n.hydrateDeviceErrors);v.useEffect(()=>{c!=null&&c.devices&&S(c.devices.map(n=>({deviceId:n.id,lastError:n.lastError??null,lastErrorType:n.lastErrorType??null})))},[c,S]);const k=I({mutationFn:async n=>{if(!(await fetch(`/api/devices/${n}`,{method:"DELETE"})).ok)throw new Error(e("device.deleteFailed"))},onSuccess:()=>{l.invalidateQueries({queryKey:["devices"]}),f.success(e("common.success"))},onError:n=>{f.error(n instanceof Error?n.message:e("common.error"))}}),E=v.useMemo(()=>[...(c==null?void 0:c.devices)??[]].sort((p,D)=>p.sortOrder-D.sortOrder||p.name.localeCompare(D.name,oe(h),{numeric:!0,sensitivity:"base"})),[c==null?void 0:c.devices,h]);return s.jsxs("div",{className:"mx-auto flex w-full max-w-6xl flex-col gap-3 p-3 pb-[calc(1rem+env(safe-area-inset-bottom))] sm:gap-4 sm:p-5","data-testid":"devices-page",children:[K?s.jsx(M,{children:s.jsx(P,{className:"py-16 text-center text-sm text-muted-foreground",children:e("common.loading")})}):N?s.jsx(M,{children:s.jsx(P,{className:"py-16 text-center text-sm text-destructive",children:e("device.loadFailed")})}):E.length===0?s.jsx(M,{children:s.jsxs(P,{className:"space-y-4 py-14 text-center",children:[s.jsx("div",{className:"mx-auto flex h-12 w-12 items-center justify-center rounded-xl border border-border bg-muted",children:s.jsx(X,{className:"h-6 w-6 text-muted-foreground"})}),s.jsxs("div",{className:"space-y-1",children:[s.jsx("h2",{className:"text-lg font-medium",children:e("device.noDevices")}),s.jsx("p",{className:"text-sm text-muted-foreground",children:e("device.addDevice")})]}),s.jsxs(C,{variant:"default","data-testid":"devices-add-empty",onClick:()=>m(!0),children:[s.jsx(Y,{className:"h-4 w-4"}),e("device.addDevice")]})]})}):s.jsx("div",{className:"grid gap-3 lg:grid-cols-2",children:E.map(n=>s.jsx(Be,{device:n,onEdit:()=>y(n),onDelete:()=>d(n)},n.id))}),o&&s.jsx(Z,{mode:"create",onClose:()=>m(!1)}),t&&s.jsx(Z,{mode:"edit",device:t,onClose:()=>y(null)}),s.jsx(ce,{open:r!==null,onOpenChange:n=>!n&&d(null),children:s.jsxs(de,{children:[s.jsxs(le,{children:[s.jsx(ue,{className:"bg-destructive/10",children:s.jsx(ee,{className:"h-5 w-5 text-destructive"})}),s.jsx(me,{children:e("device.deleteConfirm")}),s.jsx(he,{children:e("device.deleteDescription",{name:(r==null?void 0:r.name)??""})})]}),s.jsxs(pe,{children:[s.jsx(xe,{children:e("common.cancel")}),s.jsx(ve,{variant:"destructive",disabled:!r||k.isPending,onClick:()=>{r&&(k.mutate(r.id),d(null))},children:e("common.delete")})]})]})})]})}function Be({device:e,onEdit:o,onDelete:m}){const{t}=b(),y=e.type==="local"?s.jsx(X,{className:"h-4 w-4"}):s.jsx(fe,{className:"h-4 w-4"}),r=e.type==="local"?t("device.typeLocal"):`${e.username??"-"}@${e.host??"-"}:${e.port??22}`,d=I({mutationFn:async()=>{const l=await fetch(`/api/devices/${e.id}/test-connection`,{method:"POST"});let h=null;try{h=await l.json()}catch{h=null}if(!l.ok){const c=h;throw new Error((c==null?void 0:c.error)??t("common.error"))}return h},onSuccess:l=>{f.success(l.message??t("common.success"))},onError:l=>{f.error(l instanceof Error?l.message:t("common.error"))}});return s.jsxs(M,{"data-testid":"device-card","data-device-id":e.id,"data-device-name":e.name,className:"overflow-hidden border-border/50",children:[s.jsxs(Ae,{className:"space-y-2 pb-2",children:[s.jsxs("div",{className:"flex items-start justify-between gap-2",children:[s.jsxs("div",{className:"flex min-w-0 items-center gap-2.5",children:[s.jsx("div",{className:"flex h-8 w-8 shrink-0 items-center justify-center rounded-md border border-border bg-muted text-muted-foreground",children:y}),s.jsxs("div",{className:"min-w-0 space-y-0.5",children:[s.jsx(Te,{className:"line-clamp-1 text-sm",title:e.name,children:e.name}),s.jsx(Fe,{className:"line-clamp-1 text-xs",children:r})]})]}),s.jsx("div",{className:"flex shrink-0 items-center gap-1",children:s.jsxs(ge,{children:[s.jsx(ye,{render:s.jsx(C,{variant:"ghost",size:"icon-sm","data-testid":`device-card-actions-${e.id}`,"aria-label":t("common.edit"),title:t("common.edit")}),children:s.jsx(je,{className:"h-4 w-4"})}),s.jsxs(we,{align:"end",children:[s.jsxs($,{"data-testid":`device-card-edit-${e.id}`,onClick:o,children:[s.jsx(Ne,{className:"h-4 w-4"}),t("common.edit")]}),e.type==="ssh"&&s.jsxs($,{"data-testid":`device-card-test-${e.id}`,onClick:()=>d.mutate(),disabled:d.isPending,children:[s.jsx(We,{className:"h-4 w-4"}),t("common.test")]}),s.jsx(De,{}),s.jsxs($,{"data-testid":`device-card-delete-${e.id}`,variant:"destructive",onClick:m,children:[s.jsx(ee,{className:"h-4 w-4"}),t("common.delete")]})]})]})})]}),s.jsxs("div",{className:"flex flex-wrap items-center gap-1.5",children:[s.jsx(Q,{variant:"outline",className:"text-[11px] font-normal",children:e.type==="local"?t("device.typeLocal"):t("device.typeSSHBadge")}),e.session&&s.jsx(Q,{variant:"outline",className:"text-[11px] font-normal",children:e.session}),s.jsx(Ce,{deviceId:e.id})]})]}),s.jsxs(P,{className:"pt-0",children:[s.jsx(be,{className:"mb-2"}),s.jsx("div",{className:"flex items-center justify-end",children:s.jsx(Se,{to:`/devices/${e.id}`,"data-testid":`device-card-connect-${e.id}`,className:ke({variant:"outline",size:"sm"}),children:t("device.connect")})})]})]})}function Z({mode:e,device:o,onClose:m}){const{t}=b(),y=_(),[r,d]=v.useState(qe(o)),[l,h]=v.useState(!1),[c,K]=v.useState(!1),N=e==="edit",S=r.type==="ssh",k=I({mutationFn:async a=>{const i=await fetch("/api/devices",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(a)});if(!i.ok)throw new Error(await G(i,t("device.createFailed")));return i.json()},onSuccess:()=>{y.invalidateQueries({queryKey:["devices"]}),f.success(t("common.success")),m()},onError:a=>{f.error(a instanceof Error?a.message:t("common.error"))}}),E=I({mutationFn:async a=>{if(!o)throw new Error(t("apiError.deviceNotFound"));const i=await fetch(`/api/devices/${o.id}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify(a)});if(!i.ok)throw new Error(await G(i,t("device.updateFailed")));return i.json()},onSuccess:()=>{y.invalidateQueries({queryKey:["devices"]}),f.success(t("common.success")),m()},onError:a=>{f.error(a instanceof Error?a.message:t("common.error"))}}),n=async a=>{a.preventDefault(),K(!0);const i=Oe(r);if(i){f.error(t(i));return}h(!0);try{e==="create"?await k.mutateAsync(Le(r)):await E.mutateAsync(He(r))}catch{}finally{h(!1)}},p=`${e}-device-name`,D=`${e}-device-type`,A=`${e}-device-host`,T=`${e}-device-port`,F=`${e}-device-username`,W=`${e}-device-session`,q=`${e}-device-default-working-dir`,L=`${e}-device-auth-mode`,H=`${e}-device-password`,O=`${e}-device-private-key`,B=`${e}-device-private-key-passphrase`,te={local:t("device.typeLocal"),ssh:t("device.typeSSH")},ae={password:t("device.authPassword"),key:t("device.authKey"),agent:t("device.authAgent"),configRef:t("device.authConfigRef")},R=a=>s.jsx("div",{className:"text-[11px] font-semibold uppercase tracking-wider text-muted-foreground",children:a}),u=(a,i,j)=>s.jsxs("label",{className:"block text-xs font-medium text-foreground",htmlFor:a,children:[i,j&&s.jsx("span",{className:"ml-0.5 text-destructive",children:"*"})]});return s.jsx(Ee,{open:!0,onOpenChange:a=>!a&&m(),children:s.jsxs(Me,{"data-testid":"device-dialog",className:"w-full max-w-2xl",children:[s.jsxs(Pe,{children:[s.jsx(Ie,{children:t(N?"device.editDevice":"device.addDevice")}),s.jsx(Ke,{children:t(N?"device.editDeviceDescription":"device.addDeviceDescription")})]}),s.jsxs("form",{onSubmit:n,className:"space-y-4",children:[s.jsxs("div",{className:"-mr-2 max-h-[min(70dvh,720px)] space-y-5 overflow-y-auto pr-2",children:[s.jsxs("section",{className:"space-y-2.5",children:[R(t("device.sectionBasic")),s.jsxs("div",{className:"grid gap-3 sm:grid-cols-2",children:[s.jsxs("div",{className:"space-y-1.5 sm:col-span-2",children:[u(p,t("device.name"),!0),s.jsx(x,{id:p,"data-testid":"device-name-input",type:"text",value:r.name,onChange:a=>d(i=>({...i,name:a.target.value})),placeholder:t("device.namePlaceholder"),required:!0})]}),s.jsxs("div",{className:"space-y-1.5",children:[u(D,t("device.type")),s.jsxs(z,{value:r.type,onValueChange:a=>{if(!a)return;const i=a;d(j=>({...j,type:i,authMode:i==="local"?"auto":j.authMode==="auto"?"agent":j.authMode}))},disabled:N,children:[s.jsx(V,{id:D,"data-testid":"device-type-select",className:"w-full",children:s.jsx(U,{placeholder:t("device.type"),children:a=>te[a]??""})}),s.jsxs(J,{children:[s.jsx(w,{value:"local",children:t("device.typeLocal")}),s.jsx(w,{value:"ssh",children:t("device.typeSSH")})]})]})]}),s.jsxs("div",{className:"space-y-1.5",children:[u(W,t("device.session")),s.jsx(x,{id:W,"data-testid":"device-session-input",type:"text",value:r.session,onChange:a=>d(i=>({...i,session:a.target.value})),placeholder:t("device.sessionPlaceholder")})]}),s.jsxs("div",{className:"space-y-1.5 sm:col-span-2",children:[u(q,t("device.defaultWorkingDir")),s.jsx(x,{id:q,"data-testid":"device-default-working-dir-input",type:"text",value:r.defaultWorkingDir,onChange:a=>d(i=>({...i,defaultWorkingDir:a.target.value})),placeholder:t("device.defaultWorkingDirPlaceholder")})]})]})]}),S&&s.jsxs(s.Fragment,{children:[s.jsxs("section",{className:"space-y-2.5",children:[R(t("device.sectionConnection")),s.jsxs("div",{className:"grid gap-3 sm:grid-cols-3",children:[s.jsxs("div",{className:"space-y-1.5 sm:col-span-2",children:[u(A,t("device.host"),!0),s.jsx(x,{id:A,type:"text",value:r.host,onChange:a=>d(i=>({...i,host:a.target.value})),placeholder:t("device.hostPlaceholder"),"aria-invalid":c&&!r.host.trim()})]}),s.jsxs("div",{className:"space-y-1.5",children:[u(T,t("device.port"),!0),s.jsx(x,{id:T,type:"number",value:Number.isNaN(r.port)?"":r.port,onChange:a=>{const i=a.target.value;d(j=>({...j,port:i===""?Number.NaN:Number.parseInt(i,10)}))},min:1,max:65535,"aria-invalid":c&&!se(r.port)})]}),s.jsxs("div",{className:"space-y-1.5 sm:col-span-2",children:[u(F,t("device.username"),!0),s.jsx(x,{id:F,type:"text",value:r.username,onChange:a=>d(i=>({...i,username:a.target.value})),placeholder:t("device.usernamePlaceholder"),"aria-invalid":c&&!r.username.trim()})]})]})]}),s.jsxs("section",{className:"space-y-2.5",children:[R(t("device.sectionAuth")),s.jsxs("div",{className:"space-y-3",children:[s.jsxs("div",{className:"space-y-1.5",children:[u(L,t("device.authMode")),s.jsxs(z,{value:r.authMode,onValueChange:a=>{a&&d(i=>({...i,authMode:a}))},children:[s.jsx(V,{id:L,"data-testid":"device-auth-mode-select",className:"w-full",children:s.jsx(U,{placeholder:t("device.authMode"),children:a=>ae[a]??""})}),s.jsxs(J,{children:[s.jsx(w,{value:"password",children:t("device.authPassword")}),s.jsx(w,{value:"key",children:t("device.authKey")}),s.jsx(w,{value:"agent",children:t("device.authAgent")}),s.jsx(w,{value:"configRef",children:t("device.authConfigRef")})]})]})]}),r.authMode==="password"&&s.jsxs("div",{className:"space-y-1.5",children:[u(H,t("device.password")),s.jsx(x,{id:H,type:"password",value:r.password,onChange:a=>d(i=>({...i,password:a.target.value}))})]}),r.authMode==="key"&&s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:"space-y-1.5",children:[u(O,t("device.privateKey")),s.jsx(Re,{id:O,value:r.privateKey,onChange:a=>d(i=>({...i,privateKey:a.target.value})),className:"h-28 font-mono text-xs",placeholder:t("device.privateKeyPlaceholder")})]}),s.jsxs("div",{className:"space-y-1.5",children:[u(B,t("device.passphrase")),s.jsx(x,{id:B,type:"password",value:r.privateKeyPassphrase,onChange:a=>d(i=>({...i,privateKeyPassphrase:a.target.value}))})]})]}),r.authMode==="configRef"&&s.jsxs("div",{className:"space-y-1.5",children:[u(`${e}-device-ssh-config-ref`,t("device.authConfigRef"),!0),s.jsx(x,{id:`${e}-device-ssh-config-ref`,"data-testid":"device-ssh-config-ref-input",type:"text",value:r.sshConfigRef,onChange:a=>d(i=>({...i,sshConfigRef:a.target.value})),placeholder:t("device.sshConfigRefPlaceholder"),"aria-invalid":c&&!r.sshConfigRef.trim()}),s.jsx("p",{className:"text-[11px] text-muted-foreground",children:t("device.sshConfigRefHint")})]})]})]})]})]}),s.jsxs($e,{children:[s.jsx(C,{type:"button",variant:"outline",onClick:m,children:t("common.cancel")}),s.jsx(C,{type:"submit",variant:"default","data-testid":"device-dialog-save",disabled:l,children:t(l?"common.saving":"common.save")})]})]})]})})}function Je(){const{t:e}=b();return s.jsx(s.Fragment,{children:e("sidebar.manageDevices")})}function Ge(){const{t:e}=b(),o=()=>{window.dispatchEvent(new CustomEvent("tmex:open-add-device"))};return s.jsx(C,{variant:"ghost",size:"icon-sm","data-testid":"devices-add",onClick:o,"aria-label":e("sidebar.addDevice"),title:e("sidebar.addDevice"),children:s.jsx(Y,{className:"h-4 w-4"})})}export{Ge as PageActions,Je as PageTitle,Ue as default};
1
+ import{u as b,r as v,a as _,b as ie,c as re,d as ne,e as I,t as oe,j as s,M as X,B as C,P as Y,A as ce,f as de,g as le,h as ue,T as ee,i as me,k as he,l as pe,m as xe,n as ve,G as fe,o as f,D as ge,p as ye,E as je,q as we,s as $,v as Ne,w as De,x as Q,y as Ce,S as be,L as Se,z as ke,C as Ee,F as Me,H as Pe,I as Ie,J as Ke,K as x,N as z,O as V,Q as U,R as J,U as w,V as Re,W as $e}from"./index-DOaZjm8K.js";import{C as M,a as P,b as Ae,c as Te,d as Fe}from"./card-CXa-2u8i.js";import{Z as We}from"./zap-DPwn8lbv.js";function g(e){const o=e.trim();return o||void 0}function qe(e){return e?{name:e.name,type:e.type,host:e.host??"",port:e.port??22,username:e.username??"",sshConfigRef:e.sshConfigRef??"",session:e.session??"tmex",defaultWorkingDir:e.defaultWorkingDir??"",authMode:e.type==="local"?"auto":e.authMode,password:"",privateKey:"",privateKeyPassphrase:""}:{name:"",type:"local",host:"",port:22,username:"root",sshConfigRef:"",session:"tmex",defaultWorkingDir:"",authMode:"auto",password:"",privateKey:"",privateKeyPassphrase:""}}function Le(e){if(e.type==="local")return{name:e.name.trim(),type:"local",session:g(e.session)??"tmex",defaultWorkingDir:g(e.defaultWorkingDir),authMode:"auto"};const o={name:e.name.trim(),type:"ssh",host:e.host.trim(),port:e.port,username:e.username.trim(),session:g(e.session)??"tmex",defaultWorkingDir:g(e.defaultWorkingDir),authMode:e.authMode};return e.authMode==="configRef"&&(o.sshConfigRef=e.sshConfigRef.trim()),e.authMode==="password"&&(o.password=e.password),e.authMode==="key"&&(o.privateKey=e.privateKey,o.privateKeyPassphrase=e.privateKeyPassphrase||void 0),o}function He(e){if(e.type==="local")return{name:e.name.trim(),session:g(e.session)??"tmex",defaultWorkingDir:g(e.defaultWorkingDir)??"",authMode:"auto"};const o={name:e.name.trim(),host:e.host.trim(),port:e.port,username:e.username.trim(),sshConfigRef:e.authMode==="configRef"?e.sshConfigRef.trim():"",session:g(e.session)??"tmex",defaultWorkingDir:g(e.defaultWorkingDir)??"",authMode:e.authMode};return e.authMode==="password"&&e.password&&(o.password=e.password),e.authMode==="key"&&e.privateKey&&(o.privateKey=e.privateKey,o.privateKeyPassphrase=e.privateKeyPassphrase||void 0),o}function se(e){return Number.isInteger(e)&&e>=1&&e<=65535}function Oe(e){return e.type!=="ssh"?null:e.host.trim()?se(e.port)?e.username.trim()?e.authMode==="configRef"&&!e.sshConfigRef.trim()?"validation.sshConfigRequired":null:"validation.usernameRequired":"validation.portRequired":"validation.hostRequired"}async function G(e,o){try{return(await e.json()).error??o}catch{return o}}function Ue(){const{t:e}=b(),[o,m]=v.useState(!1),[t,y]=v.useState(null),[r,d]=v.useState(null),l=_(),h=ie(n=>{var p;return((p=n.settings)==null?void 0:p.language)??"en_US"});v.useEffect(()=>{const n=()=>m(!0);return window.addEventListener("tmex:open-add-device",n),()=>window.removeEventListener("tmex:open-add-device",n)},[]);const{data:c,isLoading:K,isError:N}=re({queryKey:["devices"],queryFn:async()=>{const n=await fetch("/api/devices");if(!n.ok)throw new Error(e("device.loadFailed"));return n.json()},throwOnError:!1}),S=ne(n=>n.hydrateDeviceErrors);v.useEffect(()=>{c!=null&&c.devices&&S(c.devices.map(n=>({deviceId:n.id,lastError:n.lastError??null,lastErrorType:n.lastErrorType??null})))},[c,S]);const k=I({mutationFn:async n=>{if(!(await fetch(`/api/devices/${n}`,{method:"DELETE"})).ok)throw new Error(e("device.deleteFailed"))},onSuccess:()=>{l.invalidateQueries({queryKey:["devices"]}),f.success(e("common.success"))},onError:n=>{f.error(n instanceof Error?n.message:e("common.error"))}}),E=v.useMemo(()=>[...(c==null?void 0:c.devices)??[]].sort((p,D)=>p.sortOrder-D.sortOrder||p.name.localeCompare(D.name,oe(h),{numeric:!0,sensitivity:"base"})),[c==null?void 0:c.devices,h]);return s.jsxs("div",{className:"mx-auto flex w-full max-w-6xl flex-col gap-3 p-3 pb-[calc(1rem+env(safe-area-inset-bottom))] sm:gap-4 sm:p-5","data-testid":"devices-page",children:[K?s.jsx(M,{children:s.jsx(P,{className:"py-16 text-center text-sm text-muted-foreground",children:e("common.loading")})}):N?s.jsx(M,{children:s.jsx(P,{className:"py-16 text-center text-sm text-destructive",children:e("device.loadFailed")})}):E.length===0?s.jsx(M,{children:s.jsxs(P,{className:"space-y-4 py-14 text-center",children:[s.jsx("div",{className:"mx-auto flex h-12 w-12 items-center justify-center rounded-xl border border-border bg-muted",children:s.jsx(X,{className:"h-6 w-6 text-muted-foreground"})}),s.jsxs("div",{className:"space-y-1",children:[s.jsx("h2",{className:"text-lg font-medium",children:e("device.noDevices")}),s.jsx("p",{className:"text-sm text-muted-foreground",children:e("device.addDevice")})]}),s.jsxs(C,{variant:"default","data-testid":"devices-add-empty",onClick:()=>m(!0),children:[s.jsx(Y,{className:"h-4 w-4"}),e("device.addDevice")]})]})}):s.jsx("div",{className:"grid gap-3 lg:grid-cols-2",children:E.map(n=>s.jsx(Be,{device:n,onEdit:()=>y(n),onDelete:()=>d(n)},n.id))}),o&&s.jsx(Z,{mode:"create",onClose:()=>m(!1)}),t&&s.jsx(Z,{mode:"edit",device:t,onClose:()=>y(null)}),s.jsx(ce,{open:r!==null,onOpenChange:n=>!n&&d(null),children:s.jsxs(de,{children:[s.jsxs(le,{children:[s.jsx(ue,{className:"bg-destructive/10",children:s.jsx(ee,{className:"h-5 w-5 text-destructive"})}),s.jsx(me,{children:e("device.deleteConfirm")}),s.jsx(he,{children:e("device.deleteDescription",{name:(r==null?void 0:r.name)??""})})]}),s.jsxs(pe,{children:[s.jsx(xe,{children:e("common.cancel")}),s.jsx(ve,{variant:"destructive",disabled:!r||k.isPending,onClick:()=>{r&&(k.mutate(r.id),d(null))},children:e("common.delete")})]})]})})]})}function Be({device:e,onEdit:o,onDelete:m}){const{t}=b(),y=e.type==="local"?s.jsx(X,{className:"h-4 w-4"}):s.jsx(fe,{className:"h-4 w-4"}),r=e.type==="local"?t("device.typeLocal"):`${e.username??"-"}@${e.host??"-"}:${e.port??22}`,d=I({mutationFn:async()=>{const l=await fetch(`/api/devices/${e.id}/test-connection`,{method:"POST"});let h=null;try{h=await l.json()}catch{h=null}if(!l.ok){const c=h;throw new Error((c==null?void 0:c.error)??t("common.error"))}return h},onSuccess:l=>{f.success(l.message??t("common.success"))},onError:l=>{f.error(l instanceof Error?l.message:t("common.error"))}});return s.jsxs(M,{"data-testid":"device-card","data-device-id":e.id,"data-device-name":e.name,className:"overflow-hidden border-border/50",children:[s.jsxs(Ae,{className:"space-y-2 pb-2",children:[s.jsxs("div",{className:"flex items-start justify-between gap-2",children:[s.jsxs("div",{className:"flex min-w-0 items-center gap-2.5",children:[s.jsx("div",{className:"flex h-8 w-8 shrink-0 items-center justify-center rounded-md border border-border bg-muted text-muted-foreground",children:y}),s.jsxs("div",{className:"min-w-0 space-y-0.5",children:[s.jsx(Te,{className:"line-clamp-1 text-sm",title:e.name,children:e.name}),s.jsx(Fe,{className:"line-clamp-1 text-xs",children:r})]})]}),s.jsx("div",{className:"flex shrink-0 items-center gap-1",children:s.jsxs(ge,{children:[s.jsx(ye,{render:s.jsx(C,{variant:"ghost",size:"icon-sm","data-testid":`device-card-actions-${e.id}`,"aria-label":t("common.edit"),title:t("common.edit")}),children:s.jsx(je,{className:"h-4 w-4"})}),s.jsxs(we,{align:"end",children:[s.jsxs($,{"data-testid":`device-card-edit-${e.id}`,onClick:o,children:[s.jsx(Ne,{className:"h-4 w-4"}),t("common.edit")]}),e.type==="ssh"&&s.jsxs($,{"data-testid":`device-card-test-${e.id}`,onClick:()=>d.mutate(),disabled:d.isPending,children:[s.jsx(We,{className:"h-4 w-4"}),t("common.test")]}),s.jsx(De,{}),s.jsxs($,{"data-testid":`device-card-delete-${e.id}`,variant:"destructive",onClick:m,children:[s.jsx(ee,{className:"h-4 w-4"}),t("common.delete")]})]})]})})]}),s.jsxs("div",{className:"flex flex-wrap items-center gap-1.5",children:[s.jsx(Q,{variant:"outline",className:"text-[11px] font-normal",children:e.type==="local"?t("device.typeLocal"):t("device.typeSSHBadge")}),e.session&&s.jsx(Q,{variant:"outline",className:"text-[11px] font-normal",children:e.session}),s.jsx(Ce,{deviceId:e.id})]})]}),s.jsxs(P,{className:"pt-0",children:[s.jsx(be,{className:"mb-2"}),s.jsx("div",{className:"flex items-center justify-end",children:s.jsx(Se,{to:`/devices/${e.id}`,"data-testid":`device-card-connect-${e.id}`,className:ke({variant:"outline",size:"sm"}),children:t("device.connect")})})]})]})}function Z({mode:e,device:o,onClose:m}){const{t}=b(),y=_(),[r,d]=v.useState(qe(o)),[l,h]=v.useState(!1),[c,K]=v.useState(!1),N=e==="edit",S=r.type==="ssh",k=I({mutationFn:async a=>{const i=await fetch("/api/devices",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(a)});if(!i.ok)throw new Error(await G(i,t("device.createFailed")));return i.json()},onSuccess:()=>{y.invalidateQueries({queryKey:["devices"]}),f.success(t("common.success")),m()},onError:a=>{f.error(a instanceof Error?a.message:t("common.error"))}}),E=I({mutationFn:async a=>{if(!o)throw new Error(t("apiError.deviceNotFound"));const i=await fetch(`/api/devices/${o.id}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify(a)});if(!i.ok)throw new Error(await G(i,t("device.updateFailed")));return i.json()},onSuccess:()=>{y.invalidateQueries({queryKey:["devices"]}),f.success(t("common.success")),m()},onError:a=>{f.error(a instanceof Error?a.message:t("common.error"))}}),n=async a=>{a.preventDefault(),K(!0);const i=Oe(r);if(i){f.error(t(i));return}h(!0);try{e==="create"?await k.mutateAsync(Le(r)):await E.mutateAsync(He(r))}catch{}finally{h(!1)}},p=`${e}-device-name`,D=`${e}-device-type`,A=`${e}-device-host`,T=`${e}-device-port`,F=`${e}-device-username`,W=`${e}-device-session`,q=`${e}-device-default-working-dir`,L=`${e}-device-auth-mode`,H=`${e}-device-password`,O=`${e}-device-private-key`,B=`${e}-device-private-key-passphrase`,te={local:t("device.typeLocal"),ssh:t("device.typeSSH")},ae={password:t("device.authPassword"),key:t("device.authKey"),agent:t("device.authAgent"),configRef:t("device.authConfigRef")},R=a=>s.jsx("div",{className:"text-[11px] font-semibold uppercase tracking-wider text-muted-foreground",children:a}),u=(a,i,j)=>s.jsxs("label",{className:"block text-xs font-medium text-foreground",htmlFor:a,children:[i,j&&s.jsx("span",{className:"ml-0.5 text-destructive",children:"*"})]});return s.jsx(Ee,{open:!0,onOpenChange:a=>!a&&m(),children:s.jsxs(Me,{"data-testid":"device-dialog",className:"w-full max-w-2xl",children:[s.jsxs(Pe,{children:[s.jsx(Ie,{children:t(N?"device.editDevice":"device.addDevice")}),s.jsx(Ke,{children:t(N?"device.editDeviceDescription":"device.addDeviceDescription")})]}),s.jsxs("form",{onSubmit:n,className:"space-y-4",children:[s.jsxs("div",{className:"-mr-2 max-h-[min(70dvh,720px)] space-y-5 overflow-y-auto pr-2",children:[s.jsxs("section",{className:"space-y-2.5",children:[R(t("device.sectionBasic")),s.jsxs("div",{className:"grid gap-3 sm:grid-cols-2",children:[s.jsxs("div",{className:"space-y-1.5 sm:col-span-2",children:[u(p,t("device.name"),!0),s.jsx(x,{id:p,"data-testid":"device-name-input",type:"text",value:r.name,onChange:a=>d(i=>({...i,name:a.target.value})),placeholder:t("device.namePlaceholder"),required:!0})]}),s.jsxs("div",{className:"space-y-1.5",children:[u(D,t("device.type")),s.jsxs(z,{value:r.type,onValueChange:a=>{if(!a)return;const i=a;d(j=>({...j,type:i,authMode:i==="local"?"auto":j.authMode==="auto"?"agent":j.authMode}))},disabled:N,children:[s.jsx(V,{id:D,"data-testid":"device-type-select",className:"w-full",children:s.jsx(U,{placeholder:t("device.type"),children:a=>te[a]??""})}),s.jsxs(J,{children:[s.jsx(w,{value:"local",children:t("device.typeLocal")}),s.jsx(w,{value:"ssh",children:t("device.typeSSH")})]})]})]}),s.jsxs("div",{className:"space-y-1.5",children:[u(W,t("device.session")),s.jsx(x,{id:W,"data-testid":"device-session-input",type:"text",value:r.session,onChange:a=>d(i=>({...i,session:a.target.value})),placeholder:t("device.sessionPlaceholder")})]}),s.jsxs("div",{className:"space-y-1.5 sm:col-span-2",children:[u(q,t("device.defaultWorkingDir")),s.jsx(x,{id:q,"data-testid":"device-default-working-dir-input",type:"text",value:r.defaultWorkingDir,onChange:a=>d(i=>({...i,defaultWorkingDir:a.target.value})),placeholder:t("device.defaultWorkingDirPlaceholder")})]})]})]}),S&&s.jsxs(s.Fragment,{children:[s.jsxs("section",{className:"space-y-2.5",children:[R(t("device.sectionConnection")),s.jsxs("div",{className:"grid gap-3 sm:grid-cols-3",children:[s.jsxs("div",{className:"space-y-1.5 sm:col-span-2",children:[u(A,t("device.host"),!0),s.jsx(x,{id:A,type:"text",value:r.host,onChange:a=>d(i=>({...i,host:a.target.value})),placeholder:t("device.hostPlaceholder"),"aria-invalid":c&&!r.host.trim()})]}),s.jsxs("div",{className:"space-y-1.5",children:[u(T,t("device.port"),!0),s.jsx(x,{id:T,type:"number",value:Number.isNaN(r.port)?"":r.port,onChange:a=>{const i=a.target.value;d(j=>({...j,port:i===""?Number.NaN:Number.parseInt(i,10)}))},min:1,max:65535,"aria-invalid":c&&!se(r.port)})]}),s.jsxs("div",{className:"space-y-1.5 sm:col-span-2",children:[u(F,t("device.username"),!0),s.jsx(x,{id:F,type:"text",value:r.username,onChange:a=>d(i=>({...i,username:a.target.value})),placeholder:t("device.usernamePlaceholder"),"aria-invalid":c&&!r.username.trim()})]})]})]}),s.jsxs("section",{className:"space-y-2.5",children:[R(t("device.sectionAuth")),s.jsxs("div",{className:"space-y-3",children:[s.jsxs("div",{className:"space-y-1.5",children:[u(L,t("device.authMode")),s.jsxs(z,{value:r.authMode,onValueChange:a=>{a&&d(i=>({...i,authMode:a}))},children:[s.jsx(V,{id:L,"data-testid":"device-auth-mode-select",className:"w-full",children:s.jsx(U,{placeholder:t("device.authMode"),children:a=>ae[a]??""})}),s.jsxs(J,{children:[s.jsx(w,{value:"password",children:t("device.authPassword")}),s.jsx(w,{value:"key",children:t("device.authKey")}),s.jsx(w,{value:"agent",children:t("device.authAgent")}),s.jsx(w,{value:"configRef",children:t("device.authConfigRef")})]})]})]}),r.authMode==="password"&&s.jsxs("div",{className:"space-y-1.5",children:[u(H,t("device.password")),s.jsx(x,{id:H,type:"password",value:r.password,onChange:a=>d(i=>({...i,password:a.target.value}))})]}),r.authMode==="key"&&s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:"space-y-1.5",children:[u(O,t("device.privateKey")),s.jsx(Re,{id:O,value:r.privateKey,onChange:a=>d(i=>({...i,privateKey:a.target.value})),className:"h-28 font-mono text-xs",placeholder:t("device.privateKeyPlaceholder")})]}),s.jsxs("div",{className:"space-y-1.5",children:[u(B,t("device.passphrase")),s.jsx(x,{id:B,type:"password",value:r.privateKeyPassphrase,onChange:a=>d(i=>({...i,privateKeyPassphrase:a.target.value}))})]})]}),r.authMode==="configRef"&&s.jsxs("div",{className:"space-y-1.5",children:[u(`${e}-device-ssh-config-ref`,t("device.authConfigRef"),!0),s.jsx(x,{id:`${e}-device-ssh-config-ref`,"data-testid":"device-ssh-config-ref-input",type:"text",value:r.sshConfigRef,onChange:a=>d(i=>({...i,sshConfigRef:a.target.value})),placeholder:t("device.sshConfigRefPlaceholder"),"aria-invalid":c&&!r.sshConfigRef.trim()}),s.jsx("p",{className:"text-[11px] text-muted-foreground",children:t("device.sshConfigRefHint")})]})]})]})]})]}),s.jsxs($e,{children:[s.jsx(C,{type:"button",variant:"outline",onClick:m,children:t("common.cancel")}),s.jsx(C,{type:"submit",variant:"default","data-testid":"device-dialog-save",disabled:l,children:t(l?"common.saving":"common.save")})]})]})]})})}function Je(){const{t:e}=b();return s.jsx(s.Fragment,{children:e("sidebar.manageDevices")})}function Ge(){const{t:e}=b(),o=()=>{window.dispatchEvent(new CustomEvent("tmex:open-add-device"))};return s.jsx(C,{variant:"ghost",size:"icon-sm","data-testid":"devices-add",onClick:o,"aria-label":e("sidebar.addDevice"),title:e("sidebar.addDevice"),children:s.jsx(Y,{className:"h-4 w-4"})})}export{Ge as PageActions,Je as PageTitle,Ue as default};
@@ -1,4 +1,4 @@
1
- import{Y as zn,_ as ut,r as oe,j as h,X as gt,ad as bt,u as ae,c as $n,al as qn,a as mt,B as se,Z as nn}from"./index-DAElYydw.js";import{r as pt,M as _t}from"./markdown-preview-BX0o83rG.js";import{k as ft,d as Ve,l as te,D as Kn,m as Et,f as ht,c as Nt}from"./api-CGfOwFKm.js";import{R as yt,s as vt}from"./transfer-toast-CUkPc73G.js";import"./index-DBHh6Xu1.js";/**
1
+ import{Y as zn,_ as ut,r as oe,j as h,X as gt,ad as bt,u as ae,c as $n,al as qn,a as mt,B as se,Z as nn}from"./index-DOaZjm8K.js";import{r as pt,M as _t}from"./markdown-preview-rqUSs5w8.js";import{k as ft,d as Ve,l as te,D as Kn,m as Et,f as ht,c as Nt}from"./api-CrHrQflz.js";import{R as yt,s as vt}from"./transfer-toast-mQs0hgyg.js";import"./index-BQbq6q_-.js";/**
2
2
  * @license lucide-react v0.564.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{Y as ce,u as F,j as e,L as It,M as Ze,z as Lt,r as w,c as Q,B as k,P as ge,a as O,e as M,o as b,am as D,v as De,T as te,A as xe,f as fe,g as pe,h as $t,i as we,k as ye,l as je,m as ve,n as be,C as ae,F as ne,H as ie,I as re,J as Ne,N as Ce,O as Ee,Q as ke,R as Se,U as le,K as B,W as de,al as U,G as Bt,x as xt,a4 as Dt,b as Oe,t as et,bo as $,X as ft,a5 as nt,bp as Ot,bq as _t,aS as zt,br as qt,bs as Kt,bt as Qt,bu as Ut,bv as it,Z as Ht}from"./index-DAElYydw.js";import{C as H,b as G,c as J,d as pt,a as V}from"./card-CydUB3N3.js";import{e as Vt,h as wt,F as We,i as Wt,j as Gt,D as Jt}from"./api-CGfOwFKm.js";import{R as tt,T as Yt,b as Xt,d as Zt}from"./terminal-settings-panel-ZnL_cBER.js";import{S as yt}from"./send-BtOPYsPT.js";import{M as es}from"./markdown-preview-BX0o83rG.js";import{T as jt}from"./triangle-alert-DyM9tU7d.js";import"./selection-clipboard-Dq6Zemfd.js";import"./index-DBHh6Xu1.js";/**
1
+ import{Y as ce,u as F,j as e,L as It,M as Ze,z as Lt,r as w,c as Q,B as k,P as ge,a as O,e as M,o as b,am as D,v as De,T as te,A as xe,f as fe,g as pe,h as $t,i as we,k as ye,l as je,m as ve,n as be,C as ae,F as ne,H as ie,I as re,J as Ne,N as Ce,O as Ee,Q as ke,R as Se,U as le,K as B,W as de,al as U,G as Bt,x as xt,a4 as Dt,b as Oe,t as et,bo as $,X as ft,a5 as nt,bp as Ot,bq as _t,aS as zt,br as qt,bs as Kt,bt as Qt,bu as Ut,bv as it,Z as Ht}from"./index-DOaZjm8K.js";import{C as H,b as G,c as J,d as pt,a as V}from"./card-CXa-2u8i.js";import{e as Vt,h as wt,F as We,i as Wt,j as Gt,D as Jt}from"./api-CrHrQflz.js";import{R as tt,T as Yt,b as Xt,d as Zt}from"./terminal-settings-panel-CNwWqOMV.js";import{S as yt}from"./send-CcFyyX2G.js";import{M as es}from"./markdown-preview-rqUSs5w8.js";import{T as jt}from"./triangle-alert-DMp7kMmJ.js";import"./selection-clipboard-Dq6Zemfd.js";import"./index-BQbq6q_-.js";/**
2
2
  * @license lucide-react v0.564.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- import{Y as _,r as N,j as e,X as C,u as k,aI as Y,aJ as ee,aK as te,al as se,aL as ne,aH as F,aM as B,a4 as H,B as y,G as be,M as ve,x as Ne,c as K,N as ye,O as ke,R as Ie,U as X,aN as Ce,aO as Se,V as re,v as Me,ae as _e,a5 as ze,aP as Te,ak as g,d as Ee,aQ as Be,aR as qe,P as $e,aS as Pe,am as Re,ah as Ae}from"./index-DAElYydw.js";import{M as Fe,r as He}from"./index-DBHh6Xu1.js";import{K as Ke,S as Z}from"./send-BtOPYsPT.js";import{Z as ae}from"./zap-CBgYyGBV.js";/**
1
+ import{Y as _,r as N,j as e,X as C,u as k,aI as Y,aJ as ee,aK as te,al as se,aL as ne,aH as F,aM as B,a4 as H,B as y,G as be,M as ve,x as Ne,c as K,N as ye,O as ke,R as Ie,U as X,aN as Ce,aO as Se,V as re,v as Me,ae as _e,a5 as ze,aP as Te,ak as g,d as Ee,aQ as Be,aR as qe,P as $e,aS as Pe,am as Re,ah as Ae}from"./index-DOaZjm8K.js";import{M as Fe,r as He}from"./index-BQbq6q_-.js";import{K as Ke,S as Z}from"./send-CcFyyX2G.js";import{Z as ae}from"./zap-DPwn8lbv.js";/**
2
2
  * @license lucide-react v0.564.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
1
- var N=Object.defineProperty;var P=(t,e,o)=>e in t?N(t,e,{enumerable:!0,configurable:!0,writable:!0,value:o}):t[e]=o;var O=(t,e,o)=>P(t,typeof e!="symbol"?e+"":e,o);import{Y as x}from"./index-DAElYydw.js";/**
1
+ var N=Object.defineProperty;var P=(t,e,o)=>e in t?N(t,e,{enumerable:!0,configurable:!0,writable:!0,value:o}):t[e]=o;var O=(t,e,o)=>P(t,typeof e!="symbol"?e+"":e,o);import{Y as x}from"./index-DOaZjm8K.js";/**
2
2
  * @license lucide-react v0.564.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1 +1 @@
1
- import{a0 as ln,a1 as an,a2 as H,a3 as q,a4 as B,a5 as un,a6 as y,a7 as tn,a8 as L,a9 as _,aa as rn,ab as o,ac as on,ad as sn,ae as fn}from"./mermaid.core-DoE3dx_X.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,C,a){var O=I-l,i=D-h,n=C-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function W(l,h,I,D,v,A,C){var a=l-I,O=h-D,i=(C?A:-A)/L(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,F=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*L(on(0,T*T*R-P*P)),j=(P*g-m*S)/R,z=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=j-F,e=z-t,r=w-F,G=p-t;return x*x+e*e>r*r+G*G&&(j=w,z=p),{cx:j,cy:z,x01:-n,y01:-d,x11:j*(v/T-1),y11:z*(v/T-1)}}function hn(){var l=cn,h=yn,I=B(0),D=null,v=gn,A=dn,C=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-un,c=A.apply(this,arguments)-un,F=rn(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(F>tn-y)a.moveTo(s*H(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*H(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=F,S=F,j=C.apply(this,arguments)/2,z=j>y&&(D?+D.apply(this,arguments):L(u*u+s*s)),w=_(rn(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if(z>y){var G=sn(z/u*q(j)),M=sn(z/s*q(j));(P-=G*2)>y?(G*=t?1:-1,R+=G,T-=G):(P=0,R=T=(f+c)/2),(S-=M*2)>y?(M*=t?1:-1,m+=M,g-=M):(S=0,m=g=(f+c)/2)}var J=s*H(m),K=s*q(m),N=u*H(T),Q=u*q(T);if(w>y){var U=s*H(g),V=s*q(g),X=u*H(R),Y=u*q(R),E;if(F<an)if(E=pn(J,K,X,Y,U,V,N,Q)){var Z=J-E[0],$=K-E[1],b=U-E[0],k=V-E[1],nn=1/q(fn((Z*b+$*k)/(L(Z*Z+$*$)*L(b*b+k*k)))/2),en=L(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=W(X,Y,J,K,s,x,t),r=W(U,V,N,Q,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(J,K),a.arc(0,0,s,m,g,!t)):a.moveTo(J,K),!(u>y)||!(P>y)?a.lineTo(N,Q):p>y?(e=W(N,Q,U,V,u,-p,t),r=W(J,K,X,Y,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-an/2;return[H(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:B(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:B(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:B(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:B(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:B(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:B(+n),i):A},i.padAngle=function(n){return arguments.length?(C=typeof n=="function"?n:B(+n),i):C},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};
1
+ import{a0 as ln,a1 as an,a2 as H,a3 as q,a4 as B,a5 as un,a6 as y,a7 as tn,a8 as L,a9 as _,aa as rn,ab as o,ac as on,ad as sn,ae as fn}from"./mermaid.core-QvhJBi8f.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,C,a){var O=I-l,i=D-h,n=C-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function W(l,h,I,D,v,A,C){var a=l-I,O=h-D,i=(C?A:-A)/L(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,F=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*L(on(0,T*T*R-P*P)),j=(P*g-m*S)/R,z=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=j-F,e=z-t,r=w-F,G=p-t;return x*x+e*e>r*r+G*G&&(j=w,z=p),{cx:j,cy:z,x01:-n,y01:-d,x11:j*(v/T-1),y11:z*(v/T-1)}}function hn(){var l=cn,h=yn,I=B(0),D=null,v=gn,A=dn,C=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-un,c=A.apply(this,arguments)-un,F=rn(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(F>tn-y)a.moveTo(s*H(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*H(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=F,S=F,j=C.apply(this,arguments)/2,z=j>y&&(D?+D.apply(this,arguments):L(u*u+s*s)),w=_(rn(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if(z>y){var G=sn(z/u*q(j)),M=sn(z/s*q(j));(P-=G*2)>y?(G*=t?1:-1,R+=G,T-=G):(P=0,R=T=(f+c)/2),(S-=M*2)>y?(M*=t?1:-1,m+=M,g-=M):(S=0,m=g=(f+c)/2)}var J=s*H(m),K=s*q(m),N=u*H(T),Q=u*q(T);if(w>y){var U=s*H(g),V=s*q(g),X=u*H(R),Y=u*q(R),E;if(F<an)if(E=pn(J,K,X,Y,U,V,N,Q)){var Z=J-E[0],$=K-E[1],b=U-E[0],k=V-E[1],nn=1/q(fn((Z*b+$*k)/(L(Z*Z+$*$)*L(b*b+k*k)))/2),en=L(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=W(X,Y,J,K,s,x,t),r=W(U,V,N,Q,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(J,K),a.arc(0,0,s,m,g,!t)):a.moveTo(J,K),!(u>y)||!(P>y)?a.lineTo(N,Q):p>y?(e=W(N,Q,U,V,u,-p,t),r=W(J,K,X,Y,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-an/2;return[H(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:B(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:B(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:B(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:B(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:B(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:B(+n),i):A},i.padAngle=function(n){return arguments.length?(C=typeof n=="function"?n:B(+n),i):C},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};