diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 00000000..159b739b --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,27 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +node_modules +.env \ No newline at end of file diff --git a/frontend/.next/cache/webpack/client-development/0.pack.gz b/frontend/.next/cache/webpack/client-development/0.pack.gz new file mode 100644 index 00000000..9d7d4c1b Binary files /dev/null and b/frontend/.next/cache/webpack/client-development/0.pack.gz differ diff --git a/frontend/.next/cache/webpack/client-development/index.pack.gz b/frontend/.next/cache/webpack/client-development/index.pack.gz new file mode 100644 index 00000000..74dace3b Binary files /dev/null and b/frontend/.next/cache/webpack/client-development/index.pack.gz differ diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 00000000..d2e77611 --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,73 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## React Compiler + +The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation). + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: + +```js +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + + // Remove tseslint.configs.recommended and replace with this + tseslint.configs.recommendedTypeChecked, + // Alternatively, use this for stricter rules + tseslint.configs.strictTypeChecked, + // Optionally, add this for stylistic rules + tseslint.configs.stylisticTypeChecked, + + // Other configs... + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) +``` + +You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: + +```js +// eslint.config.js +import reactX from 'eslint-plugin-react-x' +import reactDom from 'eslint-plugin-react-dom' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + // Enable lint rules for React + reactX.configs['recommended-typescript'], + // Enable lint rules for React DOM + reactDom.configs.recommended, + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) +``` diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js new file mode 100644 index 00000000..5e6b472f --- /dev/null +++ b/frontend/eslint.config.js @@ -0,0 +1,23 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' +import { defineConfig, globalIgnores } from 'eslint/config' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + js.configs.recommended, + tseslint.configs.recommended, + reactHooks.configs.flat.recommended, + reactRefresh.configs.vite, + ], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + }, +]) diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 00000000..11e001b8 --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,43 @@ + + + + + + + + practice + + +
+ + + diff --git a/frontend/next.config.js b/frontend/next.config.js deleted file mode 100644 index 7f657b56..00000000 --- a/frontend/next.config.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - reactStrictMode: true, - swcMinify: true, - images: { - domains: ['example.com'], // Add your image domains here - }, - experimental: { - appDir: true, - }, -}; \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 00000000..8cb79722 --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,4838 @@ +{ + "name": "practice", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "practice", + "version": "0.0.0", + "dependencies": { + "@monaco-editor/react": "^4.7.0", + "clsx": "^2.1.1", + "framer-motion": "^12.24.10", + "katex": "^0.16.27", + "lucide-react": "^0.562.0", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "react-katex": "^3.1.0", + "recharts": "^3.6.0", + "tailwind-merge": "^3.4.0", + "three": "^0.182.0" + }, + "devDependencies": { + "@eslint/js": "^9.39.1", + "@types/node": "^24.10.1", + "@types/react": "^19.2.5", + "@types/react-dom": "^19.2.3", + "@types/three": "^0.182.0", + "@vitejs/plugin-react": "^5.1.1", + "autoprefixer": "^10.4.23", + "eslint": "^9.39.1", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.4.24", + "globals": "^16.5.0", + "postcss": "^8.5.6", + "tailwindcss": "^3.4.17", + "typescript": "~5.9.3", + "typescript-eslint": "^8.46.4", + "vite": "^7.2.4" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", + "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.5", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@dimforge/rapier3d-compat": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz", + "integrity": "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", + "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", + "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", + "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", + "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", + "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", + "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", + "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", + "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", + "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", + "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", + "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", + "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", + "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", + "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", + "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", + "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", + "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", + "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", + "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", + "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", + "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", + "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", + "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", + "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", + "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", + "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", + "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", + "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@monaco-editor/loader": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.7.0.tgz", + "integrity": "sha512-gIwR1HrJrrx+vfyOhYmCZ0/JcWqG5kbfG7+d3f/C1LXk2EvzAbHSg3MQ5lO2sMlo9izoAZ04shohfKLVT6crVA==", + "license": "MIT", + "dependencies": { + "state-local": "^1.0.6" + } + }, + "node_modules/@monaco-editor/react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.7.0.tgz", + "integrity": "sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA==", + "license": "MIT", + "dependencies": { + "@monaco-editor/loader": "^1.5.0" + }, + "peerDependencies": { + "monaco-editor": ">= 0.25.0 < 1", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@reduxjs/toolkit": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.11.2.tgz", + "integrity": "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@standard-schema/utils": "^0.3.0", + "immer": "^11.0.0", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0", + "reselect": "^5.1.0" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", + "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, + "node_modules/@reduxjs/toolkit/node_modules/immer": { + "version": "11.1.3", + "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.3.tgz", + "integrity": "sha512-6jQTc5z0KJFtr1UgFpIL3N9XSC3saRaI9PwWtzM2pSqkNGtiNkYY2OSwkOGDK2XcTRcLb1pi/aNkKZz0nxVH4Q==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.53.tgz", + "integrity": "sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.55.1.tgz", + "integrity": "sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.55.1.tgz", + "integrity": "sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.55.1.tgz", + "integrity": "sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.55.1.tgz", + "integrity": "sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.55.1.tgz", + "integrity": "sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.55.1.tgz", + "integrity": "sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.55.1.tgz", + "integrity": "sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.55.1.tgz", + "integrity": "sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.55.1.tgz", + "integrity": "sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.55.1.tgz", + "integrity": "sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.55.1.tgz", + "integrity": "sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.55.1.tgz", + "integrity": "sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.55.1.tgz", + "integrity": "sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.55.1.tgz", + "integrity": "sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.55.1.tgz", + "integrity": "sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.55.1.tgz", + "integrity": "sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.55.1.tgz", + "integrity": "sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.55.1.tgz", + "integrity": "sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.55.1.tgz", + "integrity": "sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.55.1.tgz", + "integrity": "sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.55.1.tgz", + "integrity": "sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.55.1.tgz", + "integrity": "sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.55.1.tgz", + "integrity": "sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.55.1.tgz", + "integrity": "sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.55.1.tgz", + "integrity": "sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "license": "MIT" + }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, + "node_modules/@tweenjs/tween.js": { + "version": "23.1.3", + "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz", + "integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz", + "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.10.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.4.tgz", + "integrity": "sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", + "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@types/stats.js": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz", + "integrity": "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/three": { + "version": "0.182.0", + "resolved": "https://registry.npmjs.org/@types/three/-/three-0.182.0.tgz", + "integrity": "sha512-WByN9V3Sbwbe2OkWuSGyoqQO8Du6yhYaXtXLoA5FkKTUJorZ+yOHBZ35zUUPQXlAKABZmbYp5oAqpA4RBjtJ/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@dimforge/rapier3d-compat": "~0.12.0", + "@tweenjs/tween.js": "~23.1.3", + "@types/stats.js": "*", + "@types/webxr": ">=0.5.17", + "@webgpu/types": "*", + "fflate": "~0.8.2", + "meshoptimizer": "~0.22.0" + } + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", + "license": "MIT" + }, + "node_modules/@types/webxr": { + "version": "0.5.24", + "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.24.tgz", + "integrity": "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.52.0.tgz", + "integrity": "sha512-okqtOgqu2qmZJ5iN4TWlgfF171dZmx2FzdOv2K/ixL2LZWDStL8+JgQerI2sa8eAEfoydG9+0V96m7V+P8yE1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.52.0", + "@typescript-eslint/type-utils": "8.52.0", + "@typescript-eslint/utils": "8.52.0", + "@typescript-eslint/visitor-keys": "8.52.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.52.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.52.0.tgz", + "integrity": "sha512-iIACsx8pxRnguSYhHiMn2PvhvfpopO9FXHyn1mG5txZIsAaB6F0KwbFnUQN3KCiG3Jcuad/Cao2FAs1Wp7vAyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.52.0", + "@typescript-eslint/types": "8.52.0", + "@typescript-eslint/typescript-estree": "8.52.0", + "@typescript-eslint/visitor-keys": "8.52.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.52.0.tgz", + "integrity": "sha512-xD0MfdSdEmeFa3OmVqonHi+Cciab96ls1UhIF/qX/O/gPu5KXD0bY9lu33jj04fjzrXHcuvjBcBC+D3SNSadaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.52.0", + "@typescript-eslint/types": "^8.52.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.52.0.tgz", + "integrity": "sha512-ixxqmmCcc1Nf8S0mS0TkJ/3LKcC8mruYJPOU6Ia2F/zUUR4pApW7LzrpU3JmtePbRUTes9bEqRc1Gg4iyRnDzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.52.0", + "@typescript-eslint/visitor-keys": "8.52.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.52.0.tgz", + "integrity": "sha512-jl+8fzr/SdzdxWJznq5nvoI7qn2tNYV/ZBAEcaFMVXf+K6jmXvAFrgo/+5rxgnL152f//pDEAYAhhBAZGrVfwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.52.0.tgz", + "integrity": "sha512-JD3wKBRWglYRQkAtsyGz1AewDu3mTc7NtRjR/ceTyGoPqmdS5oCdx/oZMWD5Zuqmo6/MpsYs0wp6axNt88/2EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.52.0", + "@typescript-eslint/typescript-estree": "8.52.0", + "@typescript-eslint/utils": "8.52.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.52.0.tgz", + "integrity": "sha512-LWQV1V4q9V4cT4H5JCIx3481iIFxH1UkVk+ZkGGAV1ZGcjGI9IoFOfg3O6ywz8QqCDEp7Inlg6kovMofsNRaGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.52.0.tgz", + "integrity": "sha512-XP3LClsCc0FsTK5/frGjolyADTh3QmsLp6nKd476xNI9CsSsLnmn4f0jrzNoAulmxlmNIpeXuHYeEQv61Q6qeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.52.0", + "@typescript-eslint/tsconfig-utils": "8.52.0", + "@typescript-eslint/types": "8.52.0", + "@typescript-eslint/visitor-keys": "8.52.0", + "debug": "^4.4.3", + "minimatch": "^9.0.5", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.52.0.tgz", + "integrity": "sha512-wYndVMWkweqHpEpwPhwqE2lnD2DxC6WVLupU/DOt/0/v+/+iQbbzO3jOHjmBMnhu0DgLULvOaU4h4pwHYi2oRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.52.0", + "@typescript-eslint/types": "8.52.0", + "@typescript-eslint/typescript-estree": "8.52.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.52.0.tgz", + "integrity": "sha512-ink3/Zofus34nmBsPjow63FP5M7IGff0RKAgqR6+CFpdk22M7aLwC9gOcLGYqr7MczLPzZVERW9hRog3O4n1sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.52.0", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.2.tgz", + "integrity": "sha512-EcA07pHJouywpzsoTUqNh5NwGayl2PPVEJKUSinGGSxFGYn+shYbqMGBg6FXDqgXum9Ou/ecb+411ssw8HImJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.5", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.53", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.18.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/@webgpu/types": { + "version": "0.1.68", + "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.68.tgz", + "integrity": "sha512-3ab1B59Ojb6RwjOspYLsTpCzbNB3ZaamIAxBMmvnNkiDoLTZUOBXZ9p5nAYVEkQlDdf6qAZWi1pqj9+ypiqznA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/autoprefixer": { + "version": "10.4.23", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.23.tgz", + "integrity": "sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.1", + "caniuse-lite": "^1.0.30001760", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.12", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.12.tgz", + "integrity": "sha512-Mij6Lij93pTAIsSYy5cyBQ975Qh9uLEc5rwGTpomiZeXZL9yIS6uORJakb3ScHgfs0serMMfIbXzokPMuEiRyw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001762", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001762.tgz", + "integrity": "sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", + "license": "MIT" + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/dompurify": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.7.tgz", + "integrity": "sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==", + "license": "(MPL-2.0 OR Apache-2.0)", + "peer": true, + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.267", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", + "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", + "dev": true, + "license": "ISC" + }, + "node_modules/es-toolkit": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.43.0.tgz", + "integrity": "sha512-SKCT8AsWvYzBBuUqMk4NPwFlSdqLpJwmy6AP322ERn8W2YLIB6JBXnwMI2Qsh2gfphT3q7EKAxKb23cvFHFwKA==", + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks" + ] + }, + "node_modules/esbuild": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", + "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.2", + "@esbuild/android-arm": "0.27.2", + "@esbuild/android-arm64": "0.27.2", + "@esbuild/android-x64": "0.27.2", + "@esbuild/darwin-arm64": "0.27.2", + "@esbuild/darwin-x64": "0.27.2", + "@esbuild/freebsd-arm64": "0.27.2", + "@esbuild/freebsd-x64": "0.27.2", + "@esbuild/linux-arm": "0.27.2", + "@esbuild/linux-arm64": "0.27.2", + "@esbuild/linux-ia32": "0.27.2", + "@esbuild/linux-loong64": "0.27.2", + "@esbuild/linux-mips64el": "0.27.2", + "@esbuild/linux-ppc64": "0.27.2", + "@esbuild/linux-riscv64": "0.27.2", + "@esbuild/linux-s390x": "0.27.2", + "@esbuild/linux-x64": "0.27.2", + "@esbuild/netbsd-arm64": "0.27.2", + "@esbuild/netbsd-x64": "0.27.2", + "@esbuild/openbsd-arm64": "0.27.2", + "@esbuild/openbsd-x64": "0.27.2", + "@esbuild/openharmony-arm64": "0.27.2", + "@esbuild/sunos-x64": "0.27.2", + "@esbuild/win32-arm64": "0.27.2", + "@esbuild/win32-ia32": "0.27.2", + "@esbuild/win32-x64": "0.27.2" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", + "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.39.2", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", + "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.26", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.26.tgz", + "integrity": "sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "dev": true, + "license": "MIT" + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/framer-motion": { + "version": "12.24.10", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.24.10.tgz", + "integrity": "sha512-8yoyMkCn2RmV9UB9mfmMuzKyenQe909hRQRl0yGBhbZJjZZ9bSU87NIGAruqCXCuTNCA0qHw2LWLrcXLL9GF6A==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.24.10", + "motion-utils": "^12.24.10", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", + "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immer": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.2.0.tgz", + "integrity": "sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "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", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/katex": { + "version": "0.16.27", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.27.tgz", + "integrity": "sha512-aeQoDkuRWSqQN6nSvVCEFvfXdqo1OQiCmmW1kc9xSdjutPv7BGO7pqY9sQRJpMOGrEdfDgF2TfRXe5eUAD2Waw==", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "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/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "0.562.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.562.0.tgz", + "integrity": "sha512-82hOAu7y0dbVuFfmO4bYF1XEwYk/mEbM5E+b1jgci/udUBEE/R7LF5Ip0CCEmXe8AybRM8L+04eP+LGZeDvkiw==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/marked": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-14.0.0.tgz", + "integrity": "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==", + "license": "MIT", + "peer": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/meshoptimizer": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.22.0.tgz", + "integrity": "sha512-IebiK79sqIy+E4EgOr+CAw+Ke8hAspXKzBd0JdgEmPHiAwmvEj2S4h1rfvo+o/BnfEYd/jAOg5IeeIjzlzSnDg==", + "dev": true, + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/monaco-editor": { + "version": "0.55.1", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.55.1.tgz", + "integrity": "sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==", + "license": "MIT", + "peer": true, + "dependencies": { + "dompurify": "3.2.7", + "marked": "14.0.0" + } + }, + "node_modules/motion-dom": { + "version": "12.24.10", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.24.10.tgz", + "integrity": "sha512-H3HStYaJ6wANoZVNT0ZmYZHGvrpvi9pKJRzsgNEHkdITR4Qd9FFu2e9sH4e2Phr4tKCmyyloex6SOSmv0Tlq+g==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.24.10" + } + }, + "node_modules/motion-utils": { + "version": "12.24.10", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.24.10.tgz", + "integrity": "sha512-x5TFgkCIP4pPsRLpKoI86jv/q8t8FQOiM/0E8QKBzfMozWHfkKap2gA1hOki+B5g3IsBNpxbUnfOum1+dgvYww==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT", + "peer": true + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", + "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.3" + } + }, + "node_modules/react-is": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.3.tgz", + "integrity": "sha512-qJNJfu81ByyabuG7hPFEbXqNcWSU3+eVus+KJs+0ncpGfMyYdvSmxiJxbWR65lYi1I+/0HBcliO029gc4F+PnA==", + "license": "MIT", + "peer": true + }, + "node_modules/react-katex": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/react-katex/-/react-katex-3.1.0.tgz", + "integrity": "sha512-At9uLOkC75gwn2N+ZXc5HD8TlATsB+3Hkp9OGs6uA8tM3dwZ3Wljn74Bk3JyHFPgSnesY/EMrIAB1WJwqZqejA==", + "license": "MIT", + "dependencies": { + "katex": "^0.16.0" + }, + "peerDependencies": { + "prop-types": "^15.8.1", + "react": ">=15.3.2 <20" + } + }, + "node_modules/react-redux": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", + "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", + "license": "MIT", + "dependencies": { + "@types/use-sync-external-store": "^0.0.6", + "use-sync-external-store": "^1.4.0" + }, + "peerDependencies": { + "@types/react": "^18.2.25 || ^19", + "react": "^18.0 || ^19", + "redux": "^5.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, + "node_modules/react-refresh": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/recharts": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.6.0.tgz", + "integrity": "sha512-L5bjxvQRAe26RlToBAziKUB7whaGKEwD3znoM6fz3DrTowCIC/FnJYnuq1GEzB8Zv2kdTfaxQfi5GoH0tBinyg==", + "license": "MIT", + "workspaces": [ + "www" + ], + "dependencies": { + "@reduxjs/toolkit": "1.x.x || 2.x.x", + "clsx": "^2.1.1", + "decimal.js-light": "^2.5.1", + "es-toolkit": "^1.39.3", + "eventemitter3": "^5.0.1", + "immer": "^10.1.1", + "react-redux": "8.x.x || 9.x.x", + "reselect": "5.1.1", + "tiny-invariant": "^1.3.3", + "use-sync-external-store": "^1.2.2", + "victory-vendor": "^37.0.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/redux": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", + "license": "MIT" + }, + "node_modules/redux-thunk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", + "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", + "license": "MIT", + "peerDependencies": { + "redux": "^5.0.0" + } + }, + "node_modules/reselect": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==", + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.55.1.tgz", + "integrity": "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.55.1", + "@rollup/rollup-android-arm64": "4.55.1", + "@rollup/rollup-darwin-arm64": "4.55.1", + "@rollup/rollup-darwin-x64": "4.55.1", + "@rollup/rollup-freebsd-arm64": "4.55.1", + "@rollup/rollup-freebsd-x64": "4.55.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.55.1", + "@rollup/rollup-linux-arm-musleabihf": "4.55.1", + "@rollup/rollup-linux-arm64-gnu": "4.55.1", + "@rollup/rollup-linux-arm64-musl": "4.55.1", + "@rollup/rollup-linux-loong64-gnu": "4.55.1", + "@rollup/rollup-linux-loong64-musl": "4.55.1", + "@rollup/rollup-linux-ppc64-gnu": "4.55.1", + "@rollup/rollup-linux-ppc64-musl": "4.55.1", + "@rollup/rollup-linux-riscv64-gnu": "4.55.1", + "@rollup/rollup-linux-riscv64-musl": "4.55.1", + "@rollup/rollup-linux-s390x-gnu": "4.55.1", + "@rollup/rollup-linux-x64-gnu": "4.55.1", + "@rollup/rollup-linux-x64-musl": "4.55.1", + "@rollup/rollup-openbsd-x64": "4.55.1", + "@rollup/rollup-openharmony-arm64": "4.55.1", + "@rollup/rollup-win32-arm64-msvc": "4.55.1", + "@rollup/rollup-win32-ia32-msvc": "4.55.1", + "@rollup/rollup-win32-x64-gnu": "4.55.1", + "@rollup/rollup-win32-x64-msvc": "4.55.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/state-local": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz", + "integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==", + "license": "MIT" + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwind-merge": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz", + "integrity": "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.6", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/three": { + "version": "0.182.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.182.0.tgz", + "integrity": "sha512-GbHabT+Irv+ihI1/f5kIIsZ+Ef9Sl5A1Y7imvS5RQjWgtTPfPnZ43JmlYI7NtCRDK9zir20lQpfg8/9Yd02OvQ==", + "license": "MIT" + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", + "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.52.0.tgz", + "integrity": "sha512-atlQQJ2YkO4pfTVQmQ+wvYQwexPDOIgo+RaVcD7gHgzy/IQA+XTyuxNM9M9TVXvttkF7koBHmcwisKdOAf2EcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.52.0", + "@typescript-eslint/parser": "8.52.0", + "@typescript-eslint/typescript-estree": "8.52.0", + "@typescript-eslint/utils": "8.52.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/victory-vendor": { + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz", + "integrity": "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==", + "license": "MIT AND ISC", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, + "node_modules/vite": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.5.tgz", + "integrity": "sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + } + } +} diff --git a/frontend/package.json b/frontend/package.json index 5aa510b5..7d073269 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,28 +1,44 @@ { - "name": "automl-agentic-builder", - "version": "1.0.0", + "name": "practice", "private": true, + "version": "0.0.0", + "type": "module", "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "lint": "eslint ." + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview", + "test": "echo \"No tests configured yet\" && exit 0" }, "dependencies": { - "next": "latest", - "react": "latest", - "react-dom": "latest", - "tailwindcss": "latest", - "zustand": "latest", - "recharts": "latest", - "@monaco-editor/react": "latest", - "@supabase/supabase-js": "latest" + "@monaco-editor/react": "^4.7.0", + "clsx": "^2.1.1", + "framer-motion": "^12.24.10", + "katex": "^0.16.27", + "lucide-react": "^0.562.0", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "react-katex": "^3.1.0", + "recharts": "^3.6.0", + "tailwind-merge": "^3.4.0", + "three": "^0.182.0" }, "devDependencies": { - "typescript": "latest", - "eslint": "latest", - "eslint-config-next": "latest", - "postcss": "latest", - "autoprefixer": "latest" + "@eslint/js": "^9.39.1", + "@types/node": "^24.10.1", + "@types/react": "^19.2.5", + "@types/react-dom": "^19.2.3", + "@types/three": "^0.182.0", + "@vitejs/plugin-react": "^5.1.1", + "autoprefixer": "^10.4.23", + "eslint": "^9.39.1", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.4.24", + "globals": "^16.5.0", + "postcss": "^8.5.6", + "tailwindcss": "^3.4.17", + "typescript": "~5.9.3", + "typescript-eslint": "^8.46.4", + "vite": "^7.2.4" } -} \ No newline at end of file +} diff --git a/frontend/postcss.config.js b/frontend/postcss.config.js index 8567b4c4..e99ebc2c 100644 --- a/frontend/postcss.config.js +++ b/frontend/postcss.config.js @@ -1,6 +1,6 @@ -module.exports = { +export default { plugins: { tailwindcss: {}, autoprefixer: {}, }, -}; \ No newline at end of file +} \ No newline at end of file diff --git a/frontend/public/.gitkeep b/frontend/public/.gitkeep deleted file mode 100644 index 82789f20..00000000 --- a/frontend/public/.gitkeep +++ /dev/null @@ -1 +0,0 @@ -# This file is intentionally left blank. \ No newline at end of file diff --git a/frontend/public/vite.svg b/frontend/public/vite.svg new file mode 100644 index 00000000..e7b8dfb1 --- /dev/null +++ b/frontend/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/App.css b/frontend/src/App.css new file mode 100644 index 00000000..b9d355df --- /dev/null +++ b/frontend/src/App.css @@ -0,0 +1,42 @@ +#root { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; +} +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@media (prefers-reduced-motion: no-preference) { + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } +} + +.card { + padding: 2em; +} + +.read-the-docs { + color: #888; +} diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx new file mode 100644 index 00000000..931a1ce3 --- /dev/null +++ b/frontend/src/App.tsx @@ -0,0 +1,7 @@ +import AppRouter from './AppRouter'; + +function App() { + return ; +} + +export default App; diff --git a/frontend/src/AppRouter.tsx b/frontend/src/AppRouter.tsx new file mode 100644 index 00000000..d4455a01 --- /dev/null +++ b/frontend/src/AppRouter.tsx @@ -0,0 +1,30 @@ +import { Route, RouterProvider, Redirect, useRouter } from './router/router'; +import HomePage from './pages/HomePage'; +import WorkspacePage from './pages/WorkspacePage'; + +function Routes() { + const { pathname } = useRouter(); + + if (pathname !== '/' && pathname !== '/workspace') { + return ; + } + + return ( + <> + + + + + + + + ); +} + +export default function AppRouter() { + return ( + + + + ); +} diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx deleted file mode 100644 index 5f4805a6..00000000 --- a/frontend/src/app/layout.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; - -const Layout = ({ children }) => { - return ( -
-
-

AutoML Agentic Builder

-
-
- {children} -
-
-

© {new Date().getFullYear()} AutoML Agentic Builder

-
-
- ); -}; - -export default Layout; \ No newline at end of file diff --git a/frontend/src/app/p/[projectId]/page.tsx b/frontend/src/app/p/[projectId]/page.tsx deleted file mode 100644 index 605a0d57..00000000 --- a/frontend/src/app/p/[projectId]/page.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import React from 'react'; -import { useRouter } from 'next/router'; -import ChatPanel from '@/components/ChatPanel'; -import StageTimeline from '@/components/StageTimeline'; -import ConfirmBar from '@/components/ConfirmBar'; -import DatasetPreview from '@/components/DatasetPreview'; -import ProfilingPanel from '@/components/ProfilingPanel'; -import TrainingDashboard from '@/components/TrainingDashboard'; -import FileExplorer from '@/components/FileExplorer'; -import ArtifactsPanel from '@/components/ArtifactsPanel'; -import ConsoleLog from '@/components/ConsoleLog'; - -const ProjectPage = () => { - const router = useRouter(); - const { projectId } = router.query; - - return ( -
-
- - - -
-
- - - -
-
- - - -
-
- ); -}; - -export default ProjectPage; \ No newline at end of file diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx deleted file mode 100644 index e0f8f3cc..00000000 --- a/frontend/src/app/page.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; - -const Page = () => { - return ( -
-

Welcome to AutoML Agentic Builder

-

Log in to start building your machine learning models.

- -
- ); -}; - -export default Page; \ No newline at end of file diff --git a/frontend/src/assets/react.svg b/frontend/src/assets/react.svg new file mode 100644 index 00000000..6c87de9b --- /dev/null +++ b/frontend/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/components/ArtifactsPanel.tsx b/frontend/src/components/ArtifactsPanel.tsx deleted file mode 100644 index 49d4c511..00000000 --- a/frontend/src/components/ArtifactsPanel.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; - -const ArtifactsPanel = ({ artifacts }) => { - return ( -
-

Artifacts

- -
- ); -}; - -export default ArtifactsPanel; \ No newline at end of file diff --git a/frontend/src/components/ChatPanel.tsx b/frontend/src/components/ChatPanel.tsx deleted file mode 100644 index 632f0cdb..00000000 --- a/frontend/src/components/ChatPanel.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React, { useState } from 'react'; - -const ChatPanel = () => { - const [messages, setMessages] = useState([]); - const [input, setInput] = useState(''); - - const handleSend = () => { - if (input.trim()) { - setMessages([...messages, { text: input, sender: 'user' }]); - setInput(''); - // Here you would also send the message to the backend - } - }; - - return ( -
-
- {messages.map((msg, index) => ( -
- {msg.text} -
- ))} -
-
- setInput(e.target.value)} - placeholder="Type your message..." - /> - -
-
- ); -}; - -export default ChatPanel; \ No newline at end of file diff --git a/frontend/src/components/CodeEditor.tsx b/frontend/src/components/CodeEditor.tsx new file mode 100644 index 00000000..84c72cfa --- /dev/null +++ b/frontend/src/components/CodeEditor.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import Editor from '@monaco-editor/react'; +import type { FileNode } from '../utils/fileSystem'; + +interface CodeEditorProps { + activeFile: FileNode | null; + onChange: (value: string | undefined) => void; + theme: 'light' | 'dark'; +} + +const CodeEditor: React.FC = ({ activeFile, onChange, theme }) => { + if (!activeFile) { + return ( +
+
+

Select a file to start editing

+
+
+ ); + } + + return ( +
+ +
+ ); +}; + +export default CodeEditor; \ No newline at end of file diff --git a/frontend/src/components/ConfirmBar.tsx b/frontend/src/components/ConfirmBar.tsx deleted file mode 100644 index 39d4f790..00000000 --- a/frontend/src/components/ConfirmBar.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import React from 'react'; - -interface ConfirmBarProps { - isWaitingForConfirmation: boolean; - onConfirm: () => void; -} - -const ConfirmBar: React.FC = ({ isWaitingForConfirmation, onConfirm }) => { - return ( -
- {isWaitingForConfirmation && ( - - )} -
- ); -}; - -export default ConfirmBar; \ No newline at end of file diff --git a/frontend/src/components/ConsoleLog.tsx b/frontend/src/components/ConsoleLog.tsx deleted file mode 100644 index f265ed00..00000000 --- a/frontend/src/components/ConsoleLog.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; - -const ConsoleLog: React.FC<{ logs: string[] }> = ({ logs }) => { - return ( -
-

Console Log

-
- {logs.length === 0 ? ( -

No log messages available.

- ) : ( - logs.map((log, index) => ( -
- {log} -
- )) - )} -
-
- ); -}; - -export default ConsoleLog; \ No newline at end of file diff --git a/frontend/src/components/DatasetPreview.tsx b/frontend/src/components/DatasetPreview.tsx deleted file mode 100644 index e1731294..00000000 --- a/frontend/src/components/DatasetPreview.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React from 'react'; - -interface DatasetPreviewProps { - dataset: { - name: string; - description: string; - sampleData: Array<{ [key: string]: any }>; - }; -} - -const DatasetPreview: React.FC = ({ dataset }) => { - return ( -
-

{dataset.name}

-

{dataset.description}

-

Sample Data

- - - - {Object.keys(dataset.sampleData[0]).map((key) => ( - - ))} - - - - {dataset.sampleData.map((row, index) => ( - - {Object.values(row).map((value, idx) => ( - - ))} - - ))} - -
{key}
{value}
-
- ); -}; - -export default DatasetPreview; \ No newline at end of file diff --git a/frontend/src/components/FileExplorer.tsx b/frontend/src/components/FileExplorer.tsx index a444caf0..7304d4fd 100644 --- a/frontend/src/components/FileExplorer.tsx +++ b/frontend/src/components/FileExplorer.tsx @@ -1,17 +1,103 @@ import React from 'react'; +import { + Folder, + FileCode, + FileJson, + ChevronRight, + ChevronDown, + FileText +} from 'lucide-react'; +import type { FileNode } from '../utils/fileSystem'; +import clsx from 'clsx'; -const FileExplorer: React.FC = () => { - return ( -
-

File Explorer

-
    - {/* Placeholder for file items */} -
  • File 1
  • -
  • File 2
  • -
  • File 3
  • -
-
+interface FileIconProps { + name: string; + type: 'file' | 'folder'; + isOpen?: boolean; +} + +const FileIcon: React.FC = ({ name, type, isOpen }) => { + if (type === 'folder') { + return isOpen ? ( + + ) : ( + ); + } + + if (name.endsWith('.tsx') || name.endsWith('.ts')) { + return ; + } + if (name.endsWith('.css')) { + return ; + } + if (name.endsWith('.json')) { + return ; + } + return ; +}; + +interface FileExplorerProps { + nodes: FileNode[]; + activeFileId: string | null; + onSelectFile: (node: FileNode) => void; + onToggleFolder: (id: string) => void; + depth?: number; +} + +const FileExplorer: React.FC = ({ + nodes, + activeFileId, + onSelectFile, + onToggleFolder, + depth = 0 +}) => { + return ( +
+ {nodes.map((node) => ( +
+
{ + e.stopPropagation(); + if (node.type === 'folder') { + onToggleFolder(node.id); + } else { + onSelectFile(node); + } + }} + > + + {node.type === 'folder' && ( + node.isOpen ? : + )} + {node.type === 'file' &&
} + + + + + + {node.name} + +
+ + {node.type === 'folder' && node.isOpen && node.children && ( + + )} +
+ ))} +
+ ); }; export default FileExplorer; \ No newline at end of file diff --git a/frontend/src/components/MatrixScreenLoader.tsx b/frontend/src/components/MatrixScreenLoader.tsx new file mode 100644 index 00000000..f24842c7 --- /dev/null +++ b/frontend/src/components/MatrixScreenLoader.tsx @@ -0,0 +1,31 @@ +import { useReducedMotion } from 'framer-motion'; + +export default function MatrixScreenLoader({ label }: { label?: string }) { + const reduceMotion = useReducedMotion(); + + const cells = Array.from({ length: 9 }).map((_, i) => i); + const colors = ['bg-replit-accent/60', 'bg-replit-success/50', 'bg-replit-warning/50']; + + return ( +
+
+
+ {cells.map((i) => ( +
+ ))} +
+ {label ?
{label}
: null} +
+
+ ); +} diff --git a/frontend/src/components/MonacoViewer.tsx b/frontend/src/components/MonacoViewer.tsx deleted file mode 100644 index deb68f5a..00000000 --- a/frontend/src/components/MonacoViewer.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import React from 'react'; -import { MonacoEditor } from 'react-monaco-editor'; - -interface MonacoViewerProps { - code: string; - onChange: (newCode: string) => void; -} - -const MonacoViewer: React.FC = ({ code, onChange }) => { - const editorOptions = { - selectOnLineNumbers: true, - automaticLayout: true, - }; - - return ( - - ); -}; - -export default MonacoViewer; \ No newline at end of file diff --git a/frontend/src/components/ProfilingPanel.tsx b/frontend/src/components/ProfilingPanel.tsx deleted file mode 100644 index 80f8bccf..00000000 --- a/frontend/src/components/ProfilingPanel.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import React from 'react'; - -const ProfilingPanel: React.FC<{ profilingData: any }> = ({ profilingData }) => { - return ( -
-

Data Profiling

- {profilingData ? ( -
-

Summary Statistics

-
    - {Object.entries(profilingData.summary).map(([key, value]) => ( -
  • - {key}: {value} -
  • - ))} -
-

Data Types

-
    - {Object.entries(profilingData.dataTypes).map(([column, type]) => ( -
  • - {column}: {type} -
  • - ))} -
-
- ) : ( -

No profiling data available.

- )} -
- ); -}; - -export default ProfilingPanel; \ No newline at end of file diff --git a/frontend/src/components/StageTimeline.tsx b/frontend/src/components/StageTimeline.tsx deleted file mode 100644 index baa7aa77..00000000 --- a/frontend/src/components/StageTimeline.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; - -const stages = [ - { id: 'PARSE_INTENT', label: 'Parse Intent', status: 'completed' }, - { id: 'DATA_SOURCE', label: 'Data Source', status: 'in-progress' }, - { id: 'PROFILE_DATA', label: 'Profile Data', status: 'pending' }, - { id: 'PREPROCESS', label: 'Preprocess', status: 'pending' }, - { id: 'MODEL_SELECT', label: 'Model Select', status: 'pending' }, - { id: 'TRAIN', label: 'Train', status: 'pending' }, - { id: 'REVIEW_EDIT', label: 'Review/Edit', status: 'pending' }, - { id: 'EXPORT', label: 'Export', status: 'pending' }, -]; - -const StageTimeline: React.FC = () => { - return ( -
-

Stage Timeline

-
    - {stages.map(stage => ( -
  • - {stage.label} - {stage.status} -
  • - ))} -
-
- ); -}; - -export default StageTimeline; \ No newline at end of file diff --git a/frontend/src/components/TrainingDashboard.tsx b/frontend/src/components/TrainingDashboard.tsx deleted file mode 100644 index a83951b3..00000000 --- a/frontend/src/components/TrainingDashboard.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; - -const TrainingDashboard: React.FC = () => { - return ( -
-

Training Dashboard

-
- {/* Placeholder for training metrics visualization */} -

Training progress will be displayed here.

-
-
- {/* Placeholder for training logs */} -

Training logs will be displayed here.

-
-
- ); -}; - -export default TrainingDashboard; \ No newline at end of file diff --git a/frontend/src/components/center/AIBuilderDashboard.tsx b/frontend/src/components/center/AIBuilderDashboard.tsx new file mode 100644 index 00000000..5a807f73 --- /dev/null +++ b/frontend/src/components/center/AIBuilderDashboard.tsx @@ -0,0 +1,163 @@ +import type { FileSystemNode } from '../../lib/types'; +import { Activity, CheckCircle, Settings } from 'lucide-react'; +import { Fragment } from 'react'; + +interface AIBuilderDashboardProps { + files: FileSystemNode[]; +} + +export default function AIBuilderDashboard({ files }: AIBuilderDashboardProps) { + // Helper to read file content from VFS tree + const readFile = (path: string) => { + const findNode = (nodes: FileSystemNode[]): FileSystemNode | undefined => { + for (const node of nodes) { + if (node.path === path) return node; + if (node.children) { + const found = findNode(node.children); + if (found) return found; + } + } + }; + const node = findNode(files); + if (!node || !node.content) return null; + try { + return JSON.parse(node.content); + } catch { + return null; + } + }; + + const pipeline = readFile('/config/pipeline.json'); + const metrics = readFile('/artifacts/metrics.json'); + const confusionMatrix = readFile('/artifacts/confusion_matrix.json'); + + if (!pipeline) return
Initializing Dashboard...
; + + const maxLoss = metrics?.loss_history ? Math.max(...metrics.loss_history, 0.1) : 1; + + return ( +
+ {/* Header */} +
+
+

AI Builder Dashboard

+

Live Training Monitor

+
+
+ + Agent Active + +
+
+ +
+ {/* Pipeline Visualization */} +
+

+ Pipeline Status +

+
+ {pipeline.nodes.map((node: { id: string; label: string; status: string; progress: number }, index: number) => { + const isActive = node.status === 'running'; + const isDone = node.status === 'completed'; + const isFailed = node.status === 'failed'; + + let colorClass = 'bg-replit-surface/40 text-replit-textMuted border-replit-border/60'; + if (isActive) colorClass = 'bg-replit-accent/90 text-white border-replit-accent/90 ring-4 ring-replit-accent/15'; + if (isDone) colorClass = 'bg-replit-success/80 text-white border-replit-success/80'; + if (isFailed) colorClass = 'bg-replit-warning/70 text-white border-replit-warning/70'; + + const connectorClass = isDone + ? 'bg-replit-success/70' + : isActive + ? 'bg-replit-accent/60' + : isFailed + ? 'bg-replit-warning/70' + : 'bg-replit-border/70'; + + const hasNext = index < pipeline.nodes.length - 1; + + return ( + +
+
+ {isDone ? : {index + 1}} + {isActive ? ( +
+ ) : null} +
+ + {node.label} + +
+ {hasNext ?
: null} + + ); + })} +
+
+ + {/* Metrics Panel */} +
+
+
Model Accuracy
+
{(metrics?.accuracy * 100).toFixed(1)}%
+
+
+
F1 Score
+
{(metrics?.f1_score * 100).toFixed(1)}%
+
+
+
AUC-ROC
+
{(metrics?.auc_roc * 100).toFixed(1)}%
+
+
+ + {/* Training Loss Chart (CSS Only) */} +
+

Training Loss History

+
+ {metrics?.loss_history?.length > 0 ? ( + metrics.loss_history.map((val: number, i: number) => ( +
+
+ E{i+1} +
+ )) + ) : ( +
+ Waiting for training execution... +
+ )} +
+
+ + {/* Confusion Matrix */} +
+

Confusion Matrix

+ {confusionMatrix ? ( +
+
+
Pred N
+
Pred P
+ +
Actual N
+
{confusionMatrix[0][0]}
+
{confusionMatrix[0][1]}
+ +
Actual P
+
{confusionMatrix[1][0]}
+
{confusionMatrix[1][1]}
+
+ ) : null} +
+
+
+ ); +} diff --git a/frontend/src/components/center/ConsolePane.tsx b/frontend/src/components/center/ConsolePane.tsx new file mode 100644 index 00000000..345f6692 --- /dev/null +++ b/frontend/src/components/center/ConsolePane.tsx @@ -0,0 +1,60 @@ +import { Terminal, Trash2 } from 'lucide-react'; +import { useMemo, useState } from 'react'; + +interface ConsolePaneProps { + logsText?: string; +} + +export default function ConsolePane({ logsText }: ConsolePaneProps) { + const [input, setInput] = useState(''); + + const lines = useMemo(() => { + const raw = (logsText || '').trimEnd(); + if (!raw) return [] as string[]; + return raw.split('\n').slice(-500); + }, [logsText]); + + return ( +
+ {/* Console Header */} +
+
+ + Console +
+ +
+ + {/* Console Output */} +
+ {lines.length > 0 ? ( + lines.map((line, idx) => ( +
+ {line} +
+ )) + ) : ( +
No logs yet.
+ )} + + {/* Input Line */} +
+ {'>'} + setInput(e.target.value)} + className="flex-1 bg-transparent outline-none text-replit-text" + placeholder="Type a command..." + /> +
+
+
+ ); +} diff --git a/frontend/src/components/center/DashboardPane.tsx b/frontend/src/components/center/DashboardPane.tsx new file mode 100644 index 00000000..3e9633b7 --- /dev/null +++ b/frontend/src/components/center/DashboardPane.tsx @@ -0,0 +1,29 @@ +import AIBuilderDashboard from './AIBuilderDashboard'; +import type { FileSystemNode } from '../../lib/types'; +import { BarChart2 } from 'lucide-react'; + +interface DashboardPaneProps { + files: FileSystemNode[]; +} + +export default function DashboardPane({ files }: DashboardPaneProps) { + // Use a key to force re-render if needed or just let the dashboard handle it + // The Dashboard reads file content, so it should be reactive if files prop changes. + + return ( +
+
+
+ + Training Metrics +
+
+ Real-time Monitoring +
+
+
+ +
+
+ ); +} diff --git a/frontend/src/components/center/FileEditorPane.tsx b/frontend/src/components/center/FileEditorPane.tsx new file mode 100644 index 00000000..01ab68a4 --- /dev/null +++ b/frontend/src/components/center/FileEditorPane.tsx @@ -0,0 +1,80 @@ +import Editor, { loader } from '@monaco-editor/react'; + +loader.init().then((monaco) => { + monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({ + noSemanticValidation: true, + noSyntaxValidation: true, + }); + + monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({ + noSemanticValidation: true, + noSyntaxValidation: true, + }); + + monaco.languages.typescript.typescriptDefaults.setCompilerOptions({ + target: monaco.languages.typescript.ScriptTarget.ES2020, + allowNonTsExtensions: true, + moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs, + module: monaco.languages.typescript.ModuleKind.CommonJS, + noEmit: true, + lib: ["es2020"], + typeRoots: [], + }); +}); + +interface FileEditorPaneProps { + filePath: string; + content: string; + language?: string; + onChange?: (value: string | undefined) => void; +} + +export default function FileEditorPane({ + filePath, + content, + language = 'typescript', + onChange, +}: FileEditorPaneProps) { + + const getLanguage = (path: string) => { + if (path.endsWith('.json')) return 'json'; + if (path.endsWith('.ts') || path.endsWith('.tsx')) return 'typescript'; + if (path.endsWith('.js') || path.endsWith('.jsx')) return 'javascript'; + if (path.endsWith('.css')) return 'css'; + if (path.endsWith('.md')) return 'markdown'; + if (path.endsWith('.py')) return 'python'; + if (path.endsWith('.html')) return 'html'; + return 'plaintext'; + }; + + const detectedLang = language === 'typescript' ? getLanguage(filePath) : language; + + return ( +
+ +
+ ); +} diff --git a/frontend/src/components/center/PreviewPane.tsx b/frontend/src/components/center/PreviewPane.tsx new file mode 100644 index 00000000..ddc432ce --- /dev/null +++ b/frontend/src/components/center/PreviewPane.tsx @@ -0,0 +1,96 @@ +import { useEffect, useState } from 'react'; +import { ArrowLeft, ArrowRight, RotateCw, ExternalLink } from 'lucide-react'; +import type { FileSystemNode } from '../../lib/types'; +import EmptyPreview from './preview/EmptyPreview'; +import TrainingLoaderV2 from './preview/TrainingLoaderV2'; +import APIDocsPane from './preview/APIDocsPane'; +import type { BuildStatus } from '../../lib/buildSession'; + +interface PreviewPaneProps { + files: FileSystemNode[]; + isRunning: boolean; + onSimulationComplete: () => void; + updateFileContent: (path: string, content: string | ((prev: string) => string)) => void; + hasSession: boolean; + sessionStatus: BuildStatus; +} + +export default function PreviewPane({ + files, + isRunning, + onSimulationComplete, + updateFileContent, + hasSession, + sessionStatus, +}: PreviewPaneProps) { + const [completed, setCompleted] = useState(false); + + // Reset completion flag when a new build starts. + useEffect(() => { + const shouldReset = !hasSession || isRunning || sessionStatus === 'building'; + if (!shouldReset) return; + const t = window.setTimeout(() => setCompleted(false), 0); + return () => window.clearTimeout(t); + }, [hasSession, isRunning, sessionStatus]); + + // Determine View State + let viewState: 'empty' | 'processing' | 'docs' = 'empty'; + if (!hasSession) { + viewState = 'empty'; + } else if (completed) { + viewState = 'docs'; + } else if (isRunning || sessionStatus === 'building') { + viewState = 'processing'; + } else { + viewState = 'docs'; + } + + const handleComplete = () => { + setCompleted(true); + onSimulationComplete(); + }; + + return ( +
+ {/* Browser-like Controls */} +
+
+ + + +
+ +
+ + {viewState === 'processing' ? 'https://cluster.ai-builder.dev/jobs/run_8392' : + viewState === 'docs' ? 'https://api.ai-builder.dev/v1/docs' : + 'about:blank'} + + +
+
+ + {/* Content Area */} +
+ {viewState === 'empty' && } + {viewState === 'processing' && ( + + )} + {viewState === 'docs' && } +
+
+ ); + +} diff --git a/frontend/src/components/center/PublishingPane.tsx b/frontend/src/components/center/PublishingPane.tsx new file mode 100644 index 00000000..6212696c --- /dev/null +++ b/frontend/src/components/center/PublishingPane.tsx @@ -0,0 +1,74 @@ +import { Globe, Copy, ExternalLink } from 'lucide-react'; + +export default function PublishingPane() { + return ( +
+
+

+ Publish Your App +

+ + {/* Deployment Status */} +
+
+
+ +
+
+

Ready to Deploy

+

Your app is ready to be published

+
+
+ +
+
+ Deployment URL + +
+
+ + https://ai-builder-demo.replit.dev + + +
+
+
+ + {/* Publish Options */} +
+
+ +
+ +
+ +
+
+ + {/* Publish Button */} + + +

+ By publishing, you agree to make your app publicly accessible +

+
+
+ ); +} diff --git a/frontend/src/components/center/WorkspaceTabs.tsx b/frontend/src/components/center/WorkspaceTabs.tsx new file mode 100644 index 00000000..9da5f666 --- /dev/null +++ b/frontend/src/components/center/WorkspaceTabs.tsx @@ -0,0 +1,61 @@ +import { X } from 'lucide-react'; + +export type TabType = 'preview' | 'dashboard' | 'publishing' | 'console' | 'file'; + +export interface Tab { + id: string; + type: TabType; + title: string; + filePath?: string; + closable: boolean; +} + +interface WorkspaceTabsProps { + tabs: Tab[]; + activeTabId: string; + onTabClick: (tabId: string) => void; + onTabClose: (tabId: string) => void; +} + +export default function WorkspaceTabs({ + tabs, + activeTabId, + onTabClick, + onTabClose, +}: WorkspaceTabsProps) { + return ( +
+ {tabs.map((tab) => { + const isActive = tab.id === activeTabId; + + return ( +
onTabClick(tab.id)} + className={` + group flex items-center gap-2 px-3 py-1.5 rounded-md text-sm cursor-pointer transition-colors shrink-0 + ${isActive + ? 'bg-replit-bg text-replit-text' + : 'text-replit-textMuted hover:text-replit-text hover:bg-replit-surfaceHover' + } + `} + > + {tab.title} + + {tab.closable && ( + + )} +
+ ); + })} +
+ ); +} diff --git a/frontend/src/components/center/loader/ModelMetricsVisualizer.tsx b/frontend/src/components/center/loader/ModelMetricsVisualizer.tsx new file mode 100644 index 00000000..2bf679a0 --- /dev/null +++ b/frontend/src/components/center/loader/ModelMetricsVisualizer.tsx @@ -0,0 +1,113 @@ +import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts'; + +export type MetricPoint = { + epoch: number; + trainMetric: number; + valMetric: number; +}; + +interface ModelMetricsVisualizerProps { + data: MetricPoint[]; + metricKind: 'accuracy' | 'f1' | 'rmse'; +} + +const metricLabelMap: Record = { + accuracy: 'Accuracy', + f1: 'Macro F1', + rmse: 'RMSE', +}; + +export default function ModelMetricsVisualizer({ data, metricKind }: ModelMetricsVisualizerProps) { + const currentTrain = data.length > 0 ? data[data.length - 1]?.trainMetric : null; + const currentVal = data.length > 0 ? data[data.length - 1]?.valMetric : null; + const currentEpoch = data.length > 0 ? data[data.length - 1]?.epoch : 0; + + const label = metricLabelMap[metricKind]; + const domain = metricKind === 'rmse' ? ([0, 'auto'] as const) : ([0, 1] as const); + + return ( +
+
+
+
+
Model Metric
+
+
+ Epoch + {currentEpoch} +
+
+ +
+ {data.length === 0 && ( +
+
+
+

Waiting for data…

+
+
+ )} + + + + + + + + + + + +
+ +
+
+
+
+ Training {label} +
+

{currentTrain !== null ? currentTrain.toFixed(4) : '—'}

+
+
+
+
+ Validation {label} +
+

{currentVal !== null ? currentVal.toFixed(4) : '—'}

+
+
+
+
+ ); +} diff --git a/frontend/src/components/center/loader/TrainingLoader.tsx b/frontend/src/components/center/loader/TrainingLoader.tsx new file mode 100644 index 00000000..dc7e5e39 --- /dev/null +++ b/frontend/src/components/center/loader/TrainingLoader.tsx @@ -0,0 +1,780 @@ +import { useEffect, useMemo, useRef, useState } from 'react'; +import { AnimatePresence, motion, useReducedMotion } from 'framer-motion'; +import { BlockMath } from 'react-katex'; +import 'katex/dist/katex.min.css'; +import clsx from 'clsx'; + +import type { StepDef, StepPhase, VisualId } from './types'; +import { clamp01, formatClock, seeded } from './types'; +import { VISUAL_LABEL, VISUALS } from './visuals/visualRegistry'; +import TrainingLossVisualizer from './TrainingLossVisualizer'; +import ModelMetricsVisualizer from './ModelMetricsVisualizer'; +import { useMockAutoMLStream } from '../../../mock/useMockAutoMLStream'; +import type { ScenarioId } from '../../../mock/scenarios'; +import type { BackendEvent } from '../../../mock/backendEventTypes'; +import { SCENARIO_VIZ, type LoaderStepId } from '../../../mock/scenarioVizConfig'; + +interface MetricPoint { + epoch: number; + value: number; +} + +interface LossPoint { + epoch: number; + train_loss: number; + val_loss: number; +} + +function mapMetricSeries(series: MetricPoint[], metricKind: 'accuracy' | 'f1' | 'rmse') { + return series.map((point, idx) => { + const wobble = Math.sin(point.epoch / 4 + idx * 0.35); + if (metricKind === 'rmse') { + const trainMetric = Math.max(0, point.value * (0.92 + 0.02 * wobble)); + const valMetric = Math.max(0, point.value * (1.06 + 0.03 * wobble)); + return { epoch: point.epoch, trainMetric, valMetric }; + } + const trainMetric = clamp01(point.value + 0.03 + 0.01 * wobble); + const valMetric = clamp01(point.value - 0.03 + 0.008 * wobble); + return { epoch: point.epoch, trainMetric, valMetric }; + }); +} + +export interface TrainingLoaderProps { + onComplete: () => void; + updateFileContent: (path: string, content: string | ((prev: string) => string)) => void; + scenarioId?: ScenarioId; + seed?: number; + useMockStream?: boolean; +} + +const DEFAULT_SEED = 1337; +const FRAME_DURATION_MS = 5000; +const EVAL_DURATION_MS = 9000; +const CONFUSION_DURATION_MS = 7000; + +const STEP_TEMPLATES: Record> = { + neuralNet: { + equations: ['\\mathbf{h}=\\sigma(\\mathbf{W}\\mathbf{x}+\\mathbf{b})'], + phases: [{ kind: 'visual', visualId: 'neuralNetForward' }], + }, + matrixOps: { + equations: ['\\mathbf{Y}=\\mathbf{X}\\mathbf{W}'], + matrixLabel: 'X·W', + matrixRows: 6, + matrixCols: 8, + phases: [{ kind: 'operation' }], + }, + gradientDescent: { + equations: ['\\theta_{t+1}=\\theta_t-\\eta\\nabla L(\\theta_t)'], + phases: [{ kind: 'visual', visualId: 'gradDescent' }], + }, + trainLoss: { + equations: ['\\mathcal{L}(\\theta)'], + phases: [{ kind: 'graph', graphType: 'loss' }], + }, + modelMetric: { + equations: ['\\mathrm{metric}(\\hat{y},y)'], + phases: [{ kind: 'graph', graphType: 'accuracy' }], + }, + embedding: { + equations: ['\\mathbf{z}=f(\\mathbf{x})'], + phases: [{ kind: 'visual', visualId: 'embeddingScatter' }], + }, + evaluation: { + equations: ['\\mathrm{F1}=2\\frac{PR}{P+R}'], + phases: [{ kind: 'visual', visualId: 'evaluation' }], + }, + residuals: { + equations: ['r = y-\\hat{y}'], + phases: [{ kind: 'visual', visualId: 'residuals' }], + }, + confusionMatrix: { + equations: ['\\mathbf{C}'], + phases: [{ kind: 'visual', visualId: 'confusionMatrix' }], + }, +}; + +const phaseTitle = (phase: StepPhase) => { + if (phase.kind === 'operation') return 'Operation'; + if (phase.kind === 'graph') return phase.graphType === 'loss' ? 'Graph (Loss)' : 'Graph (Accuracy)'; + return `Visual (${VISUAL_LABEL[phase.visualId]})`; +}; + +const getPhaseKindLabel = (phase: StepPhase) => { + if (phase.kind === 'operation') return 'Operation'; + if (phase.kind === 'graph') return 'Graph'; + return 'Visual'; +}; + +function buildLossSeries(total: number): LossPoint[] { + const pts: LossPoint[] = []; + for (let i = 0; i < total; i += 1) { + const base = 1.35 * Math.exp(-i / 8); + const noise = (seeded(i + 1) - 0.5) * 0.06; + const train_loss = Math.max(0.05, base + noise); + const val_loss = Math.max(0.06, base * 1.05 + noise * 0.8); + pts.push({ epoch: i + 1, train_loss, val_loss }); + } + return pts; +} + +function buildAccuracySeries(total: number): MetricPoint[] { + const pts: MetricPoint[] = []; + for (let i = 0; i < total; i += 1) { + const base = 0.45 + 0.5 * (1 - Math.exp(-i / 10)); + const noise = (seeded(i + 100) - 0.5) * 0.03; + pts.push({ epoch: i + 1, value: clamp01(base + noise) }); + } + return pts; +} + +function writeJson(updateFileContent: TrainingLoaderProps['updateFileContent'], path: string, value: unknown) { + updateFileContent(path, JSON.stringify(value, null, 2)); +} + +function appendLog(updateFileContent: TrainingLoaderProps['updateFileContent'], line: string, level: 'INFO' | 'WARN' | 'ERROR' = 'INFO') { + updateFileContent('/logs/training.log', (prev) => `${prev || ''}[${formatClock()}] [${level}] ${line}\n`); +} + +function MatrixGrid({ + label, + rows, + cols, + timeMs, + reducedMotion, +}: { + label: string; + rows: number; + cols: number; + timeMs: number; + reducedMotion: boolean; +}) { + const cells = useMemo(() => { + const list: number[] = []; + const total = rows * cols; + for (let i = 0; i < total; i += 1) list.push(seeded(i + rows * 13 + cols * 7)); + return list; + }, [rows, cols]); + + const opIndex = reducedMotion ? 0 : Math.floor(timeMs / 110) % Math.max(1, rows * cols); + const opRow = Math.floor(opIndex / cols); + const opCol = opIndex % cols; + + return ( +
+
+
+ {label}{' '} + + {rows}×{cols} + +
+
Matrix op
+
+ +
+ {cells.map((val, idx) => { + const r = Math.floor(idx / cols); + const c = idx % cols; + + const inOpRow = r === opRow; + const inOpCol = c === opCol; + const isOpCell = inOpRow && inOpCol; + + const wave = reducedMotion ? 0 : Math.sin(timeMs / 240 + idx * 0.35) * 0.08; + const opBoost = reducedMotion ? 0 : inOpRow || inOpCol ? 0.12 : 0; + const hotBoost = reducedMotion ? 0 : isOpCell ? 0.18 : 0; + + const dyn = clamp01(val + wave + opBoost + hotBoost); + const display = (dyn * 2 - 1) * 1.15; + + return ( + + {display.toFixed(2)} + + ); + })} +
+
+ ); +} + +function computeWeightedPhase(phases: StepPhase[], elapsedMs: number, durationMs: number) { + const total = Math.max(1, durationMs); + const weights = phases.map((p) => (typeof p.weight === 'number' ? p.weight : 1)); + const sum = weights.reduce((a, b) => a + b, 0) || 1; + const normalized = weights.map((w) => w / sum); + + const durs = normalized.map((w) => Math.max(1, Math.round(total * w))); + // Fix rounding drift by snapping last duration. + const drift = durs.reduce((a, b) => a + b, 0) - total; + durs[durs.length - 1] = Math.max(1, durs[durs.length - 1] - drift); + + let remaining = Math.max(0, elapsedMs); + let idx = 0; + while (idx < durs.length - 1 && remaining >= durs[idx]) { + remaining -= durs[idx]; + idx += 1; + } + + const phase = phases[idx]; + const phaseDuration = Math.max(1, durs[idx]); + const phaseElapsed = Math.min(phaseDuration, remaining); + const phaseProgress = clamp01(phaseElapsed / phaseDuration); + + return { phaseIndex: idx, phase, phaseProgress, phases, durs }; +} + +export default function TrainingLoader({ onComplete, updateFileContent, scenarioId, seed, useMockStream = true }: TrainingLoaderProps) { + const reducedMotionPref = useReducedMotion(); + const reducedMotion = !!reducedMotionPref; + + const [activeScenario, setActiveScenario] = useState(scenarioId ?? 'A'); + useEffect(() => { + if (!scenarioId) return; + setActiveScenario(scenarioId); + }, [scenarioId]); + + const { events, metricsState } = useMockAutoMLStream({ + scenarioId: activeScenario, + seed, + enabled: useMockStream, + }); + + const scenarioConfig = useMemo(() => SCENARIO_VIZ[activeScenario], [activeScenario]); + const steps = useMemo(() => { + const base = scenarioConfig.steps.filter((s) => s.enabled); + const confusionSize = metricsState.confusionTable?.length ?? 0; + const shouldSplitConfusion = scenarioConfig.showConfusionMatrix && confusionSize > 2; + + const expanded: Array<{ id: LoaderStepId; title: string; subtitle: string; enabled: boolean }> = []; + for (const stepDef of base) { + expanded.push(stepDef); + if (stepDef.id === 'evaluation' && shouldSplitConfusion) { + expanded.push({ + id: 'confusionMatrix', + title: 'Confusion Matrix', + subtitle: 'Detailed class breakdown', + enabled: true, + }); + } + } + + return expanded.map((s) => ({ + id: s.id, + title: s.title, + subtitle: s.subtitle, + durationMs: s.id === 'evaluation' ? EVAL_DURATION_MS : s.id === 'confusionMatrix' ? CONFUSION_DURATION_MS : FRAME_DURATION_MS, + ...STEP_TEMPLATES[s.id], + })); + }, [metricsState.confusionTable, scenarioConfig]); + + const metricKind = scenarioConfig.metricKind; + + const updateFileContentRef = useRef(updateFileContent); + useEffect(() => { + updateFileContentRef.current = updateFileContent; + }, [updateFileContent]); + + const processedEventsRef = useRef(0); + useEffect(() => { + if (!useMockStream) return; + if (events.length <= processedEventsRef.current) return; + + for (let i = processedEventsRef.current; i < events.length; i += 1) { + const event = events[i] as BackendEvent; + if (event.type === 'ARTIFACT_WRITTEN') { + updateFileContentRef.current(event.path, event.content); + } + if (event.type === 'LOG_LINE') { + appendLog(updateFileContentRef.current, event.message, event.level); + } + } + + processedEventsRef.current = events.length; + }, [events, useMockStream]); + + const [now, setNow] = useState(0); + const nowRef = useRef(0); + + const [stepIndex, setStepIndex] = useState(0); + const [stepStartedAt, setStepStartedAt] = useState(0); + const stepIndexRef = useRef(0); + useEffect(() => { + stepIndexRef.current = stepIndex; + }, [stepIndex]); + + const completedRef = useRef(false); + const advanceGuardRef = useRef(-1); + const clockInitRef = useRef(false); + + const lossFull = useMemo(() => { + if (useMockStream && metricsState.lossSeries.length > 0) return metricsState.lossSeries; + return buildLossSeries(36); + }, [metricsState.lossSeries, useMockStream]); + + const accFull = useMemo(() => { + if (useMockStream && metricsState.accSeries.length > 0) return metricsState.accSeries; + return buildAccuracySeries(36); + }, [metricsState.accSeries, useMockStream]); + + const f1Full = useMemo(() => { + if (useMockStream && metricsState.f1Series.length > 0) return metricsState.f1Series; + return [] as MetricPoint[]; + }, [metricsState.f1Series, useMockStream]); + + const rmseFull = useMemo(() => { + if (useMockStream && metricsState.rmseSeries.length > 0) return metricsState.rmseSeries; + return [] as MetricPoint[]; + }, [metricsState.rmseSeries, useMockStream]); + + const [lossVisible, setLossVisible] = useState([]); + const [accVisible, setAccVisible] = useState([]); + const useRmse = metricKind === 'rmse'; + + const metricFull = useMemo(() => { + if (metricKind === 'rmse') return rmseFull; + if (metricKind === 'f1') return f1Full.length > 0 ? f1Full : accFull; + return accFull; + }, [accFull, f1Full, metricKind, rmseFull]); + const lastLossCountWritten = useRef(0); + const lastAccCountWritten = useRef(0); + const lastLossWriteAtRef = useRef(0); + const lastAccWriteAtRef = useRef(0); + const lastLossValueRef = useRef(null); + const lastAccValueRef = useRef(null); + + const startStep = (nextStepIndex: number, startedAtMs: number) => { + setStepIndex(nextStepIndex); + setStepStartedAt(startedAtMs); + + // Reset metric reveal for metric steps. + setLossVisible([]); + setAccVisible([]); + lastLossCountWritten.current = 0; + lastAccCountWritten.current = 0; + lastLossWriteAtRef.current = startedAtMs; + lastAccWriteAtRef.current = startedAtMs; + }; + + useEffect(() => { + if (!useMockStream) return; + completedRef.current = false; + advanceGuardRef.current = -1; + startStep(0, nowRef.current || performance.now()); + }, [activeScenario, useMockStream]); + + const step = steps[Math.min(stepIndex, steps.length - 1)]; + const elapsed = Math.max(0, now - stepStartedAt); + + const { phaseIndex, phase, phaseProgress } = computeWeightedPhase(step.phases, elapsed, step.durationMs); + const stepProgress = clamp01(elapsed / Math.max(1, step.durationMs)); + + const stepSeed = (seed ?? DEFAULT_SEED) + stepIndex * 1000 + phaseIndex * 100; + + useEffect(() => { + lastLossValueRef.current = lossVisible.at(-1)?.val_loss ?? null; + }, [lossVisible]); + useEffect(() => { + lastAccValueRef.current = accVisible.at(-1)?.value ?? null; + }, [accVisible]); + + // RAF clock. + useEffect(() => { + let raf = 0; + let last = 0; + + const tick = (t: number) => { + raf = requestAnimationFrame(tick); + nowRef.current = t; + + if (!clockInitRef.current) { + clockInitRef.current = true; + last = t; + setNow(t); + setStepStartedAt(t); + return; + } + + if (t - last > 50) { + setNow(t); + last = t; + } + }; + + raf = requestAnimationFrame(tick); + return () => cancelAnimationFrame(raf); + }, []); + + // Failsafe watchdog per step. + useEffect(() => { + if (completedRef.current) return; + if (stepIndex >= steps.length) return; + + const expected = stepIndex; + const timeoutMs = Math.max(250, step.durationMs + 400); + + const id = window.setTimeout(() => { + if (completedRef.current) return; + if (stepIndexRef.current !== expected) return; + startStep(expected + 1, nowRef.current || performance.now()); + }, timeoutMs); + + return () => window.clearTimeout(id); + }, [stepIndex, step.durationMs]); + + // On step start: logs + progress artifact. + useEffect(() => { + appendLog(updateFileContentRef.current, `${step.title} — ${step.subtitle}`); + writeJson(updateFileContentRef.current, '/artifacts/progress.json', { + step: step.id, + stepIndex, + startedAt: new Date().toISOString(), + }); + }, [step.id, step.title, step.subtitle, stepIndex]); + + // Advance step when complete. + useEffect(() => { + if (stepIndex >= steps.length) return; + if (stepProgress < 1) return; + + + if (step.phases.some((p) => p.kind === 'graph')) { + const graphPhase = step.phases.find((p) => p.kind === 'graph'); + if (graphPhase?.graphType === 'loss' && lossVisible.length < lossFull.length) return; + if (graphPhase?.graphType === 'accuracy' && accVisible.length < metricFull.length) return; + } + + if (advanceGuardRef.current === stepIndex) return; + advanceGuardRef.current = stepIndex; + + if (stepIndex === steps.length - 1) { + if (completedRef.current) return; + completedRef.current = true; + + if (!useMockStream) { + const model = { + model: 'AutoAI MockNet', + version: '0.2', + trainedAt: new Date().toISOString(), + metrics: { + loss: lastLossValueRef.current, + accuracy: lastAccValueRef.current, + }, + }; + writeJson(updateFileContentRef.current, '/artifacts/model.json', model); + writeJson(updateFileContentRef.current, '/config/model.json', model); + appendLog(updateFileContentRef.current, 'Build complete — artifacts written'); + } + + onComplete(); + return; + } + + const advance = () => { + startStep(stepIndex + 1, nowRef.current || performance.now()); + }; + if (typeof queueMicrotask === 'function') queueMicrotask(advance); + else Promise.resolve().then(advance); + }, [onComplete, stepIndex, stepProgress]); + + // Graph phase metric reveal + artifact writes. + useEffect(() => { + if (phase.kind !== 'graph') return; + const graphType = phase.graphType; + + const late = clamp01((phaseProgress - 0.12) / 0.88); + + if (graphType === 'loss') { + const targetCount = Math.max(0, Math.floor(lossFull.length * late)); + if (targetCount > lossVisible.length) { + const next = lossFull.slice(0, targetCount); + setLossVisible(next); + } + } + + if (graphType === 'accuracy') { + const series = metricFull; + const targetCount = Math.max(0, Math.floor(series.length * late)); + if (targetCount > accVisible.length) { + const next = series.slice(0, targetCount); + setAccVisible(next); + } + } + }, [phase, phaseProgress, lossFull, metricFull, lossVisible.length, accVisible.length]); + + const writeArtifact = (path: string, value: unknown) => { + writeJson(updateFileContentRef.current, path, value); + }; + + const phaseLabel = getPhaseKindLabel(phase); + const phaseExtra = + phase.kind === 'visual' + ? VISUAL_LABEL[phase.visualId] + : phase.kind === 'graph' + ? phase.graphType === 'accuracy' + ? metricKind + : phase.graphType + : ''; + + const evaluationMetrics = useMemo(() => { + const accuracy = metricsState.metricsSummary.accuracy; + const f1 = metricsState.metricsSummary.f1; + const rmse = metricsState.metricsSummary.rmse; + if (useRmse && typeof rmse === 'number') { + return undefined; + } + if (typeof accuracy !== 'number' && typeof f1 !== 'number') return undefined; + const precision = typeof f1 === 'number' ? Math.min(0.97, f1 + 0.04) : Math.min(0.95, (accuracy ?? 0.8) + 0.03); + const recall = typeof f1 === 'number' ? Math.max(0.55, f1 - 0.04) : Math.max(0.6, (accuracy ?? 0.8) - 0.04); + const computedF1 = typeof f1 === 'number' ? f1 : (2 * precision * recall) / Math.max(1e-9, precision + recall); + const computedAcc = typeof accuracy === 'number' ? accuracy : computedF1; + return { accuracy: computedAcc, precision, recall, f1: computedF1 }; + }, [metricsState.metricsSummary, useRmse]); + + return ( +
+
+ {/* Top: step overview grid (no horizontal scroller) */} +
+
+
+
+
{step.title}
+
{step.subtitle}
+
+
+
+ Step {stepIndex + 1}/{steps.length} · {phaseLabel} + {phaseExtra ? `: ${phaseExtra}` : ''} · ({phaseIndex + 1}/{step.phases.length}) +
+ +
+
+ +
+
+ {steps.map((s, idx) => { + const isActive = idx === stepIndex; + const isDone = idx < stepIndex; + const nodeBg = isDone + ? 'bg-replit-success/80 text-white border-replit-success/80' + : isActive + ? 'bg-replit-accent/90 text-white border-replit-accent/90' + : 'bg-replit-surface/35 text-replit-textMuted border-replit-border/60'; + + return ( +
+
+ {idx + 1} + {isActive ? ( +
+ ) : null} +
+
+ {s.title} +
+
+ ); + })} +
+
+
+
+ + {/* Main hero */} +
+
+ + {/* Operation */} + {phase.kind === 'operation' ? ( + +
+
+
Operation
+ +
+
Formula
+
+
+
+ +
+
+
+
+ +
+ +
+ +
+ {phaseProgress < 0.33 + ? 'Multiplying (row × column)…' + : phaseProgress < 0.66 + ? 'Accumulating partial sums…' + : 'Writing the next tensor…'} +
+
+
+
+ ) : null} + + {/* Graph */} + {phase.kind === 'graph' ? ( + +
+
+ {phase.graphType === 'loss' ? ( + ({ epoch: p.epoch, train_loss: p.train_loss, val_loss: p.val_loss }))} + /> + ) : ( + + )} +
+ {phase.graphType === 'loss' + ? 'Plotting loss curve…' + : useRmse + ? 'Plotting RMSE curve…' + : 'Plotting accuracy curve…'} +
+
+
+
+ ) : null} + + {/* Visual */} + {phase.kind === 'visual' ? ( + +
+
+
{phaseTitle(phase)}
+ {(() => { + const C = VISUALS[phase.visualId as VisualId]; + return ( + + ); + })()} +
+
+
+ ) : null} +
+
+
+
+
+ ); +} diff --git a/frontend/src/components/center/loader/TrainingLossVisualizer.tsx b/frontend/src/components/center/loader/TrainingLossVisualizer.tsx new file mode 100644 index 00000000..db6e2dc2 --- /dev/null +++ b/frontend/src/components/center/loader/TrainingLossVisualizer.tsx @@ -0,0 +1,95 @@ +import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts'; + +export type LossPoint = { + epoch: number; + train_loss: number; + val_loss: number; +}; + +interface TrainingLossVisualizerProps { + data: LossPoint[]; +} + +export default function TrainingLossVisualizer({ data }: TrainingLossVisualizerProps) { + const currentTrain = data.length > 0 ? data[data.length - 1]?.train_loss : null; + const currentVal = data.length > 0 ? data[data.length - 1]?.val_loss : null; + const currentEpoch = data.length > 0 ? data[data.length - 1]?.epoch : 0; + + return ( +
+
+
+
+
Training Loss
+
+
+ Epoch + {currentEpoch} +
+
+ +
+ {data.length === 0 && ( +
+
+
+

Waiting for data…

+
+
+ )} + + + + + + + + + + + +
+ +
+
+
+
+ Training Loss +
+

{currentTrain !== null ? currentTrain.toFixed(3) : '—'}

+
+
+
+
+ Validation Loss +
+

{currentVal !== null ? currentVal.toFixed(3) : '—'}

+
+
+
+
+ ); +} diff --git a/frontend/src/components/center/loader/types.ts b/frontend/src/components/center/loader/types.ts new file mode 100644 index 00000000..12728a82 --- /dev/null +++ b/frontend/src/components/center/loader/types.ts @@ -0,0 +1,60 @@ +import type { LossSurfaceSpec } from '../../../mock/backendEventTypes'; + +export type VisualId = + | 'neuralNetForward' + | 'neuralNetBackprop' + | 'gradDescent' + | 'evaluation' + | 'confusionMatrix' + | 'embeddingScatter' + | 'residuals'; + +export type StepPhase = + | { kind: 'operation'; weight?: number } + | { kind: 'graph'; graphType: 'loss' | 'accuracy'; weight?: number } + | { kind: 'visual'; visualId: VisualId; weight?: number }; + +export type StepDef = { + id: string; + title: string; + subtitle: string; + equations: string[]; + durationMs: number; + matrixLabel?: string; + matrixRows?: number; + matrixCols?: number; + phases: StepPhase[]; +}; + +export type VisualBaseProps = { + timeMs: number; + phaseProgress: number; + seed: number; + reducedMotion: boolean; +}; + +export type VisualProps = VisualBaseProps & { + writeArtifact?: (path: string, value: unknown) => void; + confusion?: number[][]; + metrics?: { accuracy: number; precision: number; recall: number; f1: number }; + curve?: { kind: 'pr' | 'roc'; points: Array<{ x: number; y: number }> }; + points?: Array<{ id: number; x: number; y: number; label: number; weight: number }>; + path?: Array<{ x: number; y: number }>; + residuals?: Array<{ pred: number; true: number; residual: number }>; + showConfusion?: boolean; + surfaceSpec?: LossSurfaceSpec | null; +}; + +export const clamp01 = (x: number) => Math.min(1, Math.max(0, x)); + +export const lerp = (a: number, b: number, t: number) => a + (b - a) * t; + +export const seeded = (n: number) => { + const x = Math.sin(n * 9999) * 10000; + return x - Math.floor(x); +}; + +export const formatClock = () => { + const t = new Date(); + return t.toISOString().split('T')[1].slice(0, 8); +}; diff --git a/frontend/src/components/center/loader/visuals/ConfusionMatrixViz.tsx b/frontend/src/components/center/loader/visuals/ConfusionMatrixViz.tsx new file mode 100644 index 00000000..1099a31d --- /dev/null +++ b/frontend/src/components/center/loader/visuals/ConfusionMatrixViz.tsx @@ -0,0 +1,98 @@ +import clsx from 'clsx'; +import type { VisualProps } from '../types'; +import { clamp01 } from '../types'; + +function buildFallbackConfusion(size: number) { + return Array.from({ length: size }, (_, r) => + Array.from({ length: size }, (_, c) => (r === c ? 45 + r * 6 : 8 + c * 3)) + ); +} + +export default function ConfusionMatrixViz({ confusion, phaseProgress, reducedMotion }: VisualProps) { + const confusionFinal = confusion?.length ? confusion : buildFallbackConfusion(3); + const classes = confusionFinal.length; + const totalCells = classes * classes; + const p = reducedMotion ? 1 : clamp01(phaseProgress); + + const flat = confusionFinal.flat(); + const maxValue = flat.length ? Math.max(...flat) : 1; + + const cellTarget = clamp01(p); + const confusionVisible = Array.from({ length: classes }, (_, r) => + Array.from({ length: classes }, (_, c) => { + const idx = r * classes + c; + const start = idx / totalCells; + const end = (idx + 1) / totalCells; + const local = clamp01((cellTarget - start) / Math.max(1e-9, end - start)); + const final = confusionFinal[r][c]; + return reducedMotion ? final : Math.min(final, Math.floor(final * local)); + }) + ); + + return ( +
+
+
+
+
Confusion Matrix
+
Large matrix view
+
+
{classes}×{classes}
+
+ +
+
+
+
Predicted Class
+
+
+ Actual Class +
+ + + + + {confusionVisible.map((_, idx) => ( + + ))} + + + + {confusionVisible.map((row, i) => ( + + + {row.map((cell, j) => { + const isDiag = i === j; + return ( + + ); + })} + + ))} + +
+ {idx} +
{i} + {cell} +
+
+
+
+
+
+
+ ); +} diff --git a/frontend/src/components/center/loader/visuals/EmbeddingScatterViz.tsx b/frontend/src/components/center/loader/visuals/EmbeddingScatterViz.tsx new file mode 100644 index 00000000..bb486da5 --- /dev/null +++ b/frontend/src/components/center/loader/visuals/EmbeddingScatterViz.tsx @@ -0,0 +1,193 @@ +import { useMemo } from 'react'; +import { Cell, ResponsiveContainer, Scatter, ScatterChart, Tooltip, XAxis, YAxis, CartesianGrid } from 'recharts'; +import type { VisualProps } from '../types'; +import { clamp01 } from '../types'; + +type ScatterPoint = { id: number; x: number; y: number; cluster: number; epoch: number }; + +function generateClusterColor(clusterIdx: number, totalClusters: number) { + const hue = (clusterIdx * 360) / Math.max(totalClusters, 20); + const saturation = 65 + (clusterIdx % 3) * 10; + const lightness = 55 + (clusterIdx % 2) * 5; + return `hsl(${hue}, ${saturation}%, ${lightness}%)`; +} + +export default function EmbeddingScatterViz({ points, phaseProgress, reducedMotion }: VisualProps) { + const data = useMemo(() => { + if (!points || points.length === 0) return []; + const sorted = [...points].sort((a, b) => a.id - b.id); + const pointsPerEpoch = 5; + return sorted.map((pt, idx) => ({ + id: pt.id, + x: pt.x, + y: pt.y, + cluster: pt.label, + epoch: Math.floor(idx / pointsPerEpoch), + })); + }, [points]); + + const maxEpoch = data.length ? Math.max(...data.map((p) => p.epoch)) : 0; + const revealProgress = reducedMotion ? 1 : clamp01(phaseProgress); + const currentEpoch = Math.max(0, Math.floor(maxEpoch * revealProgress)); + const visibleData = useMemo(() => data.filter((p) => p.epoch <= currentEpoch), [data, currentEpoch]); + + const allClusterKeys = useMemo(() => { + const keys = new Set(); + for (const p of data) keys.add(p.cluster); + return Array.from(keys).sort((a, b) => a - b); + }, [data]); + const totalClusters = allClusterKeys.length || 1; + + const stats = useMemo(() => { + const clusterCounts = new Map(); + let minX = Infinity; + let maxX = -Infinity; + let minY = Infinity; + let maxY = -Infinity; + for (const p of visibleData) { + clusterCounts.set(p.cluster, (clusterCounts.get(p.cluster) ?? 0) + 1); + minX = Math.min(minX, p.x); + maxX = Math.max(maxX, p.x); + minY = Math.min(minY, p.y); + maxY = Math.max(maxY, p.y); + } + return { clusterCounts, minX, maxX, minY, maxY }; + }, [visibleData]); + + const clusterKeys = Array.from(stats.clusterCounts.keys()).sort((a, b) => a - b); + const clustersCount = clusterKeys.length || totalClusters; + + const xPadding = stats.minX !== Infinity ? Math.max(0.5, (stats.maxX - stats.minX) * 0.15) : 1; + const yPadding = stats.minY !== Infinity ? Math.max(0.5, (stats.maxY - stats.minY) * 0.15) : 1; + const rawXMin = stats.minX !== Infinity ? stats.minX - xPadding : -10; + const rawXMax = stats.maxX !== Infinity ? stats.maxX + xPadding : 10; + const rawYMin = stats.minY !== Infinity ? stats.minY - yPadding : -10; + const rawYMax = stats.maxY !== Infinity ? stats.maxY + yPadding : 10; + + const domainStep = (min: number, max: number) => { + const span = Math.max(1e-6, max - min); + const rough = span / 5; + const pow10 = Math.pow(10, Math.floor(Math.log10(rough))); + const x = rough / pow10; + const base = x <= 1 ? 1 : x <= 2 ? 2 : x <= 5 ? 5 : 10; + return base * pow10; + }; + + const xStep = domainStep(rawXMin, rawXMax); + const yStep = domainStep(rawYMin, rawYMax); + const xDomain: [number, number] = [Math.floor(rawXMin / xStep) * xStep, Math.ceil(rawXMax / xStep) * xStep]; + const yDomain: [number, number] = [Math.floor(rawYMin / yStep) * yStep, Math.ceil(rawYMax / yStep) * yStep]; + + const tooltipStyle = { + backgroundColor: 'rgba(20,24,36,0.9)', + border: '1px solid rgba(148,163,184,0.25)', + borderRadius: '8px', + color: '#fff', + }; + + return ( +
+
+
+
+
Embedding Space Visualization
+
Point clusters converging over epochs
+
+
n={visibleData.length}
+
+ +
+ {data.length === 0 && ( +
+
+
+

Waiting for embedding data…

+
+
+ )} + + + + v.toFixed(2)} + label={{ + value: 'Embedding Dimension 1', + position: 'insideBottom', + offset: -10, + fill: 'rgb(var(--replit-text-muted))', + }} + /> + v.toFixed(2)} + label={{ + value: 'Embedding Dimension 2', + angle: -90, + position: 'insideLeft', + fill: 'rgb(var(--replit-text-muted))', + }} + /> + { + const value = Number(v ?? 0).toFixed(3); + if (name === 'x') return [value, 'X']; + if (name === 'y') return [value, 'Y']; + return [value, name ?? 'value']; + }} + /> + + {visibleData.map((entry) => ( + + ))} + + + +
+ +
+ Clusters: + {clustersCount} + | + Epoch: + {currentEpoch} + | + Points: + {visibleData.length} +
+ +
+ {clusterKeys.map((cluster) => { + const count = stats.clusterCounts.get(cluster) ?? 0; + const color = generateClusterColor(cluster, totalClusters); + return ( +
+
+
+ {cluster} +
+

{count}

+
+ ); + })} +
+
+
+ ); +} diff --git a/frontend/src/components/center/loader/visuals/EvaluationViz.tsx b/frontend/src/components/center/loader/visuals/EvaluationViz.tsx new file mode 100644 index 00000000..fa22b344 --- /dev/null +++ b/frontend/src/components/center/loader/visuals/EvaluationViz.tsx @@ -0,0 +1,403 @@ +import { useEffect, useMemo, useRef, useState } from 'react'; +import clsx from 'clsx'; +import { + Line, + LineChart, + CartesianGrid, + ResponsiveContainer, + Tooltip, + XAxis, + YAxis, +} from 'recharts'; +import type { VisualProps } from '../types'; +import { clamp01, lerp, seeded } from '../types'; + +type Metric = { + key: string; + label: string; + value: number; // 0..1 +}; + +type CurvePt = { x: number; y: number }; + +function buildConfusion(classCount: number, seed: number) { + const s = seed * 113 + 404; + const total = 220 + Math.floor(seeded(s + 1) * 220); + + // Start with diagonal-heavy matrix. + const mat: number[][] = Array.from({ length: classCount }, () => Array.from({ length: classCount }, () => 0)); + + let remaining = total; + for (let i = 0; i < classCount; i += 1) { + const diag = Math.floor((0.55 + seeded(s + 20 + i) * 0.25) * (total / classCount)); + mat[i][i] = diag; + remaining -= diag; + } + + // Distribute remaining across off-diagonals. + const slots: Array<[number, number]> = []; + for (let r = 0; r < classCount; r += 1) { + for (let c = 0; c < classCount; c += 1) { + if (r !== c) slots.push([r, c]); + } + } + + for (let k = 0; k < slots.length; k += 1) { + if (remaining <= 0) break; + const [r, c] = slots[k]; + const portion = Math.min(remaining, Math.floor(seeded(s + 300 + k * 7) * (total * 0.08))); + mat[r][c] += portion; + remaining -= portion; + } + + // Put any leftovers anywhere. + if (remaining > 0) { + mat[0][classCount - 1] += remaining; + } + + return mat; +} + +function computeMetricsFromConfusion(mat: number[][]) { + const k = mat.length; + const total = mat.flat().reduce((a, b) => a + b, 0); + const tp = Array.from({ length: k }, (_, i) => mat[i][i]); + const rowSum = Array.from({ length: k }, (_, r) => mat[r].reduce((a, b) => a + b, 0)); + const colSum = Array.from({ length: k }, (_, c) => mat.reduce((a, row) => a + row[c], 0)); + + const precisionPer = tp.map((t, i) => t / Math.max(1, colSum[i])); + const recallPer = tp.map((t, i) => t / Math.max(1, rowSum[i])); + const f1Per = tp.map((_, i) => { + const p = precisionPer[i]; + const r = recallPer[i]; + return (2 * p * r) / Math.max(1e-9, p + r); + }); + + const macroPrecision = precisionPer.reduce((a, b) => a + b, 0) / k; + const macroRecall = recallPer.reduce((a, b) => a + b, 0) / k; + const macroF1 = f1Per.reduce((a, b) => a + b, 0) / k; + const accuracy = tp.reduce((a, b) => a + b, 0) / Math.max(1, total); + + return { + accuracy, + precision: macroPrecision, + recall: macroRecall, + f1: macroF1, + }; +} + +function buildCurve(kind: 'pr' | 'roc', seed: number): CurvePt[] { + const pts: CurvePt[] = []; + const n = 60; + for (let i = 0; i < n; i += 1) { + const x = i / (n - 1); + let y: number; + if (kind === 'pr') { + // Match example: precision decays as recall increases. + y = clamp01(0.95 - x * 0.3); + } else { + // Match example: TPR rises above diagonal. + y = clamp01(x + 0.3 + (1 - x) * 0.5); + } + pts.push({ x, y }); + } + return pts; +} + +function computeAuc(points: CurvePt[]) { + if (points.length < 2) return 0; + const sorted = [...points].sort((a, b) => a.x - b.x); + let area = 0; + for (let i = 1; i < sorted.length; i += 1) { + const prev = sorted[i - 1]; + const next = sorted[i]; + area += (next.x - prev.x) * (prev.y + next.y) * 0.5; + } + return clamp01(area); +} + +export default function EvaluationViz({ timeMs, phaseProgress, seed, reducedMotion, writeArtifact, confusion, metrics, curve, showConfusion }: VisualProps) { + const [classCount, setClassCount] = useState<2 | 3>(3); + const showMatrix = showConfusion ?? true; + + useEffect(() => { + if (confusion && (confusion.length === 2 || confusion.length === 3)) { + setClassCount(confusion.length as 2 | 3); + } + }, [confusion]); + + const { confusionFinal, metricsFinal, prCurveFull, rocCurveFull } = useMemo(() => { + const confusionFinal = confusion?.length + ? confusion + : showMatrix + ? buildConfusion(classCount, seed) + : [[0]]; + const derived = computeMetricsFromConfusion(confusionFinal); + + const prCurveFull = + curve?.kind === 'pr' + ? curve.points + : buildCurve('pr', seed + confusionFinal.length * 77); + const rocCurveFull = + curve?.kind === 'roc' + ? curve.points + : buildCurve('roc', seed + confusionFinal.length * 91); + + const auprc = computeAuc(prCurveFull); + const auroc = computeAuc(rocCurveFull); + + const metricsFinal: Metric[] = metrics + ? [ + { key: 'accuracy', label: 'Accuracy', value: metrics.accuracy }, + { key: 'precision', label: 'Precision', value: metrics.precision }, + { key: 'recall', label: 'Recall', value: metrics.recall }, + { key: 'f1', label: 'F1 Score', value: metrics.f1 }, + { key: 'auroc', label: 'AUROC', value: auroc }, + { key: 'auprc', label: 'AUPRC', value: auprc }, + ] + : [ + { key: 'accuracy', label: 'Accuracy', value: derived.accuracy }, + { key: 'precision', label: 'Precision', value: derived.precision }, + { key: 'recall', label: 'Recall', value: derived.recall }, + { key: 'f1', label: 'F1 Score', value: derived.f1 }, + { key: 'auroc', label: 'AUROC', value: auroc }, + { key: 'auprc', label: 'AUPRC', value: auprc }, + ]; + + return { confusionFinal, metricsFinal, prCurveFull, rocCurveFull }; + }, [classCount, confusion, curve, metrics, seed, showMatrix]); + + const p = reducedMotion ? 1 : clamp01(phaseProgress); + const t = reducedMotion ? 1 : timeMs / 1000; + + // Confusion matrix fills cell-by-cell. + const effectiveClasses = confusionFinal.length; + const totalCells = effectiveClasses * effectiveClasses; + const cellTarget = clamp01((p - 0.05) / 0.55); + + const confusionVisible = useMemo(() => { + const out: number[][] = Array.from({ length: effectiveClasses }, () => Array.from({ length: effectiveClasses }, () => 0)); + + for (let idx = 0; idx < totalCells; idx += 1) { + const r = Math.floor(idx / effectiveClasses); + const c = idx % effectiveClasses; + const final = confusionFinal[r][c]; + + if (reducedMotion) { + out[r][c] = final; + continue; + } + + const start = idx / totalCells; + const end = (idx + 1) / totalCells; + const local = clamp01((cellTarget - start) / Math.max(1e-9, end - start)); + + // Count-up with a slight wobble. + const wobble = 0.02 * Math.sin(t * 4.2 + idx); + const v = Math.floor(final * clamp01(local + wobble)); + out[r][c] = clamp01(local) <= 0 ? 0 : Math.min(final, Math.max(0, v)); + } + + return out; + }, [confusionFinal, reducedMotion, totalCells, cellTarget, t, effectiveClasses]); + + const metricsVisible = useMemo(() => { + const local = reducedMotion ? 1 : clamp01((p - 0.45) / 0.35); + return metricsFinal.map((m) => ({ ...m, shown: clamp01(m.value * local) })); + }, [metricsFinal, p, reducedMotion]); + + const confusionMax = useMemo(() => { + const flat = confusionFinal.flat(); + return flat.length ? Math.max(...flat) : 1; + }, [confusionFinal]); + + const curveVisible = useMemo(() => { + const prSorted = [...prCurveFull].sort((a, b) => a.x - b.x); + const rocSorted = [...rocCurveFull].sort((a, b) => a.x - b.x); + if (reducedMotion) return { pr: prSorted, roc: rocSorted }; + const local = clamp01((p - 0.4) / 0.35); + const prN = Math.max(2, Math.floor(lerp(2, prSorted.length, local))); + const rocN = Math.max(2, Math.floor(lerp(2, rocSorted.length, local))); + return { pr: prSorted.slice(0, prN), roc: rocSorted.slice(0, rocN) }; + }, [prCurveFull, rocCurveFull, p, reducedMotion]); + + // Artifact writing near completion. + const wroteRef = useRef({ confusion: false, metrics: false }); + useEffect(() => { + if (!writeArtifact) return; + if (reducedMotion || p > 0.86) { + if (!wroteRef.current.confusion) { + wroteRef.current.confusion = true; + writeArtifact('/artifacts/confusion_matrix.json', { + classes: classCount, + matrix: confusionFinal, + }); + } + if (!wroteRef.current.metrics) { + wroteRef.current.metrics = true; + writeArtifact('/artifacts/metrics.json', { + classes: effectiveClasses, + metrics: Object.fromEntries(metricsFinal.map((m) => [m.key, m.value])), + }); + } + } + }, [confusionFinal, metricsFinal, p, reducedMotion, writeArtifact, effectiveClasses]); + + return ( +
+
+
+
+
Model Evaluation Dashboard
+
Confusion Matrix, PR/ROC Curves, and Performance Metrics
+
+
+ +
+ {metricsVisible.map((m) => ( +
+
{m.label}
+
{m.shown.toFixed(3)}
+
+ ))} +
+ +
+
+
Precision-Recall Curve
+
+ + ({ recall: pt.x, precision: pt.y }))} + margin={{ top: 8, right: 16, bottom: 12, left: 12 }} + > + + v.toFixed(1)} + label={{ value: 'Recall', position: 'insideBottom', offset: -5, fill: '#9CA3AF' }} + /> + v.toFixed(1)} + label={{ value: 'Precision', angle: -90, position: 'insideLeft', fill: '#9CA3AF' }} + /> + + + + +
+
+ +
+
ROC Curve
+
+ + ({ fpr: pt.x, tpr: pt.y }))} + margin={{ top: 8, right: 16, bottom: 12, left: 12 }} + > + + v.toFixed(1)} + label={{ value: 'False Positive Rate', position: 'insideBottom', offset: -5, fill: '#9CA3AF' }} + /> + v.toFixed(1)} + label={{ value: 'True Positive Rate', angle: -90, position: 'insideLeft', fill: '#9CA3AF' }} + /> + + + + + +
+
+
+ + {showMatrix ? ( +
+
Confusion Matrix
+
+
+
Predicted Class
+
+
+ Actual Class +
+ + + + + {confusionVisible.map((_, idx) => ( + + ))} + + + + {confusionVisible.map((row, i) => ( + + + {row.map((cell, j) => { + const isDiag = i === j; + return ( + + ); + })} + + ))} + +
+ {idx} +
{i} + {cell} +
+
+
+
+
+ ) : null} +
+
+ ); +} diff --git a/frontend/src/components/center/loader/visuals/GradientDescentViz.tsx b/frontend/src/components/center/loader/visuals/GradientDescentViz.tsx new file mode 100644 index 00000000..860813a3 --- /dev/null +++ b/frontend/src/components/center/loader/visuals/GradientDescentViz.tsx @@ -0,0 +1,841 @@ +import { useEffect, useMemo, useRef, useState } from 'react'; +import * as THREE from 'three'; +import type { VisualProps } from '../types'; +import type { LossSurfaceSpec } from '../../../../mock/backendEventTypes'; +import { clamp01, lerp } from '../types'; + +type RGB = { r: number; g: number; b: number }; + +function getCssVarRgb(varName: string, fallback: RGB): RGB { + if (typeof window === 'undefined') return fallback; + const raw = getComputedStyle(document.documentElement).getPropertyValue(varName).trim(); + // Expected format in this app: "R G B" (space-separated) + const parts = raw + .split(/\s+/) + .map((n) => Number(n)) + .filter((n) => Number.isFinite(n)); + if (parts.length >= 3) return { r: parts[0], g: parts[1], b: parts[2] }; + return fallback; +} + +function toThreeColor(rgb: RGB) { + return new THREE.Color(rgb.r / 255, rgb.g / 255, rgb.b / 255); +} + +/* +function heightFn(x: number, y: number) { + // Match the user's example surface. + // z = sin(x) * cos(y) * exp(-(x^2+y^2)/20) * 8 + (x^2+y^2)/10 - 3 + return Math.sin(x) * Math.cos(y) * Math.exp(-(x * x + y * y) / 20) * 8 + (x * x + y * y) / 10 - 3; +} +*/ + +const defaultHeightFn = (x: number, y: number) => + Math.sin(x) * Math.cos(y) * Math.exp(-(x * x + y * y) / 20) * 8 + (x * x + y * y) / 10 - 3; + +function makeHeightFn(spec?: LossSurfaceSpec | null) { + if (!spec) return defaultHeightFn; + switch (spec.kind) { + case 'fixed_example': + return defaultHeightFn; + case 'bowl': { + const params = spec.params; + const a = params?.a ?? 0.18; + const b = params?.b ?? 0.18; + const tiltX = params?.tiltX ?? 0; + const tiltY = params?.tiltY ?? 0; + const offset = params?.offset ?? 0; + return (x: number, y: number) => a * x * x + b * y * y + tiltX * x + tiltY * y + offset; + } + case 'multi_hill': { + const params = spec.params; + const bowlStrength = params?.bowlStrength ?? 0.06; + const offset = params?.offset ?? 0; + const hills = params?.hills ?? []; + return (x: number, y: number) => { + const r2 = x * x + y * y; + let z = bowlStrength * r2 + offset; + for (const hill of hills) { + const dx = x - hill.x; + const dy = y - hill.y; + const s2 = Math.max(1e-4, hill.sigma * hill.sigma); + z += hill.amp * Math.exp(-(dx * dx + dy * dy) / (2 * s2)); + } + return z; + }; + } + case 'ripples': { + const params = spec.params; + const amp = params?.amp ?? 2.5; + const freq = params?.freq ?? 2; + const decay = params?.decay ?? 0.15; + const bowlStrength = params?.bowlStrength ?? 0.06; + const offset = params?.offset ?? 0; + return (x: number, y: number) => { + const r2 = x * x + y * y; + const ripples = amp * Math.sin(freq * x) * Math.cos(freq * y) * Math.exp(-decay * r2); + return ripples + bowlStrength * r2 + offset; + }; + } + default: + return defaultHeightFn; + } +} + +function smoothstep(t: number) { + const x = clamp01(t); + return x * x * (3 - 2 * x); +} + +function colormap(t: number) { + // Blue -> Cyan -> Green -> Yellow -> Red + const x = clamp01(t); + const c = new THREE.Color(); + if (x < 0.25) { + // Blue to Cyan + c.setRGB(0, x * 4, 1); + } else if (x < 0.5) { + // Cyan to Green + c.setRGB(0, 1, 1 - (x - 0.25) * 4); + } else if (x < 0.75) { + // Green to Yellow + c.setRGB((x - 0.5) * 4, 1, 0); + } else { + // Yellow to Red + c.setRGB(1, 1 - (x - 0.75) * 4, 0); + } + return c; +} + +function niceStep(range: number, targetTicks: number) { + const safe = Math.max(1e-9, range); + const rough = safe / Math.max(1, targetTicks); + const pow10 = Math.pow(10, Math.floor(Math.log10(rough))); + const x = rough / pow10; + let base = 1; + if (x <= 1) base = 1; + else if (x <= 2) base = 2; + else if (x <= 5) base = 5; + else base = 10; + return base * pow10; +} + +export default function GradientDescentViz({ timeMs, phaseProgress, seed, reducedMotion, path, surfaceSpec }: VisualProps) { + const containerRef = useRef(null); + const canvasHostRef = useRef(null); + const lockRef = useRef<{ locked: boolean; x: number; y: number }>({ + locked: false, + x: 0, + y: 0, + }); + + + // Keep prop for API compatibility; this visualization is deterministic. + void seed; + + const [themeMode, setThemeMode] = useState<'dark' | 'light'>(() => { + if (typeof document === 'undefined') return 'dark'; + return document.documentElement.classList.contains('dark') ? 'dark' : 'light'; + }); + + useEffect(() => { + if (typeof document === 'undefined') return; + const root = document.documentElement; + const update = () => setThemeMode(root.classList.contains('dark') ? 'dark' : 'light'); + update(); + + const mo = new MutationObserver(() => update()); + mo.observe(root, { attributes: true, attributeFilter: ['class'] }); + return () => mo.disconnect(); + }, []); + + const heightFn = useMemo(() => makeHeightFn(surfaceSpec), [surfaceSpec]); + + const zScale = surfaceSpec?.zScale ?? 0.3; + const resolution = 50; + const size = 6; + const steps = 100; + const eta = 0.05; + + const { dfx, dfy } = useMemo(() => { + const h = 0.02; + return { + dfx: (x: number, y: number) => (heightFn(x + h, y) - heightFn(x - h, y)) / (2 * h), + dfy: (x: number, y: number) => (heightFn(x, y + h) - heightFn(x, y - h)) / (2 * h), + }; + }, [heightFn]); + + const computedDomainHalf = useMemo(() => { + // Shrink the *numeric* domain to cover the interesting region (global extrema + padding), + // while keeping the rendered surface the same world size. + let mn = Infinity; + let mx = -Infinity; + let minX = 0; + let minY = 0; + let maxX = 0; + let maxY = 0; + + for (let i = 0; i <= resolution; i += 1) { + for (let j = 0; j <= resolution; j += 1) { + const x = (i / resolution) * size * 2 - size; + const y = (j / resolution) * size * 2 - size; + const z = heightFn(x, y); + if (z < mn) { + mn = z; + minX = x; + minY = y; + } + if (z > mx) { + mx = z; + maxX = x; + maxY = y; + } + } + } + + const needed = Math.max(Math.abs(minX), Math.abs(minY), Math.abs(maxX), Math.abs(maxY)); + const padded = needed + 1.2; + // Clamp to sensible bounds so axes never disappear or blow up. + return clamp01(padded / size) * size + 2.5 * (1 - clamp01(padded / size)); + }, [heightFn, resolution, size]); + + const domainHalf = surfaceSpec?.domainHalf ?? computedDomainHalf; + + const { minZ, maxZ } = useMemo(() => { + let mn = Infinity; + let mx = -Infinity; + for (let i = 0; i <= resolution; i += 1) { + for (let j = 0; j <= resolution; j += 1) { + const x = (i / resolution) * domainHalf * 2 - domainHalf; + const y = (j / resolution) * domainHalf * 2 - domainHalf; + const z = heightFn(x, y); + mn = Math.min(mn, z); + mx = Math.max(mx, z); + } + } + return { minZ: mn, maxZ: mx }; + }, [domainHalf, heightFn, resolution]); + + const p = reducedMotion ? 1 : clamp01(phaseProgress); + + // Speed up the surface generation a bit. + const surfaceSplit = 0.45; + const surfaceProgress = reducedMotion ? 1 : clamp01(p / surfaceSplit); + const gdProgress = reducedMotion ? 1 : clamp01((p - surfaceSplit) / (1 - surfaceSplit)); + + const effectiveSteps = path && path.length > 1 ? path.length - 1 : steps; + const currentIter = reducedMotion ? effectiveSteps : Math.floor(lerp(0, effectiveSteps, gdProgress)); + + const latestRef = useRef({ + timeMs, + reducedMotion, + surfaceProgress, + gdProgress, + }); + + useEffect(() => { + latestRef.current = { timeMs, reducedMotion, surfaceProgress, gdProgress }; + }, [gdProgress, reducedMotion, surfaceProgress, timeMs]); + + useEffect(() => { + const host = canvasHostRef.current; + if (!host) return; + + // Clear any previous canvas (hot reload safety). + host.innerHTML = ''; + // ✅ reset convergence lock whenever the scene re-mounts + lockRef.current = { locked: false, x: 0, y: 0 }; + + + const fallbackAccent = { r: 99, g: 102, b: 241 }; + const fallbackSuccess = { r: 34, g: 197, b: 94 }; + + const isDark = themeMode === 'dark'; + const bgRgb = isDark ? { r: 0, g: 0, b: 0 } : { r: 255, g: 255, b: 255 }; + const accentRgb = getCssVarRgb('--replit-accent', fallbackAccent); + const successRgb = getCssVarRgb('--replit-success', fallbackSuccess); + + const axisRgb = isDark ? { r: 255, g: 255, b: 255 } : { r: 0, g: 0, b: 0 }; + + const scene = new THREE.Scene(); + scene.background = toThreeColor(bgRgb); + + const camera = new THREE.PerspectiveCamera(60, 1, 0.1, 100); + + const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: false, powerPreference: 'high-performance' }); + renderer.setPixelRatio(Math.min(2, window.devicePixelRatio || 1)); + host.appendChild(renderer.domElement); + + // Keep lighting bright even in light theme + const ambient = new THREE.AmbientLight(new THREE.Color(1, 1, 1), 0.7); + scene.add(ambient); + + const dir = new THREE.DirectionalLight(new THREE.Color(1, 1, 1), 0.85); + dir.position.set(6, 10, 6); + scene.add(dir); + + const geo = new THREE.PlaneGeometry(size * 2, size * 2, resolution, resolution); + // Rotate so Y is up. + geo.rotateX(-Math.PI / 2); + + const posAttr = geo.getAttribute('position') as THREE.BufferAttribute; + const vertexCount = posAttr.count; + + const heightsRaw = new Float32Array(vertexCount); + const heightsScaled = new Float32Array(vertexCount); + const distances = new Float32Array(vertexCount); + let mnRaw = Infinity; + let mxRaw = -Infinity; + let minIdx = 0; + let maxIdx = 0; + + for (let i = 0; i < vertexCount; i += 1) { + const x = posAttr.getX(i); + const z = posAttr.getZ(i); + const fx = (x / size) * domainHalf; + const fy = (z / size) * domainHalf; + const h = heightFn(fx, fy); + heightsRaw[i] = h; + heightsScaled[i] = h * zScale; + if (h < mnRaw) { + mnRaw = h; + minIdx = i; + } + if (h > mxRaw) { + mxRaw = h; + maxIdx = i; + } + distances[i] = Math.hypot(x, z); + } + + const colorAttr = new THREE.BufferAttribute(new Float32Array(vertexCount * 3), 3); + geo.setAttribute('color', colorAttr); + + for (let i = 0; i < vertexCount; i += 1) { + const t = (heightsRaw[i] - mnRaw) / Math.max(1e-9, mxRaw - mnRaw); + const c = colormap(t); + colorAttr.setXYZ(i, c.r, c.g, c.b); + } + colorAttr.needsUpdate = true; + + // Initialize all vertices at min height. + for (let i = 0; i < vertexCount; i += 1) { + posAttr.setY(i, mnRaw * zScale); + } + posAttr.needsUpdate = true; + geo.computeVertexNormals(); + + const mat = new THREE.MeshPhongMaterial({ + vertexColors: true, + side: THREE.DoubleSide, + shininess: 30, + transparent: true, + opacity: 0.96, + }); + + // (Optional but recommended) prevent z-fighting shimmer under the wireframe + mat.polygonOffset = true; + mat.polygonOffsetFactor = 1; + mat.polygonOffsetUnits = 1; + + const surface = new THREE.Mesh(geo, mat); + surface.scale.set(1.08, 1, 1.08); + scene.add(surface); + + // ✅ PERFECTLY STICKING WIREFRAME: + // Render a wireframe Mesh using the SAME geometry, so it always matches the surface exactly. + const wireMat = new THREE.MeshBasicMaterial({ + color: toThreeColor(axisRgb), + wireframe: true, + transparent: true, + opacity: 0, + }); + + const wireframe = new THREE.Mesh(geo, wireMat); + wireframe.scale.copy(surface.scale); + + // Render on top for consistent visibility + wireMat.depthTest = false; + wireMat.depthWrite = false; + wireframe.renderOrder = 10; + scene.add(wireframe); + + // Axes + ground grid (match user's style) + const axisOffset = mnRaw * zScale - 2; + const targetY = mnRaw * zScale + 2.2; + + const axisMaterial = new THREE.MeshBasicMaterial({ color: toThreeColor(axisRgb) }); + const axisRadius = 0.035; + const createAxisCylinder = (start: THREE.Vector3, end: THREE.Vector3) => { + const dirVec = new THREE.Vector3().subVectors(end, start); + const len = dirVec.length(); + const mid = new THREE.Vector3().addVectors(start, end).multiplyScalar(0.5); + const g = new THREE.CylinderGeometry(axisRadius, axisRadius, len, 12); + const m = axisMaterial.clone(); + const mesh = new THREE.Mesh(g, m); + mesh.position.copy(mid); + // Default cylinder is Y-up. + mesh.quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), dirVec.normalize()); + return mesh; + }; + + const axisWorldHalf = size * 1.02; + const xAxis = createAxisCylinder( + new THREE.Vector3(-axisWorldHalf, axisOffset, 0), + new THREE.Vector3(axisWorldHalf, axisOffset, 0) + ); + const yAxis = createAxisCylinder( + new THREE.Vector3(0, axisOffset, -axisWorldHalf), + new THREE.Vector3(0, axisOffset, axisWorldHalf) + ); + const zAxis = createAxisCylinder( + new THREE.Vector3(-axisWorldHalf, axisOffset, -axisWorldHalf), + new THREE.Vector3(-axisWorldHalf, mxRaw * zScale + 4, -axisWorldHalf) + ); + scene.add(xAxis); + scene.add(yAxis); + scene.add(zAxis); + + const gridMaterial = new THREE.LineBasicMaterial({ + color: toThreeColor(axisRgb), + transparent: true, + opacity: isDark ? 0.28 : 0.22, + }); + const tickStep = niceStep(domainHalf * 2, 6); + const tickMin = Math.ceil(-domainHalf / tickStep) * tickStep; + const tickMax = Math.floor(domainHalf / tickStep) * tickStep; + + for (let v = tickMin; v <= tickMax + 1e-6; v += tickStep) { + const w = (v / domainHalf) * size; + // Lines parallel to X (varying Y) + { + const pts = [new THREE.Vector3(-size, axisOffset, w), new THREE.Vector3(size, axisOffset, w)]; + const g = new THREE.BufferGeometry().setFromPoints(pts); + const line = new THREE.Line(g, gridMaterial); + scene.add(line); + } + // Lines parallel to Y (varying X) + { + const pts = [new THREE.Vector3(w, axisOffset, -size), new THREE.Vector3(w, axisOffset, size)]; + const g = new THREE.BufferGeometry().setFromPoints(pts); + const line = new THREE.Line(g, gridMaterial); + scene.add(line); + } + } + + const canvasFill = isDark ? 'rgb(255 255 255)' : 'rgb(0 0 0)'; + const createTextSprite = (message: string, x: number, y: number, z: number) => { + const canvas = document.createElement('canvas'); + const context = canvas.getContext('2d'); + if (!context) return null; + + canvas.width = 256; + canvas.height = 128; + context.clearRect(0, 0, canvas.width, canvas.height); + context.font = + 'Bold 86px ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace'; + context.fillStyle = canvasFill; + context.textAlign = 'center'; + context.textBaseline = 'middle'; + context.fillText(message, canvas.width / 2, canvas.height / 2); + + const texture = new THREE.CanvasTexture(canvas); + texture.minFilter = THREE.LinearFilter; + texture.magFilter = THREE.LinearFilter; + + const spriteMaterial = new THREE.SpriteMaterial({ map: texture, transparent: true }); + const sprite = new THREE.Sprite(spriteMaterial); + sprite.position.set(x, y, z); + sprite.scale.set(2.2, 1.1, 1); + return sprite; + }; + + for (let v = tickMin; v <= tickMax + 1e-6; v += tickStep) { + const w = (v / domainHalf) * size; + const msg = Math.abs(v) < 1e-6 ? '0' : Number.isInteger(v) ? v.toString() : v.toFixed(1); + const xLabelTick = createTextSprite(msg, w, axisOffset - 1, 0); + if (xLabelTick) scene.add(xLabelTick); + const yLabelTick = createTextSprite(msg, 0, axisOffset - 1, w); + if (yLabelTick) scene.add(yLabelTick); + } + + const zRange = mxRaw - mnRaw; + const zStep = niceStep(zRange, 6); + const zMin = Math.ceil(mnRaw / zStep) * zStep; + const zMax = Math.floor(mxRaw / zStep) * zStep; + for (let v = zMin; v <= zMax + 1e-6; v += zStep) { + const msg = Number.isInteger(v) ? v.toString() : v.toFixed(1); + const label = createTextSprite(msg, -axisWorldHalf - 1.2, v * zScale, -axisWorldHalf); + if (label) scene.add(label); + } + + const xLabel = createTextSprite('X', axisWorldHalf + 1.5, axisOffset, 0); + const yLabel = createTextSprite('Y', 0, axisOffset, axisWorldHalf + 1.5); + const zLabel = createTextSprite('Z', -axisWorldHalf - 1.5, mxRaw * zScale + 2, -axisWorldHalf); + if (xLabel) scene.add(xLabel); + if (yLabel) scene.add(yLabel); + if (zLabel) scene.add(zLabel); + + // Markers for absolute min/max. + const minPos = new THREE.Vector3(posAttr.getX(minIdx), heightsScaled[minIdx], posAttr.getZ(minIdx)); + const maxPos = new THREE.Vector3(posAttr.getX(maxIdx), heightsScaled[maxIdx], posAttr.getZ(maxIdx)); + + const markerGeo = new THREE.SphereGeometry(0.14, 18, 18); + const minMarkerMat = new THREE.MeshPhongMaterial({ + color: toThreeColor(successRgb), + emissive: toThreeColor(successRgb), + emissiveIntensity: 0.45, + }); + const maxMarkerMat = new THREE.MeshPhongMaterial({ + color: toThreeColor(accentRgb), + emissive: toThreeColor(accentRgb), + emissiveIntensity: 0.35, + }); + const minMarker = new THREE.Mesh(markerGeo, minMarkerMat); + const maxMarker = new THREE.Mesh(markerGeo, maxMarkerMat); + minMarker.position.copy(minPos); + maxMarker.position.copy(maxPos); + scene.add(minMarker); + scene.add(maxMarker); + + const ringGeo = new THREE.RingGeometry(0.18, 0.26, 48); + const minRingMat = new THREE.MeshBasicMaterial({ + color: toThreeColor(successRgb), + transparent: true, + opacity: 0.55, + side: THREE.DoubleSide, + }); + const maxRingMat = new THREE.MeshBasicMaterial({ + color: toThreeColor(accentRgb), + transparent: true, + opacity: 0.4, + side: THREE.DoubleSide, + }); + const minRing = new THREE.Mesh(ringGeo, minRingMat); + const maxRing = new THREE.Mesh(ringGeo, maxRingMat); + minRing.rotation.x = -Math.PI / 2; + maxRing.rotation.x = -Math.PI / 2; + minRing.position.set(minPos.x, mnRaw * zScale + 0.01, minPos.z); + maxRing.position.set(maxPos.x, mnRaw * zScale + 0.01, maxPos.z); + scene.add(minRing); + scene.add(maxRing); + + // Path line (preallocate max points). + const pathPositions = new Float32Array((effectiveSteps + 1) * 3); + const pathGeo = new THREE.BufferGeometry(); + const pathAttr = new THREE.BufferAttribute(pathPositions, 3); + pathGeo.setAttribute('position', pathAttr); + pathGeo.setDrawRange(0, 0); + + const markerColor = new THREE.Color(1, 0.0, 0.0); // 🔥 pure red + + const pathMat = new THREE.LineBasicMaterial({ color: markerColor }); +const pathLine = new THREE.Line(pathGeo, pathMat); +scene.add(pathLine); + +// ✅ Thick trail mesh (tube) +const trailMat = new THREE.MeshBasicMaterial({ + color: markerColor, + transparent: true, + opacity: 0.85, + depthTest: false, + depthWrite: false, +}); + +const trailCurve = new THREE.CatmullRomCurve3([ + new THREE.Vector3(0, 0, 0), + new THREE.Vector3(0.01, 0.01, 0.01), +]); + +const trailMesh = new THREE.Mesh(new THREE.TubeGeometry(trailCurve, 64, 0.07, 12, false), trailMat); +trailMesh.renderOrder = 25; +scene.add(trailMesh); + + + // ✅ Bigger + brighter moving marker + +const pointGeo = new THREE.SphereGeometry(0.20, 24, 24); +const pointMat = new THREE.MeshPhongMaterial({ + color: markerColor, + emissive: markerColor, + emissiveIntensity: 1.7, + shininess: 120, +}); + +const point = new THREE.Mesh(pointGeo, pointMat); +scene.add(point); + +// ✅ Halo ring to keep marker visible +const haloGeo = new THREE.RingGeometry(0.22, 0.34, 64); +const haloMat = new THREE.MeshBasicMaterial({ + color: markerColor, + transparent: true, + opacity: 0.55, + side: THREE.DoubleSide, + depthTest: false, + depthWrite: false, +}); +const halo = new THREE.Mesh(haloGeo, haloMat); +halo.renderOrder = 30; +scene.add(halo); + + + // Start point: mimic user's "70% high" start by sampling the existing grid. + const sortedIdx = Array.from({ length: vertexCount }, (_, i) => i).sort((a, b) => heightsRaw[a] - heightsRaw[b]); + const startIdx = sortedIdx[Math.floor(sortedIdx.length * 0.7)] ?? 0; + const startXw = posAttr.getX(startIdx); + const startYw = posAttr.getZ(startIdx); + const gdX = (startXw / size) * domainHalf; + const gdY = (startYw / size) * domainHalf; + + let raf = 0; + let lastTrailUpdate = 0; + let lastNormalsAt = 0; + + const resize = () => { + const rect = host.getBoundingClientRect(); + const w = Math.max(1, Math.floor(rect.width)); + const h = Math.max(1, Math.floor(rect.height)); + renderer.setSize(w, h, false); + camera.aspect = w / h; + camera.updateProjectionMatrix(); + }; + + resize(); + const ro = new ResizeObserver(() => resize()); + ro.observe(host); + + const render = () => { + raf = requestAnimationFrame(render); + + const { timeMs: nowMs, reducedMotion: rm, surfaceProgress: sp, gdProgress: gp } = latestRef.current; + const t = nowMs / 1000; + const orbit = rm ? 0 : 0.25 * Math.sin(t * 0.55); + + // Bring the camera closer so the surface fills more of the canvas. + camera.position.set(11.5 * Math.cos(orbit), 7.2, 11.5 * Math.sin(orbit)); + camera.lookAt(0, targetY, 0); + + // Surface reveal driven by progress. + const reveal = rm ? 1 : sp; + const radius = size * 1.45 * reveal; + const band = size * 0.35; + + for (let i = 0; i < vertexCount; i += 1) { + const d = distances[i]; + if (d <= radius) { + const v = clamp01((radius - d) / Math.max(1e-6, band)); + const e = smoothstep(v); + posAttr.setY(i, mnRaw * zScale + (heightsScaled[i] - mnRaw * zScale) * e); + } else { + posAttr.setY(i, mnRaw * zScale); + } + } + posAttr.needsUpdate = true; + + // Avoid recomputing normals every frame. + if (rm || nowMs - lastNormalsAt > 150) { + geo.computeVertexNormals(); + lastNormalsAt = nowMs; + } + + // Fade wireframe in during reveal + wireMat.opacity = Math.min(0.4, reveal * 0.4); + + // Gradient descent path driven by progress. + // Gradient descent path driven by progress. +const iter = rm ? effectiveSteps : Math.floor(lerp(0, effectiveSteps, gp)); + +// ✅ if we already converged, freeze forever +let x = lockRef.current.locked ? lockRef.current.x : gdX; +let y = lockRef.current.locked ? lockRef.current.y : gdY; + +const tolGrad = 1e-3; // ✅ gradient magnitude threshold +const tolStep = 5e-4; // ✅ step size threshold + +if (!lockRef.current.locked) { + if (path && path.length > 1) { + // ✅ STREAMED PATH CASE: lock when we reach the last path point + const lastIdx = path.length - 1; + const clampedIter = Math.min(iter, lastIdx); + + for (let k = 0; k <= clampedIter; k += 1) { + const pp = path[k]; + const px = pp.x * domainHalf; + const py = pp.y * domainHalf; + + const z = heightFn(px, py); + const wx = (px / domainHalf) * size; + const wy = (py / domainHalf) * size; + + pathAttr.setXYZ(k, wx, z * zScale, wy); + + x = px; + y = py; + } + + // ✅ lock at the final streamed point + if (clampedIter >= lastIdx) { + lockRef.current = { locked: true, x, y }; + } + } else { + // ✅ SIMULATED GD CASE: stop once we converge + for (let k = 0; k <= iter; k += 1) { + const z = heightFn(x, y); + const wx = (x / domainHalf) * size; + const wy = (y / domainHalf) * size; + + pathAttr.setXYZ(k, wx, z * zScale, wy); + + if (k < iter) { + const gx = dfx(x, y); + const gy = dfy(x, y); + + const gnorm = Math.hypot(gx, gy); + const nx = x - eta * gx; + const ny = y - eta * gy; + const stepDist = Math.hypot(nx - x, ny - y); + + // ✅ CONVERGENCE CHECK + if (gnorm < tolGrad || stepDist < tolStep) { + lockRef.current = { locked: true, x, y }; + break; // ✅ stop simulating immediately + } + + // update normally + x = nx * 0.995; + y = ny * 0.995; + } + } + } +} + + pathAttr.needsUpdate = true; + pathGeo.setDrawRange(0, Math.max(0, iter + 1)); + point.position.set((x / domainHalf) * size, heightFn(x, y) * zScale, (y / domainHalf) * size); + // ✅ Keep halo on top of marker +halo.position.copy(point.position); +halo.rotation.x = -Math.PI / 2; + +if (!rm) { + const pulse = 0.6 + 0.35 * Math.sin(t * 4.5); + haloMat.opacity = 0.25 + 0.35 * pulse; +} + + // ✅ Update thick trail tube geometry from current path points + if (iter < 1) { + trailMesh.visible = false; + } else { + trailMesh.visible = true; + if (nowMs - lastTrailUpdate > 80) { + lastTrailUpdate = nowMs; + const trailPoints: THREE.Vector3[] = []; + const count = iter + 1; + for (let k = 0; k < count; k += 1) { + const vx = pathAttr.getX(k); + const vy = pathAttr.getY(k); + const vz = pathAttr.getZ(k); + trailPoints.push(new THREE.Vector3(vx, vy, vz)); + } + + if (trailPoints.length >= 2) { + const newCurve = new THREE.CatmullRomCurve3(trailPoints); + const newGeo = new THREE.TubeGeometry(newCurve, 40, 0.05, 8, false); + trailMesh.geometry.dispose(); + trailMesh.geometry = newGeo; + } + } + } + + if (!rm) { + const pulse = 0.55 + 0.35 * Math.sin(t * 2.4); + (minRing.material as THREE.MeshBasicMaterial).opacity = 0.25 + 0.25 * pulse; + (maxRing.material as THREE.MeshBasicMaterial).opacity = 0.2 + 0.2 * pulse; + } + + renderer.render(scene, camera); + }; + + render(); + + return () => { + cancelAnimationFrame(raf); + ro.disconnect(); + + // Remove objects + scene.remove(surface); + scene.remove(wireframe); + scene.remove(pathLine); + scene.remove(point); + scene.remove(minMarker); + scene.remove(maxMarker); + scene.remove(minRing); + scene.remove(maxRing); + scene.remove(xAxis); + scene.remove(yAxis); + scene.remove(zAxis); + scene.remove(halo); +haloGeo.dispose(); +haloMat.dispose(); + +scene.remove(trailMesh); +trailMat.dispose(); +trailMesh.geometry.dispose(); + + + // Dispose (IMPORTANT: geo is shared by surface + wireframe -> dispose ONCE) + geo.dispose(); + mat.dispose(); + wireMat.dispose(); + + pathGeo.dispose(); + pathMat.dispose(); + pointGeo.dispose(); + pointMat.dispose(); + + markerGeo.dispose(); + minMarkerMat.dispose(); + maxMarkerMat.dispose(); + + ringGeo.dispose(); + minRingMat.dispose(); + maxRingMat.dispose(); + + gridMaterial.dispose(); + axisMaterial.dispose(); + + (xAxis.geometry as THREE.BufferGeometry).dispose(); + (yAxis.geometry as THREE.BufferGeometry).dispose(); + (zAxis.geometry as THREE.BufferGeometry).dispose(); + (xAxis.material as THREE.Material).dispose(); + (yAxis.material as THREE.Material).dispose(); + (zAxis.material as THREE.Material).dispose(); + + renderer.dispose(); + host.innerHTML = ''; + }; + }, [dfx, dfy, domainHalf, effectiveSteps, heightFn, path, resolution, size, themeMode, zScale]); + + return ( +
+
+
+
+ + {/* Keep only min/max + iter badges; axes are now 3D inside the scene. */} +
+
+ min {minZ.toFixed(2)} +
+
+ max {maxZ.toFixed(2)} +
+
+ iter {currentIter}/{steps} +
+
+
+
+
+ ); +} diff --git a/frontend/src/components/center/loader/visuals/NeuralNetViz.tsx b/frontend/src/components/center/loader/visuals/NeuralNetViz.tsx new file mode 100644 index 00000000..d426dee1 --- /dev/null +++ b/frontend/src/components/center/loader/visuals/NeuralNetViz.tsx @@ -0,0 +1,325 @@ +import type { ReactElement } from 'react'; +import { useMemo } from 'react'; +import type { VisualBaseProps } from '../types'; +import { clamp01, lerp, seeded } from '../types'; + +type NeuralNetVizProps = VisualBaseProps & { + mode: 'forward' | 'backprop'; +}; + +type Pulse = { + id: string; + fromLayer: number; + toLayer: number; + fromNeuron: number; + toNeuron: number; + progress: number; // 0..1 +}; + +function lerpPt(a: { x: number; y: number }, b: { x: number; y: number }, t: number) { + return { x: lerp(a.x, b.x, t), y: lerp(a.y, b.y, t) }; +} + +function easeInOut(t: number) { + const x = clamp01(t); + return x * x * (3 - 2 * x); +} + +function getNeuronPosition({ + layerIdx, + neuronIdx, + totalNeurons, + width, + height, + padX, + layerCount, +}: { + layerIdx: number; + neuronIdx: number; + totalNeurons: number; + width: number; + height: number; + padX: number; + layerCount: number; +}) { + const x = padX + (layerIdx * (width - padX * 2)) / Math.max(1, layerCount - 1); + const centerY = height / 2; + const spread = 54; + const y = centerY + (neuronIdx - (totalNeurons - 1) / 2) * spread; + return { x, y }; +} + +export default function NeuralNetViz({ timeMs, phaseProgress, seed, mode, reducedMotion }: NeuralNetVizProps) { + const t = reducedMotion ? 0 : timeMs / 1000; + const p = reducedMotion ? 1 : clamp01(phaseProgress); + + const width = 800; + const height = 400; + const padX = 90; + const neuronRadius = 16; + const layers = useMemo(() => [4, 6, 6, 4] as const, []); + const layerCount = layers.length; + + // Active layer sweeps through; forward: 0→end, backprop: end→0. + const activeLayer = useMemo(() => { + if (reducedMotion) return mode === 'forward' ? layerCount - 1 : 0; + const idx = Math.min(layerCount - 1, Math.floor(p * layerCount)); + return mode === 'forward' ? idx : (layerCount - 1 - idx); + }, [layerCount, mode, p, reducedMotion]); + + // Only propagate between one layer-pair at a time. + // Forward: activeLayer -> activeLayer+1 + // Backprop: activeLayer -> activeLayer-1 + const activeFromLayer = useMemo(() => { + if (mode === 'forward') return Math.min(layerCount - 2, activeLayer); + return Math.max(1, activeLayer); + }, [activeLayer, layerCount, mode]); + + const activeToLayer = mode === 'forward' ? activeFromLayer + 1 : activeFromLayer - 1; + // Connection lines are defined between layer k -> k+1, so map backprop to that edge index. + const activeEdgeLayer = mode === 'forward' ? activeFromLayer : activeToLayer; + + const colors = { + base: 'rgb(var(--replit-border))', + dim: 'rgb(var(--replit-text-muted))', + forward: 'rgb(var(--replit-accent))', + forwardPulse: 'rgb(var(--replit-accent-hover))', + backward: 'rgb(var(--replit-warning))', + backwardPulse: 'rgb(var(--replit-warning))', + }; + + const primary = mode === 'forward' ? colors.forward : colors.backward; + const pulseColor = mode === 'forward' ? colors.forwardPulse : colors.backwardPulse; + + const pulses = useMemo((): Pulse[] => { + if (reducedMotion) return []; + const pulses: Pulse[] = []; + + // Deterministic pulse emission using time buckets. + const tickMs = 130; + const pulseDurationMs = 760; + const now = timeMs; + const currentTick = Math.floor(now / tickMs); + + // Look back enough ticks to cover the duration. + const lookbackTicks = Math.ceil(pulseDurationMs / tickMs) + 1; + + const localSeed = seed * 911 + 1337; + + for (let tick = currentTick - lookbackTicks; tick <= currentTick; tick += 1) { + const tickStart = tick * tickMs; + const age = now - tickStart; + if (age < 0 || age > pulseDurationMs) continue; + const prog = clamp01(age / pulseDurationMs); + + // Emit a couple of pulses per tick, but ONLY for the active layer transition. + const fromLayer = activeFromLayer; + const toLayer = activeToLayer; + + // Higher probability so the active edge feels alive. + for (let slot = 0; slot < 2; slot += 1) { + const r = seeded(localSeed + tick * 97 + fromLayer * 313 + slot * 911); + if (r >= 0.8) continue; + + const fromNeuron = Math.floor(seeded(localSeed + tick * 131 + fromLayer * 503 + slot * 97 + 11) * layers[fromLayer]); + const toNeuron = Math.floor(seeded(localSeed + tick * 151 + toLayer * 547 + slot * 101 + 29) * layers[toLayer]); + + pulses.push({ + id: `t${tick}-L${fromLayer}-S${slot}`, + fromLayer, + toLayer, + fromNeuron, + toNeuron, + progress: prog, + }); + } + } + + return pulses; + }, [activeFromLayer, activeToLayer, layers, reducedMotion, seed, timeMs]); + + const connections = useMemo(() => { + const lines: Array<{ key: string; x1: number; y1: number; x2: number; y2: number; layer: number }> = []; + for (let layer = 0; layer < layerCount - 1; layer += 1) { + for (let j = 0; j < layers[layer]; j += 1) { + for (let k = 0; k < layers[layer + 1]; k += 1) { + const from = getNeuronPosition({ + layerIdx: layer, + neuronIdx: j, + totalNeurons: layers[layer], + width, + height, + padX, + layerCount, + }); + const to = getNeuronPosition({ + layerIdx: layer + 1, + neuronIdx: k, + totalNeurons: layers[layer + 1], + width, + height, + padX, + layerCount, + }); + lines.push({ + key: `${layer}-${j}-${k}`, + x1: from.x, + y1: from.y, + x2: to.x, + y2: to.y, + layer, + }); + } + } + } + return lines; + }, [height, layerCount, layers, padX, width]); + + return ( +
+
+
+
+ Neural Network · {mode === 'forward' ? 'Forward Pass' : 'Backprop'} +
+
layers={layerCount} · active={activeLayer + 1}
+
+ + + + + + + + + + + + + {/* Edges */} + {connections.map((c) => { + const isActiveEdge = c.layer === activeEdgeLayer; + + const opacity = isActiveEdge ? 0.22 : 0.05; + + // Only animate flow on the active connection layer. + const dash = !reducedMotion && isActiveEdge ? '8 14' : undefined; + const dashOffset = !reducedMotion && isActiveEdge ? -((t * 46) % 100) : undefined; + + return ( + + ); + })} + + {/* Pulses */} + {pulses.map((pulse) => { + const from = getNeuronPosition({ + layerIdx: pulse.fromLayer, + neuronIdx: pulse.fromNeuron, + totalNeurons: layers[pulse.fromLayer], + width, + height, + padX, + layerCount, + }); + const to = getNeuronPosition({ + layerIdx: pulse.toLayer, + neuronIdx: pulse.toNeuron, + totalNeurons: layers[pulse.toLayer], + width, + height, + padX, + layerCount, + }); + + const prog = easeInOut(pulse.progress); + const pt = lerpPt(from, to, prog); + const opacity = Math.sin(prog * Math.PI) * 0.9; + const size = 3 + Math.sin(prog * Math.PI) * 2.4; + + return ( + + + + + + ); + })} + + {/* Nodes */} + {layers.flatMap((count, layerIdx) => { + const isActiveLayer = layerIdx === activeLayer; + const intensity = reducedMotion ? 0.75 : isActiveLayer ? 0.7 + Math.sin(t * 2.0) * 0.3 : 0.28; + const fill = + mode === 'forward' + ? layerIdx <= activeLayer + ? `rgb(var(--replit-accent) / ${clamp01(intensity)})` + : 'rgb(var(--replit-surface))' + : layerIdx >= activeLayer + ? `rgb(var(--replit-warning) / ${clamp01(intensity)})` + : 'rgb(var(--replit-surface))'; + + const stroke = mode === 'forward' ? colors.forward : colors.backward; + + const nodes: ReactElement[] = []; + for (let i = 0; i < count; i += 1) { + const pos = getNeuronPosition({ layerIdx, neuronIdx: i, totalNeurons: count, width, height, padX, layerCount }); + const haloR = neuronRadius + 10 + (reducedMotion ? 0 : 4 * Math.sin(t * 2.2 + i)); + const innerOpacity = reducedMotion ? 0.6 : 0.45 + 0.25 * Math.sin(t * 2.6 + i); + + nodes.push( + + {isActiveLayer && !reducedMotion ? ( + + ) : null} + + {isActiveLayer ? ( + + ) : null} + + ); + } + return nodes; + })} + + {/* Labels */} + + INPUT + + + OUTPUT + + + +
+ {reducedMotion + ? 'Reduced motion: static snapshot' + : mode === 'forward' + ? '→ Computing activations layer by layer' + : '← Propagating gradients and updating weights'} +
+
+
+ ); +} diff --git a/frontend/src/components/center/loader/visuals/ResidualsViz.tsx b/frontend/src/components/center/loader/visuals/ResidualsViz.tsx new file mode 100644 index 00000000..1e298f4e --- /dev/null +++ b/frontend/src/components/center/loader/visuals/ResidualsViz.tsx @@ -0,0 +1,164 @@ +import { useMemo } from 'react'; +import { CartesianGrid, Cell, ReferenceLine, ResponsiveContainer, Scatter, ScatterChart, Tooltip, XAxis, YAxis } from 'recharts'; +import type { VisualProps } from '../types'; +import { clamp01 } from '../types'; + +type ResidualPoint = { id: number; predicted: number; actual: number; error: number }; + +function getPointColor(error: number) { + const absError = Math.abs(error); + if (absError < 3) return '#10b981'; + if (absError < 6) return '#f59e0b'; + return '#ef4444'; +} + +export default function ResidualsViz({ reducedMotion, residuals, phaseProgress }: VisualProps) { + const data = useMemo(() => { + return (residuals ?? []).map((p, idx) => ({ + id: idx, + predicted: p.pred, + actual: p.true, + error: p.residual, + })); + }, [residuals]); + + const revealProgress = reducedMotion ? 1 : clamp01(phaseProgress); + const visibleCount = Math.max(0, Math.floor(data.length * revealProgress)); + const visibleData = useMemo(() => data.slice(0, visibleCount), [data, visibleCount]); + + const metrics = useMemo(() => { + if (visibleData.length === 0) { + return { meanError: 0, mse: 0, rmse: 0, mae: 0, r2: 0 }; + } + const errors = visibleData.map((p) => p.error); + const meanError = errors.reduce((a, b) => a + b, 0) / errors.length; + const mse = errors.reduce((a, e) => a + e * e, 0) / errors.length; + const rmse = Math.sqrt(mse); + const mae = errors.reduce((a, e) => a + Math.abs(e), 0) / errors.length; + const actualValues = visibleData.map((p) => p.actual); + const meanActual = actualValues.reduce((a, b) => a + b, 0) / actualValues.length; + const ssTot = actualValues.reduce((a, v) => a + Math.pow(v - meanActual, 2), 0); + const ssRes = errors.reduce((a, e) => a + e * e, 0); + const r2 = ssTot === 0 ? 0 : 1 - ssRes / ssTot; + return { meanError, mse, rmse, mae, r2 }; + }, [visibleData]); + + const domainStep = (min: number, max: number) => { + const span = Math.max(1e-6, max - min); + const rough = span / 5; + const pow10 = Math.pow(10, Math.floor(Math.log10(rough))); + const x = rough / pow10; + const base = x <= 1 ? 1 : x <= 2 ? 2 : x <= 5 ? 5 : 10; + return base * pow10; + }; + + const { xDomain, yDomain } = useMemo(() => { + if (visibleData.length === 0) { + return { xDomain: [0, 100] as [number, number], yDomain: [-10, 10] as [number, number] }; + } + const xs = visibleData.map((p) => p.predicted); + const ys = visibleData.map((p) => p.error); + const xMin = Math.min(...xs); + const xMax = Math.max(...xs); + const yMin = Math.min(...ys); + const yMax = Math.max(...ys); + const xStep = domainStep(xMin, xMax); + const yStep = domainStep(yMin, yMax); + return { + xDomain: [Math.floor(xMin / xStep) * xStep, Math.ceil(xMax / xStep) * xStep] as [number, number], + yDomain: [Math.floor(yMin / yStep) * yStep, Math.ceil(yMax / yStep) * yStep] as [number, number], + }; + }, [visibleData]); + + return ( +
+
+
+
+
Residuals Plot
+
Predicted vs Error — Regression Analysis
+
+
n={visibleData.length}
+
+ +
+
+
Mean Error
+
{metrics.meanError.toFixed(3)}
+
+
+
RMSE
+
{metrics.rmse.toFixed(3)}
+
+
+
MAE
+
{metrics.mae.toFixed(3)}
+
+
+
MSE
+
{metrics.mse.toFixed(3)}
+
+
+
R² Score
+
{metrics.r2.toFixed(3)}
+
+
+ +
+ {data.length === 0 && ( +
+
+
+

Waiting for residual data…

+
+
+ )} + + + + + + [Number(v).toFixed(3), name]} + /> + + + {visibleData.map((entry) => ( + + ))} + + + +
+ +
+
+
+ Good (|error| < 3) +
+
+
+ Moderate (3 ≤ |error| < 6) +
+
+
+ Poor (|error| ≥ 6) +
+
+
+
+ ); +} diff --git a/frontend/src/components/center/loader/visuals/visualRegistry.ts b/frontend/src/components/center/loader/visuals/visualRegistry.ts new file mode 100644 index 00000000..ec147c76 --- /dev/null +++ b/frontend/src/components/center/loader/visuals/visualRegistry.ts @@ -0,0 +1,28 @@ +import React from 'react'; +import type { VisualId, VisualProps } from '../types'; +import NeuralNetViz from './NeuralNetViz'; +import GradientDescentViz from './GradientDescentViz'; +import EvaluationViz from './EvaluationViz'; +import ConfusionMatrixViz from './ConfusionMatrixViz'; +import EmbeddingScatterViz from './EmbeddingScatterViz'; +import ResidualsViz from './ResidualsViz'; + +export const VISUAL_LABEL: Record = { + neuralNetForward: 'Neural Net (Forward)', + neuralNetBackprop: 'Neural Net (Backprop)', + gradDescent: 'Gradient Descent', + evaluation: 'Evaluation Suite', + confusionMatrix: 'Confusion Matrix', + embeddingScatter: 'Embedding Scatter', + residuals: 'Residuals', +}; + +export const VISUALS: Record> = { + neuralNetForward: (props) => React.createElement(NeuralNetViz, { ...props, mode: 'forward' }), + neuralNetBackprop: (props) => React.createElement(NeuralNetViz, { ...props, mode: 'backprop' }), + gradDescent: GradientDescentViz, + evaluation: EvaluationViz, + confusionMatrix: ConfusionMatrixViz, + embeddingScatter: EmbeddingScatterViz, + residuals: ResidualsViz, +}; diff --git a/frontend/src/components/center/preview/APIDocsPane.tsx b/frontend/src/components/center/preview/APIDocsPane.tsx new file mode 100644 index 00000000..83cc2a1d --- /dev/null +++ b/frontend/src/components/center/preview/APIDocsPane.tsx @@ -0,0 +1,422 @@ +import { useCallback, useMemo, useState } from 'react'; +import { ChevronDown, ChevronRight, Copy, ExternalLink } from 'lucide-react'; +import type { FileSystemNode } from '../../../lib/types'; +import clsx from 'clsx'; +import { motion, AnimatePresence, useReducedMotion } from 'framer-motion'; + +interface APIDocsPaneProps { + files: FileSystemNode[]; +} + +export default function APIDocsPane({ files }: APIDocsPaneProps) { + const reducedMotion = useReducedMotion(); + + const readVfsFile = useCallback((path: string): string | null => { + const core = (nodes: FileSystemNode[]): string | null => { + for (const n of nodes) { + if (n.path === path) return n.content || ''; + if (n.children) { + const found = core(n.children); + if (found !== null) return found; + } + } + return null; + }; + return core(files); + }, [files]); + + const session = useMemo(() => { + const raw = readVfsFile('/sessions/current.json'); + if (!raw) return null; + try { + return JSON.parse(raw) as { id?: string; modelName?: string; goal?: string }; + } catch { + return null; + } + }, [readVfsFile]); + + const modelMeta = useMemo(() => { + const raw = readVfsFile('/artifacts/model.json'); + if (!raw) return { version: '0.1', model: 'AutoAI MockNet' }; + try { + const parsed = JSON.parse(raw) as { version?: string; model?: string }; + return { version: parsed.version ?? '0.1', model: parsed.model ?? 'AutoAI MockNet' }; + } catch { + return { version: '0.1', model: 'AutoAI MockNet' }; + } + }, [readVfsFile]); + + const schema = useMemo(() => deriveRequestSchema(session?.goal ?? ''), [session?.goal]); + + return ( +
+ {/* Header */} +
+
+
+
Model API
+
+ {session?.modelName ? {session.modelName} : 'Untitled model'} + · + + v{modelMeta.version} + +
+
+ Base URL: https://api.autoai.local +
+
+ + e.preventDefault()} + > + openapi.json + +
+
+ +
+
+ + +
+ + +
+
+
+ ); +} + +type HttpMethod = 'GET' | 'POST'; + +interface RequestSchema { + textField: { key: string; label: string }; + numericFields: Array<{ key: string; label: string; defaultValue: number }>; +} + +interface EndpointProps { + reducedMotion: boolean; + method: HttpMethod; + path: string; + summary: string; + description?: string; + readonly?: boolean; + schema?: RequestSchema; + modelVersion?: string; +} + +type PredictResponse = { + predicted_class: 'positive' | 'negative' | 'neutral'; + probabilities: { positive: number; negative: number; neutral: number }; + model_version: string; + latency_ms: number; + request_id: string; +}; + +function deriveRequestSchema(goal: string): RequestSchema { + const normalized = goal.toLowerCase(); + const candidates: Array<{ key: string; label: string; defaultValue: number; match: RegExp }> = [ + { key: 'age', label: 'age', defaultValue: 34, match: /\bage\b/ }, + { key: 'tenure', label: 'tenure', defaultValue: 12, match: /\btenure\b/ }, + { key: 'income', label: 'income', defaultValue: 65000, match: /\bincome\b|\bsalary\b/ }, + { key: 'balance', label: 'balance', defaultValue: 1200, match: /\bbalance\b/ }, + { key: 'rating', label: 'rating', defaultValue: 4.2, match: /\brating\b|\bscore\b/ }, + ]; + + const picked = candidates.filter((c) => c.match.test(normalized)).slice(0, 3); + const numericFields = (picked.length > 0 ? picked : candidates.slice(0, 2)).map(({ key, label, defaultValue }) => ({ + key, + label, + defaultValue, + })); + + return { + textField: { key: 'text', label: 'text' }, + numericFields, + }; +} + +function stableHash(str: string): number { + let h = 2166136261; + for (let i = 0; i < str.length; i += 1) { + h ^= str.charCodeAt(i); + h = Math.imul(h, 16777619); + } + return Math.abs(h); +} + +function localPredict(payload: Record, modelVersion: string): PredictResponse { + const text = String(payload.text ?? ''); + const numericKeys = Object.keys(payload).filter((k) => k !== 'text').sort(); + const numericSum = numericKeys.reduce((acc, k) => { + const v = Number(payload[k]); + return acc + (Number.isFinite(v) ? v : 0); + }, 0); + + const h = stableHash(`${text}::${numericSum.toFixed(3)}::${numericKeys.join(',')}`); + const idx = h % 3; + const classes: Array = ['positive', 'negative', 'neutral']; + + const p0 = 0.1 + ((h % 31) / 310); + const p1 = 0.1 + (((h >> 5) % 31) / 310); + const p2 = 0.1 + (((h >> 10) % 31) / 310); + const probs = [p0, p1, p2]; + probs[idx] += 0.55; + const s = probs.reduce((a, b) => a + b, 0); + const norm = probs.map((p) => p / s); + + return { + predicted_class: classes[idx], + probabilities: { + positive: Number(norm[0].toFixed(4)), + negative: Number(norm[1].toFixed(4)), + neutral: Number(norm[2].toFixed(4)), + }, + model_version: modelVersion, + latency_ms: 18 + (h % 27), + request_id: `req_${h.toString(16).slice(0, 8)}`, + }; +} + +async function copyText(text: string) { + try { + await navigator.clipboard.writeText(text); + } catch { + // ignore + } +} + +function Endpoint({ reducedMotion, method, path, summary, description, readonly, schema, modelVersion }: EndpointProps) { + const [isOpen, setIsOpen] = useState(true); + const [tryOut, setTryOut] = useState(false); + const [loading, setLoading] = useState(false); + const [response, setResponse] = useState(null); + + const modelVer = modelVersion ?? '0.1'; + + const [text, setText] = useState('This product is amazing!'); + const [numbers, setNumbers] = useState>(() => { + const next: Record = {}; + for (const f of schema?.numericFields ?? []) next[f.key] = f.defaultValue; + return next; + }); + + const requestBody = useMemo(() => { + const payload: Record = { text }; + for (const f of schema?.numericFields ?? []) payload[f.key] = numbers[f.key]; + return payload; + }, [text, numbers, schema]); + + const curlSnippet = useMemo(() => { + const json = JSON.stringify(requestBody); + return `curl -X ${method} "https://api.autoai.local${path}" -H "Content-Type: application/json" -d '${json}'`; + }, [method, path, requestBody]); + + const onExecute = () => { + setLoading(true); + setResponse(null); + + window.setTimeout(() => { + if (method === 'POST') { + setResponse(localPredict(requestBody, modelVer)); + } + setLoading(false); + }, 550); + }; + + const methodPill = method === 'POST' ? 'bg-replit-accent/90 text-white' : 'bg-replit-surfaceHover/50 text-replit-text'; + const accentBorder = method === 'POST' ? 'border-replit-accent/40' : 'border-replit-border/60'; + + return ( +
+ + + + {isOpen && ( + +
+ {description &&

{description}

} + + {!readonly && ( +
+ {!tryOut ? ( + + ) : ( + + )} +
+ )} + + {tryOut && !readonly && schema && ( +
+
Request Body
+ +
+
+ +