mirror of
https://github.com/jlengrand/Maestro.git
synced 2026-03-10 08:31:19 +00:00
feat(toggleMode): restore file preview tab when returning from terminal
When pressing cmd+j to switch between AI/terminal mode, the previously active file preview tab is now saved and restored. This allows users to view a file, switch to terminal, run commands, then switch back and have the same file preview re-opened automatically. Added preTerminalFileTabIdRef to UILayoutContext to track the active file tab before switching to terminal mode. Updated toggleInputMode to save/restore this state.
This commit is contained in:
@@ -460,6 +460,43 @@ describe('useMainKeyboardHandler', () => {
|
||||
expect(mockReopenUnifiedClosedTab).toHaveBeenCalledWith(mockActiveSession);
|
||||
expect(mockSetSessions).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should allow toggleMode shortcut (Cmd+J) when only overlays are open', () => {
|
||||
const { result } = renderHook(() => useMainKeyboardHandler());
|
||||
|
||||
const mockToggleInputMode = vi.fn();
|
||||
const mockActiveSession = {
|
||||
id: 'test-session',
|
||||
name: 'Test',
|
||||
inputMode: 'ai',
|
||||
aiTabs: [{ id: 'tab-1', name: 'Tab 1', logs: [] }],
|
||||
activeTabId: 'tab-1',
|
||||
filePreviewTabs: [{ id: 'file-tab-1', path: '/test.ts' }],
|
||||
activeFileTabId: 'file-tab-1', // File preview is active
|
||||
};
|
||||
|
||||
result.current.keyboardHandlerRef.current = createMockContext({
|
||||
hasOpenLayers: () => true, // Overlay is open (file preview)
|
||||
hasOpenModal: () => false, // But no true modal
|
||||
isShortcut: (_e: KeyboardEvent, actionId: string) => actionId === 'toggleMode',
|
||||
activeSessionId: 'test-session',
|
||||
activeSession: mockActiveSession,
|
||||
toggleInputMode: mockToggleInputMode,
|
||||
});
|
||||
|
||||
act(() => {
|
||||
window.dispatchEvent(
|
||||
new KeyboardEvent('keydown', {
|
||||
key: 'j',
|
||||
metaKey: true,
|
||||
bubbles: true,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// Cmd+J should toggle mode even when file preview overlay is open
|
||||
expect(mockToggleInputMode).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('navigation handlers delegation', () => {
|
||||
|
||||
@@ -757,6 +757,8 @@ function MaestroConsoleInner() {
|
||||
const { showUnreadOnly, setShowUnreadOnly } = useUILayout();
|
||||
// Track the active tab ID before entering unread filter mode, so we can restore it when exiting
|
||||
const { preFilterActiveTabIdRef } = useUILayout();
|
||||
// Track the active file tab ID before switching to terminal mode, so we can restore it when returning to AI mode
|
||||
const { preTerminalFileTabIdRef } = useUILayout();
|
||||
|
||||
// File Explorer State
|
||||
const [filePreviewLoading, setFilePreviewLoading] = useState<{
|
||||
@@ -9799,14 +9801,27 @@ You are taking over this conversation. Based on the context above, provide a bri
|
||||
prev.map((s) => {
|
||||
if (s.id !== activeSessionId) return s;
|
||||
const newMode = s.inputMode === 'ai' ? 'terminal' : 'ai';
|
||||
// Clear file preview when switching to terminal mode
|
||||
// This ensures cmd+j from file preview goes directly to terminal
|
||||
const clearFileTab = newMode === 'terminal';
|
||||
return {
|
||||
...s,
|
||||
inputMode: newMode,
|
||||
...(clearFileTab && { activeFileTabId: null }),
|
||||
};
|
||||
|
||||
if (newMode === 'terminal') {
|
||||
// Switching to terminal mode: save current file tab (if any) and clear it
|
||||
preTerminalFileTabIdRef.current = s.activeFileTabId;
|
||||
return {
|
||||
...s,
|
||||
inputMode: newMode,
|
||||
activeFileTabId: null,
|
||||
};
|
||||
} else {
|
||||
// Switching to AI mode: restore previous file tab if it still exists
|
||||
const savedFileTabId = preTerminalFileTabIdRef.current;
|
||||
const fileTabStillExists =
|
||||
savedFileTabId && s.filePreviewTabs?.some((t) => t.id === savedFileTabId);
|
||||
preTerminalFileTabIdRef.current = null;
|
||||
return {
|
||||
...s,
|
||||
inputMode: newMode,
|
||||
...(fileTabStillExists && { activeFileTabId: savedFileTabId }),
|
||||
};
|
||||
}
|
||||
})
|
||||
);
|
||||
// Close any open dropdowns when switching modes
|
||||
|
||||
@@ -50,6 +50,8 @@ export interface UILayoutContextValue {
|
||||
setShowUnreadOnly: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
toggleShowUnreadOnly: () => void;
|
||||
preFilterActiveTabIdRef: React.MutableRefObject<string | null>;
|
||||
// Track the active file tab ID before switching to terminal mode, so we can restore it when returning to AI mode
|
||||
preTerminalFileTabIdRef: React.MutableRefObject<string | null>;
|
||||
|
||||
// Session sidebar selection
|
||||
selectedSidebarIndex: number;
|
||||
@@ -128,6 +130,8 @@ export function UILayoutProvider({ children }: UILayoutProviderProps) {
|
||||
const [showUnreadOnly, setShowUnreadOnly] = useState(false);
|
||||
// Track the active tab ID before entering unread filter mode, so we can restore it when exiting
|
||||
const preFilterActiveTabIdRef = useRef<string | null>(null);
|
||||
// Track the active file tab ID before switching to terminal mode, so we can restore it when returning to AI mode
|
||||
const preTerminalFileTabIdRef = useRef<string | null>(null);
|
||||
|
||||
// Session sidebar selection
|
||||
const [selectedSidebarIndex, setSelectedSidebarIndex] = useState(0);
|
||||
@@ -206,6 +210,7 @@ export function UILayoutProvider({ children }: UILayoutProviderProps) {
|
||||
setShowUnreadOnly,
|
||||
toggleShowUnreadOnly,
|
||||
preFilterActiveTabIdRef,
|
||||
preTerminalFileTabIdRef,
|
||||
|
||||
// Session sidebar selection
|
||||
selectedSidebarIndex,
|
||||
|
||||
Reference in New Issue
Block a user