import { useCallback, useEffect, useMemo, useState } from "react"; import { Activity, Bot, ChevronRight, Clock, FileCode, FileText, Folder, GitBranch, Grid3X3, LayoutGrid, Lightbulb, Loader2, Mail, MessageSquare, MoreHorizontal, Play, Settings, Sparkles, Target, Terminal, Workflow, Map, Zap, } from "lucide-react"; import { fetchScripts } from "../api"; import { useViewportMode } from "./Header"; export interface MobileNavBarProps { /** Current task view mode */ view: "board" | "list" | "agents" | "missions" | "chat" | "documents" | "roadmaps" | "skills" | "mailbox" | "insights"; /** Change task view handler */ onChangeView: (view: "board" | "list" | "agents" | "missions" | "chat" | "documents" | "roadmaps" | "skills" | "mailbox" | "insights") => void; /** Whether the ExecutorStatusBar footer is visible */ footerVisible: boolean; /** Whether any full-screen modal is currently open (hides the tab bar) */ modalOpen?: boolean; // Navigation handlers onOpenSettings?: () => void; onOpenActivityLog?: () => void; onOpenMailbox?: () => void; mailboxUnreadCount?: number; onOpenGitManager?: () => void; onOpenWorkflowSteps?: () => void; onOpenSchedules?: () => void; onOpenScripts?: () => void; onToggleTerminal?: () => void; onOpenFiles?: () => void; onOpenGitHubImport?: () => void; onOpenPlanning?: () => void; onResumePlanning?: () => void; activePlanningSessionCount?: number; onOpenUsage?: () => void; onRunScript?: (name: string, command: string) => void; onOpenQuickChat?: () => void; projectId?: string; onViewAllProjects?: () => void; } function GitHubLogo({ size = 20 }: { size?: number }) { return ( ); } function formatCount(count: number): string { return count > 99 ? "99+" : String(count); } export function MobileNavBar({ view, onChangeView, footerVisible, modalOpen = false, onOpenSettings, onOpenActivityLog, onOpenMailbox, mailboxUnreadCount = 0, onOpenGitManager, onOpenWorkflowSteps, onOpenSchedules, onOpenScripts, onToggleTerminal, onOpenFiles, onOpenGitHubImport, onOpenPlanning, onResumePlanning, activePlanningSessionCount = 0, onOpenUsage, onRunScript, onOpenQuickChat, projectId, onViewAllProjects, }: MobileNavBarProps) { const mode = useViewportMode(); const [isMoreOpen, setIsMoreOpen] = useState(false); const [isScriptsSubmenuOpen, setIsScriptsSubmenuOpen] = useState(false); const [scripts, setScripts] = useState>({}); const [scriptsLoading, setScriptsLoading] = useState(false); const scriptEntries = useMemo( () => Object.entries(scripts).sort(([a], [b]) => a.localeCompare(b)), [scripts], ); // Fetch scripts when the submenu opens useEffect(() => { if (!isScriptsSubmenuOpen) return; let cancelled = false; setScriptsLoading(true); fetchScripts(projectId) .then((data) => { if (!cancelled) setScripts(data); }) .catch(() => { if (!cancelled) setScripts({}); }) .finally(() => { if (!cancelled) setScriptsLoading(false); }); return () => { cancelled = true; }; }, [isScriptsSubmenuOpen, projectId]); const closeMore = useCallback(() => setIsMoreOpen(false), []); const handleMoreAction = useCallback( (callback?: () => void) => { closeMore(); callback?.(); }, [closeMore], ); useEffect(() => { if (!isMoreOpen) return; const onKeyDown = (event: KeyboardEvent) => { if (event.key === "Escape") { setIsMoreOpen(false); } }; document.addEventListener("keydown", onKeyDown); return () => document.removeEventListener("keydown", onKeyDown); }, [isMoreOpen]); if (mode !== "mobile" || modalOpen) { return null; } const planningHandler = activePlanningSessionCount > 0 && onResumePlanning ? onResumePlanning : onOpenPlanning; return ( <> { // If already on a tasks view, stay there; otherwise go to board if (view === "board" || view === "list") { onChangeView(view); } else { onChangeView("board"); } }} > Tasks onChangeView("agents")} > Agents onChangeView("missions")} > Missions onChangeView("chat")} > Chat onChangeView("mailbox")} > Mailbox {mailboxUnreadCount > 0 && ( {formatCount(mailboxUnreadCount)} )} onChangeView("skills")} > Skills onChangeView("roadmaps")} > Roadmaps setIsMoreOpen((prev) => !prev)} > More {isMoreOpen && ( <> Navigate handleMoreAction(onOpenMailbox)} > Mailbox {mailboxUnreadCount > 0 && ( {formatCount(mailboxUnreadCount)} )} handleMoreAction(onOpenActivityLog)} > Activity Log handleMoreAction(onOpenGitManager)} > Git Manager handleMoreAction(onToggleTerminal)} > Terminal setIsScriptsSubmenuOpen((prev) => !prev)} aria-expanded={isScriptsSubmenuOpen} aria-haspopup="menu" aria-label="Show scripts" > {isScriptsSubmenuOpen && ( {scriptsLoading ? ( Loading scripts… ) : scriptEntries.length > 0 ? ( <> {scriptEntries.map(([name, command]) => ( { if (onRunScript) onRunScript(name, command); closeMore(); setIsScriptsSubmenuOpen(false); }} > {name} ))} {onOpenScripts && ( { closeMore(); setIsScriptsSubmenuOpen(false); onOpenScripts(); }} > Manage Scripts… )} > ) : ( onOpenScripts && ( { closeMore(); setIsScriptsSubmenuOpen(false); onOpenScripts(); }} > No scripts — add one… ) )} )} handleMoreAction(onOpenFiles)} > Files handleMoreAction(planningHandler)} > Planning {activePlanningSessionCount > 0 && ( {formatCount(activePlanningSessionCount)} )} handleMoreAction(onOpenWorkflowSteps)} > Workflow Steps handleMoreAction(onOpenSchedules)} > Schedules handleMoreAction(onOpenGitHubImport)} > Import from GitHub handleMoreAction(onOpenUsage)} > Usage handleMoreAction(onViewAllProjects)} > Projects handleMoreAction(onOpenQuickChat)} > Chat handleMoreAction(() => onChangeView("documents"))} > Documents handleMoreAction(() => onChangeView("roadmaps"))} > Roadmaps handleMoreAction(() => onChangeView("insights"))} > Insights handleMoreAction(onOpenSettings)} > Settings > )} > ); }