dmux 5.3.0 → 5.5.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.md +2 -1
- package/dist/DmuxApp.d.ts.map +1 -1
- package/dist/DmuxApp.js +226 -51
- package/dist/DmuxApp.js.map +1 -1
- package/dist/FileBrowserApp.d.ts +4 -0
- package/dist/FileBrowserApp.d.ts.map +1 -0
- package/dist/FileBrowserApp.js +693 -0
- package/dist/FileBrowserApp.js.map +1 -0
- package/dist/actions/implementations/closeAction.d.ts.map +1 -1
- package/dist/actions/implementations/closeAction.js +47 -5
- package/dist/actions/implementations/closeAction.js.map +1 -1
- package/dist/actions/implementations/mergeAction.d.ts.map +1 -1
- package/dist/actions/implementations/mergeAction.js +51 -16
- package/dist/actions/implementations/mergeAction.js.map +1 -1
- package/dist/actions/implementations/viewAction.d.ts.map +1 -1
- package/dist/actions/implementations/viewAction.js +7 -0
- package/dist/actions/implementations/viewAction.js.map +1 -1
- package/dist/actions/index.d.ts.map +1 -1
- package/dist/actions/index.js +24 -0
- package/dist/actions/index.js.map +1 -1
- package/dist/actions/merge/multiMergeOrchestrator.js +1 -1
- package/dist/actions/merge/multiMergeOrchestrator.js.map +1 -1
- package/dist/actions/types.d.ts +19 -4
- package/dist/actions/types.d.ts.map +1 -1
- package/dist/actions/types.js +91 -0
- package/dist/actions/types.js.map +1 -1
- package/dist/components/indicators/Spinner.d.ts +2 -0
- package/dist/components/indicators/Spinner.d.ts.map +1 -1
- package/dist/components/indicators/Spinner.js +4 -4
- package/dist/components/indicators/Spinner.js.map +1 -1
- package/dist/components/panes/KebabMenu.d.ts +2 -2
- package/dist/components/panes/KebabMenu.d.ts.map +1 -1
- package/dist/components/panes/KebabMenu.js +9 -4
- package/dist/components/panes/KebabMenu.js.map +1 -1
- package/dist/components/panes/PaneCard.d.ts.map +1 -1
- package/dist/components/panes/PaneCard.js +20 -4
- package/dist/components/panes/PaneCard.js.map +1 -1
- package/dist/components/panes/PanesGrid.d.ts +1 -0
- package/dist/components/panes/PanesGrid.d.ts.map +1 -1
- package/dist/components/panes/PanesGrid.js +11 -3
- package/dist/components/panes/PanesGrid.js.map +1 -1
- package/dist/components/popups/agentChoicePopup.js +29 -24
- package/dist/components/popups/agentChoicePopup.js.map +1 -1
- package/dist/components/popups/agentChoiceSelection.d.ts +8 -0
- package/dist/components/popups/agentChoiceSelection.d.ts.map +1 -0
- package/dist/components/popups/agentChoiceSelection.js +14 -0
- package/dist/components/popups/agentChoiceSelection.js.map +1 -0
- package/dist/components/popups/kebabMenuPopup.js +9 -4
- package/dist/components/popups/kebabMenuPopup.js.map +1 -1
- package/dist/components/popups/notificationSoundsPopup.d.ts +25 -0
- package/dist/components/popups/notificationSoundsPopup.d.ts.map +1 -0
- package/dist/components/popups/notificationSoundsPopup.js +165 -0
- package/dist/components/popups/notificationSoundsPopup.js.map +1 -0
- package/dist/components/popups/settingsPopup.js +361 -26
- package/dist/components/popups/settingsPopup.js.map +1 -1
- package/dist/components/popups/shortcutsPopup.js +11 -5
- package/dist/components/popups/shortcutsPopup.js.map +1 -1
- package/dist/constants/layout.d.ts +9 -0
- package/dist/constants/layout.d.ts.map +1 -0
- package/dist/constants/layout.js +9 -0
- package/dist/constants/layout.js.map +1 -0
- package/dist/hooks/useActionSystem.d.ts +9 -5
- package/dist/hooks/useActionSystem.d.ts.map +1 -1
- package/dist/hooks/useActionSystem.js +21 -19
- package/dist/hooks/useActionSystem.js.map +1 -1
- package/dist/hooks/useInputHandling.d.ts +1 -0
- package/dist/hooks/useInputHandling.d.ts.map +1 -1
- package/dist/hooks/useInputHandling.js +499 -79
- package/dist/hooks/useInputHandling.js.map +1 -1
- package/dist/hooks/usePaneCreation.d.ts +4 -2
- package/dist/hooks/usePaneCreation.d.ts.map +1 -1
- package/dist/hooks/usePaneCreation.js +6 -0
- package/dist/hooks/usePaneCreation.js.map +1 -1
- package/dist/hooks/usePaneLoading.d.ts +1 -0
- package/dist/hooks/usePaneLoading.d.ts.map +1 -1
- package/dist/hooks/usePaneLoading.js +10 -7
- package/dist/hooks/usePaneLoading.js.map +1 -1
- package/dist/hooks/usePaneSync.js +2 -2
- package/dist/hooks/usePaneSync.js.map +1 -1
- package/dist/hooks/usePanes.d.ts.map +1 -1
- package/dist/hooks/usePanes.js +18 -4
- package/dist/hooks/usePanes.js.map +1 -1
- package/dist/hooks/useProjectActivity.d.ts +7 -0
- package/dist/hooks/useProjectActivity.d.ts.map +1 -0
- package/dist/hooks/useProjectActivity.js +79 -0
- package/dist/hooks/useProjectActivity.js.map +1 -0
- package/dist/hooks/useServices.d.ts +3 -0
- package/dist/hooks/useServices.d.ts.map +1 -1
- package/dist/hooks/useServices.js +4 -0
- package/dist/hooks/useServices.js.map +1 -1
- package/dist/index.js +59 -15
- package/dist/index.js.map +1 -1
- package/dist/layout/LayoutCalculator.d.ts.map +1 -1
- package/dist/layout/LayoutCalculator.js +4 -1
- package/dist/layout/LayoutCalculator.js.map +1 -1
- package/dist/services/DmuxAttentionService.d.ts +33 -0
- package/dist/services/DmuxAttentionService.d.ts.map +1 -0
- package/dist/services/DmuxAttentionService.js +172 -0
- package/dist/services/DmuxAttentionService.js.map +1 -0
- package/dist/services/DmuxFocusService.d.ts +58 -0
- package/dist/services/DmuxFocusService.d.ts.map +1 -0
- package/dist/services/DmuxFocusService.js +671 -0
- package/dist/services/DmuxFocusService.js.map +1 -0
- package/dist/services/PaneAnalyzer.d.ts +11 -2
- package/dist/services/PaneAnalyzer.d.ts.map +1 -1
- package/dist/services/PaneAnalyzer.js +88 -22
- package/dist/services/PaneAnalyzer.js.map +1 -1
- package/dist/services/PopupManager.d.ts +31 -14
- package/dist/services/PopupManager.d.ts.map +1 -1
- package/dist/services/PopupManager.js +147 -68
- package/dist/services/PopupManager.js.map +1 -1
- package/dist/services/StatusDetector.d.ts +14 -0
- package/dist/services/StatusDetector.d.ts.map +1 -1
- package/dist/services/StatusDetector.js +60 -12
- package/dist/services/StatusDetector.js.map +1 -1
- package/dist/services/TmuxHookManager.d.ts.map +1 -1
- package/dist/services/TmuxHookManager.js +4 -2
- package/dist/services/TmuxHookManager.js.map +1 -1
- package/dist/services/TmuxService.d.ts +37 -2
- package/dist/services/TmuxService.d.ts.map +1 -1
- package/dist/services/TmuxService.js +138 -16
- package/dist/services/TmuxService.js.map +1 -1
- package/dist/types/activity.d.ts +4 -0
- package/dist/types/activity.d.ts.map +1 -0
- package/dist/types/activity.js +2 -0
- package/dist/types/activity.js.map +1 -0
- package/dist/types.d.ts +18 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/attachAgent.d.ts +4 -0
- package/dist/utils/attachAgent.d.ts.map +1 -1
- package/dist/utils/attachAgent.js +18 -7
- package/dist/utils/attachAgent.js.map +1 -1
- package/dist/utils/controlPaneRecovery.d.ts +2 -0
- package/dist/utils/controlPaneRecovery.d.ts.map +1 -0
- package/dist/utils/controlPaneRecovery.js +156 -0
- package/dist/utils/controlPaneRecovery.js.map +1 -0
- package/dist/utils/devWatchExit.d.ts +2 -0
- package/dist/utils/devWatchExit.d.ts.map +1 -0
- package/dist/utils/devWatchExit.js +10 -0
- package/dist/utils/devWatchExit.js.map +1 -0
- package/dist/utils/dmuxCommand.d.ts +3 -0
- package/dist/utils/dmuxCommand.d.ts.map +1 -0
- package/dist/utils/dmuxCommand.js +18 -0
- package/dist/utils/dmuxCommand.js.map +1 -0
- package/dist/utils/fileBrowser.d.ts +61 -0
- package/dist/utils/fileBrowser.d.ts.map +1 -0
- package/dist/utils/fileBrowser.js +567 -0
- package/dist/utils/fileBrowser.js.map +1 -0
- package/dist/utils/focusDetection.d.ts +38 -0
- package/dist/utils/focusDetection.d.ts.map +1 -0
- package/dist/utils/focusDetection.js +57 -0
- package/dist/utils/focusDetection.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/layoutManager.d.ts +5 -1
- package/dist/utils/layoutManager.d.ts.map +1 -1
- package/dist/utils/layoutManager.js +103 -26
- package/dist/utils/layoutManager.js.map +1 -1
- package/dist/utils/mergeTargets.d.ts +17 -0
- package/dist/utils/mergeTargets.d.ts.map +1 -0
- package/dist/utils/mergeTargets.js +132 -0
- package/dist/utils/mergeTargets.js.map +1 -0
- package/dist/utils/mergeValidation.d.ts.map +1 -1
- package/dist/utils/mergeValidation.js +12 -5
- package/dist/utils/mergeValidation.js.map +1 -1
- package/dist/utils/notificationSoundPreview.d.ts +10 -0
- package/dist/utils/notificationSoundPreview.d.ts.map +1 -0
- package/dist/utils/notificationSoundPreview.js +54 -0
- package/dist/utils/notificationSoundPreview.js.map +1 -0
- package/dist/utils/notificationSounds.d.ts +17 -0
- package/dist/utils/notificationSounds.d.ts.map +1 -0
- package/dist/utils/notificationSounds.js +123 -0
- package/dist/utils/notificationSounds.js.map +1 -0
- package/dist/utils/paneAttentionHeuristics.d.ts +4 -0
- package/dist/utils/paneAttentionHeuristics.d.ts.map +1 -0
- package/dist/utils/paneAttentionHeuristics.js +135 -0
- package/dist/utils/paneAttentionHeuristics.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 +23 -5
- package/dist/utils/paneCreation.js.map +1 -1
- package/dist/utils/paneVisibility.d.ts +12 -0
- package/dist/utils/paneVisibility.d.ts.map +1 -0
- package/dist/utils/paneVisibility.js +60 -0
- package/dist/utils/paneVisibility.js.map +1 -0
- package/dist/utils/processShutdown.d.ts +4 -0
- package/dist/utils/processShutdown.d.ts.map +1 -0
- package/dist/utils/processShutdown.js +27 -0
- package/dist/utils/processShutdown.js.map +1 -0
- package/dist/utils/promptStore.d.ts.map +1 -1
- package/dist/utils/promptStore.js +6 -0
- package/dist/utils/promptStore.js.map +1 -1
- package/dist/utils/reopenWorktree.d.ts.map +1 -1
- package/dist/utils/reopenWorktree.js +8 -0
- package/dist/utils/reopenWorktree.js.map +1 -1
- package/dist/utils/runtimePaths.d.ts +1 -0
- package/dist/utils/runtimePaths.d.ts.map +1 -1
- package/dist/utils/runtimePaths.js +3 -0
- package/dist/utils/runtimePaths.js.map +1 -1
- package/dist/utils/settingsManager.d.ts +3 -0
- package/dist/utils/settingsManager.d.ts.map +1 -1
- package/dist/utils/settingsManager.js +203 -11
- package/dist/utils/settingsManager.js.map +1 -1
- package/dist/utils/tmux.d.ts +5 -1
- package/dist/utils/tmux.d.ts.map +1 -1
- package/dist/utils/tmux.js +23 -5
- package/dist/utils/tmux.js.map +1 -1
- package/dist/utils/tmuxConfigOnboarding.js +1 -1
- package/dist/utils/tmuxConfigOnboarding.js.map +1 -1
- package/dist/utils/tmuxHookCommands.d.ts +14 -0
- package/dist/utils/tmuxHookCommands.d.ts.map +1 -0
- package/dist/utils/tmuxHookCommands.js +30 -0
- package/dist/utils/tmuxHookCommands.js.map +1 -0
- package/dist/utils/tmuxRuntimeCompatibility.d.ts +11 -0
- package/dist/utils/tmuxRuntimeCompatibility.d.ts.map +1 -0
- package/dist/utils/tmuxRuntimeCompatibility.js +71 -0
- package/dist/utils/tmuxRuntimeCompatibility.js.map +1 -0
- package/dist/utils/worktreeMetadata.d.ts +9 -0
- package/dist/utils/worktreeMetadata.d.ts.map +1 -0
- package/dist/utils/worktreeMetadata.js +60 -0
- package/dist/utils/worktreeMetadata.js.map +1 -0
- package/dist/workers/PaneWorker.js +64 -128
- package/dist/workers/PaneWorker.js.map +1 -1
- package/dist/workers/WorkerMessages.d.ts +4 -1
- package/dist/workers/WorkerMessages.d.ts.map +1 -1
- package/dist/workers/WorkerMessages.js.map +1 -1
- package/native/macos/dmux-helper-Info.plist +30 -0
- package/native/macos/dmux-helper-icon.png +0 -0
- package/native/macos/dmux-helper.swift +831 -0
- package/native/macos/sounds/dmux-braam.caf +0 -0
- package/native/macos/sounds/dmux-brass.caf +0 -0
- package/native/macos/sounds/dmux-ding-bell.caf +0 -0
- package/native/macos/sounds/dmux-future.caf +0 -0
- package/native/macos/sounds/dmux-harp.caf +0 -0
- package/native/macos/sounds/dmux-quiet-bells.caf +0 -0
- package/native/macos/sounds/dmux-sonar.caf +0 -0
- package/native/macos/sounds/dmux-success.caf +0 -0
- package/native/macos/sounds/dmux-triumphant-trumpet.caf +0 -0
- package/native/macos/sounds/dmux-war-horn.caf +0 -0
- package/package.json +3 -1
|
@@ -1,21 +1,52 @@
|
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
import path from "path";
|
|
1
3
|
import { useInput } from "ink";
|
|
2
4
|
import { StateManager } from "../shared/StateManager.js";
|
|
3
5
|
import { TmuxService } from "../services/TmuxService.js";
|
|
4
6
|
import { STATUS_MESSAGE_DURATION_SHORT, STATUS_MESSAGE_DURATION_LONG, ANIMATION_DELAY, } from "../constants/timing.js";
|
|
5
|
-
import { PaneAction } from "../actions/index.js";
|
|
7
|
+
import { isPaneAction, PaneAction, TOGGLE_PANE_VISIBILITY_ACTION, } from "../actions/index.js";
|
|
6
8
|
import { getMainBranch, getOrphanedWorktrees } from "../utils/git.js";
|
|
7
9
|
import { enforceControlPaneSize } from "../utils/tmux.js";
|
|
8
10
|
import { SIDEBAR_WIDTH } from "../utils/layoutManager.js";
|
|
9
11
|
import { suggestCommand } from "../utils/commands.js";
|
|
10
|
-
import { getPaneProjectRoot } from "../utils/paneProject.js";
|
|
12
|
+
import { getPaneProjectName, getPaneProjectRoot } from "../utils/paneProject.js";
|
|
11
13
|
import { getProjectActionByIndex, } from "../utils/projectActions.js";
|
|
12
14
|
import { createShellPane, getNextDmuxId } from "../utils/shellPaneDetection.js";
|
|
15
|
+
import { getBulkVisibilityAction, getProjectVisibilityAction, partitionPanesByProject, } from "../utils/paneVisibility.js";
|
|
16
|
+
import { buildFilesOnlyCommand } from "../utils/dmuxCommand.js";
|
|
13
17
|
/**
|
|
14
18
|
* Hook that handles all keyboard input for the TUI
|
|
15
19
|
* Extracted from DmuxApp.tsx to reduce component complexity
|
|
16
20
|
*/
|
|
17
21
|
export function useInputHandling(params) {
|
|
18
|
-
const { panes, selectedIndex, setSelectedIndex, isCreatingPane, setIsCreatingPane, runningCommand, isUpdating, isLoading, ignoreInput, isDevMode, quitConfirmMode, setQuitConfirmMode, showCommandPrompt, setShowCommandPrompt, commandInput, setCommandInput, showFileCopyPrompt, setShowFileCopyPrompt, currentCommandType, setCurrentCommandType, projectSettings, saveSettings, settingsManager, popupManager, actionSystem, controlPaneId, setStatusMessage, copyNonGitFiles, runCommandInternal, handlePaneCreationWithAgent, handleReopenWorktree, setDevSourceFromPane, savePanes, loadPanes, cleanExit, availableAgents, panesFile, projectRoot, projectActionItems, findCardInDirection, } = params;
|
|
22
|
+
const { panes, selectedIndex, setSelectedIndex, isCreatingPane, setIsCreatingPane, runningCommand, isUpdating, isLoading, ignoreInput, isDevMode, quitConfirmMode, setQuitConfirmMode, showCommandPrompt, setShowCommandPrompt, commandInput, setCommandInput, showFileCopyPrompt, setShowFileCopyPrompt, currentCommandType, setCurrentCommandType, projectSettings, saveSettings, settingsManager, popupManager, actionSystem, controlPaneId, setStatusMessage, copyNonGitFiles, runCommandInternal, handlePaneCreationWithAgent, handleCreateChildWorktree, handleReopenWorktree, setDevSourceFromPane, savePanes, loadPanes, cleanExit, availableAgents, panesFile, projectRoot, projectActionItems, findCardInDirection, } = params;
|
|
23
|
+
const layoutRefreshDebounceRef = useRef(null);
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
return () => {
|
|
26
|
+
if (layoutRefreshDebounceRef.current) {
|
|
27
|
+
clearTimeout(layoutRefreshDebounceRef.current);
|
|
28
|
+
layoutRefreshDebounceRef.current = null;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}, []);
|
|
32
|
+
const queueLayoutRefresh = () => {
|
|
33
|
+
if (!controlPaneId) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (layoutRefreshDebounceRef.current) {
|
|
37
|
+
clearTimeout(layoutRefreshDebounceRef.current);
|
|
38
|
+
}
|
|
39
|
+
layoutRefreshDebounceRef.current = setTimeout(async () => {
|
|
40
|
+
layoutRefreshDebounceRef.current = null;
|
|
41
|
+
try {
|
|
42
|
+
await enforceControlPaneSize(controlPaneId, SIDEBAR_WIDTH, { forceLayout: true });
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
setStatusMessage(`Setting saved but layout refresh failed: ${error?.message || String(error)}`);
|
|
46
|
+
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_LONG);
|
|
47
|
+
}
|
|
48
|
+
}, 250);
|
|
49
|
+
};
|
|
19
50
|
const handleCreateAgentPane = async (targetProjectRoot) => {
|
|
20
51
|
const promptValue = await popupManager.launchNewPanePopup(targetProjectRoot);
|
|
21
52
|
if (promptValue) {
|
|
@@ -46,13 +77,105 @@ export function useInputHandling(params) {
|
|
|
46
77
|
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_LONG);
|
|
47
78
|
}
|
|
48
79
|
};
|
|
80
|
+
const openTerminalInWorktree = async (selectedPane) => {
|
|
81
|
+
if (!selectedPane.worktreePath) {
|
|
82
|
+
setStatusMessage("Cannot open terminal: this pane has no worktree");
|
|
83
|
+
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_SHORT);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const targetProjectRoot = getPaneProjectRoot(selectedPane, projectRoot);
|
|
87
|
+
try {
|
|
88
|
+
setIsCreatingPane(true);
|
|
89
|
+
setStatusMessage(`Opening terminal in ${selectedPane.slug}...`);
|
|
90
|
+
const tmuxService = TmuxService.getInstance();
|
|
91
|
+
const newPaneId = await tmuxService.splitPane({ cwd: selectedPane.worktreePath });
|
|
92
|
+
// Wait for pane creation to settle
|
|
93
|
+
await new Promise((resolve) => setTimeout(resolve, ANIMATION_DELAY));
|
|
94
|
+
const shellPane = await createShellPane(newPaneId, getNextDmuxId(panes));
|
|
95
|
+
shellPane.projectRoot = targetProjectRoot;
|
|
96
|
+
await savePanes([...panes, shellPane]);
|
|
97
|
+
setStatusMessage(`Opened terminal in ${selectedPane.slug}`);
|
|
98
|
+
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_SHORT);
|
|
99
|
+
// Force a reload to ensure tmux metadata and pane IDs are in sync
|
|
100
|
+
await loadPanes();
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
setStatusMessage(`Failed to open terminal in worktree: ${error.message}`);
|
|
104
|
+
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_LONG);
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
setIsCreatingPane(false);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
const openFileBrowserInWorktree = async (selectedPane) => {
|
|
111
|
+
if (!selectedPane.worktreePath) {
|
|
112
|
+
setStatusMessage("Cannot open file browser: this pane has no worktree");
|
|
113
|
+
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_SHORT);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const existingBrowserPane = panes.find((pane) => pane.browserPath === selectedPane.worktreePath && !pane.hidden);
|
|
117
|
+
if (existingBrowserPane) {
|
|
118
|
+
try {
|
|
119
|
+
await TmuxService.getInstance().selectPane(existingBrowserPane.paneId);
|
|
120
|
+
setStatusMessage(`File browser already open for ${selectedPane.slug}`);
|
|
121
|
+
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_SHORT);
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
setStatusMessage(`Failed to focus file browser: ${error?.message || String(error)}`);
|
|
125
|
+
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_LONG);
|
|
126
|
+
}
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const targetProjectRoot = getPaneProjectRoot(selectedPane, projectRoot);
|
|
130
|
+
const targetProjectName = path.basename(targetProjectRoot);
|
|
131
|
+
try {
|
|
132
|
+
setIsCreatingPane(true);
|
|
133
|
+
setStatusMessage(`Opening file browser for ${selectedPane.slug}...`);
|
|
134
|
+
const tmuxService = TmuxService.getInstance();
|
|
135
|
+
const newPaneId = await tmuxService.splitPane({
|
|
136
|
+
cwd: selectedPane.worktreePath,
|
|
137
|
+
command: buildFilesOnlyCommand(),
|
|
138
|
+
});
|
|
139
|
+
await new Promise((resolve) => setTimeout(resolve, ANIMATION_DELAY));
|
|
140
|
+
const slugBase = `files-${path.basename(selectedPane.worktreePath)}`;
|
|
141
|
+
let slug = slugBase;
|
|
142
|
+
let suffix = 2;
|
|
143
|
+
while (panes.some((pane) => pane.slug === slug)) {
|
|
144
|
+
slug = `${slugBase}-${suffix}`;
|
|
145
|
+
suffix += 1;
|
|
146
|
+
}
|
|
147
|
+
const browserPane = {
|
|
148
|
+
id: `dmux-${getNextDmuxId(panes)}`,
|
|
149
|
+
slug,
|
|
150
|
+
prompt: "",
|
|
151
|
+
paneId: newPaneId,
|
|
152
|
+
projectRoot: targetProjectRoot,
|
|
153
|
+
projectName: targetProjectName,
|
|
154
|
+
type: "shell",
|
|
155
|
+
shellType: "fb",
|
|
156
|
+
browserPath: selectedPane.worktreePath,
|
|
157
|
+
};
|
|
158
|
+
await tmuxService.setPaneTitle(newPaneId, slug);
|
|
159
|
+
await savePanes([...panes, browserPane]);
|
|
160
|
+
await loadPanes();
|
|
161
|
+
setStatusMessage(`Opened file browser for ${selectedPane.slug}`);
|
|
162
|
+
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_SHORT);
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
setStatusMessage(`Failed to open file browser: ${error?.message || String(error)}`);
|
|
166
|
+
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_LONG);
|
|
167
|
+
}
|
|
168
|
+
finally {
|
|
169
|
+
setIsCreatingPane(false);
|
|
170
|
+
}
|
|
171
|
+
};
|
|
49
172
|
const handleCreatePaneInProject = async () => {
|
|
50
173
|
const selectedAction = getProjectActionByIndex(projectActionItems, selectedIndex);
|
|
51
174
|
const selectedPane = selectedIndex < panes.length ? panes[selectedIndex] : undefined;
|
|
52
175
|
const defaultProjectPath = selectedPane
|
|
53
176
|
? getPaneProjectRoot(selectedPane, projectRoot)
|
|
54
177
|
: (selectedAction?.projectRoot || projectRoot);
|
|
55
|
-
const requestedProjectPath = await popupManager.launchProjectSelectPopup(defaultProjectPath);
|
|
178
|
+
const requestedProjectPath = await popupManager.launchProjectSelectPopup(defaultProjectPath, defaultProjectPath);
|
|
56
179
|
if (!requestedProjectPath) {
|
|
57
180
|
return;
|
|
58
181
|
}
|
|
@@ -85,19 +208,309 @@ export function useInputHandling(params) {
|
|
|
85
208
|
const prompt = "I would like to create or edit my dmux hooks in .dmux-hooks. Please read AGENTS.md or CLAUDE.md first, then ask me what I want to create or modify.";
|
|
86
209
|
await handlePaneCreationWithAgent(prompt, hooksProjectRoot);
|
|
87
210
|
};
|
|
211
|
+
const refreshPaneLayout = async () => {
|
|
212
|
+
if (!controlPaneId) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
await enforceControlPaneSize(controlPaneId, SIDEBAR_WIDTH, {
|
|
216
|
+
forceLayout: true,
|
|
217
|
+
suppressLayoutLogs: true,
|
|
218
|
+
});
|
|
219
|
+
};
|
|
220
|
+
const getPaneShowTarget = async (excludedPaneId) => {
|
|
221
|
+
const visiblePaneId = panes.find((pane) => !pane.hidden && pane.paneId !== excludedPaneId)?.paneId;
|
|
222
|
+
if (visiblePaneId) {
|
|
223
|
+
return visiblePaneId;
|
|
224
|
+
}
|
|
225
|
+
if (controlPaneId) {
|
|
226
|
+
return controlPaneId;
|
|
227
|
+
}
|
|
228
|
+
try {
|
|
229
|
+
return await TmuxService.getInstance().getCurrentPaneId();
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
const togglePaneVisibility = async (selectedPane) => {
|
|
236
|
+
const tmuxService = TmuxService.getInstance();
|
|
237
|
+
try {
|
|
238
|
+
setIsCreatingPane(true);
|
|
239
|
+
setStatusMessage(selectedPane.hidden ? `Showing ${selectedPane.slug}...` : `Hiding ${selectedPane.slug}...`);
|
|
240
|
+
if (selectedPane.hidden) {
|
|
241
|
+
const targetPaneId = await getPaneShowTarget(selectedPane.paneId);
|
|
242
|
+
if (!targetPaneId) {
|
|
243
|
+
throw new Error("No target pane is available to show this pane");
|
|
244
|
+
}
|
|
245
|
+
await tmuxService.joinPaneToTarget(selectedPane.paneId, targetPaneId);
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
await tmuxService.breakPaneToWindow(selectedPane.paneId, `dmux-hidden-${selectedPane.id}`);
|
|
249
|
+
}
|
|
250
|
+
await savePanes(panes.map((pane) => pane.id === selectedPane.id
|
|
251
|
+
? { ...pane, hidden: !selectedPane.hidden }
|
|
252
|
+
: pane));
|
|
253
|
+
await refreshPaneLayout();
|
|
254
|
+
await loadPanes();
|
|
255
|
+
setStatusMessage(selectedPane.hidden
|
|
256
|
+
? `Showing ${selectedPane.slug}`
|
|
257
|
+
: `Hid ${selectedPane.slug}`);
|
|
258
|
+
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_SHORT);
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
setStatusMessage(`Failed to toggle pane visibility: ${error?.message || String(error)}`);
|
|
262
|
+
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_LONG);
|
|
263
|
+
}
|
|
264
|
+
finally {
|
|
265
|
+
setIsCreatingPane(false);
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
const toggleOtherPanesVisibility = async (selectedPane) => {
|
|
269
|
+
const action = getBulkVisibilityAction(panes, selectedPane);
|
|
270
|
+
if (!action) {
|
|
271
|
+
setStatusMessage("No other panes to toggle");
|
|
272
|
+
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_SHORT);
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
const targetPanes = panes.filter((pane) => pane.id !== selectedPane.id
|
|
276
|
+
&& (action === "hide-others" ? !pane.hidden : pane.hidden));
|
|
277
|
+
if (targetPanes.length === 0) {
|
|
278
|
+
setStatusMessage("No other panes to toggle");
|
|
279
|
+
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_SHORT);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
const tmuxService = TmuxService.getInstance();
|
|
283
|
+
const hidden = action === "hide-others";
|
|
284
|
+
try {
|
|
285
|
+
setIsCreatingPane(true);
|
|
286
|
+
setStatusMessage(hidden ? "Hiding other panes..." : "Showing other panes...");
|
|
287
|
+
for (const pane of targetPanes) {
|
|
288
|
+
if (hidden) {
|
|
289
|
+
await tmuxService.breakPaneToWindow(pane.paneId, `dmux-hidden-${pane.id}`);
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
const targetPaneId = await getPaneShowTarget(pane.paneId);
|
|
293
|
+
if (!targetPaneId) {
|
|
294
|
+
throw new Error("No target pane is available to show hidden panes");
|
|
295
|
+
}
|
|
296
|
+
await tmuxService.joinPaneToTarget(pane.paneId, targetPaneId);
|
|
297
|
+
}
|
|
298
|
+
const targetPaneIds = new Set(targetPanes.map((pane) => pane.id));
|
|
299
|
+
await savePanes(panes.map((pane) => targetPaneIds.has(pane.id) ? { ...pane, hidden } : pane));
|
|
300
|
+
await refreshPaneLayout();
|
|
301
|
+
await loadPanes();
|
|
302
|
+
setStatusMessage(hidden
|
|
303
|
+
? `Hid ${targetPanes.length} other pane${targetPanes.length === 1 ? "" : "s"}`
|
|
304
|
+
: `Showed ${targetPanes.length} other pane${targetPanes.length === 1 ? "" : "s"}`);
|
|
305
|
+
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_SHORT);
|
|
306
|
+
}
|
|
307
|
+
catch (error) {
|
|
308
|
+
setStatusMessage(`Failed to toggle other panes: ${error?.message || String(error)}`);
|
|
309
|
+
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_LONG);
|
|
310
|
+
}
|
|
311
|
+
finally {
|
|
312
|
+
setIsCreatingPane(false);
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
const toggleProjectPanesVisibility = async (targetProjectRoot = getActiveProjectRoot()) => {
|
|
316
|
+
const action = getProjectVisibilityAction(panes, targetProjectRoot, projectRoot);
|
|
317
|
+
if (!action) {
|
|
318
|
+
setStatusMessage("No project panes to toggle");
|
|
319
|
+
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_SHORT);
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
const { projectPanes, otherPanes } = partitionPanesByProject(panes, targetProjectRoot, projectRoot);
|
|
323
|
+
if (projectPanes.length === 0) {
|
|
324
|
+
setStatusMessage("No project panes to toggle");
|
|
325
|
+
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_SHORT);
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
const projectName = getPaneProjectName(projectPanes[0], projectRoot);
|
|
329
|
+
const panesToShow = action === "focus-project"
|
|
330
|
+
? projectPanes.filter((pane) => pane.hidden)
|
|
331
|
+
: panes.filter((pane) => pane.hidden);
|
|
332
|
+
const panesToHide = action === "focus-project"
|
|
333
|
+
? otherPanes.filter((pane) => !pane.hidden)
|
|
334
|
+
: [];
|
|
335
|
+
try {
|
|
336
|
+
setIsCreatingPane(true);
|
|
337
|
+
setStatusMessage(action === "focus-project"
|
|
338
|
+
? `Showing ${projectName} panes...`
|
|
339
|
+
: "Showing all panes...");
|
|
340
|
+
// Show target project panes before hiding others so we always have
|
|
341
|
+
// an attached pane available for tmux join targets.
|
|
342
|
+
for (const pane of panesToShow) {
|
|
343
|
+
const targetPaneId = await getPaneShowTarget(pane.paneId);
|
|
344
|
+
if (!targetPaneId) {
|
|
345
|
+
throw new Error("No target pane is available to show hidden panes");
|
|
346
|
+
}
|
|
347
|
+
await TmuxService.getInstance().joinPaneToTarget(pane.paneId, targetPaneId);
|
|
348
|
+
}
|
|
349
|
+
for (const pane of panesToHide) {
|
|
350
|
+
await TmuxService.getInstance().breakPaneToWindow(pane.paneId, `dmux-hidden-${pane.id}`);
|
|
351
|
+
}
|
|
352
|
+
const shownPaneIds = new Set(panesToShow.map((pane) => pane.id));
|
|
353
|
+
const hiddenPaneIds = new Set(panesToHide.map((pane) => pane.id));
|
|
354
|
+
await savePanes(panes.map((pane) => {
|
|
355
|
+
if (shownPaneIds.has(pane.id)) {
|
|
356
|
+
return { ...pane, hidden: false };
|
|
357
|
+
}
|
|
358
|
+
if (hiddenPaneIds.has(pane.id)) {
|
|
359
|
+
return { ...pane, hidden: true };
|
|
360
|
+
}
|
|
361
|
+
return pane;
|
|
362
|
+
}));
|
|
363
|
+
await refreshPaneLayout();
|
|
364
|
+
await loadPanes();
|
|
365
|
+
setStatusMessage(action === "focus-project"
|
|
366
|
+
? panesToHide.length > 0
|
|
367
|
+
? `Showing only ${projectName} panes`
|
|
368
|
+
: `Showed ${projectName} panes`
|
|
369
|
+
: "Showed all panes");
|
|
370
|
+
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_SHORT);
|
|
371
|
+
}
|
|
372
|
+
catch (error) {
|
|
373
|
+
setStatusMessage(`Failed to toggle project panes: ${error?.message || String(error)}`);
|
|
374
|
+
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_LONG);
|
|
375
|
+
}
|
|
376
|
+
finally {
|
|
377
|
+
setIsCreatingPane(false);
|
|
378
|
+
}
|
|
379
|
+
};
|
|
88
380
|
const openPaneMenu = async (pane) => {
|
|
89
|
-
const actionId = await popupManager.launchKebabMenuPopup(pane);
|
|
381
|
+
const actionId = await popupManager.launchKebabMenuPopup(pane, panes);
|
|
90
382
|
if (!actionId) {
|
|
91
383
|
return;
|
|
92
384
|
}
|
|
385
|
+
if (actionId === TOGGLE_PANE_VISIBILITY_ACTION) {
|
|
386
|
+
await togglePaneVisibility(pane);
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
if (actionId === "hide-others" || actionId === "show-others") {
|
|
390
|
+
await toggleOtherPanesVisibility(pane);
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
if (actionId === "focus-project" || actionId === "show-all") {
|
|
394
|
+
await toggleProjectPanesVisibility(getPaneProjectRoot(pane, projectRoot));
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
93
397
|
if (actionId === PaneAction.SET_SOURCE) {
|
|
94
398
|
await setDevSourceFromPane(pane);
|
|
95
399
|
return;
|
|
96
400
|
}
|
|
401
|
+
if (actionId === PaneAction.ATTACH_AGENT) {
|
|
402
|
+
await attachAgentsToPane(pane);
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
if (actionId === PaneAction.CREATE_CHILD_WORKTREE) {
|
|
406
|
+
await handleCreateChildWorktree(pane);
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
if (actionId === PaneAction.OPEN_TERMINAL_IN_WORKTREE) {
|
|
410
|
+
await openTerminalInWorktree(pane);
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
if (actionId === PaneAction.OPEN_FILE_BROWSER) {
|
|
414
|
+
await openFileBrowserInWorktree(pane);
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
if (!isPaneAction(actionId)) {
|
|
418
|
+
setStatusMessage(`Unknown menu action: ${actionId}`);
|
|
419
|
+
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_LONG);
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
97
422
|
await actionSystem.executeAction(actionId, pane, {
|
|
98
423
|
mainBranch: getMainBranch(),
|
|
99
424
|
});
|
|
100
425
|
};
|
|
426
|
+
const attachAgentsToPane = async (selectedPane) => {
|
|
427
|
+
if (!selectedPane.worktreePath) {
|
|
428
|
+
setStatusMessage("Cannot attach agent: this pane has no worktree");
|
|
429
|
+
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_SHORT);
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
const targetProjectRoot = getPaneProjectRoot(selectedPane, projectRoot);
|
|
433
|
+
// Warn if agent is actively working
|
|
434
|
+
if (selectedPane.agentStatus === "working") {
|
|
435
|
+
const confirmed = await popupManager.launchConfirmPopup("Agent Active", `Agent in "${selectedPane.slug}" is currently working. Attach another agent anyway?`, "Attach", "Cancel", targetProjectRoot);
|
|
436
|
+
if (!confirmed)
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
let selectedAgents = [];
|
|
440
|
+
if (availableAgents.length === 0) {
|
|
441
|
+
setStatusMessage("No agents available");
|
|
442
|
+
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_SHORT);
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
else if (availableAgents.length === 1) {
|
|
446
|
+
selectedAgents = [availableAgents[0]];
|
|
447
|
+
}
|
|
448
|
+
else {
|
|
449
|
+
const agents = await popupManager.launchAgentChoicePopup(targetProjectRoot);
|
|
450
|
+
if (agents === null) {
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
if (agents.length === 0) {
|
|
454
|
+
setStatusMessage("Select at least one agent");
|
|
455
|
+
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_SHORT);
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
selectedAgents = agents;
|
|
459
|
+
}
|
|
460
|
+
// Prompt input
|
|
461
|
+
const promptValue = await popupManager.launchNewPanePopup(targetProjectRoot);
|
|
462
|
+
if (!promptValue)
|
|
463
|
+
return;
|
|
464
|
+
try {
|
|
465
|
+
setIsCreatingPane(true);
|
|
466
|
+
setStatusMessage(selectedAgents.length > 1
|
|
467
|
+
? `Attaching ${selectedAgents.length} agents...`
|
|
468
|
+
: "Attaching agent...");
|
|
469
|
+
const { attachAgentToWorktree } = await import("../utils/attachAgent.js");
|
|
470
|
+
const createdPanes = [];
|
|
471
|
+
const failedAgents = [];
|
|
472
|
+
for (const agent of selectedAgents) {
|
|
473
|
+
try {
|
|
474
|
+
const result = await attachAgentToWorktree({
|
|
475
|
+
targetPane: selectedPane,
|
|
476
|
+
prompt: promptValue,
|
|
477
|
+
agent,
|
|
478
|
+
existingPanes: [...panes, ...createdPanes],
|
|
479
|
+
sessionProjectRoot: projectRoot,
|
|
480
|
+
sessionConfigPath: panesFile,
|
|
481
|
+
});
|
|
482
|
+
createdPanes.push(result.pane);
|
|
483
|
+
}
|
|
484
|
+
catch {
|
|
485
|
+
failedAgents.push(agent);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
if (createdPanes.length > 0) {
|
|
489
|
+
const updatedPanes = [...panes, ...createdPanes];
|
|
490
|
+
await savePanes(updatedPanes);
|
|
491
|
+
await loadPanes();
|
|
492
|
+
}
|
|
493
|
+
if (failedAgents.length === 0) {
|
|
494
|
+
setStatusMessage(`Attached ${createdPanes.length} agent${createdPanes.length === 1 ? "" : "s"} to ${selectedPane.slug}`);
|
|
495
|
+
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_SHORT);
|
|
496
|
+
}
|
|
497
|
+
else if (createdPanes.length === 0) {
|
|
498
|
+
setStatusMessage(`Failed to attach agents: ${failedAgents.join(", ")}`);
|
|
499
|
+
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_LONG);
|
|
500
|
+
}
|
|
501
|
+
else {
|
|
502
|
+
setStatusMessage(`Attached ${createdPanes.length}/${selectedAgents.length} agents to ${selectedPane.slug} (${failedAgents.length} failed)`);
|
|
503
|
+
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_LONG);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
catch (error) {
|
|
507
|
+
setStatusMessage(`Failed to attach agent: ${error.message}`);
|
|
508
|
+
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_LONG);
|
|
509
|
+
}
|
|
510
|
+
finally {
|
|
511
|
+
setIsCreatingPane(false);
|
|
512
|
+
}
|
|
513
|
+
};
|
|
101
514
|
useInput(async (input, key) => {
|
|
102
515
|
// Ignore input temporarily after popup operations (prevents buffered keys from being processed)
|
|
103
516
|
if (ignoreInput) {
|
|
@@ -232,67 +645,19 @@ export function useInputHandling(params) {
|
|
|
232
645
|
return;
|
|
233
646
|
}
|
|
234
647
|
if (input === "a" && selectedIndex < panes.length) {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
// Agent choice (single agent only, no A/B pairs)
|
|
249
|
-
let chosenAgent = null;
|
|
250
|
-
if (availableAgents.length === 0) {
|
|
251
|
-
setStatusMessage("No agents available");
|
|
252
|
-
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_SHORT);
|
|
253
|
-
return;
|
|
254
|
-
}
|
|
255
|
-
else if (availableAgents.length === 1) {
|
|
256
|
-
chosenAgent = availableAgents[0];
|
|
257
|
-
}
|
|
258
|
-
else {
|
|
259
|
-
const agents = await popupManager.launchAgentChoicePopup();
|
|
260
|
-
if (agents && agents.length > 0) {
|
|
261
|
-
chosenAgent = agents[0]; // Single agent for attach (no A/B)
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
if (!chosenAgent)
|
|
265
|
-
return;
|
|
266
|
-
// Prompt input
|
|
267
|
-
const promptValue = await popupManager.launchNewPanePopup(getPaneProjectRoot(selectedPane, projectRoot));
|
|
268
|
-
if (!promptValue)
|
|
269
|
-
return;
|
|
270
|
-
// Attach agent to worktree
|
|
271
|
-
try {
|
|
272
|
-
setIsCreatingPane(true);
|
|
273
|
-
setStatusMessage("Attaching agent...");
|
|
274
|
-
const { attachAgentToWorktree } = await import("../utils/attachAgent.js");
|
|
275
|
-
const result = await attachAgentToWorktree({
|
|
276
|
-
targetPane: selectedPane,
|
|
277
|
-
prompt: promptValue,
|
|
278
|
-
agent: chosenAgent,
|
|
279
|
-
existingPanes: panes,
|
|
280
|
-
sessionProjectRoot: projectRoot,
|
|
281
|
-
sessionConfigPath: panesFile,
|
|
282
|
-
});
|
|
283
|
-
const updatedPanes = [...panes, result.pane];
|
|
284
|
-
await savePanes(updatedPanes);
|
|
285
|
-
await loadPanes();
|
|
286
|
-
setStatusMessage(`Attached ${chosenAgent} to ${selectedPane.slug}`);
|
|
287
|
-
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_SHORT);
|
|
288
|
-
}
|
|
289
|
-
catch (error) {
|
|
290
|
-
setStatusMessage(`Failed to attach agent: ${error.message}`);
|
|
291
|
-
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_LONG);
|
|
292
|
-
}
|
|
293
|
-
finally {
|
|
294
|
-
setIsCreatingPane(false);
|
|
295
|
-
}
|
|
648
|
+
await attachAgentsToPane(panes[selectedIndex]);
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
else if (input === "b" && selectedIndex < panes.length) {
|
|
652
|
+
await handleCreateChildWorktree(panes[selectedIndex]);
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
else if (input === "f" && selectedIndex < panes.length) {
|
|
656
|
+
await openFileBrowserInWorktree(panes[selectedIndex]);
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
else if (input === "A" && selectedIndex < panes.length) {
|
|
660
|
+
await openTerminalInWorktree(panes[selectedIndex]);
|
|
296
661
|
return;
|
|
297
662
|
}
|
|
298
663
|
else if (input === "m" && selectedIndex < panes.length) {
|
|
@@ -306,34 +671,89 @@ export function useInputHandling(params) {
|
|
|
306
671
|
// Launch hooks popup
|
|
307
672
|
await popupManager.launchHooksPopup(async () => {
|
|
308
673
|
await launchHooksAuthoringSession();
|
|
309
|
-
});
|
|
310
|
-
});
|
|
674
|
+
}, getActiveProjectRoot());
|
|
675
|
+
}, getActiveProjectRoot());
|
|
311
676
|
if (result) {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
677
|
+
try {
|
|
678
|
+
const updates = Array.isArray(result.updates)
|
|
679
|
+
? result.updates
|
|
680
|
+
: [result];
|
|
681
|
+
let savedCount = 0;
|
|
682
|
+
let layoutBoundsUpdated = false;
|
|
683
|
+
let lastScope = null;
|
|
684
|
+
for (const update of updates) {
|
|
685
|
+
if (!update
|
|
686
|
+
|| typeof update.key !== "string"
|
|
687
|
+
|| (update.scope !== "global" && update.scope !== "project")) {
|
|
688
|
+
continue;
|
|
689
|
+
}
|
|
690
|
+
settingsManager.updateSetting(update.key, update.value, update.scope);
|
|
691
|
+
savedCount += 1;
|
|
692
|
+
lastScope = update.scope;
|
|
693
|
+
if (update.key === "minPaneWidth" || update.key === "maxPaneWidth") {
|
|
694
|
+
layoutBoundsUpdated = true;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
if (layoutBoundsUpdated) {
|
|
698
|
+
queueLayoutRefresh();
|
|
699
|
+
}
|
|
700
|
+
if (savedCount > 0) {
|
|
701
|
+
const statusMessage = savedCount === 1
|
|
702
|
+
? `Setting saved (${lastScope})`
|
|
703
|
+
: `${savedCount} settings saved`;
|
|
704
|
+
setStatusMessage(statusMessage);
|
|
705
|
+
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_SHORT);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
catch (error) {
|
|
709
|
+
setStatusMessage(`Failed to save setting: ${error?.message || String(error)}`);
|
|
710
|
+
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_LONG);
|
|
711
|
+
}
|
|
315
712
|
}
|
|
316
713
|
}
|
|
317
714
|
else if (input === "l") {
|
|
318
715
|
// Open logs popup
|
|
319
|
-
await popupManager.launchLogsPopup();
|
|
716
|
+
await popupManager.launchLogsPopup(getActiveProjectRoot());
|
|
320
717
|
}
|
|
321
718
|
else if (input === "h") {
|
|
322
|
-
|
|
323
|
-
|
|
719
|
+
if (selectedIndex < panes.length) {
|
|
720
|
+
await togglePaneVisibility(panes[selectedIndex]);
|
|
721
|
+
}
|
|
722
|
+
else {
|
|
723
|
+
setStatusMessage("Select a pane to toggle visibility");
|
|
724
|
+
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_SHORT);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
else if (input === "H") {
|
|
728
|
+
if (selectedIndex < panes.length) {
|
|
729
|
+
await toggleOtherPanesVisibility(panes[selectedIndex]);
|
|
730
|
+
}
|
|
731
|
+
else {
|
|
732
|
+
setStatusMessage("Select a pane to toggle the others");
|
|
733
|
+
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_SHORT);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
else if (input === "P") {
|
|
737
|
+
await toggleProjectPanesVisibility();
|
|
324
738
|
}
|
|
325
739
|
else if (input === "?") {
|
|
326
740
|
// Open keyboard shortcuts popup
|
|
327
|
-
const shortcutsAction = await popupManager.launchShortcutsPopup(!!controlPaneId);
|
|
741
|
+
const shortcutsAction = await popupManager.launchShortcutsPopup(!!controlPaneId, getActiveProjectRoot());
|
|
328
742
|
if (shortcutsAction === "hooks") {
|
|
329
743
|
await launchHooksAuthoringSession();
|
|
330
744
|
}
|
|
331
745
|
}
|
|
332
746
|
else if (input === "L" && controlPaneId) {
|
|
333
747
|
// Reset layout to sidebar configuration (Shift+L)
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
748
|
+
try {
|
|
749
|
+
await enforceControlPaneSize(controlPaneId, SIDEBAR_WIDTH, { forceLayout: true });
|
|
750
|
+
setStatusMessage("Layout reset");
|
|
751
|
+
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_SHORT);
|
|
752
|
+
}
|
|
753
|
+
catch (error) {
|
|
754
|
+
setStatusMessage(`Failed to reset layout: ${error?.message || String(error)}`);
|
|
755
|
+
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_LONG);
|
|
756
|
+
}
|
|
337
757
|
}
|
|
338
758
|
else if (input === "T") {
|
|
339
759
|
// Demo toasts (Shift+T) - cycles through different types
|
|
@@ -369,7 +789,7 @@ export function useInputHandling(params) {
|
|
|
369
789
|
setTimeout(() => setStatusMessage(""), STATUS_MESSAGE_DURATION_SHORT);
|
|
370
790
|
return;
|
|
371
791
|
}
|
|
372
|
-
const result = await popupManager.launchReopenWorktreePopup(orphanedWorktrees);
|
|
792
|
+
const result = await popupManager.launchReopenWorktreePopup(orphanedWorktrees, targetProjectRoot);
|
|
373
793
|
if (result) {
|
|
374
794
|
await handleReopenWorktree(result.slug, result.path, targetProjectRoot);
|
|
375
795
|
}
|