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.
- package/dist/DmuxApp.d.ts.map +1 -1
- package/dist/DmuxApp.js +221 -1596
- package/dist/DmuxApp.js.map +1 -1
- package/dist/actions/implementations/closeAction.d.ts +10 -0
- package/dist/actions/implementations/closeAction.d.ts.map +1 -0
- package/dist/actions/implementations/closeAction.js +197 -0
- package/dist/actions/implementations/closeAction.js.map +1 -0
- package/dist/actions/implementations/copyPathAction.d.ts +10 -0
- package/dist/actions/implementations/copyPathAction.d.ts.map +1 -0
- package/dist/actions/implementations/copyPathAction.js +34 -0
- package/dist/actions/implementations/copyPathAction.js.map +1 -0
- package/dist/actions/implementations/duplicateAction.d.ts +10 -0
- package/dist/actions/implementations/duplicateAction.d.ts.map +1 -0
- package/dist/actions/implementations/duplicateAction.js +25 -0
- package/dist/actions/implementations/duplicateAction.js.map +1 -0
- package/dist/actions/implementations/index.d.ts +14 -0
- package/dist/actions/implementations/index.d.ts.map +1 -0
- package/dist/actions/implementations/index.js +14 -0
- package/dist/actions/implementations/index.js.map +1 -0
- package/dist/actions/implementations/mergeAction.d.ts +14 -0
- package/dist/actions/implementations/mergeAction.d.ts.map +1 -0
- package/dist/actions/implementations/mergeAction.js +84 -0
- package/dist/actions/implementations/mergeAction.js.map +1 -0
- package/dist/actions/implementations/openInEditorAction.d.ts +12 -0
- package/dist/actions/implementations/openInEditorAction.d.ts.map +1 -0
- package/dist/actions/implementations/openInEditorAction.js +33 -0
- package/dist/actions/implementations/openInEditorAction.js.map +1 -0
- package/dist/actions/implementations/renameAction.d.ts +13 -0
- package/dist/actions/implementations/renameAction.d.ts.map +1 -0
- package/dist/actions/implementations/renameAction.js +18 -0
- package/dist/actions/implementations/renameAction.js.map +1 -0
- package/dist/actions/implementations/toggleAutopilotAction.d.ts +10 -0
- package/dist/actions/implementations/toggleAutopilotAction.d.ts.map +1 -0
- package/dist/actions/implementations/toggleAutopilotAction.js +33 -0
- package/dist/actions/implementations/toggleAutopilotAction.js.map +1 -0
- package/dist/actions/implementations/viewAction.d.ts +10 -0
- package/dist/actions/implementations/viewAction.d.ts.map +1 -0
- package/dist/actions/implementations/viewAction.js +26 -0
- package/dist/actions/implementations/viewAction.js.map +1 -0
- package/dist/actions/merge/commitMessageHandler.d.ts +27 -0
- package/dist/actions/merge/commitMessageHandler.d.ts.map +1 -0
- package/dist/actions/merge/commitMessageHandler.js +131 -0
- package/dist/actions/merge/commitMessageHandler.js.map +1 -0
- package/dist/actions/merge/conflictResolution.d.ts +13 -0
- package/dist/actions/merge/conflictResolution.d.ts.map +1 -0
- package/dist/actions/merge/conflictResolution.js +134 -0
- package/dist/actions/merge/conflictResolution.js.map +1 -0
- package/dist/actions/merge/issueHandlers/index.d.ts +11 -0
- package/dist/actions/merge/issueHandlers/index.d.ts.map +1 -0
- package/dist/actions/merge/issueHandlers/index.js +8 -0
- package/dist/actions/merge/issueHandlers/index.js.map +1 -0
- package/dist/actions/merge/issueHandlers/mainDirtyHandler.d.ts +13 -0
- package/dist/actions/merge/issueHandlers/mainDirtyHandler.d.ts.map +1 -0
- package/dist/actions/merge/issueHandlers/mainDirtyHandler.js +72 -0
- package/dist/actions/merge/issueHandlers/mainDirtyHandler.js.map +1 -0
- package/dist/actions/merge/issueHandlers/mergeConflictHandler.d.ts +13 -0
- package/dist/actions/merge/issueHandlers/mergeConflictHandler.d.ts.map +1 -0
- package/dist/actions/merge/issueHandlers/mergeConflictHandler.js +52 -0
- package/dist/actions/merge/issueHandlers/mergeConflictHandler.js.map +1 -0
- package/dist/actions/merge/issueHandlers/nothingToMergeHandler.d.ts +7 -0
- package/dist/actions/merge/issueHandlers/nothingToMergeHandler.d.ts.map +1 -0
- package/dist/actions/merge/issueHandlers/nothingToMergeHandler.js +12 -0
- package/dist/actions/merge/issueHandlers/nothingToMergeHandler.js.map +1 -0
- package/dist/actions/merge/issueHandlers/worktreeUncommittedHandler.d.ts +13 -0
- package/dist/actions/merge/issueHandlers/worktreeUncommittedHandler.d.ts.map +1 -0
- package/dist/actions/merge/issueHandlers/worktreeUncommittedHandler.js +49 -0
- package/dist/actions/merge/issueHandlers/worktreeUncommittedHandler.js.map +1 -0
- package/dist/actions/merge/mergeExecution.d.ts +22 -0
- package/dist/actions/merge/mergeExecution.d.ts.map +1 -0
- package/dist/actions/merge/mergeExecution.js +180 -0
- package/dist/actions/merge/mergeExecution.js.map +1 -0
- package/dist/actions/paneActions.d.ts +3 -40
- package/dist/actions/paneActions.d.ts.map +1 -1
- package/dist/actions/paneActions.js +4 -1038
- package/dist/actions/paneActions.js.map +1 -1
- package/dist/actions/types.d.ts +1 -0
- package/dist/actions/types.d.ts.map +1 -1
- package/dist/actions/types.js.map +1 -1
- package/dist/components/{ActionChoiceDialog.d.ts → dialogs/ActionChoiceDialog.d.ts} +1 -1
- package/dist/components/dialogs/ActionChoiceDialog.d.ts.map +1 -0
- package/dist/components/dialogs/ActionChoiceDialog.js.map +1 -0
- package/dist/components/dialogs/ActionConfirmDialog.d.ts.map +1 -0
- package/dist/components/dialogs/ActionConfirmDialog.js.map +1 -0
- package/dist/components/dialogs/ActionInputDialog.d.ts.map +1 -0
- package/dist/components/{ActionInputDialog.js → dialogs/ActionInputDialog.js} +2 -2
- package/dist/components/dialogs/ActionInputDialog.js.map +1 -0
- package/dist/components/dialogs/ActionProgressDialog.d.ts.map +1 -0
- package/dist/components/dialogs/ActionProgressDialog.js.map +1 -0
- package/dist/components/dialogs/AgentChoiceDialog.d.ts.map +1 -0
- package/dist/components/dialogs/AgentChoiceDialog.js.map +1 -0
- package/dist/components/{CloseOptionsDialog.d.ts → dialogs/CloseOptionsDialog.d.ts} +1 -1
- package/dist/components/dialogs/CloseOptionsDialog.d.ts.map +1 -0
- package/dist/components/dialogs/CloseOptionsDialog.js.map +1 -0
- package/dist/components/dialogs/CommandPromptDialog.d.ts.map +1 -0
- package/dist/components/{CommandPromptDialog.js → dialogs/CommandPromptDialog.js} +1 -1
- package/dist/components/dialogs/CommandPromptDialog.js.map +1 -0
- package/dist/components/dialogs/DialogBox.d.ts.map +1 -0
- package/dist/components/dialogs/DialogBox.js.map +1 -0
- package/dist/components/dialogs/HooksDialog.d.ts.map +1 -0
- package/dist/components/dialogs/HooksDialog.js.map +1 -0
- package/dist/components/{MergeConfirmationDialog.d.ts → dialogs/MergeConfirmationDialog.d.ts} +1 -1
- package/dist/components/dialogs/MergeConfirmationDialog.d.ts.map +1 -0
- package/dist/components/dialogs/MergeConfirmationDialog.js.map +1 -0
- package/dist/components/{SettingsDialog.d.ts → dialogs/SettingsDialog.d.ts} +1 -1
- package/dist/components/dialogs/SettingsDialog.d.ts.map +1 -0
- package/dist/components/dialogs/SettingsDialog.js.map +1 -0
- package/dist/components/dialogs/UpdateDialog.d.ts.map +1 -0
- package/dist/components/dialogs/UpdateDialog.js.map +1 -0
- package/dist/components/indicators/CreatingIndicator.d.ts.map +1 -0
- package/dist/components/indicators/CreatingIndicator.js.map +1 -0
- package/dist/components/indicators/LoadingIndicator.d.ts.map +1 -0
- package/dist/components/indicators/LoadingIndicator.js.map +1 -0
- package/dist/components/indicators/RunningIndicator.d.ts.map +1 -0
- package/dist/components/indicators/RunningIndicator.js.map +1 -0
- package/dist/components/indicators/Spinner.d.ts.map +1 -0
- package/dist/components/indicators/Spinner.js.map +1 -0
- package/dist/components/indicators/UpdatingIndicator.d.ts.map +1 -0
- package/dist/components/indicators/UpdatingIndicator.js.map +1 -0
- package/dist/components/indicators/index.d.ts +6 -0
- package/dist/components/indicators/index.d.ts.map +1 -0
- package/dist/components/indicators/index.js +6 -0
- package/dist/components/indicators/index.js.map +1 -0
- package/dist/components/inputs/CleanTextInput.d.ts.map +1 -0
- package/dist/{CleanTextInput.js → components/inputs/CleanTextInput.js} +2 -144
- package/dist/components/inputs/CleanTextInput.js.map +1 -0
- package/dist/components/inputs/StyledTextInput.d.ts.map +1 -0
- package/dist/components/inputs/StyledTextInput.js.map +1 -0
- package/dist/components/inputs/index.d.ts +3 -0
- package/dist/components/inputs/index.d.ts.map +1 -0
- package/dist/components/inputs/index.js +3 -0
- package/dist/components/inputs/index.js.map +1 -0
- package/dist/components/{KebabMenu.d.ts → panes/KebabMenu.d.ts} +1 -1
- package/dist/components/panes/KebabMenu.d.ts.map +1 -0
- package/dist/components/panes/KebabMenu.js.map +1 -0
- package/dist/components/panes/MergePane.d.ts.map +1 -0
- package/dist/{MergePane.js → components/panes/MergePane.js} +1 -1
- package/dist/components/panes/MergePane.js.map +1 -0
- package/dist/components/{PaneCard.d.ts → panes/PaneCard.d.ts} +1 -1
- package/dist/components/panes/PaneCard.d.ts.map +1 -0
- package/dist/components/{PaneCard.js → panes/PaneCard.js} +1 -1
- package/dist/components/panes/PaneCard.js.map +1 -0
- package/dist/components/{PanesGrid.d.ts → panes/PanesGrid.d.ts} +2 -2
- package/dist/components/panes/PanesGrid.d.ts.map +1 -0
- package/dist/components/{PanesGrid.js → panes/PanesGrid.js} +1 -1
- package/dist/components/panes/PanesGrid.js.map +1 -0
- package/dist/components/panes/index.d.ts +5 -0
- package/dist/components/panes/index.d.ts.map +1 -0
- package/dist/components/panes/index.js +5 -0
- package/dist/components/panes/index.js.map +1 -0
- package/dist/components/popups/agentChoicePopup.d.ts.map +1 -0
- package/dist/{popups → components/popups}/agentChoicePopup.js +1 -1
- package/dist/components/popups/agentChoicePopup.js.map +1 -0
- package/dist/components/popups/choicePopup.d.ts.map +1 -0
- package/dist/{popups → components/popups}/choicePopup.js +3 -3
- package/dist/components/popups/choicePopup.js.map +1 -0
- package/dist/components/popups/config.d.ts.map +1 -0
- package/dist/{popups → components/popups}/config.js +1 -1
- package/dist/components/popups/config.js.map +1 -0
- package/dist/components/popups/confirmPopup.d.ts.map +1 -0
- package/dist/{popups → components/popups}/confirmPopup.js +2 -3
- package/dist/components/popups/confirmPopup.js.map +1 -0
- package/dist/components/popups/hooksPopup.d.ts.map +1 -0
- package/dist/{popups → components/popups}/hooksPopup.js +1 -1
- package/dist/components/popups/hooksPopup.js.map +1 -0
- package/dist/components/popups/inputPopup.d.ts.map +1 -0
- package/dist/{popups → components/popups}/inputPopup.js +3 -4
- package/dist/components/popups/inputPopup.js.map +1 -0
- package/dist/components/popups/kebabMenuPopup.d.ts.map +1 -0
- package/dist/{popups → components/popups}/kebabMenuPopup.js +1 -1
- package/dist/components/popups/kebabMenuPopup.js.map +1 -0
- package/dist/components/popups/logsPopup.d.ts.map +1 -0
- package/dist/{popups → components/popups}/logsPopup.js +15 -7
- package/dist/components/popups/logsPopup.js.map +1 -0
- package/dist/components/popups/mergePopup.d.ts.map +1 -0
- package/dist/{popups → components/popups}/mergePopup.js +6 -6
- package/dist/components/popups/mergePopup.js.map +1 -0
- package/dist/components/popups/newPanePopup.d.ts.map +1 -0
- package/dist/{popups → components/popups}/newPanePopup.js +3 -3
- package/dist/components/popups/newPanePopup.js.map +1 -0
- package/dist/components/popups/progressPopup.d.ts.map +1 -0
- package/dist/{popups → components/popups}/progressPopup.js +1 -2
- package/dist/components/popups/progressPopup.js.map +1 -0
- package/dist/components/popups/remotePopup.d.ts.map +1 -0
- package/dist/{popups → components/popups}/remotePopup.js +1 -1
- package/dist/components/popups/remotePopup.js.map +1 -0
- package/dist/components/popups/settingsPopup.d.ts.map +1 -0
- package/dist/{popups → components/popups}/settingsPopup.js +1 -1
- package/dist/components/popups/settingsPopup.js.map +1 -0
- package/dist/components/popups/shared/FileList.d.ts.map +1 -0
- package/dist/components/popups/shared/FileList.js.map +1 -0
- package/dist/components/popups/shared/PopupContainer.d.ts.map +1 -0
- package/dist/components/popups/shared/PopupContainer.js.map +1 -0
- package/dist/components/popups/shared/PopupInputBox.d.ts.map +1 -0
- package/dist/components/popups/shared/PopupInputBox.js.map +1 -0
- package/dist/components/popups/shared/PopupWrapper.d.ts.map +1 -0
- package/dist/components/popups/shared/PopupWrapper.js.map +1 -0
- package/dist/components/popups/shared/index.d.ts.map +1 -0
- package/dist/components/popups/shared/index.js.map +1 -0
- package/dist/components/popups/shortcutsPopup.d.ts.map +1 -0
- package/dist/{popups → components/popups}/shortcutsPopup.js +1 -1
- package/dist/components/popups/shortcutsPopup.js.map +1 -0
- package/dist/components/popups/templates/SimpleInputPopup.d.ts.map +1 -0
- package/dist/{popups → components/popups}/templates/SimpleInputPopup.js +2 -2
- package/dist/components/popups/templates/SimpleInputPopup.js.map +1 -0
- package/dist/components/ui/FileCopyPrompt.d.ts.map +1 -0
- package/dist/components/ui/FileCopyPrompt.js.map +1 -0
- package/dist/components/ui/FooterHelp.d.ts.map +1 -0
- package/dist/components/ui/FooterHelp.js.map +1 -0
- package/dist/components/ui/QRCode.d.ts.map +1 -0
- package/dist/components/ui/QRCode.js.map +1 -0
- package/dist/components/ui/index.d.ts +4 -0
- package/dist/components/ui/index.d.ts.map +1 -0
- package/dist/components/ui/index.js +4 -0
- package/dist/components/ui/index.js.map +1 -0
- package/dist/constants/timing.d.ts +22 -0
- package/dist/constants/timing.d.ts.map +1 -0
- package/dist/constants/timing.js +26 -0
- package/dist/constants/timing.js.map +1 -0
- package/dist/hooks/useActionSystem.d.ts +5 -4
- package/dist/hooks/useActionSystem.d.ts.map +1 -1
- package/dist/hooks/useActionSystem.js +120 -114
- package/dist/hooks/useActionSystem.js.map +1 -1
- package/dist/hooks/useDebugInfo.d.ts +11 -0
- package/dist/hooks/useDebugInfo.d.ts.map +1 -0
- package/dist/hooks/useDebugInfo.js +34 -0
- package/dist/hooks/useDebugInfo.js.map +1 -0
- package/dist/hooks/useDialogState.d.ts +22 -0
- package/dist/hooks/useDialogState.d.ts.map +1 -0
- package/dist/hooks/useDialogState.js +62 -0
- package/dist/hooks/useDialogState.js.map +1 -0
- package/dist/hooks/useInputHandling.d.ts +60 -0
- package/dist/hooks/useInputHandling.d.ts.map +1 -0
- package/dist/hooks/useInputHandling.js +280 -0
- package/dist/hooks/useInputHandling.js.map +1 -0
- package/dist/hooks/useLayoutManagement.d.ts +12 -0
- package/dist/hooks/useLayoutManagement.d.ts.map +1 -0
- package/dist/hooks/useLayoutManagement.js +58 -0
- package/dist/hooks/useLayoutManagement.js.map +1 -0
- package/dist/hooks/usePaneCreation.d.ts.map +1 -1
- package/dist/hooks/usePaneCreation.js +4 -9
- package/dist/hooks/usePaneCreation.js.map +1 -1
- package/dist/hooks/usePaneLoading.d.ts +45 -0
- package/dist/hooks/usePaneLoading.d.ts.map +1 -0
- package/dist/hooks/usePaneLoading.js +188 -0
- package/dist/hooks/usePaneLoading.js.map +1 -0
- package/dist/hooks/usePaneRunner.d.ts.map +1 -1
- package/dist/hooks/usePaneRunner.js +9 -6
- package/dist/hooks/usePaneRunner.js.map +1 -1
- package/dist/hooks/usePaneSync.d.ts +34 -0
- package/dist/hooks/usePaneSync.d.ts.map +1 -0
- package/dist/hooks/usePaneSync.js +182 -0
- package/dist/hooks/usePaneSync.js.map +1 -0
- package/dist/hooks/usePanes.d.ts.map +1 -1
- package/dist/hooks/usePanes.js +55 -405
- package/dist/hooks/usePanes.js.map +1 -1
- package/dist/hooks/useServices.d.ts +24 -0
- package/dist/hooks/useServices.d.ts.map +1 -0
- package/dist/hooks/useServices.js +39 -0
- package/dist/hooks/useServices.js.map +1 -0
- package/dist/hooks/useShellDetection.d.ts +10 -0
- package/dist/hooks/useShellDetection.d.ts.map +1 -0
- package/dist/hooks/useShellDetection.js +53 -0
- package/dist/hooks/useShellDetection.js.map +1 -0
- package/dist/hooks/useStatusMessages.d.ts +11 -0
- package/dist/hooks/useStatusMessages.d.ts.map +1 -0
- package/dist/hooks/useStatusMessages.js +32 -0
- package/dist/hooks/useStatusMessages.js.map +1 -0
- package/dist/hooks/useTemporaryStatus.d.ts +13 -0
- package/dist/hooks/useTemporaryStatus.d.ts.map +1 -0
- package/dist/hooks/useTemporaryStatus.js +30 -0
- package/dist/hooks/useTemporaryStatus.js.map +1 -0
- package/dist/hooks/useTerminalWidth.d.ts.map +1 -1
- package/dist/hooks/useTerminalWidth.js +7 -12
- package/dist/hooks/useTerminalWidth.js.map +1 -1
- package/dist/hooks/useTunnelManagement.d.ts +18 -0
- package/dist/hooks/useTunnelManagement.d.ts.map +1 -0
- package/dist/hooks/useTunnelManagement.js +55 -0
- package/dist/hooks/useTunnelManagement.js.map +1 -0
- package/dist/hooks/useWorktreeActions.d.ts.map +1 -1
- package/dist/hooks/useWorktreeActions.js +18 -13
- package/dist/hooks/useWorktreeActions.js.map +1 -1
- package/dist/index.js +75 -57
- package/dist/index.js.map +1 -1
- package/dist/layout/LayoutCalculator.d.ts +62 -0
- package/dist/layout/LayoutCalculator.d.ts.map +1 -0
- package/dist/layout/LayoutCalculator.js +143 -0
- package/dist/layout/LayoutCalculator.js.map +1 -0
- package/dist/layout/SpacerManager.d.ts +64 -0
- package/dist/layout/SpacerManager.d.ts.map +1 -0
- package/dist/layout/SpacerManager.js +148 -0
- package/dist/layout/SpacerManager.js.map +1 -0
- package/dist/layout/TmuxLayoutApplier.d.ts +59 -0
- package/dist/layout/TmuxLayoutApplier.d.ts.map +1 -0
- package/dist/layout/TmuxLayoutApplier.js +135 -0
- package/dist/layout/TmuxLayoutApplier.js.map +1 -0
- package/dist/panes/decorative-pane.d.ts.map +1 -0
- package/dist/{decorative-pane.js → panes/decorative-pane.js} +1 -1
- package/dist/panes/decorative-pane.js.map +1 -0
- package/dist/panes/spacer-pane.d.ts.map +1 -0
- package/dist/panes/spacer-pane.js.map +1 -0
- package/dist/server/embedded-assets.d.ts.map +1 -1
- package/dist/server/embedded-assets.js +636 -5637
- package/dist/server/embedded-assets.js.map +1 -1
- package/dist/server/routes/actionsRoutes.d.ts +2 -0
- package/dist/server/routes/actionsRoutes.d.ts.map +1 -0
- package/dist/server/routes/actionsRoutes.js +110 -0
- package/dist/server/routes/actionsRoutes.js.map +1 -0
- package/dist/server/routes/healthRoutes.d.ts +13 -0
- package/dist/server/routes/healthRoutes.d.ts.map +1 -0
- package/dist/server/routes/healthRoutes.js +70 -0
- package/dist/server/routes/healthRoutes.js.map +1 -0
- package/dist/server/routes/index.d.ts +8 -0
- package/dist/server/routes/index.d.ts.map +1 -0
- package/dist/server/routes/index.js +67 -0
- package/dist/server/routes/index.js.map +1 -0
- package/dist/server/routes/keysRoutes.d.ts +20 -0
- package/dist/server/routes/keysRoutes.d.ts.map +1 -0
- package/dist/server/routes/keysRoutes.js +111 -0
- package/dist/server/routes/keysRoutes.js.map +1 -0
- package/dist/server/routes/panesRoutes.d.ts +2 -0
- package/dist/server/routes/panesRoutes.d.ts.map +1 -0
- package/dist/server/routes/panesRoutes.js +373 -0
- package/dist/server/routes/panesRoutes.js.map +1 -0
- package/dist/server/routes/settingsRoutes.d.ts +94 -0
- package/dist/server/routes/settingsRoutes.d.ts.map +1 -0
- package/dist/server/routes/settingsRoutes.js +159 -0
- package/dist/server/routes/settingsRoutes.js.map +1 -0
- package/dist/server/routes/streamRoutes.d.ts +18 -0
- package/dist/server/routes/streamRoutes.d.ts.map +1 -0
- package/dist/server/routes/streamRoutes.js +87 -0
- package/dist/server/routes/streamRoutes.js.map +1 -0
- package/dist/server/routes/tunnelRoutes.d.ts +11 -0
- package/dist/server/routes/tunnelRoutes.d.ts.map +1 -0
- package/dist/server/routes/tunnelRoutes.js +28 -0
- package/dist/server/routes/tunnelRoutes.js.map +1 -0
- package/dist/server/routes.d.ts +16 -2
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +16 -878
- package/dist/server/routes.js.map +1 -1
- package/dist/{AutoUpdater.d.ts → services/AutoUpdater.d.ts} +1 -0
- package/dist/services/AutoUpdater.d.ts.map +1 -0
- package/dist/{AutoUpdater.js → services/AutoUpdater.js} +42 -9
- package/dist/services/AutoUpdater.js.map +1 -0
- package/dist/services/PaneAnalyzer.d.ts.map +1 -0
- package/dist/{PaneAnalyzer.js → services/PaneAnalyzer.js} +1 -1
- package/dist/services/PaneAnalyzer.js.map +1 -0
- package/dist/services/PaneLifecycleManager.d.ts +60 -0
- package/dist/services/PaneLifecycleManager.d.ts.map +1 -0
- package/dist/services/PaneLifecycleManager.js +119 -0
- package/dist/services/PaneLifecycleManager.js.map +1 -0
- package/dist/services/PaneWorkerManager.d.ts.map +1 -1
- package/dist/services/PaneWorkerManager.js +6 -3
- package/dist/services/PaneWorkerManager.js.map +1 -1
- package/dist/services/PopupManager.d.ts +68 -0
- package/dist/services/PopupManager.d.ts.map +1 -0
- package/dist/services/PopupManager.js +415 -0
- package/dist/services/PopupManager.js.map +1 -0
- package/dist/services/StatusDetector.js +1 -1
- package/dist/services/StatusDetector.js.map +1 -1
- package/dist/services/TerminalStreamer.d.ts +1 -0
- package/dist/services/TerminalStreamer.d.ts.map +1 -1
- package/dist/services/TerminalStreamer.js +22 -16
- package/dist/services/TerminalStreamer.js.map +1 -1
- package/dist/services/TmuxService.d.ts +298 -0
- package/dist/services/TmuxService.d.ts.map +1 -0
- package/dist/services/TmuxService.js +768 -0
- package/dist/services/TmuxService.js.map +1 -0
- package/dist/services/TunnelService.d.ts.map +1 -1
- package/dist/services/TunnelService.js +2 -1
- package/dist/services/TunnelService.js.map +1 -1
- package/dist/types.d.ts +4 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/asciiArt.d.ts.map +1 -1
- package/dist/utils/asciiArt.js +6 -19
- package/dist/utils/asciiArt.js.map +1 -1
- package/dist/utils/conflictMonitor.d.ts +20 -0
- package/dist/utils/conflictMonitor.d.ts.map +1 -0
- package/dist/utils/conflictMonitor.js +113 -0
- package/dist/utils/conflictMonitor.js.map +1 -0
- package/dist/utils/conflictResolutionPane.d.ts.map +1 -1
- package/dist/utils/conflictResolutionPane.js +70 -79
- package/dist/utils/conflictResolutionPane.js.map +1 -1
- package/dist/utils/errorHandling.d.ts +37 -0
- package/dist/utils/errorHandling.d.ts.map +1 -0
- package/dist/utils/errorHandling.js +112 -0
- package/dist/utils/errorHandling.js.map +1 -0
- package/dist/utils/generated-agents-doc.d.ts +1 -1
- package/dist/utils/generated-agents-doc.js +1 -1
- package/dist/utils/git.d.ts +4 -0
- package/dist/utils/git.d.ts.map +1 -1
- package/dist/utils/git.js +15 -0
- package/dist/utils/git.js.map +1 -1
- package/dist/utils/hooksDocs.d.ts +1 -1
- package/dist/utils/layoutManager.d.ts +20 -16
- package/dist/utils/layoutManager.d.ts.map +1 -1
- package/dist/utils/layoutManager.js +116 -367
- package/dist/utils/layoutManager.js.map +1 -1
- package/dist/utils/mergeExecution.d.ts.map +1 -1
- package/dist/utils/mergeExecution.js +8 -1
- package/dist/utils/mergeExecution.js.map +1 -1
- package/dist/utils/mergeValidation.d.ts.map +1 -1
- package/dist/utils/mergeValidation.js +42 -13
- package/dist/utils/mergeValidation.js.map +1 -1
- package/dist/utils/paneCreation.d.ts.map +1 -1
- package/dist/utils/paneCreation.js +111 -103
- package/dist/utils/paneCreation.js.map +1 -1
- package/dist/utils/paneRebinding.d.ts +14 -0
- package/dist/utils/paneRebinding.d.ts.map +1 -0
- package/dist/utils/paneRebinding.js +28 -0
- package/dist/utils/paneRebinding.js.map +1 -0
- package/dist/utils/popup.d.ts.map +1 -1
- package/dist/utils/popup.js +11 -17
- package/dist/utils/popup.js.map +1 -1
- package/dist/utils/postPaneCleanup.d.ts.map +1 -1
- package/dist/utils/postPaneCleanup.js +4 -3
- package/dist/utils/postPaneCleanup.js.map +1 -1
- package/dist/utils/shellPaneDetection.d.ts +3 -3
- package/dist/utils/shellPaneDetection.d.ts.map +1 -1
- package/dist/utils/shellPaneDetection.js +10 -6
- package/dist/utils/shellPaneDetection.js.map +1 -1
- package/dist/utils/systemCheck.d.ts +19 -0
- package/dist/utils/systemCheck.d.ts.map +1 -0
- package/dist/utils/systemCheck.js +160 -0
- package/dist/utils/systemCheck.js.map +1 -0
- package/dist/utils/tmux.d.ts +26 -7
- package/dist/utils/tmux.d.ts.map +1 -1
- package/dist/utils/tmux.js +66 -64
- package/dist/utils/tmux.js.map +1 -1
- package/dist/utils/welcomePane.d.ts +2 -2
- package/dist/utils/welcomePane.d.ts.map +1 -1
- package/dist/utils/welcomePane.js +20 -28
- package/dist/utils/welcomePane.js.map +1 -1
- package/dist/utils/welcomePaneManager.js +1 -1
- package/dist/utils/welcomePaneManager.js.map +1 -1
- package/dist/workers/PaneWorker.js +7 -9
- package/dist/workers/PaneWorker.js.map +1 -1
- package/dist/workers/updateChecker.js +1 -1
- package/dist/workers/updateChecker.js.map +1 -1
- package/package.json +3 -1
- package/dist/AutoUpdater.d.ts.map +0 -1
- package/dist/AutoUpdater.js.map +0 -1
- package/dist/BetterTextInput.d.ts +0 -10
- package/dist/BetterTextInput.d.ts.map +0 -1
- package/dist/BetterTextInput.js +0 -177
- package/dist/BetterTextInput.js.map +0 -1
- package/dist/CleanTextInput.d.ts.map +0 -1
- package/dist/CleanTextInput.js.map +0 -1
- package/dist/EnhancedTextInput.d.ts +0 -13
- package/dist/EnhancedTextInput.d.ts.map +0 -1
- package/dist/EnhancedTextInput.js +0 -443
- package/dist/EnhancedTextInput.js.map +0 -1
- package/dist/GeminiTextInput.d.ts +0 -12
- package/dist/GeminiTextInput.d.ts.map +0 -1
- package/dist/GeminiTextInput.js +0 -210
- package/dist/GeminiTextInput.js.map +0 -1
- package/dist/MergePane.d.ts.map +0 -1
- package/dist/MergePane.js.map +0 -1
- package/dist/MultilineTextInput.d.ts +0 -10
- package/dist/MultilineTextInput.d.ts.map +0 -1
- package/dist/MultilineTextInput.js +0 -184
- package/dist/MultilineTextInput.js.map +0 -1
- package/dist/PaneAnalyzer.d.ts.map +0 -1
- package/dist/PaneAnalyzer.js.map +0 -1
- package/dist/SimpleEnhancedInput.d.ts +0 -13
- package/dist/SimpleEnhancedInput.d.ts.map +0 -1
- package/dist/SimpleEnhancedInput.js +0 -639
- package/dist/SimpleEnhancedInput.js.map +0 -1
- package/dist/SimpleGeminiInput.d.ts +0 -12
- package/dist/SimpleGeminiInput.d.ts.map +0 -1
- package/dist/SimpleGeminiInput.js +0 -223
- package/dist/SimpleGeminiInput.js.map +0 -1
- package/dist/StyledTextInput.d.ts.map +0 -1
- package/dist/StyledTextInput.js.map +0 -1
- package/dist/components/ActionChoiceDialog.d.ts.map +0 -1
- package/dist/components/ActionChoiceDialog.js.map +0 -1
- package/dist/components/ActionConfirmDialog.d.ts.map +0 -1
- package/dist/components/ActionConfirmDialog.js.map +0 -1
- package/dist/components/ActionInputDialog.d.ts.map +0 -1
- package/dist/components/ActionInputDialog.js.map +0 -1
- package/dist/components/ActionProgressDialog.d.ts.map +0 -1
- package/dist/components/ActionProgressDialog.js.map +0 -1
- package/dist/components/AgentChoiceDialog.d.ts.map +0 -1
- package/dist/components/AgentChoiceDialog.js.map +0 -1
- package/dist/components/CloseOptionsDialog.d.ts.map +0 -1
- package/dist/components/CloseOptionsDialog.js.map +0 -1
- package/dist/components/CommandPromptDialog.d.ts.map +0 -1
- package/dist/components/CommandPromptDialog.js.map +0 -1
- package/dist/components/CreatingIndicator.d.ts.map +0 -1
- package/dist/components/CreatingIndicator.js.map +0 -1
- package/dist/components/DialogBox.d.ts.map +0 -1
- package/dist/components/DialogBox.js.map +0 -1
- package/dist/components/FileCopyPrompt.d.ts.map +0 -1
- package/dist/components/FileCopyPrompt.js.map +0 -1
- package/dist/components/FooterHelp.d.ts.map +0 -1
- package/dist/components/FooterHelp.js.map +0 -1
- package/dist/components/HooksDialog.d.ts.map +0 -1
- package/dist/components/HooksDialog.js.map +0 -1
- package/dist/components/KebabMenu.d.ts.map +0 -1
- package/dist/components/KebabMenu.js.map +0 -1
- package/dist/components/LoadingIndicator.d.ts.map +0 -1
- package/dist/components/LoadingIndicator.js.map +0 -1
- package/dist/components/MergeConfirmationDialog.d.ts.map +0 -1
- package/dist/components/MergeConfirmationDialog.js.map +0 -1
- package/dist/components/PaneCard.d.ts.map +0 -1
- package/dist/components/PaneCard.js.map +0 -1
- package/dist/components/PanesGrid.d.ts.map +0 -1
- package/dist/components/PanesGrid.js.map +0 -1
- package/dist/components/QRCode.d.ts.map +0 -1
- package/dist/components/QRCode.js.map +0 -1
- package/dist/components/RunningIndicator.d.ts.map +0 -1
- package/dist/components/RunningIndicator.js.map +0 -1
- package/dist/components/SettingsDialog.d.ts.map +0 -1
- package/dist/components/SettingsDialog.js.map +0 -1
- package/dist/components/Spinner.d.ts.map +0 -1
- package/dist/components/Spinner.js.map +0 -1
- package/dist/components/UpdateDialog.d.ts.map +0 -1
- package/dist/components/UpdateDialog.js.map +0 -1
- package/dist/components/UpdatingIndicator.d.ts.map +0 -1
- package/dist/components/UpdatingIndicator.js.map +0 -1
- package/dist/decorative-pane.d.ts.map +0 -1
- package/dist/decorative-pane.js.map +0 -1
- package/dist/popups/agentChoicePopup.d.ts.map +0 -1
- package/dist/popups/agentChoicePopup.js.map +0 -1
- package/dist/popups/choicePopup.d.ts.map +0 -1
- package/dist/popups/choicePopup.js.map +0 -1
- package/dist/popups/components/FileList.d.ts.map +0 -1
- package/dist/popups/components/FileList.js.map +0 -1
- package/dist/popups/components/PopupContainer.d.ts.map +0 -1
- package/dist/popups/components/PopupContainer.js.map +0 -1
- package/dist/popups/components/PopupInputBox.d.ts.map +0 -1
- package/dist/popups/components/PopupInputBox.js.map +0 -1
- package/dist/popups/components/PopupWrapper.d.ts.map +0 -1
- package/dist/popups/components/PopupWrapper.js.map +0 -1
- package/dist/popups/components/index.d.ts.map +0 -1
- package/dist/popups/components/index.js.map +0 -1
- package/dist/popups/config.d.ts.map +0 -1
- package/dist/popups/config.js.map +0 -1
- package/dist/popups/confirmPopup.d.ts.map +0 -1
- package/dist/popups/confirmPopup.js.map +0 -1
- package/dist/popups/hooksPopup.d.ts.map +0 -1
- package/dist/popups/hooksPopup.js.map +0 -1
- package/dist/popups/inputPopup.d.ts.map +0 -1
- package/dist/popups/inputPopup.js.map +0 -1
- package/dist/popups/kebabMenuPopup.d.ts.map +0 -1
- package/dist/popups/kebabMenuPopup.js.map +0 -1
- package/dist/popups/logsPopup.d.ts.map +0 -1
- package/dist/popups/logsPopup.js.map +0 -1
- package/dist/popups/mergePopup.d.ts.map +0 -1
- package/dist/popups/mergePopup.js.map +0 -1
- package/dist/popups/newPanePopup.d.ts.map +0 -1
- package/dist/popups/newPanePopup.js.map +0 -1
- package/dist/popups/progressPopup.d.ts.map +0 -1
- package/dist/popups/progressPopup.js.map +0 -1
- package/dist/popups/remotePopup.d.ts.map +0 -1
- package/dist/popups/remotePopup.js.map +0 -1
- package/dist/popups/settingsPopup.d.ts.map +0 -1
- package/dist/popups/settingsPopup.js.map +0 -1
- package/dist/popups/shortcutsPopup.d.ts.map +0 -1
- package/dist/popups/shortcutsPopup.js.map +0 -1
- package/dist/popups/templates/SimpleInputPopup.d.ts.map +0 -1
- package/dist/popups/templates/SimpleInputPopup.js.map +0 -1
- package/dist/server/static.d.ts +0 -6
- package/dist/server/static.d.ts.map +0 -1
- package/dist/server/static.js +0 -3040
- package/dist/server/static.js.map +0 -1
- package/dist/spacer-pane.d.ts.map +0 -1
- package/dist/spacer-pane.js.map +0 -1
- /package/dist/components/{ActionChoiceDialog.js → dialogs/ActionChoiceDialog.js} +0 -0
- /package/dist/components/{ActionConfirmDialog.d.ts → dialogs/ActionConfirmDialog.d.ts} +0 -0
- /package/dist/components/{ActionConfirmDialog.js → dialogs/ActionConfirmDialog.js} +0 -0
- /package/dist/components/{ActionInputDialog.d.ts → dialogs/ActionInputDialog.d.ts} +0 -0
- /package/dist/components/{ActionProgressDialog.d.ts → dialogs/ActionProgressDialog.d.ts} +0 -0
- /package/dist/components/{ActionProgressDialog.js → dialogs/ActionProgressDialog.js} +0 -0
- /package/dist/components/{AgentChoiceDialog.d.ts → dialogs/AgentChoiceDialog.d.ts} +0 -0
- /package/dist/components/{AgentChoiceDialog.js → dialogs/AgentChoiceDialog.js} +0 -0
- /package/dist/components/{CloseOptionsDialog.js → dialogs/CloseOptionsDialog.js} +0 -0
- /package/dist/components/{CommandPromptDialog.d.ts → dialogs/CommandPromptDialog.d.ts} +0 -0
- /package/dist/components/{DialogBox.d.ts → dialogs/DialogBox.d.ts} +0 -0
- /package/dist/components/{DialogBox.js → dialogs/DialogBox.js} +0 -0
- /package/dist/components/{HooksDialog.d.ts → dialogs/HooksDialog.d.ts} +0 -0
- /package/dist/components/{HooksDialog.js → dialogs/HooksDialog.js} +0 -0
- /package/dist/components/{MergeConfirmationDialog.js → dialogs/MergeConfirmationDialog.js} +0 -0
- /package/dist/components/{SettingsDialog.js → dialogs/SettingsDialog.js} +0 -0
- /package/dist/components/{UpdateDialog.d.ts → dialogs/UpdateDialog.d.ts} +0 -0
- /package/dist/components/{UpdateDialog.js → dialogs/UpdateDialog.js} +0 -0
- /package/dist/components/{CreatingIndicator.d.ts → indicators/CreatingIndicator.d.ts} +0 -0
- /package/dist/components/{CreatingIndicator.js → indicators/CreatingIndicator.js} +0 -0
- /package/dist/components/{LoadingIndicator.d.ts → indicators/LoadingIndicator.d.ts} +0 -0
- /package/dist/components/{LoadingIndicator.js → indicators/LoadingIndicator.js} +0 -0
- /package/dist/components/{RunningIndicator.d.ts → indicators/RunningIndicator.d.ts} +0 -0
- /package/dist/components/{RunningIndicator.js → indicators/RunningIndicator.js} +0 -0
- /package/dist/components/{Spinner.d.ts → indicators/Spinner.d.ts} +0 -0
- /package/dist/components/{Spinner.js → indicators/Spinner.js} +0 -0
- /package/dist/components/{UpdatingIndicator.d.ts → indicators/UpdatingIndicator.d.ts} +0 -0
- /package/dist/components/{UpdatingIndicator.js → indicators/UpdatingIndicator.js} +0 -0
- /package/dist/{CleanTextInput.d.ts → components/inputs/CleanTextInput.d.ts} +0 -0
- /package/dist/{StyledTextInput.d.ts → components/inputs/StyledTextInput.d.ts} +0 -0
- /package/dist/{StyledTextInput.js → components/inputs/StyledTextInput.js} +0 -0
- /package/dist/components/{KebabMenu.js → panes/KebabMenu.js} +0 -0
- /package/dist/{MergePane.d.ts → components/panes/MergePane.d.ts} +0 -0
- /package/dist/{popups → components/popups}/agentChoicePopup.d.ts +0 -0
- /package/dist/{popups → components/popups}/choicePopup.d.ts +0 -0
- /package/dist/{popups → components/popups}/config.d.ts +0 -0
- /package/dist/{popups → components/popups}/confirmPopup.d.ts +0 -0
- /package/dist/{popups → components/popups}/hooksPopup.d.ts +0 -0
- /package/dist/{popups → components/popups}/inputPopup.d.ts +0 -0
- /package/dist/{popups → components/popups}/kebabMenuPopup.d.ts +0 -0
- /package/dist/{popups → components/popups}/logsPopup.d.ts +0 -0
- /package/dist/{popups → components/popups}/mergePopup.d.ts +0 -0
- /package/dist/{popups → components/popups}/newPanePopup.d.ts +0 -0
- /package/dist/{popups → components/popups}/progressPopup.d.ts +0 -0
- /package/dist/{popups → components/popups}/remotePopup.d.ts +0 -0
- /package/dist/{popups → components/popups}/settingsPopup.d.ts +0 -0
- /package/dist/{popups/components → components/popups/shared}/FileList.d.ts +0 -0
- /package/dist/{popups/components → components/popups/shared}/FileList.js +0 -0
- /package/dist/{popups/components → components/popups/shared}/PopupContainer.d.ts +0 -0
- /package/dist/{popups/components → components/popups/shared}/PopupContainer.js +0 -0
- /package/dist/{popups/components → components/popups/shared}/PopupInputBox.d.ts +0 -0
- /package/dist/{popups/components → components/popups/shared}/PopupInputBox.js +0 -0
- /package/dist/{popups/components → components/popups/shared}/PopupWrapper.d.ts +0 -0
- /package/dist/{popups/components → components/popups/shared}/PopupWrapper.js +0 -0
- /package/dist/{popups/components → components/popups/shared}/index.d.ts +0 -0
- /package/dist/{popups/components → components/popups/shared}/index.js +0 -0
- /package/dist/{popups → components/popups}/shortcutsPopup.d.ts +0 -0
- /package/dist/{popups → components/popups}/templates/SimpleInputPopup.d.ts +0 -0
- /package/dist/components/{FileCopyPrompt.d.ts → ui/FileCopyPrompt.d.ts} +0 -0
- /package/dist/components/{FileCopyPrompt.js → ui/FileCopyPrompt.js} +0 -0
- /package/dist/components/{FooterHelp.d.ts → ui/FooterHelp.d.ts} +0 -0
- /package/dist/components/{FooterHelp.js → ui/FooterHelp.js} +0 -0
- /package/dist/components/{QRCode.d.ts → ui/QRCode.d.ts} +0 -0
- /package/dist/components/{QRCode.js → ui/QRCode.js} +0 -0
- /package/dist/{decorative-pane.d.ts → panes/decorative-pane.d.ts} +0 -0
- /package/dist/{spacer-pane.d.ts → panes/spacer-pane.d.ts} +0 -0
- /package/dist/{spacer-pane.js → panes/spacer-pane.js} +0 -0
- /package/dist/{PaneAnalyzer.d.ts → services/PaneAnalyzer.d.ts} +0 -0
package/dist/DmuxApp.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import React, { useState, useEffect } from "react";
|
|
2
|
-
import { Box, Text,
|
|
3
|
-
import { execSync } from "child_process";
|
|
4
|
-
import fs from "fs/promises";
|
|
5
|
-
import path from "path";
|
|
2
|
+
import { Box, Text, useApp, useStdout } from "ink";
|
|
6
3
|
import { createRequire } from "module";
|
|
4
|
+
import { TmuxService } from "./services/TmuxService.js";
|
|
7
5
|
// Hooks
|
|
8
6
|
import usePanes from "./hooks/usePanes.js";
|
|
9
7
|
import useProjectSettings from "./hooks/useProjectSettings.js";
|
|
@@ -15,38 +13,40 @@ import useAgentStatus from "./hooks/useAgentStatus.js";
|
|
|
15
13
|
import usePaneRunner from "./hooks/usePaneRunner.js";
|
|
16
14
|
import usePaneCreation from "./hooks/usePaneCreation.js";
|
|
17
15
|
import useActionSystem from "./hooks/useActionSystem.js";
|
|
16
|
+
import { useStatusMessages } from "./hooks/useStatusMessages.js";
|
|
17
|
+
import { useLayoutManagement } from "./hooks/useLayoutManagement.js";
|
|
18
|
+
import { useInputHandling } from "./hooks/useInputHandling.js";
|
|
19
|
+
import { useDialogState } from "./hooks/useDialogState.js";
|
|
20
|
+
import { useTunnelManagement } from "./hooks/useTunnelManagement.js";
|
|
21
|
+
import { useDebugInfo } from "./hooks/useDebugInfo.js";
|
|
18
22
|
// Utils
|
|
19
|
-
import { enforceControlPaneSize } from "./utils/tmux.js";
|
|
20
23
|
import { SIDEBAR_WIDTH } from "./utils/layoutManager.js";
|
|
21
|
-
import {
|
|
22
|
-
import { generateSlug } from "./utils/slug.js";
|
|
23
|
-
import { getMainBranch } from "./utils/git.js";
|
|
24
|
-
import { capturePaneContent } from "./utils/paneCapture.js";
|
|
25
|
-
import { supportsPopups, launchNodePopupNonBlocking, POPUP_POSITIONING, } from "./utils/popup.js";
|
|
24
|
+
import { supportsPopups } from "./utils/popup.js";
|
|
26
25
|
import { StateManager } from "./shared/StateManager.js";
|
|
27
|
-
import {
|
|
26
|
+
import { REPAINT_SPINNER_DURATION, } from "./constants/timing.js";
|
|
28
27
|
import { getStatusDetector, } from "./services/StatusDetector.js";
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
28
|
+
import { SettingsManager } from "./utils/settingsManager.js";
|
|
29
|
+
import { useServices } from "./hooks/useServices.js";
|
|
30
|
+
import { PaneLifecycleManager } from "./services/PaneLifecycleManager.js";
|
|
31
31
|
import { fileURLToPath } from "url";
|
|
32
32
|
import { dirname } from "path";
|
|
33
33
|
const __filename = fileURLToPath(import.meta.url);
|
|
34
34
|
const __dirname = dirname(__filename);
|
|
35
35
|
const require = createRequire(import.meta.url);
|
|
36
36
|
const packageJson = require("../package.json");
|
|
37
|
-
import PanesGrid from "./components/PanesGrid.js";
|
|
38
|
-
import CommandPromptDialog from "./components/CommandPromptDialog.js";
|
|
39
|
-
import FileCopyPrompt from "./components/FileCopyPrompt.js";
|
|
40
|
-
import LoadingIndicator from "./components/LoadingIndicator.js";
|
|
41
|
-
import RunningIndicator from "./components/RunningIndicator.js";
|
|
42
|
-
import UpdatingIndicator from "./components/UpdatingIndicator.js";
|
|
43
|
-
import FooterHelp from "./components/FooterHelp.js";
|
|
37
|
+
import PanesGrid from "./components/panes/PanesGrid.js";
|
|
38
|
+
import CommandPromptDialog from "./components/dialogs/CommandPromptDialog.js";
|
|
39
|
+
import FileCopyPrompt from "./components/ui/FileCopyPrompt.js";
|
|
40
|
+
import LoadingIndicator from "./components/indicators/LoadingIndicator.js";
|
|
41
|
+
import RunningIndicator from "./components/indicators/RunningIndicator.js";
|
|
42
|
+
import UpdatingIndicator from "./components/indicators/UpdatingIndicator.js";
|
|
43
|
+
import FooterHelp from "./components/ui/FooterHelp.js";
|
|
44
44
|
const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoot, autoUpdater, serverPort, server, controlPaneId, }) => {
|
|
45
45
|
const { stdout } = useStdout();
|
|
46
46
|
const terminalHeight = stdout?.rows || 40;
|
|
47
47
|
/* panes state moved to usePanes */
|
|
48
48
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
49
|
-
const
|
|
49
|
+
const { statusMessage, setStatusMessage, showStatus, clearStatus } = useStatusMessages();
|
|
50
50
|
const [isCreatingPane, setIsCreatingPane] = useState(false);
|
|
51
51
|
// Settings state
|
|
52
52
|
const [settingsManager] = useState(() => new SettingsManager(projectRoot));
|
|
@@ -55,18 +55,16 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
55
55
|
// Spinner state - shows for a few frames to force render
|
|
56
56
|
const [showRepaintSpinner, setShowRepaintSpinner] = useState(false);
|
|
57
57
|
const { projectSettings, saveSettings } = useProjectSettings(settingsFile);
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
const
|
|
64
|
-
// Debug
|
|
65
|
-
const
|
|
66
|
-
// Current git branch state (for dev builds)
|
|
67
|
-
const [currentBranch, setCurrentBranch] = useState(null);
|
|
58
|
+
// Dialog state management
|
|
59
|
+
const dialogState = useDialogState();
|
|
60
|
+
const { showCommandPrompt, setShowCommandPrompt, commandInput, setCommandInput, showFileCopyPrompt, setShowFileCopyPrompt, currentCommandType, setCurrentCommandType, runningCommand, setRunningCommand, quitConfirmMode, setQuitConfirmMode, } = dialogState;
|
|
61
|
+
// Tunnel/network state management
|
|
62
|
+
const tunnelState = useTunnelManagement();
|
|
63
|
+
const { tunnelUrl, setTunnelUrl, tunnelCreating, setTunnelCreating, tunnelCopied, setTunnelCopied, localIp, setLocalIp, } = tunnelState;
|
|
64
|
+
// Debug/development info
|
|
65
|
+
const { debugMessage, setDebugMessage, currentBranch } = useDebugInfo(__dirname);
|
|
68
66
|
// Update state handled by hook
|
|
69
|
-
const { updateInfo,
|
|
67
|
+
const { updateInfo, isUpdating, updateAvailable, } = useAutoUpdater(autoUpdater, setStatusMessage);
|
|
70
68
|
const { exit } = useApp();
|
|
71
69
|
// Flag to ignore input temporarily after popup closes (prevents buffered keys)
|
|
72
70
|
const [ignoreInput, setIgnoreInput] = useState(false);
|
|
@@ -80,12 +78,6 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
80
78
|
// Track unread error and warning counts for logs badge
|
|
81
79
|
const [unreadErrorCount, setUnreadErrorCount] = useState(0);
|
|
82
80
|
const [unreadWarningCount, setUnreadWarningCount] = useState(0);
|
|
83
|
-
// Tunnel state
|
|
84
|
-
const [tunnelUrl, setTunnelUrl] = useState(null);
|
|
85
|
-
const [tunnelCreating, setTunnelCreating] = useState(false);
|
|
86
|
-
const [tunnelSpinnerFrame, setTunnelSpinnerFrame] = useState(0);
|
|
87
|
-
const [localIp, setLocalIp] = useState("127.0.0.1");
|
|
88
|
-
const [tunnelCopied, setTunnelCopied] = useState(false);
|
|
89
81
|
// Subscribe to StateManager for unread error/warning count updates
|
|
90
82
|
useEffect(() => {
|
|
91
83
|
const stateManager = StateManager.getInstance();
|
|
@@ -103,12 +95,18 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
103
95
|
}, []);
|
|
104
96
|
// Panes state and persistence (skipLoading will be updated after actionSystem is initialized)
|
|
105
97
|
const { panes, setPanes, isLoading, loadPanes, savePanes } = usePanes(panesFile, false);
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
-
|
|
109
|
-
|
|
98
|
+
// Pane lifecycle manager - handles locking to prevent race conditions
|
|
99
|
+
// Replaces the old timeout-based intentionallyClosedPanes Set
|
|
100
|
+
const lifecycleManager = React.useMemo(() => PaneLifecycleManager.getInstance(), []);
|
|
101
|
+
// Clean up stale lifecycle operations periodically
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
const cleanupInterval = setInterval(() => {
|
|
104
|
+
lifecycleManager.cleanupStaleOperations();
|
|
105
|
+
}, 60000); // Every 60 seconds
|
|
106
|
+
return () => clearInterval(cleanupInterval);
|
|
107
|
+
}, [lifecycleManager]);
|
|
110
108
|
// Pane runner
|
|
111
|
-
const { copyNonGitFiles, runCommandInternal,
|
|
109
|
+
const { copyNonGitFiles, runCommandInternal, } = usePaneRunner({
|
|
112
110
|
panes,
|
|
113
111
|
savePanes,
|
|
114
112
|
projectSettings,
|
|
@@ -120,15 +118,16 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
120
118
|
setForceRepaintTrigger((prev) => prev + 1);
|
|
121
119
|
setShowRepaintSpinner(true);
|
|
122
120
|
// Hide spinner after a few frames (enough to trigger multiple renders)
|
|
123
|
-
setTimeout(() => setShowRepaintSpinner(false),
|
|
121
|
+
setTimeout(() => setShowRepaintSpinner(false), REPAINT_SPINNER_DURATION);
|
|
124
122
|
};
|
|
125
123
|
// Force repaint effect - ensures Ink re-renders when trigger changes
|
|
126
124
|
useEffect(() => {
|
|
127
125
|
if (forceRepaintTrigger > 0) {
|
|
128
126
|
// Small delay to ensure terminal is ready
|
|
129
|
-
const timer = setTimeout(() => {
|
|
127
|
+
const timer = setTimeout(async () => {
|
|
130
128
|
try {
|
|
131
|
-
|
|
129
|
+
const tmuxService = TmuxService.getInstance();
|
|
130
|
+
await tmuxService.refreshClient();
|
|
132
131
|
}
|
|
133
132
|
catch { }
|
|
134
133
|
}, 50);
|
|
@@ -137,48 +136,26 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
137
136
|
}, [forceRepaintTrigger]);
|
|
138
137
|
// Get local network IP on mount
|
|
139
138
|
useEffect(() => {
|
|
140
|
-
|
|
141
|
-
// Get local IP address (not 127.0.0.1)
|
|
142
|
-
const result = execSync(`hostname -I 2>/dev/null || ifconfig | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | head -1`, {
|
|
143
|
-
encoding: "utf-8",
|
|
144
|
-
stdio: "pipe",
|
|
145
|
-
}).trim();
|
|
146
|
-
if (result) {
|
|
147
|
-
setLocalIp(result.split(" ")[0]); // Take first IP if multiple
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
catch {
|
|
151
|
-
// Fallback to 127.0.0.1
|
|
152
|
-
setLocalIp("127.0.0.1");
|
|
153
|
-
}
|
|
154
|
-
}, []);
|
|
155
|
-
// Spinner animation for tunnel creation
|
|
156
|
-
useEffect(() => {
|
|
157
|
-
if (!tunnelCreating)
|
|
158
|
-
return;
|
|
159
|
-
const spinnerInterval = setInterval(() => {
|
|
160
|
-
setTunnelSpinnerFrame((prev) => (prev + 1) % 10);
|
|
161
|
-
}, 80); // Update every 80ms
|
|
162
|
-
return () => clearInterval(spinnerInterval);
|
|
163
|
-
}, [tunnelCreating]);
|
|
164
|
-
// Get current git branch on mount (only for dev builds)
|
|
165
|
-
useEffect(() => {
|
|
166
|
-
const isDev = process.env.DMUX_DEV === "true" || __dirname.includes("dist") === false;
|
|
167
|
-
if (isDev) {
|
|
139
|
+
const getLocalIp = async () => {
|
|
168
140
|
try {
|
|
169
|
-
|
|
141
|
+
// Get local IP address (not 127.0.0.1)
|
|
142
|
+
const { execSync } = await import("child_process");
|
|
143
|
+
const result = execSync(`hostname -I 2>/dev/null || ifconfig | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | head -1`, {
|
|
170
144
|
encoding: "utf-8",
|
|
171
145
|
stdio: "pipe",
|
|
172
|
-
cwd: projectRoot,
|
|
173
146
|
}).trim();
|
|
174
|
-
|
|
147
|
+
if (result) {
|
|
148
|
+
setLocalIp(result.split(" ")[0]); // Take first IP if multiple
|
|
149
|
+
}
|
|
175
150
|
}
|
|
176
151
|
catch {
|
|
177
|
-
//
|
|
178
|
-
|
|
152
|
+
// Fallback to 127.0.0.1
|
|
153
|
+
setLocalIp("127.0.0.1");
|
|
179
154
|
}
|
|
180
|
-
}
|
|
181
|
-
|
|
155
|
+
};
|
|
156
|
+
getLocalIp();
|
|
157
|
+
}, []);
|
|
158
|
+
// Spinner animation and branch detection now handled in hooks
|
|
182
159
|
// Pane creation
|
|
183
160
|
const { createNewPane: createNewPaneHook } = usePaneCreation({
|
|
184
161
|
panes,
|
|
@@ -191,6 +168,26 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
191
168
|
availableAgents,
|
|
192
169
|
forceRepaint,
|
|
193
170
|
});
|
|
171
|
+
// Initialize services
|
|
172
|
+
const { popupManager } = useServices({
|
|
173
|
+
// PopupManager config
|
|
174
|
+
sidebarWidth: SIDEBAR_WIDTH,
|
|
175
|
+
projectRoot: projectRoot || process.cwd(),
|
|
176
|
+
popupsSupported,
|
|
177
|
+
terminalWidth,
|
|
178
|
+
terminalHeight,
|
|
179
|
+
availableAgents,
|
|
180
|
+
agentChoice,
|
|
181
|
+
serverPort,
|
|
182
|
+
server,
|
|
183
|
+
settingsManager,
|
|
184
|
+
projectSettings,
|
|
185
|
+
// Callbacks
|
|
186
|
+
setStatusMessage,
|
|
187
|
+
setIgnoreInput,
|
|
188
|
+
savePanes,
|
|
189
|
+
loadPanes,
|
|
190
|
+
});
|
|
194
191
|
// Listen for status updates with analysis data and merge into panes
|
|
195
192
|
useEffect(() => {
|
|
196
193
|
const statusDetector = getStatusDetector();
|
|
@@ -308,897 +305,142 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
308
305
|
// findCardInDirection provided by useNavigation
|
|
309
306
|
// savePanes moved to usePanes
|
|
310
307
|
// applySmartLayout moved to utils/tmux
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
return;
|
|
317
|
-
}
|
|
318
|
-
try {
|
|
319
|
-
// Resolve the popup script path from the project root
|
|
320
|
-
// This handles both dev (tsx running from src) and prod (compiled to dist)
|
|
321
|
-
const projectRootForPopup = __dirname.includes("/dist")
|
|
322
|
-
? path.resolve(__dirname, "..") // If in dist/, go up one level
|
|
323
|
-
: path.resolve(__dirname, ".."); // If in src/, go up one level
|
|
324
|
-
const popupScriptPath = path.join(projectRootForPopup, "dist", "popups", "newPanePopup.js");
|
|
325
|
-
// Calculate popup height as 80% of terminal height to allow room for file list
|
|
326
|
-
const popupHeight = Math.floor(terminalHeight * 0.8);
|
|
327
|
-
// Launch the popup non-blocking and track it
|
|
328
|
-
const popupHandle = launchNodePopupNonBlocking(popupScriptPath, [], {
|
|
329
|
-
...POPUP_POSITIONING.centeredWithSidebar(SIDEBAR_WIDTH),
|
|
330
|
-
width: 90,
|
|
331
|
-
height: popupHeight,
|
|
332
|
-
title: " ✨ dmux - Create New Pane ",
|
|
333
|
-
});
|
|
334
|
-
LogService.getInstance().debug(`Popup created - PID: ${popupHandle.pid}, bounds: ${JSON.stringify(popupHandle.bounds)}`, "PopupTracking");
|
|
335
|
-
// Wait for the popup to close
|
|
336
|
-
const result = await popupHandle.resultPromise;
|
|
337
|
-
// Clear active popup tracking
|
|
338
|
-
LogService.getInstance().debug("Popup closed, clearing tracking", "PopupTracking");
|
|
339
|
-
// Ignore input briefly after popup closes to prevent buffered keys
|
|
340
|
-
setIgnoreInput(true);
|
|
341
|
-
setTimeout(() => setIgnoreInput(false), 100);
|
|
342
|
-
if (result.success && result.data) {
|
|
343
|
-
// User entered a prompt - now decide which agent to use
|
|
344
|
-
const promptValue = result.data;
|
|
345
|
-
const agents = availableAgents;
|
|
346
|
-
if (agents.length === 0) {
|
|
347
|
-
await createNewPaneHook(promptValue);
|
|
348
|
-
}
|
|
349
|
-
else if (agents.length === 1) {
|
|
350
|
-
await createNewPaneHook(promptValue, agents[0]);
|
|
351
|
-
}
|
|
352
|
-
else {
|
|
353
|
-
// Multiple agents available - check for default agent setting first
|
|
354
|
-
const settings = settingsManager.getSettings();
|
|
355
|
-
if (settings.defaultAgent && agents.includes(settings.defaultAgent)) {
|
|
356
|
-
// Use the default agent from settings
|
|
357
|
-
await createNewPaneHook(promptValue, settings.defaultAgent);
|
|
358
|
-
}
|
|
359
|
-
else {
|
|
360
|
-
// No default agent configured or default not available - show agent choice popup
|
|
361
|
-
const selectedAgent = await launchAgentChoicePopup(promptValue);
|
|
362
|
-
if (selectedAgent) {
|
|
363
|
-
await createNewPaneHook(promptValue, selectedAgent);
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
else if (result.cancelled) {
|
|
369
|
-
// User pressed ESC - do nothing
|
|
370
|
-
return;
|
|
371
|
-
}
|
|
372
|
-
else if (result.error) {
|
|
373
|
-
setStatusMessage(`Popup error: ${result.error}`);
|
|
374
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
375
|
-
}
|
|
308
|
+
// Helper function to handle agent choice and pane creation
|
|
309
|
+
const handlePaneCreationWithAgent = async (prompt) => {
|
|
310
|
+
const agents = availableAgents;
|
|
311
|
+
if (agents.length === 0) {
|
|
312
|
+
await createNewPaneHook(prompt);
|
|
376
313
|
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
314
|
+
else if (agents.length === 1) {
|
|
315
|
+
await createNewPaneHook(prompt, agents[0]);
|
|
380
316
|
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
387
|
-
return;
|
|
388
|
-
}
|
|
389
|
-
const selectedPane = panes[paneIndex];
|
|
390
|
-
if (!selectedPane) {
|
|
391
|
-
return;
|
|
392
|
-
}
|
|
393
|
-
try {
|
|
394
|
-
// Resolve the popup script path
|
|
395
|
-
const projectRootForPopup = __dirname.includes("/dist")
|
|
396
|
-
? path.resolve(__dirname, "..") // If in dist/, go up one level
|
|
397
|
-
: path.resolve(__dirname, ".."); // If in src/, go up one level
|
|
398
|
-
const popupScriptPath = path.join(projectRootForPopup, "dist", "popups", "kebabMenuPopup.js");
|
|
399
|
-
// Get available actions for this pane
|
|
400
|
-
const actions = getAvailableActions(selectedPane, projectSettings);
|
|
401
|
-
const actionsJson = JSON.stringify(actions);
|
|
402
|
-
// Launch the popup non-blocking and track it
|
|
403
|
-
const popupHandle = launchNodePopupNonBlocking(popupScriptPath, [selectedPane.slug, actionsJson], {
|
|
404
|
-
...POPUP_POSITIONING.standard(SIDEBAR_WIDTH),
|
|
405
|
-
width: 60,
|
|
406
|
-
height: Math.min(20, actions.length + 5),
|
|
407
|
-
title: `Menu: ${selectedPane.slug}`,
|
|
408
|
-
});
|
|
409
|
-
// Wait for the popup to close
|
|
410
|
-
const result = await popupHandle.resultPromise;
|
|
411
|
-
// Clear active popup tracking
|
|
412
|
-
// Log the entire result for debugging
|
|
413
|
-
LogService.getInstance().debug(`Kebab menu result: ${JSON.stringify(result)}`, "KebabMenu");
|
|
414
|
-
if (result.success && result.data) {
|
|
415
|
-
// User selected an action
|
|
416
|
-
const actionId = result.data;
|
|
417
|
-
LogService.getInstance().debug(`Action selected: ${actionId}`, "KebabMenu");
|
|
418
|
-
// Handle merge action with dedicated popup
|
|
419
|
-
if (actionId === PaneAction.MERGE) {
|
|
420
|
-
LogService.getInstance().debug(`Merge action selected for pane: ${selectedPane.slug}`, "MergeAction");
|
|
421
|
-
try {
|
|
422
|
-
await launchMergePopup(selectedPane);
|
|
423
|
-
LogService.getInstance().debug("Merge popup completed", "MergeAction");
|
|
424
|
-
}
|
|
425
|
-
catch (error) {
|
|
426
|
-
LogService.getInstance().error("Merge popup error", "MergeAction", selectedPane.id, error instanceof Error ? error : undefined);
|
|
427
|
-
setStatusMessage(`Merge popup failed: ${error}`);
|
|
428
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
else {
|
|
432
|
-
// Execute other actions through action system
|
|
433
|
-
await actionSystem.executeAction(actionId, selectedPane, {
|
|
434
|
-
mainBranch: getMainBranch(),
|
|
435
|
-
});
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
else if (result.cancelled) {
|
|
439
|
-
// User pressed ESC - do nothing
|
|
440
|
-
LogService.getInstance().debug("Kebab menu cancelled", "KebabMenu");
|
|
441
|
-
return;
|
|
442
|
-
}
|
|
443
|
-
else if (result.error) {
|
|
444
|
-
LogService.getInstance().error(`Kebab menu error: ${result.error}`, "KebabMenu");
|
|
445
|
-
setStatusMessage(`Popup error: ${result.error}`);
|
|
446
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
317
|
+
else {
|
|
318
|
+
// Multiple agents available - check for default agent setting first
|
|
319
|
+
const settings = settingsManager.getSettings();
|
|
320
|
+
if (settings.defaultAgent && agents.includes(settings.defaultAgent)) {
|
|
321
|
+
await createNewPaneHook(prompt, settings.defaultAgent);
|
|
447
322
|
}
|
|
448
323
|
else {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
setStatusMessage(`Failed to launch popup: ${error.message}`);
|
|
454
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
455
|
-
}
|
|
456
|
-
};
|
|
457
|
-
const launchConfirmPopup = async (title, message, yesLabel, noLabel) => {
|
|
458
|
-
// Only launch popup if tmux supports it
|
|
459
|
-
if (!popupsSupported) {
|
|
460
|
-
setStatusMessage("Popups require tmux 3.2+");
|
|
461
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
462
|
-
return false;
|
|
463
|
-
}
|
|
464
|
-
try {
|
|
465
|
-
// Resolve the popup script path
|
|
466
|
-
const projectRootForPopup = __dirname.includes("/dist")
|
|
467
|
-
? path.resolve(__dirname, "..") // If in dist/, go up one level
|
|
468
|
-
: path.resolve(__dirname, ".."); // If in src/, go up one level
|
|
469
|
-
const popupScriptPath = path.join(projectRootForPopup, "dist", "popups", "confirmPopup.js");
|
|
470
|
-
// Write data to temp file to avoid shell escaping issues
|
|
471
|
-
const dataFile = `/tmp/dmux-confirm-${Date.now()}.json`;
|
|
472
|
-
const dataJson = JSON.stringify({ title, message, yesLabel, noLabel });
|
|
473
|
-
await fs.writeFile(dataFile, dataJson);
|
|
474
|
-
// Launch the popup non-blocking and track it
|
|
475
|
-
const popupHandle = launchNodePopupNonBlocking(popupScriptPath, [dataFile], {
|
|
476
|
-
...POPUP_POSITIONING.standard(SIDEBAR_WIDTH),
|
|
477
|
-
width: 60,
|
|
478
|
-
height: 12,
|
|
479
|
-
title: title || "Confirm",
|
|
480
|
-
});
|
|
481
|
-
// Wait for the popup to close
|
|
482
|
-
const result = await popupHandle.resultPromise;
|
|
483
|
-
// Clear active popup tracking
|
|
484
|
-
// Clean up temp file
|
|
485
|
-
try {
|
|
486
|
-
await fs.unlink(dataFile);
|
|
487
|
-
}
|
|
488
|
-
catch { }
|
|
489
|
-
if (result.success && result.data !== undefined) {
|
|
490
|
-
return result.data;
|
|
491
|
-
}
|
|
492
|
-
else if (result.cancelled) {
|
|
493
|
-
return false;
|
|
494
|
-
}
|
|
495
|
-
else if (result.error) {
|
|
496
|
-
setStatusMessage(`Popup error: ${result.error}`);
|
|
497
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
498
|
-
return false;
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
catch (error) {
|
|
502
|
-
setStatusMessage(`Failed to launch popup: ${error.message}`);
|
|
503
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
504
|
-
}
|
|
505
|
-
return false;
|
|
506
|
-
};
|
|
507
|
-
const launchAgentChoicePopup = async (prompt) => {
|
|
508
|
-
// Only launch popup if tmux supports it
|
|
509
|
-
if (!popupsSupported) {
|
|
510
|
-
setStatusMessage("Popups require tmux 3.2+");
|
|
511
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
512
|
-
return null;
|
|
513
|
-
}
|
|
514
|
-
try {
|
|
515
|
-
// Resolve the popup script path
|
|
516
|
-
const projectRootForPopup = __dirname.includes("/dist")
|
|
517
|
-
? path.resolve(__dirname, "..") // If in dist/, go up one level
|
|
518
|
-
: path.resolve(__dirname, ".."); // If in src/, go up one level
|
|
519
|
-
const popupScriptPath = path.join(projectRootForPopup, "dist", "popups", "agentChoicePopup.js");
|
|
520
|
-
const agentsJson = JSON.stringify(availableAgents);
|
|
521
|
-
const defaultAgentArg = agentChoice || availableAgents[0] || "claude";
|
|
522
|
-
// Launch the popup non-blocking and track it
|
|
523
|
-
const popupHandle = launchNodePopupNonBlocking(popupScriptPath, [agentsJson, defaultAgentArg], {
|
|
524
|
-
...POPUP_POSITIONING.standard(SIDEBAR_WIDTH),
|
|
525
|
-
width: 50,
|
|
526
|
-
height: 10,
|
|
527
|
-
title: "Select Agent",
|
|
528
|
-
});
|
|
529
|
-
// Wait for the popup to close
|
|
530
|
-
const result = await popupHandle.resultPromise;
|
|
531
|
-
// Clear active popup tracking
|
|
532
|
-
if (result.success && result.data) {
|
|
533
|
-
return result.data;
|
|
534
|
-
}
|
|
535
|
-
else if (result.cancelled) {
|
|
536
|
-
return null;
|
|
537
|
-
}
|
|
538
|
-
else if (result.error) {
|
|
539
|
-
setStatusMessage(`Popup error: ${result.error}`);
|
|
540
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
541
|
-
return null;
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
catch (error) {
|
|
545
|
-
setStatusMessage(`Failed to launch popup: ${error.message}`);
|
|
546
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
547
|
-
}
|
|
548
|
-
return null;
|
|
549
|
-
};
|
|
550
|
-
const launchHooksPopup = async () => {
|
|
551
|
-
// Only launch popup if tmux supports it
|
|
552
|
-
if (!popupsSupported) {
|
|
553
|
-
setStatusMessage("Popups require tmux 3.2+");
|
|
554
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
555
|
-
return;
|
|
556
|
-
}
|
|
557
|
-
try {
|
|
558
|
-
// Resolve the popup script path
|
|
559
|
-
const projectRootForPopup = __dirname.includes("/dist")
|
|
560
|
-
? path.resolve(__dirname, "..") // If in dist/, go up one level
|
|
561
|
-
: path.resolve(__dirname, ".."); // If in src/, go up one level
|
|
562
|
-
const popupScriptPath = path.join(projectRootForPopup, "dist", "popups", "hooksPopup.js");
|
|
563
|
-
// Get hooks data
|
|
564
|
-
const { hasHook } = await import("./utils/hooks.js");
|
|
565
|
-
const allHookTypes = [
|
|
566
|
-
"before_pane_create",
|
|
567
|
-
"pane_created",
|
|
568
|
-
"worktree_created",
|
|
569
|
-
"before_pane_close",
|
|
570
|
-
"pane_closed",
|
|
571
|
-
"before_worktree_remove",
|
|
572
|
-
"worktree_removed",
|
|
573
|
-
"pre_merge",
|
|
574
|
-
"post_merge",
|
|
575
|
-
"run_test",
|
|
576
|
-
"run_dev",
|
|
577
|
-
];
|
|
578
|
-
const hooks = allHookTypes.map((hookName) => ({
|
|
579
|
-
name: hookName,
|
|
580
|
-
active: hasHook(projectRoot || process.cwd(), hookName),
|
|
581
|
-
}));
|
|
582
|
-
const hooksJson = JSON.stringify(hooks);
|
|
583
|
-
// Launch the popup non-blocking and track it
|
|
584
|
-
const popupHandle = launchNodePopupNonBlocking(popupScriptPath, [hooksJson], {
|
|
585
|
-
...POPUP_POSITIONING.standard(SIDEBAR_WIDTH),
|
|
586
|
-
width: 70,
|
|
587
|
-
height: 24,
|
|
588
|
-
title: "🪝 Manage Hooks",
|
|
589
|
-
});
|
|
590
|
-
// Wait for the popup to close
|
|
591
|
-
const result = await popupHandle.resultPromise;
|
|
592
|
-
// Clear active popup tracking
|
|
593
|
-
if (result.success && result.data?.action === "edit") {
|
|
594
|
-
// Edit hooks using an agent
|
|
595
|
-
const prompt = "I would like to edit my dmux hooks in .dmux-hooks, please read the instructions in there and ask me what I want to edit";
|
|
596
|
-
// Choose agent
|
|
597
|
-
const agents = availableAgents;
|
|
598
|
-
if (agents.length === 0) {
|
|
599
|
-
await createNewPaneHook(prompt);
|
|
324
|
+
// Show agent choice popup
|
|
325
|
+
const selectedAgent = await popupManager.launchAgentChoicePopup();
|
|
326
|
+
if (selectedAgent) {
|
|
327
|
+
await createNewPaneHook(prompt, selectedAgent);
|
|
600
328
|
}
|
|
601
|
-
else if (agents.length === 1) {
|
|
602
|
-
await createNewPaneHook(prompt, agents[0]);
|
|
603
|
-
}
|
|
604
|
-
else {
|
|
605
|
-
// Multiple agents available - check for default agent setting first
|
|
606
|
-
const settings = settingsManager.getSettings();
|
|
607
|
-
if (settings.defaultAgent && agents.includes(settings.defaultAgent)) {
|
|
608
|
-
// Use the default agent from settings
|
|
609
|
-
await createNewPaneHook(prompt, settings.defaultAgent);
|
|
610
|
-
}
|
|
611
|
-
else {
|
|
612
|
-
// No default agent configured or default not available - show agent choice popup
|
|
613
|
-
const selectedAgent = await launchAgentChoicePopup(prompt);
|
|
614
|
-
if (selectedAgent) {
|
|
615
|
-
await createNewPaneHook(prompt, selectedAgent);
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
else if (result.success && result.data?.action === "view") {
|
|
621
|
-
// View hooks file in editor - could implement this later
|
|
622
|
-
setStatusMessage("View in editor not yet implemented");
|
|
623
|
-
setTimeout(() => setStatusMessage(""), 2000);
|
|
624
|
-
}
|
|
625
|
-
else if (result.cancelled) {
|
|
626
|
-
// User pressed ESC - do nothing
|
|
627
|
-
return;
|
|
628
|
-
}
|
|
629
|
-
else if (result.error) {
|
|
630
|
-
setStatusMessage(`Popup error: ${result.error}`);
|
|
631
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
catch (error) {
|
|
635
|
-
setStatusMessage(`Failed to launch popup: ${error.message}`);
|
|
636
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
637
|
-
}
|
|
638
|
-
};
|
|
639
|
-
const launchLogsPopup = async () => {
|
|
640
|
-
// Only launch popup if tmux supports it
|
|
641
|
-
if (!popupsSupported) {
|
|
642
|
-
setStatusMessage("Popups require tmux 3.2+");
|
|
643
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
644
|
-
return;
|
|
645
|
-
}
|
|
646
|
-
try {
|
|
647
|
-
// Resolve the popup script path
|
|
648
|
-
const projectRootForPopup = __dirname.includes("/dist")
|
|
649
|
-
? path.resolve(__dirname, "..") // If in dist/, go up one level
|
|
650
|
-
: path.resolve(__dirname, ".."); // If in src/, go up one level
|
|
651
|
-
const popupScriptPath = path.join(projectRootForPopup, "dist", "popups", "logsPopup.js");
|
|
652
|
-
// Get logs from StateManager and write to temp file
|
|
653
|
-
const stateManager = StateManager.getInstance();
|
|
654
|
-
const allLogs = stateManager.getLogs();
|
|
655
|
-
const stats = stateManager.getLogStats();
|
|
656
|
-
const logsData = { logs: allLogs, stats };
|
|
657
|
-
// Write data to temp file to avoid shell escaping issues with complex JSON
|
|
658
|
-
const dataFile = `/tmp/dmux-logs-${Date.now()}.json`;
|
|
659
|
-
const dataJson = JSON.stringify(logsData);
|
|
660
|
-
await fs.writeFile(dataFile, dataJson);
|
|
661
|
-
// Launch the popup with large positioning
|
|
662
|
-
// Get tmux client dimensions (not process.stdout which is just the sidebar)
|
|
663
|
-
const tmuxDims = execSync('tmux display-message -p "#{client_width},#{client_height}"', { encoding: "utf-8" }).trim();
|
|
664
|
-
const [termWidth, termHeight] = tmuxDims.split(",").map(Number);
|
|
665
|
-
// Launch the popup non-blocking and track it
|
|
666
|
-
const popupHandle = launchNodePopupNonBlocking(popupScriptPath, [dataFile], {
|
|
667
|
-
...POPUP_POSITIONING.large(SIDEBAR_WIDTH, termWidth, termHeight),
|
|
668
|
-
title: "🪵 dmux Logs",
|
|
669
|
-
});
|
|
670
|
-
// Wait for the popup to close
|
|
671
|
-
const result = await popupHandle.resultPromise;
|
|
672
|
-
// Clear active popup tracking
|
|
673
|
-
// Clean up temp file
|
|
674
|
-
try {
|
|
675
|
-
await fs.unlink(dataFile);
|
|
676
|
-
}
|
|
677
|
-
catch (err) {
|
|
678
|
-
// Ignore cleanup errors
|
|
679
|
-
}
|
|
680
|
-
// Popup closed - mark all logs as read
|
|
681
|
-
if (result.success) {
|
|
682
|
-
stateManager.markAllLogsAsRead();
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
catch (error) {
|
|
686
|
-
setStatusMessage(`Failed to launch popup: ${error.message}`);
|
|
687
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
688
|
-
}
|
|
689
|
-
};
|
|
690
|
-
const launchShortcutsPopup = async () => {
|
|
691
|
-
// Only launch popup if tmux supports it
|
|
692
|
-
if (!popupsSupported) {
|
|
693
|
-
setStatusMessage("Popups require tmux 3.2+");
|
|
694
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
695
|
-
return;
|
|
696
|
-
}
|
|
697
|
-
try {
|
|
698
|
-
// Resolve the popup script path
|
|
699
|
-
const projectRootForPopup = __dirname.includes("/dist")
|
|
700
|
-
? path.resolve(__dirname, "..") // If in dist/, go up one level
|
|
701
|
-
: path.resolve(__dirname, ".."); // If in src/, go up one level
|
|
702
|
-
const popupScriptPath = path.join(projectRootForPopup, "dist", "popups", "shortcutsPopup.js");
|
|
703
|
-
// Prepare data for shortcuts popup
|
|
704
|
-
const shortcutsData = {
|
|
705
|
-
hasSidebarLayout: !!controlPaneId,
|
|
706
|
-
showRemoteKey: !!server,
|
|
707
|
-
};
|
|
708
|
-
// Write data to temp file
|
|
709
|
-
const dataFile = `/tmp/dmux-shortcuts-${Date.now()}.json`;
|
|
710
|
-
const dataJson = JSON.stringify(shortcutsData);
|
|
711
|
-
await fs.writeFile(dataFile, dataJson);
|
|
712
|
-
// Launch the popup non-blocking and track it
|
|
713
|
-
const popupHandle = launchNodePopupNonBlocking(popupScriptPath, [dataFile], {
|
|
714
|
-
...POPUP_POSITIONING.standard(SIDEBAR_WIDTH),
|
|
715
|
-
width: 50,
|
|
716
|
-
height: 20,
|
|
717
|
-
title: "⌨️ Keyboard Shortcuts",
|
|
718
|
-
});
|
|
719
|
-
// Wait for the popup to close
|
|
720
|
-
const result = await popupHandle.resultPromise;
|
|
721
|
-
// Clear active popup tracking
|
|
722
|
-
// Clean up temp file
|
|
723
|
-
try {
|
|
724
|
-
await fs.unlink(dataFile);
|
|
725
|
-
}
|
|
726
|
-
catch (err) {
|
|
727
|
-
// Ignore cleanup errors
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
|
-
catch (error) {
|
|
731
|
-
setStatusMessage(`Failed to launch popup: ${error.message}`);
|
|
732
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
733
|
-
}
|
|
734
|
-
};
|
|
735
|
-
const launchRemotePopup = async () => {
|
|
736
|
-
// Only launch popup if tmux supports it
|
|
737
|
-
if (!popupsSupported) {
|
|
738
|
-
setStatusMessage("Popups require tmux 3.2+");
|
|
739
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
740
|
-
return;
|
|
741
|
-
}
|
|
742
|
-
// Check if server is available and tunnel URL exists
|
|
743
|
-
if (!server || !serverPort || !tunnelUrl) {
|
|
744
|
-
setStatusMessage("Tunnel not ready");
|
|
745
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
746
|
-
return;
|
|
747
|
-
}
|
|
748
|
-
try {
|
|
749
|
-
// Resolve the popup script path
|
|
750
|
-
const projectRootForPopup = __dirname.includes("/dist")
|
|
751
|
-
? path.resolve(__dirname, "..") // If in dist/, go up one level
|
|
752
|
-
: path.resolve(__dirname, ".."); // If in src/, go up one level
|
|
753
|
-
const popupScriptPath = path.join(projectRootForPopup, "dist", "popups", "remotePopup.js");
|
|
754
|
-
// Prepare status file with existing tunnel URL
|
|
755
|
-
const tunnelStatusFile = `/tmp/dmux-tunnel-status-${Date.now()}.json`;
|
|
756
|
-
await fs.writeFile(tunnelStatusFile, JSON.stringify({ url: tunnelUrl }));
|
|
757
|
-
// Prepare data for remote popup
|
|
758
|
-
const remoteData = {
|
|
759
|
-
loading: false,
|
|
760
|
-
serverPort: serverPort,
|
|
761
|
-
statusFile: tunnelStatusFile,
|
|
762
|
-
};
|
|
763
|
-
// Write data to temp file
|
|
764
|
-
const dataFile = `/tmp/dmux-remote-${Date.now()}.json`;
|
|
765
|
-
await fs.writeFile(dataFile, JSON.stringify(remoteData));
|
|
766
|
-
// Launch the popup non-blocking and track it
|
|
767
|
-
const popupHandle = launchNodePopupNonBlocking(popupScriptPath, [dataFile], {
|
|
768
|
-
...POPUP_POSITIONING.centeredWithSidebar(SIDEBAR_WIDTH),
|
|
769
|
-
width: 60,
|
|
770
|
-
height: 30,
|
|
771
|
-
title: "🌐 Remote Access",
|
|
772
|
-
});
|
|
773
|
-
// Wait for the popup to close
|
|
774
|
-
const result = await popupHandle.resultPromise;
|
|
775
|
-
// Clear active popup tracking
|
|
776
|
-
// Show "Copied!" message if URL was copied
|
|
777
|
-
// Note: result is the parsed JSON directly, not wrapped in PopupResult.data
|
|
778
|
-
if (result && result.copied) {
|
|
779
|
-
setTunnelCopied(true);
|
|
780
|
-
setTimeout(() => setTunnelCopied(false), 2000);
|
|
781
|
-
}
|
|
782
|
-
// Clean up temp files
|
|
783
|
-
try {
|
|
784
|
-
await fs.unlink(dataFile);
|
|
785
329
|
}
|
|
786
|
-
catch (err) {
|
|
787
|
-
// Ignore cleanup errors
|
|
788
|
-
}
|
|
789
|
-
try {
|
|
790
|
-
await fs.unlink(tunnelStatusFile);
|
|
791
|
-
}
|
|
792
|
-
catch (err) {
|
|
793
|
-
// Ignore cleanup errors (file might not exist yet)
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
catch (error) {
|
|
797
|
-
setStatusMessage(`Failed to launch popup: ${error.message}`);
|
|
798
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
799
330
|
}
|
|
800
331
|
};
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
332
|
+
// Helper function to handle action results recursively
|
|
333
|
+
const handleActionResult = async (result) => {
|
|
334
|
+
// Handle ActionResults from background callbacks (e.g., conflict resolution completion)
|
|
335
|
+
// This allows showing dialogs even when not in the normal action flow
|
|
336
|
+
if (!popupsSupported)
|
|
806
337
|
return;
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
const settingsData = {
|
|
816
|
-
settingDefinitions: SETTING_DEFINITIONS,
|
|
817
|
-
settings: settingsManager.getSettings(),
|
|
818
|
-
globalSettings: settingsManager.getGlobalSettings(),
|
|
819
|
-
projectSettings: settingsManager.getProjectSettings(),
|
|
820
|
-
};
|
|
821
|
-
const settingsJson = JSON.stringify(settingsData);
|
|
822
|
-
// Launch the popup non-blocking and track it
|
|
823
|
-
const popupHandle = launchNodePopupNonBlocking(popupScriptPath, [settingsJson], {
|
|
824
|
-
...POPUP_POSITIONING.standard(SIDEBAR_WIDTH),
|
|
825
|
-
width: 70,
|
|
826
|
-
height: Math.min(25, SETTING_DEFINITIONS.length + 8),
|
|
827
|
-
title: "⚙️ Settings",
|
|
828
|
-
});
|
|
829
|
-
// Wait for the popup to close
|
|
830
|
-
const result = await popupHandle.resultPromise;
|
|
831
|
-
// Clear active popup tracking
|
|
832
|
-
if (result.success) {
|
|
833
|
-
// Check if this is an action result (action field at top level)
|
|
834
|
-
if (result.action) {
|
|
835
|
-
// Action type setting (like 'hooks')
|
|
836
|
-
if (result.action === "hooks") {
|
|
837
|
-
// Launch hooks popup
|
|
838
|
-
await launchHooksPopup();
|
|
839
|
-
}
|
|
338
|
+
// Handle the result type and show appropriate dialog
|
|
339
|
+
if (result.type === "confirm") {
|
|
340
|
+
const confirmed = await popupManager.launchConfirmPopup(result.title || "Confirm", result.message, result.confirmLabel, result.cancelLabel);
|
|
341
|
+
if (confirmed && result.onConfirm) {
|
|
342
|
+
const nextResult = await result.onConfirm();
|
|
343
|
+
// Recursively handle nested results
|
|
344
|
+
if (nextResult) {
|
|
345
|
+
await handleActionResult(nextResult);
|
|
840
346
|
}
|
|
841
|
-
else if (result.data) {
|
|
842
|
-
// Regular setting change (result.data contains the setting)
|
|
843
|
-
const { key, value, scope } = result.data;
|
|
844
|
-
settingsManager.updateSetting(key, value, scope);
|
|
845
|
-
setStatusMessage(`Setting saved (${scope})`);
|
|
846
|
-
setTimeout(() => setStatusMessage(""), 2000);
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
else if (result.cancelled) {
|
|
850
|
-
// User pressed ESC - do nothing
|
|
851
|
-
return;
|
|
852
347
|
}
|
|
853
|
-
else if (result.
|
|
854
|
-
|
|
855
|
-
|
|
348
|
+
else if (!confirmed && result.onCancel) {
|
|
349
|
+
const nextResult = await result.onCancel();
|
|
350
|
+
if (nextResult) {
|
|
351
|
+
await handleActionResult(nextResult);
|
|
352
|
+
}
|
|
856
353
|
}
|
|
857
354
|
}
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
861
|
-
}
|
|
862
|
-
};
|
|
863
|
-
const launchMergePopup = async (pane) => {
|
|
864
|
-
LogService.getInstance().debug(`launchMergePopup called for pane: ${pane.slug}`, "MergePopup");
|
|
865
|
-
// Only launch popup if tmux supports it
|
|
866
|
-
if (!popupsSupported) {
|
|
867
|
-
LogService.getInstance().warn("Popups not supported", "MergePopup");
|
|
868
|
-
setStatusMessage("Popups require tmux 3.2+");
|
|
869
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
870
|
-
return;
|
|
871
|
-
}
|
|
872
|
-
if (!pane.worktreePath) {
|
|
873
|
-
LogService.getInstance().warn("No worktree path for pane", "MergePopup", pane.id);
|
|
874
|
-
setStatusMessage("This pane has no worktree to merge");
|
|
875
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
876
|
-
return;
|
|
877
|
-
}
|
|
878
|
-
try {
|
|
879
|
-
// Resolve the popup script path
|
|
880
|
-
const projectRootForPopup = __dirname.includes("/dist")
|
|
881
|
-
? path.resolve(__dirname, "..") // If in dist/, go up one level
|
|
882
|
-
: path.resolve(__dirname, ".."); // If in src/, go up one level
|
|
883
|
-
const popupScriptPath = path.join(projectRootForPopup, "dist", "popups", "mergePopup.js");
|
|
884
|
-
LogService.getInstance().debug(`Popup script path: ${popupScriptPath}`, "MergePopup");
|
|
885
|
-
// Check if popup script exists
|
|
886
|
-
try {
|
|
887
|
-
await fs.access(popupScriptPath);
|
|
888
|
-
LogService.getInstance().debug("Popup script exists", "MergePopup");
|
|
889
|
-
}
|
|
890
|
-
catch {
|
|
891
|
-
LogService.getInstance().error(`Popup script NOT found at: ${popupScriptPath}`, "MergePopup");
|
|
892
|
-
setStatusMessage(`Merge popup script not found: ${popupScriptPath}`);
|
|
893
|
-
setTimeout(() => setStatusMessage(""), 5000);
|
|
355
|
+
else if (result.type === "choice") {
|
|
356
|
+
if (!result.options || !result.onSelect)
|
|
894
357
|
return;
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
mainRepoPath,
|
|
902
|
-
mainBranch: getMainBranch(),
|
|
903
|
-
};
|
|
904
|
-
// Write data to temp file
|
|
905
|
-
const dataFile = `/tmp/dmux-merge-${Date.now()}.json`;
|
|
906
|
-
await fs.writeFile(dataFile, JSON.stringify(mergeData));
|
|
907
|
-
LogService.getInstance().debug(`Merge data written to: ${dataFile}`, "MergePopup");
|
|
908
|
-
LogService.getInstance().debug(`Merge data: ${JSON.stringify(mergeData)}`, "MergePopup");
|
|
909
|
-
// Launch the popup non-blocking and track it
|
|
910
|
-
LogService.getInstance().debug("Launching merge popup...", "MergePopup");
|
|
911
|
-
const popupHandle = launchNodePopupNonBlocking(popupScriptPath, [dataFile], {
|
|
912
|
-
...POPUP_POSITIONING.large(SIDEBAR_WIDTH, terminalWidth, terminalHeight),
|
|
913
|
-
width: 80,
|
|
914
|
-
height: 30,
|
|
915
|
-
title: `🔀 Merge: ${pane.slug}`,
|
|
916
|
-
});
|
|
917
|
-
LogService.getInstance().debug(`Popup launched, PID: ${popupHandle.pid}`, "MergePopup");
|
|
918
|
-
// Wait for the popup to close
|
|
919
|
-
const result = await popupHandle.resultPromise;
|
|
920
|
-
// Clear active popup tracking
|
|
921
|
-
// Clean up temp file
|
|
922
|
-
try {
|
|
923
|
-
await fs.unlink(dataFile);
|
|
924
|
-
}
|
|
925
|
-
catch { }
|
|
926
|
-
if (result.success && result.data?.merged) {
|
|
927
|
-
if (result.data.closedPane) {
|
|
928
|
-
// Pane was closed during merge, refresh pane list
|
|
929
|
-
await loadPanes();
|
|
930
|
-
setStatusMessage("Merge complete, pane closed");
|
|
358
|
+
const selectedId = await popupManager.launchChoicePopup(result.title || "Choose Option", result.message, result.options);
|
|
359
|
+
if (selectedId) {
|
|
360
|
+
const nextResult = await result.onSelect(selectedId);
|
|
361
|
+
// Recursively handle nested results
|
|
362
|
+
if (nextResult) {
|
|
363
|
+
await handleActionResult(nextResult);
|
|
931
364
|
}
|
|
932
|
-
else {
|
|
933
|
-
setStatusMessage("Merge complete");
|
|
934
|
-
}
|
|
935
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
936
|
-
}
|
|
937
|
-
else if (result.cancelled) {
|
|
938
|
-
// User cancelled
|
|
939
|
-
return;
|
|
940
365
|
}
|
|
941
|
-
else if (result.error) {
|
|
942
|
-
setStatusMessage(`Merge error: ${result.error}`);
|
|
943
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
|
-
catch (error) {
|
|
947
|
-
setStatusMessage(`Failed to launch merge popup: ${error.message}`);
|
|
948
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
949
366
|
}
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
const projectRootForPopup = __dirname.includes("/dist")
|
|
961
|
-
? path.resolve(__dirname, "..") // If in dist/, go up one level
|
|
962
|
-
: path.resolve(__dirname, ".."); // If in src/, go up one level
|
|
963
|
-
const popupScriptPath = path.join(projectRootForPopup, "dist", "popups", "choicePopup.js");
|
|
964
|
-
// Write data to temp file to avoid shell escaping issues
|
|
965
|
-
const dataFile = `/tmp/dmux-choice-${Date.now()}.json`;
|
|
966
|
-
const dataJson = JSON.stringify({ title, message, options });
|
|
967
|
-
await fs.writeFile(dataFile, dataJson);
|
|
968
|
-
// Launch the popup non-blocking and track it
|
|
969
|
-
const popupHandle = launchNodePopupNonBlocking(popupScriptPath, [dataFile], {
|
|
970
|
-
...POPUP_POSITIONING.standard(SIDEBAR_WIDTH),
|
|
971
|
-
width: 70,
|
|
972
|
-
height: Math.min(25, options.length * 3 + 8),
|
|
973
|
-
title: title || "Choose Option",
|
|
974
|
-
});
|
|
975
|
-
// Wait for the popup to close
|
|
976
|
-
const result = await popupHandle.resultPromise;
|
|
977
|
-
// Clear active popup tracking
|
|
978
|
-
// Clean up temp file
|
|
979
|
-
try {
|
|
980
|
-
await fs.unlink(dataFile);
|
|
981
|
-
}
|
|
982
|
-
catch { }
|
|
983
|
-
if (result.success && result.data) {
|
|
984
|
-
return result.data;
|
|
985
|
-
}
|
|
986
|
-
else if (result.cancelled) {
|
|
987
|
-
return null;
|
|
988
|
-
}
|
|
989
|
-
else if (result.error) {
|
|
990
|
-
setStatusMessage(`Popup error: ${result.error}`);
|
|
991
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
992
|
-
return null;
|
|
367
|
+
else if (result.type === "input") {
|
|
368
|
+
if (!result.onSubmit)
|
|
369
|
+
return;
|
|
370
|
+
const inputValue = await popupManager.launchInputPopup(result.title || "Input", result.message, result.placeholder, result.defaultValue);
|
|
371
|
+
if (inputValue !== null) {
|
|
372
|
+
const nextResult = await result.onSubmit(inputValue);
|
|
373
|
+
// Recursively handle nested results
|
|
374
|
+
if (nextResult) {
|
|
375
|
+
await handleActionResult(nextResult);
|
|
376
|
+
}
|
|
993
377
|
}
|
|
994
378
|
}
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
}
|
|
1008
|
-
try {
|
|
1009
|
-
// Resolve the popup script path
|
|
1010
|
-
const projectRootForPopup = __dirname.includes("/dist")
|
|
1011
|
-
? path.resolve(__dirname, "..") // If in dist/, go up one level
|
|
1012
|
-
: path.resolve(__dirname, ".."); // If in src/, go up one level
|
|
1013
|
-
const popupScriptPath = path.join(projectRootForPopup, "dist", "popups", "inputPopup.js");
|
|
1014
|
-
// Write data to temp file to avoid shell escaping issues
|
|
1015
|
-
const dataFile = `/tmp/dmux-input-${Date.now()}.json`;
|
|
1016
|
-
const dataJson = JSON.stringify({
|
|
1017
|
-
title,
|
|
1018
|
-
message,
|
|
1019
|
-
placeholder,
|
|
1020
|
-
defaultValue,
|
|
1021
|
-
});
|
|
1022
|
-
await fs.writeFile(dataFile, dataJson);
|
|
1023
|
-
// Launch the popup non-blocking and track it
|
|
1024
|
-
const popupHandle = launchNodePopupNonBlocking(popupScriptPath, [dataFile], {
|
|
1025
|
-
...POPUP_POSITIONING.standard(SIDEBAR_WIDTH),
|
|
1026
|
-
width: 70,
|
|
1027
|
-
height: 15,
|
|
1028
|
-
title: title || "Input",
|
|
1029
|
-
});
|
|
1030
|
-
// Wait for the popup to close
|
|
1031
|
-
const result = await popupHandle.resultPromise;
|
|
1032
|
-
// Clear active popup tracking
|
|
1033
|
-
// Clean up temp file
|
|
1034
|
-
try {
|
|
1035
|
-
await fs.unlink(dataFile);
|
|
1036
|
-
}
|
|
1037
|
-
catch { }
|
|
1038
|
-
if (result.success && result.data !== undefined) {
|
|
1039
|
-
return result.data;
|
|
1040
|
-
}
|
|
1041
|
-
else if (result.cancelled) {
|
|
1042
|
-
return null;
|
|
1043
|
-
}
|
|
1044
|
-
else if (result.error) {
|
|
1045
|
-
setStatusMessage(`Popup error: ${result.error}`);
|
|
1046
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
1047
|
-
return null;
|
|
379
|
+
else if (result.type === "navigation") {
|
|
380
|
+
// Navigate to target pane if specified
|
|
381
|
+
if (result.targetPaneId) {
|
|
382
|
+
const targetPane = panes.find(p => p.id === result.targetPaneId);
|
|
383
|
+
if (targetPane) {
|
|
384
|
+
try {
|
|
385
|
+
TmuxService.getInstance().selectPane(targetPane.paneId);
|
|
386
|
+
}
|
|
387
|
+
catch (error) {
|
|
388
|
+
console.error('[onActionResult] Failed to navigate to pane:', error);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
1048
391
|
}
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
1053
|
-
}
|
|
1054
|
-
return null;
|
|
1055
|
-
};
|
|
1056
|
-
const launchProgressPopup = async (message, type = "info", timeout = 2000) => {
|
|
1057
|
-
// Only launch popup if tmux supports it
|
|
1058
|
-
if (!popupsSupported) {
|
|
1059
|
-
setStatusMessage(message);
|
|
1060
|
-
setTimeout(() => setStatusMessage(""), timeout);
|
|
1061
|
-
return;
|
|
1062
|
-
}
|
|
1063
|
-
try {
|
|
1064
|
-
// Resolve the popup script path
|
|
1065
|
-
const projectRootForPopup = __dirname.includes("/dist")
|
|
1066
|
-
? path.resolve(__dirname, "..") // If in dist/, go up one level
|
|
1067
|
-
: path.resolve(__dirname, ".."); // If in src/, go up one level
|
|
1068
|
-
const popupScriptPath = path.join(projectRootForPopup, "dist", "popups", "progressPopup.js");
|
|
1069
|
-
// Write data to temp file to avoid shell escaping issues
|
|
1070
|
-
const dataFile = `/tmp/dmux-progress-${Date.now()}.json`;
|
|
1071
|
-
const dataJson = JSON.stringify({ message, type, timeout });
|
|
1072
|
-
await fs.writeFile(dataFile, dataJson);
|
|
1073
|
-
// Launch the popup - position at top, 1 char right of sidebar
|
|
1074
|
-
// Height depends on message length
|
|
1075
|
-
const lines = Math.ceil(message.length / 60) + 3; // Estimate lines needed
|
|
1076
|
-
const titleText = type === "success"
|
|
1077
|
-
? "✓ Success"
|
|
1078
|
-
: type === "error"
|
|
1079
|
-
? "✗ Error"
|
|
1080
|
-
: type === "info"
|
|
1081
|
-
? "ℹ Info"
|
|
1082
|
-
: "Progress";
|
|
1083
|
-
// Launch the popup non-blocking and track it
|
|
1084
|
-
const popupHandle = launchNodePopupNonBlocking(popupScriptPath, [dataFile], {
|
|
1085
|
-
...POPUP_POSITIONING.standard(SIDEBAR_WIDTH),
|
|
1086
|
-
width: 70,
|
|
1087
|
-
height: Math.min(15, lines + 4),
|
|
1088
|
-
title: titleText,
|
|
1089
|
-
});
|
|
1090
|
-
// Wait for the popup to close
|
|
1091
|
-
await popupHandle.resultPromise;
|
|
1092
|
-
// Clear active popup tracking
|
|
1093
|
-
// Clean up temp file
|
|
1094
|
-
try {
|
|
1095
|
-
await fs.unlink(dataFile);
|
|
392
|
+
// Show message if dismissable
|
|
393
|
+
if (result.message && result.dismissable) {
|
|
394
|
+
await popupManager.launchProgressPopup(result.message, "info", 3000);
|
|
1096
395
|
}
|
|
1097
|
-
catch { }
|
|
1098
396
|
}
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
397
|
+
else if (result.type === "info" ||
|
|
398
|
+
result.type === "success" ||
|
|
399
|
+
result.type === "error") {
|
|
400
|
+
await popupManager.launchProgressPopup(result.message, result.type, 3000);
|
|
1103
401
|
}
|
|
1104
402
|
};
|
|
1105
|
-
// Action system - initialized after
|
|
403
|
+
// Action system - initialized after services are defined
|
|
1106
404
|
const actionSystem = useActionSystem({
|
|
1107
405
|
panes,
|
|
1108
406
|
savePanes,
|
|
1109
407
|
sessionName,
|
|
1110
408
|
projectName,
|
|
1111
|
-
onPaneRemove: (paneId) => {
|
|
1112
|
-
// Mark
|
|
1113
|
-
|
|
409
|
+
onPaneRemove: async (paneId) => {
|
|
410
|
+
// Mark pane as closing to prevent race condition with worker
|
|
411
|
+
await lifecycleManager.beginClose(paneId, 'user requested');
|
|
1114
412
|
// Remove from panes list
|
|
1115
413
|
const updatedPanes = panes.filter((p) => p.paneId !== paneId);
|
|
1116
414
|
savePanes(updatedPanes);
|
|
1117
|
-
//
|
|
1118
|
-
|
|
1119
|
-
intentionallyClosedPanes.current.delete(paneId);
|
|
1120
|
-
}, 5000);
|
|
415
|
+
// Mark close as completed (no more lock needed)
|
|
416
|
+
await lifecycleManager.completeClose(paneId);
|
|
1121
417
|
},
|
|
418
|
+
onActionResult: handleActionResult,
|
|
1122
419
|
forceRepaint,
|
|
1123
420
|
popupLaunchers: popupsSupported
|
|
1124
421
|
? {
|
|
1125
|
-
launchConfirmPopup,
|
|
1126
|
-
launchChoicePopup,
|
|
1127
|
-
launchInputPopup,
|
|
1128
|
-
launchProgressPopup,
|
|
422
|
+
launchConfirmPopup: popupManager.launchConfirmPopup.bind(popupManager),
|
|
423
|
+
launchChoicePopup: popupManager.launchChoicePopup.bind(popupManager),
|
|
424
|
+
launchInputPopup: popupManager.launchInputPopup.bind(popupManager),
|
|
425
|
+
launchProgressPopup: popupManager.launchProgressPopup.bind(popupManager),
|
|
1129
426
|
}
|
|
1130
427
|
: undefined,
|
|
1131
428
|
});
|
|
1132
429
|
// Auto-show new pane dialog removed - users can press 'n' to create panes via popup
|
|
1133
430
|
// Periodic enforcement of control pane size and content pane rebalancing (left sidebar at 40 chars)
|
|
1134
|
-
|
|
1135
|
-
if (!controlPaneId) {
|
|
1136
|
-
return; // No sidebar layout configured
|
|
1137
|
-
}
|
|
1138
|
-
// Enforce sidebar width immediately on mount
|
|
1139
|
-
enforceControlPaneSize(controlPaneId, SIDEBAR_WIDTH);
|
|
1140
|
-
// Debounce resize handler to prevent infinite loops
|
|
1141
|
-
let resizeTimeout = null;
|
|
1142
|
-
let isApplyingLayout = false;
|
|
1143
|
-
const handleResize = () => {
|
|
1144
|
-
// Skip if we're already applying a layout (prevents loops)
|
|
1145
|
-
if (isApplyingLayout) {
|
|
1146
|
-
return;
|
|
1147
|
-
}
|
|
1148
|
-
// Clear any pending resize
|
|
1149
|
-
if (resizeTimeout) {
|
|
1150
|
-
clearTimeout(resizeTimeout);
|
|
1151
|
-
}
|
|
1152
|
-
// Debounce: wait 500ms after last resize event (prevents excessive recalculations)
|
|
1153
|
-
resizeTimeout = setTimeout(() => {
|
|
1154
|
-
// Only enforce if not showing dialogs (to avoid interference)
|
|
1155
|
-
const hasActiveDialog = actionSystem.actionState.showConfirmDialog ||
|
|
1156
|
-
actionSystem.actionState.showChoiceDialog ||
|
|
1157
|
-
actionSystem.actionState.showInputDialog ||
|
|
1158
|
-
actionSystem.actionState.showProgressDialog ||
|
|
1159
|
-
!!showCommandPrompt ||
|
|
1160
|
-
showFileCopyPrompt ||
|
|
1161
|
-
isCreatingPane ||
|
|
1162
|
-
runningCommand ||
|
|
1163
|
-
isUpdating;
|
|
1164
|
-
if (!hasActiveDialog) {
|
|
1165
|
-
isApplyingLayout = true;
|
|
1166
|
-
// Only enforce sidebar width when terminal resizes
|
|
1167
|
-
enforceControlPaneSize(controlPaneId, SIDEBAR_WIDTH);
|
|
1168
|
-
// Force Ink to repaint after resize to prevent blank dmux pane
|
|
1169
|
-
forceRepaint();
|
|
1170
|
-
// Reset flag after a brief delay
|
|
1171
|
-
setTimeout(() => {
|
|
1172
|
-
isApplyingLayout = false;
|
|
1173
|
-
}, 100);
|
|
1174
|
-
}
|
|
1175
|
-
}, 500);
|
|
1176
|
-
};
|
|
1177
|
-
// Listen to stdout resize events
|
|
1178
|
-
process.stdout.on("resize", handleResize);
|
|
1179
|
-
// Also listen for SIGWINCH and SIGUSR1 (tmux hook sends USR1)
|
|
1180
|
-
process.on("SIGWINCH", handleResize);
|
|
1181
|
-
process.on("SIGUSR1", handleResize);
|
|
1182
|
-
return () => {
|
|
1183
|
-
process.stdout.off("resize", handleResize);
|
|
1184
|
-
process.off("SIGWINCH", handleResize);
|
|
1185
|
-
process.off("SIGUSR1", handleResize);
|
|
1186
|
-
if (resizeTimeout) {
|
|
1187
|
-
clearTimeout(resizeTimeout);
|
|
1188
|
-
}
|
|
1189
|
-
};
|
|
1190
|
-
}, [
|
|
431
|
+
useLayoutManagement({
|
|
1191
432
|
controlPaneId,
|
|
1192
|
-
actionSystem.actionState.showConfirmDialog
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
433
|
+
hasActiveDialog: actionSystem.actionState.showConfirmDialog ||
|
|
434
|
+
actionSystem.actionState.showChoiceDialog ||
|
|
435
|
+
actionSystem.actionState.showInputDialog ||
|
|
436
|
+
actionSystem.actionState.showProgressDialog ||
|
|
437
|
+
!!showCommandPrompt ||
|
|
438
|
+
showFileCopyPrompt ||
|
|
439
|
+
isCreatingPane ||
|
|
440
|
+
runningCommand ||
|
|
441
|
+
isUpdating,
|
|
442
|
+
onForceRepaint: forceRepaint,
|
|
443
|
+
});
|
|
1202
444
|
// Monitor agent status across panes (returns a map of pane ID to status)
|
|
1203
445
|
const agentStatuses = useAgentStatus({
|
|
1204
446
|
panes,
|
|
@@ -1209,9 +451,9 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
1209
451
|
!!showCommandPrompt ||
|
|
1210
452
|
showFileCopyPrompt,
|
|
1211
453
|
onPaneRemoved: (paneId) => {
|
|
1212
|
-
// Check if this pane
|
|
454
|
+
// Check if this pane is being closed intentionally or is locked
|
|
1213
455
|
// If so, don't re-save - the close action already handled it
|
|
1214
|
-
if (
|
|
456
|
+
if (lifecycleManager.isClosing(paneId) || lifecycleManager.isLocked(paneId)) {
|
|
1215
457
|
return;
|
|
1216
458
|
}
|
|
1217
459
|
// Pane was removed unexpectedly (e.g., user killed tmux pane manually)
|
|
@@ -1220,415 +462,9 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
1220
462
|
savePanes(updatedPanes);
|
|
1221
463
|
},
|
|
1222
464
|
});
|
|
1223
|
-
|
|
1224
|
-
setIsCreatingPane(true);
|
|
1225
|
-
setStatusMessage("Generating slug...");
|
|
1226
|
-
const slug = await generateSlug(prompt);
|
|
1227
|
-
setStatusMessage(`Creating worktree: ${slug}...`);
|
|
1228
|
-
// Get git root directory for consistent worktree placement
|
|
1229
|
-
let projectRoot;
|
|
1230
|
-
try {
|
|
1231
|
-
projectRoot = execSync("git rev-parse --show-toplevel", {
|
|
1232
|
-
encoding: "utf-8",
|
|
1233
|
-
stdio: "pipe",
|
|
1234
|
-
}).trim();
|
|
1235
|
-
}
|
|
1236
|
-
catch {
|
|
1237
|
-
// Fallback to current directory if not in a git repo
|
|
1238
|
-
projectRoot = process.cwd();
|
|
1239
|
-
}
|
|
1240
|
-
// Create worktree path inside .dmux/worktrees directory
|
|
1241
|
-
const worktreePath = path.join(projectRoot, ".dmux", "worktrees", slug);
|
|
1242
|
-
// Get the original pane ID (where dmux is running) before clearing
|
|
1243
|
-
const originalPaneId = execSync('tmux display-message -p "#{pane_id}"', {
|
|
1244
|
-
encoding: "utf-8",
|
|
1245
|
-
}).trim();
|
|
1246
|
-
// Minimal clearing to avoid layout shifts
|
|
1247
|
-
process.stdout.write("\x1b[2J\x1b[H");
|
|
1248
|
-
// Get current pane count to determine layout
|
|
1249
|
-
const paneCount = parseInt(execSync("tmux list-panes | wc -l", { encoding: "utf-8" }).trim());
|
|
1250
|
-
// Enable pane borders to show titles
|
|
1251
|
-
try {
|
|
1252
|
-
execSync(`tmux set-option -g pane-border-status top`, { stdio: "pipe" });
|
|
1253
|
-
}
|
|
1254
|
-
catch {
|
|
1255
|
-
// Ignore if already set or fails
|
|
1256
|
-
}
|
|
1257
|
-
// Create new pane
|
|
1258
|
-
const paneInfo = execSync(`tmux split-window -h -P -F '#{pane_id}'`, {
|
|
1259
|
-
encoding: "utf-8",
|
|
1260
|
-
}).trim();
|
|
1261
|
-
// Wait for pane creation to settle
|
|
1262
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1263
|
-
// Set pane title to match the slug
|
|
1264
|
-
try {
|
|
1265
|
-
execSync(`tmux select-pane -t '${paneInfo}' -T "${slug}"`, {
|
|
1266
|
-
stdio: "pipe",
|
|
1267
|
-
});
|
|
1268
|
-
}
|
|
1269
|
-
catch {
|
|
1270
|
-
// Ignore if setting title fails
|
|
1271
|
-
}
|
|
1272
|
-
// Don't apply global layouts - let content panes arrange naturally
|
|
1273
|
-
// Only enforce sidebar width
|
|
1274
|
-
try {
|
|
1275
|
-
const controlPaneId = execSync('tmux display-message -p "#{pane_id}"', {
|
|
1276
|
-
encoding: "utf-8",
|
|
1277
|
-
}).trim();
|
|
1278
|
-
enforceControlPaneSize(controlPaneId, SIDEBAR_WIDTH);
|
|
1279
|
-
}
|
|
1280
|
-
catch { }
|
|
1281
|
-
// Create git worktree and cd into it
|
|
1282
|
-
// This MUST happen before launching Claude to ensure we're in the right directory
|
|
1283
|
-
try {
|
|
1284
|
-
// First, create the worktree and cd into it as a single command
|
|
1285
|
-
// Use ; instead of && to ensure cd runs even if worktree already exists
|
|
1286
|
-
const worktreeCmd = `git worktree add "${worktreePath}" -b ${slug} 2>/dev/null ; cd "${worktreePath}"`;
|
|
1287
|
-
execSync(`tmux send-keys -t '${paneInfo}' '${worktreeCmd}' Enter`, {
|
|
1288
|
-
stdio: "pipe",
|
|
1289
|
-
});
|
|
1290
|
-
// Wait longer for worktree creation and cd to complete
|
|
1291
|
-
// This is critical - if we don't wait long enough, Claude will start in the wrong directory
|
|
1292
|
-
await new Promise((resolve) => setTimeout(resolve, 2500));
|
|
1293
|
-
// Verify we're in the worktree directory by sending pwd command
|
|
1294
|
-
execSync(`tmux send-keys -t '${paneInfo}' 'echo "Worktree created at:" && pwd' Enter`, { stdio: "pipe" });
|
|
1295
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1296
|
-
setStatusMessage(agent
|
|
1297
|
-
? `Worktree created, launching ${agent === "opencode" ? "opencode" : "Claude"}...`
|
|
1298
|
-
: "Worktree created.");
|
|
1299
|
-
}
|
|
1300
|
-
catch (error) {
|
|
1301
|
-
// Log error but continue - worktree creation is essential
|
|
1302
|
-
setStatusMessage(`Warning: Worktree issue: ${error}`);
|
|
1303
|
-
// Even if worktree creation failed, try to cd to the directory in case it exists
|
|
1304
|
-
execSync(`tmux send-keys -t '${paneInfo}' 'cd "${worktreePath}" 2>/dev/null || (echo "ERROR: Failed to create/enter worktree ${slug}" && pwd)' Enter`, { stdio: "pipe" });
|
|
1305
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1306
|
-
}
|
|
1307
|
-
// Prepare and send the agent command
|
|
1308
|
-
let escapedCmd = "";
|
|
1309
|
-
if (agent === "claude") {
|
|
1310
|
-
// Claude should always be launched AFTER we're in the worktree directory
|
|
1311
|
-
let claudeCmd;
|
|
1312
|
-
if (prompt && prompt.trim()) {
|
|
1313
|
-
const escapedPrompt = prompt
|
|
1314
|
-
.replace(/\\/g, "\\\\")
|
|
1315
|
-
.replace(/"/g, '\\"')
|
|
1316
|
-
.replace(/`/g, "\\`")
|
|
1317
|
-
.replace(/\$/g, "\\$");
|
|
1318
|
-
claudeCmd = `claude "${escapedPrompt}" --permission-mode=acceptEdits`;
|
|
1319
|
-
}
|
|
1320
|
-
else {
|
|
1321
|
-
claudeCmd = `claude --permission-mode=acceptEdits`;
|
|
1322
|
-
}
|
|
1323
|
-
// Send Claude command to new pane
|
|
1324
|
-
escapedCmd = claudeCmd.replace(/'/g, "'\\''");
|
|
1325
|
-
execSync(`tmux send-keys -t '${paneInfo}' '${escapedCmd}'`, {
|
|
1326
|
-
stdio: "pipe",
|
|
1327
|
-
});
|
|
1328
|
-
execSync(`tmux send-keys -t '${paneInfo}' Enter`, { stdio: "pipe" });
|
|
1329
|
-
}
|
|
1330
|
-
else if (agent === "opencode") {
|
|
1331
|
-
// opencode: start the TUI, then paste the prompt and submit
|
|
1332
|
-
const openCoderCmd = `opencode`;
|
|
1333
|
-
const escapedOpenCmd = openCoderCmd.replace(/'/g, "'\\''");
|
|
1334
|
-
execSync(`tmux send-keys -t '${paneInfo}' '${escapedOpenCmd}'`, {
|
|
1335
|
-
stdio: "pipe",
|
|
1336
|
-
});
|
|
1337
|
-
execSync(`tmux send-keys -t '${paneInfo}' Enter`, { stdio: "pipe" });
|
|
1338
|
-
if (prompt && prompt.trim()) {
|
|
1339
|
-
await new Promise((resolve) => setTimeout(resolve, 1500));
|
|
1340
|
-
const bufName = `dmux_prompt_${Date.now()}`;
|
|
1341
|
-
const promptEsc = prompt.replace(/\\/g, "\\\\").replace(/'/g, "'\\''");
|
|
1342
|
-
execSync(`tmux set-buffer -b '${bufName}' -- '${promptEsc}'`, {
|
|
1343
|
-
stdio: "pipe",
|
|
1344
|
-
});
|
|
1345
|
-
execSync(`tmux paste-buffer -b '${bufName}' -t '${paneInfo}'`, {
|
|
1346
|
-
stdio: "pipe",
|
|
1347
|
-
});
|
|
1348
|
-
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
1349
|
-
execSync(`tmux delete-buffer -b '${bufName}'`, { stdio: "pipe" });
|
|
1350
|
-
execSync(`tmux send-keys -t '${paneInfo}' Enter`, { stdio: "pipe" });
|
|
1351
|
-
}
|
|
1352
|
-
}
|
|
1353
|
-
if (agent === "claude") {
|
|
1354
|
-
// Monitor for Claude Code trust prompt and auto-respond
|
|
1355
|
-
const autoApproveTrust = async () => {
|
|
1356
|
-
// Wait for Claude to start up before checking for prompts
|
|
1357
|
-
await new Promise((resolve) => setTimeout(resolve, 800));
|
|
1358
|
-
const maxChecks = 100; // 100 checks * 100ms = 10 seconds total
|
|
1359
|
-
const checkInterval = 100; // Check every 100ms
|
|
1360
|
-
let lastContent = "";
|
|
1361
|
-
let stableContentCount = 0;
|
|
1362
|
-
let promptHandled = false;
|
|
1363
|
-
// More comprehensive trust prompt patterns
|
|
1364
|
-
const trustPromptPatterns = [
|
|
1365
|
-
/Do you trust the files in this folder\?/i,
|
|
1366
|
-
/Trust the files in this workspace\?/i,
|
|
1367
|
-
/Do you trust the authors of the files/i,
|
|
1368
|
-
/Do you want to trust this workspace\?/i,
|
|
1369
|
-
/trust.*files.*folder/i,
|
|
1370
|
-
/trust.*workspace/i,
|
|
1371
|
-
/Do you trust/i,
|
|
1372
|
-
/Trust this folder/i,
|
|
1373
|
-
/trust.*directory/i,
|
|
1374
|
-
/permission.*grant/i,
|
|
1375
|
-
/allow.*access/i,
|
|
1376
|
-
/workspace.*trust/i,
|
|
1377
|
-
/accept.*edits/i, // Claude's accept edits prompt
|
|
1378
|
-
/permission.*mode/i, // Permission mode prompt
|
|
1379
|
-
/allow.*claude/i, // Allow Claude prompt
|
|
1380
|
-
/\[y\/n\]/i, // Common yes/no prompt pattern
|
|
1381
|
-
/\(y\/n\)/i,
|
|
1382
|
-
/Yes\/No/i,
|
|
1383
|
-
/\[Y\/n\]/i, // Default yes pattern
|
|
1384
|
-
/press.*enter.*accept/i, // Press enter to accept
|
|
1385
|
-
/press.*enter.*continue/i, // Press enter to continue
|
|
1386
|
-
/❯\s*1\.\s*Yes,\s*proceed/i, // New Claude numbered menu format
|
|
1387
|
-
/Enter to confirm.*Esc to exit/i, // New Claude confirmation format
|
|
1388
|
-
/1\.\s*Yes,\s*proceed/i, // Yes proceed option
|
|
1389
|
-
/2\.\s*No,\s*exit/i, // No exit option
|
|
1390
|
-
];
|
|
1391
|
-
for (let i = 0; i < maxChecks; i++) {
|
|
1392
|
-
await new Promise((resolve) => setTimeout(resolve, checkInterval));
|
|
1393
|
-
try {
|
|
1394
|
-
// Capture the pane content
|
|
1395
|
-
const paneContent = capturePaneContent(paneInfo, 30);
|
|
1396
|
-
if (i % 10 === 0) {
|
|
1397
|
-
// Log every 10 checks (every second)
|
|
1398
|
-
}
|
|
1399
|
-
// Check if content has stabilized (same for 3 checks = prompt is waiting)
|
|
1400
|
-
if (paneContent === lastContent) {
|
|
1401
|
-
stableContentCount++;
|
|
1402
|
-
}
|
|
1403
|
-
else {
|
|
1404
|
-
stableContentCount = 0;
|
|
1405
|
-
lastContent = paneContent;
|
|
1406
|
-
}
|
|
1407
|
-
// Look for trust prompt in the current content
|
|
1408
|
-
const hasTrustPrompt = trustPromptPatterns.some((pattern) => pattern.test(paneContent));
|
|
1409
|
-
// Also check if we see specific Claude permission text
|
|
1410
|
-
const hasClaudePermissionPrompt = paneContent.includes("Do you trust") ||
|
|
1411
|
-
paneContent.includes("trust the files") ||
|
|
1412
|
-
paneContent.includes("permission") ||
|
|
1413
|
-
paneContent.includes("allow") ||
|
|
1414
|
-
(paneContent.includes("folder") && paneContent.includes("?"));
|
|
1415
|
-
if ((hasTrustPrompt || hasClaudePermissionPrompt) &&
|
|
1416
|
-
!promptHandled) {
|
|
1417
|
-
// Content is stable and we found a prompt
|
|
1418
|
-
if (stableContentCount >= 2) {
|
|
1419
|
-
// Check if this is the new Claude numbered menu format
|
|
1420
|
-
const isNewClaudeFormat = /❯\s*1\.\s*Yes,\s*proceed/i.test(paneContent) ||
|
|
1421
|
-
/Enter to confirm.*Esc to exit/i.test(paneContent);
|
|
1422
|
-
if (isNewClaudeFormat) {
|
|
1423
|
-
// For new Claude format, just press Enter to confirm default "Yes, proceed"
|
|
1424
|
-
execSync(`tmux send-keys -t '${paneInfo}' Enter`, {
|
|
1425
|
-
stdio: "pipe",
|
|
1426
|
-
});
|
|
1427
|
-
}
|
|
1428
|
-
else {
|
|
1429
|
-
// Try multiple response methods for older formats
|
|
1430
|
-
// Method 1: Send 'y' followed by Enter (most explicit)
|
|
1431
|
-
execSync(`tmux send-keys -t '${paneInfo}' 'y'`, {
|
|
1432
|
-
stdio: "pipe",
|
|
1433
|
-
});
|
|
1434
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
1435
|
-
execSync(`tmux send-keys -t '${paneInfo}' Enter`, {
|
|
1436
|
-
stdio: "pipe",
|
|
1437
|
-
});
|
|
1438
|
-
// Method 2: Just Enter (if it's a yes/no with default yes)
|
|
1439
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
1440
|
-
execSync(`tmux send-keys -t '${paneInfo}' Enter`, {
|
|
1441
|
-
stdio: "pipe",
|
|
1442
|
-
});
|
|
1443
|
-
}
|
|
1444
|
-
// Mark as handled to avoid duplicate responses
|
|
1445
|
-
promptHandled = true;
|
|
1446
|
-
// Wait and check if prompt is gone
|
|
1447
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1448
|
-
// Verify the prompt is gone
|
|
1449
|
-
const updatedContent = capturePaneContent(paneInfo, 10);
|
|
1450
|
-
// If trust prompt is gone, check if we need to resend the Claude command
|
|
1451
|
-
const promptGone = !trustPromptPatterns.some((p) => p.test(updatedContent));
|
|
1452
|
-
if (promptGone) {
|
|
1453
|
-
// Check if Claude is running or if we need to restart it
|
|
1454
|
-
const claudeRunning = updatedContent.includes("Claude") ||
|
|
1455
|
-
updatedContent.includes("claude") ||
|
|
1456
|
-
updatedContent.includes("Assistant") ||
|
|
1457
|
-
(prompt &&
|
|
1458
|
-
updatedContent.includes(prompt.substring(0, Math.min(20, prompt.length))));
|
|
1459
|
-
if (!claudeRunning && !updatedContent.includes("$")) {
|
|
1460
|
-
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1461
|
-
execSync(`tmux send-keys -t '${paneInfo}' '${escapedCmd}'`, { stdio: "pipe" });
|
|
1462
|
-
execSync(`tmux send-keys -t '${paneInfo}' Enter`, {
|
|
1463
|
-
stdio: "pipe",
|
|
1464
|
-
});
|
|
1465
|
-
}
|
|
1466
|
-
break;
|
|
1467
|
-
}
|
|
1468
|
-
}
|
|
1469
|
-
}
|
|
1470
|
-
// If we see Claude is already running without prompts, we're done
|
|
1471
|
-
if (!hasTrustPrompt &&
|
|
1472
|
-
!hasClaudePermissionPrompt &&
|
|
1473
|
-
(paneContent.includes("Claude") ||
|
|
1474
|
-
paneContent.includes("Assistant"))) {
|
|
1475
|
-
break;
|
|
1476
|
-
}
|
|
1477
|
-
}
|
|
1478
|
-
catch (error) {
|
|
1479
|
-
// Continue checking, errors are non-fatal
|
|
1480
|
-
}
|
|
1481
|
-
}
|
|
1482
|
-
};
|
|
1483
|
-
// Start monitoring for trust prompt in background
|
|
1484
|
-
autoApproveTrust().catch((err) => { });
|
|
1485
|
-
}
|
|
1486
|
-
// Keep focus on the new pane
|
|
1487
|
-
execSync(`tmux select-pane -t '${paneInfo}'`, { stdio: "pipe" });
|
|
1488
|
-
// Save pane info
|
|
1489
|
-
const newPane = {
|
|
1490
|
-
id: `dmux-${Date.now()}`,
|
|
1491
|
-
slug,
|
|
1492
|
-
prompt: prompt || "No initial prompt",
|
|
1493
|
-
paneId: paneInfo,
|
|
1494
|
-
worktreePath,
|
|
1495
|
-
agent,
|
|
1496
|
-
};
|
|
1497
|
-
const updatedPanes = [...panes, newPane];
|
|
1498
|
-
await savePanes(updatedPanes);
|
|
1499
|
-
// Switch back to the original pane (where dmux is running)
|
|
1500
|
-
execSync(`tmux select-pane -t '${originalPaneId}'`, { stdio: "pipe" });
|
|
1501
|
-
// Re-set the title for the dmux pane
|
|
1502
|
-
try {
|
|
1503
|
-
execSync(`tmux select-pane -t '${originalPaneId}' -T "dmux v${packageJson.version} - ${projectName}"`, { stdio: "pipe" });
|
|
1504
|
-
}
|
|
1505
|
-
catch {
|
|
1506
|
-
// Ignore if setting title fails
|
|
1507
|
-
}
|
|
1508
|
-
// Clear the screen and redraw the UI
|
|
1509
|
-
process.stdout.write("\x1b[2J\x1b[H");
|
|
1510
|
-
// Reset the creating pane flag and refresh
|
|
1511
|
-
setIsCreatingPane(false);
|
|
1512
|
-
setStatusMessage("");
|
|
1513
|
-
// Force a reload of panes to ensure UI is up to date
|
|
1514
|
-
await loadPanes();
|
|
1515
|
-
};
|
|
1516
|
-
const jumpToPane = (paneId) => {
|
|
1517
|
-
try {
|
|
1518
|
-
// Enable pane borders to show titles (if not already enabled)
|
|
1519
|
-
try {
|
|
1520
|
-
execSync(`tmux set-option -g pane-border-status top`, { stdio: "pipe" });
|
|
1521
|
-
}
|
|
1522
|
-
catch {
|
|
1523
|
-
// Ignore if already set or fails
|
|
1524
|
-
}
|
|
1525
|
-
execSync(`tmux select-pane -t '${paneId}'`, { stdio: "pipe" });
|
|
1526
|
-
// Clear screen after jump to remove artifacts
|
|
1527
|
-
clearScreen();
|
|
1528
|
-
setStatusMessage("Jumped to pane");
|
|
1529
|
-
setTimeout(() => setStatusMessage(""), 2000);
|
|
1530
|
-
}
|
|
1531
|
-
catch {
|
|
1532
|
-
setStatusMessage("Failed to jump - pane may be closed");
|
|
1533
|
-
setTimeout(() => setStatusMessage(""), 2000);
|
|
1534
|
-
}
|
|
1535
|
-
};
|
|
1536
|
-
const runCommand = async (type, pane) => {
|
|
1537
|
-
if (!pane.worktreePath) {
|
|
1538
|
-
setStatusMessage("No worktree path for this pane");
|
|
1539
|
-
setTimeout(() => setStatusMessage(""), 2000);
|
|
1540
|
-
return;
|
|
1541
|
-
}
|
|
1542
|
-
const command = type === "test" ? projectSettings.testCommand : projectSettings.devCommand;
|
|
1543
|
-
const isFirstRun = type === "test"
|
|
1544
|
-
? !projectSettings.firstTestRun
|
|
1545
|
-
: !projectSettings.firstDevRun;
|
|
1546
|
-
if (!command) {
|
|
1547
|
-
// No command configured, prompt user
|
|
1548
|
-
setShowCommandPrompt(type);
|
|
1549
|
-
return;
|
|
1550
|
-
}
|
|
1551
|
-
// Check if this is the first run and offer to copy non-git files
|
|
1552
|
-
if (isFirstRun) {
|
|
1553
|
-
// Show file copy prompt and wait for response
|
|
1554
|
-
setShowFileCopyPrompt(true);
|
|
1555
|
-
setCurrentCommandType(type);
|
|
1556
|
-
setStatusMessage(`First time running ${type} command...`);
|
|
1557
|
-
// Return here - the actual command will be run after user responds to prompt
|
|
1558
|
-
return;
|
|
1559
|
-
}
|
|
1560
|
-
try {
|
|
1561
|
-
setRunningCommand(true);
|
|
1562
|
-
setStatusMessage(`Starting ${type} in background window...`);
|
|
1563
|
-
// Kill existing window if present
|
|
1564
|
-
const existingWindowId = type === "test" ? pane.testWindowId : pane.devWindowId;
|
|
1565
|
-
if (existingWindowId) {
|
|
1566
|
-
try {
|
|
1567
|
-
execSync(`tmux kill-window -t '${existingWindowId}'`, {
|
|
1568
|
-
stdio: "pipe",
|
|
1569
|
-
});
|
|
1570
|
-
}
|
|
1571
|
-
catch { }
|
|
1572
|
-
}
|
|
1573
|
-
// Create a new background window for the command
|
|
1574
|
-
const windowName = `${pane.slug}-${type}`;
|
|
1575
|
-
const windowId = execSync(`tmux new-window -d -n '${windowName}' -P -F '#{window_id}'`, { encoding: "utf-8", stdio: "pipe" }).trim();
|
|
1576
|
-
// Create a log file to capture output
|
|
1577
|
-
const logFile = `/tmp/dmux-${pane.id}-${type}.log`;
|
|
1578
|
-
// Build the command with output capture
|
|
1579
|
-
const fullCommand = type === "test"
|
|
1580
|
-
? `cd "${pane.worktreePath}" && ${command} 2>&1 | tee ${logFile}`
|
|
1581
|
-
: `cd "${pane.worktreePath}" && ${command} 2>&1 | tee ${logFile}`;
|
|
1582
|
-
// Send the command to the new window
|
|
1583
|
-
execSync(`tmux send-keys -t '${windowId}' '${fullCommand.replace(/'/g, "'\\''")}'`, { stdio: "pipe" });
|
|
1584
|
-
execSync(`tmux send-keys -t '${windowId}' Enter`, { stdio: "pipe" });
|
|
1585
|
-
// Update pane with window info
|
|
1586
|
-
const updatedPane = {
|
|
1587
|
-
...pane,
|
|
1588
|
-
[type === "test" ? "testWindowId" : "devWindowId"]: windowId,
|
|
1589
|
-
[type === "test" ? "testStatus" : "devStatus"]: "running",
|
|
1590
|
-
};
|
|
1591
|
-
const updatedPanes = panes.map((p) => p.id === pane.id ? updatedPane : p);
|
|
1592
|
-
await savePanes(updatedPanes);
|
|
1593
|
-
// Start monitoring the output
|
|
1594
|
-
if (type === "test") {
|
|
1595
|
-
// For tests, monitor for completion
|
|
1596
|
-
setTimeout(() => monitorTestOutput(pane.id, logFile), 2000);
|
|
1597
|
-
}
|
|
1598
|
-
else {
|
|
1599
|
-
// For dev, monitor for server URL
|
|
1600
|
-
setTimeout(() => monitorDevOutput(pane.id, logFile), 2000);
|
|
1601
|
-
}
|
|
1602
|
-
setRunningCommand(false);
|
|
1603
|
-
setStatusMessage(`${type === "test" ? "Test" : "Dev server"} started in background`);
|
|
1604
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
1605
|
-
}
|
|
1606
|
-
catch (error) {
|
|
1607
|
-
setRunningCommand(false);
|
|
1608
|
-
setStatusMessage(`Failed to run ${type} command`);
|
|
1609
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
1610
|
-
}
|
|
1611
|
-
};
|
|
465
|
+
// jumpToPane and runCommand functions removed - now handled by action system and pane runner
|
|
1612
466
|
// Update handling moved to useAutoUpdater
|
|
1613
|
-
//
|
|
1614
|
-
const clearScreen = () => {
|
|
1615
|
-
// CRITICAL: Force Ink to re-render FIRST, before clearing
|
|
1616
|
-
// This prevents blank screen by ensuring React starts rendering immediately
|
|
1617
|
-
setForceRepaintTrigger((prev) => prev + 1);
|
|
1618
|
-
// Multiple clearing strategies to prevent artifacts
|
|
1619
|
-
// 1. Clear screen with ANSI codes
|
|
1620
|
-
process.stdout.write("\x1b[2J\x1b[H");
|
|
1621
|
-
// 2. Clear tmux history
|
|
1622
|
-
try {
|
|
1623
|
-
execSync("tmux clear-history", { stdio: "pipe" });
|
|
1624
|
-
}
|
|
1625
|
-
catch { }
|
|
1626
|
-
// 3. Force tmux to refresh the display
|
|
1627
|
-
try {
|
|
1628
|
-
execSync("tmux refresh-client", { stdio: "pipe" });
|
|
1629
|
-
}
|
|
1630
|
-
catch { }
|
|
1631
|
-
};
|
|
467
|
+
// clearScreen function removed - no longer used (was only used by removed jumpToPane function)
|
|
1632
468
|
// Cleanup function for exit
|
|
1633
469
|
const cleanExit = () => {
|
|
1634
470
|
// Clear screen before exiting Ink
|
|
@@ -1636,15 +472,16 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
1636
472
|
// Exit the Ink app (this cleans up the React tree)
|
|
1637
473
|
exit();
|
|
1638
474
|
// Give Ink a moment to clean up its rendering, then do final cleanup
|
|
1639
|
-
setTimeout(() => {
|
|
475
|
+
setTimeout(async () => {
|
|
1640
476
|
// Multiple aggressive clearing strategies
|
|
1641
477
|
process.stdout.write("\x1b[2J\x1b[H"); // Clear screen and move cursor to home
|
|
1642
478
|
process.stdout.write("\x1b[3J"); // Clear scrollback buffer
|
|
1643
479
|
process.stdout.write("\x1b[0m"); // Reset all attributes
|
|
1644
480
|
// Clear tmux history and pane
|
|
1645
481
|
try {
|
|
1646
|
-
|
|
1647
|
-
|
|
482
|
+
const tmuxService = TmuxService.getInstance();
|
|
483
|
+
tmuxService.clearHistorySync();
|
|
484
|
+
await tmuxService.sendKeys("", "C-l");
|
|
1648
485
|
}
|
|
1649
486
|
catch { }
|
|
1650
487
|
// One more final clear
|
|
@@ -1655,244 +492,46 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
1655
492
|
process.exit(0);
|
|
1656
493
|
}, 100);
|
|
1657
494
|
};
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
const selectedPane = panes[selectedIndex];
|
|
1699
|
-
if (selectedPane && selectedPane.worktreePath && currentCommandType) {
|
|
1700
|
-
await copyNonGitFiles(selectedPane.worktreePath);
|
|
1701
|
-
// Mark as not first run and continue with command
|
|
1702
|
-
const newSettings = {
|
|
1703
|
-
...projectSettings,
|
|
1704
|
-
[currentCommandType === "test" ? "firstTestRun" : "firstDevRun"]: true,
|
|
1705
|
-
};
|
|
1706
|
-
await saveSettings(newSettings);
|
|
1707
|
-
// Now run the actual command
|
|
1708
|
-
await runCommandInternal(currentCommandType, selectedPane);
|
|
1709
|
-
}
|
|
1710
|
-
setCurrentCommandType(null);
|
|
1711
|
-
}
|
|
1712
|
-
else if (input === "n" || input === "N" || key.escape) {
|
|
1713
|
-
setShowFileCopyPrompt(false);
|
|
1714
|
-
const selectedPane = panes[selectedIndex];
|
|
1715
|
-
if (selectedPane && currentCommandType) {
|
|
1716
|
-
// Mark as not first run and continue without copying
|
|
1717
|
-
const newSettings = {
|
|
1718
|
-
...projectSettings,
|
|
1719
|
-
[currentCommandType === "test" ? "firstTestRun" : "firstDevRun"]: true,
|
|
1720
|
-
};
|
|
1721
|
-
await saveSettings(newSettings);
|
|
1722
|
-
// Now run the actual command
|
|
1723
|
-
await runCommandInternal(currentCommandType, selectedPane);
|
|
1724
|
-
}
|
|
1725
|
-
setCurrentCommandType(null);
|
|
1726
|
-
}
|
|
1727
|
-
return;
|
|
1728
|
-
}
|
|
1729
|
-
if (showCommandPrompt) {
|
|
1730
|
-
if (key.escape) {
|
|
1731
|
-
setShowCommandPrompt(null);
|
|
1732
|
-
setCommandInput("");
|
|
1733
|
-
}
|
|
1734
|
-
else if (key.return) {
|
|
1735
|
-
if (commandInput.trim() === "") {
|
|
1736
|
-
// If empty, suggest a default command based on package manager
|
|
1737
|
-
const suggested = await suggestCommand(showCommandPrompt);
|
|
1738
|
-
if (suggested) {
|
|
1739
|
-
setCommandInput(suggested);
|
|
1740
|
-
}
|
|
1741
|
-
}
|
|
1742
|
-
else {
|
|
1743
|
-
// User provided manual command
|
|
1744
|
-
const newSettings = {
|
|
1745
|
-
...projectSettings,
|
|
1746
|
-
[showCommandPrompt === "test" ? "testCommand" : "devCommand"]: commandInput.trim(),
|
|
1747
|
-
};
|
|
1748
|
-
await saveSettings(newSettings);
|
|
1749
|
-
const selectedPane = panes[selectedIndex];
|
|
1750
|
-
if (selectedPane) {
|
|
1751
|
-
// Check if first run
|
|
1752
|
-
const isFirstRun = showCommandPrompt === "test"
|
|
1753
|
-
? !projectSettings.firstTestRun
|
|
1754
|
-
: !projectSettings.firstDevRun;
|
|
1755
|
-
if (isFirstRun) {
|
|
1756
|
-
setCurrentCommandType(showCommandPrompt);
|
|
1757
|
-
setShowCommandPrompt(null);
|
|
1758
|
-
setShowFileCopyPrompt(true);
|
|
1759
|
-
}
|
|
1760
|
-
else {
|
|
1761
|
-
await runCommandInternal(showCommandPrompt, selectedPane);
|
|
1762
|
-
setShowCommandPrompt(null);
|
|
1763
|
-
setCommandInput("");
|
|
1764
|
-
}
|
|
1765
|
-
}
|
|
1766
|
-
else {
|
|
1767
|
-
setShowCommandPrompt(null);
|
|
1768
|
-
setCommandInput("");
|
|
1769
|
-
}
|
|
1770
|
-
}
|
|
1771
|
-
}
|
|
1772
|
-
return;
|
|
1773
|
-
}
|
|
1774
|
-
// Handle directional navigation with spatial awareness based on card grid layout
|
|
1775
|
-
if (key.upArrow || key.downArrow || key.leftArrow || key.rightArrow) {
|
|
1776
|
-
let targetIndex = null;
|
|
1777
|
-
if (key.upArrow) {
|
|
1778
|
-
targetIndex = findCardInDirection(selectedIndex, "up");
|
|
1779
|
-
}
|
|
1780
|
-
else if (key.downArrow) {
|
|
1781
|
-
targetIndex = findCardInDirection(selectedIndex, "down");
|
|
1782
|
-
}
|
|
1783
|
-
else if (key.leftArrow) {
|
|
1784
|
-
targetIndex = findCardInDirection(selectedIndex, "left");
|
|
1785
|
-
}
|
|
1786
|
-
else if (key.rightArrow) {
|
|
1787
|
-
targetIndex = findCardInDirection(selectedIndex, "right");
|
|
1788
|
-
}
|
|
1789
|
-
if (targetIndex !== null) {
|
|
1790
|
-
setSelectedIndex(targetIndex);
|
|
1791
|
-
}
|
|
1792
|
-
return;
|
|
1793
|
-
}
|
|
1794
|
-
if (input === "m" && selectedIndex < panes.length) {
|
|
1795
|
-
// Open kebab menu popup for selected pane
|
|
1796
|
-
await launchKebabMenuPopup(selectedIndex);
|
|
1797
|
-
}
|
|
1798
|
-
else if (input === "s") {
|
|
1799
|
-
// Open settings popup
|
|
1800
|
-
await launchSettingsPopup();
|
|
1801
|
-
}
|
|
1802
|
-
else if (input === "l") {
|
|
1803
|
-
// Open logs popup
|
|
1804
|
-
await launchLogsPopup();
|
|
1805
|
-
}
|
|
1806
|
-
else if (input === "?") {
|
|
1807
|
-
// Open keyboard shortcuts popup
|
|
1808
|
-
await launchShortcutsPopup();
|
|
1809
|
-
}
|
|
1810
|
-
else if (input === "L" && controlPaneId) {
|
|
1811
|
-
// Reset layout to sidebar configuration (Shift+L)
|
|
1812
|
-
enforceControlPaneSize(controlPaneId, SIDEBAR_WIDTH);
|
|
1813
|
-
setStatusMessage("Layout reset");
|
|
1814
|
-
setTimeout(() => setStatusMessage(""), 2000);
|
|
1815
|
-
}
|
|
1816
|
-
else if (input === "q") {
|
|
1817
|
-
cleanExit();
|
|
1818
|
-
}
|
|
1819
|
-
else if (input === "r" && server) {
|
|
1820
|
-
// Handle remote tunnel
|
|
1821
|
-
if (tunnelUrl) {
|
|
1822
|
-
// Tunnel exists - open popup with QR code
|
|
1823
|
-
await launchRemotePopup();
|
|
1824
|
-
}
|
|
1825
|
-
else if (!tunnelCreating) {
|
|
1826
|
-
// Start tunnel creation
|
|
1827
|
-
setTunnelCreating(true);
|
|
1828
|
-
(async () => {
|
|
1829
|
-
try {
|
|
1830
|
-
const url = await server.startTunnel();
|
|
1831
|
-
setTunnelUrl(url);
|
|
1832
|
-
}
|
|
1833
|
-
catch (error) {
|
|
1834
|
-
setStatusMessage(`Failed to create tunnel: ${error.message}`);
|
|
1835
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
1836
|
-
}
|
|
1837
|
-
finally {
|
|
1838
|
-
setTunnelCreating(false);
|
|
1839
|
-
}
|
|
1840
|
-
})();
|
|
1841
|
-
}
|
|
1842
|
-
// If tunnelCreating is true, do nothing (already creating)
|
|
1843
|
-
return;
|
|
1844
|
-
}
|
|
1845
|
-
else if (!isLoading &&
|
|
1846
|
-
(input === "n" || (key.return && selectedIndex === panes.length))) {
|
|
1847
|
-
// Launch popup modal for new pane
|
|
1848
|
-
await launchNewPanePopup();
|
|
1849
|
-
return;
|
|
1850
|
-
}
|
|
1851
|
-
else if (!isLoading &&
|
|
1852
|
-
(input === "t" || (key.return && selectedIndex === panes.length + 1))) {
|
|
1853
|
-
// Create a new terminal pane without an agent
|
|
1854
|
-
try {
|
|
1855
|
-
setIsCreatingPane(true);
|
|
1856
|
-
setStatusMessage("Creating terminal pane...");
|
|
1857
|
-
// Create a simple tmux pane split
|
|
1858
|
-
const paneInfo = execSync(`tmux split-window -h -P -F '#{pane_id}'`, {
|
|
1859
|
-
encoding: "utf-8",
|
|
1860
|
-
}).trim();
|
|
1861
|
-
// Wait for pane creation to settle
|
|
1862
|
-
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1863
|
-
// The shell pane will be automatically detected by the shell pane detection system
|
|
1864
|
-
// No need to manually add it to the panes array
|
|
1865
|
-
setIsCreatingPane(false);
|
|
1866
|
-
setStatusMessage("Terminal pane created");
|
|
1867
|
-
setTimeout(() => setStatusMessage(""), 2000);
|
|
1868
|
-
// Force a reload to pick up the new shell pane
|
|
1869
|
-
await loadPanes();
|
|
1870
|
-
}
|
|
1871
|
-
catch (error) {
|
|
1872
|
-
setIsCreatingPane(false);
|
|
1873
|
-
setStatusMessage(`Failed to create terminal pane: ${error.message}`);
|
|
1874
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
1875
|
-
}
|
|
1876
|
-
return;
|
|
1877
|
-
}
|
|
1878
|
-
else if (input === "j" && selectedIndex < panes.length) {
|
|
1879
|
-
// Jump to pane (NEW: using action system)
|
|
1880
|
-
StateManager.getInstance().setDebugMessage(`Jumping to pane: ${panes[selectedIndex].slug}`);
|
|
1881
|
-
setTimeout(() => StateManager.getInstance().setDebugMessage(""), 2000);
|
|
1882
|
-
actionSystem.executeAction(PaneAction.VIEW, panes[selectedIndex]);
|
|
1883
|
-
}
|
|
1884
|
-
else if (input === "x" && selectedIndex < panes.length) {
|
|
1885
|
-
// Close pane (NEW: using action system)
|
|
1886
|
-
StateManager.getInstance().setDebugMessage(`Closing pane: ${panes[selectedIndex].slug}`);
|
|
1887
|
-
setTimeout(() => StateManager.getInstance().setDebugMessage(""), 2000);
|
|
1888
|
-
actionSystem.executeAction(PaneAction.CLOSE, panes[selectedIndex]);
|
|
1889
|
-
}
|
|
1890
|
-
else if (key.return && selectedIndex < panes.length) {
|
|
1891
|
-
// Jump to pane (NEW: using action system)
|
|
1892
|
-
StateManager.getInstance().setDebugMessage(`Jumping to pane: ${panes[selectedIndex].slug}`);
|
|
1893
|
-
setTimeout(() => StateManager.getInstance().setDebugMessage(""), 2000);
|
|
1894
|
-
actionSystem.executeAction(PaneAction.VIEW, panes[selectedIndex]);
|
|
1895
|
-
}
|
|
495
|
+
// Input handling - extracted to dedicated hook
|
|
496
|
+
useInputHandling({
|
|
497
|
+
panes,
|
|
498
|
+
selectedIndex,
|
|
499
|
+
setSelectedIndex,
|
|
500
|
+
isCreatingPane,
|
|
501
|
+
setIsCreatingPane,
|
|
502
|
+
runningCommand,
|
|
503
|
+
isUpdating,
|
|
504
|
+
isLoading,
|
|
505
|
+
ignoreInput,
|
|
506
|
+
quitConfirmMode,
|
|
507
|
+
setQuitConfirmMode,
|
|
508
|
+
showCommandPrompt,
|
|
509
|
+
setShowCommandPrompt,
|
|
510
|
+
commandInput,
|
|
511
|
+
setCommandInput,
|
|
512
|
+
showFileCopyPrompt,
|
|
513
|
+
setShowFileCopyPrompt,
|
|
514
|
+
currentCommandType,
|
|
515
|
+
setCurrentCommandType,
|
|
516
|
+
projectSettings,
|
|
517
|
+
saveSettings,
|
|
518
|
+
settingsManager,
|
|
519
|
+
tunnelUrl,
|
|
520
|
+
setTunnelUrl,
|
|
521
|
+
tunnelCreating,
|
|
522
|
+
setTunnelCreating,
|
|
523
|
+
setTunnelCopied,
|
|
524
|
+
popupManager,
|
|
525
|
+
actionSystem,
|
|
526
|
+
server,
|
|
527
|
+
controlPaneId,
|
|
528
|
+
setStatusMessage,
|
|
529
|
+
copyNonGitFiles,
|
|
530
|
+
runCommandInternal,
|
|
531
|
+
handlePaneCreationWithAgent,
|
|
532
|
+
loadPanes,
|
|
533
|
+
cleanExit,
|
|
534
|
+
findCardInDirection,
|
|
1896
535
|
});
|
|
1897
536
|
// Calculate available height for content (terminal height - footer lines - active status messages)
|
|
1898
537
|
// Footer height varies based on state:
|
|
@@ -1949,21 +588,7 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
1949
588
|
: actionSystem.actionState.statusType === "success"
|
|
1950
589
|
? "green"
|
|
1951
590
|
: "cyan" }, actionSystem.actionState.statusMessage))),
|
|
1952
|
-
React.createElement(FooterHelp, { show: !showCommandPrompt, showRemoteKey: !!server, quitConfirmMode: quitConfirmMode, hasSidebarLayout: !!controlPaneId, serverPort: serverPort, unreadErrorCount: unreadErrorCount, unreadWarningCount: unreadWarningCount, localIp: localIp, tunnelUrl: tunnelUrl, tunnelCreating: tunnelCreating, tunnelCopied: tunnelCopied, tunnelSpinner: (() => {
|
|
1953
|
-
const spinnerFrames = [
|
|
1954
|
-
"⠋",
|
|
1955
|
-
"⠙",
|
|
1956
|
-
"⠹",
|
|
1957
|
-
"⠸",
|
|
1958
|
-
"⠼",
|
|
1959
|
-
"⠴",
|
|
1960
|
-
"⠦",
|
|
1961
|
-
"⠧",
|
|
1962
|
-
"⠇",
|
|
1963
|
-
"⠏",
|
|
1964
|
-
];
|
|
1965
|
-
return spinnerFrames[tunnelSpinnerFrame];
|
|
1966
|
-
})(), gridInfo: (() => {
|
|
591
|
+
React.createElement(FooterHelp, { show: !showCommandPrompt, showRemoteKey: !!server, quitConfirmMode: quitConfirmMode, hasSidebarLayout: !!controlPaneId, serverPort: serverPort, unreadErrorCount: unreadErrorCount, unreadWarningCount: unreadWarningCount, localIp: localIp, tunnelUrl: tunnelUrl, tunnelCreating: tunnelCreating, tunnelCopied: tunnelCopied, tunnelSpinner: tunnelState.getSpinnerChar(), gridInfo: (() => {
|
|
1967
592
|
if (!process.env.DEBUG_DMUX)
|
|
1968
593
|
return undefined;
|
|
1969
594
|
const cols = Math.max(1, Math.floor(terminalWidth / 37));
|