import React, { useState, useEffect } from "react"; import type { Project, Session, Phase } from "../types"; const api = window.api; type Theme = "dark" | "light"; interface HeaderProps { projects: Project[]; sessions: Session[]; selectedProject: Project | null; selectedSession: Session | null; onSelectProject: (project: Project | null) => void; onSelectSession: (session: Session | null) => void; onCreateProject: () => void; onCreateSession: () => void; onDeleteProject?: (id: string) => void; onDeleteSession?: (id: string) => void; onRenameSession?: (id: string, name: string) => void; theme: Theme; onToggleTheme: () => void; gitBranch: string | null; onOpenSettings: () => void; } const phaseLabels: Record = { research: "Research", plan: "Plan", implement: "Implement", }; const phases: Phase[] = ["research", "plan", "implement"]; export function Header({ projects, sessions, selectedProject, selectedSession, onSelectProject, onSelectSession, onCreateProject, onCreateSession, onDeleteProject, onDeleteSession, onRenameSession, theme, onToggleTheme, gitBranch, onOpenSettings, }: HeaderProps) { const handleDeleteProject = () => { if (!selectedProject || !onDeleteProject) return; if (confirm(`Delete project "${selectedProject.name}"? This cannot be undone.`)) { onDeleteProject(selectedProject.id); } }; const handleDeleteSession = () => { if (!selectedSession || !onDeleteSession) return; if (confirm(`Delete session "${selectedSession.name}"? This cannot be undone.`)) { onDeleteSession(selectedSession.id); } }; const [isRenamingSession, setIsRenamingSession] = useState(false); const [renameValue, setRenameValue] = useState(""); // Guard against double-commit (onKeyDown Enter → unmount → onBlur) const renameCommitted = React.useRef(false); const startRename = () => { if (!selectedSession) return; renameCommitted.current = false; setRenameValue(selectedSession.name); setIsRenamingSession(true); }; const commitRename = () => { if (renameCommitted.current) return; renameCommitted.current = true; if (selectedSession && onRenameSession && renameValue.trim()) { onRenameSession(selectedSession.id, renameValue.trim()); } setIsRenamingSession(false); }; const cancelRename = () => { renameCommitted.current = true; // prevent blur from committing after cancel setIsRenamingSession(false); }; // ── Maximize ───────────────────────────────────────────────── const [isMaximized, setIsMaximized] = useState(false); useEffect(() => { // Returns the unsubscribe function; React cleanup calls it on unmount. // On macOS, clicking the native green traffic light also fires this, // keeping the glyph accurate when native controls are used. return api.onWindowMaximized(setIsMaximized); }, []); // ── Branch copy ────────────────────────────────────────────── const [copied, setCopied] = useState(false); const handleCopyBranch = () => { if (!gitBranch) return; navigator.clipboard.writeText(gitBranch); setCopied(true); setTimeout(() => setCopied(false), 1500); }; return (
{/* ── Wordmark ── */} Claude Flow {selectedProject && onDeleteProject && ( )} {selectedProject && ( <> {isRenamingSession ? ( setRenameValue(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") commitRename(); if (e.key === "Escape") cancelRename(); }} onBlur={commitRename} className="session-rename-input" /> ) : ( )} {selectedSession && onRenameSession && !isRenamingSession && selectedSession.phase !== "implement" && ( )} {selectedSession && onDeleteSession && ( )} )}
{selectedSession && (
{phases.map((phase) => { const phaseIndex = phases.indexOf(phase); const currentIndex = phases.indexOf(selectedSession.phase); const isComplete = phaseIndex < currentIndex; const isActive = phase === selectedSession.phase; return ( {phaseLabels[phase]} ); })}
)} {/* ── Branch badge ── */} {selectedSession && gitBranch && ( )} {/* ── Theme toggle ── */} {/* ── Maximize toggle ── */} {/* ── Settings button ── */}
); }