Skip to content

Commit 2ff8127

Browse files
grimmerkclaude
andcommitted
feat: add Ctrl+Cmd+T global shortcut for Terminal tab
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent ff8edb9 commit 2ff8127

5 files changed

Lines changed: 41 additions & 2 deletions

File tree

src/electron-api.d.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ interface IElectronAPI {
6363
onFolderSelected: (callback: IpcCallback) => void;
6464
onWorkingFolderIterated: (callback: IpcCallback) => void;
6565
onFocusWindow: (callback: IpcCallback) => void;
66+
onSwitchToTerminal: (callback: IpcCallback) => void;
67+
onCheckTerminalAndHide: (callback: IpcCallback) => void;
6668
onXWinNotFound: (callback: IpcCallback) => void;
6769

6870
// AI Assistant insight events
@@ -108,9 +110,9 @@ interface IElectronAPI {
108110
searchConversations: (searchTerm: string) => Promise<any>;
109111

110112
// Keyboard shortcuts
111-
getShortcuts: () => Promise<{ quickSwitcher: string; aiInsight: string; aiChat: string }>;
113+
getShortcuts: () => Promise<{ quickSwitcher: string; aiInsight: string; aiChat: string; terminal: string }>;
112114
setShortcut: (key: string, accelerator: string) => Promise<{ success: boolean; error?: string }>;
113-
resetShortcuts: () => Promise<{ quickSwitcher: string; aiInsight: string; aiChat: string }>;
115+
resetShortcuts: () => Promise<{ quickSwitcher: string; aiInsight: string; aiChat: string; terminal: string }>;
114116
pauseShortcut: (key: string) => Promise<void>;
115117
resumeShortcut: (key: string) => Promise<void>;
116118

src/main.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1205,6 +1205,7 @@ const trayToggleEvtHandler = async () => {
12051205
quickSwitcher: 'Command+Control+R',
12061206
aiInsight: 'Command+Control+E',
12071207
aiChat: 'Command+Control+C',
1208+
terminal: 'Command+Control+T',
12081209
};
12091210

12101211
// Shortcut callback: Quick Switcher
@@ -1516,11 +1517,27 @@ const trayToggleEvtHandler = async () => {
15161517
// End of the new implementation
15171518
};
15181519

1520+
// Shortcut callback: Terminal (Ctrl+Cmd+T) — toggle behavior like Quick Switcher
1521+
const terminalCallback = () => {
1522+
if (BrowserWindow.getAllWindows().length === 0) {
1523+
switcherWindow = createSwitcherWindow();
1524+
}
1525+
const window = getSwitcherWindow();
1526+
if (window && window.isVisible()) {
1527+
// If already showing Terminal tab, hide; otherwise switch to Terminal
1528+
window.webContents.send('check-terminal-and-hide');
1529+
} else if (window) {
1530+
window.webContents.send('switch-to-terminal');
1531+
showSwitcherWindow();
1532+
}
1533+
};
1534+
15191535
// Map shortcut keys to their callbacks
15201536
const shortcutCallbacks: Record<string, () => void> = {
15211537
quickSwitcher: quickSwitcherCallback,
15221538
aiInsight: aiInsightCallback,
15231539
aiChat: aiChatCallback,
1540+
terminal: terminalCallback,
15241541
};
15251542

15261543
// Register all shortcuts from settings (or defaults)
@@ -1542,6 +1559,7 @@ const trayToggleEvtHandler = async () => {
15421559
quickSwitcher: ((await settings.get('shortcut-quickSwitcher')) as string) || DEFAULT_SHORTCUTS.quickSwitcher,
15431560
aiInsight: ((await settings.get('shortcut-aiInsight')) as string) || DEFAULT_SHORTCUTS.aiInsight,
15441561
aiChat: ((await settings.get('shortcut-aiChat')) as string) || DEFAULT_SHORTCUTS.aiChat,
1562+
terminal: ((await settings.get('shortcut-terminal')) as string) || DEFAULT_SHORTCUTS.terminal,
15451563
});
15461564

15471565
const syncTrayShortcuts = async () => {

src/popup.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ const PopupDefaultExample = ({
6868
quickSwitcher: 'Command+Control+R',
6969
aiInsight: 'Command+Control+E',
7070
aiChat: 'Command+Control+C',
71+
terminal: 'Command+Control+T',
7172
});
7273
const [editingShortcut, setEditingShortcut] = useState<string | null>(null);
7374
const [shortcutError, setShortcutError] = useState('');
@@ -214,6 +215,7 @@ const PopupDefaultExample = ({
214215
quickSwitcher: defaults.quickSwitcher,
215216
aiInsight: defaults.aiInsight,
216217
aiChat: defaults.aiChat,
218+
terminal: defaults.terminal,
217219
});
218220
}
219221
setEditingShortcut(null);
@@ -232,6 +234,7 @@ const PopupDefaultExample = ({
232234
{ key: 'quickSwitcher', label: 'Quick Switcher' },
233235
{ key: 'aiInsight', label: 'AI Insight' },
234236
{ key: 'aiChat', label: 'AI Chat' },
237+
{ key: 'terminal', label: 'Terminal' },
235238
];
236239

237240
return (

src/preload.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ contextBridge.exposeInMainWorld('electronAPI', {
7171
ipcRenderer.on('working-folder-iterated', callback),
7272
openExternal: (url: string) => ipcRenderer.send('open-external', url),
7373
onFocusWindow: (callback: any) => ipcRenderer.on('window-focus', callback),
74+
onSwitchToTerminal: (callback: any) => ipcRenderer.on('switch-to-terminal', callback),
75+
onCheckTerminalAndHide: (callback: any) => ipcRenderer.on('check-terminal-and-hide', callback),
7476
onXWinNotFound: (callback: any) => ipcRenderer.on('xwin-not-found', callback),
7577

7678
// Listen for code to explain in the ai assistant window

src/switcher-ui.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,20 @@ function SwitcherApp() {
491491
forceFocusOnInput();
492492
});
493493

494+
window.electronAPI.onSwitchToTerminal(() => {
495+
modeRef.current = 'terminal';
496+
setMode('terminal');
497+
});
498+
499+
window.electronAPI.onCheckTerminalAndHide(() => {
500+
if (modeRef.current === 'terminal') {
501+
window.electronAPI.hideApp();
502+
} else {
503+
modeRef.current = 'terminal';
504+
setMode('terminal');
505+
}
506+
});
507+
494508
// Data refresh on window focus:
495509
// - Projects: always refetch (complements tab-switch which doesn't refetch projects)
496510
// - Sessions: only refetch if sessions tab is active (tab-switch already fetches on entry)

0 commit comments

Comments
 (0)