Spaces:
Sleeping
Sleeping
| 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> | |
| ); | |
| } | |