tfrere's picture
tfrere HF Staff
Deploy hf-model-viewer 2026-05-22T16:59:58Z
fc01079 verified
import {
AppBar,
Box,
Button,
Chip,
Divider,
IconButton,
Stack,
Toolbar,
Tooltip,
Typography,
} from "@mui/material";
import OpenInNewRoundedIcon from "@mui/icons-material/OpenInNewRounded";
import RefreshRoundedIcon from "@mui/icons-material/RefreshRounded";
import HubRoundedIcon from "@mui/icons-material/HubRounded";
import type { IRGraphMeta, ResolvedModel } from "../types";
import { GranularitySlider } from "./GranularitySlider";
interface Props {
resolved: ResolvedModel | null;
meta: IRGraphMeta | null;
onReset: () => void;
granularity: number;
maxGranularity: number;
visibleNodeCount: number;
onGranularityChange: (v: number) => void;
bytesTransferred: number;
}
function bytesPretty(n: number): string {
if (n < 1024) return `${n} B`;
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
if (n < 1024 * 1024 * 1024) return `${(n / (1024 * 1024)).toFixed(1)} MB`;
return `${(n / (1024 * 1024 * 1024)).toFixed(2)} GB`;
}
function formatParams(n: number): string {
if (n === 0) return "0";
if (n < 1_000_000) return `${(n / 1_000).toFixed(1)}K`;
if (n < 1_000_000_000) return `${(n / 1_000_000).toFixed(1)}M`;
return `${(n / 1_000_000_000).toFixed(2)}B`;
}
/** Split `owner/name` into a 2-tuple. Falls back gracefully on malformed input. */
function splitRepo(repoId: string): { owner: string; name: string } {
const i = repoId.indexOf("/");
if (i < 0) return { owner: "", name: repoId };
return { owner: repoId.slice(0, i), name: repoId.slice(i + 1) };
}
const MONO_FONT =
'"JetBrains Mono", "SFMono-Regular", ui-monospace, Menlo, Consolas, monospace';
const verticalRule = (
<Divider
orientation="vertical"
flexItem
sx={{ borderColor: "rgba(155,180,255,0.12)", mx: 1, my: 1.5 }}
/>
);
export function Header({
resolved,
meta,
onReset,
granularity,
maxGranularity,
visibleNodeCount,
onGranularityChange,
bytesTransferred,
}: Props) {
// Effective bandwidth savings: compare the *expected* full-file size to the
// bytes we actually pulled over the wire (just header metadata thanks to
// raw_data skipping + external-data detection).
const totalSize = resolved?.sizeBytes ?? 0;
const savings =
totalSize > 0 && bytesTransferred > 0 && bytesTransferred < totalSize
? 1 - bytesTransferred / totalSize
: 0;
const savingsLabel =
savings > 0.05 ? `${(savings * 100).toFixed(0)}% saved` : null;
const { owner, name } = splitRepo(resolved?.repoId ?? "");
return (
<AppBar
position="sticky"
elevation={0}
sx={{
background:
"linear-gradient(180deg, rgba(13,19,36,0.92) 0%, rgba(10,15,28,0.92) 100%)",
backdropFilter: "blur(12px) saturate(140%)",
WebkitBackdropFilter: "blur(12px) saturate(140%)",
borderBottom: "1px solid rgba(155,180,255,0.12)",
boxShadow: "0 1px 0 rgba(0,0,0,0.25)",
color: "rgba(235,240,255,0.92)",
}}
>
<Toolbar
disableGutters
sx={{
gap: { xs: 0.75, sm: 1.25, md: 1.5 },
minHeight: { xs: 52, sm: 60 },
px: { xs: 1.25, sm: 2, md: 2.5 },
py: { xs: 0.75, sm: 0 },
flexWrap: { xs: "wrap", md: "nowrap" },
rowGap: { xs: 0.75, md: 0 },
}}
>
{/* ──────────── Brand ──────────── */}
<Stack
direction="row"
alignItems="center"
spacing={1}
sx={{ flexShrink: 0 }}
>
<Box
sx={{
width: { xs: 28, sm: 30 },
height: { xs: 28, sm: 30 },
borderRadius: 1,
display: "flex",
alignItems: "center",
justifyContent: "center",
background:
"linear-gradient(135deg, rgba(120,160,255,0.22) 0%, rgba(180,130,255,0.18) 100%)",
border: "1px solid rgba(180,200,255,0.25)",
}}
>
<HubRoundedIcon
sx={{ color: "#cbd6ff", fontSize: { xs: 16, sm: 18 } }}
/>
</Box>
<Stack
spacing={0}
sx={{ lineHeight: 1, display: { xs: "none", md: "flex" } }}
>
<Typography
sx={{
fontWeight: 700,
fontSize: 13.5,
letterSpacing: 0.3,
color: "rgba(245,248,255,0.96)",
whiteSpace: "nowrap",
lineHeight: 1.1,
}}
>
HF Model Viewer
</Typography>
<Typography
sx={{
fontSize: 9.5,
letterSpacing: 1.2,
textTransform: "uppercase",
color: "rgba(160,180,220,0.55)",
lineHeight: 1.1,
whiteSpace: "nowrap",
}}
>
architecture explorer
</Typography>
</Stack>
</Stack>
{resolved && (
<>
<Box sx={{ display: { xs: "none", md: "block" } }}>
{verticalRule}
</Box>
{/* ──────────── Repo crumb ──────────── */}
<Stack
direction="row"
alignItems="center"
spacing={0.5}
sx={{
minWidth: 0,
flex: { xs: 1, md: "0 1 auto" },
fontFamily: MONO_FONT,
fontSize: 12.5,
color: "rgba(220,228,255,0.88)",
}}
>
{owner && (
<Box
sx={{
display: { xs: "none", lg: "flex" },
alignItems: "center",
}}
>
<Typography
component="span"
sx={{
fontFamily: MONO_FONT,
fontSize: 12.5,
color: "rgba(180,195,235,0.7)",
whiteSpace: "nowrap",
}}
>
{owner}
</Typography>
<Typography
component="span"
sx={{
fontFamily: MONO_FONT,
color: "rgba(140,160,210,0.5)",
mx: 0.4,
}}
>
/
</Typography>
</Box>
)}
<Tooltip title={resolved.repoId} arrow placement="bottom">
<Typography
component="span"
sx={{
fontFamily: MONO_FONT,
fontSize: { xs: 12, sm: 12.5 },
fontWeight: 600,
color: "rgba(245,248,255,0.96)",
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
minWidth: 0,
maxWidth: { xs: "100%", sm: 160, md: 200, lg: 260 },
}}
>
{name}
</Typography>
</Tooltip>
{meta?.modelType && (
<Chip
size="small"
label={meta.modelType}
sx={{
ml: 0.75,
height: 19,
fontSize: 10.5,
fontWeight: 600,
letterSpacing: 0.3,
background: "rgba(120,200,160,0.15)",
color: "#9ce6c3",
border: "1px solid rgba(140,220,170,0.35)",
flexShrink: 0,
"& .MuiChip-label": { px: 0.9 },
}}
/>
)}
</Stack>
<Box sx={{ display: { xs: "none", md: "block" }, flex: 1 }} />
{/* ──────────── Granularity (full-width second row on mobile) ──────────── */}
{maxGranularity > 0 && (
<Box
sx={{
flexShrink: 0,
order: { xs: 3, md: 0 },
width: { xs: "100%", md: "auto" },
}}
>
<GranularitySlider
value={granularity}
max={maxGranularity}
nodeCount={visibleNodeCount}
onChange={onGranularityChange}
/>
</Box>
)}
<Box sx={{ display: { xs: "none", md: "block" } }}>
{verticalRule}
</Box>
{/* ──────────── Stats + actions ──────────── */}
<Stack
direction="row"
spacing={{ xs: 0.5, sm: 1 }}
alignItems="center"
sx={{ flexShrink: 0, ml: { xs: "auto", md: 0 } }}
>
{meta && (
<Tooltip title="Total parameters" arrow>
<Chip
size="small"
label={`${formatParams(meta.totalParams)} params`}
sx={{
height: 24,
fontSize: 11.5,
fontWeight: 600,
fontFamily: MONO_FONT,
background: "rgba(120,160,255,0.10)",
color: "rgba(200,215,255,0.95)",
border: "1px solid rgba(140,180,255,0.25)",
"& .MuiChip-label": { px: 1 },
display: { xs: "none", sm: "inline-flex" },
}}
/>
</Tooltip>
)}
{savingsLabel && (
<Tooltip
title={`Pulled ${bytesPretty(bytesTransferred)} of ${bytesPretty(totalSize)} over the wire β€” only the safetensors header was read.`}
arrow
>
<Chip
size="small"
label={savingsLabel}
sx={{
height: 24,
fontSize: 11.5,
fontWeight: 600,
background: "rgba(34,193,160,0.12)",
color: "#7be0c1",
border: "1px solid rgba(60,210,170,0.4)",
"& .MuiChip-label": { px: 1 },
display: { xs: "none", lg: "inline-flex" },
}}
/>
</Tooltip>
)}
<Tooltip title="Open this model on Hugging Face" arrow>
<Button
size="small"
variant="text"
endIcon={
<OpenInNewRoundedIcon
sx={{
fontSize: 14,
ml: { xs: -0.5, sm: 0 },
}}
/>
}
href={`https://huggingface.co/${resolved.repoId}`}
target="_blank"
rel="noreferrer"
sx={{
fontSize: 12,
fontWeight: 600,
textTransform: "none",
whiteSpace: "nowrap",
color: "rgba(220,228,255,0.85)",
px: { xs: 0.8, sm: 1.2 },
minWidth: { xs: 32, sm: "auto" },
"&:hover": {
background: "rgba(155,180,255,0.08)",
color: "rgba(245,248,255,0.96)",
},
"& .MuiButton-endIcon": {
ml: { xs: 0, sm: 0.5 },
},
}}
>
<Box
component="span"
sx={{ display: { xs: "none", sm: "inline" } }}
>
View on HF
</Box>
</Button>
</Tooltip>
<Tooltip title="Load another model" arrow>
<IconButton
size="small"
onClick={onReset}
sx={{
color: "rgba(200,215,255,0.75)",
border: "1px solid rgba(155,180,255,0.15)",
width: 30,
height: 30,
flexShrink: 0,
"&:hover": {
background: "rgba(155,180,255,0.1)",
color: "rgba(245,248,255,0.95)",
},
}}
>
<RefreshRoundedIcon sx={{ fontSize: 16 }} />
</IconButton>
</Tooltip>
</Stack>
</>
)}
{!resolved && <Box sx={{ flex: 1 }} />}
</Toolbar>
</AppBar>
);
}