dmux 5.6.2 → 5.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +93 -0
- package/README.md +7 -1
- package/dist/DmuxApp.d.ts.map +1 -1
- package/dist/DmuxApp.js +279 -40
- package/dist/DmuxApp.js.map +1 -1
- package/dist/actions/implementations/closeAction.d.ts.map +1 -1
- package/dist/actions/implementations/closeAction.js +93 -75
- 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/duplicateAction.d.ts.map +1 -1
- package/dist/actions/implementations/duplicateAction.js +3 -1
- package/dist/actions/implementations/duplicateAction.js.map +1 -1
- 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.d.ts.map +1 -1
- package/dist/actions/implementations/mergeAction.js +17 -9
- package/dist/actions/implementations/mergeAction.js.map +1 -1
- package/dist/actions/implementations/renameAction.d.ts +0 -9
- package/dist/actions/implementations/renameAction.d.ts.map +1 -1
- package/dist/actions/implementations/renameAction.js +77 -13
- package/dist/actions/implementations/renameAction.js.map +1 -1
- package/dist/actions/implementations/toggleAutopilotAction.d.ts.map +1 -1
- package/dist/actions/implementations/toggleAutopilotAction.js +3 -1
- package/dist/actions/implementations/toggleAutopilotAction.js.map +1 -1
- package/dist/actions/implementations/viewAction.d.ts.map +1 -1
- package/dist/actions/implementations/viewAction.js +4 -2
- package/dist/actions/implementations/viewAction.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/merge/issueHandlers/mergeConflictHandler.d.ts.map +1 -1
- package/dist/actions/merge/issueHandlers/mergeConflictHandler.js +3 -1
- package/dist/actions/merge/issueHandlers/mergeConflictHandler.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/panes/PaneCard.d.ts +3 -1
- package/dist/components/panes/PaneCard.d.ts.map +1 -1
- package/dist/components/panes/PaneCard.js +18 -5
- 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/logsPopup.js +2 -1
- package/dist/components/popups/logsPopup.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 +2 -1
- package/dist/hooks/useInputHandling.d.ts.map +1 -1
- package/dist/hooks/useInputHandling.js +100 -31
- package/dist/hooks/useInputHandling.js.map +1 -1
- package/dist/hooks/usePaneLoading.d.ts.map +1 -1
- package/dist/hooks/usePaneLoading.js +29 -4
- package/dist/hooks/usePaneLoading.js.map +1 -1
- package/dist/hooks/usePaneSync.d.ts +2 -2
- package/dist/hooks/usePaneSync.d.ts.map +1 -1
- package/dist/hooks/usePaneSync.js +10 -7
- package/dist/hooks/usePaneSync.js.map +1 -1
- package/dist/hooks/usePanes.d.ts.map +1 -1
- package/dist/hooks/usePanes.js +10 -2
- 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 +35 -8
- 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.js +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/PaneAnalyzer.d.ts.map +1 -1
- package/dist/services/PaneAnalyzer.js +2 -1
- package/dist/services/PaneAnalyzer.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 +17 -5
- package/dist/services/PopupManager.d.ts.map +1 -1
- package/dist/services/PopupManager.js +143 -32
- 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 +47 -2
- 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 +11 -0
- package/dist/services/TmuxService.d.ts.map +1 -1
- package/dist/services/TmuxService.js +27 -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 +9 -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 +6 -2
- package/dist/utils/conflictResolutionPane.js.map +1 -1
- package/dist/utils/generated-agents-doc.d.ts +1 -1
- package/dist/utils/generated-agents-doc.js +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/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/mergeTargets.d.ts +1 -1
- package/dist/utils/mergeTargets.d.ts.map +1 -1
- package/dist/utils/mergeTargets.js +12 -8
- package/dist/utils/mergeTargets.js.map +1 -1
- package/dist/utils/mergeValidation.d.ts.map +1 -1
- package/dist/utils/mergeValidation.js +45 -11
- package/dist/utils/mergeValidation.js.map +1 -1
- 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.map +1 -1
- package/dist/utils/paneCreation.js +110 -36
- package/dist/utils/paneCreation.js.map +1 -1
- package/dist/utils/paneRebinding.d.ts +1 -1
- package/dist/utils/paneRebinding.js +1 -1
- package/dist/utils/paneTitle.d.ts +8 -1
- package/dist/utils/paneTitle.d.ts.map +1 -1
- package/dist/utils/paneTitle.js +58 -10
- 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/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 +40 -7
- 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 +96 -21
- package/dist/utils/settingsManager.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/tmux.d.ts +4 -0
- package/dist/utils/tmux.d.ts.map +1 -1
- package/dist/utils/tmux.js +8 -0
- package/dist/utils/tmux.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/utils/worktreeMetadata.d.ts +1 -0
- package/dist/utils/worktreeMetadata.d.ts.map +1 -1
- package/dist/utils/worktreeMetadata.js +17 -1
- package/dist/utils/worktreeMetadata.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";
|
|
@@ -39,17 +40,25 @@ import { getPaneBranchName } from "./utils/git.js";
|
|
|
39
40
|
import { getGitStatus } from "./utils/mergeValidation.js";
|
|
40
41
|
import { createMergeTargetChain } from "./utils/mergeTargets.js";
|
|
41
42
|
import { claimProcessShutdown } from "./utils/processShutdown.js";
|
|
43
|
+
import { getPaneDisplayName } from "./utils/paneTitle.js";
|
|
42
44
|
import { FOOTER_TIP_ROTATION_INTERVAL, getFooterTips, getNextFooterTipIndex, getRandomFooterTipIndex, } from "./utils/footerTips.js";
|
|
43
45
|
const __filename = fileURLToPath(import.meta.url);
|
|
44
46
|
const __dirname = dirname(__filename);
|
|
47
|
+
const ACTIVE_PANE_SYNC_INTERVAL_MS = 125;
|
|
45
48
|
import PanesGrid from "./components/panes/PanesGrid.js";
|
|
46
49
|
import CommandPromptDialog from "./components/dialogs/CommandPromptDialog.js";
|
|
47
50
|
import FileCopyPrompt from "./components/ui/FileCopyPrompt.js";
|
|
48
51
|
import FooterHelp from "./components/ui/FooterHelp.js";
|
|
49
52
|
import TmuxHooksPromptDialog from "./components/dialogs/TmuxHooksPromptDialog.js";
|
|
50
53
|
import { PaneEventService } from "./services/PaneEventService.js";
|
|
51
|
-
import { buildProjectActionLayout, buildVisualNavigationRows, buildGroupStartRows, getProjectActionByIndex, } from "./utils/projectActions.js";
|
|
54
|
+
import { buildProjectActionLayout, buildVisualNavigationRows, buildGroupStartRows, getProjectActionByIndex, resolveSelectionAfterPaneClose, } from "./utils/projectActions.js";
|
|
52
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";
|
|
53
62
|
const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoot, autoUpdater, controlPaneId, }) => {
|
|
54
63
|
const { stdout } = useStdout();
|
|
55
64
|
const terminalHeight = stdout?.rows || 40;
|
|
@@ -57,13 +66,19 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
57
66
|
const sessionProjectRoot = projectRoot || process.cwd();
|
|
58
67
|
/* panes state moved to usePanes */
|
|
59
68
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
69
|
+
const [focusedPaneId, setFocusedPaneId] = useState(null);
|
|
60
70
|
const { statusMessage, setStatusMessage } = useStatusMessages();
|
|
61
71
|
const [isCreatingPane, setIsCreatingPane] = useState(false);
|
|
62
72
|
const { trackProjectActivity, isProjectBusy: isTrackedProjectBusy, } = useProjectActivity(sessionProjectRoot);
|
|
63
73
|
// Settings state
|
|
64
74
|
const [settingsManager] = useState(() => new SettingsManager(projectRoot));
|
|
65
75
|
const { projectSettings, saveSettings } = useProjectSettings(settingsFile);
|
|
66
|
-
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);
|
|
67
82
|
// Dialog state management
|
|
68
83
|
const dialogState = useDialogState();
|
|
69
84
|
const { showCommandPrompt, setShowCommandPrompt, commandInput, setCommandInput, showFileCopyPrompt, setShowFileCopyPrompt, currentCommandType, setCurrentCommandType, runningCommand, setRunningCommand, quitConfirmMode, setQuitConfirmMode, } = dialogState;
|
|
@@ -77,6 +92,7 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
77
92
|
// Agent selection is settings-driven.
|
|
78
93
|
// Installation checks are performed lazily in the Enabled Agents settings popup.
|
|
79
94
|
const availableAgents = resolveEnabledAgentsSelection(settings.enabledAgents);
|
|
95
|
+
const getAvailableAgentsForProject = (targetProjectRoot = selectedProjectRoot) => resolveEnabledAgentsSelection(new SettingsManager(targetProjectRoot).getSettings().enabledAgents);
|
|
80
96
|
const footerTips = useMemo(() => getFooterTips(isDevMode), [isDevMode]);
|
|
81
97
|
const showFooterTips = settings.showFooterTips !== false && footerTips.length > 0;
|
|
82
98
|
const [footerTipIndex, setFooterTipIndex] = useState(() => getRandomFooterTipIndex(footerTips.length));
|
|
@@ -132,12 +148,12 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
132
148
|
};
|
|
133
149
|
}, []);
|
|
134
150
|
// Panes state and persistence (skipLoading will be updated after actionSystem is initialized)
|
|
135
|
-
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);
|
|
136
152
|
// Check for tmux hooks preference on startup
|
|
137
153
|
useEffect(() => {
|
|
138
154
|
const checkHooksPreference = async () => {
|
|
139
155
|
// Check if user already has a preference
|
|
140
|
-
const settings =
|
|
156
|
+
const settings = new SettingsManager(sessionProjectRoot).getSettings();
|
|
141
157
|
if (settings.useTmuxHooks !== undefined) {
|
|
142
158
|
// User has already decided
|
|
143
159
|
setUseHooks(settings.useTmuxHooks);
|
|
@@ -152,6 +168,7 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
152
168
|
setUseHooks(true);
|
|
153
169
|
// Save the preference
|
|
154
170
|
settingsManager.updateSetting('useTmuxHooks', true, 'global');
|
|
171
|
+
refreshDmuxSettings();
|
|
155
172
|
}
|
|
156
173
|
else {
|
|
157
174
|
// Need to ask user - show prompt
|
|
@@ -346,18 +363,163 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
346
363
|
// getPanePositions moved to utils/tmux
|
|
347
364
|
const activeDevSourcePath = isDevMode ? process.cwd() : undefined;
|
|
348
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]);
|
|
349
375
|
const selectedProjectRoot = useMemo(() => {
|
|
350
|
-
const selectedPane = selectedIndex < panes.length ? panes[selectedIndex] : undefined;
|
|
351
376
|
if (selectedPane) {
|
|
352
377
|
return getPaneProjectRoot(selectedPane, sessionProjectRoot);
|
|
353
378
|
}
|
|
354
379
|
return (getProjectActionByIndex(projectActionLayout.actionItems, selectedIndex)?.projectRoot
|
|
355
380
|
|| sessionProjectRoot);
|
|
356
|
-
}, [
|
|
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
|
+
};
|
|
357
411
|
const navigationRows = useMemo(() => isLoading
|
|
358
412
|
? projectActionLayout.groups.flatMap((group) => group.panes.map((entry) => [entry.index]))
|
|
359
413
|
: buildVisualNavigationRows(projectActionLayout), [isLoading, projectActionLayout]);
|
|
360
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
|
+
]);
|
|
361
523
|
useEffect(() => {
|
|
362
524
|
const maxIndex = Math.max(0, projectActionLayout.totalItems - 1);
|
|
363
525
|
if (selectedIndex > maxIndex) {
|
|
@@ -367,14 +529,59 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
367
529
|
// Navigation logic moved to hook
|
|
368
530
|
const { getCardGridPosition, findCardInDirection } = useNavigation(navigationRows, groupStartRows);
|
|
369
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]);
|
|
370
576
|
// savePanes moved to usePanes
|
|
371
577
|
// applySmartLayout moved to utils/tmux
|
|
372
578
|
// Helper function to handle agent choice and pane creation
|
|
373
579
|
const selectAgentsForPaneCreation = async (targetProjectRoot) => {
|
|
374
|
-
|
|
580
|
+
const targetRoot = targetProjectRoot || selectedProjectRoot;
|
|
581
|
+
if (getAvailableAgentsForProject(targetRoot).length === 0) {
|
|
375
582
|
return [];
|
|
376
583
|
}
|
|
377
|
-
const selectedAgents = await popupManager.launchAgentChoicePopup(
|
|
584
|
+
const selectedAgents = await popupManager.launchAgentChoicePopup(targetRoot);
|
|
378
585
|
if (selectedAgents === null) {
|
|
379
586
|
return null;
|
|
380
587
|
}
|
|
@@ -433,12 +640,12 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
433
640
|
if (createdCount > 0) {
|
|
434
641
|
return {
|
|
435
642
|
type: "success",
|
|
436
|
-
message: `Created ${createdCount} sub-worktree${createdCount === 1 ? "" : "s"} from ${parentPane
|
|
643
|
+
message: `Created ${createdCount} sub-worktree${createdCount === 1 ? "" : "s"} from ${getPaneDisplayName(parentPane)}`,
|
|
437
644
|
};
|
|
438
645
|
}
|
|
439
646
|
return {
|
|
440
647
|
type: "error",
|
|
441
|
-
message: `Failed to create a sub-worktree from ${parentPane
|
|
648
|
+
message: `Failed to create a sub-worktree from ${getPaneDisplayName(parentPane)}`,
|
|
442
649
|
dismissable: true,
|
|
443
650
|
};
|
|
444
651
|
};
|
|
@@ -453,7 +660,7 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
453
660
|
const branchFromDirtyResult = {
|
|
454
661
|
type: "choice",
|
|
455
662
|
title: "Parent Worktree Has Uncommitted Changes",
|
|
456
|
-
message: `"${parentPane
|
|
663
|
+
message: `"${getPaneDisplayName(parentPane)}" has uncommitted changes. Commit them before creating a sub-worktree.`,
|
|
457
664
|
options: [
|
|
458
665
|
{
|
|
459
666
|
id: "commit_automatic",
|
|
@@ -513,7 +720,7 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
513
720
|
const reopenProjectRoot = targetProjectRoot || projectRoot || process.cwd();
|
|
514
721
|
let selectedAgent;
|
|
515
722
|
if (!candidate.path) {
|
|
516
|
-
if (
|
|
723
|
+
if (getAvailableAgentsForProject(reopenProjectRoot).length === 0) {
|
|
517
724
|
setStatusMessage("No enabled agents available for opening this branch");
|
|
518
725
|
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_SHORT);
|
|
519
726
|
return;
|
|
@@ -549,7 +756,7 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
549
756
|
const updatedPanes = [...panes, result.pane];
|
|
550
757
|
await savePanes(updatedPanes);
|
|
551
758
|
await loadPanes();
|
|
552
|
-
setStatusMessage(`${candidate.path ? "Reopened" : "Opened"} ${
|
|
759
|
+
setStatusMessage(`${candidate.path ? "Reopened" : "Opened"} ${getPaneDisplayName(result.pane)}`);
|
|
553
760
|
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_SHORT);
|
|
554
761
|
}
|
|
555
762
|
catch (error) {
|
|
@@ -590,7 +797,7 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
590
797
|
await trackProjectActivity(() => restartDevSessionAtSource(nextSource.nextSourcePath), sessionProjectRoot);
|
|
591
798
|
return;
|
|
592
799
|
}
|
|
593
|
-
setStatusMessage(`Switching source to "${pane
|
|
800
|
+
setStatusMessage(`Switching source to "${getPaneDisplayName(pane)}"...`);
|
|
594
801
|
await trackProjectActivity(() => restartDevSessionAtSource(nextSource.nextSourcePath), paneProjectRoot);
|
|
595
802
|
};
|
|
596
803
|
// Helper function to handle action results recursively
|
|
@@ -631,7 +838,7 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
631
838
|
else if (result.type === "input") {
|
|
632
839
|
if (!result.onSubmit)
|
|
633
840
|
return;
|
|
634
|
-
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);
|
|
635
842
|
if (inputValue !== null) {
|
|
636
843
|
const nextResult = await trackProjectActivity(() => result.onSubmit(inputValue), selectedProjectRoot);
|
|
637
844
|
// Recursively handle nested results
|
|
@@ -640,6 +847,26 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
640
847
|
}
|
|
641
848
|
}
|
|
642
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
|
+
}
|
|
643
870
|
else if (result.type === "navigation") {
|
|
644
871
|
// Navigate to target pane if specified
|
|
645
872
|
if (result.targetPaneId) {
|
|
@@ -672,25 +899,25 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
672
899
|
projectName,
|
|
673
900
|
defaultProjectRoot: sessionProjectRoot,
|
|
674
901
|
onPaneRemove: async (paneId) => {
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
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) {
|
|
689
916
|
try {
|
|
690
|
-
await TmuxService.getInstance().selectPane(
|
|
917
|
+
await TmuxService.getInstance().selectPane(targetPaneId);
|
|
691
918
|
}
|
|
692
919
|
catch {
|
|
693
|
-
// Ignore -
|
|
920
|
+
// Ignore - the target pane might have closed during cleanup
|
|
694
921
|
}
|
|
695
922
|
}
|
|
696
923
|
},
|
|
@@ -701,6 +928,7 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
701
928
|
launchConfirmPopup: popupManager.launchConfirmPopup.bind(popupManager),
|
|
702
929
|
launchChoicePopup: popupManager.launchChoicePopup.bind(popupManager),
|
|
703
930
|
launchInputPopup: popupManager.launchInputPopup.bind(popupManager),
|
|
931
|
+
launchPRReviewPopup: popupManager.launchPRReviewPopup.bind(popupManager),
|
|
704
932
|
launchProgressPopup: popupManager.launchProgressPopup.bind(popupManager),
|
|
705
933
|
}
|
|
706
934
|
: undefined,
|
|
@@ -815,12 +1043,14 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
815
1043
|
setShowHooksPrompt(false);
|
|
816
1044
|
setUseHooks(true);
|
|
817
1045
|
settingsManager.updateSetting('useTmuxHooks', true, 'global');
|
|
1046
|
+
refreshDmuxSettings();
|
|
818
1047
|
}
|
|
819
1048
|
else if (input === 'n') {
|
|
820
1049
|
// No - use polling
|
|
821
1050
|
setShowHooksPrompt(false);
|
|
822
1051
|
setUseHooks(false);
|
|
823
1052
|
settingsManager.updateSetting('useTmuxHooks', false, 'global');
|
|
1053
|
+
refreshDmuxSettings();
|
|
824
1054
|
}
|
|
825
1055
|
else if (key.return) {
|
|
826
1056
|
// Select current option
|
|
@@ -828,6 +1058,7 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
828
1058
|
const selected = hooksPromptIndex === 0;
|
|
829
1059
|
setUseHooks(selected);
|
|
830
1060
|
settingsManager.updateSetting('useTmuxHooks', selected, 'global');
|
|
1061
|
+
refreshDmuxSettings();
|
|
831
1062
|
}
|
|
832
1063
|
}, { isActive: showHooksPrompt });
|
|
833
1064
|
// Input handling - extracted to dedicated hook
|
|
@@ -855,6 +1086,7 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
855
1086
|
projectSettings,
|
|
856
1087
|
saveSettings,
|
|
857
1088
|
settingsManager,
|
|
1089
|
+
refreshDmuxSettings,
|
|
858
1090
|
popupManager,
|
|
859
1091
|
actionSystem,
|
|
860
1092
|
controlPaneId,
|
|
@@ -871,7 +1103,7 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
871
1103
|
saveSidebarProjects,
|
|
872
1104
|
loadPanes,
|
|
873
1105
|
cleanExit,
|
|
874
|
-
|
|
1106
|
+
getAvailableAgentsForProject,
|
|
875
1107
|
panesFile,
|
|
876
1108
|
projectRoot: sessionProjectRoot,
|
|
877
1109
|
projectActionItems: projectActionLayout.actionItems,
|
|
@@ -883,7 +1115,8 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
883
1115
|
// - Normal mode calculation:
|
|
884
1116
|
// - Base footer: 4 lines (marginTop + logs divider + logs line + keyboard shortcuts)
|
|
885
1117
|
// - Footer tip: +1 line when footer tips are enabled
|
|
886
|
-
// - Toast:
|
|
1118
|
+
// - Toast (active): wrapped lines + header + marginBottom
|
|
1119
|
+
// - Toast (queued, transitioning): header + marginBottom (2 lines)
|
|
887
1120
|
// - Debug info: +1 line if DEBUG_DMUX
|
|
888
1121
|
// - Status line: +1 line if updateAvailable/currentBranch/debugMessage
|
|
889
1122
|
// - Status messages: +1 line per active message
|
|
@@ -895,20 +1128,26 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
895
1128
|
else {
|
|
896
1129
|
footerLines = 0;
|
|
897
1130
|
if (showFooterHelp) {
|
|
898
|
-
footerLines =
|
|
1131
|
+
footerLines = 3; // logs divider + logs + shortcuts
|
|
899
1132
|
if (currentFooterTip) {
|
|
900
1133
|
footerLines += 1;
|
|
901
1134
|
}
|
|
902
1135
|
// Add toast notification (calculate wrapped lines + header)
|
|
903
1136
|
if (currentToast) {
|
|
904
1137
|
// Toast format: "✓ message" - icon (1) + space (1) + message
|
|
905
|
-
|
|
906
|
-
const
|
|
1138
|
+
// Use stringWidth for CJK-aware display width calculation
|
|
1139
|
+
const iconAndSpaceWidth = 2;
|
|
1140
|
+
const toastDisplayWidth = iconAndSpaceWidth + stringWidth(currentToast.message);
|
|
907
1141
|
// Available width is sidebar width (40) minus padding/margins (~2)
|
|
908
1142
|
const availableWidth = SIDEBAR_WIDTH - 2;
|
|
909
|
-
const wrappedLines = Math.ceil(
|
|
1143
|
+
const wrappedLines = Math.ceil(toastDisplayWidth / availableWidth);
|
|
910
1144
|
footerLines += wrappedLines + 1 + 1; // wrapped lines + header line + marginBottom
|
|
911
1145
|
}
|
|
1146
|
+
else if (toastQueueLength > 0) {
|
|
1147
|
+
// When there are queued toasts but no current toast (transition state),
|
|
1148
|
+
// FooterHelp still renders the notification header + marginBottom
|
|
1149
|
+
footerLines += 1 + 1; // header line + marginBottom
|
|
1150
|
+
}
|
|
912
1151
|
// Add debug info
|
|
913
1152
|
if (process.env.DEBUG_DMUX) {
|
|
914
1153
|
footerLines += 1;
|
|
@@ -927,9 +1166,9 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, projectRoo
|
|
|
927
1166
|
}
|
|
928
1167
|
}
|
|
929
1168
|
const contentHeight = Math.max(terminalHeight - footerLines, 10);
|
|
930
|
-
return (React.createElement(Box, { flexDirection: "column", height: terminalHeight },
|
|
1169
|
+
return (React.createElement(Box, { key: `theme-${selectedThemeName}-${themeRefreshNonce}`, flexDirection: "column", height: terminalHeight },
|
|
931
1170
|
React.createElement(Box, { flexDirection: "column", height: contentHeight, overflow: "hidden" },
|
|
932
|
-
React.createElement(PanesGrid, { panes: panes, selectedIndex: selectedIndex, isLoading: isLoading, agentStatuses: agentStatuses, activeDevSourcePath: activeDevSourcePath, sidebarProjects: sidebarProjects, fallbackProjectRoot: projectRoot || process.cwd(), fallbackProjectName: projectName, isProjectBusy: isProjectHeaderBusy }),
|
|
1171
|
+
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 }),
|
|
933
1172
|
showCommandPrompt && (React.createElement(CommandPromptDialog, { type: showCommandPrompt, value: commandInput, onChange: setCommandInput })),
|
|
934
1173
|
showFileCopyPrompt && React.createElement(FileCopyPrompt, null),
|
|
935
1174
|
showHooksPrompt && (React.createElement(TmuxHooksPromptDialog, { selectedIndex: hooksPromptIndex }))),
|