dmux 3.2.0 → 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.map +1 -1
  2. package/dist/DmuxApp.js +221 -1596
  3. package/dist/DmuxApp.js.map +1 -1
  4. package/dist/actions/implementations/closeAction.d.ts +10 -0
  5. package/dist/actions/implementations/closeAction.d.ts.map +1 -0
  6. package/dist/actions/implementations/closeAction.js +197 -0
  7. package/dist/actions/implementations/closeAction.js.map +1 -0
  8. package/dist/actions/implementations/copyPathAction.d.ts +10 -0
  9. package/dist/actions/implementations/copyPathAction.d.ts.map +1 -0
  10. package/dist/actions/implementations/copyPathAction.js +34 -0
  11. package/dist/actions/implementations/copyPathAction.js.map +1 -0
  12. package/dist/actions/implementations/duplicateAction.d.ts +10 -0
  13. package/dist/actions/implementations/duplicateAction.d.ts.map +1 -0
  14. package/dist/actions/implementations/duplicateAction.js +25 -0
  15. package/dist/actions/implementations/duplicateAction.js.map +1 -0
  16. package/dist/actions/implementations/index.d.ts +14 -0
  17. package/dist/actions/implementations/index.d.ts.map +1 -0
  18. package/dist/actions/implementations/index.js +14 -0
  19. package/dist/actions/implementations/index.js.map +1 -0
  20. package/dist/actions/implementations/mergeAction.d.ts +14 -0
  21. package/dist/actions/implementations/mergeAction.d.ts.map +1 -0
  22. package/dist/actions/implementations/mergeAction.js +84 -0
  23. package/dist/actions/implementations/mergeAction.js.map +1 -0
  24. package/dist/actions/implementations/openInEditorAction.d.ts +12 -0
  25. package/dist/actions/implementations/openInEditorAction.d.ts.map +1 -0
  26. package/dist/actions/implementations/openInEditorAction.js +33 -0
  27. package/dist/actions/implementations/openInEditorAction.js.map +1 -0
  28. package/dist/actions/implementations/renameAction.d.ts +13 -0
  29. package/dist/actions/implementations/renameAction.d.ts.map +1 -0
  30. package/dist/actions/implementations/renameAction.js +18 -0
  31. package/dist/actions/implementations/renameAction.js.map +1 -0
  32. package/dist/actions/implementations/toggleAutopilotAction.d.ts +10 -0
  33. package/dist/actions/implementations/toggleAutopilotAction.d.ts.map +1 -0
  34. package/dist/actions/implementations/toggleAutopilotAction.js +33 -0
  35. package/dist/actions/implementations/toggleAutopilotAction.js.map +1 -0
  36. package/dist/actions/implementations/viewAction.d.ts +10 -0
  37. package/dist/actions/implementations/viewAction.d.ts.map +1 -0
  38. package/dist/actions/implementations/viewAction.js +26 -0
  39. package/dist/actions/implementations/viewAction.js.map +1 -0
  40. package/dist/actions/merge/commitMessageHandler.d.ts +27 -0
  41. package/dist/actions/merge/commitMessageHandler.d.ts.map +1 -0
  42. package/dist/actions/merge/commitMessageHandler.js +131 -0
  43. package/dist/actions/merge/commitMessageHandler.js.map +1 -0
  44. package/dist/actions/merge/conflictResolution.d.ts +13 -0
  45. package/dist/actions/merge/conflictResolution.d.ts.map +1 -0
  46. package/dist/actions/merge/conflictResolution.js +134 -0
  47. package/dist/actions/merge/conflictResolution.js.map +1 -0
  48. package/dist/actions/merge/issueHandlers/index.d.ts +11 -0
  49. package/dist/actions/merge/issueHandlers/index.d.ts.map +1 -0
  50. package/dist/actions/merge/issueHandlers/index.js +8 -0
  51. package/dist/actions/merge/issueHandlers/index.js.map +1 -0
  52. package/dist/actions/merge/issueHandlers/mainDirtyHandler.d.ts +13 -0
  53. package/dist/actions/merge/issueHandlers/mainDirtyHandler.d.ts.map +1 -0
  54. package/dist/actions/merge/issueHandlers/mainDirtyHandler.js +72 -0
  55. package/dist/actions/merge/issueHandlers/mainDirtyHandler.js.map +1 -0
  56. package/dist/actions/merge/issueHandlers/mergeConflictHandler.d.ts +13 -0
  57. package/dist/actions/merge/issueHandlers/mergeConflictHandler.d.ts.map +1 -0
  58. package/dist/actions/merge/issueHandlers/mergeConflictHandler.js +52 -0
  59. package/dist/actions/merge/issueHandlers/mergeConflictHandler.js.map +1 -0
  60. package/dist/actions/merge/issueHandlers/nothingToMergeHandler.d.ts +7 -0
  61. package/dist/actions/merge/issueHandlers/nothingToMergeHandler.d.ts.map +1 -0
  62. package/dist/actions/merge/issueHandlers/nothingToMergeHandler.js +12 -0
  63. package/dist/actions/merge/issueHandlers/nothingToMergeHandler.js.map +1 -0
  64. package/dist/actions/merge/issueHandlers/worktreeUncommittedHandler.d.ts +13 -0
  65. package/dist/actions/merge/issueHandlers/worktreeUncommittedHandler.d.ts.map +1 -0
  66. package/dist/actions/merge/issueHandlers/worktreeUncommittedHandler.js +49 -0
  67. package/dist/actions/merge/issueHandlers/worktreeUncommittedHandler.js.map +1 -0
  68. package/dist/actions/merge/mergeExecution.d.ts +22 -0
  69. package/dist/actions/merge/mergeExecution.d.ts.map +1 -0
  70. package/dist/actions/merge/mergeExecution.js +180 -0
  71. package/dist/actions/merge/mergeExecution.js.map +1 -0
  72. package/dist/actions/paneActions.d.ts +3 -40
  73. package/dist/actions/paneActions.d.ts.map +1 -1
  74. package/dist/actions/paneActions.js +4 -1038
  75. package/dist/actions/paneActions.js.map +1 -1
  76. package/dist/actions/types.d.ts +1 -0
  77. package/dist/actions/types.d.ts.map +1 -1
  78. package/dist/actions/types.js.map +1 -1
  79. package/dist/components/{ActionChoiceDialog.d.ts → dialogs/ActionChoiceDialog.d.ts} +1 -1
  80. package/dist/components/dialogs/ActionChoiceDialog.d.ts.map +1 -0
  81. package/dist/components/dialogs/ActionChoiceDialog.js.map +1 -0
  82. package/dist/components/dialogs/ActionConfirmDialog.d.ts.map +1 -0
  83. package/dist/components/dialogs/ActionConfirmDialog.js.map +1 -0
  84. package/dist/components/dialogs/ActionInputDialog.d.ts.map +1 -0
  85. package/dist/components/{ActionInputDialog.js → dialogs/ActionInputDialog.js} +2 -2
  86. package/dist/components/dialogs/ActionInputDialog.js.map +1 -0
  87. package/dist/components/dialogs/ActionProgressDialog.d.ts.map +1 -0
  88. package/dist/components/dialogs/ActionProgressDialog.js.map +1 -0
  89. package/dist/components/dialogs/AgentChoiceDialog.d.ts.map +1 -0
  90. package/dist/components/dialogs/AgentChoiceDialog.js.map +1 -0
  91. package/dist/components/{CloseOptionsDialog.d.ts → dialogs/CloseOptionsDialog.d.ts} +1 -1
  92. package/dist/components/dialogs/CloseOptionsDialog.d.ts.map +1 -0
  93. package/dist/components/dialogs/CloseOptionsDialog.js.map +1 -0
  94. package/dist/components/dialogs/CommandPromptDialog.d.ts.map +1 -0
  95. package/dist/components/{CommandPromptDialog.js → dialogs/CommandPromptDialog.js} +1 -1
  96. package/dist/components/dialogs/CommandPromptDialog.js.map +1 -0
  97. package/dist/components/dialogs/DialogBox.d.ts.map +1 -0
  98. package/dist/components/dialogs/DialogBox.js.map +1 -0
  99. package/dist/components/dialogs/HooksDialog.d.ts.map +1 -0
  100. package/dist/components/dialogs/HooksDialog.js.map +1 -0
  101. package/dist/components/{MergeConfirmationDialog.d.ts → dialogs/MergeConfirmationDialog.d.ts} +1 -1
  102. package/dist/components/dialogs/MergeConfirmationDialog.d.ts.map +1 -0
  103. package/dist/components/dialogs/MergeConfirmationDialog.js.map +1 -0
  104. package/dist/components/{SettingsDialog.d.ts → dialogs/SettingsDialog.d.ts} +1 -1
  105. package/dist/components/dialogs/SettingsDialog.d.ts.map +1 -0
  106. package/dist/components/dialogs/SettingsDialog.js.map +1 -0
  107. package/dist/components/dialogs/UpdateDialog.d.ts.map +1 -0
  108. package/dist/components/dialogs/UpdateDialog.js.map +1 -0
  109. package/dist/components/indicators/CreatingIndicator.d.ts.map +1 -0
  110. package/dist/components/indicators/CreatingIndicator.js.map +1 -0
  111. package/dist/components/indicators/LoadingIndicator.d.ts.map +1 -0
  112. package/dist/components/indicators/LoadingIndicator.js.map +1 -0
  113. package/dist/components/indicators/RunningIndicator.d.ts.map +1 -0
  114. package/dist/components/indicators/RunningIndicator.js.map +1 -0
  115. package/dist/components/indicators/Spinner.d.ts.map +1 -0
  116. package/dist/components/indicators/Spinner.js.map +1 -0
  117. package/dist/components/indicators/UpdatingIndicator.d.ts.map +1 -0
  118. package/dist/components/indicators/UpdatingIndicator.js.map +1 -0
  119. package/dist/components/indicators/index.d.ts +6 -0
  120. package/dist/components/indicators/index.d.ts.map +1 -0
  121. package/dist/components/indicators/index.js +6 -0
  122. package/dist/components/indicators/index.js.map +1 -0
  123. package/dist/components/inputs/CleanTextInput.d.ts.map +1 -0
  124. package/dist/{CleanTextInput.js → components/inputs/CleanTextInput.js} +2 -144
  125. package/dist/components/inputs/CleanTextInput.js.map +1 -0
  126. package/dist/components/inputs/StyledTextInput.d.ts.map +1 -0
  127. package/dist/components/inputs/StyledTextInput.js.map +1 -0
  128. package/dist/components/inputs/index.d.ts +3 -0
  129. package/dist/components/inputs/index.d.ts.map +1 -0
  130. package/dist/components/inputs/index.js +3 -0
  131. package/dist/components/inputs/index.js.map +1 -0
  132. package/dist/components/{KebabMenu.d.ts → panes/KebabMenu.d.ts} +1 -1
  133. package/dist/components/panes/KebabMenu.d.ts.map +1 -0
  134. package/dist/components/panes/KebabMenu.js.map +1 -0
  135. package/dist/components/panes/MergePane.d.ts.map +1 -0
  136. package/dist/{MergePane.js → components/panes/MergePane.js} +1 -1
  137. package/dist/components/panes/MergePane.js.map +1 -0
  138. package/dist/components/{PaneCard.d.ts → panes/PaneCard.d.ts} +1 -1
  139. package/dist/components/panes/PaneCard.d.ts.map +1 -0
  140. package/dist/components/{PaneCard.js → panes/PaneCard.js} +1 -1
  141. package/dist/components/panes/PaneCard.js.map +1 -0
  142. package/dist/components/{PanesGrid.d.ts → panes/PanesGrid.d.ts} +2 -2
  143. package/dist/components/panes/PanesGrid.d.ts.map +1 -0
  144. package/dist/components/{PanesGrid.js → panes/PanesGrid.js} +1 -1
  145. package/dist/components/panes/PanesGrid.js.map +1 -0
  146. package/dist/components/panes/index.d.ts +5 -0
  147. package/dist/components/panes/index.d.ts.map +1 -0
  148. package/dist/components/panes/index.js +5 -0
  149. package/dist/components/panes/index.js.map +1 -0
  150. package/dist/components/popups/agentChoicePopup.d.ts.map +1 -0
  151. package/dist/{popups → components/popups}/agentChoicePopup.js +1 -1
  152. package/dist/components/popups/agentChoicePopup.js.map +1 -0
  153. package/dist/components/popups/choicePopup.d.ts.map +1 -0
  154. package/dist/{popups → components/popups}/choicePopup.js +3 -3
  155. package/dist/components/popups/choicePopup.js.map +1 -0
  156. package/dist/components/popups/config.d.ts.map +1 -0
  157. package/dist/{popups → components/popups}/config.js +1 -1
  158. package/dist/components/popups/config.js.map +1 -0
  159. package/dist/components/popups/confirmPopup.d.ts.map +1 -0
  160. package/dist/{popups → components/popups}/confirmPopup.js +2 -3
  161. package/dist/components/popups/confirmPopup.js.map +1 -0
  162. package/dist/components/popups/hooksPopup.d.ts.map +1 -0
  163. package/dist/{popups → components/popups}/hooksPopup.js +1 -1
  164. package/dist/components/popups/hooksPopup.js.map +1 -0
  165. package/dist/components/popups/inputPopup.d.ts.map +1 -0
  166. package/dist/{popups → components/popups}/inputPopup.js +3 -4
  167. package/dist/components/popups/inputPopup.js.map +1 -0
  168. package/dist/components/popups/kebabMenuPopup.d.ts.map +1 -0
  169. package/dist/{popups → components/popups}/kebabMenuPopup.js +1 -1
  170. package/dist/components/popups/kebabMenuPopup.js.map +1 -0
  171. package/dist/components/popups/logsPopup.d.ts.map +1 -0
  172. package/dist/{popups → components/popups}/logsPopup.js +15 -7
  173. package/dist/components/popups/logsPopup.js.map +1 -0
  174. package/dist/components/popups/mergePopup.d.ts.map +1 -0
  175. package/dist/{popups → components/popups}/mergePopup.js +6 -6
  176. package/dist/components/popups/mergePopup.js.map +1 -0
  177. package/dist/components/popups/newPanePopup.d.ts.map +1 -0
  178. package/dist/{popups → components/popups}/newPanePopup.js +3 -3
  179. package/dist/components/popups/newPanePopup.js.map +1 -0
  180. package/dist/components/popups/progressPopup.d.ts.map +1 -0
  181. package/dist/{popups → components/popups}/progressPopup.js +1 -2
  182. package/dist/components/popups/progressPopup.js.map +1 -0
  183. package/dist/components/popups/remotePopup.d.ts.map +1 -0
  184. package/dist/{popups → components/popups}/remotePopup.js +1 -1
  185. package/dist/components/popups/remotePopup.js.map +1 -0
  186. package/dist/components/popups/settingsPopup.d.ts.map +1 -0
  187. package/dist/{popups → components/popups}/settingsPopup.js +1 -1
  188. package/dist/components/popups/settingsPopup.js.map +1 -0
  189. package/dist/components/popups/shared/FileList.d.ts.map +1 -0
  190. package/dist/components/popups/shared/FileList.js.map +1 -0
  191. package/dist/components/popups/shared/PopupContainer.d.ts.map +1 -0
  192. package/dist/components/popups/shared/PopupContainer.js.map +1 -0
  193. package/dist/components/popups/shared/PopupInputBox.d.ts.map +1 -0
  194. package/dist/components/popups/shared/PopupInputBox.js.map +1 -0
  195. package/dist/components/popups/shared/PopupWrapper.d.ts.map +1 -0
  196. package/dist/components/popups/shared/PopupWrapper.js.map +1 -0
  197. package/dist/components/popups/shared/index.d.ts.map +1 -0
  198. package/dist/components/popups/shared/index.js.map +1 -0
  199. package/dist/components/popups/shortcutsPopup.d.ts.map +1 -0
  200. package/dist/{popups → components/popups}/shortcutsPopup.js +1 -1
  201. package/dist/components/popups/shortcutsPopup.js.map +1 -0
  202. package/dist/components/popups/templates/SimpleInputPopup.d.ts.map +1 -0
  203. package/dist/{popups → components/popups}/templates/SimpleInputPopup.js +2 -2
  204. package/dist/components/popups/templates/SimpleInputPopup.js.map +1 -0
  205. package/dist/components/ui/FileCopyPrompt.d.ts.map +1 -0
  206. package/dist/components/ui/FileCopyPrompt.js.map +1 -0
  207. package/dist/components/ui/FooterHelp.d.ts.map +1 -0
  208. package/dist/components/ui/FooterHelp.js.map +1 -0
  209. package/dist/components/ui/QRCode.d.ts.map +1 -0
  210. package/dist/components/ui/QRCode.js.map +1 -0
  211. package/dist/components/ui/index.d.ts +4 -0
  212. package/dist/components/ui/index.d.ts.map +1 -0
  213. package/dist/components/ui/index.js +4 -0
  214. package/dist/components/ui/index.js.map +1 -0
  215. package/dist/constants/timing.d.ts +22 -0
  216. package/dist/constants/timing.d.ts.map +1 -0
  217. package/dist/constants/timing.js +26 -0
  218. package/dist/constants/timing.js.map +1 -0
  219. package/dist/hooks/useActionSystem.d.ts +5 -4
  220. package/dist/hooks/useActionSystem.d.ts.map +1 -1
  221. package/dist/hooks/useActionSystem.js +120 -114
  222. package/dist/hooks/useActionSystem.js.map +1 -1
  223. package/dist/hooks/useDebugInfo.d.ts +11 -0
  224. package/dist/hooks/useDebugInfo.d.ts.map +1 -0
  225. package/dist/hooks/useDebugInfo.js +34 -0
  226. package/dist/hooks/useDebugInfo.js.map +1 -0
  227. package/dist/hooks/useDialogState.d.ts +22 -0
  228. package/dist/hooks/useDialogState.d.ts.map +1 -0
  229. package/dist/hooks/useDialogState.js +62 -0
  230. package/dist/hooks/useDialogState.js.map +1 -0
  231. package/dist/hooks/useInputHandling.d.ts +60 -0
  232. package/dist/hooks/useInputHandling.d.ts.map +1 -0
  233. package/dist/hooks/useInputHandling.js +280 -0
  234. package/dist/hooks/useInputHandling.js.map +1 -0
  235. package/dist/hooks/useLayoutManagement.d.ts +12 -0
  236. package/dist/hooks/useLayoutManagement.d.ts.map +1 -0
  237. package/dist/hooks/useLayoutManagement.js +58 -0
  238. package/dist/hooks/useLayoutManagement.js.map +1 -0
  239. package/dist/hooks/usePaneCreation.d.ts.map +1 -1
  240. package/dist/hooks/usePaneCreation.js +4 -9
  241. package/dist/hooks/usePaneCreation.js.map +1 -1
  242. package/dist/hooks/usePaneLoading.d.ts +45 -0
  243. package/dist/hooks/usePaneLoading.d.ts.map +1 -0
  244. package/dist/hooks/usePaneLoading.js +188 -0
  245. package/dist/hooks/usePaneLoading.js.map +1 -0
  246. package/dist/hooks/usePaneRunner.d.ts.map +1 -1
  247. package/dist/hooks/usePaneRunner.js +9 -6
  248. package/dist/hooks/usePaneRunner.js.map +1 -1
  249. package/dist/hooks/usePaneSync.d.ts +34 -0
  250. package/dist/hooks/usePaneSync.d.ts.map +1 -0
  251. package/dist/hooks/usePaneSync.js +182 -0
  252. package/dist/hooks/usePaneSync.js.map +1 -0
  253. package/dist/hooks/usePanes.d.ts.map +1 -1
  254. package/dist/hooks/usePanes.js +55 -405
  255. package/dist/hooks/usePanes.js.map +1 -1
  256. package/dist/hooks/useServices.d.ts +24 -0
  257. package/dist/hooks/useServices.d.ts.map +1 -0
  258. package/dist/hooks/useServices.js +39 -0
  259. package/dist/hooks/useServices.js.map +1 -0
  260. package/dist/hooks/useShellDetection.d.ts +10 -0
  261. package/dist/hooks/useShellDetection.d.ts.map +1 -0
  262. package/dist/hooks/useShellDetection.js +53 -0
  263. package/dist/hooks/useShellDetection.js.map +1 -0
  264. package/dist/hooks/useStatusMessages.d.ts +11 -0
  265. package/dist/hooks/useStatusMessages.d.ts.map +1 -0
  266. package/dist/hooks/useStatusMessages.js +32 -0
  267. package/dist/hooks/useStatusMessages.js.map +1 -0
  268. package/dist/hooks/useTemporaryStatus.d.ts +13 -0
  269. package/dist/hooks/useTemporaryStatus.d.ts.map +1 -0
  270. package/dist/hooks/useTemporaryStatus.js +30 -0
  271. package/dist/hooks/useTemporaryStatus.js.map +1 -0
  272. package/dist/hooks/useTerminalWidth.d.ts.map +1 -1
  273. package/dist/hooks/useTerminalWidth.js +7 -12
  274. package/dist/hooks/useTerminalWidth.js.map +1 -1
  275. package/dist/hooks/useTunnelManagement.d.ts +18 -0
  276. package/dist/hooks/useTunnelManagement.d.ts.map +1 -0
  277. package/dist/hooks/useTunnelManagement.js +55 -0
  278. package/dist/hooks/useTunnelManagement.js.map +1 -0
  279. package/dist/hooks/useWorktreeActions.d.ts.map +1 -1
  280. package/dist/hooks/useWorktreeActions.js +18 -13
  281. package/dist/hooks/useWorktreeActions.js.map +1 -1
  282. package/dist/index.js +75 -57
  283. package/dist/index.js.map +1 -1
  284. package/dist/layout/LayoutCalculator.d.ts +62 -0
  285. package/dist/layout/LayoutCalculator.d.ts.map +1 -0
  286. package/dist/layout/LayoutCalculator.js +143 -0
  287. package/dist/layout/LayoutCalculator.js.map +1 -0
  288. package/dist/layout/SpacerManager.d.ts +64 -0
  289. package/dist/layout/SpacerManager.d.ts.map +1 -0
  290. package/dist/layout/SpacerManager.js +148 -0
  291. package/dist/layout/SpacerManager.js.map +1 -0
  292. package/dist/layout/TmuxLayoutApplier.d.ts +59 -0
  293. package/dist/layout/TmuxLayoutApplier.d.ts.map +1 -0
  294. package/dist/layout/TmuxLayoutApplier.js +135 -0
  295. package/dist/layout/TmuxLayoutApplier.js.map +1 -0
  296. package/dist/panes/decorative-pane.d.ts.map +1 -0
  297. package/dist/{decorative-pane.js → panes/decorative-pane.js} +1 -1
  298. package/dist/panes/decorative-pane.js.map +1 -0
  299. package/dist/panes/spacer-pane.d.ts.map +1 -0
  300. package/dist/panes/spacer-pane.js.map +1 -0
  301. package/dist/server/embedded-assets.d.ts.map +1 -1
  302. package/dist/server/embedded-assets.js +636 -5637
  303. package/dist/server/embedded-assets.js.map +1 -1
  304. package/dist/server/routes/actionsRoutes.d.ts +2 -0
  305. package/dist/server/routes/actionsRoutes.d.ts.map +1 -0
  306. package/dist/server/routes/actionsRoutes.js +110 -0
  307. package/dist/server/routes/actionsRoutes.js.map +1 -0
  308. package/dist/server/routes/healthRoutes.d.ts +13 -0
  309. package/dist/server/routes/healthRoutes.d.ts.map +1 -0
  310. package/dist/server/routes/healthRoutes.js +70 -0
  311. package/dist/server/routes/healthRoutes.js.map +1 -0
  312. package/dist/server/routes/index.d.ts +8 -0
  313. package/dist/server/routes/index.d.ts.map +1 -0
  314. package/dist/server/routes/index.js +67 -0
  315. package/dist/server/routes/index.js.map +1 -0
  316. package/dist/server/routes/keysRoutes.d.ts +20 -0
  317. package/dist/server/routes/keysRoutes.d.ts.map +1 -0
  318. package/dist/server/routes/keysRoutes.js +111 -0
  319. package/dist/server/routes/keysRoutes.js.map +1 -0
  320. package/dist/server/routes/panesRoutes.d.ts +2 -0
  321. package/dist/server/routes/panesRoutes.d.ts.map +1 -0
  322. package/dist/server/routes/panesRoutes.js +373 -0
  323. package/dist/server/routes/panesRoutes.js.map +1 -0
  324. package/dist/server/routes/settingsRoutes.d.ts +94 -0
  325. package/dist/server/routes/settingsRoutes.d.ts.map +1 -0
  326. package/dist/server/routes/settingsRoutes.js +159 -0
  327. package/dist/server/routes/settingsRoutes.js.map +1 -0
  328. package/dist/server/routes/streamRoutes.d.ts +18 -0
  329. package/dist/server/routes/streamRoutes.d.ts.map +1 -0
  330. package/dist/server/routes/streamRoutes.js +87 -0
  331. package/dist/server/routes/streamRoutes.js.map +1 -0
  332. package/dist/server/routes/tunnelRoutes.d.ts +11 -0
  333. package/dist/server/routes/tunnelRoutes.d.ts.map +1 -0
  334. package/dist/server/routes/tunnelRoutes.js +28 -0
  335. package/dist/server/routes/tunnelRoutes.js.map +1 -0
  336. package/dist/server/routes.d.ts +16 -2
  337. package/dist/server/routes.d.ts.map +1 -1
  338. package/dist/server/routes.js +16 -878
  339. package/dist/server/routes.js.map +1 -1
  340. package/dist/{AutoUpdater.d.ts → services/AutoUpdater.d.ts} +1 -0
  341. package/dist/services/AutoUpdater.d.ts.map +1 -0
  342. package/dist/{AutoUpdater.js → services/AutoUpdater.js} +42 -9
  343. package/dist/services/AutoUpdater.js.map +1 -0
  344. package/dist/services/PaneAnalyzer.d.ts.map +1 -0
  345. package/dist/{PaneAnalyzer.js → services/PaneAnalyzer.js} +1 -1
  346. package/dist/services/PaneAnalyzer.js.map +1 -0
  347. package/dist/services/PaneLifecycleManager.d.ts +60 -0
  348. package/dist/services/PaneLifecycleManager.d.ts.map +1 -0
  349. package/dist/services/PaneLifecycleManager.js +119 -0
  350. package/dist/services/PaneLifecycleManager.js.map +1 -0
  351. package/dist/services/PaneWorkerManager.d.ts.map +1 -1
  352. package/dist/services/PaneWorkerManager.js +6 -3
  353. package/dist/services/PaneWorkerManager.js.map +1 -1
  354. package/dist/services/PopupManager.d.ts +68 -0
  355. package/dist/services/PopupManager.d.ts.map +1 -0
  356. package/dist/services/PopupManager.js +415 -0
  357. package/dist/services/PopupManager.js.map +1 -0
  358. package/dist/services/StatusDetector.js +1 -1
  359. package/dist/services/StatusDetector.js.map +1 -1
  360. package/dist/services/TerminalStreamer.d.ts +1 -0
  361. package/dist/services/TerminalStreamer.d.ts.map +1 -1
  362. package/dist/services/TerminalStreamer.js +22 -16
  363. package/dist/services/TerminalStreamer.js.map +1 -1
  364. package/dist/services/TmuxService.d.ts +298 -0
  365. package/dist/services/TmuxService.d.ts.map +1 -0
  366. package/dist/services/TmuxService.js +768 -0
  367. package/dist/services/TmuxService.js.map +1 -0
  368. package/dist/services/TunnelService.d.ts.map +1 -1
  369. package/dist/services/TunnelService.js +2 -1
  370. package/dist/services/TunnelService.js.map +1 -1
  371. package/dist/types.d.ts +4 -0
  372. package/dist/types.d.ts.map +1 -1
  373. package/dist/utils/asciiArt.d.ts.map +1 -1
  374. package/dist/utils/asciiArt.js +6 -19
  375. package/dist/utils/asciiArt.js.map +1 -1
  376. package/dist/utils/conflictMonitor.d.ts +20 -0
  377. package/dist/utils/conflictMonitor.d.ts.map +1 -0
  378. package/dist/utils/conflictMonitor.js +113 -0
  379. package/dist/utils/conflictMonitor.js.map +1 -0
  380. package/dist/utils/conflictResolutionPane.d.ts.map +1 -1
  381. package/dist/utils/conflictResolutionPane.js +70 -79
  382. package/dist/utils/conflictResolutionPane.js.map +1 -1
  383. package/dist/utils/errorHandling.d.ts +37 -0
  384. package/dist/utils/errorHandling.d.ts.map +1 -0
  385. package/dist/utils/errorHandling.js +112 -0
  386. package/dist/utils/errorHandling.js.map +1 -0
  387. package/dist/utils/generated-agents-doc.d.ts +1 -1
  388. package/dist/utils/generated-agents-doc.js +1 -1
  389. package/dist/utils/git.d.ts +4 -0
  390. package/dist/utils/git.d.ts.map +1 -1
  391. package/dist/utils/git.js +15 -0
  392. package/dist/utils/git.js.map +1 -1
  393. package/dist/utils/hooksDocs.d.ts +1 -1
  394. package/dist/utils/layoutManager.d.ts +20 -16
  395. package/dist/utils/layoutManager.d.ts.map +1 -1
  396. package/dist/utils/layoutManager.js +116 -367
  397. package/dist/utils/layoutManager.js.map +1 -1
  398. package/dist/utils/mergeExecution.d.ts.map +1 -1
  399. package/dist/utils/mergeExecution.js +8 -1
  400. package/dist/utils/mergeExecution.js.map +1 -1
  401. package/dist/utils/mergeValidation.d.ts.map +1 -1
  402. package/dist/utils/mergeValidation.js +42 -13
  403. package/dist/utils/mergeValidation.js.map +1 -1
  404. package/dist/utils/paneCreation.d.ts.map +1 -1
  405. package/dist/utils/paneCreation.js +111 -103
  406. package/dist/utils/paneCreation.js.map +1 -1
  407. package/dist/utils/paneRebinding.d.ts +14 -0
  408. package/dist/utils/paneRebinding.d.ts.map +1 -0
  409. package/dist/utils/paneRebinding.js +28 -0
  410. package/dist/utils/paneRebinding.js.map +1 -0
  411. package/dist/utils/popup.d.ts.map +1 -1
  412. package/dist/utils/popup.js +11 -17
  413. package/dist/utils/popup.js.map +1 -1
  414. package/dist/utils/postPaneCleanup.d.ts.map +1 -1
  415. package/dist/utils/postPaneCleanup.js +4 -3
  416. package/dist/utils/postPaneCleanup.js.map +1 -1
  417. package/dist/utils/shellPaneDetection.d.ts +3 -3
  418. package/dist/utils/shellPaneDetection.d.ts.map +1 -1
  419. package/dist/utils/shellPaneDetection.js +10 -6
  420. package/dist/utils/shellPaneDetection.js.map +1 -1
  421. package/dist/utils/systemCheck.d.ts +19 -0
  422. package/dist/utils/systemCheck.d.ts.map +1 -0
  423. package/dist/utils/systemCheck.js +160 -0
  424. package/dist/utils/systemCheck.js.map +1 -0
  425. package/dist/utils/tmux.d.ts +26 -7
  426. package/dist/utils/tmux.d.ts.map +1 -1
  427. package/dist/utils/tmux.js +66 -64
  428. package/dist/utils/tmux.js.map +1 -1
  429. package/dist/utils/welcomePane.d.ts +2 -2
  430. package/dist/utils/welcomePane.d.ts.map +1 -1
  431. package/dist/utils/welcomePane.js +20 -28
  432. package/dist/utils/welcomePane.js.map +1 -1
  433. package/dist/utils/welcomePaneManager.js +1 -1
  434. package/dist/utils/welcomePaneManager.js.map +1 -1
  435. package/dist/workers/PaneWorker.js +7 -9
  436. package/dist/workers/PaneWorker.js.map +1 -1
  437. package/dist/workers/updateChecker.js +1 -1
  438. package/dist/workers/updateChecker.js.map +1 -1
  439. package/package.json +3 -1
  440. package/dist/AutoUpdater.d.ts.map +0 -1
  441. package/dist/AutoUpdater.js.map +0 -1
  442. package/dist/BetterTextInput.d.ts +0 -10
  443. package/dist/BetterTextInput.d.ts.map +0 -1
  444. package/dist/BetterTextInput.js +0 -177
  445. package/dist/BetterTextInput.js.map +0 -1
  446. package/dist/CleanTextInput.d.ts.map +0 -1
  447. package/dist/CleanTextInput.js.map +0 -1
  448. package/dist/EnhancedTextInput.d.ts +0 -13
  449. package/dist/EnhancedTextInput.d.ts.map +0 -1
  450. package/dist/EnhancedTextInput.js +0 -443
  451. package/dist/EnhancedTextInput.js.map +0 -1
  452. package/dist/GeminiTextInput.d.ts +0 -12
  453. package/dist/GeminiTextInput.d.ts.map +0 -1
  454. package/dist/GeminiTextInput.js +0 -210
  455. package/dist/GeminiTextInput.js.map +0 -1
  456. package/dist/MergePane.d.ts.map +0 -1
  457. package/dist/MergePane.js.map +0 -1
  458. package/dist/MultilineTextInput.d.ts +0 -10
  459. package/dist/MultilineTextInput.d.ts.map +0 -1
  460. package/dist/MultilineTextInput.js +0 -184
  461. package/dist/MultilineTextInput.js.map +0 -1
  462. package/dist/PaneAnalyzer.d.ts.map +0 -1
  463. package/dist/PaneAnalyzer.js.map +0 -1
  464. package/dist/SimpleEnhancedInput.d.ts +0 -13
  465. package/dist/SimpleEnhancedInput.d.ts.map +0 -1
  466. package/dist/SimpleEnhancedInput.js +0 -639
  467. package/dist/SimpleEnhancedInput.js.map +0 -1
  468. package/dist/SimpleGeminiInput.d.ts +0 -12
  469. package/dist/SimpleGeminiInput.d.ts.map +0 -1
  470. package/dist/SimpleGeminiInput.js +0 -223
  471. package/dist/SimpleGeminiInput.js.map +0 -1
  472. package/dist/StyledTextInput.d.ts.map +0 -1
  473. package/dist/StyledTextInput.js.map +0 -1
  474. package/dist/components/ActionChoiceDialog.d.ts.map +0 -1
  475. package/dist/components/ActionChoiceDialog.js.map +0 -1
  476. package/dist/components/ActionConfirmDialog.d.ts.map +0 -1
  477. package/dist/components/ActionConfirmDialog.js.map +0 -1
  478. package/dist/components/ActionInputDialog.d.ts.map +0 -1
  479. package/dist/components/ActionInputDialog.js.map +0 -1
  480. package/dist/components/ActionProgressDialog.d.ts.map +0 -1
  481. package/dist/components/ActionProgressDialog.js.map +0 -1
  482. package/dist/components/AgentChoiceDialog.d.ts.map +0 -1
  483. package/dist/components/AgentChoiceDialog.js.map +0 -1
  484. package/dist/components/CloseOptionsDialog.d.ts.map +0 -1
  485. package/dist/components/CloseOptionsDialog.js.map +0 -1
  486. package/dist/components/CommandPromptDialog.d.ts.map +0 -1
  487. package/dist/components/CommandPromptDialog.js.map +0 -1
  488. package/dist/components/CreatingIndicator.d.ts.map +0 -1
  489. package/dist/components/CreatingIndicator.js.map +0 -1
  490. package/dist/components/DialogBox.d.ts.map +0 -1
  491. package/dist/components/DialogBox.js.map +0 -1
  492. package/dist/components/FileCopyPrompt.d.ts.map +0 -1
  493. package/dist/components/FileCopyPrompt.js.map +0 -1
  494. package/dist/components/FooterHelp.d.ts.map +0 -1
  495. package/dist/components/FooterHelp.js.map +0 -1
  496. package/dist/components/HooksDialog.d.ts.map +0 -1
  497. package/dist/components/HooksDialog.js.map +0 -1
  498. package/dist/components/KebabMenu.d.ts.map +0 -1
  499. package/dist/components/KebabMenu.js.map +0 -1
  500. package/dist/components/LoadingIndicator.d.ts.map +0 -1
  501. package/dist/components/LoadingIndicator.js.map +0 -1
  502. package/dist/components/MergeConfirmationDialog.d.ts.map +0 -1
  503. package/dist/components/MergeConfirmationDialog.js.map +0 -1
  504. package/dist/components/PaneCard.d.ts.map +0 -1
  505. package/dist/components/PaneCard.js.map +0 -1
  506. package/dist/components/PanesGrid.d.ts.map +0 -1
  507. package/dist/components/PanesGrid.js.map +0 -1
  508. package/dist/components/QRCode.d.ts.map +0 -1
  509. package/dist/components/QRCode.js.map +0 -1
  510. package/dist/components/RunningIndicator.d.ts.map +0 -1
  511. package/dist/components/RunningIndicator.js.map +0 -1
  512. package/dist/components/SettingsDialog.d.ts.map +0 -1
  513. package/dist/components/SettingsDialog.js.map +0 -1
  514. package/dist/components/Spinner.d.ts.map +0 -1
  515. package/dist/components/Spinner.js.map +0 -1
  516. package/dist/components/UpdateDialog.d.ts.map +0 -1
  517. package/dist/components/UpdateDialog.js.map +0 -1
  518. package/dist/components/UpdatingIndicator.d.ts.map +0 -1
  519. package/dist/components/UpdatingIndicator.js.map +0 -1
  520. package/dist/decorative-pane.d.ts.map +0 -1
  521. package/dist/decorative-pane.js.map +0 -1
  522. package/dist/popups/agentChoicePopup.d.ts.map +0 -1
  523. package/dist/popups/agentChoicePopup.js.map +0 -1
  524. package/dist/popups/choicePopup.d.ts.map +0 -1
  525. package/dist/popups/choicePopup.js.map +0 -1
  526. package/dist/popups/components/FileList.d.ts.map +0 -1
  527. package/dist/popups/components/FileList.js.map +0 -1
  528. package/dist/popups/components/PopupContainer.d.ts.map +0 -1
  529. package/dist/popups/components/PopupContainer.js.map +0 -1
  530. package/dist/popups/components/PopupInputBox.d.ts.map +0 -1
  531. package/dist/popups/components/PopupInputBox.js.map +0 -1
  532. package/dist/popups/components/PopupWrapper.d.ts.map +0 -1
  533. package/dist/popups/components/PopupWrapper.js.map +0 -1
  534. package/dist/popups/components/index.d.ts.map +0 -1
  535. package/dist/popups/components/index.js.map +0 -1
  536. package/dist/popups/config.d.ts.map +0 -1
  537. package/dist/popups/config.js.map +0 -1
  538. package/dist/popups/confirmPopup.d.ts.map +0 -1
  539. package/dist/popups/confirmPopup.js.map +0 -1
  540. package/dist/popups/hooksPopup.d.ts.map +0 -1
  541. package/dist/popups/hooksPopup.js.map +0 -1
  542. package/dist/popups/inputPopup.d.ts.map +0 -1
  543. package/dist/popups/inputPopup.js.map +0 -1
  544. package/dist/popups/kebabMenuPopup.d.ts.map +0 -1
  545. package/dist/popups/kebabMenuPopup.js.map +0 -1
  546. package/dist/popups/logsPopup.d.ts.map +0 -1
  547. package/dist/popups/logsPopup.js.map +0 -1
  548. package/dist/popups/mergePopup.d.ts.map +0 -1
  549. package/dist/popups/mergePopup.js.map +0 -1
  550. package/dist/popups/newPanePopup.d.ts.map +0 -1
  551. package/dist/popups/newPanePopup.js.map +0 -1
  552. package/dist/popups/progressPopup.d.ts.map +0 -1
  553. package/dist/popups/progressPopup.js.map +0 -1
  554. package/dist/popups/remotePopup.d.ts.map +0 -1
  555. package/dist/popups/remotePopup.js.map +0 -1
  556. package/dist/popups/settingsPopup.d.ts.map +0 -1
  557. package/dist/popups/settingsPopup.js.map +0 -1
  558. package/dist/popups/shortcutsPopup.d.ts.map +0 -1
  559. package/dist/popups/shortcutsPopup.js.map +0 -1
  560. package/dist/popups/templates/SimpleInputPopup.d.ts.map +0 -1
  561. package/dist/popups/templates/SimpleInputPopup.js.map +0 -1
  562. package/dist/server/static.d.ts +0 -6
  563. package/dist/server/static.d.ts.map +0 -1
  564. package/dist/server/static.js +0 -3040
  565. package/dist/server/static.js.map +0 -1
  566. package/dist/spacer-pane.d.ts.map +0 -1
  567. package/dist/spacer-pane.js.map +0 -1
  568. /package/dist/components/{ActionChoiceDialog.js → dialogs/ActionChoiceDialog.js} +0 -0
  569. /package/dist/components/{ActionConfirmDialog.d.ts → dialogs/ActionConfirmDialog.d.ts} +0 -0
  570. /package/dist/components/{ActionConfirmDialog.js → dialogs/ActionConfirmDialog.js} +0 -0
  571. /package/dist/components/{ActionInputDialog.d.ts → dialogs/ActionInputDialog.d.ts} +0 -0
  572. /package/dist/components/{ActionProgressDialog.d.ts → dialogs/ActionProgressDialog.d.ts} +0 -0
  573. /package/dist/components/{ActionProgressDialog.js → dialogs/ActionProgressDialog.js} +0 -0
  574. /package/dist/components/{AgentChoiceDialog.d.ts → dialogs/AgentChoiceDialog.d.ts} +0 -0
  575. /package/dist/components/{AgentChoiceDialog.js → dialogs/AgentChoiceDialog.js} +0 -0
  576. /package/dist/components/{CloseOptionsDialog.js → dialogs/CloseOptionsDialog.js} +0 -0
  577. /package/dist/components/{CommandPromptDialog.d.ts → dialogs/CommandPromptDialog.d.ts} +0 -0
  578. /package/dist/components/{DialogBox.d.ts → dialogs/DialogBox.d.ts} +0 -0
  579. /package/dist/components/{DialogBox.js → dialogs/DialogBox.js} +0 -0
  580. /package/dist/components/{HooksDialog.d.ts → dialogs/HooksDialog.d.ts} +0 -0
  581. /package/dist/components/{HooksDialog.js → dialogs/HooksDialog.js} +0 -0
  582. /package/dist/components/{MergeConfirmationDialog.js → dialogs/MergeConfirmationDialog.js} +0 -0
  583. /package/dist/components/{SettingsDialog.js → dialogs/SettingsDialog.js} +0 -0
  584. /package/dist/components/{UpdateDialog.d.ts → dialogs/UpdateDialog.d.ts} +0 -0
  585. /package/dist/components/{UpdateDialog.js → dialogs/UpdateDialog.js} +0 -0
  586. /package/dist/components/{CreatingIndicator.d.ts → indicators/CreatingIndicator.d.ts} +0 -0
  587. /package/dist/components/{CreatingIndicator.js → indicators/CreatingIndicator.js} +0 -0
  588. /package/dist/components/{LoadingIndicator.d.ts → indicators/LoadingIndicator.d.ts} +0 -0
  589. /package/dist/components/{LoadingIndicator.js → indicators/LoadingIndicator.js} +0 -0
  590. /package/dist/components/{RunningIndicator.d.ts → indicators/RunningIndicator.d.ts} +0 -0
  591. /package/dist/components/{RunningIndicator.js → indicators/RunningIndicator.js} +0 -0
  592. /package/dist/components/{Spinner.d.ts → indicators/Spinner.d.ts} +0 -0
  593. /package/dist/components/{Spinner.js → indicators/Spinner.js} +0 -0
  594. /package/dist/components/{UpdatingIndicator.d.ts → indicators/UpdatingIndicator.d.ts} +0 -0
  595. /package/dist/components/{UpdatingIndicator.js → indicators/UpdatingIndicator.js} +0 -0
  596. /package/dist/{CleanTextInput.d.ts → components/inputs/CleanTextInput.d.ts} +0 -0
  597. /package/dist/{StyledTextInput.d.ts → components/inputs/StyledTextInput.d.ts} +0 -0
  598. /package/dist/{StyledTextInput.js → components/inputs/StyledTextInput.js} +0 -0
  599. /package/dist/components/{KebabMenu.js → panes/KebabMenu.js} +0 -0
  600. /package/dist/{MergePane.d.ts → components/panes/MergePane.d.ts} +0 -0
  601. /package/dist/{popups → components/popups}/agentChoicePopup.d.ts +0 -0
  602. /package/dist/{popups → components/popups}/choicePopup.d.ts +0 -0
  603. /package/dist/{popups → components/popups}/config.d.ts +0 -0
  604. /package/dist/{popups → components/popups}/confirmPopup.d.ts +0 -0
  605. /package/dist/{popups → components/popups}/hooksPopup.d.ts +0 -0
  606. /package/dist/{popups → components/popups}/inputPopup.d.ts +0 -0
  607. /package/dist/{popups → components/popups}/kebabMenuPopup.d.ts +0 -0
  608. /package/dist/{popups → components/popups}/logsPopup.d.ts +0 -0
  609. /package/dist/{popups → components/popups}/mergePopup.d.ts +0 -0
  610. /package/dist/{popups → components/popups}/newPanePopup.d.ts +0 -0
  611. /package/dist/{popups → components/popups}/progressPopup.d.ts +0 -0
  612. /package/dist/{popups → components/popups}/remotePopup.d.ts +0 -0
  613. /package/dist/{popups → components/popups}/settingsPopup.d.ts +0 -0
  614. /package/dist/{popups/components → components/popups/shared}/FileList.d.ts +0 -0
  615. /package/dist/{popups/components → components/popups/shared}/FileList.js +0 -0
  616. /package/dist/{popups/components → components/popups/shared}/PopupContainer.d.ts +0 -0
  617. /package/dist/{popups/components → components/popups/shared}/PopupContainer.js +0 -0
  618. /package/dist/{popups/components → components/popups/shared}/PopupInputBox.d.ts +0 -0
  619. /package/dist/{popups/components → components/popups/shared}/PopupInputBox.js +0 -0
  620. /package/dist/{popups/components → components/popups/shared}/PopupWrapper.d.ts +0 -0
  621. /package/dist/{popups/components → components/popups/shared}/PopupWrapper.js +0 -0
  622. /package/dist/{popups/components → components/popups/shared}/index.d.ts +0 -0
  623. /package/dist/{popups/components → components/popups/shared}/index.js +0 -0
  624. /package/dist/{popups → components/popups}/shortcutsPopup.d.ts +0 -0
  625. /package/dist/{popups → components/popups}/templates/SimpleInputPopup.d.ts +0 -0
  626. /package/dist/components/{FileCopyPrompt.d.ts → ui/FileCopyPrompt.d.ts} +0 -0
  627. /package/dist/components/{FileCopyPrompt.js → ui/FileCopyPrompt.js} +0 -0
  628. /package/dist/components/{FooterHelp.d.ts → ui/FooterHelp.d.ts} +0 -0
  629. /package/dist/components/{FooterHelp.js → ui/FooterHelp.js} +0 -0
  630. /package/dist/components/{QRCode.d.ts → ui/QRCode.d.ts} +0 -0
  631. /package/dist/components/{QRCode.js → ui/QRCode.js} +0 -0
  632. /package/dist/{decorative-pane.d.ts → panes/decorative-pane.d.ts} +0 -0
  633. /package/dist/{spacer-pane.d.ts → panes/spacer-pane.d.ts} +0 -0
  634. /package/dist/{spacer-pane.js → panes/spacer-pane.js} +0 -0
  635. /package/dist/{PaneAnalyzer.d.ts → services/PaneAnalyzer.d.ts} +0 -0
@@ -1,3040 +0,0 @@
1
- export function getTerminalViewerHtml() {
2
- return `<!DOCTYPE html>
3
- <html lang="en">
4
- <head>
5
- <meta charset="UTF-8">
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
7
- <title>Terminal Viewer - dmux</title>
8
- <link rel="stylesheet" href="/styles.css">
9
- </head>
10
- <body>
11
- <div id="app"></div>
12
-
13
- <script type="module" src="/terminal.js"></script>
14
- </body>
15
- </html>`;
16
- }
17
- export function getDashboardHtml() {
18
- return `<!DOCTYPE html>
19
- <html lang="en">
20
- <head>
21
- <meta charset="UTF-8">
22
- <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
23
- <title>dmux Dashboard</title>
24
- <link rel="stylesheet" href="/styles.css">
25
- </head>
26
- <body>
27
- <div id="app"></div>
28
-
29
- <script type="module" src="/dashboard.js"></script>
30
- </body>
31
- </html>`;
32
- }
33
- export function getDashboardCss() {
34
- return `* {
35
- margin: 0;
36
- padding: 0;
37
- box-sizing: border-box;
38
- }
39
-
40
- :root {
41
- /* Dark theme (default) */
42
- --bg-gradient-start: #0f0f23;
43
- --bg-gradient-mid: #1a1a2e;
44
- --bg-gradient-end: #16213e;
45
- --text-primary: #e0e0e0;
46
- --text-secondary: #a0a0a0;
47
- --text-tertiary: #808080;
48
- --text-dim: #606060;
49
- --text-dimmer: #666;
50
- --text-bright: #fff;
51
- --border-color: rgba(255, 255, 255, 0.1);
52
- --border-accent: rgba(255, 140, 0, 0.3);
53
- --card-bg: rgba(255, 255, 255, 0.05);
54
- --card-border: rgba(255, 255, 255, 0.1);
55
- --header-bg: rgba(255, 255, 255, 0.05);
56
- --input-bg: rgba(255, 255, 255, 0.05);
57
- --input-border: rgba(255, 255, 255, 0.12);
58
- --input-focus-border: rgba(255, 140, 0, 0.5);
59
- --input-focus-bg: rgba(255, 255, 255, 0.08);
60
- --input-focus-shadow: rgba(255, 140, 0, 0.1);
61
- --button-bg: rgba(200, 210, 230, 0.15);
62
- --button-border: rgba(255, 255, 255, 0.08);
63
- --button-hover-bg: rgba(200, 210, 230, 0.25);
64
- --button-hover-border: rgba(255, 255, 255, 0.15);
65
- --tooltip-bg: rgba(20, 20, 30, 0.98);
66
- --tooltip-border: rgba(255, 255, 255, 0.15);
67
- --hint-bg: rgba(255, 255, 255, 0.05);
68
- --agent-bg: rgba(255, 255, 255, 0.08);
69
- --agent-border: rgba(255, 255, 255, 0.15);
70
- --idle-badge-bg: rgba(255, 255, 255, 0.08);
71
- --idle-badge-border: rgba(255, 255, 255, 0.1);
72
- }
73
-
74
- [data-theme="light"] {
75
- /* Light theme */
76
- --bg-gradient-start: #f0f4f8;
77
- --bg-gradient-mid: #e6eef5;
78
- --bg-gradient-end: #dce7f0;
79
- --text-primary: #1a1a2e;
80
- --text-secondary: #4a5568;
81
- --text-tertiary: #718096;
82
- --text-dim: #a0aec0;
83
- --text-dimmer: #cbd5e0;
84
- --text-bright: #000;
85
- --border-color: rgba(0, 0, 0, 0.1);
86
- --border-accent: rgba(255, 140, 0, 0.4);
87
- --card-bg: rgba(255, 255, 255, 0.8);
88
- --card-border: rgba(0, 0, 0, 0.08);
89
- --header-bg: rgba(255, 255, 255, 0.9);
90
- --input-bg: rgba(255, 255, 255, 0.6);
91
- --input-border: rgba(0, 0, 0, 0.15);
92
- --input-focus-border: rgba(255, 140, 0, 0.6);
93
- --input-focus-bg: rgba(255, 255, 255, 0.9);
94
- --input-focus-shadow: rgba(255, 140, 0, 0.15);
95
- --button-bg: rgba(0, 0, 0, 0.05);
96
- --button-border: rgba(0, 0, 0, 0.1);
97
- --button-hover-bg: rgba(0, 0, 0, 0.1);
98
- --button-hover-border: rgba(0, 0, 0, 0.2);
99
- --tooltip-bg: rgba(255, 255, 255, 0.98);
100
- --tooltip-border: rgba(0, 0, 0, 0.15);
101
- --hint-bg: rgba(0, 0, 0, 0.03);
102
- --agent-bg: rgba(0, 0, 0, 0.05);
103
- --agent-border: rgba(0, 0, 0, 0.12);
104
- --idle-badge-bg: rgba(0, 0, 0, 0.05);
105
- --idle-badge-border: rgba(0, 0, 0, 0.1);
106
- }
107
-
108
- @keyframes fadeIn {
109
- from { opacity: 0; transform: translateY(10px); }
110
- to { opacity: 1; transform: translateY(0); }
111
- }
112
-
113
- @keyframes pulse {
114
- 0%, 100% { opacity: 1; }
115
- 50% { opacity: 0.5; }
116
- }
117
-
118
- @keyframes slideInFromTop {
119
- from { opacity: 0; transform: translateY(-20px); }
120
- to { opacity: 1; transform: translateY(0); }
121
- }
122
-
123
- body {
124
- font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'SF Pro Text', 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
125
- background: linear-gradient(135deg, var(--bg-gradient-start) 0%, var(--bg-gradient-mid) 50%, var(--bg-gradient-end) 100%);
126
- background-attachment: fixed;
127
- color: var(--text-primary);
128
- min-height: 100vh;
129
- display: flex;
130
- flex-direction: column;
131
- -webkit-font-smoothing: antialiased;
132
- -moz-osx-font-smoothing: grayscale;
133
- transition: background 0.3s ease, color 0.3s ease;
134
- }
135
-
136
- .container {
137
- max-width: 1400px;
138
- margin: 0 auto;
139
- padding: 40px 20px;
140
- width: 100%;
141
- flex: 1;
142
- display: flex;
143
- flex-direction: column;
144
- animation: fadeIn 0.5s ease-out;
145
- }
146
-
147
- header {
148
- display: flex;
149
- justify-content: space-between;
150
- align-items: center;
151
- padding: 16px 24px;
152
- margin-bottom: 0;
153
- background: var(--header-bg);
154
- backdrop-filter: blur(10px);
155
- -webkit-backdrop-filter: blur(10px);
156
- border-bottom: 2px solid var(--border-accent);
157
- animation: slideInFromTop 0.6s ease-out;
158
- gap: 16px;
159
- }
160
-
161
- .logo {
162
- height: 24px;
163
- width: auto;
164
- flex-shrink: 0;
165
- }
166
-
167
- h1 {
168
- font-size: 18px;
169
- font-weight: 600;
170
- letter-spacing: -0.5px;
171
- color: var(--text-primary);
172
- flex: 1;
173
- text-align: center;
174
- white-space: nowrap;
175
- overflow: hidden;
176
- text-overflow: ellipsis;
177
- min-width: 0;
178
- max-width: 500px;
179
- margin: 0 auto;
180
- }
181
-
182
- .session-info {
183
- display: flex;
184
- gap: 12px;
185
- align-items: center;
186
- font-size: 13px;
187
- color: var(--text-secondary);
188
- flex-shrink: 0;
189
- }
190
-
191
- .theme-toggle {
192
- background: transparent;
193
- border: none;
194
- padding: 4px;
195
- cursor: pointer;
196
- transition: all 0.2s ease;
197
- display: flex;
198
- align-items: center;
199
- justify-content: center;
200
- color: var(--text-secondary);
201
- flex-shrink: 0;
202
- width: 24px;
203
- height: 24px;
204
- }
205
-
206
- .theme-toggle:hover {
207
- color: var(--text-primary);
208
- transform: scale(1.1);
209
- }
210
-
211
- .theme-toggle svg {
212
- width: 20px;
213
- height: 20px;
214
- fill: currentColor;
215
- }
216
-
217
- .session-info span {
218
- display: flex;
219
- align-items: center;
220
- gap: 6px;
221
- }
222
-
223
- .status-indicator {
224
- color: #4ade80;
225
- font-size: 16px;
226
- animation: pulse 2s ease-in-out infinite;
227
- }
228
-
229
- main {
230
- flex: 1;
231
- padding-top: 40px;
232
- min-height: 0;
233
- }
234
-
235
- .panes-grid {
236
- display: grid;
237
- grid-template-columns: repeat(auto-fill, minmax(380px, 1fr));
238
- gap: 24px;
239
- margin-bottom: 40px;
240
- }
241
-
242
- .pane-card {
243
- background: var(--card-bg);
244
- backdrop-filter: blur(20px);
245
- -webkit-backdrop-filter: blur(20px);
246
- border: 1px solid var(--card-border);
247
- border-radius: 12px;
248
- padding: 12px;
249
- position: relative;
250
- overflow: hidden;
251
- animation: fadeIn 0.5s ease-out backwards;
252
- color: inherit;
253
- display: block;
254
- }
255
-
256
- .pane-card:nth-child(1) { animation-delay: 0.1s; }
257
- .pane-card:nth-child(2) { animation-delay: 0.15s; }
258
- .pane-card:nth-child(3) { animation-delay: 0.2s; }
259
- .pane-card:nth-child(4) { animation-delay: 0.25s; }
260
- .pane-card:nth-child(5) { animation-delay: 0.3s; }
261
- .pane-card:nth-child(6) { animation-delay: 0.35s; }
262
-
263
- .pane-header {
264
- margin-bottom: 16px;
265
- display: flex;
266
- align-items: flex-start;
267
- justify-content: space-between;
268
- gap: 12px;
269
- position: relative;
270
- }
271
-
272
- .pane-header-content {
273
- flex: 1;
274
- display: flex;
275
- flex-direction: column;
276
- gap: 6px;
277
- }
278
-
279
- .action-menu-btn {
280
- background: transparent;
281
- border: none;
282
- color: var(--text-tertiary);
283
- font-size: 20px;
284
- padding: 4px 8px;
285
- cursor: pointer;
286
- transition: all 0.2s ease;
287
- line-height: 1;
288
- flex-shrink: 0;
289
- }
290
-
291
- .action-menu-btn:hover {
292
- color: var(--text-primary);
293
- background: var(--button-hover-bg);
294
- border-radius: 4px;
295
- }
296
-
297
- .action-menu-dropdown {
298
- position: absolute;
299
- top: 32px;
300
- right: 0;
301
- background: var(--tooltip-bg);
302
- backdrop-filter: blur(20px);
303
- -webkit-backdrop-filter: blur(20px);
304
- border: 1px solid var(--tooltip-border);
305
- border-radius: 8px;
306
- padding: 4px;
307
- z-index: 100;
308
- min-width: 180px;
309
- box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
310
- animation: fadeIn 0.2s ease-out;
311
- }
312
-
313
- .action-menu-item {
314
- width: 100%;
315
- display: flex;
316
- align-items: center;
317
- gap: 8px;
318
- padding: 8px 12px;
319
- background: transparent;
320
- border: none;
321
- border-radius: 4px;
322
- color: var(--text-primary);
323
- font-size: 13px;
324
- cursor: pointer;
325
- transition: all 0.15s ease;
326
- text-align: left;
327
- }
328
-
329
- .action-menu-item:hover:not(:disabled) {
330
- background: var(--button-hover-bg);
331
- }
332
-
333
- .action-menu-item:disabled {
334
- opacity: 0.5;
335
- cursor: not-allowed;
336
- }
337
-
338
- .action-icon {
339
- font-size: 14px;
340
- width: 16px;
341
- text-align: center;
342
- }
343
-
344
- .action-label {
345
- flex: 1;
346
- }
347
-
348
- .pane-title-link {
349
- display: inline-flex;
350
- align-items: center;
351
- gap: 8px;
352
- text-decoration: none;
353
- color: inherit;
354
- width: fit-content;
355
- }
356
-
357
- .pane-title-link:hover .pane-title {
358
- text-decoration: underline;
359
- }
360
-
361
- .pane-title {
362
- font-size: 20px;
363
- font-weight: 600;
364
- color: var(--text-bright);
365
- letter-spacing: -0.3px;
366
- }
367
-
368
- .pane-arrow {
369
- font-size: 16px;
370
- color: var(--text-secondary);
371
- transition: all 0.2s ease;
372
- opacity: 0.6;
373
- }
374
-
375
- .pane-title-link:hover .pane-arrow {
376
- color: #ff8c00;
377
- transform: translateX(2px);
378
- opacity: 1;
379
- }
380
-
381
- .pane-meta {
382
- display: flex;
383
- align-items: center;
384
- gap: 12px;
385
- }
386
-
387
- .pane-agent {
388
- padding: 2px 8px;
389
- border-radius: 4px;
390
- font-size: 10px;
391
- font-weight: 600;
392
- text-transform: uppercase;
393
- letter-spacing: 0.3px;
394
- background: var(--agent-bg);
395
- border: 1px solid var(--agent-border);
396
- color: var(--text-tertiary);
397
- white-space: nowrap;
398
- }
399
-
400
- .pane-agent.claude {
401
- background: rgba(217, 119, 87, 0.15);
402
- border-color: rgba(217, 119, 87, 0.3);
403
- color: #D97757;
404
- }
405
-
406
- .pane-agent.opencode {
407
- background: rgba(102, 126, 234, 0.15);
408
- border-color: rgba(102, 126, 234, 0.3);
409
- color: #667eea;
410
- }
411
-
412
- .pane-prompt-section {
413
- margin-bottom: 12px;
414
- }
415
-
416
- .pane-prompt-header {
417
- display: flex;
418
- justify-content: space-between;
419
- align-items: center;
420
- padding: 6px 8px;
421
- background: var(--input-bg);
422
- border: 1px solid var(--input-border);
423
- border-radius: 6px;
424
- cursor: pointer;
425
- transition: all 0.2s ease;
426
- }
427
-
428
- .pane-prompt-header:hover {
429
- background: var(--input-focus-bg);
430
- border-color: var(--input-border);
431
- }
432
-
433
- .prompt-label {
434
- font-size: 11px;
435
- font-weight: 600;
436
- text-transform: uppercase;
437
- letter-spacing: 0.5px;
438
- color: var(--text-tertiary);
439
- }
440
-
441
- .expand-icon {
442
- font-size: 10px;
443
- color: var(--text-tertiary);
444
- transition: transform 0.2s ease;
445
- }
446
-
447
- .pane-prompt {
448
- color: var(--text-secondary);
449
- font-size: 13px;
450
- margin-top: 8px;
451
- padding: 8px 12px;
452
- line-height: 1.6;
453
- background: var(--input-bg);
454
- border: 1px solid var(--input-border);
455
- border-radius: 6px;
456
- font-family: 'SF Mono', Monaco, 'Courier New', monospace;
457
- }
458
-
459
- .agent-summary {
460
- color: var(--text-secondary);
461
- font-size: 13px;
462
- margin-bottom: 12px;
463
- padding: 10px 12px;
464
- line-height: 1.5;
465
- background: rgba(96, 165, 250, 0.08);
466
- border: 1px solid rgba(96, 165, 250, 0.2);
467
- border-radius: 6px;
468
- font-style: italic;
469
- }
470
-
471
- .tooltip {
472
- position: absolute;
473
- background: var(--tooltip-bg);
474
- backdrop-filter: blur(20px);
475
- -webkit-backdrop-filter: blur(20px);
476
- border: 1px solid var(--tooltip-border);
477
- padding: 16px;
478
- border-radius: 12px;
479
- z-index: 1000;
480
- white-space: pre-wrap;
481
- max-width: 400px;
482
- max-height: 200px;
483
- overflow-y: auto;
484
- box-shadow:
485
- 0 20px 60px rgba(0, 0, 0, 0.3),
486
- 0 0 0 1px var(--border-color);
487
- font-size: 13px;
488
- color: var(--text-primary);
489
- pointer-events: none;
490
- animation: fadeIn 0.2s ease-out;
491
- }
492
-
493
- .pane-status {
494
- display: flex;
495
- flex-direction: column;
496
- gap: 10px;
497
- }
498
-
499
- .status-item {
500
- display: flex;
501
- justify-content: space-between;
502
- align-items: center;
503
- font-size: 13px;
504
- padding: 8px 0;
505
- }
506
-
507
- .status-label {
508
- color: var(--text-tertiary);
509
- font-weight: 500;
510
- }
511
-
512
- .status-value {
513
- display: flex;
514
- align-items: center;
515
- gap: 6px;
516
- }
517
-
518
- .status-badge {
519
- padding: 4px 10px;
520
- border-radius: 6px;
521
- font-size: 11px;
522
- font-weight: 600;
523
- text-transform: uppercase;
524
- letter-spacing: 0.3px;
525
- transition: all 0.2s ease;
526
- }
527
-
528
- .status-badge.working {
529
- background: linear-gradient(135deg, #fbbf24 0%, #f59e0b 100%);
530
- color: #000;
531
- box-shadow: 0 2px 8px rgba(251, 191, 36, 0.4);
532
- }
533
-
534
- .status-badge.waiting {
535
- background: linear-gradient(135deg, #60a5fa 0%, #3b82f6 100%);
536
- color: #000;
537
- box-shadow: 0 2px 8px rgba(96, 165, 250, 0.4);
538
- }
539
-
540
- .status-badge.idle {
541
- background: var(--idle-badge-bg);
542
- color: var(--text-tertiary);
543
- border: 1px solid var(--idle-badge-border);
544
- }
545
-
546
- .status-badge.running {
547
- background: linear-gradient(135deg, #4ade80 0%, #22c55e 100%);
548
- color: #000;
549
- box-shadow: 0 2px 8px rgba(74, 222, 128, 0.4);
550
- }
551
-
552
- .status-badge.passed {
553
- background: linear-gradient(135deg, #4ade80 0%, #22c55e 100%);
554
- color: #000;
555
- box-shadow: 0 2px 8px rgba(74, 222, 128, 0.4);
556
- }
557
-
558
- .status-badge.failed {
559
- background: linear-gradient(135deg, #f87171 0%, #ef4444 100%);
560
- color: #000;
561
- box-shadow: 0 2px 8px rgba(248, 113, 113, 0.4);
562
- }
563
-
564
- .status-badge.analyzing {
565
- background: linear-gradient(135deg, #a78bfa 0%, #8b5cf6 100%);
566
- color: #000;
567
- box-shadow: 0 2px 8px rgba(167, 139, 250, 0.4);
568
- animation: pulse 2s ease-in-out infinite;
569
- }
570
-
571
- .pane-id {
572
- font-family: 'SF Mono', Monaco, monospace;
573
- font-size: 10px;
574
- color: var(--text-dimmer);
575
- font-weight: 500;
576
- letter-spacing: 0.2px;
577
- }
578
-
579
- /* Interactive Area Styles */
580
- .pane-interactive {
581
- margin-top: 12px;
582
- }
583
-
584
- .options-dialog {
585
- display: flex;
586
- flex-direction: column;
587
- gap: 12px;
588
- }
589
-
590
- .options-question {
591
- font-size: 14px;
592
- font-weight: 500;
593
- color: var(--text-primary);
594
- line-height: 1.4;
595
- }
596
-
597
- .options-warning {
598
- padding: 8px 12px;
599
- background: rgba(248, 113, 113, 0.1);
600
- border: 1px solid rgba(248, 113, 113, 0.3);
601
- border-radius: 6px;
602
- color: #fca5a5;
603
- font-size: 12px;
604
- display: flex;
605
- align-items: center;
606
- gap: 6px;
607
- }
608
-
609
- .options-buttons {
610
- display: flex;
611
- flex-wrap: wrap;
612
- gap: 8px;
613
- }
614
-
615
- .option-button {
616
- padding: 8px 16px;
617
- background: linear-gradient(135deg, #60a5fa 0%, #3b82f6 100%);
618
- color: #000;
619
- border: none;
620
- border-radius: 6px;
621
- font-size: 13px;
622
- font-weight: 600;
623
- cursor: pointer;
624
- transition: all 0.2s ease;
625
- box-shadow: 0 2px 8px rgba(96, 165, 250, 0.3);
626
- }
627
-
628
- .option-button:hover {
629
- transform: translateY(-2px);
630
- box-shadow: 0 4px 12px rgba(96, 165, 250, 0.4);
631
- }
632
-
633
- .option-button:active {
634
- transform: translateY(0);
635
- }
636
-
637
- .option-button-danger {
638
- background: linear-gradient(135deg, #f87171 0%, #ef4444 100%);
639
- box-shadow: 0 2px 8px rgba(248, 113, 113, 0.3);
640
- }
641
-
642
- .option-button-danger:hover {
643
- box-shadow: 0 4px 12px rgba(248, 113, 113, 0.4);
644
- }
645
-
646
- .analyzing-state {
647
- display: flex;
648
- align-items: center;
649
- gap: 12px;
650
- padding: 8px 0;
651
- color: #a78bfa;
652
- font-size: 14px;
653
- font-weight: 500;
654
- }
655
-
656
- .loader-spinner {
657
- width: 20px;
658
- height: 20px;
659
- border: 3px solid rgba(167, 139, 250, 0.2);
660
- border-top-color: #a78bfa;
661
- border-radius: 50%;
662
- animation: spin 1s linear infinite;
663
- }
664
-
665
- @keyframes spin {
666
- to { transform: rotate(360deg); }
667
- }
668
-
669
- .prompt-input-wrapper {
670
- display: flex;
671
- align-items: flex-start;
672
- gap: 8px;
673
- padding: 8px;
674
- background: var(--input-bg);
675
- border: 1px solid var(--input-border);
676
- border-radius: 8px;
677
- transition: all 0.2s ease;
678
- }
679
-
680
- .prompt-input-wrapper:focus-within {
681
- border-color: var(--input-focus-border);
682
- background: var(--input-focus-bg);
683
- box-shadow: 0 0 0 3px var(--input-focus-shadow);
684
- }
685
-
686
- .queued-message {
687
- margin-top: 8px;
688
- padding: 6px 10px;
689
- background: rgba(74, 222, 128, 0.1);
690
- border: 1px solid rgba(74, 222, 128, 0.3);
691
- border-radius: 6px;
692
- color: #4ade80;
693
- font-size: 12px;
694
- animation: fadeIn 0.3s ease-out;
695
- }
696
-
697
- .prompt-textarea {
698
- flex: 1;
699
- min-height: 20px;
700
- max-height: 150px;
701
- padding: 0;
702
- background: transparent;
703
- border: none;
704
- color: var(--text-primary);
705
- font-family: 'SF Mono', Monaco, 'Courier New', monospace;
706
- font-size: 13px;
707
- line-height: 1.4;
708
- resize: none;
709
- overflow-y: auto;
710
- }
711
-
712
- .prompt-textarea:focus {
713
- outline: none;
714
- }
715
-
716
- .prompt-textarea:disabled {
717
- opacity: 0.5;
718
- cursor: not-allowed;
719
- }
720
-
721
- .prompt-textarea::placeholder {
722
- color: var(--text-dimmer);
723
- }
724
-
725
- .send-button {
726
- flex-shrink: 0;
727
- width: 28px;
728
- height: 28px;
729
- padding: 6px;
730
- background: var(--button-bg);
731
- color: var(--text-secondary);
732
- border: 1px solid var(--button-border);
733
- border-radius: 50%;
734
- cursor: pointer;
735
- transition: all 0.2s ease;
736
- display: flex;
737
- align-items: center;
738
- justify-content: center;
739
- }
740
-
741
- .send-button:hover:not(:disabled) {
742
- background: var(--button-hover-bg);
743
- border-color: var(--button-hover-border);
744
- }
745
-
746
- .send-button:active:not(:disabled) {
747
- transform: scale(0.92);
748
- }
749
-
750
- .send-button:disabled {
751
- opacity: 0.3;
752
- cursor: not-allowed;
753
- }
754
-
755
- .send-button svg {
756
- width: 100%;
757
- height: 100%;
758
- fill: currentColor;
759
- }
760
-
761
- .button-loader {
762
- width: 14px;
763
- height: 14px;
764
- border: 2px solid rgba(0, 0, 0, 0.2);
765
- border-top-color: #000;
766
- border-radius: 50%;
767
- animation: spin 0.8s linear infinite;
768
- }
769
-
770
- .dev-server-status {
771
- margin-top: 12px;
772
- padding-top: 12px;
773
- border-top: 1px solid rgba(255, 255, 255, 0.08);
774
- display: flex;
775
- align-items: center;
776
- gap: 8px;
777
- font-size: 12px;
778
- }
779
-
780
- .dev-link {
781
- color: #ff8c00;
782
- text-decoration: none;
783
- font-weight: 600;
784
- transition: color 0.2s ease;
785
- }
786
-
787
- .dev-link:hover {
788
- color: #ffa500;
789
- }
790
-
791
- .no-panes {
792
- text-align: center;
793
- padding: 100px 20px;
794
- color: var(--text-tertiary);
795
- animation: fadeIn 0.6s ease-out;
796
- }
797
-
798
- .no-panes p {
799
- margin-bottom: 16px;
800
- font-size: 18px;
801
- font-weight: 500;
802
- }
803
-
804
- .hint {
805
- font-size: 14px;
806
- color: var(--text-dim);
807
- background: var(--hint-bg);
808
- padding: 12px 24px;
809
- border-radius: 12px;
810
- display: inline-block;
811
- margin-top: 8px;
812
- }
813
-
814
- footer {
815
- padding: 12px 0;
816
- margin-top: auto;
817
- animation: fadeIn 0.8s ease-out;
818
- }
819
-
820
- .footer-info {
821
- display: flex;
822
- justify-content: space-between;
823
- font-size: 11px;
824
- color: var(--text-dim);
825
- padding: 0;
826
- }
827
-
828
- .footer-info span {
829
- display: flex;
830
- align-items: center;
831
- gap: 8px;
832
- }
833
-
834
- @media (max-width: 768px) {
835
- .container {
836
- padding: 0 16px 24px;
837
- }
838
-
839
- header {
840
- padding: 12px 18px;
841
- gap: 8px;
842
- }
843
-
844
- .logo {
845
- height: 20px;
846
- }
847
-
848
- h1 {
849
- font-size: 14px;
850
- max-width: none;
851
- }
852
-
853
- .session-info {
854
- font-size: 11px;
855
- gap: 8px;
856
- }
857
-
858
- .session-info span:not(.status-indicator) {
859
- display: none;
860
- }
861
-
862
- main {
863
- padding-top: 24px;
864
- }
865
-
866
- .panes-grid {
867
- grid-template-columns: 1fr;
868
- gap: 16px;
869
- }
870
-
871
- .footer-info {
872
- flex-direction: column;
873
- gap: 6px;
874
- font-size: 10px;
875
- }
876
- }
877
-
878
- /* Terminal text colors */
879
- .term-fg-black { color: #000000; }
880
- .term-fg-red { color: #cd3131; }
881
- .term-fg-green { color: #0dbc79; }
882
- .term-fg-yellow { color: #e5e510; }
883
- .term-fg-blue { color: #2472c8; }
884
- .term-fg-magenta { color: #bc3fbc; }
885
- .term-fg-cyan { color: #11a8cd; }
886
- .term-fg-white { color: #e5e5e5; }
887
-
888
- .term-fg-bright-black { color: #666666; }
889
- .term-fg-bright-red { color: #f14c4c; }
890
- .term-fg-bright-green { color: #23d18b; }
891
- .term-fg-bright-yellow { color: #f5f543; }
892
- .term-fg-bright-blue { color: #3b8eea; }
893
- .term-fg-bright-magenta { color: #d670d6; }
894
- .term-fg-bright-cyan { color: #29b8db; }
895
- .term-fg-bright-white { color: #ffffff; }
896
-
897
- .term-bg-black { background-color: #000000; }
898
- .term-bg-red { background-color: #cd3131; }
899
- .term-bg-green { background-color: #0dbc79; }
900
- .term-bg-yellow { background-color: #e5e510; }
901
- .term-bg-blue { background-color: #2472c8; }
902
- .term-bg-magenta { background-color: #bc3fbc; }
903
- .term-bg-cyan { background-color: #11a8cd; }
904
- .term-bg-white { background-color: #e5e5e5; }
905
-
906
- .term-bold { font-weight: bold; }
907
- .term-dim { opacity: 0.7; }
908
- .term-italic { font-style: italic; }
909
- .term-underline { text-decoration: underline; }
910
- .term-strikethrough { text-decoration: line-through; }
911
-
912
- /* Cursor styling - disabled by default for Ink apps */
913
- .term-cursor {
914
- /* Cursor hidden by default since Ink apps don't have meaningful cursor position */
915
- /* background-color: rgba(255, 255, 255, 0.3); */
916
- /* animation: cursor-blink 1s step-end infinite; */
917
- }
918
-
919
- /* Uncomment to enable cursor display */
920
- /*
921
- .term-cursor {
922
- background-color: rgba(255, 255, 255, 0.3);
923
- animation: cursor-blink 1s step-end infinite;
924
- }
925
-
926
- @keyframes cursor-blink {
927
- 0%, 50% { background-color: rgba(255, 255, 255, 0.3); }
928
- 51%, 100% { background-color: transparent; }
929
- }
930
- */
931
-
932
- /* Action Dialog Styles */
933
- .action-dialog-overlay {
934
- position: fixed;
935
- top: 0;
936
- left: 0;
937
- right: 0;
938
- bottom: 0;
939
- background: rgba(0, 0, 0, 0.7);
940
- backdrop-filter: blur(4px);
941
- -webkit-backdrop-filter: blur(4px);
942
- display: flex;
943
- align-items: center;
944
- justify-content: center;
945
- z-index: 1000;
946
- animation: fadeIn 0.2s ease-out;
947
- }
948
-
949
- .action-dialog {
950
- background: var(--card-bg);
951
- backdrop-filter: blur(20px);
952
- -webkit-backdrop-filter: blur(20px);
953
- border: 1px solid var(--card-border);
954
- border-radius: 16px;
955
- padding: 24px;
956
- max-width: 500px;
957
- width: 90%;
958
- box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4);
959
- animation: slideInFromTop 0.3s ease-out;
960
- }
961
-
962
- .action-dialog h3 {
963
- margin: 0 0 12px 0;
964
- font-size: 20px;
965
- font-weight: 600;
966
- color: var(--text-bright);
967
- }
968
-
969
- .action-dialog p {
970
- margin: 0 0 20px 0;
971
- color: var(--text-secondary);
972
- font-size: 14px;
973
- line-height: 1.5;
974
- }
975
-
976
- .dialog-buttons {
977
- display: flex;
978
- gap: 12px;
979
- justify-content: flex-end;
980
- }
981
-
982
- .dialog-btn {
983
- padding: 10px 20px;
984
- border: 1px solid var(--button-border);
985
- border-radius: 8px;
986
- background: var(--button-bg);
987
- color: var(--text-primary);
988
- font-size: 14px;
989
- font-weight: 500;
990
- cursor: pointer;
991
- transition: all 0.2s ease;
992
- }
993
-
994
- .dialog-btn:hover {
995
- background: var(--button-hover-bg);
996
- border-color: var(--button-hover-border);
997
- }
998
-
999
- .dialog-btn-primary {
1000
- background: linear-gradient(135deg, #60a5fa 0%, #3b82f6 100%);
1001
- color: #000;
1002
- border-color: transparent;
1003
- box-shadow: 0 2px 8px rgba(96, 165, 250, 0.3);
1004
- }
1005
-
1006
- .dialog-btn-primary:hover {
1007
- transform: translateY(-1px);
1008
- box-shadow: 0 4px 12px rgba(96, 165, 250, 0.4);
1009
- }
1010
-
1011
- .choice-options {
1012
- display: flex;
1013
- flex-direction: column;
1014
- gap: 8px;
1015
- }
1016
-
1017
- .choice-option-btn {
1018
- width: 100%;
1019
- padding: 12px 16px;
1020
- background: var(--button-bg);
1021
- border: 1px solid var(--button-border);
1022
- border-radius: 8px;
1023
- color: var(--text-primary);
1024
- font-size: 14px;
1025
- text-align: left;
1026
- cursor: pointer;
1027
- transition: all 0.2s ease;
1028
- display: flex;
1029
- flex-direction: column;
1030
- gap: 4px;
1031
- }
1032
-
1033
- .choice-option-btn:hover {
1034
- background: var(--button-hover-bg);
1035
- border-color: var(--button-hover-border);
1036
- transform: translateX(4px);
1037
- }
1038
-
1039
- .choice-option-btn.danger {
1040
- border-color: rgba(248, 113, 113, 0.3);
1041
- background: rgba(248, 113, 113, 0.1);
1042
- }
1043
-
1044
- .choice-option-btn.danger:hover {
1045
- border-color: rgba(248, 113, 113, 0.5);
1046
- background: rgba(248, 113, 113, 0.2);
1047
- }
1048
-
1049
- .option-description {
1050
- font-size: 12px;
1051
- color: var(--text-tertiary);
1052
- font-weight: normal;
1053
- }
1054
-
1055
- /* Terminal Page Layout */
1056
- .terminal-page {
1057
- display: flex;
1058
- flex-direction: column;
1059
- height: 100vh;
1060
- background: #000;
1061
- }
1062
-
1063
- .back-button {
1064
- color: #e0e0e0;
1065
- text-decoration: none;
1066
- font-size: 14px;
1067
- font-weight: 500;
1068
- transition: color 0.2s;
1069
- white-space: nowrap;
1070
- flex-shrink: 0;
1071
- }
1072
-
1073
- .back-button:hover {
1074
- color: #fff;
1075
- }
1076
-
1077
- .terminal-content {
1078
- flex: 1;
1079
- overflow: auto;
1080
- padding: 10px;
1081
- }
1082
-
1083
- .terminal-page .terminal-output {
1084
- font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', monospace;
1085
- line-height: 1.0;
1086
- color: #f0f0f0;
1087
- margin: 0;
1088
- min-height: 100%;
1089
- }
1090
-
1091
- .terminal-row {
1092
- white-space: pre;
1093
- margin: 0;
1094
- padding: 0;
1095
- line-height: 1.0;
1096
- }
1097
-
1098
- /* Mobile toolbar */
1099
- .mobile-toolbar {
1100
- display: flex;
1101
- gap: 6px;
1102
- padding: 8px;
1103
- background: #1a1a1a;
1104
- border-bottom: 1px solid #333;
1105
- overflow-x: auto;
1106
- flex-wrap: nowrap;
1107
- }
1108
-
1109
- .toolbar-key {
1110
- background: #2d2d2d;
1111
- border: 1px solid #444;
1112
- border-radius: 4px;
1113
- color: #e0e0e0;
1114
- padding: 8px 12px;
1115
- font-size: 13px;
1116
- font-family: 'SF Mono', Monaco, monospace;
1117
- cursor: pointer;
1118
- flex-shrink: 0;
1119
- min-width: 44px;
1120
- transition: all 0.15s;
1121
- user-select: none;
1122
- -webkit-tap-highlight-color: transparent;
1123
- }
1124
-
1125
- .toolbar-key:active {
1126
- background: #3d3d3d;
1127
- transform: scale(0.95);
1128
- }
1129
-
1130
- .toolbar-key.active {
1131
- background: #667eea;
1132
- border-color: #667eea;
1133
- color: #fff;
1134
- }
1135
-
1136
- /* Hidden mobile input */
1137
- .mobile-input {
1138
- position: absolute;
1139
- left: -9999px;
1140
- width: 1px;
1141
- height: 1px;
1142
- opacity: 0.01;
1143
- pointer-events: none;
1144
- }
1145
-
1146
- /* Actions bar */
1147
- .actions-bar {
1148
- display: flex;
1149
- justify-content: flex-end;
1150
- margin-bottom: 20px;
1151
- padding: 0 4px;
1152
- }
1153
-
1154
- .create-pane-button {
1155
- display: flex;
1156
- align-items: center;
1157
- gap: 6px;
1158
- padding: 6px 12px;
1159
- background: var(--button-bg);
1160
- color: var(--text-primary);
1161
- border: 1px solid var(--button-border);
1162
- border-radius: 6px;
1163
- font-size: 13px;
1164
- font-weight: 500;
1165
- cursor: pointer;
1166
- transition: all 0.15s ease;
1167
- }
1168
-
1169
- .create-pane-button:hover:not(:disabled) {
1170
- background: var(--button-hover-bg);
1171
- border-color: var(--button-hover-border);
1172
- }
1173
-
1174
- .create-pane-button:disabled {
1175
- opacity: 0.5;
1176
- cursor: not-allowed;
1177
- }
1178
-
1179
- .create-pane-button svg {
1180
- width: 14px;
1181
- height: 14px;
1182
- fill: none;
1183
- }
1184
-
1185
- /* Modal styles */
1186
- .modal-overlay {
1187
- position: fixed;
1188
- top: 0;
1189
- left: 0;
1190
- width: 100%;
1191
- height: 100%;
1192
- background: rgba(0, 0, 0, 0.7);
1193
- backdrop-filter: blur(8px);
1194
- -webkit-backdrop-filter: blur(8px);
1195
- display: flex;
1196
- align-items: center;
1197
- justify-content: center;
1198
- z-index: 2000;
1199
- animation: fadeIn 0.2s ease-out;
1200
- }
1201
-
1202
- .modal-dialog {
1203
- background: var(--card-bg);
1204
- backdrop-filter: blur(20px);
1205
- -webkit-backdrop-filter: blur(20px);
1206
- border: 1px solid var(--card-border);
1207
- border-radius: 16px;
1208
- width: 90%;
1209
- max-width: 600px;
1210
- max-height: 90vh;
1211
- overflow: hidden;
1212
- display: flex;
1213
- flex-direction: column;
1214
- box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4);
1215
- animation: slideUp 0.3s ease-out;
1216
- }
1217
-
1218
- @keyframes slideUp {
1219
- from {
1220
- opacity: 0;
1221
- transform: translateY(20px);
1222
- }
1223
- to {
1224
- opacity: 1;
1225
- transform: translateY(0);
1226
- }
1227
- }
1228
-
1229
- .modal-header {
1230
- display: flex;
1231
- align-items: center;
1232
- justify-content: space-between;
1233
- padding: 20px 24px;
1234
- border-bottom: 1px solid var(--border-color);
1235
- }
1236
-
1237
- .modal-header h2 {
1238
- font-size: 20px;
1239
- font-weight: 600;
1240
- color: var(--text-primary);
1241
- margin: 0;
1242
- }
1243
-
1244
- .modal-close {
1245
- background: none;
1246
- border: none;
1247
- color: var(--text-secondary);
1248
- font-size: 32px;
1249
- line-height: 1;
1250
- cursor: pointer;
1251
- padding: 0;
1252
- width: 32px;
1253
- height: 32px;
1254
- display: flex;
1255
- align-items: center;
1256
- justify-content: center;
1257
- border-radius: 6px;
1258
- transition: all 0.2s ease;
1259
- }
1260
-
1261
- .modal-close:hover {
1262
- background: var(--button-hover-bg);
1263
- color: var(--text-primary);
1264
- }
1265
-
1266
- .modal-body {
1267
- padding: 24px;
1268
- overflow-y: auto;
1269
- flex: 1;
1270
- }
1271
-
1272
- .form-group {
1273
- margin-bottom: 20px;
1274
- }
1275
-
1276
- .form-group:last-child {
1277
- margin-bottom: 0;
1278
- }
1279
-
1280
- .form-group label {
1281
- display: block;
1282
- font-size: 14px;
1283
- font-weight: 600;
1284
- color: var(--text-primary);
1285
- margin-bottom: 8px;
1286
- }
1287
-
1288
- .modal-textarea {
1289
- width: 100%;
1290
- padding: 12px;
1291
- background: var(--input-bg);
1292
- border: 1px solid var(--input-border);
1293
- border-radius: 8px;
1294
- color: var(--text-primary);
1295
- font-size: 14px;
1296
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
1297
- resize: vertical;
1298
- min-height: 100px;
1299
- transition: all 0.2s ease;
1300
- }
1301
-
1302
- .modal-textarea:focus {
1303
- outline: none;
1304
- border-color: var(--input-focus-border);
1305
- background: var(--input-focus-bg);
1306
- box-shadow: 0 0 0 3px var(--input-focus-shadow);
1307
- }
1308
-
1309
- .modal-textarea:disabled {
1310
- opacity: 0.5;
1311
- cursor: not-allowed;
1312
- }
1313
-
1314
- .input-hint {
1315
- font-size: 12px;
1316
- color: var(--text-tertiary);
1317
- margin-top: 6px;
1318
- }
1319
-
1320
- .agent-selector {
1321
- display: flex;
1322
- gap: 12px;
1323
- }
1324
-
1325
- .agent-option {
1326
- flex: 1;
1327
- display: flex;
1328
- align-items: center;
1329
- gap: 12px;
1330
- padding: 12px 20px;
1331
- background: var(--button-bg);
1332
- border: 2px solid var(--button-border);
1333
- border-radius: 8px;
1334
- color: var(--text-primary);
1335
- font-size: 14px;
1336
- font-weight: 600;
1337
- text-transform: capitalize;
1338
- cursor: pointer;
1339
- transition: all 0.2s ease;
1340
- }
1341
-
1342
- .agent-logo {
1343
- width: 40px;
1344
- height: 40px;
1345
- flex-shrink: 0;
1346
- }
1347
-
1348
- .agent-option:hover:not(:disabled) {
1349
- background: var(--button-hover-bg);
1350
- border-color: var(--button-hover-border);
1351
- }
1352
-
1353
- .agent-option.selected {
1354
- background: var(--input-focus-bg);
1355
- border-color: var(--input-focus-border);
1356
- color: var(--text-bright);
1357
- }
1358
-
1359
- .agent-option:disabled {
1360
- opacity: 0.5;
1361
- cursor: not-allowed;
1362
- }
1363
-
1364
- .modal-footer {
1365
- display: flex;
1366
- gap: 12px;
1367
- padding: 20px 24px;
1368
- border-top: 1px solid var(--border-color);
1369
- justify-content: flex-end;
1370
- }
1371
-
1372
- .modal-button {
1373
- padding: 10px 24px;
1374
- border-radius: 8px;
1375
- font-size: 14px;
1376
- font-weight: 600;
1377
- cursor: pointer;
1378
- transition: all 0.2s ease;
1379
- border: none;
1380
- display: flex;
1381
- align-items: center;
1382
- gap: 8px;
1383
- }
1384
-
1385
- .modal-button-secondary {
1386
- background: var(--button-bg);
1387
- color: var(--text-primary);
1388
- border: 1px solid var(--button-border);
1389
- }
1390
-
1391
- .modal-button-secondary:hover:not(:disabled) {
1392
- background: var(--button-hover-bg);
1393
- border-color: var(--button-hover-border);
1394
- }
1395
-
1396
- .modal-button-primary {
1397
- background: var(--input-focus-bg);
1398
- color: var(--text-bright);
1399
- border: 1px solid var(--input-focus-border);
1400
- }
1401
-
1402
- .modal-button-primary:hover:not(:disabled) {
1403
- background: var(--button-hover-bg);
1404
- border-color: var(--button-hover-border);
1405
- }
1406
-
1407
- .modal-button:disabled {
1408
- opacity: 0.5;
1409
- cursor: not-allowed;
1410
- }
1411
-
1412
- .button-loader {
1413
- width: 16px;
1414
- height: 16px;
1415
- border: 2px solid rgba(255, 255, 255, 0.3);
1416
- border-top-color: #fff;
1417
- border-radius: 50%;
1418
- animation: spin 0.6s linear infinite;
1419
- }
1420
-
1421
- @keyframes spin {
1422
- to { transform: rotate(360deg); }
1423
- }`;
1424
- }
1425
- export function getDashboardJs() {
1426
- return `// Dashboard with Vue.js
1427
- import { createApp } from '/vue.esm-browser.js';
1428
-
1429
- let refreshInterval = null;
1430
-
1431
- const app = createApp({
1432
- data() {
1433
- return {
1434
- projectName: 'Loading...',
1435
- sessionName: '',
1436
- connected: false,
1437
- panes: [],
1438
- lastUpdate: null,
1439
- timeSinceUpdate: 'Never',
1440
- promptInputs: {}, // Map of pane ID to prompt text
1441
- sendingPrompts: new Set(), // Set of pane IDs currently sending
1442
- queuedMessages: {}, // Map of pane ID to temporary "queued" message
1443
- theme: localStorage.getItem('dmux-theme') || 'dark', // Theme state
1444
- expandedPrompts: new Set(), // Set of pane IDs with expanded initial prompts
1445
- loadingOptions: new Set(), // Set of pane IDs with loading option dialogs
1446
- showCreateDialog: false,
1447
- newPanePrompt: '',
1448
- newPaneAgent: null,
1449
- creatingPane: false,
1450
- availableAgents: [],
1451
- needsAgentChoice: false,
1452
- createStep: 'prompt', // 'prompt' or 'agent'
1453
- // Action system
1454
- actions: [], // Available actions from API
1455
- paneActions: {}, // Map of pane ID to available actions
1456
- showActionMenu: null, // Pane ID with open action menu
1457
- actionDialog: null, // Current action dialog { type: 'confirm'|'choice', ... }
1458
- executingAction: false // Whether an action is currently executing
1459
- };
1460
- },
1461
- template: \`
1462
- <header>
1463
- <img src="https://cdn.formk.it/dmux/dmux.png" alt="dmux" class="logo" />
1464
- <h1>{{ projectName }}</h1>
1465
- <div class="session-info">
1466
- <button @click="toggleTheme" class="theme-toggle" :title="theme === 'dark' ? 'Switch to light mode' : 'Switch to dark mode'">
1467
- <svg v-if="theme === 'dark'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
1468
- <path d="M12 2.25a.75.75 0 01.75.75v2.25a.75.75 0 01-1.5 0V3a.75.75 0 01.75-.75zM7.5 12a4.5 4.5 0 119 0 4.5 4.5 0 01-9 0zM18.894 6.166a.75.75 0 00-1.06-1.06l-1.591 1.59a.75.75 0 101.06 1.061l1.591-1.59zM21.75 12a.75.75 0 01-.75.75h-2.25a.75.75 0 010-1.5H21a.75.75 0 01.75.75zM17.834 18.894a.75.75 0 001.06-1.06l-1.59-1.591a.75.75 0 10-1.061 1.06l1.59 1.591zM12 18a.75.75 0 01.75.75V21a.75.75 0 01-1.5 0v-2.25A.75.75 0 0112 18zM7.758 17.303a.75.75 0 00-1.061-1.06l-1.591 1.59a.75.75 0 001.06 1.061l1.591-1.59zM6 12a.75.75 0 01-.75.75H3a.75.75 0 010-1.5h2.25A.75.75 0 016 12zM6.697 7.757a.75.75 0 001.06-1.06l-1.59-1.591a.75.75 0 00-1.061 1.06l1.59 1.591z"/>
1469
- </svg>
1470
- <svg v-else xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
1471
- <path d="M9.528 1.718a.75.75 0 01.162.819A8.97 8.97 0 009 6.5a9 9 0 009 9 8.97 8.97 0 003.963-.69.75.75 0 01.981.98 10.503 10.503 0 01-9.694 6.46c-5.799 0-10.5-4.701-10.5-10.5 0-4.368 2.667-8.112 6.46-9.694a.75.75 0 01.818.162z"/>
1472
- </svg>
1473
- </button>
1474
- <span v-if="sessionName">{{ sessionName }}</span>
1475
- <span class="status-indicator" :style="{ color: connected ? '#4ade80' : '#f87171' }">●</span>
1476
- </div>
1477
- </header>
1478
-
1479
- <div class="container">
1480
- <main>
1481
- <div class="actions-bar">
1482
- <button @click="openCreateDialog" class="create-pane-button" :disabled="creatingPane">
1483
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
1484
- <path d="M12 4.5v15m7.5-7.5h-15" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
1485
- </svg>
1486
- Create New Pane
1487
- </button>
1488
- </div>
1489
-
1490
- <div v-if="panes.length === 0" class="no-panes">
1491
- <p>No dmux panes active</p>
1492
- <p class="hint">Click "Create New Pane" above or press 'n' in dmux</p>
1493
- </div>
1494
-
1495
- <div v-else class="panes-grid">
1496
- <div
1497
- v-for="pane in panes"
1498
- :key="pane.id"
1499
- class="pane-card"
1500
- >
1501
- <div class="pane-header">
1502
- <div class="pane-header-content">
1503
- <a :href="'/panes/' + pane.id" class="pane-title-link">
1504
- <span class="pane-title">{{ pane.slug }}</span>
1505
- <span class="pane-arrow">→</span>
1506
- </a>
1507
- <div class="pane-meta">
1508
- <span class="pane-agent" :class="pane.agent || ''">{{ pane.agent || 'unknown' }}</span>
1509
- <span class="pane-id">{{ pane.paneId }}</span>
1510
- </div>
1511
- </div>
1512
- <button @click="toggleActionMenu(pane.id)" class="action-menu-btn" title="Actions">
1513
- <span>⋮</span>
1514
- </button>
1515
- </div>
1516
-
1517
- <!-- Action Menu Dropdown -->
1518
- <div v-if="showActionMenu === pane.id && paneActions[pane.id]" class="action-menu-dropdown">
1519
- <button
1520
- v-for="action in paneActions[pane.id]"
1521
- :key="action.id"
1522
- @click="executeAction(pane, action)"
1523
- class="action-menu-item"
1524
- :disabled="executingAction"
1525
- >
1526
- <span class="action-icon">{{ action.icon || '•' }}</span>
1527
- <span class="action-label">{{ action.label }}</span>
1528
- </button>
1529
- </div>
1530
-
1531
- <div class="pane-prompt-section">
1532
- <div
1533
- class="pane-prompt-header"
1534
- @click="togglePrompt(pane.id)"
1535
- :class="{ 'expanded': expandedPrompts.has(pane.id) }"
1536
- >
1537
- <span class="prompt-label">Initial Prompt</span>
1538
- <span class="expand-icon">{{ expandedPrompts.has(pane.id) ? '▼' : '▶' }}</span>
1539
- </div>
1540
- <div v-if="expandedPrompts.has(pane.id)" class="pane-prompt">
1541
- {{ pane.prompt || 'No prompt' }}
1542
- </div>
1543
- </div>
1544
-
1545
- <!-- Show agent summary when idle -->
1546
- <div v-if="pane.agentStatus === 'idle' && pane.agentSummary" class="agent-summary">
1547
- {{ pane.agentSummary }}
1548
- </div>
1549
-
1550
- <div class="pane-interactive" @click.prevent>
1551
- <!-- Options Dialog (when waiting with options) -->
1552
- <div v-if="pane.agentStatus === 'waiting' && pane.options && pane.options.length > 0" class="options-dialog">
1553
- <div class="options-question">{{ pane.optionsQuestion || 'Choose an option:' }}</div>
1554
- <div v-if="pane.potentialHarm && pane.potentialHarm.hasRisk" class="options-warning">
1555
- ⚠️ {{ pane.potentialHarm.description }}
1556
- </div>
1557
- <div v-if="loadingOptions.has(pane.id)" class="analyzing-state">
1558
- <div class="loader-spinner"></div>
1559
- <span>Processing selection...</span>
1560
- </div>
1561
- <div v-else class="options-buttons">
1562
- <button
1563
- v-for="option in pane.options"
1564
- :key="option.action"
1565
- @click="selectOption(pane, option)"
1566
- class="option-button"
1567
- :class="{ 'option-button-danger': pane.potentialHarm && pane.potentialHarm.hasRisk }"
1568
- :disabled="loadingOptions.has(pane.id)"
1569
- >
1570
- {{ option.action }}
1571
- </button>
1572
- </div>
1573
- </div>
1574
-
1575
- <!-- Analyzing (show loader) -->
1576
- <div v-else-if="pane.agentStatus === 'analyzing'" class="analyzing-state">
1577
- <div class="loader-spinner"></div>
1578
- <span>Analyzing...</span>
1579
- </div>
1580
-
1581
- <!-- Working/Idle (show prompt input) -->
1582
- <div v-else>
1583
- <div class="prompt-input-wrapper">
1584
- <textarea
1585
- v-model="promptInputs[pane.id]"
1586
- @input="autoExpand"
1587
- :placeholder="pane.agentStatus === 'working' ? 'Queue a prompt...' : 'Send a prompt...'"
1588
- :disabled="sendingPrompts.has(pane.id)"
1589
- class="prompt-textarea"
1590
- rows="1"
1591
- ></textarea>
1592
- <button
1593
- @click="sendPrompt(pane)"
1594
- :disabled="!promptInputs[pane.id] || sendingPrompts.has(pane.id)"
1595
- class="send-button"
1596
- :title="pane.agentStatus === 'working' ? 'Queue prompt' : 'Send prompt'"
1597
- >
1598
- <span v-if="sendingPrompts.has(pane.id)" class="button-loader"></span>
1599
- <svg v-else xmlns="http://www.w3.org/2000/svg" viewBox="0 0 988.44 1200.05">
1600
- <path d="M425.13,28.37L30.09,423.41C11.19,441.37.34,466.2,0,492.27c-.34,26.07,9.86,51.17,28.29,69.61,18.43,18.45,43.52,28.67,69.59,28.35,26.07-.31,50.91-11.14,68.88-30.02l233.16-233.52v776.64c0,34.56,18.43,66.48,48.36,83.76,29.93,17.28,66.8,17.28,96.72,0,29.93-17.28,48.36-49.21,48.36-83.76V328.85l231.72,231.36c24.63,23.41,59.74,32.18,92.48,23.09,32.74-9.08,58.32-34.68,67.38-67.43,9.05-32.75.25-67.85-23.18-92.46L566.73,28.37C548.63,10.16,524-.04,498.33.05c-.8-.06-1.6-.06-2.4,0-.8-.06-1.6-.06-2.4,0-25.65,0-50.25,10.19-68.4,28.32h0Z"/>
1601
- </svg>
1602
- </button>
1603
- </div>
1604
- <div v-if="queuedMessages[pane.id]" class="queued-message">
1605
- ✓ {{ queuedMessages[pane.id] }}
1606
- </div>
1607
- </div>
1608
- </div>
1609
-
1610
- <div v-if="pane.devStatus && pane.devStatus !== 'stopped'" class="dev-server-status">
1611
- <span class="status-label">Dev Server:</span>
1612
- <span class="status-badge" :class="pane.devStatus">{{ pane.devStatus }}</span>
1613
- <a v-if="pane.devUrl" :href="pane.devUrl" target="_blank" class="dev-link">↗</a>
1614
- </div>
1615
- </div>
1616
- </div>
1617
- </main>
1618
-
1619
- <!-- Action Dialogs -->
1620
- <div v-if="actionDialog" class="action-dialog-overlay" @click.self="closeActionDialog">
1621
- <!-- Confirm Dialog -->
1622
- <div v-if="actionDialog.type === 'confirm'" class="action-dialog">
1623
- <h3>{{ actionDialog.title }}</h3>
1624
- <p>{{ actionDialog.message }}</p>
1625
- <div class="dialog-buttons">
1626
- <button @click="handleConfirm(true)" class="dialog-btn dialog-btn-primary">
1627
- {{ actionDialog.yesLabel || 'Yes' }}
1628
- </button>
1629
- <button @click="handleConfirm(false)" class="dialog-btn">
1630
- {{ actionDialog.noLabel || 'No' }}
1631
- </button>
1632
- </div>
1633
- </div>
1634
-
1635
- <!-- Choice Dialog -->
1636
- <div v-if="actionDialog.type === 'choice'" class="action-dialog">
1637
- <h3>{{ actionDialog.title }}</h3>
1638
- <p v-if="actionDialog.message">{{ actionDialog.message }}</p>
1639
- <div class="choice-options">
1640
- <button
1641
- v-for="option in actionDialog.options"
1642
- :key="option.id"
1643
- @click="handleChoice(option.id)"
1644
- class="choice-option-btn"
1645
- :class="{ 'danger': option.danger }"
1646
- >
1647
- {{ option.label }}
1648
- <span v-if="option.description" class="option-description">{{ option.description }}</span>
1649
- </button>
1650
- </div>
1651
- </div>
1652
- </div>
1653
-
1654
- <footer>
1655
- <div class="footer-info">
1656
- <span>Auto-refresh: <span>ON</span></span>
1657
- <span>Last update: <span>{{ timeSinceUpdate }}</span></span>
1658
- </div>
1659
- </footer>
1660
- </div>
1661
-
1662
- <!-- Create Pane Dialog -->
1663
- <div v-if="showCreateDialog" class="modal-overlay" @click.self="closeCreateDialog">
1664
- <div class="modal-dialog">
1665
- <div class="modal-header">
1666
- <h2>{{ createStep === 'prompt' ? 'Create New Pane' : 'Select Agent' }}</h2>
1667
- <button @click="closeCreateDialog" class="modal-close">&times;</button>
1668
- </div>
1669
- <div class="modal-body">
1670
- <!-- Step 1: Prompt -->
1671
- <div v-if="createStep === 'prompt'" class="form-group">
1672
- <label for="pane-prompt">Prompt</label>
1673
- <textarea
1674
- id="pane-prompt"
1675
- v-model="newPanePrompt"
1676
- placeholder="Enter task description (e.g., 'Add tests for the authentication module')"
1677
- rows="4"
1678
- class="modal-textarea"
1679
- :disabled="creatingPane"
1680
- @keydown.meta.enter="submitPrompt"
1681
- @keydown.ctrl.enter="submitPrompt"
1682
- ></textarea>
1683
- <div class="input-hint">Cmd/Ctrl + Enter to continue</div>
1684
- </div>
1685
-
1686
- <!-- Step 2: Agent Selection -->
1687
- <div v-if="createStep === 'agent'" class="form-group">
1688
- <label>Select Agent</label>
1689
- <div class="agent-selector">
1690
- <button
1691
- v-for="agent in availableAgents"
1692
- :key="agent"
1693
- @click="newPaneAgent = agent"
1694
- class="agent-option"
1695
- :class="{ 'selected': newPaneAgent === agent }"
1696
- :disabled="creatingPane"
1697
- >
1698
- <img
1699
- :src="'https://cdn.formk.it/dmux/' + agent + '.svg'"
1700
- :alt="agent"
1701
- class="agent-logo"
1702
- />
1703
- <span>{{ agent }}</span>
1704
- </button>
1705
- </div>
1706
- </div>
1707
- </div>
1708
- <div class="modal-footer">
1709
- <button
1710
- v-if="createStep === 'agent'"
1711
- @click="createStep = 'prompt'"
1712
- class="modal-button modal-button-secondary"
1713
- :disabled="creatingPane"
1714
- >
1715
- Back
1716
- </button>
1717
- <button @click="closeCreateDialog" class="modal-button modal-button-secondary" :disabled="creatingPane">
1718
- Cancel
1719
- </button>
1720
- <button
1721
- v-if="createStep === 'prompt'"
1722
- @click="submitPrompt"
1723
- class="modal-button modal-button-primary"
1724
- :disabled="!newPanePrompt.trim() || creatingPane"
1725
- >
1726
- <span v-if="creatingPane" class="button-loader"></span>
1727
- <span v-else>Continue</span>
1728
- </button>
1729
- <button
1730
- v-if="createStep === 'agent'"
1731
- @click="createPane"
1732
- class="modal-button modal-button-primary"
1733
- :disabled="!newPaneAgent || creatingPane"
1734
- >
1735
- <span v-if="creatingPane" class="button-loader"></span>
1736
- <span v-else>Create Pane</span>
1737
- </button>
1738
- </div>
1739
- </div>
1740
- </div>
1741
- \`,
1742
- methods: {
1743
- toggleTheme() {
1744
- this.theme = this.theme === 'dark' ? 'light' : 'dark';
1745
- localStorage.setItem('dmux-theme', this.theme);
1746
- document.documentElement.setAttribute('data-theme', this.theme);
1747
- },
1748
- togglePrompt(paneId) {
1749
- if (this.expandedPrompts.has(paneId)) {
1750
- this.expandedPrompts.delete(paneId);
1751
- } else {
1752
- this.expandedPrompts.add(paneId);
1753
- }
1754
- // Force reactivity
1755
- this.expandedPrompts = new Set(this.expandedPrompts);
1756
- },
1757
- openCreateDialog() {
1758
- this.showCreateDialog = true;
1759
- this.newPanePrompt = '';
1760
- this.newPaneAgent = null;
1761
- this.availableAgents = [];
1762
- this.needsAgentChoice = false;
1763
- this.createStep = 'prompt';
1764
- // Focus the textarea after dialog opens
1765
- setTimeout(() => {
1766
- const textarea = document.getElementById('pane-prompt');
1767
- if (textarea) textarea.focus();
1768
- }, 100);
1769
- },
1770
- closeCreateDialog() {
1771
- this.showCreateDialog = false;
1772
- this.newPanePrompt = '';
1773
- this.newPaneAgent = null;
1774
- this.creatingPane = false;
1775
- this.createStep = 'prompt';
1776
- this.availableAgents = [];
1777
- this.needsAgentChoice = false;
1778
- },
1779
- async submitPrompt() {
1780
- if (!this.newPanePrompt.trim() || this.creatingPane) return;
1781
-
1782
- this.creatingPane = true;
1783
-
1784
- try {
1785
- // First API call: Send prompt without agent
1786
- const response = await fetch('/api/panes', {
1787
- method: 'POST',
1788
- headers: { 'Content-Type': 'application/json' },
1789
- body: JSON.stringify({
1790
- prompt: this.newPanePrompt.trim()
1791
- })
1792
- });
1793
-
1794
- const data = await response.json();
1795
-
1796
- if (data.success) {
1797
- // Pane created without agent selection (only one agent available)
1798
- this.closeCreateDialog();
1799
- await this.fetchPanes();
1800
- } else if (data.needsAgentChoice) {
1801
- // Agent selection required
1802
- this.availableAgents = data.availableAgents || [];
1803
- this.needsAgentChoice = true;
1804
- this.createStep = 'agent';
1805
- this.creatingPane = false;
1806
- // Auto-select first agent
1807
- if (this.availableAgents.length > 0) {
1808
- this.newPaneAgent = this.availableAgents[0];
1809
- }
1810
- } else {
1811
- // Error occurred
1812
- console.error('Failed to create pane:', data.error);
1813
- alert('Failed to create pane: ' + (data.error || 'Unknown error'));
1814
- this.creatingPane = false;
1815
- }
1816
- } catch (err) {
1817
- console.error('Failed to create pane:', err);
1818
- alert('Failed to create pane: ' + err.message);
1819
- this.creatingPane = false;
1820
- }
1821
- },
1822
- async createPane() {
1823
- if (!this.newPaneAgent || this.creatingPane) return;
1824
-
1825
- this.creatingPane = true;
1826
-
1827
- try {
1828
- // Second API call: Send prompt with selected agent
1829
- const response = await fetch('/api/panes', {
1830
- method: 'POST',
1831
- headers: { 'Content-Type': 'application/json' },
1832
- body: JSON.stringify({
1833
- prompt: this.newPanePrompt.trim(),
1834
- agent: this.newPaneAgent
1835
- })
1836
- });
1837
-
1838
- const data = await response.json();
1839
-
1840
- if (data.success) {
1841
- // Pane created successfully
1842
- this.closeCreateDialog();
1843
- await this.fetchPanes();
1844
- } else {
1845
- // Error occurred
1846
- console.error('Failed to create pane:', data.error);
1847
- alert('Failed to create pane: ' + (data.error || 'Unknown error'));
1848
- }
1849
- } catch (err) {
1850
- console.error('Failed to create pane:', err);
1851
- alert('Failed to create pane: ' + err.message);
1852
- } finally {
1853
- this.creatingPane = false;
1854
- }
1855
- },
1856
- async fetchPanes() {
1857
- try {
1858
- const response = await fetch('/api/panes');
1859
- const data = await response.json();
1860
- this.projectName = data.projectName || 'Unknown Project';
1861
- this.sessionName = data.sessionName || '';
1862
- this.panes = data.panes || [];
1863
- this.lastUpdate = new Date();
1864
- this.connected = true;
1865
- this.updateTimeSinceUpdate();
1866
-
1867
- // Clear loading state for panes that are no longer waiting
1868
- this.loadingOptions.forEach(paneId => {
1869
- const pane = this.panes.find(p => p.id === paneId);
1870
- if (!pane || pane.agentStatus !== 'waiting') {
1871
- this.loadingOptions.delete(paneId);
1872
- }
1873
- });
1874
- // Force reactivity
1875
- this.loadingOptions = new Set(this.loadingOptions);
1876
- } catch (err) {
1877
- this.connected = false;
1878
- }
1879
- },
1880
- async sendKeys(paneId, keys) {
1881
- try {
1882
- const response = await fetch(\`/api/keys/\${paneId}\`, {
1883
- method: 'POST',
1884
- headers: { 'Content-Type': 'application/json' },
1885
- body: JSON.stringify({ key: keys })
1886
- });
1887
- return response.ok;
1888
- } catch (err) {
1889
- console.error('Failed to send keys:', err);
1890
- return false;
1891
- }
1892
- },
1893
- async sendPrompt(pane) {
1894
- const prompt = this.promptInputs[pane.id];
1895
- if (!prompt || this.sendingPrompts.has(pane.id)) return;
1896
-
1897
- this.sendingPrompts.add(pane.id);
1898
-
1899
- try {
1900
- // Send each character of the prompt
1901
- for (const char of prompt) {
1902
- await this.sendKeys(pane.id, char);
1903
- await new Promise(resolve => setTimeout(resolve, 5)); // Small delay between chars
1904
- }
1905
-
1906
- // Send Enter to submit
1907
- await this.sendKeys(pane.id, 'Enter');
1908
-
1909
- // Show queued message for working status
1910
- if (pane.agentStatus === 'working') {
1911
- this.queuedMessages[pane.id] = 'Sent to queue';
1912
- setTimeout(() => {
1913
- delete this.queuedMessages[pane.id];
1914
- }, 3000);
1915
- }
1916
-
1917
- // Clear input
1918
- this.promptInputs[pane.id] = '';
1919
- } catch (err) {
1920
- console.error('Failed to send prompt:', err);
1921
- } finally {
1922
- this.sendingPrompts.delete(pane.id);
1923
- }
1924
- },
1925
- async selectOption(pane, option) {
1926
- if (!option.keys || option.keys.length === 0) return;
1927
-
1928
- // Set loading state immediately
1929
- this.loadingOptions.add(pane.id);
1930
- // Force reactivity
1931
- this.loadingOptions = new Set(this.loadingOptions);
1932
-
1933
- try {
1934
- // Send the first key in the array (usually the main option key)
1935
- const key = option.keys[0];
1936
- await this.sendKeys(pane.id, key);
1937
-
1938
- // Clear loading state after a short delay to ensure the state has transitioned
1939
- // The 2-second delay in the worker will prevent premature state detection
1940
- setTimeout(() => {
1941
- this.loadingOptions.delete(pane.id);
1942
- this.loadingOptions = new Set(this.loadingOptions);
1943
- }, 500);
1944
- } catch (err) {
1945
- console.error('Failed to select option:', err);
1946
- // Clear loading state on error
1947
- this.loadingOptions.delete(pane.id);
1948
- this.loadingOptions = new Set(this.loadingOptions);
1949
- }
1950
- },
1951
- autoExpand(event) {
1952
- const textarea = event.target;
1953
- textarea.style.height = 'auto';
1954
- textarea.style.height = Math.min(textarea.scrollHeight, 150) + 'px';
1955
- },
1956
- async toggleActionMenu(paneId) {
1957
- if (this.showActionMenu === paneId) {
1958
- this.showActionMenu = null;
1959
- } else {
1960
- // Load actions for this pane if not already loaded
1961
- if (!this.paneActions[paneId]) {
1962
- try {
1963
- const response = await fetch(\`/api/panes/\${paneId}/actions\`);
1964
- const data = await response.json();
1965
- this.paneActions[paneId] = data.actions || [];
1966
- } catch (err) {
1967
- console.error('Failed to load actions:', err);
1968
- this.paneActions[paneId] = [];
1969
- }
1970
- }
1971
- this.showActionMenu = paneId;
1972
- }
1973
- },
1974
- async executeAction(pane, action) {
1975
- this.executingAction = true;
1976
- this.showActionMenu = null;
1977
-
1978
- try {
1979
- const response = await fetch(\`/api/panes/\${pane.id}/actions/\${action.id}\`, {
1980
- method: 'POST',
1981
- headers: { 'Content-Type': 'application/json' },
1982
- body: JSON.stringify({})
1983
- });
1984
-
1985
- const result = await response.json();
1986
-
1987
- // Handle different response types
1988
- if (result.requiresInteraction) {
1989
- if (result.interactionType === 'confirm') {
1990
- this.actionDialog = {
1991
- type: 'confirm',
1992
- title: result.confirmData.title,
1993
- message: result.confirmData.message,
1994
- yesLabel: result.confirmData.yesLabel,
1995
- noLabel: result.confirmData.noLabel,
1996
- callbackId: result.confirmData.callbackId
1997
- };
1998
- } else if (result.interactionType === 'choice') {
1999
- this.actionDialog = {
2000
- type: 'choice',
2001
- title: result.choiceData.title,
2002
- message: result.choiceData.message,
2003
- options: result.choiceData.options,
2004
- callbackId: result.choiceData.callbackId
2005
- };
2006
- }
2007
- }
2008
- } catch (err) {
2009
- console.error('Failed to execute action:', err);
2010
- } finally {
2011
- this.executingAction = false;
2012
- }
2013
- },
2014
- async handleConfirm(confirmed) {
2015
- if (!this.actionDialog || !this.actionDialog.callbackId) return;
2016
-
2017
- try {
2018
- const response = await fetch(\`/api/callbacks/confirm/\${this.actionDialog.callbackId}\`, {
2019
- method: 'POST',
2020
- headers: { 'Content-Type': 'application/json' },
2021
- body: JSON.stringify({ confirmed })
2022
- });
2023
-
2024
- await response.json();
2025
- } catch (err) {
2026
- console.error('Failed to handle confirm:', err);
2027
- } finally {
2028
- this.actionDialog = null;
2029
- }
2030
- },
2031
- async handleChoice(optionId) {
2032
- if (!this.actionDialog || !this.actionDialog.callbackId) return;
2033
-
2034
- try {
2035
- const response = await fetch(\`/api/callbacks/choice/\${this.actionDialog.callbackId}\`, {
2036
- method: 'POST',
2037
- headers: { 'Content-Type': 'application/json' },
2038
- body: JSON.stringify({ optionId })
2039
- });
2040
-
2041
- await response.json();
2042
- } catch (err) {
2043
- console.error('Failed to handle choice:', err);
2044
- } finally {
2045
- this.actionDialog = null;
2046
- }
2047
- },
2048
- closeActionDialog() {
2049
- this.actionDialog = null;
2050
- },
2051
- updateTimeSinceUpdate() {
2052
- if (!this.lastUpdate) return;
2053
-
2054
- const now = new Date();
2055
- const diff = Math.floor((now - this.lastUpdate) / 1000);
2056
-
2057
- if (diff < 60) {
2058
- this.timeSinceUpdate = diff + 's ago';
2059
- } else if (diff < 3600) {
2060
- this.timeSinceUpdate = Math.floor(diff / 60) + 'm ago';
2061
- } else {
2062
- this.timeSinceUpdate = Math.floor(diff / 3600) + 'h ago';
2063
- }
2064
- },
2065
- startAutoRefresh() {
2066
- this.fetchPanes();
2067
- refreshInterval = setInterval(() => {
2068
- this.fetchPanes();
2069
- }, 2000);
2070
-
2071
- // Update time display every second
2072
- setInterval(() => {
2073
- this.updateTimeSinceUpdate();
2074
- }, 1000);
2075
- },
2076
- stopAutoRefresh() {
2077
- if (refreshInterval) {
2078
- clearInterval(refreshInterval);
2079
- refreshInterval = null;
2080
- }
2081
- }
2082
- },
2083
- mounted() {
2084
- // Apply theme on mount
2085
- document.documentElement.setAttribute('data-theme', this.theme);
2086
-
2087
- this.startAutoRefresh();
2088
-
2089
- // Handle page visibility to pause/resume updates
2090
- document.addEventListener('visibilitychange', () => {
2091
- if (document.hidden) {
2092
- this.stopAutoRefresh();
2093
- } else {
2094
- this.startAutoRefresh();
2095
- }
2096
- });
2097
- }
2098
- });
2099
-
2100
- app.mount('#app');`;
2101
- }
2102
- export function getTerminalJs() {
2103
- return `// Terminal viewer with Vue.js and ANSI parsing
2104
- import { createApp } from '/vue.esm-browser.js';
2105
-
2106
- const paneId = window.location.pathname.split('/').pop();
2107
-
2108
- // Helper to access Vue reactive data
2109
- let vueApp = null;
2110
-
2111
- // ANSI parsing state
2112
- let currentAttrs = {};
2113
-
2114
- // Convenience accessors for Vue data - these will be bound after Vue mounts
2115
- const getTerminalBuffer = () => window.terminalBuffer || [];
2116
- const setTerminalBuffer = (val) => { window.terminalBuffer = val; };
2117
- const getTerminalDimensions = () => window.terminalDimensions || { width: 80, height: 24 };
2118
- const setTerminalDimensions = (val) => { window.terminalDimensions = val; };
2119
- const getCursorRow = () => window.cursorRow || 0;
2120
- const setCursorRow = (val) => { window.cursorRow = val; };
2121
- const getCursorCol = () => window.cursorCol || 0;
2122
- const setCursorCol = (val) => { window.cursorCol = val; };
2123
-
2124
- // Color palette for 256-color mode
2125
- const colorPalette = [
2126
- '#000000', '#800000', '#008000', '#808000', '#000080', '#800080', '#008080', '#c0c0c0',
2127
- '#808080', '#ff0000', '#00ff00', '#ffff00', '#0000ff', '#ff00ff', '#00ffff', '#ffffff',
2128
- '#000000', '#00005f', '#000087', '#0000af', '#0000d7', '#0000ff', '#005f00', '#005f5f',
2129
- '#005f87', '#005faf', '#005fd7', '#005fff', '#008700', '#00875f', '#008787', '#0087af',
2130
- '#0087d7', '#0087ff', '#00af00', '#00af5f', '#00af87', '#00afaf', '#00afd7', '#00afff',
2131
- '#00d700', '#00d75f', '#00d787', '#00d7af', '#00d7d7', '#00d7ff', '#00ff00', '#00ff5f',
2132
- '#00ff87', '#00ffaf', '#00ffd7', '#00ffff', '#5f0000', '#5f005f', '#5f0087', '#5f00af',
2133
- '#5f00d7', '#5f00ff', '#5f5f00', '#5f5f5f', '#5f5f87', '#5f5faf', '#5f5fd7', '#5f5fff',
2134
- '#5f8700', '#5f875f', '#5f8787', '#5f87af', '#5f87d7', '#5f87ff', '#5faf00', '#5faf5f',
2135
- '#5faf87', '#5fafaf', '#5fafd7', '#5fafff', '#5fd700', '#5fd75f', '#5fd787', '#5fd7af',
2136
- '#5fd7d7', '#5fd7ff', '#5fff00', '#5fff5f', '#5fff87', '#5fffaf', '#5fffd7', '#5fffff',
2137
- '#870000', '#87005f', '#870087', '#8700af', '#8700d7', '#8700ff', '#875f00', '#875f5f',
2138
- '#875f87', '#875faf', '#875fd7', '#875fff', '#878700', '#87875f', '#878787', '#8787af',
2139
- '#8787d7', '#8787ff', '#87af00', '#87af5f', '#87af87', '#87afaf', '#87afd7', '#87afff',
2140
- '#87d700', '#87d75f', '#87d787', '#87d7af', '#87d7d7', '#87d7ff', '#87ff00', '#87ff5f',
2141
- '#87ff87', '#87ffaf', '#87ffd7', '#87ffff', '#af0000', '#af005f', '#af0087', '#af00af',
2142
- '#af00d7', '#af00ff', '#af5f00', '#af5f5f', '#af5f87', '#af5faf', '#af5fd7', '#af5fff',
2143
- '#af8700', '#af875f', '#af8787', '#af87af', '#af87d7', '#af87ff', '#afaf00', '#afaf5f',
2144
- '#afaf87', '#afafaf', '#afafd7', '#afafff', '#afd700', '#afd75f', '#afd787', '#afd7af',
2145
- '#afd7d7', '#afd7ff', '#afff00', '#afff5f', '#afff87', '#afffaf', '#afffd7', '#afffff',
2146
- '#d70000', '#d7005f', '#d70087', '#d700af', '#d700d7', '#d700ff', '#d75f00', '#d75f5f',
2147
- '#d75f87', '#d75faf', '#d75fd7', '#d75fff', '#d78700', '#d7875f', '#d78787', '#d787af',
2148
- '#d787d7', '#d787ff', '#d7af00', '#d7af5f', '#d7af87', '#d7afaf', '#d7afd7', '#d7afff',
2149
- '#d7d700', '#d7d75f', '#d7d787', '#d7d7af', '#d7d7d7', '#d7d7ff', '#d7ff00', '#d7ff5f',
2150
- '#d7ff87', '#d7ffaf', '#d7ffd7', '#d7ffff', '#ff0000', '#ff005f', '#ff0087', '#ff00af',
2151
- '#ff00d7', '#ff00ff', '#ff5f00', '#ff5f5f', '#ff5f87', '#ff5faf', '#ff5fd7', '#ff5fff',
2152
- '#ff8700', '#ff875f', '#ff8787', '#ff87af', '#ff87d7', '#ff87ff', '#ffaf00', '#ffaf5f',
2153
- '#ffaf87', '#ffafaf', '#ffafd7', '#ffafff', '#ffd700', '#ffd75f', '#ffd787', '#ffd7af',
2154
- '#ffd7d7', '#ffd7ff', '#ffff00', '#ffff5f', '#ffff87', '#ffffaf', '#ffffd7', '#ffffff',
2155
- '#080808', '#121212', '#1c1c1c', '#262626', '#303030', '#3a3a3a', '#444444', '#4e4e4e',
2156
- '#585858', '#626262', '#6c6c6c', '#767676', '#808080', '#8a8a8a', '#949494', '#9e9e9e',
2157
- '#a8a8a8', '#b2b2b2', '#bcbcbc', '#c6c6c6', '#d0d0d0', '#dadada', '#e4e4e4', '#eeeeee'
2158
- ];
2159
-
2160
- // Initialize terminal buffer
2161
- function initTerminal() {
2162
- window.terminalBuffer = Array(window.terminalDimensions.height).fill(null).map(() =>
2163
- Array(window.terminalDimensions.width).fill(null).map(() => ({
2164
- char: ' ',
2165
- fg: null,
2166
- bg: null,
2167
- bold: false,
2168
- dim: false,
2169
- italic: false,
2170
- underline: false,
2171
- strikethrough: false
2172
- }))
2173
- );
2174
- }
2175
-
2176
- // Parse ANSI codes and update buffer with target cursor constraint
2177
- // Used for patches where we know the final cursor position and don't want to go past it
2178
- function parseAnsiAndUpdateWithTarget(text, targetRow, targetCol) {
2179
- let i = 0;
2180
-
2181
- while (i < text.length) {
2182
- const code = text.charCodeAt(i);
2183
-
2184
- // Check for escape sequence (ESC = 27)
2185
- if (code === 27) {
2186
- const seqEnd = findEscapeSequenceEnd(text, i);
2187
- if (seqEnd > i) {
2188
- handleEscapeSequence(text.substring(i, seqEnd));
2189
- i = seqEnd;
2190
- continue;
2191
- }
2192
- }
2193
-
2194
- // Handle backspace
2195
- if (code === 8) {
2196
- if (window.cursorCol > 0) {
2197
- window.cursorCol--;
2198
- }
2199
- i++;
2200
- continue;
2201
- }
2202
-
2203
- // Handle character - don't allow scrolling, clamp to target cursor
2204
- handleCharacterWithTarget(text[i], targetRow);
2205
- i++;
2206
- }
2207
- }
2208
-
2209
- // Parse ANSI codes and update buffer
2210
- // allowScrolling: if false, prevents buffer scrolling (for patches)
2211
- function parseAnsiAndUpdate(text, debugPatch = false, allowScrolling = true) {
2212
- let i = 0;
2213
-
2214
- while (i < text.length) {
2215
- const code = text.charCodeAt(i);
2216
-
2217
- // Check for escape sequence (ESC = 27)
2218
- if (code === 27) {
2219
- // Escape sequence
2220
- const seqEnd = findEscapeSequenceEnd(text, i);
2221
- if (seqEnd > i) {
2222
- const seq = text.substring(i, seqEnd);
2223
- handleEscapeSequence(seq);
2224
- i = seqEnd;
2225
- continue;
2226
- }
2227
- }
2228
-
2229
- // Handle backspace
2230
- if (code === 8) {
2231
- if (window.cursorCol > 0) {
2232
- window.cursorCol--;
2233
- }
2234
- i++;
2235
- continue;
2236
- }
2237
-
2238
- // Regular character
2239
- handleCharacter(text[i], allowScrolling);
2240
- i++;
2241
- }
2242
- }
2243
-
2244
- function findEscapeSequenceEnd(text, start) {
2245
- if (start + 1 >= text.length) return start + 1;
2246
-
2247
- const next = text[start + 1];
2248
-
2249
- // CSI sequence: ESC[
2250
- if (next === '[') {
2251
- for (let i = start + 2; i < text.length; i++) {
2252
- const c = text[i];
2253
- if ((c >= '@' && c <= '~')) {
2254
- return i + 1;
2255
- }
2256
- }
2257
- }
2258
-
2259
- // OSC sequence: ESC]
2260
- if (next === ']') {
2261
- for (let i = start + 2; i < text.length; i++) {
2262
- const code = text.charCodeAt(i);
2263
- if (code === 7) { // BEL
2264
- return i + 1;
2265
- }
2266
- if (code === 27 && i + 1 < text.length && text[i + 1] === '\\\\') { // ESC \\
2267
- return i + 2;
2268
- }
2269
- }
2270
- }
2271
-
2272
- // Simple escape
2273
- return start + 2;
2274
- }
2275
-
2276
- function handleEscapeSequence(seq) {
2277
- if (seq.length < 2) return;
2278
-
2279
- if (seq[1] === '[') {
2280
- // CSI sequence
2281
- const params = seq.substring(2, seq.length - 1);
2282
- const command = seq[seq.length - 1];
2283
- handleCSI(params, command);
2284
- }
2285
- }
2286
-
2287
- function handleCSI(params, command) {
2288
- const args = params.split(';').map(p => parseInt(p) || 0);
2289
- const oldRow = window.cursorRow;
2290
- const oldCol = window.cursorCol;
2291
-
2292
- switch (command) {
2293
- case 'H': // Cursor position
2294
- case 'f':
2295
- window.cursorRow = Math.min(Math.max((args[0] || 1) - 1, 0), window.terminalDimensions.height - 1);
2296
- window.cursorCol = Math.min(Math.max((args[1] || 1) - 1, 0), window.terminalDimensions.width - 1);
2297
- break;
2298
-
2299
- case 'A': // Cursor up
2300
- window.cursorRow = Math.max(window.cursorRow - (args[0] || 1), 0);
2301
- break;
2302
-
2303
- case 'B': // Cursor down
2304
- window.cursorRow = Math.min(window.cursorRow + (args[0] || 1), window.terminalDimensions.height - 1);
2305
- break;
2306
-
2307
- case 'C': // Cursor forward
2308
- window.cursorCol = Math.min(window.cursorCol + (args[0] || 1), window.terminalDimensions.width - 1);
2309
- break;
2310
-
2311
- case 'D': // Cursor back
2312
- window.cursorCol = Math.max(window.cursorCol - (args[0] || 1), 0);
2313
- break;
2314
-
2315
- case 'G': // Cursor Horizontal Absolute
2316
- window.cursorCol = Math.min(Math.max((args[0] || 1) - 1, 0), window.terminalDimensions.width - 1);
2317
- break;
2318
-
2319
- case 'J': // Erase display
2320
- handleEraseDisplay(args[0] || 0);
2321
- break;
2322
-
2323
- case 'K': // Erase line
2324
- handleEraseLine(args[0] || 0);
2325
- break;
2326
-
2327
- case 'm': // SGR (colors and attributes)
2328
- handleSGR(args);
2329
- break;
2330
- }
2331
- }
2332
-
2333
- function handleSGR(args) {
2334
- if (args.length === 0 || args[0] === 0) {
2335
- currentAttrs = {};
2336
- return;
2337
- }
2338
-
2339
- let i = 0;
2340
- while (i < args.length) {
2341
- const arg = args[i];
2342
-
2343
- if (arg === 0) {
2344
- currentAttrs = {};
2345
- } else if (arg === 1) {
2346
- currentAttrs.bold = true;
2347
- } else if (arg === 2) {
2348
- currentAttrs.dim = true;
2349
- } else if (arg === 3) {
2350
- currentAttrs.italic = true;
2351
- } else if (arg === 4) {
2352
- currentAttrs.underline = true;
2353
- } else if (arg === 9) {
2354
- currentAttrs.strikethrough = true;
2355
- } else if (arg === 22) {
2356
- currentAttrs.bold = false;
2357
- currentAttrs.dim = false;
2358
- } else if (arg === 23) {
2359
- currentAttrs.italic = false;
2360
- } else if (arg === 24) {
2361
- currentAttrs.underline = false;
2362
- } else if (arg === 29) {
2363
- currentAttrs.strikethrough = false;
2364
- } else if (arg >= 30 && arg <= 37) {
2365
- // Standard foreground colors
2366
- currentAttrs.fg = ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'][arg - 30];
2367
- } else if (arg === 38) {
2368
- // Extended foreground color
2369
- if (i + 1 < args.length) {
2370
- if (args[i + 1] === 5 && i + 2 < args.length) {
2371
- // 256 color: 38;5;n
2372
- currentAttrs.fg = 'c' + args[i + 2];
2373
- i += 2;
2374
- } else if (args[i + 1] === 2 && i + 4 < args.length) {
2375
- // RGB color: 38;2;r;g;b
2376
- currentAttrs.fg = \`rgb(\${args[i + 2]},\${args[i + 3]},\${args[i + 4]})\`;
2377
- i += 4;
2378
- }
2379
- }
2380
- } else if (arg === 39) {
2381
- currentAttrs.fg = null;
2382
- } else if (arg >= 40 && arg <= 47) {
2383
- // Standard background colors
2384
- currentAttrs.bg = ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'][arg - 40];
2385
- } else if (arg === 48) {
2386
- // Extended background color
2387
- if (i + 1 < args.length) {
2388
- if (args[i + 1] === 5 && i + 2 < args.length) {
2389
- // 256 color: 48;5;n
2390
- currentAttrs.bg = 'c' + args[i + 2];
2391
- i += 2;
2392
- } else if (args[i + 1] === 2 && i + 4 < args.length) {
2393
- // RGB color: 48;2;r;g;b
2394
- currentAttrs.bg = \`rgb(\${args[i + 2]},\${args[i + 3]},\${args[i + 4]})\`;
2395
- i += 4;
2396
- }
2397
- }
2398
- } else if (arg === 49) {
2399
- currentAttrs.bg = null;
2400
- } else if (arg >= 90 && arg <= 97) {
2401
- // Bright foreground colors
2402
- currentAttrs.fg = 'bright-' + ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'][arg - 90];
2403
- } else if (arg >= 100 && arg <= 107) {
2404
- // Bright background colors
2405
- currentAttrs.bg = 'bright-' + ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'][arg - 100];
2406
- }
2407
-
2408
- i++;
2409
- }
2410
- }
2411
-
2412
- // Handle character with target row constraint - don't go past target
2413
- function handleCharacterWithTarget(char, targetRow) {
2414
- if (char === '\\n') {
2415
- window.cursorRow++;
2416
- window.cursorCol = 0;
2417
- // Clamp to target row - never go past where tmux says we should end up
2418
- if (window.cursorRow > targetRow) {
2419
- window.cursorRow = targetRow;
2420
- }
2421
- return;
2422
- }
2423
-
2424
- if (char === '\\r') {
2425
- window.cursorCol = 0;
2426
- return;
2427
- }
2428
-
2429
- if (char === '\\t') {
2430
- window.cursorCol = Math.min(Math.floor((window.cursorCol + 8) / 8) * 8, window.terminalDimensions.width - 1);
2431
- return;
2432
- }
2433
-
2434
- if (window.cursorCol >= window.terminalDimensions.width) {
2435
- window.cursorCol = 0;
2436
- window.cursorRow++;
2437
- // Clamp to target row
2438
- if (window.cursorRow > targetRow) {
2439
- window.cursorRow = targetRow;
2440
- }
2441
- }
2442
-
2443
- if (window.cursorRow < window.terminalDimensions.height && window.cursorCol < window.terminalDimensions.width) {
2444
- window.terminalBuffer[window.cursorRow][window.cursorCol] = {
2445
- char: char,
2446
- ...currentAttrs
2447
- };
2448
- window.cursorCol++;
2449
- }
2450
- }
2451
-
2452
- function handleCharacter(char, allowScrolling = true) {
2453
- if (char === '\\n') {
2454
- window.cursorRow++;
2455
- window.cursorCol = 0;
2456
- if (window.cursorRow >= window.terminalDimensions.height) {
2457
- if (allowScrolling) {
2458
- // Scroll up
2459
- window.terminalBuffer.shift();
2460
- window.terminalBuffer.push(Array(window.terminalDimensions.width).fill(null).map(() => ({
2461
- char: ' ',
2462
- fg: null,
2463
- bg: null,
2464
- bold: false,
2465
- dim: false,
2466
- italic: false,
2467
- underline: false,
2468
- strikethrough: false
2469
- })));
2470
- window.cursorRow = window.terminalDimensions.height - 1;
2471
- } else {
2472
- // Don't scroll during patches - just clamp cursor
2473
- window.cursorRow = window.terminalDimensions.height - 1;
2474
- }
2475
- }
2476
- return;
2477
- }
2478
-
2479
- if (char === '\\r') {
2480
- window.cursorCol = 0;
2481
- return;
2482
- }
2483
-
2484
- if (char === '\\t') {
2485
- window.cursorCol = Math.min(Math.floor((window.cursorCol + 8) / 8) * 8, window.terminalDimensions.width - 1);
2486
- return;
2487
- }
2488
-
2489
- if (window.cursorCol >= window.terminalDimensions.width) {
2490
- window.cursorCol = 0;
2491
- window.cursorRow++;
2492
- if (window.cursorRow >= window.terminalDimensions.height) {
2493
- if (allowScrolling) {
2494
- window.terminalBuffer.shift();
2495
- window.terminalBuffer.push(Array(window.terminalDimensions.width).fill(null).map(() => ({
2496
- char: ' ',
2497
- fg: null,
2498
- bg: null,
2499
- bold: false,
2500
- dim: false,
2501
- italic: false,
2502
- underline: false,
2503
- strikethrough: false
2504
- })));
2505
- window.cursorRow = window.terminalDimensions.height - 1;
2506
- } else {
2507
- // Don't scroll - just clamp
2508
- window.cursorRow = window.terminalDimensions.height - 1;
2509
- }
2510
- }
2511
- }
2512
-
2513
- if (window.cursorRow < window.terminalDimensions.height && window.cursorCol < window.terminalDimensions.width) {
2514
- window.terminalBuffer[window.cursorRow][window.cursorCol] = {
2515
- char: char,
2516
- ...currentAttrs
2517
- };
2518
- window.cursorCol++;
2519
- }
2520
- }
2521
-
2522
- function handleEraseDisplay(mode) {
2523
- // Implement erase display modes
2524
- if (mode === 2) {
2525
- initTerminal();
2526
- }
2527
- }
2528
-
2529
- function handleEraseLine(mode) {
2530
- if (mode === 0) {
2531
- for (let col = window.cursorCol; col < window.terminalDimensions.width; col++) {
2532
- window.terminalBuffer[window.cursorRow][col] = { char: ' ', fg: null, bg: null, bold: false, dim: false, italic: false, underline: false, strikethrough: false };
2533
- }
2534
- } else if (mode === 2) {
2535
- for (let col = 0; col < window.terminalDimensions.width; col++) {
2536
- window.terminalBuffer[window.cursorRow][col] = { char: ' ', fg: null, bg: null, bold: false, dim: false, italic: false, underline: false, strikethrough: false };
2537
- }
2538
- }
2539
- }
2540
-
2541
- // Removed duplicate colorPalette - using the one declared earlier
2542
-
2543
- // HTML entity encoding
2544
- function escapeHtml(text) {
2545
- return text
2546
- .replace(/&/g, '&amp;')
2547
- .replace(/</g, '&lt;')
2548
- .replace(/>/g, '&gt;')
2549
- .replace(/"/g, '&quot;')
2550
- .replace(/'/g, '&#39;');
2551
- }
2552
-
2553
- // Check if two cells have the same styling
2554
- function hasSameStyle(cell1, cell2) {
2555
- return cell1.fg === cell2.fg &&
2556
- cell1.bg === cell2.bg &&
2557
- cell1.bold === cell2.bold &&
2558
- cell1.dim === cell2.dim &&
2559
- cell1.italic === cell2.italic &&
2560
- cell1.underline === cell2.underline &&
2561
- cell1.strikethrough === cell2.strikethrough;
2562
- }
2563
-
2564
- // Build style attributes for a cell
2565
- function buildStyleAttrs(cell) {
2566
- const classes = [];
2567
- const styles = [];
2568
-
2569
- // Handle foreground color
2570
- if (cell.fg) {
2571
- if (cell.fg.startsWith('rgb(')) {
2572
- styles.push(\`color: \${cell.fg}\`);
2573
- } else if (cell.fg.startsWith('c')) {
2574
- const colorIndex = parseInt(cell.fg.substring(1));
2575
- if (colorIndex >= 0 && colorIndex < colorPalette.length) {
2576
- styles.push(\`color: \${colorPalette[colorIndex]}\`);
2577
- }
2578
- } else {
2579
- classes.push('term-fg-' + cell.fg);
2580
- }
2581
- }
2582
-
2583
- // Handle background color
2584
- if (cell.bg) {
2585
- if (cell.bg.startsWith('rgb(')) {
2586
- styles.push(\`background-color: \${cell.bg}\`);
2587
- } else if (cell.bg.startsWith('c')) {
2588
- const colorIndex = parseInt(cell.bg.substring(1));
2589
- if (colorIndex >= 0 && colorIndex < colorPalette.length) {
2590
- styles.push(\`background-color: \${colorPalette[colorIndex]}\`);
2591
- }
2592
- } else {
2593
- classes.push('term-bg-' + cell.bg);
2594
- }
2595
- }
2596
-
2597
- // Add attribute classes
2598
- if (cell.bold) classes.push('term-bold');
2599
- if (cell.dim) classes.push('term-dim');
2600
- if (cell.italic) classes.push('term-italic');
2601
- if (cell.underline) classes.push('term-underline');
2602
- if (cell.strikethrough) classes.push('term-strikethrough');
2603
-
2604
- return { classes, styles };
2605
- }
2606
-
2607
- // Render buffer to HTML with one div per row
2608
- // Connect to stream
2609
- function connectToStream() {
2610
- const streamPaneId = window.actualPaneId || paneId;
2611
- const url = \`/api/stream/\${streamPaneId}\`;
2612
-
2613
- fetch(url)
2614
- .then(response => {
2615
- if (!response.ok) throw new Error('Failed to connect');
2616
- if (!response.body) throw new Error('No response body');
2617
-
2618
- const reader = response.body.getReader();
2619
- const decoder = new TextDecoder();
2620
- let buffer = '';
2621
-
2622
- updateConnectionStatus(true);
2623
-
2624
- const processStream = async () => {
2625
- try {
2626
- while (true) {
2627
- const { done, value } = await reader.read();
2628
- if (done) break;
2629
-
2630
- buffer += decoder.decode(value, { stream: true });
2631
-
2632
- let newlineIndex;
2633
- while ((newlineIndex = buffer.indexOf('\\n')) !== -1) {
2634
- const message = buffer.substring(0, newlineIndex);
2635
- buffer = buffer.substring(newlineIndex + 1);
2636
-
2637
- if (message) {
2638
- processMessage(message);
2639
- }
2640
- }
2641
- }
2642
- } catch (error) {
2643
- updateConnectionStatus(false);
2644
- }
2645
- };
2646
-
2647
- processStream();
2648
- })
2649
- .catch(error => {
2650
- updateConnectionStatus(false);
2651
- });
2652
- }
2653
-
2654
- function processMessage(message) {
2655
- const colonIndex = message.indexOf(':');
2656
- if (colonIndex === -1) return;
2657
-
2658
- const type = message.substring(0, colonIndex);
2659
- const jsonStr = message.substring(colonIndex + 1);
2660
-
2661
- try {
2662
- const data = JSON.parse(jsonStr);
2663
-
2664
- switch (type) {
2665
- case 'INIT':
2666
- window.terminalDimensions = { width: data.width, height: data.height };
2667
- initTerminal();
2668
-
2669
- // Reset cursor to top-left before parsing INIT content
2670
- window.cursorRow = 0;
2671
- window.cursorCol = 0;
2672
-
2673
- // Parse content - NO scrolling for INIT, just clamp cursor to buffer
2674
- // This prevents losing the first line when content fills the entire buffer
2675
- parseAnsiAndUpdate(data.content || '', false, false);
2676
-
2677
- // Set cursor to actual tmux cursor position if provided
2678
- if (data.cursorRow !== undefined && data.cursorCol !== undefined) {
2679
- window.cursorRow = data.cursorRow;
2680
- window.cursorCol = data.cursorCol;
2681
- }
2682
- renderToHtml();
2683
- break;
2684
-
2685
- case 'PATCH':
2686
- // PATCH: The backend sends us the raw diff between terminal states
2687
- // This diff contains ANSI escape sequences that position the cursor
2688
- // and write text. We need to simply replay these sequences.
2689
- // The key insight: scrolling already happened in tmux BEFORE we captured
2690
- // the diff. We're not replaying terminal output - we're applying a diff.
2691
- const targetCursorRow = data.cursorRow;
2692
- const targetCursorCol = data.cursorCol;
2693
-
2694
- // Apply changes - NO SCROLLING during patches
2695
- // The diff tells us exactly what cells changed in the visible buffer
2696
- data.changes.forEach(change => {
2697
- parseAnsiAndUpdate(change.text, false, false);
2698
- });
2699
-
2700
- // Set cursor to final position from tmux
2701
- if (targetCursorRow !== undefined && targetCursorCol !== undefined) {
2702
- window.cursorRow = targetCursorRow;
2703
- window.cursorCol = targetCursorCol;
2704
- }
2705
-
2706
- break;
2707
-
2708
- case 'RESIZE':
2709
- terminalDimensions = { width: data.width, height: data.height };
2710
- initTerminal();
2711
- parseAnsiAndUpdate(data.content || '');
2712
- renderToHtml();
2713
- break;
2714
-
2715
- case 'HEARTBEAT':
2716
- break;
2717
- }
2718
- } catch (error) {
2719
- // Silently ignore parse errors
2720
- }
2721
- }
2722
-
2723
- function updateConnectionStatus(connected) {
2724
- if (vueApp) {
2725
- vueApp.connected = connected;
2726
- }
2727
- }
2728
-
2729
- // Initialize Vue app
2730
- const app = createApp({
2731
- data() {
2732
- return {
2733
- terminalBuffer: [],
2734
- dimensions: { width: 80, height: 24 },
2735
- connected: false,
2736
- cursorRow: 0,
2737
- cursorCol: 0,
2738
- paneTitle: 'Loading...',
2739
- isMobile: false,
2740
- ctrlActive: false,
2741
- altActive: false,
2742
- shiftActive: false,
2743
- mobileInputValue: ''
2744
- };
2745
- },
2746
- template: \`
2747
- <div class="terminal-page">
2748
- <header>
2749
- <a href="/" class="back-button">← dmux</a>
2750
- <h1>{{ paneTitle }}</h1>
2751
- <div class="session-info">
2752
- <span>{{ dimensions.width }}×{{ dimensions.height }}</span>
2753
- <span class="status-indicator" :style="{ color: connected ? '#4ade80' : '#f87171' }">●</span>
2754
- </div>
2755
- </header>
2756
-
2757
- <!-- Mobile keyboard toolbar -->
2758
- <div v-if="isMobile" class="mobile-toolbar">
2759
- <button @click="toggleCtrl" :class="{ active: ctrlActive }" class="toolbar-key">Ctrl</button>
2760
- <button @click="toggleAlt" :class="{ active: altActive }" class="toolbar-key">Alt</button>
2761
- <button @click="toggleShift" :class="{ active: shiftActive }" class="toolbar-key">Shift</button>
2762
- <button @click="sendKey('Escape')" class="toolbar-key">Esc</button>
2763
- <button @click="sendKey('Tab')" class="toolbar-key">Tab</button>
2764
- <button @click="sendKey('Enter')" class="toolbar-key">Enter</button>
2765
- <button @click="sendKey('ArrowUp')" class="toolbar-key">↑</button>
2766
- <button @click="sendKey('ArrowDown')" class="toolbar-key">↓</button>
2767
- <button @click="sendKey('ArrowLeft')" class="toolbar-key">←</button>
2768
- <button @click="sendKey('ArrowRight')" class="toolbar-key">→</button>
2769
- </div>
2770
-
2771
- <!-- Hidden input for mobile keyboard -->
2772
- <input
2773
- v-if="isMobile"
2774
- ref="mobileInput"
2775
- type="text"
2776
- class="mobile-input"
2777
- v-model="mobileInputValue"
2778
- @input="handleMobileInput"
2779
- @keydown="handleMobileKeydown"
2780
- autocomplete="off"
2781
- autocapitalize="off"
2782
- autocorrect="off"
2783
- />
2784
-
2785
- <div class="terminal-content" @click="focusMobileInput">
2786
- <div class="terminal-output" :style="terminalContainerStyle">
2787
- <div
2788
- v-for="(row, rowIndex) in terminalBuffer"
2789
- :key="rowIndex"
2790
- class="terminal-row"
2791
- :data-row="rowIndex"
2792
- v-html="renderRow(row, rowIndex)"
2793
- ></div>
2794
- </div>
2795
- </div>
2796
- </div>
2797
- \`,
2798
- computed: {
2799
- terminalContainerStyle() {
2800
- // Set width to fit exactly the terminal columns
2801
- // Using ch units (character width in monospace fonts)
2802
- return {
2803
- width: \`\${this.dimensions.width}ch\`,
2804
- maxWidth: '100vw',
2805
- fontSize: \`calc(100vw / \${this.dimensions.width} / 0.6)\`
2806
- };
2807
- }
2808
- },
2809
- methods: {
2810
- renderRow(row, rowIndex) {
2811
- let html = '';
2812
- let col = 0;
2813
-
2814
- while (col < row.length) {
2815
- const cell = row[col];
2816
- const isCursor = (rowIndex === this.cursorRow && col === this.cursorCol);
2817
- const hasStyle = cell.fg || cell.bg || cell.bold || cell.dim || cell.italic || cell.underline || cell.strikethrough || isCursor;
2818
-
2819
- if (!hasStyle) {
2820
- let text = '';
2821
- while (col < row.length) {
2822
- const c = row[col];
2823
- const isCur = (rowIndex === this.cursorRow && col === this.cursorCol);
2824
- if (c.fg || c.bg || c.bold || c.dim || c.italic || c.underline || c.strikethrough || isCur) break;
2825
- text += c.char;
2826
- col++;
2827
- }
2828
- html += escapeHtml(text);
2829
- } else {
2830
- const { classes, styles } = buildStyleAttrs(cell);
2831
- if (isCursor) classes.push('term-cursor');
2832
-
2833
- let text = cell.char;
2834
- col++;
2835
-
2836
- while (col < row.length) {
2837
- const nextCell = row[col];
2838
- const nextIsCursor = (rowIndex === this.cursorRow && col === this.cursorCol);
2839
- if (nextIsCursor || !hasSameStyle(cell, nextCell)) break;
2840
- text += nextCell.char;
2841
- col++;
2842
- }
2843
-
2844
- const classAttr = classes.length ? ' class="' + classes.join(' ') + '"' : '';
2845
- const styleAttr = styles.length ? ' style="' + styles.join('; ') + '"' : '';
2846
- html += '<span' + classAttr + styleAttr + '>' + escapeHtml(text) + '</span>';
2847
- }
2848
- }
2849
-
2850
- return html;
2851
- },
2852
- toggleCtrl() {
2853
- this.ctrlActive = !this.ctrlActive;
2854
- if (this.ctrlActive && this.altActive) {
2855
- this.altActive = false;
2856
- }
2857
- },
2858
- toggleAlt() {
2859
- this.altActive = !this.altActive;
2860
- if (this.altActive && this.ctrlActive) {
2861
- this.ctrlActive = false;
2862
- }
2863
- },
2864
- toggleShift() {
2865
- this.shiftActive = !this.shiftActive;
2866
- },
2867
- async sendKey(key) {
2868
- const keystrokeData = {
2869
- key: key,
2870
- ctrlKey: this.ctrlActive,
2871
- altKey: this.altActive,
2872
- shiftKey: this.shiftActive,
2873
- metaKey: false
2874
- };
2875
-
2876
- // Reset modifiers after sending
2877
- this.ctrlActive = false;
2878
- this.altActive = false;
2879
- this.shiftActive = false;
2880
-
2881
- try {
2882
- await fetch(\`/api/keys/\${window.actualPaneId}\`, {
2883
- method: 'POST',
2884
- headers: { 'Content-Type': 'application/json' },
2885
- body: JSON.stringify(keystrokeData)
2886
- });
2887
- } catch (error) {
2888
- // Silently ignore
2889
- }
2890
- },
2891
- focusMobileInput() {
2892
- if (this.isMobile && this.$refs.mobileInput) {
2893
- this.$refs.mobileInput.focus();
2894
- }
2895
- },
2896
- handleMobileInput(event) {
2897
- // Get the new character(s) added
2898
- const newValue = event.target.value;
2899
- const oldValue = this.mobileInputValue;
2900
-
2901
- if (newValue.length > oldValue.length) {
2902
- // Characters were added
2903
- const addedChars = newValue.substring(oldValue.length);
2904
-
2905
- // Send each character
2906
- for (const char of addedChars) {
2907
- this.sendKey(char);
2908
- }
2909
- } else if (newValue.length < oldValue.length) {
2910
- // Characters were deleted - send backspace
2911
- const deletedCount = oldValue.length - newValue.length;
2912
- for (let i = 0; i < deletedCount; i++) {
2913
- this.sendKey('Backspace');
2914
- }
2915
- }
2916
-
2917
- // Clear the input to allow continuous typing
2918
- this.$nextTick(() => {
2919
- this.mobileInputValue = '';
2920
- });
2921
- },
2922
- handleMobileKeydown(event) {
2923
- // Handle special keys
2924
- if (event.key === 'Enter') {
2925
- event.preventDefault();
2926
- this.sendKey('Enter');
2927
- } else if (event.key === 'Backspace' && this.mobileInputValue === '') {
2928
- event.preventDefault();
2929
- this.sendKey('Backspace');
2930
- }
2931
- }
2932
- },
2933
- mounted() {
2934
- // Make Vue app instance globally accessible
2935
- window.vueApp = this;
2936
- vueApp = this;
2937
-
2938
- // Detect mobile device
2939
- this.isMobile = 'ontouchstart' in window || navigator.maxTouchPoints > 0 || window.innerWidth < 768;
2940
-
2941
- // Wire up global variables to Vue's reactive properties
2942
- // When code reads/writes terminalBuffer, it actually reads/writes this.terminalBuffer
2943
- Object.defineProperty(window, 'terminalBuffer', {
2944
- get: () => this.terminalBuffer,
2945
- set: (val) => { this.terminalBuffer = val; },
2946
- configurable: true
2947
- });
2948
- Object.defineProperty(window, 'cursorRow', {
2949
- get: () => this.cursorRow,
2950
- set: (val) => { this.cursorRow = val; },
2951
- configurable: true
2952
- });
2953
- Object.defineProperty(window, 'cursorCol', {
2954
- get: () => this.cursorCol,
2955
- set: (val) => { this.cursorCol = val; },
2956
- configurable: true
2957
- });
2958
- Object.defineProperty(window, 'terminalDimensions', {
2959
- get: () => this.dimensions,
2960
- set: (val) => { this.dimensions = val; },
2961
- configurable: true
2962
- });
2963
-
2964
- // Assign to module-level variables so code can use them
2965
- terminalBuffer = this.terminalBuffer;
2966
- terminalDimensions = this.dimensions;
2967
- cursorRow = this.cursorRow;
2968
- cursorCol = this.cursorCol;
2969
-
2970
- // Load pane info and start streaming
2971
- fetch('/api/panes')
2972
- .then(r => r.json())
2973
- .then(data => {
2974
- // Try to find pane by ID first, then by slug (for backwards compat)
2975
- let pane = data.panes.find(p => p.id === paneId);
2976
- if (!pane) {
2977
- pane = data.panes.find(p => p.slug === paneId);
2978
- }
2979
- if (pane) {
2980
- this.paneTitle = pane.slug;
2981
- // Use the actual pane ID for streaming
2982
- window.actualPaneId = pane.id;
2983
- }
2984
- connectToStream();
2985
- })
2986
- .catch(err => {
2987
- connectToStream();
2988
- });
2989
- }
2990
- });
2991
-
2992
- // Keyboard input handling - send keystrokes to backend
2993
- document.addEventListener('keydown', async (event) => {
2994
- // Don't capture keyboard if user is in browser UI (not focused on terminal)
2995
- const activeElement = document.activeElement;
2996
- if (activeElement && (activeElement.tagName === 'INPUT' || activeElement.tagName === 'TEXTAREA')) {
2997
- return; // Let normal input handling work
2998
- }
2999
-
3000
- // Ignore modifier keys by themselves - they should only affect other keys
3001
- const modifierKeys = ['Shift', 'Control', 'Alt', 'Meta'];
3002
- if (modifierKeys.includes(event.key)) {
3003
- return;
3004
- }
3005
-
3006
- // Prevent default for most keys to avoid browser shortcuts
3007
- if (!event.metaKey && !event.ctrlKey || event.key === 'c' || event.key === 'd') {
3008
- event.preventDefault();
3009
- }
3010
-
3011
- // Build the keystroke data
3012
- const keystrokeData = {
3013
- key: event.key,
3014
- ctrlKey: event.ctrlKey,
3015
- altKey: event.altKey,
3016
- shiftKey: event.shiftKey,
3017
- metaKey: event.metaKey
3018
- };
3019
-
3020
- try {
3021
- const response = await fetch(\`/api/keys/\${window.actualPaneId}\`, {
3022
- method: 'POST',
3023
- headers: {
3024
- 'Content-Type': 'application/json'
3025
- },
3026
- body: JSON.stringify(keystrokeData)
3027
- });
3028
- } catch (error) {
3029
- // Silently ignore keystroke errors
3030
- }
3031
- });
3032
-
3033
- app.mount('#app');
3034
-
3035
- // Remove the old renderToHtml function - Vue handles rendering
3036
- function renderToHtml() {
3037
- // No-op: Vue reactively renders terminalBuffer changes
3038
- }`;
3039
- }
3040
- //# sourceMappingURL=static.js.map