dmux 2.2.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/DmuxApp.d.ts.map +1 -1
- package/dist/DmuxApp.js +412 -179
- package/dist/DmuxApp.js.map +1 -1
- package/dist/MergePane.d.ts.map +1 -1
- package/dist/MergePane.js +4 -15
- package/dist/MergePane.js.map +1 -1
- package/dist/PaneAnalyzer.d.ts +45 -0
- package/dist/PaneAnalyzer.d.ts.map +1 -0
- package/dist/PaneAnalyzer.js +278 -0
- package/dist/PaneAnalyzer.js.map +1 -0
- package/dist/_plugin-vue_export-helper.css +1 -0
- package/dist/actions/index.d.ts +19 -0
- package/dist/actions/index.d.ts.map +1 -0
- package/dist/actions/index.js +54 -0
- package/dist/actions/index.js.map +1 -0
- package/dist/actions/paneActions.d.ts +45 -0
- package/dist/actions/paneActions.d.ts.map +1 -0
- package/dist/actions/paneActions.js +932 -0
- package/dist/actions/paneActions.js.map +1 -0
- package/dist/actions/types.d.ts +101 -0
- package/dist/actions/types.d.ts.map +1 -0
- package/dist/actions/types.js +129 -0
- package/dist/actions/types.js.map +1 -0
- package/dist/adapters/apiActionHandler.d.ts +64 -0
- package/dist/adapters/apiActionHandler.d.ts.map +1 -0
- package/dist/adapters/apiActionHandler.js +170 -0
- package/dist/adapters/apiActionHandler.js.map +1 -0
- package/dist/adapters/tuiActionHandler.d.ts +57 -0
- package/dist/adapters/tuiActionHandler.d.ts.map +1 -0
- package/dist/adapters/tuiActionHandler.js +152 -0
- package/dist/adapters/tuiActionHandler.js.map +1 -0
- package/dist/chunks/_plugin-vue_export-helper-Cvoq67hi.js +28 -0
- package/dist/components/ActionChoiceDialog.d.ts +16 -0
- package/dist/components/ActionChoiceDialog.d.ts.map +1 -0
- package/dist/components/ActionChoiceDialog.js +29 -0
- package/dist/components/ActionChoiceDialog.js.map +1 -0
- package/dist/components/ActionConfirmDialog.d.ts +16 -0
- package/dist/components/ActionConfirmDialog.d.ts.map +1 -0
- package/dist/components/ActionConfirmDialog.js +31 -0
- package/dist/components/ActionConfirmDialog.js.map +1 -0
- package/dist/components/ActionInputDialog.d.ts +16 -0
- package/dist/components/ActionInputDialog.d.ts.map +1 -0
- package/dist/components/ActionInputDialog.js +49 -0
- package/dist/components/ActionInputDialog.js.map +1 -0
- package/dist/components/ActionProgressDialog.d.ts +13 -0
- package/dist/components/ActionProgressDialog.d.ts.map +1 -0
- package/dist/components/ActionProgressDialog.js +20 -0
- package/dist/components/ActionProgressDialog.js.map +1 -0
- package/dist/components/FooterHelp.d.ts +2 -0
- package/dist/components/FooterHelp.d.ts.map +1 -1
- package/dist/components/FooterHelp.js +9 -2
- package/dist/components/FooterHelp.js.map +1 -1
- package/dist/components/KebabMenu.d.ts +10 -0
- package/dist/components/KebabMenu.d.ts.map +1 -0
- package/dist/components/KebabMenu.js +18 -0
- package/dist/components/KebabMenu.js.map +1 -0
- package/dist/components/LoadingIndicator.d.ts.map +1 -1
- package/dist/components/LoadingIndicator.js +5 -5
- package/dist/components/LoadingIndicator.js.map +1 -1
- package/dist/components/PaneCard.d.ts +1 -0
- package/dist/components/PaneCard.d.ts.map +1 -1
- package/dist/components/PaneCard.js +21 -20
- package/dist/components/PaneCard.js.map +1 -1
- package/dist/components/PanesGrid.d.ts +1 -0
- package/dist/components/PanesGrid.d.ts.map +1 -1
- package/dist/components/PanesGrid.js +5 -4
- package/dist/components/PanesGrid.js.map +1 -1
- package/dist/components/QRCode.d.ts +7 -0
- package/dist/components/QRCode.d.ts.map +1 -0
- package/dist/components/QRCode.js +19 -0
- package/dist/components/QRCode.js.map +1 -0
- package/dist/components/Spinner.d.ts +10 -0
- package/dist/components/Spinner.d.ts.map +1 -0
- package/dist/components/Spinner.js +15 -0
- package/dist/components/Spinner.js.map +1 -0
- package/dist/dashboard.html +14 -0
- package/dist/dashboard.js +2 -0
- package/dist/hooks/useActionSystem.d.ts +31 -0
- package/dist/hooks/useActionSystem.d.ts.map +1 -0
- package/dist/hooks/useActionSystem.js +95 -0
- package/dist/hooks/useActionSystem.js.map +1 -0
- package/dist/hooks/useAgentStatus.d.ts +4 -3
- package/dist/hooks/useAgentStatus.d.ts.map +1 -1
- package/dist/hooks/useAgentStatus.js +45 -194
- package/dist/hooks/useAgentStatus.js.map +1 -1
- package/dist/hooks/usePaneCreation.d.ts +2 -1
- package/dist/hooks/usePaneCreation.d.ts.map +1 -1
- package/dist/hooks/usePaneCreation.js +46 -100
- package/dist/hooks/usePaneCreation.js.map +1 -1
- package/dist/hooks/usePanes.d.ts.map +1 -1
- package/dist/hooks/usePanes.js +5 -3
- package/dist/hooks/usePanes.js.map +1 -1
- package/dist/hooks/useTerminalWidth.d.ts.map +1 -1
- package/dist/hooks/useTerminalWidth.js +18 -1
- package/dist/hooks/useTerminalWidth.js.map +1 -1
- package/dist/hooks/useWorktreeActions.d.ts.map +1 -1
- package/dist/hooks/useWorktreeActions.js +4 -0
- package/dist/hooks/useWorktreeActions.js.map +1 -1
- package/dist/index.js +43 -6
- package/dist/index.js.map +1 -1
- package/dist/server/actionsApi.d.ts +37 -0
- package/dist/server/actionsApi.d.ts.map +1 -0
- package/dist/server/actionsApi.js +256 -0
- package/dist/server/actionsApi.js.map +1 -0
- package/dist/server/embedded-assets.d.ts +13 -0
- package/dist/server/embedded-assets.d.ts.map +1 -0
- package/dist/server/embedded-assets.js +5012 -0
- package/dist/server/embedded-assets.js.map +1 -0
- package/dist/server/index.d.ts +21 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +99 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/routes.d.ts +3 -0
- package/dist/server/routes.d.ts.map +1 -0
- package/dist/server/routes.js +672 -0
- package/dist/server/routes.js.map +1 -0
- package/dist/server/static.d.ts +6 -0
- package/dist/server/static.d.ts.map +1 -0
- package/dist/server/static.js +3040 -0
- package/dist/server/static.js.map +1 -0
- package/dist/services/ConfigWatcher.d.ts +20 -0
- package/dist/services/ConfigWatcher.d.ts.map +1 -0
- package/dist/services/ConfigWatcher.js +75 -0
- package/dist/services/ConfigWatcher.js.map +1 -0
- package/dist/services/PaneWorkerManager.d.ts +69 -0
- package/dist/services/PaneWorkerManager.d.ts.map +1 -0
- package/dist/services/PaneWorkerManager.js +272 -0
- package/dist/services/PaneWorkerManager.js.map +1 -0
- package/dist/services/StatusDetector.d.ts +87 -0
- package/dist/services/StatusDetector.d.ts.map +1 -0
- package/dist/services/StatusDetector.js +387 -0
- package/dist/services/StatusDetector.js.map +1 -0
- package/dist/services/TerminalDiffer.d.ts +85 -0
- package/dist/services/TerminalDiffer.d.ts.map +1 -0
- package/dist/services/TerminalDiffer.js +499 -0
- package/dist/services/TerminalDiffer.js.map +1 -0
- package/dist/services/TerminalStreamer.d.ts +80 -0
- package/dist/services/TerminalStreamer.d.ts.map +1 -0
- package/dist/services/TerminalStreamer.js +490 -0
- package/dist/services/TerminalStreamer.js.map +1 -0
- package/dist/services/TunnelService.d.ts +9 -0
- package/dist/services/TunnelService.d.ts.map +1 -0
- package/dist/services/TunnelService.js +34 -0
- package/dist/services/TunnelService.js.map +1 -0
- package/dist/services/WorkerMessageBus.d.ts +48 -0
- package/dist/services/WorkerMessageBus.d.ts.map +1 -0
- package/dist/services/WorkerMessageBus.js +120 -0
- package/dist/services/WorkerMessageBus.js.map +1 -0
- package/dist/shared/StateManager.d.ts +34 -0
- package/dist/shared/StateManager.d.ts.map +1 -0
- package/dist/shared/StateManager.js +108 -0
- package/dist/shared/StateManager.js.map +1 -0
- package/dist/shared/StreamProtocol.d.ts +75 -0
- package/dist/shared/StreamProtocol.d.ts.map +1 -0
- package/dist/shared/StreamProtocol.js +37 -0
- package/dist/shared/StreamProtocol.js.map +1 -0
- package/dist/terminal.html +17 -0
- package/dist/terminal.js +3 -0
- package/dist/types.d.ts +21 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/agentDetection.d.ts +18 -0
- package/dist/utils/agentDetection.d.ts.map +1 -0
- package/dist/utils/agentDetection.js +73 -0
- package/dist/utils/agentDetection.js.map +1 -0
- package/dist/utils/aiMerge.d.ts +35 -0
- package/dist/utils/aiMerge.d.ts.map +1 -0
- package/dist/utils/aiMerge.js +298 -0
- package/dist/utils/aiMerge.js.map +1 -0
- package/dist/utils/conflictResolutionPane.d.ts +19 -0
- package/dist/utils/conflictResolutionPane.d.ts.map +1 -0
- package/dist/utils/conflictResolutionPane.js +214 -0
- package/dist/utils/conflictResolutionPane.js.map +1 -0
- package/dist/utils/mergeExecution.d.ts +52 -0
- package/dist/utils/mergeExecution.d.ts.map +1 -0
- package/dist/utils/mergeExecution.js +192 -0
- package/dist/utils/mergeExecution.js.map +1 -0
- package/dist/utils/mergeValidation.d.ts +67 -0
- package/dist/utils/mergeValidation.d.ts.map +1 -0
- package/dist/utils/mergeValidation.js +213 -0
- package/dist/utils/mergeValidation.js.map +1 -0
- package/dist/utils/paneCreation.d.ts +17 -0
- package/dist/utils/paneCreation.d.ts.map +1 -0
- package/dist/utils/paneCreation.js +274 -0
- package/dist/utils/paneCreation.js.map +1 -0
- package/dist/utils/port.d.ts +5 -0
- package/dist/utils/port.d.ts.map +1 -0
- package/dist/utils/port.js +54 -0
- package/dist/utils/port.js.map +1 -0
- package/dist/workers/PaneWorker.d.ts +2 -0
- package/dist/workers/PaneWorker.d.ts.map +1 -0
- package/dist/workers/PaneWorker.js +362 -0
- package/dist/workers/PaneWorker.js.map +1 -0
- package/dist/workers/WorkerMessages.d.ts +36 -0
- package/dist/workers/WorkerMessages.d.ts.map +1 -0
- package/dist/workers/WorkerMessages.js +9 -0
- package/dist/workers/WorkerMessages.js.map +1 -0
- package/package.json +19 -5
package/dist/DmuxApp.js
CHANGED
|
@@ -11,21 +11,22 @@ import useNavigation from './hooks/useNavigation.js';
|
|
|
11
11
|
import useAutoUpdater from './hooks/useAutoUpdater.js';
|
|
12
12
|
import useAgentDetection from './hooks/useAgentDetection.js';
|
|
13
13
|
import useAgentStatus from './hooks/useAgentStatus.js';
|
|
14
|
-
import useWorktreeActions from './hooks/useWorktreeActions.js';
|
|
15
14
|
import usePaneRunner from './hooks/usePaneRunner.js';
|
|
16
15
|
import usePaneCreation from './hooks/usePaneCreation.js';
|
|
16
|
+
import useActionSystem from './hooks/useActionSystem.js';
|
|
17
17
|
// Utils
|
|
18
18
|
import { applySmartLayout } from './utils/tmux.js';
|
|
19
19
|
import { suggestCommand } from './utils/commands.js';
|
|
20
20
|
import { generateSlug } from './utils/slug.js';
|
|
21
21
|
import { getMainBranch } from './utils/git.js';
|
|
22
|
+
import { StateManager } from './shared/StateManager.js';
|
|
23
|
+
import { getStatusDetector } from './services/StatusDetector.js';
|
|
24
|
+
import { PaneAction, getAvailableActions } from './actions/index.js';
|
|
22
25
|
const require = createRequire(import.meta.url);
|
|
23
26
|
const packageJson = require('../package.json');
|
|
24
27
|
import PanesGrid from './components/PanesGrid.js';
|
|
25
28
|
import NewPaneDialog from './components/NewPaneDialog.js';
|
|
26
29
|
import AgentChoiceDialog from './components/AgentChoiceDialog.js';
|
|
27
|
-
import CloseOptionsDialog from './components/CloseOptionsDialog.js';
|
|
28
|
-
import MergeConfirmationDialog from './components/MergeConfirmationDialog.js';
|
|
29
30
|
import CommandPromptDialog from './components/CommandPromptDialog.js';
|
|
30
31
|
import FileCopyPrompt from './components/FileCopyPrompt.js';
|
|
31
32
|
import LoadingIndicator from './components/LoadingIndicator.js';
|
|
@@ -33,27 +34,33 @@ import RunningIndicator from './components/RunningIndicator.js';
|
|
|
33
34
|
import UpdatingIndicator from './components/UpdatingIndicator.js';
|
|
34
35
|
import CreatingIndicator from './components/CreatingIndicator.js';
|
|
35
36
|
import FooterHelp from './components/FooterHelp.js';
|
|
36
|
-
import
|
|
37
|
-
|
|
37
|
+
import QRCode from './components/QRCode.js';
|
|
38
|
+
import KebabMenu from './components/KebabMenu.js';
|
|
39
|
+
import ActionChoiceDialog from './components/ActionChoiceDialog.js';
|
|
40
|
+
import ActionConfirmDialog from './components/ActionConfirmDialog.js';
|
|
41
|
+
import ActionInputDialog from './components/ActionInputDialog.js';
|
|
42
|
+
import ActionProgressDialog from './components/ActionProgressDialog.js';
|
|
43
|
+
const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, autoUpdater, serverPort, server }) => {
|
|
38
44
|
/* panes state moved to usePanes */
|
|
39
45
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
40
46
|
const [showNewPaneDialog, setShowNewPaneDialog] = useState(false);
|
|
41
47
|
const [newPanePrompt, setNewPanePrompt] = useState('');
|
|
42
48
|
const [statusMessage, setStatusMessage] = useState('');
|
|
43
|
-
const [showMergeConfirmation, setShowMergeConfirmation] = useState(false);
|
|
44
|
-
const [mergedPane, setMergedPane] = useState(null);
|
|
45
|
-
const [showMergePane, setShowMergePane] = useState(false);
|
|
46
|
-
const [mergingPane, setMergingPane] = useState(null);
|
|
47
|
-
const [showCloseOptions, setShowCloseOptions] = useState(false);
|
|
48
|
-
const [selectedCloseOption, setSelectedCloseOption] = useState(0);
|
|
49
|
-
const [closingPane, setClosingPane] = useState(null);
|
|
50
49
|
const [isCreatingPane, setIsCreatingPane] = useState(false);
|
|
50
|
+
const [showQRCode, setShowQRCode] = useState(false);
|
|
51
|
+
const [tunnelUrl, setTunnelUrl] = useState(null);
|
|
52
|
+
const [isCreatingTunnel, setIsCreatingTunnel] = useState(false);
|
|
51
53
|
const { projectSettings, saveSettings } = useProjectSettings(settingsFile);
|
|
52
54
|
const [showCommandPrompt, setShowCommandPrompt] = useState(null);
|
|
53
55
|
const [commandInput, setCommandInput] = useState('');
|
|
54
56
|
const [showFileCopyPrompt, setShowFileCopyPrompt] = useState(false);
|
|
55
57
|
const [currentCommandType, setCurrentCommandType] = useState(null);
|
|
56
58
|
const [runningCommand, setRunningCommand] = useState(false);
|
|
59
|
+
const [quitConfirmMode, setQuitConfirmMode] = useState(false);
|
|
60
|
+
const [showKebabMenu, setShowKebabMenu] = useState(false);
|
|
61
|
+
const [kebabMenuPaneIndex, setKebabMenuPaneIndex] = useState(null);
|
|
62
|
+
const [kebabMenuOption, setKebabMenuOption] = useState(0);
|
|
63
|
+
const [kebabMenuActions, setKebabMenuActions] = useState([]);
|
|
57
64
|
// Update state handled by hook
|
|
58
65
|
const { updateInfo, showUpdateDialog, isUpdating, performUpdate, skipUpdate, dismissUpdate, updateAvailable } = useAutoUpdater(autoUpdater, setStatusMessage);
|
|
59
66
|
const { exit } = useApp();
|
|
@@ -64,17 +71,28 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, autoUpdate
|
|
|
64
71
|
const [pendingPrompt, setPendingPrompt] = useState('');
|
|
65
72
|
// Track terminal dimensions for responsive layout
|
|
66
73
|
const terminalWidth = useTerminalWidth();
|
|
67
|
-
// Panes state and persistence
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
//
|
|
72
|
-
const
|
|
74
|
+
// Panes state and persistence (skipLoading will be updated after actionSystem is initialized)
|
|
75
|
+
const { panes, setPanes, isLoading, loadPanes, savePanes } = usePanes(panesFile, false);
|
|
76
|
+
// Track intentionally closed panes to prevent race condition
|
|
77
|
+
// When a user closes a pane, we add it to this set. If the worker detects
|
|
78
|
+
// the pane is gone (which it will), we check this set first before re-saving.
|
|
79
|
+
const intentionallyClosedPanes = React.useRef(new Set());
|
|
80
|
+
// Action system
|
|
81
|
+
const actionSystem = useActionSystem({
|
|
73
82
|
panes,
|
|
74
83
|
savePanes,
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
84
|
+
sessionName,
|
|
85
|
+
projectName,
|
|
86
|
+
onPaneRemove: (paneId) => {
|
|
87
|
+
// Mark this pane as intentionally closed
|
|
88
|
+
intentionallyClosedPanes.current.add(paneId);
|
|
89
|
+
const updated = panes.filter(p => p.id !== paneId);
|
|
90
|
+
setPanes(updated);
|
|
91
|
+
// Clean up the tracking after a delay (in case of race conditions)
|
|
92
|
+
setTimeout(() => {
|
|
93
|
+
intentionallyClosedPanes.current.delete(paneId);
|
|
94
|
+
}, 5000);
|
|
95
|
+
},
|
|
78
96
|
});
|
|
79
97
|
// Pane runner
|
|
80
98
|
const { copyNonGitFiles, runCommandInternal, monitorTestOutput, monitorDevOutput, attachBackgroundWindow } = usePaneRunner({
|
|
@@ -94,32 +112,88 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, autoUpdate
|
|
|
94
112
|
setNewPanePrompt,
|
|
95
113
|
loadPanes,
|
|
96
114
|
panesFile,
|
|
115
|
+
availableAgents,
|
|
97
116
|
});
|
|
117
|
+
// Listen for status updates with analysis data and merge into panes
|
|
118
|
+
useEffect(() => {
|
|
119
|
+
const statusDetector = getStatusDetector();
|
|
120
|
+
const handleStatusUpdate = (event) => {
|
|
121
|
+
setPanes(prevPanes => {
|
|
122
|
+
const updatedPanes = prevPanes.map(pane => {
|
|
123
|
+
if (pane.id === event.paneId) {
|
|
124
|
+
const updated = {
|
|
125
|
+
...pane,
|
|
126
|
+
agentStatus: event.status,
|
|
127
|
+
};
|
|
128
|
+
// Only update analysis fields if they're present in the event (not undefined)
|
|
129
|
+
// This prevents simple status changes from overwriting PaneAnalyzer results
|
|
130
|
+
if (event.optionsQuestion !== undefined) {
|
|
131
|
+
updated.optionsQuestion = event.optionsQuestion;
|
|
132
|
+
}
|
|
133
|
+
if (event.options !== undefined) {
|
|
134
|
+
updated.options = event.options;
|
|
135
|
+
}
|
|
136
|
+
if (event.potentialHarm !== undefined) {
|
|
137
|
+
updated.potentialHarm = event.potentialHarm;
|
|
138
|
+
}
|
|
139
|
+
if (event.summary !== undefined) {
|
|
140
|
+
updated.agentSummary = event.summary;
|
|
141
|
+
}
|
|
142
|
+
if (event.analyzerError !== undefined) {
|
|
143
|
+
updated.analyzerError = event.analyzerError;
|
|
144
|
+
}
|
|
145
|
+
// Clear option dialog data when transitioning away from 'waiting' state
|
|
146
|
+
if (event.status !== 'waiting' && pane.agentStatus === 'waiting') {
|
|
147
|
+
updated.optionsQuestion = undefined;
|
|
148
|
+
updated.options = undefined;
|
|
149
|
+
updated.potentialHarm = undefined;
|
|
150
|
+
}
|
|
151
|
+
// Clear summary when transitioning away from 'idle' state
|
|
152
|
+
if (event.status !== 'idle' && pane.agentStatus === 'idle') {
|
|
153
|
+
updated.agentSummary = undefined;
|
|
154
|
+
}
|
|
155
|
+
// Clear analyzer error when successfully getting a new analysis
|
|
156
|
+
// or when transitioning to 'working' status
|
|
157
|
+
if (event.status === 'working') {
|
|
158
|
+
updated.analyzerError = undefined;
|
|
159
|
+
}
|
|
160
|
+
else if (event.status === 'waiting' || event.status === 'idle') {
|
|
161
|
+
if (event.analyzerError === undefined && (event.optionsQuestion || event.summary)) {
|
|
162
|
+
updated.analyzerError = undefined;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return updated;
|
|
166
|
+
}
|
|
167
|
+
return pane;
|
|
168
|
+
});
|
|
169
|
+
// Persist to disk - ConfigWatcher will handle syncing to StateManager
|
|
170
|
+
savePanes(updatedPanes).catch(err => {
|
|
171
|
+
console.error('Failed to save panes after status update:', err);
|
|
172
|
+
});
|
|
173
|
+
return updatedPanes;
|
|
174
|
+
});
|
|
175
|
+
};
|
|
176
|
+
statusDetector.on('status-updated', handleStatusUpdate);
|
|
177
|
+
return () => {
|
|
178
|
+
statusDetector.off('status-updated', handleStatusUpdate);
|
|
179
|
+
};
|
|
180
|
+
}, [setPanes, savePanes]);
|
|
181
|
+
// Note: No need to sync panes with StateManager here.
|
|
182
|
+
// The ConfigWatcher automatically updates StateManager when the config file changes.
|
|
183
|
+
// This prevents unnecessary SSE broadcasts on every local state update.
|
|
184
|
+
// Sync settings with StateManager
|
|
185
|
+
useEffect(() => {
|
|
186
|
+
const stateManager = StateManager.getInstance();
|
|
187
|
+
stateManager.updateSettings(projectSettings);
|
|
188
|
+
}, [projectSettings]);
|
|
98
189
|
// Load panes and settings on mount and refresh periodically
|
|
99
190
|
useEffect(() => {
|
|
100
|
-
//
|
|
191
|
+
// SIGTERM should quit immediately (for process management)
|
|
101
192
|
const handleTermination = () => {
|
|
102
|
-
|
|
103
|
-
process.stdout.write('\x1b[2J\x1b[H'); // Clear screen and move to home
|
|
104
|
-
process.stdout.write('\x1b[3J'); // Clear scrollback buffer
|
|
105
|
-
process.stdout.write('\n'.repeat(100)); // Push any remaining content off screen
|
|
106
|
-
// Clear tmux pane
|
|
107
|
-
try {
|
|
108
|
-
execSync('tmux clear-history', { stdio: 'pipe' });
|
|
109
|
-
execSync('tmux send-keys C-l', { stdio: 'pipe' });
|
|
110
|
-
}
|
|
111
|
-
catch { }
|
|
112
|
-
// Wait a moment for clearing to settle, then show goodbye message
|
|
113
|
-
setTimeout(() => {
|
|
114
|
-
process.stdout.write('\x1b[2J\x1b[H');
|
|
115
|
-
process.stdout.write('\n\n dmux session ended.\n\n');
|
|
116
|
-
process.exit(0);
|
|
117
|
-
}, 100);
|
|
193
|
+
cleanExit();
|
|
118
194
|
};
|
|
119
|
-
process.on('SIGINT', handleTermination);
|
|
120
195
|
process.on('SIGTERM', handleTermination);
|
|
121
196
|
return () => {
|
|
122
|
-
process.removeListener('SIGINT', handleTermination);
|
|
123
197
|
process.removeListener('SIGTERM', handleTermination);
|
|
124
198
|
};
|
|
125
199
|
}, []);
|
|
@@ -133,8 +207,10 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, autoUpdate
|
|
|
133
207
|
if (!isLoading &&
|
|
134
208
|
panes.length === 0 &&
|
|
135
209
|
!showNewPaneDialog &&
|
|
136
|
-
!
|
|
137
|
-
!
|
|
210
|
+
!actionSystem.actionState.showConfirmDialog &&
|
|
211
|
+
!actionSystem.actionState.showChoiceDialog &&
|
|
212
|
+
!actionSystem.actionState.showInputDialog &&
|
|
213
|
+
!actionSystem.actionState.showProgressDialog &&
|
|
138
214
|
!showCommandPrompt &&
|
|
139
215
|
!showFileCopyPrompt &&
|
|
140
216
|
!showAgentChoiceDialog &&
|
|
@@ -143,7 +219,7 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, autoUpdate
|
|
|
143
219
|
!isUpdating) {
|
|
144
220
|
setShowNewPaneDialog(true);
|
|
145
221
|
}
|
|
146
|
-
}, [isLoading, panes.length, showNewPaneDialog,
|
|
222
|
+
}, [isLoading, panes.length, showNewPaneDialog, actionSystem.actionState.showConfirmDialog, actionSystem.actionState.showChoiceDialog, actionSystem.actionState.showInputDialog, actionSystem.actionState.showProgressDialog, showCommandPrompt, showFileCopyPrompt, showAgentChoiceDialog, isCreatingPane, runningCommand, isUpdating]);
|
|
147
223
|
// Update checking moved to useAutoUpdater
|
|
148
224
|
// Set default agent choice when detection completes
|
|
149
225
|
useEffect(() => {
|
|
@@ -154,7 +230,18 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, autoUpdate
|
|
|
154
230
|
// Monitor agent status across panes (returns a map of pane ID to status)
|
|
155
231
|
const agentStatuses = useAgentStatus({
|
|
156
232
|
panes,
|
|
157
|
-
suspend: showNewPaneDialog ||
|
|
233
|
+
suspend: showNewPaneDialog || actionSystem.actionState.showConfirmDialog || actionSystem.actionState.showChoiceDialog || actionSystem.actionState.showInputDialog || actionSystem.actionState.showProgressDialog || !!showCommandPrompt || showFileCopyPrompt,
|
|
234
|
+
onPaneRemoved: (paneId) => {
|
|
235
|
+
// Check if this pane was intentionally closed
|
|
236
|
+
// If so, don't re-save - the close action already handled it
|
|
237
|
+
if (intentionallyClosedPanes.current.has(paneId)) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
// Pane was removed unexpectedly (e.g., user killed tmux pane manually)
|
|
241
|
+
// Remove it from our tracking
|
|
242
|
+
const updatedPanes = panes.filter(p => p.id !== paneId);
|
|
243
|
+
savePanes(updatedPanes);
|
|
244
|
+
},
|
|
158
245
|
});
|
|
159
246
|
// loadPanes moved to usePanes
|
|
160
247
|
// getPanePositions moved to utils/tmux
|
|
@@ -288,7 +375,6 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, autoUpdate
|
|
|
288
375
|
if (agent === 'claude') {
|
|
289
376
|
// Monitor for Claude Code trust prompt and auto-respond
|
|
290
377
|
const autoApproveTrust = async () => {
|
|
291
|
-
console.error('[TRUST DEBUG] Starting autoApproveTrust monitoring...');
|
|
292
378
|
// Wait for Claude to start up before checking for prompts
|
|
293
379
|
await new Promise(resolve => setTimeout(resolve, 800));
|
|
294
380
|
const maxChecks = 100; // 100 checks * 100ms = 10 seconds total
|
|
@@ -331,7 +417,6 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, autoUpdate
|
|
|
331
417
|
const paneContent = execSync(`tmux capture-pane -t '${paneInfo}' -p -S -30`, // Capture last 30 lines
|
|
332
418
|
{ encoding: 'utf-8', stdio: 'pipe' });
|
|
333
419
|
if (i % 10 === 0) { // Log every 10 checks (every second)
|
|
334
|
-
console.error(`[TRUST DEBUG] Check ${i + 1}/${maxChecks}, content preview: "${paneContent.substring(0, 100).replace(/\n/g, '\\n')}"...`);
|
|
335
420
|
}
|
|
336
421
|
// Check if content has stabilized (same for 3 checks = prompt is waiting)
|
|
337
422
|
if (paneContent === lastContent) {
|
|
@@ -350,29 +435,23 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, autoUpdate
|
|
|
350
435
|
paneContent.includes('allow') ||
|
|
351
436
|
(paneContent.includes('folder') && paneContent.includes('?'));
|
|
352
437
|
if ((hasTrustPrompt || hasClaudePermissionPrompt) && !promptHandled) {
|
|
353
|
-
console.error(`[TRUST DEBUG] Trust prompt detected! Pattern match: ${hasTrustPrompt}, Permission text: ${hasClaudePermissionPrompt}, Stable count: ${stableContentCount}`);
|
|
354
|
-
console.error(`[TRUST DEBUG] Content that triggered detection: "${paneContent}"`);
|
|
355
438
|
// Content is stable and we found a prompt
|
|
356
439
|
if (stableContentCount >= 2) {
|
|
357
|
-
console.error('[TRUST DEBUG] Content is stable, attempting to auto-approve trust prompt...');
|
|
358
440
|
// Check if this is the new Claude numbered menu format
|
|
359
441
|
const isNewClaudeFormat = /❯\s*1\.\s*Yes,\s*proceed/i.test(paneContent) ||
|
|
360
442
|
/Enter to confirm.*Esc to exit/i.test(paneContent);
|
|
361
443
|
if (isNewClaudeFormat) {
|
|
362
444
|
// For new Claude format, just press Enter to confirm default "Yes, proceed"
|
|
363
|
-
console.error('[TRUST DEBUG] Detected new Claude format, sending Enter to confirm default option');
|
|
364
445
|
execSync(`tmux send-keys -t '${paneInfo}' Enter`, { stdio: 'pipe' });
|
|
365
446
|
}
|
|
366
447
|
else {
|
|
367
448
|
// Try multiple response methods for older formats
|
|
368
449
|
// Method 1: Send 'y' followed by Enter (most explicit)
|
|
369
|
-
console.error('[TRUST DEBUG] Sending "y" + Enter for legacy format');
|
|
370
450
|
execSync(`tmux send-keys -t '${paneInfo}' 'y'`, { stdio: 'pipe' });
|
|
371
451
|
await new Promise(resolve => setTimeout(resolve, 50));
|
|
372
452
|
execSync(`tmux send-keys -t '${paneInfo}' Enter`, { stdio: 'pipe' });
|
|
373
453
|
// Method 2: Just Enter (if it's a yes/no with default yes)
|
|
374
454
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
375
|
-
console.error('[TRUST DEBUG] Sending additional Enter');
|
|
376
455
|
execSync(`tmux send-keys -t '${paneInfo}' Enter`, { stdio: 'pipe' });
|
|
377
456
|
}
|
|
378
457
|
// Mark as handled to avoid duplicate responses
|
|
@@ -384,20 +463,16 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, autoUpdate
|
|
|
384
463
|
// If trust prompt is gone, check if we need to resend the Claude command
|
|
385
464
|
const promptGone = !trustPromptPatterns.some(p => p.test(updatedContent));
|
|
386
465
|
if (promptGone) {
|
|
387
|
-
console.error('[TRUST DEBUG] Trust prompt appears to be gone');
|
|
388
466
|
// Check if Claude is running or if we need to restart it
|
|
389
467
|
const claudeRunning = updatedContent.includes('Claude') ||
|
|
390
468
|
updatedContent.includes('claude') ||
|
|
391
469
|
updatedContent.includes('Assistant') ||
|
|
392
470
|
(prompt && updatedContent.includes(prompt.substring(0, Math.min(20, prompt.length))));
|
|
393
|
-
console.error(`[TRUST DEBUG] Claude running check: ${claudeRunning}, has $: ${updatedContent.includes('$')}`);
|
|
394
471
|
if (!claudeRunning && !updatedContent.includes('$')) {
|
|
395
|
-
console.error('[TRUST DEBUG] Claude not running after trust approval, restarting...');
|
|
396
472
|
await new Promise(resolve => setTimeout(resolve, 300));
|
|
397
473
|
execSync(`tmux send-keys -t '${paneInfo}' '${escapedCmd}'`, { stdio: 'pipe' });
|
|
398
474
|
execSync(`tmux send-keys -t '${paneInfo}' Enter`, { stdio: 'pipe' });
|
|
399
475
|
}
|
|
400
|
-
console.error('[TRUST DEBUG] Successfully handled the trust prompt');
|
|
401
476
|
break;
|
|
402
477
|
}
|
|
403
478
|
}
|
|
@@ -410,13 +485,11 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, autoUpdate
|
|
|
410
485
|
}
|
|
411
486
|
catch (error) {
|
|
412
487
|
// Continue checking, errors are non-fatal
|
|
413
|
-
console.error(`[TRUST DEBUG] Error checking for trust prompt: ${error instanceof Error ? error.message : error}`);
|
|
414
488
|
}
|
|
415
489
|
}
|
|
416
490
|
};
|
|
417
491
|
// Start monitoring for trust prompt in background
|
|
418
492
|
autoApproveTrust().catch(err => {
|
|
419
|
-
console.error(`[TRUST DEBUG] Error in autoApproveTrust: ${err instanceof Error ? err.message : err}`);
|
|
420
493
|
});
|
|
421
494
|
}
|
|
422
495
|
// Keep focus on the new pane
|
|
@@ -425,7 +498,7 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, autoUpdate
|
|
|
425
498
|
const newPane = {
|
|
426
499
|
id: `dmux-${Date.now()}`,
|
|
427
500
|
slug,
|
|
428
|
-
prompt: prompt
|
|
501
|
+
prompt: prompt || 'No initial prompt',
|
|
429
502
|
paneId: paneInfo,
|
|
430
503
|
worktreePath,
|
|
431
504
|
agent
|
|
@@ -541,32 +614,227 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, autoUpdate
|
|
|
541
614
|
}
|
|
542
615
|
};
|
|
543
616
|
// Update handling moved to useAutoUpdater
|
|
544
|
-
//
|
|
545
|
-
const
|
|
546
|
-
//
|
|
547
|
-
|
|
548
|
-
process.stdout.write('\x1b[
|
|
549
|
-
|
|
550
|
-
// Clear tmux pane
|
|
617
|
+
// Helper function to clear screen artifacts
|
|
618
|
+
const clearScreen = () => {
|
|
619
|
+
// Multiple clearing strategies to prevent artifacts
|
|
620
|
+
// 1. Clear screen with ANSI codes
|
|
621
|
+
process.stdout.write('\x1b[2J\x1b[H');
|
|
622
|
+
// 2. Clear tmux history
|
|
551
623
|
try {
|
|
552
624
|
execSync('tmux clear-history', { stdio: 'pipe' });
|
|
553
|
-
execSync('tmux send-keys C-l', { stdio: 'pipe' });
|
|
554
625
|
}
|
|
555
626
|
catch { }
|
|
556
|
-
//
|
|
627
|
+
// 3. Force tmux to refresh the display
|
|
628
|
+
try {
|
|
629
|
+
execSync('tmux refresh-client', { stdio: 'pipe' });
|
|
630
|
+
}
|
|
631
|
+
catch { }
|
|
632
|
+
};
|
|
633
|
+
// Cleanup function for exit
|
|
634
|
+
const cleanExit = () => {
|
|
635
|
+
// Clear screen before exiting Ink
|
|
636
|
+
process.stdout.write('\x1b[2J\x1b[3J\x1b[H');
|
|
637
|
+
// Exit the Ink app (this cleans up the React tree)
|
|
638
|
+
exit();
|
|
639
|
+
// Give Ink a moment to clean up its rendering, then do final cleanup
|
|
557
640
|
setTimeout(() => {
|
|
558
|
-
//
|
|
641
|
+
// Multiple aggressive clearing strategies
|
|
642
|
+
process.stdout.write('\x1b[2J\x1b[H'); // Clear screen and move cursor to home
|
|
643
|
+
process.stdout.write('\x1b[3J'); // Clear scrollback buffer
|
|
644
|
+
process.stdout.write('\x1b[0m'); // Reset all attributes
|
|
645
|
+
// Clear tmux history and pane
|
|
646
|
+
try {
|
|
647
|
+
execSync('tmux clear-history', { stdio: 'pipe' });
|
|
648
|
+
execSync('tmux send-keys C-l', { stdio: 'pipe' });
|
|
649
|
+
}
|
|
650
|
+
catch { }
|
|
651
|
+
// One more final clear
|
|
559
652
|
process.stdout.write('\x1b[2J\x1b[H');
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
653
|
+
// Show clean goodbye message
|
|
654
|
+
process.stdout.write('\n Run dmux again to resume. Goodbye 👋\n\n');
|
|
655
|
+
// Exit process
|
|
656
|
+
process.exit(0);
|
|
563
657
|
}, 100);
|
|
564
658
|
};
|
|
565
659
|
useInput(async (input, key) => {
|
|
566
|
-
|
|
660
|
+
// Handle Ctrl+C for quit confirmation (must be first, before any other checks)
|
|
661
|
+
if (key.ctrl && input === 'c') {
|
|
662
|
+
if (quitConfirmMode) {
|
|
663
|
+
// Second Ctrl+C - actually quit
|
|
664
|
+
cleanExit();
|
|
665
|
+
}
|
|
666
|
+
else {
|
|
667
|
+
// First Ctrl+C - show confirmation
|
|
668
|
+
setQuitConfirmMode(true);
|
|
669
|
+
// Reset after 3 seconds if user doesn't press Ctrl+C again
|
|
670
|
+
setTimeout(() => {
|
|
671
|
+
setQuitConfirmMode(false);
|
|
672
|
+
}, 3000);
|
|
673
|
+
}
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
if (isCreatingPane || runningCommand || isUpdating || isLoading || isCreatingTunnel) {
|
|
567
677
|
// Disable input while performing operations or loading
|
|
568
678
|
return;
|
|
569
679
|
}
|
|
680
|
+
// Handle kebab menu navigation
|
|
681
|
+
if (showKebabMenu && kebabMenuPaneIndex !== null) {
|
|
682
|
+
const currentPane = panes[kebabMenuPaneIndex];
|
|
683
|
+
const availableActions = kebabMenuActions;
|
|
684
|
+
if (key.escape) {
|
|
685
|
+
setShowKebabMenu(false);
|
|
686
|
+
setKebabMenuPaneIndex(null);
|
|
687
|
+
setKebabMenuOption(0);
|
|
688
|
+
setKebabMenuActions([]);
|
|
689
|
+
clearScreen();
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
else if (key.upArrow) {
|
|
693
|
+
setKebabMenuOption(Math.max(0, kebabMenuOption - 1));
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
else if (key.downArrow) {
|
|
697
|
+
setKebabMenuOption(Math.min(availableActions.length - 1, kebabMenuOption + 1));
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
else if (key.return) {
|
|
701
|
+
// Execute the selected menu action
|
|
702
|
+
setShowKebabMenu(false);
|
|
703
|
+
clearScreen();
|
|
704
|
+
const selectedAction = availableActions[kebabMenuOption];
|
|
705
|
+
if (selectedAction) {
|
|
706
|
+
// Use the action system to execute
|
|
707
|
+
actionSystem.executeAction(selectedAction.id, currentPane, { mainBranch: getMainBranch() });
|
|
708
|
+
}
|
|
709
|
+
setKebabMenuPaneIndex(null);
|
|
710
|
+
setKebabMenuOption(0);
|
|
711
|
+
setKebabMenuActions([]);
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
// Don't process other inputs while menu is open
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
// Handle quit confirm mode - ESC cancels it
|
|
718
|
+
if (quitConfirmMode) {
|
|
719
|
+
if (key.escape) {
|
|
720
|
+
setQuitConfirmMode(false);
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
// Allow other inputs to continue (don't return early)
|
|
724
|
+
}
|
|
725
|
+
// Handle QR code view
|
|
726
|
+
if (showQRCode) {
|
|
727
|
+
if (key.escape) {
|
|
728
|
+
setShowQRCode(false);
|
|
729
|
+
}
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
// Handle action system confirm dialog
|
|
733
|
+
if (actionSystem.actionState.showConfirmDialog) {
|
|
734
|
+
if (key.escape) {
|
|
735
|
+
if (actionSystem.actionState.onConfirmNo) {
|
|
736
|
+
actionSystem.executeCallback(actionSystem.actionState.onConfirmNo);
|
|
737
|
+
}
|
|
738
|
+
else {
|
|
739
|
+
actionSystem.setActionState(prev => ({ ...prev, showConfirmDialog: false }));
|
|
740
|
+
}
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
else if (key.upArrow) {
|
|
744
|
+
actionSystem.setActionState(prev => ({
|
|
745
|
+
...prev,
|
|
746
|
+
confirmSelectedIndex: Math.max(0, prev.confirmSelectedIndex - 1)
|
|
747
|
+
}));
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
else if (key.downArrow) {
|
|
751
|
+
actionSystem.setActionState(prev => ({
|
|
752
|
+
...prev,
|
|
753
|
+
confirmSelectedIndex: Math.min(1, prev.confirmSelectedIndex + 1)
|
|
754
|
+
}));
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
else if (key.return) {
|
|
758
|
+
// Execute based on selected index
|
|
759
|
+
if (actionSystem.actionState.confirmSelectedIndex === 0) {
|
|
760
|
+
if (actionSystem.actionState.onConfirmYes) {
|
|
761
|
+
actionSystem.executeCallback(actionSystem.actionState.onConfirmYes);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
else {
|
|
765
|
+
if (actionSystem.actionState.onConfirmNo) {
|
|
766
|
+
actionSystem.executeCallback(actionSystem.actionState.onConfirmNo);
|
|
767
|
+
}
|
|
768
|
+
else {
|
|
769
|
+
actionSystem.setActionState(prev => ({ ...prev, showConfirmDialog: false }));
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
else if (input === 'y' || input === 'Y') {
|
|
775
|
+
// Shortcut: yes
|
|
776
|
+
if (actionSystem.actionState.onConfirmYes) {
|
|
777
|
+
actionSystem.executeCallback(actionSystem.actionState.onConfirmYes);
|
|
778
|
+
}
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
else if (input === 'n' || input === 'N') {
|
|
782
|
+
// Shortcut: no
|
|
783
|
+
if (actionSystem.actionState.onConfirmNo) {
|
|
784
|
+
actionSystem.executeCallback(actionSystem.actionState.onConfirmNo);
|
|
785
|
+
}
|
|
786
|
+
else {
|
|
787
|
+
actionSystem.setActionState(prev => ({ ...prev, showConfirmDialog: false }));
|
|
788
|
+
}
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
// Handle action system input dialog
|
|
794
|
+
if (actionSystem.actionState.showInputDialog) {
|
|
795
|
+
if (key.escape) {
|
|
796
|
+
actionSystem.setActionState(prev => ({ ...prev, showInputDialog: false }));
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
else if (key.return) {
|
|
800
|
+
if (actionSystem.actionState.onInputSubmit) {
|
|
801
|
+
actionSystem.executeCallback(async () => actionSystem.actionState.onInputSubmit(actionSystem.actionState.inputValue));
|
|
802
|
+
}
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
805
|
+
// Let CleanTextInput handle all other key events
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
// Handle action system choice dialog
|
|
809
|
+
if (actionSystem.actionState.showChoiceDialog) {
|
|
810
|
+
if (key.escape) {
|
|
811
|
+
actionSystem.setActionState(prev => ({ ...prev, showChoiceDialog: false }));
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
else if (key.upArrow) {
|
|
815
|
+
actionSystem.setActionState(prev => ({
|
|
816
|
+
...prev,
|
|
817
|
+
choiceSelectedIndex: Math.max(0, prev.choiceSelectedIndex - 1)
|
|
818
|
+
}));
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
else if (key.downArrow) {
|
|
822
|
+
const maxIndex = actionSystem.actionState.choiceOptions.length - 1;
|
|
823
|
+
actionSystem.setActionState(prev => ({
|
|
824
|
+
...prev,
|
|
825
|
+
choiceSelectedIndex: Math.min(maxIndex, prev.choiceSelectedIndex + 1)
|
|
826
|
+
}));
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
else if (key.return) {
|
|
830
|
+
const selectedOption = actionSystem.actionState.choiceOptions[actionSystem.actionState.choiceSelectedIndex];
|
|
831
|
+
if (selectedOption && actionSystem.actionState.onChoiceSelect) {
|
|
832
|
+
actionSystem.executeCallback(async () => actionSystem.actionState.onChoiceSelect(selectedOption.id));
|
|
833
|
+
}
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
570
838
|
if (showFileCopyPrompt) {
|
|
571
839
|
if (input === 'y' || input === 'Y') {
|
|
572
840
|
setShowFileCopyPrompt(false);
|
|
@@ -667,10 +935,6 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, autoUpdate
|
|
|
667
935
|
}
|
|
668
936
|
return;
|
|
669
937
|
}
|
|
670
|
-
if (showMergePane) {
|
|
671
|
-
// MergePane handles its own input
|
|
672
|
-
return;
|
|
673
|
-
}
|
|
674
938
|
if (showNewPaneDialog) {
|
|
675
939
|
if (key.escape) {
|
|
676
940
|
setShowNewPaneDialog(false);
|
|
@@ -683,49 +947,6 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, autoUpdate
|
|
|
683
947
|
// TextInput handles other input events
|
|
684
948
|
return;
|
|
685
949
|
}
|
|
686
|
-
if (showMergeConfirmation) {
|
|
687
|
-
if (input === 'y' || input === 'Y') {
|
|
688
|
-
if (mergedPane) {
|
|
689
|
-
closePane(mergedPane);
|
|
690
|
-
}
|
|
691
|
-
setShowMergeConfirmation(false);
|
|
692
|
-
setMergedPane(null);
|
|
693
|
-
}
|
|
694
|
-
else if (input === 'n' || input === 'N' || key.escape) {
|
|
695
|
-
setShowMergeConfirmation(false);
|
|
696
|
-
setMergedPane(null);
|
|
697
|
-
}
|
|
698
|
-
return;
|
|
699
|
-
}
|
|
700
|
-
if (showCloseOptions) {
|
|
701
|
-
if (key.escape) {
|
|
702
|
-
setShowCloseOptions(false);
|
|
703
|
-
setClosingPane(null);
|
|
704
|
-
setSelectedCloseOption(0);
|
|
705
|
-
}
|
|
706
|
-
else if (key.upArrow) {
|
|
707
|
-
setSelectedCloseOption(Math.max(0, selectedCloseOption - 1));
|
|
708
|
-
}
|
|
709
|
-
else if (key.downArrow) {
|
|
710
|
-
setSelectedCloseOption(Math.min(3, selectedCloseOption + 1));
|
|
711
|
-
}
|
|
712
|
-
else if (key.return && closingPane) {
|
|
713
|
-
handleCloseOption(selectedCloseOption, closingPane).then(() => {
|
|
714
|
-
// Close the dialog after the action is performed
|
|
715
|
-
setShowCloseOptions(false);
|
|
716
|
-
setClosingPane(null);
|
|
717
|
-
setSelectedCloseOption(0);
|
|
718
|
-
}).catch(error => {
|
|
719
|
-
setStatusMessage('Failed to close pane');
|
|
720
|
-
setTimeout(() => setStatusMessage(''), 2000);
|
|
721
|
-
// Also close the dialog on error
|
|
722
|
-
setShowCloseOptions(false);
|
|
723
|
-
setClosingPane(null);
|
|
724
|
-
setSelectedCloseOption(0);
|
|
725
|
-
});
|
|
726
|
-
}
|
|
727
|
-
return;
|
|
728
|
-
}
|
|
729
950
|
// Handle directional navigation with spatial awareness based on card grid layout
|
|
730
951
|
if (key.upArrow || key.downArrow || key.leftArrow || key.rightArrow) {
|
|
731
952
|
let targetIndex = null;
|
|
@@ -746,9 +967,42 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, autoUpdate
|
|
|
746
967
|
}
|
|
747
968
|
return;
|
|
748
969
|
}
|
|
749
|
-
if (input === '
|
|
970
|
+
if ((input === 'm' || key.return) && selectedIndex < panes.length) {
|
|
971
|
+
// Open kebab menu for selected pane
|
|
972
|
+
const selectedPane = panes[selectedIndex];
|
|
973
|
+
const actions = getAvailableActions(selectedPane, projectSettings);
|
|
974
|
+
setKebabMenuActions(actions);
|
|
975
|
+
setShowKebabMenu(true);
|
|
976
|
+
setKebabMenuPaneIndex(selectedIndex);
|
|
977
|
+
setKebabMenuOption(0);
|
|
978
|
+
}
|
|
979
|
+
else if (input === 'q') {
|
|
750
980
|
cleanExit();
|
|
751
981
|
}
|
|
982
|
+
else if (input === 'r' && server) {
|
|
983
|
+
// Create tunnel if not already created, then show QR code
|
|
984
|
+
if (!tunnelUrl) {
|
|
985
|
+
setIsCreatingTunnel(true);
|
|
986
|
+
setStatusMessage('Creating tunnel...');
|
|
987
|
+
try {
|
|
988
|
+
const url = await server.startTunnel();
|
|
989
|
+
setTunnelUrl(url);
|
|
990
|
+
setStatusMessage('');
|
|
991
|
+
setShowQRCode(true);
|
|
992
|
+
}
|
|
993
|
+
catch (error) {
|
|
994
|
+
setStatusMessage('Failed to create tunnel');
|
|
995
|
+
setTimeout(() => setStatusMessage(''), 3000);
|
|
996
|
+
}
|
|
997
|
+
finally {
|
|
998
|
+
setIsCreatingTunnel(false);
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
else {
|
|
1002
|
+
// Tunnel already exists, just show the QR code
|
|
1003
|
+
setShowQRCode(true);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
752
1006
|
else if (!isLoading && (input === 'n' || (key.return && selectedIndex === panes.length))) {
|
|
753
1007
|
// Clear the prompt and show dialog in next tick to prevent 'n' bleeding through
|
|
754
1008
|
setNewPanePrompt('');
|
|
@@ -756,61 +1010,29 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, autoUpdate
|
|
|
756
1010
|
return; // Consume the 'n' keystroke so it doesn't propagate
|
|
757
1011
|
}
|
|
758
1012
|
else if (input === 'j' && selectedIndex < panes.length) {
|
|
759
|
-
|
|
1013
|
+
// Jump to pane (NEW: using action system)
|
|
1014
|
+
actionSystem.executeAction(PaneAction.VIEW, panes[selectedIndex]);
|
|
760
1015
|
}
|
|
761
1016
|
else if (input === 'x' && selectedIndex < panes.length) {
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
setSelectedCloseOption(0);
|
|
765
|
-
}
|
|
766
|
-
else if (input === 'm' && selectedIndex < panes.length) {
|
|
767
|
-
setMergingPane(panes[selectedIndex]);
|
|
768
|
-
setShowMergePane(true);
|
|
769
|
-
}
|
|
770
|
-
else if (input === 't' && selectedIndex < panes.length) {
|
|
771
|
-
await runCommand('test', panes[selectedIndex]);
|
|
772
|
-
}
|
|
773
|
-
else if (input === 'd' && selectedIndex < panes.length) {
|
|
774
|
-
await runCommand('dev', panes[selectedIndex]);
|
|
775
|
-
}
|
|
776
|
-
else if (input === 'o' && selectedIndex < panes.length) {
|
|
777
|
-
const pane = panes[selectedIndex];
|
|
778
|
-
if (pane.testWindowId || pane.devWindowId) {
|
|
779
|
-
// If both exist, prefer dev (since it's usually more interactive)
|
|
780
|
-
if (pane.devWindowId && pane.devStatus === 'running') {
|
|
781
|
-
await attachBackgroundWindow(pane, 'dev');
|
|
782
|
-
}
|
|
783
|
-
else if (pane.testWindowId) {
|
|
784
|
-
await attachBackgroundWindow(pane, 'test');
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
else {
|
|
788
|
-
setStatusMessage('No test or dev window to open');
|
|
789
|
-
setTimeout(() => setStatusMessage(''), 2000);
|
|
790
|
-
}
|
|
1017
|
+
// Close pane (NEW: using action system)
|
|
1018
|
+
actionSystem.executeAction(PaneAction.CLOSE, panes[selectedIndex]);
|
|
791
1019
|
}
|
|
792
1020
|
else if (key.return && selectedIndex < panes.length) {
|
|
793
|
-
|
|
1021
|
+
// Jump to pane (NEW: using action system)
|
|
1022
|
+
actionSystem.executeAction(PaneAction.VIEW, panes[selectedIndex]);
|
|
794
1023
|
}
|
|
795
1024
|
});
|
|
796
|
-
// If showing
|
|
797
|
-
if (
|
|
798
|
-
return (React.createElement(
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
setShowMergePane(false);
|
|
805
|
-
setMergingPane(null);
|
|
806
|
-
} }));
|
|
1025
|
+
// If showing QR code, render only that
|
|
1026
|
+
if (showQRCode && tunnelUrl) {
|
|
1027
|
+
return (React.createElement(Box, { flexDirection: "column" },
|
|
1028
|
+
React.createElement(Box, { marginBottom: 1 },
|
|
1029
|
+
React.createElement(Text, { bold: true, color: "cyan" }, "dmux - Remote Access")),
|
|
1030
|
+
React.createElement(QRCode, { url: tunnelUrl }),
|
|
1031
|
+
React.createElement(Box, { marginTop: 1 },
|
|
1032
|
+
React.createElement(Text, { dimColor: true }, "Press ESC to return to pane list"))));
|
|
807
1033
|
}
|
|
808
1034
|
return (React.createElement(Box, { flexDirection: "column" },
|
|
809
|
-
React.createElement(
|
|
810
|
-
React.createElement(Text, { bold: true, color: "cyan" },
|
|
811
|
-
"dmux - ",
|
|
812
|
-
projectName)),
|
|
813
|
-
React.createElement(PanesGrid, { panes: panes, selectedIndex: selectedIndex, isLoading: isLoading, showNewPaneDialog: showNewPaneDialog, agentStatuses: agentStatuses }),
|
|
1035
|
+
React.createElement(PanesGrid, { panes: panes, selectedIndex: selectedIndex, isLoading: isLoading, showNewPaneDialog: showNewPaneDialog, agentStatuses: agentStatuses, kebabMenuPaneIndex: kebabMenuPaneIndex ?? undefined }),
|
|
814
1036
|
isLoading && (React.createElement(LoadingIndicator, null)),
|
|
815
1037
|
showNewPaneDialog && !showAgentChoiceDialog && (React.createElement(NewPaneDialog, { value: newPanePrompt, onChange: setNewPanePrompt, onSubmit: (value) => {
|
|
816
1038
|
const promptValue = value;
|
|
@@ -835,15 +1057,28 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, autoUpdate
|
|
|
835
1057
|
} })),
|
|
836
1058
|
showAgentChoiceDialog && (React.createElement(AgentChoiceDialog, { agentChoice: agentChoice })),
|
|
837
1059
|
isCreatingPane && (React.createElement(CreatingIndicator, { message: statusMessage })),
|
|
838
|
-
showMergeConfirmation && mergedPane && (React.createElement(MergeConfirmationDialog, { pane: mergedPane })),
|
|
839
|
-
showCloseOptions && closingPane && (React.createElement(CloseOptionsDialog, { pane: closingPane, selectedIndex: selectedCloseOption })),
|
|
840
1060
|
showCommandPrompt && (React.createElement(CommandPromptDialog, { type: showCommandPrompt, value: commandInput, onChange: setCommandInput })),
|
|
841
1061
|
showFileCopyPrompt && (React.createElement(FileCopyPrompt, null)),
|
|
1062
|
+
showKebabMenu && kebabMenuPaneIndex !== null && panes[kebabMenuPaneIndex] && (React.createElement(KebabMenu, { selectedOption: kebabMenuOption, actions: kebabMenuActions, paneName: panes[kebabMenuPaneIndex].slug })),
|
|
1063
|
+
actionSystem.actionState.showConfirmDialog && (React.createElement(ActionConfirmDialog, { key: "confirm-dialog", title: actionSystem.actionState.confirmTitle, message: actionSystem.actionState.confirmMessage, yesLabel: actionSystem.actionState.confirmYesLabel, noLabel: actionSystem.actionState.confirmNoLabel, selectedIndex: actionSystem.actionState.confirmSelectedIndex })),
|
|
1064
|
+
actionSystem.actionState.showChoiceDialog && (React.createElement(ActionChoiceDialog, { key: "choice-dialog", title: actionSystem.actionState.choiceTitle, message: actionSystem.actionState.choiceMessage, options: actionSystem.actionState.choiceOptions, selectedIndex: actionSystem.actionState.choiceSelectedIndex })),
|
|
1065
|
+
actionSystem.actionState.showInputDialog && (React.createElement(ActionInputDialog, { key: "input-dialog", title: actionSystem.actionState.inputTitle, message: actionSystem.actionState.inputMessage, placeholder: actionSystem.actionState.inputPlaceholder, value: actionSystem.actionState.inputValue, onValueChange: (value) => {
|
|
1066
|
+
actionSystem.setActionState(prev => ({ ...prev, inputValue: value }));
|
|
1067
|
+
} })),
|
|
1068
|
+
actionSystem.actionState.showProgressDialog && (React.createElement(ActionProgressDialog, { key: "progress-dialog", message: actionSystem.actionState.progressMessage, percent: actionSystem.actionState.progressPercent })),
|
|
842
1069
|
runningCommand && (React.createElement(RunningIndicator, null)),
|
|
843
1070
|
isUpdating && (React.createElement(UpdatingIndicator, null)),
|
|
1071
|
+
isCreatingTunnel && (React.createElement(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", padding: 1, marginTop: 1 },
|
|
1072
|
+
React.createElement(Text, { bold: true, color: "cyan" }, "Creating tunnel..."),
|
|
1073
|
+
React.createElement(Box, { marginTop: 1 },
|
|
1074
|
+
React.createElement(Text, { dimColor: true }, "This may take a few moments...")))),
|
|
844
1075
|
statusMessage && (React.createElement(Box, { marginTop: 1 },
|
|
845
1076
|
React.createElement(Text, { color: "green" }, statusMessage))),
|
|
846
|
-
React.createElement(
|
|
1077
|
+
actionSystem.actionState.statusMessage && (React.createElement(Box, { marginTop: 1 },
|
|
1078
|
+
React.createElement(Text, { color: actionSystem.actionState.statusType === 'error' ? 'red' :
|
|
1079
|
+
actionSystem.actionState.statusType === 'success' ? 'green' :
|
|
1080
|
+
'cyan' }, actionSystem.actionState.statusMessage))),
|
|
1081
|
+
React.createElement(FooterHelp, { show: !showNewPaneDialog && !showCommandPrompt, showRemoteKey: !!server, quitConfirmMode: quitConfirmMode, gridInfo: (() => {
|
|
847
1082
|
if (!process.env.DEBUG_DMUX)
|
|
848
1083
|
return undefined;
|
|
849
1084
|
const cols = Math.max(1, Math.floor(terminalWidth / 37));
|
|
@@ -851,14 +1086,12 @@ const DmuxApp = ({ panesFile, projectName, sessionName, settingsFile, autoUpdate
|
|
|
851
1086
|
const pos = getCardGridPosition(selectedIndex);
|
|
852
1087
|
return `Grid: ${cols} cols × ${rows} rows | Selected: row ${pos.row}, col ${pos.col} | Terminal: ${terminalWidth}w`;
|
|
853
1088
|
})() }),
|
|
854
|
-
React.createElement(
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
updateInfo.latestVersion,
|
|
861
|
-
" available! Run: npm i -g dmux@latest"))))));
|
|
1089
|
+
React.createElement(Text, { dimColor: true },
|
|
1090
|
+
"v",
|
|
1091
|
+
packageJson.version,
|
|
1092
|
+
updateAvailable && updateInfo && (React.createElement(Text, { color: "yellow" },
|
|
1093
|
+
" \u2022 Update available: ",
|
|
1094
|
+
updateInfo.latestVersion)))));
|
|
862
1095
|
};
|
|
863
1096
|
export default DmuxApp;
|
|
864
1097
|
//# sourceMappingURL=DmuxApp.js.map
|