diff options
Diffstat (limited to 'renderer/src/components')
| -rw-r--r-- | renderer/src/components/Header.tsx | 23 | ||||
| -rw-r--r-- | renderer/src/components/SettingsPage.tsx | 17 | ||||
| -rw-r--r-- | renderer/src/components/settings/GitSettings.tsx | 78 |
3 files changed, 113 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 @@ | |||
| 1 | import React, { useState } from "react"; | 1 | import React, { useState, useEffect } from "react"; |
| 2 | import type { Project, Session, Phase } from "../types"; | 2 | import type { Project, Session, Phase } from "../types"; |
| 3 | 3 | ||
| 4 | const api = window.api; | ||
| 5 | |||
| 4 | type Theme = "dark" | "light"; | 6 | type Theme = "dark" | "light"; |
| 5 | 7 | ||
| 6 | interface HeaderProps { | 8 | interface 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 | ⚙ | 251 | ⚙ |
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 @@ | |||
| 1 | import React, { useState } from "react"; | 1 | import React, { useState } from "react"; |
| 2 | import { SystemPromptsSettings } from "./settings/SystemPromptsSettings"; | 2 | import { SystemPromptsSettings } from "./settings/SystemPromptsSettings"; |
| 3 | import { GitSettings } from "./settings/GitSettings"; | ||
| 3 | 4 | ||
| 4 | type SettingsSection = "system-prompts"; | 5 | type SettingsSection = "system-prompts" | "git"; |
| 5 | 6 | ||
| 6 | interface SettingsPageProps { | 7 | interface 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 @@ | |||
| 1 | import React, { useState, useEffect } from "react"; | ||
| 2 | |||
| 3 | const api = window.api; | ||
| 4 | |||
| 5 | export 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/<name>-<id></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 | } | ||
