import { useState, useEffect, useRef, useCallback } from "react"; import { Settings, Pause, Play, Square, LayoutGrid, List, Terminal, Lightbulb, Search, X, Activity, MoreHorizontal, Clock, Folder, History, GitBranch } from "lucide-react"; // GitHub logo icon (Octocat mark) - uses currentColor for theme compatibility function GitHubLogo({ size = 16 }: { size?: number }) { return ( ); } interface HeaderProps { onOpenSettings?: () => void; onOpenGitHubImport?: () => void; onOpenPlanning?: () => void; onOpenUsage?: () => void; onOpenActivityLog?: () => void; onOpenSchedules?: () => void; onOpenGitManager?: () => void; onToggleTerminal?: () => void; /** Opens the top-level workspace-aware file browser modal. */ onOpenFiles?: () => void; filesOpen?: boolean; globalPaused?: boolean; enginePaused?: boolean; onToggleGlobalPause?: () => void; onToggleEnginePause?: () => void; view?: "board" | "list"; onChangeView?: (view: "board" | "list") => void; searchQuery?: string; onSearchChange?: (query: string) => void; } function useIsMobile() { const [isMobile, setIsMobile] = useState(() => { if (typeof window === "undefined") return false; return window.matchMedia("(max-width: 768px)").matches; }); useEffect(() => { if (typeof window === "undefined") return; const mediaQuery = window.matchMedia("(max-width: 768px)"); const handleChange = (e: MediaQueryListEvent) => setIsMobile(e.matches); mediaQuery.addEventListener("change", handleChange); return () => mediaQuery.removeEventListener("change", handleChange); }, []); return isMobile; } export function Header({ onOpenSettings, onOpenGitHubImport, onOpenPlanning, onOpenUsage, onOpenActivityLog, onOpenSchedules, onOpenGitManager, onToggleTerminal, onOpenFiles, filesOpen, globalPaused, enginePaused, onToggleGlobalPause, onToggleEnginePause, view = "board", onChangeView, searchQuery = "", onSearchChange, }: HeaderProps) { const isMobile = useIsMobile(); const [isMobileSearchOpen, setIsMobileSearchOpen] = useState(false); const [isOverflowMenuOpen, setIsOverflowMenuOpen] = useState(false); const overflowButtonRef = useRef(null); const overflowMenuRef = useRef(null); const mobileSearchRef = useRef(null); const mobileSearchInputRef = useRef(null); // Keep mobile search open if there's an active search query const shouldShowMobileSearch = isMobileSearchOpen || searchQuery.length > 0; // Close overflow menu on outside click useEffect(() => { if (!isOverflowMenuOpen) return; const handleClickOutside = (e: MouseEvent) => { if ( overflowMenuRef.current && !overflowMenuRef.current.contains(e.target as Node) && overflowButtonRef.current && !overflowButtonRef.current.contains(e.target as Node) ) { setIsOverflowMenuOpen(false); } }; document.addEventListener("mousedown", handleClickOutside); return () => document.removeEventListener("mousedown", handleClickOutside); }, [isOverflowMenuOpen]); // Close menus on Escape key useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key === "Escape") { setIsOverflowMenuOpen(false); setIsMobileSearchOpen(false); } }; document.addEventListener("keydown", handleKeyDown); return () => document.removeEventListener("keydown", handleKeyDown); }, []); // Focus input when mobile search opens useEffect(() => { if (isMobileSearchOpen && mobileSearchInputRef.current) { setTimeout(() => mobileSearchInputRef.current?.focus(), 0); } }, [isMobileSearchOpen]); const handleMobileSearchToggle = useCallback(() => { setIsMobileSearchOpen((prev) => !prev); }, []); const handleOverflowToggle = useCallback(() => { setIsOverflowMenuOpen((prev) => !prev); }, []); const handleOverflowAction = useCallback((callback?: () => void) => { if (callback) callback(); setIsOverflowMenuOpen(false); }, []); const handleMobileSearchClose = useCallback(() => { setIsMobileSearchOpen(false); if (onSearchChange) onSearchChange(""); }, [onSearchChange]); return (
Fusion logo

Fusion

tasks
{/* Desktop Search - only show in board view */} {onSearchChange && view === "board" && !isMobile && (
onSearchChange(e.target.value)} className="header-search-input" /> {searchQuery && ( )}
)} {/* Mobile Search Trigger - only show in board view on mobile */} {onSearchChange && view === "board" && isMobile && ( <> {!shouldShowMobileSearch ? ( ) : (
onSearchChange(e.target.value)} className="header-search-input" />
)} )} {/* View Toggle - always inline, even on mobile */} {onChangeView && (
)} {/* Usage button - inline on all screens when onOpenUsage provided */} {onOpenUsage && ( )} {/* Activity Log button */} {onOpenActivityLog && ( )} {/* Desktop actions */} {!isMobile && ( )} {!isMobile && ( )} {/* Schedules button - desktop only (moved to overflow on mobile) */} {!isMobile && ( )} {/* Terminal button - desktop only (moved to overflow on mobile) */} {!isMobile && ( )} {/* Files button - desktop only */} {!isMobile && onOpenFiles && ( )} {/* Git Manager button - desktop only */} {!isMobile && onOpenGitManager && ( )} {/* Pause button (soft pause) - always inline */} {/* Stop button (hard stop) - always inline */} {/* Mobile overflow menu trigger */} {isMobile && ( )} {/* Settings - always inline on desktop */} {!isMobile && ( )} {/* Mobile overflow menu */} {isMobile && isOverflowMenuOpen && (
{/* Terminal - in overflow on mobile */} {/* Files - in overflow on mobile */} {onOpenFiles && ( )} {/* Git Manager - in overflow on mobile */} {onOpenGitManager && ( )}
)}
); }