diff options
Diffstat (limited to 'src/main')
| -rw-r--r-- | src/main/git.ts | 23 | ||||
| -rw-r--r-- | src/main/index.ts | 14 | ||||
| -rw-r--r-- | src/main/ipc/handlers.ts | 27 | ||||
| -rw-r--r-- | src/main/preload.ts | 12 |
4 files changed, 70 insertions, 6 deletions
diff --git a/src/main/git.ts b/src/main/git.ts index 20362a7..ec81e8f 100644 --- a/src/main/git.ts +++ b/src/main/git.ts | |||
| @@ -58,6 +58,29 @@ export function ensureGitRepo(projectPath: string): void { | |||
| 58 | } | 58 | } |
| 59 | 59 | ||
| 60 | // --------------------------------------------------------------------------- | 60 | // --------------------------------------------------------------------------- |
| 61 | // Current branch query | ||
| 62 | // --------------------------------------------------------------------------- | ||
| 63 | |||
| 64 | /** | ||
| 65 | * Returns the name of the currently checked-out branch, | ||
| 66 | * or null if git is unavailable or HEAD is detached. | ||
| 67 | */ | ||
| 68 | export function getCurrentBranch(projectPath: string): string | null { | ||
| 69 | try { | ||
| 70 | return ( | ||
| 71 | execFileSync("git", ["branch", "--show-current"], { | ||
| 72 | cwd: projectPath, | ||
| 73 | stdio: "pipe", | ||
| 74 | }) | ||
| 75 | .toString() | ||
| 76 | .trim() || null | ||
| 77 | ); | ||
| 78 | } catch { | ||
| 79 | return null; | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | // --------------------------------------------------------------------------- | ||
| 61 | // Branch creation | 84 | // Branch creation |
| 62 | // --------------------------------------------------------------------------- | 85 | // --------------------------------------------------------------------------- |
| 63 | 86 | ||
diff --git a/src/main/index.ts b/src/main/index.ts index a7bed00..f913ac0 100644 --- a/src/main/index.ts +++ b/src/main/index.ts | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | import { app, BrowserWindow, Menu } from "electron"; | 1 | import { app, BrowserWindow, Menu, ipcMain } from "electron"; |
| 2 | import path from "node:path"; | 2 | import path from "node:path"; |
| 3 | import { getDb, closeDb } from "./db"; | 3 | import { getDb, closeDb } from "./db"; |
| 4 | import { registerIpcHandlers } from "./ipc/handlers"; | 4 | import { registerIpcHandlers } from "./ipc/handlers"; |
| @@ -23,6 +23,18 @@ function createWindow() { | |||
| 23 | 23 | ||
| 24 | registerIpcHandlers(mainWindow); | 24 | registerIpcHandlers(mainWindow); |
| 25 | 25 | ||
| 26 | // Maximize toggle — works identically on Linux and macOS | ||
| 27 | ipcMain.handle("window:toggleMaximize", () => { | ||
| 28 | if (mainWindow!.isMaximized()) mainWindow!.unmaximize(); | ||
| 29 | else mainWindow!.maximize(); | ||
| 30 | }); | ||
| 31 | |||
| 32 | // Push state to renderer so the button glyph stays accurate. | ||
| 33 | // On macOS, clicking the green traffic light also fires these events, | ||
| 34 | // keeping our custom button in sync with the native control. | ||
| 35 | mainWindow.on("maximize", () => mainWindow!.webContents.send("window:maximized", true)); | ||
| 36 | mainWindow.on("unmaximize", () => mainWindow!.webContents.send("window:maximized", false)); | ||
| 37 | |||
| 26 | if (isDev) { | 38 | if (isDev) { |
| 27 | const url = process.env.VITE_DEV_SERVER_URL ?? "http://localhost:5173"; | 39 | const url = process.env.VITE_DEV_SERVER_URL ?? "http://localhost:5173"; |
| 28 | mainWindow.loadURL(url).finally(() => { | 40 | mainWindow.loadURL(url).finally(() => { |
diff --git a/src/main/ipc/handlers.ts b/src/main/ipc/handlers.ts index bc7d78d..e0863f3 100644 --- a/src/main/ipc/handlers.ts +++ b/src/main/ipc/handlers.ts | |||
| @@ -3,7 +3,7 @@ import * as projects from "../db/projects"; | |||
| 3 | import * as sessions from "../db/sessions"; | 3 | import * as sessions from "../db/sessions"; |
| 4 | import * as claude from "../claude"; | 4 | import * as claude from "../claude"; |
| 5 | import * as settingsDb from "../db/settings"; | 5 | import * as settingsDb from "../db/settings"; |
| 6 | import { createSessionBranch, ensureGitIgnore } from "../git"; | 6 | import { createSessionBranch, ensureGitIgnore, ensureGitRepo, getCurrentBranch } from "../git"; |
| 7 | import type { UserPermissionMode } from "../claude/phases"; | 7 | import type { UserPermissionMode } from "../claude/phases"; |
| 8 | import { getDefaultSystemPromptTemplate } from "../claude/phases"; | 8 | import { getDefaultSystemPromptTemplate } from "../claude/phases"; |
| 9 | 9 | ||
| @@ -113,10 +113,27 @@ export function registerIpcHandlers(mainWindow: BrowserWindow): void { | |||
| 113 | if (nextPhase === "implement") { | 113 | if (nextPhase === "implement") { |
| 114 | const project = projects.getProject(session.project_id); | 114 | const project = projects.getProject(session.project_id); |
| 115 | if (project) { | 115 | if (project) { |
| 116 | const branchName = createSessionBranch(project.path, session.name, session.id); | 116 | const branchingSetting = settingsDb.getSetting("git.branchingEnabled"); |
| 117 | if (branchName) { | 117 | const branchingEnabled = branchingSetting === "true"; // opt-in; default = off |
| 118 | sessions.updateSession(sessionId, { git_branch: branchName }); | 118 | |
| 119 | git_branch = branchName; | 119 | // Always ensure repo + gitignore so commits work regardless of mode |
| 120 | try { ensureGitIgnore(project.path); } catch { /* non-fatal */ } | ||
| 121 | try { ensureGitRepo(project.path); } catch { /* non-fatal */ } | ||
| 122 | |||
| 123 | if (branchingEnabled) { | ||
| 124 | // createSessionBranch internally calls ensureGitIgnore/ensureGitRepo again | ||
| 125 | // (belt-and-suspenders), then checks out a new claude-flow/<slug>-<id> branch | ||
| 126 | const branchName = createSessionBranch(project.path, session.name, session.id); | ||
| 127 | if (branchName) { | ||
| 128 | sessions.updateSession(sessionId, { git_branch: branchName }); | ||
| 129 | git_branch = branchName; | ||
| 130 | } | ||
| 131 | } else { | ||
| 132 | // No new branch — commit to whatever branch is currently checked out. | ||
| 133 | // Store the branch name so autoCommitTurn's boolean guard passes. | ||
| 134 | const currentBranch = getCurrentBranch(project.path) ?? "main"; | ||
| 135 | sessions.updateSession(sessionId, { git_branch: currentBranch }); | ||
| 136 | git_branch = currentBranch; | ||
| 120 | } | 137 | } |
| 121 | } | 138 | } |
| 122 | } | 139 | } |
diff --git a/src/main/preload.ts b/src/main/preload.ts index 52e947b..44467db 100644 --- a/src/main/preload.ts +++ b/src/main/preload.ts | |||
| @@ -62,6 +62,10 @@ export interface ClaudeFlowAPI { | |||
| 62 | 62 | ||
| 63 | // Dialogs | 63 | // Dialogs |
| 64 | selectDirectory: () => Promise<string | null>; | 64 | selectDirectory: () => Promise<string | null>; |
| 65 | |||
| 66 | // Window | ||
| 67 | toggleMaximize: () => Promise<void>; | ||
| 68 | onWindowMaximized: (cb: (isMaximized: boolean) => void) => () => void; | ||
| 65 | } | 69 | } |
| 66 | 70 | ||
| 67 | const api: ClaudeFlowAPI = { | 71 | const api: ClaudeFlowAPI = { |
| @@ -127,6 +131,14 @@ const api: ClaudeFlowAPI = { | |||
| 127 | const result = await ipcRenderer.invoke("dialog:selectDirectory"); | 131 | const result = await ipcRenderer.invoke("dialog:selectDirectory"); |
| 128 | return result; | 132 | return result; |
| 129 | }, | 133 | }, |
| 134 | |||
| 135 | // Window | ||
| 136 | toggleMaximize: () => ipcRenderer.invoke("window:toggleMaximize"), | ||
| 137 | onWindowMaximized: (cb) => { | ||
| 138 | const handler = (_: IpcRendererEvent, val: boolean) => cb(val); | ||
| 139 | ipcRenderer.on("window:maximized", handler); | ||
| 140 | return () => ipcRenderer.removeListener("window:maximized", handler); | ||
| 141 | }, | ||
| 130 | }; | 142 | }; |
| 131 | 143 | ||
| 132 | contextBridge.exposeInMainWorld("api", api); | 144 | contextBridge.exposeInMainWorld("api", api); |
