fdeniz commited on
Commit
5491d8f
·
0 Parent(s):

Initial commit with current state

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +36 -0
  2. .gitignore +24 -0
  3. Dockerfile +9 -0
  4. README.md +11 -0
  5. docker-compose.yaml +16 -0
  6. index.html +13 -0
  7. package-lock.json +0 -0
  8. package.json +51 -0
  9. public/aiXamine-favicon.png +0 -0
  10. public/assets/aiXamine-logo-dark.png +3 -0
  11. public/assets/aiXamine-logo-light.png +3 -0
  12. public/assets/api-logo-dark.png +3 -0
  13. public/assets/api-logo-light.png +3 -0
  14. public/assets/claude-logo-dark.png +3 -0
  15. public/assets/claude-logo-light.png +3 -0
  16. public/assets/deepseek-logo-dark.png +3 -0
  17. public/assets/deepseek-logo-light.png +3 -0
  18. public/assets/fanar-logo-dark.png +3 -0
  19. public/assets/fanar-logo-light.png +3 -0
  20. public/assets/gemini-logo-dark.png +3 -0
  21. public/assets/gemini-logo-light.png +3 -0
  22. public/assets/grok-logo-dark.png +3 -0
  23. public/assets/grok-logo-light.png +3 -0
  24. public/assets/huggingface-logo.png +3 -0
  25. public/assets/openai-logo-dark.png +3 -0
  26. public/assets/openai-logo-light.png +3 -0
  27. public/assets/overview-dark.png +3 -0
  28. public/assets/overview-light.png +3 -0
  29. public/assets/report-dark.png +3 -0
  30. public/assets/report-light.png +3 -0
  31. public/assets/submission-dark.png +3 -0
  32. public/assets/submission-light.png +3 -0
  33. src/constants/theme.js +650 -0
  34. src/features/app/App.jsx +36 -0
  35. src/features/app/appSlice.js +92 -0
  36. src/features/landing/AppAppBar/AppAppBar-styles.js +60 -0
  37. src/features/landing/AppAppBar/AppAppBar.jsx +167 -0
  38. src/features/landing/FAQ/FAQ-styles.js +15 -0
  39. src/features/landing/FAQ/FAQ.jsx +96 -0
  40. src/features/landing/Features/Features-styles.js +91 -0
  41. src/features/landing/Features/Features.jsx +203 -0
  42. src/features/landing/Hero/Hero-styles.js +39 -0
  43. src/features/landing/Hero/Hero.jsx +39 -0
  44. src/features/landing/Landing/Landing.jsx +38 -0
  45. src/features/landing/Reference/Reference-styles.js +48 -0
  46. src/features/landing/Reference/Reference.jsx +38 -0
  47. src/features/landing/Services/Services-styles.js +36 -0
  48. src/features/landing/Services/Services.jsx +112 -0
  49. src/features/main/CompareModels/CompareModels-styles.js +282 -0
  50. src/features/main/CompareModels/CompareModelsFullDialog/CompareModelsFullDialog-styles.js +183 -0
.gitattributes ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ public/assets/* filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Logs
2
+ logs
3
+ *.log
4
+ npm-debug.log*
5
+ yarn-debug.log*
6
+ yarn-error.log*
7
+ pnpm-debug.log*
8
+ lerna-debug.log*
9
+
10
+ node_modules
11
+ dist
12
+ dist-ssr
13
+ *.local
14
+
15
+ # Editor directories and files
16
+ .vscode/*
17
+ !.vscode/extensions.json
18
+ .idea
19
+ .DS_Store
20
+ *.suo
21
+ *.ntvs*
22
+ *.njsproj
23
+ *.sln
24
+ *.sw?
Dockerfile ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ FROM node:22-alpine
2
+ WORKDIR /app
3
+ COPY . .
4
+ RUN npm install --silent
5
+
6
+ ENV VITE_API_BASE_URL=https://aixamine.qcri.org/api/v1
7
+ ENV VITE_AMPLITUDE_API_KEY=default_key
8
+
9
+ CMD ["npm", "run", "dev"]
README.md ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Leaderboard
3
+ emoji: 📈
4
+ colorFrom: indigo
5
+ colorTo: pink
6
+ sdk: docker
7
+ pinned: false
8
+ short_description: safety leaderboard
9
+ ---
10
+
11
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
docker-compose.yaml ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ services:
2
+ react:
3
+ build:
4
+ context: .
5
+ args:
6
+ - VITE_API_BASE_URL=https://aixamine.qcri.org/api/v1
7
+ - VITE_AMPLITUDE_API_KEY=${VITE_AMPLITUDE_API_KEY}
8
+ restart: always
9
+ ports:
10
+ - 7860:7860
11
+ volumes:
12
+ - ./:/app
13
+ - node-modules:/app/node_modules
14
+
15
+ volumes:
16
+ node-modules:
index.html ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en" style="height: 100%">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/png" href="/aiXamine-favicon.png" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>aiXamine Leaderboard</title>
8
+ </head>
9
+ <body style="height: 100%">
10
+ <div id="root" style="height: 100%"></div>
11
+ <script type="module" src="/src/index.jsx"></script>
12
+ </body>
13
+ </html>
package-lock.json ADDED
The diff for this file is too large to render. See raw diff
 
package.json ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "web",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10
+ "preview": "vite preview"
11
+ },
12
+ "dependencies": {
13
+ "@emotion/react": "^11.11.4",
14
+ "@emotion/styled": "^11.11.5",
15
+ "@fontsource/inter": "^5.0.18",
16
+ "@huggingface/hub": "^0.15.1",
17
+ "@mui/icons-material": "^5.15.19",
18
+ "@mui/material": "^5.15.18",
19
+ "@mui/x-charts": "^7.11.0",
20
+ "@mui/x-data-grid": "^7.10.0",
21
+ "@reduxjs/toolkit": "^2.2.5",
22
+ "amplitude-js": "^8.21.9",
23
+ "axios": "^1.7.2",
24
+ "chart.js": "^4.4.4",
25
+ "dompurify": "^3.2.7",
26
+ "formik": "^2.4.6",
27
+ "html2pdf.js": "^0.10.3",
28
+ "jspdf": "^3.0.1",
29
+ "jwt-decode": "^4.0.0",
30
+ "marked": "^15.0.6",
31
+ "prop-types": "^15.8.1",
32
+ "react": "^18.2.0",
33
+ "react-chartjs-2": "^5.2.0",
34
+ "react-dom": "^18.2.0",
35
+ "react-helmet-async": "^2.0.5",
36
+ "react-redux": "^9.1.2",
37
+ "react-router-dom": "^6.23.1",
38
+ "react-syntax-highlighter": "^15.5.0",
39
+ "yup": "^1.4.0"
40
+ },
41
+ "devDependencies": {
42
+ "@types/react": "^18.2.66",
43
+ "@types/react-dom": "^18.2.22",
44
+ "@vitejs/plugin-react-swc": "^3.5.0",
45
+ "eslint": "^8.57.0",
46
+ "eslint-plugin-react": "^7.34.1",
47
+ "eslint-plugin-react-hooks": "^4.6.0",
48
+ "eslint-plugin-react-refresh": "^0.4.6",
49
+ "vite": "^5.2.0"
50
+ }
51
+ }
public/aiXamine-favicon.png ADDED
public/assets/aiXamine-logo-dark.png ADDED

Git LFS Details

  • SHA256: 481d0c17c9ce86be777422c7bdab27435163a3539895501e030a1bc108c435c4
  • Pointer size: 130 Bytes
  • Size of remote file: 41.4 kB
public/assets/aiXamine-logo-light.png ADDED

Git LFS Details

  • SHA256: d3346f88220904b84894e452fd628e7bbde16d990b7679cb002f6a728c813bd2
  • Pointer size: 130 Bytes
  • Size of remote file: 46.4 kB
public/assets/api-logo-dark.png ADDED

Git LFS Details

  • SHA256: 14e7e2c0e38d650732558cc31aa16ec45b89af0754e30ab21c3ae51e8a2b5509
  • Pointer size: 130 Bytes
  • Size of remote file: 27.9 kB
public/assets/api-logo-light.png ADDED

Git LFS Details

  • SHA256: 95b95bb6e91ca37a4dd94d9b1cf19b375df3a110462ccf61e23ef24ed019848c
  • Pointer size: 130 Bytes
  • Size of remote file: 25.8 kB
public/assets/claude-logo-dark.png ADDED

Git LFS Details

  • SHA256: 6021f0871c276f09c6b1e2d2c573ba79b491c3404e8a724e8d0e612ea414238a
  • Pointer size: 131 Bytes
  • Size of remote file: 128 kB
public/assets/claude-logo-light.png ADDED

Git LFS Details

  • SHA256: 6021f0871c276f09c6b1e2d2c573ba79b491c3404e8a724e8d0e612ea414238a
  • Pointer size: 131 Bytes
  • Size of remote file: 128 kB
public/assets/deepseek-logo-dark.png ADDED

Git LFS Details

  • SHA256: 0d19ca270228362efa48b6b7280d90b11b0e4b7c24051efee543e74a04c57476
  • Pointer size: 130 Bytes
  • Size of remote file: 43.3 kB
public/assets/deepseek-logo-light.png ADDED

Git LFS Details

  • SHA256: 0d19ca270228362efa48b6b7280d90b11b0e4b7c24051efee543e74a04c57476
  • Pointer size: 130 Bytes
  • Size of remote file: 43.3 kB
public/assets/fanar-logo-dark.png ADDED

Git LFS Details

  • SHA256: a49bcc31705224cf6772c5a4a523d5beef6c464e7e162e2033b72b5592249546
  • Pointer size: 130 Bytes
  • Size of remote file: 18.8 kB
public/assets/fanar-logo-light.png ADDED

Git LFS Details

  • SHA256: 9ad6d69dc10558a5c03d1be514e9a325a1dfc23b42f915bfd9dfd3b76fe303d1
  • Pointer size: 130 Bytes
  • Size of remote file: 15.6 kB
public/assets/gemini-logo-dark.png ADDED

Git LFS Details

  • SHA256: f5027ab4f626e95b996f670c9795fca122cea63edf620c587c295b04d7a317bf
  • Pointer size: 130 Bytes
  • Size of remote file: 23.8 kB
public/assets/gemini-logo-light.png ADDED

Git LFS Details

  • SHA256: f5027ab4f626e95b996f670c9795fca122cea63edf620c587c295b04d7a317bf
  • Pointer size: 130 Bytes
  • Size of remote file: 23.8 kB
public/assets/grok-logo-dark.png ADDED

Git LFS Details

  • SHA256: 36eb518517fbff7718f7a72d765142b7e1e5403b3c6c6a3a8dace520e79d63ed
  • Pointer size: 130 Bytes
  • Size of remote file: 31 kB
public/assets/grok-logo-light.png ADDED

Git LFS Details

  • SHA256: 4451006a6161a44b86fb87ea2156c01dce92024cbdf212d41336c7ac3be180aa
  • Pointer size: 130 Bytes
  • Size of remote file: 37 kB
public/assets/huggingface-logo.png ADDED

Git LFS Details

  • SHA256: aef45eca5e838339066d8663312f7942d1088956045f92fd80038d65e10bf344
  • Pointer size: 130 Bytes
  • Size of remote file: 39.1 kB
public/assets/openai-logo-dark.png ADDED

Git LFS Details

  • SHA256: b9fb896970a0eea9229ca2ac88c6c13416240b7fd5c4080251a9710099f1a9ac
  • Pointer size: 129 Bytes
  • Size of remote file: 8.54 kB
public/assets/openai-logo-light.png ADDED

Git LFS Details

  • SHA256: ba06c8e624b5cbc7e53aeea3b235578615c7ccb1f2a1ca4199e152efe2cf5e5a
  • Pointer size: 130 Bytes
  • Size of remote file: 15.9 kB
public/assets/overview-dark.png ADDED

Git LFS Details

  • SHA256: 8c67127c3073de7e3c33918c22dffe75999283ae3998e7a939203765fbcd6710
  • Pointer size: 131 Bytes
  • Size of remote file: 147 kB
public/assets/overview-light.png ADDED

Git LFS Details

  • SHA256: 88634502c0ce5e34dbb28f1e38234286d67cda730bab87771e50fdb81e4bb12d
  • Pointer size: 131 Bytes
  • Size of remote file: 150 kB
public/assets/report-dark.png ADDED

Git LFS Details

  • SHA256: e0ae17a18f0fbaa1ea20482697ae80f8ca56cd794b3cd66dab1808e64e6d3a0d
  • Pointer size: 131 Bytes
  • Size of remote file: 161 kB
public/assets/report-light.png ADDED

Git LFS Details

  • SHA256: f578897c99b158e924df2485864f24780ec73fb44f182fd373615da9c1c5c73e
  • Pointer size: 131 Bytes
  • Size of remote file: 168 kB
public/assets/submission-dark.png ADDED

Git LFS Details

  • SHA256: 3170f8f1a604c6247a75157ad37c93df4167b1401f7a99f04175e3e54e8a94fc
  • Pointer size: 131 Bytes
  • Size of remote file: 208 kB
public/assets/submission-light.png ADDED

Git LFS Details

  • SHA256: 5bab151faf28b95ec7dc52afdc083e0ba32f8f425196e48f0f15591877ec3650
  • Pointer size: 131 Bytes
  • Size of remote file: 222 kB
src/constants/theme.js ADDED
@@ -0,0 +1,650 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { alpha } from '@mui/material/styles';
2
+ import { red } from '@mui/material/colors';
3
+
4
+ export const brand = {
5
+ 50: '#F0F7FF',
6
+ 100: '#CEE5FD',
7
+ 200: '#9CCCFC',
8
+ 300: '#55A6F6',
9
+ 400: '#0A66C2',
10
+ 500: '#0959AA',
11
+ 600: '#064079',
12
+ 700: '#033363',
13
+ 800: '#02294F',
14
+ 900: '#021F3B',
15
+ };
16
+
17
+ export const secondary = {
18
+ 50: '#F9F0FF',
19
+ 100: '#E9CEFD',
20
+ 200: '#D49CFC',
21
+ 300: '#B355F6',
22
+ 400: '#750AC2',
23
+ 500: '#6709AA',
24
+ 600: '#490679',
25
+ 700: '#3B0363',
26
+ 800: '#2F024F',
27
+ 900: '#23023B',
28
+ };
29
+
30
+ export const gray = {
31
+ 50: '#FBFCFE',
32
+ 100: '#EAF0F5',
33
+ 200: '#D6E2EB',
34
+ 300: '#BFCCD9',
35
+ 400: '#94A6B8',
36
+ 500: '#5B6B7C',
37
+ 600: '#4C5967',
38
+ 700: '#364049',
39
+ 800: '#131B20',
40
+ 900: '#090E10',
41
+ };
42
+
43
+ export const green = {
44
+ 50: '#F6FEF6',
45
+ 100: '#E3FBE3',
46
+ 200: '#C7F7C7',
47
+ 300: '#A1E8A1',
48
+ 400: '#51BC51',
49
+ 500: '#1F7A1F',
50
+ 600: '#136C13',
51
+ 700: '#0A470A',
52
+ 800: '#042F04',
53
+ 900: '#021D02',
54
+ };
55
+
56
+ const getDesignTokens = (mode) => ({
57
+ palette: {
58
+ mode,
59
+ primary: {
60
+ light: brand[200],
61
+ main: brand[500],
62
+ dark: brand[800],
63
+ contrastText: brand[50],
64
+ ...(mode === 'dark' && {
65
+ contrastText: brand[100],
66
+ light: brand[300],
67
+ main: brand[400],
68
+ dark: brand[800],
69
+ }),
70
+ },
71
+ secondary: {
72
+ light: secondary[300],
73
+ main: secondary[500],
74
+ dark: secondary[800],
75
+ ...(mode === 'dark' && {
76
+ light: secondary[400],
77
+ main: secondary[500],
78
+ dark: secondary[900],
79
+ }),
80
+ },
81
+ warning: {
82
+ main: '#F7B538',
83
+ dark: '#F79F00',
84
+ ...(mode === 'dark' && { main: '#F7B538', dark: '#F79F00' }),
85
+ },
86
+ error: {
87
+ light: red[50],
88
+ main: red[500],
89
+ dark: red[700],
90
+ ...(mode === 'dark' && { light: '#D32F2F', main: '#D32F2F', dark: '#B22A2A' }),
91
+ },
92
+ success: {
93
+ light: green[300],
94
+ main: green[400],
95
+ dark: green[800],
96
+ ...(mode === 'dark' && {
97
+ light: green[400],
98
+ main: green[500],
99
+ dark: green[700],
100
+ }),
101
+ },
102
+ grey: {
103
+ 50: gray[50],
104
+ 100: gray[100],
105
+ 200: gray[200],
106
+ 300: gray[300],
107
+ 400: gray[400],
108
+ 500: gray[500],
109
+ 600: gray[600],
110
+ 700: gray[700],
111
+ 800: gray[800],
112
+ 900: gray[900],
113
+ },
114
+ divider: mode === 'dark' ? alpha(gray[600], 0.3) : alpha(gray[300], 0.5),
115
+ background: {
116
+ default: '#fff',
117
+ paper: gray[50],
118
+ ...(mode === 'dark' && { default: gray[900], paper: gray[800] }),
119
+ },
120
+ text: {
121
+ primary: gray[800],
122
+ secondary: gray[600],
123
+ ...(mode === 'dark' && { primary: '#fff', secondary: gray[400] }),
124
+ },
125
+ action: {
126
+ selected: `${alpha(brand[200], 0.2)}`,
127
+ ...(mode === 'dark' && {
128
+ selected: alpha(brand[800], 0.2),
129
+ }),
130
+ },
131
+ },
132
+ typography: {
133
+ fontFamily: ['"Inter", "sans-serif"'].join(','),
134
+ h1: {
135
+ fontSize: 60,
136
+ fontWeight: 600,
137
+ lineHeight: 78 / 70,
138
+ letterSpacing: -0.2,
139
+ },
140
+ h2: {
141
+ fontSize: 48,
142
+ fontWeight: 600,
143
+ lineHeight: 1.2,
144
+ },
145
+ h3: {
146
+ fontSize: 42,
147
+ lineHeight: 1.2,
148
+ },
149
+ h4: {
150
+ fontSize: 36,
151
+ fontWeight: 500,
152
+ lineHeight: 1.5,
153
+ },
154
+ h5: {
155
+ fontSize: 20,
156
+ fontWeight: 600,
157
+ },
158
+ h6: {
159
+ fontSize: 18,
160
+ },
161
+ subtitle1: {
162
+ fontSize: 18,
163
+ },
164
+ subtitle2: {
165
+ fontSize: 16,
166
+ },
167
+ body1: {
168
+ fontWeight: 400,
169
+ fontSize: 15,
170
+ },
171
+ body2: {
172
+ fontWeight: 400,
173
+ fontSize: 14,
174
+ },
175
+ caption: {
176
+ fontWeight: 400,
177
+ fontSize: 12,
178
+ },
179
+ },
180
+ });
181
+
182
+ export const getTheme = (mode) => {
183
+ return {
184
+ ...getDesignTokens(mode),
185
+ components: {
186
+ MuiCssBaseline: {
187
+ styleOverrides: {
188
+ html: {
189
+ scrollbarGutter: 'stable',
190
+ overflowY: 'scroll',
191
+ },
192
+ body: {
193
+ scrollbarGutter: 'stable',
194
+ overflowY: 'scroll',
195
+ },
196
+ },
197
+ },
198
+ MuiPopover: {
199
+ defaultProps: {
200
+ disableScrollLock: true,
201
+ },
202
+ },
203
+ MuiMenu: {
204
+ defaultProps: {
205
+ disableScrollLock: true,
206
+ },
207
+ },
208
+ MuiAccordion: {
209
+ defaultProps: {
210
+ elevation: 0,
211
+ disableGutters: true,
212
+ },
213
+ styleOverrides: {
214
+ root: ({ theme }) => ({
215
+ padding: 8,
216
+ overflow: 'clip',
217
+ backgroundColor: '#fff',
218
+ border: '1px solid',
219
+ borderColor: gray[100],
220
+ ':before': {
221
+ backgroundColor: 'transparent',
222
+ },
223
+ '&:first-of-type': {
224
+ borderTopLeftRadius: 10,
225
+ borderTopRightRadius: 10,
226
+ },
227
+ '&:last-of-type': {
228
+ borderBottomLeftRadius: 10,
229
+ borderBottomRightRadius: 10,
230
+ },
231
+ ...(theme.palette.mode === 'dark' && {
232
+ backgroundColor: gray[900],
233
+ borderColor: gray[800],
234
+ }),
235
+ }),
236
+ },
237
+ },
238
+ MuiAccordionSummary: {
239
+ styleOverrides: {
240
+ root: ({ theme }) => ({
241
+ border: 'none',
242
+ borderRadius: 8,
243
+ '&:hover': { backgroundColor: gray[100] },
244
+ ...(theme.palette.mode === 'dark' && {
245
+ '&:hover': { backgroundColor: gray[800] },
246
+ }),
247
+ }),
248
+ },
249
+ },
250
+ MuiAccordionDetails: {
251
+ styleOverrides: {
252
+ root: { mb: 20, border: 'none' },
253
+ },
254
+ },
255
+ MuiToggleButtonGroup: {
256
+ styleOverrides: {
257
+ root: ({ theme }) => ({
258
+ borderRadius: '10px',
259
+ boxShadow: `0 4px 16px ${alpha(gray[400], 0.2)}`,
260
+ '& .Mui-selected': {
261
+ color: brand[500],
262
+ },
263
+ ...(theme.palette.mode === 'dark' && {
264
+ '& .Mui-selected': {
265
+ color: '#fff',
266
+ },
267
+ boxShadow: `0 4px 16px ${alpha(brand[700], 0.5)}`,
268
+ }),
269
+ }),
270
+ },
271
+ },
272
+ MuiToggleButton: {
273
+ styleOverrides: {
274
+ root: ({ theme }) => ({
275
+ padding: '12px 16px',
276
+ textTransform: 'none',
277
+ borderRadius: '10px',
278
+ fontWeight: 500,
279
+ ...(theme.palette.mode === 'dark' && {
280
+ color: gray[400],
281
+ boxShadow: '0 4px 16px rgba(0, 0, 0, 0.5)',
282
+ '&.Mui-selected': { color: brand[300] },
283
+ }),
284
+ }),
285
+ },
286
+ },
287
+ MuiButtonBase: {
288
+ defaultProps: {
289
+ disableTouchRipple: true,
290
+ disableRipple: true,
291
+ },
292
+ styleOverrides: {
293
+ root: {
294
+ boxSizing: 'border-box',
295
+ transition: 'all 100ms ease-in',
296
+ '&:focus-visible': {
297
+ outline: `3px solid ${alpha(brand[500], 0.5)}`,
298
+ outlineOffset: '2px',
299
+ },
300
+ },
301
+ },
302
+ },
303
+ MuiButton: {
304
+ styleOverrides: {
305
+ root: ({ theme, ownerState }) => ({
306
+ boxSizing: 'border-box',
307
+ boxShadow: 'unset',
308
+ borderRadius: '10px',
309
+ textTransform: 'none',
310
+ ...(ownerState.size === 'small' && {
311
+ maxHeight: '32px',
312
+ }),
313
+ ...(ownerState.size === 'medium' && {
314
+ height: '40px',
315
+ }),
316
+ ...(ownerState.variant === 'contained' &&
317
+ ownerState.color === 'primary' && {
318
+ color: brand[50],
319
+ background: brand[400],
320
+ '&:hover': {
321
+ background: brand[500],
322
+ backgroundImage: 'none',
323
+ boxShadow: 'unset',
324
+ },
325
+ }),
326
+ ...(ownerState.variant === 'contained' &&
327
+ ownerState.color === 'error' && {
328
+ color: brand[50],
329
+ background: red[600],
330
+ '&:hover': {
331
+ background: red[800],
332
+ backgroundImage: 'none',
333
+ boxShadow: 'unset',
334
+ },
335
+ }),
336
+ ...(ownerState.variant === 'outlined' && {
337
+ backgroundColor: alpha(brand[300], 0.1),
338
+ borderColor: brand[300],
339
+ color: brand[500],
340
+ '&:hover': {
341
+ backgroundColor: alpha(brand[300], 0.3),
342
+ borderColor: brand[200],
343
+ boxShadow: 'unset',
344
+ },
345
+ }),
346
+ ...(ownerState.variant === 'text' && {
347
+ color: brand[500],
348
+ '&:hover': {
349
+ backgroundColor: alpha(brand[300], 0.3),
350
+ borderColor: brand[200],
351
+ boxShadow: 'unset',
352
+ },
353
+ }),
354
+ ...(theme.palette.mode === 'dark' && {
355
+ ...(ownerState.variant === 'outlined' && {
356
+ backgroundColor: alpha(brand[600], 0.1),
357
+ borderColor: brand[700],
358
+ color: brand[300],
359
+ '&:hover': {
360
+ backgroundColor: alpha(brand[600], 0.3),
361
+ borderColor: brand[700],
362
+ boxShadow: 'unset',
363
+ },
364
+ }),
365
+ ...(ownerState.variant === 'text' && {
366
+ color: brand[300],
367
+ '&:hover': {
368
+ backgroundColor: alpha(brand[600], 0.3),
369
+ borderColor: brand[700],
370
+ boxShadow: 'unset',
371
+ },
372
+ }),
373
+ }),
374
+ }),
375
+ },
376
+ },
377
+ MuiCard: {
378
+ styleOverrides: {
379
+ root: ({ theme, ownerState }) => ({
380
+ backgroundColor: gray[50],
381
+ borderRadius: 10,
382
+ border: `1px solid ${alpha(gray[200], 0.8)}`,
383
+ boxShadow: 'none',
384
+ transition: 'background-color, border, 80ms ease',
385
+ ...(ownerState.variant === 'outlined' && {
386
+ background: `linear-gradient(to bottom, #FFF, ${gray[50]})`,
387
+ '&:hover': {
388
+ borderColor: brand[300],
389
+ boxShadow: `0 0 24px ${brand[100]}`,
390
+ },
391
+ }),
392
+ ...(theme.palette.mode === 'dark' && {
393
+ backgroundColor: alpha(gray[800], 0.6),
394
+ border: `1px solid ${alpha(gray[700], 0.3)}`,
395
+ ...(ownerState.variant === 'outlined' && {
396
+ background: `linear-gradient(to bottom, ${gray[900]}, ${alpha(gray[800], 0.5)})`,
397
+ '&:hover': {
398
+ borderColor: brand[700],
399
+ boxShadow: `0 0 24px ${brand[800]}`,
400
+ },
401
+ }),
402
+ }),
403
+ }),
404
+ },
405
+ },
406
+ MuiChip: {
407
+ styleOverrides: {
408
+ root: ({ theme }) => ({
409
+ alignSelf: 'center',
410
+ py: 1.5,
411
+ px: 0.5,
412
+ background: `linear-gradient(to bottom right, ${brand[50]}, ${brand[100]})`,
413
+ border: '1px solid',
414
+ borderColor: `${alpha(brand[500], 0.3)}`,
415
+ fontWeight: '600',
416
+ '&:hover': {
417
+ backgroundColor: brand[500],
418
+ },
419
+ '&:focus-visible': {
420
+ borderColor: brand[800],
421
+ backgroundColor: brand[200],
422
+ },
423
+ '& .MuiChip-label': {
424
+ color: brand[500],
425
+ },
426
+ '& .MuiChip-icon': {
427
+ color: brand[500],
428
+ },
429
+ ...(theme.palette.mode === 'dark' && {
430
+ background: `linear-gradient(to bottom right, ${brand[700]}, ${brand[900]})`,
431
+ borderColor: `${alpha(brand[500], 0.5)}`,
432
+ '&:hover': {
433
+ backgroundColor: brand[600],
434
+ },
435
+ '&:focus-visible': {
436
+ borderColor: brand[200],
437
+ backgroundColor: brand[600],
438
+ },
439
+ '& .MuiChip-label': {
440
+ color: brand[200],
441
+ },
442
+ '& .MuiChip-icon': {
443
+ color: brand[200],
444
+ },
445
+ }),
446
+ }),
447
+ },
448
+ },
449
+ MuiDivider: {
450
+ styleOverrides: {
451
+ root: ({ theme }) => ({
452
+ borderColor: `${alpha(gray[200], 0.8)}`,
453
+ ...(theme.palette.mode === 'dark' && {
454
+ borderColor: `${alpha(gray[700], 0.4)}`,
455
+ }),
456
+ }),
457
+ },
458
+ },
459
+ MuiLink: {
460
+ defaultProps: {
461
+ underline: 'none',
462
+ },
463
+ styleOverrides: {
464
+ root: ({ theme }) => ({
465
+ color: brand[600],
466
+ fontWeight: 500,
467
+ position: 'relative',
468
+ textDecoration: 'none',
469
+ '&::before': {
470
+ content: '""',
471
+ position: 'absolute',
472
+ width: 0,
473
+ height: '1px',
474
+ bottom: 0,
475
+ left: 0,
476
+ backgroundColor: brand[200],
477
+ opacity: 0.7,
478
+ transition: 'width 0.3s ease, opacity 0.3s ease',
479
+ },
480
+ '&:hover::before': {
481
+ width: '100%',
482
+ opacity: 1,
483
+ },
484
+ ...(theme.palette.mode === 'dark' && {
485
+ color: brand[200],
486
+ }),
487
+ }),
488
+ },
489
+ },
490
+ MuiList: {
491
+ styleOverrides: {
492
+ root: ({ theme }) => ({
493
+ backgroundColor: theme.palette.mode === 'dark' ? '#000' : 'transparent',
494
+ }),
495
+ },
496
+ },
497
+ MuiMenuItem: {
498
+ styleOverrides: {
499
+ root: ({ theme }) => ({
500
+ borderRadius: '10px',
501
+ color: gray[500],
502
+ fontWeight: 500,
503
+ ...(theme.palette.mode === 'dark' && {
504
+ color: gray[300],
505
+ }),
506
+ }),
507
+ },
508
+ },
509
+ MuiPaper: {
510
+ styleOverrides: {
511
+ root: ({ theme }) => ({
512
+ backgroundImage: 'none',
513
+ backgroundColor: gray[100],
514
+ ...(theme.palette.mode === 'dark' && {
515
+ backgroundColor: alpha(gray[900], 0.6),
516
+ }),
517
+ }),
518
+ },
519
+ },
520
+ MuiSwitch: {
521
+ styleOverrides: {
522
+ root: ({ theme }) => ({
523
+ boxSizing: 'border-box',
524
+ width: 36,
525
+ height: 24,
526
+ padding: 0,
527
+ transition: 'background-color 100ms ease-in',
528
+ '&:hover': {
529
+ '& .MuiSwitch-track': {
530
+ backgroundColor: brand[600],
531
+ },
532
+ },
533
+ '& .MuiSwitch-switchBase': {
534
+ '&.Mui-checked': {
535
+ transform: 'translateX(13px)',
536
+ },
537
+ },
538
+ '& .MuiSwitch-track': {
539
+ borderRadius: 50,
540
+ },
541
+ '& .MuiSwitch-thumb': {
542
+ boxShadow: '0 0 2px 2px rgba(0, 0, 0, 0.2)',
543
+ backgroundColor: '#FFF',
544
+ width: 16,
545
+ height: 16,
546
+ margin: 2,
547
+ },
548
+ ...(theme.palette.mode === 'dark' && {
549
+ width: 36,
550
+ height: 24,
551
+ padding: 0,
552
+ transition: 'background-color 100ms ease-in',
553
+ '&:hover': {
554
+ '& .MuiSwitch-track': {
555
+ backgroundColor: brand[600],
556
+ },
557
+ },
558
+ '& .MuiSwitch-switchBase': {
559
+ '&.Mui-checked': {
560
+ transform: 'translateX(13px)',
561
+ },
562
+ },
563
+ '& .MuiSwitch-thumb': {
564
+ boxShadow: '0 0 2px 2px rgba(0, 0, 0, 0.2)',
565
+ backgroundColor: '#FFF',
566
+ width: 16,
567
+ height: 16,
568
+ margin: 2,
569
+ },
570
+ }),
571
+ }),
572
+ switchBase: {
573
+ height: 24,
574
+ width: 24,
575
+ padding: 0,
576
+ color: '#fff',
577
+ '&.Mui-checked + .MuiSwitch-track': {
578
+ opacity: 1,
579
+ },
580
+ },
581
+ },
582
+ },
583
+ MuiTextField: {
584
+ styleOverrides: {
585
+ root: ({ theme }) => ({
586
+ '& label .Mui-focused': {
587
+ color: 'white',
588
+ },
589
+ '& .MuiInputBase-input': {
590
+ boxSizing: 'border-box',
591
+ '&::placeholder': {
592
+ opacity: 0.7,
593
+ },
594
+ },
595
+ '& .MuiOutlinedInput-root': {
596
+ boxSizing: 'border-box',
597
+ minWidth: 140,
598
+ minHeight: 40,
599
+ height: '100%',
600
+ borderRadius: '10px',
601
+ border: '1px solid',
602
+ borderColor: gray[200],
603
+ transition: 'border-color 120ms ease-in',
604
+ '& fieldset': {
605
+ border: 'none',
606
+ boxShadow: '0px 2px 4px rgba(0, 0, 0, 0.1)',
607
+ background: `${alpha('#FFF', 0.3)}`,
608
+ },
609
+ '&:hover': {
610
+ borderColor: brand[300],
611
+ },
612
+ '&.Mui-focused': {
613
+ borderColor: brand[400],
614
+ outline: '4px solid',
615
+ outlineColor: brand[200],
616
+ },
617
+ },
618
+ ...(theme.palette.mode === 'dark' && {
619
+ '& .MuiOutlinedInput-root': {
620
+ boxSizing: 'border-box',
621
+ minWidth: 140,
622
+ minHeight: 40,
623
+ height: '100%',
624
+ borderRadius: '10px',
625
+ border: '1px solid',
626
+ borderColor: gray[600],
627
+ transition: 'border-color 120ms ease-in',
628
+ '& fieldset': {
629
+ border: 'none',
630
+ boxShadow: ' 0px 2px 4px rgba(0, 0, 0, 0.4)',
631
+ background: `${alpha(gray[800], 0.4)}`,
632
+ },
633
+ '&:hover': {
634
+ borderColor: brand[300],
635
+ },
636
+ '&.Mui-focused': {
637
+ borderColor: brand[400],
638
+ outline: '4px solid',
639
+ outlineColor: alpha(brand[500], 0.5),
640
+ },
641
+ },
642
+ }),
643
+ }),
644
+ },
645
+ },
646
+ },
647
+ };
648
+ };
649
+
650
+ export default getTheme;
src/features/app/App.jsx ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useEffect } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { useDispatch, useSelector } from 'react-redux';
4
+ import { RouterProvider } from 'react-router-dom';
5
+
6
+ import { CssBaseline } from '@mui/material';
7
+ import { ThemeProvider, createTheme } from '@mui/material/styles';
8
+ import '@fontsource/inter';
9
+
10
+ import getTheme from '../../constants/theme';
11
+ import { hideToast, initApp, selectThemeMode, selectToast } from './appSlice';
12
+ import Toast from '../shared/Toast/Toast';
13
+
14
+ export function App({ router }) {
15
+ const dispatch = useDispatch();
16
+ const themeMode = useSelector(selectThemeMode);
17
+ const toast = useSelector(selectToast);
18
+
19
+ useEffect(() => {
20
+ dispatch(initApp());
21
+ }, [dispatch]);
22
+
23
+ return (
24
+ <ThemeProvider theme={createTheme(getTheme(themeMode))}>
25
+ <CssBaseline />
26
+ <Toast {...toast} onClose={() => dispatch(hideToast())} />
27
+ <RouterProvider router={router} />
28
+ </ThemeProvider>
29
+ );
30
+ }
31
+
32
+ App.propTypes = {
33
+ router: PropTypes.shape({
34
+ subscribe: PropTypes.func.isRequired,
35
+ }).isRequired,
36
+ };
src/features/app/appSlice.js ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { createAppSlice } from '../../store/createAppSlice';
2
+
3
+ const initialState = {
4
+ authToken: '',
5
+ themeMode: 'light',
6
+ toast: {
7
+ open: false,
8
+ autoHideDuration: 3000,
9
+ severity: 'success',
10
+ messageParts: [],
11
+ },
12
+ appbar: { open: false },
13
+ };
14
+
15
+ export const appSlice = createAppSlice({
16
+ name: 'app',
17
+ initialState,
18
+ reducers: (create) => ({
19
+ initApp: create.reducer((state) => {
20
+ const themeMode = localStorage.getItem('themeMode');
21
+ if (themeMode) {
22
+ state.themeMode = themeMode;
23
+ } else {
24
+ state.themeMode = initialState.themeMode;
25
+ localStorage.setItem('themeMode', state.themeMode);
26
+ }
27
+ const authToken = localStorage.getItem('authToken');
28
+ if (authToken) {
29
+ state.authToken = authToken;
30
+ } else {
31
+ state.authToken = initialState.authToken;
32
+ localStorage.setItem('authToken', state.authToken);
33
+ }
34
+ }),
35
+ setAuthToken: create.reducer((state, action) => {
36
+ state.authToken = action.payload;
37
+ localStorage.setItem('authToken', state.authToken);
38
+ }),
39
+ resetAuthToken: create.reducer((state) => {
40
+ state.authToken = initialState.authToken;
41
+ localStorage.setItem('authToken', state.authToken);
42
+ }),
43
+ setThemeMode: create.reducer((state, action) => {
44
+ state.themeMode = action.payload;
45
+ localStorage.setItem('themeMode', state.themeMode);
46
+ }),
47
+ switchThemeMode: create.reducer((state) => {
48
+ state.themeMode = state.themeMode === 'light' ? 'dark' : 'light';
49
+ localStorage.setItem('themeMode', state.themeMode);
50
+ }),
51
+ resetApp: create.reducer((state) => {
52
+ state.authToken = initialState.authToken;
53
+ state.themeMode = initialState.themeMode;
54
+ state.toast = initialState.toast;
55
+ localStorage.setItem('authToken', state.authToken);
56
+ localStorage.setItem('themeMode', state.themeMode);
57
+ }),
58
+ showToast: create.reducer((state, action) => {
59
+ state.toast.open = true;
60
+ state.toast.messageParts = action.payload.messageParts;
61
+ state.toast.severity = action.payload.severity ?? initialState.toast.severity;
62
+ state.toast.autoHideDuration =
63
+ action.payload.autoHideDuration ?? initialState.toast.autoHideDuration;
64
+ }),
65
+ hideToast: create.reducer((state) => {
66
+ state.toast.open = false;
67
+ }),
68
+ switchAppbarOpen: create.reducer((state, action) => {
69
+ state.appbar.open = action.payload;
70
+ }),
71
+ }),
72
+ selectors: {
73
+ selectAuthToken: (app) => app.authToken,
74
+ selectThemeMode: (app) => app.themeMode,
75
+ selectToast: (app) => app.toast,
76
+ selectAppbar: (app) => app.appbar,
77
+ },
78
+ });
79
+
80
+ export const {
81
+ hideToast,
82
+ initApp,
83
+ resetApp,
84
+ resetAuthToken,
85
+ setAuthToken,
86
+ setThemeMode,
87
+ switchThemeMode,
88
+ showToast,
89
+ switchAppbarOpen,
90
+ } = appSlice.actions;
91
+
92
+ export const { selectAuthToken, selectThemeMode, selectToast, selectAppbar } = appSlice.selectors;
src/features/landing/AppAppBar/AppAppBar-styles.js ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export const styles = {
2
+ appbar: {
3
+ boxShadow: 0,
4
+ bgcolor: 'transparent',
5
+ backgroundImage: 'none',
6
+ mt: { xs: 3, sm: 4, md: 4 },
7
+ },
8
+ authItemBox: {
9
+ display: { xs: 'none', md: 'flex' },
10
+ gap: 0.5,
11
+ alignItems: 'center',
12
+ },
13
+ largeAppBar: {
14
+ flexGrow: 1,
15
+ display: 'flex',
16
+ alignItems: 'center',
17
+ },
18
+
19
+ largeMenuItem: { py: '6px', px: '12px' },
20
+ largeMenuItemBox: { display: { xs: 'none', md: 'flex' } },
21
+
22
+ logoBox: { pr: 2 },
23
+
24
+ smallAppBar: { display: { sm: '', md: 'none' } },
25
+ smallMenuIconButton: { minWidth: '30px', p: '4px', color: 'primary' },
26
+ smallMenuItem: { width: '100%', color: 'primary' },
27
+ smallMenuItemBox: {
28
+ minWidth: '60dvw',
29
+ p: 2,
30
+ backgroundColor: 'background.paper',
31
+ flexGrow: 1,
32
+ },
33
+
34
+ themeModeToggleBox: {
35
+ display: 'flex',
36
+ flexDirection: 'column',
37
+ alignItems: 'end',
38
+ flexGrow: 1,
39
+ },
40
+
41
+ toolbar: {
42
+ display: 'flex',
43
+ alignItems: 'center',
44
+ justifyContent: 'space-between',
45
+ flexShrink: 0,
46
+ borderRadius: '10px',
47
+ bgcolor: (theme) =>
48
+ theme.palette.mode === 'light' ? 'rgba(255, 255, 255, 0.4)' : 'rgba(0, 0, 0, 0.4)',
49
+ backdropFilter: 'blur(24px)',
50
+ maxHeight: 40,
51
+ border: '1px solid',
52
+ borderColor: 'divider',
53
+ boxShadow: (theme) =>
54
+ theme.palette.mode === 'light'
55
+ ? `0 0 1px rgba(85, 166, 246, 0.1), 1px 1.5px 2px -1px rgba(85, 166, 246, 0.15), 4px 4px 12px -2.5px rgba(85, 166, 246, 0.15)`
56
+ : '0 0 1px rgba(2, 31, 59, 0.7), 1px 1.5px 2px -1px rgba(2, 31, 59, 0.65), 4px 4px 12px -2.5px rgba(2, 31, 59, 0.65)',
57
+ },
58
+ };
59
+
60
+ export default styles;
src/features/landing/AppAppBar/AppAppBar.jsx ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useState } from "react";
2
+ import PropTypes from "prop-types";
3
+
4
+ import { useSelector } from "react-redux";
5
+
6
+ import {
7
+ AppBar,
8
+ Box,
9
+ Button,
10
+ Container,
11
+ Divider,
12
+ Drawer,
13
+ MenuItem,
14
+ Toolbar,
15
+ Typography,
16
+ } from "@mui/material";
17
+ import { Menu } from "@mui/icons-material";
18
+
19
+ import styles from "./AppAppBar-styles";
20
+
21
+ import { selectAuthToken, selectThemeMode } from "../../app/appSlice";
22
+
23
+ import ThemeModeToggle from "../../shared/ThemeModeToggle/ThemeModeToggle";
24
+ import Logo from "../../shared/Logo/Logo";
25
+
26
+ export const AppAppBar = ({ onClickThemeMode }) => {
27
+ const themeMode = useSelector(selectThemeMode);
28
+ const authToken = useSelector(selectAuthToken);
29
+ const [open, setOpen] = useState(false);
30
+
31
+ const scrollToSection = (sectionId) => {
32
+ const sectionElement = document.getElementById(sectionId);
33
+ const offset = 128;
34
+ if (sectionElement) {
35
+ const targetScroll = sectionElement.offsetTop - offset;
36
+ sectionElement.scrollIntoView({ behavior: "smooth" });
37
+ window.scrollTo({
38
+ top: targetScroll,
39
+ behavior: "smooth",
40
+ });
41
+ setOpen(false);
42
+ }
43
+ };
44
+
45
+ const desktop = (
46
+ <Box sx={styles.largeAppBar}>
47
+ <Box sx={styles.logoBox}>
48
+ <Logo />
49
+ </Box>
50
+ <Box sx={styles.largeMenuItemBox}>
51
+ <MenuItem
52
+ onClick={() => scrollToSection("leaderboard")}
53
+ sx={styles.largeMenuItem}
54
+ >
55
+ <Typography variant="body2" color="text.primary">
56
+ Leaderboard
57
+ </Typography>
58
+ </MenuItem>
59
+ <MenuItem
60
+ onClick={() => scrollToSection("services")}
61
+ sx={styles.largeMenuItem}
62
+ >
63
+ <Typography variant="body2" color="text.primary">
64
+ Services
65
+ </Typography>
66
+ </MenuItem>
67
+
68
+ <MenuItem
69
+ onClick={() => scrollToSection("features")}
70
+ sx={styles.largeMenuItem}
71
+ >
72
+ <Typography variant="body2" color="text.primary">
73
+ Features
74
+ </Typography>
75
+ </MenuItem>
76
+
77
+ <MenuItem
78
+ onClick={() => scrollToSection("faq")}
79
+ sx={styles.largeMenuItem}
80
+ >
81
+ <Typography variant="body2" color="text.primary">
82
+ FAQ
83
+ </Typography>
84
+ </MenuItem>
85
+ </Box>
86
+ </Box>
87
+ );
88
+
89
+ const desktopAuth = (
90
+ <Box sx={styles.authItemBox}>
91
+ <ThemeModeToggle mode={themeMode} onClick={onClickThemeMode} />
92
+ <Button
93
+ color="primary"
94
+ variant="contained"
95
+ size="small"
96
+ component="a"
97
+ href="https://aixamine.qcri.org/main"
98
+ >
99
+ {"Homepage"}
100
+ </Button>
101
+ </Box>
102
+ );
103
+
104
+ const mobile = (
105
+ <Box sx={styles.smallAppBar}>
106
+ <Button
107
+ variant="text"
108
+ onClick={() => setOpen(true)}
109
+ sx={styles.smallMenuIconButton}
110
+ >
111
+ <Menu />
112
+ </Button>
113
+ <Drawer anchor="right" open={open} onClose={() => setOpen(false)}>
114
+ <Box sx={styles.smallMenuItemBox}>
115
+ <Box sx={styles.themeModeToggleBox}>
116
+ <ThemeModeToggle mode={themeMode} onClick={onClickThemeMode} />
117
+ </Box>
118
+ <MenuItem onClick={() => scrollToSection("leaderboard")}>
119
+ Leaderboard
120
+ </MenuItem>
121
+ <MenuItem onClick={() => scrollToSection("services")}>
122
+ Services
123
+ </MenuItem>
124
+ <MenuItem onClick={() => scrollToSection("features")}>
125
+ Features
126
+ </MenuItem>
127
+ <MenuItem onClick={() => scrollToSection("faq")}>FAQ</MenuItem>
128
+ <Box mt={2} mb={2}>
129
+ <Divider />
130
+ </Box>
131
+
132
+ <MenuItem>
133
+ <Button
134
+ color="primary"
135
+ variant="contained"
136
+ size="small"
137
+ component="a"
138
+ href="https://aixamine.qcri.org/main"
139
+ >
140
+ {"Homepage"}
141
+ </Button>
142
+ </MenuItem>
143
+ </Box>
144
+ </Drawer>
145
+ </Box>
146
+ );
147
+
148
+ return (
149
+ <div>
150
+ <AppBar position="fixed" sx={styles.appbar}>
151
+ <Container maxWidth="lg">
152
+ <Toolbar variant="regular" sx={styles.toolbar}>
153
+ {desktop}
154
+ {desktopAuth}
155
+ {mobile}
156
+ </Toolbar>
157
+ </Container>
158
+ </AppBar>
159
+ </div>
160
+ );
161
+ };
162
+
163
+ AppAppBar.propTypes = {
164
+ onClickThemeMode: PropTypes.func.isRequired,
165
+ };
166
+
167
+ export default AppAppBar;
src/features/landing/FAQ/FAQ-styles.js ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export const styles = {
2
+ faqBox: { width: "100%" },
3
+ root: { py: { xs: 8, sm: 16 } },
4
+
5
+ description: {
6
+ maxWidth: "100%",
7
+ },
8
+ title: {
9
+ color: "text.primary",
10
+ width: { sm: "100%", md: "60%" },
11
+ textAlign: { sm: "left", md: "center" },
12
+ },
13
+ };
14
+
15
+ export default styles;
src/features/landing/FAQ/FAQ.jsx ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useState } from 'react';
2
+ import Accordion from '@mui/material/Accordion';
3
+
4
+ import {
5
+ AccordionDetails,
6
+ AccordionSummary,
7
+ Box,
8
+ Container,
9
+ Link,
10
+ Typography,
11
+ } from '@mui/material';
12
+
13
+ import styles from './FAQ-styles';
14
+
15
+ import { ExpandMore } from '@mui/icons-material';
16
+
17
+ export function FAQ() {
18
+ const [expanded, setExpanded] = useState(false);
19
+
20
+ const handleChange = (panel) => (event, isExpanded) => {
21
+ setExpanded(expanded === panel ? false : panel);
22
+ };
23
+
24
+ const title = (
25
+ <Typography variant="h4" sx={styles.title}>
26
+ Frequently asked questions
27
+ </Typography>
28
+ );
29
+
30
+ return (
31
+ <Container id="faq" sx={styles.root}>
32
+ {title}
33
+ <Box sx={styles.faqBox}>
34
+ <Accordion expanded={expanded === 'panel1'} onChange={handleChange('panel1')}>
35
+ <AccordionSummary expandIcon={<ExpandMore />} id="panel1d-header">
36
+ <Typography component="h3" variant="subtitle2">
37
+ How do I submit my model for examination?
38
+ </Typography>
39
+ </AccordionSummary>
40
+ <AccordionDetails>
41
+ <Typography variant="body2" gutterBottom sx={styles.description}>
42
+ We support examination of all models published on Hugging Face. To examine a model,
43
+ simply provide the Hugging Face path to the model, a Hugging Face token with read
44
+ permission, and select the service/test that you want to perform. To examine a private
45
+ model, simply provide the model name, API endpoint, and API token.
46
+ </Typography>
47
+ </AccordionDetails>
48
+ </Accordion>
49
+ <Accordion expanded={expanded === 'panel2'} onChange={handleChange('panel2')}>
50
+ <AccordionSummary expandIcon={<ExpandMore />} id="panel2d-header">
51
+ <Typography component="h3" variant="subtitle2">
52
+ How long does it take to examine my model?
53
+ </Typography>
54
+ </AccordionSummary>
55
+ <AccordionDetails>
56
+ <Typography variant="body2" gutterBottom sx={styles.description}>
57
+ The examination time of a model is dependent on the model's inference time. For a 7B
58
+ parameter model such as LLaMA 2, the average examination time is 15 minutes.
59
+ </Typography>
60
+ </AccordionDetails>
61
+ </Accordion>
62
+ <Accordion expanded={expanded === 'panel3'} onChange={handleChange('panel3')}>
63
+ <AccordionSummary expandIcon={<ExpandMore />} id="panel3d-header">
64
+ <Typography component="h3" variant="subtitle2">
65
+ What makes your services stand out from others in the market?
66
+ </Typography>
67
+ </AccordionSummary>
68
+ <AccordionDetails>
69
+ <Typography variant="body2" gutterBottom sx={styles.description}>
70
+ Our product unifies the broad landscape of model safety and security concerns via a
71
+ single report. We conduct a wide range of tests under each service, providing users
72
+ with detailed breakdowns and insights of the results. Users can compare models via our
73
+ interactive leaderboard and export results as a PDF report.
74
+ </Typography>
75
+ </AccordionDetails>
76
+ </Accordion>
77
+ <Accordion expanded={expanded === 'panel4'} onChange={handleChange('panel4')}>
78
+ <AccordionSummary expandIcon={<ExpandMore />} id="panel4d-header">
79
+ <Typography component="h3" variant="subtitle2">
80
+ How can the examination improve my model?
81
+ </Typography>
82
+ </AccordionSummary>
83
+ <AccordionDetails>
84
+ <Typography variant="body2" gutterBottom sx={styles.description}>
85
+ The examination produces a report of the model's robustness across different areas of
86
+ safety and security. These results reveal threats that the model can be guarded
87
+ against and identify areas where model safety can be improved.
88
+ </Typography>
89
+ </AccordionDetails>
90
+ </Accordion>
91
+ </Box>
92
+ </Container>
93
+ );
94
+ }
95
+
96
+ export default FAQ;
src/features/landing/Features/Features-styles.js ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export const styles = {
2
+ root: { py: { xs: 8, sm: 16 } },
3
+ rootBox: { width: { sm: "100%", md: "80%" } },
4
+
5
+ feature: { display: "flex", gap: 2, overflow: "auto" },
6
+ featureBox: {
7
+ display: { xs: "flex", sm: "none" },
8
+ flexDirection: "column",
9
+ gap: 2,
10
+ },
11
+
12
+ descriptionBox: {
13
+ width: "100%",
14
+ display: "flex",
15
+ flexDirection: "column",
16
+ alignItems: "flex-start",
17
+ gap: 1,
18
+ textAlign: "left",
19
+ textTransform: "none",
20
+ },
21
+ desktopImageBox: {
22
+ m: "auto",
23
+ width: "100%",
24
+ height: "100%",
25
+ backgroundSize: "contain",
26
+ backgroundPosition: "center",
27
+ backgroundRepeat: "no-repeat",
28
+ },
29
+
30
+ main: {
31
+ display: { xs: "none", sm: "flex" },
32
+ flexDirection: "column",
33
+ gap: 2,
34
+ height: "100%",
35
+ },
36
+ mainBox: {
37
+ display: "flex",
38
+ flexDirection: { xs: "column", md: "row-reverse" },
39
+ gap: 2,
40
+ },
41
+ mobileBox: {
42
+ display: { xs: "none", sm: "flex" },
43
+ width: { xs: "100%", md: "70%" },
44
+ height: "var(--items-image-height)",
45
+ },
46
+ mobileCard: {
47
+ height: "100%",
48
+ width: "100%",
49
+ display: { xs: "none", sm: "flex" },
50
+ pointerEvents: "none",
51
+ border: "none",
52
+ boxShadow: "none",
53
+ },
54
+ mobileImageBox: {
55
+ mb: 2,
56
+ backgroundSize: "contain",
57
+ backgroundPosition: "center",
58
+ width: "100%",
59
+ height: "100%",
60
+ backgroundRepeat: "no-repeat",
61
+ minHeight: 280,
62
+ },
63
+
64
+ featureChip: (selectedItemIdx, idx) => {
65
+ return {
66
+ borderColor: (theme) => {
67
+ if (theme.palette.mode === "light") {
68
+ return selectedItemIdx === idx ? "primary.light" : "";
69
+ }
70
+ return selectedItemIdx === idx ? "primary.light" : "";
71
+ },
72
+ background: (theme) => {
73
+ if (theme.palette.mode === "light") {
74
+ return selectedItemIdx === idx ? "none" : "";
75
+ }
76
+ return selectedItemIdx === idx ? "none" : "";
77
+ },
78
+ backgroundColor: selectedItemIdx === idx ? "primary.main" : "",
79
+ "& .MuiChip-label": {
80
+ color: selectedItemIdx === idx ? "#fff" : "",
81
+ },
82
+ };
83
+ },
84
+ featureDescription: { px: 2, pb: 2 },
85
+ featureText: { color: "text.primary" },
86
+ subtitle: { mb: { xs: 2, sm: 4 }, color: "text.secondary" },
87
+ textBody: { color: "text.secondary", mb: 1.5 },
88
+ textTitle: { color: "text.primary", fontWeight: "medium" },
89
+ };
90
+
91
+ export default styles;
src/features/landing/Features/Features.jsx ADDED
@@ -0,0 +1,203 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import PropTypes from "prop-types";
2
+ import { useEffect, useState } from "react";
3
+ import { Box, Button, Card, Chip, Container, Typography } from "@mui/material";
4
+ import { VisibilityOff, Search, Assessment } from "@mui/icons-material";
5
+
6
+ import submissionDark from "/assets/submission-dark.png";
7
+ import submissionLight from "/assets/submission-light.png";
8
+ import overviewDark from "/assets/overview-dark.png";
9
+ import overviewLight from "/assets/overview-light.png";
10
+ import reportDark from "/assets/report-dark.png";
11
+ import reportLight from "/assets/report-light.png";
12
+
13
+ import styles from "./Features-styles";
14
+
15
+ const items = [
16
+ {
17
+ icon: <VisibilityOff />,
18
+ title: "Private Submission",
19
+ description:
20
+ "Closed source model? No problem, just provide us with API access. aiXamine is a unified framework for evaluating both proprietary and open-source models without requiring access to model internals or weights.",
21
+ imageLight: [submissionLight],
22
+ imageDark: [submissionDark],
23
+ },
24
+ {
25
+ icon: <Search />,
26
+ title: "Comprehensive Examination",
27
+ description:
28
+ "Evaluate models across 46 tests in 9 services spanning Safety & Reliability, Security & Robustness, and Privacy & Fairness dimensions.",
29
+ imageLight: [overviewLight],
30
+ imageDark: [overviewDark],
31
+ },
32
+ {
33
+ icon: <Assessment />,
34
+ title: "Rich Reporting",
35
+ description:
36
+ "Get structured, reproducible reports with category and subcategory-level diagnostics to identify systemic and localized vulnerabilities.",
37
+ imageLight: [reportLight],
38
+ imageDark: [reportDark],
39
+ },
40
+ ];
41
+
42
+ export const MobileLayout = ({
43
+ selectedItemIdx,
44
+ selectedImageIdx,
45
+ handleItemClick,
46
+ selectedFeature,
47
+ }) => {
48
+ if (!items[selectedItemIdx]) {
49
+ return null;
50
+ }
51
+
52
+ return (
53
+ <Box sx={styles.featureBox}>
54
+ <Box sx={styles.feature}>
55
+ {items.map(({ title }, index) => (
56
+ <Chip
57
+ size="medium"
58
+ key={index}
59
+ label={title}
60
+ onClick={() => handleItemClick(index)}
61
+ selected={selectedItemIdx === index}
62
+ sx={styles.featureChip(selectedItemIdx, index)}
63
+ />
64
+ ))}
65
+ </Box>
66
+ <Card variant="outlined">
67
+ <Box
68
+ sx={(theme) => ({
69
+ ...styles.mobileImageBox,
70
+ backgroundImage: `url("${
71
+ theme.palette.mode === "dark"
72
+ ? selectedFeature.imageDark[
73
+ selectedImageIdx % selectedFeature.imageDark.length
74
+ ]
75
+ : selectedFeature.imageLight[
76
+ selectedImageIdx % selectedFeature.imageLight.length
77
+ ]
78
+ }")`,
79
+ })}
80
+ />
81
+ <Box sx={styles.featureDescription}>
82
+ <Typography gutterBottom sx={styles.textTitle}>
83
+ {selectedFeature.title}
84
+ </Typography>
85
+ <Typography variant="body2" sx={styles.textBody}>
86
+ {selectedFeature.description}
87
+ </Typography>
88
+ </Box>
89
+ </Card>
90
+ </Box>
91
+ );
92
+ };
93
+
94
+ MobileLayout.propTypes = {
95
+ selectedItemIdx: PropTypes.number.isRequired,
96
+ selectedImageIdx: PropTypes.number.isRequired,
97
+ handleItemClick: PropTypes.func.isRequired,
98
+ selectedFeature: PropTypes.object.isRequired,
99
+ };
100
+
101
+ export default function Features() {
102
+ const [selectedItemIdx, setSelectedItemIdx] = useState(0);
103
+ const [selectedImageIdx, setSelectedImageIdx] = useState(0);
104
+
105
+ const handleItemClick = (index) => {
106
+ setSelectedItemIdx(index);
107
+ setSelectedImageIdx(0);
108
+ };
109
+
110
+ const useInterval = (callback, delay) => {
111
+ useEffect(() => {
112
+ const intervalId = setInterval(callback, delay);
113
+ return () => clearInterval(intervalId);
114
+ }, [callback, delay]);
115
+ };
116
+
117
+ useInterval(() => setSelectedImageIdx((prev) => prev + 1), 5000);
118
+
119
+ const selectedFeature = items[selectedItemIdx];
120
+
121
+ return (
122
+ <Container id="features" sx={styles.root}>
123
+ <Box sx={styles.rootBox}>
124
+ <Typography
125
+ component="h2"
126
+ variant="h4"
127
+ gutterBottom
128
+ sx={styles.featureText}
129
+ >
130
+ Features
131
+ </Typography>
132
+ <Typography variant="body1" sx={styles.subtitle}>
133
+ aiXamine provides a unified black-box framework for comprehensive
134
+ evaluation of LLM safety, security, and privacy.
135
+ </Typography>
136
+ </Box>
137
+ <Box sx={styles.mainBox}>
138
+ <div>
139
+ <Box sx={styles.main}>
140
+ {items.map(({ icon, title, description }, index) => (
141
+ <Box
142
+ key={index}
143
+ component={Button}
144
+ onClick={() => handleItemClick(index)}
145
+ sx={[
146
+ (theme) => ({
147
+ p: 2,
148
+ height: "100%",
149
+ width: "100%",
150
+ "&:hover": {
151
+ backgroundColor: theme.palette.action.hover,
152
+ },
153
+ }),
154
+ selectedItemIdx === index && {
155
+ backgroundColor: "action.selected",
156
+ },
157
+ ]}
158
+ >
159
+ <Box
160
+ sx={{
161
+ color:
162
+ selectedItemIdx === index
163
+ ? "text.primary"
164
+ : "text.secondary",
165
+ ...styles.descriptionBox,
166
+ }}
167
+ >
168
+ {icon}
169
+ <Typography variant="h6">{title}</Typography>
170
+ <Typography variant="body2">{description}</Typography>
171
+ </Box>
172
+ </Box>
173
+ ))}
174
+ </Box>
175
+ <MobileLayout
176
+ selectedItemIdx={selectedItemIdx}
177
+ selectedImageIdx={selectedImageIdx}
178
+ handleItemClick={handleItemClick}
179
+ selectedFeature={selectedFeature}
180
+ />
181
+ </div>
182
+ <Box sx={styles.mobileBox}>
183
+ <Card variant="outlined" sx={styles.mobileCard}>
184
+ <Box
185
+ sx={(theme) => ({
186
+ ...styles.desktopImageBox,
187
+ backgroundImage: `url("${
188
+ theme.palette.mode === "dark"
189
+ ? selectedFeature.imageDark[
190
+ selectedImageIdx % selectedFeature.imageDark.length
191
+ ]
192
+ : selectedFeature.imageLight[
193
+ selectedImageIdx % selectedFeature.imageLight.length
194
+ ]
195
+ }")`,
196
+ })}
197
+ />
198
+ </Card>
199
+ </Box>
200
+ </Box>
201
+ </Container>
202
+ );
203
+ }
src/features/landing/Hero/Hero-styles.js ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { alpha } from '@mui/material';
2
+
3
+ export const styles = {
4
+ backgroundBox: {
5
+ width: '100%',
6
+ backgroundImage: (theme) =>
7
+ theme.palette.mode === 'light'
8
+ ? 'linear-gradient(180deg, #CEE5FD, #FFF)'
9
+ : `linear-gradient(#02294F, ${alpha('#090E10', 0.0)})`,
10
+ backgroundSize: { xs: '100% 80px', sm: '100% 96px' },
11
+ backgroundRepeat: 'no-repeat',
12
+ },
13
+ root: {
14
+ display: 'flex',
15
+ flexDirection: 'column',
16
+ alignItems: 'center',
17
+ pt: { xs: 20, sm: 28 },
18
+ },
19
+ description: {
20
+ alignSelf: 'center',
21
+ width: { sm: '100%', md: '80%' },
22
+ textAlign: 'center',
23
+ color: 'text.secondary',
24
+ },
25
+ title: {
26
+ display: 'flex',
27
+ flexDirection: { xs: 'column', md: 'column' },
28
+ alignSelf: 'center',
29
+ textAlign: 'center',
30
+ fontSize: 'clamp(1.25rem, 8vw, 2.5rem)',
31
+ },
32
+ heroStack: { width: { xs: '100%', sm: '70%' } },
33
+ subtitle: {
34
+ fontSize: 'clamp(1.25rem, 8vw, 2.5rem)',
35
+ color: (theme) => (theme.palette.mode === 'light' ? 'primary.main' : 'primary.light'),
36
+ },
37
+ };
38
+
39
+ export default styles;
src/features/landing/Hero/Hero.jsx ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Box, Container, Stack, Typography, Link } from "@mui/material";
2
+
3
+ import styles from "./Hero-styles";
4
+
5
+ export const Hero = () => {
6
+ return (
7
+ <Box id="hero" sx={styles.backgroundBox}>
8
+ <Container sx={styles.root}>
9
+ <Stack spacing={2} useFlexGap sx={styles.title}>
10
+ <Typography variant="h1" sx={styles.title}>
11
+ {/* Safety, Security, &amp; Privacy Evaluation of LLMs */}
12
+ LLM Safety &amp; Security
13
+ {/* &amp; Privacy */}
14
+ <Typography component="span" variant="h1" sx={styles.subtitle}>
15
+ Simplified
16
+ </Typography>
17
+ </Typography>
18
+ <Typography sx={styles.description}>
19
+ Examines LLMs for a wide range of safety risks and security
20
+ vulnerabilities across nine evaluation services spanning Safety &
21
+ Reliability, Security & Robustness, and Privacy & Fairness
22
+ dimensions. You can explore detailed model evaluation reports, or
23
+ submit and run your own examinations. Visit{" "}
24
+ <Link
25
+ href="https://aixamine.qcri.org/main"
26
+ target="_blank"
27
+ rel="noopener noreferrer"
28
+ >
29
+ the production system
30
+ </Link>{" "}
31
+ to get started.
32
+ </Typography>
33
+ </Stack>
34
+ </Container>
35
+ </Box>
36
+ );
37
+ };
38
+
39
+ export default Hero;
src/features/landing/Landing/Landing.jsx ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Box, Divider, Typography } from "@mui/material";
2
+ import { useDispatch } from "react-redux";
3
+
4
+ import { switchThemeMode } from "../../app/appSlice";
5
+
6
+ import AppAppBar from "../AppAppBar/AppAppBar";
7
+ import FAQ from "../FAQ/FAQ";
8
+ import Features from "../Features/Features";
9
+ import Footer from "../../shared/Footer/Footer";
10
+ import Hero from "../Hero/Hero";
11
+ import Services from "../Services/Services";
12
+ import Leaderboard from "../../main/Leaderboard/Leaderboard";
13
+ import Reference from "../Reference/Reference";
14
+
15
+ export function Landing() {
16
+ const dispatch = useDispatch();
17
+
18
+ const onClickThemeMode = () => {
19
+ dispatch(switchThemeMode());
20
+ };
21
+
22
+ return (
23
+ <div>
24
+ <AppAppBar onClickThemeMode={onClickThemeMode} />
25
+ <Hero />
26
+ <Leaderboard />
27
+ <Divider />
28
+ <Services />
29
+ <Features />
30
+ <Divider />
31
+ <FAQ />
32
+ <Reference />
33
+ <Footer />
34
+ </div>
35
+ );
36
+ }
37
+
38
+ export default Landing;
src/features/landing/Reference/Reference-styles.js ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export const styles = {
2
+ root: { width: "100%" },
3
+ referenceBox: { width: "100%" },
4
+ stack: { width: "100%" },
5
+ titleBox: {
6
+ width: { sm: "100%", md: "60%" },
7
+ textAlign: { sm: "left", md: "center" },
8
+ },
9
+ iconBox: { opacity: "50%" },
10
+ rootContainer: {
11
+ position: "relative",
12
+ display: "flex",
13
+ flexDirection: "column",
14
+ alignItems: "center",
15
+ gap: { xs: 3, sm: 6 },
16
+ },
17
+ icon: {
18
+ width: "2.5rem",
19
+ height: "2.5rem",
20
+ },
21
+ contentStack: {
22
+ p: 3,
23
+ color: "inherit",
24
+ height: "100%",
25
+ border: "1px solid",
26
+ borderColor: "grey.800",
27
+ background: "transparent",
28
+ backgroundColor: "grey.900",
29
+ },
30
+ paper: {
31
+ backgroundColor: "grey.900",
32
+ borderColor: "grey.800",
33
+ p: 2.5,
34
+ borderRadius: 2,
35
+ overflowX: "auto",
36
+ maxWidth: "100%",
37
+ },
38
+ citation: {
39
+ m: 0,
40
+ fontFamily: "monospace",
41
+ fontSize: "0.9rem",
42
+ color: "grey.300",
43
+ whiteSpace: "pre",
44
+ },
45
+ description: { color: "grey.400" },
46
+ };
47
+
48
+ export default styles;
src/features/landing/Reference/Reference.jsx ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Container, Box, Typography, Paper, Stack } from "@mui/material";
2
+ import styles from "./Reference-styles";
3
+
4
+ const Reference = () => {
5
+ return (
6
+ <Container id="reference" sx={styles.root}>
7
+ <Box sx={styles.referenceBox}>
8
+ <Stack spacing={2} sx={styles.stack}>
9
+ <Typography
10
+ variant="h4"
11
+ sx={{
12
+ letterSpacing: "0.5px",
13
+ }}
14
+ >
15
+ Reference
16
+ </Typography>
17
+ <Paper variant="outlined" sx={styles.paper}>
18
+ <Typography component="pre" sx={styles.citation}>
19
+ {`@article{aixamine2025,
20
+ title={aiXamine: Simplified LLM Safety and Security},
21
+ author={Deniz, Fatih and Popovic, Dorde and Boshmaf, Yazan
22
+ and Jeong, Euisuh and Ahmad, Minhaj
23
+ and Chawla, Sanjay and Khalil, Issa},
24
+ journal={arXiv preprint arXiv:2504.14985},
25
+ year={2025}
26
+ }`}
27
+ </Typography>
28
+ </Paper>
29
+ <Typography sx={{ color: "grey.500", mt: 1 }}>
30
+ Please cite aiXamine if you use our framework in your research.
31
+ </Typography>
32
+ </Stack>
33
+ </Box>
34
+ </Container>
35
+ );
36
+ };
37
+
38
+ export default Reference;
src/features/landing/Services/Services-styles.js ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export const styles = {
2
+ root: {
3
+ pt: { xs: 4, sm: 12 },
4
+ pb: { xs: 8, sm: 16 },
5
+ color: 'white',
6
+ bgcolor: '#06090a',
7
+ },
8
+ titleBox: {
9
+ width: { sm: '100%', md: '60%' },
10
+ textAlign: { sm: 'left', md: 'center' },
11
+ },
12
+ iconBox: { opacity: '50%' },
13
+ rootContainer: {
14
+ position: 'relative',
15
+ display: 'flex',
16
+ flexDirection: 'column',
17
+ alignItems: 'center',
18
+ gap: { xs: 3, sm: 6 },
19
+ },
20
+ icon: {
21
+ width: '2.5rem',
22
+ height: '2.5rem',
23
+ },
24
+ contentStack: {
25
+ p: 3,
26
+ color: 'inherit',
27
+ height: '100%',
28
+ border: '1px solid',
29
+ borderColor: 'grey.800',
30
+ background: 'transparent',
31
+ backgroundColor: 'grey.900',
32
+ },
33
+ description: { color: 'grey.400' },
34
+ };
35
+
36
+ export default styles;
src/features/landing/Services/Services.jsx ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Box, Card, Container, Grid, Stack, Typography } from "@mui/material";
2
+ import {
3
+ AutoAwesome,
4
+ Balance,
5
+ BarChart,
6
+ DoNotDisturbOn,
7
+ Group,
8
+ LensBlur,
9
+ NoEncryption,
10
+ Policy,
11
+ RemoveRedEye,
12
+ Security,
13
+ Terminal,
14
+ } from "@mui/icons-material";
15
+
16
+ import styles from "./Services-styles";
17
+ import { useGetServiceStatsQuery } from "../../main/statApi";
18
+
19
+ export const Services = () => {
20
+ const { data: statServices = [] } = useGetServiceStatsQuery({
21
+ omitTest: true,
22
+ });
23
+
24
+ const services = Array.isArray(statServices.services)
25
+ ? statServices.services
26
+ : [];
27
+
28
+ const serviceIcon = {
29
+ "adversarial-robustness": <Security style={styles.icon} />,
30
+ "backdoor-detection": <RemoveRedEye style={styles.icon} />,
31
+ "code-security": <Terminal style={styles.icon} />,
32
+ "fairness-bias": <Balance style={styles.icon} />,
33
+ hallucination: <LensBlur style={styles.icon} />,
34
+ jailbreak: <NoEncryption style={styles.icon} />,
35
+ "model-data-privacy": <Policy style={styles.icon} />,
36
+ "ood-robustness": <BarChart style={styles.icon} />,
37
+ "over-refusal": <DoNotDisturbOn style={styles.icon} />,
38
+ "safety-alignment": <Group style={styles.icon} />,
39
+ };
40
+
41
+ const title = (
42
+ <Box sx={styles.titleBox}>
43
+ <Typography component="h2" variant="h4">
44
+ Services
45
+ </Typography>
46
+ <Typography variant="body1" sx={styles.description}>
47
+ aiXamine provides a comprehensive set of services to evaluate your LLM
48
+ against a broad range of evolving safety risks and security threats.
49
+ </Typography>
50
+ </Box>
51
+ );
52
+
53
+ const content = (
54
+ <Grid container spacing={2.5}>
55
+ {services.map((item, idx) => (
56
+ <Grid item xs={12} sm={6} md={4} key={idx}>
57
+ <Stack
58
+ direction="column"
59
+ component={Card}
60
+ spacing={1}
61
+ useFlexGap
62
+ sx={styles.contentStack}
63
+ >
64
+ <Box sx={styles.iconBox}>
65
+ {serviceIcon[item.value] ?? <Group style={styles.icon} />}
66
+ </Box>
67
+ <div>
68
+ <Typography fontWeight="medium" gutterBottom>
69
+ {item.name}
70
+ </Typography>
71
+ <Typography variant="body2" sx={styles.description}>
72
+ {item.description}
73
+ </Typography>
74
+ </div>
75
+ </Stack>
76
+ </Grid>
77
+ ))}
78
+ <Grid item xs={12} sm={6} md={4}>
79
+ <Stack
80
+ direction="column"
81
+ component={Card}
82
+ spacing={1}
83
+ useFlexGap
84
+ sx={styles.contentStack}
85
+ >
86
+ <Box sx={styles.iconBox}>
87
+ <AutoAwesome style={styles.icon} />
88
+ </Box>
89
+ <div>
90
+ <Typography fontWeight="medium" gutterBottom>
91
+ Coming Soon
92
+ </Typography>
93
+ <Typography variant="body2" sx={styles.description}>
94
+ More services are in development and will be available soon.
95
+ </Typography>
96
+ </div>
97
+ </Stack>
98
+ </Grid>
99
+ </Grid>
100
+ );
101
+
102
+ return (
103
+ <Box id="services" sx={styles.root}>
104
+ <Container sx={styles.rootContainer}>
105
+ {title}
106
+ {content}
107
+ </Container>
108
+ </Box>
109
+ );
110
+ };
111
+
112
+ export default Services;
src/features/main/CompareModels/CompareModels-styles.js ADDED
@@ -0,0 +1,282 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { alpha } from "@mui/material";
2
+ import { brand, gray } from "../../../constants/theme";
3
+
4
+ const selectMenuBase = (theme) => ({
5
+ maxHeight: 300,
6
+ color:
7
+ theme.palette.mode === "dark"
8
+ ? "rgba(76, 89, 103, 0.3)"
9
+ : "rgba(191, 204, 217, 0.5)",
10
+ backgroundColor: theme.palette.mode === "dark" ? "black" : "white",
11
+ boxShadow:
12
+ theme.palette.mode === "dark"
13
+ ? "0 0 1px rgba(2, 31, 59, 0.7), 1px 1.5px 2px -1px rgba(2, 31, 59, 0.65), 4px 4px 12px -2.5px rgba(2, 31, 59, 0.65)"
14
+ : "0 0 1px rgba(85, 166, 246, 0.1), 1px 1.5px 2px -1px rgba(85, 166, 246, 0.15), 4px 4px 12px -2.5px rgba(85, 166, 246, 0.15)",
15
+ });
16
+
17
+ export const styles = {
18
+ // root
19
+ root: {
20
+ display: "flex",
21
+ flexDirection: "column",
22
+ justifyContent: "space-between",
23
+ alignItems: "center",
24
+ width: "100%",
25
+ pt: { xs: 4, sm: 12 },
26
+ pb: { xs: 8, sm: 16 },
27
+ },
28
+ cardContainer: { height: "100%" },
29
+ circularBox: {
30
+ display: "flex",
31
+ justifyContent: "center",
32
+ alignItems: "center",
33
+ height: "200px",
34
+ },
35
+ disclaimerText: {
36
+ color: gray[400],
37
+ textAlign: "left",
38
+ pt: 2,
39
+ pl: 0.5,
40
+ pb: 2,
41
+ },
42
+
43
+ // datagrid
44
+ formBox: {
45
+ display: "flex",
46
+ justifyContent: "space-between",
47
+ alignItems: "flex-start",
48
+ width: "100%",
49
+ mb: 2,
50
+ },
51
+ formCard: { p: 3, overflow: "auto", width: "100%" },
52
+ landingText: { color: "text.primary" },
53
+
54
+ formTitle: { fontSize: "22.5px", mb: 2 },
55
+ titleLink: { "&:before": { height: 0 } },
56
+ datagrid: {
57
+ "& .MuiDataGrid-columnHeaderTitle": {
58
+ fontWeight: "600",
59
+ },
60
+ "& .MuiDataGrid-columnHeader": {
61
+ backgroundColor: (theme) =>
62
+ theme.palette.mode === "light"
63
+ ? alpha(brand[500], 0.08)
64
+ : alpha(brand[400], 0.16),
65
+ borderBottom: "2px solid",
66
+ borderColor: (theme) => theme.palette.divider,
67
+ "&:hover": { outline: "none" },
68
+ "&:focus-within": { outline: "none" },
69
+ },
70
+ "& .MuiDataGrid-cell": {
71
+ "&:hover": { outline: "none" },
72
+ "&:focus-within": { outline: "none" },
73
+ },
74
+ "& .MuiDataGrid-cellContent": {
75
+ overflow: "hidden",
76
+ textOverflow: "ellipsis",
77
+ whiteSpace: "nowrap",
78
+ minWidth: 0,
79
+ },
80
+ "& .MuiDataGrid-main": {
81
+ borderRadius: "10px",
82
+ border: "1px solid",
83
+ borderColor: (theme) =>
84
+ theme.palette.mode === "light"
85
+ ? alpha(brand[500], 0.08)
86
+ : alpha(brand[400], 0.16),
87
+ },
88
+ "& .MuiDataGrid-row": {
89
+ bgcolor: (theme) =>
90
+ theme.palette.mode === "light"
91
+ ? "rgba(255, 255, 255, 0.4)"
92
+ : "rgba(0, 0, 0, 0.4)",
93
+ },
94
+ "& .MuiDataGrid-row:hover": {
95
+ cursor: "pointer",
96
+ },
97
+ },
98
+ datagridFooterIcon: { fontSize: "18px" },
99
+ datagridScoreCell: {
100
+ textAlign: "right",
101
+ display: "block",
102
+ width: "100%",
103
+ overflow: "hidden",
104
+ textOverflow: "ellipsis",
105
+ whiteSpace: "nowrap",
106
+ },
107
+ datagridModelName: {
108
+ display: "flex",
109
+ alignItems: "center",
110
+ width: "100%",
111
+ minWidth: 0,
112
+ gap: 0.5,
113
+ },
114
+ datagridModelText: {
115
+ flex: 1,
116
+ minWidth: 0,
117
+ overflow: "hidden",
118
+ textOverflow: "ellipsis",
119
+ whiteSpace: "nowrap",
120
+ },
121
+ datagridModelIcon: { width: 16, height: 16, marginRight: 4 },
122
+
123
+ select: (theme) => ({
124
+ borderRadius: "10px",
125
+ height: "40px",
126
+ "& .MuiSelect-select": {
127
+ borderColor:
128
+ theme.palette.mode === "dark"
129
+ ? "rgba(76, 89, 103, 0.3)"
130
+ : "rgba(191, 204, 217, 0.5)",
131
+ backgroundColor: "transparent",
132
+ whiteSpace: "nowrap",
133
+ },
134
+ }),
135
+ selectMenu: selectMenuBase,
136
+ selectMenuModels: (theme) => ({
137
+ ...selectMenuBase(theme),
138
+ width: "auto",
139
+ [theme.breakpoints.up("lg")]: {
140
+ width: "28ch",
141
+ },
142
+ }),
143
+ selectMenuServices: (theme) => ({
144
+ ...selectMenuBase(theme),
145
+ width: "auto",
146
+ [theme.breakpoints.up("lg")]: {
147
+ width: "30ch",
148
+ },
149
+ }),
150
+ selectMenuItem: (theme) => ({
151
+ height: "32px",
152
+ minHeight: "32px",
153
+ borderColor:
154
+ theme.palette.mode === "dark"
155
+ ? "rgba(76, 89, 103, 0.3)"
156
+ : "rgba(191, 204, 217, 0.5)",
157
+ backgroundColor: theme.palette.mode === "dark" ? "black" : "white",
158
+ "&.Mui-selected": {
159
+ backgroundColor: theme.palette.mode === "dark" ? "black" : "white",
160
+ },
161
+ }),
162
+ selectMenuServiceItem: (theme) => ({
163
+ height: "32px",
164
+ minHeight: "32px",
165
+ maxHeight: "32px",
166
+ pl: 4,
167
+ borderColor:
168
+ theme.palette.mode === "dark"
169
+ ? "rgba(76, 89, 103, 0.3)"
170
+ : "rgba(191, 204, 217, 0.5)",
171
+ backgroundColor: theme.palette.mode === "dark" ? "black" : "white",
172
+ "&.Mui-selected": {
173
+ backgroundColor: theme.palette.mode === "dark" ? "black" : "white",
174
+ },
175
+ }),
176
+
177
+ filterFormSources: { width: { xs: "100%", lg: "18ch" } },
178
+ filterFormModels: { width: { xs: "100%", lg: "26ch" } },
179
+ filterFormServices: { width: { xs: "100%", lg: "30ch" } },
180
+ filterFormCheck: { padding: "4px" },
181
+ filterLabel: {
182
+ display: "flex",
183
+ alignItems: "center",
184
+ fontWeight: 500,
185
+ },
186
+ filterControlGroup: (theme) => ({
187
+ display: "flex",
188
+ alignItems: "center",
189
+ gap: 0.75,
190
+ flex: "0 0 auto",
191
+ [theme.breakpoints.down("lg")]: {
192
+ width: "100%",
193
+ flex: "1 1 100%",
194
+ },
195
+ }),
196
+ filterStepBadge: (theme) => {
197
+ const background = theme.palette.mode === "light" ? brand[500] : brand[300];
198
+ return {
199
+ display: "inline-flex",
200
+ alignItems: "center",
201
+ justifyContent: "center",
202
+ width: 28,
203
+ height: 28,
204
+ borderRadius: "50%",
205
+ fontSize: 14,
206
+ fontWeight: 600,
207
+ lineHeight: 1,
208
+ flexShrink: 0,
209
+ backgroundColor: background,
210
+ color: theme.palette.getContrastText(background),
211
+ };
212
+ },
213
+ filterPanel: (theme) => ({
214
+ mb: 2,
215
+ display: "flex",
216
+ alignItems: "center",
217
+ flexDirection: "row",
218
+ flexWrap: "nowrap",
219
+ columnGap: theme.spacing(3),
220
+ [theme.breakpoints.down("lg")]: {
221
+ flexDirection: "column",
222
+ alignItems: "stretch",
223
+ rowGap: 2,
224
+ },
225
+ }),
226
+
227
+ gridContainer: { overflow: "auto", mt: -1 },
228
+ gridItem: { mt: 1 },
229
+ learnMoreLink: {
230
+ color: brand[300],
231
+ },
232
+
233
+ // report
234
+ reportCard: { p: 3, overflow: "auto", flex: 1, minHeight: "755px" },
235
+ reportList: {
236
+ pt: 0,
237
+ mb: -1,
238
+ backgroundColor: "transparent",
239
+ },
240
+ reportListSubheader: {
241
+ color: (theme) =>
242
+ theme.palette.mode === "light" ? brand[500] : brand[300],
243
+ fontWeight: 500,
244
+ px: 0,
245
+ mt: -1.75,
246
+ mb: -1,
247
+ fontSize: "16px",
248
+ },
249
+
250
+ // metric
251
+ metricBox: { textAlign: "center" },
252
+ metricCard: { p: 3, overflow: "auto", width: "100%", mb: { xs: 3, sm: 4 } },
253
+ metricLabel: { fontSize: "14px", color: "text.secondary" },
254
+ metricLabelBox: { display: "flex", alignItems: "flex-start", fontSize: 14 },
255
+ metricLabelIcon: { ml: 0.5, fontSize: 14 },
256
+ metricNumber: {
257
+ fontSize: "18px",
258
+ color: (theme) =>
259
+ theme.palette.mode === "light" ? brand[500] : brand[300],
260
+ },
261
+
262
+ searchField: {
263
+ mb: { xs: 3, sm: 4 },
264
+ borderRadius: "10px",
265
+ boxShadow: (theme) =>
266
+ theme.palette.mode === "light"
267
+ ? `0 0 1px rgba(85, 166, 246, 0.1), 1px 1.5px 2px -1px rgba(85, 166, 246, 0.15), 4px 4px 12px -2.5px rgba(85, 166, 246, 0.15)`
268
+ : "0 0 1px rgba(2, 31, 59, 0.7), 1px 1.5px 2px -1px rgba(2, 31, 59, 0.65), 4px 4px 12px -2.5px rgba(2, 31, 59, 0.65)",
269
+ },
270
+ searchFieldIcon: { ml: -1 },
271
+ searchFieldCloseIcon: { mr: -1 },
272
+
273
+ tooltipBox: { display: "flex", alignItems: "center" },
274
+ tooltipText: {
275
+ ml: 1,
276
+ whiteSpace: "nowrap",
277
+ overflow: "hidden",
278
+ textOverflow: "ellipsis",
279
+ },
280
+ };
281
+
282
+ export default styles;
src/features/main/CompareModels/CompareModelsFullDialog/CompareModelsFullDialog-styles.js ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { alpha } from '@mui/material';
2
+ import { brand } from '../../../../constants/theme';
3
+
4
+ export const styles = {
5
+ circularBox: { display: 'flex', justifyContent: 'center', alignItems: 'center', height: '200px' },
6
+ closeButton: { p: 0 },
7
+
8
+ // datagrid
9
+ formBox: {
10
+ display: 'flex',
11
+ justifyContent: 'space-between',
12
+ alignItems: 'center',
13
+ width: '100%',
14
+ mb: 2,
15
+ },
16
+
17
+ // dialog
18
+ dialogRoot: { '& .MuiDialog-paper': { padding: { sm: 1, md: 2 } }, backdropFilter: 'blur(24px)' },
19
+ dialogPaper: {
20
+ margin: '40px',
21
+ width: 'calc(100% - 80px)',
22
+ height: 'calc(100% - 80px)',
23
+ maxWidth: 'calc(100% - 80px)',
24
+ maxHeight: 'calc(100% - 80px)',
25
+
26
+ backdropFilter: 'blur(24px)',
27
+ bgcolor: (theme) =>
28
+ theme.palette.mode === 'light' ? 'rgba(255, 255, 255, 1)' : 'rgba(0, 0, 0, 0.4)',
29
+ boxShadow: (theme) =>
30
+ theme.palette.mode === 'light'
31
+ ? `0 0 1px rgba(85, 166, 246, 0.1), 1px 1.5px 2px -1px rgba(85, 166, 246, 0.15), 4px 4px 12px -2.5px rgba(85, 166, 246, 0.15)`
32
+ : '0 0 1px rgba(2, 31, 59, 0.7), 1px 1.5px 2px -1px rgba(2, 31, 59, 0.65), 4px 4px 12px -2.5px rgba(2, 31, 59, 0.65)',
33
+ borderRadius: '10px',
34
+ border: '1px solid',
35
+ borderColor: 'divider',
36
+ backgroundImage: 'none',
37
+ },
38
+
39
+ formTitle: { fontSize: '22.5px' },
40
+ datagrid: {
41
+ '& .MuiDataGrid-columnHeaderTitle': {
42
+ fontWeight: '600',
43
+ },
44
+ '& .MuiDataGrid-columnHeader': {
45
+ backgroundColor: (theme) =>
46
+ theme.palette.mode === 'light' ? alpha(brand[500], 0.08) : alpha(brand[400], 0.16),
47
+ borderBottom: '2px solid',
48
+ borderColor: (theme) => theme.palette.divider,
49
+ '&:hover': { outline: 'none' },
50
+ '&:focus-within': { outline: 'none' },
51
+ },
52
+ '& .MuiDataGrid-cell': {
53
+ '&:hover': { outline: 'none' },
54
+ '&:focus-within': { outline: 'none' },
55
+ },
56
+ '& .MuiDataGrid-cellContent': {
57
+ overflow: 'hidden',
58
+ textOverflow: 'ellipsis',
59
+ whiteSpace: 'nowrap',
60
+ minWidth: 0,
61
+ },
62
+ '& .MuiDataGrid-main': {
63
+ borderRadius: '10px',
64
+ border: '1px solid',
65
+ borderColor: (theme) =>
66
+ theme.palette.mode === 'light' ? alpha(brand[500], 0.08) : alpha(brand[400], 0.16),
67
+ },
68
+ '& .MuiDataGrid-row': {
69
+ bgcolor: (theme) =>
70
+ theme.palette.mode === 'light' ? 'rgba(255, 255, 255, 0.4)' : 'rgba(0, 0, 0, 0.4)',
71
+ },
72
+ },
73
+ datagridFooterIcon: { fontSize: '18px' },
74
+ datagridScoreCell: {
75
+ textAlign: 'right',
76
+ display: 'block',
77
+ width: '100%',
78
+ overflow: 'hidden',
79
+ textOverflow: 'ellipsis',
80
+ whiteSpace: 'nowrap',
81
+ },
82
+ datagridModelName: {
83
+ display: 'flex',
84
+ alignItems: 'center',
85
+ width: '100%',
86
+ minWidth: 0,
87
+ gap: 0.5,
88
+ },
89
+ datagridModelText: {
90
+ flex: 1,
91
+ minWidth: 0,
92
+ overflow: 'hidden',
93
+ textOverflow: 'ellipsis',
94
+ whiteSpace: 'nowrap',
95
+ },
96
+ datagridModelIcon: { width: 16, height: 16, marginRight: 4 },
97
+
98
+ select: (theme) => ({
99
+ borderRadius: '10px',
100
+ height: '40px',
101
+ '& .MuiSelect-select': {
102
+ borderColor:
103
+ theme.palette.mode === 'dark' ? 'rgba(76, 89, 103, 0.3)' : 'rgba(191, 204, 217, 0.5)',
104
+ backgroundColor: 'transparent',
105
+ },
106
+ }),
107
+ selectMenu: (theme) => ({
108
+ maxHeight: 300,
109
+ color: theme.palette.mode === 'dark' ? 'rgba(76, 89, 103, 0.3)' : 'rgba(191, 204, 217, 0.5)',
110
+ backgroundColor: theme.palette.mode === 'dark' ? 'black' : 'white',
111
+ boxShadow:
112
+ theme.palette.mode === 'dark'
113
+ ? '0 0 1px rgba(2, 31, 59, 0.7), 1px 1.5px 2px -1px rgba(2, 31, 59, 0.65), 4px 4px 12px -2.5px rgba(2, 31, 59, 0.65)'
114
+ : '0 0 1px rgba(85, 166, 246, 0.1), 1px 1.5px 2px -1px rgba(85, 166, 246, 0.15), 4px 4px 12px -2.5px rgba(85, 166, 246, 0.15)',
115
+ }),
116
+ selectMenuItem: (theme) => ({
117
+ height: '32px',
118
+ minHeight: '32px',
119
+ borderColor:
120
+ theme.palette.mode === 'dark' ? 'rgba(76, 89, 103, 0.3)' : 'rgba(191, 204, 217, 0.5)',
121
+ backgroundColor: theme.palette.mode === 'dark' ? 'black' : 'white',
122
+ '&.Mui-selected': {
123
+ backgroundColor: theme.palette.mode === 'dark' ? 'black' : 'white',
124
+ },
125
+ }),
126
+ selectMenuServiceItem: (theme) => ({
127
+ height: '32px',
128
+ minHeight: '32px',
129
+ maxHeight: '32px',
130
+ pl: 4,
131
+ borderColor:
132
+ theme.palette.mode === 'dark' ? 'rgba(76, 89, 103, 0.3)' : 'rgba(191, 204, 217, 0.5)',
133
+ backgroundColor: theme.palette.mode === 'dark' ? 'black' : 'white',
134
+ '&.Mui-selected': {
135
+ backgroundColor: theme.palette.mode === 'dark' ? 'black' : 'white',
136
+ },
137
+ }),
138
+
139
+ filterForm: { minWidth: 180 },
140
+ filterFormCheck: { padding: '4px' },
141
+ filterPanel: (theme) => ({
142
+ mb: 2,
143
+ display: 'flex',
144
+ alignItems: 'center',
145
+ flexDirection: 'row',
146
+ flexWrap: 'nowrap',
147
+ columnGap: theme.spacing(3),
148
+ [theme.breakpoints.down('lg')]: {
149
+ flexDirection: 'column',
150
+ alignItems: 'stretch',
151
+ rowGap: 2,
152
+ },
153
+ }),
154
+ filterControlGroup: (theme) => ({
155
+ display: 'flex',
156
+ alignItems: 'center',
157
+ gap: 0.75,
158
+ flex: '0 0 auto',
159
+ [theme.breakpoints.down('lg')]: {
160
+ width: '100%',
161
+ flex: '1 1 100%',
162
+ },
163
+ }),
164
+ filterStepBadge: (theme) => {
165
+ const background = theme.palette.mode === 'light' ? brand[500] : brand[300];
166
+ return {
167
+ display: 'inline-flex',
168
+ alignItems: 'center',
169
+ justifyContent: 'center',
170
+ width: 28,
171
+ height: 28,
172
+ borderRadius: '50%',
173
+ fontSize: 14,
174
+ fontWeight: 600,
175
+ lineHeight: 1,
176
+ flexShrink: 0,
177
+ backgroundColor: background,
178
+ color: theme.palette.getContrastText(background),
179
+ };
180
+ },
181
+ };
182
+
183
+ export default styles;