tfrere commited on
Commit
953a39e
·
1 Parent(s): 2541c3c

update front

Browse files
client/src/components/LeaderboardCard.jsx CHANGED
@@ -2,13 +2,28 @@ import React from "react";
2
  import { Card, CardContent, Typography, Box, Stack } from "@mui/material";
3
  import FavoriteBorderIcon from "@mui/icons-material/FavoriteBorder";
4
  import { alpha } from "@mui/material/styles";
 
5
 
6
  const LeaderboardCard = ({ leaderboard }) => {
7
- const { card_data, likes, enriched, consolidated_notes, organization } =
8
- leaderboard;
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
  // Fonction pour capitaliser la première lettre
11
  const capitalizeFirst = (str) => {
 
12
  return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
13
  };
14
 
@@ -21,7 +36,7 @@ const LeaderboardCard = ({ leaderboard }) => {
21
  };
22
 
23
  // Séparer les emojis s'il y en a plusieurs
24
- const emojis = card_data.emoji.trim().split(/\s+/);
25
 
26
  return (
27
  <Box
@@ -44,7 +59,9 @@ const LeaderboardCard = ({ leaderboard }) => {
44
  sx={{
45
  position: "relative",
46
  minHeight: 180,
47
- background: `linear-gradient(135deg, ${card_data.colorFrom} 0%, ${card_data.colorTo} 100%)`,
 
 
48
  color: "white",
49
  overflow: "visible",
50
  "&::after": {
@@ -161,24 +178,38 @@ const LeaderboardCard = ({ leaderboard }) => {
161
  }}
162
  >
163
  <Typography
164
- variant="h6"
165
- component="h2"
166
- align="center"
167
  sx={{
168
- fontSize: "1.25rem",
169
- fontWeight: 800,
170
- maxWidth: "90%",
171
- margin: "0 auto",
172
- zIndex: 5,
173
- textShadow: "rgba(0, 0, 0, 0.25) 0px 1px 2px",
174
  }}
175
  >
176
- {card_data.title}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
  </Typography>
178
  </CardContent>
179
  </Card>
180
 
181
- {consolidated_notes && (
182
  <Typography
183
  variant="body2"
184
  sx={{
@@ -186,7 +217,7 @@ const LeaderboardCard = ({ leaderboard }) => {
186
  fontSize: "0.875rem",
187
  }}
188
  >
189
- {consolidated_notes}
190
  </Typography>
191
  )}
192
  </Stack>
 
2
  import { Card, CardContent, Typography, Box, Stack } from "@mui/material";
3
  import FavoriteBorderIcon from "@mui/icons-material/FavoriteBorder";
4
  import { alpha } from "@mui/material/styles";
5
+ import { useLeaderboard } from "../context/LeaderboardContext";
6
 
7
  const LeaderboardCard = ({ leaderboard }) => {
8
+ const {
9
+ card_data = {},
10
+ likes = 0,
11
+ enriched,
12
+ consolidated_notes,
13
+ organization,
14
+ } = leaderboard;
15
+
16
+ const { getHighlightedText, searchQuery } = useLeaderboard();
17
+ const {
18
+ text: highlightedText,
19
+ shouldHighlight,
20
+ highlightStart,
21
+ highlightEnd,
22
+ } = getHighlightedText(card_data.title || leaderboard.id, searchQuery);
23
 
24
  // Fonction pour capitaliser la première lettre
25
  const capitalizeFirst = (str) => {
26
+ if (!str) return "";
27
  return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
28
  };
29
 
 
36
  };
37
 
38
  // Séparer les emojis s'il y en a plusieurs
39
+ const emojis = card_data.emoji?.trim().split(/\s+/) || ["🎯"];
40
 
41
  return (
42
  <Box
 
59
  sx={{
60
  position: "relative",
61
  minHeight: 180,
62
+ background: `linear-gradient(135deg, ${
63
+ card_data.colorFrom || "#6366f1"
64
+ } 0%, ${card_data.colorTo || "#a855f7"} 100%)`,
65
  color: "white",
66
  overflow: "visible",
67
  "&::after": {
 
178
  }}
179
  >
180
  <Typography
181
+ variant="subtitle1"
 
 
182
  sx={{
183
+ fontWeight: 900,
184
+ whiteSpace: "nowrap",
185
+ overflow: "hidden",
186
+ textOverflow: "ellipsis",
 
 
187
  }}
188
  >
189
+ {shouldHighlight ? (
190
+ <>
191
+ {highlightedText.slice(0, highlightStart)}
192
+ <Box
193
+ component="span"
194
+ sx={{
195
+ bgcolor: "primary.main",
196
+ color: "primary.contrastText",
197
+ px: 0.5,
198
+ borderRadius: 0.5,
199
+ }}
200
+ >
201
+ {highlightedText.slice(highlightStart, highlightEnd)}
202
+ </Box>
203
+ {highlightedText.slice(highlightEnd)}
204
+ </>
205
+ ) : (
206
+ card_data.title || leaderboard.id
207
+ )}
208
  </Typography>
209
  </CardContent>
210
  </Card>
211
 
212
+ {(card_data.short_description || consolidated_notes) && (
213
  <Typography
214
  variant="body2"
215
  sx={{
 
217
  fontSize: "0.875rem",
218
  }}
219
  >
220
+ {card_data.short_description || consolidated_notes}
221
  </Typography>
222
  )}
223
  </Stack>
client/src/components/LeaderboardFilters/LeaderboardFilters.jsx ADDED
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState } from "react";
2
+ import {
3
+ Box,
4
+ Stack,
5
+ Button,
6
+ FormControlLabel,
7
+ Switch,
8
+ TextField,
9
+ InputAdornment,
10
+ Typography,
11
+ Divider,
12
+ } from "@mui/material";
13
+ import SearchIcon from "@mui/icons-material/Search";
14
+ import { useLeaderboard } from "../../context/LeaderboardContext";
15
+ import { useDebounce } from "../../hooks/useDebounce";
16
+ import { alpha } from "@mui/material/styles";
17
+ import { useMediaQuery } from "@mui/material";
18
+
19
+ const LeaderboardFilters = ({ allSections }) => {
20
+ const {
21
+ setSearchQuery,
22
+ arenaOnly,
23
+ setArenaOnly,
24
+ totalLeaderboards,
25
+ filteredCount,
26
+ filterLeaderboards,
27
+ } = useLeaderboard();
28
+
29
+ const [inputValue, setInputValue] = useState("");
30
+ const debouncedSearch = useDebounce(inputValue, 200);
31
+
32
+ // Update the search query after debounce
33
+ React.useEffect(() => {
34
+ setSearchQuery(debouncedSearch);
35
+ }, [debouncedSearch, setSearchQuery]);
36
+
37
+ // Check if any filter is active
38
+ const isFilterActive = debouncedSearch || arenaOnly;
39
+
40
+ const isMobile = useMediaQuery((theme) => theme.breakpoints.down("md"));
41
+
42
+ return (
43
+ <Stack spacing={4} sx={{ width: "100%", mb: 4, alignItems: "center" }}>
44
+ {/* Categories */}
45
+ <Box sx={{ width: "60%" }}>
46
+ <Stack
47
+ direction="row"
48
+ spacing={1}
49
+ useFlexGap
50
+ flexWrap="wrap"
51
+ justifyContent="center"
52
+ sx={{ pb: 2 }}
53
+ >
54
+ {allSections.map(({ id, title, data }) => {
55
+ const filteredCount = filterLeaderboards(data).length;
56
+ return (
57
+ <Button
58
+ key={id}
59
+ onClick={() => {
60
+ if (filteredCount > 0) {
61
+ document.getElementById(id)?.scrollIntoView({
62
+ behavior: "smooth",
63
+ block: "start",
64
+ });
65
+ }
66
+ }}
67
+ variant="outlined"
68
+ size="small"
69
+ disabled={filteredCount === 0}
70
+ sx={{
71
+ textTransform: "none",
72
+ cursor: filteredCount === 0 ? "default" : "pointer",
73
+ mb: 1,
74
+ backgroundColor: (theme) =>
75
+ theme.palette.mode === "dark"
76
+ ? "background.paper"
77
+ : "white",
78
+ "&:hover": {
79
+ backgroundColor: (theme) =>
80
+ theme.palette.mode === "dark"
81
+ ? "background.paper"
82
+ : "white",
83
+ },
84
+ "& .MuiTouchRipple-root": {
85
+ transition: "none",
86
+ },
87
+ transition: "none",
88
+ }}
89
+ >
90
+ {title}
91
+ <Box
92
+ component="span"
93
+ sx={{
94
+ display: "inline-flex",
95
+ alignItems: "center",
96
+ gap: 0.75,
97
+ color: "text.secondary",
98
+ ml: 0.75,
99
+ }}
100
+ >
101
+ <Box
102
+ component="span"
103
+ sx={(theme) => ({
104
+ width: "4px",
105
+ height: "4px",
106
+ borderRadius: "100%",
107
+ backgroundColor: alpha(
108
+ theme.palette.text.primary,
109
+ theme.palette.mode === "dark" ? 0.2 : 0.15
110
+ ),
111
+ })}
112
+ />
113
+ {filteredCount}
114
+ </Box>
115
+ </Button>
116
+ );
117
+ })}
118
+ </Stack>
119
+ </Box>
120
+
121
+ {/* Search bar */}
122
+ <Box sx={{ width: "100%" }}>
123
+ <TextField
124
+ size="large"
125
+ placeholder={
126
+ isMobile
127
+ ? "Search by name or tags..."
128
+ : "Search by name or use domain:, language:, judge:, test:, modality:, submission:"
129
+ }
130
+ value={inputValue}
131
+ onChange={(e) => setInputValue(e.target.value)}
132
+ fullWidth
133
+ sx={{
134
+ backgroundColor: (theme) =>
135
+ theme.palette.mode === "dark" ? "background.paper" : "white",
136
+ "& .MuiOutlinedInput-root": {
137
+ borderRadius: 1,
138
+ fontSize: "0.875rem",
139
+ backgroundColor: (theme) =>
140
+ theme.palette.mode === "dark" ? "background.paper" : "white",
141
+ },
142
+ }}
143
+ InputProps={{
144
+ startAdornment: (
145
+ <InputAdornment position="start">
146
+ <SearchIcon
147
+ sx={{ fontSize: "1.25rem", color: "text.secondary" }}
148
+ />
149
+ </InputAdornment>
150
+ ),
151
+ endAdornment: (
152
+ <InputAdornment position="end">
153
+ <Stack direction="row" spacing={2} alignItems="center">
154
+ <Typography
155
+ variant="body2"
156
+ sx={{
157
+ color: isFilterActive ? "primary.main" : "text.secondary",
158
+ fontWeight: isFilterActive ? 500 : 400,
159
+ }}
160
+ >
161
+ {isFilterActive
162
+ ? `${filteredCount}/${totalLeaderboards}`
163
+ : totalLeaderboards}{" "}
164
+ leaderboards
165
+ </Typography>
166
+ <Divider orientation="vertical" flexItem />
167
+ <FormControlLabel
168
+ control={
169
+ <Switch
170
+ checked={arenaOnly}
171
+ onChange={(e) => setArenaOnly(e.target.checked)}
172
+ size="small"
173
+ />
174
+ }
175
+ label={isMobile ? "Arena only" : "Show arena only"}
176
+ sx={{ mr: 0 }}
177
+ />
178
+ </Stack>
179
+ </InputAdornment>
180
+ ),
181
+ }}
182
+ />
183
+ </Box>
184
+ </Stack>
185
+ );
186
+ };
187
+
188
+ export default LeaderboardFilters;
client/src/components/LeaderboardSection.jsx CHANGED
@@ -1,12 +1,15 @@
1
  import React, { useState } from "react";
2
- import { Typography, Grid, Box, Button } from "@mui/material";
3
  import { alpha } from "@mui/material/styles";
4
  import LeaderboardCard from "./LeaderboardCard";
 
 
5
 
6
  const ITEMS_PER_PAGE = 3;
7
 
8
  const LeaderboardSection = ({ title, leaderboards }) => {
9
  const [showAll, setShowAll] = useState(false);
 
10
 
11
  if (!leaderboards || leaderboards.length === 0) return null;
12
 
@@ -14,6 +17,11 @@ const LeaderboardSection = ({ title, leaderboards }) => {
14
  ? leaderboards
15
  : leaderboards.slice(0, ITEMS_PER_PAGE);
16
 
 
 
 
 
 
17
  return (
18
  <Box sx={{ mb: 6 }}>
19
  <Box
@@ -59,7 +67,8 @@ const LeaderboardSection = ({ title, leaderboards }) => {
59
  </Box>
60
  {leaderboards.length > ITEMS_PER_PAGE && (
61
  <Button
62
- onClick={() => setShowAll(!showAll)}
 
63
  sx={{
64
  color: "text.secondary",
65
  fontSize: "0.875rem",
@@ -72,18 +81,35 @@ const LeaderboardSection = ({ title, leaderboards }) => {
72
  ),
73
  },
74
  }}
 
 
 
 
 
 
 
 
75
  >
76
- {showAll ? "Show less" : "Show more"}
77
  </Button>
78
  )}
79
  </Box>
80
  <Grid container spacing={3}>
81
- {displayedLeaderboards.map((leaderboard, index) => (
82
  <Grid item xs={12} sm={6} md={4} key={index}>
83
  <LeaderboardCard leaderboard={leaderboard} />
84
  </Grid>
85
  ))}
86
  </Grid>
 
 
 
 
 
 
 
 
 
87
  </Box>
88
  );
89
  };
 
1
  import React, { useState } from "react";
2
+ import { Typography, Grid, Box, Button, Collapse } from "@mui/material";
3
  import { alpha } from "@mui/material/styles";
4
  import LeaderboardCard from "./LeaderboardCard";
5
+ import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
6
+ import ExpandLessIcon from "@mui/icons-material/ExpandLess";
7
 
8
  const ITEMS_PER_PAGE = 3;
9
 
10
  const LeaderboardSection = ({ title, leaderboards }) => {
11
  const [showAll, setShowAll] = useState(false);
12
+ const [expanded, setExpanded] = useState(false);
13
 
14
  if (!leaderboards || leaderboards.length === 0) return null;
15
 
 
17
  ? leaderboards
18
  : leaderboards.slice(0, ITEMS_PER_PAGE);
19
 
20
+ const toggleExpanded = () => {
21
+ setShowAll(!showAll);
22
+ setExpanded(!expanded);
23
+ };
24
+
25
  return (
26
  <Box sx={{ mb: 6 }}>
27
  <Box
 
67
  </Box>
68
  {leaderboards.length > ITEMS_PER_PAGE && (
69
  <Button
70
+ onClick={toggleExpanded}
71
+ size="small"
72
  sx={{
73
  color: "text.secondary",
74
  fontSize: "0.875rem",
 
81
  ),
82
  },
83
  }}
84
+ endIcon={
85
+ <ExpandMoreIcon
86
+ sx={{
87
+ transform: expanded ? "rotate(180deg)" : "rotate(0deg)",
88
+ transition: "transform 300ms",
89
+ }}
90
+ />
91
+ }
92
  >
93
+ {expanded ? "Show less" : "Show more"}
94
  </Button>
95
  )}
96
  </Box>
97
  <Grid container spacing={3}>
98
+ {leaderboards.slice(0, ITEMS_PER_PAGE).map((leaderboard, index) => (
99
  <Grid item xs={12} sm={6} md={4} key={index}>
100
  <LeaderboardCard leaderboard={leaderboard} />
101
  </Grid>
102
  ))}
103
  </Grid>
104
+ <Collapse in={showAll} timeout={300}>
105
+ <Grid container spacing={3} sx={{ mt: 0 }}>
106
+ {leaderboards.slice(ITEMS_PER_PAGE).map((leaderboard, index) => (
107
+ <Grid item xs={12} sm={6} md={4} key={index + ITEMS_PER_PAGE}>
108
+ <LeaderboardCard leaderboard={leaderboard} />
109
+ </Grid>
110
+ ))}
111
+ </Grid>
112
+ </Collapse>
113
  </Box>
114
  );
115
  };
client/src/components/Navigation/Navigation.jsx CHANGED
@@ -4,26 +4,16 @@ import {
4
  Toolbar,
5
  Box,
6
  Link as MuiLink,
7
- IconButton,
8
- Tooltip,
9
  ButtonBase,
10
- Typography,
11
- Menu,
12
- MenuItem,
13
- useMediaQuery,
14
- useTheme,
15
  } from "@mui/material";
16
  import { alpha } from "@mui/material/styles";
17
  import OpenInNewIcon from "@mui/icons-material/OpenInNew";
18
  import LightModeOutlinedIcon from "@mui/icons-material/LightModeOutlined";
19
  import DarkModeOutlinedIcon from "@mui/icons-material/DarkModeOutlined";
20
- import MenuIcon from "@mui/icons-material/Menu";
21
  import { Link, useLocation } from "react-router-dom";
22
 
23
  const Navigation = ({ onToggleTheme, mode }) => {
24
- const [anchorEl, setAnchorEl] = useState(null);
25
- const theme = useTheme();
26
- const isMobile = useMediaQuery(theme.breakpoints.down("md"));
27
  const [hasChanged, setHasChanged] = useState(false);
28
  const location = useLocation();
29
 
@@ -99,14 +89,6 @@ const Navigation = ({ onToggleTheme, mode }) => {
99
  />
100
  );
101
 
102
- const handleMenuClose = () => {
103
- setAnchorEl(null);
104
- };
105
-
106
- const handleMenuOpen = (event) => {
107
- setAnchorEl(event.currentTarget);
108
- };
109
-
110
  return (
111
  <AppBar
112
  position="static"
@@ -116,199 +98,88 @@ const Navigation = ({ onToggleTheme, mode }) => {
116
  }}
117
  >
118
  <Toolbar sx={{ justifyContent: "center" }}>
119
- {isMobile ? (
120
- <Box
121
- sx={{
122
- display: "flex",
123
- width: "100%",
124
- justifyContent: "space-between",
125
- alignItems: "center",
126
- }}
127
- >
128
- <IconButton
129
- onClick={handleMenuOpen}
130
- sx={{ color: "text.secondary" }}
 
131
  >
132
- <MenuIcon />
133
- </IconButton>
134
- <Menu
135
- anchorEl={anchorEl}
136
- open={Boolean(anchorEl)}
137
- onClose={handleMenuClose}
138
- PaperProps={{
139
- elevation: 3,
140
- sx: {
141
- mt: 1.5,
142
- minWidth: 220,
143
- borderRadius: "12px",
144
- border: (theme) =>
145
- `1px solid ${alpha(theme.palette.divider, 0.1)}`,
146
- backgroundColor: (theme) =>
147
- theme.palette.mode === "dark"
148
- ? alpha(theme.palette.background.paper, 0.8)
149
- : theme.palette.background.paper,
150
- backdropFilter: "blur(20px)",
 
 
 
 
 
 
 
151
  },
152
  }}
153
  >
154
- <MenuItem
155
- component={Link}
156
- to="/"
157
- onClick={handleMenuClose}
158
- selected={location.pathname === "/"}
159
- sx={linkStyle(location.pathname === "/")}
160
- >
161
- Explorer
162
- </MenuItem>
163
- <MenuItem
164
- component={Link}
165
- to="/submit"
166
- onClick={handleMenuClose}
167
- selected={location.pathname === "/submit"}
168
- sx={linkStyle(location.pathname === "/submit")}
169
- >
170
- How to submit?
171
- </MenuItem>
172
- <MenuItem
173
- component={MuiLink}
174
- href="https://huggingface.co/docs/leaderboards/open_llm_leaderboard/about"
175
- target="_blank"
176
- sx={{
177
- ...linkStyle(),
178
- px: 2,
179
- py: 1,
180
- "& svg": {
181
- ml: "auto",
182
- fontSize: "0.875rem",
183
- opacity: 0.6,
184
- },
185
- }}
186
- >
187
- About
188
- <OpenInNewIcon />
189
- </MenuItem>
190
- </Menu>
191
- <Tooltip
192
- title={
193
- mode === "light"
194
- ? "Switch to dark mode"
195
- : "Switch to light mode"
196
- }
197
- >
198
- <ButtonBase
199
- onClick={handleThemeToggle}
200
- sx={(theme) => ({
201
- color: "text.secondary",
202
- borderRadius: "100%",
203
- padding: 0,
204
- width: "36px",
205
- height: "36px",
206
- display: "flex",
207
- alignItems: "center",
208
- justifyContent: "center",
209
- transition: "all 0.2s ease-in-out",
210
- "&:hover": {
211
- color: "text.primary",
212
- backgroundColor: alpha(
213
- theme.palette.text.primary,
214
- theme.palette.mode === "dark" ? 0.1 : 0.06
215
- ),
216
- },
217
- })}
218
- >
219
- {mode === "light" ? (
220
- <DarkModeOutlinedIcon sx={iconStyle} />
221
- ) : (
222
- <LightModeOutlinedIcon sx={iconStyle} />
223
- )}
224
- </ButtonBase>
225
- </Tooltip>
226
  </Box>
227
- ) : (
228
- <Box
229
- sx={{
230
- display: "flex",
231
- gap: 2.5,
232
- alignItems: "center",
233
- padding: "0.5rem 0",
234
- }}
235
  >
236
- <Box sx={{ display: "flex", gap: 2.5, alignItems: "center" }}>
237
- <Link
238
- to="/"
239
- style={{ textDecoration: "none" }}
240
- sx={linkStyle(location.pathname === "/")}
241
- >
242
- <Box sx={linkStyle(location.pathname === "/")}>Explorer</Box>
243
- </Link>
244
- <Link
245
- to="/submit"
246
- style={{ textDecoration: "none" }}
247
- sx={linkStyle(location.pathname === "/submit")}
248
- >
249
- <Box sx={linkStyle(location.pathname === "/submit")}>
250
- How to submit?
251
- </Box>
252
- </Link>
253
- <MuiLink
254
- href="https://huggingface.co/docs/leaderboards/open_llm_leaderboard/about"
255
- target="_blank"
256
- rel="noopener noreferrer"
257
- sx={{
258
- ...linkStyle(),
259
- "& svg": {
260
- fontSize: "0.75rem",
261
- ml: 0.5,
262
- opacity: 0.6,
263
- transition: "opacity 0.2s ease-in-out",
264
- },
265
- "&:hover svg": {
266
- opacity: 0.8,
267
- },
268
- }}
269
- >
270
- About
271
- <OpenInNewIcon />
272
- </MuiLink>
273
- </Box>
274
- <Separator />
275
- <Tooltip
276
- title={
277
- mode === "light"
278
- ? "Switch to dark mode"
279
- : "Switch to light mode"
280
- }
281
  >
282
- <ButtonBase
283
- onClick={handleThemeToggle}
284
- sx={(theme) => ({
285
- color: "text.secondary",
286
- borderRadius: "100%",
287
- padding: 0,
288
- width: "36px",
289
- height: "36px",
290
- display: "flex",
291
- alignItems: "center",
292
- justifyContent: "center",
293
- transition: "all 0.2s ease-in-out",
294
- "&:hover": {
295
- color: "text.primary",
296
- backgroundColor: alpha(
297
- theme.palette.text.primary,
298
- theme.palette.mode === "dark" ? 0.1 : 0.06
299
- ),
300
- },
301
- })}
302
- >
303
- {mode === "light" ? (
304
- <DarkModeOutlinedIcon sx={iconStyle} />
305
- ) : (
306
- <LightModeOutlinedIcon sx={iconStyle} />
307
- )}
308
- </ButtonBase>
309
- </Tooltip>
310
- </Box>
311
- )}
312
  </Toolbar>
313
  </AppBar>
314
  );
 
4
  Toolbar,
5
  Box,
6
  Link as MuiLink,
 
 
7
  ButtonBase,
8
+ Tooltip,
 
 
 
 
9
  } from "@mui/material";
10
  import { alpha } from "@mui/material/styles";
11
  import OpenInNewIcon from "@mui/icons-material/OpenInNew";
12
  import LightModeOutlinedIcon from "@mui/icons-material/LightModeOutlined";
13
  import DarkModeOutlinedIcon from "@mui/icons-material/DarkModeOutlined";
 
14
  import { Link, useLocation } from "react-router-dom";
15
 
16
  const Navigation = ({ onToggleTheme, mode }) => {
 
 
 
17
  const [hasChanged, setHasChanged] = useState(false);
18
  const location = useLocation();
19
 
 
89
  />
90
  );
91
 
 
 
 
 
 
 
 
 
92
  return (
93
  <AppBar
94
  position="static"
 
98
  }}
99
  >
100
  <Toolbar sx={{ justifyContent: "center" }}>
101
+ <Box
102
+ sx={{
103
+ display: "flex",
104
+ gap: 2.5,
105
+ alignItems: "center",
106
+ padding: "0.5rem 0",
107
+ }}
108
+ >
109
+ <Box sx={{ display: "flex", gap: 2.5, alignItems: "center" }}>
110
+ <Link
111
+ to="/"
112
+ style={{ textDecoration: "none" }}
113
+ sx={linkStyle(location.pathname === "/")}
114
  >
115
+ <Box sx={linkStyle(location.pathname === "/")}>Explorer</Box>
116
+ </Link>
117
+ <Link
118
+ to="/submit"
119
+ style={{ textDecoration: "none" }}
120
+ sx={linkStyle(location.pathname === "/submit")}
121
+ >
122
+ <Box sx={linkStyle(location.pathname === "/submit")}>
123
+ How to submit
124
+ </Box>
125
+ </Link>
126
+ <Separator />
127
+ <MuiLink
128
+ href="https://huggingface.co/docs/leaderboards/open_llm_leaderboard/about"
129
+ target="_blank"
130
+ rel="noopener noreferrer"
131
+ sx={{
132
+ ...linkStyle(),
133
+ "& svg": {
134
+ fontSize: "0.75rem",
135
+ ml: 0.5,
136
+ opacity: 0.6,
137
+ transition: "opacity 0.2s ease-in-out",
138
+ },
139
+ "&:hover svg": {
140
+ opacity: 0.8,
141
  },
142
  }}
143
  >
144
+ About
145
+ <OpenInNewIcon />
146
+ </MuiLink>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
147
  </Box>
148
+ <Separator />
149
+ <Tooltip
150
+ title={
151
+ mode === "light" ? "Switch to dark mode" : "Switch to light mode"
152
+ }
 
 
 
153
  >
154
+ <ButtonBase
155
+ onClick={handleThemeToggle}
156
+ sx={(theme) => ({
157
+ color: "text.secondary",
158
+ borderRadius: "100%",
159
+ padding: 0,
160
+ width: "36px",
161
+ height: "36px",
162
+ display: "flex",
163
+ alignItems: "center",
164
+ justifyContent: "center",
165
+ transition: "all 0.2s ease-in-out",
166
+ "&:hover": {
167
+ color: "text.primary",
168
+ backgroundColor: alpha(
169
+ theme.palette.text.primary,
170
+ theme.palette.mode === "dark" ? 0.1 : 0.06
171
+ ),
172
+ },
173
+ })}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
  >
175
+ {mode === "light" ? (
176
+ <DarkModeOutlinedIcon sx={iconStyle} />
177
+ ) : (
178
+ <LightModeOutlinedIcon sx={iconStyle} />
179
+ )}
180
+ </ButtonBase>
181
+ </Tooltip>
182
+ </Box>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
  </Toolbar>
184
  </AppBar>
185
  );
client/src/context/LeaderboardContext.jsx ADDED
@@ -0,0 +1,270 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, {
2
+ createContext,
3
+ useContext,
4
+ useState,
5
+ useCallback,
6
+ useMemo,
7
+ } from "react";
8
+
9
+ const LeaderboardContext = createContext();
10
+
11
+ export const LeaderboardProvider = ({ children }) => {
12
+ const [leaderboards, setLeaderboards] = useState([]);
13
+ const [searchQuery, setSearchQuery] = useState("");
14
+ const [arenaOnly, setArenaOnly] = useState(false);
15
+
16
+ // Calculate total number of unique leaderboards (excluding duplicates)
17
+ const totalLeaderboards = useMemo(() => {
18
+ const uniqueIds = new Set(leaderboards.map((board) => board.id));
19
+ return uniqueIds.size;
20
+ }, [leaderboards]);
21
+
22
+ // Filter functions for categories
23
+ const filterByTag = useCallback((tag, boards) => {
24
+ return (
25
+ boards?.filter(
26
+ (board) =>
27
+ board.tags?.includes(tag) || board.consolidated_tags?.includes(tag)
28
+ ) || []
29
+ );
30
+ }, []);
31
+
32
+ const filterByLanguage = useCallback((boards) => {
33
+ return (
34
+ boards?.filter(
35
+ (board) =>
36
+ board.tags?.some((tag) => tag.startsWith("language:")) ||
37
+ board.consolidated_tags?.some((tag) => tag.startsWith("language:"))
38
+ ) || []
39
+ );
40
+ }, []);
41
+
42
+ const filterByVision = useCallback((boards) => {
43
+ return (
44
+ boards?.filter(
45
+ (board) =>
46
+ board.tags?.some(
47
+ (tag) => tag === "modality:video" || tag === "modality:image"
48
+ ) ||
49
+ board.consolidated_tags?.some(
50
+ (tag) => tag === "modality:video" || tag === "modality:image"
51
+ )
52
+ ) || []
53
+ );
54
+ }, []);
55
+
56
+ const filterUncategorized = useCallback((boards) => {
57
+ const categorizedTags = [
58
+ "eval:code",
59
+ "eval:math",
60
+ "modality:video",
61
+ "modality:image",
62
+ "modality:audio",
63
+ "modality:tools",
64
+ "domain:financial",
65
+ "domain:medical",
66
+ "domain:legal",
67
+ ];
68
+
69
+ return (
70
+ boards?.filter((board) => {
71
+ if (
72
+ (!board.tags || board.tags.length === 0) &&
73
+ (!board.consolidated_tags || board.consolidated_tags.length === 0)
74
+ ) {
75
+ return true;
76
+ }
77
+
78
+ const hasNoTagsInCategory = !board.tags?.some(
79
+ (tag) => categorizedTags.includes(tag) || tag.startsWith("language:")
80
+ );
81
+ const hasNoConsolidatedTagsInCategory = !board.consolidated_tags?.some(
82
+ (tag) => categorizedTags.includes(tag) || tag.startsWith("language:")
83
+ );
84
+
85
+ return hasNoTagsInCategory && hasNoConsolidatedTagsInCategory;
86
+ }) || []
87
+ );
88
+ }, []);
89
+
90
+ // Define sections
91
+ const allSections = useMemo(() => {
92
+ if (!leaderboards) return [];
93
+
94
+ return [
95
+ {
96
+ id: "code",
97
+ title: "Code",
98
+ data: filterByTag("eval:code", leaderboards),
99
+ },
100
+ {
101
+ id: "math",
102
+ title: "Math",
103
+ data: filterByTag("eval:math", leaderboards),
104
+ },
105
+ {
106
+ id: "language",
107
+ title: "Language Specific",
108
+ data: filterByLanguage(leaderboards),
109
+ },
110
+ { id: "vision", title: "Vision", data: filterByVision(leaderboards) },
111
+ {
112
+ id: "audio",
113
+ title: "Audio",
114
+ data: filterByTag("modality:audio", leaderboards),
115
+ },
116
+ {
117
+ id: "agentic",
118
+ title: "Agentic",
119
+ data: filterByTag("modality:tools", leaderboards),
120
+ },
121
+ {
122
+ id: "financial",
123
+ title: "Financial",
124
+ data: filterByTag("domain:financial", leaderboards),
125
+ },
126
+ {
127
+ id: "medical",
128
+ title: "Medical",
129
+ data: filterByTag("domain:medical", leaderboards),
130
+ },
131
+ {
132
+ id: "legal",
133
+ title: "Legal",
134
+ data: filterByTag("domain:legal", leaderboards),
135
+ },
136
+ {
137
+ id: "uncategorized",
138
+ title: "Uncategorized",
139
+ data: filterUncategorized(leaderboards),
140
+ },
141
+ ];
142
+ }, [
143
+ leaderboards,
144
+ filterByTag,
145
+ filterByLanguage,
146
+ filterByVision,
147
+ filterUncategorized,
148
+ ]);
149
+
150
+ // Get sections with data
151
+ const sections = useMemo(() => {
152
+ return allSections.filter((section) => section.data.length > 0);
153
+ }, [allSections]);
154
+
155
+ // Filter leaderboards based on search query and arena toggle
156
+ const filterLeaderboards = useCallback(
157
+ (boards) => {
158
+ if (!boards) return [];
159
+
160
+ let filtered = [...boards];
161
+
162
+ // Filter by search query
163
+ if (searchQuery) {
164
+ const query = searchQuery.toLowerCase();
165
+ const searchableTagPrefixes = [
166
+ "domain:",
167
+ "language:",
168
+ "judge:",
169
+ "test:",
170
+ "modality:",
171
+ "submission:",
172
+ ];
173
+
174
+ filtered = filtered.filter((board) => {
175
+ const isTagSearch = searchableTagPrefixes.some((prefix) =>
176
+ query.startsWith(prefix)
177
+ );
178
+
179
+ if (isTagSearch) {
180
+ return (
181
+ board.tags?.some((tag) => tag.toLowerCase().includes(query)) ||
182
+ board.consolidated_tags?.some((tag) =>
183
+ tag.toLowerCase().includes(query)
184
+ )
185
+ );
186
+ }
187
+
188
+ return board.card_data?.title?.toLowerCase().includes(query);
189
+ });
190
+ }
191
+
192
+ // Filter arena only
193
+ if (arenaOnly) {
194
+ filtered = filtered.filter(
195
+ (board) =>
196
+ board.tags?.includes("judge:humans") ||
197
+ board.consolidated_tags?.includes("judge:humans")
198
+ );
199
+ }
200
+
201
+ return filtered;
202
+ },
203
+ [searchQuery, arenaOnly]
204
+ );
205
+
206
+ // Get filtered count
207
+ const filteredCount = useMemo(() => {
208
+ return filterLeaderboards(leaderboards).length;
209
+ }, [filterLeaderboards, leaderboards]);
210
+
211
+ // Function to get highlighted parts of text
212
+ const getHighlightedText = useCallback((text, searchTerm) => {
213
+ if (!searchTerm || !text) return { text, shouldHighlight: false };
214
+
215
+ const query = searchTerm.toLowerCase();
216
+ const searchableTagPrefixes = [
217
+ "domain:",
218
+ "language:",
219
+ "judge:",
220
+ "test:",
221
+ "modality:",
222
+ "submission:",
223
+ ];
224
+
225
+ // Si c'est une recherche par tag, on ne highlight rien
226
+ if (searchableTagPrefixes.some((prefix) => query.startsWith(prefix))) {
227
+ return { text, shouldHighlight: false };
228
+ }
229
+
230
+ // Sinon on highlight les parties qui matchent
231
+ const index = text.toLowerCase().indexOf(query);
232
+ if (index === -1) return { text, shouldHighlight: false };
233
+
234
+ return {
235
+ text,
236
+ shouldHighlight: true,
237
+ highlightStart: index,
238
+ highlightEnd: index + query.length,
239
+ };
240
+ }, []);
241
+
242
+ const value = {
243
+ leaderboards,
244
+ setLeaderboards,
245
+ searchQuery,
246
+ setSearchQuery,
247
+ arenaOnly,
248
+ setArenaOnly,
249
+ totalLeaderboards,
250
+ filteredCount,
251
+ filterLeaderboards,
252
+ sections,
253
+ allSections,
254
+ getHighlightedText,
255
+ };
256
+
257
+ return (
258
+ <LeaderboardContext.Provider value={value}>
259
+ {children}
260
+ </LeaderboardContext.Provider>
261
+ );
262
+ };
263
+
264
+ export const useLeaderboard = () => {
265
+ const context = useContext(LeaderboardContext);
266
+ if (!context) {
267
+ throw new Error("useLeaderboard must be used within a LeaderboardProvider");
268
+ }
269
+ return context;
270
+ };
client/src/hooks/useDebounce.js ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useState, useEffect } from "react";
2
+
3
+ export const useDebounce = (value, delay = 200) => {
4
+ const [debouncedValue, setDebouncedValue] = useState(value);
5
+
6
+ useEffect(() => {
7
+ const timer = setTimeout(() => {
8
+ setDebouncedValue(value);
9
+ }, delay);
10
+
11
+ return () => {
12
+ clearTimeout(timer);
13
+ };
14
+ }, [value, delay]);
15
+
16
+ return debouncedValue;
17
+ };
client/src/pages/HowToSubmitPage/HowToSubmitPage.jsx CHANGED
@@ -10,6 +10,28 @@ import {
10
  } from "@mui/material";
11
  import PageHeader from "../../components/PageHeader/PageHeader";
12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  const Section = ({ title, children }) => (
14
  <Paper
15
  elevation={0}
@@ -83,9 +105,8 @@ const TagSection = ({ title, description, tags, explanations }) => (
83
  component="span"
84
  variant="body2"
85
  sx={{ color: "text.secondary", ml: 1 }}
86
- >
87
- {explanations[index]}
88
- </Typography>
89
  )}
90
  </Box>
91
  ))}
@@ -117,30 +138,154 @@ const HowToSubmitPage = () => {
117
  title="How to Submit"
118
  subtitle={
119
  <>
120
- Learn how to <span style={{ fontWeight: 600 }}>be listed</span> on
121
- the explorer
122
  </>
123
  }
124
  />
125
 
126
  <Section title="How to submit your leaderboard?">
127
- <Stack spacing={2}>
128
- <Typography variant="body1">
129
- Your space will appear automatically in the lists if it contains
130
- correct metadata!
131
- </Typography>
132
- <Typography variant="body1">
133
- Make sure to either use the tag leaderboard or arena to your space,
134
- by adding the following to your README:
135
- </Typography>
136
- <CodeBlock>
137
- tags:
138
- <br />
139
- &nbsp;&nbsp;- leaderboard
140
- </CodeBlock>
141
- <Typography variant="body1">
142
- then any other tags of interest to you.
143
  </Typography>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
  </Stack>
145
  </Section>
146
 
@@ -155,10 +300,10 @@ const HowToSubmitPage = () => {
155
  "submission:closed",
156
  ]}
157
  explanations={[
158
- "users can submit their models as such to the leaderboard, and evaluation is run automatically without human intervention",
159
- "the leaderboard requires the model owner to run evaluations on his side and submit the results",
160
- "the leaderboard requires the leaderboard owner to run evaluations for new submissions",
161
- "the leaderboard does not accept submissions at the moment",
162
  ]}
163
  />
164
 
@@ -169,10 +314,10 @@ const HowToSubmitPage = () => {
169
  description="Arenas are not concerned by this category."
170
  tags={["test:public", "test:mix", "test:private", "test:rolling"]}
171
  explanations={[
172
- "all the test sets used are public, the evaluations are completely reproducible",
173
- "some test sets are public and some private",
174
- "all the test sets used are private, the evaluations are hard to game",
175
- "the test sets used change regularly through time and evaluation scores are refreshed",
176
  ]}
177
  />
178
 
@@ -181,16 +326,16 @@ const HowToSubmitPage = () => {
181
  <TagSection
182
  title="Judges"
183
  tags={[
184
- "judge:auto",
185
  "judge:model",
186
  "judge:humans",
187
  "judge:vibe_check",
188
  ]}
189
  explanations={[
190
- "evaluations are run automatically, using an evaluation suite such as lm_eval or lighteval",
191
- "evaluations are run using a model as a judge approach to rate answer",
192
- "evaluations are done by humans to rate answer - this is an arena",
193
- "evaluations are done manually by one or several humans",
194
  ]}
195
  />
196
 
@@ -212,8 +357,8 @@ const HowToSubmitPage = () => {
212
  "",
213
  "",
214
  "",
215
- "requires added tool usage - mostly for assistant models (a bit outside of usual modalities)",
216
- "the leaderboard concerns itself with machine learning artefacts as themselves, for example, quality evaluation of text embeddings (a bit outside of usual modalities)",
217
  ]}
218
  />
219
 
@@ -228,13 +373,15 @@ const HowToSubmitPage = () => {
228
  "eval:code",
229
  "eval:performance",
230
  "eval:safety",
 
231
  ]}
232
  explanations={[
233
- "the evaluation looks at generation capabilities specifically (can be image generation, text generation, ...)",
234
- "the evaluation tests math abilities",
235
- "the evaluation tests coding capabilities",
236
- "model performance (speed, energy consumption, ...)",
237
- "the evaluation considers safety, toxicity, bias",
 
238
  ]}
239
  />
240
 
@@ -280,8 +427,6 @@ const HowToSubmitPage = () => {
280
  </Link>{" "}
281
  on Hugging Face.
282
  </Typography>
283
-
284
- <Divider sx={{ my: 3 }} />
285
  </Section>
286
  </Box>
287
  );
 
10
  } from "@mui/material";
11
  import PageHeader from "../../components/PageHeader/PageHeader";
12
 
13
+ const StepNumber = ({ number }) => (
14
+ <Box
15
+ sx={{
16
+ width: 32,
17
+ height: 32,
18
+ borderRadius: "50%",
19
+ display: "flex",
20
+ alignItems: "center",
21
+ justifyContent: "center",
22
+ border: "1px solid",
23
+ borderColor: "primary.main",
24
+ color: "primary.main",
25
+ fontSize: "0.875rem",
26
+ fontWeight: 600,
27
+ flexShrink: 0,
28
+ bgcolor: "transparent",
29
+ }}
30
+ >
31
+ {number}
32
+ </Box>
33
+ );
34
+
35
  const Section = ({ title, children }) => (
36
  <Paper
37
  elevation={0}
 
105
  component="span"
106
  variant="body2"
107
  sx={{ color: "text.secondary", ml: 1 }}
108
+ dangerouslySetInnerHTML={{ __html: explanations[index] }}
109
+ />
 
110
  )}
111
  </Box>
112
  ))}
 
138
  title="How to Submit"
139
  subtitle={
140
  <>
141
+ How to <span style={{ fontWeight: 600 }}>be a part</span> of{" "}
142
+ <span style={{ fontWeight: 600 }}>"leaderboards on the Hub"</span>
143
  </>
144
  }
145
  />
146
 
147
  <Section title="How to submit your leaderboard?">
148
+ <Stack spacing={4}>
149
+ <Typography variant="body1" color="text.secondary">
150
+ To be listed on this explorer, your leaderboard must be hosted on a
151
+ Hugging Face Space. Follow these steps to make your Space
152
+ discoverable:
 
 
 
 
 
 
 
 
 
 
 
153
  </Typography>
154
+
155
+ <Box
156
+ sx={{
157
+ display: "flex",
158
+ flexDirection: { xs: "column", md: "row" },
159
+ gap: 4,
160
+ position: "relative",
161
+ }}
162
+ >
163
+ <Box sx={{ flex: 1 }}>
164
+ <Stack spacing={3}>
165
+ <Stack direction="row" spacing={2} alignItems="center">
166
+ <StepNumber number={1} />
167
+ <Typography
168
+ variant="subtitle1"
169
+ sx={{
170
+ fontWeight: 600,
171
+ color: "text.primary",
172
+ letterSpacing: "-0.01em",
173
+ }}
174
+ >
175
+ Is it an Arena or a Leaderboard ?
176
+ </Typography>
177
+ </Stack>
178
+ <Box sx={{ pl: 7 }}>
179
+ <Stack spacing={2}>
180
+ <Typography variant="body2" color="text.secondary">
181
+ Add either the <strong>leaderboard</strong> or{" "}
182
+ <strong>arena</strong> tag to your README.md:
183
+ </Typography>
184
+ <CodeBlock>
185
+ tags:
186
+ <br />
187
+ &nbsp;&nbsp;- leaderboard # or arena
188
+ </CodeBlock>
189
+ </Stack>
190
+ </Box>
191
+ </Stack>
192
+ </Box>
193
+
194
+ <Divider
195
+ orientation="vertical"
196
+ flexItem
197
+ sx={{
198
+ display: { xs: "none", md: "block" },
199
+ }}
200
+ />
201
+
202
+ <Box sx={{ flex: 1 }}>
203
+ <Stack spacing={3}>
204
+ <Stack direction="row" spacing={2} alignItems="center">
205
+ <StepNumber number={2} />
206
+ <Typography
207
+ variant="subtitle1"
208
+ sx={{
209
+ fontWeight: 600,
210
+ color: "text.primary",
211
+ letterSpacing: "-0.01em",
212
+ }}
213
+ >
214
+ Describe it briefly
215
+ </Typography>
216
+ </Stack>
217
+ <Box sx={{ pl: 7 }}>
218
+ <Stack spacing={2}>
219
+ <Typography variant="body2" color="text.secondary">
220
+ Include a <strong>short_description</strong> field in your
221
+ README.md.
222
+ </Typography>
223
+ <CodeBlock>
224
+ short_description: Evaluating LLMs on math reasoning tasks
225
+ </CodeBlock>
226
+ </Stack>
227
+ </Box>
228
+ </Stack>
229
+ </Box>
230
+ </Box>
231
+
232
+ <Divider />
233
+
234
+ <Box>
235
+ <Stack spacing={3}>
236
+ <Stack direction="row" spacing={2} alignItems="center">
237
+ <StepNumber number={3} />
238
+ <Typography
239
+ variant="subtitle1"
240
+ sx={{
241
+ fontWeight: 600,
242
+ color: "text.primary",
243
+ letterSpacing: "-0.01em",
244
+ }}
245
+ >
246
+ Add relevant tags
247
+ </Typography>
248
+ </Stack>
249
+ <Box sx={{ pl: 7 }}>
250
+ <Stack spacing={2}>
251
+ <Typography variant="body2" color="text.secondary">
252
+ Add relevant tags from these categories to help users find
253
+ and understand your leaderboard.
254
+ </Typography>
255
+ <CodeBlock>
256
+ tags:
257
+ <br />
258
+ &nbsp;&nbsp;- leaderboard
259
+ <br />
260
+ &nbsp;&nbsp;- submission:automatic &nbsp;&nbsp;# How models
261
+ are submitted
262
+ <br />
263
+ &nbsp;&nbsp;- test:public
264
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#
265
+ Test set visibility
266
+ <br />
267
+ &nbsp;&nbsp;- judge:function
268
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#
269
+ Evaluation method
270
+ <br />
271
+ &nbsp;&nbsp;- modality:text
272
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#
273
+ Input/output type
274
+ <br />
275
+ &nbsp;&nbsp;- language:english
276
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# Language coverage
277
+ <br />
278
+ &nbsp;&nbsp;- domain:financial
279
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# Specific domain
280
+ </CodeBlock>
281
+ <Typography variant="body2" color="text.secondary">
282
+ See the complete list of available tags and their meanings
283
+ below.
284
+ </Typography>
285
+ </Stack>
286
+ </Box>
287
+ </Stack>
288
+ </Box>
289
  </Stack>
290
  </Section>
291
 
 
300
  "submission:closed",
301
  ]}
302
  explanations={[
303
+ "users can submit their models as such to the leaderboard, and evaluation is run <strong>automatically</strong> without human intervention",
304
+ "the leaderboard requires the <strong>model owner</strong> to run evaluations on his side and submit the results",
305
+ "the leaderboard requires the <strong>leaderboard owner</strong> to run evaluations for new submissions",
306
+ "the leaderboard <strong>does not accept</strong> submissions at the moment",
307
  ]}
308
  />
309
 
 
314
  description="Arenas are not concerned by this category."
315
  tags={["test:public", "test:mix", "test:private", "test:rolling"]}
316
  explanations={[
317
+ "all the test sets used are <strong>public</strong>, the evaluations are completely <strong>reproducible</strong>",
318
+ "some test sets are <strong>public</strong> and some <strong>private</strong>",
319
+ "all the test sets used are <strong>private</strong>, the evaluations are hard to game",
320
+ "the test sets used <strong>change regularly</strong> through time and evaluation scores are refreshed",
321
  ]}
322
  />
323
 
 
326
  <TagSection
327
  title="Judges"
328
  tags={[
329
+ "judge:function",
330
  "judge:model",
331
  "judge:humans",
332
  "judge:vibe_check",
333
  ]}
334
  explanations={[
335
+ "evaluations are run <strong>automatically</strong>, using an evaluation suite such as <strong>lm_eval</strong> or <strong>lighteval</strong>",
336
+ "evaluations are run using a <strong>model as a judge</strong> approach to rate answer",
337
+ "evaluations are <strong>done by humans</strong> to rate answer - <strong>this is an arena</strong>",
338
+ "evaluations are <strong>done manually</strong> by one or several humans",
339
  ]}
340
  />
341
 
 
357
  "",
358
  "",
359
  "",
360
+ "requires added <strong>tool usage</strong> - mostly for <strong>assistant models</strong> (a bit outside of usual modalities)",
361
+ "the leaderboard concerns itself with <strong>machine learning artefacts</strong> as themselves, for example, quality evaluation of <strong>text embeddings</strong> (a bit outside of usual modalities)",
362
  ]}
363
  />
364
 
 
373
  "eval:code",
374
  "eval:performance",
375
  "eval:safety",
376
+ "task:rag",
377
  ]}
378
  explanations={[
379
+ "the evaluation looks at <strong>generation capabilities</strong> specifically (can be image generation, text generation, ...)",
380
+ "the evaluation tests <strong>math abilities</strong>",
381
+ "the evaluation tests <strong>coding capabilities</strong>",
382
+ "model <strong>performance</strong> (speed, energy consumption, ...)",
383
+ "the evaluation considers <strong>safety</strong>, <strong>toxicity</strong>, <strong>bias</strong>",
384
+ "the evaluation tests <strong>RAG</strong> (Retrieval-Augmented Generation) capabilities",
385
  ]}
386
  />
387
 
 
427
  </Link>{" "}
428
  on Hugging Face.
429
  </Typography>
 
 
430
  </Section>
431
  </Box>
432
  );
client/src/pages/LeaderboardPage/LeaderboardPage.jsx CHANGED
@@ -6,17 +6,26 @@ import {
6
  Button,
7
  FormControlLabel,
8
  Switch,
 
 
 
9
  } from "@mui/material";
 
10
  import Logo from "../../components/Logo/Logo";
11
  import PageHeader from "../../components/PageHeader/PageHeader";
12
  import LeaderboardSection from "../../components/LeaderboardSection";
 
13
  import { alpha } from "@mui/material/styles";
14
  import API_URLS from "../../config/api";
 
 
 
 
15
 
16
- const LeaderboardPage = () => {
17
- const [leaderboards, setLeaderboards] = useState(null);
18
  const [loading, setLoading] = useState(true);
19
- const [arenaOnly, setArenaOnly] = useState(false);
 
20
 
21
  useEffect(() => {
22
  fetch(API_URLS.leaderboards)
@@ -32,67 +41,7 @@ const LeaderboardPage = () => {
32
  console.error("Error fetching leaderboards:", error);
33
  setLoading(false);
34
  });
35
- }, []);
36
-
37
- if (!leaderboards && !loading) {
38
- return null;
39
- }
40
-
41
- const filterByTag = (tag) => {
42
- let filtered =
43
- leaderboards?.filter((leaderboard) =>
44
- leaderboard.consolidated_tags?.includes(tag)
45
- ) || [];
46
-
47
- if (arenaOnly) {
48
- filtered = filtered.filter((leaderboard) =>
49
- leaderboard.consolidated_tags?.includes("judge:humans")
50
- );
51
- }
52
-
53
- return filtered.sort((a, b) => (b.likes || 0) - (a.likes || 0));
54
- };
55
-
56
- const filterByLanguage = () => {
57
- let filtered =
58
- leaderboards?.filter((leaderboard) =>
59
- leaderboard.consolidated_tags?.some((tag) =>
60
- tag.startsWith("language:")
61
- )
62
- ) || [];
63
-
64
- if (arenaOnly) {
65
- filtered = filtered.filter((leaderboard) =>
66
- leaderboard.consolidated_tags?.includes("judge:humans")
67
- );
68
- }
69
-
70
- return filtered.sort((a, b) => (b.likes || 0) - (a.likes || 0));
71
- };
72
-
73
- const codeLeaderboards = filterByTag("eval:code");
74
- const mathLeaderboards = filterByTag("eval:math");
75
- const languageLeaderboards = filterByLanguage();
76
- const videoLeaderboards = filterByTag("modality:video");
77
- const audioLeaderboards = filterByTag("modality:audio");
78
- const agenticLeaderboards = filterByTag("modality:tools");
79
- const financialLeaderboards = filterByTag("domain:financial");
80
- const medicalLeaderboards = filterByTag("domain:medical");
81
- const legalLeaderboards = filterByTag("domain:legal");
82
-
83
- const allSections = [
84
- { id: "code", title: "Code", data: codeLeaderboards },
85
- { id: "math", title: "Math", data: mathLeaderboards },
86
- { id: "language", title: "Language Specific", data: languageLeaderboards },
87
- { id: "video", title: "Vision language model", data: videoLeaderboards },
88
- { id: "audio", title: "Audio", data: audioLeaderboards },
89
- { id: "agentic", title: "Agentic", data: agenticLeaderboards },
90
- { id: "financial", title: "Financial", data: financialLeaderboards },
91
- { id: "medical", title: "Medical", data: medicalLeaderboards },
92
- { id: "legal", title: "Legal", data: legalLeaderboards },
93
- ];
94
-
95
- const sections = allSections.filter((section) => section.data.length > 0);
96
 
97
  return (
98
  <Box
@@ -108,17 +57,27 @@ const LeaderboardPage = () => {
108
  >
109
  <Logo />
110
  </Box>
111
- <PageHeader
112
- title="Leaderboards on the Hub"
113
- subtitle={
114
- <>
115
- <span style={{ fontWeight: 600 }}>Discover</span> and{" "}
116
- <span style={{ fontWeight: 600 }}>explore</span> all leaderboards
117
- from the{" "}
118
- <span style={{ fontWeight: 600 }}>Hugging Face community</span>
119
- </>
120
- }
121
- />
 
 
 
 
 
 
 
 
 
 
122
 
123
  {loading ? (
124
  <Box sx={{ display: "flex", justifyContent: "center", mt: 4 }}>
@@ -133,87 +92,14 @@ const LeaderboardPage = () => {
133
  mt: 4,
134
  }}
135
  >
136
- {/* Table of Contents with Arena Toggle */}
137
- <Box sx={{ display: "flex", alignItems: "center", mb: 4 }}>
138
- <Stack
139
- direction="row"
140
- spacing={1}
141
- sx={{
142
- flexWrap: "wrap",
143
- gap: 1,
144
- flex: 1,
145
- }}
146
- >
147
- {allSections.map(({ id, title, data }) => (
148
- <Button
149
- key={id}
150
- onClick={() => {
151
- if (data.length > 0) {
152
- document.getElementById(id)?.scrollIntoView({
153
- behavior: "smooth",
154
- block: "start",
155
- });
156
- }
157
- }}
158
- variant="text"
159
- size="small"
160
- disabled={data.length === 0}
161
- sx={{
162
- color:
163
- data.length === 0 ? "text.disabled" : "text.secondary",
164
- textTransform: "none",
165
- fontSize: "0.875rem",
166
- opacity: data.length === 0 ? 0.5 : 1,
167
- cursor: data.length === 0 ? "default" : "pointer",
168
- display: "flex",
169
- alignItems: "center",
170
- gap: 1,
171
- "&:hover": {
172
- backgroundColor:
173
- data.length === 0
174
- ? "transparent"
175
- : (theme) =>
176
- alpha(
177
- theme.palette.text.primary,
178
- theme.palette.mode === "dark" ? 0.1 : 0.06
179
- ),
180
- },
181
- }}
182
- >
183
- {title}
184
- <Box
185
- sx={(theme) => ({
186
- width: "3px",
187
- height: "3px",
188
- borderRadius: "100%",
189
- backgroundColor: alpha(
190
- theme.palette.text.primary,
191
- theme.palette.mode === "dark" ? 0.2 : 0.15
192
- ),
193
- opacity: data.length === 0 ? 0.5 : 1,
194
- })}
195
- />
196
- {data.length}
197
- </Button>
198
- ))}
199
- </Stack>
200
- <FormControlLabel
201
- control={
202
- <Switch
203
- checked={arenaOnly}
204
- onChange={(e) => setArenaOnly(e.target.checked)}
205
- size="small"
206
- />
207
- }
208
- label="Arena only"
209
- sx={{ ml: 2 }}
210
- />
211
- </Box>
212
 
213
- {/* Sections */}
214
  {sections.map(({ id, title, data }) => (
215
  <Box key={id} id={id}>
216
- <LeaderboardSection title={title} leaderboards={data} />
 
 
 
217
  </Box>
218
  ))}
219
  </Box>
@@ -222,4 +108,12 @@ const LeaderboardPage = () => {
222
  );
223
  };
224
 
 
 
 
 
 
 
 
 
225
  export default LeaderboardPage;
 
6
  Button,
7
  FormControlLabel,
8
  Switch,
9
+ TextField,
10
+ InputAdornment,
11
+ Typography,
12
  } from "@mui/material";
13
+ import SearchIcon from "@mui/icons-material/Search";
14
  import Logo from "../../components/Logo/Logo";
15
  import PageHeader from "../../components/PageHeader/PageHeader";
16
  import LeaderboardSection from "../../components/LeaderboardSection";
17
+ import LeaderboardFilters from "../../components/LeaderboardFilters/LeaderboardFilters";
18
  import { alpha } from "@mui/material/styles";
19
  import API_URLS from "../../config/api";
20
+ import {
21
+ LeaderboardProvider,
22
+ useLeaderboard,
23
+ } from "../../context/LeaderboardContext";
24
 
25
+ const LeaderboardPageContent = () => {
 
26
  const [loading, setLoading] = useState(true);
27
+ const { setLeaderboards, filterLeaderboards, sections, allSections } =
28
+ useLeaderboard();
29
 
30
  useEffect(() => {
31
  fetch(API_URLS.leaderboards)
 
41
  console.error("Error fetching leaderboards:", error);
42
  setLoading(false);
43
  });
44
+ }, [setLeaderboards]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
 
46
  return (
47
  <Box
 
57
  >
58
  <Logo />
59
  </Box>
60
+
61
+ <Box
62
+ sx={{
63
+ display: "flex",
64
+ flexDirection: "column",
65
+ alignItems: "center",
66
+ textAlign: "center",
67
+ mb: 0,
68
+ mt: 6,
69
+ gap: 2,
70
+ }}
71
+ >
72
+ <Typography fontWeight="bold" variant="h3" component="h1">
73
+ Leaderboards on the Hub
74
+ </Typography>
75
+ <Typography variant="h6" color="text.secondary">
76
+ <span style={{ fontWeight: 600 }}>Discover</span> and{" "}
77
+ <span style={{ fontWeight: 600 }}>explore</span> all leaderboards from
78
+ the <span style={{ fontWeight: 600 }}>Hugging Face community</span>
79
+ </Typography>
80
+ </Box>
81
 
82
  {loading ? (
83
  <Box sx={{ display: "flex", justifyContent: "center", mt: 4 }}>
 
92
  mt: 4,
93
  }}
94
  >
95
+ <LeaderboardFilters allSections={allSections} />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
 
 
97
  {sections.map(({ id, title, data }) => (
98
  <Box key={id} id={id}>
99
+ <LeaderboardSection
100
+ title={title}
101
+ leaderboards={filterLeaderboards(data)}
102
+ />
103
  </Box>
104
  ))}
105
  </Box>
 
108
  );
109
  };
110
 
111
+ const LeaderboardPage = () => {
112
+ return (
113
+ <LeaderboardProvider>
114
+ <LeaderboardPageContent />
115
+ </LeaderboardProvider>
116
+ );
117
+ };
118
+
119
  export default LeaderboardPage;