dmux 3.1.1 → 3.3.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 (635) hide show
  1. package/dist/DmuxApp.d.ts +2 -2
  2. package/dist/DmuxApp.d.ts.map +1 -1
  3. package/dist/DmuxApp.js +405 -1134
  4. package/dist/DmuxApp.js.map +1 -1
  5. package/dist/actions/implementations/closeAction.d.ts +10 -0
  6. package/dist/actions/implementations/closeAction.d.ts.map +1 -0
  7. package/dist/actions/implementations/closeAction.js +197 -0
  8. package/dist/actions/implementations/closeAction.js.map +1 -0
  9. package/dist/actions/implementations/copyPathAction.d.ts +10 -0
  10. package/dist/actions/implementations/copyPathAction.d.ts.map +1 -0
  11. package/dist/actions/implementations/copyPathAction.js +34 -0
  12. package/dist/actions/implementations/copyPathAction.js.map +1 -0
  13. package/dist/actions/implementations/duplicateAction.d.ts +10 -0
  14. package/dist/actions/implementations/duplicateAction.d.ts.map +1 -0
  15. package/dist/actions/implementations/duplicateAction.js +25 -0
  16. package/dist/actions/implementations/duplicateAction.js.map +1 -0
  17. package/dist/actions/implementations/index.d.ts +14 -0
  18. package/dist/actions/implementations/index.d.ts.map +1 -0
  19. package/dist/actions/implementations/index.js +14 -0
  20. package/dist/actions/implementations/index.js.map +1 -0
  21. package/dist/actions/implementations/mergeAction.d.ts +14 -0
  22. package/dist/actions/implementations/mergeAction.d.ts.map +1 -0
  23. package/dist/actions/implementations/mergeAction.js +84 -0
  24. package/dist/actions/implementations/mergeAction.js.map +1 -0
  25. package/dist/actions/implementations/openInEditorAction.d.ts +12 -0
  26. package/dist/actions/implementations/openInEditorAction.d.ts.map +1 -0
  27. package/dist/actions/implementations/openInEditorAction.js +33 -0
  28. package/dist/actions/implementations/openInEditorAction.js.map +1 -0
  29. package/dist/actions/implementations/renameAction.d.ts +13 -0
  30. package/dist/actions/implementations/renameAction.d.ts.map +1 -0
  31. package/dist/actions/implementations/renameAction.js +18 -0
  32. package/dist/actions/implementations/renameAction.js.map +1 -0
  33. package/dist/actions/implementations/toggleAutopilotAction.d.ts +10 -0
  34. package/dist/actions/implementations/toggleAutopilotAction.d.ts.map +1 -0
  35. package/dist/actions/implementations/toggleAutopilotAction.js +33 -0
  36. package/dist/actions/implementations/toggleAutopilotAction.js.map +1 -0
  37. package/dist/actions/implementations/viewAction.d.ts +10 -0
  38. package/dist/actions/implementations/viewAction.d.ts.map +1 -0
  39. package/dist/actions/implementations/viewAction.js +26 -0
  40. package/dist/actions/implementations/viewAction.js.map +1 -0
  41. package/dist/actions/merge/commitMessageHandler.d.ts +27 -0
  42. package/dist/actions/merge/commitMessageHandler.d.ts.map +1 -0
  43. package/dist/actions/merge/commitMessageHandler.js +131 -0
  44. package/dist/actions/merge/commitMessageHandler.js.map +1 -0
  45. package/dist/actions/merge/conflictResolution.d.ts +13 -0
  46. package/dist/actions/merge/conflictResolution.d.ts.map +1 -0
  47. package/dist/actions/merge/conflictResolution.js +134 -0
  48. package/dist/actions/merge/conflictResolution.js.map +1 -0
  49. package/dist/actions/merge/issueHandlers/index.d.ts +11 -0
  50. package/dist/actions/merge/issueHandlers/index.d.ts.map +1 -0
  51. package/dist/actions/merge/issueHandlers/index.js +8 -0
  52. package/dist/actions/merge/issueHandlers/index.js.map +1 -0
  53. package/dist/actions/merge/issueHandlers/mainDirtyHandler.d.ts +13 -0
  54. package/dist/actions/merge/issueHandlers/mainDirtyHandler.d.ts.map +1 -0
  55. package/dist/actions/merge/issueHandlers/mainDirtyHandler.js +72 -0
  56. package/dist/actions/merge/issueHandlers/mainDirtyHandler.js.map +1 -0
  57. package/dist/actions/merge/issueHandlers/mergeConflictHandler.d.ts +13 -0
  58. package/dist/actions/merge/issueHandlers/mergeConflictHandler.d.ts.map +1 -0
  59. package/dist/actions/merge/issueHandlers/mergeConflictHandler.js +52 -0
  60. package/dist/actions/merge/issueHandlers/mergeConflictHandler.js.map +1 -0
  61. package/dist/actions/merge/issueHandlers/nothingToMergeHandler.d.ts +7 -0
  62. package/dist/actions/merge/issueHandlers/nothingToMergeHandler.d.ts.map +1 -0
  63. package/dist/actions/merge/issueHandlers/nothingToMergeHandler.js +12 -0
  64. package/dist/actions/merge/issueHandlers/nothingToMergeHandler.js.map +1 -0
  65. package/dist/actions/merge/issueHandlers/worktreeUncommittedHandler.d.ts +13 -0
  66. package/dist/actions/merge/issueHandlers/worktreeUncommittedHandler.d.ts.map +1 -0
  67. package/dist/actions/merge/issueHandlers/worktreeUncommittedHandler.js +49 -0
  68. package/dist/actions/merge/issueHandlers/worktreeUncommittedHandler.js.map +1 -0
  69. package/dist/actions/merge/mergeExecution.d.ts +22 -0
  70. package/dist/actions/merge/mergeExecution.d.ts.map +1 -0
  71. package/dist/actions/merge/mergeExecution.js +180 -0
  72. package/dist/actions/merge/mergeExecution.js.map +1 -0
  73. package/dist/actions/paneActions.d.ts +3 -40
  74. package/dist/actions/paneActions.d.ts.map +1 -1
  75. package/dist/actions/paneActions.js +4 -981
  76. package/dist/actions/paneActions.js.map +1 -1
  77. package/dist/actions/types.d.ts +2 -0
  78. package/dist/actions/types.d.ts.map +1 -1
  79. package/dist/actions/types.js +1 -0
  80. package/dist/actions/types.js.map +1 -1
  81. package/dist/components/{ActionChoiceDialog.d.ts → dialogs/ActionChoiceDialog.d.ts} +1 -1
  82. package/dist/components/dialogs/ActionChoiceDialog.d.ts.map +1 -0
  83. package/dist/components/dialogs/ActionChoiceDialog.js.map +1 -0
  84. package/dist/components/dialogs/ActionConfirmDialog.d.ts.map +1 -0
  85. package/dist/components/dialogs/ActionConfirmDialog.js.map +1 -0
  86. package/dist/components/dialogs/ActionInputDialog.d.ts.map +1 -0
  87. package/dist/components/{ActionInputDialog.js → dialogs/ActionInputDialog.js} +4 -3
  88. package/dist/components/dialogs/ActionInputDialog.js.map +1 -0
  89. package/dist/components/dialogs/ActionProgressDialog.d.ts.map +1 -0
  90. package/dist/components/dialogs/ActionProgressDialog.js.map +1 -0
  91. package/dist/components/dialogs/AgentChoiceDialog.d.ts.map +1 -0
  92. package/dist/components/dialogs/AgentChoiceDialog.js.map +1 -0
  93. package/dist/components/{CloseOptionsDialog.d.ts → dialogs/CloseOptionsDialog.d.ts} +1 -1
  94. package/dist/components/dialogs/CloseOptionsDialog.d.ts.map +1 -0
  95. package/dist/components/dialogs/CloseOptionsDialog.js.map +1 -0
  96. package/dist/components/dialogs/CommandPromptDialog.d.ts.map +1 -0
  97. package/dist/components/{CommandPromptDialog.js → dialogs/CommandPromptDialog.js} +1 -1
  98. package/dist/components/dialogs/CommandPromptDialog.js.map +1 -0
  99. package/dist/components/dialogs/DialogBox.d.ts +16 -0
  100. package/dist/components/dialogs/DialogBox.d.ts.map +1 -0
  101. package/dist/components/dialogs/DialogBox.js +12 -0
  102. package/dist/components/dialogs/DialogBox.js.map +1 -0
  103. package/dist/components/dialogs/HooksDialog.d.ts.map +1 -0
  104. package/dist/components/dialogs/HooksDialog.js.map +1 -0
  105. package/dist/components/{MergeConfirmationDialog.d.ts → dialogs/MergeConfirmationDialog.d.ts} +1 -1
  106. package/dist/components/dialogs/MergeConfirmationDialog.d.ts.map +1 -0
  107. package/dist/components/dialogs/MergeConfirmationDialog.js.map +1 -0
  108. package/dist/components/{SettingsDialog.d.ts → dialogs/SettingsDialog.d.ts} +1 -1
  109. package/dist/components/dialogs/SettingsDialog.d.ts.map +1 -0
  110. package/dist/components/dialogs/SettingsDialog.js.map +1 -0
  111. package/dist/components/dialogs/UpdateDialog.d.ts.map +1 -0
  112. package/dist/components/dialogs/UpdateDialog.js.map +1 -0
  113. package/dist/components/indicators/CreatingIndicator.d.ts.map +1 -0
  114. package/dist/components/indicators/CreatingIndicator.js.map +1 -0
  115. package/dist/components/indicators/LoadingIndicator.d.ts.map +1 -0
  116. package/dist/components/indicators/LoadingIndicator.js.map +1 -0
  117. package/dist/components/indicators/RunningIndicator.d.ts.map +1 -0
  118. package/dist/components/indicators/RunningIndicator.js.map +1 -0
  119. package/dist/components/indicators/Spinner.d.ts.map +1 -0
  120. package/dist/components/indicators/Spinner.js.map +1 -0
  121. package/dist/components/indicators/UpdatingIndicator.d.ts.map +1 -0
  122. package/dist/components/indicators/UpdatingIndicator.js.map +1 -0
  123. package/dist/components/indicators/index.d.ts +6 -0
  124. package/dist/components/indicators/index.d.ts.map +1 -0
  125. package/dist/components/indicators/index.js +6 -0
  126. package/dist/components/indicators/index.js.map +1 -0
  127. package/dist/components/inputs/CleanTextInput.d.ts +19 -0
  128. package/dist/components/inputs/CleanTextInput.d.ts.map +1 -0
  129. package/dist/{CleanTextInput.js → components/inputs/CleanTextInput.js} +98 -160
  130. package/dist/components/inputs/CleanTextInput.js.map +1 -0
  131. package/dist/components/inputs/StyledTextInput.d.ts.map +1 -0
  132. package/dist/components/inputs/StyledTextInput.js.map +1 -0
  133. package/dist/components/inputs/index.d.ts +3 -0
  134. package/dist/components/inputs/index.d.ts.map +1 -0
  135. package/dist/components/inputs/index.js +3 -0
  136. package/dist/components/inputs/index.js.map +1 -0
  137. package/dist/components/{KebabMenu.d.ts → panes/KebabMenu.d.ts} +1 -1
  138. package/dist/components/panes/KebabMenu.d.ts.map +1 -0
  139. package/dist/components/panes/KebabMenu.js.map +1 -0
  140. package/dist/components/panes/MergePane.d.ts.map +1 -0
  141. package/dist/{MergePane.js → components/panes/MergePane.js} +1 -1
  142. package/dist/components/panes/MergePane.js.map +1 -0
  143. package/dist/components/{PaneCard.d.ts → panes/PaneCard.d.ts} +4 -2
  144. package/dist/components/panes/PaneCard.d.ts.map +1 -0
  145. package/dist/components/panes/PaneCard.js +55 -0
  146. package/dist/components/panes/PaneCard.js.map +1 -0
  147. package/dist/components/panes/PanesGrid.d.ts +12 -0
  148. package/dist/components/panes/PanesGrid.d.ts.map +1 -0
  149. package/dist/components/panes/PanesGrid.js +49 -0
  150. package/dist/components/panes/PanesGrid.js.map +1 -0
  151. package/dist/components/panes/index.d.ts +5 -0
  152. package/dist/components/panes/index.d.ts.map +1 -0
  153. package/dist/components/panes/index.js +5 -0
  154. package/dist/components/panes/index.js.map +1 -0
  155. package/dist/components/popups/agentChoicePopup.d.ts +7 -0
  156. package/dist/components/popups/agentChoicePopup.d.ts.map +1 -0
  157. package/dist/components/popups/agentChoicePopup.js +74 -0
  158. package/dist/components/popups/agentChoicePopup.js.map +1 -0
  159. package/dist/components/popups/choicePopup.d.ts +7 -0
  160. package/dist/components/popups/choicePopup.d.ts.map +1 -0
  161. package/dist/components/popups/choicePopup.js +64 -0
  162. package/dist/components/popups/choicePopup.js.map +1 -0
  163. package/dist/components/popups/config.d.ts +40 -0
  164. package/dist/components/popups/config.d.ts.map +1 -0
  165. package/dist/components/popups/config.js +40 -0
  166. package/dist/components/popups/config.js.map +1 -0
  167. package/dist/components/popups/confirmPopup.d.ts +7 -0
  168. package/dist/components/popups/confirmPopup.d.ts.map +1 -0
  169. package/dist/components/popups/confirmPopup.js +71 -0
  170. package/dist/components/popups/confirmPopup.js.map +1 -0
  171. package/dist/components/popups/hooksPopup.d.ts +7 -0
  172. package/dist/components/popups/hooksPopup.d.ts.map +1 -0
  173. package/dist/components/popups/hooksPopup.js +71 -0
  174. package/dist/components/popups/hooksPopup.js.map +1 -0
  175. package/dist/components/popups/inputPopup.d.ts +7 -0
  176. package/dist/components/popups/inputPopup.d.ts.map +1 -0
  177. package/dist/components/popups/inputPopup.js +47 -0
  178. package/dist/components/popups/inputPopup.js.map +1 -0
  179. package/dist/components/popups/kebabMenuPopup.d.ts +7 -0
  180. package/dist/components/popups/kebabMenuPopup.d.ts.map +1 -0
  181. package/dist/components/popups/kebabMenuPopup.js +52 -0
  182. package/dist/components/popups/kebabMenuPopup.js.map +1 -0
  183. package/dist/components/popups/logsPopup.d.ts +12 -0
  184. package/dist/components/popups/logsPopup.d.ts.map +1 -0
  185. package/dist/components/popups/logsPopup.js +372 -0
  186. package/dist/components/popups/logsPopup.js.map +1 -0
  187. package/dist/components/popups/mergePopup.d.ts +7 -0
  188. package/dist/components/popups/mergePopup.d.ts.map +1 -0
  189. package/dist/components/popups/mergePopup.js +310 -0
  190. package/dist/components/popups/mergePopup.js.map +1 -0
  191. package/dist/components/popups/newPanePopup.d.ts +7 -0
  192. package/dist/components/popups/newPanePopup.d.ts.map +1 -0
  193. package/dist/components/popups/newPanePopup.js +234 -0
  194. package/dist/components/popups/newPanePopup.js.map +1 -0
  195. package/dist/components/popups/progressPopup.d.ts +8 -0
  196. package/dist/components/popups/progressPopup.d.ts.map +1 -0
  197. package/dist/components/popups/progressPopup.js +53 -0
  198. package/dist/components/popups/progressPopup.js.map +1 -0
  199. package/dist/components/popups/remotePopup.d.ts +6 -0
  200. package/dist/components/popups/remotePopup.d.ts.map +1 -0
  201. package/dist/components/popups/remotePopup.js +166 -0
  202. package/dist/components/popups/remotePopup.js.map +1 -0
  203. package/dist/components/popups/settingsPopup.d.ts +7 -0
  204. package/dist/components/popups/settingsPopup.d.ts.map +1 -0
  205. package/dist/components/popups/settingsPopup.js +212 -0
  206. package/dist/components/popups/settingsPopup.js.map +1 -0
  207. package/dist/components/popups/shared/FileList.d.ts +13 -0
  208. package/dist/components/popups/shared/FileList.d.ts.map +1 -0
  209. package/dist/components/popups/shared/FileList.js +61 -0
  210. package/dist/components/popups/shared/FileList.js.map +1 -0
  211. package/dist/components/popups/shared/PopupContainer.d.ts +14 -0
  212. package/dist/components/popups/shared/PopupContainer.d.ts.map +1 -0
  213. package/dist/components/popups/shared/PopupContainer.js +15 -0
  214. package/dist/components/popups/shared/PopupContainer.js.map +1 -0
  215. package/dist/components/popups/shared/PopupInputBox.d.ts +11 -0
  216. package/dist/components/popups/shared/PopupInputBox.d.ts.map +1 -0
  217. package/dist/components/popups/shared/PopupInputBox.js +10 -0
  218. package/dist/components/popups/shared/PopupInputBox.js.map +1 -0
  219. package/dist/components/popups/shared/PopupWrapper.d.ts +37 -0
  220. package/dist/components/popups/shared/PopupWrapper.d.ts.map +1 -0
  221. package/dist/components/popups/shared/PopupWrapper.js +88 -0
  222. package/dist/components/popups/shared/PopupWrapper.js.map +1 -0
  223. package/dist/components/popups/shared/index.d.ts +8 -0
  224. package/dist/components/popups/shared/index.d.ts.map +1 -0
  225. package/dist/components/popups/shared/index.js +8 -0
  226. package/dist/components/popups/shared/index.js.map +1 -0
  227. package/dist/components/popups/shortcutsPopup.d.ts +6 -0
  228. package/dist/components/popups/shortcutsPopup.d.ts.map +1 -0
  229. package/dist/components/popups/shortcutsPopup.js +74 -0
  230. package/dist/components/popups/shortcutsPopup.js.map +1 -0
  231. package/dist/components/popups/templates/SimpleInputPopup.d.ts +15 -0
  232. package/dist/components/popups/templates/SimpleInputPopup.d.ts.map +1 -0
  233. package/dist/components/popups/templates/SimpleInputPopup.js +28 -0
  234. package/dist/components/popups/templates/SimpleInputPopup.js.map +1 -0
  235. package/dist/components/ui/FileCopyPrompt.d.ts.map +1 -0
  236. package/dist/components/ui/FileCopyPrompt.js.map +1 -0
  237. package/dist/components/ui/FooterHelp.d.ts +19 -0
  238. package/dist/components/ui/FooterHelp.d.ts.map +1 -0
  239. package/dist/components/ui/FooterHelp.js +55 -0
  240. package/dist/components/ui/FooterHelp.js.map +1 -0
  241. package/dist/components/ui/QRCode.d.ts.map +1 -0
  242. package/dist/components/ui/QRCode.js.map +1 -0
  243. package/dist/components/ui/index.d.ts +4 -0
  244. package/dist/components/ui/index.d.ts.map +1 -0
  245. package/dist/components/ui/index.js +4 -0
  246. package/dist/components/ui/index.js.map +1 -0
  247. package/dist/constants/timing.d.ts +22 -0
  248. package/dist/constants/timing.d.ts.map +1 -0
  249. package/dist/constants/timing.js +26 -0
  250. package/dist/constants/timing.js.map +1 -0
  251. package/dist/dashboard.js +1 -1
  252. package/dist/hooks/useActionSystem.d.ts +18 -4
  253. package/dist/hooks/useActionSystem.d.ts.map +1 -1
  254. package/dist/hooks/useActionSystem.js +125 -30
  255. package/dist/hooks/useActionSystem.js.map +1 -1
  256. package/dist/hooks/useDebugInfo.d.ts +11 -0
  257. package/dist/hooks/useDebugInfo.d.ts.map +1 -0
  258. package/dist/hooks/useDebugInfo.js +34 -0
  259. package/dist/hooks/useDebugInfo.js.map +1 -0
  260. package/dist/hooks/useDialogState.d.ts +22 -0
  261. package/dist/hooks/useDialogState.d.ts.map +1 -0
  262. package/dist/hooks/useDialogState.js +62 -0
  263. package/dist/hooks/useDialogState.js.map +1 -0
  264. package/dist/hooks/useInputHandling.d.ts +60 -0
  265. package/dist/hooks/useInputHandling.d.ts.map +1 -0
  266. package/dist/hooks/useInputHandling.js +280 -0
  267. package/dist/hooks/useInputHandling.js.map +1 -0
  268. package/dist/hooks/useLayoutManagement.d.ts +12 -0
  269. package/dist/hooks/useLayoutManagement.d.ts.map +1 -0
  270. package/dist/hooks/useLayoutManagement.js +58 -0
  271. package/dist/hooks/useLayoutManagement.js.map +1 -0
  272. package/dist/hooks/useNavigation.js +1 -1
  273. package/dist/hooks/useNavigation.js.map +1 -1
  274. package/dist/hooks/usePaneCreation.d.ts +1 -2
  275. package/dist/hooks/usePaneCreation.d.ts.map +1 -1
  276. package/dist/hooks/usePaneCreation.js +17 -36
  277. package/dist/hooks/usePaneCreation.js.map +1 -1
  278. package/dist/hooks/usePaneLoading.d.ts +45 -0
  279. package/dist/hooks/usePaneLoading.d.ts.map +1 -0
  280. package/dist/hooks/usePaneLoading.js +188 -0
  281. package/dist/hooks/usePaneLoading.js.map +1 -0
  282. package/dist/hooks/usePaneRunner.d.ts.map +1 -1
  283. package/dist/hooks/usePaneRunner.js +16 -8
  284. package/dist/hooks/usePaneRunner.js.map +1 -1
  285. package/dist/hooks/usePaneSync.d.ts +34 -0
  286. package/dist/hooks/usePaneSync.d.ts.map +1 -0
  287. package/dist/hooks/usePaneSync.js +182 -0
  288. package/dist/hooks/usePaneSync.js.map +1 -0
  289. package/dist/hooks/usePanes.d.ts.map +1 -1
  290. package/dist/hooks/usePanes.js +81 -258
  291. package/dist/hooks/usePanes.js.map +1 -1
  292. package/dist/hooks/useServices.d.ts +24 -0
  293. package/dist/hooks/useServices.d.ts.map +1 -0
  294. package/dist/hooks/useServices.js +39 -0
  295. package/dist/hooks/useServices.js.map +1 -0
  296. package/dist/hooks/useShellDetection.d.ts +10 -0
  297. package/dist/hooks/useShellDetection.d.ts.map +1 -0
  298. package/dist/hooks/useShellDetection.js +53 -0
  299. package/dist/hooks/useShellDetection.js.map +1 -0
  300. package/dist/hooks/useStatusMessages.d.ts +11 -0
  301. package/dist/hooks/useStatusMessages.d.ts.map +1 -0
  302. package/dist/hooks/useStatusMessages.js +32 -0
  303. package/dist/hooks/useStatusMessages.js.map +1 -0
  304. package/dist/hooks/useTemporaryStatus.d.ts +13 -0
  305. package/dist/hooks/useTemporaryStatus.d.ts.map +1 -0
  306. package/dist/hooks/useTemporaryStatus.js +30 -0
  307. package/dist/hooks/useTemporaryStatus.js.map +1 -0
  308. package/dist/hooks/useTerminalWidth.d.ts.map +1 -1
  309. package/dist/hooks/useTerminalWidth.js +7 -12
  310. package/dist/hooks/useTerminalWidth.js.map +1 -1
  311. package/dist/hooks/useTunnelManagement.d.ts +18 -0
  312. package/dist/hooks/useTunnelManagement.d.ts.map +1 -0
  313. package/dist/hooks/useTunnelManagement.js +55 -0
  314. package/dist/hooks/useTunnelManagement.js.map +1 -0
  315. package/dist/hooks/useWorktreeActions.d.ts.map +1 -1
  316. package/dist/hooks/useWorktreeActions.js +23 -24
  317. package/dist/hooks/useWorktreeActions.js.map +1 -1
  318. package/dist/index.js +279 -73
  319. package/dist/index.js.map +1 -1
  320. package/dist/layout/LayoutCalculator.d.ts +62 -0
  321. package/dist/layout/LayoutCalculator.d.ts.map +1 -0
  322. package/dist/layout/LayoutCalculator.js +143 -0
  323. package/dist/layout/LayoutCalculator.js.map +1 -0
  324. package/dist/layout/SpacerManager.d.ts +64 -0
  325. package/dist/layout/SpacerManager.d.ts.map +1 -0
  326. package/dist/layout/SpacerManager.js +148 -0
  327. package/dist/layout/SpacerManager.js.map +1 -0
  328. package/dist/layout/TmuxLayoutApplier.d.ts +59 -0
  329. package/dist/layout/TmuxLayoutApplier.d.ts.map +1 -0
  330. package/dist/layout/TmuxLayoutApplier.js +135 -0
  331. package/dist/layout/TmuxLayoutApplier.js.map +1 -0
  332. package/dist/panes/decorative-pane.d.ts +3 -0
  333. package/dist/panes/decorative-pane.d.ts.map +1 -0
  334. package/dist/panes/decorative-pane.js +136 -0
  335. package/dist/panes/decorative-pane.js.map +1 -0
  336. package/dist/panes/spacer-pane.d.ts +8 -0
  337. package/dist/panes/spacer-pane.d.ts.map +1 -0
  338. package/dist/panes/spacer-pane.js +40 -0
  339. package/dist/panes/spacer-pane.js.map +1 -0
  340. package/dist/server/embedded-assets.d.ts.map +1 -1
  341. package/dist/server/embedded-assets.js +849 -4752
  342. package/dist/server/embedded-assets.js.map +1 -1
  343. package/dist/server/index.js +1 -1
  344. package/dist/server/index.js.map +1 -1
  345. package/dist/server/routes/actionsRoutes.d.ts +2 -0
  346. package/dist/server/routes/actionsRoutes.d.ts.map +1 -0
  347. package/dist/server/routes/actionsRoutes.js +110 -0
  348. package/dist/server/routes/actionsRoutes.js.map +1 -0
  349. package/dist/server/routes/healthRoutes.d.ts +13 -0
  350. package/dist/server/routes/healthRoutes.d.ts.map +1 -0
  351. package/dist/server/routes/healthRoutes.js +70 -0
  352. package/dist/server/routes/healthRoutes.js.map +1 -0
  353. package/dist/server/routes/index.d.ts +8 -0
  354. package/dist/server/routes/index.d.ts.map +1 -0
  355. package/dist/server/routes/index.js +67 -0
  356. package/dist/server/routes/index.js.map +1 -0
  357. package/dist/server/routes/keysRoutes.d.ts +20 -0
  358. package/dist/server/routes/keysRoutes.d.ts.map +1 -0
  359. package/dist/server/routes/keysRoutes.js +111 -0
  360. package/dist/server/routes/keysRoutes.js.map +1 -0
  361. package/dist/server/routes/panesRoutes.d.ts +2 -0
  362. package/dist/server/routes/panesRoutes.d.ts.map +1 -0
  363. package/dist/server/routes/panesRoutes.js +373 -0
  364. package/dist/server/routes/panesRoutes.js.map +1 -0
  365. package/dist/server/routes/settingsRoutes.d.ts +94 -0
  366. package/dist/server/routes/settingsRoutes.d.ts.map +1 -0
  367. package/dist/server/routes/settingsRoutes.js +159 -0
  368. package/dist/server/routes/settingsRoutes.js.map +1 -0
  369. package/dist/server/routes/streamRoutes.d.ts +18 -0
  370. package/dist/server/routes/streamRoutes.d.ts.map +1 -0
  371. package/dist/server/routes/streamRoutes.js +87 -0
  372. package/dist/server/routes/streamRoutes.js.map +1 -0
  373. package/dist/server/routes/tunnelRoutes.d.ts +11 -0
  374. package/dist/server/routes/tunnelRoutes.d.ts.map +1 -0
  375. package/dist/server/routes/tunnelRoutes.js +28 -0
  376. package/dist/server/routes/tunnelRoutes.js.map +1 -0
  377. package/dist/server/routes.d.ts +16 -2
  378. package/dist/server/routes.d.ts.map +1 -1
  379. package/dist/server/routes.js +16 -811
  380. package/dist/server/routes.js.map +1 -1
  381. package/dist/{AutoUpdater.d.ts → services/AutoUpdater.d.ts} +1 -0
  382. package/dist/services/AutoUpdater.d.ts.map +1 -0
  383. package/dist/{AutoUpdater.js → services/AutoUpdater.js} +42 -9
  384. package/dist/services/AutoUpdater.js.map +1 -0
  385. package/dist/services/ConfigWatcher.d.ts.map +1 -1
  386. package/dist/services/ConfigWatcher.js +10 -3
  387. package/dist/services/ConfigWatcher.js.map +1 -1
  388. package/dist/services/LogService.d.ts +112 -0
  389. package/dist/services/LogService.d.ts.map +1 -0
  390. package/dist/services/LogService.js +252 -0
  391. package/dist/services/LogService.js.map +1 -0
  392. package/dist/services/PaneAnalyzer.d.ts.map +1 -0
  393. package/dist/{PaneAnalyzer.js → services/PaneAnalyzer.js} +1 -1
  394. package/dist/services/PaneAnalyzer.js.map +1 -0
  395. package/dist/services/PaneLifecycleManager.d.ts +60 -0
  396. package/dist/services/PaneLifecycleManager.d.ts.map +1 -0
  397. package/dist/services/PaneLifecycleManager.js +119 -0
  398. package/dist/services/PaneLifecycleManager.js.map +1 -0
  399. package/dist/services/PaneWorkerManager.d.ts.map +1 -1
  400. package/dist/services/PaneWorkerManager.js +41 -12
  401. package/dist/services/PaneWorkerManager.js.map +1 -1
  402. package/dist/services/PopupManager.d.ts +68 -0
  403. package/dist/services/PopupManager.d.ts.map +1 -0
  404. package/dist/services/PopupManager.js +415 -0
  405. package/dist/services/PopupManager.js.map +1 -0
  406. package/dist/services/StatusDetector.js +1 -1
  407. package/dist/services/StatusDetector.js.map +1 -1
  408. package/dist/services/TerminalStreamer.d.ts +1 -0
  409. package/dist/services/TerminalStreamer.d.ts.map +1 -1
  410. package/dist/services/TerminalStreamer.js +22 -16
  411. package/dist/services/TerminalStreamer.js.map +1 -1
  412. package/dist/services/TmuxService.d.ts +298 -0
  413. package/dist/services/TmuxService.d.ts.map +1 -0
  414. package/dist/services/TmuxService.js +768 -0
  415. package/dist/services/TmuxService.js.map +1 -0
  416. package/dist/services/TunnelService.d.ts +1 -0
  417. package/dist/services/TunnelService.d.ts.map +1 -1
  418. package/dist/services/TunnelService.js +57 -15
  419. package/dist/services/TunnelService.js.map +1 -1
  420. package/dist/shared/StateManager.d.ts +49 -1
  421. package/dist/shared/StateManager.d.ts.map +1 -1
  422. package/dist/shared/StateManager.js +97 -2
  423. package/dist/shared/StateManager.js.map +1 -1
  424. package/dist/theme/colors.d.ts +25 -0
  425. package/dist/theme/colors.d.ts.map +1 -0
  426. package/dist/theme/colors.js +33 -0
  427. package/dist/theme/colors.js.map +1 -0
  428. package/dist/types.d.ts +18 -0
  429. package/dist/types.d.ts.map +1 -1
  430. package/dist/utils/asciiArt.d.ts +26 -0
  431. package/dist/utils/asciiArt.d.ts.map +1 -0
  432. package/dist/utils/asciiArt.js +75 -0
  433. package/dist/utils/asciiArt.js.map +1 -0
  434. package/dist/utils/conflictMonitor.d.ts +20 -0
  435. package/dist/utils/conflictMonitor.d.ts.map +1 -0
  436. package/dist/utils/conflictMonitor.js +113 -0
  437. package/dist/utils/conflictMonitor.js.map +1 -0
  438. package/dist/utils/conflictResolutionPane.d.ts.map +1 -1
  439. package/dist/utils/conflictResolutionPane.js +75 -80
  440. package/dist/utils/conflictResolutionPane.js.map +1 -1
  441. package/dist/utils/errorHandling.d.ts +37 -0
  442. package/dist/utils/errorHandling.d.ts.map +1 -0
  443. package/dist/utils/errorHandling.js +112 -0
  444. package/dist/utils/errorHandling.js.map +1 -0
  445. package/dist/utils/fileScanner.d.ts +23 -0
  446. package/dist/utils/fileScanner.d.ts.map +1 -0
  447. package/dist/utils/fileScanner.js +123 -0
  448. package/dist/utils/fileScanner.js.map +1 -0
  449. package/dist/utils/generated-agents-doc.d.ts +1 -1
  450. package/dist/utils/generated-agents-doc.js +1 -1
  451. package/dist/utils/git.d.ts +4 -0
  452. package/dist/utils/git.d.ts.map +1 -1
  453. package/dist/utils/git.js +15 -0
  454. package/dist/utils/git.js.map +1 -1
  455. package/dist/utils/hooks.d.ts.map +1 -1
  456. package/dist/utils/hooks.js +65 -36
  457. package/dist/utils/hooks.js.map +1 -1
  458. package/dist/utils/hooksDocs.d.ts +1 -1
  459. package/dist/utils/layoutManager.d.ts +49 -0
  460. package/dist/utils/layoutManager.d.ts.map +1 -0
  461. package/dist/utils/layoutManager.js +249 -0
  462. package/dist/utils/layoutManager.js.map +1 -0
  463. package/dist/utils/mergeExecution.d.ts.map +1 -1
  464. package/dist/utils/mergeExecution.js +8 -1
  465. package/dist/utils/mergeExecution.js.map +1 -1
  466. package/dist/utils/mergeValidation.d.ts.map +1 -1
  467. package/dist/utils/mergeValidation.js +42 -13
  468. package/dist/utils/mergeValidation.js.map +1 -1
  469. package/dist/utils/paneCreation.d.ts.map +1 -1
  470. package/dist/utils/paneCreation.js +210 -88
  471. package/dist/utils/paneCreation.js.map +1 -1
  472. package/dist/utils/paneRebinding.d.ts +14 -0
  473. package/dist/utils/paneRebinding.d.ts.map +1 -0
  474. package/dist/utils/paneRebinding.js +28 -0
  475. package/dist/utils/paneRebinding.js.map +1 -0
  476. package/dist/utils/popup.d.ts +97 -0
  477. package/dist/utils/popup.d.ts.map +1 -0
  478. package/dist/utils/popup.js +503 -0
  479. package/dist/utils/popup.js.map +1 -0
  480. package/dist/utils/postPaneCleanup.d.ts +12 -0
  481. package/dist/utils/postPaneCleanup.d.ts.map +1 -0
  482. package/dist/utils/postPaneCleanup.js +54 -0
  483. package/dist/utils/postPaneCleanup.js.map +1 -0
  484. package/dist/utils/shellPaneDetection.d.ts +44 -0
  485. package/dist/utils/shellPaneDetection.d.ts.map +1 -0
  486. package/dist/utils/shellPaneDetection.js +179 -0
  487. package/dist/utils/shellPaneDetection.js.map +1 -0
  488. package/dist/utils/systemCheck.d.ts +19 -0
  489. package/dist/utils/systemCheck.d.ts.map +1 -0
  490. package/dist/utils/systemCheck.js +160 -0
  491. package/dist/utils/systemCheck.js.map +1 -0
  492. package/dist/utils/tmux.d.ts +72 -1
  493. package/dist/utils/tmux.d.ts.map +1 -1
  494. package/dist/utils/tmux.js +369 -99
  495. package/dist/utils/tmux.js.map +1 -1
  496. package/dist/utils/welcomePane.d.ts +22 -0
  497. package/dist/utils/welcomePane.d.ts.map +1 -0
  498. package/dist/utils/welcomePane.js +111 -0
  499. package/dist/utils/welcomePane.js.map +1 -0
  500. package/dist/utils/welcomePaneManager.d.ts +36 -0
  501. package/dist/utils/welcomePaneManager.d.ts.map +1 -0
  502. package/dist/utils/welcomePaneManager.js +160 -0
  503. package/dist/utils/welcomePaneManager.js.map +1 -0
  504. package/dist/workers/PaneWorker.js +8 -8
  505. package/dist/workers/PaneWorker.js.map +1 -1
  506. package/dist/workers/updateChecker.js +1 -1
  507. package/dist/workers/updateChecker.js.map +1 -1
  508. package/package.json +7 -2
  509. package/dist/AutoUpdater.d.ts.map +0 -1
  510. package/dist/AutoUpdater.js.map +0 -1
  511. package/dist/BetterTextInput.d.ts +0 -10
  512. package/dist/BetterTextInput.d.ts.map +0 -1
  513. package/dist/BetterTextInput.js +0 -177
  514. package/dist/BetterTextInput.js.map +0 -1
  515. package/dist/CleanTextInput.d.ts +0 -10
  516. package/dist/CleanTextInput.d.ts.map +0 -1
  517. package/dist/CleanTextInput.js.map +0 -1
  518. package/dist/EnhancedTextInput.d.ts +0 -13
  519. package/dist/EnhancedTextInput.d.ts.map +0 -1
  520. package/dist/EnhancedTextInput.js +0 -443
  521. package/dist/EnhancedTextInput.js.map +0 -1
  522. package/dist/GeminiTextInput.d.ts +0 -12
  523. package/dist/GeminiTextInput.d.ts.map +0 -1
  524. package/dist/GeminiTextInput.js +0 -210
  525. package/dist/GeminiTextInput.js.map +0 -1
  526. package/dist/MergePane.d.ts.map +0 -1
  527. package/dist/MergePane.js.map +0 -1
  528. package/dist/MultilineTextInput.d.ts +0 -10
  529. package/dist/MultilineTextInput.d.ts.map +0 -1
  530. package/dist/MultilineTextInput.js +0 -184
  531. package/dist/MultilineTextInput.js.map +0 -1
  532. package/dist/PaneAnalyzer.d.ts.map +0 -1
  533. package/dist/PaneAnalyzer.js.map +0 -1
  534. package/dist/SimpleEnhancedInput.d.ts +0 -13
  535. package/dist/SimpleEnhancedInput.d.ts.map +0 -1
  536. package/dist/SimpleEnhancedInput.js +0 -639
  537. package/dist/SimpleEnhancedInput.js.map +0 -1
  538. package/dist/SimpleGeminiInput.d.ts +0 -12
  539. package/dist/SimpleGeminiInput.d.ts.map +0 -1
  540. package/dist/SimpleGeminiInput.js +0 -223
  541. package/dist/SimpleGeminiInput.js.map +0 -1
  542. package/dist/StyledTextInput.d.ts.map +0 -1
  543. package/dist/StyledTextInput.js.map +0 -1
  544. package/dist/components/ActionChoiceDialog.d.ts.map +0 -1
  545. package/dist/components/ActionChoiceDialog.js.map +0 -1
  546. package/dist/components/ActionConfirmDialog.d.ts.map +0 -1
  547. package/dist/components/ActionConfirmDialog.js.map +0 -1
  548. package/dist/components/ActionInputDialog.d.ts.map +0 -1
  549. package/dist/components/ActionInputDialog.js.map +0 -1
  550. package/dist/components/ActionProgressDialog.d.ts.map +0 -1
  551. package/dist/components/ActionProgressDialog.js.map +0 -1
  552. package/dist/components/AgentChoiceDialog.d.ts.map +0 -1
  553. package/dist/components/AgentChoiceDialog.js.map +0 -1
  554. package/dist/components/CloseOptionsDialog.d.ts.map +0 -1
  555. package/dist/components/CloseOptionsDialog.js.map +0 -1
  556. package/dist/components/CommandPromptDialog.d.ts.map +0 -1
  557. package/dist/components/CommandPromptDialog.js.map +0 -1
  558. package/dist/components/CreatingIndicator.d.ts.map +0 -1
  559. package/dist/components/CreatingIndicator.js.map +0 -1
  560. package/dist/components/FileCopyPrompt.d.ts.map +0 -1
  561. package/dist/components/FileCopyPrompt.js.map +0 -1
  562. package/dist/components/FooterHelp.d.ts +0 -10
  563. package/dist/components/FooterHelp.d.ts.map +0 -1
  564. package/dist/components/FooterHelp.js +0 -19
  565. package/dist/components/FooterHelp.js.map +0 -1
  566. package/dist/components/HooksDialog.d.ts.map +0 -1
  567. package/dist/components/HooksDialog.js.map +0 -1
  568. package/dist/components/KebabMenu.d.ts.map +0 -1
  569. package/dist/components/KebabMenu.js.map +0 -1
  570. package/dist/components/LoadingIndicator.d.ts.map +0 -1
  571. package/dist/components/LoadingIndicator.js.map +0 -1
  572. package/dist/components/MergeConfirmationDialog.d.ts.map +0 -1
  573. package/dist/components/MergeConfirmationDialog.js.map +0 -1
  574. package/dist/components/NewPaneDialog.d.ts +0 -9
  575. package/dist/components/NewPaneDialog.d.ts.map +0 -1
  576. package/dist/components/NewPaneDialog.js +0 -11
  577. package/dist/components/NewPaneDialog.js.map +0 -1
  578. package/dist/components/PaneCard.d.ts.map +0 -1
  579. package/dist/components/PaneCard.js +0 -38
  580. package/dist/components/PaneCard.js.map +0 -1
  581. package/dist/components/PanesGrid.d.ts +0 -14
  582. package/dist/components/PanesGrid.d.ts.map +0 -1
  583. package/dist/components/PanesGrid.js +0 -19
  584. package/dist/components/PanesGrid.js.map +0 -1
  585. package/dist/components/QRCode.d.ts.map +0 -1
  586. package/dist/components/QRCode.js.map +0 -1
  587. package/dist/components/RunningIndicator.d.ts.map +0 -1
  588. package/dist/components/RunningIndicator.js.map +0 -1
  589. package/dist/components/SettingsDialog.d.ts.map +0 -1
  590. package/dist/components/SettingsDialog.js.map +0 -1
  591. package/dist/components/Spinner.d.ts.map +0 -1
  592. package/dist/components/Spinner.js.map +0 -1
  593. package/dist/components/UpdateDialog.d.ts.map +0 -1
  594. package/dist/components/UpdateDialog.js.map +0 -1
  595. package/dist/components/UpdatingIndicator.d.ts.map +0 -1
  596. package/dist/components/UpdatingIndicator.js.map +0 -1
  597. package/dist/server/static.d.ts +0 -6
  598. package/dist/server/static.d.ts.map +0 -1
  599. package/dist/server/static.js +0 -3040
  600. package/dist/server/static.js.map +0 -1
  601. /package/dist/components/{ActionChoiceDialog.js → dialogs/ActionChoiceDialog.js} +0 -0
  602. /package/dist/components/{ActionConfirmDialog.d.ts → dialogs/ActionConfirmDialog.d.ts} +0 -0
  603. /package/dist/components/{ActionConfirmDialog.js → dialogs/ActionConfirmDialog.js} +0 -0
  604. /package/dist/components/{ActionInputDialog.d.ts → dialogs/ActionInputDialog.d.ts} +0 -0
  605. /package/dist/components/{ActionProgressDialog.d.ts → dialogs/ActionProgressDialog.d.ts} +0 -0
  606. /package/dist/components/{ActionProgressDialog.js → dialogs/ActionProgressDialog.js} +0 -0
  607. /package/dist/components/{AgentChoiceDialog.d.ts → dialogs/AgentChoiceDialog.d.ts} +0 -0
  608. /package/dist/components/{AgentChoiceDialog.js → dialogs/AgentChoiceDialog.js} +0 -0
  609. /package/dist/components/{CloseOptionsDialog.js → dialogs/CloseOptionsDialog.js} +0 -0
  610. /package/dist/components/{CommandPromptDialog.d.ts → dialogs/CommandPromptDialog.d.ts} +0 -0
  611. /package/dist/components/{HooksDialog.d.ts → dialogs/HooksDialog.d.ts} +0 -0
  612. /package/dist/components/{HooksDialog.js → dialogs/HooksDialog.js} +0 -0
  613. /package/dist/components/{MergeConfirmationDialog.js → dialogs/MergeConfirmationDialog.js} +0 -0
  614. /package/dist/components/{SettingsDialog.js → dialogs/SettingsDialog.js} +0 -0
  615. /package/dist/components/{UpdateDialog.d.ts → dialogs/UpdateDialog.d.ts} +0 -0
  616. /package/dist/components/{UpdateDialog.js → dialogs/UpdateDialog.js} +0 -0
  617. /package/dist/components/{CreatingIndicator.d.ts → indicators/CreatingIndicator.d.ts} +0 -0
  618. /package/dist/components/{CreatingIndicator.js → indicators/CreatingIndicator.js} +0 -0
  619. /package/dist/components/{LoadingIndicator.d.ts → indicators/LoadingIndicator.d.ts} +0 -0
  620. /package/dist/components/{LoadingIndicator.js → indicators/LoadingIndicator.js} +0 -0
  621. /package/dist/components/{RunningIndicator.d.ts → indicators/RunningIndicator.d.ts} +0 -0
  622. /package/dist/components/{RunningIndicator.js → indicators/RunningIndicator.js} +0 -0
  623. /package/dist/components/{Spinner.d.ts → indicators/Spinner.d.ts} +0 -0
  624. /package/dist/components/{Spinner.js → indicators/Spinner.js} +0 -0
  625. /package/dist/components/{UpdatingIndicator.d.ts → indicators/UpdatingIndicator.d.ts} +0 -0
  626. /package/dist/components/{UpdatingIndicator.js → indicators/UpdatingIndicator.js} +0 -0
  627. /package/dist/{StyledTextInput.d.ts → components/inputs/StyledTextInput.d.ts} +0 -0
  628. /package/dist/{StyledTextInput.js → components/inputs/StyledTextInput.js} +0 -0
  629. /package/dist/components/{KebabMenu.js → panes/KebabMenu.js} +0 -0
  630. /package/dist/{MergePane.d.ts → components/panes/MergePane.d.ts} +0 -0
  631. /package/dist/components/{FileCopyPrompt.d.ts → ui/FileCopyPrompt.d.ts} +0 -0
  632. /package/dist/components/{FileCopyPrompt.js → ui/FileCopyPrompt.js} +0 -0
  633. /package/dist/components/{QRCode.d.ts → ui/QRCode.d.ts} +0 -0
  634. /package/dist/components/{QRCode.js → ui/QRCode.js} +0 -0
  635. /package/dist/{PaneAnalyzer.d.ts → services/PaneAnalyzer.d.ts} +0 -0
package/dist/DmuxApp.js CHANGED
@@ -1,123 +1,112 @@
1
- import React, { useState, useEffect } from 'react';
2
- import { Box, Text, useInput, useApp } from 'ink';
3
- import { execSync } from 'child_process';
4
- import path from 'path';
5
- import { createRequire } from 'module';
1
+ import React, { useState, useEffect } from "react";
2
+ import { Box, Text, useApp, useStdout } from "ink";
3
+ import { createRequire } from "module";
4
+ import { TmuxService } from "./services/TmuxService.js";
6
5
  // Hooks
7
- import usePanes from './hooks/usePanes.js';
8
- import useProjectSettings from './hooks/useProjectSettings.js';
9
- import useTerminalWidth from './hooks/useTerminalWidth.js';
10
- import useNavigation from './hooks/useNavigation.js';
11
- import useAutoUpdater from './hooks/useAutoUpdater.js';
12
- import useAgentDetection from './hooks/useAgentDetection.js';
13
- import useAgentStatus from './hooks/useAgentStatus.js';
14
- import usePaneRunner from './hooks/usePaneRunner.js';
15
- import usePaneCreation from './hooks/usePaneCreation.js';
16
- import useActionSystem from './hooks/useActionSystem.js';
6
+ import usePanes from "./hooks/usePanes.js";
7
+ import useProjectSettings from "./hooks/useProjectSettings.js";
8
+ import useTerminalWidth from "./hooks/useTerminalWidth.js";
9
+ import useNavigation from "./hooks/useNavigation.js";
10
+ import useAutoUpdater from "./hooks/useAutoUpdater.js";
11
+ import useAgentDetection from "./hooks/useAgentDetection.js";
12
+ import useAgentStatus from "./hooks/useAgentStatus.js";
13
+ import usePaneRunner from "./hooks/usePaneRunner.js";
14
+ import usePaneCreation from "./hooks/usePaneCreation.js";
15
+ import useActionSystem from "./hooks/useActionSystem.js";
16
+ import { useStatusMessages } from "./hooks/useStatusMessages.js";
17
+ import { useLayoutManagement } from "./hooks/useLayoutManagement.js";
18
+ import { useInputHandling } from "./hooks/useInputHandling.js";
19
+ import { useDialogState } from "./hooks/useDialogState.js";
20
+ import { useTunnelManagement } from "./hooks/useTunnelManagement.js";
21
+ import { useDebugInfo } from "./hooks/useDebugInfo.js";
17
22
  // Utils
18
- import { applySmartLayout } from './utils/tmux.js';
19
- import { suggestCommand } from './utils/commands.js';
20
- import { generateSlug } from './utils/slug.js';
21
- import { getMainBranch } from './utils/git.js';
22
- import { capturePaneContent } from './utils/paneCapture.js';
23
- import { StateManager } from './shared/StateManager.js';
24
- import { getStatusDetector } from './services/StatusDetector.js';
25
- import { PaneAction, getAvailableActions } from './actions/index.js';
26
- import { SettingsManager, SETTING_DEFINITIONS } from './utils/settingsManager.js';
23
+ import { SIDEBAR_WIDTH } from "./utils/layoutManager.js";
24
+ import { supportsPopups } from "./utils/popup.js";
25
+ import { StateManager } from "./shared/StateManager.js";
26
+ import { REPAINT_SPINNER_DURATION, } from "./constants/timing.js";
27
+ import { getStatusDetector, } from "./services/StatusDetector.js";
28
+ import { SettingsManager } from "./utils/settingsManager.js";
29
+ import { useServices } from "./hooks/useServices.js";
30
+ import { PaneLifecycleManager } from "./services/PaneLifecycleManager.js";
31
+ import { fileURLToPath } from "url";
32
+ import { dirname } from "path";
33
+ const __filename = fileURLToPath(import.meta.url);
34
+ const __dirname = dirname(__filename);
27
35
  const require = createRequire(import.meta.url);
28
- const packageJson = require('../package.json');
29
- import PanesGrid from './components/PanesGrid.js';
30
- import NewPaneDialog from './components/NewPaneDialog.js';
31
- import AgentChoiceDialog from './components/AgentChoiceDialog.js';
32
- import CommandPromptDialog from './components/CommandPromptDialog.js';
33
- import FileCopyPrompt from './components/FileCopyPrompt.js';
34
- import LoadingIndicator from './components/LoadingIndicator.js';
35
- import RunningIndicator from './components/RunningIndicator.js';
36
- import UpdatingIndicator from './components/UpdatingIndicator.js';
37
- import CreatingIndicator from './components/CreatingIndicator.js';
38
- import FooterHelp from './components/FooterHelp.js';
39
- import QRCode from './components/QRCode.js';
40
- import KebabMenu from './components/KebabMenu.js';
41
- import ActionChoiceDialog from './components/ActionChoiceDialog.js';
42
- import ActionConfirmDialog from './components/ActionConfirmDialog.js';
43
- import ActionInputDialog from './components/ActionInputDialog.js';
44
- import ActionProgressDialog from './components/ActionProgressDialog.js';
45
- import SettingsDialog from './components/SettingsDialog.js';
46
- import HooksDialog from './components/HooksDialog.js';
47
- const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoot, autoUpdater, serverPort, server }) => {
36
+ const packageJson = require("../package.json");
37
+ import PanesGrid from "./components/panes/PanesGrid.js";
38
+ import CommandPromptDialog from "./components/dialogs/CommandPromptDialog.js";
39
+ import FileCopyPrompt from "./components/ui/FileCopyPrompt.js";
40
+ import LoadingIndicator from "./components/indicators/LoadingIndicator.js";
41
+ import RunningIndicator from "./components/indicators/RunningIndicator.js";
42
+ import UpdatingIndicator from "./components/indicators/UpdatingIndicator.js";
43
+ import FooterHelp from "./components/ui/FooterHelp.js";
44
+ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoot, autoUpdater, serverPort, server, controlPaneId, }) => {
45
+ const { stdout } = useStdout();
46
+ const terminalHeight = stdout?.rows || 40;
48
47
  /* panes state moved to usePanes */
49
48
  const [selectedIndex, setSelectedIndex] = useState(0);
50
- const [showNewPaneDialog, setShowNewPaneDialog] = useState(false);
51
- const [newPanePrompt, setNewPanePrompt] = useState('');
52
- const [statusMessage, setStatusMessage] = useState('');
49
+ const { statusMessage, setStatusMessage, showStatus, clearStatus } = useStatusMessages();
53
50
  const [isCreatingPane, setIsCreatingPane] = useState(false);
54
- const [showQRCode, setShowQRCode] = useState(false);
55
- const [tunnelUrl, setTunnelUrl] = useState(null);
56
- const [isCreatingTunnel, setIsCreatingTunnel] = useState(false);
57
51
  // Settings state
58
52
  const [settingsManager] = useState(() => new SettingsManager(projectRoot));
59
- const [showSettingsDialog, setShowSettingsDialog] = useState(false);
60
- const [settingsMode, setSettingsMode] = useState('list');
61
- const [settingsSelectedIndex, setSettingsSelectedIndex] = useState(0);
62
- const [settingsEditingKey, setSettingsEditingKey] = useState();
63
- const [settingsEditingValueIndex, setSettingsEditingValueIndex] = useState(0);
64
- const [settingsScopeIndex, setSettingsScopeIndex] = useState(0);
65
53
  // Force repaint trigger - incrementing this causes Ink to re-render
66
54
  const [forceRepaintTrigger, setForceRepaintTrigger] = useState(0);
67
55
  // Spinner state - shows for a few frames to force render
68
56
  const [showRepaintSpinner, setShowRepaintSpinner] = useState(false);
69
57
  const { projectSettings, saveSettings } = useProjectSettings(settingsFile);
70
- // Hooks management state
71
- const [showHooksDialog, setShowHooksDialog] = useState(false);
72
- const [hooksSelectedIndex, setHooksSelectedIndex] = useState(0);
73
- const [hooksData, setHooksData] = useState([]);
74
- const [showCommandPrompt, setShowCommandPrompt] = useState(null);
75
- const [commandInput, setCommandInput] = useState('');
76
- const [showFileCopyPrompt, setShowFileCopyPrompt] = useState(false);
77
- const [currentCommandType, setCurrentCommandType] = useState(null);
78
- const [runningCommand, setRunningCommand] = useState(false);
79
- const [quitConfirmMode, setQuitConfirmMode] = useState(false);
80
- const [showKebabMenu, setShowKebabMenu] = useState(false);
81
- const [kebabMenuPaneIndex, setKebabMenuPaneIndex] = useState(null);
82
- const [kebabMenuOption, setKebabMenuOption] = useState(0);
83
- const [kebabMenuActions, setKebabMenuActions] = useState([]);
84
- // Debug message state - for temporary logging messages
85
- const [debugMessage, setDebugMessage] = useState('');
58
+ // Dialog state management
59
+ const dialogState = useDialogState();
60
+ const { showCommandPrompt, setShowCommandPrompt, commandInput, setCommandInput, showFileCopyPrompt, setShowFileCopyPrompt, currentCommandType, setCurrentCommandType, runningCommand, setRunningCommand, quitConfirmMode, setQuitConfirmMode, } = dialogState;
61
+ // Tunnel/network state management
62
+ const tunnelState = useTunnelManagement();
63
+ const { tunnelUrl, setTunnelUrl, tunnelCreating, setTunnelCreating, tunnelCopied, setTunnelCopied, localIp, setLocalIp, } = tunnelState;
64
+ // Debug/development info
65
+ const { debugMessage, setDebugMessage, currentBranch } = useDebugInfo(__dirname);
86
66
  // Update state handled by hook
87
- const { updateInfo, showUpdateDialog, isUpdating, performUpdate, skipUpdate, dismissUpdate, updateAvailable } = useAutoUpdater(autoUpdater, setStatusMessage);
67
+ const { updateInfo, isUpdating, updateAvailable, } = useAutoUpdater(autoUpdater, setStatusMessage);
88
68
  const { exit } = useApp();
69
+ // Flag to ignore input temporarily after popup closes (prevents buffered keys)
70
+ const [ignoreInput, setIgnoreInput] = useState(false);
89
71
  // Agent selection state
90
72
  const { availableAgents } = useAgentDetection();
91
- const [showAgentChoiceDialog, setShowAgentChoiceDialog] = useState(false);
92
73
  const [agentChoice, setAgentChoice] = useState(null);
93
- const [pendingPrompt, setPendingPrompt] = useState('');
74
+ // Popup support detection
75
+ const [popupsSupported, setPopupsSupported] = useState(false);
94
76
  // Track terminal dimensions for responsive layout
95
77
  const terminalWidth = useTerminalWidth();
78
+ // Track unread error and warning counts for logs badge
79
+ const [unreadErrorCount, setUnreadErrorCount] = useState(0);
80
+ const [unreadWarningCount, setUnreadWarningCount] = useState(0);
81
+ // Subscribe to StateManager for unread error/warning count updates
82
+ useEffect(() => {
83
+ const stateManager = StateManager.getInstance();
84
+ const updateCounts = () => {
85
+ setUnreadErrorCount(stateManager.getUnreadErrorCount());
86
+ setUnreadWarningCount(stateManager.getUnreadWarningCount());
87
+ };
88
+ // Initial count
89
+ updateCounts();
90
+ // Subscribe to changes
91
+ const unsubscribe = stateManager.subscribe(updateCounts);
92
+ return () => {
93
+ unsubscribe();
94
+ };
95
+ }, []);
96
96
  // Panes state and persistence (skipLoading will be updated after actionSystem is initialized)
97
97
  const { panes, setPanes, isLoading, loadPanes, savePanes } = usePanes(panesFile, false);
98
- // Track intentionally closed panes to prevent race condition
99
- // When a user closes a pane, we add it to this set. If the worker detects
100
- // the pane is gone (which it will), we check this set first before re-saving.
101
- const intentionallyClosedPanes = React.useRef(new Set());
102
- // Action system
103
- const actionSystem = useActionSystem({
104
- panes,
105
- savePanes,
106
- sessionName,
107
- projectName,
108
- onPaneRemove: (paneId) => {
109
- // Mark this pane as intentionally closed
110
- intentionallyClosedPanes.current.add(paneId);
111
- const updated = panes.filter(p => p.id !== paneId);
112
- setPanes(updated);
113
- // Clean up the tracking after a delay (in case of race conditions)
114
- setTimeout(() => {
115
- intentionallyClosedPanes.current.delete(paneId);
116
- }, 5000);
117
- },
118
- });
98
+ // Pane lifecycle manager - handles locking to prevent race conditions
99
+ // Replaces the old timeout-based intentionallyClosedPanes Set
100
+ const lifecycleManager = React.useMemo(() => PaneLifecycleManager.getInstance(), []);
101
+ // Clean up stale lifecycle operations periodically
102
+ useEffect(() => {
103
+ const cleanupInterval = setInterval(() => {
104
+ lifecycleManager.cleanupStaleOperations();
105
+ }, 60000); // Every 60 seconds
106
+ return () => clearInterval(cleanupInterval);
107
+ }, [lifecycleManager]);
119
108
  // Pane runner
120
- const { copyNonGitFiles, runCommandInternal, monitorTestOutput, monitorDevOutput, attachBackgroundWindow } = usePaneRunner({
109
+ const { copyNonGitFiles, runCommandInternal, } = usePaneRunner({
121
110
  panes,
122
111
  savePanes,
123
112
  projectSettings,
@@ -126,24 +115,47 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
126
115
  });
127
116
  // Force repaint helper - shows spinner for a few frames to force full re-render
128
117
  const forceRepaint = () => {
129
- setForceRepaintTrigger(prev => prev + 1);
118
+ setForceRepaintTrigger((prev) => prev + 1);
130
119
  setShowRepaintSpinner(true);
131
120
  // Hide spinner after a few frames (enough to trigger multiple renders)
132
- setTimeout(() => setShowRepaintSpinner(false), 100);
121
+ setTimeout(() => setShowRepaintSpinner(false), REPAINT_SPINNER_DURATION);
133
122
  };
134
123
  // Force repaint effect - ensures Ink re-renders when trigger changes
135
124
  useEffect(() => {
136
125
  if (forceRepaintTrigger > 0) {
137
126
  // Small delay to ensure terminal is ready
138
- const timer = setTimeout(() => {
127
+ const timer = setTimeout(async () => {
139
128
  try {
140
- execSync('tmux refresh-client', { stdio: 'pipe' });
129
+ const tmuxService = TmuxService.getInstance();
130
+ await tmuxService.refreshClient();
141
131
  }
142
132
  catch { }
143
133
  }, 50);
144
134
  return () => clearTimeout(timer);
145
135
  }
146
136
  }, [forceRepaintTrigger]);
137
+ // Get local network IP on mount
138
+ useEffect(() => {
139
+ const getLocalIp = async () => {
140
+ try {
141
+ // Get local IP address (not 127.0.0.1)
142
+ const { execSync } = await import("child_process");
143
+ const result = execSync(`hostname -I 2>/dev/null || ifconfig | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | head -1`, {
144
+ encoding: "utf-8",
145
+ stdio: "pipe",
146
+ }).trim();
147
+ if (result) {
148
+ setLocalIp(result.split(" ")[0]); // Take first IP if multiple
149
+ }
150
+ }
151
+ catch {
152
+ // Fallback to 127.0.0.1
153
+ setLocalIp("127.0.0.1");
154
+ }
155
+ };
156
+ getLocalIp();
157
+ }, []);
158
+ // Spinner animation and branch detection now handled in hooks
147
159
  // Pane creation
148
160
  const { createNewPane: createNewPaneHook } = usePaneCreation({
149
161
  panes,
@@ -151,18 +163,37 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
151
163
  projectName,
152
164
  setIsCreatingPane,
153
165
  setStatusMessage,
154
- setNewPanePrompt,
155
166
  loadPanes,
156
167
  panesFile,
157
168
  availableAgents,
158
169
  forceRepaint,
159
170
  });
171
+ // Initialize services
172
+ const { popupManager } = useServices({
173
+ // PopupManager config
174
+ sidebarWidth: SIDEBAR_WIDTH,
175
+ projectRoot: projectRoot || process.cwd(),
176
+ popupsSupported,
177
+ terminalWidth,
178
+ terminalHeight,
179
+ availableAgents,
180
+ agentChoice,
181
+ serverPort,
182
+ server,
183
+ settingsManager,
184
+ projectSettings,
185
+ // Callbacks
186
+ setStatusMessage,
187
+ setIgnoreInput,
188
+ savePanes,
189
+ loadPanes,
190
+ });
160
191
  // Listen for status updates with analysis data and merge into panes
161
192
  useEffect(() => {
162
193
  const statusDetector = getStatusDetector();
163
194
  const handleStatusUpdate = (event) => {
164
- setPanes(prevPanes => {
165
- const updatedPanes = prevPanes.map(pane => {
195
+ setPanes((prevPanes) => {
196
+ const updatedPanes = prevPanes.map((pane) => {
166
197
  if (pane.id === event.paneId) {
167
198
  const updated = {
168
199
  ...pane,
@@ -186,22 +217,23 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
186
217
  updated.analyzerError = event.analyzerError;
187
218
  }
188
219
  // Clear option dialog data when transitioning away from 'waiting' state
189
- if (event.status !== 'waiting' && pane.agentStatus === 'waiting') {
220
+ if (event.status !== "waiting" && pane.agentStatus === "waiting") {
190
221
  updated.optionsQuestion = undefined;
191
222
  updated.options = undefined;
192
223
  updated.potentialHarm = undefined;
193
224
  }
194
225
  // Clear summary when transitioning away from 'idle' state
195
- if (event.status !== 'idle' && pane.agentStatus === 'idle') {
226
+ if (event.status !== "idle" && pane.agentStatus === "idle") {
196
227
  updated.agentSummary = undefined;
197
228
  }
198
229
  // Clear analyzer error when successfully getting a new analysis
199
230
  // or when transitioning to 'working' status
200
- if (event.status === 'working') {
231
+ if (event.status === "working") {
201
232
  updated.analyzerError = undefined;
202
233
  }
203
- else if (event.status === 'waiting' || event.status === 'idle') {
204
- if (event.analyzerError === undefined && (event.optionsQuestion || event.summary)) {
234
+ else if (event.status === "waiting" || event.status === "idle") {
235
+ if (event.analyzerError === undefined &&
236
+ (event.optionsQuestion || event.summary)) {
205
237
  updated.analyzerError = undefined;
206
238
  }
207
239
  }
@@ -210,15 +242,15 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
210
242
  return pane;
211
243
  });
212
244
  // Persist to disk - ConfigWatcher will handle syncing to StateManager
213
- savePanes(updatedPanes).catch(err => {
214
- console.error('Failed to save panes after status update:', err);
245
+ savePanes(updatedPanes).catch((err) => {
246
+ console.error("Failed to save panes after status update:", err);
215
247
  });
216
248
  return updatedPanes;
217
249
  });
218
250
  };
219
- statusDetector.on('status-updated', handleStatusUpdate);
251
+ statusDetector.on("status-updated", handleStatusUpdate);
220
252
  return () => {
221
- statusDetector.off('status-updated', handleStatusUpdate);
253
+ statusDetector.off("status-updated", handleStatusUpdate);
222
254
  };
223
255
  }, [setPanes, savePanes]);
224
256
  // Note: No need to sync panes with StateManager here.
@@ -243,62 +275,29 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
243
275
  const handleTermination = () => {
244
276
  cleanExit();
245
277
  };
246
- process.on('SIGTERM', handleTermination);
247
- // Test debug message on mount
248
- StateManager.getInstance().setDebugMessage('Debug logging initialized - watching for AI activity...');
249
- setTimeout(() => {
250
- StateManager.getInstance().setDebugMessage('');
251
- }, 3000);
278
+ process.on("SIGTERM", handleTermination);
279
+ // Check if tmux supports popups (3.2+) and enable mouse mode for click-outside-to-close
280
+ const popupSupport = supportsPopups();
281
+ setPopupsSupported(popupSupport);
282
+ if (popupSupport) {
283
+ // Enable mouse mode only for this dmux session (not global)
284
+ }
252
285
  return () => {
253
- process.removeListener('SIGTERM', handleTermination);
286
+ process.removeListener("SIGTERM", handleTermination);
254
287
  };
255
288
  }, []);
256
- // Auto-show new pane dialog when starting with no panes
257
- useEffect(() => {
258
- // Only show the dialog if:
259
- // 1. Initial load is complete (!isLoading)
260
- // 2. We have no panes
261
- // 3. We're not already showing the dialog
262
- // 4. We're not showing any other dialogs or prompts
263
- if (!isLoading &&
264
- panes.length === 0 &&
265
- !showNewPaneDialog &&
266
- !actionSystem.actionState.showConfirmDialog &&
267
- !actionSystem.actionState.showChoiceDialog &&
268
- !actionSystem.actionState.showInputDialog &&
269
- !actionSystem.actionState.showProgressDialog &&
270
- !showCommandPrompt &&
271
- !showFileCopyPrompt &&
272
- !showAgentChoiceDialog &&
273
- !isCreatingPane &&
274
- !runningCommand &&
275
- !isUpdating) {
276
- setShowNewPaneDialog(true);
277
- }
278
- }, [isLoading, panes.length, showNewPaneDialog, actionSystem.actionState.showConfirmDialog, actionSystem.actionState.showChoiceDialog, actionSystem.actionState.showInputDialog, actionSystem.actionState.showProgressDialog, showCommandPrompt, showFileCopyPrompt, showAgentChoiceDialog, isCreatingPane, runningCommand, isUpdating]);
279
289
  // Update checking moved to useAutoUpdater
280
290
  // Set default agent choice when detection completes
281
291
  useEffect(() => {
282
292
  if (agentChoice == null && availableAgents.length > 0) {
283
- setAgentChoice(availableAgents[0] || 'claude');
293
+ setAgentChoice(availableAgents[0] || "claude");
284
294
  }
285
295
  }, [availableAgents]);
286
- // Monitor agent status across panes (returns a map of pane ID to status)
287
- const agentStatuses = useAgentStatus({
288
- panes,
289
- suspend: showNewPaneDialog || actionSystem.actionState.showConfirmDialog || actionSystem.actionState.showChoiceDialog || actionSystem.actionState.showInputDialog || actionSystem.actionState.showProgressDialog || !!showCommandPrompt || showFileCopyPrompt,
290
- onPaneRemoved: (paneId) => {
291
- // Check if this pane was intentionally closed
292
- // If so, don't re-save - the close action already handled it
293
- if (intentionallyClosedPanes.current.has(paneId)) {
294
- return;
295
- }
296
- // Pane was removed unexpectedly (e.g., user killed tmux pane manually)
297
- // Remove it from our tracking
298
- const updatedPanes = panes.filter(p => p.id !== paneId);
299
- savePanes(updatedPanes);
300
- },
301
- });
296
+ // Welcome pane is now fully event-based:
297
+ // - Created at startup (in src/index.ts)
298
+ // - Destroyed when first pane is created (in paneCreation.ts)
299
+ // - Recreated when last pane is closed (in paneActions.ts)
300
+ // No polling needed!
302
301
  // loadPanes moved to usePanes
303
302
  // getPanePositions moved to utils/tmux
304
303
  // Navigation logic moved to hook
@@ -306,1016 +305,290 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
306
305
  // findCardInDirection provided by useNavigation
307
306
  // savePanes moved to usePanes
308
307
  // applySmartLayout moved to utils/tmux
309
- const createNewPane = async (prompt, agent) => {
310
- setIsCreatingPane(true);
311
- setStatusMessage('Generating slug...');
312
- const slug = await generateSlug(prompt);
313
- setStatusMessage(`Creating worktree: ${slug}...`);
314
- // Get git root directory for consistent worktree placement
315
- let projectRoot;
316
- try {
317
- projectRoot = execSync('git rev-parse --show-toplevel', {
318
- encoding: 'utf-8',
319
- stdio: 'pipe'
320
- }).trim();
321
- }
322
- catch {
323
- // Fallback to current directory if not in a git repo
324
- projectRoot = process.cwd();
325
- }
326
- // Create worktree path inside .dmux/worktrees directory
327
- const worktreePath = path.join(projectRoot, '.dmux', 'worktrees', slug);
328
- // Get the original pane ID (where dmux is running) before clearing
329
- const originalPaneId = execSync('tmux display-message -p "#{pane_id}"', { encoding: 'utf-8' }).trim();
330
- // Multiple clearing strategies to prevent artifacts
331
- // 1. Clear screen with ANSI codes
332
- process.stdout.write('\x1b[2J\x1b[H');
333
- // 2. Fill with blank lines to push content off screen
334
- process.stdout.write('\n'.repeat(100));
335
- // 3. Clear tmux history and send clear command
336
- try {
337
- execSync('tmux clear-history', { stdio: 'pipe' });
338
- execSync('tmux send-keys C-l', { stdio: 'pipe' });
308
+ // Helper function to handle agent choice and pane creation
309
+ const handlePaneCreationWithAgent = async (prompt) => {
310
+ const agents = availableAgents;
311
+ if (agents.length === 0) {
312
+ await createNewPaneHook(prompt);
339
313
  }
340
- catch { }
341
- // Wait a bit for clearing to settle
342
- await new Promise(resolve => setTimeout(resolve, 100));
343
- // 4. Force tmux to refresh the display
344
- try {
345
- execSync('tmux refresh-client', { stdio: 'pipe' });
314
+ else if (agents.length === 1) {
315
+ await createNewPaneHook(prompt, agents[0]);
346
316
  }
347
- catch { }
348
- // Get current pane count to determine layout
349
- const paneCount = parseInt(execSync('tmux list-panes | wc -l', { encoding: 'utf-8' }).trim());
350
- // Enable pane borders to show titles
351
- try {
352
- execSync(`tmux set-option -g pane-border-status top`, { stdio: 'pipe' });
353
- }
354
- catch {
355
- // Ignore if already set or fails
356
- }
357
- // Create new pane
358
- const paneInfo = execSync(`tmux split-window -h -P -F '#{pane_id}'`, { encoding: 'utf-8' }).trim();
359
- // Wait for pane creation to settle
360
- await new Promise(resolve => setTimeout(resolve, 500));
361
- // Set pane title to match the slug
362
- try {
363
- execSync(`tmux select-pane -t '${paneInfo}' -T "${slug}"`, { stdio: 'pipe' });
364
- }
365
- catch {
366
- // Ignore if setting title fails
367
- }
368
- // Apply smart layout based on pane count
369
- const newPaneCount = paneCount + 1;
370
- applySmartLayout(newPaneCount);
371
- // Create git worktree and cd into it
372
- // This MUST happen before launching Claude to ensure we're in the right directory
373
- try {
374
- // First, create the worktree and cd into it as a single command
375
- // Use ; instead of && to ensure cd runs even if worktree already exists
376
- const worktreeCmd = `git worktree add "${worktreePath}" -b ${slug} 2>/dev/null ; cd "${worktreePath}"`;
377
- execSync(`tmux send-keys -t '${paneInfo}' '${worktreeCmd}' Enter`, { stdio: 'pipe' });
378
- // Wait longer for worktree creation and cd to complete
379
- // This is critical - if we don't wait long enough, Claude will start in the wrong directory
380
- await new Promise(resolve => setTimeout(resolve, 2500));
381
- // Verify we're in the worktree directory by sending pwd command
382
- execSync(`tmux send-keys -t '${paneInfo}' 'echo "Worktree created at:" && pwd' Enter`, { stdio: 'pipe' });
383
- await new Promise(resolve => setTimeout(resolve, 500));
384
- setStatusMessage(agent ? `Worktree created, launching ${agent === 'opencode' ? 'opencode' : 'Claude'}...` : 'Worktree created.');
385
- }
386
- catch (error) {
387
- // Log error but continue - worktree creation is essential
388
- setStatusMessage(`Warning: Worktree issue: ${error}`);
389
- // Even if worktree creation failed, try to cd to the directory in case it exists
390
- execSync(`tmux send-keys -t '${paneInfo}' 'cd "${worktreePath}" 2>/dev/null || (echo "ERROR: Failed to create/enter worktree ${slug}" && pwd)' Enter`, { stdio: 'pipe' });
391
- await new Promise(resolve => setTimeout(resolve, 1000));
392
- }
393
- // Prepare and send the agent command
394
- let escapedCmd = '';
395
- if (agent === 'claude') {
396
- // Claude should always be launched AFTER we're in the worktree directory
397
- let claudeCmd;
398
- if (prompt && prompt.trim()) {
399
- const escapedPrompt = prompt
400
- .replace(/\\/g, '\\\\')
401
- .replace(/"/g, '\\"')
402
- .replace(/`/g, '\\`')
403
- .replace(/\$/g, '\\$');
404
- claudeCmd = `claude "${escapedPrompt}" --permission-mode=acceptEdits`;
317
+ else {
318
+ // Multiple agents available - check for default agent setting first
319
+ const settings = settingsManager.getSettings();
320
+ if (settings.defaultAgent && agents.includes(settings.defaultAgent)) {
321
+ await createNewPaneHook(prompt, settings.defaultAgent);
405
322
  }
406
323
  else {
407
- claudeCmd = `claude --permission-mode=acceptEdits`;
324
+ // Show agent choice popup
325
+ const selectedAgent = await popupManager.launchAgentChoicePopup();
326
+ if (selectedAgent) {
327
+ await createNewPaneHook(prompt, selectedAgent);
328
+ }
408
329
  }
409
- // Send Claude command to new pane
410
- escapedCmd = claudeCmd.replace(/'/g, "'\\''");
411
- execSync(`tmux send-keys -t '${paneInfo}' '${escapedCmd}'`, { stdio: 'pipe' });
412
- execSync(`tmux send-keys -t '${paneInfo}' Enter`, { stdio: 'pipe' });
413
330
  }
414
- else if (agent === 'opencode') {
415
- // opencode: start the TUI, then paste the prompt and submit
416
- const openCoderCmd = `opencode`;
417
- const escapedOpenCmd = openCoderCmd.replace(/'/g, "'\\''");
418
- execSync(`tmux send-keys -t '${paneInfo}' '${escapedOpenCmd}'`, { stdio: 'pipe' });
419
- execSync(`tmux send-keys -t '${paneInfo}' Enter`, { stdio: 'pipe' });
420
- if (prompt && prompt.trim()) {
421
- await new Promise(resolve => setTimeout(resolve, 1500));
422
- const bufName = `dmux_prompt_${Date.now()}`;
423
- const promptEsc = prompt.replace(/\\/g, '\\\\').replace(/'/g, "'\\''");
424
- execSync(`tmux set-buffer -b '${bufName}' -- '${promptEsc}'`, { stdio: 'pipe' });
425
- execSync(`tmux paste-buffer -b '${bufName}' -t '${paneInfo}'`, { stdio: 'pipe' });
426
- await new Promise(resolve => setTimeout(resolve, 200));
427
- execSync(`tmux delete-buffer -b '${bufName}'`, { stdio: 'pipe' });
428
- execSync(`tmux send-keys -t '${paneInfo}' Enter`, { stdio: 'pipe' });
331
+ };
332
+ // Helper function to handle action results recursively
333
+ const handleActionResult = async (result) => {
334
+ // Handle ActionResults from background callbacks (e.g., conflict resolution completion)
335
+ // This allows showing dialogs even when not in the normal action flow
336
+ if (!popupsSupported)
337
+ return;
338
+ // Handle the result type and show appropriate dialog
339
+ if (result.type === "confirm") {
340
+ const confirmed = await popupManager.launchConfirmPopup(result.title || "Confirm", result.message, result.confirmLabel, result.cancelLabel);
341
+ if (confirmed && result.onConfirm) {
342
+ const nextResult = await result.onConfirm();
343
+ // Recursively handle nested results
344
+ if (nextResult) {
345
+ await handleActionResult(nextResult);
346
+ }
347
+ }
348
+ else if (!confirmed && result.onCancel) {
349
+ const nextResult = await result.onCancel();
350
+ if (nextResult) {
351
+ await handleActionResult(nextResult);
352
+ }
429
353
  }
430
354
  }
431
- if (agent === 'claude') {
432
- // Monitor for Claude Code trust prompt and auto-respond
433
- const autoApproveTrust = async () => {
434
- // Wait for Claude to start up before checking for prompts
435
- await new Promise(resolve => setTimeout(resolve, 800));
436
- const maxChecks = 100; // 100 checks * 100ms = 10 seconds total
437
- const checkInterval = 100; // Check every 100ms
438
- let lastContent = '';
439
- let stableContentCount = 0;
440
- let promptHandled = false;
441
- // More comprehensive trust prompt patterns
442
- const trustPromptPatterns = [
443
- /Do you trust the files in this folder\?/i,
444
- /Trust the files in this workspace\?/i,
445
- /Do you trust the authors of the files/i,
446
- /Do you want to trust this workspace\?/i,
447
- /trust.*files.*folder/i,
448
- /trust.*workspace/i,
449
- /Do you trust/i,
450
- /Trust this folder/i,
451
- /trust.*directory/i,
452
- /permission.*grant/i,
453
- /allow.*access/i,
454
- /workspace.*trust/i,
455
- /accept.*edits/i, // Claude's accept edits prompt
456
- /permission.*mode/i, // Permission mode prompt
457
- /allow.*claude/i, // Allow Claude prompt
458
- /\[y\/n\]/i, // Common yes/no prompt pattern
459
- /\(y\/n\)/i,
460
- /Yes\/No/i,
461
- /\[Y\/n\]/i, // Default yes pattern
462
- /press.*enter.*accept/i, // Press enter to accept
463
- /press.*enter.*continue/i, // Press enter to continue
464
- /❯\s*1\.\s*Yes,\s*proceed/i, // New Claude numbered menu format
465
- /Enter to confirm.*Esc to exit/i, // New Claude confirmation format
466
- /1\.\s*Yes,\s*proceed/i, // Yes proceed option
467
- /2\.\s*No,\s*exit/i // No exit option
468
- ];
469
- for (let i = 0; i < maxChecks; i++) {
470
- await new Promise(resolve => setTimeout(resolve, checkInterval));
355
+ else if (result.type === "choice") {
356
+ if (!result.options || !result.onSelect)
357
+ return;
358
+ const selectedId = await popupManager.launchChoicePopup(result.title || "Choose Option", result.message, result.options);
359
+ if (selectedId) {
360
+ const nextResult = await result.onSelect(selectedId);
361
+ // Recursively handle nested results
362
+ if (nextResult) {
363
+ await handleActionResult(nextResult);
364
+ }
365
+ }
366
+ }
367
+ else if (result.type === "input") {
368
+ if (!result.onSubmit)
369
+ return;
370
+ const inputValue = await popupManager.launchInputPopup(result.title || "Input", result.message, result.placeholder, result.defaultValue);
371
+ if (inputValue !== null) {
372
+ const nextResult = await result.onSubmit(inputValue);
373
+ // Recursively handle nested results
374
+ if (nextResult) {
375
+ await handleActionResult(nextResult);
376
+ }
377
+ }
378
+ }
379
+ else if (result.type === "navigation") {
380
+ // Navigate to target pane if specified
381
+ if (result.targetPaneId) {
382
+ const targetPane = panes.find(p => p.id === result.targetPaneId);
383
+ if (targetPane) {
471
384
  try {
472
- // Capture the pane content
473
- const paneContent = capturePaneContent(paneInfo, 30);
474
- if (i % 10 === 0) { // Log every 10 checks (every second)
475
- }
476
- // Check if content has stabilized (same for 3 checks = prompt is waiting)
477
- if (paneContent === lastContent) {
478
- stableContentCount++;
479
- }
480
- else {
481
- stableContentCount = 0;
482
- lastContent = paneContent;
483
- }
484
- // Look for trust prompt in the current content
485
- const hasTrustPrompt = trustPromptPatterns.some(pattern => pattern.test(paneContent));
486
- // Also check if we see specific Claude permission text
487
- const hasClaudePermissionPrompt = paneContent.includes('Do you trust') ||
488
- paneContent.includes('trust the files') ||
489
- paneContent.includes('permission') ||
490
- paneContent.includes('allow') ||
491
- (paneContent.includes('folder') && paneContent.includes('?'));
492
- if ((hasTrustPrompt || hasClaudePermissionPrompt) && !promptHandled) {
493
- // Content is stable and we found a prompt
494
- if (stableContentCount >= 2) {
495
- // Check if this is the new Claude numbered menu format
496
- const isNewClaudeFormat = /❯\s*1\.\s*Yes,\s*proceed/i.test(paneContent) ||
497
- /Enter to confirm.*Esc to exit/i.test(paneContent);
498
- if (isNewClaudeFormat) {
499
- // For new Claude format, just press Enter to confirm default "Yes, proceed"
500
- execSync(`tmux send-keys -t '${paneInfo}' Enter`, { stdio: 'pipe' });
501
- }
502
- else {
503
- // Try multiple response methods for older formats
504
- // Method 1: Send 'y' followed by Enter (most explicit)
505
- execSync(`tmux send-keys -t '${paneInfo}' 'y'`, { stdio: 'pipe' });
506
- await new Promise(resolve => setTimeout(resolve, 50));
507
- execSync(`tmux send-keys -t '${paneInfo}' Enter`, { stdio: 'pipe' });
508
- // Method 2: Just Enter (if it's a yes/no with default yes)
509
- await new Promise(resolve => setTimeout(resolve, 100));
510
- execSync(`tmux send-keys -t '${paneInfo}' Enter`, { stdio: 'pipe' });
511
- }
512
- // Mark as handled to avoid duplicate responses
513
- promptHandled = true;
514
- // Wait and check if prompt is gone
515
- await new Promise(resolve => setTimeout(resolve, 500));
516
- // Verify the prompt is gone
517
- const updatedContent = capturePaneContent(paneInfo, 10);
518
- // If trust prompt is gone, check if we need to resend the Claude command
519
- const promptGone = !trustPromptPatterns.some(p => p.test(updatedContent));
520
- if (promptGone) {
521
- // Check if Claude is running or if we need to restart it
522
- const claudeRunning = updatedContent.includes('Claude') ||
523
- updatedContent.includes('claude') ||
524
- updatedContent.includes('Assistant') ||
525
- (prompt && updatedContent.includes(prompt.substring(0, Math.min(20, prompt.length))));
526
- if (!claudeRunning && !updatedContent.includes('$')) {
527
- await new Promise(resolve => setTimeout(resolve, 300));
528
- execSync(`tmux send-keys -t '${paneInfo}' '${escapedCmd}'`, { stdio: 'pipe' });
529
- execSync(`tmux send-keys -t '${paneInfo}' Enter`, { stdio: 'pipe' });
530
- }
531
- break;
532
- }
533
- }
534
- }
535
- // If we see Claude is already running without prompts, we're done
536
- if (!hasTrustPrompt && !hasClaudePermissionPrompt &&
537
- (paneContent.includes('Claude') || paneContent.includes('Assistant'))) {
538
- break;
539
- }
385
+ TmuxService.getInstance().selectPane(targetPane.paneId);
540
386
  }
541
387
  catch (error) {
542
- // Continue checking, errors are non-fatal
388
+ console.error('[onActionResult] Failed to navigate to pane:', error);
543
389
  }
544
390
  }
545
- };
546
- // Start monitoring for trust prompt in background
547
- autoApproveTrust().catch(err => {
548
- });
549
- }
550
- // Keep focus on the new pane
551
- execSync(`tmux select-pane -t '${paneInfo}'`, { stdio: 'pipe' });
552
- // Save pane info
553
- const newPane = {
554
- id: `dmux-${Date.now()}`,
555
- slug,
556
- prompt: prompt || 'No initial prompt',
557
- paneId: paneInfo,
558
- worktreePath,
559
- agent
560
- };
561
- const updatedPanes = [...panes, newPane];
562
- await savePanes(updatedPanes);
563
- // Switch back to the original pane (where dmux is running)
564
- execSync(`tmux select-pane -t '${originalPaneId}'`, { stdio: 'pipe' });
565
- // Re-set the title for the dmux pane
566
- try {
567
- execSync(`tmux select-pane -t '${originalPaneId}' -T "dmux-${projectName}"`, { stdio: 'pipe' });
568
- }
569
- catch {
570
- // Ignore if setting title fails
571
- }
572
- // Clear the screen and redraw the UI
573
- process.stdout.write('\x1b[2J\x1b[H');
574
- // Reset the creating pane flag and refresh
575
- setIsCreatingPane(false);
576
- setStatusMessage('');
577
- setNewPanePrompt('');
578
- // Force a reload of panes to ensure UI is up to date
579
- await loadPanes();
580
- };
581
- const jumpToPane = (paneId) => {
582
- try {
583
- // Enable pane borders to show titles (if not already enabled)
584
- try {
585
- execSync(`tmux set-option -g pane-border-status top`, { stdio: 'pipe' });
586
391
  }
587
- catch {
588
- // Ignore if already set or fails
392
+ // Show message if dismissable
393
+ if (result.message && result.dismissable) {
394
+ await popupManager.launchProgressPopup(result.message, "info", 3000);
589
395
  }
590
- execSync(`tmux select-pane -t '${paneId}'`, { stdio: 'pipe' });
591
- // Clear screen after jump to remove artifacts
592
- clearScreen();
593
- setStatusMessage('Jumped to pane');
594
- setTimeout(() => setStatusMessage(''), 2000);
595
396
  }
596
- catch {
597
- setStatusMessage('Failed to jump - pane may be closed');
598
- setTimeout(() => setStatusMessage(''), 2000);
397
+ else if (result.type === "info" ||
398
+ result.type === "success" ||
399
+ result.type === "error") {
400
+ await popupManager.launchProgressPopup(result.message, result.type, 3000);
599
401
  }
600
402
  };
601
- const runCommand = async (type, pane) => {
602
- if (!pane.worktreePath) {
603
- setStatusMessage('No worktree path for this pane');
604
- setTimeout(() => setStatusMessage(''), 2000);
605
- return;
606
- }
607
- const command = type === 'test' ? projectSettings.testCommand : projectSettings.devCommand;
608
- const isFirstRun = type === 'test' ? !projectSettings.firstTestRun : !projectSettings.firstDevRun;
609
- if (!command) {
610
- // No command configured, prompt user
611
- setShowCommandPrompt(type);
612
- return;
613
- }
614
- // Check if this is the first run and offer to copy non-git files
615
- if (isFirstRun) {
616
- // Show file copy prompt and wait for response
617
- setShowFileCopyPrompt(true);
618
- setCurrentCommandType(type);
619
- setStatusMessage(`First time running ${type} command...`);
620
- // Return here - the actual command will be run after user responds to prompt
621
- return;
622
- }
623
- try {
624
- setRunningCommand(true);
625
- setStatusMessage(`Starting ${type} in background window...`);
626
- // Kill existing window if present
627
- const existingWindowId = type === 'test' ? pane.testWindowId : pane.devWindowId;
628
- if (existingWindowId) {
629
- try {
630
- execSync(`tmux kill-window -t '${existingWindowId}'`, { stdio: 'pipe' });
631
- }
632
- catch { }
633
- }
634
- // Create a new background window for the command
635
- const windowName = `${pane.slug}-${type}`;
636
- const windowId = execSync(`tmux new-window -d -n '${windowName}' -P -F '#{window_id}'`, { encoding: 'utf-8', stdio: 'pipe' }).trim();
637
- // Create a log file to capture output
638
- const logFile = `/tmp/dmux-${pane.id}-${type}.log`;
639
- // Build the command with output capture
640
- const fullCommand = type === 'test'
641
- ? `cd "${pane.worktreePath}" && ${command} 2>&1 | tee ${logFile}`
642
- : `cd "${pane.worktreePath}" && ${command} 2>&1 | tee ${logFile}`;
643
- // Send the command to the new window
644
- execSync(`tmux send-keys -t '${windowId}' '${fullCommand.replace(/'/g, "'\\''")}'`, { stdio: 'pipe' });
645
- execSync(`tmux send-keys -t '${windowId}' Enter`, { stdio: 'pipe' });
646
- // Update pane with window info
647
- const updatedPane = {
648
- ...pane,
649
- [type === 'test' ? 'testWindowId' : 'devWindowId']: windowId,
650
- [type === 'test' ? 'testStatus' : 'devStatus']: 'running'
651
- };
652
- const updatedPanes = panes.map(p => p.id === pane.id ? updatedPane : p);
653
- await savePanes(updatedPanes);
654
- // Start monitoring the output
655
- if (type === 'test') {
656
- // For tests, monitor for completion
657
- setTimeout(() => monitorTestOutput(pane.id, logFile), 2000);
658
- }
659
- else {
660
- // For dev, monitor for server URL
661
- setTimeout(() => monitorDevOutput(pane.id, logFile), 2000);
403
+ // Action system - initialized after services are defined
404
+ const actionSystem = useActionSystem({
405
+ panes,
406
+ savePanes,
407
+ sessionName,
408
+ projectName,
409
+ onPaneRemove: async (paneId) => {
410
+ // Mark pane as closing to prevent race condition with worker
411
+ await lifecycleManager.beginClose(paneId, 'user requested');
412
+ // Remove from panes list
413
+ const updatedPanes = panes.filter((p) => p.paneId !== paneId);
414
+ savePanes(updatedPanes);
415
+ // Mark close as completed (no more lock needed)
416
+ await lifecycleManager.completeClose(paneId);
417
+ },
418
+ onActionResult: handleActionResult,
419
+ forceRepaint,
420
+ popupLaunchers: popupsSupported
421
+ ? {
422
+ launchConfirmPopup: popupManager.launchConfirmPopup.bind(popupManager),
423
+ launchChoicePopup: popupManager.launchChoicePopup.bind(popupManager),
424
+ launchInputPopup: popupManager.launchInputPopup.bind(popupManager),
425
+ launchProgressPopup: popupManager.launchProgressPopup.bind(popupManager),
426
+ }
427
+ : undefined,
428
+ });
429
+ // Auto-show new pane dialog removed - users can press 'n' to create panes via popup
430
+ // Periodic enforcement of control pane size and content pane rebalancing (left sidebar at 40 chars)
431
+ useLayoutManagement({
432
+ controlPaneId,
433
+ hasActiveDialog: actionSystem.actionState.showConfirmDialog ||
434
+ actionSystem.actionState.showChoiceDialog ||
435
+ actionSystem.actionState.showInputDialog ||
436
+ actionSystem.actionState.showProgressDialog ||
437
+ !!showCommandPrompt ||
438
+ showFileCopyPrompt ||
439
+ isCreatingPane ||
440
+ runningCommand ||
441
+ isUpdating,
442
+ onForceRepaint: forceRepaint,
443
+ });
444
+ // Monitor agent status across panes (returns a map of pane ID to status)
445
+ const agentStatuses = useAgentStatus({
446
+ panes,
447
+ suspend: actionSystem.actionState.showConfirmDialog ||
448
+ actionSystem.actionState.showChoiceDialog ||
449
+ actionSystem.actionState.showInputDialog ||
450
+ actionSystem.actionState.showProgressDialog ||
451
+ !!showCommandPrompt ||
452
+ showFileCopyPrompt,
453
+ onPaneRemoved: (paneId) => {
454
+ // Check if this pane is being closed intentionally or is locked
455
+ // If so, don't re-save - the close action already handled it
456
+ if (lifecycleManager.isClosing(paneId) || lifecycleManager.isLocked(paneId)) {
457
+ return;
662
458
  }
663
- setRunningCommand(false);
664
- setStatusMessage(`${type === 'test' ? 'Test' : 'Dev server'} started in background`);
665
- setTimeout(() => setStatusMessage(''), 3000);
666
- }
667
- catch (error) {
668
- setRunningCommand(false);
669
- setStatusMessage(`Failed to run ${type} command`);
670
- setTimeout(() => setStatusMessage(''), 3000);
671
- }
672
- };
459
+ // Pane was removed unexpectedly (e.g., user killed tmux pane manually)
460
+ // Remove it from our tracking
461
+ const updatedPanes = panes.filter((p) => p.id !== paneId);
462
+ savePanes(updatedPanes);
463
+ },
464
+ });
465
+ // jumpToPane and runCommand functions removed - now handled by action system and pane runner
673
466
  // Update handling moved to useAutoUpdater
674
- // Helper function to clear screen artifacts
675
- const clearScreen = () => {
676
- // CRITICAL: Force Ink to re-render FIRST, before clearing
677
- // This prevents blank screen by ensuring React starts rendering immediately
678
- setForceRepaintTrigger(prev => prev + 1);
679
- // Multiple clearing strategies to prevent artifacts
680
- // 1. Clear screen with ANSI codes
681
- process.stdout.write('\x1b[2J\x1b[H');
682
- // 2. Clear tmux history
683
- try {
684
- execSync('tmux clear-history', { stdio: 'pipe' });
685
- }
686
- catch { }
687
- // 3. Force tmux to refresh the display
688
- try {
689
- execSync('tmux refresh-client', { stdio: 'pipe' });
690
- }
691
- catch { }
692
- };
467
+ // clearScreen function removed - no longer used (was only used by removed jumpToPane function)
693
468
  // Cleanup function for exit
694
469
  const cleanExit = () => {
695
470
  // Clear screen before exiting Ink
696
- process.stdout.write('\x1b[2J\x1b[3J\x1b[H');
471
+ process.stdout.write("\x1b[2J\x1b[3J\x1b[H");
697
472
  // Exit the Ink app (this cleans up the React tree)
698
473
  exit();
699
474
  // Give Ink a moment to clean up its rendering, then do final cleanup
700
- setTimeout(() => {
475
+ setTimeout(async () => {
701
476
  // Multiple aggressive clearing strategies
702
- process.stdout.write('\x1b[2J\x1b[H'); // Clear screen and move cursor to home
703
- process.stdout.write('\x1b[3J'); // Clear scrollback buffer
704
- process.stdout.write('\x1b[0m'); // Reset all attributes
477
+ process.stdout.write("\x1b[2J\x1b[H"); // Clear screen and move cursor to home
478
+ process.stdout.write("\x1b[3J"); // Clear scrollback buffer
479
+ process.stdout.write("\x1b[0m"); // Reset all attributes
705
480
  // Clear tmux history and pane
706
481
  try {
707
- execSync('tmux clear-history', { stdio: 'pipe' });
708
- execSync('tmux send-keys C-l', { stdio: 'pipe' });
482
+ const tmuxService = TmuxService.getInstance();
483
+ tmuxService.clearHistorySync();
484
+ await tmuxService.sendKeys("", "C-l");
709
485
  }
710
486
  catch { }
711
487
  // One more final clear
712
- process.stdout.write('\x1b[2J\x1b[H');
488
+ process.stdout.write("\x1b[2J\x1b[H");
713
489
  // Show clean goodbye message
714
- process.stdout.write('\n Run dmux again to resume. Goodbye 👋\n\n');
490
+ process.stdout.write("\n Run dmux again to resume. Goodbye 👋\n\n");
715
491
  // Exit process
716
492
  process.exit(0);
717
493
  }, 100);
718
494
  };
719
- useInput(async (input, key) => {
720
- // Handle Ctrl+C for quit confirmation (must be first, before any other checks)
721
- if (key.ctrl && input === 'c') {
722
- if (quitConfirmMode) {
723
- // Second Ctrl+C - actually quit
724
- cleanExit();
725
- }
726
- else {
727
- // First Ctrl+C - show confirmation
728
- setQuitConfirmMode(true);
729
- // Reset after 3 seconds if user doesn't press Ctrl+C again
730
- setTimeout(() => {
731
- setQuitConfirmMode(false);
732
- }, 3000);
733
- }
734
- return;
735
- }
736
- if (isCreatingPane || runningCommand || isUpdating || isLoading || isCreatingTunnel) {
737
- // Disable input while performing operations or loading
738
- return;
739
- }
740
- // Handle kebab menu navigation
741
- if (showKebabMenu && kebabMenuPaneIndex !== null) {
742
- const currentPane = panes[kebabMenuPaneIndex];
743
- const availableActions = kebabMenuActions;
744
- if (key.escape) {
745
- setShowKebabMenu(false);
746
- setKebabMenuPaneIndex(null);
747
- setKebabMenuOption(0);
748
- setKebabMenuActions([]);
749
- return;
750
- }
751
- else if (key.upArrow) {
752
- setKebabMenuOption(Math.max(0, kebabMenuOption - 1));
753
- return;
754
- }
755
- else if (key.downArrow) {
756
- setKebabMenuOption(Math.min(availableActions.length - 1, kebabMenuOption + 1));
757
- return;
758
- }
759
- else if (key.return) {
760
- // Execute the selected menu action
761
- setShowKebabMenu(false);
762
- const selectedAction = availableActions[kebabMenuOption];
763
- if (selectedAction) {
764
- // Use the action system to execute
765
- actionSystem.executeAction(selectedAction.id, currentPane, { mainBranch: getMainBranch() });
766
- }
767
- setKebabMenuPaneIndex(null);
768
- setKebabMenuOption(0);
769
- setKebabMenuActions([]);
770
- return;
771
- }
772
- // Don't process other inputs while menu is open
773
- return;
774
- }
775
- // Handle quit confirm mode - ESC cancels it
776
- if (quitConfirmMode) {
777
- if (key.escape) {
778
- setQuitConfirmMode(false);
779
- return;
780
- }
781
- // Allow other inputs to continue (don't return early)
782
- }
783
- // Handle QR code view
784
- if (showQRCode) {
785
- if (key.escape) {
786
- setShowQRCode(false);
787
- }
788
- return;
789
- }
790
- // Handle hooks dialog
791
- if (showHooksDialog) {
792
- if (key.escape) {
793
- setShowHooksDialog(false);
794
- setHooksSelectedIndex(0);
795
- // Go back to settings dialog
796
- setShowSettingsDialog(true);
797
- setSettingsMode('list');
798
- return;
799
- }
800
- else if (key.upArrow) {
801
- setHooksSelectedIndex(Math.max(0, hooksSelectedIndex - 1));
802
- return;
803
- }
804
- else if (key.downArrow) {
805
- setHooksSelectedIndex(Math.min(hooksData.length - 1, hooksSelectedIndex + 1));
806
- return;
807
- }
808
- else if (input === 'e') {
809
- // Edit hooks using an agent
810
- setShowHooksDialog(false);
811
- setHooksSelectedIndex(0);
812
- const prompt = "I would like to edit my dmux hooks in .dmux-hooks, please read the instructions in there and ask me what I want to edit";
813
- setPendingPrompt(prompt);
814
- setNewPanePrompt(prompt);
815
- // Choose agent
816
- const agents = availableAgents;
817
- if (agents.length === 0) {
818
- createNewPaneHook(prompt);
819
- }
820
- else if (agents.length === 1) {
821
- createNewPaneHook(prompt, agents[0]);
822
- }
823
- else {
824
- setShowAgentChoiceDialog(true);
825
- setAgentChoice(agentChoice || 'claude');
826
- }
827
- return;
828
- }
829
- return;
830
- }
831
- // Handle settings dialog
832
- if (showSettingsDialog) {
833
- if (key.escape) {
834
- if (settingsMode === 'list') {
835
- // Close settings dialog
836
- setShowSettingsDialog(false);
837
- setSettingsMode('list');
838
- setSettingsSelectedIndex(0);
839
- setSettingsEditingKey(undefined);
840
- }
841
- else {
842
- // Go back to list
843
- setSettingsMode('list');
844
- setSettingsEditingKey(undefined);
845
- setSettingsEditingValueIndex(0);
846
- setSettingsScopeIndex(0);
847
- }
848
- return;
849
- }
850
- else if (key.upArrow) {
851
- if (settingsMode === 'list') {
852
- setSettingsSelectedIndex(Math.max(0, settingsSelectedIndex - 1));
853
- }
854
- else if (settingsMode === 'edit') {
855
- const currentDef = SETTING_DEFINITIONS[settingsSelectedIndex];
856
- const maxIndex = currentDef.type === 'boolean' ? 1 : (currentDef.options?.length || 1) - 1;
857
- setSettingsEditingValueIndex(Math.max(0, settingsEditingValueIndex - 1));
858
- }
859
- else if (settingsMode === 'scope') {
860
- setSettingsScopeIndex(Math.max(0, settingsScopeIndex - 1));
861
- }
862
- return;
863
- }
864
- else if (key.downArrow) {
865
- if (settingsMode === 'list') {
866
- setSettingsSelectedIndex(Math.min(SETTING_DEFINITIONS.length - 1, settingsSelectedIndex + 1));
867
- }
868
- else if (settingsMode === 'edit') {
869
- const currentDef = SETTING_DEFINITIONS[settingsSelectedIndex];
870
- const maxIndex = currentDef.type === 'boolean' ? 1 : (currentDef.options?.length || 1) - 1;
871
- setSettingsEditingValueIndex(Math.min(maxIndex, settingsEditingValueIndex + 1));
872
- }
873
- else if (settingsMode === 'scope') {
874
- setSettingsScopeIndex(Math.min(1, settingsScopeIndex + 1));
875
- }
876
- return;
877
- }
878
- else if (key.return) {
879
- if (settingsMode === 'list') {
880
- const currentDef = SETTING_DEFINITIONS[settingsSelectedIndex];
881
- // Handle action type - trigger the action instead of editing
882
- if (currentDef.type === 'action') {
883
- if (currentDef.key === 'hooks') {
884
- // Show hooks dialog
885
- setShowSettingsDialog(false);
886
- const { hasHook } = await import('./utils/hooks.js');
887
- const allHookTypes = [
888
- 'before_pane_create',
889
- 'pane_created',
890
- 'worktree_created',
891
- 'before_pane_close',
892
- 'pane_closed',
893
- 'before_worktree_remove',
894
- 'worktree_removed',
895
- 'pre_merge',
896
- 'post_merge',
897
- 'run_test',
898
- 'run_dev',
899
- ];
900
- const hooks = allHookTypes.map(hookName => ({
901
- name: hookName,
902
- active: hasHook(projectRoot || process.cwd(), hookName)
903
- }));
904
- setHooksData(hooks);
905
- setShowHooksDialog(true);
906
- setHooksSelectedIndex(0);
907
- }
908
- return;
909
- }
910
- // Enter edit mode for regular settings
911
- setSettingsEditingKey(currentDef.key);
912
- setSettingsMode('edit');
913
- // Set initial value index based on current setting
914
- const currentValue = settingsManager.getSetting(currentDef.key);
915
- if (currentDef.type === 'boolean') {
916
- setSettingsEditingValueIndex(currentValue ? 0 : 1);
917
- }
918
- else if (currentDef.type === 'select' && currentDef.options) {
919
- const optIndex = currentDef.options.findIndex(o => o.value === currentValue);
920
- setSettingsEditingValueIndex(Math.max(0, optIndex));
921
- }
922
- }
923
- else if (settingsMode === 'edit') {
924
- // Go to scope selection
925
- setSettingsMode('scope');
926
- setSettingsScopeIndex(0);
927
- }
928
- else if (settingsMode === 'scope') {
929
- // Save the setting
930
- const currentDef = SETTING_DEFINITIONS[settingsSelectedIndex];
931
- const scope = settingsScopeIndex === 0 ? 'global' : 'project';
932
- // Only save if this is not an action type (actions don't have values)
933
- if (currentDef.type !== 'action') {
934
- // Calculate the new value
935
- let newValue;
936
- if (currentDef.type === 'boolean') {
937
- newValue = settingsEditingValueIndex === 0;
938
- }
939
- else if (currentDef.type === 'select' && currentDef.options) {
940
- newValue = currentDef.options[settingsEditingValueIndex]?.value || '';
941
- }
942
- // Update the setting - cast key to proper type since we know it's not an action
943
- settingsManager.updateSetting(currentDef.key, newValue, scope);
944
- }
945
- // Reset to list view
946
- setSettingsMode('list');
947
- setSettingsEditingKey(undefined);
948
- setSettingsEditingValueIndex(0);
949
- setSettingsScopeIndex(0);
950
- setStatusMessage(`Setting saved (${scope})`);
951
- setTimeout(() => setStatusMessage(''), 2000);
952
- }
953
- return;
954
- }
955
- return;
956
- }
957
- // Handle action system confirm dialog
958
- if (actionSystem.actionState.showConfirmDialog) {
959
- if (key.escape) {
960
- if (actionSystem.actionState.onConfirmNo) {
961
- actionSystem.executeCallback(actionSystem.actionState.onConfirmNo);
962
- }
963
- else {
964
- actionSystem.setActionState(prev => ({ ...prev, showConfirmDialog: false }));
965
- }
966
- return;
967
- }
968
- else if (key.upArrow) {
969
- actionSystem.setActionState(prev => ({
970
- ...prev,
971
- confirmSelectedIndex: Math.max(0, prev.confirmSelectedIndex - 1)
972
- }));
973
- return;
974
- }
975
- else if (key.downArrow) {
976
- actionSystem.setActionState(prev => ({
977
- ...prev,
978
- confirmSelectedIndex: Math.min(1, prev.confirmSelectedIndex + 1)
979
- }));
980
- return;
981
- }
982
- else if (key.return) {
983
- // Execute based on selected index
984
- if (actionSystem.actionState.confirmSelectedIndex === 0) {
985
- if (actionSystem.actionState.onConfirmYes) {
986
- actionSystem.executeCallback(actionSystem.actionState.onConfirmYes);
987
- }
988
- }
989
- else {
990
- if (actionSystem.actionState.onConfirmNo) {
991
- actionSystem.executeCallback(actionSystem.actionState.onConfirmNo);
992
- }
993
- else {
994
- actionSystem.setActionState(prev => ({ ...prev, showConfirmDialog: false }));
995
- }
996
- }
997
- return;
998
- }
999
- else if (input === 'y' || input === 'Y') {
1000
- // Shortcut: yes
1001
- if (actionSystem.actionState.onConfirmYes) {
1002
- actionSystem.executeCallback(actionSystem.actionState.onConfirmYes);
1003
- }
1004
- return;
1005
- }
1006
- else if (input === 'n' || input === 'N') {
1007
- // Shortcut: no
1008
- if (actionSystem.actionState.onConfirmNo) {
1009
- actionSystem.executeCallback(actionSystem.actionState.onConfirmNo);
1010
- }
1011
- else {
1012
- actionSystem.setActionState(prev => ({ ...prev, showConfirmDialog: false }));
1013
- }
1014
- return;
1015
- }
1016
- return;
1017
- }
1018
- // Handle action system input dialog
1019
- if (actionSystem.actionState.showInputDialog) {
1020
- if (key.escape) {
1021
- actionSystem.setActionState(prev => ({ ...prev, showInputDialog: false }));
1022
- return;
1023
- }
1024
- else if (key.return) {
1025
- if (actionSystem.actionState.onInputSubmit) {
1026
- actionSystem.executeCallback(async () => actionSystem.actionState.onInputSubmit(actionSystem.actionState.inputValue));
1027
- }
1028
- return;
1029
- }
1030
- // Let CleanTextInput handle all other key events
1031
- return;
1032
- }
1033
- // Handle action system choice dialog
1034
- if (actionSystem.actionState.showChoiceDialog) {
1035
- if (key.escape) {
1036
- actionSystem.setActionState(prev => ({ ...prev, showChoiceDialog: false }));
1037
- return;
1038
- }
1039
- else if (key.upArrow) {
1040
- actionSystem.setActionState(prev => ({
1041
- ...prev,
1042
- choiceSelectedIndex: Math.max(0, prev.choiceSelectedIndex - 1)
1043
- }));
1044
- return;
1045
- }
1046
- else if (key.downArrow) {
1047
- const maxIndex = actionSystem.actionState.choiceOptions.length - 1;
1048
- actionSystem.setActionState(prev => ({
1049
- ...prev,
1050
- choiceSelectedIndex: Math.min(maxIndex, prev.choiceSelectedIndex + 1)
1051
- }));
1052
- return;
1053
- }
1054
- else if (key.return) {
1055
- const selectedOption = actionSystem.actionState.choiceOptions[actionSystem.actionState.choiceSelectedIndex];
1056
- if (selectedOption && actionSystem.actionState.onChoiceSelect) {
1057
- actionSystem.executeCallback(async () => actionSystem.actionState.onChoiceSelect(selectedOption.id));
1058
- }
1059
- return;
1060
- }
1061
- return;
1062
- }
1063
- if (showFileCopyPrompt) {
1064
- if (input === 'y' || input === 'Y') {
1065
- setShowFileCopyPrompt(false);
1066
- const selectedPane = panes[selectedIndex];
1067
- if (selectedPane && selectedPane.worktreePath && currentCommandType) {
1068
- await copyNonGitFiles(selectedPane.worktreePath);
1069
- // Mark as not first run and continue with command
1070
- const newSettings = {
1071
- ...projectSettings,
1072
- [currentCommandType === 'test' ? 'firstTestRun' : 'firstDevRun']: true
1073
- };
1074
- await saveSettings(newSettings);
1075
- // Now run the actual command
1076
- await runCommandInternal(currentCommandType, selectedPane);
1077
- }
1078
- setCurrentCommandType(null);
1079
- }
1080
- else if (input === 'n' || input === 'N' || key.escape) {
1081
- setShowFileCopyPrompt(false);
1082
- const selectedPane = panes[selectedIndex];
1083
- if (selectedPane && currentCommandType) {
1084
- // Mark as not first run and continue without copying
1085
- const newSettings = {
1086
- ...projectSettings,
1087
- [currentCommandType === 'test' ? 'firstTestRun' : 'firstDevRun']: true
1088
- };
1089
- await saveSettings(newSettings);
1090
- // Now run the actual command
1091
- await runCommandInternal(currentCommandType, selectedPane);
1092
- }
1093
- setCurrentCommandType(null);
1094
- }
1095
- return;
1096
- }
1097
- if (showAgentChoiceDialog) {
1098
- if (key.escape) {
1099
- setShowAgentChoiceDialog(false);
1100
- setShowNewPaneDialog(true);
1101
- setNewPanePrompt(pendingPrompt);
1102
- setPendingPrompt('');
1103
- }
1104
- else if (key.leftArrow || input === '1' || (input && input.toLowerCase() === 'c')) {
1105
- setAgentChoice('claude');
1106
- }
1107
- else if (key.rightArrow || input === '2' || (input && input.toLowerCase() === 'o')) {
1108
- setAgentChoice('opencode');
1109
- }
1110
- else if (key.return) {
1111
- const chosen = agentChoice || (availableAgents[0] || 'claude');
1112
- const promptValue = pendingPrompt;
1113
- setShowAgentChoiceDialog(false);
1114
- setPendingPrompt('');
1115
- await createNewPaneHook(promptValue, chosen);
1116
- setNewPanePrompt('');
1117
- }
1118
- return;
1119
- }
1120
- if (showCommandPrompt) {
1121
- if (key.escape) {
1122
- setShowCommandPrompt(null);
1123
- setCommandInput('');
1124
- }
1125
- else if (key.return) {
1126
- if (commandInput.trim() === '') {
1127
- // If empty, suggest a default command based on package manager
1128
- const suggested = await suggestCommand(showCommandPrompt);
1129
- if (suggested) {
1130
- setCommandInput(suggested);
1131
- }
1132
- }
1133
- else {
1134
- // User provided manual command
1135
- const newSettings = {
1136
- ...projectSettings,
1137
- [showCommandPrompt === 'test' ? 'testCommand' : 'devCommand']: commandInput.trim()
1138
- };
1139
- await saveSettings(newSettings);
1140
- const selectedPane = panes[selectedIndex];
1141
- if (selectedPane) {
1142
- // Check if first run
1143
- const isFirstRun = showCommandPrompt === 'test' ? !projectSettings.firstTestRun : !projectSettings.firstDevRun;
1144
- if (isFirstRun) {
1145
- setCurrentCommandType(showCommandPrompt);
1146
- setShowCommandPrompt(null);
1147
- setShowFileCopyPrompt(true);
1148
- }
1149
- else {
1150
- await runCommandInternal(showCommandPrompt, selectedPane);
1151
- setShowCommandPrompt(null);
1152
- setCommandInput('');
1153
- }
1154
- }
1155
- else {
1156
- setShowCommandPrompt(null);
1157
- setCommandInput('');
1158
- }
1159
- }
1160
- }
1161
- return;
1162
- }
1163
- if (showNewPaneDialog) {
1164
- if (key.escape) {
1165
- setShowNewPaneDialog(false);
1166
- setNewPanePrompt('');
1167
- }
1168
- // TextInput handles other input events
1169
- return;
1170
- }
1171
- // Handle directional navigation with spatial awareness based on card grid layout
1172
- if (key.upArrow || key.downArrow || key.leftArrow || key.rightArrow) {
1173
- let targetIndex = null;
1174
- if (key.upArrow) {
1175
- targetIndex = findCardInDirection(selectedIndex, 'up');
1176
- }
1177
- else if (key.downArrow) {
1178
- targetIndex = findCardInDirection(selectedIndex, 'down');
1179
- }
1180
- else if (key.leftArrow) {
1181
- targetIndex = findCardInDirection(selectedIndex, 'left');
1182
- }
1183
- else if (key.rightArrow) {
1184
- targetIndex = findCardInDirection(selectedIndex, 'right');
1185
- }
1186
- if (targetIndex !== null) {
1187
- setSelectedIndex(targetIndex);
1188
- }
1189
- return;
1190
- }
1191
- if ((input === 'm' || key.return) && selectedIndex < panes.length) {
1192
- // Open kebab menu for selected pane
1193
- const selectedPane = panes[selectedIndex];
1194
- const actions = getAvailableActions(selectedPane, projectSettings);
1195
- setKebabMenuActions(actions);
1196
- setShowKebabMenu(true);
1197
- setKebabMenuPaneIndex(selectedIndex);
1198
- setKebabMenuOption(0);
1199
- }
1200
- else if (input === 's') {
1201
- // Open settings dialog
1202
- setShowSettingsDialog(true);
1203
- setSettingsMode('list');
1204
- setSettingsSelectedIndex(0);
1205
- }
1206
- else if (input === 'q') {
1207
- cleanExit();
1208
- }
1209
- else if (input === 'r' && server) {
1210
- // Create tunnel if not already created, then show QR code
1211
- if (!tunnelUrl) {
1212
- setIsCreatingTunnel(true);
1213
- setStatusMessage('Creating tunnel...');
1214
- try {
1215
- const url = await server.startTunnel();
1216
- setTunnelUrl(url);
1217
- setStatusMessage('');
1218
- setShowQRCode(true);
1219
- }
1220
- catch (error) {
1221
- setStatusMessage('Failed to create tunnel');
1222
- setTimeout(() => setStatusMessage(''), 3000);
1223
- }
1224
- finally {
1225
- setIsCreatingTunnel(false);
1226
- }
1227
- }
1228
- else {
1229
- // Tunnel already exists, just show the QR code
1230
- setShowQRCode(true);
1231
- }
495
+ // Input handling - extracted to dedicated hook
496
+ useInputHandling({
497
+ panes,
498
+ selectedIndex,
499
+ setSelectedIndex,
500
+ isCreatingPane,
501
+ setIsCreatingPane,
502
+ runningCommand,
503
+ isUpdating,
504
+ isLoading,
505
+ ignoreInput,
506
+ quitConfirmMode,
507
+ setQuitConfirmMode,
508
+ showCommandPrompt,
509
+ setShowCommandPrompt,
510
+ commandInput,
511
+ setCommandInput,
512
+ showFileCopyPrompt,
513
+ setShowFileCopyPrompt,
514
+ currentCommandType,
515
+ setCurrentCommandType,
516
+ projectSettings,
517
+ saveSettings,
518
+ settingsManager,
519
+ tunnelUrl,
520
+ setTunnelUrl,
521
+ tunnelCreating,
522
+ setTunnelCreating,
523
+ setTunnelCopied,
524
+ popupManager,
525
+ actionSystem,
526
+ server,
527
+ controlPaneId,
528
+ setStatusMessage,
529
+ copyNonGitFiles,
530
+ runCommandInternal,
531
+ handlePaneCreationWithAgent,
532
+ loadPanes,
533
+ cleanExit,
534
+ findCardInDirection,
535
+ });
536
+ // Calculate available height for content (terminal height - footer lines - active status messages)
537
+ // Footer height varies based on state:
538
+ // - Quit confirm mode: 2 lines (marginTop + 1 text line)
539
+ // - Normal mode calculation:
540
+ // - Base: 4 lines (marginTop + logs divider + logs line + keyboard shortcuts)
541
+ // - Network section: +4 lines (divider, local IP, remote tunnel, divider) if serverPort exists
542
+ // - Debug info: +1 line if DEBUG_DMUX
543
+ // - Status line: +1 line if updateAvailable/currentBranch/debugMessage
544
+ // - Status messages: +1 line per active message
545
+ let footerLines = 2;
546
+ if (quitConfirmMode) {
547
+ footerLines = 2;
548
+ }
549
+ else {
550
+ // Base footer (logs divider + logs + shortcuts - always shown)
551
+ footerLines = 4; // marginTop + logs divider + logs + shortcuts
552
+ // Add network section (now 2 lines for local IP + remote tunnel, plus 2 dividers)
553
+ if (serverPort && serverPort > 0) {
554
+ footerLines += 4;
1232
555
  }
1233
- else if (!isLoading && (input === 'n' || (key.return && selectedIndex === panes.length))) {
1234
- // Clear the prompt and show dialog in next tick to prevent 'n' bleeding through
1235
- setNewPanePrompt('');
1236
- setShowNewPaneDialog(true);
1237
- return; // Consume the 'n' keystroke so it doesn't propagate
556
+ // Add debug info
557
+ if (process.env.DEBUG_DMUX) {
558
+ footerLines += 1;
1238
559
  }
1239
- else if (input === 'j' && selectedIndex < panes.length) {
1240
- // Jump to pane (NEW: using action system)
1241
- StateManager.getInstance().setDebugMessage(`Jumping to pane: ${panes[selectedIndex].slug}`);
1242
- setTimeout(() => StateManager.getInstance().setDebugMessage(''), 2000);
1243
- actionSystem.executeAction(PaneAction.VIEW, panes[selectedIndex]);
560
+ // Add status line
561
+ if (updateAvailable || currentBranch || debugMessage) {
562
+ footerLines += 1;
1244
563
  }
1245
- else if (input === 'x' && selectedIndex < panes.length) {
1246
- // Close pane (NEW: using action system)
1247
- StateManager.getInstance().setDebugMessage(`Closing pane: ${panes[selectedIndex].slug}`);
1248
- setTimeout(() => StateManager.getInstance().setDebugMessage(''), 2000);
1249
- actionSystem.executeAction(PaneAction.CLOSE, panes[selectedIndex]);
564
+ // Add line for each active status message
565
+ if (statusMessage) {
566
+ footerLines += 1;
1250
567
  }
1251
- else if (key.return && selectedIndex < panes.length) {
1252
- // Jump to pane (NEW: using action system)
1253
- StateManager.getInstance().setDebugMessage(`Jumping to pane: ${panes[selectedIndex].slug}`);
1254
- setTimeout(() => StateManager.getInstance().setDebugMessage(''), 2000);
1255
- actionSystem.executeAction(PaneAction.VIEW, panes[selectedIndex]);
568
+ if (actionSystem.actionState.statusMessage) {
569
+ footerLines += 1;
1256
570
  }
1257
- });
1258
- // If showing QR code, render only that
1259
- if (showQRCode && tunnelUrl) {
1260
- return (React.createElement(Box, { flexDirection: "column" },
1261
- React.createElement(Box, { marginBottom: 1 },
1262
- React.createElement(Text, { bold: true, color: "cyan" }, "dmux - Remote Access")),
1263
- React.createElement(QRCode, { url: tunnelUrl }),
1264
- React.createElement(Box, { marginTop: 1 },
1265
- React.createElement(Text, { dimColor: true }, "Press ESC to return to pane list"))));
1266
571
  }
1267
- return (React.createElement(Box, { flexDirection: "column" },
572
+ const contentHeight = Math.max(terminalHeight - footerLines, 10);
573
+ return (React.createElement(Box, { flexDirection: "column", height: terminalHeight },
1268
574
  showRepaintSpinner && (React.createElement(Box, { marginTop: -10, marginLeft: -100 },
1269
575
  React.createElement(Text, null, "\u27F3"))),
1270
- React.createElement(PanesGrid, { panes: panes, selectedIndex: selectedIndex, isLoading: isLoading, showNewPaneDialog: showNewPaneDialog, agentStatuses: agentStatuses, kebabMenuPaneIndex: kebabMenuPaneIndex ?? undefined }),
1271
- isLoading && (React.createElement(LoadingIndicator, null)),
1272
- showNewPaneDialog && !showAgentChoiceDialog && (React.createElement(NewPaneDialog, { value: newPanePrompt, onChange: setNewPanePrompt, onSubmit: (value) => {
1273
- const promptValue = value;
1274
- const agents = availableAgents;
1275
- if (agents.length === 0) {
1276
- setShowNewPaneDialog(false);
1277
- setNewPanePrompt('');
1278
- createNewPaneHook(promptValue);
1279
- }
1280
- else if (agents.length === 1) {
1281
- setShowNewPaneDialog(false);
1282
- setNewPanePrompt('');
1283
- createNewPaneHook(promptValue, agents[0]);
1284
- }
1285
- else {
1286
- setPendingPrompt(promptValue);
1287
- setShowNewPaneDialog(false);
1288
- setNewPanePrompt('');
1289
- setShowAgentChoiceDialog(true);
1290
- setAgentChoice(agentChoice || 'claude');
1291
- }
1292
- } })),
1293
- showAgentChoiceDialog && (React.createElement(AgentChoiceDialog, { agentChoice: agentChoice })),
1294
- isCreatingPane && (React.createElement(CreatingIndicator, { message: statusMessage })),
1295
- showCommandPrompt && (React.createElement(CommandPromptDialog, { type: showCommandPrompt, value: commandInput, onChange: setCommandInput })),
1296
- showFileCopyPrompt && (React.createElement(FileCopyPrompt, null)),
1297
- showKebabMenu && kebabMenuPaneIndex !== null && panes[kebabMenuPaneIndex] && (React.createElement(KebabMenu, { selectedOption: kebabMenuOption, actions: kebabMenuActions, paneName: panes[kebabMenuPaneIndex].slug })),
1298
- showSettingsDialog && (React.createElement(SettingsDialog, { settings: settingsManager.getSettings(), globalSettings: settingsManager.getGlobalSettings(), projectSettings: settingsManager.getProjectSettings(), settingDefinitions: SETTING_DEFINITIONS, selectedIndex: settingsSelectedIndex, mode: settingsMode, editingKey: settingsEditingKey, editingValueIndex: settingsEditingValueIndex, scopeIndex: settingsScopeIndex })),
1299
- showHooksDialog && (React.createElement(HooksDialog, { hooks: hooksData, selectedIndex: hooksSelectedIndex })),
1300
- actionSystem.actionState.showConfirmDialog && (React.createElement(ActionConfirmDialog, { key: "confirm-dialog", title: actionSystem.actionState.confirmTitle, message: actionSystem.actionState.confirmMessage, yesLabel: actionSystem.actionState.confirmYesLabel, noLabel: actionSystem.actionState.confirmNoLabel, selectedIndex: actionSystem.actionState.confirmSelectedIndex })),
1301
- actionSystem.actionState.showChoiceDialog && (React.createElement(ActionChoiceDialog, { key: "choice-dialog", title: actionSystem.actionState.choiceTitle, message: actionSystem.actionState.choiceMessage, options: actionSystem.actionState.choiceOptions, selectedIndex: actionSystem.actionState.choiceSelectedIndex })),
1302
- actionSystem.actionState.showInputDialog && (React.createElement(ActionInputDialog, { key: "input-dialog", title: actionSystem.actionState.inputTitle, message: actionSystem.actionState.inputMessage, placeholder: actionSystem.actionState.inputPlaceholder, value: actionSystem.actionState.inputValue, onValueChange: (value) => {
1303
- actionSystem.setActionState(prev => ({ ...prev, inputValue: value }));
1304
- } })),
1305
- actionSystem.actionState.showProgressDialog && (React.createElement(ActionProgressDialog, { key: "progress-dialog", message: actionSystem.actionState.progressMessage, percent: actionSystem.actionState.progressPercent })),
1306
- runningCommand && (React.createElement(RunningIndicator, null)),
1307
- isUpdating && (React.createElement(UpdatingIndicator, null)),
1308
- isCreatingTunnel && (React.createElement(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", padding: 1, marginTop: 1 },
1309
- React.createElement(Text, { bold: true, color: "cyan" }, "Creating tunnel..."),
1310
- React.createElement(Box, { marginTop: 1 },
1311
- React.createElement(Text, { dimColor: true }, "This may take a few moments...")))),
1312
- statusMessage && (React.createElement(Box, { marginTop: 1 },
576
+ React.createElement(Box, { flexDirection: "column", height: contentHeight, overflow: "hidden" },
577
+ React.createElement(PanesGrid, { panes: panes, selectedIndex: selectedIndex, isLoading: isLoading, agentStatuses: agentStatuses }),
578
+ isLoading && React.createElement(LoadingIndicator, null),
579
+ showCommandPrompt && (React.createElement(CommandPromptDialog, { type: showCommandPrompt, value: commandInput, onChange: setCommandInput })),
580
+ showFileCopyPrompt && React.createElement(FileCopyPrompt, null),
581
+ runningCommand && React.createElement(RunningIndicator, null),
582
+ isUpdating && React.createElement(UpdatingIndicator, null)),
583
+ statusMessage && (React.createElement(Box, null,
1313
584
  React.createElement(Text, { color: "green" }, statusMessage))),
1314
- actionSystem.actionState.statusMessage && (React.createElement(Box, { marginTop: 1 },
1315
- React.createElement(Text, { color: actionSystem.actionState.statusType === 'error' ? 'red' :
1316
- actionSystem.actionState.statusType === 'success' ? 'green' :
1317
- 'cyan' }, actionSystem.actionState.statusMessage))),
1318
- React.createElement(FooterHelp, { show: !showNewPaneDialog && !showCommandPrompt, showRemoteKey: !!server, quitConfirmMode: quitConfirmMode, gridInfo: (() => {
585
+ actionSystem.actionState.statusMessage && (React.createElement(Box, null,
586
+ React.createElement(Text, { color: actionSystem.actionState.statusType === "error"
587
+ ? "red"
588
+ : actionSystem.actionState.statusType === "success"
589
+ ? "green"
590
+ : "cyan" }, actionSystem.actionState.statusMessage))),
591
+ React.createElement(FooterHelp, { show: !showCommandPrompt, showRemoteKey: !!server, quitConfirmMode: quitConfirmMode, hasSidebarLayout: !!controlPaneId, serverPort: serverPort, unreadErrorCount: unreadErrorCount, unreadWarningCount: unreadWarningCount, localIp: localIp, tunnelUrl: tunnelUrl, tunnelCreating: tunnelCreating, tunnelCopied: tunnelCopied, tunnelSpinner: tunnelState.getSpinnerChar(), gridInfo: (() => {
1319
592
  if (!process.env.DEBUG_DMUX)
1320
593
  return undefined;
1321
594
  const cols = Math.max(1, Math.floor(terminalWidth / 37));
@@ -1323,16 +596,14 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
1323
596
  const pos = getCardGridPosition(selectedIndex);
1324
597
  return `Grid: ${cols} cols × ${rows} rows | Selected: row ${pos.row}, col ${pos.col} | Terminal: ${terminalWidth}w`;
1325
598
  })() }),
1326
- React.createElement(Text, { dimColor: true },
1327
- updateAvailable && updateInfo && (React.createElement(Text, { color: "red", bold: true }, "Update available: npm i -g dmux@latest ")),
1328
- "v",
1329
- packageJson.version,
1330
- serverPort && serverPort > 0 && (React.createElement(Text, { dimColor: true },
1331
- " \u2022 ",
1332
- React.createElement(Text, { color: "cyan" },
1333
- "http://127.0.0.1:",
1334
- serverPort))),
1335
- debugMessage && (React.createElement(Text, { dimColor: true },
599
+ (updateAvailable || currentBranch || debugMessage) && (React.createElement(Text, { dimColor: true },
600
+ updateAvailable && updateInfo && (React.createElement(Text, { color: "red", bold: true },
601
+ "Update available: npm i -g dmux@latest",
602
+ " ")),
603
+ currentBranch && (React.createElement(Text, { color: "magenta", bold: true },
604
+ "branch: ",
605
+ currentBranch)),
606
+ debugMessage && React.createElement(Text, { dimColor: true },
1336
607
  " \u2022 ",
1337
608
  debugMessage)))));
1338
609
  };