import { useState, useEffect, useCallback } from "react"; import { Outlet, Link, useLocation, useNavigate } from "react-router-dom"; import { Terminal } from "../components/terminal/Terminal"; import { useWebSocket } from "../hooks/useWebSocket"; import { useTheme } from "../hooks/useTheme"; import { Code2, Wrench, Hammer, Play, GitBranch, Settings, Sun, Moon, Terminal as TerminalIcon, ChevronDown, ChevronUp, } from "lucide-react"; import type { LucideIcon } from "lucide-react"; const NAV_ITEMS: { path: string; label: string; Icon: LucideIcon }[] = [ { path: "/editor", label: "Editor", Icon: Code2 }, { path: "/toolset", label: "Toolset", Icon: Wrench }, { path: "/build", label: "Build", Icon: Hammer }, { path: "/server", label: "Server", Icon: Play }, { path: "/repos", label: "Repos", Icon: GitBranch }, { path: "/settings", label: "Settings", Icon: Settings }, ]; export function IDELayout({ sidebar }: { sidebar?: React.ReactNode }) { const [terminalOpen, setTerminalOpen] = useState(false); const [upstreamBehind, setUpstreamBehind] = useState(0); const [pendingToolset, setPendingToolset] = useState(0); const location = useLocation(); const navigate = useNavigate(); const { subscribe } = useWebSocket(); const { theme, toggleTheme } = useTheme(); const showSidebar = location.pathname === "/editor" || location.pathname.startsWith("/editor/"); useEffect(() => { return subscribe("git:upstream-update", (event) => { const data = event.data as { behind: number }; if (data.behind > 0) { setUpstreamBehind((prev) => prev + data.behind); } }); }, [subscribe]); useEffect(() => { return subscribe("toolset:changes", (event) => { const data = event.data as { count?: number }; if (data.count !== undefined) setPendingToolset(data.count); }); }, [subscribe]); useEffect(() => { if (location.pathname === "/repos") setUpstreamBehind(0); }, [location.pathname]); useEffect(() => { if (location.pathname === "/toolset") setPendingToolset(0); }, [location.pathname]); const handleKeyDown = useCallback( (e: KeyboardEvent) => { const tag = (e.target as HTMLElement)?.tagName; if (tag === "INPUT" || tag === "TEXTAREA" || (e.target as HTMLElement)?.closest?.(".monaco-editor")) { return; } if (e.ctrlKey && e.shiftKey && e.key === "B") { e.preventDefault(); navigate("/build"); } else if (e.ctrlKey && e.key === "`") { e.preventDefault(); setTerminalOpen((v) => !v); } else if (e.ctrlKey && e.shiftKey && e.key === "F") { e.preventDefault(); navigate("/editor"); } else if (e.ctrlKey && e.shiftKey && e.key === "G") { e.preventDefault(); navigate("/repos"); } else if (e.ctrlKey && e.key === ",") { e.preventDefault(); navigate("/settings"); } }, [navigate], ); useEffect(() => { window.addEventListener("keydown", handleKeyDown); return () => window.removeEventListener("keydown", handleKeyDown); }, [handleKeyDown]); const getBadge = (path: string): number => { if (path === "/repos") return upstreamBehind; if (path === "/toolset") return pendingToolset; return 0; }; return (