dmux 3.2.0 → 3.3.1
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 +270 -1606
- 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 +201 -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 +123 -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 +184 -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/{FooterHelp.d.ts → ui/FooterHelp.d.ts} +4 -0
- package/dist/components/ui/FooterHelp.d.ts.map +1 -0
- package/dist/components/{FooterHelp.js → ui/FooterHelp.js} +45 -1
- 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/ToastNotification.d.ts +13 -0
- package/dist/components/ui/ToastNotification.d.ts.map +1 -0
- package/dist/components/ui/ToastNotification.js +22 -0
- package/dist/components/ui/ToastNotification.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/dashboard.js +2 -2
- package/dist/hooks/useActionSystem.d.ts +5 -4
- package/dist/hooks/useActionSystem.d.ts.map +1 -1
- package/dist/hooks/useActionSystem.js +105 -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 +294 -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 +63 -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 +208 -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 +186 -0
- package/dist/hooks/usePaneSync.js.map +1 -0
- package/dist/hooks/usePanes.d.ts.map +1 -1
- package/dist/hooks/usePanes.js +68 -406
- 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 +67 -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 +124 -94
- 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 +140 -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 +156 -0
- package/dist/layout/SpacerManager.js.map +1 -0
- package/dist/layout/TmuxLayoutApplier.d.ts +62 -0
- package/dist/layout/TmuxLayoutApplier.d.ts.map +1 -0
- package/dist/layout/TmuxLayoutApplier.js +147 -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 +727 -5677
- 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 +33 -0
- package/dist/server/routes/keysRoutes.d.ts.map +1 -0
- package/dist/server/routes/keysRoutes.js +128 -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} +44 -10
- 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.d.ts.map +1 -1
- package/dist/services/StatusDetector.js +6 -8
- 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 +346 -0
- package/dist/services/TmuxService.d.ts.map +1 -0
- package/dist/services/TmuxService.js +849 -0
- package/dist/services/TmuxService.js.map +1 -0
- package/dist/services/ToastService.d.ts +71 -0
- package/dist/services/ToastService.d.ts.map +1 -0
- package/dist/services/ToastService.js +151 -0
- package/dist/services/ToastService.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/shared/StateManager.d.ts +22 -0
- package/dist/shared/StateManager.d.ts.map +1 -1
- package/dist/shared/StateManager.js +68 -1
- package/dist/shared/StateManager.js.map +1 -1
- package/dist/terminal.js +3 -3
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/asciiArt.d.ts.map +1 -1
- package/dist/utils/asciiArt.js +9 -22
- package/dist/utils/asciiArt.js.map +1 -1
- package/dist/utils/atomicWrite.d.ts +33 -0
- package/dist/utils/atomicWrite.d.ts.map +1 -0
- package/dist/utils/atomicWrite.js +89 -0
- package/dist/utils/atomicWrite.js.map +1 -0
- 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 +138 -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 +159 -380
- 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 +46 -13
- package/dist/utils/mergeValidation.js.map +1 -1
- package/dist/utils/paneCreation.d.ts.map +1 -1
- package/dist/utils/paneCreation.js +115 -106
- 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 +30 -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 +5 -14
- 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 +43 -29
- 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 +84 -76
- 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 +19 -33
- package/dist/utils/welcomePane.js.map +1 -1
- package/dist/utils/welcomePaneManager.d.ts.map +1 -1
- package/dist/utils/welcomePaneManager.js +11 -25
- 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/{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";
|
|
44
|
-
const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoot, autoUpdater, serverPort, server, controlPaneId, }) => {
|
|
37
|
+
import PanesGrid from "./components/panes/PanesGrid.js";
|
|
38
|
+
import CommandPromptDialog from "./components/dialogs/CommandPromptDialog.js";
|
|
39
|
+
import FileCopyPrompt from "./components/ui/FileCopyPrompt.js";
|
|
40
|
+
import LoadingIndicator from "./components/indicators/LoadingIndicator.js";
|
|
41
|
+
import RunningIndicator from "./components/indicators/RunningIndicator.js";
|
|
42
|
+
import UpdatingIndicator from "./components/indicators/UpdatingIndicator.js";
|
|
43
|
+
import FooterHelp from "./components/ui/FooterHelp.js";
|
|
44
|
+
const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoot, autoUpdater, serverPort, server, controlPaneId, rerenderRef, }) => {
|
|
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,35 +78,43 @@ 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
|
-
//
|
|
84
|
-
const [
|
|
85
|
-
const [
|
|
86
|
-
const [
|
|
87
|
-
|
|
88
|
-
const [tunnelCopied, setTunnelCopied] = useState(false);
|
|
89
|
-
// Subscribe to StateManager for unread error/warning count updates
|
|
81
|
+
// Track toast state
|
|
82
|
+
const [currentToast, setCurrentToast] = useState(null);
|
|
83
|
+
const [toastQueueLength, setToastQueueLength] = useState(0);
|
|
84
|
+
const [toastQueuePosition, setToastQueuePosition] = useState(null);
|
|
85
|
+
// Subscribe to StateManager for unread error/warning count and toast updates
|
|
90
86
|
useEffect(() => {
|
|
91
87
|
const stateManager = StateManager.getInstance();
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
88
|
+
const updateState = () => {
|
|
89
|
+
const state = stateManager.getState();
|
|
90
|
+
setUnreadErrorCount(state.unreadErrorCount);
|
|
91
|
+
setUnreadWarningCount(state.unreadWarningCount);
|
|
92
|
+
setCurrentToast(state.currentToast);
|
|
93
|
+
setToastQueueLength(state.toastQueueLength);
|
|
94
|
+
setToastQueuePosition(state.toastQueuePosition);
|
|
95
95
|
};
|
|
96
|
-
// Initial
|
|
97
|
-
|
|
96
|
+
// Initial state
|
|
97
|
+
updateState();
|
|
98
98
|
// Subscribe to changes
|
|
99
|
-
const unsubscribe = stateManager.subscribe(
|
|
99
|
+
const unsubscribe = stateManager.subscribe(updateState);
|
|
100
100
|
return () => {
|
|
101
101
|
unsubscribe();
|
|
102
102
|
};
|
|
103
103
|
}, []);
|
|
104
104
|
// Panes state and persistence (skipLoading will be updated after actionSystem is initialized)
|
|
105
105
|
const { panes, setPanes, isLoading, loadPanes, savePanes } = usePanes(panesFile, false);
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
-
|
|
109
|
-
|
|
106
|
+
// Pane lifecycle manager - handles locking to prevent race conditions
|
|
107
|
+
// Replaces the old timeout-based intentionallyClosedPanes Set
|
|
108
|
+
const lifecycleManager = React.useMemo(() => PaneLifecycleManager.getInstance(), []);
|
|
109
|
+
// Clean up stale lifecycle operations periodically
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
const cleanupInterval = setInterval(() => {
|
|
112
|
+
lifecycleManager.cleanupStaleOperations();
|
|
113
|
+
}, 60000); // Every 60 seconds
|
|
114
|
+
return () => clearInterval(cleanupInterval);
|
|
115
|
+
}, [lifecycleManager]);
|
|
110
116
|
// Pane runner
|
|
111
|
-
const { copyNonGitFiles, runCommandInternal,
|
|
117
|
+
const { copyNonGitFiles, runCommandInternal, } = usePaneRunner({
|
|
112
118
|
panes,
|
|
113
119
|
savePanes,
|
|
114
120
|
projectSettings,
|
|
@@ -119,16 +125,34 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
119
125
|
const forceRepaint = () => {
|
|
120
126
|
setForceRepaintTrigger((prev) => prev + 1);
|
|
121
127
|
setShowRepaintSpinner(true);
|
|
128
|
+
// CRITICAL: Use Ink's official rerender method to force complete redraw
|
|
129
|
+
// When tmux clears the pane via selectLayout, Ink's output is lost
|
|
130
|
+
// Calling rerender forces Ink to redraw the entire component tree
|
|
131
|
+
if (rerenderRef?.current) {
|
|
132
|
+
rerenderRef.current(React.createElement(DmuxApp, {
|
|
133
|
+
panesFile,
|
|
134
|
+
projectName,
|
|
135
|
+
sessionName,
|
|
136
|
+
settingsFile,
|
|
137
|
+
projectRoot,
|
|
138
|
+
autoUpdater,
|
|
139
|
+
serverPort,
|
|
140
|
+
server,
|
|
141
|
+
controlPaneId,
|
|
142
|
+
rerenderRef,
|
|
143
|
+
}));
|
|
144
|
+
}
|
|
122
145
|
// Hide spinner after a few frames (enough to trigger multiple renders)
|
|
123
|
-
setTimeout(() => setShowRepaintSpinner(false),
|
|
146
|
+
setTimeout(() => setShowRepaintSpinner(false), REPAINT_SPINNER_DURATION);
|
|
124
147
|
};
|
|
125
148
|
// Force repaint effect - ensures Ink re-renders when trigger changes
|
|
126
149
|
useEffect(() => {
|
|
127
150
|
if (forceRepaintTrigger > 0) {
|
|
128
151
|
// Small delay to ensure terminal is ready
|
|
129
|
-
const timer = setTimeout(() => {
|
|
152
|
+
const timer = setTimeout(async () => {
|
|
130
153
|
try {
|
|
131
|
-
|
|
154
|
+
const tmuxService = TmuxService.getInstance();
|
|
155
|
+
await tmuxService.refreshClient();
|
|
132
156
|
}
|
|
133
157
|
catch { }
|
|
134
158
|
}, 50);
|
|
@@ -137,48 +161,26 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
137
161
|
}, [forceRepaintTrigger]);
|
|
138
162
|
// Get local network IP on mount
|
|
139
163
|
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) {
|
|
164
|
+
const getLocalIp = async () => {
|
|
168
165
|
try {
|
|
169
|
-
|
|
166
|
+
// Get local IP address (not 127.0.0.1)
|
|
167
|
+
const { execSync } = await import("child_process");
|
|
168
|
+
const result = execSync(`hostname -I 2>/dev/null || ifconfig | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | head -1`, {
|
|
170
169
|
encoding: "utf-8",
|
|
171
170
|
stdio: "pipe",
|
|
172
|
-
cwd: projectRoot,
|
|
173
171
|
}).trim();
|
|
174
|
-
|
|
172
|
+
if (result) {
|
|
173
|
+
setLocalIp(result.split(" ")[0]); // Take first IP if multiple
|
|
174
|
+
}
|
|
175
175
|
}
|
|
176
176
|
catch {
|
|
177
|
-
//
|
|
178
|
-
|
|
177
|
+
// Fallback to 127.0.0.1
|
|
178
|
+
setLocalIp("127.0.0.1");
|
|
179
179
|
}
|
|
180
|
-
}
|
|
181
|
-
|
|
180
|
+
};
|
|
181
|
+
getLocalIp();
|
|
182
|
+
}, []);
|
|
183
|
+
// Spinner animation and branch detection now handled in hooks
|
|
182
184
|
// Pane creation
|
|
183
185
|
const { createNewPane: createNewPaneHook } = usePaneCreation({
|
|
184
186
|
panes,
|
|
@@ -191,6 +193,26 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
191
193
|
availableAgents,
|
|
192
194
|
forceRepaint,
|
|
193
195
|
});
|
|
196
|
+
// Initialize services
|
|
197
|
+
const { popupManager } = useServices({
|
|
198
|
+
// PopupManager config
|
|
199
|
+
sidebarWidth: SIDEBAR_WIDTH,
|
|
200
|
+
projectRoot: projectRoot || process.cwd(),
|
|
201
|
+
popupsSupported,
|
|
202
|
+
terminalWidth,
|
|
203
|
+
terminalHeight,
|
|
204
|
+
availableAgents,
|
|
205
|
+
agentChoice,
|
|
206
|
+
serverPort,
|
|
207
|
+
server,
|
|
208
|
+
settingsManager,
|
|
209
|
+
projectSettings,
|
|
210
|
+
// Callbacks
|
|
211
|
+
setStatusMessage,
|
|
212
|
+
setIgnoreInput,
|
|
213
|
+
savePanes,
|
|
214
|
+
loadPanes,
|
|
215
|
+
});
|
|
194
216
|
// Listen for status updates with analysis data and merge into panes
|
|
195
217
|
useEffect(() => {
|
|
196
218
|
const statusDetector = getStatusDetector();
|
|
@@ -308,897 +330,144 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
308
330
|
// findCardInDirection provided by useNavigation
|
|
309
331
|
// savePanes moved to usePanes
|
|
310
332
|
// applySmartLayout moved to utils/tmux
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
return;
|
|
333
|
+
// Helper function to handle agent choice and pane creation
|
|
334
|
+
const handlePaneCreationWithAgent = async (prompt) => {
|
|
335
|
+
const agents = availableAgents;
|
|
336
|
+
if (agents.length === 0) {
|
|
337
|
+
await createNewPaneHook(prompt);
|
|
317
338
|
}
|
|
318
|
-
|
|
319
|
-
|
|
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
|
-
}
|
|
376
|
-
}
|
|
377
|
-
catch (error) {
|
|
378
|
-
setStatusMessage(`Failed to launch popup: ${error.message}`);
|
|
379
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
339
|
+
else if (agents.length === 1) {
|
|
340
|
+
await createNewPaneHook(prompt, agents[0]);
|
|
380
341
|
}
|
|
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);
|
|
342
|
+
else {
|
|
343
|
+
// Multiple agents available - check for default agent setting first
|
|
344
|
+
const settings = settingsManager.getSettings();
|
|
345
|
+
if (settings.defaultAgent && agents.includes(settings.defaultAgent)) {
|
|
346
|
+
await createNewPaneHook(prompt, settings.defaultAgent);
|
|
447
347
|
}
|
|
448
348
|
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);
|
|
349
|
+
// Show agent choice popup
|
|
350
|
+
const selectedAgent = await popupManager.launchAgentChoicePopup();
|
|
351
|
+
if (selectedAgent) {
|
|
352
|
+
await createNewPaneHook(prompt, selectedAgent);
|
|
600
353
|
}
|
|
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
354
|
}
|
|
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
355
|
}
|
|
734
356
|
};
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
357
|
+
// Helper function to handle action results recursively
|
|
358
|
+
const handleActionResult = async (result) => {
|
|
359
|
+
// Handle ActionResults from background callbacks (e.g., conflict resolution completion)
|
|
360
|
+
// This allows showing dialogs even when not in the normal action flow
|
|
361
|
+
if (!popupsSupported)
|
|
740
362
|
return;
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
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
|
-
}
|
|
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
|
-
}
|
|
800
|
-
};
|
|
801
|
-
const launchSettingsPopup = async () => {
|
|
802
|
-
// Only launch popup if tmux supports it
|
|
803
|
-
if (!popupsSupported) {
|
|
804
|
-
setStatusMessage("Popups require tmux 3.2+");
|
|
805
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
806
|
-
return;
|
|
807
|
-
}
|
|
808
|
-
try {
|
|
809
|
-
// Resolve the popup script path
|
|
810
|
-
const projectRootForPopup = __dirname.includes("/dist")
|
|
811
|
-
? path.resolve(__dirname, "..") // If in dist/, go up one level
|
|
812
|
-
: path.resolve(__dirname, ".."); // If in src/, go up one level
|
|
813
|
-
const popupScriptPath = path.join(projectRootForPopup, "dist", "popups", "settingsPopup.js");
|
|
814
|
-
// Prepare settings data for popup
|
|
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
|
-
}
|
|
840
|
-
}
|
|
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);
|
|
363
|
+
// Handle the result type and show appropriate dialog
|
|
364
|
+
if (result.type === "confirm") {
|
|
365
|
+
const confirmed = await popupManager.launchConfirmPopup(result.title || "Confirm", result.message, result.confirmLabel, result.cancelLabel);
|
|
366
|
+
if (confirmed && result.onConfirm) {
|
|
367
|
+
const nextResult = await result.onConfirm();
|
|
368
|
+
// Recursively handle nested results
|
|
369
|
+
if (nextResult) {
|
|
370
|
+
await handleActionResult(nextResult);
|
|
847
371
|
}
|
|
848
372
|
}
|
|
849
|
-
else if (result.
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
setStatusMessage(`Popup error: ${result.error}`);
|
|
855
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
373
|
+
else if (!confirmed && result.onCancel) {
|
|
374
|
+
const nextResult = await result.onCancel();
|
|
375
|
+
if (nextResult) {
|
|
376
|
+
await handleActionResult(nextResult);
|
|
377
|
+
}
|
|
856
378
|
}
|
|
857
379
|
}
|
|
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);
|
|
380
|
+
else if (result.type === "choice") {
|
|
381
|
+
if (!result.options || !result.onSelect)
|
|
894
382
|
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");
|
|
931
|
-
}
|
|
932
|
-
else {
|
|
933
|
-
setStatusMessage("Merge complete");
|
|
383
|
+
const selectedId = await popupManager.launchChoicePopup(result.title || "Choose Option", result.message, result.options);
|
|
384
|
+
if (selectedId) {
|
|
385
|
+
const nextResult = await result.onSelect(selectedId);
|
|
386
|
+
// Recursively handle nested results
|
|
387
|
+
if (nextResult) {
|
|
388
|
+
await handleActionResult(nextResult);
|
|
934
389
|
}
|
|
935
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
936
|
-
}
|
|
937
|
-
else if (result.cancelled) {
|
|
938
|
-
// User cancelled
|
|
939
|
-
return;
|
|
940
|
-
}
|
|
941
|
-
else if (result.error) {
|
|
942
|
-
setStatusMessage(`Merge error: ${result.error}`);
|
|
943
|
-
setTimeout(() => setStatusMessage(""), 3000);
|
|
944
390
|
}
|
|
945
391
|
}
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
return null;
|
|
957
|
-
}
|
|
958
|
-
try {
|
|
959
|
-
// Resolve the popup script path
|
|
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;
|
|
392
|
+
else if (result.type === "input") {
|
|
393
|
+
if (!result.onSubmit)
|
|
394
|
+
return;
|
|
395
|
+
const inputValue = await popupManager.launchInputPopup(result.title || "Input", result.message, result.placeholder, result.defaultValue);
|
|
396
|
+
if (inputValue !== null) {
|
|
397
|
+
const nextResult = await result.onSubmit(inputValue);
|
|
398
|
+
// Recursively handle nested results
|
|
399
|
+
if (nextResult) {
|
|
400
|
+
await handleActionResult(nextResult);
|
|
401
|
+
}
|
|
993
402
|
}
|
|
994
403
|
}
|
|
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;
|
|
404
|
+
else if (result.type === "navigation") {
|
|
405
|
+
// Navigate to target pane if specified
|
|
406
|
+
if (result.targetPaneId) {
|
|
407
|
+
const targetPane = panes.find(p => p.id === result.targetPaneId);
|
|
408
|
+
if (targetPane) {
|
|
409
|
+
try {
|
|
410
|
+
TmuxService.getInstance().selectPane(targetPane.paneId);
|
|
411
|
+
}
|
|
412
|
+
catch (error) {
|
|
413
|
+
console.error('[onActionResult] Failed to navigate to pane:', error);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
1048
416
|
}
|
|
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);
|
|
417
|
+
// Show message if dismissable
|
|
418
|
+
if (result.message && result.dismissable) {
|
|
419
|
+
await popupManager.launchProgressPopup(result.message, "info", 3000);
|
|
1096
420
|
}
|
|
1097
|
-
catch { }
|
|
1098
421
|
}
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
422
|
+
else if (result.type === "info" ||
|
|
423
|
+
result.type === "success" ||
|
|
424
|
+
result.type === "error") {
|
|
425
|
+
// Use toast notification instead of popup for better UX
|
|
426
|
+
const { default: stateManager } = await import("./shared/StateManager.js");
|
|
427
|
+
stateManager.showToast(result.message, result.type);
|
|
1103
428
|
}
|
|
1104
429
|
};
|
|
1105
|
-
// Action system - initialized after
|
|
430
|
+
// Action system - initialized after services are defined
|
|
1106
431
|
const actionSystem = useActionSystem({
|
|
1107
432
|
panes,
|
|
1108
433
|
savePanes,
|
|
1109
434
|
sessionName,
|
|
1110
435
|
projectName,
|
|
1111
|
-
onPaneRemove: (paneId) => {
|
|
1112
|
-
// Mark
|
|
1113
|
-
|
|
436
|
+
onPaneRemove: async (paneId) => {
|
|
437
|
+
// Mark pane as closing to prevent race condition with worker
|
|
438
|
+
await lifecycleManager.beginClose(paneId, 'user requested');
|
|
1114
439
|
// Remove from panes list
|
|
1115
440
|
const updatedPanes = panes.filter((p) => p.paneId !== paneId);
|
|
1116
441
|
savePanes(updatedPanes);
|
|
1117
|
-
//
|
|
1118
|
-
|
|
1119
|
-
intentionallyClosedPanes.current.delete(paneId);
|
|
1120
|
-
}, 5000);
|
|
442
|
+
// Mark close as completed (no more lock needed)
|
|
443
|
+
await lifecycleManager.completeClose(paneId);
|
|
1121
444
|
},
|
|
445
|
+
onActionResult: handleActionResult,
|
|
1122
446
|
forceRepaint,
|
|
1123
447
|
popupLaunchers: popupsSupported
|
|
1124
448
|
? {
|
|
1125
|
-
launchConfirmPopup,
|
|
1126
|
-
launchChoicePopup,
|
|
1127
|
-
launchInputPopup,
|
|
1128
|
-
launchProgressPopup,
|
|
449
|
+
launchConfirmPopup: popupManager.launchConfirmPopup.bind(popupManager),
|
|
450
|
+
launchChoicePopup: popupManager.launchChoicePopup.bind(popupManager),
|
|
451
|
+
launchInputPopup: popupManager.launchInputPopup.bind(popupManager),
|
|
452
|
+
launchProgressPopup: popupManager.launchProgressPopup.bind(popupManager),
|
|
1129
453
|
}
|
|
1130
454
|
: undefined,
|
|
1131
455
|
});
|
|
1132
456
|
// Auto-show new pane dialog removed - users can press 'n' to create panes via popup
|
|
1133
457
|
// 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
|
-
}, [
|
|
458
|
+
useLayoutManagement({
|
|
1191
459
|
controlPaneId,
|
|
1192
|
-
actionSystem.actionState.showConfirmDialog
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
460
|
+
hasActiveDialog: actionSystem.actionState.showConfirmDialog ||
|
|
461
|
+
actionSystem.actionState.showChoiceDialog ||
|
|
462
|
+
actionSystem.actionState.showInputDialog ||
|
|
463
|
+
actionSystem.actionState.showProgressDialog ||
|
|
464
|
+
!!showCommandPrompt ||
|
|
465
|
+
showFileCopyPrompt ||
|
|
466
|
+
isCreatingPane ||
|
|
467
|
+
runningCommand ||
|
|
468
|
+
isUpdating,
|
|
469
|
+
onForceRepaint: forceRepaint,
|
|
470
|
+
});
|
|
1202
471
|
// Monitor agent status across panes (returns a map of pane ID to status)
|
|
1203
472
|
const agentStatuses = useAgentStatus({
|
|
1204
473
|
panes,
|
|
@@ -1209,9 +478,9 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
1209
478
|
!!showCommandPrompt ||
|
|
1210
479
|
showFileCopyPrompt,
|
|
1211
480
|
onPaneRemoved: (paneId) => {
|
|
1212
|
-
// Check if this pane
|
|
481
|
+
// Check if this pane is being closed intentionally or is locked
|
|
1213
482
|
// If so, don't re-save - the close action already handled it
|
|
1214
|
-
if (
|
|
483
|
+
if (lifecycleManager.isClosing(paneId) || lifecycleManager.isLocked(paneId)) {
|
|
1215
484
|
return;
|
|
1216
485
|
}
|
|
1217
486
|
// Pane was removed unexpectedly (e.g., user killed tmux pane manually)
|
|
@@ -1220,415 +489,9 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
1220
489
|
savePanes(updatedPanes);
|
|
1221
490
|
},
|
|
1222
491
|
});
|
|
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
|
-
};
|
|
492
|
+
// jumpToPane and runCommand functions removed - now handled by action system and pane runner
|
|
1612
493
|
// 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
|
-
};
|
|
494
|
+
// clearScreen function removed - no longer used (was only used by removed jumpToPane function)
|
|
1632
495
|
// Cleanup function for exit
|
|
1633
496
|
const cleanExit = () => {
|
|
1634
497
|
// Clear screen before exiting Ink
|
|
@@ -1636,15 +499,16 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
1636
499
|
// Exit the Ink app (this cleans up the React tree)
|
|
1637
500
|
exit();
|
|
1638
501
|
// Give Ink a moment to clean up its rendering, then do final cleanup
|
|
1639
|
-
setTimeout(() => {
|
|
502
|
+
setTimeout(async () => {
|
|
1640
503
|
// Multiple aggressive clearing strategies
|
|
1641
504
|
process.stdout.write("\x1b[2J\x1b[H"); // Clear screen and move cursor to home
|
|
1642
505
|
process.stdout.write("\x1b[3J"); // Clear scrollback buffer
|
|
1643
506
|
process.stdout.write("\x1b[0m"); // Reset all attributes
|
|
1644
507
|
// Clear tmux history and pane
|
|
1645
508
|
try {
|
|
1646
|
-
|
|
1647
|
-
|
|
509
|
+
const tmuxService = TmuxService.getInstance();
|
|
510
|
+
tmuxService.clearHistorySync();
|
|
511
|
+
await tmuxService.sendKeys("", "C-l");
|
|
1648
512
|
}
|
|
1649
513
|
catch { }
|
|
1650
514
|
// One more final clear
|
|
@@ -1655,250 +519,53 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
1655
519
|
process.exit(0);
|
|
1656
520
|
}, 100);
|
|
1657
521
|
};
|
|
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
|
-
}
|
|
522
|
+
// Input handling - extracted to dedicated hook
|
|
523
|
+
useInputHandling({
|
|
524
|
+
panes,
|
|
525
|
+
selectedIndex,
|
|
526
|
+
setSelectedIndex,
|
|
527
|
+
isCreatingPane,
|
|
528
|
+
setIsCreatingPane,
|
|
529
|
+
runningCommand,
|
|
530
|
+
isUpdating,
|
|
531
|
+
isLoading,
|
|
532
|
+
ignoreInput,
|
|
533
|
+
quitConfirmMode,
|
|
534
|
+
setQuitConfirmMode,
|
|
535
|
+
showCommandPrompt,
|
|
536
|
+
setShowCommandPrompt,
|
|
537
|
+
commandInput,
|
|
538
|
+
setCommandInput,
|
|
539
|
+
showFileCopyPrompt,
|
|
540
|
+
setShowFileCopyPrompt,
|
|
541
|
+
currentCommandType,
|
|
542
|
+
setCurrentCommandType,
|
|
543
|
+
projectSettings,
|
|
544
|
+
saveSettings,
|
|
545
|
+
settingsManager,
|
|
546
|
+
tunnelUrl,
|
|
547
|
+
setTunnelUrl,
|
|
548
|
+
tunnelCreating,
|
|
549
|
+
setTunnelCreating,
|
|
550
|
+
setTunnelCopied,
|
|
551
|
+
popupManager,
|
|
552
|
+
actionSystem,
|
|
553
|
+
server,
|
|
554
|
+
controlPaneId,
|
|
555
|
+
setStatusMessage,
|
|
556
|
+
copyNonGitFiles,
|
|
557
|
+
runCommandInternal,
|
|
558
|
+
handlePaneCreationWithAgent,
|
|
559
|
+
loadPanes,
|
|
560
|
+
cleanExit,
|
|
561
|
+
findCardInDirection,
|
|
1896
562
|
});
|
|
1897
563
|
// Calculate available height for content (terminal height - footer lines - active status messages)
|
|
1898
564
|
// Footer height varies based on state:
|
|
1899
565
|
// - Quit confirm mode: 2 lines (marginTop + 1 text line)
|
|
1900
566
|
// - Normal mode calculation:
|
|
1901
567
|
// - Base: 4 lines (marginTop + logs divider + logs line + keyboard shortcuts)
|
|
568
|
+
// - Toast: +2 lines (toast message + marginBottom) if currentToast exists
|
|
1902
569
|
// - Network section: +4 lines (divider, local IP, remote tunnel, divider) if serverPort exists
|
|
1903
570
|
// - Debug info: +1 line if DEBUG_DMUX
|
|
1904
571
|
// - Status line: +1 line if updateAvailable/currentBranch/debugMessage
|
|
@@ -1910,6 +577,17 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
1910
577
|
else {
|
|
1911
578
|
// Base footer (logs divider + logs + shortcuts - always shown)
|
|
1912
579
|
footerLines = 4; // marginTop + logs divider + logs + shortcuts
|
|
580
|
+
// Add toast notification (calculate wrapped lines + header)
|
|
581
|
+
if (currentToast) {
|
|
582
|
+
// Calculate how many lines the toast will take
|
|
583
|
+
// Toast format: "✓ message" - icon (1) + space (1) + message
|
|
584
|
+
const iconAndSpaceLength = 2;
|
|
585
|
+
const toastTextLength = iconAndSpaceLength + currentToast.message.length;
|
|
586
|
+
// Available width is sidebar width (40) minus padding/margins (~2)
|
|
587
|
+
const availableWidth = SIDEBAR_WIDTH - 2;
|
|
588
|
+
const wrappedLines = Math.ceil(toastTextLength / availableWidth);
|
|
589
|
+
footerLines += wrappedLines + 1 + 1; // wrapped lines + header line + marginBottom
|
|
590
|
+
}
|
|
1913
591
|
// Add network section (now 2 lines for local IP + remote tunnel, plus 2 dividers)
|
|
1914
592
|
if (serverPort && serverPort > 0) {
|
|
1915
593
|
footerLines += 4;
|
|
@@ -1949,21 +627,7 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
1949
627
|
: actionSystem.actionState.statusType === "success"
|
|
1950
628
|
? "green"
|
|
1951
629
|
: "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: (() => {
|
|
630
|
+
React.createElement(FooterHelp, { show: !showCommandPrompt, showRemoteKey: !!server, quitConfirmMode: quitConfirmMode, hasSidebarLayout: !!controlPaneId, serverPort: serverPort, unreadErrorCount: unreadErrorCount, unreadWarningCount: unreadWarningCount, currentToast: currentToast, toastQueueLength: toastQueueLength, toastQueuePosition: toastQueuePosition, localIp: localIp, tunnelUrl: tunnelUrl, tunnelCreating: tunnelCreating, tunnelCopied: tunnelCopied, tunnelSpinner: tunnelState.getSpinnerChar(), gridInfo: (() => {
|
|
1967
631
|
if (!process.env.DEBUG_DMUX)
|
|
1968
632
|
return undefined;
|
|
1969
633
|
const cols = Math.max(1, Math.floor(terminalWidth / 37));
|
|
@@ -1975,8 +639,8 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
1975
639
|
updateAvailable && updateInfo && (React.createElement(Text, { color: "red", bold: true },
|
|
1976
640
|
"Update available: npm i -g dmux@latest",
|
|
1977
641
|
" ")),
|
|
1978
|
-
currentBranch && (React.createElement(Text, { color: "magenta"
|
|
1979
|
-
"
|
|
642
|
+
currentBranch && (React.createElement(Text, { color: "magenta" },
|
|
643
|
+
"dev: ",
|
|
1980
644
|
currentBranch)),
|
|
1981
645
|
debugMessage && React.createElement(Text, { dimColor: true },
|
|
1982
646
|
" \u2022 ",
|