"use client"; import { useEffect, useMemo, useState } from "react"; import { usePathname, useRouter } from "next/navigation"; import Link from "next/link"; import PropTypes from "prop-types"; import ProviderIcon from "@/shared/components/ProviderIcon"; import HeaderMenu from "@/shared/components/HeaderMenu"; import HeaderLanguage from "@/shared/components/HeaderLanguage"; import ThemeToggle from "@/shared/components/ThemeToggle"; import DonateModal from "@/shared/components/DonateModal"; import { useHeaderSearchStore } from "@/store/headerSearchStore"; import { OAUTH_PROVIDERS, APIKEY_PROVIDERS } from "@/shared/constants/config"; import { MEDIA_PROVIDER_KINDS, AI_PROVIDERS } from "@/shared/constants/providers"; import { translate } from "@/i18n/runtime"; const getPageInfo = (pathname) => { if (!pathname) return { title: "", description: "", breadcrumbs: [] }; // Media provider detail: /dashboard/media-providers/[kind]/[id] const mediaDetailMatch = pathname.match(/\/media-providers\/([^/]+)\/([^/]+)$/); if (mediaDetailMatch) { const kindId = mediaDetailMatch[1]; const providerId = mediaDetailMatch[2]; const kindConfig = MEDIA_PROVIDER_KINDS.find((k) => k.id === kindId); const provider = AI_PROVIDERS[providerId]; return { title: provider?.name || providerId, description: "", breadcrumbs: [ { label: "Media Providers", href: `/dashboard/media-providers/${kindId}` }, { label: kindConfig?.label || kindId, href: `/dashboard/media-providers/${kindId}` }, { label: provider?.name || providerId, image: `/providers/${providerId}.png` }, ], }; } // Media provider kind: /dashboard/media-providers/[kind] const mediaKindMatch = pathname.match(/\/media-providers\/([^/]+)$/); if (mediaKindMatch) { const kindId = mediaKindMatch[1]; const kindConfig = MEDIA_PROVIDER_KINDS.find((k) => k.id === kindId); return { title: kindConfig?.label || kindId, description: `Manage your ${kindConfig?.label || kindId} providers`, icon: kindConfig?.icon || "perm_media", breadcrumbs: [], }; } // Provider detail page: /dashboard/providers/[id] const providerMatch = pathname.match(/\/providers\/([^/]+)$/); if (providerMatch) { const providerId = providerMatch[1]; const providerInfo = OAUTH_PROVIDERS[providerId] || APIKEY_PROVIDERS[providerId]; if (providerInfo) { return { title: providerInfo.name, description: "", breadcrumbs: [ { label: "Providers", href: "/dashboard/providers" }, { label: providerInfo.name, image: `/providers/${providerInfo.id}.png`, }, ], }; } } if (pathname.includes("/providers") && !pathname.includes("/media-providers")) return { title: "Providers", description: "Manage your AI provider connections", icon: "dns", breadcrumbs: [], }; if (pathname.includes("/combos")) return { title: "Combos", description: "Model combos with fallback", icon: "layers", breadcrumbs: [], }; if (pathname.includes("/usage")) return { title: "Usage & Analytics", description: "Monitor your API usage, token consumption, and request logs", icon: "bar_chart", breadcrumbs: [], }; if (pathname.includes("/auth-files")) return { title: "Auth Files", description: "Map provider credentials stored in the local database", icon: "vpn_key", breadcrumbs: [], }; if (pathname.includes("/quota")) return { title: "Quota Tracker", description: "Track and manage your API quota limits", icon: "data_usage", breadcrumbs: [], }; if (pathname.includes("/mitm")) return { title: "MITM Proxy", description: "Intercept CLI tool traffic and route through 9Router", icon: "security", breadcrumbs: [], }; if (pathname.includes("/cli-tools")) return { title: "CLI Tools", description: "Configure CLI tools", icon: "terminal", breadcrumbs: [], }; if (pathname.includes("/proxy-pools")) return { title: "Proxy Pools", description: "Manage your proxy pool configurations", icon: "lan", breadcrumbs: [], }; if (pathname.includes("/skills")) return { title: "Agent Skills", description: "Copy a link and paste to your AI to use 9Router — no install needed", icon: "extension", breadcrumbs: [], }; if (pathname.includes("/endpoint")) return { title: "Endpoint", description: "API endpoint configuration", icon: "api", breadcrumbs: [], }; if (pathname.includes("/profile")) return { title: "Settings", description: "Manage your preferences", icon: "settings", breadcrumbs: [], }; if (pathname.includes("/translator")) return { title: "Translator", description: "Debug translation flow between formats", icon: "translate", breadcrumbs: [], }; if (pathname.includes("/console-log")) return { title: "Console Log", description: "Live server console output", icon: "monitor", breadcrumbs: [], }; if (pathname === "/dashboard") return { title: "Endpoint", description: "API endpoint configuration", icon: "api", breadcrumbs: [], }; return { title: "", description: "", breadcrumbs: [] }; }; export default function Header({ onMenuClick, showMenuButton = true }) { const pathname = usePathname(); const router = useRouter(); const [displayName, setDisplayName] = useState(""); const [loginMethod, setLoginMethod] = useState(""); const [donateOpen, setDonateOpen] = useState(false); // Memoize page info to prevent unnecessary recalculations const pageInfo = useMemo(() => getPageInfo(pathname), [pathname]); const { title, description, icon, breadcrumbs } = pageInfo; useEffect(() => { let cancelled = false; async function loadAuthStatus() { try { const res = await fetch("/api/auth/status", { cache: "no-store" }); if (!res.ok) return; const data = await res.json(); if (!cancelled) { setDisplayName(data?.displayName || data?.oidcName || data?.oidcEmail || ""); setLoginMethod(data?.loginMethod || ""); } } catch { if (!cancelled) { setDisplayName(""); setLoginMethod(""); } } } loadAuthStatus(); return () => { cancelled = true; }; }, []); const handleLogout = async () => { try { const res = await fetch("/api/auth/logout", { method: "POST" }); if (res.ok) { router.push("/login"); router.refresh(); } } catch (err) { console.error("Failed to logout:", err); } }; return (
{/* Mobile menu button */}
{showMenuButton && ( )}
{/* Page title with breadcrumbs */}
{breadcrumbs.length > 0 ? (
{breadcrumbs.map((crumb, index) => (
{index > 0 && ( chevron_right )} {crumb.href ? ( {crumb.label} ) : (
{crumb.image && ( )}

{translate(crumb.label)}

)}
))}
) : title ? (
{icon && ( {icon} )}

{translate(title)}

{description && (

{translate(description)}

)}
) : null}
{/* Right actions */}
{displayName && loginMethod === "OIDC" && (
person {displayName} OIDC
)}
setDonateOpen(false)} />
); } function HeaderSearch() { const visible = useHeaderSearchStore((s) => s.visible); const query = useHeaderSearchStore((s) => s.query); const placeholder = useHeaderSearchStore((s) => s.placeholder); const setQuery = useHeaderSearchStore((s) => s.setQuery); if (!visible) return null; return (
search setQuery(e.target.value)} placeholder={placeholder} className="w-full h-8 pl-7 pr-7 rounded-lg border border-border bg-surface/60 text-sm focus:outline-none focus:border-primary/50 transition-colors" /> {query && ( )}
); } Header.propTypes = { onMenuClick: PropTypes.func, showMenuButton: PropTypes.bool, };