import { useCallback, useEffect } from "react"; import { Link, NavLink, Outlet, useLocation } from "react-router-dom"; import { getUserSessions } from "../api/sessions"; import type { Session } from "../api/sessions"; import { useTheme } from "../hooks/use-theme"; import { useAuth } from "../state/auth"; import { useSessions } from "../state/sessions"; import { useMobileViewport } from "../hooks/use-mobile-viewport"; import { EventProvider } from "../state/events"; import { ToastProvider } from "../state/toast"; import { NotificationProvider } from "../state/notifications"; import { EventToastBridge } from "./features/notification/event-toast-bridge"; import { NotificationCenter } from "./features/notification/notification-center"; import { Icon } from "./icon"; import { MobileNav } from "./features/mobile/mobile-nav"; import { StartToolFAB } from "./features/tool/start-tool-fab"; import type { IconName } from "../utils/icons"; const NAV_ITEMS: { to: string; label: string; icon: IconName; badge?: "sessions"; }[] = [ { to: "/", label: "Home", icon: "dashboard" }, { to: "/sessions", label: "Sessions", icon: "terminal", badge: "sessions" }, { to: "/workspaces", label: "Workspaces", icon: "folder" }, { to: "/projects", label: "Projects", icon: "projects" }, { to: "/tool-workshop", label: "Tool Workshop", icon: "settings" }, { to: "/config-profiles", label: "Config Profiles", icon: "folder" }, { to: "/settings", label: "Settings", icon: "settings" }, ]; const SessionItem = ({ session }: { session: Session }) => { const isRunning = session.status === "running"; // Determine the link target: // - Web tools open their tunnel URL // - Terminal tools open the terminal page // - Everything else falls back to the project page const hasTerminal = session.tool_type_interfaces.includes("terminal"); const hasWeb = session.tool_type_interfaces.includes("web"); const href = session.url && hasWeb ? session.url : hasTerminal ? `/instances/${session.id}/terminal` : `/projects/${session.project_id}`; return ( {session.display_name} ); }; export const AppShell = () => { useTheme(); const { user, logout } = useAuth(); const { sessions, setAllSessions } = useSessions(); const location = useLocation(); const isMobile = useMobileViewport(); const isMobileTerminal = isMobile && location.pathname.includes("/instances/") && location.pathname.includes("/terminal"); const loadSessions = useCallback(async () => { try { const data = await getUserSessions(); setAllSessions(data); } catch { // Silently fail - sessions are optional } }, [setAllSessions]); useEffect(() => { void loadSessions(); // Poll every 30 seconds (reduced from 10s to avoid ERR_NETWORK_CHANGED from Docker network changes) const interval = setInterval(() => { void loadSessions(); }, 30000); return () => clearInterval(interval); }, [loadSessions]); if (isMobileTerminal) { return (
); } return (
Headquarter
{user?.name ?? "User"}
{!isMobile && ( )}
{isMobile && ( s.status === "running").length } /> )}
); };