dmux 5.6.3 → 5.7.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/README.ja.md +93 -0
- package/README.md +16 -1
- package/dist/DmuxApp.d.ts.map +1 -1
- package/dist/DmuxApp.js +282 -43
- package/dist/DmuxApp.js.map +1 -1
- package/dist/actions/implementations/closeAction.d.ts.map +1 -1
- package/dist/actions/implementations/closeAction.js +85 -70
- package/dist/actions/implementations/closeAction.js.map +1 -1
- package/dist/actions/implementations/createPullRequestAction.d.ts +4 -0
- package/dist/actions/implementations/createPullRequestAction.d.ts.map +1 -0
- package/dist/actions/implementations/createPullRequestAction.js +218 -0
- package/dist/actions/implementations/createPullRequestAction.js.map +1 -0
- package/dist/actions/implementations/index.d.ts +1 -0
- package/dist/actions/implementations/index.d.ts.map +1 -1
- package/dist/actions/implementations/index.js +1 -0
- package/dist/actions/implementations/index.js.map +1 -1
- package/dist/actions/implementations/mergeAction.js +8 -3
- package/dist/actions/implementations/mergeAction.js.map +1 -1
- package/dist/actions/index.d.ts.map +1 -1
- package/dist/actions/index.js +2 -0
- package/dist/actions/index.js.map +1 -1
- package/dist/actions/paneActions.d.ts +1 -1
- package/dist/actions/paneActions.d.ts.map +1 -1
- package/dist/actions/paneActions.js +1 -1
- package/dist/actions/paneActions.js.map +1 -1
- package/dist/actions/types.d.ts +11 -2
- package/dist/actions/types.d.ts.map +1 -1
- package/dist/actions/types.js +9 -0
- package/dist/actions/types.js.map +1 -1
- package/dist/components/inputs/CleanTextInput.d.ts +1 -0
- package/dist/components/inputs/CleanTextInput.d.ts.map +1 -1
- package/dist/components/inputs/CleanTextInput.js +40 -42
- package/dist/components/inputs/CleanTextInput.js.map +1 -1
- package/dist/components/inputs/InlineCursorInput.d.ts +10 -0
- package/dist/components/inputs/InlineCursorInput.d.ts.map +1 -0
- package/dist/components/inputs/InlineCursorInput.js +101 -0
- package/dist/components/inputs/InlineCursorInput.js.map +1 -0
- package/dist/components/panes/PaneCard.d.ts +3 -1
- package/dist/components/panes/PaneCard.d.ts.map +1 -1
- package/dist/components/panes/PaneCard.js +14 -4
- package/dist/components/panes/PaneCard.js.map +1 -1
- package/dist/components/panes/PanesGrid.d.ts +4 -1
- package/dist/components/panes/PanesGrid.d.ts.map +1 -1
- package/dist/components/panes/PanesGrid.js +27 -15
- package/dist/components/panes/PanesGrid.js.map +1 -1
- package/dist/components/popups/config.d.ts +4 -4
- package/dist/components/popups/config.js +6 -6
- package/dist/components/popups/config.js.map +1 -1
- package/dist/components/popups/inputPopup.js +3 -3
- package/dist/components/popups/inputPopup.js.map +1 -1
- package/dist/components/popups/newPaneFieldNavigation.d.ts +9 -0
- package/dist/components/popups/newPaneFieldNavigation.d.ts.map +1 -0
- package/dist/components/popups/newPaneFieldNavigation.js +20 -0
- package/dist/components/popups/newPaneFieldNavigation.js.map +1 -0
- package/dist/components/popups/newPaneGitOptions.d.ts +23 -0
- package/dist/components/popups/newPaneGitOptions.d.ts.map +1 -0
- package/dist/components/popups/newPaneGitOptions.js +91 -0
- package/dist/components/popups/newPaneGitOptions.js.map +1 -0
- package/dist/components/popups/newPanePopup.d.ts +7 -2
- package/dist/components/popups/newPanePopup.d.ts.map +1 -1
- package/dist/components/popups/newPanePopup.js +256 -19
- package/dist/components/popups/newPanePopup.js.map +1 -1
- package/dist/components/popups/prReviewPopup.d.ts +12 -0
- package/dist/components/popups/prReviewPopup.d.ts.map +1 -0
- package/dist/components/popups/prReviewPopup.js +278 -0
- package/dist/components/popups/prReviewPopup.js.map +1 -0
- package/dist/components/popups/reopenWorktreePopup.js +1 -1
- package/dist/components/popups/reopenWorktreePopup.js.map +1 -1
- package/dist/components/popups/settingsPopup.js +72 -6
- package/dist/components/popups/settingsPopup.js.map +1 -1
- package/dist/components/ui/FooterHelp.d.ts.map +1 -1
- package/dist/components/ui/FooterHelp.js +5 -4
- package/dist/components/ui/FooterHelp.js.map +1 -1
- package/dist/hooks/useActionSystem.d.ts +12 -2
- package/dist/hooks/useActionSystem.d.ts.map +1 -1
- package/dist/hooks/useActionSystem.js +19 -1
- package/dist/hooks/useActionSystem.js.map +1 -1
- package/dist/hooks/useInputHandling.d.ts +5 -3
- package/dist/hooks/useInputHandling.d.ts.map +1 -1
- package/dist/hooks/useInputHandling.js +93 -32
- package/dist/hooks/useInputHandling.js.map +1 -1
- package/dist/hooks/usePaneCreation.d.ts +5 -3
- package/dist/hooks/usePaneCreation.d.ts.map +1 -1
- package/dist/hooks/usePaneCreation.js +16 -3
- package/dist/hooks/usePaneCreation.js.map +1 -1
- package/dist/hooks/usePaneLoading.d.ts.map +1 -1
- package/dist/hooks/usePaneLoading.js +28 -3
- package/dist/hooks/usePaneLoading.js.map +1 -1
- package/dist/hooks/usePaneSync.d.ts.map +1 -1
- package/dist/hooks/usePaneSync.js +8 -5
- package/dist/hooks/usePaneSync.js.map +1 -1
- package/dist/hooks/usePanes.d.ts.map +1 -1
- package/dist/hooks/usePanes.js +8 -1
- package/dist/hooks/usePanes.js.map +1 -1
- package/dist/hooks/useShellDetection.d.ts.map +1 -1
- package/dist/hooks/useShellDetection.js +7 -1
- package/dist/hooks/useShellDetection.js.map +1 -1
- package/dist/index.js +49 -22
- package/dist/index.js.map +1 -1
- package/dist/panes/decorative-pane.js +57 -27
- package/dist/panes/decorative-pane.js.map +1 -1
- package/dist/server/embedded-assets.d.ts.map +1 -1
- package/dist/server/embedded-assets.js +4 -12
- package/dist/server/embedded-assets.js.map +1 -1
- package/dist/services/DmuxFocusService.d.ts.map +1 -1
- package/dist/services/DmuxFocusService.js +19 -14
- package/dist/services/DmuxFocusService.js.map +1 -1
- package/dist/services/PaneEventService.d.ts +11 -0
- package/dist/services/PaneEventService.d.ts.map +1 -1
- package/dist/services/PaneEventService.js +31 -3
- package/dist/services/PaneEventService.js.map +1 -1
- package/dist/services/PaneWorkerManager.d.ts.map +1 -1
- package/dist/services/PaneWorkerManager.js +1 -0
- package/dist/services/PaneWorkerManager.js.map +1 -1
- package/dist/services/PopupManager.d.ts +23 -6
- package/dist/services/PopupManager.d.ts.map +1 -1
- package/dist/services/PopupManager.js +170 -33
- package/dist/services/PopupManager.js.map +1 -1
- package/dist/services/StatusDetector.d.ts +1 -0
- package/dist/services/StatusDetector.d.ts.map +1 -1
- package/dist/services/StatusDetector.js +44 -0
- package/dist/services/StatusDetector.js.map +1 -1
- package/dist/services/TmuxHookManager.d.ts.map +1 -1
- package/dist/services/TmuxHookManager.js +3 -2
- package/dist/services/TmuxHookManager.js.map +1 -1
- package/dist/services/TmuxService.d.ts +7 -0
- package/dist/services/TmuxService.d.ts.map +1 -1
- package/dist/services/TmuxService.js +15 -1
- package/dist/services/TmuxService.js.map +1 -1
- package/dist/theme/colors.d.ts +24 -10
- package/dist/theme/colors.d.ts.map +1 -1
- package/dist/theme/colors.js +143 -19
- package/dist/theme/colors.js.map +1 -1
- package/dist/theme/themePalette.d.ts +6 -0
- package/dist/theme/themePalette.d.ts.map +1 -0
- package/dist/theme/themePalette.js +18 -0
- package/dist/theme/themePalette.js.map +1 -0
- package/dist/types.d.ts +13 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/agentLaunch.d.ts +2 -0
- package/dist/utils/agentLaunch.d.ts.map +1 -1
- package/dist/utils/agentLaunch.js +10 -9
- package/dist/utils/agentLaunch.js.map +1 -1
- package/dist/utils/aiMerge.d.ts +4 -0
- package/dist/utils/aiMerge.d.ts.map +1 -1
- package/dist/utils/aiMerge.js +7 -4
- package/dist/utils/aiMerge.js.map +1 -1
- package/dist/utils/asciiArt.d.ts.map +1 -1
- package/dist/utils/asciiArt.js +2 -1
- package/dist/utils/asciiArt.js.map +1 -1
- package/dist/utils/attachAgent.d.ts.map +1 -1
- package/dist/utils/attachAgent.js +20 -1
- package/dist/utils/attachAgent.js.map +1 -1
- package/dist/utils/codexHooks.d.ts +25 -0
- package/dist/utils/codexHooks.d.ts.map +1 -0
- package/dist/utils/codexHooks.js +131 -0
- package/dist/utils/codexHooks.js.map +1 -0
- package/dist/utils/conflictResolutionPane.d.ts.map +1 -1
- package/dist/utils/conflictResolutionPane.js +4 -0
- package/dist/utils/conflictResolutionPane.js.map +1 -1
- package/dist/utils/devWatchCommand.d.ts.map +1 -1
- package/dist/utils/devWatchCommand.js +6 -2
- package/dist/utils/devWatchCommand.js.map +1 -1
- package/dist/utils/dmuxCommand.d.ts +4 -3
- package/dist/utils/dmuxCommand.d.ts.map +1 -1
- package/dist/utils/dmuxCommand.js +15 -14
- package/dist/utils/dmuxCommand.js.map +1 -1
- package/dist/utils/generated-agents-doc.d.ts +1 -1
- package/dist/utils/generated-agents-doc.d.ts.map +1 -1
- package/dist/utils/generated-agents-doc.js +46 -11
- package/dist/utils/generated-agents-doc.js.map +1 -1
- package/dist/utils/git.d.ts +8 -0
- package/dist/utils/git.d.ts.map +1 -1
- package/dist/utils/git.js +26 -0
- package/dist/utils/git.js.map +1 -1
- package/dist/utils/githubPullRequest.d.ts +18 -0
- package/dist/utils/githubPullRequest.d.ts.map +1 -0
- package/dist/utils/githubPullRequest.js +196 -0
- package/dist/utils/githubPullRequest.js.map +1 -0
- package/dist/utils/hooks.d.ts +15 -0
- package/dist/utils/hooks.d.ts.map +1 -1
- package/dist/utils/hooks.js +105 -0
- package/dist/utils/hooks.js.map +1 -1
- package/dist/utils/hooksDocs.d.ts +1 -1
- package/dist/utils/hooksDocs.d.ts.map +1 -1
- package/dist/utils/hooksDocs.js +21 -11
- package/dist/utils/hooksDocs.js.map +1 -1
- package/dist/utils/input.d.ts +7 -6
- package/dist/utils/input.d.ts.map +1 -1
- package/dist/utils/input.js +47 -24
- package/dist/utils/input.js.map +1 -1
- package/dist/utils/paneBootstrapConfig.d.ts +27 -0
- package/dist/utils/paneBootstrapConfig.d.ts.map +1 -0
- package/dist/utils/paneBootstrapConfig.js +2 -0
- package/dist/utils/paneBootstrapConfig.js.map +1 -0
- package/dist/utils/paneBootstrapRunner.d.ts +3 -0
- package/dist/utils/paneBootstrapRunner.d.ts.map +1 -0
- package/dist/utils/paneBootstrapRunner.js +517 -0
- package/dist/utils/paneBootstrapRunner.js.map +1 -0
- package/dist/utils/paneColors.d.ts +7 -0
- package/dist/utils/paneColors.d.ts.map +1 -0
- package/dist/utils/paneColors.js +42 -0
- package/dist/utils/paneColors.js.map +1 -0
- package/dist/utils/paneCreation.d.ts +3 -1
- package/dist/utils/paneCreation.d.ts.map +1 -1
- package/dist/utils/paneCreation.js +165 -225
- package/dist/utils/paneCreation.js.map +1 -1
- package/dist/utils/paneNaming.d.ts +16 -0
- package/dist/utils/paneNaming.d.ts.map +1 -0
- package/dist/utils/paneNaming.js +47 -0
- package/dist/utils/paneNaming.js.map +1 -0
- package/dist/utils/paneTitle.d.ts +1 -0
- package/dist/utils/paneTitle.d.ts.map +1 -1
- package/dist/utils/paneTitle.js +5 -1
- package/dist/utils/paneTitle.js.map +1 -1
- package/dist/utils/paneTitlePrefix.d.ts +8 -0
- package/dist/utils/paneTitlePrefix.d.ts.map +1 -0
- package/dist/utils/paneTitlePrefix.js +20 -0
- package/dist/utils/paneTitlePrefix.js.map +1 -0
- package/dist/utils/pathEnvironment.d.ts +6 -0
- package/dist/utils/pathEnvironment.d.ts.map +1 -0
- package/dist/utils/pathEnvironment.js +59 -0
- package/dist/utils/pathEnvironment.js.map +1 -0
- package/dist/utils/popup.d.ts +1 -0
- package/dist/utils/popup.d.ts.map +1 -1
- package/dist/utils/popup.js +9 -2
- package/dist/utils/popup.js.map +1 -1
- package/dist/utils/prSummary.d.ts +34 -0
- package/dist/utils/prSummary.d.ts.map +1 -0
- package/dist/utils/prSummary.js +130 -0
- package/dist/utils/prSummary.js.map +1 -0
- package/dist/utils/projectActions.d.ts +6 -0
- package/dist/utils/projectActions.d.ts.map +1 -1
- package/dist/utils/projectActions.js +46 -0
- package/dist/utils/projectActions.js.map +1 -1
- package/dist/utils/reopenWorktree.d.ts.map +1 -1
- package/dist/utils/reopenWorktree.js +37 -5
- package/dist/utils/reopenWorktree.js.map +1 -1
- package/dist/utils/resumeBranches.d.ts.map +1 -1
- package/dist/utils/resumeBranches.js +9 -3
- package/dist/utils/resumeBranches.js.map +1 -1
- package/dist/utils/settingsManager.d.ts +11 -3
- package/dist/utils/settingsManager.d.ts.map +1 -1
- package/dist/utils/settingsManager.js +106 -21
- package/dist/utils/settingsManager.js.map +1 -1
- package/dist/utils/shellPaneDetection.d.ts.map +1 -1
- package/dist/utils/shellPaneDetection.js +4 -0
- package/dist/utils/shellPaneDetection.js.map +1 -1
- package/dist/utils/sidebarProjects.d.ts +10 -3
- package/dist/utils/sidebarProjects.d.ts.map +1 -1
- package/dist/utils/sidebarProjects.js +125 -10
- package/dist/utils/sidebarProjects.js.map +1 -1
- package/dist/utils/tmuxHookCommands.d.ts +6 -0
- package/dist/utils/tmuxHookCommands.d.ts.map +1 -1
- package/dist/utils/tmuxHookCommands.js +12 -0
- package/dist/utils/tmuxHookCommands.js.map +1 -1
- package/dist/utils/tmuxSendKeys.d.ts +2 -0
- package/dist/utils/tmuxSendKeys.d.ts.map +1 -0
- package/dist/utils/tmuxSendKeys.js +8 -0
- package/dist/utils/tmuxSendKeys.js.map +1 -0
- package/dist/utils/toastLayout.d.ts +10 -0
- package/dist/utils/toastLayout.d.ts.map +1 -0
- package/dist/utils/toastLayout.js +24 -0
- package/dist/utils/toastLayout.js.map +1 -0
- package/dist/utils/welcomePane.d.ts +5 -1
- package/dist/utils/welcomePane.d.ts.map +1 -1
- package/dist/utils/welcomePane.js +33 -3
- package/dist/utils/welcomePane.js.map +1 -1
- package/dist/utils/welcomePaneManager.d.ts +3 -1
- package/dist/utils/welcomePaneManager.d.ts.map +1 -1
- package/dist/utils/welcomePaneManager.js +40 -2
- package/dist/utils/welcomePaneManager.js.map +1 -1
- package/dist/workers/PaneWorker.js +77 -0
- package/dist/workers/PaneWorker.js.map +1 -1
- package/dist/workers/WorkerMessages.d.ts +9 -1
- package/dist/workers/WorkerMessages.d.ts.map +1 -1
- package/dist/workers/WorkerMessages.js.map +1 -1
- package/dist/workers/panePollingWorker.js +33 -2
- package/dist/workers/panePollingWorker.js.map +1 -1
- package/package.json +1 -1
package/dist/DmuxApp.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import React, { useState, useEffect, useMemo } from "react";
|
|
1
|
+
import React, { useState, useEffect, useMemo, useRef } from "react";
|
|
2
2
|
import { Box, Text, useApp, useStdout, useInput } from "ink";
|
|
3
|
+
import stringWidth from "string-width";
|
|
3
4
|
import { TmuxService } from "./services/TmuxService.js";
|
|
4
5
|
// Hooks
|
|
5
6
|
import usePanes from "./hooks/usePanes.js";
|
|
@@ -43,14 +44,21 @@ import { getPaneDisplayName } from "./utils/paneTitle.js";
|
|
|
43
44
|
import { FOOTER_TIP_ROTATION_INTERVAL, getFooterTips, getNextFooterTipIndex, getRandomFooterTipIndex, } from "./utils/footerTips.js";
|
|
44
45
|
const __filename = fileURLToPath(import.meta.url);
|
|
45
46
|
const __dirname = dirname(__filename);
|
|
47
|
+
const ACTIVE_PANE_SYNC_INTERVAL_MS = 125;
|
|
46
48
|
import PanesGrid from "./components/panes/PanesGrid.js";
|
|
47
49
|
import CommandPromptDialog from "./components/dialogs/CommandPromptDialog.js";
|
|
48
50
|
import FileCopyPrompt from "./components/ui/FileCopyPrompt.js";
|
|
49
51
|
import FooterHelp from "./components/ui/FooterHelp.js";
|
|
50
52
|
import TmuxHooksPromptDialog from "./components/dialogs/TmuxHooksPromptDialog.js";
|
|
51
53
|
import { PaneEventService } from "./services/PaneEventService.js";
|
|
52
|
-
import { buildProjectActionLayout, buildVisualNavigationRows, buildGroupStartRows, getProjectActionByIndex, } from "./utils/projectActions.js";
|
|
54
|
+
import { buildProjectActionLayout, buildVisualNavigationRows, buildGroupStartRows, getProjectActionByIndex, resolveSelectionAfterPaneClose, } from "./utils/projectActions.js";
|
|
53
55
|
import { getPaneProjectRoot } from "./utils/paneProject.js";
|
|
56
|
+
import { applyDmuxTheme, getDmuxThemePalette, } from "./theme/colors.js";
|
|
57
|
+
import { applyTmuxThemeToSession, refreshWelcomePaneTheme, } from "./utils/welcomePane.js";
|
|
58
|
+
import { syncWelcomePaneVisibility } from "./utils/welcomePaneManager.js";
|
|
59
|
+
import { getPaneColorTheme, resolveProjectColorTheme, } from "./utils/paneColors.js";
|
|
60
|
+
import { getPaneTitlePrefixValue, paneNeedsAnimatedTitlePrefix, PANE_TITLE_BUSY_FRAMES, } from "./utils/paneTitlePrefix.js";
|
|
61
|
+
import { getPaneTmuxDisplayTitle } from "./utils/paneTitle.js";
|
|
54
62
|
const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoot, autoUpdater, controlPaneId, }) => {
|
|
55
63
|
const { stdout } = useStdout();
|
|
56
64
|
const terminalHeight = stdout?.rows || 40;
|
|
@@ -58,13 +66,19 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
58
66
|
const sessionProjectRoot = projectRoot || process.cwd();
|
|
59
67
|
/* panes state moved to usePanes */
|
|
60
68
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
69
|
+
const [focusedPaneId, setFocusedPaneId] = useState(null);
|
|
61
70
|
const { statusMessage, setStatusMessage } = useStatusMessages();
|
|
62
71
|
const [isCreatingPane, setIsCreatingPane] = useState(false);
|
|
63
72
|
const { trackProjectActivity, isProjectBusy: isTrackedProjectBusy, } = useProjectActivity(sessionProjectRoot);
|
|
64
73
|
// Settings state
|
|
65
74
|
const [settingsManager] = useState(() => new SettingsManager(projectRoot));
|
|
66
75
|
const { projectSettings, saveSettings } = useProjectSettings(settingsFile);
|
|
67
|
-
const
|
|
76
|
+
const [themeRefreshNonce, setThemeRefreshNonce] = useState(0);
|
|
77
|
+
const [settings, setSettings] = useState(() => new SettingsManager(sessionProjectRoot).getSettings());
|
|
78
|
+
const paneTitlePrefixCacheRef = useRef(new Map());
|
|
79
|
+
const paneTitleLabelCacheRef = useRef(new Map());
|
|
80
|
+
const paneActiveBorderStyleCacheRef = useRef(new Map());
|
|
81
|
+
const paneTitleSpinnerFrameRef = useRef(0);
|
|
68
82
|
// Dialog state management
|
|
69
83
|
const dialogState = useDialogState();
|
|
70
84
|
const { showCommandPrompt, setShowCommandPrompt, commandInput, setCommandInput, showFileCopyPrompt, setShowFileCopyPrompt, currentCommandType, setCurrentCommandType, runningCommand, setRunningCommand, quitConfirmMode, setQuitConfirmMode, } = dialogState;
|
|
@@ -78,6 +92,7 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
78
92
|
// Agent selection is settings-driven.
|
|
79
93
|
// Installation checks are performed lazily in the Enabled Agents settings popup.
|
|
80
94
|
const availableAgents = resolveEnabledAgentsSelection(settings.enabledAgents);
|
|
95
|
+
const getAvailableAgentsForProject = (targetProjectRoot = selectedProjectRoot) => resolveEnabledAgentsSelection(new SettingsManager(targetProjectRoot).getSettings().enabledAgents);
|
|
81
96
|
const footerTips = useMemo(() => getFooterTips(isDevMode), [isDevMode]);
|
|
82
97
|
const showFooterTips = settings.showFooterTips !== false && footerTips.length > 0;
|
|
83
98
|
const [footerTipIndex, setFooterTipIndex] = useState(() => getRandomFooterTipIndex(footerTips.length));
|
|
@@ -133,12 +148,12 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
133
148
|
};
|
|
134
149
|
}, []);
|
|
135
150
|
// Panes state and persistence (skipLoading will be updated after actionSystem is initialized)
|
|
136
|
-
const { panes, setPanes, sidebarProjects, isLoading, loadPanes, savePanes, saveSidebarProjects, } = usePanes(panesFile, false, sessionName, controlPaneId, useHooks);
|
|
151
|
+
const { panes, setPanes, sidebarProjects, isLoading, loadPanes, savePanes, saveSidebarProjects, eventMode, } = usePanes(panesFile, false, sessionName, controlPaneId, useHooks);
|
|
137
152
|
// Check for tmux hooks preference on startup
|
|
138
153
|
useEffect(() => {
|
|
139
154
|
const checkHooksPreference = async () => {
|
|
140
155
|
// Check if user already has a preference
|
|
141
|
-
const settings =
|
|
156
|
+
const settings = new SettingsManager(sessionProjectRoot).getSettings();
|
|
142
157
|
if (settings.useTmuxHooks !== undefined) {
|
|
143
158
|
// User has already decided
|
|
144
159
|
setUseHooks(settings.useTmuxHooks);
|
|
@@ -153,6 +168,7 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
153
168
|
setUseHooks(true);
|
|
154
169
|
// Save the preference
|
|
155
170
|
settingsManager.updateSetting('useTmuxHooks', true, 'global');
|
|
171
|
+
refreshDmuxSettings();
|
|
156
172
|
}
|
|
157
173
|
else {
|
|
158
174
|
// Need to ask user - show prompt
|
|
@@ -347,18 +363,163 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
347
363
|
// getPanePositions moved to utils/tmux
|
|
348
364
|
const activeDevSourcePath = isDevMode ? process.cwd() : undefined;
|
|
349
365
|
const projectActionLayout = useMemo(() => buildProjectActionLayout(panes, sidebarProjects, sessionProjectRoot, projectName), [panes, sidebarProjects, sessionProjectRoot, projectName]);
|
|
366
|
+
const selectedPane = useMemo(() => {
|
|
367
|
+
for (const group of projectActionLayout.groups) {
|
|
368
|
+
const entry = group.panes.find((candidate) => candidate.index === selectedIndex);
|
|
369
|
+
if (entry) {
|
|
370
|
+
return entry.pane;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return undefined;
|
|
374
|
+
}, [projectActionLayout.groups, selectedIndex]);
|
|
350
375
|
const selectedProjectRoot = useMemo(() => {
|
|
351
|
-
const selectedPane = selectedIndex < panes.length ? panes[selectedIndex] : undefined;
|
|
352
376
|
if (selectedPane) {
|
|
353
377
|
return getPaneProjectRoot(selectedPane, sessionProjectRoot);
|
|
354
378
|
}
|
|
355
379
|
return (getProjectActionByIndex(projectActionLayout.actionItems, selectedIndex)?.projectRoot
|
|
356
380
|
|| sessionProjectRoot);
|
|
357
|
-
}, [
|
|
381
|
+
}, [selectedPane, selectedIndex, projectActionLayout.actionItems, sessionProjectRoot]);
|
|
382
|
+
const focusedPane = useMemo(() => focusedPaneId
|
|
383
|
+
? panes.find((pane) => pane.paneId === focusedPaneId)
|
|
384
|
+
: undefined, [focusedPaneId, panes]);
|
|
385
|
+
const activeProjectRoot = selectedProjectRoot;
|
|
386
|
+
const resolveProjectThemeName = React.useCallback((activeProjectRoot) => {
|
|
387
|
+
return resolveProjectColorTheme(activeProjectRoot, sidebarProjects);
|
|
388
|
+
}, [sidebarProjects]);
|
|
389
|
+
const activeBorderPane = focusedPane || selectedPane;
|
|
390
|
+
const activeBorderPaneId = activeBorderPane?.paneId;
|
|
391
|
+
const selectedThemeName = useMemo(() => resolveProjectThemeName(activeProjectRoot), [
|
|
392
|
+
resolveProjectThemeName,
|
|
393
|
+
activeProjectRoot,
|
|
394
|
+
themeRefreshNonce,
|
|
395
|
+
]);
|
|
396
|
+
const visiblePaneCount = useMemo(() => panes.filter((pane) => !pane.hidden).length, [panes]);
|
|
397
|
+
const controlPaneActiveBorderStyle = useMemo(() => `fg=colour${getDmuxThemePalette(selectedThemeName).activeBorder}`, [selectedThemeName]);
|
|
398
|
+
const projectThemeByRoot = useMemo(() => {
|
|
399
|
+
const themeMap = new Map();
|
|
400
|
+
for (const group of projectActionLayout.groups) {
|
|
401
|
+
const paneTheme = group.panes.find((entry) => entry.pane.colorTheme)?.pane.colorTheme;
|
|
402
|
+
themeMap.set(group.projectRoot, paneTheme || resolveProjectThemeName(group.projectRoot));
|
|
403
|
+
}
|
|
404
|
+
return themeMap;
|
|
405
|
+
}, [projectActionLayout.groups, resolveProjectThemeName, themeRefreshNonce]);
|
|
406
|
+
applyDmuxTheme(selectedThemeName);
|
|
407
|
+
const refreshDmuxSettings = (_activeProjectRoot = selectedProjectRoot) => {
|
|
408
|
+
setSettings(new SettingsManager(sessionProjectRoot).getSettings());
|
|
409
|
+
setThemeRefreshNonce((current) => current + 1);
|
|
410
|
+
};
|
|
358
411
|
const navigationRows = useMemo(() => isLoading
|
|
359
412
|
? projectActionLayout.groups.flatMap((group) => group.panes.map((entry) => [entry.index]))
|
|
360
413
|
: buildVisualNavigationRows(projectActionLayout), [isLoading, projectActionLayout]);
|
|
361
414
|
const groupStartRows = useMemo(() => isLoading ? [] : buildGroupStartRows(projectActionLayout), [isLoading, projectActionLayout]);
|
|
415
|
+
useEffect(() => {
|
|
416
|
+
try {
|
|
417
|
+
applyTmuxThemeToSession(sessionName, activeProjectRoot, selectedThemeName);
|
|
418
|
+
}
|
|
419
|
+
catch {
|
|
420
|
+
// Theme updates are best-effort at runtime.
|
|
421
|
+
}
|
|
422
|
+
void refreshWelcomePaneTheme(panesFile, activeProjectRoot, selectedThemeName);
|
|
423
|
+
}, [panesFile, activeProjectRoot, selectedThemeName, sessionName]);
|
|
424
|
+
useEffect(() => {
|
|
425
|
+
if (isLoading) {
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
void syncWelcomePaneVisibility(sessionProjectRoot, controlPaneId, visiblePaneCount === 0, selectedThemeName);
|
|
429
|
+
}, [
|
|
430
|
+
controlPaneId,
|
|
431
|
+
isLoading,
|
|
432
|
+
selectedThemeName,
|
|
433
|
+
sessionProjectRoot,
|
|
434
|
+
visiblePaneCount,
|
|
435
|
+
]);
|
|
436
|
+
useEffect(() => {
|
|
437
|
+
if (!process.env.TMUX) {
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
const tmuxService = TmuxService.getInstance();
|
|
441
|
+
const syncPaneTitlePrefixes = () => {
|
|
442
|
+
const cachedPrefixes = paneTitlePrefixCacheRef.current;
|
|
443
|
+
const cachedLabels = paneTitleLabelCacheRef.current;
|
|
444
|
+
const cachedActiveBorderStyles = paneActiveBorderStyleCacheRef.current;
|
|
445
|
+
const activePaneIds = new Set(panes.map((pane) => pane.paneId));
|
|
446
|
+
const activeBorderStylePaneIds = new Set(activePaneIds);
|
|
447
|
+
if (controlPaneId) {
|
|
448
|
+
activeBorderStylePaneIds.add(controlPaneId);
|
|
449
|
+
}
|
|
450
|
+
for (const paneId of Array.from(cachedPrefixes.keys())) {
|
|
451
|
+
if (!activePaneIds.has(paneId)) {
|
|
452
|
+
tmuxService.unsetPaneOptionSync(paneId, '@dmux_title_prefix');
|
|
453
|
+
cachedPrefixes.delete(paneId);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
for (const paneId of Array.from(cachedLabels.keys())) {
|
|
457
|
+
if (!activePaneIds.has(paneId)) {
|
|
458
|
+
tmuxService.unsetPaneOptionSync(paneId, '@dmux_title_label');
|
|
459
|
+
cachedLabels.delete(paneId);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
for (const paneId of Array.from(cachedActiveBorderStyles.keys())) {
|
|
463
|
+
if (!activeBorderStylePaneIds.has(paneId)) {
|
|
464
|
+
tmuxService.unsetPaneOptionSync(paneId, '@dmux_active_border_style');
|
|
465
|
+
cachedActiveBorderStyles.delete(paneId);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
for (const pane of panes) {
|
|
469
|
+
const paneThemeName = getPaneColorTheme(pane, sidebarProjects, sessionProjectRoot);
|
|
470
|
+
const prefixValue = getPaneTitlePrefixValue(pane, sidebarProjects, sessionProjectRoot, paneTitleSpinnerFrameRef.current);
|
|
471
|
+
const labelValue = getPaneTmuxDisplayTitle(pane, sessionProjectRoot, projectName);
|
|
472
|
+
const activeBorderStyle = `fg=colour${getDmuxThemePalette(paneThemeName).activeBorder}`;
|
|
473
|
+
if (cachedPrefixes.get(pane.paneId) !== prefixValue) {
|
|
474
|
+
tmuxService.setPaneOptionSync(pane.paneId, '@dmux_title_prefix', prefixValue);
|
|
475
|
+
cachedPrefixes.set(pane.paneId, prefixValue);
|
|
476
|
+
}
|
|
477
|
+
if (cachedLabels.get(pane.paneId) !== labelValue) {
|
|
478
|
+
tmuxService.setPaneOptionSync(pane.paneId, '@dmux_title_label', labelValue);
|
|
479
|
+
cachedLabels.set(pane.paneId, labelValue);
|
|
480
|
+
}
|
|
481
|
+
if (cachedActiveBorderStyles.get(pane.paneId) !== activeBorderStyle) {
|
|
482
|
+
tmuxService.setPaneOptionSync(pane.paneId, '@dmux_active_border_style', activeBorderStyle);
|
|
483
|
+
cachedActiveBorderStyles.set(pane.paneId, activeBorderStyle);
|
|
484
|
+
}
|
|
485
|
+
if (pane.paneId === activeBorderPaneId) {
|
|
486
|
+
tmuxService.setSessionOptionSync(sessionName, 'pane-active-border-style', activeBorderStyle);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
if (controlPaneId && cachedActiveBorderStyles.get(controlPaneId) !== controlPaneActiveBorderStyle) {
|
|
490
|
+
tmuxService.setPaneOptionSync(controlPaneId, '@dmux_active_border_style', controlPaneActiveBorderStyle);
|
|
491
|
+
cachedActiveBorderStyles.set(controlPaneId, controlPaneActiveBorderStyle);
|
|
492
|
+
}
|
|
493
|
+
if (!focusedPane) {
|
|
494
|
+
tmuxService.setSessionOptionSync(sessionName, 'pane-active-border-style', controlPaneActiveBorderStyle);
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
const hasAnimatedPrefix = panes.some(paneNeedsAnimatedTitlePrefix);
|
|
498
|
+
if (!hasAnimatedPrefix) {
|
|
499
|
+
paneTitleSpinnerFrameRef.current = 0;
|
|
500
|
+
}
|
|
501
|
+
syncPaneTitlePrefixes();
|
|
502
|
+
if (!hasAnimatedPrefix) {
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
const interval = setInterval(() => {
|
|
506
|
+
paneTitleSpinnerFrameRef.current = (paneTitleSpinnerFrameRef.current + 1) % PANE_TITLE_BUSY_FRAMES.length;
|
|
507
|
+
syncPaneTitlePrefixes();
|
|
508
|
+
}, 90);
|
|
509
|
+
return () => {
|
|
510
|
+
clearInterval(interval);
|
|
511
|
+
};
|
|
512
|
+
}, [
|
|
513
|
+
panes,
|
|
514
|
+
sidebarProjects,
|
|
515
|
+
sessionProjectRoot,
|
|
516
|
+
projectName,
|
|
517
|
+
activeBorderPaneId,
|
|
518
|
+
sessionName,
|
|
519
|
+
controlPaneId,
|
|
520
|
+
controlPaneActiveBorderStyle,
|
|
521
|
+
focusedPane,
|
|
522
|
+
]);
|
|
362
523
|
useEffect(() => {
|
|
363
524
|
const maxIndex = Math.max(0, projectActionLayout.totalItems - 1);
|
|
364
525
|
if (selectedIndex > maxIndex) {
|
|
@@ -368,14 +529,59 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
368
529
|
// Navigation logic moved to hook
|
|
369
530
|
const { getCardGridPosition, findCardInDirection } = useNavigation(navigationRows, groupStartRows);
|
|
370
531
|
// findCardInDirection provided by useNavigation
|
|
532
|
+
const syncSelectedIndexToFocusedPane = React.useCallback(async (activePaneId) => {
|
|
533
|
+
try {
|
|
534
|
+
const focusedPaneId = activePaneId ?? await TmuxService.getInstance().getActivePaneId();
|
|
535
|
+
if (!focusedPaneId || focusedPaneId === controlPaneId) {
|
|
536
|
+
setFocusedPaneId(null);
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
setFocusedPaneId((currentPaneId) => currentPaneId === focusedPaneId ? currentPaneId : focusedPaneId);
|
|
540
|
+
const focusedIndex = panes.findIndex((pane) => pane.paneId === focusedPaneId);
|
|
541
|
+
if (focusedIndex === -1) {
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
setSelectedIndex((currentIndex) => currentIndex === focusedIndex ? currentIndex : focusedIndex);
|
|
545
|
+
}
|
|
546
|
+
catch {
|
|
547
|
+
// Focus sync is best-effort; pane lifecycle handling will correct stale IDs.
|
|
548
|
+
}
|
|
549
|
+
}, [controlPaneId, panes]);
|
|
550
|
+
useEffect(() => {
|
|
551
|
+
const paneEventService = PaneEventService.getInstance();
|
|
552
|
+
return paneEventService.onPaneFocusChanged((event) => {
|
|
553
|
+
void syncSelectedIndexToFocusedPane(event.activePaneId);
|
|
554
|
+
});
|
|
555
|
+
}, [syncSelectedIndexToFocusedPane]);
|
|
556
|
+
useEffect(() => {
|
|
557
|
+
if (!process.env.TMUX || panes.length === 0) {
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
let syncInFlight = false;
|
|
561
|
+
const syncActivePane = () => {
|
|
562
|
+
if (syncInFlight) {
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
syncInFlight = true;
|
|
566
|
+
void syncSelectedIndexToFocusedPane().finally(() => {
|
|
567
|
+
syncInFlight = false;
|
|
568
|
+
});
|
|
569
|
+
};
|
|
570
|
+
syncActivePane();
|
|
571
|
+
const interval = setInterval(syncActivePane, ACTIVE_PANE_SYNC_INTERVAL_MS);
|
|
572
|
+
return () => {
|
|
573
|
+
clearInterval(interval);
|
|
574
|
+
};
|
|
575
|
+
}, [eventMode, panes.length, syncSelectedIndexToFocusedPane]);
|
|
371
576
|
// savePanes moved to usePanes
|
|
372
577
|
// applySmartLayout moved to utils/tmux
|
|
373
578
|
// Helper function to handle agent choice and pane creation
|
|
374
579
|
const selectAgentsForPaneCreation = async (targetProjectRoot) => {
|
|
375
|
-
|
|
580
|
+
const targetRoot = targetProjectRoot || selectedProjectRoot;
|
|
581
|
+
if (getAvailableAgentsForProject(targetRoot).length === 0) {
|
|
376
582
|
return [];
|
|
377
583
|
}
|
|
378
|
-
const selectedAgents = await popupManager.launchAgentChoicePopup(
|
|
584
|
+
const selectedAgents = await popupManager.launchAgentChoicePopup(targetRoot);
|
|
379
585
|
if (selectedAgents === null) {
|
|
380
586
|
return null;
|
|
381
587
|
}
|
|
@@ -386,9 +592,9 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
386
592
|
}
|
|
387
593
|
return selectedAgents;
|
|
388
594
|
};
|
|
389
|
-
const createPaneSelection = async (
|
|
595
|
+
const createPaneSelection = async (paneInput, selectedAgents, targetProjectRoot, createOptions) => {
|
|
390
596
|
if (selectedAgents.length === 0) {
|
|
391
|
-
const pane = await createNewPaneHook(
|
|
597
|
+
const pane = await createNewPaneHook(paneInput, undefined, {
|
|
392
598
|
targetProjectRoot,
|
|
393
599
|
skipAgentSelection: true,
|
|
394
600
|
startPointBranch: createOptions?.startPointBranch,
|
|
@@ -396,7 +602,7 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
396
602
|
});
|
|
397
603
|
return pane ? 1 : 0;
|
|
398
604
|
}
|
|
399
|
-
const createdPanes = await createPanesForAgentsHook(
|
|
605
|
+
const createdPanes = await createPanesForAgentsHook(paneInput, selectedAgents, {
|
|
400
606
|
existingPanes: panes,
|
|
401
607
|
targetProjectRoot,
|
|
402
608
|
startPointBranch: createOptions?.startPointBranch,
|
|
@@ -404,12 +610,12 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
404
610
|
});
|
|
405
611
|
return createdPanes.length;
|
|
406
612
|
};
|
|
407
|
-
const handlePaneCreationWithAgent = async (
|
|
613
|
+
const handlePaneCreationWithAgent = async (paneInput, targetProjectRoot, createOptions) => {
|
|
408
614
|
const selectedAgents = await selectAgentsForPaneCreation(targetProjectRoot);
|
|
409
615
|
if (selectedAgents === null) {
|
|
410
616
|
return;
|
|
411
617
|
}
|
|
412
|
-
await createPaneSelection(
|
|
618
|
+
await createPaneSelection(paneInput, selectedAgents, targetProjectRoot, createOptions);
|
|
413
619
|
};
|
|
414
620
|
const handleCreateChildWorktree = async (parentPane) => {
|
|
415
621
|
if (!parentPane.worktreePath) {
|
|
@@ -418,8 +624,8 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
418
624
|
return;
|
|
419
625
|
}
|
|
420
626
|
const targetProjectRoot = getPaneProjectRoot(parentPane, sessionProjectRoot);
|
|
421
|
-
const
|
|
422
|
-
if (!
|
|
627
|
+
const paneInput = await popupManager.launchNewPanePopup(targetProjectRoot);
|
|
628
|
+
if (!paneInput) {
|
|
423
629
|
return;
|
|
424
630
|
}
|
|
425
631
|
const selectedAgents = await selectAgentsForPaneCreation(targetProjectRoot);
|
|
@@ -427,7 +633,7 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
427
633
|
return;
|
|
428
634
|
}
|
|
429
635
|
const createSubWorktree = async () => {
|
|
430
|
-
const createdCount = await createPaneSelection(
|
|
636
|
+
const createdCount = await createPaneSelection(paneInput, selectedAgents, targetProjectRoot, {
|
|
431
637
|
startPointBranch: getPaneBranchName(parentPane),
|
|
432
638
|
mergeTargetChain: createMergeTargetChain(parentPane, targetProjectRoot),
|
|
433
639
|
});
|
|
@@ -514,7 +720,7 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
514
720
|
const reopenProjectRoot = targetProjectRoot || projectRoot || process.cwd();
|
|
515
721
|
let selectedAgent;
|
|
516
722
|
if (!candidate.path) {
|
|
517
|
-
if (
|
|
723
|
+
if (getAvailableAgentsForProject(reopenProjectRoot).length === 0) {
|
|
518
724
|
setStatusMessage("No enabled agents available for opening this branch");
|
|
519
725
|
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_SHORT);
|
|
520
726
|
return;
|
|
@@ -632,7 +838,7 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
632
838
|
else if (result.type === "input") {
|
|
633
839
|
if (!result.onSubmit)
|
|
634
840
|
return;
|
|
635
|
-
const inputValue = await popupManager.launchInputPopup(result.title || "Input", result.message, result.placeholder, result.defaultValue, selectedProjectRoot);
|
|
841
|
+
const inputValue = await popupManager.launchInputPopup(result.title || "Input", result.message, result.placeholder, result.defaultValue, selectedProjectRoot, result.inputMaxVisibleLines);
|
|
636
842
|
if (inputValue !== null) {
|
|
637
843
|
const nextResult = await trackProjectActivity(() => result.onSubmit(inputValue), selectedProjectRoot);
|
|
638
844
|
// Recursively handle nested results
|
|
@@ -641,6 +847,26 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
641
847
|
}
|
|
642
848
|
}
|
|
643
849
|
}
|
|
850
|
+
else if (result.type === "pr_review") {
|
|
851
|
+
if (!result.onSubmit || !result.reviewData)
|
|
852
|
+
return;
|
|
853
|
+
const inputValue = await popupManager.launchPRReviewPopup({
|
|
854
|
+
title: result.title || "Pull Request",
|
|
855
|
+
message: result.message || "",
|
|
856
|
+
defaultValue: result.defaultValue || "",
|
|
857
|
+
repoPath: result.reviewData.repoPath,
|
|
858
|
+
sourceBranch: result.reviewData.sourceBranch,
|
|
859
|
+
targetBranch: result.reviewData.targetBranch,
|
|
860
|
+
files: result.reviewData.files,
|
|
861
|
+
aiFailed: result.reviewData.aiFailed,
|
|
862
|
+
}, selectedProjectRoot);
|
|
863
|
+
if (inputValue !== null) {
|
|
864
|
+
const nextResult = await trackProjectActivity(() => result.onSubmit(inputValue), selectedProjectRoot);
|
|
865
|
+
if (nextResult) {
|
|
866
|
+
await handleActionResult(nextResult);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
}
|
|
644
870
|
else if (result.type === "navigation") {
|
|
645
871
|
// Navigate to target pane if specified
|
|
646
872
|
if (result.targetPaneId) {
|
|
@@ -673,25 +899,25 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
673
899
|
projectName,
|
|
674
900
|
defaultProjectRoot: sessionProjectRoot,
|
|
675
901
|
onPaneRemove: async (paneId) => {
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
if (
|
|
902
|
+
const nextSelection = resolveSelectionAfterPaneClose(panes, paneId, sidebarProjects, sessionProjectRoot, projectName);
|
|
903
|
+
if (nextSelection) {
|
|
904
|
+
setSelectedIndex(nextSelection.selectedIndex);
|
|
905
|
+
}
|
|
906
|
+
else {
|
|
907
|
+
const maxIndex = Math.max(0, projectActionLayout.totalItems - 2);
|
|
908
|
+
if (selectedIndex > maxIndex) {
|
|
909
|
+
setSelectedIndex(maxIndex);
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
const targetPaneId = nextSelection?.pane && !nextSelection.pane.hidden
|
|
913
|
+
? nextSelection.pane.paneId
|
|
914
|
+
: controlPaneId;
|
|
915
|
+
if (targetPaneId) {
|
|
690
916
|
try {
|
|
691
|
-
await TmuxService.getInstance().selectPane(
|
|
917
|
+
await TmuxService.getInstance().selectPane(targetPaneId);
|
|
692
918
|
}
|
|
693
919
|
catch {
|
|
694
|
-
// Ignore -
|
|
920
|
+
// Ignore - the target pane might have closed during cleanup
|
|
695
921
|
}
|
|
696
922
|
}
|
|
697
923
|
},
|
|
@@ -702,6 +928,7 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
702
928
|
launchConfirmPopup: popupManager.launchConfirmPopup.bind(popupManager),
|
|
703
929
|
launchChoicePopup: popupManager.launchChoicePopup.bind(popupManager),
|
|
704
930
|
launchInputPopup: popupManager.launchInputPopup.bind(popupManager),
|
|
931
|
+
launchPRReviewPopup: popupManager.launchPRReviewPopup.bind(popupManager),
|
|
705
932
|
launchProgressPopup: popupManager.launchProgressPopup.bind(popupManager),
|
|
706
933
|
}
|
|
707
934
|
: undefined,
|
|
@@ -816,12 +1043,14 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
816
1043
|
setShowHooksPrompt(false);
|
|
817
1044
|
setUseHooks(true);
|
|
818
1045
|
settingsManager.updateSetting('useTmuxHooks', true, 'global');
|
|
1046
|
+
refreshDmuxSettings();
|
|
819
1047
|
}
|
|
820
1048
|
else if (input === 'n') {
|
|
821
1049
|
// No - use polling
|
|
822
1050
|
setShowHooksPrompt(false);
|
|
823
1051
|
setUseHooks(false);
|
|
824
1052
|
settingsManager.updateSetting('useTmuxHooks', false, 'global');
|
|
1053
|
+
refreshDmuxSettings();
|
|
825
1054
|
}
|
|
826
1055
|
else if (key.return) {
|
|
827
1056
|
// Select current option
|
|
@@ -829,6 +1058,7 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
829
1058
|
const selected = hooksPromptIndex === 0;
|
|
830
1059
|
setUseHooks(selected);
|
|
831
1060
|
settingsManager.updateSetting('useTmuxHooks', selected, 'global');
|
|
1061
|
+
refreshDmuxSettings();
|
|
832
1062
|
}
|
|
833
1063
|
}, { isActive: showHooksPrompt });
|
|
834
1064
|
// Input handling - extracted to dedicated hook
|
|
@@ -856,6 +1086,7 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
856
1086
|
projectSettings,
|
|
857
1087
|
saveSettings,
|
|
858
1088
|
settingsManager,
|
|
1089
|
+
refreshDmuxSettings,
|
|
859
1090
|
popupManager,
|
|
860
1091
|
actionSystem,
|
|
861
1092
|
controlPaneId,
|
|
@@ -872,9 +1103,10 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
872
1103
|
saveSidebarProjects,
|
|
873
1104
|
loadPanes,
|
|
874
1105
|
cleanExit,
|
|
875
|
-
|
|
1106
|
+
getAvailableAgentsForProject,
|
|
876
1107
|
panesFile,
|
|
877
1108
|
projectRoot: sessionProjectRoot,
|
|
1109
|
+
activeProjectRoot: selectedProjectRoot,
|
|
878
1110
|
projectActionItems: projectActionLayout.actionItems,
|
|
879
1111
|
findCardInDirection,
|
|
880
1112
|
});
|
|
@@ -884,7 +1116,8 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
884
1116
|
// - Normal mode calculation:
|
|
885
1117
|
// - Base footer: 4 lines (marginTop + logs divider + logs line + keyboard shortcuts)
|
|
886
1118
|
// - Footer tip: +1 line when footer tips are enabled
|
|
887
|
-
// - Toast:
|
|
1119
|
+
// - Toast (active): wrapped lines + header + marginBottom
|
|
1120
|
+
// - Toast (queued, transitioning): header + marginBottom (2 lines)
|
|
888
1121
|
// - Debug info: +1 line if DEBUG_DMUX
|
|
889
1122
|
// - Status line: +1 line if updateAvailable/currentBranch/debugMessage
|
|
890
1123
|
// - Status messages: +1 line per active message
|
|
@@ -896,20 +1129,26 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
896
1129
|
else {
|
|
897
1130
|
footerLines = 0;
|
|
898
1131
|
if (showFooterHelp) {
|
|
899
|
-
footerLines =
|
|
1132
|
+
footerLines = 3; // logs divider + logs + shortcuts
|
|
900
1133
|
if (currentFooterTip) {
|
|
901
1134
|
footerLines += 1;
|
|
902
1135
|
}
|
|
903
1136
|
// Add toast notification (calculate wrapped lines + header)
|
|
904
1137
|
if (currentToast) {
|
|
905
1138
|
// Toast format: "✓ message" - icon (1) + space (1) + message
|
|
906
|
-
|
|
907
|
-
const
|
|
1139
|
+
// Use stringWidth for CJK-aware display width calculation
|
|
1140
|
+
const iconAndSpaceWidth = 2;
|
|
1141
|
+
const toastDisplayWidth = iconAndSpaceWidth + stringWidth(currentToast.message);
|
|
908
1142
|
// Available width is sidebar width (40) minus padding/margins (~2)
|
|
909
1143
|
const availableWidth = SIDEBAR_WIDTH - 2;
|
|
910
|
-
const wrappedLines = Math.ceil(
|
|
1144
|
+
const wrappedLines = Math.ceil(toastDisplayWidth / availableWidth);
|
|
911
1145
|
footerLines += wrappedLines + 1 + 1; // wrapped lines + header line + marginBottom
|
|
912
1146
|
}
|
|
1147
|
+
else if (toastQueueLength > 0) {
|
|
1148
|
+
// When there are queued toasts but no current toast (transition state),
|
|
1149
|
+
// FooterHelp still renders the notification header + marginBottom
|
|
1150
|
+
footerLines += 1 + 1; // header line + marginBottom
|
|
1151
|
+
}
|
|
913
1152
|
// Add debug info
|
|
914
1153
|
if (process.env.DEBUG_DMUX) {
|
|
915
1154
|
footerLines += 1;
|
|
@@ -928,9 +1167,9 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
928
1167
|
}
|
|
929
1168
|
}
|
|
930
1169
|
const contentHeight = Math.max(terminalHeight - footerLines, 10);
|
|
931
|
-
return (React.createElement(Box, { flexDirection: "column", height: terminalHeight },
|
|
1170
|
+
return (React.createElement(Box, { key: `theme-${selectedThemeName}-${themeRefreshNonce}`, flexDirection: "column", height: terminalHeight },
|
|
932
1171
|
React.createElement(Box, { flexDirection: "column", height: contentHeight, overflow: "hidden" },
|
|
933
|
-
React.createElement(PanesGrid, { panes: panes, selectedIndex: selectedIndex, isLoading: isLoading, agentStatuses: agentStatuses, activeDevSourcePath: activeDevSourcePath, sidebarProjects: sidebarProjects, fallbackProjectRoot: projectRoot || process.cwd(), fallbackProjectName: projectName, isProjectBusy: isProjectHeaderBusy }),
|
|
1172
|
+
React.createElement(PanesGrid, { panes: panes, selectedIndex: selectedIndex, activeProjectRoot: activeProjectRoot, isLoading: isLoading, themeName: selectedThemeName, projectThemeByRoot: projectThemeByRoot, agentStatuses: agentStatuses, activeDevSourcePath: activeDevSourcePath, sidebarProjects: sidebarProjects, fallbackProjectRoot: projectRoot || process.cwd(), fallbackProjectName: projectName, isProjectBusy: isProjectHeaderBusy }),
|
|
934
1173
|
showCommandPrompt && (React.createElement(CommandPromptDialog, { type: showCommandPrompt, value: commandInput, onChange: setCommandInput })),
|
|
935
1174
|
showFileCopyPrompt && React.createElement(FileCopyPrompt, null),
|
|
936
1175
|
showHooksPrompt && (React.createElement(TmuxHooksPromptDialog, { selectedIndex: hooksPromptIndex }))),
|