From f3372eb0addec4c4a24d7ecdb3da63ae9568367c Mon Sep 17 00:00:00 2001 From: DeOwl Date: Mon, 25 May 2026 12:55:46 +0300 Subject: [PATCH] v1.0 - added task rendering instead of molecule - fied a lot of errors - changes experiment page to show generic experiment info --- .gita/workflows/build-and-push.yml | 112 + package.json | 56 +- pnpm-lock.yaml | 2921 ++++++++ public/index.html | 5975 ----------------- src/Api/ConvertBackendCalls.tsx | 49 - src/Api/Keycloak/Keycloak.tsx | 3 - src/Api/PluginLoader/PluginLoader.tsx | 99 +- .../QuantumBackend/ExperimentsManagment.tsx | 186 + src/Api/QuantumBackend/UserManagement.tsx | 41 +- src/App.tsx | 3 +- src/Components/CustomButton/CustomButton.css | 13 +- src/Components/CustomButton/CustomButton.tsx | 19 +- src/Components/Layout/Header/Header.tsx | 3 +- src/Components/Layout/Sidebar/Sidebar.tsx | 70 +- .../ListCard/ExperimentsListCard.tsx | 101 +- .../MachineListCard/MachinesListCard.tsx | 7 +- .../ListCard/TaskListCard/TaskListCard.css | 28 + .../ListCard/TaskListCard/TaskListCard.tsx | 176 + .../TeamInMachineCard/TeamInMachine.tsx | 82 +- .../TeamMemberCard/TeamMemberCard.tsx | 45 +- .../AddTeamToDevice/AddTeamToDevice.tsx | 23 +- src/Modals/NewExperiment/NewExperiment.tsx | 55 +- src/Modals/NewMolecule/NewMolecule.css | 3 - src/Modals/NewMolecule/NewMolecule.tsx | 241 - src/Modals/NewTask/NewTask.css | 0 src/Modals/NewTask/NewTask.tsx | 127 + .../DevicesPage/DevicePage/DevicePage.tsx | 17 +- .../DocumentationPage/DocumentationPage.tsx | 300 +- src/Pages/ExperimentsPage/ExperimentPage.css | 12 +- src/Pages/ExperimentsPage/ExperimentPage.tsx | 625 +- src/Pages/ExperimentsPage/ExperimentsPage.css | 2 + src/Pages/ExperimentsPage/ExperimentsPage.tsx | 141 +- src/Pages/TaskPage/TaskPage.css | 13 + src/Pages/TaskPage/TaskPage.tsx | 193 +- src/Pages/TeamsPage/TeamPage/TeamPage.tsx | 10 +- src/Pages/UserPage/UserPage.tsx | 330 +- src/Routes/Breadcrumbs/Breadcrumbs.tsx | 11 +- src/Routes/Routes.tsx | 119 +- src/Stores/ExperimentStore.tsx | 86 +- .../ApiCalls/ConvertBackendCallsTypes.tsx | 7 - src/Types/Experiment/Experiment.tsx | 127 +- src/main.tsx | 16 +- vite.config.ts | 2 +- 43 files changed, 5541 insertions(+), 6908 deletions(-) create mode 100644 .gita/workflows/build-and-push.yml create mode 100644 pnpm-lock.yaml delete mode 100644 public/index.html delete mode 100755 src/Api/ConvertBackendCalls.tsx create mode 100644 src/Api/QuantumBackend/ExperimentsManagment.tsx create mode 100644 src/Components/ListCard/TaskListCard/TaskListCard.css create mode 100644 src/Components/ListCard/TaskListCard/TaskListCard.tsx delete mode 100755 src/Modals/NewMolecule/NewMolecule.css delete mode 100755 src/Modals/NewMolecule/NewMolecule.tsx create mode 100644 src/Modals/NewTask/NewTask.css create mode 100644 src/Modals/NewTask/NewTask.tsx create mode 100644 src/Pages/TaskPage/TaskPage.css delete mode 100755 src/Types/ApiCalls/ConvertBackendCallsTypes.tsx diff --git a/.gita/workflows/build-and-push.yml b/.gita/workflows/build-and-push.yml new file mode 100644 index 0000000..2d610d2 --- /dev/null +++ b/.gita/workflows/build-and-push.yml @@ -0,0 +1,112 @@ +name: Build and Deploy React Package + +# Controls when the workflow will run. Here, it runs on every push to the 'main' branch. +on: + push: + branches: ["main"] + +# Environment variables used across the workflow +env: + # The URL of your Gitea instance (without http:// or https://) + GITEA_INSTANCE_URL: git.deowl.ru + # The full name of your package (e.g., 'myusername/myproject') + PACKAGE_NAME: vkrb/quantum_frontend + # Node.js version to use + NODE_VERSION: "25.9.0" + +jobs: + build-and-publish: + # Runs the job on a runner with the 'ubuntu-latest' label. + runs-on: ubuntu-latest + + steps: + # 1. Check out your repository code so the workflow can access it. + - name: Checkout code + uses: actions/checkout@v4 + + # 2. Set up Node.js environment + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + + # 3. Install pnpm globally + - name: Install pnpm + run: npm install -g pnpm + + # 4. Install dependencies with pnpm + - name: Install dependencies + run: pnpm install + + # 5. Build the React/Vite application + # This assumes your vite.config.js/ts is configured to output to 'dist' + - name: Build application + run: pnpm run build + + # 6. Create a tarball of the build artifacts + - name: Create package tarball + run: | + # Create a directory for the package + mkdir -p package_artifact + + # Copy build artifacts (adjust path if your build outputs to a different directory) + cp -r dist package_artifact/ + + # Optionally copy other important files + # cp package.json package_artifact/ + # cp README.md package_artifact/ + + # Create tarball + tar -czf package.tar.gz -C package_artifact . + + # Generate checksum for integrity + sha256sum package.tar.gz > package.tar.gz.sha256 + + # 7. Log in to Gitea Package Registry + - name: Log in to Gitea Package Registry + run: | + # Create .netrc file for authentication with Gitea + cat > ~/.netrc << EOF + machine ${{ env.GITEA_INSTANCE_URL }} + login ${{ gitea.repository_owner }} + password ${{ secrets.REGISTRY_TOKEN }} + EOF + + # 8. Upload the package to Gitea Generic Package Registry + - name: Upload to Gitea Generic Package Registry + run: | + # The format for Gitea generic packages: + # https://{GITEA_INSTANCE_URL}/api/packages/{owner}/generic/{package-name}/{version}/{file-name} + + # Generate version from git commit SHA and timestamp + VERSION="${{ gitea.sha }}" + + # Upload the tarball + curl --fail-with-body \ + --netrc \ + --upload-file package.tar.gz \ + "https://${{ env.GITEA_INSTANCE_URL }}/api/packages/${{ gitea.repository_owner }}/generic/${{ env.PACKAGE_NAME }}/${VERSION}/package.tar.gz" + + # Upload the checksum file + curl --fail-with-body \ + --netrc \ + --upload-file package.tar.gz.sha256 \ + "https://${{ env.GITEA_INSTANCE_URL }}/api/packages/${{ gitea.repository_owner }}/generic/${{ env.PACKAGE_NAME }}/${VERSION}/package.tar.gz.sha256" + + # 9. (Optional) Create a latest version for convenience + - name: Update latest version + run: | + # Upload as 'latest' version + curl --fail-with-body \ + --netrc \ + --upload-file package.tar.gz \ + "https://${{ env.GITEA_INSTANCE_URL }}/api/packages/${{ gitea.repository_owner }}/generic/${{ env.PACKAGE_NAME }}/latest/package.tar.gz" + + curl --fail-with-body \ + --netrc \ + --upload-file package.tar.gz.sha256 \ + "https://${{ env.GITEA_INSTANCE_URL }}/api/packages/${{ gitea.repository_owner }}/generic/${{ env.PACKAGE_NAME }}/latest/package.tar.gz.sha256" + + # 10. Clean up + - name: Clean up + run: rm -f package.tar.gz package.tar.gz.sha256 diff --git a/package.json b/package.json index e057522..24ac64d 100755 --- a/package.json +++ b/package.json @@ -10,36 +10,36 @@ "preview": "vite preview" }, "dependencies": { - "@mantine/core": "^9.1.1", - "@mantine/hooks": "^9.1.1", - "@mantine/notifications": "^9.1.1", - "@tabler/icons-react": "^3.34.1", - "axios": "^1.13.2", - "dotenv": "^17.4.2", - "immer": "^11.1.4", - "keycloak-js": "^26.2.3", - "miew-react": "^0.11.0", - "react": "^19.2.5", - "react-dom": "^19.2.5", - "react-helmet": "^6.1.0", - "react-resizable-panels": "^4.5.4", - "react-router": "^7.9.6", - "vite-tsconfig-paths": "^5.1.4", - "zustand": "^5.0.8" + "@mantine/core": "9.1.1", + "@mantine/hooks": "9.1.1", + "@mantine/notifications": "9.1.1", + "@tabler/icons-react": "3.34.1", + "axios": "1.13.2", + "dotenv": "17.4.2", + "immer": "11.1.4", + "keycloak-js": "26.2.3", + "miew-react": "0.11.0", + "react": "19.2.5", + "react-dom": "19.2.5", + "react-helmet": "6.1.0", + "react-resizable-panels": "4.5.4", + "react-router": "7.9.6", + "vite-tsconfig-paths": "5.1.4", + "zustand": "5.0.8" }, "devDependencies": { - "@eslint/js": "^9.33.0", - "@types/node": "^25.6.0", - "@types/react": "^18.2.0", - "@types/react-dom": "^18.2.0", - "@types/react-helmet": "^6.1.11", - "@vitejs/plugin-react": "^5.0.2", - "eslint": "^9.33.0", - "eslint-plugin-react-hooks": "^5.2.0", - "eslint-plugin-react-refresh": "^0.4.20", - "globals": "^16.3.0", + "@eslint/js": "9.33.0", + "@types/node": "25.6.0", + "@types/react": "18.2.0", + "@types/react-dom": "18.2.0", + "@types/react-helmet": "6.1.11", + "@vitejs/plugin-react": "5.0.2", + "eslint": "9.33.0", + "eslint-plugin-react-hooks": "5.2.0", + "eslint-plugin-react-refresh": "0.4.20", + "globals": "16.3.0", "typescript": "~5.8.3", - "typescript-eslint": "^8.39.1", - "vite": "^7.1.2" + "typescript-eslint": "8.39.1", + "vite": "7.1.2" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..b7a4a00 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,2921 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@mantine/core': + specifier: 9.1.1 + version: 9.1.1(@mantine/hooks@9.1.1(react@19.2.5))(@types/react@18.2.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@mantine/hooks': + specifier: 9.1.1 + version: 9.1.1(react@19.2.5) + '@mantine/notifications': + specifier: 9.1.1 + version: 9.1.1(@mantine/core@9.1.1(@mantine/hooks@9.1.1(react@19.2.5))(@types/react@18.2.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@mantine/hooks@9.1.1(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@tabler/icons-react': + specifier: 3.34.1 + version: 3.34.1(react@19.2.5) + axios: + specifier: 1.13.2 + version: 1.13.2 + dotenv: + specifier: 17.4.2 + version: 17.4.2 + immer: + specifier: 11.1.4 + version: 11.1.4 + keycloak-js: + specifier: 26.2.3 + version: 26.2.3 + miew-react: + specifier: 0.11.0 + version: 0.11.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + react: + specifier: 19.2.5 + version: 19.2.5 + react-dom: + specifier: 19.2.5 + version: 19.2.5(react@19.2.5) + react-helmet: + specifier: 6.1.0 + version: 6.1.0(react@19.2.5) + react-resizable-panels: + specifier: 4.5.4 + version: 4.5.4(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + react-router: + specifier: 7.9.6 + version: 7.9.6(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + vite-tsconfig-paths: + specifier: 5.1.4 + version: 5.1.4(typescript@5.8.3)(vite@7.1.2(@types/node@25.6.0)) + zustand: + specifier: 5.0.8 + version: 5.0.8(@types/react@18.2.0)(immer@11.1.4)(react@19.2.5) + devDependencies: + '@eslint/js': + specifier: 9.33.0 + version: 9.33.0 + '@types/node': + specifier: 25.6.0 + version: 25.6.0 + '@types/react': + specifier: 18.2.0 + version: 18.2.0 + '@types/react-dom': + specifier: 18.2.0 + version: 18.2.0 + '@types/react-helmet': + specifier: 6.1.11 + version: 6.1.11 + '@vitejs/plugin-react': + specifier: 5.0.2 + version: 5.0.2(vite@7.1.2(@types/node@25.6.0)) + eslint: + specifier: 9.33.0 + version: 9.33.0 + eslint-plugin-react-hooks: + specifier: 5.2.0 + version: 5.2.0(eslint@9.33.0) + eslint-plugin-react-refresh: + specifier: 0.4.20 + version: 0.4.20(eslint@9.33.0) + globals: + specifier: 16.3.0 + version: 16.3.0 + typescript: + specifier: ~5.8.3 + version: 5.8.3 + typescript-eslint: + specifier: 8.39.1 + version: 8.39.1(eslint@9.33.0)(typescript@5.8.3) + vite: + specifier: 7.1.2 + version: 7.1.2(@types/node@25.6.0) + +packages: + + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.0': + resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.0': + resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.1': + resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.28.6': + resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.28.6': + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.6': + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.28.6': + resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.2': + resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.2': + resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-transform-react-jsx-self@7.27.1': + resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.27.1': + resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.29.2': + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.28.6': + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.0': + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + + '@esbuild/aix-ppc64@0.25.12': + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.12': + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.12': + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.12': + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.12': + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.12': + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.12': + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.12': + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.12': + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.12': + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.12': + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.12': + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.12': + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.2': + resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.3.1': + resolution: {integrity: sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.15.2': + resolution: {integrity: sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.5': + resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.33.0': + resolution: {integrity: sha512-5K1/mKhWaMfreBGJTwval43JJmkip0RmM+3+IuqupeSKNC/Th2Kc7ucaq5ovTSra/OOKB9c58CGSz3QMVbWt0A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.3.5': + resolution: {integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@floating-ui/core@1.7.5': + resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} + + '@floating-ui/dom@1.7.6': + resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==} + + '@floating-ui/react-dom@2.1.8': + resolution: {integrity: sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/react@0.27.19': + resolution: {integrity: sha512-31B8h5mm8YxotlE7/AU/PhNAl8eWxAmjL/v2QOxroDNkTFLk3Uu82u63N3b6TXa4EGJeeZLVcd/9AlNlVqzeog==} + peerDependencies: + react: '>=17.0.0' + react-dom: '>=17.0.0' + + '@floating-ui/utils@0.2.11': + resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} + + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} + engines: {node: '>=18.18.0'} + + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@mantine/core@9.1.1': + resolution: {integrity: sha512-vClOZdCeZ4oLYuA/3jAOgKGQ6dXbF6ZkzpYz09Gied9nZpB7HcQeb3dcMh8UPBE4f+EM7KlYWk6dch7GoASeaA==} + peerDependencies: + '@mantine/hooks': 9.1.1 + react: ^19.2.0 + react-dom: ^19.2.0 + + '@mantine/hooks@9.1.1': + resolution: {integrity: sha512-tTJK73nGFyy1v214TLdvBq0be7QCoc6osfbXVuJgOH3YG85lWk9Mvvor6k+w6hC6HXSqKMqLKePyiGm83xGcMg==} + peerDependencies: + react: ^19.2.0 + + '@mantine/notifications@9.1.1': + resolution: {integrity: sha512-ZfcEMMDp0BQ+yKmVp8ifPXLKej8pv9TcaRnmy2CZ07USD61E9LH5ClRAP/hxQuCyf/qLb5BPHsI7+f3K8uhj4Q==} + peerDependencies: + '@mantine/core': 9.1.1 + '@mantine/hooks': 9.1.1 + react: ^19.2.0 + react-dom: ^19.2.0 + + '@mantine/store@9.1.1': + resolution: {integrity: sha512-kbxEU8wVGbobHlmQmk0lu9M+xCILKjuAPcMAshgzPznGLfXeE9zrB0gNT2cbk11Ik8dlV9J6Vsn9cuACyOSpfQ==} + peerDependencies: + react: ^19.2.0 + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@rolldown/pluginutils@1.0.0-beta.34': + resolution: {integrity: sha512-LyAREkZHP5pMom7c24meKmJCdhf2hEyvam2q0unr3or9ydwDL+DJ8chTF6Av/RFPb3rH8UFBdMzO5MxTZW97oA==} + + '@rollup/rollup-android-arm-eabi@4.60.2': + resolution: {integrity: sha512-dnlp69efPPg6Uaw2dVqzWRfAWRnYVb1XJ8CyyhIbZeaq4CA5/mLeZ1IEt9QqQxmbdvagjLIm2ZL8BxXv5lH4Yw==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.60.2': + resolution: {integrity: sha512-OqZTwDRDchGRHHm/hwLOL7uVPB9aUvI0am/eQuWMNyFHf5PSEQmyEeYYheA0EPPKUO/l0uigCp+iaTjoLjVoHg==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.60.2': + resolution: {integrity: sha512-UwRE7CGpvSVEQS8gUMBe1uADWjNnVgP3Iusyda1nSRwNDCsRjnGc7w6El6WLQsXmZTbLZx9cecegumcitNfpmA==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.60.2': + resolution: {integrity: sha512-gjEtURKLCC5VXm1I+2i1u9OhxFsKAQJKTVB8WvDAHF+oZlq0GTVFOlTlO1q3AlCTE/DF32c16ESvfgqR7343/g==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.60.2': + resolution: {integrity: sha512-Bcl6CYDeAgE70cqZaMojOi/eK63h5Me97ZqAQoh77VPjMysA/4ORQBRGo3rRy45x4MzVlU9uZxs8Uwy7ZaKnBw==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.60.2': + resolution: {integrity: sha512-LU+TPda3mAE2QB0/Hp5VyeKJivpC6+tlOXd1VMoXV/YFMvk/MNk5iXeBfB4MQGRWyOYVJ01625vjkr0Az98OJQ==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.60.2': + resolution: {integrity: sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.60.2': + resolution: {integrity: sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.60.2': + resolution: {integrity: sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.60.2': + resolution: {integrity: sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loong64-gnu@4.60.2': + resolution: {integrity: sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-loong64-musl@4.60.2': + resolution: {integrity: sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q==} + cpu: [loong64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-ppc64-gnu@4.60.2': + resolution: {integrity: sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-ppc64-musl@4.60.2': + resolution: {integrity: sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ==} + cpu: [ppc64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-riscv64-gnu@4.60.2': + resolution: {integrity: sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.60.2': + resolution: {integrity: sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.60.2': + resolution: {integrity: sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.60.2': + resolution: {integrity: sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.60.2': + resolution: {integrity: sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-openbsd-x64@4.60.2': + resolution: {integrity: sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.60.2': + resolution: {integrity: sha512-NetAg5iO2uN7eB8zE5qrZ3CSil+7IJt4WDFLcC75Ymywq1VZVD6qJ6EvNLjZ3rEm6gB7XW5JdT60c6MN35Z85Q==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.60.2': + resolution: {integrity: sha512-NCYhOotpgWZ5kdxCZsv6Iudx0wX8980Q/oW4pNFNihpBKsDbEA1zpkfxJGC0yugsUuyDZ7gL37dbzwhR0VI7pQ==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.60.2': + resolution: {integrity: sha512-RXsaOqXxfoUBQoOgvmmijVxJnW2IGB0eoMO7F8FAjaj0UTywUO/luSqimWBJn04WNgUkeNhh7fs7pESXajWmkg==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.60.2': + resolution: {integrity: sha512-qdAzEULD+/hzObedtmV6iBpdL5TIbKVztGiK7O3/KYSf+HIzU257+MX1EXJcyIiDbMAqmbwaufcYPvyRryeZtA==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.60.2': + resolution: {integrity: sha512-Nd/SgG27WoA9e+/TdK74KnHz852TLa94ovOYySo/yMPuTmpckK/jIF2jSwS3g7ELSKXK13/cVdmg1Z/DaCWKxA==} + cpu: [x64] + os: [win32] + + '@tabler/icons-react@3.34.1': + resolution: {integrity: sha512-Ld6g0NqOO05kyyHsfU8h787PdHBm7cFmOycQSIrGp45XcXYDuOK2Bs0VC4T2FWSKZ6bx5g04imfzazf/nqtk1A==} + peerDependencies: + react: '>= 16' + + '@tabler/icons@3.34.1': + resolution: {integrity: sha512-9gTnUvd7Fd/DmQgr3MKY+oJLa1RfNsQo8c/ir3TJAWghOuZXodbtbVp0QBY2DxWuuvrSZFys0HEbv1CoiI5y6A==} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/node@25.6.0': + resolution: {integrity: sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==} + + '@types/prop-types@15.7.15': + resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} + + '@types/react-dom@18.2.0': + resolution: {integrity: sha512-8yQrvS6sMpSwIovhPOwfyNf2Wz6v/B62LFSVYQ85+Rq3tLsBIG7rP5geMxaijTUxSkrO6RzN/IRuIAADYQsleA==} + + '@types/react-helmet@6.1.11': + resolution: {integrity: sha512-0QcdGLddTERotCXo3VFlUSWO3ztraw8nZ6e3zJSgG7apwV5xt+pJUS8ewPBqT4NYB1optGLprNQzFleIY84u/g==} + + '@types/react@18.2.0': + resolution: {integrity: sha512-0FLj93y5USLHdnhIhABk83rm8XEGA7kH3cr+YUlvxoUGp1xNt/DINUMvqPxLyOQMzLmZe8i4RTHbvb8MC7NmrA==} + + '@types/scheduler@0.26.0': + resolution: {integrity: sha512-WFHp9YUJQ6CKshqoC37iOlHnQSmxNc795UhB26CyBBttrN9svdIrUjl/NjnNmfcwtncN0h/0PPAFWv9ovP8mLA==} + + '@typescript-eslint/eslint-plugin@8.39.1': + resolution: {integrity: sha512-yYegZ5n3Yr6eOcqgj2nJH8cH/ZZgF+l0YIdKILSDjYFRjgYQMgv/lRjV5Z7Up04b9VYUondt8EPMqg7kTWgJ2g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.39.1 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/parser@8.39.1': + resolution: {integrity: sha512-pUXGCuHnnKw6PyYq93lLRiZm3vjuslIy7tus1lIQTYVK9bL8XBgJnCWm8a0KcTtHC84Yya1Q6rtll+duSMj0dg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/project-service@8.39.1': + resolution: {integrity: sha512-8fZxek3ONTwBu9ptw5nCKqZOSkXshZB7uAxuFF0J/wTMkKydjXCzqqga7MlFMpHi9DoG4BadhmTkITBcg8Aybw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/scope-manager@8.39.1': + resolution: {integrity: sha512-RkBKGBrjgskFGWuyUGz/EtD8AF/GW49S21J8dvMzpJitOF1slLEbbHnNEtAHtnDAnx8qDEdRrULRnWVx27wGBw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.39.1': + resolution: {integrity: sha512-ePUPGVtTMR8XMU2Hee8kD0Pu4NDE1CN9Q1sxGSGd/mbOtGZDM7pnhXNJnzW63zk/q+Z54zVzj44HtwXln5CvHA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/tsconfig-utils@8.59.1': + resolution: {integrity: sha512-/0nEyPbX7gRsk0Uwfe4ALwwgxuA66d/l2mhRDNlAvaj4U3juhUtJNq0DsY8M2AYwwb9rEq2hrC3IcIcEt++iJA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/type-utils@8.39.1': + resolution: {integrity: sha512-gu9/ahyatyAdQbKeHnhT4R+y3YLtqqHyvkfDxaBYk97EcbfChSJXyaJnIL3ygUv7OuZatePHmQvuH5ru0lnVeA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/types@8.39.1': + resolution: {integrity: sha512-7sPDKQQp+S11laqTrhHqeAbsCfMkwJMrV7oTDvtDds4mEofJYir414bYKUEb8YPUm9QL3U+8f6L6YExSoAGdQw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/types@8.59.1': + resolution: {integrity: sha512-ZDCjgccSdYPw5Bxh+my4Z0lJU96ZDN7jbBzvmEn0FZx3RtU1C7VWl6NbDx94bwY3V5YsgwRzJPOgeY2Q/nLG8A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.39.1': + resolution: {integrity: sha512-EKkpcPuIux48dddVDXyQBlKdeTPMmALqBUbEk38McWv0qVEZwOpVJBi7ugK5qVNgeuYjGNQxrrnoM/5+TI/BPw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/utils@8.39.1': + resolution: {integrity: sha512-VF5tZ2XnUSTuiqZFXCZfZs1cgkdd3O/sSYmdo2EpSyDlC86UM/8YytTmKnehOW3TGAlivqTDT6bS87B/GQ/jyg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/visitor-keys@8.39.1': + resolution: {integrity: sha512-W8FQi6kEh2e8zVhQ0eeRnxdvIoOkAp/CPAahcNio6nO9dsIwb9b34z90KOlheoyuVf6LSOEdjlkxSkapNEc+4A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@vitejs/plugin-react@5.0.2': + resolution: {integrity: sha512-tmyFgixPZCx2+e6VO9TNITWcCQl8+Nl/E8YbAyPVv85QCc7/A3JrdfG2A8gIzvVhWuzMOVrFW1aReaNxrI6tbw==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + axios@1.13.2: + resolution: {integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + baseline-browser-mapping@2.10.24: + resolution: {integrity: sha512-I2NkZOOrj2XuguvWCK6OVh9GavsNjZjK908Rq3mIBK25+GD8vPX5w2WdxVqnQ7xx3SrZJiCiZFu+/Oz50oSYSA==} + engines: {node: '>=6.0.0'} + hasBin: true + + brace-expansion@1.1.14: + resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==} + + brace-expansion@2.1.0: + resolution: {integrity: sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.28.2: + resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001791: + resolution: {integrity: sha512-yk0l/YSrOnFZk3UROpDLQD9+kC1l4meK/wed583AXrzoarMGJcbRi2Q4RaUYbKxYAsZ8sWmaSa/DsLmdBeI1vQ==} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie@1.1.1: + resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} + engines: {node: '>=18'} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + detect-node-es@1.1.0: + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + + dom-helpers@5.2.1: + resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} + + dotenv@17.4.2: + resolution: {integrity: sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==} + engines: {node: '>=12'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + electron-to-chromium@1.5.344: + resolution: {integrity: sha512-4MxfbmNDm+KPh066EZy+eUnkcDPcZ35wNmOWzFuh/ijvHsve6kbLTLURy88uCNK5FbpN+yk2nQY6BYh1GEt+wg==} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + esbuild@0.25.12: + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-plugin-react-hooks@5.2.0: + resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + + eslint-plugin-react-refresh@0.4.20: + resolution: {integrity: sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==} + peerDependencies: + eslint: '>=8.40' + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.33.0: + resolution: {integrity: sha512-TS9bTNIryDzStCpJN93aC5VRSW3uTx9sClUn4B87pwiCaJh220otoI0X8mJKr+VcPtniMdN8GKjlwgWGUv5ZKA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.4.2: + resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + + follow-redirects@1.16.0: + resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} + engines: {node: '>= 6'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-nonce@1.0.1: + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} + engines: {node: '>=6'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@16.3.0: + resolution: {integrity: sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==} + engines: {node: '>=18'} + + globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.3: + resolution: {integrity: sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==} + engines: {node: '>= 0.4'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + immer@11.1.4: + resolution: {integrity: sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw==} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + keycloak-js@26.2.3: + resolution: {integrity: sha512-widjzw/9T6bHRgEp6H/Se3NCCarU7u5CwFKBcwtu7xfA1IfdZb+7Q7/KGusAnBo34Vtls8Oz9vzSqkQvQ7+b4Q==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash@4.18.1: + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + miew-react@0.11.0: + resolution: {integrity: sha512-IdJYzrbkpotS4Hx0JYg7KyOcpwyTdGRS1hnc504Y//Xvdl24Ft6uahuf9mbCEI2PNVPDARLc+hqzMTbpDWD88A==} + peerDependencies: + react: ^18.2.0 + react-dom: ^18.2.0 + + miew@0.11.1: + resolution: {integrity: sha512-CIhZfI9eB0P/covNrst45Y8Xd+MUncyxjNLkZULr+dcyS1ALxjtb+W2icbZEHEsn7zZJJ4ybIMIwQGnzS25IbQ==} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + + minimatch@9.0.9: + resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} + engines: {node: '>=16 || 14 >=14.17'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + node-releases@2.0.38: + resolution: {integrity: sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + postcss@8.5.12: + resolution: {integrity: sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + react-dom@19.2.5: + resolution: {integrity: sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==} + peerDependencies: + react: ^19.2.5 + + react-fast-compare@3.2.2: + resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} + + react-helmet@6.1.0: + resolution: {integrity: sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==} + peerDependencies: + react: '>=16.3.0' + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-number-format@5.4.5: + resolution: {integrity: sha512-y8O2yHHj3w0aE9XO8d2BCcUOOdQTRSVq+WIuMlLVucAm5XNjJAy+BoOJiuQMldVYVOKTMyvVNfnbl2Oqp+YxGw==} + peerDependencies: + react: ^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + react-refresh@0.17.0: + resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} + engines: {node: '>=0.10.0'} + + react-remove-scroll-bar@2.3.8: + resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + react-remove-scroll@2.7.2: + resolution: {integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react-resizable-panels@4.5.4: + resolution: {integrity: sha512-zK+Tz+73MBZKDtBUUcKjezaq+in8K10TJ8+slH/LNza5IL2CRFMKm0GbKyMPAGDPKY1x8G7sWx3Wu9inFRZGFA==} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + + react-router@7.9.6: + resolution: {integrity: sha512-Y1tUp8clYRXpfPITyuifmSoE2vncSME18uVLgaqyxh9H35JWpIfzHo+9y3Fzh5odk/jxPW29IgLgzcdwxGqyNA==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + peerDependenciesMeta: + react-dom: + optional: true + + react-side-effect@2.1.2: + resolution: {integrity: sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw==} + peerDependencies: + react: ^16.3.0 || ^17.0.0 || ^18.0.0 + + react-style-singleton@2.2.3: + resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react-transition-group@4.4.5: + resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} + peerDependencies: + react: '>=16.6.0' + react-dom: '>=16.6.0' + + react@19.2.5: + resolution: {integrity: sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rollup@4.60.2: + resolution: {integrity: sha512-J9qZyW++QK/09NyN/zeO0dG/1GdGfyp9lV8ajHnRVLfo/uFsbji5mHnDgn/qYdUHyCkM2N+8VyspgZclfAh0eQ==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} + hasBin: true + + set-cookie-parser@2.7.2: + resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + tabbable@6.4.0: + resolution: {integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==} + + tagged-tag@1.0.0: + resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} + engines: {node: '>=20'} + + three@0.153.0: + resolution: {integrity: sha512-OCP2/uQR6GcDpSLnJt/3a4mdS0kNWcbfUXIwLoEMgLzEUIVIYsSDwskpmOii/AkDM+BBwrl6+CKgrjX9+E2aWg==} + + tinyglobby@0.2.16: + resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} + engines: {node: '>=12.0.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + tsconfck@3.1.6: + resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} + engines: {node: ^18 || >=20} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-fest@5.6.0: + resolution: {integrity: sha512-8ZiHFm91orbSAe2PSAiSVBVko18pbhbiB3U9GglSzF/zCGkR+rxpHx6sEMCUm4kxY4LjDIUGgCfUMtwfZfjfUA==} + engines: {node: '>=20'} + + typescript-eslint@8.39.1: + resolution: {integrity: sha512-GDUv6/NDYngUlNvwaHM1RamYftxf782IyEDbdj3SeaIHHv8fNQVRC++fITT7kUJV/5rIA/tkoRSSskt6osEfqg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@7.19.2: + resolution: {integrity: sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==} + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + use-callback-ref@1.3.3: + resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + use-sidecar@1.1.3: + resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + vite-tsconfig-paths@5.1.4: + resolution: {integrity: sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==} + peerDependencies: + vite: '*' + peerDependenciesMeta: + vite: + optional: true + + vite@7.1.2: + resolution: {integrity: sha512-J0SQBPlQiEXAF7tajiH+rUooJPo0l8KQgyg4/aMunNtrOa7bwuZJsJbDWzeljqQpgftxuq5yNJxQ91O9ts29UQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + 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 + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zustand@5.0.8: + resolution: {integrity: sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + +snapshots: + + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.0': {} + + '@babel/core@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helpers': 7.29.2 + '@babel/parser': 7.29.2 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.1': + dependencies: + '@babel/parser': 7.29.2 + '@babel/types': 7.29.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.28.6': + dependencies: + '@babel/compat-data': 7.29.0 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.2 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-module-imports@7.28.6': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.28.6': {} + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.29.2': + dependencies: + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + + '@babel/parser@7.29.2': + dependencies: + '@babel/types': 7.29.0 + + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/runtime@7.29.2': {} + + '@babel/template@7.28.6': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/parser': 7.29.2 + '@babel/types': 7.29.0 + + '@babel/traverse@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.29.2 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@esbuild/aix-ppc64@0.25.12': + optional: true + + '@esbuild/android-arm64@0.25.12': + optional: true + + '@esbuild/android-arm@0.25.12': + optional: true + + '@esbuild/android-x64@0.25.12': + optional: true + + '@esbuild/darwin-arm64@0.25.12': + optional: true + + '@esbuild/darwin-x64@0.25.12': + optional: true + + '@esbuild/freebsd-arm64@0.25.12': + optional: true + + '@esbuild/freebsd-x64@0.25.12': + optional: true + + '@esbuild/linux-arm64@0.25.12': + optional: true + + '@esbuild/linux-arm@0.25.12': + optional: true + + '@esbuild/linux-ia32@0.25.12': + optional: true + + '@esbuild/linux-loong64@0.25.12': + optional: true + + '@esbuild/linux-mips64el@0.25.12': + optional: true + + '@esbuild/linux-ppc64@0.25.12': + optional: true + + '@esbuild/linux-riscv64@0.25.12': + optional: true + + '@esbuild/linux-s390x@0.25.12': + optional: true + + '@esbuild/linux-x64@0.25.12': + optional: true + + '@esbuild/netbsd-arm64@0.25.12': + optional: true + + '@esbuild/netbsd-x64@0.25.12': + optional: true + + '@esbuild/openbsd-arm64@0.25.12': + optional: true + + '@esbuild/openbsd-x64@0.25.12': + optional: true + + '@esbuild/openharmony-arm64@0.25.12': + optional: true + + '@esbuild/sunos-x64@0.25.12': + optional: true + + '@esbuild/win32-arm64@0.25.12': + optional: true + + '@esbuild/win32-ia32@0.25.12': + optional: true + + '@esbuild/win32-x64@0.25.12': + optional: true + + '@eslint-community/eslint-utils@4.9.1(eslint@9.33.0)': + dependencies: + eslint: 9.33.0 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.21.2': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.3.1': {} + + '@eslint/core@0.15.2': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.5': + dependencies: + ajv: 6.15.0 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.33.0': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.3.5': + dependencies: + '@eslint/core': 0.15.2 + levn: 0.4.1 + + '@floating-ui/core@1.7.5': + dependencies: + '@floating-ui/utils': 0.2.11 + + '@floating-ui/dom@1.7.6': + dependencies: + '@floating-ui/core': 1.7.5 + '@floating-ui/utils': 0.2.11 + + '@floating-ui/react-dom@2.1.8(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@floating-ui/dom': 1.7.6 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@floating-ui/react@0.27.19(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@floating-ui/react-dom': 2.1.8(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@floating-ui/utils': 0.2.11 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + tabbable: 6.4.0 + + '@floating-ui/utils@0.2.11': {} + + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 + + '@humanfs/node@0.16.8': + dependencies: + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 + '@humanwhocodes/retry': 0.4.3 + + '@humanfs/types@0.15.0': {} + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@mantine/core@9.1.1(@mantine/hooks@9.1.1(react@19.2.5))(@types/react@18.2.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@floating-ui/react': 0.27.19(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@mantine/hooks': 9.1.1(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + react-number-format: 5.4.5(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + react-remove-scroll: 2.7.2(@types/react@18.2.0)(react@19.2.5) + type-fest: 5.6.0 + transitivePeerDependencies: + - '@types/react' + + '@mantine/hooks@9.1.1(react@19.2.5)': + dependencies: + react: 19.2.5 + + '@mantine/notifications@9.1.1(@mantine/core@9.1.1(@mantine/hooks@9.1.1(react@19.2.5))(@types/react@18.2.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@mantine/hooks@9.1.1(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@mantine/core': 9.1.1(@mantine/hooks@9.1.1(react@19.2.5))(@types/react@18.2.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@mantine/hooks': 9.1.1(react@19.2.5) + '@mantine/store': 9.1.1(react@19.2.5) + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + react-transition-group: 4.4.5(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + + '@mantine/store@9.1.1(react@19.2.5)': + dependencies: + react: 19.2.5 + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.1 + + '@rolldown/pluginutils@1.0.0-beta.34': {} + + '@rollup/rollup-android-arm-eabi@4.60.2': + optional: true + + '@rollup/rollup-android-arm64@4.60.2': + optional: true + + '@rollup/rollup-darwin-arm64@4.60.2': + optional: true + + '@rollup/rollup-darwin-x64@4.60.2': + optional: true + + '@rollup/rollup-freebsd-arm64@4.60.2': + optional: true + + '@rollup/rollup-freebsd-x64@4.60.2': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.60.2': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.60.2': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.60.2': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.60.2': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.60.2': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.60.2': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.60.2': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.60.2': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.60.2': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.60.2': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.60.2': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.60.2': + optional: true + + '@rollup/rollup-linux-x64-musl@4.60.2': + optional: true + + '@rollup/rollup-openbsd-x64@4.60.2': + optional: true + + '@rollup/rollup-openharmony-arm64@4.60.2': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.60.2': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.60.2': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.60.2': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.60.2': + optional: true + + '@tabler/icons-react@3.34.1(react@19.2.5)': + dependencies: + '@tabler/icons': 3.34.1 + react: 19.2.5 + + '@tabler/icons@3.34.1': {} + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.29.2 + '@babel/types': 7.29.0 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.29.0 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.29.2 + '@babel/types': 7.29.0 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.29.0 + + '@types/estree@1.0.8': {} + + '@types/json-schema@7.0.15': {} + + '@types/node@25.6.0': + dependencies: + undici-types: 7.19.2 + + '@types/prop-types@15.7.15': {} + + '@types/react-dom@18.2.0': + dependencies: + '@types/react': 18.2.0 + + '@types/react-helmet@6.1.11': + dependencies: + '@types/react': 18.2.0 + + '@types/react@18.2.0': + dependencies: + '@types/prop-types': 15.7.15 + '@types/scheduler': 0.26.0 + csstype: 3.2.3 + + '@types/scheduler@0.26.0': {} + + '@typescript-eslint/eslint-plugin@8.39.1(@typescript-eslint/parser@8.39.1(eslint@9.33.0)(typescript@5.8.3))(eslint@9.33.0)(typescript@5.8.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.39.1(eslint@9.33.0)(typescript@5.8.3) + '@typescript-eslint/scope-manager': 8.39.1 + '@typescript-eslint/type-utils': 8.39.1(eslint@9.33.0)(typescript@5.8.3) + '@typescript-eslint/utils': 8.39.1(eslint@9.33.0)(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.39.1 + eslint: 9.33.0 + graphemer: 1.4.0 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.39.1(eslint@9.33.0)(typescript@5.8.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.39.1 + '@typescript-eslint/types': 8.39.1 + '@typescript-eslint/typescript-estree': 8.39.1(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.39.1 + debug: 4.4.3 + eslint: 9.33.0 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.39.1(typescript@5.8.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.59.1(typescript@5.8.3) + '@typescript-eslint/types': 8.59.1 + debug: 4.4.3 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.39.1': + dependencies: + '@typescript-eslint/types': 8.39.1 + '@typescript-eslint/visitor-keys': 8.39.1 + + '@typescript-eslint/tsconfig-utils@8.39.1(typescript@5.8.3)': + dependencies: + typescript: 5.8.3 + + '@typescript-eslint/tsconfig-utils@8.59.1(typescript@5.8.3)': + dependencies: + typescript: 5.8.3 + + '@typescript-eslint/type-utils@8.39.1(eslint@9.33.0)(typescript@5.8.3)': + dependencies: + '@typescript-eslint/types': 8.39.1 + '@typescript-eslint/typescript-estree': 8.39.1(typescript@5.8.3) + '@typescript-eslint/utils': 8.39.1(eslint@9.33.0)(typescript@5.8.3) + debug: 4.4.3 + eslint: 9.33.0 + ts-api-utils: 2.5.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.39.1': {} + + '@typescript-eslint/types@8.59.1': {} + + '@typescript-eslint/typescript-estree@8.39.1(typescript@5.8.3)': + dependencies: + '@typescript-eslint/project-service': 8.39.1(typescript@5.8.3) + '@typescript-eslint/tsconfig-utils': 8.39.1(typescript@5.8.3) + '@typescript-eslint/types': 8.39.1 + '@typescript-eslint/visitor-keys': 8.39.1 + debug: 4.4.3 + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.9 + semver: 7.7.4 + ts-api-utils: 2.5.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.39.1(eslint@9.33.0)(typescript@5.8.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.33.0) + '@typescript-eslint/scope-manager': 8.39.1 + '@typescript-eslint/types': 8.39.1 + '@typescript-eslint/typescript-estree': 8.39.1(typescript@5.8.3) + eslint: 9.33.0 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.39.1': + dependencies: + '@typescript-eslint/types': 8.39.1 + eslint-visitor-keys: 4.2.1 + + '@vitejs/plugin-react@5.0.2(vite@7.1.2(@types/node@25.6.0))': + dependencies: + '@babel/core': 7.29.0 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0) + '@rolldown/pluginutils': 1.0.0-beta.34 + '@types/babel__core': 7.20.5 + react-refresh: 0.17.0 + vite: 7.1.2(@types/node@25.6.0) + transitivePeerDependencies: + - supports-color + + acorn-jsx@5.3.2(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + + acorn@8.16.0: {} + + ajv@6.15.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + argparse@2.0.1: {} + + asynckit@0.4.0: {} + + axios@1.13.2: + dependencies: + follow-redirects: 1.16.0 + form-data: 4.0.5 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + balanced-match@1.0.2: {} + + baseline-browser-mapping@2.10.24: {} + + brace-expansion@1.1.14: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.1.0: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.28.2: + dependencies: + baseline-browser-mapping: 2.10.24 + caniuse-lite: 1.0.30001791 + electron-to-chromium: 1.5.344 + node-releases: 2.0.38 + update-browserslist-db: 1.2.3(browserslist@4.28.2) + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + callsites@3.1.0: {} + + caniuse-lite@1.0.30001791: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + clsx@2.1.1: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + concat-map@0.0.1: {} + + convert-source-map@2.0.0: {} + + cookie@1.1.1: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + csstype@3.2.3: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + deep-is@0.1.4: {} + + delayed-stream@1.0.0: {} + + detect-node-es@1.1.0: {} + + dom-helpers@5.2.1: + dependencies: + '@babel/runtime': 7.29.2 + csstype: 3.2.3 + + dotenv@17.4.2: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + electron-to-chromium@1.5.344: {} + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.3 + + esbuild@0.25.12: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.12 + '@esbuild/android-arm': 0.25.12 + '@esbuild/android-arm64': 0.25.12 + '@esbuild/android-x64': 0.25.12 + '@esbuild/darwin-arm64': 0.25.12 + '@esbuild/darwin-x64': 0.25.12 + '@esbuild/freebsd-arm64': 0.25.12 + '@esbuild/freebsd-x64': 0.25.12 + '@esbuild/linux-arm': 0.25.12 + '@esbuild/linux-arm64': 0.25.12 + '@esbuild/linux-ia32': 0.25.12 + '@esbuild/linux-loong64': 0.25.12 + '@esbuild/linux-mips64el': 0.25.12 + '@esbuild/linux-ppc64': 0.25.12 + '@esbuild/linux-riscv64': 0.25.12 + '@esbuild/linux-s390x': 0.25.12 + '@esbuild/linux-x64': 0.25.12 + '@esbuild/netbsd-arm64': 0.25.12 + '@esbuild/netbsd-x64': 0.25.12 + '@esbuild/openbsd-arm64': 0.25.12 + '@esbuild/openbsd-x64': 0.25.12 + '@esbuild/openharmony-arm64': 0.25.12 + '@esbuild/sunos-x64': 0.25.12 + '@esbuild/win32-arm64': 0.25.12 + '@esbuild/win32-ia32': 0.25.12 + '@esbuild/win32-x64': 0.25.12 + + escalade@3.2.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-plugin-react-hooks@5.2.0(eslint@9.33.0): + dependencies: + eslint: 9.33.0 + + eslint-plugin-react-refresh@0.4.20(eslint@9.33.0): + dependencies: + eslint: 9.33.0 + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint@9.33.0: + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.33.0) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.2 + '@eslint/config-helpers': 0.3.1 + '@eslint/core': 0.15.2 + '@eslint/eslintrc': 3.3.5 + '@eslint/js': 9.33.0 + '@eslint/plugin-kit': 0.3.5 + '@humanfs/node': 0.16.8 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + ajv: 6.15.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 4.2.1 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fastq@1.20.1: + dependencies: + reusify: 1.1.0 + + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.4.2 + keyv: 4.5.4 + + flatted@3.4.2: {} + + follow-redirects@1.16.0: {} + + form-data@4.0.5: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.3 + mime-types: 2.1.35 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gensync@1.0.0-beta.2: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.3 + math-intrinsics: 1.1.0 + + get-nonce@1.0.1: {} + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + + globals@16.3.0: {} + + globrex@0.1.2: {} + + gopd@1.2.0: {} + + graphemer@1.4.0: {} + + has-flag@4.0.0: {} + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.3: + dependencies: + function-bind: 1.1.2 + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + immer@11.1.4: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + + isexe@2.0.0: {} + + js-tokens@4.0.0: {} + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + jsesc@3.1.0: {} + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@2.2.3: {} + + keycloak-js@26.2.3: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + lodash@4.18.1: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + math-intrinsics@1.1.0: {} + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.2 + + miew-react@0.11.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5): + dependencies: + miew: 0.11.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + miew@0.11.1: + dependencies: + lodash: 4.18.1 + three: 0.153.0 + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.14 + + minimatch@9.0.9: + dependencies: + brace-expansion: 2.1.0 + + ms@2.1.3: {} + + nanoid@3.3.11: {} + + natural-compare@1.4.0: {} + + node-releases@2.0.38: {} + + object-assign@4.1.1: {} + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + picocolors@1.1.1: {} + + picomatch@2.3.2: {} + + picomatch@4.0.4: {} + + postcss@8.5.12: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + proxy-from-env@1.1.0: {} + + punycode@2.3.1: {} + + queue-microtask@1.2.3: {} + + react-dom@19.2.5(react@19.2.5): + dependencies: + react: 19.2.5 + scheduler: 0.27.0 + + react-fast-compare@3.2.2: {} + + react-helmet@6.1.0(react@19.2.5): + dependencies: + object-assign: 4.1.1 + prop-types: 15.8.1 + react: 19.2.5 + react-fast-compare: 3.2.2 + react-side-effect: 2.1.2(react@19.2.5) + + react-is@16.13.1: {} + + react-number-format@5.4.5(react-dom@19.2.5(react@19.2.5))(react@19.2.5): + dependencies: + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + react-refresh@0.17.0: {} + + react-remove-scroll-bar@2.3.8(@types/react@18.2.0)(react@19.2.5): + dependencies: + react: 19.2.5 + react-style-singleton: 2.2.3(@types/react@18.2.0)(react@19.2.5) + tslib: 2.8.1 + optionalDependencies: + '@types/react': 18.2.0 + + react-remove-scroll@2.7.2(@types/react@18.2.0)(react@19.2.5): + dependencies: + react: 19.2.5 + react-remove-scroll-bar: 2.3.8(@types/react@18.2.0)(react@19.2.5) + react-style-singleton: 2.2.3(@types/react@18.2.0)(react@19.2.5) + tslib: 2.8.1 + use-callback-ref: 1.3.3(@types/react@18.2.0)(react@19.2.5) + use-sidecar: 1.1.3(@types/react@18.2.0)(react@19.2.5) + optionalDependencies: + '@types/react': 18.2.0 + + react-resizable-panels@4.5.4(react-dom@19.2.5(react@19.2.5))(react@19.2.5): + dependencies: + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + react-router@7.9.6(react-dom@19.2.5(react@19.2.5))(react@19.2.5): + dependencies: + cookie: 1.1.1 + react: 19.2.5 + set-cookie-parser: 2.7.2 + optionalDependencies: + react-dom: 19.2.5(react@19.2.5) + + react-side-effect@2.1.2(react@19.2.5): + dependencies: + react: 19.2.5 + + react-style-singleton@2.2.3(@types/react@18.2.0)(react@19.2.5): + dependencies: + get-nonce: 1.0.1 + react: 19.2.5 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 18.2.0 + + react-transition-group@4.4.5(react-dom@19.2.5(react@19.2.5))(react@19.2.5): + dependencies: + '@babel/runtime': 7.29.2 + dom-helpers: 5.2.1 + loose-envify: 1.4.0 + prop-types: 15.8.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + react@19.2.5: {} + + resolve-from@4.0.0: {} + + reusify@1.1.0: {} + + rollup@4.60.2: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.60.2 + '@rollup/rollup-android-arm64': 4.60.2 + '@rollup/rollup-darwin-arm64': 4.60.2 + '@rollup/rollup-darwin-x64': 4.60.2 + '@rollup/rollup-freebsd-arm64': 4.60.2 + '@rollup/rollup-freebsd-x64': 4.60.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.60.2 + '@rollup/rollup-linux-arm-musleabihf': 4.60.2 + '@rollup/rollup-linux-arm64-gnu': 4.60.2 + '@rollup/rollup-linux-arm64-musl': 4.60.2 + '@rollup/rollup-linux-loong64-gnu': 4.60.2 + '@rollup/rollup-linux-loong64-musl': 4.60.2 + '@rollup/rollup-linux-ppc64-gnu': 4.60.2 + '@rollup/rollup-linux-ppc64-musl': 4.60.2 + '@rollup/rollup-linux-riscv64-gnu': 4.60.2 + '@rollup/rollup-linux-riscv64-musl': 4.60.2 + '@rollup/rollup-linux-s390x-gnu': 4.60.2 + '@rollup/rollup-linux-x64-gnu': 4.60.2 + '@rollup/rollup-linux-x64-musl': 4.60.2 + '@rollup/rollup-openbsd-x64': 4.60.2 + '@rollup/rollup-openharmony-arm64': 4.60.2 + '@rollup/rollup-win32-arm64-msvc': 4.60.2 + '@rollup/rollup-win32-ia32-msvc': 4.60.2 + '@rollup/rollup-win32-x64-gnu': 4.60.2 + '@rollup/rollup-win32-x64-msvc': 4.60.2 + fsevents: 2.3.3 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + scheduler@0.27.0: {} + + semver@6.3.1: {} + + semver@7.7.4: {} + + set-cookie-parser@2.7.2: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + source-map-js@1.2.1: {} + + strip-json-comments@3.1.1: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + tabbable@6.4.0: {} + + tagged-tag@1.0.0: {} + + three@0.153.0: {} + + tinyglobby@0.2.16: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + ts-api-utils@2.5.0(typescript@5.8.3): + dependencies: + typescript: 5.8.3 + + tsconfck@3.1.6(typescript@5.8.3): + optionalDependencies: + typescript: 5.8.3 + + tslib@2.8.1: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-fest@5.6.0: + dependencies: + tagged-tag: 1.0.0 + + typescript-eslint@8.39.1(eslint@9.33.0)(typescript@5.8.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.39.1(@typescript-eslint/parser@8.39.1(eslint@9.33.0)(typescript@5.8.3))(eslint@9.33.0)(typescript@5.8.3) + '@typescript-eslint/parser': 8.39.1(eslint@9.33.0)(typescript@5.8.3) + '@typescript-eslint/typescript-estree': 8.39.1(typescript@5.8.3) + '@typescript-eslint/utils': 8.39.1(eslint@9.33.0)(typescript@5.8.3) + eslint: 9.33.0 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + typescript@5.8.3: {} + + undici-types@7.19.2: {} + + update-browserslist-db@1.2.3(browserslist@4.28.2): + dependencies: + browserslist: 4.28.2 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + use-callback-ref@1.3.3(@types/react@18.2.0)(react@19.2.5): + dependencies: + react: 19.2.5 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 18.2.0 + + use-sidecar@1.1.3(@types/react@18.2.0)(react@19.2.5): + dependencies: + detect-node-es: 1.1.0 + react: 19.2.5 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 18.2.0 + + vite-tsconfig-paths@5.1.4(typescript@5.8.3)(vite@7.1.2(@types/node@25.6.0)): + dependencies: + debug: 4.4.3 + globrex: 0.1.2 + tsconfck: 3.1.6(typescript@5.8.3) + optionalDependencies: + vite: 7.1.2(@types/node@25.6.0) + transitivePeerDependencies: + - supports-color + - typescript + + vite@7.1.2(@types/node@25.6.0): + dependencies: + esbuild: 0.25.12 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + postcss: 8.5.12 + rollup: 4.60.2 + tinyglobby: 0.2.16 + optionalDependencies: + '@types/node': 25.6.0 + fsevents: 2.3.3 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + yallist@3.1.1: {} + + yocto-queue@0.1.0: {} + + zustand@5.0.8(@types/react@18.2.0)(immer@11.1.4)(react@19.2.5): + optionalDependencies: + '@types/react': 18.2.0 + immer: 11.1.4 + react: 19.2.5 diff --git a/public/index.html b/public/index.html deleted file mode 100644 index e543358..0000000 --- a/public/index.html +++ /dev/null @@ -1,5975 +0,0 @@ - - - - - - - - - - - -
- - diff --git a/src/Api/ConvertBackendCalls.tsx b/src/Api/ConvertBackendCalls.tsx deleted file mode 100755 index 8504068..0000000 --- a/src/Api/ConvertBackendCalls.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import axios, { AxiosError } from "axios"; -import type { ConvertSchema } from "Types/ApiCalls/ConvertBackendCallsTypes"; - -export async function ConvertMoleculeToStandart( - data: ConvertSchema, -): Promise { - try { - const response = await axios.post( - import.meta.env.VITE_MOLECULAR_BACKEND_URL + "/convert", - { - text: data.inputText, - format: data.inputFormat, - convert_3d: data.make_3d, - add_hydrogen: data.add_h, - optimize_geometry: data.optimize, - }, - ); - - // Response from the FastAPI JSONResponse - return response.data.molfile; - } catch (error) { - //Error handling - if (axios.isAxiosError(error)) { - // Do something with the axios error... - return error; - } else { - throw error; - } - } -} - -export async function GetInFormats(): Promise< - { [key: string]: string } | AxiosError -> { - try { - const response = await axios.get( - import.meta.env.VITE_MOLECULAR_BACKEND_URL + "/informats", - ); - return response.data; - } catch (error) { - //Error handling - if (axios.isAxiosError(error)) { - // Do something with the axios error... - return error; - } else { - throw error; - } - } -} diff --git a/src/Api/Keycloak/Keycloak.tsx b/src/Api/Keycloak/Keycloak.tsx index 57691e5..0b97088 100644 --- a/src/Api/Keycloak/Keycloak.tsx +++ b/src/Api/Keycloak/Keycloak.tsx @@ -61,8 +61,6 @@ export const updatePasswordWithRedirect = async ( redirectUri: successRedirectUrl || window.location.origin + - "/" + - import.meta.env.VITE_BASE_PATH + "/" + routes.SettingsPage.path + "?password_updated=true", @@ -85,7 +83,6 @@ export const SendEmailVerification = async () => { action: "VERIFY_EMAIL", redirectUri: window.location.origin + - import.meta.env.VITE_BASE_PATH + "/" + routes.SettingsPage.path + "?email_sent=true", diff --git a/src/Api/PluginLoader/PluginLoader.tsx b/src/Api/PluginLoader/PluginLoader.tsx index 48e46a1..4ab579c 100644 --- a/src/Api/PluginLoader/PluginLoader.tsx +++ b/src/Api/PluginLoader/PluginLoader.tsx @@ -1,58 +1,59 @@ -import React, { useRef, useEffect, useState, useMemo } from "react"; +import { LoadingOverlay } from "@mantine/core"; +import React, { useRef, useEffect, useState } from "react"; import { useUserPreferencesStore } from "Stores/PreferencesStore"; -import type { TaskData } from "Types/Experiment/Experiment"; interface IframePluginProps { - pluginUrl: string; - mode: "list" | "editor"; - taskData: TaskData; - onUpdate?: (data: any) => void; + index: number; + plugin: string; + mode: "List" | "Editor"; + taskData: string; + simProgress: string; + qubits_needed: number; + onUpdate?: (data: string, qubits_needed: number) => void; } export const IframePlugin: React.FC = ({ - pluginUrl, + index, + plugin, mode, taskData, + simProgress, + qubits_needed, onUpdate, }) => { const iframeRef = useRef(null); const theme = useUserPreferencesStore(); const [isIframeReady, setIsIframeReady] = useState(false); - - // Memoize the iframe component to prevent recreation when taskData changes - const memoizedIframe = useMemo(() => { - const url = `${pluginUrl}?mode=${mode}`; - return ( - + + + ); }; diff --git a/src/Api/QuantumBackend/ExperimentsManagment.tsx b/src/Api/QuantumBackend/ExperimentsManagment.tsx new file mode 100644 index 0000000..d0e32ed --- /dev/null +++ b/src/Api/QuantumBackend/ExperimentsManagment.tsx @@ -0,0 +1,186 @@ +import keycloak from "Api/Keycloak/Keycloak"; +import axios from "axios"; +import type { + CreateExperimentRequest, + ExperimentData, + ExperimentListResponse, + CreateInstanceRequest, + SimpleInstanceData, + UpdateInstanceRequest, + InstanceListResponse, + InstanceData, + StartExperimentRequest, + UpdateExperimentRequest, + CreateExperimentTypeResponse, + ExperimentTypeList, +} from "Types/Experiment/Experiment"; + +const api = axios.create({ + baseURL: `${import.meta.env.VITE_QUANTUM_BACKEND_URL}/experiment`, + headers: { "Content-Type": "application/json" }, +}); + +// Add auth token to requests +api.interceptors.request.use((config) => { + const token = keycloak.token; + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } + return config; +}); + +// ============= EXPERIMENT TYPE ENDPOINTS ============= + +// 1. Get all experiment types +export const getExperimentTypes = async (): Promise => { + const response = await api.get("/types"); + return response.data; +}; + +// 2. Create experiment type (admin only, with file uploads) +export const createExperimentType = async ( + name: string, + fileFrontend: File, + fileCompSystem: File, + description?: string, +): Promise => { + const formData = new FormData(); + formData.append("name", name); + formData.append("file_frontend", fileFrontend); + formData.append("file_comp_system", fileCompSystem); + if (description) { + formData.append("description", description); + } + + const response = await api.post( + "/types", + formData, + { + headers: { "Content-Type": "multipart/form-data" }, + }, + ); + return response.data; +}; + +// 3. Get frontend HTML file for experiment type +export const getFrontendFile = async ( + experiment_type_id: number, +): Promise => { + const response = await api.get("/types/frontend", { + params: { experiment_type_id }, + }); + return response.data; +}; + +// ============= EXPERIMENT ENDPOINTS ============= + +// 5. Create a new experiment +export const createExperiment = async ( + data: CreateExperimentRequest, +): Promise => { + const response = await api.post("", data); + return response.data; +}; + +// 6. Get single experiment by ID +export const getExperimentById = async ( + experiment_id: number, +): Promise => { + const response = await api.get("", { + params: { experiment_id }, + }); + return response.data; +}; + +// 7. Update experiment +export const updateExperiment = async ( + data: UpdateExperimentRequest, +): Promise => { + const response = await api.put("", data); + return response.data; +}; + +// 8. Get user's experiments (with pagination) +export const getUserExperiments = async ({ + page_num = 1, + page_size = 10, +}: { + page_num?: number; + page_size?: number; +}): Promise => { + const response = await api.get("/user", { + params: { page_num, page_size }, + }); + return response.data; +}; + +// 9. Delete experiment +export const deleteExperiment = async ( + experiment_id: number, +): Promise<{ message: string }> => { + const response = await api.delete<{ message: string }>("", { + params: { experiment_id }, + }); + return response.data; +}; + +// ============= INSTANCE ENDPOINTS ============= + +// 10. Create instance for an experiment +export const createInstance = async ( + data: CreateInstanceRequest, +): Promise => { + const response = await api.post("/instance", data); + return response.data; +}; + +// 11. Update instance +export const updateInstance = async ( + data: UpdateInstanceRequest, +): Promise => { + const response = await api.put("/instance", data); + return response.data; +}; + +// 12. Get all instances of an experiment (with pagination) +export const getExperimentInstances = async ( + experiment_id: number, + page_num: number = 1, + page_size: number = 6, +): Promise => { + page_num = Math.max(page_num, 1); + const response = await api.get("/instance", { + params: { experiment_id, page_num, page_size }, + }); + return response.data; +}; + +// 13. Get single instance by ID +export const getInstanceById = async ( + instance_id: number, +): Promise => { + const response = await api.get("/instance/id", { + params: { instance_id }, + }); + return response.data; +}; + +// 14. Delete instance +export const deleteInstance = async ( + instance_id: number, +): Promise<{ message: string }> => { + const response = await api.delete<{ message: string }>("/instance", { + params: { instance_id }, + }); + return response.data; +}; + +// ============= SIMULATION ENDPOINTS ============= + +// 15. Start experiment (run simulation) +export const startExperiment = async ( + data: StartExperimentRequest, +): Promise<200> => { + const response = await api.post<200>("/start", data); + return response.data; +}; diff --git a/src/Api/QuantumBackend/UserManagement.tsx b/src/Api/QuantumBackend/UserManagement.tsx index 4c477bb..ef4a243 100644 --- a/src/Api/QuantumBackend/UserManagement.tsx +++ b/src/Api/QuantumBackend/UserManagement.tsx @@ -25,25 +25,38 @@ export const GetCurrentUserInfo = async (): Promise => { } }; +// In Api/QuantumBackend/UserManagement.ts export const UpdateCurrentUserInfo = async ( - profile_picture_path: string, -): Promise => { - const response = await axios.put( - `${import.meta.env.VITE_QUANTUM_BACKEND_URL}/user`, - { profile_picture_path: profile_picture_path }, - { - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${keycloak.token}`, + file: File, +): Promise<{ profile_picture_path: string } | undefined> => { + try { + const formData = new FormData(); + formData.append("file", file); + + const response = await fetch( + `${import.meta.env.VITE_QUANTUM_BACKEND_URL}/user/upload`, + { + method: "POST", + headers: { + Authorization: `Bearer ${keycloak.token}`, + }, + body: formData, }, - withCredentials: true, // Important: allows credentials in CORS - }, - ); - if (response.status === 200) { - return response.data; + ); + + if (!response.ok) { + throw new Error("Failed to upload profile picture"); + } + + return await response.json(); + } catch (error) { + console.error("Error uploading profile picture:", error); + return undefined; } }; +// Keep your existing GetCurrentUserInfo function + export const GetUserByEmail = async ( email: string, ): Promise => { diff --git a/src/App.tsx b/src/App.tsx index 263b2ec..60ea604 100755 --- a/src/App.tsx +++ b/src/App.tsx @@ -35,8 +35,7 @@ function App() { keycloak.updateToken(60).catch((error) => { console.log(error); keycloak.logout({ - redirectUri: - window.location.origin + "/" + import.meta.env.VITE_BASE_PATH, + redirectUri: window.location.origin, }); }); }; diff --git a/src/Components/CustomButton/CustomButton.css b/src/Components/CustomButton/CustomButton.css index dd1a3be..51ce509 100644 --- a/src/Components/CustomButton/CustomButton.css +++ b/src/Components/CustomButton/CustomButton.css @@ -5,9 +5,11 @@ display: flex; padding-left: 5px; padding-right: 5px; + position: relative; + overflow: "hidden"; } -.colored:hover { +:not(.button-Disabled).colored:hover { background-color: color; } @@ -38,7 +40,7 @@ --hover_color: color-mix(in srgb, var(--hovercolor) 20%, transparent); } -.outline:hover { +:not(.button-Disabled).outline:hover { background: var(--hover_color); } @@ -50,7 +52,7 @@ --hover_color: var(--hovercolor); } -.color:hover { +:not(.button-Disabled).color:hover { background-color: var(--hover_color); } @@ -103,11 +105,10 @@ --hover_color: color-mix(in srgb, var(--color) 20%, transparent); } -.subtle:hover { +:not(.button-Disabled).subtle:hover { background-color: --hover_color; } .button-Disabled { - background-color: var(--hover_color); - cursor: inherit; + cursor: default; } diff --git a/src/Components/CustomButton/CustomButton.tsx b/src/Components/CustomButton/CustomButton.tsx index a11eee2..40e0ada 100644 --- a/src/Components/CustomButton/CustomButton.tsx +++ b/src/Components/CustomButton/CustomButton.tsx @@ -33,10 +33,8 @@ function CustomButton({ }: CustomButtonProps) { return ( + {disabled && ( +
+ )} {icon} {icon && } {text}
diff --git a/src/Components/Layout/Header/Header.tsx b/src/Components/Layout/Header/Header.tsx index 48e6221..2caceaa 100755 --- a/src/Components/Layout/Header/Header.tsx +++ b/src/Components/Layout/Header/Header.tsx @@ -4,8 +4,7 @@ import { useLayoutStore } from "Stores/LayoutStore"; import { Link } from "react-router"; import { routes } from "Routes/Routes"; -const logoUrl = - window.location.origin + "/" + import.meta.env.VITE_BASE_PATH + "/bitmap.png"; +const logoUrl = window.location.origin + "/bitmap.png"; function Header() { const { is_navbar_open, set_navbar_open } = useLayoutStore(); diff --git a/src/Components/Layout/Sidebar/Sidebar.tsx b/src/Components/Layout/Sidebar/Sidebar.tsx index 7d0fcad..765029c 100755 --- a/src/Components/Layout/Sidebar/Sidebar.tsx +++ b/src/Components/Layout/Sidebar/Sidebar.tsx @@ -18,8 +18,13 @@ import { IconUsers, type IconProps, } from "@tabler/icons-react"; -import { Link, useLocation } from "react-router"; -import { useState, type ForwardRefExoticComponent } from "react"; +import { + Link, + useLocation, + useNavigate, + type NavigateFunction, +} from "react-router"; +import { useEffect, useState, type ForwardRefExoticComponent } from "react"; import { routes } from "Routes/Routes"; import keycloak from "Api/Keycloak/Keycloak"; import { useAuthenticationStore } from "Stores/AuthenticationStore"; @@ -33,6 +38,7 @@ interface SubtleLinkButtonProps { text: string; color: string; selected?: boolean; + nav: NavigateFunction; } function SubtleLinkButton(props: SubtleLinkButtonProps) { @@ -44,16 +50,48 @@ function SubtleLinkButton(props: SubtleLinkButtonProps) { color={props.selected ? props.color : "contrast"} text={props.text} textAlign="left" + onClick={() => { + props.nav(props.link); + }} /> ); } function Sidebar() { - const { profile, profile_picture_path } = useAuthenticationStore(); + const { profile, set_profile_picture_path, profile_picture_path } = + useAuthenticationStore(); const [open, set_open] = useState(false); const theme = useMantineTheme(); const location = useLocation(); // get current URL + const navigate = useNavigate(); + + useEffect(() => { + if (!profile) return; + + const fetchAvatar = async () => { + try { + const response = await fetch( + `${import.meta.env.VITE_QUANTUM_BACKEND_URL}/user/serve/${profile.id}`, + { + headers: { + Authorization: `Bearer ${keycloak.token}`, + }, + }, + ); + + if (response.ok) { + const blob = await response.blob(); + const url = URL.createObjectURL(blob); + set_profile_picture_path(url); + } + } catch (error) { + console.error("Failed to load avatar:", error); + } + }; + + fetchAvatar(); + }, [profile]); return (
@@ -70,8 +108,7 @@ function Sidebar() { text="Подтвердить" onClick={() => keycloak.logout({ - redirectUri: - window.location.origin + "/" + import.meta.env.VITE_BASE_PATH, + redirectUri: window.location.origin, }) } textSize="lg" @@ -94,6 +131,7 @@ function Sidebar() { text="Эксперименты" color={theme.colors.teal[7]} selected={location.pathname.startsWith(routes.ExperimentsPage.path)} + nav={navigate} />
@@ -134,15 +175,16 @@ function Sidebar() { style={{ width: "100%", height: "5px", margin: "5px" }} />
- - } - text="Настройки" - color="contrast" - textSize="lg" - /> - + } + text="Настройки" + color="contrast" + textSize="lg" + onClick={() => { + navigate(routes.SettingsPage.path); + }} + /> } diff --git a/src/Components/ListCard/ExperimentsListCard.tsx b/src/Components/ListCard/ExperimentsListCard.tsx index 98f73fd..de87de0 100644 --- a/src/Components/ListCard/ExperimentsListCard.tsx +++ b/src/Components/ListCard/ExperimentsListCard.tsx @@ -1,24 +1,45 @@ import { Card, Pill, SimpleGrid, Text, UnstyledButton } from "@mantine/core"; import "./ExperimentsListCard.css"; import { useNavigate } from "react-router"; -import type { Experiment, TaskData } from "Types/Experiment/Experiment"; +import type { + ExperimentData, + SimpleInstanceData, +} from "Types/Experiment/Experiment"; import { IconTrash } from "@tabler/icons-react"; import type { MouseEvent } from "react"; import { useExperimentStore } from "Stores/ExperimentStore"; +import { deleteExperiment } from "Api/QuantumBackend/ExperimentsManagment"; -function ExperimentsListCard(props: { - experiment: Experiment; - team: { team_id: number; team_name: string } | undefined; -}) { +function ExperimentsListCard(props: { experiment: ExperimentData }) { const navigate = useNavigate(); - const { removeExperiment, tasks } = useExperimentStore(); + const { removeExperiment } = useExperimentStore(); - const experiment_tasks = tasks.filter( - (a) => a.id in props.experiment.tasks_ids, - ); + const getStatusColor = (status: string) => { + const statusLower = status; + + switch (statusLower) { + case "draft": + return "var(--mantine-color-gray-5)"; + case "in queue": + return "var(--mantine-color-blue-5)"; + case "processing": + case "running": + return "var(--mantine-color-yellow-5)"; + case "complete": + return "var(--mantine-color-green-5)"; + case "error": + case "complete with error": + case "failed": + return "var(--mantine-color-red-5)"; + default: + return "var(--mantine-color-gray-5)"; + } + }; const handleDelete = () => { - removeExperiment(props.experiment.id); + deleteExperiment(props.experiment.id).then(() => { + removeExperiment(props.experiment.id); + }); }; return ( @@ -34,30 +55,38 @@ function ExperimentsListCard(props: { Эксперимент: {props.experiment.name} - Команда: {props.team?.team_name} + Команда: {props.experiment.team?.team_name}
Статус: - - {props.experiment.experiment_status} + + {props.experiment.status}
- - Задачи: + + Задачи: - {experiment_tasks.map( + {props.experiment.instance_preview.slice(0, 6).map( ( // eslint-disable-next-line @typescript-eslint/no-explicit-any - task: TaskData, + instance: SimpleInstanceData, ) => { return ( - {task.name} + {instance.name} ); }, )} - {experiment_tasks.length == 0 ? ( - + {props.experiment.instances_count == 0 ? ( + Нет Задач ) : ( <> )} + {props.experiment.instances_count > + props.experiment.instance_preview.length ? ( + + и еще{" "} + {props.experiment.instances_count - + props.experiment.instance_preview.length} + + ) : ( + <> + )}
- {new Date( - props.experiment.date_created + "Z", - ).toLocaleDateString("ru")}{" "} + {new Date(props.experiment.created_at + "Z").toLocaleDateString( + "ru", + )}{" "} - {new Date( - props.experiment.date_created + "Z", - ).toLocaleTimeString("ru")} + {new Date(props.experiment.created_at + "Z").toLocaleTimeString( + "ru", + )}
Тип эксперимента: - {props.experiment.experiment_type} + {props.experiment.experiment_type.name}
diff --git a/src/Components/ListCard/MachineListCard/MachinesListCard.tsx b/src/Components/ListCard/MachineListCard/MachinesListCard.tsx index a14b713..79196ae 100644 --- a/src/Components/ListCard/MachineListCard/MachinesListCard.tsx +++ b/src/Components/ListCard/MachineListCard/MachinesListCard.tsx @@ -64,7 +64,8 @@ function MachinesListCard(props: SystemWithTeams) { //className="MachinePill" style={{ marginLeft: "10px", - backgroundColor: colors[props.system.status || "ONLINE"], + backgroundColor: + colors[props.system.status as "ONLINE" | "OFFLINE" | "BUSY"], }} > {props.system.status} @@ -82,12 +83,12 @@ function MachinesListCard(props: SystemWithTeams) {
- {new Date(props.system.created_at + "Z").toLocaleDateString( + {new Date(props.system.last_updated + "Z").toLocaleDateString( "ru", )}{" "} - {new Date(props.system.created_at + "Z").toLocaleTimeString("ru")} + {new Date(props.system.last_updated + "Z").toLocaleTimeString("ru")}
diff --git a/src/Components/ListCard/TaskListCard/TaskListCard.css b/src/Components/ListCard/TaskListCard/TaskListCard.css new file mode 100644 index 0000000..a7310f6 --- /dev/null +++ b/src/Components/ListCard/TaskListCard/TaskListCard.css @@ -0,0 +1,28 @@ +.InstancesListCard { + height: 120px; +} + +.InstanceSectionWithLine { + width: 250px; +} + +.InstanceIframeSection { + flex-grow: 1; + position: relative; +} + +.DatesContainer { + display: flex; + flex-direction: column; +} +.RightInstanceSection { + display: flex; + flex-direction: row; + width: 275px; + justify-content: space-between; +} + +.MachinePill { + background-color: var(--mantine-color-contrast-filled); + color: var(--mantine-color-primary-filled); +} diff --git a/src/Components/ListCard/TaskListCard/TaskListCard.tsx b/src/Components/ListCard/TaskListCard/TaskListCard.tsx new file mode 100644 index 0000000..8901cb9 --- /dev/null +++ b/src/Components/ListCard/TaskListCard/TaskListCard.tsx @@ -0,0 +1,176 @@ +import { Card, Pill, Text, UnstyledButton } from "@mantine/core"; +import { useNavigate } from "react-router"; +import type { InstanceData } from "Types/Experiment/Experiment"; +import { IconCheck, IconTrash } from "@tabler/icons-react"; +import type { MouseEvent } from "react"; +import { IframePlugin } from "Api/PluginLoader/PluginLoader"; +import "./TaskListCard.css"; +import { deleteInstance } from "Api/QuantumBackend/ExperimentsManagment"; +import { useExperimentStore } from "Stores/ExperimentStore"; +import { notifications } from "@mantine/notifications"; + +function InstancesListCard(props: { instance: InstanceData; plugin: string }) { + const navigate = useNavigate(); + const { removeInstance } = useExperimentStore(); + + const getStatusColor = (status: string) => { + const statusLower = status.toLowerCase(); + + switch (statusLower) { + case "draft": + return "var(--mantine-color-gray-5)"; + case "in queue": + return "var(--mantine-color-blue-5)"; + case "processing": + case "running": + return "var(--mantine-color-yellow-5)"; + case "complete": + case "completed": + return "var(--mantine-color-green-5)"; + case "error": + case "failed": + return "var(--mantine-color-red-5)"; + default: + return "var(--mantine-color-gray-5)"; + } + }; + + const handleDelete = () => { + deleteInstance(props.instance.instance_id).then(() => { + removeInstance(props.instance.instance_id); + notifications.show({ + radius: "md", + title: "Задача удалена успешно", + message: "", + icon: , + style: { paddingLeft: "5px" }, + }); + }); + }; + return ( + { + console.log("A"); + ev.stopPropagation(); + navigate(props.instance.instance_id.toString()); + }} + > + + + Задача: {props.instance.name} + +
+ Статус: + + + {props.instance.simulation_result?.status || "DRAFT"} + + +
+
+ ВС: + + + {props.instance.simulation_result?.comp_system?.system_name || + "--"} + + +
+
+ + {JSON.stringify(props.instance.instance_data) && ( + + )} + + +
+
+ Время начала: + {props.instance.simulation_result ? ( + <> + + {new Date( + props.instance.simulation_result?.started_at + "Z", + ).toLocaleDateString("ru")}{" "} + + + {new Date( + props.instance.simulation_result?.started_at + "Z", + ).toLocaleTimeString("ru")} + + + ) : ( + -- + )} +
+
+ Время окончания: + {props.instance.simulation_result?.ended_at ? ( + <> + + {new Date( + props.instance.simulation_result?.ended_at + "Z", + ).toLocaleDateString("ru")}{" "} + + + {new Date( + props.instance.simulation_result?.ended_at + "Z", + ).toLocaleTimeString("ru")} + + + ) : ( + -- + )} +
+
+
+ { + e.stopPropagation(); + handleDelete(); + }} + style={{ cursor: "pointer" }} + > + + + + + #{props.instance.instance_id} + +
+
+
+ ); +} + +export default InstancesListCard; diff --git a/src/Components/ListCard/TeamListCard/TeamInMachineCard/TeamInMachine.tsx b/src/Components/ListCard/TeamListCard/TeamInMachineCard/TeamInMachine.tsx index 1c15467..8adea4e 100644 --- a/src/Components/ListCard/TeamListCard/TeamInMachineCard/TeamInMachine.tsx +++ b/src/Components/ListCard/TeamListCard/TeamInMachineCard/TeamInMachine.tsx @@ -1,16 +1,26 @@ -import { Card, Text, UnstyledButton } from "@mantine/core"; -import { IconCancel, IconCheck, IconTrash } from "@tabler/icons-react"; -import type { MouseEvent } from "react"; +import { Card, NumberInput, Text, UnstyledButton } from "@mantine/core"; +import { + IconCancel, + IconCheck, + IconPencil, + IconTrash, +} from "@tabler/icons-react"; +import { useState, type MouseEvent } from "react"; import { notifications } from "@mantine/notifications"; import type { SystemTeamData, SystemWithTeams } from "Types/Machine/Machine"; -import { removeSystemFromTeam } from "Api/QuantumBackend/MachineManagment"; +import { + giveSystemToTeam, + removeSystemFromTeam, +} from "Api/QuantumBackend/MachineManagment"; import { useDeviceStore } from "Stores/DeviceStore"; function TeamInMachineListCard(props: { team: SystemTeamData; system: SystemWithTeams; }) { + const [isEditing, setIsEditing] = useState(false); const { updateDevice } = useDeviceStore(); + const [count, setCount] = useState(props.team.num_qubits); const handleRemovePerm = () => { // TODO: fix delete @@ -76,7 +86,69 @@ function TeamInMachineListCard(props: { flexDirection: "row", }} > - Количество кубит: {props.team.num_qubits} + {isEditing && ( + <> + Количество кубит: + { + setCount(Number(ev.valueOf())); + }} + > + { + giveSystemToTeam({ + system_id: props.system.system.id, + team_id: props.team.team.team_id, + qubits_given: count, + }).then(() => { + const team = props.system.teams.find((t) => { + return t.team.team_id == props.team.team.team_id; + }); + if (team) { + const updatedItem = { ...team, num_qubits: count }; + + // Create new array with the updated item in the same position + const updatedItems = props.system.teams.map( + (currentItem) => + currentItem.team.team_id === team.team.team_id + ? updatedItem + : currentItem, + ); + + updateDevice(props.system.system.id, { + teams: updatedItems, + }); + } + + setIsEditing(false); + }); + }} + > + + + { + setCount(props.team.num_qubits); + setIsEditing(false); + }} + > + + + + )} + {!isEditing && ( + <> + Количество кубит: {count} + { + setIsEditing(true); + }} + > + + + + )} { - notifications.show({ - radius: "md", - title: "Пользователь удален успешно", - message: "", - icon: , - style: { paddingLeft: "5px" }, + }) + .then(() => { + notifications.show({ + radius: "md", + title: "Пользователь удален успешно", + message: "", + icon: , + style: { paddingLeft: "5px" }, + }); + updateTeam(props.cur_team.id, { + members: [ + ...props.cur_team.members.filter( + (member) => + member.user.keycloak_id != + props.member.user.keycloak_id, + ), + ], + }); + }) + .catch(() => { + notifications.show({ + radius: "md", + title: "Пользователя не получилось удалить", + message: "", + color: "red", + icon: , + style: { paddingLeft: "5px" }, + }); }); - updateTeam(props.cur_team.id, { - members: [ - ...props.cur_team.members.filter( - (member) => - member.user.keycloak_id != props.member.user.keycloak_id, - ), - ], - }); - }); }} style={{ cursor: "pointer", diff --git a/src/Modals/AddTeamToDevice/AddTeamToDevice.tsx b/src/Modals/AddTeamToDevice/AddTeamToDevice.tsx index 4524d5c..a383d53 100644 --- a/src/Modals/AddTeamToDevice/AddTeamToDevice.tsx +++ b/src/Modals/AddTeamToDevice/AddTeamToDevice.tsx @@ -11,6 +11,8 @@ import CustomButton from "Components/CustomButton/CustomButton"; import { useDeviceStore } from "Stores/DeviceStore"; import type { SystemWithTeams } from "Types/Machine/Machine"; import { giveSystemToTeam } from "Api/QuantumBackend/MachineManagment"; +import { notifications } from "@mantine/notifications"; +import { IconCancel } from "@tabler/icons-react"; interface AddTeamToDeviceProps { isOpened: boolean; @@ -20,7 +22,7 @@ interface AddTeamToDeviceProps { } export function AddTeamToDevice(props: AddTeamToDeviceProps) { - const [numQubits, setNumQubits] = useState(0); + const [numQubits, setNumQubits] = useState(1); const { updateDevice } = useDeviceStore(); const [selectedTeam, setSelectedTeam] = useState<{ label: string; @@ -29,7 +31,7 @@ export function AddTeamToDevice(props: AddTeamToDeviceProps) { //reset on open dialog useEffect(() => { if (props.isOpened) { - setNumQubits(0); + setNumQubits(1); setSelectedTeam(undefined); } }, [props.isOpened]); @@ -63,14 +65,23 @@ export function AddTeamToDevice(props: AddTeamToDeviceProps) { }); props.setIsOpened(false); }) - .catch(() => {}); + .catch(() => { + notifications.show({ + radius: "md", + title: "Не удалось предоставить доступ", + message: "", + color: "red", + icon: , + style: { paddingLeft: "5px" }, + }); + }); }; return ( Добавить члена команды + title=Предоставить команде права на ВС centered size="75%" styles={{ @@ -98,11 +109,13 @@ export function AddTeamToDevice(props: AddTeamToDeviceProps) { /> { setNumQubits(Number(event.valueOf())); }} + max={props.device.system.max_qubits} + min={1} >
diff --git a/src/Modals/NewExperiment/NewExperiment.tsx b/src/Modals/NewExperiment/NewExperiment.tsx index 6d58822..c494da5 100755 --- a/src/Modals/NewExperiment/NewExperiment.tsx +++ b/src/Modals/NewExperiment/NewExperiment.tsx @@ -9,23 +9,33 @@ import { } from "@mantine/core"; import { useEffect, useState } from "react"; import "./NewExperiment.css"; -import { useExperimentStore } from "Stores/ExperimentStore"; +//import { useExperimentStore } from "Stores/ExperimentStore"; import CustomButton from "Components/CustomButton/CustomButton"; +import { createExperiment } from "Api/QuantumBackend/ExperimentsManagment"; +import { useExperimentStore } from "Stores/ExperimentStore"; interface NewExperimentModalProps { isOpened: boolean; setIsOpened: (opened: boolean) => void; teams: { team_id: number; team_name: string }[] | undefined; + types: { type_id: number; type_name: string }[] | undefined; } export function NewExperimentModal(props: NewExperimentModalProps) { const [name, setName] = useState(""); const [description, setDescription] = useState(""); - const { addExperiment } = useExperimentStore(); + //const { addExperiment } = useExperimentStore(); const [selectedTeam, setSelectedTeam] = useState<{ label: string; value: string; }>(); + + const [selectedType, setSelectedType] = useState<{ + label: string; + value: string; + }>(); + + const { addExperiment } = useExperimentStore(); //reset on open dialog useEffect(() => { if (props.isOpened) { @@ -41,18 +51,16 @@ export function NewExperimentModal(props: NewExperimentModalProps) { const handleCreateExperiment = () => { //TODO: add logic for backend server - if (selectedTeam) { - addExperiment({ - id: 1, + if (selectedTeam && selectedType) { + createExperiment({ + team_id: Number(selectedTeam.value), name: name, description: description, - team_id: Number(selectedTeam?.value), - tasks_ids: [1, 2], - date_created: new Date(), - experiment_status: "PROCESSING", - experiment_type: "VQE", + experiment_type_id: Number(selectedType.value), + }).then((exp) => { + addExperiment(exp); + props.setIsOpened(false); }); - props.setIsOpened(false); } }; @@ -109,9 +117,32 @@ export function NewExperimentModal(props: NewExperimentModalProps) { onChange={(_value, option) => setSelectedTeam(option)} /> + + +
+ + + { + props.setIsOpened(false); + }} + text="Отменить" + > +
+ + ); +} diff --git a/src/Pages/DevicesPage/DevicePage/DevicePage.tsx b/src/Pages/DevicesPage/DevicePage/DevicePage.tsx index faf1011..028a23e 100644 --- a/src/Pages/DevicesPage/DevicePage/DevicePage.tsx +++ b/src/Pages/DevicesPage/DevicePage/DevicePage.tsx @@ -21,6 +21,12 @@ import TeamInMachineListCard from "Components/ListCard/TeamListCard/TeamInMachin import { AddTeamToDevice } from "Modals/AddTeamToDevice/AddTeamToDevice"; import { getShortTeamsList } from "Api/QuantumBackend/TeamManagement"; +const colors = { + ONLINE: "var(--mantine-color-green-7)", + OFFLINE: "var(--mantine-color-red-7)", + BUSY: "var(--mantine-color-yellow-5)", +}; + function DevicePage() { const [isOpen, setIsOpen] = useState(false); const { device_id } = useParams(); @@ -107,7 +113,16 @@ function DevicePage() {
Статус: - + {cur_device?.system.status}
diff --git a/src/Pages/DocumentationPage/DocumentationPage.tsx b/src/Pages/DocumentationPage/DocumentationPage.tsx index a65dcb2..2a6e2a6 100755 --- a/src/Pages/DocumentationPage/DocumentationPage.tsx +++ b/src/Pages/DocumentationPage/DocumentationPage.tsx @@ -1,4 +1,13 @@ -import { Box, Divider, TableOfContents, Title } from "@mantine/core"; +import { + Box, + Divider, + TableOfContents, + Title, + Text, + List, + Code, + Anchor, +} from "@mantine/core"; import "./DocumentationPage.css"; import { Helmet } from "react-helmet"; @@ -6,15 +15,19 @@ function DocumentationPage() { return ( <> - Documentation | QMolSim + Документация | QMolSim - Документация + Руководство пользователя + + Автоматизированная система распределенного расчета энергии основного + состояния молекул +
@@ -26,24 +39,291 @@ function DocumentationPage() { minDepthToOffset={0} depthOffset={20} scrollSpyOptions={{ - selector: "section h1, h2", + selector: "section h1, section h2, section h3", }} - className="" getControlProps={({ data }) => ({ onClick: () => data .getNode() - .scrollIntoView({ behavior: "smooth", block: "center" }), + .scrollIntoView({ behavior: "smooth", block: "start" }), children: data.value, })} />
-
+ {/* ================= 1 ВВЕДЕНИЕ ================= */} +
1. Введение + + + 1.1 Область применения + + Требования настоящего документа применяются при: + + предварительных комплексных испытаниях; + опытной эксплуатации; + приемочных испытаниях; + промышленной эксплуатации. + + + + 1.2 Краткое описание возможностей + + + Автоматизированная система распределенного расчета энергии + основного состояния молекул с помощью квантовых алгоритмов + представляет собой распределенный веб-сервис, предназначенный для + выполнения ресурсоемких квантово-химических расчетов с + использованием гибридной архитектуры, состоящей из центрального + сервера и распределенных квантовых симуляторов. + + + + Управление командной работой + + + Пользователи могут создавать команды, приглашать других + исследователей, назначать права доступа. + + + + Подключение вычислительных систем + + + Исследователи регистрируют в системе свои вычислительные узлы. Для + каждого узла исследователь задает максимальное количество кубитов, + а также предоставляет доступ командам на использование устройства. + + + + Создание и запуск экспериментов + + + В рамках команды пользователь создает эксперимент (набор задач для + разных молекул). Для каждой задачи загружается или редактируется + структура молекулы в формате XYZ, задаются квантово-химические + параметры. + + + + Распределенные вычисления VQE + + + При запуске эксперимента система автоматически распределяет задачи + по доступным вычислительным узлам с учетом их ограничений по числу + кубит. В процессе расчета на сервер передаются промежуточные + результаты. + + + + Отказоустойчивость и восстановление + + + Каждый вычислительный узел каждые 5 секунд отправляет сигнал о + своей работоспособности. При выходе узла из строя незавершенная + задача автоматически перенаправляется в очередь и назначается на + другой узел с сохранением промежуточных весов оптимизации. + + + + Визуализация молекул + + + Для каждой задачи доступна интерактивная 3D-визуализация молекулы + в шаростержневой модели. + + + + Импорт молекулярных данных + + + Система поддерживает преобразование молекул в требуемый формат из + большинства существующих химических форматов. +
-
- 1.1 Быстрое начало + + {/* ================= 2 НАЗНАЧЕНИЕ И УСЛОВИЯ ================= */} +
+ 2. Назначение и условия применения + + + 2.1 Назначение системы + + + Система предназначена для автоматизированного распределенного + расчета энергии основного состояния молекул с использованием + квантового алгоритма VQE. Она обеспечивает создание команд + исследователей с настройкой прав доступа, автоматическое + распределение вычислительных задач между доступными узлами, + мониторинг состояния вычислений и восстановление прогресса расчета + при сбое отдельных вычислительных систем. Применение системы + позволяет повысить скорость проведения квантово-химических + расчетов и снизить нагрузку на пользователя по управлению + вычислительным процессом. Система ориентирована на специалистов в + области квантовой химии и вычислительных технологий. + + + + 2.2 Требования к техническим средствам + + + + Клиент-браузер: + + + Оперативная память от 4 Гб; + Свободное пространство на диске от 2 Гб; + Процессор 4-ядерный с частотой от 2 ГГц; + Скорость подключения в интернет от 50 Мб/c; + + Наличие манипулятора "мышь" или аналогичного устройства для + взаимодействия с интерфейсом; + + Наличие Клавиатуры. + + + + Клиент-ВС: + + + Оперативная память от 8 Гб; + Свободное пространство на диске от 5 Гб; + + Процессор 8-ядерный с частотой от 2-4,4 ГГц; + + Скорость подключения в интернет от 50 Мб/с; + + Наличие манипулятора "мышь" или аналогичного устройства для + взаимодействия с интерфейсом; + + Наличие Клавиатуры. + + + + 2.3 Требования к программным средствам + + + + Клиент-браузер: + + + Браузер (Safari 18.1.1, Яндекс Браузер 25.2.1, Google Chrome + 110.0.5481.100) + + + + Клиент-ВС: + + + ОС Windows 10, Windows 11, MacOS, Linux + + Браузер (Safari 18.1.1, Яндекс Браузер 25.2.1, Google Chrome + 110.0.5481.100) + + Docker + Docker-compose v2 + + + + 3. Условия выполнения программы + + + Для работы системы требуется веб-браузер, поддерживающий + современные функции JavaScript (Google Chrome версии 110 и выше, + Яндекс Браузер версии 25.2.1 и выше, Safari версии 18.1.1 и выше). + Доступ к системе осуществляется через веб-интерфейс по адресу, + предоставленному администратором. Для работы вычислительных узлов + дополнительно требуется установленный Docker и Docker Compose v2 + на каждой подключаемой вычислительной системе. Необходимо наличие + постоянного сетевого подключения к серверу для всех + взаимодействующих компонентов системы. + +
+ + {/* ================= 4 ВЫПОЛНЕНИЕ ПРОГРАММЫ ================= */} +
+ 4. Выполнение программы + + + 4.1 Инсталяция/деинсталяция + + + Клиент-браузер инсталляции и деинсталляции не требуется, для + работы необходимо только наличие на системе совместимого браузера. + + + + Для инсталляции клиента-ВС: + + + + На системе необходимо наличие docker и docker-compose v2 + + + Необходимо скачать контейнер с помощью команды:{" "} + docker pull git.deowl.ru/vkrb/client:0.1.0 + + + Затем скачать файл docker-compose с помощью команды:{" "} + + curl -O + "https://git.deowl.ru/vkrb/local_quantum_simulator/raw/branch/main/docker-compose.yml" + + + + Наконец, в той же папке необходимо создать файл переменных среды + с названием ".env" и содержимым: + + {`PORT=5001 +STORAGE_PATH="/storage" +RABBITMQ_HOST=rabbit.deowl.ru +RABBITMQ_PORT=5672 +KEYCLOAK_URL=https://quantum-auth.deowl.ru +KEYCLOAK_REALM_NAME=quant_sim-realm +KEACLOAK_CLIENT_ID=local_quantum_sim +QUANTUM_BACKEND_URL=https://quantum.deowl.ru`} + + + + + + Для деинсталляции клиента-ВС: + + + + Удаляем файлы «docker-compose.yml», «.env» и папку + «localStorage» (при ее наличие) + + + Удаляем установленное изображение с помощью команды:{" "} + docker image rm git.deowl.ru/vkrb/client:0.1.0 + + + + + 4.2 Запуск / Остановка программы + + + Клиент-браузер может быть открыт по ссылке:{" "} + + http://quantum.deowl.ru/ + + + + Для запуска клиента-ВС необходимо выполнить команду, находясь в + папке с файлом «docker-compose.yml»:{" "} + docker compose up --d + + + Для остановки клиента-ВС: docker compose down + + + Для первичного подключения и мониторинга статуса клиента-ВС + необходимо открыть ссылку:{" "} + + http://localhost:5001/ + +
diff --git a/src/Pages/ExperimentsPage/ExperimentPage.css b/src/Pages/ExperimentsPage/ExperimentPage.css index cf10508..e17751b 100644 --- a/src/Pages/ExperimentsPage/ExperimentPage.css +++ b/src/Pages/ExperimentsPage/ExperimentPage.css @@ -2,15 +2,19 @@ flex-grow: 1; display: flex; flex-direction: column; + position: relative; } .experimentButtons { display: flex; - gap: 20px; + flex-direction: row; - justify-content: right; - width: fit-content; - margin-left: auto; + justify-content: space-between; + + width: 100%; flex-wrap: nowrap; text-wrap: nowrap; + * { + max-width: 200px; + } } diff --git a/src/Pages/ExperimentsPage/ExperimentPage.tsx b/src/Pages/ExperimentsPage/ExperimentPage.tsx index 8c3a2ab..3b89fb1 100644 --- a/src/Pages/ExperimentsPage/ExperimentPage.tsx +++ b/src/Pages/ExperimentsPage/ExperimentPage.tsx @@ -1,22 +1,221 @@ -import { Alert, Center, Text } from "@mantine/core"; +import { + Alert, + Center, + LoadingOverlay, + Text, + UnstyledButton, + SimpleGrid, + Title, + TextInput, + Button, + Group, + Card, + Badge, + Collapse, + ActionIcon, +} from "@mantine/core"; +import { notifications } from "@mantine/notifications"; import "./ExperimentPage.css"; import { PaginationContainer } from "Components/PaginationContainer/PaginationContainer"; -//import { NewMoleculeModal } from "Modals/NewMolecule/NewMolecule"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import { Helmet } from "react-helmet"; -import { IconPlus, IconSettings } from "@tabler/icons-react"; +import { + IconPlus, + IconReload, + IconEdit, + IconX, + IconCheck, + IconChevronUp, + IconChevronDown, + IconCancel, +} from "@tabler/icons-react"; import { useParams } from "react-router"; import CustomButton from "Components/CustomButton/CustomButton"; import { useExperimentStore } from "Stores/ExperimentStore"; +import type { ExperimentData, InstanceData } from "Types/Experiment/Experiment"; +import { useAuthenticationStore } from "Stores/AuthenticationStore"; +import { + getExperimentById, + getExperimentInstances, + getFrontendFile, + startExperiment, + updateExperiment, +} from "Api/QuantumBackend/ExperimentsManagment"; +import InstancesListCard from "Components/ListCard/TaskListCard/TaskListCard"; +import { NewInstanceModal } from "Modals/NewTask/NewTask"; function ExperimentPage() { - const [isOpen, setIsOpen] = useState(false); + const [isOpened, setIsOpen] = useState(false); const { experiment_id } = useParams(); - const { experiments, addTask } = useExperimentStore(); + const { experiments, addExperiment, instances, setInstances } = + useExperimentStore(); + const [experiment, set_experiment] = useState( + experiments.find((exp) => { + return exp.id == Number(experiment_id); + }), + ); - const experiment = experiments.find((exp) => { - return exp.id == Number(experiment_id); - }); + const { profile, is_loading } = useAuthenticationStore(); + const [is_loading_, set_is_loading] = useState(true); + const [isEditing, setIsEditing] = useState(false); + const [editedName, setEditedName] = useState(""); + const [editedDesc, setEditedDesc] = useState(""); + const [isExpanded, setIsExpanded] = useState(false); + + const { loadedHtmlFiles, addLoadedHtml } = useExperimentStore(); + + const [total_experiment_inst, set_total_experiment_inst] = + useState(0); + const [page_size, set_page_size] = useState(5); + const [cur_page, set_cur_page] = useState(1); + + const saveExperimentDetails = () => { + if (experiment && editedName.trim()) { + updateExperiment({ + experiment_id: experiment.id, + name: editedName, + description: editedDesc, + }) + .then((updated) => { + const updatedExperiment = { + ...experiment, + name: updated.name, + description: updated.description, + }; + addExperiment(updatedExperiment); + set_experiment(updatedExperiment); + setIsEditing(false); + notifications.show({ + title: "Успех", + message: "Информация об эксперименте обновлена", + color: "green", + }); + }) + .catch(() => { + notifications.show({ + title: "Ошибка", + message: "Не удалось обновить информацию", + color: "red", + }); + }); + } + }; + + const cancelEditing = () => { + if (experiment) { + setEditedName(experiment.name); + setEditedDesc(experiment.description || ""); + setIsEditing(false); + } + }; + + const startEditing = () => { + if (experiment) { + setEditedName(experiment.name); + setEditedDesc(experiment.description || ""); + setIsEditing(true); + } + }; + + const handleExperimentStart = () => { + if (experiment) { + startExperiment({ experiment_id: experiment.id }) + .then(() => { + getExperimentById(Number(experiment_id)) + .then((exp) => { + addExperiment(exp); + set_experiment(exp); + getExperimentInstances(exp.id, cur_page, page_size) + .then((inst) => { + setInstances(inst.instances); + set_cur_page(inst.cur_page); + set_total_experiment_inst(inst.total_instances); + set_page_size(inst.page_size); + set_is_loading(false); + }) + .catch(() => { + set_is_loading(false); + }); + getFrontendFile(exp.experiment_type.id).then((file) => { + addLoadedHtml(exp.experiment_type.id, file); + }); + }) + .catch(() => { + set_is_loading(false); + }); + }) + .catch(() => { + notifications.show({ + title: "Ошибка", + message: + "Не удалось начать эксперимент, проверьте наличие задач и их правильность", + color: "red", + icon: , + }); + }); + } + }; + + useEffect(() => { + if (profile && !is_loading) { + if (!experiment) { + getExperimentById(Number(experiment_id)) + .then((exp) => { + addExperiment(exp); + set_experiment(exp); + getExperimentInstances(exp.id, cur_page, page_size) + .then((inst) => { + setInstances(inst.instances); + set_cur_page(inst.cur_page); + set_total_experiment_inst(inst.total_instances); + set_page_size(inst.page_size); + set_is_loading(false); + }) + .catch(() => { + set_is_loading(false); + }); + getFrontendFile(exp.experiment_type.id).then((file) => { + addLoadedHtml(exp.experiment_type.id, file); + }); + }) + .catch(() => { + set_is_loading(false); + }); + } else { + if ( + (instances.length == 0 && cur_page != 0) || + (total_experiment_inst > page_size && + instances.length != page_size && + cur_page != Math.ceil(total_experiment_inst / page_size)) + ) { + getExperimentInstances( + experiment.id, + Math.min(cur_page, Math.ceil(total_experiment_inst / page_size)), + page_size, + ) + .then((inst) => { + if (inst.instances.length == 0) { + set_cur_page(0); + setInstances([]); + } else { + setInstances(inst.instances); + set_cur_page(inst.cur_page); + set_total_experiment_inst(inst.total_instances); + set_page_size(inst.page_size); + } + + set_is_loading(false); + }) + .catch(() => { + set_is_loading(false); + }); + getFrontendFile(experiment.experiment_type.id).then((file) => { + addLoadedHtml(experiment.experiment_type.id, file); + }); + } + } + } + }, [profile, is_loading, instances]); return ( <> @@ -24,82 +223,350 @@ function ExperimentPage() { {experiment ? "Experiment " + experiment.id + " | QMolSim" - : "Error |QmolSim"} + : "Error | QMolSim"} - {experiment && ( -
-
-
- { - addTask(Number(experiment_id) || 0, { - id: 1, - name: "Задача 1", - description: "", - data: {}, - }); - setIsOpen(true); - }} - icon={} - text="Добавить задачу" - /> - { - setIsOpen(true); - }} - icon={} - text="Параметры эксперимента" - /> -
-
- {experiment.tasks_ids.length > 0 && ( - {}} +
+ {experiment && ( + + )} + + {experiment && ( + <> +
- {experiment.tasks_ids.map((task: number) => { - return
{task}
; - })} - - )} - {experiment.tasks_ids.length == 0 && ( - -
- - Нет задач - -
-
- )} -
- )} - {!experiment && ( - -
- {" "} - - Ошибка. Эксперимент не найден - {" "} -
-
- )} +
+ {experiment.status == "DRAFT" && ( + <> + { + handleExperimentStart(); + }} + icon={} + text="Начать эксперимент" + /> + { + setIsOpen(true); + }} + icon={} + text="Добавить задачу" + /> + + )} + {experiment.status != "DRAFT" && ( + { + getExperimentById(Number(experiment_id)) + .then((exp) => { + addExperiment(exp); + set_experiment(exp); + getExperimentInstances(exp.id) + .then((inst) => { + setInstances(inst.instances); + set_is_loading(false); + }) + .catch(() => { + set_is_loading(false); + }); + getFrontendFile(exp.experiment_type.id).then( + (file) => { + addLoadedHtml(exp.experiment_type.id, file); + }, + ); + }) + .catch(() => { + set_is_loading(false); + }); + }} + > + + + )} +
+
+ + {/* Experiment Information Card */} + {/* Experiment Information Card */} + + + + + setIsExpanded(!isExpanded)} + > + {isExpanded ? ( + + ) : ( + + )} + + Информация об эксперименте + + {!isEditing ? ( + + ) : ( + + + + + )} + + + + + + {!isEditing ? ( + +
+ + ID + + + {experiment.id} + +
+
+ + Тип + + + {experiment.experiment_type.name} + +
+
+ + Название + + + {experiment.name} + +
+
+ + Статус + + + {experiment.status} + +
+
+ + Команда + + + {experiment.team.team_name} + +
+
+ + Количество задач + + + {experiment.instances_count} + +
+
+ + Описание + + + {experiment.description || "Нет описания"} + +
+
+ + Создан + + + {new Date(experiment.created_at).toLocaleString()} + +
+
+ ) : ( + +
+ + Название + + setEditedName(e.target.value)} + placeholder="Введите название" + /> +
+
+ + Описание + + setEditedDesc(e.target.value)} + placeholder="Введите описание" + /> +
+
+ + ID + + + {experiment.id} + +
+
+ + Тип + + + {experiment.experiment_type.name} + +
+
+ + Команда + + + {experiment.team.team_name} + +
+
+ + Статус + + + {experiment.status} + +
+
+ )} +
+
+
+ + {!is_loading_ && ( + { + getExperimentInstances(experiment.id, page_num, page_size) + .then((inst) => { + setInstances(inst.instances); + set_cur_page(inst.cur_page); + set_total_experiment_inst(inst.total_instances); + set_page_size(inst.page_size); + set_is_loading(false); + }) + .catch(() => { + set_is_loading(false); + }); + }} + > + {instances && + loadedHtmlFiles.get(experiment.experiment_type.id) && + instances.map((inst: InstanceData) => { + return ( + + ); + })} + {instances.length == 0 && !is_loading && ( + +
+ + Нет задач + +
+
+ )} +
+ )} + + )} + {!experiment && !is_loading_ && ( + +
+ {" "} + + Ошибка. Эксперимент не найден + {" "} +
+
+ )} +
); } diff --git a/src/Pages/ExperimentsPage/ExperimentsPage.css b/src/Pages/ExperimentsPage/ExperimentsPage.css index 2c63e84..a9244f4 100644 --- a/src/Pages/ExperimentsPage/ExperimentsPage.css +++ b/src/Pages/ExperimentsPage/ExperimentsPage.css @@ -2,6 +2,8 @@ flex-grow: 1; display: flex; flex-direction: column; + position: relative; + gap: 15px; } .experimentsButtons { diff --git a/src/Pages/ExperimentsPage/ExperimentsPage.tsx b/src/Pages/ExperimentsPage/ExperimentsPage.tsx index faff927..0f59a9d 100644 --- a/src/Pages/ExperimentsPage/ExperimentsPage.tsx +++ b/src/Pages/ExperimentsPage/ExperimentsPage.tsx @@ -6,23 +6,39 @@ import { IconMicroscope } from "@tabler/icons-react"; import { NewExperimentModal } from "Modals/NewExperiment/NewExperiment"; import ExperimentsListCard from "Components/ListCard/ExperimentsListCard"; import { useExperimentStore } from "Stores/ExperimentStore"; -import type { Experiment } from "Types/Experiment/Experiment"; import CustomButton from "Components/CustomButton/CustomButton"; import { Alert } from "@mantine/core"; import { getShortTeamsList } from "Api/QuantumBackend/TeamManagement"; import { useAuthenticationStore } from "Stores/AuthenticationStore"; +import { + type ExperimentData, + type ExperimentTypeList, +} from "Types/Experiment/Experiment"; +import { + getExperimentTypes, + getUserExperiments, +} from "Api/QuantumBackend/ExperimentsManagment"; function ExperimentsPage() { const [isOpen, setIsOpen] = useState(false); - const { experiments } = useExperimentStore(); + const { experiments, setExperiments, setInstances } = useExperimentStore(); const [teams, setTeams] = useState<{ team_id: number; team_name: string }[]>(); const { profile, is_loading } = useAuthenticationStore(); + const [is_loading_, set_is_loading] = useState(true); + const [exp_types, set_exp_types] = useState([]); + const [total_experiments, set_total_experiments] = useState(0); + const [page_size, set_page_size] = useState(6); + const [cur_page, set_cur_page] = useState(1); useEffect(() => { - if (profile || !is_loading) - getShortTeamsList() + if (profile && !is_loading) { + setInstances([]); + getExperimentTypes().then((exp_types) => { + set_exp_types(exp_types); + }); + getShortTeamsList({}) .then((teams) => { if (teams) { setTeams(teams); @@ -31,6 +47,18 @@ function ExperimentsPage() { .catch(() => { setTeams([]); }); + getUserExperiments({ page_num: cur_page, page_size: page_size }) + .then((expData) => { + setExperiments(expData.experiments); + set_total_experiments(expData.total_experiments); + set_page_size(expData.page_size); + set_cur_page(expData.cur_page); + set_is_loading(false); + }) + .catch(() => { + set_is_loading(false); + }); + } }, [profile, is_loading]); return ( @@ -46,64 +74,59 @@ function ExperimentsPage() { isOpened={isOpen} setIsOpened={setIsOpen} teams={teams} + types={exp_types.map((type) => { + return { type_id: type.id, type_name: type.name }; + })} />
-
- {teams && teams.length > 0 && ( -
- { - setIsOpen(true); - }} - icon={} - text="Создать эксперимент" - /> -
- )} +
+ { + setIsOpen(true); + }} + icon={} + text="Создать эксперимент" + /> +
+
+ { + getUserExperiments({ page_num: page_num, page_size: page_size }) + .then((expData) => { + setExperiments(expData.experiments); + set_total_experiments(expData.total_experiments); + set_page_size(expData.page_size); + set_cur_page(expData.cur_page); + set_is_loading(false); + }) + .catch(() => { + set_is_loading(false); + }); + }} + > + {experiments.map((exp: ExperimentData) => { + return ; + })} + {teams && teams.length == 0 && ( + + Создайте или войдите в команду чтобы начать работу с + экспериментами + + )} + {teams && + teams.length != 0 && + experiments.length == 0 && + !is_loading_ && ( + + Вы еще не создали не один эксперимент + + )} +
- {}} - > - {experiments.map((exp: Experiment) => { - return ( - team.team_id == exp.team_id)} - /> - ); - })} - {teams && teams.length == 0 && ( - - Создайте или войдите в команду чтобы начать работу с - экспериментами - - )} - {teams && teams.length != 0 && experiments.length == 0 && ( - - Вы еще не создали не один эксперимент - - )} -
); diff --git a/src/Pages/TaskPage/TaskPage.css b/src/Pages/TaskPage/TaskPage.css new file mode 100644 index 0000000..ca81b0c --- /dev/null +++ b/src/Pages/TaskPage/TaskPage.css @@ -0,0 +1,13 @@ +.taskButtons { + display: flex; + + flex-direction: row; + justify-content: right; + gap: 15px; + width: 100%; + flex-wrap: nowrap; + text-wrap: nowrap; + * { + max-width: 200px; + } +} diff --git a/src/Pages/TaskPage/TaskPage.tsx b/src/Pages/TaskPage/TaskPage.tsx index d114112..cd134f1 100644 --- a/src/Pages/TaskPage/TaskPage.tsx +++ b/src/Pages/TaskPage/TaskPage.tsx @@ -1,13 +1,100 @@ import { Helmet } from "react-helmet"; -import { IconCancel, IconPlus, IconSettings } from "@tabler/icons-react"; +import { IconCancel, IconPlus } from "@tabler/icons-react"; import { useParams } from "react-router"; import CustomButton from "Components/CustomButton/CustomButton"; import { IframePlugin } from "Api/PluginLoader/PluginLoader"; -import { useState } from "react"; +import { useEffect, useState } from "react"; +import type { InstanceData } from "Types/Experiment/Experiment"; +import { + getFrontendFile, + getInstanceById, + updateInstance, +} from "Api/QuantumBackend/ExperimentsManagment"; +import { useAuthenticationStore } from "Stores/AuthenticationStore"; +import { useExperimentStore } from "Stores/ExperimentStore"; +import { SimpleGrid, TextInput, Title } from "@mantine/core"; +import { notifications } from "@mantine/notifications"; +import "./TaskPage.css"; function TaskPage() { const { task_id } = useParams(); - const [data, setData] = useState<{ text: string }>({ text: "" }); + + const [instance, set_instance] = useState(); + const { loadedHtmlFiles, addLoadedHtml, setInstances } = useExperimentStore(); + const { profile, is_loading } = useAuthenticationStore(); + const [, set_is_loading] = useState(true); + const [data, setData] = useState(); + const [progress, setProgress] = useState(); + const [qubits_needed, set_qubits_needed] = useState(); + + const [name, setName] = useState(); + const [descr, setDescr] = useState(""); + const [reload, setReload] = useState(0); + + const saveData = () => { + if (instance && qubits_needed) { + updateInstance({ + instance_id: instance.instance_id, + name: name, + description: descr, + instance_data: JSON.stringify(data), + qubits_needed: qubits_needed, + }).then((updated) => { + set_instance({ + instance_id: instance.instance_id, + name: updated.name, + description: updated.description, + instance_data: updated.instance_data, + qubits_needed: updated.qubits_needed, + simulation_result: instance.simulation_result, + }); + setName(updated.name); + setDescr(updated.description || ""); + setData(updated.instance_data); + }); + } else { + if (!qubits_needed) { + notifications.show({ + message: "Количество кубит не было определено", + }); + } + } + }; + + const ResetData = () => { + if (instance) { + setData(instance.instance_data); + setName(instance.name); + setDescr(instance.description || ""); + setReload(reload + 1); + } + }; + + useEffect(() => { + if (profile && !is_loading && task_id) { + getInstanceById(Number(task_id)) + .then((inst) => { + set_instance(inst); + setData(inst.instance_data); + setProgress(inst.simulation_result?.simulation_result); + setName(inst.name); + setDescr(inst.description || ""); + set_qubits_needed(inst.qubits_needed); + getFrontendFile(1).then((file) => { + addLoadedHtml(1, file); + }); + set_is_loading(false); + }) + .catch(() => { + set_is_loading(false); + }); + } + + return () => { + setInstances([]); + }; + }, [profile, is_loading]); + return ( <> @@ -29,39 +116,77 @@ function TaskPage() { marginBottom: "15px", }} > -
- {}} - icon={} - text="Сохранить" - /> - {}} - icon={} - text="Отменить" - /> + {(!instance?.simulation_result || + instance?.simulation_result?.status == "DRAFT") && ( + <> +
+ } + text="Сохранить" + /> + } + text="Отменить" + disabled={ + JSON.stringify(data) == + JSON.stringify(instance?.instance_data) && + instance?.name == name && + (instance?.description || "") == (descr || "") + } + /> +
+ + )} +
+ + Имя: + { + setName(e.target.value); + } //set_username(e.currentTarget.value) + } + /> + Описание: + { + setDescr(e.target.value); + } //set_email(e.currentTarget.value) + } + /> +
- + {instance && ( + { + setData(JSON.parse(data)); + set_qubits_needed(qubits_need); + }} + index={instance.instance_id + reload} + simProgress={JSON.stringify(progress)} + /> + )}
); diff --git a/src/Pages/TeamsPage/TeamPage/TeamPage.tsx b/src/Pages/TeamsPage/TeamPage/TeamPage.tsx index 1169420..c3c0720 100644 --- a/src/Pages/TeamsPage/TeamPage/TeamPage.tsx +++ b/src/Pages/TeamsPage/TeamPage/TeamPage.tsx @@ -56,7 +56,7 @@ function TeamPage() { addTeam(team); set_cur_team(team); set_team_name(team.name); - set_team_descr(team.description); + set_team_descr(team.description || ""); set_is_loading(false); } }); @@ -66,7 +66,7 @@ function TeamPage() { profile && !is_loading ) { - getTeamSystems(Number(team_id), cur_page, 9 ) + getTeamSystems(Number(team_id), cur_page, 9) .then((systems) => { if (systems) { set_machines(systems); @@ -210,8 +210,8 @@ function TeamPage() { cur_team?.description == team_descr } onClick={() => { - set_team_name(cur_team?.name); - set_team_descr(cur_team?.description); + set_team_name(cur_team?.name || ""); + set_team_descr(cur_team?.description || ""); }} /> @@ -252,7 +252,7 @@ function TeamPage() { })}
)} - {!cur_team && !is_loading && ( + {!cur_team && !is_loading_this && (
{" "} diff --git a/src/Pages/UserPage/UserPage.tsx b/src/Pages/UserPage/UserPage.tsx index c3ed3f8..597182a 100644 --- a/src/Pages/UserPage/UserPage.tsx +++ b/src/Pages/UserPage/UserPage.tsx @@ -7,6 +7,12 @@ import { Switch, LoadingOverlay, Space, + Avatar, + Center, + Grid, + FileInput, + Group, + rem, } from "@mantine/core"; import { IconChartCandle, @@ -15,6 +21,7 @@ import { IconCancel, IconSun, IconMoonStars, + IconUpload, } from "@tabler/icons-react"; import keycloak, { SendEmailVerification, @@ -29,19 +36,22 @@ import { useUserPreferencesStore } from "Stores/PreferencesStore"; import CustomButton from "Components/CustomButton/CustomButton"; import { notifications } from "@mantine/notifications"; import { useSearchParams } from "react-router"; -import { - GetCurrentUserInfo, - UpdateCurrentUserInfo, -} from "Api/QuantumBackend/UserManagement"; -import type { UserData } from "Types/User/User"; +import { UpdateCurrentUserInfo } from "Api/QuantumBackend/UserManagement"; + function UserPage() { - const { profile, is_loading, profile_picture_path } = - useAuthenticationStore(); + const { + profile, + is_loading, + profile_picture_path, + set_profile_picture_path, + } = useAuthenticationStore(); const { theme, set_theme } = useUserPreferencesStore(); const [username, set_username] = useState(""); const [email, set_email] = useState(""); - const [pfp_path, set_pfp_path] = useState(""); const [is_editing_path, set_is_editing_path] = useState(false); + const [selectedFile, setSelectedFile] = useState(null); + const [previewUrl, setPreviewUrl] = useState(null); + const [isUploading, setIsUploading] = useState(false); const [searchParams] = useSearchParams(); const updateData = () => { @@ -53,9 +63,7 @@ function UserPage() { const prof_1 = profile; prof_1.email = email; prof_1.username = username; - useAuthenticationStore.setState({ - is_loading: true, - }); + updateUserData(prof_1).then((data) => { if (data) { notifications.show({ @@ -76,15 +84,81 @@ function UserPage() { } }; + const handleFileChange = (file: File | null) => { + setSelectedFile(file); + if (file) { + // Create preview + const url = URL.createObjectURL(file); + setPreviewUrl(url); + } else { + if (previewUrl) { + URL.revokeObjectURL(previewUrl); + setPreviewUrl(null); + } + } + }; + const update_pfp = async () => { - UpdateCurrentUserInfo(pfp_path).then(() => { - GetCurrentUserInfo().then((info: UserData | undefined) => { - if (info && info.profile_picture_path) - useAuthenticationStore.setState({ - profile_picture_path: info.profile_picture_path, - }); + if (!selectedFile) return; + + setIsUploading(true); + try { + const result = await UpdateCurrentUserInfo(selectedFile); + if (result && result.profile_picture_path) { + try { + const response = await fetch( + `${import.meta.env.VITE_QUANTUM_BACKEND_URL}/user/serve/${profile?.id}`, + { + headers: { + Authorization: `Bearer ${keycloak.token}`, + }, + }, + ); + + if (response.ok) { + const blob = await response.blob(); + const url = URL.createObjectURL(blob); + set_profile_picture_path(url); + } + } catch (error) { + console.error("Failed to load avatar:", error); + } + notifications.show({ + radius: "md", + title: "Фотография профиля обновлена", + message: "", + icon: , + color: "green", + }); + set_is_editing_path(false); + // Clean up + if (previewUrl) { + URL.revokeObjectURL(previewUrl); + setPreviewUrl(null); + } + setSelectedFile(null); + } + } catch { + notifications.show({ + radius: "md", + title: "Ошибка", + message: "Не удалось обновить фотографию профиля", + icon: , + color: "red", }); - }); + } finally { + setIsUploading(false); + set_is_editing_path(false); + } + }; + + const cancelUpload = () => { + set_is_editing_path(false); + setSelectedFile(null); + if (previewUrl) { + URL.revokeObjectURL(previewUrl); + setPreviewUrl(null); + } }; useEffect(() => { @@ -103,10 +177,6 @@ function UserPage() { } }, [profile, searchParams]); - useEffect(() => { - if (profile_picture_path) set_pfp_path(profile_picture_path); - }, [profile_picture_path]); - return ( <> @@ -153,52 +223,126 @@ function UserPage() { overlayProps={{ radius: "sm", blur: 2 }} loaderProps={{ size: 50, type: "dots" }} /> - - Имя пользователя: - set_username(e.currentTarget.value)} - /> - Почта: - set_email(e.currentTarget.value)} - /> - - - { - if (profile && profile.email && profile.username) { - set_username(profile.username); - set_email(profile.email); - } + + +
+
+ Фотография профиля +
+
+
+ + {is_editing_path && ( + } + clearable + style={{ width: "100%" }} + disabled={isUploading} + /> + )} +
+
+ {!is_editing_path ? ( + { + set_is_editing_path(true); + }} + /> + ) : ( + + + + + )} +
+
+ -
+ > + Имя пользователя: + + Почта: + + + + set_username(e.currentTarget.value)} + /> + set_email(e.currentTarget.value)} + /> + + { + if (profile && profile.email && profile.username) { + set_username(profile.username); + set_email(profile.email); + } + }} + disabled={ + !( + profile != null && + ((profile.username != undefined && + profile.username != username) || + (profile.email != undefined && + profile.email != email)) + ) + } + /> + + </div> </SimpleGrid> - <Divider my="lg" /> - <div style={{ display: "flex", flexDirection: "column" }}> - <Title size="lg">Фотография профиля - - - {!is_editing_path ? ( - - ) : ( - set_pfp_path(e.currentTarget.value)} - /> - )} - -
- {!is_editing_path ? ( - { - set_is_editing_path(true); - }} - /> - ) : ( - <> - { - update_pfp(); - set_is_editing_path(false); - }} - /> - - { - set_is_editing_path(false); - set_pfp_path(profile_picture_path); - }} - /> - - )} -
-
+ Тема приложения: + { + navigate(unique_matches[u_match]); + }} > {routes[prop].breadcrumbs(unique_matches[u_match])[i]} - , +
, ); } else { elements.push( diff --git a/src/Routes/Routes.tsx b/src/Routes/Routes.tsx index e3eee38..3b9b678 100755 --- a/src/Routes/Routes.tsx +++ b/src/Routes/Routes.tsx @@ -34,7 +34,7 @@ export const routes: { ], }, TaskPage: { - path: "/experiments/:experiment_id/:molecule_id", + path: "/experiments/:experiment_id/:task_id", breadcrumbs: (path: string) => [ <>Молекула #{path.split("/")[path.split("/").length - 1]}, ], @@ -62,66 +62,63 @@ export const routes: { SettingsPage: { path: "/settings", breadcrumbs: () => [<>Настройки] }, }; -const router = createBrowserRouter( - [ - { - path: "/", - element: , - children: [ - { - path: routes.MainPage.path, - Component: MainPage, - }, - { - path: routes.DocumentationPage.path, - Component: DocumentationPage, - }, - { - path: routes.SettingsPage.path, - Component: UserPage, - }, +const router = createBrowserRouter([ + { + path: "/", + element: , + children: [ + { + path: routes.MainPage.path, + Component: MainPage, + }, + { + path: routes.DocumentationPage.path, + Component: DocumentationPage, + }, + { + path: routes.SettingsPage.path, + Component: UserPage, + }, - { - element: , - children: [ - { - path: routes.ExperimentsPage.path, - Component: ExperimentsPage, - }, - { - path: routes.ExperimentPage.path, - Component: ExperimentPage, - }, - { - path: routes.TaskPage.path, - Component: TaskPage, - }, - { - path: routes.TeamsPage.path, - Component: TeamsPage, - }, - { - path: routes.TeamPage.path, - Component: TeamPage, - }, - { - path: routes.MachinesPage.path, - Component: DevicesPage, - }, - { - path: routes.MachinePage.path, - Component: DevicePage, - }, - ], - }, + { + element: , + children: [ + { + path: routes.ExperimentsPage.path, + Component: ExperimentsPage, + }, + { + path: routes.ExperimentPage.path, + Component: ExperimentPage, + }, + { + path: routes.TaskPage.path, + Component: TaskPage, + }, + { + path: routes.TeamsPage.path, + Component: TeamsPage, + }, + { + path: routes.TeamPage.path, + Component: TeamPage, + }, + { + path: routes.MachinesPage.path, + Component: DevicesPage, + }, + { + path: routes.MachinePage.path, + Component: DevicePage, + }, + ], + }, - { - path: routes.ErrorPage.path, - Component: ErrorPage, - }, - ], - }, - ], - { basename: import.meta.env.VITE_BASE_PATH }, -); + { + path: routes.ErrorPage.path, + Component: ErrorPage, + }, + ], + }, +]); export default router; diff --git a/src/Stores/ExperimentStore.tsx b/src/Stores/ExperimentStore.tsx index 4667462..bcc7faa 100644 --- a/src/Stores/ExperimentStore.tsx +++ b/src/Stores/ExperimentStore.tsx @@ -1,30 +1,70 @@ import { create } from "zustand"; import { immer } from "zustand/middleware/immer"; -import type { Experiment, TaskData } from "Types/Experiment/Experiment"; +import type { + ExperimentData, + ExperimentTypeList, + InstanceData, +} from "Types/Experiment/Experiment"; +import { enableMapSet } from "immer"; + +// Call this once at your app's entry point (before using Immer) +enableMapSet(); interface ExperimentStoreState { - experiments: Experiment[]; - tasks: TaskData[]; + experiments: ExperimentData[]; + instances: InstanceData[]; + experimentTypes: ExperimentTypeList[] | null; + loadedHtmlFiles: Map; // experiment_type_id -> HTML content - addExperiment: (experiment: Experiment) => void; - updateExperiment: (id: number, data: Partial) => void; + setInstances: (instances: InstanceData[]) => void; + updateInstance: (id: number, data: Partial) => void; + removeInstance: (id: number) => void; + + setExperiments: (experiment: ExperimentData[]) => void; + addExperiment: (experiment: ExperimentData) => void; + updateExperiment: (id: number, data: Partial) => void; removeExperiment: (id: number) => void; - addTask: (experimentId: number, task: TaskData) => void; - updateTask: (taskId: number, data: Partial) => void; - removeTask: (experimentId: number, taskId: number) => void; + // Only what you asked for: + setExperimentTypes: (types: ExperimentTypeList[]) => void; + addLoadedHtml: (typeId: number, htmlContent: string) => void; } export const useExperimentStore = create()( immer((set) => ({ experiments: [], - tasks: [], teams: [], + instances: [], + experimentTypes: null, + loadedHtmlFiles: new Map(), + + setExperiments: (experiments) => + set((state) => { + state.experiments = experiments; + }), addExperiment: (experiment) => set((state) => { - state.experiments.push(experiment); + state.experiments = [experiment, ...state.experiments]; + }), + + setInstances: (instances) => + set((state) => { + state.instances = instances; + }), + + updateInstance: (id, data) => + set((state) => { + const exp = state.instances.find((e) => e.instance_id === id); + if (!exp) return; + + Object.assign(exp, data); + }), + + removeInstance: (id) => + set((state) => { + state.instances = state.instances.filter((e) => e.instance_id !== id); }), updateExperiment: (id, data) => @@ -39,31 +79,15 @@ export const useExperimentStore = create()( set((state) => { state.experiments = state.experiments.filter((e) => e.id !== id); }), - - addTask: (experimentId, task) => + // Only these two new methods + setExperimentTypes: (types) => set((state) => { - const exp = state.experiments.find((e) => e.id === experimentId); - if (!exp) return; - - exp.tasks_ids.push(task.id); - state.tasks.push(task); + state.experimentTypes = types; }), - updateTask: (taskId, data) => + addLoadedHtml: (typeId, htmlContent) => set((state) => { - const task = state.tasks.find((t) => t.id === taskId); - if (!task) return; - - Object.assign(task, data); - }), - - removeTask: (experimentId, taskId) => - set((state) => { - const exp = state.experiments.find((e) => e.id === experimentId); - if (!exp) return; - - state.tasks = state.tasks.filter((t) => t.data.id !== taskId); - exp.tasks_ids = exp.tasks_ids.filter((t) => t !== taskId); + state.loadedHtmlFiles.set(typeId, htmlContent); }), })), ); diff --git a/src/Types/ApiCalls/ConvertBackendCallsTypes.tsx b/src/Types/ApiCalls/ConvertBackendCallsTypes.tsx deleted file mode 100755 index 98697df..0000000 --- a/src/Types/ApiCalls/ConvertBackendCallsTypes.tsx +++ /dev/null @@ -1,7 +0,0 @@ -export interface ConvertSchema { - inputText: string; - inputFormat: string; - add_h: boolean; - make_3d: boolean; - optimize: boolean; -} diff --git a/src/Types/Experiment/Experiment.tsx b/src/Types/Experiment/Experiment.tsx index 270f5fe..683f8d8 100755 --- a/src/Types/Experiment/Experiment.tsx +++ b/src/Types/Experiment/Experiment.tsx @@ -1,40 +1,101 @@ -export type ExperimentStatus = - | "DRAFT" - | "QUEUE" - | "PROCESSING" - | "SUCCESS" - | "ERROR"; - -export interface Experiment { +export interface ExperimentTypeList { id: number; name: string; - description: string; + description?: string; +} + +export interface CreateExperimentTypeResponse { + id: number; + name: string; + description?: string; +} + +export interface CreateExperimentRequest { team_id: number; - date_created: Date; - experiment_status: ExperimentStatus; - experiment_type: string; - tasks_ids: number[]; -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export interface TaskTypePlugin { - type: string; - // how it appears in the experiment task list - ListItem: React.ComponentType>; - // full editor UI when clicking task - Editor: React.ComponentType>; -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export interface TaskData { - id: number; + experiment_type_id: number; name: string; - description: string; - data: TData; + description?: string; } -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export interface TaskEditorProps { - data: TData; - setData: (data: TData) => void; +export interface UpdateExperimentRequest { + experiment_id: number; + name?: string; + description?: string; +} + +export interface ExperimentData { + id: number; + team: { + team_id: number; + team_name: string; + }; + name: string; + description?: string; + created_at: string; + experiment_type: ExperimentTypeList; + instances_count: number; + instance_preview: SimpleInstanceData[]; + status: string; +} + +export interface ExperimentListResponse { + experiments: ExperimentData[]; + cur_page: number; + total_experiments: number; + page_size: number; +} + +export interface CreateInstanceRequest { + experiment_id: number; + instance_data: string; + name: string; + description?: string; +} + +export interface UpdateInstanceRequest { + instance_id: number; + name?: string; + description?: string; + instance_data?: string; + qubits_needed: number; +} + +export interface SimpleInstanceData { + id: number; + instance_data: string; + name: string; + description?: string; + qubits_needed: number; +} + +export interface SimulationResultData { + id: number; + comp_system: { + system_id: number; + system_name: string; + }; + simulation_result: string; + status: string; + started_at?: string; + ended_at?: string; +} + +export interface InstanceData { + instance_id: number; + instance_data: string; + name: string; + description?: string; + simulation_result?: SimulationResultData; + qubits_needed: number; +} + +export interface InstanceListResponse { + instances: InstanceData[]; + cur_page: number; + total_instances: number; + page_size: number; +} + +export interface StartExperimentRequest { + experiment_id: number; } diff --git a/src/main.tsx b/src/main.tsx index ed46f85..b921975 100755 --- a/src/main.tsx +++ b/src/main.tsx @@ -15,7 +15,7 @@ import { useAuthenticationStore } from "Stores/AuthenticationStore"; import { useUserPreferencesStore } from "Stores/PreferencesStore"; import type { KeycloakProfile } from "keycloak-js"; import { GetCurrentUserInfo } from "Api/QuantumBackend/UserManagement"; -import type { UserData } from "Types/User/User"; + import { notifications } from "@mantine/notifications"; import { IconForbid } from "@tabler/icons-react"; @@ -71,11 +71,8 @@ async function bootstrap() { onLoad: "check-sso", pkceMethod: "S256", silentCheckSsoRedirectUri: - window.location.origin + - "/" + - import.meta.env.VITE_BASE_PATH + - "/silent-check-sso.html", - silentCheckSsoFallback: false, + window.location.origin + "/silent-check-sso.html", + silentCheckSsoFallback: true, }) .then((authenticated: boolean) => { if (authenticated) { @@ -85,12 +82,7 @@ async function bootstrap() { profile: profile, }); GetCurrentUserInfo() - .then((info: UserData | undefined) => { - if (info && info.profile_picture_path) - useAuthenticationStore.setState({ - profile_picture_path: info.profile_picture_path, - }); - }) + .then() .catch(() => { notifications.show({ radius: "md", diff --git a/vite.config.ts b/vite.config.ts index 6ab8980..c2496d3 100755 --- a/vite.config.ts +++ b/vite.config.ts @@ -14,7 +14,7 @@ export default defineConfig({ preview: { port: Number(process.env.VITE_PORT), }, - base: "/" + process.env.VITE_BASE_PATH, + build: { rollupOptions: { output: {