diff options
| author | bndw <ben@bdw.to> | 2026-02-28 20:22:12 -0800 |
|---|---|---|
| committer | bndw <ben@bdw.to> | 2026-02-28 20:22:12 -0800 |
| commit | dc4156fec54a8efdab84834fe2f5bc90120e32c1 (patch) | |
| tree | 8eb3b979fcce0d0e6ddec1ab66232fb1229c3688 /renderer/src | |
| parent | 9d192d16b7a4026b35ad2bcaff9edb9f2670de2b (diff) | |
feat: Complete 7 tasks
- ✅ Add `.branch-badge` CSS block to `globals.css` after `.phase-step.complete` rule
- ✅ Extend `HeaderProps` interface with `gitBranch: string | null`
- ✅ Add `gitBranch` to `Header` function destructure
- ✅ Add `copied` state and `handleCopyBranch` function inside `Header`
- ✅ Update session `<option>` text to append branch name / "git unavailable"
- ✅ Add branch badge button to `header-right` between phase indicator and theme toggle
- ✅ Pass `gitBranch={selectedSession?.git_branch ?? null}` to `<Header>` in `App.tsx`
Diffstat (limited to 'renderer/src')
| -rw-r--r-- | renderer/src/App.tsx | 1 | ||||
| -rw-r--r-- | renderer/src/components/Header.tsx | 37 | ||||
| -rw-r--r-- | renderer/src/styles/globals.css | 33 |
3 files changed, 71 insertions, 0 deletions
diff --git a/renderer/src/App.tsx b/renderer/src/App.tsx index 19f6284..ecbb5b2 100644 --- a/renderer/src/App.tsx +++ b/renderer/src/App.tsx | |||
| @@ -360,6 +360,7 @@ export function App() { | |||
| 360 | onRenameSession={handleRenameSession} | 360 | onRenameSession={handleRenameSession} |
| 361 | theme={theme} | 361 | theme={theme} |
| 362 | onToggleTheme={handleToggleTheme} | 362 | onToggleTheme={handleToggleTheme} |
| 363 | gitBranch={selectedSession?.git_branch ?? null} | ||
| 363 | /> | 364 | /> |
| 364 | 365 | ||
| 365 | <div className="main-content"> | 366 | <div className="main-content"> |
diff --git a/renderer/src/components/Header.tsx b/renderer/src/components/Header.tsx index a435519..e56264f 100644 --- a/renderer/src/components/Header.tsx +++ b/renderer/src/components/Header.tsx | |||
| @@ -17,6 +17,7 @@ interface HeaderProps { | |||
| 17 | onRenameSession?: (id: string, name: string) => void; | 17 | onRenameSession?: (id: string, name: string) => void; |
| 18 | theme: Theme; | 18 | theme: Theme; |
| 19 | onToggleTheme: () => void; | 19 | onToggleTheme: () => void; |
| 20 | gitBranch: string | null; | ||
| 20 | } | 21 | } |
| 21 | 22 | ||
| 22 | const phaseLabels: Record<Phase, string> = { | 23 | const phaseLabels: Record<Phase, string> = { |
| @@ -41,6 +42,7 @@ export function Header({ | |||
| 41 | onRenameSession, | 42 | onRenameSession, |
| 42 | theme, | 43 | theme, |
| 43 | onToggleTheme, | 44 | onToggleTheme, |
| 45 | gitBranch, | ||
| 44 | }: HeaderProps) { | 46 | }: HeaderProps) { |
| 45 | const handleDeleteProject = () => { | 47 | const handleDeleteProject = () => { |
| 46 | if (!selectedProject || !onDeleteProject) return; | 48 | if (!selectedProject || !onDeleteProject) return; |
| @@ -82,6 +84,16 @@ export function Header({ | |||
| 82 | setIsRenamingSession(false); | 84 | setIsRenamingSession(false); |
| 83 | }; | 85 | }; |
| 84 | 86 | ||
| 87 | // ── Branch copy ────────────────────────────────────────────── | ||
| 88 | const [copied, setCopied] = useState(false); | ||
| 89 | |||
| 90 | const handleCopyBranch = () => { | ||
| 91 | if (!gitBranch) return; | ||
| 92 | navigator.clipboard.writeText(gitBranch); | ||
| 93 | setCopied(true); | ||
| 94 | setTimeout(() => setCopied(false), 1500); | ||
| 95 | }; | ||
| 96 | |||
| 85 | return ( | 97 | return ( |
| 86 | <header className="header"> | 98 | <header className="header"> |
| 87 | <div className="header-left"> | 99 | <div className="header-left"> |
| @@ -140,6 +152,7 @@ export function Header({ | |||
| 140 | {sessions.map((s) => ( | 152 | {sessions.map((s) => ( |
| 141 | <option key={s.id} value={s.id}> | 153 | <option key={s.id} value={s.id}> |
| 142 | {s.name} | 154 | {s.name} |
| 155 | {s.git_branch ? ` · ${s.git_branch}` : " · git unavailable"} | ||
| 143 | </option> | 156 | </option> |
| 144 | ))} | 157 | ))} |
| 145 | </select> | 158 | </select> |
| @@ -190,6 +203,30 @@ export function Header({ | |||
| 190 | </div> | 203 | </div> |
| 191 | )} | 204 | )} |
| 192 | 205 | ||
| 206 | {/* ── Branch badge ── */} | ||
| 207 | {selectedSession && ( | ||
| 208 | <button | ||
| 209 | className={[ | ||
| 210 | "branch-badge", | ||
| 211 | gitBranch ? "" : "branch-unavailable", | ||
| 212 | copied ? "branch-copied" : "", | ||
| 213 | ] | ||
| 214 | .filter(Boolean) | ||
| 215 | .join(" ")} | ||
| 216 | onClick={handleCopyBranch} | ||
| 217 | disabled={!gitBranch} | ||
| 218 | title={ | ||
| 219 | gitBranch | ||
| 220 | ? copied | ||
| 221 | ? "Copied!" | ||
| 222 | : `Click to copy: ${gitBranch}` | ||
| 223 | : "Git unavailable for this session" | ||
| 224 | } | ||
| 225 | > | ||
| 226 | ⎇ {gitBranch ?? "git unavailable"} | ||
| 227 | </button> | ||
| 228 | )} | ||
| 229 | |||
| 193 | {/* ── Theme toggle ── */} | 230 | {/* ── Theme toggle ── */} |
| 194 | <button className="theme-toggle" onClick={onToggleTheme}> | 231 | <button className="theme-toggle" onClick={onToggleTheme}> |
| 195 | {theme === "dark" ? "[light]" : "[dark]"} | 232 | {theme === "dark" ? "[light]" : "[dark]"} |
diff --git a/renderer/src/styles/globals.css b/renderer/src/styles/globals.css index f141538..9d37742 100644 --- a/renderer/src/styles/globals.css +++ b/renderer/src/styles/globals.css | |||
| @@ -179,6 +179,39 @@ html[data-theme="light"] .session-rename-input { | |||
| 179 | color: white; | 179 | color: white; |
| 180 | } | 180 | } |
| 181 | 181 | ||
| 182 | /* ── Branch Badge ────────────────────────────────────────────── */ | ||
| 183 | .branch-badge { | ||
| 184 | padding: 3px 10px; | ||
| 185 | font-size: 11px; | ||
| 186 | letter-spacing: 0.04em; | ||
| 187 | border-radius: 2px; | ||
| 188 | background: var(--bg-tertiary); | ||
| 189 | border: 1px solid var(--border); | ||
| 190 | color: var(--text-secondary); | ||
| 191 | cursor: pointer; | ||
| 192 | font-family: inherit; | ||
| 193 | white-space: nowrap; | ||
| 194 | transition: background 0.15s, color 0.15s, border-color 0.15s; | ||
| 195 | } | ||
| 196 | |||
| 197 | .branch-badge:hover:not(:disabled) { | ||
| 198 | background: var(--border); | ||
| 199 | color: var(--text-primary); | ||
| 200 | border-color: var(--accent); | ||
| 201 | } | ||
| 202 | |||
| 203 | .branch-badge.branch-copied { | ||
| 204 | background: var(--success); | ||
| 205 | border-color: var(--success); | ||
| 206 | color: white; | ||
| 207 | } | ||
| 208 | |||
| 209 | .branch-badge.branch-unavailable { | ||
| 210 | border-style: dashed; | ||
| 211 | opacity: 0.5; | ||
| 212 | cursor: default; | ||
| 213 | } | ||
| 214 | |||
| 182 | /* ── Main Content ─────────────────────────────────────────────── */ | 215 | /* ── Main Content ─────────────────────────────────────────────── */ |
| 183 | .main-content { | 216 | .main-content { |
| 184 | flex: 1; | 217 | flex: 1; |
