aboutsummaryrefslogtreecommitdiffstats
path: root/renderer/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 /renderer/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 'renderer/src')
-rw-r--r--renderer/src/components/Header.tsx23
-rw-r--r--renderer/src/components/SettingsPage.tsx17
-rw-r--r--renderer/src/components/settings/GitSettings.tsx78
-rw-r--r--renderer/src/styles/globals.css93
4 files changed, 206 insertions, 5 deletions
diff --git a/renderer/src/components/Header.tsx b/renderer/src/components/Header.tsx
index 3a530d3..1d954ec 100644
--- a/renderer/src/components/Header.tsx
+++ b/renderer/src/components/Header.tsx
@@ -1,6 +1,8 @@
1import React, { useState } from "react"; 1import React, { useState, useEffect } from "react";
2import type { Project, Session, Phase } from "../types"; 2import type { Project, Session, Phase } from "../types";
3 3
4const api = window.api;
5
4type Theme = "dark" | "light"; 6type Theme = "dark" | "light";
5 7
6interface HeaderProps { 8interface HeaderProps {
@@ -86,6 +88,16 @@ export function Header({
86 setIsRenamingSession(false); 88 setIsRenamingSession(false);
87 }; 89 };
88 90
91 // ── Maximize ─────────────────────────────────────────────────
92 const [isMaximized, setIsMaximized] = useState(false);
93
94 useEffect(() => {
95 // Returns the unsubscribe function; React cleanup calls it on unmount.
96 // On macOS, clicking the native green traffic light also fires this,
97 // keeping the glyph accurate when native controls are used.
98 return api.onWindowMaximized(setIsMaximized);
99 }, []);
100
89 // ── Branch copy ────────────────────────────────────────────── 101 // ── Branch copy ──────────────────────────────────────────────
90 const [copied, setCopied] = useState(false); 102 const [copied, setCopied] = useState(false);
91 103
@@ -225,6 +237,15 @@ export function Header({
225 {theme === "dark" ? "[light]" : "[dark]"} 237 {theme === "dark" ? "[light]" : "[dark]"}
226 </button> 238 </button>
227 239
240 {/* ── Maximize toggle ── */}
241 <button
242 className="maximize-btn"
243 onClick={() => api.toggleMaximize()}
244 title={isMaximized ? "Restore window" : "Maximize window"}
245 >
246 {isMaximized ? '⊡' : '□'}
247 </button>
248
228 {/* ── Settings button ── */} 249 {/* ── Settings button ── */}
229 <button className="settings-btn" onClick={onOpenSettings} title="Settings"> 250 <button className="settings-btn" onClick={onOpenSettings} title="Settings">
230 &#9881; 251 &#9881;
diff --git a/renderer/src/components/SettingsPage.tsx b/renderer/src/components/SettingsPage.tsx
index 5267665..9ebde44 100644
--- a/renderer/src/components/SettingsPage.tsx
+++ b/renderer/src/components/SettingsPage.tsx
@@ -1,7 +1,8 @@
1import React, { useState } from "react"; 1import React, { useState } from "react";
2import { SystemPromptsSettings } from "./settings/SystemPromptsSettings"; 2import { SystemPromptsSettings } from "./settings/SystemPromptsSettings";
3import { GitSettings } from "./settings/GitSettings";
3 4
4type SettingsSection = "system-prompts"; 5type SettingsSection = "system-prompts" | "git";
5 6
6interface SettingsPageProps { 7interface SettingsPageProps {
7 onClose: () => void; 8 onClose: () => void;
@@ -16,14 +17,14 @@ export function SettingsPage({ onClose }: SettingsPageProps) {
16 {/* Header — matches the main app header height/style */} 17 {/* Header — matches the main app header height/style */}
17 <div className="settings-header"> 18 <div className="settings-header">
18 <div className="settings-header-left"> 19 <div className="settings-header-left">
19 <span className="settings-title">\u2699 Settings</span> 20 <span className="settings-title">{'⚙'} Settings</span>
20 </div> 21 </div>
21 <button 22 <button
22 className="settings-close" 23 className="settings-close"
23 onClick={onClose} 24 onClick={onClose}
24 title="Close settings" 25 title="Close settings"
25 > 26 >
26 \u00d7 27 {'×'}
27 </button> 28 </button>
28 </div> 29 </div>
29 30
@@ -38,12 +39,20 @@ export function SettingsPage({ onClose }: SettingsPageProps) {
38 > 39 >
39 System Prompts 40 System Prompts
40 </button> 41 </button>
41 {/* Future sections added here */} 42 <button
43 className={`settings-nav-item${
44 activeSection === "git" ? " active" : ""
45 }`}
46 onClick={() => setActiveSection("git")}
47 >
48 Git
49 </button>
42 </nav> 50 </nav>
43 51
44 {/* Content */} 52 {/* Content */}
45 <div className="settings-content"> 53 <div className="settings-content">
46 {activeSection === "system-prompts" && <SystemPromptsSettings />} 54 {activeSection === "system-prompts" && <SystemPromptsSettings />}
55 {activeSection === "git" && <GitSettings />}
47 </div> 56 </div>
48 </div> 57 </div>
49 </div> 58 </div>
diff --git a/renderer/src/components/settings/GitSettings.tsx b/renderer/src/components/settings/GitSettings.tsx
new file mode 100644
index 0000000..0ec9875
--- /dev/null
+++ b/renderer/src/components/settings/GitSettings.tsx
@@ -0,0 +1,78 @@
1import React, { useState, useEffect } from "react";
2
3const api = window.api;
4
5export function GitSettings() {
6 // null = setting not yet loaded; true/false = loaded value
7 const [branchingEnabled, setBranchingEnabled] = useState<boolean | null>(null);
8 const [saveStatus, setSaveStatus] = useState<"idle" | "saved">("idle");
9
10 useEffect(() => {
11 api.getSettings(["git.branchingEnabled"]).then((settings) => {
12 // Key absent or any value other than "true" → false (opt-in default)
13 setBranchingEnabled(settings["git.branchingEnabled"] === "true");
14 });
15 }, []);
16
17 const handleToggle = async (enabled: boolean) => {
18 setBranchingEnabled(enabled);
19 if (enabled) {
20 await api.setSetting("git.branchingEnabled", "true");
21 } else {
22 await api.deleteSetting("git.branchingEnabled");
23 }
24 setSaveStatus("saved");
25 setTimeout(() => setSaveStatus("idle"), 1500);
26 };
27
28 if (branchingEnabled === null) {
29 return (
30 <div style={{ color: "var(--text-secondary)", fontSize: 12 }}>
31 Loading...
32 </div>
33 );
34 }
35
36 return (
37 <div>
38 <div className="settings-section-title">Git</div>
39 <div className="settings-section-desc">
40 Configure how Claude Flow interacts with your project's git repository
41 during the implement phase.
42 </div>
43
44 <div className="settings-toggle-row">
45 <label className="settings-toggle-label" htmlFor="git-branching-toggle">
46 <span className="settings-toggle-text">
47 <strong>Create a branch per session</strong>
48 <span className="settings-toggle-hint">
49 When enabled, Claude Flow checks out a new{" "}
50 <code>claude-flow/&lt;name&gt;-&lt;id&gt;</code> branch when
51 entering the implement phase. When disabled, commits land on
52 your current branch.
53 </span>
54 </span>
55 <span
56 className={`settings-toggle${branchingEnabled ? " on" : ""}`}
57 role="switch"
58 aria-checked={branchingEnabled}
59 id="git-branching-toggle"
60 tabIndex={0}
61 onClick={() => handleToggle(!branchingEnabled)}
62 onKeyDown={(e) => {
63 if (e.key === " " || e.key === "Enter") {
64 e.preventDefault();
65 handleToggle(!branchingEnabled);
66 }
67 }}
68 />
69 </label>
70 {saveStatus === "saved" && (
71 <span className="settings-custom-badge" style={{ color: "var(--accent)" }}>
72 saved ✓
73 </span>
74 )}
75 </div>
76 </div>
77 );
78}
diff --git a/renderer/src/styles/globals.css b/renderer/src/styles/globals.css
index 61a37c0..8e43f3a 100644
--- a/renderer/src/styles/globals.css
+++ b/renderer/src/styles/globals.css
@@ -905,3 +905,96 @@ html[data-theme="light"] .settings-textarea:focus {
905 background: var(--bg-tertiary); 905 background: var(--bg-tertiary);
906 color: var(--text-primary); 906 color: var(--text-primary);
907} 907}
908
909/* ── Settings Toggle Row ─────────────────────────────────────── */
910.settings-toggle-row {
911 display: flex;
912 align-items: center;
913 gap: 12px;
914 margin-top: 16px;
915}
916
917.settings-toggle-label {
918 display: flex;
919 align-items: flex-start;
920 gap: 14px;
921 cursor: pointer;
922 flex: 1;
923}
924
925.settings-toggle-text {
926 display: flex;
927 flex-direction: column;
928 gap: 4px;
929 font-size: 12px;
930 color: var(--text-primary);
931 line-height: 1.4;
932}
933
934.settings-toggle-hint {
935 color: var(--text-secondary);
936 font-size: 11px;
937}
938
939.settings-toggle-hint code {
940 font-family: monospace;
941 background: var(--bg-tertiary);
942 padding: 1px 3px;
943 border-radius: 2px;
944}
945
946/* The pill toggle switch */
947.settings-toggle {
948 flex-shrink: 0;
949 width: 36px;
950 height: 20px;
951 border-radius: 10px;
952 background: var(--border);
953 position: relative;
954 cursor: pointer;
955 transition: background 0.2s;
956 outline-offset: 2px;
957}
958
959.settings-toggle:focus-visible {
960 outline: 2px solid var(--accent);
961}
962
963.settings-toggle::after {
964 content: "";
965 position: absolute;
966 top: 3px;
967 left: 3px;
968 width: 14px;
969 height: 14px;
970 border-radius: 50%;
971 background: var(--text-secondary);
972 transition: transform 0.2s, background 0.2s;
973}
974
975.settings-toggle.on {
976 background: var(--accent);
977}
978
979.settings-toggle.on::after {
980 transform: translateX(16px);
981 background: #fff;
982}
983
984/* ── Header Maximize Button ──────────────────────────────────── */
985.maximize-btn {
986 padding: 5px 8px;
987 background: transparent;
988 border: 1px solid var(--border);
989 border-radius: 2px;
990 color: var(--text-secondary);
991 cursor: pointer;
992 font-size: 13px;
993 font-family: inherit;
994 transition: background 0.15s, color 0.15s;
995}
996
997.maximize-btn:hover {
998 background: var(--bg-tertiary);
999 color: var(--text-primary);
1000}