import { useCallback, useMemo, type ReactNode } from "react"; import { CircleNotch, TerminalWindow } from "@phosphor-icons/react"; import { LocaleContext, TranslationContext, type TranslationFn } from "./i18n"; import type { AppLocale } from "@shared/locale"; import { CompanyOnboarding } from "./components/CompanyOnboarding"; import TitleBar from "./components/TitleBar"; import ToastContainer from "./components/Toast"; import { SidebarView } from "./views/SidebarView"; import { SectionContentView } from "./views/SectionContentView"; import { ConsoleView } from "./views/ConsoleView"; import { CommandPaletteView } from "./views/CommandPaletteView"; import { useAppStore } from "./state/store"; import { useUIController } from "./hooks/useUIController"; import { useCompanyController } from "./hooks/useCompanyController"; import { useCommandPaletteHotkeys } from "./hooks/useCommandPaletteHotkeys"; import { AppActionsContext, type AppActions } from "./hooks/useAppActions"; import { unwrap } from "./lib/desktop"; function Shell({ locale, t, showCustomTitleBar, className, children, }: { locale: AppLocale; t: TranslationFn; showCustomTitleBar: boolean; className?: string; children: ReactNode; }) { return ( {showCustomTitleBar && } {children} ); } export function App() { const ui = useUIController(); const company = useCompanyController(); const error = useAppStore((s) => s.error); const setError = useAppStore((s) => s.setError); const setSelectedRunId = useAppStore((s) => s.setSelectedRunId); const setConsoleOpen = useAppStore((s) => s.setConsoleOpen); const navigateTo = useAppStore((s) => s.navigateTo); const showCustomTitleBar = window.agentCompany.platform !== "darwin"; const isMac = !showCustomTitleBar; const startRun = useCallback(async (taskId: string) => { if (!company.currentCompany) { setError("Select a company before starting a run."); return; } const result = await window.agentCompany.startTaskRun({ companyId: company.currentCompany.id, taskId }); if (!result.ok) { setError(result.error.message); return; } setSelectedRunId(result.data); setConsoleOpen(true); await company.refresh(); // eslint-disable-next-line react-hooks/exhaustive-deps -- Zustand setters and company.refresh are stable }, [company.currentCompany, company.refresh]); const cancelRun = useCallback(async (runId: string) => { try { const run = company.runs.find((entry) => entry.id === runId); if (!run) throw new Error("Selected run is no longer available."); unwrap(await window.agentCompany.cancelRun({ companyId: run.companyId, runId })); await company.refresh(); setError(null); } catch (err) { setError(err instanceof Error ? err.message : String(err)); } // eslint-disable-next-line react-hooks/exhaustive-deps -- setError is a stable Zustand setter }, [company.refresh, company.runs]); const { filteredNavItems, matchedAgents, matchedTasks, matchedRuns, matchedGoals, matchedProjects, hasEntityResults } = useMemo(() => ui.buildPaletteResults( ui.navItems, company.companyAgents, company.tasks, company.runs, company.companyGoals, company.companyProjects, ), [ ui.navItems, ui.paletteQuery, company.companyAgents, company.tasks, company.runs, company.companyGoals, company.companyProjects, ]); const paletteActions = useMemo( () => ui.buildPaletteActions(filteredNavItems, matchedAgents, matchedTasks, matchedRuns, matchedGoals, matchedProjects), [ui, filteredNavItems, matchedAgents, matchedTasks, matchedRuns, matchedGoals, matchedProjects], ); const paletteActionIndex = useMemo( () => new Map(paletteActions.map((action, index) => [action.key, index])), [paletteActions], ); useCommandPaletteHotkeys({ commandPaletteOpen: ui.commandPaletteOpen, setCommandPaletteOpen: ui.setCommandPaletteOpen, setCommandPaletteSearch: ui.setCommandPaletteSearch, setSelectedPaletteIndex: ui.setSelectedPaletteIndex, paletteActions, selectedPaletteIndex: ui.selectedPaletteIndex, navigateTo, commandPaletteSearch: ui.commandPaletteSearch, }); const appActions = useMemo(() => ({ refresh: company.refresh, setError, openPathWithFeedback: company.openPathWithFeedback, startRun, cancelRun, }), [company.refresh, company.openPathWithFeedback, startRun, cancelRun, setError]); if (!company.snapshot) { return ( {ui.t("app.loading")} ); } if (company.snapshot.companies.length === 0 || ui.showOnboarding || ui.initialOnboardingLocked) { return ( 0 && !ui.initialOnboardingLocked} onCancel={() => { ui.setShowOnboarding(false); ui.setInitialOnboardingLocked(false); setError(null); }} currentLocale={ui.locale} onLocaleChange={ui.handleLocaleChange} isFirstLaunch={company.snapshot.companies.length === 0} /> ); } return ( {showCustomTitleBar && } {ui.navItems.find((item) => item.id === ui.section)?.label ?? ui.t(`nav.${ui.section}`)} ui.setConsoleOpen(!ui.consoleOpen)} > {ui.consoleOpen ? ui.t("app.hideConsole") : ui.t("app.showConsole")} ); } export default App;