From 3f8527b667baa09053bdcf3b44b0a2cce452e6e6 Mon Sep 17 00:00:00 2001 From: Naved Rangwala Date: Mon, 27 Apr 2026 14:16:36 -0700 Subject: [PATCH 1/2] disable cursor blink to reduce idle terminal CPU usage 5 idle terminals with cursorBlink: true cause ~28% CPU from constant WebGL repaint cycles. With blink disabled, idle terminals drop to near-zero. Co-Authored-By: Claude Opus 4.6 (1M context) --- public/terminal-manager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/terminal-manager.js b/public/terminal-manager.js index d533500..6863b22 100644 --- a/public/terminal-manager.js +++ b/public/terminal-manager.js @@ -175,7 +175,7 @@ function createTerminalEntry(session) { fontSize: 12, fontFamily: "'SF Mono', 'Fira Code', 'Cascadia Code', Menlo, monospace", theme: TERMINAL_THEME, - cursorBlink: true, + cursorBlink: false, scrollback: 10000, convertEol: true, allowProposedApi: true, From c59792244316c9f4cefa481eb4b12160590e4cfd Mon Sep 17 00:00:00 2001 From: Naved Rangwala Date: Mon, 27 Apr 2026 14:46:49 -0700 Subject: [PATCH 2/2] fix add-project on Windows: handle backslash and colon in drive paths The folder-name regex only replaced / and _, so Windows paths like Q:\src\DevBox produced invalid mkdir targets under .claude/projects/. Add \ and : to the character class across all 10 occurrences. Co-Authored-By: Claude Opus 4.6 (1M context) --- main.js | 6 +++--- public/app.js | 4 ++-- public/dialogs.js | 4 ++-- schedule-ipc.js | 4 ++-- session-cache.js | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/main.js b/main.js index a24a1b2..ba47eb7 100644 --- a/main.js +++ b/main.js @@ -293,7 +293,7 @@ ipcMain.handle('add-project', (_event, projectPath) => { } // Create the corresponding folder in ~/.claude/projects/ so it persists - const folder = projectPath.replace(/[/_]/g, '-').replace(/^-/, '-'); + const folder = projectPath.replace(/[\\/:_]/g, '-').replace(/^-/, '-'); const folderPath = path.join(PROJECTS_DIR, folder); if (!fs.existsSync(folderPath)) { fs.mkdirSync(folderPath, { recursive: true }); @@ -329,7 +329,7 @@ ipcMain.handle('remove-project', (_event, projectPath) => { setSetting('global', global); // Clean up DB cache and search index for this folder - const folder = projectPath.replace(/[/_]/g, '-').replace(/^-/, '-'); + const folder = projectPath.replace(/[\\/:_]/g, '-').replace(/^-/, '-'); deleteCachedFolder(folder); deleteSearchFolder(folder); deleteSetting('project:' + projectPath); @@ -981,7 +981,7 @@ ipcMain.handle('open-terminal', async (_event, sessionId, projectPath, isNew, se if (!isPlainTerminal) { // Snapshot existing .jsonl files before spawning (for new session + fork/plan detection) - projectFolder = projectPath.replace(/[/_]/g, '-').replace(/^-/, '-'); + projectFolder = projectPath.replace(/[\\/:_]/g, '-').replace(/^-/, '-'); const claudeProjectDir = path.join(PROJECTS_DIR, projectFolder); if (fs.existsSync(claudeProjectDir)) { try { diff --git a/public/app.js b/public/app.js index 9875965..9b5f815 100644 --- a/public/app.js +++ b/public/app.js @@ -670,7 +670,7 @@ async function loadProjects({ resort = false } = {}) { const activeTerminals = await window.api.getActiveTerminals(); for (const { sessionId, projectPath } of activeTerminals) { if (pendingSessions.has(sessionId)) continue; // already tracked - const folder = projectPath.replace(/[/_]/g, '-').replace(/^-/, '-'); + const folder = projectPath.replace(/[\\/:_]/g, '-').replace(/^-/, '-'); // Find the session object already injected by the backend let session; for (const proj of cachedAllProjects) { @@ -709,7 +709,7 @@ async function launchNewSession(project, sessionOptions) { }; // Track as pending (no .jsonl yet) - const folder = projectPath.replace(/[/_]/g, '-').replace(/^-/, '-'); + const folder = projectPath.replace(/[\\/:_]/g, '-').replace(/^-/, '-'); pendingSessions.set(sessionId, { session, projectPath, folder }); // Inject into cached project data so it appears in sidebar immediately diff --git a/public/dialogs.js b/public/dialogs.js index dab5b16..9388562 100644 --- a/public/dialogs.js +++ b/public/dialogs.js @@ -49,7 +49,7 @@ async function launchScheduleCreator(project) { }; // Inject into sidebar - const folder = project.projectPath.replace(/[/_]/g, '-').replace(/^-/, '-'); + const folder = project.projectPath.replace(/[\\/:_]/g, '-').replace(/^-/, '-'); pendingSessions.set(result.sessionId, { session, projectPath: project.projectPath, folder }); sessionMap.set(result.sessionId, session); for (const projList of [cachedProjects, cachedAllProjects]) { @@ -141,7 +141,7 @@ async function launchTerminalSession(project) { }; // Track as pending - const folder = projectPath.replace(/[/_]/g, '-').replace(/^-/, '-'); + const folder = projectPath.replace(/[\\/:_]/g, '-').replace(/^-/, '-'); pendingSessions.set(sessionId, { session, projectPath, folder }); // Inject into cached project data diff --git a/schedule-ipc.js b/schedule-ipc.js index 7d83e66..0eb5d84 100644 --- a/schedule-ipc.js +++ b/schedule-ipc.js @@ -144,7 +144,7 @@ function init(log, runCommand) { const sessionId = crypto.randomUUID(); const msgId = crypto.randomUUID(); const timestamp = new Date().toISOString(); - const folder = projectPath.replace(/[/_]/g, '-').replace(/^-/, '-'); + const folder = projectPath.replace(/[\\/:_]/g, '-').replace(/^-/, '-'); const claudeProjectDir = path.join(PROJECTS_DIR, folder); fs.mkdirSync(claudeProjectDir, { recursive: true }); @@ -191,7 +191,7 @@ function init(log, runCommand) { const dotClaudeDir = path.dirname(commandsDir); const projectPath = path.dirname(dotClaudeDir); - const folder = projectPath.replace(/[/_]/g, '-').replace(/^-/, '-'); + const folder = projectPath.replace(/[\\/:_]/g, '-').replace(/^-/, '-'); const schedule = { file: path.basename(filePath), filePath, projectPath, folder, diff --git a/session-cache.js b/session-cache.js index 4a306de..5174f74 100644 --- a/session-cache.js +++ b/session-cache.js @@ -212,7 +212,7 @@ function buildProjectsFromCache(showArchived) { // Inject active plain terminal sessions so they participate in sorting for (const [sessionId, session] of activeSessions) { if (session.exited || !session.isPlainTerminal) continue; - const folder = session.projectPath.replace(/[/_]/g, '-').replace(/^-/, '-'); + const folder = session.projectPath.replace(/[\\/:_]/g, '-').replace(/^-/, '-'); if (hiddenProjects.has(session.projectPath)) continue; if (!projectMap.has(folder)) { projectMap.set(folder, { folder, projectPath: session.projectPath, sessions: [] });