import React, { useState } from "react"; import { AppBar, Toolbar, Box, Link as MuiLink, IconButton, Tooltip, ButtonBase, Typography, } from "@mui/material"; import { useLocation, useNavigate, useSearchParams } from "react-router-dom"; import OpenInNewIcon from "@mui/icons-material/OpenInNew"; import LightModeOutlinedIcon from "@mui/icons-material/LightModeOutlined"; import DarkModeOutlinedIcon from "@mui/icons-material/DarkModeOutlined"; import { alpha } from "@mui/material/styles"; import MenuIcon from "@mui/icons-material/Menu"; import { Menu, MenuItem, useMediaQuery, useTheme } from "@mui/material"; const Navigation = ({ onToggleTheme, mode }) => { const location = useLocation(); const navigate = useNavigate(); const [searchParams] = useSearchParams(); const [anchorEl, setAnchorEl] = useState(null); const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down("md")); const [hasChanged, setHasChanged] = useState(false); const handleThemeToggle = () => { setHasChanged(true); onToggleTheme(); }; const iconStyle = { fontSize: "1.125rem", ...(hasChanged && { animation: "rotateIn 0.3s cubic-bezier(0.4, 0, 0.2, 1)", "@keyframes rotateIn": { "0%": { opacity: 0, transform: mode === "light" ? "rotate(-90deg) scale(0.8)" : "rotate(90deg) scale(0.8)", }, "100%": { opacity: 1, transform: "rotate(0) scale(1)", }, }, }), }; // Function to sync URL with parent HF page const syncUrlWithParent = (queryString, hash) => { // Check if we're in an HF Space iframe const isHFSpace = window.location !== window.parent.location; if (isHFSpace) { try { // Build complete URL with hash const fullPath = `${queryString}${hash ? "#" + hash : ""}`; window.parent.postMessage( { type: "urlUpdate", path: fullPath, }, "https://huggingface.co" ); } catch (e) { console.warn("Unable to sync URL with parent:", e); } } }; const linkStyle = (isActive = false) => ({ textDecoration: "none", color: isActive ? "text.primary" : "text.secondary", fontSize: "0.8125rem", opacity: isActive ? 1 : 0.8, display: "flex", alignItems: "center", gap: 0.5, paddingBottom: "2px", cursor: "pointer", position: "relative", "&:hover": { opacity: 1, color: "text.primary", }, "&::after": isActive ? { content: '""', position: "absolute", bottom: "-4px", left: "0", width: "100%", height: "2px", backgroundColor: (theme) => alpha( theme.palette.text.primary, theme.palette.mode === "dark" ? 0.3 : 0.2 ), borderRadius: "2px", } : {}, }); const Separator = () => ( ({ width: "4px", height: "4px", borderRadius: "100%", backgroundColor: alpha( theme.palette.text.primary, theme.palette.mode === "dark" ? 0.2 : 0.15 ), })} /> ); const handleNavigation = (path) => (e) => { e.preventDefault(); const searchString = searchParams.toString(); const queryString = searchString ? `?${searchString}` : ""; const newPath = `${path}${queryString}`; // Local navigation via React Router navigate(newPath); // If in HF Space, sync with parent if (window.location !== window.parent.location) { syncUrlWithParent(queryString, newPath); } }; const handleMenuOpen = (event) => { setAnchorEl(event.currentTarget); }; const handleMenuClose = () => { setAnchorEl(null); }; return ( {isMobile ? ( `1px solid ${alpha(theme.palette.divider, 0.1)}`, backgroundColor: (theme) => theme.palette.mode === "dark" ? alpha(theme.palette.background.paper, 0.8) : theme.palette.background.paper, backdropFilter: "blur(20px)", "& .MuiList-root": { py: 1, }, "& .MuiMenuItem-root": { px: 2, py: 1, fontSize: "0.8125rem", color: "text.secondary", transition: "all 0.2s ease-in-out", position: "relative", "&:hover": { backgroundColor: (theme) => alpha( theme.palette.text.primary, theme.palette.mode === "dark" ? 0.1 : 0.06 ), color: "text.primary", }, "&.Mui-selected": { backgroundColor: "transparent", color: "text.primary", "&::after": { content: '""', position: "absolute", left: "8px", width: "4px", height: "100%", top: "0", backgroundColor: (theme) => alpha( theme.palette.text.primary, theme.palette.mode === "dark" ? 0.3 : 0.2 ), borderRadius: "2px", }, "&:hover": { backgroundColor: (theme) => alpha( theme.palette.text.primary, theme.palette.mode === "dark" ? 0.1 : 0.06 ), }, }, }, }, }} transformOrigin={{ horizontal: "left", vertical: "top" }} anchorOrigin={{ horizontal: "left", vertical: "bottom" }} > {/* Navigation Section */} Navigation { handleNavigation("/")(e); handleMenuClose(); }} selected={location.pathname === "/"} > Leaderboard { handleNavigation("/add")(e); handleMenuClose(); }} selected={location.pathname === "/add"} > Submit model { handleNavigation("/vote")(e); handleMenuClose(); }} selected={location.pathname === "/vote"} > Vote for next model { handleNavigation("/quote")(e); handleMenuClose(); }} selected={location.pathname === "/quote"} > Citations {/* Separator */} `1px solid ${alpha(theme.palette.divider, 0.1)}`, }} /> {/* External Links Section */} External links Compare models About ({ color: "text.secondary", borderRadius: "100%", padding: 0, width: "36px", height: "36px", display: "flex", alignItems: "center", justifyContent: "center", transition: "all 0.2s ease-in-out", "&:hover": { color: "text.primary", backgroundColor: alpha( theme.palette.text.primary, theme.palette.mode === "dark" ? 0.1 : 0.06 ), }, "&.MuiButtonBase-root": { overflow: "hidden", }, "& .MuiTouchRipple-root": { color: alpha(theme.palette.text.primary, 0.3), }, })} > {mode === "light" ? ( ) : ( )} ) : ( // Desktop version {/* Internal navigation */} Leaderboard Submit model Vote for next model Citations {/* External links */} Compare models About {/* Dark mode toggle */} ({ color: "text.secondary", borderRadius: "100%", padding: 0, width: "36px", height: "36px", display: "flex", alignItems: "center", justifyContent: "center", transition: "all 0.2s ease-in-out", "&:hover": { color: "text.primary", backgroundColor: alpha( theme.palette.text.primary, theme.palette.mode === "dark" ? 0.1 : 0.06 ), }, "&.MuiButtonBase-root": { overflow: "hidden", }, "& .MuiTouchRipple-root": { color: alpha(theme.palette.text.primary, 0.3), }, })} > {mode === "light" ? ( ) : ( )} )} ); }; export default Navigation;