diff --git a/src/frontend/index.html b/src/frontend/index.html
index f4720d6d..d6a3eff2 100644
--- a/src/frontend/index.html
+++ b/src/frontend/index.html
@@ -6,22 +6,6 @@
-
-
-
-
%VITE_APP_TITLE%
diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json
index 8f6d7ff5..9fee6bd1 100644
--- a/src/frontend/package-lock.json
+++ b/src/frontend/package-lock.json
@@ -10,8 +10,6 @@
"dependencies": {
"@fontsource-variable/atkinson-hyperlegible-next": "5.2.6",
"@fontsource-variable/lexend": "5.2.11",
- "@fontsource-variable/material-symbols-outlined": "5.2.34",
- "@fontsource/material-icons-outlined": "5.2.6",
"@fontsource/opendyslexic": "5.2.5",
"@livekit/components-react": "2.9.19",
"@livekit/components-styles": "1.2.0",
@@ -61,6 +59,7 @@
"rollup-plugin-visualizer": "7.0.1",
"typescript": "5.8.3",
"vite": "7.3.2",
+ "vite-plugin-svgr": "5.2.0",
"vite-tsconfig-paths": "6.1.1"
}
},
@@ -1012,24 +1011,6 @@
"url": "https://github.com/sponsors/ayuhito"
}
},
- "node_modules/@fontsource-variable/material-symbols-outlined": {
- "version": "5.2.34",
- "resolved": "https://registry.npmjs.org/@fontsource-variable/material-symbols-outlined/-/material-symbols-outlined-5.2.34.tgz",
- "integrity": "sha512-FfJVoKdYlxvIHflVZlrevf6e1WmbJGeAdbcLmOmkftoP2Du0qrRD/SmNchIXijsKXF7j3z1m++e92Qi6/U+jRA==",
- "license": "OFL-1.1",
- "funding": {
- "url": "https://github.com/sponsors/ayuhito"
- }
- },
- "node_modules/@fontsource/material-icons-outlined": {
- "version": "5.2.6",
- "resolved": "https://registry.npmjs.org/@fontsource/material-icons-outlined/-/material-icons-outlined-5.2.6.tgz",
- "integrity": "sha512-99XKAkwnCg0s0/ywax+o3m01HSNM5gGzSBw+WnlWG2+WY3wOjcN+wXMfm4zP37Yme7Yze2DvKxF78tHWOrlwFw==",
- "license": "OFL-1.1",
- "funding": {
- "url": "https://github.com/sponsors/ayuhito"
- }
- },
"node_modules/@fontsource/opendyslexic": {
"version": "5.2.5",
"resolved": "https://registry.npmjs.org/@fontsource/opendyslexic/-/opendyslexic-5.2.5.tgz",
@@ -4106,6 +4087,42 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@rollup/pluginutils": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz",
+ "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "estree-walker": "^2.0.2",
+ "picomatch": "^4.0.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
+ },
+ "peerDependenciesMeta": {
+ "rollup": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rollup/pluginutils/node_modules/picomatch": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
+ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz",
@@ -4456,6 +4473,231 @@
"win32"
]
},
+ "node_modules/@svgr/babel-plugin-add-jsx-attribute": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz",
+ "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/babel-plugin-remove-jsx-attribute": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz",
+ "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz",
+ "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz",
+ "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/babel-plugin-svg-dynamic-title": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz",
+ "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/babel-plugin-svg-em-dimensions": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz",
+ "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/babel-plugin-transform-react-native-svg": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz",
+ "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/babel-plugin-transform-svg-component": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz",
+ "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/babel-preset": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz",
+ "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@svgr/babel-plugin-add-jsx-attribute": "8.0.0",
+ "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0",
+ "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0",
+ "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0",
+ "@svgr/babel-plugin-svg-dynamic-title": "8.0.0",
+ "@svgr/babel-plugin-svg-em-dimensions": "8.0.0",
+ "@svgr/babel-plugin-transform-react-native-svg": "8.1.0",
+ "@svgr/babel-plugin-transform-svg-component": "8.0.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/core": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz",
+ "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.21.3",
+ "@svgr/babel-preset": "8.1.0",
+ "camelcase": "^6.2.0",
+ "cosmiconfig": "^8.1.3",
+ "snake-case": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ }
+ },
+ "node_modules/@svgr/hast-util-to-babel-ast": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz",
+ "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.21.3",
+ "entities": "^4.4.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ }
+ },
+ "node_modules/@svgr/plugin-jsx": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz",
+ "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.21.3",
+ "@svgr/babel-preset": "8.1.0",
+ "@svgr/hast-util-to-babel-ast": "8.0.0",
+ "svg-parser": "^2.0.4"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@svgr/core": "*"
+ }
+ },
"node_modules/@swc/helpers": {
"version": "0.5.13",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.13.tgz",
@@ -5880,6 +6122,19 @@
"node": ">=6"
}
},
+ "node_modules/camelcase": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/caniuse-lite": {
"version": "1.0.30001774",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001774.tgz",
@@ -6190,6 +6445,33 @@
"url": "https://opencollective.com/express"
}
},
+ "node_modules/cosmiconfig": {
+ "version": "8.3.6",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz",
+ "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "import-fresh": "^3.3.0",
+ "js-yaml": "^4.1.0",
+ "parse-json": "^5.2.0",
+ "path-type": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/d-fischer"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.9.5"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
"node_modules/crisp-sdk-web": {
"version": "1.0.27",
"resolved": "https://registry.npmjs.org/crisp-sdk-web/-/crisp-sdk-web-1.0.27.tgz",
@@ -6543,6 +6825,17 @@
"url": "https://github.com/fb55/domutils?sponsor=1"
}
},
+ "node_modules/dot-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
+ "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "no-case": "^3.0.4",
+ "tslib": "^2.0.3"
+ }
+ },
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
@@ -6621,6 +6914,16 @@
"resolved": "https://registry.npmjs.org/eol/-/eol-0.9.1.tgz",
"integrity": "sha512-Ds/TEoZjwggRoz/Q2O7SE3i4Jm66mqTDfmdHdq/7DKVk3bro9Q8h6WdXKdPqFLMoqxrDK5SVRzHVPOS6uuGtrg=="
},
+ "node_modules/error-ex": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz",
+ "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
"node_modules/es-abstract": {
"version": "1.23.3",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz",
@@ -8250,6 +8553,13 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/is-bigint": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
@@ -8643,6 +8953,13 @@
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
"dev": true
},
+ "node_modules/json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
@@ -9032,6 +9349,13 @@
"url": "https://github.com/sponsors/antonk52"
}
},
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/livekit-client": {
"version": "2.17.1",
"resolved": "https://registry.npmjs.org/livekit-client/-/livekit-client-2.17.1.tgz",
@@ -9138,6 +9462,16 @@
"loose-envify": "cli.js"
}
},
+ "node_modules/lower-case": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
+ "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.3"
+ }
+ },
"node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -9352,6 +9686,17 @@
"node": ">= 0.6"
}
},
+ "node_modules/no-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
+ "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lower-case": "^2.0.2",
+ "tslib": "^2.0.3"
+ }
+ },
"node_modules/node-eval": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/node-eval/-/node-eval-2.0.0.tgz",
@@ -9610,6 +9955,25 @@
"node": ">=6"
}
},
+ "node_modules/parse-json": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+ "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/parse5": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz",
@@ -9702,6 +10066,16 @@
"url": "https://opencollective.com/express"
}
},
+ "node_modules/path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/pathe": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
@@ -10928,6 +11302,17 @@
"url": "https://github.com/chalk/slice-ansi?sponsor=1"
}
},
+ "node_modules/snake-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz",
+ "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dot-case": "^3.0.4",
+ "tslib": "^2.0.3"
+ }
+ },
"node_modules/sort-keys": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-5.1.0.tgz",
@@ -11115,6 +11500,13 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/svg-parser": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz",
+ "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/symlink-or-copy": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/symlink-or-copy/-/symlink-or-copy-1.3.1.tgz",
@@ -11823,6 +12215,21 @@
}
}
},
+ "node_modules/vite-plugin-svgr": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/vite-plugin-svgr/-/vite-plugin-svgr-5.2.0.tgz",
+ "integrity": "sha512-qj2eAKF8C6PZWemVTvQA0xgQIcP1hHU6Buh7fl6BhvayWwnuxE+z417miKxeDvRWbDrupQ1oK99hfxElopJ3sQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rollup/pluginutils": "^5.3.0",
+ "@svgr/core": "^8.1.0",
+ "@svgr/plugin-jsx": "^8.1.0"
+ },
+ "peerDependencies": {
+ "vite": ">=3.0.0"
+ }
+ },
"node_modules/vite-tsconfig-paths": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-6.1.1.tgz",
diff --git a/src/frontend/package.json b/src/frontend/package.json
index 993753f6..d3f8033a 100644
--- a/src/frontend/package.json
+++ b/src/frontend/package.json
@@ -16,8 +16,6 @@
"dependencies": {
"@fontsource-variable/atkinson-hyperlegible-next": "5.2.6",
"@fontsource-variable/lexend": "5.2.11",
- "@fontsource-variable/material-symbols-outlined": "5.2.34",
- "@fontsource/material-icons-outlined": "5.2.6",
"@fontsource/opendyslexic": "5.2.5",
"@livekit/components-react": "2.9.19",
"@livekit/components-styles": "1.2.0",
@@ -67,6 +65,7 @@
"rollup-plugin-visualizer": "7.0.1",
"typescript": "5.8.3",
"vite": "7.3.2",
+ "vite-plugin-svgr": "5.2.0",
"vite-tsconfig-paths": "6.1.1"
}
}
diff --git a/src/frontend/src/assets/icons/article.svg b/src/frontend/src/assets/icons/article.svg
new file mode 100644
index 00000000..9cd25255
--- /dev/null
+++ b/src/frontend/src/assets/icons/article.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/frontend/src/assets/icons/chevron_forward.svg b/src/frontend/src/assets/icons/chevron_forward.svg
new file mode 100644
index 00000000..976b2ae4
--- /dev/null
+++ b/src/frontend/src/assets/icons/chevron_forward.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/frontend/src/assets/icons/cloud_download.svg b/src/frontend/src/assets/icons/cloud_download.svg
new file mode 100644
index 00000000..d45089bc
--- /dev/null
+++ b/src/frontend/src/assets/icons/cloud_download.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/frontend/src/assets/icons/language.svg b/src/frontend/src/assets/icons/language.svg
new file mode 100644
index 00000000..f851a272
--- /dev/null
+++ b/src/frontend/src/assets/icons/language.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/frontend/src/assets/icons/login.svg b/src/frontend/src/assets/icons/login.svg
new file mode 100644
index 00000000..ddc929be
--- /dev/null
+++ b/src/frontend/src/assets/icons/login.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/frontend/src/assets/icons/mail.svg b/src/frontend/src/assets/icons/mail.svg
new file mode 100644
index 00000000..82e6eb1e
--- /dev/null
+++ b/src/frontend/src/assets/icons/mail.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/frontend/src/assets/icons/mode_standby.svg b/src/frontend/src/assets/icons/mode_standby.svg
new file mode 100644
index 00000000..f9c74c99
--- /dev/null
+++ b/src/frontend/src/assets/icons/mode_standby.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/frontend/src/assets/icons/person_raised_hand.svg b/src/frontend/src/assets/icons/person_raised_hand.svg
new file mode 100644
index 00000000..4b89fe3a
--- /dev/null
+++ b/src/frontend/src/assets/icons/person_raised_hand.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/frontend/src/assets/icons/screen_record.svg b/src/frontend/src/assets/icons/screen_record.svg
new file mode 100644
index 00000000..1934b235
--- /dev/null
+++ b/src/frontend/src/assets/icons/screen_record.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/frontend/src/assets/icons/speech_to_text.svg b/src/frontend/src/assets/icons/speech_to_text.svg
new file mode 100644
index 00000000..ef9a538a
--- /dev/null
+++ b/src/frontend/src/assets/icons/speech_to_text.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/frontend/src/features/recording/components/LoginPrompt.tsx b/src/frontend/src/features/recording/components/LoginPrompt.tsx
index e46740ba..cdc7edf0 100644
--- a/src/frontend/src/features/recording/components/LoginPrompt.tsx
+++ b/src/frontend/src/features/recording/components/LoginPrompt.tsx
@@ -24,7 +24,7 @@ export const LoginPrompt = ({ heading, body }: LoginPromptProps) => {
})}
>
-
+
{heading}
diff --git a/src/frontend/src/features/recording/components/RecordingStatusIcon.tsx b/src/frontend/src/features/recording/components/RecordingStatusIcon.tsx
index 7bf65cb9..bc0f109f 100644
--- a/src/frontend/src/features/recording/components/RecordingStatusIcon.tsx
+++ b/src/frontend/src/features/recording/components/RecordingStatusIcon.tsx
@@ -15,8 +15,8 @@ export const RecordingStatusIcon = ({
}
if (isTranscriptActive) {
- return
+ return
}
- return
+ return
}
diff --git a/src/frontend/src/features/recording/components/RequestRecording.tsx b/src/frontend/src/features/recording/components/RequestRecording.tsx
index 8af710bd..7135355d 100644
--- a/src/frontend/src/features/recording/components/RequestRecording.tsx
+++ b/src/frontend/src/features/recording/components/RequestRecording.tsx
@@ -59,7 +59,7 @@ export const RequestRecording = ({
})}
>
-
+
{heading}
diff --git a/src/frontend/src/features/recording/components/RowWrapper.tsx b/src/frontend/src/features/recording/components/RowWrapper.tsx
index f94140a0..6d7038c1 100644
--- a/src/frontend/src/features/recording/components/RowWrapper.tsx
+++ b/src/frontend/src/features/recording/components/RowWrapper.tsx
@@ -45,7 +45,6 @@ export const RowWrapper = ({
paddingInline: '0.25rem',
})}
>
- {/* fixme - doesn't handle properly material-symbols */}
-
+
)
@@ -168,7 +168,7 @@ export const Tools = () => {
{isTranscriptEnabled && (
}
+ icon={}
title={t('tools.transcript.title')}
description={t('tools.transcript.body')}
onPress={() => openTranscript()}
@@ -176,7 +176,7 @@ export const Tools = () => {
)}
{isScreenRecordingEnabled && (
}
+ icon={}
title={t('tools.screenRecording.title')}
description={t('tools.screenRecording.body')}
onPress={() => openScreenRecording()}
diff --git a/src/frontend/src/features/settings/components/SettingsDialogExtended.tsx b/src/frontend/src/features/settings/components/SettingsDialogExtended.tsx
index ed4a782b..5f6f6cca 100644
--- a/src/frontend/src/features/settings/components/SettingsDialogExtended.tsx
+++ b/src/frontend/src/features/settings/components/SettingsDialogExtended.tsx
@@ -116,7 +116,7 @@ export const SettingsDialogExtended = (props: SettingsDialogExtended) => {
{isAdminOrOwner && (
-
+
{isWideScreen &&
t(`tabs.${SettingsDialogExtendedKey.TRANSCRIPTION}`)}
diff --git a/src/frontend/src/main.tsx b/src/frontend/src/main.tsx
index ee1483ae..ed319314 100644
--- a/src/frontend/src/main.tsx
+++ b/src/frontend/src/main.tsx
@@ -2,9 +2,6 @@ import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
-// Supports weights 100-700
-import '@fontsource-variable/material-symbols-outlined'
-
ReactDOM.createRoot(document.getElementById('root')!).render(
diff --git a/src/frontend/src/primitives/Icon.tsx b/src/frontend/src/primitives/Icon.tsx
index 7275fad0..9f64b095 100644
--- a/src/frontend/src/primitives/Icon.tsx
+++ b/src/frontend/src/primitives/Icon.tsx
@@ -1,48 +1,43 @@
import { cva, RecipeVariantProps } from '@/styled-system/css'
-import { ComponentPropsWithoutRef } from 'react'
+import { ComponentPropsWithoutRef, FunctionComponent, SVGProps } from 'react'
+
+// Eagerly import every .svg in the icons folder as a React component.
+// Drop a new file in /assets/icons and it's available immediately.
+const modules = import.meta.glob<{
+ default: FunctionComponent>
+}>('../assets/icons/*.svg', { eager: true, query: '?react', import: 'default' })
+
+// Build the registry: { 'chevron-down': Component, 'check': Component, ... }
+// Key = filename without extension.
+const icons = Object.fromEntries(
+ Object.entries(modules).map(([path, mod]) => {
+ const name = path
+ .split('/')
+ .pop()!
+ .replace(/\.svg$/, '')
+ return [name, mod as unknown as FunctionComponent>]
+ })
+) as Record>>
+
+export type IconName = keyof typeof icons
const iconRecipe = cva({
base: {
- fontWeight: 'normal',
- fontStyle: 'normal',
display: 'inline-block',
+ flexShrink: 0,
lineHeight: 1,
- textTransform: 'none',
- letterSpacing: 'normal',
- wordWrap: 'normal',
- whiteSpace: 'nowrap',
- direction: 'ltr',
+ color: 'currentColor',
+ fill: 'currentColor',
},
variants: {
- type: {
- icons: {
- fontFamily: 'Material Icons Outlined',
- webkitFontSmoothing: 'antialiased',
- mozOsxFontSmoothing: 'grayscale',
- textRendering: 'optimizeLegibility',
- fontFeatureSettings: '"liga"',
- },
- symbols: {
- fontFamily: 'Material Symbols Outlined Variable',
- },
- },
size: {
- sm: {
- fontSize: '18px',
- },
- md: {
- fontSize: '24px',
- },
- lg: {
- fontSize: '32px',
- },
- xl: {
- fontSize: '40px',
- },
+ sm: { width: '18px', height: '18px' },
+ md: { width: '24px', height: '24px' },
+ lg: { width: '32px', height: '32px' },
+ xl: { width: '40px', height: '40px' },
},
},
defaultVariants: {
- type: 'icons',
size: 'md',
},
})
@@ -50,22 +45,33 @@ const iconRecipe = cva({
export type IconRecipeProps = RecipeVariantProps
export type IconProps = IconRecipeProps &
- ComponentPropsWithoutRef<'span'> & {
- name: string
+ Omit, 'name'> & {
+ name: IconName
}
export const Icon = ({ name, ...props }: IconProps) => {
const [variantProps, componentProps] = iconRecipe.splitVariantProps(props)
- const { className, ...remainingComponentProps } = componentProps
+ const { className, ...rest } = componentProps
+
+ const SvgIcon = icons[name]
+
+ if (!SvgIcon) {
+ if (process.env.NODE_ENV !== 'production') {
+ console.warn(
+ `[Icon] Unknown icon name: "${name}". Available: ${Object.keys(icons).join(', ')}`
+ )
+ }
+ return null
+ }
return (
-
- {name}
-
+ focusable="false"
+ className={[iconRecipe(variantProps), className]
+ .filter(Boolean)
+ .join(' ')}
+ {...rest}
+ />
)
}
diff --git a/src/frontend/src/styles/index.css b/src/frontend/src/styles/index.css
index 8944fe9a..69019621 100644
--- a/src/frontend/src/styles/index.css
+++ b/src/frontend/src/styles/index.css
@@ -76,39 +76,6 @@ html:has(.lk-video-conference) {
}
}
-/* Generated using the Advanced installation guidelines from
- * https://fontsource.org/fonts/material-icons-outlined/install
- */
-@font-face {
- font-family: 'Material Icons Outlined';
- font-style: normal;
- font-display: block;
- font-weight: 400;
- src: url(@fontsource/material-icons-outlined/files/material-icons-outlined-latin-400-normal.woff2)
- format('woff2');
- unicode-range:
- U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
- U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193,
- U+2212, U+2215, U+FEFF, U+FFFD;
-}
-
-/* Generated using the Advanced installation guidelines from
- * https://fontsource.org/fonts/material-symbols-outlined/install
- */
-/* material-symbols-outlined-latin-wght-normal */
-@font-face {
- font-family: 'Material Symbols Outlined Variable';
- font-style: normal;
- font-display: block;
- font-weight: 100 700;
- src: url(@fontsource-variable/material-symbols-outlined/files/material-symbols-outlined-latin-wght-normal.woff2)
- format('woff2-variations');
- unicode-range:
- U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
- U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193,
- U+2212, U+2215, U+FEFF, U+FFFD;
-}
-
.hoverGroup .hoverGroupChild {
opacity: 0;
pointer-events: none;
diff --git a/src/frontend/vite.config.ts b/src/frontend/vite.config.ts
index cfef2a31..a0258422 100644
--- a/src/frontend/vite.config.ts
+++ b/src/frontend/vite.config.ts
@@ -2,6 +2,7 @@ import { defineConfig, loadEnv } from 'vite'
import react from '@vitejs/plugin-react'
import tsconfigPaths from 'vite-tsconfig-paths'
import { visualizer } from 'rollup-plugin-visualizer'
+import svgr from 'vite-plugin-svgr'
// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
@@ -10,6 +11,11 @@ export default defineConfig(({ mode }) => {
plugins: [
react(),
tsconfigPaths(),
+ svgr({
+ svgrOptions: {
+ replaceAttrValues: { '#000': 'currentColor', '#000000': 'currentColor', '#1f1f1f': 'currentColor' },
+ },
+ }),
env.VITE_ANALYZE === 'true' && visualizer({
open: true,
filename: 'rollup-plugin-visualizer/stats.html',