Skip to content

Commit 8acf951

Browse files
authored
Merge pull request #86 from grimmerk/feat/terminal-shortcut
feat: ⌃+⌘+T global shortcut for Terminal tab + xterm Cmd+←/→
2 parents e0d73b5 + 2aef0f6 commit 8acf951

12 files changed

Lines changed: 62 additions & 4 deletions

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,6 @@ typings/
9494
# Electron-Forge
9595
out/
9696
src/build.json
97+
98+
# Claude Code worktrees
99+
.claude/worktrees/

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
## 1.0.58
4+
5+
- Feat: ⌃+⌘+T global shortcut for Terminal tab (customizable in Settings)
6+
- Fix: Cmd+←/→ in xterm (beginning/end of line)
7+
38
## 1.0.57
49

510
- Fix: menubar Keyboard Shortcuts submenu now reflects custom shortcuts

CLAUDE.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# CodeV Development Guide
22

3+
## Git
4+
5+
- **Default branch: `main`** (changed from `develop` — PRs should target `main`)
6+
- `develop` branch is legacy and no longer used
7+
38
## Build Commands
49

510
- Electron: `yarn start` (dev), `yarn make` (build), `yarn dev` (with server)

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ For the full same-cwd accuracy matrix (detection + switch by launch method and t
4141

4242
### Embedded Terminal
4343

44-
CodeV includes a built-in terminal tab (powered by xterm.js + node-pty, same technology as VS Code's integrated terminal). Press `⌘+3` or click the **Term** tab to open it.
44+
CodeV includes a built-in terminal tab (powered by xterm.js + node-pty, same technology as VS Code's integrated terminal). Press `⌃+⌘+T` from anywhere (global shortcut) or `⌘+3` when CodeV is in foreground to open it.
4545

4646
- Pre-spawned on app start for instant access
4747
- Default working directory: Settings → Working Directory (fallback to home)

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "CodeV",
33
"productName": "CodeV",
4-
"version": "1.0.57",
4+
"version": "1.0.58",
55
"description": "Quick switcher for VS Code, Cursor, and Claude Code sessions",
66
"repository": {
77
"type": "git",

src/TrayGenerator.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export interface ShortcutSettings {
1414
quickSwitcher: string;
1515
aiInsight: string;
1616
aiChat: string;
17+
terminal: string;
1718
}
1819

1920
export class TrayGenerator {
@@ -25,6 +26,7 @@ export class TrayGenerator {
2526
quickSwitcher: 'Command+Control+R',
2627
aiInsight: 'Command+Control+E',
2728
aiChat: 'Command+Control+C',
29+
terminal: 'Command+Control+T',
2830
};
2931

3032
constructor(
@@ -91,6 +93,7 @@ export class TrayGenerator {
9193
label: 'Keyboard Shortcuts',
9294
submenu: [
9395
{ label: `${this.acceleratorToMenuLabel(this.shortcuts.quickSwitcher)}: Open CodeV Quick Switcher`, enabled: false },
96+
{ label: `${this.acceleratorToMenuLabel(this.shortcuts.terminal)}: Terminal`, enabled: false },
9497
{ label: 'Tab: Switch Projects / Sessions', enabled: false },
9598
{ label: `${this.acceleratorToMenuLabel(this.shortcuts.aiInsight)}: AI Assistant Insight`, enabled: false },
9699
{ label: `${this.acceleratorToMenuLabel(this.shortcuts.aiChat)}: AI Assistant Smart Chat`, enabled: false },

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

0 commit comments

Comments
 (0)