tmex-cli 0.16.3 → 0.16.5

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 (72) hide show
  1. package/CHANGELOG.md +5 -13
  2. package/dist/runtime/server.js +277 -50
  3. package/package.json +1 -1
  4. package/resources/fe-dist/assets/{DevicePage-DtByPprm.js → DevicePage-BIjGEtCP.js} +1 -1
  5. package/resources/fe-dist/assets/{DevicesPage-WMW0FcaU.js → DevicesPage-XMtf7VIy.js} +1 -1
  6. package/resources/fe-dist/assets/{FilePage-C3yjgRDh.js → FilePage-C1_JLsEc.js} +1 -1
  7. package/resources/fe-dist/assets/{SettingsPage-CKZEPwEq.js → SettingsPage-BAx39LyM.js} +1 -1
  8. package/resources/fe-dist/assets/{agent-tab-B77EotmX.js → agent-tab-BgvIAv35.js} +1 -1
  9. package/resources/fe-dist/assets/{api-DZDjPn8l.js → api-Dto6Spgl.js} +1 -1
  10. package/resources/fe-dist/assets/{arc-DNrUdLWP.js → arc-BX-Po9p7.js} +1 -1
  11. package/resources/fe-dist/assets/{architectureDiagram-3BPJPVTR-B5DYf-5o.js → architectureDiagram-3BPJPVTR-BneOtjGd.js} +1 -1
  12. package/resources/fe-dist/assets/{blockDiagram-GPEHLZMM-DSgjcUbw.js → blockDiagram-GPEHLZMM-Dcf0shYG.js} +1 -1
  13. package/resources/fe-dist/assets/{c4Diagram-AAUBKEIU-DKjqulHT.js → c4Diagram-AAUBKEIU-Cwhji_wO.js} +1 -1
  14. package/resources/fe-dist/assets/{card-DWQzPt-3.js → card-IkOsEv2J.js} +1 -1
  15. package/resources/fe-dist/assets/channel-DD10rSnh.js +1 -0
  16. package/resources/fe-dist/assets/{chunk-2J33WTMH-DHQ_-AlN.js → chunk-2J33WTMH-IFwQoyns.js} +1 -1
  17. package/resources/fe-dist/assets/{chunk-4BX2VUAB-C7sszhBq.js → chunk-4BX2VUAB-BZhwH7K2.js} +1 -1
  18. package/resources/fe-dist/assets/{chunk-55IACEB6-BkPT2okv.js → chunk-55IACEB6-pYNojBsx.js} +1 -1
  19. package/resources/fe-dist/assets/{chunk-727SXJPM-BsD5c54G.js → chunk-727SXJPM-BwBn-X-G.js} +1 -1
  20. package/resources/fe-dist/assets/{chunk-AQP2D5EJ-Dx3mc4xO.js → chunk-AQP2D5EJ-Cv5U78aO.js} +1 -1
  21. package/resources/fe-dist/assets/{chunk-FMBD7UC4-CyZO-UAp.js → chunk-FMBD7UC4-DnaDXP2z.js} +1 -1
  22. package/resources/fe-dist/assets/{chunk-ND2GUHAM-DNxRBeA7.js → chunk-ND2GUHAM-BCRcaTWl.js} +1 -1
  23. package/resources/fe-dist/assets/{chunk-QZHKN3VN-DsPa138W.js → chunk-QZHKN3VN-1sPBtZCH.js} +1 -1
  24. package/resources/fe-dist/assets/classDiagram-4FO5ZUOK-CEWAB_yV.js +1 -0
  25. package/resources/fe-dist/assets/classDiagram-v2-Q7XG4LA2-CEWAB_yV.js +1 -0
  26. package/resources/fe-dist/assets/{copy-DwXKDgGT.js → copy-CJIriuQt.js} +1 -1
  27. package/resources/fe-dist/assets/{cose-bilkent-S5V4N54A-C1d180_v.js → cose-bilkent-S5V4N54A-CY6ERT7s.js} +1 -1
  28. package/resources/fe-dist/assets/{dagre-BM42HDAG-Bv8hvnH7.js → dagre-BM42HDAG-c_oJjVCK.js} +1 -1
  29. package/resources/fe-dist/assets/{diagram-2AECGRRQ-DfbJBHI6.js → diagram-2AECGRRQ-g-DJAnzv.js} +1 -1
  30. package/resources/fe-dist/assets/{diagram-5GNKFQAL-COnOrAiu.js → diagram-5GNKFQAL-0DikWL30.js} +1 -1
  31. package/resources/fe-dist/assets/{diagram-KO2AKTUF-BOtU5EdV.js → diagram-KO2AKTUF-wWVJH7kq.js} +1 -1
  32. package/resources/fe-dist/assets/{diagram-LMA3HP47-B-WcT-Mc.js → diagram-LMA3HP47-UwQ9OpsG.js} +1 -1
  33. package/resources/fe-dist/assets/{diagram-OG6HWLK6-BptGbu0F.js → diagram-OG6HWLK6-ZFNNtnbo.js} +1 -1
  34. package/resources/fe-dist/assets/{erDiagram-TEJ5UH35-H06uoCHU.js → erDiagram-TEJ5UH35-D3na7WWE.js} +1 -1
  35. package/resources/fe-dist/assets/{files-tab-C841Mmn_.js → files-tab-1FVCHULV.js} +1 -1
  36. package/resources/fe-dist/assets/{flowDiagram-I6XJVG4X-QOk_IVWN.js → flowDiagram-I6XJVG4X-Dsbf9z99.js} +1 -1
  37. package/resources/fe-dist/assets/{ganttDiagram-6RSMTGT7-Q7s2jHyi.js → ganttDiagram-6RSMTGT7-CF3CKflH.js} +1 -1
  38. package/resources/fe-dist/assets/{gitGraphDiagram-PVQCEYII-Q7tAtx1u.js → gitGraphDiagram-PVQCEYII-CRpX3WPf.js} +1 -1
  39. package/resources/fe-dist/assets/{index-C2v9tHtI.js → index-C-RSAqjj.js} +1 -1
  40. package/resources/fe-dist/assets/index-C4Fdxowt.css +1 -0
  41. package/resources/fe-dist/assets/{index-Dc3Fwpho.js → index-U4iZ6Kxr.js} +2 -2
  42. package/resources/fe-dist/assets/{infoDiagram-5YYISTIA-B9n7MrtF.js → infoDiagram-5YYISTIA-DMCNm7PZ.js} +1 -1
  43. package/resources/fe-dist/assets/{ishikawaDiagram-YF4QCWOH-oZeau8IS.js → ishikawaDiagram-YF4QCWOH-CMe-J41c.js} +1 -1
  44. package/resources/fe-dist/assets/{journeyDiagram-JHISSGLW-ClXjUmc1.js → journeyDiagram-JHISSGLW-CxFZDT81.js} +1 -1
  45. package/resources/fe-dist/assets/{kanban-definition-UN3LZRKU-Cwn4VzH2.js → kanban-definition-UN3LZRKU-DwBxD_Gg.js} +1 -1
  46. package/resources/fe-dist/assets/{linear-BNOQBDiB.js → linear-CBM17AV1.js} +1 -1
  47. package/resources/fe-dist/assets/{markdown-preview-BsYVAcJ1.js → markdown-preview-CVeuDkuY.js} +3 -3
  48. package/resources/fe-dist/assets/{mermaid.core-C3nmuA6R.js → mermaid.core-CvSv2dqw.js} +5 -5
  49. package/resources/fe-dist/assets/{mindmap-definition-RKZ34NQL-BKstxajf.js → mindmap-definition-RKZ34NQL-DUu53RHy.js} +1 -1
  50. package/resources/fe-dist/assets/{pieDiagram-4H26LBE5-D_2bHoC9.js → pieDiagram-4H26LBE5-CAbw9YBY.js} +1 -1
  51. package/resources/fe-dist/assets/{quadrantDiagram-W4KKPZXB-D6K_QmUy.js → quadrantDiagram-W4KKPZXB-NmJUURAz.js} +1 -1
  52. package/resources/fe-dist/assets/{requirementDiagram-4Y6WPE33-B1SWUDkA.js → requirementDiagram-4Y6WPE33-B45iu7jV.js} +1 -1
  53. package/resources/fe-dist/assets/{sankeyDiagram-5OEKKPKP-DPmalO0b.js → sankeyDiagram-5OEKKPKP-af9JvT_p.js} +1 -1
  54. package/resources/fe-dist/assets/{send-J45szXFM.js → send-e_FTy5nC.js} +1 -1
  55. package/resources/fe-dist/assets/{sequenceDiagram-3UESZ5HK-C08DcZug.js → sequenceDiagram-3UESZ5HK-CSpSOeHp.js} +1 -1
  56. package/resources/fe-dist/assets/{stateDiagram-AJRCARHV-K9bVAVhg.js → stateDiagram-AJRCARHV-vM6MSoBd.js} +1 -1
  57. package/resources/fe-dist/assets/stateDiagram-v2-BHNVJYJU-8FQLtPkV.js +1 -0
  58. package/resources/fe-dist/assets/{terminal-settings-panel-tZuaqdqz.js → terminal-settings-panel-DU9rOuZd.js} +1 -1
  59. package/resources/fe-dist/assets/{timeline-definition-PNZ67QCA-DG0FtOvK.js → timeline-definition-PNZ67QCA-A63tNc2d.js} +1 -1
  60. package/resources/fe-dist/assets/{transfer-toast-D72q4082.js → transfer-toast-CmSO0e95.js} +1 -1
  61. package/resources/fe-dist/assets/{triangle-alert-CagsqsWp.js → triangle-alert-jNMwsJMj.js} +1 -1
  62. package/resources/fe-dist/assets/{vennDiagram-CIIHVFJN-CKBEKUkA.js → vennDiagram-CIIHVFJN-BktPK2LC.js} +1 -1
  63. package/resources/fe-dist/assets/{wardley-L42UT6IY-DTlpnptg.js → wardley-L42UT6IY-viU1xsE2.js} +1 -1
  64. package/resources/fe-dist/assets/{wardleyDiagram-YWT4CUSO-CnwEikRb.js → wardleyDiagram-YWT4CUSO-DICOr7Mv.js} +1 -1
  65. package/resources/fe-dist/assets/{xychartDiagram-2RQKCTM6-uaYRX4Ue.js → xychartDiagram-2RQKCTM6-bjue7pS9.js} +1 -1
  66. package/resources/fe-dist/assets/{zap-DcJ8Gp1I.js → zap-ClXbg92C.js} +1 -1
  67. package/resources/fe-dist/index.html +2 -2
  68. package/resources/fe-dist/assets/channel-CpZANba-.js +0 -1
  69. package/resources/fe-dist/assets/classDiagram-4FO5ZUOK-BW09ep_0.js +0 -1
  70. package/resources/fe-dist/assets/classDiagram-v2-Q7XG4LA2-BW09ep_0.js +0 -1
  71. package/resources/fe-dist/assets/index-j9kTGUS5.css +0 -1
  72. package/resources/fe-dist/assets/stateDiagram-v2-BHNVJYJU-DTGkSC6X.js +0 -1
package/CHANGELOG.md CHANGED
@@ -1,25 +1,17 @@
1
- # 0.16.3
1
+ # 0.16.5
2
2
 
3
3
  _2026-07-04_
4
4
 
5
5
  ## English
6
6
 
7
- ### Features
7
+ ### Fixes
8
8
 
9
- - **Theme sync across devices**: Dark/light theme is now persisted server-side and broadcast to all connected web clients and devices in real time. Switching theme on one browser instantly updates every other open tab and remote SSH device. TUI coding agents (OpenCode, Codex, Claude Code) detect the correct theme on startup via tmux's native OSC 11 color query response.
10
-
11
- ### Bug Fixes
12
-
13
- - **Split-pane selection cleared by opposite pane output**: When one pane continuously outputs text (e.g. a running build), selecting text in another pane no longer gets immediately cleared. The terminal resize logic now skips unnecessary selection resets when pane dimensions haven't changed.
9
+ - Fixed the pane action buttons (menu and close) in the sidebar device list being vertically misaligned when the pane has agent sessions attached.
14
10
 
15
11
  ---
16
12
 
17
13
  ## 中文
18
14
 
19
- ### 新功能
20
-
21
- - **跨设备主题同步**:dark/light 主题现在持久化在服务端,并实时广播给所有已连接的网页客户端和设备。在一个浏览器上切换主题,其他所有标签页和远程 SSH 设备会立即同步。终端内的 Coding Agent(OpenCode、Codex、Claude Code)启动时能通过 tmux 原生 OSC 11 颜色查询正确探测当前主题。
22
-
23
- ### Bug 修复
15
+ ### 修复
24
16
 
25
- - **分屏对端输出时选中文字被清空**:当一个 pane 持续输出文本(如正在运行的构建),在另一个 pane 中选取文字不再被立即清空。终端 resize 逻辑现在在 pane 尺寸未变时跳过不必要的 selection 重置。
17
+ - 修复侧边栏设备列表中,pane 挂有 Agent 会话时其菜单/关闭按钮垂直错位的问题。
@@ -28376,6 +28376,7 @@ var config = {
28376
28376
  bellThrottleSecondsDefault: Number.parseInt(getEnv("TMEX_BELL_THROTTLE_SECONDS", "6"), 10),
28377
28377
  notificationThrottleSecondsDefault: Number.parseInt(getEnv("TMEX_NOTIFICATION_THROTTLE_SECONDS", "3"), 10),
28378
28378
  tmuxAllowPassthrough: getBooleanEnv("TMEX_TMUX_ALLOW_PASSTHROUGH", false),
28379
+ themeNotify2031Enabled: getBooleanEnv("TMEX_THEME_NOTIFY_2031", true),
28379
28380
  tmuxTermProgram: getEnv("TMEX_TMUX_TERM_PROGRAM", "ghostty"),
28380
28381
  tmuxWindowStyle: getEnv("TMEX_TMUX_WINDOW_STYLE", "fg=#d0d0d0,bg=#262626"),
28381
28382
  tmuxSocket: getEnv("TMEX_TMUX_SOCKET", ""),
@@ -98433,7 +98434,9 @@ var MAX_OSC_KIND_BYTES = 16;
98433
98434
  var MAX_OSC_PAYLOAD_BYTES = 8 * 1024;
98434
98435
  var MAX_DCS_PASSTHROUGH_BYTES = 64 * 1024;
98435
98436
  var MAX_KITTY_PENDING_IDS = 16;
98437
+ var MAX_CSI_BYTES = 64;
98436
98438
  var TMUX_PASSTHROUGH_PREFIX = "tmux;";
98439
+ var THEME_UPDATES_MODE = "2031";
98437
98440
  function createPaneStreamParser(options) {
98438
98441
  let phase = "normal";
98439
98442
  let oscKind = "";
@@ -98443,6 +98446,8 @@ function createPaneStreamParser(options) {
98443
98446
  let warnedDcsOverflow = false;
98444
98447
  let dcsPrefix = "";
98445
98448
  let dcsBytes = [];
98449
+ let csiBytes = [];
98450
+ let inTmuxPassthrough = false;
98446
98451
  const kittyPending = new Map;
98447
98452
  function resetOscState() {
98448
98453
  oscKind = "";
@@ -98592,8 +98597,19 @@ function createPaneStreamParser(options) {
98592
98597
  dcsBytes = [];
98593
98598
  dcsPrefix = "";
98594
98599
  phase = "normal";
98595
- for (const byte of content) {
98596
- processByte(byte);
98600
+ inTmuxPassthrough = true;
98601
+ try {
98602
+ for (const byte of content) {
98603
+ processByte(byte);
98604
+ }
98605
+ } finally {
98606
+ inTmuxPassthrough = false;
98607
+ }
98608
+ const phaseAfterFlush = phase;
98609
+ if (phaseAfterFlush === "csi") {
98610
+ output.push(27, 91, ...csiBytes);
98611
+ csiBytes = [];
98612
+ phase = "normal";
98597
98613
  }
98598
98614
  }
98599
98615
  function appendDcsByte(byte) {
@@ -98638,10 +98654,38 @@ function createPaneStreamParser(options) {
98638
98654
  phase = "dcs-detect";
98639
98655
  return;
98640
98656
  }
98657
+ if (byte === 91) {
98658
+ csiBytes = [];
98659
+ phase = "csi";
98660
+ return;
98661
+ }
98641
98662
  output.push(27, byte);
98642
98663
  phase = "normal";
98643
98664
  return;
98644
98665
  }
98666
+ if (phase === "csi") {
98667
+ if (byte >= 64 && byte <= 126) {
98668
+ output.push(27, 91, ...csiBytes, byte);
98669
+ if ((byte === 104 || byte === 108) && csiBytes[0] === 63 && !inTmuxPassthrough) {
98670
+ const params = decoder2.decode(new Uint8Array(csiBytes.slice(1))).split(";");
98671
+ if (params.includes(THEME_UPDATES_MODE)) {
98672
+ options.onThemeSubscription?.(byte === 104);
98673
+ }
98674
+ }
98675
+ csiBytes = [];
98676
+ phase = "normal";
98677
+ return;
98678
+ }
98679
+ if (byte >= 32 && byte <= 63 && csiBytes.length < MAX_CSI_BYTES) {
98680
+ csiBytes.push(byte);
98681
+ return;
98682
+ }
98683
+ output.push(27, 91, ...csiBytes);
98684
+ csiBytes = [];
98685
+ phase = "normal";
98686
+ processByte(byte);
98687
+ return;
98688
+ }
98645
98689
  if (phase === "dcs-detect") {
98646
98690
  const expected = TMUX_PASSTHROUGH_PREFIX.charCodeAt(dcsPrefix.length);
98647
98691
  if (byte === expected) {
@@ -98834,7 +98878,8 @@ function createControlModeSubscription(callbacks) {
98834
98878
  onBell: () => callbacks.onBell(paneId),
98835
98879
  onNotification: (notification) => callbacks.onNotification(paneId, notification),
98836
98880
  onPromptMarker: (marker24) => callbacks.onPromptMarker?.(paneId, marker24),
98837
- onClipboardWrite: (text3) => callbacks.onClipboardWrite?.(paneId, text3)
98881
+ onClipboardWrite: (text3) => callbacks.onClipboardWrite?.(paneId, text3),
98882
+ onThemeSubscription: (subscribed) => callbacks.onThemeSubscription?.(paneId, subscribed)
98838
98883
  });
98839
98884
  paneParsers.set(paneId, parser2);
98840
98885
  return parser2;
@@ -99175,6 +99220,44 @@ function isTargetMissingMessage(message) {
99175
99220
  return normalized.includes("can't find window") || normalized.includes("can't find pane") || normalized.includes("no such window") || normalized.includes("no such pane");
99176
99221
  }
99177
99222
 
99223
+ // ../../apps/gateway/src/tmux-client/theme-subscriptions.ts
99224
+ function createThemeSubscriptionTracker() {
99225
+ const subscribed = new Set;
99226
+ return {
99227
+ note(paneId, isSubscribed) {
99228
+ if (isSubscribed) {
99229
+ subscribed.add(paneId);
99230
+ } else {
99231
+ subscribed.delete(paneId);
99232
+ }
99233
+ },
99234
+ clear(paneId) {
99235
+ subscribed.delete(paneId);
99236
+ },
99237
+ prune(validPaneIds) {
99238
+ for (const paneId of subscribed) {
99239
+ if (!validPaneIds.has(paneId)) {
99240
+ subscribed.delete(paneId);
99241
+ }
99242
+ }
99243
+ },
99244
+ restore(paneIds) {
99245
+ for (const paneId of paneIds) {
99246
+ subscribed.add(paneId);
99247
+ }
99248
+ },
99249
+ has(paneId) {
99250
+ return subscribed.has(paneId);
99251
+ },
99252
+ list() {
99253
+ return [...subscribed];
99254
+ },
99255
+ reset() {
99256
+ subscribed.clear();
99257
+ }
99258
+ };
99259
+ }
99260
+
99178
99261
  // ../../apps/gateway/src/tmux-client/tmux-version.ts
99179
99262
  var MIN_CONTROL_MODE_VERSION = { major: 3, minor: 0 };
99180
99263
  function parseTmuxVersion(versionOutput) {
@@ -99312,6 +99395,8 @@ class LocalExternalTmuxConnection {
99312
99395
  heartbeatTimer = null;
99313
99396
  heartbeatPending = false;
99314
99397
  heartbeatTimeoutTimer = null;
99398
+ themeSubscriptions = createThemeSubscriptionTracker();
99399
+ themeSubscriptionsRestored = false;
99315
99400
  constructor(options, inputDeps = {}) {
99316
99401
  this.deviceId = options.deviceId;
99317
99402
  this.callbacks = options;
@@ -99566,21 +99651,68 @@ class LocalExternalTmuxConnection {
99566
99651
  ]);
99567
99652
  }
99568
99653
  }
99569
- setWindowStyle(style) {
99654
+ async setWindowStyle(style) {
99570
99655
  if (!this.connected) {
99571
99656
  return;
99572
99657
  }
99573
99658
  if (!resolveTmuxWindowStyle(config.tmuxWindowStyle)) {
99574
99659
  return;
99575
99660
  }
99576
- this.configureWindowStyle(style).catch((error51) => {
99661
+ await this.configureWindowStyle(style).catch((error51) => {
99577
99662
  this.callbacks.onError(error51);
99578
99663
  });
99579
99664
  }
99580
- signalThemeChange(_paneId, _theme) {
99581
- if (!this.connected) {
99665
+ signalThemeChange(paneId, theme) {
99666
+ if (!this.connected || !config.themeNotify2031Enabled) {
99667
+ return;
99668
+ }
99669
+ if (!this.themeSubscriptions.has(paneId)) {
99670
+ return;
99671
+ }
99672
+ this.sendInput(paneId, `\x1B[?997;${theme === "dark" ? "1" : "2"}n`);
99673
+ }
99674
+ noteThemeSubscription(paneId, subscribed) {
99675
+ this.themeSubscriptions.note(paneId, subscribed);
99676
+ this.runTmuxAllowFailure([
99677
+ "set-option",
99678
+ "-p",
99679
+ "-t",
99680
+ paneId,
99681
+ "@tmex_2031",
99682
+ subscribed ? "on" : "off"
99683
+ ]).catch(() => {});
99684
+ }
99685
+ clearThemeSubscription(paneId) {
99686
+ if (!this.themeSubscriptions.has(paneId)) {
99582
99687
  return;
99583
99688
  }
99689
+ this.themeSubscriptions.clear(paneId);
99690
+ this.runTmuxAllowFailure(["set-option", "-p", "-t", paneId, "@tmex_2031", "off"]).catch(() => {});
99691
+ }
99692
+ restoreThemeSubscriptionsOnce() {
99693
+ if (this.themeSubscriptionsRestored) {
99694
+ return;
99695
+ }
99696
+ this.themeSubscriptionsRestored = true;
99697
+ this.runTmuxAllowFailure([
99698
+ "list-panes",
99699
+ "-a",
99700
+ "-F",
99701
+ "#{pane_id}|#{@tmex_2031}"
99702
+ ]).then((result) => {
99703
+ if (!result || result.exitCode !== 0) {
99704
+ return;
99705
+ }
99706
+ const restored = [];
99707
+ for (const line of result.stdout.split(`
99708
+ `)) {
99709
+ const [paneId, flag] = line.trim().split("|");
99710
+ if (paneId && flag === "on") {
99711
+ restored.push(paneId);
99712
+ }
99713
+ }
99714
+ this.themeSubscriptions.restore(restored);
99715
+ }).catch(() => {});
99584
99716
  }
99585
99717
  async capturePaneText(paneId, opts) {
99586
99718
  if (!this.connected) {
@@ -99808,11 +99940,17 @@ class LocalExternalTmuxConnection {
99808
99940
  this.emitNotification(paneId, notification);
99809
99941
  },
99810
99942
  onPromptMarker: (paneId, marker24) => {
99943
+ if (marker24.kind === "A") {
99944
+ this.clearThemeSubscription(paneId);
99945
+ }
99811
99946
  this.callbacks.onPromptMarker?.(paneId, marker24);
99812
99947
  },
99813
99948
  onClipboardWrite: (paneId, text3) => {
99814
99949
  this.callbacks.onClipboardWrite?.(paneId, text3);
99815
99950
  },
99951
+ onThemeSubscription: (paneId, subscribed) => {
99952
+ this.noteThemeSubscription(paneId, subscribed);
99953
+ },
99816
99954
  onStructureChanged: () => {
99817
99955
  this.requestSnapshot();
99818
99956
  },
@@ -100162,7 +100300,10 @@ ${panesRes.stderr}`;
100162
100300
  this.parseSnapshotWindows(windowsRes.stdout.split(/\r?\n/));
100163
100301
  this.parseSnapshotPanes(panesRes.stdout.split(/\r?\n/));
100164
100302
  this.discardInvalidSnapshot();
100165
- this.controlSubscription?.prunePanes(new Set(this.getExpectedPaneIds()));
100303
+ const expectedPaneIds = new Set(this.getExpectedPaneIds());
100304
+ this.controlSubscription?.prunePanes(expectedPaneIds);
100305
+ this.themeSubscriptions.prune(expectedPaneIds);
100306
+ this.restoreThemeSubscriptionsOnce();
100166
100307
  this.markSpawnRecovered();
100167
100308
  this.emitSnapshot();
100168
100309
  }
@@ -100789,6 +100930,8 @@ class SshExternalTmuxConnection {
100789
100930
  heartbeatTimer = null;
100790
100931
  heartbeatPending = false;
100791
100932
  heartbeatTimeoutTimer = null;
100933
+ themeSubscriptions = createThemeSubscriptionTracker();
100934
+ themeSubscriptionsRestored = false;
100792
100935
  sshClient = null;
100793
100936
  commandStream = null;
100794
100937
  commandStdoutBuffer = "";
@@ -100850,10 +100993,57 @@ class SshExternalTmuxConnection {
100850
100993
  });
100851
100994
  }
100852
100995
  }
100853
- signalThemeChange(_paneId, _theme) {
100854
- if (!this.connected) {
100996
+ signalThemeChange(paneId, theme) {
100997
+ if (!this.connected || !config.themeNotify2031Enabled) {
100998
+ return;
100999
+ }
101000
+ if (!this.themeSubscriptions.has(paneId)) {
101001
+ return;
101002
+ }
101003
+ this.sendInput(paneId, `\x1B[?997;${theme === "dark" ? "1" : "2"}n`);
101004
+ }
101005
+ noteThemeSubscription(paneId, subscribed) {
101006
+ this.themeSubscriptions.note(paneId, subscribed);
101007
+ this.runTmuxAllowFailure([
101008
+ "set-option",
101009
+ "-p",
101010
+ "-t",
101011
+ paneId,
101012
+ "@tmex_2031",
101013
+ subscribed ? "on" : "off"
101014
+ ]).catch(() => {});
101015
+ }
101016
+ clearThemeSubscription(paneId) {
101017
+ if (!this.themeSubscriptions.has(paneId)) {
100855
101018
  return;
100856
101019
  }
101020
+ this.themeSubscriptions.clear(paneId);
101021
+ this.runTmuxAllowFailure(["set-option", "-p", "-t", paneId, "@tmex_2031", "off"]).catch(() => {});
101022
+ }
101023
+ restoreThemeSubscriptionsOnce() {
101024
+ if (this.themeSubscriptionsRestored) {
101025
+ return;
101026
+ }
101027
+ this.themeSubscriptionsRestored = true;
101028
+ this.runTmuxAllowFailure([
101029
+ "list-panes",
101030
+ "-a",
101031
+ "-F",
101032
+ "#{pane_id}|#{@tmex_2031}"
101033
+ ]).then((result) => {
101034
+ if (!result || result.exitCode !== 0) {
101035
+ return;
101036
+ }
101037
+ const restored = [];
101038
+ for (const line of result.stdout.split(`
101039
+ `)) {
101040
+ const [paneId, flag] = line.trim().split("|");
101041
+ if (paneId && flag === "on") {
101042
+ restored.push(paneId);
101043
+ }
101044
+ }
101045
+ this.themeSubscriptions.restore(restored);
101046
+ }).catch(() => {});
100857
101047
  }
100858
101048
  resizePane(paneId, cols, rows) {
100859
101049
  if (!this.connected) {
@@ -101033,14 +101223,14 @@ class SshExternalTmuxConnection {
101033
101223
  ]);
101034
101224
  }
101035
101225
  }
101036
- setWindowStyle(style) {
101226
+ async setWindowStyle(style) {
101037
101227
  if (!this.connected) {
101038
101228
  return;
101039
101229
  }
101040
101230
  if (!resolveTmuxWindowStyle(config.tmuxWindowStyle)) {
101041
101231
  return;
101042
101232
  }
101043
- this.configureWindowStyle(style).catch((error51) => {
101233
+ await this.configureWindowStyle(style).catch((error51) => {
101044
101234
  this.callbacks.onError(error51);
101045
101235
  });
101046
101236
  }
@@ -101377,11 +101567,17 @@ class SshExternalTmuxConnection {
101377
101567
  this.emitNotification(paneId, notification);
101378
101568
  },
101379
101569
  onPromptMarker: (paneId, marker24) => {
101570
+ if (marker24.kind === "A") {
101571
+ this.clearThemeSubscription(paneId);
101572
+ }
101380
101573
  this.callbacks.onPromptMarker?.(paneId, marker24);
101381
101574
  },
101382
101575
  onClipboardWrite: (paneId, text3) => {
101383
101576
  this.callbacks.onClipboardWrite?.(paneId, text3);
101384
101577
  },
101578
+ onThemeSubscription: (paneId, subscribed) => {
101579
+ this.noteThemeSubscription(paneId, subscribed);
101580
+ },
101385
101581
  onStructureChanged: () => {
101386
101582
  this.requestSnapshot();
101387
101583
  },
@@ -101689,7 +101885,10 @@ ${panesRes.stderr}`;
101689
101885
  this.parseSnapshotWindows(windowsRes.stdout.split(/\r?\n/));
101690
101886
  this.parseSnapshotPanes(panesRes.stdout.split(/\r?\n/));
101691
101887
  this.discardInvalidSnapshot();
101692
- this.controlSubscription?.prunePanes(new Set(this.getExpectedPaneIds()));
101888
+ const expectedPaneIds = new Set(this.getExpectedPaneIds());
101889
+ this.controlSubscription?.prunePanes(expectedPaneIds);
101890
+ this.themeSubscriptions.prune(expectedPaneIds);
101891
+ this.restoreThemeSubscriptionsOnce();
101693
101892
  this.emitSnapshot();
101694
101893
  }
101695
101894
  parseSnapshotSession(lines) {
@@ -102189,7 +102388,7 @@ class DeviceSessionRuntime {
102189
102388
  this.connection.renameWindow(windowId, name24);
102190
102389
  }
102191
102390
  setWindowStyle(style) {
102192
- this.connection.setWindowStyle(style);
102391
+ return this.connection.setWindowStyle(style);
102193
102392
  }
102194
102393
  signalThemeChange(paneId, theme) {
102195
102394
  this.connection.signalThemeChange(paneId, theme);
@@ -107724,8 +107923,8 @@ function getBaseVersion() {
107724
107923
  if (cachedBase !== undefined)
107725
107924
  return cachedBase;
107726
107925
  let base = null;
107727
- if ("0.16.3") {
107728
- base = "0.16.3";
107926
+ if ("0.16.5") {
107927
+ base = "0.16.5";
107729
107928
  }
107730
107929
  if (!base && config.isProd) {
107731
107930
  base = readInstallMeta()?.cliVersion ?? null;
@@ -110934,6 +111133,14 @@ var defaultDeps5 = {
110934
111133
  await tmuxRuntimeRegistry.release(deviceId, runtime);
110935
111134
  }
110936
111135
  };
111136
+ function parseWindowLayoutSize(layout) {
111137
+ if (!layout)
111138
+ return null;
111139
+ const match = /^[0-9a-fA-F]{4},(\d+)x(\d+)/.exec(layout);
111140
+ if (!match)
111141
+ return null;
111142
+ return { cols: Number(match[1]), rows: Number(match[2]) };
111143
+ }
110937
111144
 
110938
111145
  class WebSocketServer {
110939
111146
  connections = new Map;
@@ -110944,8 +111151,9 @@ class WebSocketServer {
110944
111151
  currentTheme = null;
110945
111152
  themeSignalLast = new Map;
110946
111153
  lastThemeTimestamp = 0n;
111154
+ pendingTmuxTheme = null;
111155
+ themeApplyInFlight = false;
110947
111156
  lastBroadcastTheme = new Map;
110948
- lastBroadcastSize = new Map;
110949
111157
  deps;
110950
111158
  constructor(options = {}) {
110951
111159
  this.deps = {
@@ -110979,7 +111187,6 @@ class WebSocketServer {
110979
111187
  entry.detachRuntime = null;
110980
111188
  this.themeSignalLast.delete(deviceId);
110981
111189
  this.lastBroadcastTheme.delete(deviceId);
110982
- this.lastBroadcastSize.delete(deviceId);
110983
111190
  this.deps.releaseRuntime(deviceId, entry.runtime);
110984
111191
  }
110985
111192
  attachRuntime(deviceId, runtime) {
@@ -111515,18 +111722,18 @@ class WebSocketServer {
111515
111722
  const entry = this.connections.get(deviceId);
111516
111723
  if (!entry)
111517
111724
  return;
111518
- const last = this.lastBroadcastSize.get(deviceId);
111519
- if (last && last.cols === cols && last.rows === rows) {
111520
- return;
111521
- }
111522
- this.lastBroadcastSize.set(deviceId, { cols, rows });
111523
- const snapshot = entry.lastSnapshot;
111524
- if (snapshot?.session?.windows) {
111525
- const window2 = snapshot.session.windows.find((w) => w.panes && w.panes.some((p) => p.id === paneId));
111526
- if (window2 && window2.panes && window2.panes.length > 1) {
111527
- entry.runtime.resizeWindow(window2.id, cols, rows);
111725
+ const window2 = entry.lastSnapshot?.session?.windows?.find((w) => w.panes?.some((p) => p.id === paneId));
111726
+ if (window2?.panes && window2.panes.length > 1) {
111727
+ const currentSize = parseWindowLayoutSize(window2.layout);
111728
+ if (currentSize && currentSize.cols === cols && currentSize.rows === rows) {
111528
111729
  return;
111529
111730
  }
111731
+ entry.runtime.resizeWindow(window2.id, cols, rows);
111732
+ return;
111733
+ }
111734
+ const pane = window2?.panes.find((p) => p.id === paneId);
111735
+ if (pane && pane.width === cols && pane.height === rows) {
111736
+ return;
111530
111737
  }
111531
111738
  entry.runtime.resizePane(paneId, cols, rows);
111532
111739
  }
@@ -111617,14 +111824,20 @@ class WebSocketServer {
111617
111824
  const entry = this.connections.get(deviceId);
111618
111825
  if (!entry)
111619
111826
  return;
111620
- entry.runtime.setWindowStyle(style);
111621
- if (this.currentTheme !== null) {
111622
- const theme = this.currentTheme;
111623
- if (this.lastBroadcastTheme.get(deviceId) !== theme) {
111624
- this.lastBroadcastTheme.set(deviceId, theme);
111625
- this.broadcastThemeChange(theme);
111827
+ (async () => {
111828
+ try {
111829
+ await entry.runtime.setWindowStyle(style);
111830
+ } catch (err) {
111831
+ console.error("[ws] setWindowStyle failed:", err);
111626
111832
  }
111627
- }
111833
+ if (this.currentTheme !== null) {
111834
+ const theme = this.currentTheme;
111835
+ if (this.lastBroadcastTheme.get(deviceId) !== theme) {
111836
+ this.lastBroadcastTheme.set(deviceId, theme);
111837
+ this.broadcastThemeChange(theme);
111838
+ }
111839
+ }
111840
+ })();
111628
111841
  }
111629
111842
  handleSiteThemeUpdate(ws, decoded) {
111630
111843
  if (decoded.theme !== exports_ws_borsh.SITE_THEME_DARK && decoded.theme !== exports_ws_borsh.SITE_THEME_LIGHT) {
@@ -111633,10 +111846,28 @@ class WebSocketServer {
111633
111846
  }
111634
111847
  const themeName = decoded.theme === exports_ws_borsh.SITE_THEME_LIGHT ? "light" : "dark";
111635
111848
  updateSiteSettings({ theme: themeName });
111636
- this.handleSiteThemeChange(themeName);
111637
- this.broadcastThemeChange(themeName);
111849
+ this.scheduleTmuxThemeApply(themeName);
111638
111850
  this.broadcastSiteThemeUpdateS2C(themeName);
111639
111851
  }
111852
+ scheduleTmuxThemeApply(theme) {
111853
+ this.pendingTmuxTheme = theme;
111854
+ if (this.themeApplyInFlight) {
111855
+ return;
111856
+ }
111857
+ this.themeApplyInFlight = true;
111858
+ (async () => {
111859
+ try {
111860
+ while (this.pendingTmuxTheme !== null) {
111861
+ const next = this.pendingTmuxTheme;
111862
+ this.pendingTmuxTheme = null;
111863
+ await this.handleSiteThemeChange(next);
111864
+ this.broadcastThemeChange(next);
111865
+ }
111866
+ } finally {
111867
+ this.themeApplyInFlight = false;
111868
+ }
111869
+ })();
111870
+ }
111640
111871
  broadcastSiteThemeUpdateS2C(theme) {
111641
111872
  const now2 = BigInt(Date.now());
111642
111873
  if (now2 <= this.lastThemeTimestamp) {
@@ -111654,17 +111885,16 @@ class WebSocketServer {
111654
111885
  this.sendEnvelope(client, exports_ws_borsh.KIND_SITE_THEME_UPDATE, payloadBytes);
111655
111886
  }
111656
111887
  }
111657
- handleSiteThemeChange(theme) {
111888
+ async handleSiteThemeChange(theme) {
111658
111889
  if (theme !== "dark" && theme !== "light") {
111659
111890
  return;
111660
111891
  }
111661
111892
  this.currentTheme = theme;
111662
111893
  const style = getTmuxWindowStyle(theme);
111663
- for (const [, entry] of this.connections) {
111664
- try {
111665
- entry.runtime.setWindowStyle(style);
111666
- } catch (err) {
111667
- console.error("[ws] setWindowStyle on theme change failed:", err);
111894
+ const results = await Promise.allSettled([...this.connections.values()].map((entry) => entry.runtime.setWindowStyle(style)));
111895
+ for (const result of results) {
111896
+ if (result.status === "rejected") {
111897
+ console.error("[ws] setWindowStyle on theme change failed:", result.reason);
111668
111898
  }
111669
111899
  }
111670
111900
  }
@@ -111677,11 +111907,9 @@ class WebSocketServer {
111677
111907
  return;
111678
111908
  }
111679
111909
  const style = getTmuxWindowStyle(this.currentTheme);
111680
- try {
111681
- entry.runtime.setWindowStyle(style);
111682
- } catch (err) {
111910
+ entry.runtime.setWindowStyle(style).catch((err) => {
111683
111911
  console.error(`[ws] setWindowStyle on device ${deviceId} failed:`, err);
111684
- }
111912
+ });
111685
111913
  }
111686
111914
  broadcastThemeChange(theme) {
111687
111915
  const now2 = Date.now();
@@ -111845,7 +112073,7 @@ class WebSocketServer {
111845
112073
  detachRuntime = this.attachRuntime(deviceId, runtime);
111846
112074
  await runtime.connect();
111847
112075
  if (this.currentTheme !== null) {
111848
- runtime.setWindowStyle(getTmuxWindowStyle(this.currentTheme));
112076
+ await runtime.setWindowStyle(getTmuxWindowStyle(this.currentTheme));
111849
112077
  }
111850
112078
  return {
111851
112079
  runtime,
@@ -112155,8 +112383,7 @@ async function createGatewayRuntime(options = {}) {
112155
112383
  });
112156
112384
  registerSnapshotLookup((deviceId) => wsServer.getLastSnapshot(deviceId));
112157
112385
  registerThemeBroadcaster((theme) => {
112158
- wsServer.handleSiteThemeChange(theme);
112159
- wsServer.broadcastThemeChange(theme);
112386
+ wsServer.scheduleTmuxThemeApply(theme);
112160
112387
  }, (theme) => {
112161
112388
  wsServer.broadcastSiteThemeUpdateS2C(theme);
112162
112389
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tmex-cli",
3
- "version": "0.16.3",
3
+ "version": "0.16.5",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "tmex": "./bin/tmex.js",
@@ -1,4 +1,4 @@
1
- import{Y as qe,u as Ie,j as i,aD as jt,aE as Ct,aF as Nt,aG as Pt,aH as kt,at as It,r as n,d as H,af as ge,ai as ut,ah as dt,ag as Mt,aj as Lt,aI as At,aJ as _t,o as Le,aK as zt,D as Dt,p as $t,q as Bt,s as Ft,ay as ft,aL as Ze,aM as et,aw as ht,b as mt,aN as tt,aO as Ht,c as nt,aC as pt,aP as Pe,aQ as Wt,aq as Ue,y as Ot,a2 as qt,B as Ne,T as Xt,aR as Yt,aS as Ut,aT as Vt,aU as Kt,aV as Gt,$ as Qt,aW as Jt,A as Zt,f as en,g as tn,i as nn,k as rn,l as sn,m as on,n as cn}from"./index-Dc3Fwpho.js";import{T as an,C as ln,c as un,F as dn,a as wt,R as fn,A as hn,f as mn,t as pn,S as wn}from"./terminal-settings-panel-tZuaqdqz.js";import{e as gn}from"./selection-clipboard-D3gUQQ7L.js";import{C as xn}from"./copy-DwXKDgGT.js";import{S as ct,K as vn}from"./send-J45szXFM.js";const bn=/^[0-9a-fA-F]{4}$/;function Sn(t){if(typeof t!="string"||t.length<6)return null;const e=t.slice(0,4);if(!bn.test(e)||t[4]!==",")return null;const s={input:t,pos:5},c=gt(s);return!c||s.pos!==t.length?null:{checksum:e,root:c}}function Be(t){const e=t.pos;for(;t.pos<t.input.length;){const s=t.input.charCodeAt(t.pos);if(s<48||s>57)break;t.pos+=1}return t.pos===e?null:Number.parseInt(t.input.slice(e,t.pos),10)}function Fe(t,e){return t.input[t.pos]!==e?!1:(t.pos+=1,!0)}function gt(t){const e=Be(t);if(e===null||!Fe(t,"x"))return null;const s=Be(t);if(s===null||!Fe(t,","))return null;const c=Be(t);if(c===null||!Fe(t,","))return null;const u=Be(t);if(u===null)return null;const w=t.input[t.pos];if(w===","){t.pos+=1;const g=Be(t);return g===null?null:{type:"leaf",paneNumId:g,width:e,height:s,x:c,y:u}}if(w==="{"||w==="["){const g=w==="{"?"}":"]";t.pos+=1;const x=[];for(;;){const I=gt(t);if(!I)return null;if(x.push(I),!Fe(t,",")){if(Fe(t,g))break;return null}}return x.length<2?null:{type:w==="{"?"row":"column",width:e,height:s,x:c,y:u,children:x}}return null}function Ve(t){return`%${t.paneNumId}`}/**
1
+ import{Y as qe,u as Ie,j as i,aD as jt,aE as Ct,aF as Nt,aG as Pt,aH as kt,at as It,r as n,d as H,af as ge,ai as ut,ah as dt,ag as Mt,aj as Lt,aI as At,aJ as _t,o as Le,aK as zt,D as Dt,p as $t,q as Bt,s as Ft,ay as ft,aL as Ze,aM as et,aw as ht,b as mt,aN as tt,aO as Ht,c as nt,aC as pt,aP as Pe,aQ as Wt,aq as Ue,y as Ot,a2 as qt,B as Ne,T as Xt,aR as Yt,aS as Ut,aT as Vt,aU as Kt,aV as Gt,$ as Qt,aW as Jt,A as Zt,f as en,g as tn,i as nn,k as rn,l as sn,m as on,n as cn}from"./index-U4iZ6Kxr.js";import{T as an,C as ln,c as un,F as dn,a as wt,R as fn,A as hn,f as mn,t as pn,S as wn}from"./terminal-settings-panel-DU9rOuZd.js";import{e as gn}from"./selection-clipboard-D3gUQQ7L.js";import{C as xn}from"./copy-CJIriuQt.js";import{S as ct,K as vn}from"./send-e_FTy5nC.js";const bn=/^[0-9a-fA-F]{4}$/;function Sn(t){if(typeof t!="string"||t.length<6)return null;const e=t.slice(0,4);if(!bn.test(e)||t[4]!==",")return null;const s={input:t,pos:5},c=gt(s);return!c||s.pos!==t.length?null:{checksum:e,root:c}}function Be(t){const e=t.pos;for(;t.pos<t.input.length;){const s=t.input.charCodeAt(t.pos);if(s<48||s>57)break;t.pos+=1}return t.pos===e?null:Number.parseInt(t.input.slice(e,t.pos),10)}function Fe(t,e){return t.input[t.pos]!==e?!1:(t.pos+=1,!0)}function gt(t){const e=Be(t);if(e===null||!Fe(t,"x"))return null;const s=Be(t);if(s===null||!Fe(t,","))return null;const c=Be(t);if(c===null||!Fe(t,","))return null;const u=Be(t);if(u===null)return null;const w=t.input[t.pos];if(w===","){t.pos+=1;const g=Be(t);return g===null?null:{type:"leaf",paneNumId:g,width:e,height:s,x:c,y:u}}if(w==="{"||w==="["){const g=w==="{"?"}":"]";t.pos+=1;const x=[];for(;;){const I=gt(t);if(!I)return null;if(x.push(I),!Fe(t,",")){if(Fe(t,g))break;return null}}return x.length<2?null:{type:w==="{"?"row":"column",width:e,height:s,x:c,y:u,children:x}}return null}function Ve(t){return`%${t.paneNumId}`}/**
2
2
  * @license lucide-react v0.564.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.