Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
b014196
feat: normal app mode (dock visible, no auto-hide, draggable)
grimmerk Apr 6, 2026
06efb1b
fix: normal mode show on startup, shortcut only focus, conditional drag
grimmerk Apr 6, 2026
007b867
fix: re-center on menu bar switch, hide toggle in normal mode
grimmerk Apr 6, 2026
b526e3c
feat: mode indicator in title bar, banner on mode change, dock activate
grimmerk Apr 6, 2026
e27d5e4
fix: shortcut on right side of title bar, banner only on mode switch
grimmerk Apr 6, 2026
7afdb2c
fix: sync shortcut display on save/reset
grimmerk Apr 6, 2026
e02a9ea
style: add Dev Hub sub-title + mode indicator below
grimmerk Apr 6, 2026
b225e22
style: reduce title bar padding from 10px to 8px
grimmerk Apr 6, 2026
68c501f
feat: Settings UI with tabs (General / Sessions / Shortcuts)
grimmerk Apr 6, 2026
a771beb
style: reorder settings, add hints, rename Terminal.app
grimmerk Apr 6, 2026
b28fcd6
chore: bump version to 1.0.70
grimmerk Apr 6, 2026
5bd9bb9
fix: remove duplicate Working Dir, fix Launch Mode hint
grimmerk Apr 6, 2026
1a876e1
fix: remove Launch Terminal hint, rename Launch Mode to Open In
grimmerk Apr 6, 2026
f0f9f57
fix: add hint to Launch Terminal, simplify Open In label
grimmerk Apr 6, 2026
1dde984
style: indent Open In as sub-item of Launch Terminal
grimmerk Apr 6, 2026
36a9694
style: use arrow prefix for Open In sub-item
grimmerk Apr 6, 2026
aa5d8a0
docs: add App Mode comparison table to README
grimmerk Apr 6, 2026
25e456b
fix: address review — banner timeout, dock await, startup timing, sho…
grimmerk Apr 6, 2026
50d4bf0
fix: guard shortcut cleanup on tab re-click, show after bootstrap
grimmerk Apr 6, 2026
5a16167
docs: update Settings paths to match tabbed UI
grimmerk Apr 6, 2026
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
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# Changelog

## 1.0.70

- Feat: Normal App mode — window stays visible, shows in Dock, draggable
- Toggle in Settings: Normal App (default for new users) / Menu Bar
- Instant switching, no restart needed
- `Cmd+Ctrl+R` toggles show/hide in both modes
- Title bar shows "Dev Hub" sub-title + mode indicator + shortcut key
- Banner on first launch and mode switch (auto-dismiss)
- Clicking Dock icon shows hidden window
- Feat: Settings UI redesigned with tabs (General / Sessions / Shortcuts)
- All settings visible without scrolling
- No more content changing based on active main tab
- Hints on context-specific settings (projects/sessions/tray)
- Style: Terminal renamed to Terminal.app in Launch Terminal dropdown
- Style: title bar padding reduced for tighter layout

## 1.0.69

- Feat: adaptive VS Code resume via IDE lock file polling
Expand Down
24 changes: 20 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ Quick switcher for VS Code/Cursor projects, Claude Code session manager with liv

### Quick Switcher for VS Code / Cursor Projects

Spotlight-like quick open: press `⌃+⌘+R` or click the menu bar icon to launch the Quick Switcher. Search and select a project to open or switch to it in VS Code or Cursor — even if the IDE is not running yet.
Press `⌃+⌘+R` or click the menu bar icon to launch the Quick Switcher. Search and select a project to open or switch to it in VS Code or Cursor — even if the IDE is not running yet. In Normal App mode, the window stays visible for monitoring; in Menu Bar mode, it works like Spotlight.

- **Recent projects** (white items): your latest VS Code/Cursor folders, workspaces, and recently opened files — read directly from IDE data, no extension required
- **Working folder items** (green items): first-level subfolders found by scanning a folder you choose (Settings → Working Directory)
- **Working folder items** (green items): first-level subfolders found by scanning a folder you choose (Settings → General → Working Dir)
- **Git branch display**: shows the current branch for each recently opened project
- Multi-word search across project names, paths, and branch names
- Supports VS Code and Cursor — switch between them in Settings → IDE Preference
- Supports VS Code and Cursor — switch between them in Settings → General → IDE
- Remove items from the recent list by hovering and clicking "x"
- **Quick-launch Claude session**: `⌘+Enter` to launch a new Claude Code session in the configured terminal, `⇧+Enter` to launch in CodeV's embedded terminal, `⌘+Click` as mouse alternative

Expand Down Expand Up @@ -46,12 +46,28 @@ For the full same-cwd accuracy matrix (detection + switch by launch method and t
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)
- Default working directory: Settings → General → Working Dir (fallback to home)
- Terminal state preserved when switching tabs
- `⌘+K` clears screen, `Shift+Enter` for multi-line input (Claude Code compatible)
- `Cmd+←/→` jumps to beginning/end of line
- **"Claude in Terminal" button**: launches a new Claude Code session in the configured external terminal using the current working directory

### App Mode

CodeV supports two window modes, configurable in Settings → General → App Mode:

| | Normal App (default) | Menu Bar |
|--|--|--|
| **Dock** | Visible | Hidden |
| **On blur** | Stays visible | Auto-hides |
| **Window position** | Remembers last position, draggable | Centers on screen each time |
| **On startup** | Shows window | Hidden until shortcut/tray click |
| **`⌃+⌘+R`** | Toggle show/hide | Toggle show/hide |
| **Click Dock icon** | Shows hidden window | N/A |
| **Best for** | Dashboard / monitoring (keep in corner) | Quick access (spotlight-like) |

**Real-time updates when unfocused (Normal mode):** Status dots, final assistant/user messages, and session order update via fs.watch — no need to re-focus. New sessions and full list refresh only occur on re-focus.

### Tab Switching

| Shortcut | Action |
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.69",
"version": "1.0.70",
"description": "Quick switcher for VS Code, Cursor, and Claude Code sessions",
"repository": {
"type": "git",
Expand Down
6 changes: 6 additions & 0 deletions src/electron-api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ interface IElectronAPI {
getUpdateStatus: () => Promise<{ status: string; releaseName?: string; error?: string } | null>;
onUpdateStatus: (callback: IpcCallback) => void;

// App mode
getAppMode: () => Promise<string>;
setAppMode: (mode: string) => void;
onAppModeChanged: (callback: IpcCallback) => void;
onShortcutsUpdated: (callback: IpcCallback) => void;

// Session terminal settings
getSessionTerminalApp: () => Promise<string>;
setSessionTerminalApp: (app: string) => void;
Expand Down
70 changes: 62 additions & 8 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ let serverProcess: any;
const WIN_WIDTH = 800;
const WIN_HEIGHT = 600;

// App mode: 'normal' (dock visible, no auto-hide) or 'menubar' (hidden dock, auto-hide on blur)
let appMode: 'normal' | 'menubar' = 'normal'; // default to normal for new users

const getWindowPosition = () => {
const primaryDisplay = screen.getPrimaryDisplay();
const { width, height } = primaryDisplay.workAreaSize;
Expand All @@ -95,19 +98,23 @@ const getWindowPosition = () => {
// NOTE: setVisibleOnAllWorkspaces is needed ?
const showSwitcherWindow = () => {
let window = getSwitcherWindow();

if (!window) {
// Recreate window if it has been destroyed
switcherWindow = createSwitcherWindow();
window = switcherWindow;
}

const position = getWindowPosition();
window.setPosition(position.x, position.y, false);

if (appMode === 'menubar') {
// Menu bar mode: always center on screen
const position = getWindowPosition();
window.setPosition(position.x, position.y, false);
}
if (window.isMinimized()) {
window.restore();
}
window.show();
// mainWindow.setVisibleOnAllWorkspaces(true);
window.focus();
// mainWindow.setVisibleOnAllWorkspaces(false);
};

const showAIAssistantWindow = () => {
Expand Down Expand Up @@ -258,6 +265,8 @@ const hideSwitcherWindow = () => {
};

const onBlur = (event: any) => {
// Normal mode: don't auto-hide on blur
if (appMode === 'normal') return;
hideSwitcherWindow();
};

Expand Down Expand Up @@ -506,6 +515,11 @@ app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
switcherWindow = createSwitcherWindow();
}
// Normal mode: clicking Dock icon shows hidden window
const window = getSwitcherWindow();
if (window && !window.isVisible()) {
showSwitcherWindow();
}
});

/** https://www.electronjs.org/docs/latest/tutorial/ipc */
Expand Down Expand Up @@ -1016,6 +1030,12 @@ const trayToggleEvtHandler = async () => {
(async () => {
await app.whenReady();

// Load app mode setting early (before window creation)
appMode = ((await settings.get('app-mode')) as 'normal' | 'menubar') || 'normal';
if (appMode === 'menubar') {
app.dock.hide();
}

// Auto-update: check for updates via update.electronjs.org (non-MAS only)
if (!isMAS()) {
try {
Expand Down Expand Up @@ -1255,6 +1275,11 @@ const trayToggleEvtHandler = async () => {
// Load user settings
await loadUserSettings();

// Normal mode: show window after bootstrap + settings loaded (server ready for API calls)
if (appMode === 'normal') {
showSwitcherWindow();
}

let title = '';
if (!isDebug) {
title = ``;
Expand Down Expand Up @@ -1293,7 +1318,7 @@ const trayToggleEvtHandler = async () => {
} else {
const window = getSwitcherWindow();

if (window && window.isVisible()) {
if (window && window.isVisible() && !window.isMinimized()) {
if (isDebug) {
console.log('Switcher window visible, hiding it');
}
Expand Down Expand Up @@ -1679,6 +1704,8 @@ const trayToggleEvtHandler = async () => {
if (registered) {
await settings.set(`shortcut-${key}`, accelerator);
await syncTrayShortcuts();
// Notify switcher window to update shortcut display
switcherWindow?.webContents.send('shortcuts-updated', await getCurrentShortcuts());
return { success: true };
} else {
// Re-register the old shortcut since the new one failed
Expand All @@ -1694,6 +1721,8 @@ const trayToggleEvtHandler = async () => {
}
await registerAllShortcuts();
await syncTrayShortcuts();
// Notify switcher window to update shortcut display
switcherWindow?.webContents.send('shortcuts-updated', DEFAULT_SHORTCUTS);
return DEFAULT_SHORTCUTS;
});
})();
Expand Down Expand Up @@ -1934,6 +1963,31 @@ ipcMain.handle('get-session-statuses', async () => {
return obj;
});

ipcMain.handle('get-app-mode', async () => {
return appMode;
});

ipcMain.on('set-app-mode', async (_event, mode: string) => {
const newMode = mode === 'menubar' ? 'menubar' : 'normal';
await settings.set('app-mode', newMode);
appMode = newMode;
if (newMode === 'menubar') {
app.dock.hide();
} else {
await app.dock.show();
}
// Notify renderer to update drag region
const window = getSwitcherWindow();
if (window) {
window.webContents.send('app-mode-changed', newMode);
// Re-center when switching to menu bar mode
if (newMode === 'menubar') {
const position = getWindowPosition();
window.setPosition(position.x, position.y, false);
}
}
});

ipcMain.handle('get-session-terminal-app', async () => {
return (await settings.get('session-terminal-app')) || 'iterm2';
});
Expand Down Expand Up @@ -2077,4 +2131,4 @@ ipcMain.handle('detect-active-ide-projects', async () => {
return Array.from(folderNames);
});

app.dock.hide();
// app.dock.hide() moved to async init block (after settings loaded)
Loading