Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,6 @@ typings/
# Electron-Forge
out/
src/build.json

# Claude Code worktrees
.claude/worktrees/
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## 1.0.58

- Feat: ⌃+⌘+T global shortcut for Terminal tab (customizable in Settings)
- Fix: Cmd+←/→ in xterm (beginning/end of line)

## 1.0.57

- Fix: menubar Keyboard Shortcuts submenu now reflects custom shortcuts
Expand Down
5 changes: 5 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# CodeV Development Guide

## Git

- **Default branch: `main`** (changed from `develop` — PRs should target `main`)
- `develop` branch is legacy and no longer used

## Build Commands

- Electron: `yarn start` (dev), `yarn make` (build), `yarn dev` (with server)
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ For the full same-cwd accuracy matrix (detection + switch by launch method and t

### Embedded Terminal

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.
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.

- Pre-spawned on app start for instant access
- Default working directory: Settings → Working Directory (fallback to home)
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "CodeV",
"productName": "CodeV",
"version": "1.0.57",
"version": "1.0.58",
"description": "Quick switcher for VS Code, Cursor, and Claude Code sessions",
"repository": {
"type": "git",
Expand Down
3 changes: 3 additions & 0 deletions src/TrayGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export interface ShortcutSettings {
quickSwitcher: string;
aiInsight: string;
aiChat: string;
terminal: string;
}

export class TrayGenerator {
Expand All @@ -25,6 +26,7 @@ export class TrayGenerator {
quickSwitcher: 'Command+Control+R',
aiInsight: 'Command+Control+E',
aiChat: 'Command+Control+C',
terminal: 'Command+Control+T',
};

constructor(
Expand Down Expand Up @@ -91,6 +93,7 @@ export class TrayGenerator {
label: 'Keyboard Shortcuts',
submenu: [
{ label: `${this.acceleratorToMenuLabel(this.shortcuts.quickSwitcher)}: Open CodeV Quick Switcher`, enabled: false },
{ label: `${this.acceleratorToMenuLabel(this.shortcuts.terminal)}: Terminal`, enabled: false },
{ label: 'Tab: Switch Projects / Sessions', enabled: false },
{ label: `${this.acceleratorToMenuLabel(this.shortcuts.aiInsight)}: AI Assistant Insight`, enabled: false },
{ label: `${this.acceleratorToMenuLabel(this.shortcuts.aiChat)}: AI Assistant Smart Chat`, enabled: false },
Expand Down
6 changes: 4 additions & 2 deletions src/electron-api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ interface IElectronAPI {
onFolderSelected: (callback: IpcCallback) => void;
onWorkingFolderIterated: (callback: IpcCallback) => void;
onFocusWindow: (callback: IpcCallback) => void;
onSwitchToTerminal: (callback: IpcCallback) => void;
onCheckTerminalAndHide: (callback: IpcCallback) => void;
onXWinNotFound: (callback: IpcCallback) => void;

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

// Keyboard shortcuts
getShortcuts: () => Promise<{ quickSwitcher: string; aiInsight: string; aiChat: string }>;
getShortcuts: () => Promise<{ quickSwitcher: string; aiInsight: string; aiChat: string; terminal: string }>;
setShortcut: (key: string, accelerator: string) => Promise<{ success: boolean; error?: string }>;
resetShortcuts: () => Promise<{ quickSwitcher: string; aiInsight: string; aiChat: string }>;
resetShortcuts: () => Promise<{ quickSwitcher: string; aiInsight: string; aiChat: string; terminal: string }>;
pauseShortcut: (key: string) => Promise<void>;
resumeShortcut: (key: string) => Promise<void>;

Expand Down
18 changes: 18 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1205,6 +1205,7 @@ const trayToggleEvtHandler = async () => {
quickSwitcher: 'Command+Control+R',
aiInsight: 'Command+Control+E',
aiChat: 'Command+Control+C',
terminal: 'Command+Control+T',
};

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

// Shortcut callback: Terminal (Ctrl+Cmd+T) — toggle behavior like Quick Switcher
const terminalCallback = () => {
if (BrowserWindow.getAllWindows().length === 0) {
switcherWindow = createSwitcherWindow();
}
const window = getSwitcherWindow();
if (window && window.isVisible()) {
// If already showing Terminal tab, hide; otherwise switch to Terminal
window.webContents.send('check-terminal-and-hide');
} else if (window) {
window.webContents.send('switch-to-terminal');
showSwitcherWindow();
}
};

// Map shortcut keys to their callbacks
const shortcutCallbacks: Record<string, () => void> = {
quickSwitcher: quickSwitcherCallback,
aiInsight: aiInsightCallback,
aiChat: aiChatCallback,
terminal: terminalCallback,
};

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

const syncTrayShortcuts = async () => {
Expand Down
3 changes: 3 additions & 0 deletions src/popup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ const PopupDefaultExample = ({
quickSwitcher: 'Command+Control+R',
aiInsight: 'Command+Control+E',
aiChat: 'Command+Control+C',
terminal: 'Command+Control+T',
});
const [editingShortcut, setEditingShortcut] = useState<string | null>(null);
const [shortcutError, setShortcutError] = useState('');
Expand Down Expand Up @@ -214,6 +215,7 @@ const PopupDefaultExample = ({
quickSwitcher: defaults.quickSwitcher,
aiInsight: defaults.aiInsight,
aiChat: defaults.aiChat,
terminal: defaults.terminal,
});
}
setEditingShortcut(null);
Expand All @@ -232,6 +234,7 @@ const PopupDefaultExample = ({
{ key: 'quickSwitcher', label: 'Quick Switcher' },
{ key: 'aiInsight', label: 'AI Insight' },
{ key: 'aiChat', label: 'AI Chat' },
{ key: 'terminal', label: 'Terminal' },
];

return (
Expand Down
2 changes: 2 additions & 0 deletions src/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ contextBridge.exposeInMainWorld('electronAPI', {
ipcRenderer.on('working-folder-iterated', callback),
openExternal: (url: string) => ipcRenderer.send('open-external', url),
onFocusWindow: (callback: any) => ipcRenderer.on('window-focus', callback),
onSwitchToTerminal: (callback: any) => ipcRenderer.on('switch-to-terminal', callback),
onCheckTerminalAndHide: (callback: any) => ipcRenderer.on('check-terminal-and-hide', callback),
onXWinNotFound: (callback: any) => ipcRenderer.on('xwin-not-found', callback),

// Listen for code to explain in the ai assistant window
Expand Down
14 changes: 14 additions & 0 deletions src/switcher-ui.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,20 @@ function SwitcherApp() {
forceFocusOnInput();
});

window.electronAPI.onSwitchToTerminal(() => {
modeRef.current = 'terminal';
setMode('terminal');
});

window.electronAPI.onCheckTerminalAndHide(() => {
if (modeRef.current === 'terminal') {
window.electronAPI.hideApp();
} else {
modeRef.current = 'terminal';
setMode('terminal');
}
});

// Data refresh on window focus:
// - Projects: always refetch (complements tab-switch which doesn't refetch projects)
// - Sessions: only refetch if sessions tab is active (tab-switch already fetches on entry)
Expand Down
3 changes: 3 additions & 0 deletions src/terminal-tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ const TerminalTab = ({ visible }: { visible: boolean }) => {
term.attachCustomKeyEventHandler((e) => {
if (e.metaKey && ['1', '2', '3', '[', ']'].includes(e.key)) return false;
if (e.ctrlKey && e.key === 'Tab') return false;
// Cmd+←/→ → beginning/end of line
if (e.type === 'keydown' && e.metaKey && e.key === 'ArrowLeft') { term.input('\x01'); return false; }
if (e.type === 'keydown' && e.metaKey && e.key === 'ArrowRight') { term.input('\x05'); return false; }
return true;
});

Expand Down