aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorbndw <ben@bdw.to>2026-02-28 21:35:39 -0800
committerbndw <ben@bdw.to>2026-02-28 21:35:39 -0800
commit0484d97dfbc3b8a2e7878d3ab35a9895decdf467 (patch)
treecdc6fffe8b169c83058e8d64ca42723f6ccb6dcf /src
parent04c63d4ef601876186e5d7fab980d76575c494ec (diff)
feat: **1 — `git.ts`:** Add exported `getCurrentBranch` helpe… (+8 more)
- ✅ **1 — `git.ts`:** Add exported `getCurrentBranch` helper after `ensureGitRepo` - ✅ **2a — `ipc/handlers.ts`:** Update git import to include `ensureGitRepo` and `getCurrentBranch` - ✅ **2b — `ipc/handlers.ts`:** Replace `workflow:advance` implement-phase block with branching-toggle logic - ✅ **3 — `GitSettings.tsx`:** Create new settings component with pill toggle - ✅ **4 — `SettingsPage.tsx`:** Add `"git"` section type, import, nav item, content render; fix both unicode glyphs - ✅ **5 — `globals.css`:** Append toggle-row + pill toggle + maximize-btn CSS - ✅ **6 — `index.ts`:** Add `ipcMain` to import; add `window:toggleMaximize` handler + maximize/unmaximize events inside `createWindow()` - ✅ **7 — `preload.ts`:** Add `toggleMaximize` + `onWindowMaximized` to interface and `api` object - ✅ **8 — `Header.tsx`:** Add `isMaximized` state + effect + maximize button in JSX
Diffstat (limited to 'src')
-rw-r--r--src/main/git.ts23
-rw-r--r--src/main/index.ts14
-rw-r--r--src/main/ipc/handlers.ts27
-rw-r--r--src/main/preload.ts12
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 */
68export 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 @@
1import { app, BrowserWindow, Menu } from "electron"; 1import { app, BrowserWindow, Menu, ipcMain } from "electron";
2import path from "node:path"; 2import path from "node:path";
3import { getDb, closeDb } from "./db"; 3import { getDb, closeDb } from "./db";
4import { registerIpcHandlers } from "./ipc/handlers"; 4import { 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";
3import * as sessions from "../db/sessions"; 3import * as sessions from "../db/sessions";
4import * as claude from "../claude"; 4import * as claude from "../claude";
5import * as settingsDb from "../db/settings"; 5import * as settingsDb from "../db/settings";
6import { createSessionBranch, ensureGitIgnore } from "../git"; 6import { createSessionBranch, ensureGitIgnore, ensureGitRepo, getCurrentBranch } from "../git";
7import type { UserPermissionMode } from "../claude/phases"; 7import type { UserPermissionMode } from "../claude/phases";
8import { getDefaultSystemPromptTemplate } from "../claude/phases"; 8import { 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
67const api: ClaudeFlowAPI = { 71const 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
132contextBridge.exposeInMainWorld("api", api); 144contextBridge.exposeInMainWorld("api", api);