diff --git a/.github/workflows/release_web_app.yml b/.github/workflows/release_web_app.yml new file mode 100644 index 0000000..39baed7 --- /dev/null +++ b/.github/workflows/release_web_app.yml @@ -0,0 +1,114 @@ +name: Build & Publish Web App (Docker + Helm) + +on: + release: + types: [published] + workflow_dispatch: + inputs: + tag: + description: "Release tag in the form appName@x.y.z" + required: true + type: string + +permissions: + contents: read + packages: write + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + parse-tag: + name: Parse app name and version + runs-on: ubuntu-latest + outputs: + app: ${{ steps.parse.outputs.app }} + version: ${{ steps.parse.outputs.version }} + steps: + - name: Determine tag + id: tag + run: | + if [ "${{ github.event_name }}" = "release" ]; then + echo "TAG=${{ github.event.release.tag_name }}" >> $GITHUB_ENV + else + echo "TAG=${{ inputs.tag }}" >> $GITHUB_ENV + fi + + - name: Parse tag + id: parse + run: | + TAG="${TAG}" + + if [[ ! "$TAG" =~ ^[^@]+@[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Invalid tag format: $TAG" + exit 1 + fi + + APP="${TAG%@*}" + VERSION="${TAG#*@}" + + echo "app=$APP" >> $GITHUB_OUTPUT + echo "version=$VERSION" >> $GITHUB_OUTPUT + + docker: + name: Build & Push Docker image + runs-on: ubuntu-latest + needs: parse-tag + + steps: + - uses: actions/checkout@v4 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GHCR_PAT }} + + + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: . + file: docker/Dockerfile.web-app + push: true + tags: | + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ needs.parse-tag.outputs.app }}:${{ needs.parse-tag.outputs.version }} + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ needs.parse-tag.outputs.app }}:latest + + helm: + name: Package & Push Helm chart + runs-on: ubuntu-latest + needs: parse-tag + + steps: + - uses: actions/checkout@v4 + + - name: Install Helm + uses: azure/setup-helm@v4 + with: + version: v3.14.0 + + - name: Log in to GHCR (Helm OCI) + run: | + echo "${{ secrets.GHCR_PAT }}" | \ + helm registry login ghcr.io \ + --username ${{ github.actor }} \ + --password-stdin + + + - name: Package chart + run: | + CHART_DIR="apps/${{ needs.parse-tag.outputs.app }}/helm" + + helm package "$CHART_DIR" \ + --version "${{ needs.parse-tag.outputs.version }}" \ + --app-version "${{ needs.parse-tag.outputs.version }}" + + - name: Push chart + run: | + CHART_PACKAGE=$(ls *.tgz) + + helm push "$CHART_PACKAGE" \ + oci://${{ env.REGISTRY }}/${{ github.repository }}/charts diff --git a/.gitignore b/.gitignore index 64718f1..afdd68f 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,5 @@ dist-ssr # Turborepo .turbo + +*/charts diff --git a/apps/visr/.dockerignore b/apps/visr/.dockerignore new file mode 100644 index 0000000..3d938ea --- /dev/null +++ b/apps/visr/.dockerignore @@ -0,0 +1,6 @@ +.dockerignore +coverage +node_modules +dist +README.md +.git \ No newline at end of file diff --git a/apps/visr/.gitignore b/apps/visr/.gitignore new file mode 100644 index 0000000..8b7e502 --- /dev/null +++ b/apps/visr/.gitignore @@ -0,0 +1,22 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/apps/visr/.prettierignore b/apps/visr/.prettierignore new file mode 100644 index 0000000..25983d8 --- /dev/null +++ b/apps/visr/.prettierignore @@ -0,0 +1,5 @@ +.github +.yarn +dist +node_modules +yarn.lock diff --git a/apps/visr/.prettierrc b/apps/visr/.prettierrc new file mode 100644 index 0000000..2aecfbd --- /dev/null +++ b/apps/visr/.prettierrc @@ -0,0 +1,6 @@ +{ + "arrowParens": "avoid", + "semi": true, + "singleQuote": false, + "trailingComma": "all" +} \ No newline at end of file diff --git a/apps/visr/.vscode/extensions.json b/apps/visr/.vscode/extensions.json new file mode 100644 index 0000000..9edd4f1 --- /dev/null +++ b/apps/visr/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["esbenp.prettier-vscode"] +} \ No newline at end of file diff --git a/apps/visr/.vscode/settings.json b/apps/visr/.vscode/settings.json new file mode 100644 index 0000000..66dd16e --- /dev/null +++ b/apps/visr/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true, + "prettier.requireConfig": true, + "prettier.configPath": "./.prettierrc", + "prettier.prettierPath": "./node_modules/prettier" +} diff --git a/apps/visr/LICENSE b/apps/visr/LICENSE new file mode 100644 index 0000000..9c8f3ea --- /dev/null +++ b/apps/visr/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/apps/visr/README.md b/apps/visr/README.md new file mode 100644 index 0000000..6421932 --- /dev/null +++ b/apps/visr/README.md @@ -0,0 +1 @@ +# ViSR UI diff --git a/apps/visr/eslint.config.js b/apps/visr/eslint.config.js new file mode 100644 index 0000000..d94e7de --- /dev/null +++ b/apps/visr/eslint.config.js @@ -0,0 +1,23 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' +import { globalIgnores } from 'eslint/config' + +export default tseslint.config([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + js.configs.recommended, + tseslint.configs.recommended, + reactHooks.configs['recommended-latest'], + reactRefresh.configs.vite, + ], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + }, +]) diff --git a/apps/visr/helm/Chart.yaml b/apps/visr/helm/Chart.yaml new file mode 100644 index 0000000..776c2c2 --- /dev/null +++ b/apps/visr/helm/Chart.yaml @@ -0,0 +1,11 @@ +apiVersion: v2 +name: visr-ui +description: ViSR UI +type: application +version: 0.1.0 +appVersion: "0.1.0" + +dependencies: + - name: ui-base + repository: "file://../ui-base/" + version: 0.1.0 \ No newline at end of file diff --git a/apps/visr/helm/values.yaml b/apps/visr/helm/values.yaml new file mode 100644 index 0000000..2fd0bca --- /dev/null +++ b/apps/visr/helm/values.yaml @@ -0,0 +1,41 @@ +ui-base: + name: visr-ui + host: visr.diamond.ac.uk + + image: + repository: ghcr.io/diamondlightsource/visr-ui + tag: 0.5.0 + + upstreams: + - id: blueapi + path: /api/ + rewriteTarget: / + target: + external: + uri: http://b01-1-blueapi.diamond.ac.uk + + - id: workflows + path: /api/workflows + rewriteTarget: /graphql + target: + external: + uri: https://workflows.diamond.ac.uk + + - id: data + path: /api/data/ + rewriteTarget: / + sse: true + target: + service: + name: dataserver + port: 8000 + + - id: supergraph + path: /api/graphql + rewriteTarget: / + target: + external: + uri: https://graph-nightly.diamond.ac.uk + passHostHeader: false + + identityProvider: legacy \ No newline at end of file diff --git a/apps/visr/index.html b/apps/visr/index.html new file mode 100644 index 0000000..8668e6d --- /dev/null +++ b/apps/visr/index.html @@ -0,0 +1,13 @@ + + + + + + + ViSR + + +
+ + + diff --git a/apps/visr/package.json b/apps/visr/package.json new file mode 100644 index 0000000..db8db1d --- /dev/null +++ b/apps/visr/package.json @@ -0,0 +1,60 @@ +{ + "name": "@atlas/visr", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "build": "tsc -b && vite build", + "dev": "vite", + "lint": "eslint .", + "preview": "vite preview", + "relay": "relay-compiler", + "test": "vitest run", + "test:watch": "vitest" + }, + "dependencies": { + "@diamondlightsource/davidia": "^1.0.3", + "@diamondlightsource/sci-react-ui": "^0.2.0", + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.1", + "@jsonforms/core": "3.6.0", + "@jsonforms/material-renderers": "3.6.0", + "@jsonforms/react": "3.6.0", + "@mui/icons-material": "^6.5.0", + "@mui/lab": "6.0.0-beta.22", + "@mui/material": "<7.0.0", + "@mui/x-date-pickers": "^7.17.0", + "ndarray": "^1.0.19", + "react-error-boundary": "^6.0.0", + "react-router-dom": "^7.7.1" + }, + "devDependencies": { + "@atlas/vitest-conf": "workspace:*", + "@eslint/js": "^9.33.0", + "@mui/material": "^6.5.0", + "@mui/x-date-pickers": "^7.29.4", + "@types/ndarray": "^1.0.14", + "@types/react-relay": "^18.2.1", + "@types/relay-runtime": "^19.0.2", + "@vitejs/plugin-react-swc": "^3.11.0", + "ajv": "^8.17.1", + "babel-plugin-relay": "^20.1.1", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.20", + "globals": "^16.3.0", + "msw": "^2.10.4", + "react-relay": "^20.1.1", + "react-router-dom": "^7.8.0", + "relay-compiler": "^20.1.1", + "relay-runtime": "^20.1.1", + "vite": "^7.1.2", + "vite-plugin-relay": "^2.1.0", + "vitest": "*" + }, + "packageManager": "pnpm@10.12.3+sha512.467df2c586056165580ad6dfb54ceaad94c5a30f80893ebdec5a44c5aa73c205ae4a5bb9d5ed6bb84ea7c249ece786642bbb49d06a307df218d03da41c317417", + "msw": { + "workerDirectory": [ + "public" + ] + } +} diff --git a/apps/visr/public/diamond.svg b/apps/visr/public/diamond.svg new file mode 100644 index 0000000..0af1a17 --- /dev/null +++ b/apps/visr/public/diamond.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/apps/visr/public/mockServiceWorker.js b/apps/visr/public/mockServiceWorker.js new file mode 100644 index 0000000..f22ebf3 --- /dev/null +++ b/apps/visr/public/mockServiceWorker.js @@ -0,0 +1,343 @@ +/* tslint:disable */ + +/** + * Mock Service Worker. + * @see https://github.com/mswjs/msw + * - Please do NOT modify this file. + */ + +const PACKAGE_VERSION = "2.10.4"; +const INTEGRITY_CHECKSUM = "f5825c521429caf22a4dd13b66e243af"; +const IS_MOCKED_RESPONSE = Symbol("isMockedResponse"); +const activeClientIds = new Set(); + +addEventListener("install", function () { + self.skipWaiting(); +}); + +addEventListener("activate", function (event) { + event.waitUntil(self.clients.claim()); +}); + +addEventListener("message", async function (event) { + const clientId = Reflect.get(event.source || {}, "id"); + + if (!clientId || !self.clients) { + return; + } + + const client = await self.clients.get(clientId); + + if (!client) { + return; + } + + const allClients = await self.clients.matchAll({ + type: "window", + }); + + switch (event.data) { + case "KEEPALIVE_REQUEST": { + sendToClient(client, { + type: "KEEPALIVE_RESPONSE", + }); + break; + } + + case "INTEGRITY_CHECK_REQUEST": { + sendToClient(client, { + type: "INTEGRITY_CHECK_RESPONSE", + payload: { + packageVersion: PACKAGE_VERSION, + checksum: INTEGRITY_CHECKSUM, + }, + }); + break; + } + + case "MOCK_ACTIVATE": { + activeClientIds.add(clientId); + + sendToClient(client, { + type: "MOCKING_ENABLED", + payload: { + client: { + id: client.id, + frameType: client.frameType, + }, + }, + }); + break; + } + + case "MOCK_DEACTIVATE": { + activeClientIds.delete(clientId); + break; + } + + case "CLIENT_CLOSED": { + activeClientIds.delete(clientId); + + const remainingClients = allClients.filter(client => { + return client.id !== clientId; + }); + + // Unregister itself when there are no more clients + if (remainingClients.length === 0) { + self.registration.unregister(); + } + + break; + } + } +}); + +addEventListener("fetch", function (event) { + // Bypass navigation requests. + if (event.request.mode === "navigate") { + return; + } + + // Opening the DevTools triggers the "only-if-cached" request + // that cannot be handled by the worker. Bypass such requests. + if ( + event.request.cache === "only-if-cached" && + event.request.mode !== "same-origin" + ) { + return; + } + + // Bypass all requests when there are no active clients. + // Prevents the self-unregistered worked from handling requests + // after it's been deleted (still remains active until the next reload). + if (activeClientIds.size === 0) { + return; + } + + const requestId = crypto.randomUUID(); + event.respondWith(handleRequest(event, requestId)); +}); + +/** + * @param {FetchEvent} event + * @param {string} requestId + */ +async function handleRequest(event, requestId) { + const client = await resolveMainClient(event); + const requestCloneForEvents = event.request.clone(); + const response = await getResponse(event, client, requestId); + + // Send back the response clone for the "response:*" life-cycle events. + // Ensure MSW is active and ready to handle the message, otherwise + // this message will pend indefinitely. + if (client && activeClientIds.has(client.id)) { + const serializedRequest = await serializeRequest(requestCloneForEvents); + + // Clone the response so both the client and the library could consume it. + const responseClone = response.clone(); + + sendToClient( + client, + { + type: "RESPONSE", + payload: { + isMockedResponse: IS_MOCKED_RESPONSE in response, + request: { + id: requestId, + ...serializedRequest, + }, + response: { + type: responseClone.type, + status: responseClone.status, + statusText: responseClone.statusText, + headers: Object.fromEntries(responseClone.headers.entries()), + body: responseClone.body, + }, + }, + }, + responseClone.body ? [serializedRequest.body, responseClone.body] : [], + ); + } + + return response; +} + +/** + * Resolve the main client for the given event. + * Client that issues a request doesn't necessarily equal the client + * that registered the worker. It's with the latter the worker should + * communicate with during the response resolving phase. + * @param {FetchEvent} event + * @returns {Promise} + */ +async function resolveMainClient(event) { + const client = await self.clients.get(event.clientId); + + if (activeClientIds.has(event.clientId)) { + return client; + } + + if (client?.frameType === "top-level") { + return client; + } + + const allClients = await self.clients.matchAll({ + type: "window", + }); + + return allClients + .filter(client => { + // Get only those clients that are currently visible. + return client.visibilityState === "visible"; + }) + .find(client => { + // Find the client ID that's recorded in the + // set of clients that have registered the worker. + return activeClientIds.has(client.id); + }); +} + +/** + * @param {FetchEvent} event + * @param {Client | undefined} client + * @param {string} requestId + * @returns {Promise} + */ +async function getResponse(event, client, requestId) { + // Clone the request because it might've been already used + // (i.e. its body has been read and sent to the client). + const requestClone = event.request.clone(); + + function passthrough() { + // Cast the request headers to a new Headers instance + // so the headers can be manipulated with. + const headers = new Headers(requestClone.headers); + + // Remove the "accept" header value that marked this request as passthrough. + // This prevents request alteration and also keeps it compliant with the + // user-defined CORS policies. + const acceptHeader = headers.get("accept"); + if (acceptHeader) { + const values = acceptHeader.split(",").map(value => value.trim()); + const filteredValues = values.filter( + value => value !== "msw/passthrough", + ); + + if (filteredValues.length > 0) { + headers.set("accept", filteredValues.join(", ")); + } else { + headers.delete("accept"); + } + } + + return fetch(requestClone, { headers }); + } + + // Bypass mocking when the client is not active. + if (!client) { + return passthrough(); + } + + // Bypass initial page load requests (i.e. static assets). + // The absence of the immediate/parent client in the map of the active clients + // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet + // and is not ready to handle requests. + if (!activeClientIds.has(client.id)) { + return passthrough(); + } + + // Notify the client that a request has been intercepted. + const serializedRequest = await serializeRequest(event.request); + const clientMessage = await sendToClient( + client, + { + type: "REQUEST", + payload: { + id: requestId, + ...serializedRequest, + }, + }, + [serializedRequest.body], + ); + + switch (clientMessage.type) { + case "MOCK_RESPONSE": { + return respondWithMock(clientMessage.data); + } + + case "PASSTHROUGH": { + return passthrough(); + } + } + + return passthrough(); +} + +/** + * @param {Client} client + * @param {any} message + * @param {Array} transferrables + * @returns {Promise} + */ +function sendToClient(client, message, transferrables = []) { + return new Promise((resolve, reject) => { + const channel = new MessageChannel(); + + channel.port1.onmessage = event => { + if (event.data && event.data.error) { + return reject(event.data.error); + } + + resolve(event.data); + }; + + client.postMessage(message, [ + channel.port2, + ...transferrables.filter(Boolean), + ]); + }); +} + +/** + * @param {Response} response + * @returns {Response} + */ +function respondWithMock(response) { + // Setting response status code to 0 is a no-op. + // However, when responding with a "Response.error()", the produced Response + // instance will have status code set to 0. Since it's not possible to create + // a Response instance with status code 0, handle that use-case separately. + if (response.status === 0) { + return Response.error(); + } + + const mockedResponse = new Response(response.body, response); + + Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, { + value: true, + enumerable: true, + }); + + return mockedResponse; +} + +/** + * @param {Request} request + */ +async function serializeRequest(request) { + return { + url: request.url, + mode: request.mode, + method: request.method, + headers: Object.fromEntries(request.headers.entries()), + cache: request.cache, + credentials: request.credentials, + destination: request.destination, + integrity: request.integrity, + redirect: request.redirect, + referrer: request.referrer, + referrerPolicy: request.referrerPolicy, + body: await request.arrayBuffer(), + keepalive: request.keepalive, + }; +} diff --git a/apps/visr/relay.config.json b/apps/visr/relay.config.json new file mode 100644 index 0000000..cb1be38 --- /dev/null +++ b/apps/visr/relay.config.json @@ -0,0 +1,7 @@ +{ + "src": "./src", + "language": "typescript", + "schema": "./supergraph.graphql", + "exclude": ["**/node_modules/**", "**/__mocks__/**", "**/__generated__/**"], + "eagerEsModules": true +} diff --git a/apps/visr/src/RelayEnvironment.ts b/apps/visr/src/RelayEnvironment.ts new file mode 100644 index 0000000..3e85e88 --- /dev/null +++ b/apps/visr/src/RelayEnvironment.ts @@ -0,0 +1,34 @@ +import { + Environment, + Network, + RecordSource, + Store, + type FetchFunction, +} from "relay-runtime"; + +const HTTP_ENDPOINT = "/api/graphql"; +const fetchFn: FetchFunction = async (request, variables) => { + const resp = await fetch(HTTP_ENDPOINT, { + method: "POST", + headers: { + Accept: + "application/graphql-response+json; charset=utf-8, application/json; charset=utf-8", + "Content-Type": "application/json", + }, + body: JSON.stringify({ + query: request.text, + variables, + }), + }); + + return await resp.json(); +}; + +function createRelayEnvironment() { + return new Environment({ + network: Network.create(fetchFn), + store: new Store(new RecordSource()), + }); +} + +export const RelayEnvironment = createRelayEnvironment(); diff --git a/apps/visr/src/components/InstrumentSessionSelection/InstrumentSession.tsx b/apps/visr/src/components/InstrumentSessionSelection/InstrumentSession.tsx new file mode 100644 index 0000000..d0f854f --- /dev/null +++ b/apps/visr/src/components/InstrumentSessionSelection/InstrumentSession.tsx @@ -0,0 +1,46 @@ +import { visitToText, type Visit } from "@diamondlightsource/sci-react-ui"; +import { useLazyLoadQuery } from "react-relay/hooks"; +import { graphql } from "relay-runtime"; +import type { InstrumentSessionQuery as InstrumentSessionQueryType } from "./__generated__/InstrumentSessionQuery.graphql"; + +const instrumentSessionQuery = graphql` + query InstrumentSessionQuery($instrumentName: String!) { + instrument(instrumentName: $instrumentName) { + instrumentSessions { + instrumentSessionNumber + proposal { + proposalCategory + proposalNumber + } + } + } + } +`; + +function GetInstrumentSessions() { + const data = useLazyLoadQuery( + instrumentSessionQuery, + { instrumentName: "ViSR" }, + ); + + const sessionListLen = data.instrument?.instrumentSessions.length ?? 1; + const sessionsList = []; + + for (let i = 0; i < sessionListLen; i++) { + const visit: Visit = { + proposalCode: + data.instrument?.instrumentSessions[ + i + ].proposal?.proposalCategory?.toLowerCase() ?? "cm", + proposalNumber: + data.instrument?.instrumentSessions[i].proposal?.proposalNumber ?? + 12345, + number: + data.instrument?.instrumentSessions[i].instrumentSessionNumber ?? 1, + }; + sessionsList.push(visitToText(visit)); + } + return sessionsList; +} + +export default GetInstrumentSessions; diff --git a/apps/visr/src/components/InstrumentSessionSelection/InstrumentSessionView.tsx b/apps/visr/src/components/InstrumentSessionSelection/InstrumentSessionView.tsx new file mode 100644 index 0000000..c10225d --- /dev/null +++ b/apps/visr/src/components/InstrumentSessionSelection/InstrumentSessionView.tsx @@ -0,0 +1,93 @@ +import { useState } from "react"; +import { visitToText, VisitInput } from "@diamondlightsource/sci-react-ui"; +import { useInstrumentSession } from "../../context/instrumentSession/useInstrumentSession"; +import { + Divider, + List, + ListItemButton, + ListItemText, + Menu, + MenuItem, +} from "@mui/material"; + +function InstrumentSessionView({ sessionsList }: { sessionsList: string[] }) { + const { instrumentSession, setInstrumentSession } = useInstrumentSession(); + + const [anchorEl, setAnchorEl] = useState(null); + const [selectedIndex, setSelectedIndex] = useState(1); + const open = Boolean(anchorEl); + const handleClickListItem = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + + const handleMenuItemClick = ( + event: React.MouseEvent, + index: number, + ) => { + setSelectedIndex(index); + setAnchorEl(null); + setInstrumentSession(event.currentTarget.textContent ?? ""); + }; + + const handleClose = () => { + setAnchorEl(null); + }; + + return ( +
+ + + + + + + e.stopPropagation()}> + { + setInstrumentSession(visitToText(visit)); + setAnchorEl(null); + }} + /> + + + {sessionsList.map((option, index) => ( + handleMenuItemClick(event, index)} + > + {option} + + ))} + +
+ ); +} + +export default InstrumentSessionView; diff --git a/apps/visr/src/components/InstrumentSessionSelection/__generated__/InstrumentSessionQuery.graphql.ts b/apps/visr/src/components/InstrumentSessionSelection/__generated__/InstrumentSessionQuery.graphql.ts new file mode 100644 index 0000000..89ed37f --- /dev/null +++ b/apps/visr/src/components/InstrumentSessionSelection/__generated__/InstrumentSessionQuery.graphql.ts @@ -0,0 +1,131 @@ +/** + * @generated SignedSource<> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest } from 'relay-runtime'; +export type InstrumentSessionQuery$variables = { + instrumentName: string; +}; +export type InstrumentSessionQuery$data = { + readonly instrument: { + readonly instrumentSessions: ReadonlyArray<{ + readonly instrumentSessionNumber: number; + readonly proposal: { + readonly proposalCategory: string | null | undefined; + readonly proposalNumber: number; + } | null | undefined; + }>; + } | null | undefined; +}; +export type InstrumentSessionQuery = { + response: InstrumentSessionQuery$data; + variables: InstrumentSessionQuery$variables; +}; + +const node: ConcreteRequest = (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "instrumentName" + } +], +v1 = [ + { + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "instrumentName", + "variableName": "instrumentName" + } + ], + "concreteType": "Instrument", + "kind": "LinkedField", + "name": "instrument", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "InstrumentSession", + "kind": "LinkedField", + "name": "instrumentSessions", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "instrumentSessionNumber", + "storageKey": null + }, + { + "alias": null, + "args": null, + "concreteType": "Proposal", + "kind": "LinkedField", + "name": "proposal", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "proposalCategory", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "proposalNumber", + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": null + } +]; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "InstrumentSessionQuery", + "selections": (v1/*: any*/), + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "InstrumentSessionQuery", + "selections": (v1/*: any*/) + }, + "params": { + "cacheID": "625bde94466c698bcacfb6d41e195cbe", + "id": null, + "metadata": {}, + "name": "InstrumentSessionQuery", + "operationKind": "query", + "text": "query InstrumentSessionQuery(\n $instrumentName: String!\n) {\n instrument(instrumentName: $instrumentName) {\n instrumentSessions {\n instrumentSessionNumber\n proposal {\n proposalCategory\n proposalNumber\n }\n }\n }\n}\n" + } +}; +})(); + +(node as any).hash = "3cb865e4a1f45b6db5b70c2cb201f4e7"; + +export default node; diff --git a/apps/visr/src/components/NumberTextField.tsx b/apps/visr/src/components/NumberTextField.tsx new file mode 100644 index 0000000..ddaf0be --- /dev/null +++ b/apps/visr/src/components/NumberTextField.tsx @@ -0,0 +1,48 @@ +import { TextField } from "@mui/material"; +import type { SpectroscopyFormData } from "./SpectroscopyForm"; + +type NumberTextFieldProps = { + formData: SpectroscopyFormData; + setFormData: (f: SpectroscopyFormData) => void; + field: keyof SpectroscopyFormData; + step: number; + label: string; +}; + +const NumberTextField = ({ + formData, + setFormData, + field, + step = 1, + label = field, +}: NumberTextFieldProps) => { + const handleChange = ( + e: React.ChangeEvent, + ) => { + const value = e.target.value; + const parsedValue = + value === "" + ? 0 + : Number.isInteger(step) // parse to Int or Float depending on step + ? parseInt(value, 10) + : parseFloat(value); + + setFormData({ + ...formData, + [field]: parsedValue, + }); + }; + + return ( + + ); +}; + +export default NumberTextField; diff --git a/apps/visr/src/components/PlanBrowser/PlanBrowser.test.tsx b/apps/visr/src/components/PlanBrowser/PlanBrowser.test.tsx new file mode 100644 index 0000000..7ec51a4 --- /dev/null +++ b/apps/visr/src/components/PlanBrowser/PlanBrowser.test.tsx @@ -0,0 +1,83 @@ +import type { Plan } from "../../utils/api"; +import { render, screen, userEvent } from "@atlas/vitest-conf"; +import PlanBrowser from "./PlanBrowser"; + +const plans: Plan[] = [ + { name: "Plan 1", schema: {}, description: "" }, + { name: "Plan 2", schema: {}, description: "" }, + { name: "Plan 3", schema: {}, description: "" }, +]; + +const renderPlan = (plan: Plan) => ( +
{plan.name}
+); + +function renderBrowser() { + return render(); +} + +describe("PlanBrowser", () => { + it("shows a placeholder before initial plan selection", () => { + renderBrowser(); + + expect(screen.getByText("Select a plan")).toBeInTheDocument(); + expect( + screen.getByText("Choose from the list on the left to see details."), + ).toBeInTheDocument(); + }); + + it("does not invoke renderPlan before selection", () => { + const mockRender = vi.fn(); + render(); + expect(mockRender).not.toBeCalled(); + }); + + it("renders plan details when a plan is selected", async () => { + renderBrowser(); + + const selectedPlan = screen.getByRole("button", { name: "Plan 2" }); + const user = userEvent.setup(); + await user.click(selectedPlan); + + // placeholder disappears... + expect(screen.queryByText("Select a plan")).not.toBeInTheDocument(); + + // ...plan details appear + const planDetails = screen.getByTestId("plan-view"); + expect(planDetails).toBeInTheDocument(); + expect(planDetails).toHaveTextContent("Plan 2"); + }); + + it("renders plan details with every selection", async () => { + renderBrowser(); + + const user = userEvent.setup(); + await user.click(screen.getByRole("button", { name: "Plan 3" })); + + const planDetails = screen.getByTestId("plan-view"); + expect(planDetails).toBeInTheDocument(); + expect(planDetails).toHaveTextContent("Plan 3"); + + await user.click(screen.getByRole("button", { name: "Plan 1" })); + expect(planDetails).toHaveTextContent("Plan 1"); + }); + + it("persists plan details through search/filtering", async () => { + renderBrowser(); + const user = userEvent.setup(); + + // select plan 1 + await user.click(screen.getByRole("button", { name: "Plan 1" })); + + // plan 1 details appear + const planDetails = screen.getByTestId("plan-view"); + expect(planDetails).toHaveTextContent("Plan 1"); + + // search for a different plan + const searchbox = screen.getByRole("textbox", { name: /search plans/i }); + await user.type(searchbox, "Plan 3"); + + // but user has not selected it, so plan 1 details remain + expect(planDetails).toHaveTextContent("Plan 1"); + }); +}); diff --git a/apps/visr/src/components/PlanBrowser/PlanBrowser.tsx b/apps/visr/src/components/PlanBrowser/PlanBrowser.tsx new file mode 100644 index 0000000..d5a1174 --- /dev/null +++ b/apps/visr/src/components/PlanBrowser/PlanBrowser.tsx @@ -0,0 +1,60 @@ +import { useState, type ReactNode } from "react"; +import type { Plan } from "../../utils/api"; +import { + Box, + Container, + Grid2 as Grid, + Paper, + Typography, +} from "@mui/material"; +import SearchablePlanList from "./SearchablePlanList"; + +export type PlanBrowserProps = { + plans: Plan[]; + renderPlan: (plan: Plan) => ReactNode; +}; + +export default function PlanBrowser({ plans, renderPlan }: PlanBrowserProps) { + const [selectedPlan, setSelectedPlan] = useState(null); + + return ( + + + + + + + + + + {selectedPlan ? ( + + {renderPlan(selectedPlan)} + + ) : ( + + + Select a plan + + + Choose from the list on the left to see details. + + + )} + + + + + ); +} diff --git a/apps/visr/src/components/PlanBrowser/PlanParameters.test.tsx b/apps/visr/src/components/PlanBrowser/PlanParameters.test.tsx new file mode 100644 index 0000000..cb7f3e7 --- /dev/null +++ b/apps/visr/src/components/PlanBrowser/PlanParameters.test.tsx @@ -0,0 +1,54 @@ +import type { Plan } from "../../utils/api"; +import { render, screen } from "@atlas/vitest-conf"; + +import PlanParameters from "./PlanParameters"; +import { InstrumentSessionProvider } from "../../context/instrumentSession/InstrumentSessionProvider"; + +const mockJsonFormsImpl = vi.fn(() => { + return
; +}); + +vi.mock("@jsonforms/react", () => { + return { + JsonForms: () => mockJsonFormsImpl(), + }; +}); + +const plan: Plan = { + name: "hi_plan", + description: "Says hi to you", + schema: { + properties: { name: { title: "Name", type: "string" } }, + }, +}; + +describe("PlanParameters", () => { + it("renders a plan's name, description, parameters, session, and run button", () => { + render( + + + , + ); + + expect(screen.getByText(plan.name)).toBeInTheDocument(); + expect(screen.getByText(plan.description!)).toBeInTheDocument(); + expect(screen.getByTestId("jsonforms-sentinel")).toBeInTheDocument(); + expect(screen.getByRole("textbox", { name: "Instrument Session" })); + expect(screen.getByRole("button", { name: "Run" })); + }); + + it("renders fallback UI if JSON Forms component fails", async () => { + // this time JsonForms will throw + mockJsonFormsImpl.mockImplementation(() => { + throw new Error("I can't do it!"); + }); + + render( + + + , + ); + + expect(screen.getByText("UI unavailable")).toBeInTheDocument(); + }); +}); diff --git a/apps/visr/src/components/PlanBrowser/PlanParameters.tsx b/apps/visr/src/components/PlanBrowser/PlanParameters.tsx new file mode 100644 index 0000000..db50577 --- /dev/null +++ b/apps/visr/src/components/PlanBrowser/PlanParameters.tsx @@ -0,0 +1,76 @@ +import { useState } from "react"; +import { Box, TextField, Typography } from "@mui/material"; +import { JsonForms } from "@jsonforms/react"; +import { + materialRenderers, + materialCells, +} from "@jsonforms/material-renderers"; +import sanitizeSchema from "../../utils/schema"; +import type { Plan } from "../../utils/api"; +import RunPlanButton from "../RunPlanButton"; +import { useInstrumentSession } from "../../context/instrumentSession/useInstrumentSession"; + +import { ErrorBoundary } from "react-error-boundary"; + +/** + * If the UI generation fails, we show a simple apology + */ +function UIFallback() { + return ( + + UI unavailable + + ); +} +type PlanParametersProps = { + plan: Plan; +}; + +const PlanParameters: React.FC = ( + props: PlanParametersProps, +) => { + const schema = sanitizeSchema(props.plan.schema); + + // const renderers = materialRenderers; + const [planParameters, setPlanParameters] = useState({}); + const { instrumentSession, setInstrumentSession } = useInstrumentSession(); + + return ( + + + {props.plan.name} + + {props.plan.description && ( + + {props.plan.description} + + )} + setPlanParameters(data)} + /> + setInstrumentSession(e.target.value)} + > + + + + + ); +}; + +export default PlanParameters; diff --git a/apps/visr/src/components/PlanBrowser/SearchablePlanList.test.tsx b/apps/visr/src/components/PlanBrowser/SearchablePlanList.test.tsx new file mode 100644 index 0000000..2ac9fd2 --- /dev/null +++ b/apps/visr/src/components/PlanBrowser/SearchablePlanList.test.tsx @@ -0,0 +1,161 @@ +import * as React from "react"; +import { render, screen, within, userEvent } from "@atlas/vitest-conf"; +import SearchablePlanList from "./SearchablePlanList"; +import type { Plan } from "../../utils/api"; + +const plans: Plan[] = [ + { name: "Align Beam", description: "", schema: {} }, + { name: "Dark Current", description: "", schema: {} }, + { name: "Flat Field", description: "", schema: {} }, +]; + +function renderList( + overrides?: Partial>, +) { + const updateSelection = vi.fn(); + const props: React.ComponentProps = { + plans, + selectedPlan: null, + updateSelection, + ...overrides, + }; + const utils = render(); + + // merge useful things for the test + return { updateSelection, props, ...utils }; +} + +describe("SearchablePlanList", () => { + it("renders search, heading, and all plans initially", () => { + renderList(); + + // search field + expect( + screen.getByRole("textbox", { name: /search plans/i }), + ).toBeInTheDocument(); + + // heading + expect(screen.getByText("Plans")).toBeInTheDocument(); + + // all items + for (const p of plans) { + expect(screen.getByRole("button", { name: p.name })).toBeInTheDocument(); + } + + // no selection initially + for (const p of plans) { + expect(screen.getByRole("button", { name: p.name })).toHaveAttribute( + "aria-selected", + "false", + ); + } + }); + + it("calls updateSelection with the clicked plan", async () => { + const user = userEvent.setup(); + const { updateSelection } = renderList(); + + const item = screen.getByRole("button", { name: "Flat Field" }); + await user.click(item); + + expect(updateSelection).toHaveBeenCalledTimes(1); + expect(updateSelection).toHaveBeenCalledWith({ + name: "Flat Field", + schema: {}, + description: "", + }); + }); + + it("reflects controlled selection via selectedPlan prop", async () => { + const { props, rerender } = renderList(); + + const selectedButton = screen.getByRole("button", { name: "Dark Current" }); + + // none selected initially + expect(selectedButton).toHaveAttribute("aria-selected", "false"); + + // re-render with selected plan + const selected = { name: "Dark Current", schema: {}, description: "" }; + rerender(); + // selected plan marked so with aria-selected + expect(selectedButton).toHaveAttribute("aria-selected", "true"); + // others remain unselected + expect(screen.getByRole("button", { name: "Align Beam" })).toHaveAttribute( + "aria-selected", + "false", + ); + }); + + it("filters plans by case-insensitive search", async () => { + const user = userEvent.setup(); + renderList(); + + const search = screen.getByRole("textbox", { name: /search plans/i }); + await user.type(search, "dark"); // lower-case query + + expect( + screen.getByRole("button", { name: "Dark Current" }), + ).toBeInTheDocument(); + expect( + screen.queryByRole("button", { name: "Align Beam" }), + ).not.toBeInTheDocument(); + expect( + screen.queryByRole("button", { name: "Flat Field" }), + ).not.toBeInTheDocument(); + + // Case-insensitive check + await user.clear(search); + await user.type(search, "ALIG"); + expect( + screen.getByRole("button", { name: "Align Beam" }), + ).toBeInTheDocument(); + }); + + it("shows empty-state message when no plans match search", async () => { + const user = userEvent.setup(); + renderList(); + + const search = screen.getByRole("textbox", { name: /search plans/i }); + await user.type(search, "zzz"); + + expect(screen.getByText(/no plans match/i)).toBeInTheDocument(); + }); + + it("handles empty plans array", () => { + renderList({ plans: [] }); + // List is empty, but the heading and search are present + expect(screen.getByText("Plans")).toBeInTheDocument(); + expect( + screen.getByRole("textbox", { name: /search plans/i }), + ).toBeInTheDocument(); + }); + + it("shows all items after clearing a search", async () => { + renderList(); + const user = userEvent.setup(); + + const planList = screen.getByRole("list"); + const { getAllByRole, queryAllByRole } = within(planList); + + // three initial plans + expect(getAllByRole("button")).toHaveLength(3); + + // search + const search = screen.getByRole("textbox", { name: /search plans/i }); + await user.type(search, "zzz"); + + // no matches + expect(screen.getByText(/no plans match/i)).toBeInTheDocument(); + // no plans shown + expect(queryAllByRole("button")).toHaveLength(0); + + // clear search + await user.clear(search); + + // no matches label disappears + expect(screen.queryByText(/no plans match/i)).not.toBeInTheDocument(); + + // three plans again + expect(getAllByRole("button")).toHaveLength(3); + }); +}); diff --git a/apps/visr/src/components/PlanBrowser/SearchablePlanList.tsx b/apps/visr/src/components/PlanBrowser/SearchablePlanList.tsx new file mode 100644 index 0000000..5a9873c --- /dev/null +++ b/apps/visr/src/components/PlanBrowser/SearchablePlanList.tsx @@ -0,0 +1,71 @@ +import { + Box, + Divider, + List, + ListItemButton, + TextField, + Typography, +} from "@mui/material"; +import type { Plan } from "../../utils/api"; +import { useMemo, useState } from "react"; + +type Props = { + plans: Plan[]; + selectedPlan: Plan | null; + updateSelection: (plan: Plan) => void; +}; +export default function SearchablePlanList({ + plans, + selectedPlan, + updateSelection, +}: Props) { + const [query, setQuery] = useState(""); + + const matchingPlans = useMemo(() => { + const q = query.trim().toLowerCase(); + if (!q) return plans; + return plans.filter(plan => plan.name.toLowerCase().includes(q)); + }, [plans, query]); + + return ( + <> + + setQuery(e.target.value)} + /> + + + + + Plans + + + + {matchingPlans.map(plan => { + const selected = selectedPlan?.name === plan.name; + return ( + updateSelection(plan)} + > + {plan.name} + + ); + })} + {matchingPlans.length === 0 && plans.length > 0 && ( + + + No plans match “{query}”. + + + )} + + + ); +} diff --git a/apps/visr/src/components/RawSpectroscopyData.tsx b/apps/visr/src/components/RawSpectroscopyData.tsx new file mode 100644 index 0000000..517fd3b --- /dev/null +++ b/apps/visr/src/components/RawSpectroscopyData.tsx @@ -0,0 +1,113 @@ +import { ImagePlot, type NDT } from "@diamondlightsource/davidia"; +import Box from "@mui/material/Box"; +import ndarray from "ndarray"; +import { useSpectroscopyData, type RGBColour } from "./useSpectroscopyData"; + +type RGBColor = "red" | "green" | "blue" | "gray"; + +function toNDT(matrix: (number | null)[][], colour: RGBColor): NDT { + if (!matrix?.length || !matrix[0]?.length) { + return EMPTY_NDT; // skip invalid input + } + const height = matrix.length; + const width = matrix[0].length; + + // Flatten and filter out nulls for normalisation + const flat = matrix.flat(); + const valid = flat.filter((v): v is number => v !== null && !isNaN(v)); + + // Avoid crashes when no valid values + const min = valid.length ? Math.min(...valid) : 0; + const max = valid.length ? Math.max(...valid) : 1; + const scale = max > min ? 255 / (max - min) : 1; + + const rgb = new Uint8Array(width * height * 3); + + for (let i = 0; i < flat.length; i++) { + const v = flat[i]; + let scaled = 0; + if (v !== null && !isNaN(v)) { + scaled = Math.round((v - min) * scale); + } // else stays 0 (black) + + switch (colour) { + case "red": + rgb[i * 3] = scaled; + break; + case "green": + rgb[i * 3 + 1] = scaled; + break; + case "blue": + rgb[i * 3 + 2] = scaled; + break; + case "gray": + rgb[i * 3] = scaled; + rgb[i * 3 + 1] = scaled; + rgb[i * 3 + 2] = scaled; + break; + } + } + + return ndarray(rgb, [height, width, 3]) as NDT; +} +/** Placeholder empty gray dataset */ +const EMPTY_NDT = toNDT([[0]], "gray"); + +/** Return type of `/api/data/map` */ +interface MapResponse { + values: (number | null)[][]; +} + +async function fetchMap(filepath: string, datapath: string, colour: RGBColour) { + const url = `/api/data/map?filepath=${encodeURIComponent(filepath)}&datapath=${encodeURIComponent(datapath)}`; + const resp = await fetch(url); + if (!resp.ok) throw new Error(resp.statusText); + const mapResponse: MapResponse = await resp.json(); + return toNDT(mapResponse.values, colour); +} + +function RawSpectroscopyData() { + const { data } = useSpectroscopyData(fetchMap); + return ( + + + + + + + + ); +} + +export default RawSpectroscopyData; diff --git a/apps/visr/src/components/RunPlanButton.tsx b/apps/visr/src/components/RunPlanButton.tsx new file mode 100644 index 0000000..f4f477d --- /dev/null +++ b/apps/visr/src/components/RunPlanButton.tsx @@ -0,0 +1,34 @@ +import { Button } from "@mui/material"; + +import { createAndStartTask, type TaskRequest } from "../utils/api"; + +type RunPlanButtonProps = { + name: string; + params: object; + instrumentSession: string; +}; + +const RunPlanButton = ({ + name, + params, + instrumentSession, +}: RunPlanButtonProps) => { + return ( + + ); +}; + +export default RunPlanButton; diff --git a/apps/visr/src/components/SpectroscopyForm.tsx b/apps/visr/src/components/SpectroscopyForm.tsx new file mode 100644 index 0000000..8bf2d40 --- /dev/null +++ b/apps/visr/src/components/SpectroscopyForm.tsx @@ -0,0 +1,102 @@ +import { useState } from "react"; +import { Box, TextField } from "@mui/material"; +import NumberTextField from "./NumberTextField"; +import RunPlanButton from "./RunPlanButton"; +import RawSpectroscopyData from "./RawSpectroscopyData"; +import { useInstrumentSession } from "../context/instrumentSession/useInstrumentSession"; + +export type SpectroscopyFormData = { + total_number_of_scan_points: number; + grid_size: number; + grid_origin_x: number; + grid_origin_y: number; + exposure_time: number; +}; + +function SpectroscopyForm() { + const [formData, setFormData] = useState({ + total_number_of_scan_points: 25, + grid_size: 5.0, + grid_origin_x: 0.0, + grid_origin_y: 0.0, + exposure_time: 0.1, + }); + + const { instrumentSession, setInstrumentSession } = useInstrumentSession(); + + return ( + + + + + + + + + setInstrumentSession(e.target.value)} + /> + + + + + + ); +} + +export default SpectroscopyForm; diff --git a/apps/visr/src/components/SubmissionForm.tsx b/apps/visr/src/components/SubmissionForm.tsx new file mode 100644 index 0000000..5500cc0 --- /dev/null +++ b/apps/visr/src/components/SubmissionForm.tsx @@ -0,0 +1,30 @@ +import { useFragment } from "react-relay"; +import { type JSONObject, type Visit } from "../utils/types"; +import type { JsonSchema, UISchemaElement } from "@jsonforms/core"; +import type { workflowTemplateFragment$key } from "../graphql/__generated__/workflowTemplateFragment.graphql"; +import { workflowTemplateFragment } from "../graphql/workflowTemplateFragment"; +import TemplateSubmissionForm from "./TemplateSubmissionForm"; + +const SubmissionForm = (props: { + template: workflowTemplateFragment$key; + prepopulatedParameters?: JSONObject; + visit?: Visit; + onSubmit: (visit: Visit, parameters: object) => void; +}) => { + const data = useFragment(workflowTemplateFragment, props.template); + return ( + + ); +}; + +export default SubmissionForm; diff --git a/apps/visr/src/components/SubmittedMessagesList.tsx b/apps/visr/src/components/SubmittedMessagesList.tsx new file mode 100644 index 0000000..1b68e49 --- /dev/null +++ b/apps/visr/src/components/SubmittedMessagesList.tsx @@ -0,0 +1,124 @@ +import React from "react"; +import { + Accordion, + AccordionSummary, + AccordionDetails, + Box, + Divider, + Paper, + Typography, +} from "@mui/material"; +import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; +import { Link } from "react-router-dom"; +import type { + SubmissionGraphQLErrorMessage, + SubmissionNetworkErrorMessage, + SubmissionSuccessMessage, +} from "../utils/types"; + +const renderSubmittedMessage = ( + r: + | SubmissionGraphQLErrorMessage + | SubmissionNetworkErrorMessage + | SubmissionSuccessMessage, + i: number, +) => { + switch (r.type) { + case "success": + return ( + {}} + > + + + Successfully submitted{" "} + + {r.message} + + + + + ); + + case "networkError": + return ( + + }> + + Submission error type {r.error.name} + + + + Submission error message {r.error.message} + + + ); + case "graphQLError": + default: + return ( + + }> + + Submission error type GraphQL + + + + {r.errors.map((e, j) => { + return ( + + Error {j} {e.message} + + ); + })} + + + ); + } +}; + +interface SubmittedMessagesListProps { + submissionResults: ( + | SubmissionGraphQLErrorMessage + | SubmissionNetworkErrorMessage + | SubmissionSuccessMessage + )[]; +} + +const SubmittedMessagesList: React.FC = ({ + submissionResults, +}) => { + return ( + + + {submissionResults.length > 0 && ( + <> + + + Submissions + + + {submissionResults.map((r, i) => renderSubmittedMessage(r, i))} + + )} + + ); +}; + +export default SubmittedMessagesList; diff --git a/apps/visr/src/components/TemplateSubmissionForm.tsx b/apps/visr/src/components/TemplateSubmissionForm.tsx new file mode 100644 index 0000000..5296979 --- /dev/null +++ b/apps/visr/src/components/TemplateSubmissionForm.tsx @@ -0,0 +1,112 @@ +import { + materialCells, + materialRenderers, +} from "@jsonforms/material-renderers"; +import { + type JsonSchema, + type UISchemaElement, + createAjv, +} from "@jsonforms/core"; +import { JsonForms } from "@jsonforms/react"; +import React, { useState } from "react"; +import { Divider, Snackbar, Stack, Typography, useTheme } from "@mui/material"; +import type { ErrorObject } from "ajv"; +import type { JSONObject, Visit } from "../utils/types"; +import { VisitInput } from "@diamondlightsource/sci-react-ui"; + +interface TemplateSubmissionFormProps { + title: string; + maintainer: string; + repository?: string | null; + description?: string; + parametersSchema: JsonSchema; + parametersUISchema?: UISchemaElement; + prepopulatedParameters?: JSONObject; + visit?: Visit; + onSubmit: (visit: Visit, parameters: object) => void; +} + +const TemplateSubmissionForm: React.FC = ({ + title, + maintainer, + repository, + description, + parametersSchema, + parametersUISchema, + prepopulatedParameters, + visit, + onSubmit, +}) => { + const theme = useTheme(); + const validator = createAjv({ useDefaults: true, coerceTypes: true }); + + const [parameters, setParameters] = useState(prepopulatedParameters ?? {}); + const [errors, setErrors] = useState([]); + + const [submitted, setSubmitted] = useState(false); + + const onClick = (visit: Visit, parameters?: object) => { + if (errors.length === 0) { + onSubmit(visit, parameters ?? {}); + setSubmitted(true); + } + }; + + const handleCloseSnackbar = () => { + setSubmitted(false); + }; + const formWidth = + (parametersUISchema?.options?.formWidth as string | undefined) ?? "100%"; + + return ( + + + {title} + + + {description} + + + Maintainer: {maintainer} + + {repository && ( + + Repository: {repository} + + )} + + { + setParameters(data as JSONObject); + setErrors(errors ? errors : []); + }} + data-testid="paramters-form" + /> + + + + + ); +}; + +export default TemplateSubmissionForm; diff --git a/apps/visr/src/components/TemplateView.tsx b/apps/visr/src/components/TemplateView.tsx new file mode 100644 index 0000000..c0d529b --- /dev/null +++ b/apps/visr/src/components/TemplateView.tsx @@ -0,0 +1,122 @@ +import { useState } from "react"; +import { useLazyLoadQuery, useMutation } from "react-relay/hooks"; +import { graphql } from "relay-runtime"; +import type { + JSONObject, + SubmissionGraphQLErrorMessage, + SubmissionNetworkErrorMessage, + SubmissionSuccessMessage, +} from "../utils/types"; +import { type Visit, visitToText } from "@diamondlightsource/sci-react-ui"; +import SubmissionForm from "./SubmissionForm"; +import type { TemplateViewQuery as TemplateViewQueryType } from "./__generated__/TemplateViewQuery.graphql"; +import type { TemplateViewMutation as TemplateViewMutationType } from "./__generated__/TemplateViewMutation.graphql"; +import { visitTextToVisit } from "../utils/common"; +import { Box } from "@mui/material"; +import SubmittedMessagesList from "./SubmittedMessagesList"; + +const templateViewQuery = graphql` + query TemplateViewQuery($templateName: String!) { + workflowTemplate(name: $templateName) { + ...workflowTemplateFragment + } + } +`; + +const templateViewMutation = graphql` + mutation TemplateViewMutation( + $templateName: String! + $visit: VisitInput! + $parameters: JSON! + ) { + submitWorkflowTemplate( + name: $templateName + visit: $visit + parameters: $parameters + ) { + name + } + } +`; + +export default function TemplateView({ + templateName, + visit, + prepopulatedParameters, +}: { + templateName: string; + visit?: Visit; + prepopulatedParameters?: JSONObject; +}) { + const data = useLazyLoadQuery(templateViewQuery, { + templateName, + }); + const [submissionResults, setSubmissionResults] = useState< + ( + | SubmissionSuccessMessage + | SubmissionNetworkErrorMessage + | SubmissionGraphQLErrorMessage + )[] + >([]); + + const storedVisit = visitTextToVisit( + localStorage.getItem("instrumentSessionID") ?? "", + ); + + const [commitMutation] = + useMutation(templateViewMutation); + + function submitWorkflow(visit: Visit, parameters: object) { + commitMutation({ + variables: { + templateName: templateName, + visit: visit, + parameters: parameters, + }, + onCompleted: (response, errors) => { + if (errors?.length) { + console.error("GraphQL errors:", errors); + setSubmissionResults(prev => [ + { + type: "graphQLError", + errors: errors, + }, + ...prev, + ]); + } else { + const submittedName = response.submitWorkflowTemplate.name; + console.log("Successfully submitted:", submittedName); + setSubmissionResults(prev => [ + { + type: "success", + message: `${visitToText(visit)}/${submittedName}`, + }, + ...prev, + ]); + localStorage.setItem("instrumentSessionID", visitToText(visit)); + } + }, + onError: err => { + console.error("Submission failed:", err); + setSubmissionResults(prev => [ + { + type: "networkError", + error: err, + }, + ...prev, + ]); + }, + }); + } + return ( + + + + + ); +} diff --git a/apps/visr/src/components/VisrNavbar.tsx b/apps/visr/src/components/VisrNavbar.tsx new file mode 100644 index 0000000..12df262 --- /dev/null +++ b/apps/visr/src/components/VisrNavbar.tsx @@ -0,0 +1,48 @@ +import { Box } from "@mui/material"; +import { Link } from "react-router-dom"; +import { + ColourSchemeButton, + Navbar, + NavLink, + NavLinks, +} from "@diamondlightsource/sci-react-ui"; + +function VisrNavbar() { + return ( + + + + Spectroscopy + + + Plans + + + Workflows + + + + } + rightSlot={ + + + + } + /> + ); +} + +export default VisrNavbar; diff --git a/apps/visr/src/components/__generated__/TemplateViewMutation.graphql.ts b/apps/visr/src/components/__generated__/TemplateViewMutation.graphql.ts new file mode 100644 index 0000000..0a92c0c --- /dev/null +++ b/apps/visr/src/components/__generated__/TemplateViewMutation.graphql.ts @@ -0,0 +1,122 @@ +/** + * @generated SignedSource<> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest } from 'relay-runtime'; +export type VisitInput = { + number: number; + proposalCode: string; + proposalNumber: number; +}; +export type TemplateViewMutation$variables = { + parameters: any; + templateName: string; + visit: VisitInput; +}; +export type TemplateViewMutation$data = { + readonly submitWorkflowTemplate: { + readonly name: string; + }; +}; +export type TemplateViewMutation = { + response: TemplateViewMutation$data; + variables: TemplateViewMutation$variables; +}; + +const node: ConcreteRequest = (function(){ +var v0 = { + "defaultValue": null, + "kind": "LocalArgument", + "name": "parameters" +}, +v1 = { + "defaultValue": null, + "kind": "LocalArgument", + "name": "templateName" +}, +v2 = { + "defaultValue": null, + "kind": "LocalArgument", + "name": "visit" +}, +v3 = [ + { + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "name", + "variableName": "templateName" + }, + { + "kind": "Variable", + "name": "parameters", + "variableName": "parameters" + }, + { + "kind": "Variable", + "name": "visit", + "variableName": "visit" + } + ], + "concreteType": "Workflow", + "kind": "LinkedField", + "name": "submitWorkflowTemplate", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + } + ], + "storageKey": null + } +]; +return { + "fragment": { + "argumentDefinitions": [ + (v0/*: any*/), + (v1/*: any*/), + (v2/*: any*/) + ], + "kind": "Fragment", + "metadata": null, + "name": "TemplateViewMutation", + "selections": (v3/*: any*/), + "type": "Mutation", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [ + (v1/*: any*/), + (v2/*: any*/), + (v0/*: any*/) + ], + "kind": "Operation", + "name": "TemplateViewMutation", + "selections": (v3/*: any*/) + }, + "params": { + "cacheID": "b9ea26832024cbe126ef60c2639577e9", + "id": null, + "metadata": {}, + "name": "TemplateViewMutation", + "operationKind": "mutation", + "text": "mutation TemplateViewMutation(\n $templateName: String!\n $visit: VisitInput!\n $parameters: JSON!\n) {\n submitWorkflowTemplate(name: $templateName, visit: $visit, parameters: $parameters) {\n name\n }\n}\n" + } +}; +})(); + +(node as any).hash = "660c30e6a917a69df3bd2b5cbf6a3379"; + +export default node; diff --git a/apps/visr/src/components/__generated__/TemplateViewQuery.graphql.ts b/apps/visr/src/components/__generated__/TemplateViewQuery.graphql.ts new file mode 100644 index 0000000..32f7d5a --- /dev/null +++ b/apps/visr/src/components/__generated__/TemplateViewQuery.graphql.ts @@ -0,0 +1,149 @@ +/** + * @generated SignedSource<> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest } from 'relay-runtime'; +import { FragmentRefs } from "relay-runtime"; +export type TemplateViewQuery$variables = { + templateName: string; +}; +export type TemplateViewQuery$data = { + readonly workflowTemplate: { + readonly " $fragmentSpreads": FragmentRefs<"workflowTemplateFragment">; + }; +}; +export type TemplateViewQuery = { + response: TemplateViewQuery$data; + variables: TemplateViewQuery$variables; +}; + +const node: ConcreteRequest = (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "templateName" + } +], +v1 = [ + { + "kind": "Variable", + "name": "name", + "variableName": "templateName" + } +]; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "TemplateViewQuery", + "selections": [ + { + "alias": null, + "args": (v1/*: any*/), + "concreteType": "WorkflowTemplate", + "kind": "LinkedField", + "name": "workflowTemplate", + "plural": false, + "selections": [ + { + "args": null, + "kind": "FragmentSpread", + "name": "workflowTemplateFragment" + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "TemplateViewQuery", + "selections": [ + { + "alias": null, + "args": (v1/*: any*/), + "concreteType": "WorkflowTemplate", + "kind": "LinkedField", + "name": "workflowTemplate", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "maintainer", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "title", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "description", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "arguments", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "uiSchema", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "repository", + "storageKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "9fb9e57ce71efced848172180274ed2a", + "id": null, + "metadata": {}, + "name": "TemplateViewQuery", + "operationKind": "query", + "text": "query TemplateViewQuery(\n $templateName: String!\n) {\n workflowTemplate(name: $templateName) {\n ...workflowTemplateFragment\n }\n}\n\nfragment workflowTemplateFragment on WorkflowTemplate {\n name\n maintainer\n title\n description\n arguments\n uiSchema\n repository\n}\n" + } +}; +})(); + +(node as any).hash = "907837f3f9cf4408bf964271f9a981d1"; + +export default node; diff --git a/apps/visr/src/components/useSpectroscopyData.ts b/apps/visr/src/components/useSpectroscopyData.ts new file mode 100644 index 0000000..3090a3c --- /dev/null +++ b/apps/visr/src/components/useSpectroscopyData.ts @@ -0,0 +1,104 @@ +import { useEffect, useRef, useState } from "react"; +import { type NDT } from "@diamondlightsource/davidia"; + +export type RGBColour = "red" | "green" | "blue" | "gray"; + +export type FetchMapFunction = ( + filepath: string, + datapath: string, + colour: RGBColour, +) => Promise; + +export interface DataChannels { + red: NDT | null; + green: NDT | null; + blue: NDT | null; +} + +export interface ScanEventMessage { + status: "running" | "finished" | "failed"; + filepath: string; + uuid: string; +} + +export function useSpectroscopyData(fetchMap: FetchMapFunction) { + const [running, setRunning] = useState(false); + const [filepath, setFilepath] = useState(null); + const [data, setData] = useState({ + red: null, + green: null, + blue: null, + }); + + /** Cached interval id */ + const pollInterval = useRef | null>(null); + + // Subscribe to SSE + useEffect(() => { + const evtSource = new EventSource("/api/data/events"); + + evtSource.onmessage = event => { + try { + const msg = JSON.parse(event.data) as ScanEventMessage; + console.log("SSE message:", msg); + + if (msg.status === "running") { + setRunning(true); + setFilepath(msg.filepath); + } else if (msg.status === "finished" || msg.status === "failed") { + setRunning(false); // triggers final poll below + } + } catch (err) { + console.error("Error parsing SSE:", err); + } + }; + + evtSource.onerror = err => { + console.warn("Temporary SSE connection error:", err); + }; + + evtSource.onopen = () => { + console.log("SSE connection opened or re-established"); + }; + + return () => evtSource.close(); + }, []); + + // Poll during scan + once more afterwards + useEffect(() => { + async function poll() { + if (!filepath) return; + try { + const basePath = "/entry/instrument/spectroscopy_detector/"; + const [red, green, blue] = await Promise.all([ + fetchMap(filepath, basePath + "RedTotal", "red"), + fetchMap(filepath, basePath + "GreenTotal", "green"), + fetchMap(filepath, basePath + "BlueTotal", "blue"), + ]); + setData({ red, green, blue }); + } catch (err) { + console.error("Polling error:", err); + } + } + + if (running && filepath) { + // start polling + pollInterval.current = setInterval(poll, 500); // 2 Hz + } else if (!running && pollInterval.current) { + // poll once more then clear interval + poll().finally(() => { + clearInterval(pollInterval.current!); + pollInterval.current = null; + }); + } + + return () => { + if (pollInterval.current) { + clearInterval(pollInterval.current); + pollInterval.current = null; + } + }; + }, [running, filepath, fetchMap]); + + return { data, running }; +} diff --git a/apps/visr/src/context/instrumentSession/InstrumentSessionContext.ts b/apps/visr/src/context/instrumentSession/InstrumentSessionContext.ts new file mode 100644 index 0000000..74a9c93 --- /dev/null +++ b/apps/visr/src/context/instrumentSession/InstrumentSessionContext.ts @@ -0,0 +1,10 @@ +import { createContext } from "react"; + +export type InstrumentSessionContextType = { + instrumentSession: string; + setInstrumentSession: (session: string) => void; +}; + +export const InstrumentSessionContext = createContext< + InstrumentSessionContextType | undefined +>(undefined); diff --git a/apps/visr/src/context/instrumentSession/InstrumentSessionProvider.tsx b/apps/visr/src/context/instrumentSession/InstrumentSessionProvider.tsx new file mode 100644 index 0000000..056918d --- /dev/null +++ b/apps/visr/src/context/instrumentSession/InstrumentSessionProvider.tsx @@ -0,0 +1,28 @@ +import { useState, useEffect, type ReactNode } from "react"; +import { InstrumentSessionContext } from "./InstrumentSessionContext"; + +const STORAGE_KEY = "instrument-session-id"; + +export const InstrumentSessionProvider = ({ + children, + defaultSessionId = "cm40661-6", +}: { + children: ReactNode; + defaultSessionId?: string; +}) => { + const [instrumentSession, setInstrumentSession] = useState(() => { + return localStorage.getItem(STORAGE_KEY) ?? defaultSessionId; + }); + + useEffect(() => { + localStorage.setItem(STORAGE_KEY, instrumentSession); + }, [instrumentSession]); + + return ( + + {children} + + ); +}; diff --git a/apps/visr/src/context/instrumentSession/useInstrumentSession.ts b/apps/visr/src/context/instrumentSession/useInstrumentSession.ts new file mode 100644 index 0000000..2b19f86 --- /dev/null +++ b/apps/visr/src/context/instrumentSession/useInstrumentSession.ts @@ -0,0 +1,12 @@ +import { useContext } from "react"; +import { InstrumentSessionContext } from "./InstrumentSessionContext"; + +export const useInstrumentSession = () => { + const context = useContext(InstrumentSessionContext); + if (!context) { + throw new Error( + "useInstrumentSession must be used within InstrumentSessionProvider", + ); + } + return context; +}; diff --git a/apps/visr/src/graphql/__generated__/workflowTemplateFragment.graphql.ts b/apps/visr/src/graphql/__generated__/workflowTemplateFragment.graphql.ts new file mode 100644 index 0000000..f519396 --- /dev/null +++ b/apps/visr/src/graphql/__generated__/workflowTemplateFragment.graphql.ts @@ -0,0 +1,90 @@ +/** + * @generated SignedSource<<7eb6c59915242a110bb98268ee6f12dc>> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ReaderFragment } from 'relay-runtime'; +import { FragmentRefs } from "relay-runtime"; +export type workflowTemplateFragment$data = { + readonly arguments: any; + readonly description: string | null | undefined; + readonly maintainer: string; + readonly name: string; + readonly repository: string | null | undefined; + readonly title: string | null | undefined; + readonly uiSchema: any | null | undefined; + readonly " $fragmentType": "workflowTemplateFragment"; +}; +export type workflowTemplateFragment$key = { + readonly " $data"?: workflowTemplateFragment$data; + readonly " $fragmentSpreads": FragmentRefs<"workflowTemplateFragment">; +}; + +const node: ReaderFragment = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "workflowTemplateFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "maintainer", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "title", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "description", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "arguments", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "uiSchema", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "repository", + "storageKey": null + } + ], + "type": "WorkflowTemplate", + "abstractKey": null +}; + +(node as any).hash = "959850317e316a423e70b0c89c011f27"; + +export default node; diff --git a/apps/visr/src/graphql/__generated__/workflowsQuery.graphql.ts b/apps/visr/src/graphql/__generated__/workflowsQuery.graphql.ts new file mode 100644 index 0000000..d55fe50 --- /dev/null +++ b/apps/visr/src/graphql/__generated__/workflowsQuery.graphql.ts @@ -0,0 +1,205 @@ +/** + * @generated SignedSource<> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest } from 'relay-runtime'; +export type VisitInput = { + number: number; + proposalCode: string; + proposalNumber: number; +}; +export type WorkflowFilter = { + creator?: any | null | undefined; + template?: any | null | undefined; + workflowStatusFilter?: WorkflowStatusFilter | null | undefined; +}; +export type WorkflowStatusFilter = { + error?: boolean; + failed?: boolean; + pending?: boolean; + running?: boolean; + succeeded?: boolean; +}; +export type workflowsQuery$variables = { + cursor?: string | null | undefined; + filter?: WorkflowFilter | null | undefined; + limit: number; + visit: VisitInput; +}; +export type workflowsQuery$data = { + readonly workflows: { + readonly nodes: ReadonlyArray<{ + readonly name: string; + }>; + readonly pageInfo: { + readonly endCursor: string | null | undefined; + readonly hasNextPage: boolean; + readonly hasPreviousPage: boolean; + readonly startCursor: string | null | undefined; + }; + }; +}; +export type workflowsQuery = { + response: workflowsQuery$data; + variables: workflowsQuery$variables; +}; + +const node: ConcreteRequest = (function(){ +var v0 = { + "defaultValue": null, + "kind": "LocalArgument", + "name": "cursor" +}, +v1 = { + "defaultValue": null, + "kind": "LocalArgument", + "name": "filter" +}, +v2 = { + "defaultValue": null, + "kind": "LocalArgument", + "name": "limit" +}, +v3 = { + "defaultValue": null, + "kind": "LocalArgument", + "name": "visit" +}, +v4 = [ + { + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "cursor", + "variableName": "cursor" + }, + { + "kind": "Variable", + "name": "filter", + "variableName": "filter" + }, + { + "kind": "Variable", + "name": "limit", + "variableName": "limit" + }, + { + "kind": "Variable", + "name": "visit", + "variableName": "visit" + } + ], + "concreteType": "WorkflowConnection", + "kind": "LinkedField", + "name": "workflows", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Workflow", + "kind": "LinkedField", + "name": "nodes", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + } + ], + "storageKey": null + }, + { + "alias": null, + "args": null, + "concreteType": "PageInfo", + "kind": "LinkedField", + "name": "pageInfo", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "endCursor", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "hasNextPage", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "hasPreviousPage", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "startCursor", + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": null + } +]; +return { + "fragment": { + "argumentDefinitions": [ + (v0/*: any*/), + (v1/*: any*/), + (v2/*: any*/), + (v3/*: any*/) + ], + "kind": "Fragment", + "metadata": null, + "name": "workflowsQuery", + "selections": (v4/*: any*/), + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [ + (v3/*: any*/), + (v0/*: any*/), + (v2/*: any*/), + (v1/*: any*/) + ], + "kind": "Operation", + "name": "workflowsQuery", + "selections": (v4/*: any*/) + }, + "params": { + "cacheID": "6e6da56e134de097d20cafd8abe7e0aa", + "id": null, + "metadata": {}, + "name": "workflowsQuery", + "operationKind": "query", + "text": "query workflowsQuery(\n $visit: VisitInput!\n $cursor: String\n $limit: Int!\n $filter: WorkflowFilter\n) {\n workflows(visit: $visit, cursor: $cursor, limit: $limit, filter: $filter) {\n nodes {\n name\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n}\n" + } +}; +})(); + +(node as any).hash = "7451d3fab389359463e7424e2fcaa8d0"; + +export default node; diff --git a/apps/visr/src/graphql/workflowTemplateFragment.ts b/apps/visr/src/graphql/workflowTemplateFragment.ts new file mode 100644 index 0000000..28db243 --- /dev/null +++ b/apps/visr/src/graphql/workflowTemplateFragment.ts @@ -0,0 +1,13 @@ +import { graphql } from "react-relay"; + +export const workflowTemplateFragment = graphql` + fragment workflowTemplateFragment on WorkflowTemplate { + name + maintainer + title + description + arguments + uiSchema + repository + } +`; diff --git a/apps/visr/src/graphql/workflowsQuery.ts b/apps/visr/src/graphql/workflowsQuery.ts new file mode 100644 index 0000000..e194444 --- /dev/null +++ b/apps/visr/src/graphql/workflowsQuery.ts @@ -0,0 +1,22 @@ +import { graphql } from "relay-runtime"; + +export const workflowsQuery = graphql` + query workflowsQuery( + $visit: VisitInput! + $cursor: String + $limit: Int! + $filter: WorkflowFilter + ) { + workflows(visit: $visit, cursor: $cursor, limit: $limit, filter: $filter) { + nodes { + name + } + pageInfo { + endCursor + hasNextPage + hasPreviousPage + startCursor + } + } + } +`; diff --git a/apps/visr/src/main.tsx b/apps/visr/src/main.tsx new file mode 100644 index 0000000..e29eeb5 --- /dev/null +++ b/apps/visr/src/main.tsx @@ -0,0 +1,67 @@ +import { DiamondTheme, ThemeProvider } from "@diamondlightsource/sci-react-ui"; +import { RouterProvider, createBrowserRouter } from "react-router-dom"; + +import Dashboard from "./routes/Dashboard.tsx"; +import { InstrumentSessionProvider } from "./context/instrumentSession/InstrumentSessionProvider.tsx"; +import JsonFormsPlans from "./routes/Plans.tsx"; +import { Layout } from "./routes/Layout.tsx"; +import Spectroscopy from "./routes/Spectroscopy.tsx"; +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; + +declare global { + interface Window { + global?: typeof globalThis; + } +} + +window.global ||= window; +import Workflows from "./routes/Workflows.tsx"; +import { RelayEnvironmentProvider } from "react-relay"; +import { RelayEnvironment } from "./RelayEnvironment.ts"; + +async function enableMocking() { + if (import.meta.env.DEV) { + const { worker } = await import("./mocks/browser"); + return worker.start(); + } +} + +const router = createBrowserRouter([ + { + path: "/", + element: , + children: [ + { + index: true, + element: , + }, + { + path: "spectroscopy", + element: , + }, + { + path: "plans", + element: , + }, + { + path: "/workflows", + element: , + }, + ], + }, +]); + +enableMocking().then(() => { + createRoot(document.getElementById("root")!).render( + + + + + + + + + , + ); +}); diff --git a/apps/visr/src/mocks/browser.ts b/apps/visr/src/mocks/browser.ts new file mode 100644 index 0000000..bcd82e4 --- /dev/null +++ b/apps/visr/src/mocks/browser.ts @@ -0,0 +1,4 @@ +import { setupWorker } from "msw/browser"; +import { handlers } from "./handlers"; + +export const worker = setupWorker(...handlers); diff --git a/apps/visr/src/mocks/handlers.ts b/apps/visr/src/mocks/handlers.ts new file mode 100644 index 0000000..b716c96 --- /dev/null +++ b/apps/visr/src/mocks/handlers.ts @@ -0,0 +1,122 @@ +import { http, HttpResponse } from "msw"; +import workflowsResponse from "./workflows-response.json"; +import plansResponse from "./plans-response.json"; +import instrumentSessionsResponse from "./instrumentSessions-response.json"; +import type { ScanEventMessage } from "../components/useSpectroscopyData"; + +const fakeTaskId = "7304e8e0-81c6-4978-9a9d-9046ab79ce3c"; + +function mapData(): (number | null)[][] { + return [ + [ + 5467227.0, 5467227.0, 5480663.0, 5478486.0, 5477020.0, 5474645.0, + 5472603.0, 5470330.0, 5468827.0, 5467346.0, + ], + [ + 5465947.0, 5464940.0, 5483401.0, 5480384.0, 5477378.0, 5474776.0, + 5471462.0, 5450634.0, 5454651.0, 5465208.0, + ], + [ + 5463907.0, 5463469.0, 5481975.0, 5479338.0, 5476649.0, 5474993.0, + 5473529.0, 5471431.0, 5470030.0, 5468721.0, + ], + [ + 5466907.0, 5466023.0, 5483679.0, 5480875.0, 5478044.0, 5475180.0, + 5473196.0, 5471168.0, 5469539.0, 5468190.0, + ], + [ + 5466626.0, 5465494.0, 5483503.0, 5480586.0, 5477659.0, 5475069.0, + 5473287.0, 5471380.0, 5469737.0, 5468496.0, + ], + [ + 5466713.0, 5465920.0, 5483587.0, 5480582.0, 5477829.0, 5475202.0, + 5473244.0, 5471103.0, 5468010.0, 5468222.0, + ], + [5467700.0, null, null, null, null, null, null, null, null, null], + [null, null, null, null, null, null, null, null, null, null], + [null, null, null, null, null, null, null, null, null, null], + [null, null, null, null, null, null, null, null, null, null], + ]; +} + +export const handlers = [ + http.post("/api/graphql", request => { + const referrer = request.request.referrer; + if (referrer.search("workflows") > 0) { + return HttpResponse.json(workflowsResponse); + } else { + return HttpResponse.json(instrumentSessionsResponse); + } + }), + + http.get("/api/plans", () => { + return HttpResponse.json(plansResponse); + }), + + http.put("/api/worker/task", () => { + return HttpResponse.json({ + task_id: fakeTaskId, + }); + }), + + http.post("/api/tasks", () => { + return HttpResponse.json({ + task_id: fakeTaskId, + }); + }), + + http.get("/api/data/map", ({ request }) => { + const url = new URL(request.url); + const filepath = url.searchParams.get("filepath"); + const datapath = url.searchParams.get("datapath"); + console.log("Mock /api/data/map called", { filepath, datapath }); + const data = mapData(); + return HttpResponse.json({ values: data }); + }), + + http.get("/api/data/events", async () => { + const encoder = new TextEncoder(); + + // Create a ReadableStream that emits fake scan events + const stream = new ReadableStream({ + start(controller) { + const send = (event: ScanEventMessage) => { + controller.enqueue( + encoder.encode(`data: ${JSON.stringify(event)}\n\n`), + ); + }; + + // scan starts + send({ + uuid: "fake-scan-uuid", + filepath: "/mock/path/fake.nxs", + status: "running", + }); + + // simulate data collection for ~5 seconds + let counter = 0; + const interval = setInterval(() => { + counter++; + console.log("Mock event tick", counter); + if (counter >= 25) { + clearInterval(interval); + // Scan stops + send({ + uuid: "fake-scan-uuid", + filepath: "/mock/path/fake.nxs", + status: "finished", + }); + } + }, 200); + }, + }); + + return new HttpResponse(stream, { + headers: { + "Content-Type": "text/event-stream", + "Cache-Control": "no-cache", + Connection: "keep-alive", + }, + }); + }), +]; diff --git a/apps/visr/src/mocks/instrumentSessions-response.json b/apps/visr/src/mocks/instrumentSessions-response.json new file mode 100644 index 0000000..c4629c7 --- /dev/null +++ b/apps/visr/src/mocks/instrumentSessions-response.json @@ -0,0 +1,71 @@ +{ + "data": { + "instrument": { + "instrumentSessions": [ + { + "instrumentSessionNumber": 1, + "proposal": { + "proposalCategory": "CM", + "proposalNumber": 39125 + } + }, + { + "instrumentSessionNumber": 2, + "proposal": { + "proposalCategory": "CM", + "proposalNumber": 39125 + } + }, + { + "instrumentSessionNumber": 3, + "proposal": { + "proposalCategory": "CM", + "proposalNumber": 39125 + } + }, + { + "instrumentSessionNumber": 1, + "proposal": { + "proposalCategory": "CM", + "proposalNumber": 40661 + } + }, + { + "instrumentSessionNumber": 2, + "proposal": { + "proposalCategory": "CM", + "proposalNumber": 40661 + } + }, + { + "instrumentSessionNumber": 3, + "proposal": { + "proposalCategory": "CM", + "proposalNumber": 40661 + } + }, + { + "instrumentSessionNumber": 4, + "proposal": { + "proposalCategory": "CM", + "proposalNumber": 40661 + } + }, + { + "instrumentSessionNumber": 5, + "proposal": { + "proposalCategory": "CM", + "proposalNumber": 40661 + } + }, + { + "instrumentSessionNumber": 6, + "proposal": { + "proposalCategory": "CM", + "proposalNumber": 40661 + } + } + ] + } + } +} diff --git a/apps/visr/src/mocks/plans-response.json b/apps/visr/src/mocks/plans-response.json new file mode 100644 index 0000000..ee809a5 --- /dev/null +++ b/apps/visr/src/mocks/plans-response.json @@ -0,0 +1,868 @@ +{ + "plans": [ + { + "name": "hi_plan", + "description": "Says hi to you", + "schema": { + "properties": { "name": { "title": "Name", "type": "string" } } + } + }, + { + "name": "count", + "description": "Reads from a number of devices.\n Wraps bluesky.plans.count(det, num, delay, md=metadata) exposing only serializable\n parameters and metadata.", + "schema": { + "additionalProperties": false, + "properties": { + "detectors": { + "items": { + "enum": [ + "sample_stage", + "sample_det", + "oav", + "synchrotron", + "panda" + ], + "type": "bluesky.protocols.Readable" + }, + "title": "Detectors", + "type": "array", + "uniqueItems": true + }, + "num": { + "title": "Num", + "type": "integer" + }, + "delay": { + "anyOf": [ + { + "type": "number" + }, + { + "items": { + "type": "number" + }, + "type": "array" + } + ], + "title": "Delay" + }, + "metadata": { + "title": "Metadata", + "type": "object" + } + }, + "required": ["detectors"], + "title": "count", + "type": "object" + } + }, + { + "name": "spec_scan", + "description": "Generic plan for reading `detectors` at every point of a ScanSpec `Spec`.\n A `Spec` is an N-dimensional path.\n ", + "schema": { + "$defs": { + "Circle_Reference_": { + "additionalProperties": false, + "description": "Mask contains points of axis within an xy circle of given radius.\n\n.. example_spec::\n\n from scanspec.regions import Circle\n from scanspec.specs import Line\n\n grid = Line(\"y\", 1, 3, 10) * ~Line(\"x\", 0, 2, 10)\n spec = grid & Circle(\"x\", \"y\", 1, 2, 0.9)", + "properties": { + "x_axis": { + "description": "The name matching the x axis of the spec", + "enum": ["sample_stage.x", "sample_stage.y", "sample_stage.z"], + "title": "X Axis", + "type": "bluesky.protocols.Movable" + }, + "y_axis": { + "description": "The name matching the y axis of the spec", + "enum": ["sample_stage.x", "sample_stage.y", "sample_stage.z"], + "title": "Y Axis", + "type": "bluesky.protocols.Movable" + }, + "x_middle": { + "description": "The central x point of the circle", + "title": "X Middle", + "type": "number" + }, + "y_middle": { + "description": "The central y point of the circle", + "title": "Y Middle", + "type": "number" + }, + "radius": { + "description": "Radius of the circle", + "exclusiveMinimum": 0, + "title": "Radius", + "type": "number" + }, + "type": { + "const": "Circle", + "default": "Circle", + "title": "Type", + "type": "string" + } + }, + "required": ["x_axis", "y_axis", "x_middle", "y_middle", "radius"], + "title": "Circle", + "type": "object" + }, + "CombinationOf_Reference_": { + "additionalProperties": false, + "description": "Abstract baseclass for a combination of two regions, left and right.", + "properties": { + "left": { + "$ref": "#/$defs/Region", + "description": "The left-hand Region to combine" + }, + "right": { + "$ref": "#/$defs/Region", + "description": "The right-hand Region to combine" + }, + "type": { + "const": "CombinationOf", + "default": "CombinationOf", + "title": "Type", + "type": "string" + } + }, + "required": ["left", "right"], + "title": "CombinationOf", + "type": "object" + }, + "Concat_Reference_": { + "additionalProperties": false, + "description": "Concatenate two Specs together, running one after the other.\n\nEach Dimension of left and right must contain the same axes. Typically\nformed using `Spec.concat`.\n\n.. example_spec::\n\n from scanspec.specs import Line\n\n spec = Line(\"x\", 1, 3, 3).concat(Line(\"x\", 4, 5, 5))", + "properties": { + "left": { + "$ref": "#/$defs/Spec", + "description": "The left-hand Spec to Concat, midpoints will appear earlier" + }, + "right": { + "$ref": "#/$defs/Spec", + "description": "The right-hand Spec to Concat, midpoints will appear later" + }, + "gap": { + "default": false, + "description": "If True, force a gap in the output at the join", + "title": "Gap", + "type": "boolean" + }, + "check_path_changes": { + "default": true, + "description": "If True path through scan will not be modified by squash", + "title": "Check Path Changes", + "type": "boolean" + }, + "type": { + "const": "Concat", + "default": "Concat", + "title": "Type", + "type": "string" + } + }, + "required": ["left", "right"], + "title": "Concat", + "type": "object" + }, + "DifferenceOf_Reference_": { + "additionalProperties": false, + "description": "A point is in DifferenceOf(a, b) if in a and not in b.\n\nTypically created with the ``-`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) - Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False, True, False, False, False])", + "properties": { + "left": { + "$ref": "#/$defs/Region", + "description": "The left-hand Region to combine" + }, + "right": { + "$ref": "#/$defs/Region", + "description": "The right-hand Region to combine" + }, + "type": { + "const": "DifferenceOf", + "default": "DifferenceOf", + "title": "Type", + "type": "string" + } + }, + "required": ["left", "right"], + "title": "DifferenceOf", + "type": "object" + }, + "Ellipse_Reference_": { + "additionalProperties": false, + "description": "Mask contains points of axis within an xy ellipse of given radius.\n\n.. example_spec::\n\n from scanspec.regions import Ellipse\n from scanspec.specs import Line\n\n grid = Line(\"y\", 3, 8, 10) * ~Line(\"x\", 1 ,8, 10)\n spec = grid & Ellipse(\"x\", \"y\", 5, 5, 2, 3, 75)", + "properties": { + "x_axis": { + "description": "The name matching the x axis of the spec", + "enum": ["sample_stage.x", "sample_stage.y", "sample_stage.z"], + "title": "X Axis", + "type": "bluesky.protocols.Movable" + }, + "y_axis": { + "description": "The name matching the y axis of the spec", + "enum": ["sample_stage.x", "sample_stage.y", "sample_stage.z"], + "title": "Y Axis", + "type": "bluesky.protocols.Movable" + }, + "x_middle": { + "description": "The central x point of the ellipse", + "title": "X Middle", + "type": "number" + }, + "y_middle": { + "description": "The central y point of the ellipse", + "title": "Y Middle", + "type": "number" + }, + "x_radius": { + "description": "The radius along the x axis of the ellipse", + "exclusiveMinimum": 0, + "title": "X Radius", + "type": "number" + }, + "y_radius": { + "description": "The radius along the y axis of the ellipse", + "exclusiveMinimum": 0, + "title": "Y Radius", + "type": "number" + }, + "angle": { + "default": 0, + "description": "The angle of the ellipse (degrees)", + "title": "Angle", + "type": "number" + }, + "type": { + "const": "Ellipse", + "default": "Ellipse", + "title": "Type", + "type": "string" + } + }, + "required": [ + "x_axis", + "y_axis", + "x_middle", + "y_middle", + "x_radius", + "y_radius" + ], + "title": "Ellipse", + "type": "object" + }, + "IntersectionOf_Reference_": { + "additionalProperties": false, + "description": "A point is in IntersectionOf(a, b) if in both a and b.\n\nTypically created with the ``&`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) & Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False, False, True, False, False])", + "properties": { + "left": { + "$ref": "#/$defs/Region", + "description": "The left-hand Region to combine" + }, + "right": { + "$ref": "#/$defs/Region", + "description": "The right-hand Region to combine" + }, + "type": { + "const": "IntersectionOf", + "default": "IntersectionOf", + "title": "Type", + "type": "string" + } + }, + "required": ["left", "right"], + "title": "IntersectionOf", + "type": "object" + }, + "Line_Reference_": { + "additionalProperties": false, + "description": "Linearly spaced frames with start and stop as first and last midpoints.\n\n.. example_spec::\n\n from scanspec.specs import Line\n\n spec = Line(\"x\", 1, 2, 5)", + "properties": { + "axis": { + "description": "An identifier for what to move", + "enum": ["sample_stage.x", "sample_stage.y", "sample_stage.z"], + "title": "Axis", + "type": "bluesky.protocols.Movable" + }, + "start": { + "description": "Midpoint of the first point of the line", + "title": "Start", + "type": "number" + }, + "stop": { + "description": "Midpoint of the last point of the line", + "title": "Stop", + "type": "number" + }, + "num": { + "description": "Number of frames to produce", + "minimum": 1, + "title": "Num", + "type": "integer" + }, + "type": { + "const": "Line", + "default": "Line", + "title": "Type", + "type": "string" + } + }, + "required": ["axis", "start", "stop", "num"], + "title": "Line", + "type": "object" + }, + "Mask_Reference_": { + "additionalProperties": false, + "description": "Restrict Spec to only midpoints that fall inside the given Region.\n\nTypically created with the ``&`` operator. It also pushes down the\n``& | ^ -`` operators to its `Region` to avoid the need for brackets on\ncombinations of Regions.\n\nIf a Region spans multiple Frames objects, they will be squashed together.\n\n.. example_spec::\n\n from scanspec.regions import Circle\n from scanspec.specs import Line\n\n spec = Line(\"y\", 1, 3, 3) * Line(\"x\", 3, 5, 5) & Circle(\"x\", \"y\", 4, 2, 1.2)\n\nSee Also: `why-squash-can-change-path`", + "properties": { + "spec": { + "$ref": "#/$defs/Spec", + "description": "The Spec containing the source midpoints" + }, + "region": { + "$ref": "#/$defs/Region", + "description": "The Region that midpoints will be inside" + }, + "check_path_changes": { + "default": true, + "description": "If True path through scan will not be modified by squash", + "title": "Check Path Changes", + "type": "boolean" + }, + "type": { + "const": "Mask", + "default": "Mask", + "title": "Type", + "type": "string" + } + }, + "required": ["spec", "region"], + "title": "Mask", + "type": "object" + }, + "Polygon_Reference_": { + "additionalProperties": false, + "description": "Mask contains points of axis within a rotated xy polygon.\n\n.. example_spec::\n\n from scanspec.regions import Polygon\n from scanspec.specs import Line\n\n grid = Line(\"y\", 3, 8, 10) * ~Line(\"x\", 1 ,8, 10)\n spec = grid & Polygon(\"x\", \"y\", [1.0, 6.0, 8.0, 2.0], [4.0, 10.0, 6.0, 1.0])", + "properties": { + "x_axis": { + "description": "The name matching the x axis of the spec", + "enum": ["sample_stage.x", "sample_stage.y", "sample_stage.z"], + "title": "X Axis", + "type": "bluesky.protocols.Movable" + }, + "y_axis": { + "description": "The name matching the y axis of the spec", + "enum": ["sample_stage.x", "sample_stage.y", "sample_stage.z"], + "title": "Y Axis", + "type": "bluesky.protocols.Movable" + }, + "x_verts": { + "description": "The Nx1 x coordinates of the polygons vertices", + "items": { + "type": "number" + }, + "minItems": 3, + "title": "X Verts", + "type": "array" + }, + "y_verts": { + "description": "The Nx1 y coordinates of the polygons vertices", + "items": { + "type": "number" + }, + "minItems": 3, + "title": "Y Verts", + "type": "array" + }, + "type": { + "const": "Polygon", + "default": "Polygon", + "title": "Type", + "type": "string" + } + }, + "required": ["x_axis", "y_axis", "x_verts", "y_verts"], + "title": "Polygon", + "type": "object" + }, + "Product_Reference_": { + "additionalProperties": false, + "description": "Outer product of two Specs, nesting inner within outer.\n\nThis means that inner will run in its entirety at each point in outer.\n\n.. example_spec::\n\n from scanspec.specs import Line\n\n spec = Line(\"y\", 1, 2, 3) * Line(\"x\", 3, 4, 12)", + "properties": { + "outer": { + "$ref": "#/$defs/Spec", + "description": "Will be executed once" + }, + "inner": { + "$ref": "#/$defs/Spec", + "description": "Will be executed len(outer) times" + }, + "type": { + "const": "Product", + "default": "Product", + "title": "Type", + "type": "string" + } + }, + "required": ["outer", "inner"], + "title": "Product", + "type": "object" + }, + "Range_Reference_": { + "additionalProperties": false, + "description": "Mask contains points of axis >= min and <= max.\n\n>>> r = Range(\"x\", 1, 2)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False, True, True, False, False])", + "properties": { + "axis": { + "description": "The name matching the axis to mask in spec", + "enum": ["sample_stage.x", "sample_stage.y", "sample_stage.z"], + "title": "Axis", + "type": "bluesky.protocols.Movable" + }, + "min": { + "description": "The minimum inclusive value in the region", + "title": "Min", + "type": "number" + }, + "max": { + "description": "The minimum inclusive value in the region", + "title": "Max", + "type": "number" + }, + "type": { + "const": "Range", + "default": "Range", + "title": "Type", + "type": "string" + } + }, + "required": ["axis", "min", "max"], + "title": "Range", + "type": "object" + }, + "Rectangle_Reference_": { + "additionalProperties": false, + "description": "Mask contains points of axis within a rotated xy rectangle.\n\n.. example_spec::\n\n from scanspec.regions import Rectangle\n from scanspec.specs import Line\n\n grid = Line(\"y\", 1, 3, 10) * ~Line(\"x\", 0, 2, 10)\n spec = grid & Rectangle(\"x\", \"y\", 0, 1.1, 1.5, 2.1, 30)", + "properties": { + "x_axis": { + "description": "The name matching the x axis of the spec", + "enum": ["sample_stage.x", "sample_stage.y", "sample_stage.z"], + "title": "X Axis", + "type": "bluesky.protocols.Movable" + }, + "y_axis": { + "description": "The name matching the y axis of the spec", + "enum": ["sample_stage.x", "sample_stage.y", "sample_stage.z"], + "title": "Y Axis", + "type": "bluesky.protocols.Movable" + }, + "x_min": { + "description": "Minimum inclusive x value in the region", + "title": "X Min", + "type": "number" + }, + "y_min": { + "description": "Minimum inclusive y value in the region", + "title": "Y Min", + "type": "number" + }, + "x_max": { + "description": "Maximum inclusive x value in the region", + "title": "X Max", + "type": "number" + }, + "y_max": { + "description": "Maximum inclusive y value in the region", + "title": "Y Max", + "type": "number" + }, + "angle": { + "default": 0, + "description": "Clockwise rotation angle of the rectangle", + "title": "Angle", + "type": "number" + }, + "type": { + "const": "Rectangle", + "default": "Rectangle", + "title": "Type", + "type": "string" + } + }, + "required": [ + "x_axis", + "y_axis", + "x_min", + "y_min", + "x_max", + "y_max" + ], + "title": "Rectangle", + "type": "object" + }, + "Region": { + "discriminator": { + "mapping": { + "Circle": "#/$defs/Circle_Reference_", + "CombinationOf": "#/$defs/CombinationOf_Reference_", + "DifferenceOf": "#/$defs/DifferenceOf_Reference_", + "Ellipse": "#/$defs/Ellipse_Reference_", + "IntersectionOf": "#/$defs/IntersectionOf_Reference_", + "Polygon": "#/$defs/Polygon_Reference_", + "Range": "#/$defs/Range_Reference_", + "Rectangle": "#/$defs/Rectangle_Reference_", + "SymmetricDifferenceOf": "#/$defs/SymmetricDifferenceOf_Reference_", + "UnionOf": "#/$defs/UnionOf_Reference_" + }, + "propertyName": "type" + }, + "oneOf": [ + { + "$ref": "#/$defs/Range_Reference_" + }, + { + "$ref": "#/$defs/Rectangle_Reference_" + }, + { + "$ref": "#/$defs/Polygon_Reference_" + }, + { + "$ref": "#/$defs/Circle_Reference_" + }, + { + "$ref": "#/$defs/Ellipse_Reference_" + }, + { + "$ref": "#/$defs/CombinationOf_Reference_" + }, + { + "$ref": "#/$defs/UnionOf_Reference_" + }, + { + "$ref": "#/$defs/IntersectionOf_Reference_" + }, + { + "$ref": "#/$defs/DifferenceOf_Reference_" + }, + { + "$ref": "#/$defs/SymmetricDifferenceOf_Reference_" + } + ] + }, + "Repeat_Reference_": { + "additionalProperties": false, + "description": "Repeat an empty frame num times.\n\nCan be used on the outside of a scan to repeat the same scan many times.\n\n.. example_spec::\n\n from scanspec.specs import Line\n\n spec = 2 * ~Line.bounded(\"x\", 3, 4, 1)\n\nIf you want snaked axes to have no gap between iterations you can do:\n\n.. example_spec::\n\n from scanspec.specs import Line, Repeat\n\n spec = Repeat(2, gap=False) * ~Line.bounded(\"x\", 3, 4, 1)\n\n.. note:: There is no turnaround arrow at x=4", + "properties": { + "num": { + "description": "Number of frames to produce", + "minimum": 1, + "title": "Num", + "type": "integer" + }, + "gap": { + "default": true, + "description": "If False and the slowest of the stack of Frames is snaked then the end and start of consecutive iterations of Spec will have no gap", + "title": "Gap", + "type": "boolean" + }, + "type": { + "const": "Repeat", + "default": "Repeat", + "title": "Type", + "type": "string" + } + }, + "required": ["num"], + "title": "Repeat", + "type": "object" + }, + "Snake_Reference_": { + "additionalProperties": false, + "description": "Run the Spec in reverse on every other iteration when nested.\n\nTypically created with the ``~`` operator.\n\n.. example_spec::\n\n from scanspec.specs import Line\n\n spec = Line(\"y\", 1, 3, 3) * ~Line(\"x\", 3, 5, 5)", + "properties": { + "spec": { + "$ref": "#/$defs/Spec", + "description": "The Spec to run in reverse every other iteration" + }, + "type": { + "const": "Snake", + "default": "Snake", + "title": "Type", + "type": "string" + } + }, + "required": ["spec"], + "title": "Snake", + "type": "object" + }, + "Spec": { + "discriminator": { + "mapping": { + "Concat": "#/$defs/Concat_Reference_", + "Line": "#/$defs/Line_Reference_", + "Mask": "#/$defs/Mask_Reference_", + "Product": "#/$defs/Product_Reference_", + "Repeat": "#/$defs/Repeat_Reference_", + "Snake": "#/$defs/Snake_Reference_", + "Spiral": "#/$defs/Spiral_Reference_", + "Squash": "#/$defs/Squash_Reference_", + "Static": "#/$defs/Static_Reference_", + "Zip": "#/$defs/Zip_Reference_" + }, + "propertyName": "type" + }, + "oneOf": [ + { + "$ref": "#/$defs/Line_Reference_" + }, + { + "$ref": "#/$defs/Static_Reference_" + }, + { + "$ref": "#/$defs/Squash_Reference_" + }, + { + "$ref": "#/$defs/Spiral_Reference_" + }, + { + "$ref": "#/$defs/Product_Reference_" + }, + { + "$ref": "#/$defs/Repeat_Reference_" + }, + { + "$ref": "#/$defs/Zip_Reference_" + }, + { + "$ref": "#/$defs/Snake_Reference_" + }, + { + "$ref": "#/$defs/Concat_Reference_" + }, + { + "$ref": "#/$defs/Mask_Reference_" + } + ] + }, + "Spiral_Reference_": { + "additionalProperties": false, + "description": "Archimedean spiral of \"x_axis\" and \"y_axis\".\n\nStarts at centre point (\"x_start\", \"y_start\") with angle \"rotate\". Produces\n\"num\" points in a spiral spanning width of \"x_range\" and height of \"y_range\"\n\n.. example_spec::\n\n from scanspec.specs import Spiral\n\n spec = Spiral(\"x\", \"y\", 1, 5, 10, 50, 30)", + "properties": { + "x_axis": { + "description": "An identifier for what to move for x", + "enum": ["sample_stage.x", "sample_stage.y", "sample_stage.z"], + "title": "X Axis", + "type": "bluesky.protocols.Movable" + }, + "y_axis": { + "description": "An identifier for what to move for y", + "enum": ["sample_stage.x", "sample_stage.y", "sample_stage.z"], + "title": "Y Axis", + "type": "bluesky.protocols.Movable" + }, + "x_start": { + "description": "x centre of the spiral", + "title": "X Start", + "type": "number" + }, + "y_start": { + "description": "y centre of the spiral", + "title": "Y Start", + "type": "number" + }, + "x_range": { + "description": "x width of the spiral", + "title": "X Range", + "type": "number" + }, + "y_range": { + "description": "y width of the spiral", + "title": "Y Range", + "type": "number" + }, + "num": { + "description": "Number of frames to produce", + "minimum": 1, + "title": "Num", + "type": "integer" + }, + "rotate": { + "default": 0, + "description": "How much to rotate the angle of the spiral", + "title": "Rotate", + "type": "number" + }, + "type": { + "const": "Spiral", + "default": "Spiral", + "title": "Type", + "type": "string" + } + }, + "required": [ + "x_axis", + "y_axis", + "x_start", + "y_start", + "x_range", + "y_range", + "num" + ], + "title": "Spiral", + "type": "object" + }, + "Squash_Reference_": { + "additionalProperties": false, + "description": "Squash a stack of Frames together into a single expanded Frames object.\n\nSee Also:\n `why-squash-can-change-path`\n\n.. example_spec::\n\n from scanspec.specs import Line, Squash\n\n spec = Squash(Line(\"y\", 1, 2, 3) * Line(\"x\", 0, 1, 4))", + "properties": { + "spec": { + "$ref": "#/$defs/Spec", + "description": "The Spec to squash the dimensions of" + }, + "check_path_changes": { + "default": true, + "description": "If True path through scan will not be modified by squash", + "title": "Check Path Changes", + "type": "boolean" + }, + "type": { + "const": "Squash", + "default": "Squash", + "title": "Type", + "type": "string" + } + }, + "required": ["spec"], + "title": "Squash", + "type": "object" + }, + "Static_Reference_": { + "additionalProperties": false, + "description": "A static frame, repeated num times, with axis at value.\n\nCan be used to set axis=value at every point in a scan.\n\n.. example_spec::\n\n from scanspec.specs import Line, Static\n\n spec = Line(\"y\", 1, 2, 3).zip(Static(\"x\", 3))", + "properties": { + "axis": { + "description": "An identifier for what to move", + "enum": ["sample_stage.x", "sample_stage.y", "sample_stage.z"], + "title": "Axis", + "type": "bluesky.protocols.Movable" + }, + "value": { + "description": "The value at each point", + "title": "Value", + "type": "number" + }, + "num": { + "default": 1, + "description": "Number of frames to produce", + "minimum": 1, + "title": "Num", + "type": "integer" + }, + "type": { + "const": "Static", + "default": "Static", + "title": "Type", + "type": "string" + } + }, + "required": ["axis", "value"], + "title": "Static", + "type": "object" + }, + "SymmetricDifferenceOf_Reference_": { + "additionalProperties": false, + "description": "A point is in SymmetricDifferenceOf(a, b) if in either a or b, but not both.\n\nTypically created with the ``^`` operator.\n\n>>> r = Range(\"x\", 0.5, 2.5) ^ Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False, True, False, True, False])", + "properties": { + "left": { + "$ref": "#/$defs/Region", + "description": "The left-hand Region to combine" + }, + "right": { + "$ref": "#/$defs/Region", + "description": "The right-hand Region to combine" + }, + "type": { + "const": "SymmetricDifferenceOf", + "default": "SymmetricDifferenceOf", + "title": "Type", + "type": "string" + } + }, + "required": ["left", "right"], + "title": "SymmetricDifferenceOf", + "type": "object" + }, + "UnionOf_Reference_": { + "additionalProperties": false, + "description": "A point is in UnionOf(a, b) if in either a or b.\n\nTypically created with the ``|`` operator\n\n>>> r = Range(\"x\", 0.5, 2.5) | Range(\"x\", 1.5, 3.5)\n>>> r.mask({\"x\": np.array([0, 1, 2, 3, 4])})\narray([False, True, True, True, False])", + "properties": { + "left": { + "$ref": "#/$defs/Region", + "description": "The left-hand Region to combine" + }, + "right": { + "$ref": "#/$defs/Region", + "description": "The right-hand Region to combine" + }, + "type": { + "const": "UnionOf", + "default": "UnionOf", + "title": "Type", + "type": "string" + } + }, + "required": ["left", "right"], + "title": "UnionOf", + "type": "object" + }, + "Zip_Reference_": { + "additionalProperties": false, + "description": "Run two Specs in parallel, merging their midpoints together.\n\nTypically formed using `Spec.zip`.\n\nStacks of Frames are merged by:\n\n- If right creates a stack of a single Frames object of size 1, expand it to\n the size of the fastest Frames object created by left\n- Merge individual Frames objects together from fastest to slowest\n\nThis means that Zipping a Spec producing stack [l2, l1] with a Spec\nproducing stack [r1] will assert len(l1)==len(r1), and produce\nstack [l2, l1.zip(r1)].\n\n.. example_spec::\n\n from scanspec.specs import Line\n\n spec = Line(\"z\", 1, 2, 3) * Line(\"y\", 3, 4, 5).zip(Line(\"x\", 4, 5, 5))", + "properties": { + "left": { + "$ref": "#/$defs/Spec", + "description": "The left-hand Spec to Zip, will appear earlier in axes" + }, + "right": { + "$ref": "#/$defs/Spec", + "description": "The right-hand Spec to Zip, will appear later in axes" + }, + "type": { + "const": "Zip", + "default": "Zip", + "title": "Type", + "type": "string" + } + }, + "required": ["left", "right"], + "title": "Zip", + "type": "object" + } + }, + "additionalProperties": false, + "properties": { + "detectors": { + "items": { + "enum": [ + "sample_stage", + "sample_det", + "oav", + "synchrotron", + "panda" + ], + "type": "bluesky.protocols.Readable" + }, + "title": "Detectors", + "type": "array", + "uniqueItems": true + }, + "spec": { + "$ref": "#/$defs/Spec" + }, + "metadata": { + "title": "Metadata", + "type": "object" + } + }, + "required": ["detectors", "spec"], + "title": "spec_scan", + "type": "object" + } + } + ] +} diff --git a/apps/visr/src/mocks/workflows-response.json b/apps/visr/src/mocks/workflows-response.json new file mode 100644 index 0000000..424c19d --- /dev/null +++ b/apps/visr/src/mocks/workflows-response.json @@ -0,0 +1,22 @@ +{ + "data": { + "workflowTemplate": { + "name": "visr", + "maintainer": "visr-group", + "title": "visr", + "description": "Data processing for the Visible Light Spectroscopy beamline (ViSR).\n", + "arguments": { + "properties": { + "inpath": { + "default": "", + "type": "string" + } + }, + "required": ["inpath"], + "type": "object" + }, + "uiSchema": null, + "repository": null + } + } +} diff --git a/apps/visr/src/routes/Dashboard.tsx b/apps/visr/src/routes/Dashboard.tsx new file mode 100644 index 0000000..fa1f7c4 --- /dev/null +++ b/apps/visr/src/routes/Dashboard.tsx @@ -0,0 +1,53 @@ +import { Container, Typography, Button, Stack } from "@mui/material"; +import ArticleIcon from "@mui/icons-material/Article"; +import FeedIcon from "@mui/icons-material/Feed"; +import AddchartIcon from "@mui/icons-material/Addchart"; +import { Link } from "react-router-dom"; +import InstrumentSessionView from "../components/InstrumentSessionSelection/InstrumentSessionView"; +import GetInstrumentSessions from "../components/InstrumentSessionSelection/InstrumentSession"; + +function Dashboard() { + return ( + <> + + + + Welcome to ViSR + + + + + + + + + + + ); +} + +export default Dashboard; diff --git a/apps/visr/src/routes/Layout.tsx b/apps/visr/src/routes/Layout.tsx new file mode 100644 index 0000000..09935f0 --- /dev/null +++ b/apps/visr/src/routes/Layout.tsx @@ -0,0 +1,15 @@ +import { Link, Outlet, useLocation } from "react-router-dom"; +import VisrNavbar from "../components/VisrNavbar"; +import { Breadcrumbs } from "@diamondlightsource/sci-react-ui"; + +/* A common layout for all routes, consisting of Navbar and breadcrumbs */ +export function Layout() { + const location = useLocation(); + return ( +
+ + + +
+ ); +} diff --git a/apps/visr/src/routes/Plans.tsx b/apps/visr/src/routes/Plans.tsx new file mode 100644 index 0000000..80281d5 --- /dev/null +++ b/apps/visr/src/routes/Plans.tsx @@ -0,0 +1,31 @@ +import { useEffect, useState } from "react"; +import { Box } from "@mui/material"; +import { getPlans, type PlansResponse } from "../utils/api"; +import PlanBrowser from "../components/PlanBrowser/PlanBrowser"; +import PlanParameters from "../components/PlanBrowser/PlanParameters"; + +function JsonFormsPlans() { + const [planData, setPlanData] = useState({ plans: [] }); + + useEffect(() => { + async function fetchPlans() { + const results = await getPlans(); + setPlanData(results); + } + + fetchPlans(); + }, []); + + return ( + <> + + } + /> + + + ); +} + +export default JsonFormsPlans; diff --git a/apps/visr/src/routes/Spectroscopy.tsx b/apps/visr/src/routes/Spectroscopy.tsx new file mode 100644 index 0000000..ffa28fa --- /dev/null +++ b/apps/visr/src/routes/Spectroscopy.tsx @@ -0,0 +1,14 @@ +import { Box } from "@mui/material"; +import SpectroscopyForm from "../components/SpectroscopyForm"; + +function Spectroscopy() { + return ( + <> + + + + + ); +} + +export default Spectroscopy; diff --git a/apps/visr/src/routes/Workflows.tsx b/apps/visr/src/routes/Workflows.tsx new file mode 100644 index 0000000..0501317 --- /dev/null +++ b/apps/visr/src/routes/Workflows.tsx @@ -0,0 +1,25 @@ +import { Suspense } from "react"; +import { Container, Box } from "@mui/material"; +import TemplateView from "../components/TemplateView"; + +const Workflows: React.FC = () => { + return ( + <> + + + + + + + + + ); +}; + +export default Workflows; diff --git a/apps/visr/src/test/global.d.ts b/apps/visr/src/test/global.d.ts new file mode 100644 index 0000000..edfa8b5 --- /dev/null +++ b/apps/visr/src/test/global.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/apps/visr/src/utils/api.ts b/apps/visr/src/utils/api.ts new file mode 100644 index 0000000..734c617 --- /dev/null +++ b/apps/visr/src/utils/api.ts @@ -0,0 +1,79 @@ +export interface Plan { + name: string; + description: string | undefined; + schema: object; +} + +export interface PlansResponse { + plans: Plan[]; +} + +export interface TaskResponse { + task_id: string; +} + +export interface TaskRequest { + name: string; + params: object; + instrument_session: string; +} + +export async function getPlans(): Promise { + const url = "/api/plans"; + + const headers = new Headers(); + headers.append("Content-Type", "application/json"); + headers.append("X-Requested-By", "XMLHttpRequest"); + + const response = await fetch(url, { + method: "GET", + headers: headers, + }); + + return await response.json(); +} + +export async function createAndStartTask( + request: TaskRequest, +): Promise { + const task = await createTask(request); + return await startTask(task.task_id); +} + +export async function createTask(request: TaskRequest): Promise { + const url = "/api/tasks"; + + const headers = new Headers(); + headers.append("Content-Type", "application/json"); + headers.append("X-Requested-By", "XMLHttpRequest"); + + const response = await fetch(url, { + method: "POST", + headers: headers, + body: JSON.stringify(request), + }); + + if (!response.ok) { + const errorText = await response.text(); + throw new Error( + `Error ${response.status}: ${response.statusText}\n${errorText}`, + ); + } + return await response.json(); +} + +export async function startTask(task_id: string): Promise { + const url = "/api/worker/task"; + + const headers = new Headers(); + headers.append("Content-Type", "application/json"); + headers.append("X-Requested-By", "XMLHttpRequest"); + + const response = await fetch(url, { + method: "PUT", + headers: headers, + body: JSON.stringify({ task_id: task_id }), + }); + + return await response.json(); +} diff --git a/apps/visr/src/utils/common.ts b/apps/visr/src/utils/common.ts new file mode 100644 index 0000000..e27649f --- /dev/null +++ b/apps/visr/src/utils/common.ts @@ -0,0 +1,14 @@ +import { type Visit, regexToVisit } from "@diamondlightsource/sci-react-ui"; + +const visitRegex = /^([a-z]{2})([1-9]\d*)-([1-9]\d*)/; +export const visitWithTemplateRegex = new RegExp(`${visitRegex.source}-(.+)$`); + +export function visitTextToVisit(visitid?: string): Visit | null { + if (visitid) { + const parsedVisit = visitRegex.exec(visitid); + if (parsedVisit != null) { + return regexToVisit(parsedVisit); + } + } + return null; +} diff --git a/apps/visr/src/utils/schema.ts b/apps/visr/src/utils/schema.ts new file mode 100644 index 0000000..d23d0d7 --- /dev/null +++ b/apps/visr/src/utils/schema.ts @@ -0,0 +1,25 @@ +const shouldJustBeStrings = [ + "bluesky.protocols.Readable", + "bluesky.protocols.Movable", +]; + +const sanitizeSchema = (schema: object): object => { + const entries = Object.entries(schema); + + const processed = entries.map(([key, value]) => { + if (key == "type" && shouldJustBeStrings.includes(value)) { + return [key, "string"]; + } else if ( + typeof value === "object" && + !Array.isArray(value) && + value !== null + ) { + return [key, sanitizeSchema(value)]; + } else { + return [key, value]; + } + }); + return Object.fromEntries(processed); +}; + +export default sanitizeSchema; diff --git a/apps/visr/src/utils/types.ts b/apps/visr/src/utils/types.ts new file mode 100644 index 0000000..9599e83 --- /dev/null +++ b/apps/visr/src/utils/types.ts @@ -0,0 +1,94 @@ +import type { PayloadError } from "relay-runtime"; + +export type TaskStatus = + | "PENDING" + | "RUNNING" + | "SUCCEEDED" + | "SKIPPED" + | "FAILED" + | "ERROR" + | "OMITTED"; + +export interface Task { + id: string; + name: string; + status: TaskStatus; + depends?: string[]; + artifacts: Artifact[]; + workflow: string; + instrumentSession: Visit; + stepType: string; +} + +export interface Artifact { + name: string; + url: string; + mimeType: string; + parentTask: string; +} + +export interface TaskNode extends Task { + children?: TaskNode[]; +} + +export type WorkflowStatus = + | "Unknown" + | "WorkflowPendingStatus" + | "WorkflowRunningStatus" + | "WorkflowSucceededStatus" + | "WorkflowFailedStatus" + | "WorkflowErroredStatus"; + +export interface Workflow { + name: string; + instrumentSession: Visit; + status: WorkflowStatus; +} + +export interface Visit { + proposalCode: string; + proposalNumber: number; + number: number; +} + +export interface Template { + name: string; + description?: string | null; + title?: string | null; + maintainer: string; + repository?: string | null; +} + +export interface WorkflowStatusBool { + pending?: boolean; + running?: boolean; + succeeded?: boolean; + failed?: boolean; + error?: boolean; +} + +export interface SubmissionSuccessMessage { + type: "success"; + message: string; +} + +export interface SubmissionNetworkErrorMessage { + type: "networkError"; + error: Error; +} + +export interface SubmissionGraphQLErrorMessage { + type: "graphQLError"; + errors: PayloadError[]; +} + +export type JSONValue = + | string + | number + | boolean + | null + | JSONObject + | JSONValue[]; +export interface JSONObject { + [key: string]: JSONValue; +} diff --git a/apps/visr/src/vite-env.d.ts b/apps/visr/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/apps/visr/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/apps/visr/supergraph.graphql b/apps/visr/supergraph.graphql new file mode 100644 index 0000000..eb96ae8 --- /dev/null +++ b/apps/visr/supergraph.graphql @@ -0,0 +1,648 @@ +type Artifact { + """ + The file name of the artifact + """ + name: String! + """ + The download URL for the artifact + """ + url: Url! + """ + The MIME type of the artifact data + """ + mimeType: String! +} + +scalar Creator + +""" +Implement the DateTime scalar + +The input/output is a string in RFC3339 format. +""" +scalar DateTime + +""" +A scalar that can represent any JSON value. +""" +scalar JSON + +""" +A scalar that can represent any JSON Object value. +""" +scalar JSONObject + +""" +A single log line streamed from a pod +""" +type LogEntry { + """ + The log line content + """ + content: String! + """ + The name of the pod producing the log + """ + podName: String! +} + +""" +The root mutation of the service +""" +type Mutation { + submitWorkflowTemplate( + name: String! + visit: VisitInput! + parameters: JSON! + ): Workflow! +} + +""" +Information about pagination in a connection +""" +type PageInfo @shareable { + """ + When paginating backwards, are there more items? + """ + hasPreviousPage: Boolean! + """ + When paginating forwards, are there more items? + """ + hasNextPage: Boolean! + """ + When paginating backwards, the cursor to continue. + """ + startCursor: String + """ + When paginating forwards, the cursor to continue. + """ + endCursor: String +} + +""" +Supported DLS science groups +""" +enum ScienceGroup { + """ + Macromolecular Crystallography + """ + MX + """ + Workflows Examples + """ + EXAMPLES + """ + Magnetic Materials + """ + MAGNETIC_MATERIALS + """ + Soft Condensed Matter + """ + CONDENSED_MATTER + """ + Imaging and Microscopy + """ + IMAGING + """ + Biological Cryo-Imaging + """ + BIO_CRYO_IMAGING + """ + Structures and Surfaces + """ + SURFACES + """ + Crystallography + """ + CRYSTALLOGRAPHY + """ + Spectroscopy + """ + SPECTROSCOPY +} + +""" +The root mutation of the service +""" +type Subscription { + """ + Processing to subscribe to logs for a single pod of a workflow + """ + logs(visit: VisitInput!, workflowName: String!, taskId: String!): LogEntry! + """ + Processing to subscribe to data for all workflows in a session + """ + workflow(visit: VisitInput!, name: String!): Workflow! +} + +type Task { + """ + Unique name of the task + """ + id: String! + """ + Display name of the task + """ + name: String! + """ + Current status of a task + """ + status: TaskStatus! + """ + Parent of a task + """ + depends: [String!]! + """ + Children of a task + """ + dependencies: [String!]! + """ + Artifacts produced by a task + """ + artifacts: [Artifact!]! + """ + Node type - Pod, DAG, etc + """ + stepType: String! + """ + Start time for a task on a workflow + """ + startTime: DateTime + """ + End time for a task on a workflow + """ + endTime: DateTime + """ + A human readable message indicating details about why this step is in this condition + """ + message: String +} + +enum TaskStatus { + PENDING + RUNNING + SUCCEEDED + SKIPPED + FAILED + ERROR + OMITTED +} + +scalar Template + +""" +URL is a String implementing the [URL Standard](http://url.spec.whatwg.org/) +""" +scalar Url + +""" +A visit to an instrument as part of a session +""" +type Visit { + """ + Project Proposal Code + """ + proposalCode: String! + """ + Project Proposal Number + """ + proposalNumber: Int! + """ + Session visit Number + """ + number: Int! +} + +""" +A visit to an instrument as part of a session +""" +input VisitInput { + """ + Project Proposal Code + """ + proposalCode: String! + """ + Project Proposal Number + """ + proposalNumber: Int! + """ + Session visit Number + """ + number: Int! +} + +type Workflow { + """ + The name given to the workflow, unique within a given visit + """ + name: String! + """ + The visit the Workflow was run against + """ + visit: Visit! + """ + The current status of the workflow + """ + status: WorkflowStatus + """ + The top-level workflow parameters + """ + parameters: JSONObject + """ + The name of the template used to run the workflow + """ + templateRef: String + """ + The workflow creator + """ + creator: WorkflowCreator! +} + +type WorkflowConnection @shareable { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + """ + A list of edges. + """ + edges: [WorkflowEdge!]! + """ + A list of nodes. + """ + nodes: [Workflow!]! +} + +""" +Information about the creator of a workflow. +""" +type WorkflowCreator { + """ + An identifier unique to the creator of the workflow. + Typically this is the creator's Fed-ID. + """ + creatorId: String! +} + +""" +An edge in a connection. +""" +type WorkflowEdge @shareable { + """ + The item at the end of the edge + """ + node: Workflow! + """ + A cursor for use in pagination + """ + cursor: String! +} + +""" +All tasks in the workflow have errored +""" +type WorkflowErroredStatus { + """ + Time at which this workflow started + """ + startTime: DateTime! + """ + Time at which this workflow completed + """ + endTime: DateTime! + """ + A human readable message indicating details about why the workflow is in this condition + """ + message: String + """ + Tasks created by the workflow + """ + tasks: [Task!]! +} + +""" +All tasks in the workflow have failed +""" +type WorkflowFailedStatus { + """ + Time at which this workflow started + """ + startTime: DateTime! + """ + Time at which this workflow completed + """ + endTime: DateTime! + """ + A human readable message indicating details about why the workflow is in this condition + """ + message: String + """ + Tasks created by the workflow + """ + tasks: [Task!]! +} + +""" +All the supported Workflows filters +""" +input WorkflowFilter { + """ + The status field for a workflow + """ + workflowStatusFilter: WorkflowStatusFilter + """ + The fedid of the user who created the workflow + """ + creator: Creator + """ + The name of the workflow template + """ + template: Template +} + +type WorkflowPendingStatus { + """ + A human readable message indicating details about why the workflow is in this condition + """ + message: String +} + +type WorkflowRunningStatus { + """ + Time at which this workflow started + """ + startTime: DateTime! + """ + A human readable message indicating details about why the workflow is in this condition + """ + message: String + """ + Tasks created by the workflow + """ + tasks: [Task!]! +} + +""" +The status of a workflow +""" +union WorkflowStatus = + | WorkflowPendingStatus + | WorkflowRunningStatus + | WorkflowSucceededStatus + | WorkflowFailedStatus + | WorkflowErroredStatus + +""" +Represents workflow status filters +""" +input WorkflowStatusFilter { + pending: Boolean! = false + running: Boolean! = false + succeeded: Boolean! = false + failed: Boolean! = false + error: Boolean! = false +} + +""" +All tasks in the workflow have succeded +""" +type WorkflowSucceededStatus { + """ + Time at which this workflow started + """ + startTime: DateTime! + """ + Time at which this workflow completed + """ + endTime: DateTime! + """ + A human readable message indicating details about why the workflow is in this condition + """ + message: String + """ + Tasks created by the workflow + """ + tasks: [Task!]! +} + +type WorkflowTemplate { + """ + The name given to the workflow template, globally unique + """ + name: String! + """ + The group who maintains the workflow template + """ + maintainer: String! + """ + A human readable title for the workflow template + """ + title: String + """ + A human readable description of the workflow which is created + """ + description: String + """ + The repository storing the code associated with this template. + """ + repository: String + """ + A JSON Schema describing the arguments of a Workflow Template + """ + arguments: JSON! + """ + A JSON Forms UI Schema describing how to render the arguments of the Workflow Template + """ + uiSchema: JSON +} + +type WorkflowTemplateConnection @shareable { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + """ + A list of edges. + """ + edges: [WorkflowTemplateEdge!]! + """ + A list of nodes. + """ + nodes: [WorkflowTemplate!]! +} + +""" +An edge in a connection. +""" +type WorkflowTemplateEdge @shareable { + """ + The item at the end of the edge + """ + node: WorkflowTemplate! + """ + A cursor for use in pagination + """ + cursor: String! +} + +""" +Supported label filters for ClusterWorkflowTemplates +""" +input WorkflowTemplatesFilter { + """ + The science group owning the template eg imaging + """ + scienceGroup: [ScienceGroup!] +} + +""" +Directs the executor to include this field or fragment only when the `if` argument is true. +""" +directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT +""" +Directs the executor to skip this field or fragment when the `if` argument is true. +""" +directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT +""" +Provides a scalar specification URL for specifying the behavior of custom scalar types. +""" +directive @specifiedBy(url: String!) on SCALAR + +type Account { + accountId: Int! + username: String! + emailAddress: String + title: String + givenName: String + familyName: String + type: AccountType! + state: AccountState! + proposalRoles: [ProposalAccount!]! + instrumentSessionRoles: [InstrumentSessionRole!]! +} + +enum AccountState { + enabled + disabled +} + +enum AccountType { + user + staff + functional +} + +""" +Date with time (isoformat) +""" +scalar DateTime + +type Instrument { + name: String! + scienceGroup: String + description: String + proposals: [Proposal!]! + instrumentSessions: [InstrumentSession!]! +} + +type InstrumentSession { + instrumentSessionId: Int! + instrumentSessionNumber: Int! + startTime: DateTime + endTime: DateTime + type: String + state: String + riskRating: String + proposal: Proposal + instrument: Instrument! + roles: [InstrumentSessionRole!]! +} + +type InstrumentSessionRole { + instrumentSession: InstrumentSession! + account: Account! + role: String! + onSite: Boolean! +} + +type Proposal { + proposalNumber: Int! + proposalCategory: String + title: String + summary: String + state: ProposalState! + instrumentSessions: [InstrumentSession!]! + instruments: [Instrument!]! + roles: [ProposalAccount!]! +} + +type ProposalAccount { + proposal: Proposal! + account: Account! + role: String! +} + +enum ProposalState { + Open + Closed + Cancelled +} + +type Query { + """ + Get a single [`Workflow`] by proposal, visit, and name + """ + workflow(visit: VisitInput!, name: String!): Workflow! + workflows( + visit: VisitInput! + cursor: String + limit: Int + filter: WorkflowFilter + ): WorkflowConnection! + workflowTemplate(name: String!): WorkflowTemplate! + workflowTemplates( + cursor: String + limit: Int + filter: WorkflowTemplatesFilter + ): WorkflowTemplateConnection! + """ + Get a proposal by its number + """ + proposal(proposalNumber: Int!): Proposal + + """ + Get a list of proposals + """ + proposals(proposalCategory: String = null): [Proposal!]! + + """ + Get a instrument session + """ + instrumentSession( + proposalNumber: Int! + instrumentSessionNumber: Int! + ): InstrumentSession + + """ + Get a instrument session + """ + instrumentSessions( + proposalNumber: Int = null + proposalCategory: String = null + ): [InstrumentSession!] + + """ + Get an instrument + """ + instrument(instrumentName: String!): Instrument + + """ + Get a list of instruments + """ + instruments(scienceGroup: String = null): [Instrument!]! + + """ + Get an account + """ + account(username: String!): Account +} diff --git a/apps/visr/tsconfig.json b/apps/visr/tsconfig.json new file mode 100644 index 0000000..8657541 --- /dev/null +++ b/apps/visr/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "types": ["vite/client", "@atlas/vitest-conf/global-types"] + }, + "include": ["src"] +} diff --git a/apps/visr/vite.config.ts b/apps/visr/vite.config.ts new file mode 100644 index 0000000..fab2919 --- /dev/null +++ b/apps/visr/vite.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react-swc"; +import relay from "vite-plugin-relay"; + +export default defineConfig({ + plugins: [react(), relay], + define: { + global: {}, + }, +}); diff --git a/apps/visr/vitest.config.ts b/apps/visr/vitest.config.ts new file mode 100644 index 0000000..6dbadc2 --- /dev/null +++ b/apps/visr/vitest.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from "vitest/config"; +import baseConfig from "@atlas/vitest-conf/vitest.config"; + +export default defineConfig({ + ...baseConfig, +}); diff --git a/docker/Dockerfile.web-app b/docker/Dockerfile.web-app new file mode 100644 index 0000000..5719602 --- /dev/null +++ b/docker/Dockerfile.web-app @@ -0,0 +1,44 @@ +FROM node:22-alpine AS base + +# Use a consistent working directory across all stages +WORKDIR /app + +# 1) Install dependencies +FROM base AS deps + +ARG APP_NAME + +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc* ./ +COPY packages ./packages +COPY apps/${APP_NAME}/package.json ./apps/${APP_NAME}/ + +# Install dependencies +RUN corepack enable pnpm && pnpm i --frozen-lockfile --filter=@atlas/${APP_NAME}... + +# 2) Run the build +FROM base AS builder + +ARG APP_NAME + +# Copy all node_modules from deps stage +COPY --from=deps /app ./ + +# Copy source files +COPY apps/${APP_NAME} ./apps/${APP_NAME} +COPY packages ./packages +COPY turbo.json package.json pnpm-workspace.yaml tsconfig.json ./ +RUN corepack enable pnpm && pnpm --filter=@atlas/${APP_NAME}... build + +# 3) Create minimal image to serve the app +FROM nginxinc/nginx-unprivileged:1.25-alpine AS runner + +ARG APP_NAME + +# Copy built files to nginx web root +COPY --from=builder /app/apps/${APP_NAME}/dist /usr/share/nginx/html + +# Copy your custom nginx config +COPY docker/nginx.conf /etc/nginx/nginx.conf + +EXPOSE 8080 +CMD ["nginx", "-g", "daemon off;"] diff --git a/docker/nginx.conf b/docker/nginx.conf new file mode 100644 index 0000000..00ce775 --- /dev/null +++ b/docker/nginx.conf @@ -0,0 +1,61 @@ +worker_processes auto; # Let Nginx decide based on CPU cores +pid /tmp/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + # Logging to stdout/stderr is standard for Docker + access_log /dev/stdout; + error_log /dev/stderr warn; + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + + # Gzip Compression - Critical for web app performance + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml image/svg+xml; + + # Temp paths for unprivileged user + client_body_temp_path /tmp/client_temp; + proxy_temp_path /tmp/proxy_temp_path; + fastcgi_temp_path /tmp/fastcgi_temp; + uwsgi_temp_path /tmp/uwsgi_temp; + scgi_temp_path /tmp/scgi_temp; + + server { + listen 8080; + server_name _; + root /usr/share/nginx/html; + index index.html; + + # SPA routing: try file, then directory, then fall back to index.html + location / { + try_files $uri $uri/ /index.html; + add_header Cache-Control "no-store" always; + } + + # Cache hashed assets (JS/CSS/Images) for a long time + # This assumes your build tool (Vite/Webpack) uses hashes in filenames + location ~* \.(?:js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + access_log off; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } + } +} \ No newline at end of file diff --git a/helm/Chart.yaml b/helm/Chart.yaml new file mode 100644 index 0000000..a5869b6 --- /dev/null +++ b/helm/Chart.yaml @@ -0,0 +1,11 @@ +apiVersion: v2 +name: ui-base +description: Web UI behind oauth2-proxy +type: application +version: 0.1.0 +appVersion: "0.1.0" + +dependencies: + - name: oauth2-proxy + repository: https://oauth2-proxy.github.io/manifests + version: 10.1.0 \ No newline at end of file diff --git a/helm/templates/_helpers.tpl b/helm/templates/_helpers.tpl new file mode 100644 index 0000000..5d63e4a --- /dev/null +++ b/helm/templates/_helpers.tpl @@ -0,0 +1,82 @@ +{{/* +Chart name +*/}} +{{- define "ui-base.name" -}} +{{- .Values.name | default .Chart.Name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Fully qualified release name +*/}} +{{- define "ui-base.fullname" -}} +{{- printf "%s-%s" .Release.Name (include "ui-base.name" .) | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Return the container image reference. +Defaults tag to .Chart.AppVersion if empty. +*/}} +{{- define "ui-base.image" -}} +{{- $repo := .Values.image.repository -}} +{{- $tag := .Values.image.tag | default .Chart.AppVersion -}} +{{ printf "%s:%s" $repo $tag }} +{{- end -}} + + +{{/* +Return the number of replicas. +*/}} +{{- define "ui-base.replicas" -}} +{{- .Values.replicas | default 1 -}} +{{- end -}} + +{{/* +Common labels applied to all resources +*/}} +{{- define "ui-base.labels" -}} +app.kubernetes.io/name: {{ include "ui-base.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end -}} + +{{- define "ui-base.renderUpstreams" -}} + +# Base UI upstream (always present) +- id: ui + path: / + uri: http://{{ include "ui-base.name" . }}.{{ .Release.Namespace }}.svc.cluster.local:80 + passHostHeader: true + +{{- range (.Values.upstreams | default list) }} +- id: {{ .id }} + path: {{ .path }} + uri: {{ include "ui-base.upstreamUri" (dict "target" .target "Release" $.Release) }} + {{- if .rewriteTarget }} + rewriteTarget: {{ .rewriteTarget }} + {{- end }} + passHostHeader: {{ .passHostHeader | default false }} +{{- end }} + +{{- end }} + + +{{- define "ui-base.upstreamUri" -}} +{{- if .target.service -}} +http://{{ .target.service.name }}.{{ .Release.Namespace }}.svc.cluster.local:{{ .target.service.port }} +{{- else if .target.external -}} +{{ .target.external.uri }} +{{- end -}} +{{- end }} + +{{- define "ui-base.identityIssuerURL" -}} +{{- $provider := .Values.identityProvider -}} +{{- $providers := .Values.identityProviders -}} + +{{- if not (hasKey $providers $provider) -}} +{{- fail (printf "Invalid provider '%s'. Must be one of: %s" + $provider (keys $providers | sortAlpha | join ", ")) -}} +{{- end -}} + +{{- index $providers $provider -}} +{{- end -}} \ No newline at end of file diff --git a/helm/templates/configmap.yaml b/helm/templates/configmap.yaml new file mode 100644 index 0000000..a47fe49 --- /dev/null +++ b/helm/templates/configmap.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: oauth2-proxy-config +data: + oauth2_proxy.cfg: | + email_domains = ["*"] + cookie_secure = true + cookie_samesite = "lax" + cookie_expire = "168h" + cookie_refresh = "45s" + cookie_name = "{{ include "oauth2-proxy.fullname" . }}-cookie" \ No newline at end of file diff --git a/helm/templates/deployment.yaml b/helm/templates/deployment.yaml new file mode 100644 index 0000000..110255d --- /dev/null +++ b/helm/templates/deployment.yaml @@ -0,0 +1,22 @@ +apiVersion: apps/v1 +kind: Deployment + +metadata: + name: {{ include "ui-base.name" . }} + namespace: {{ .Release.Namespace }} +spec: + replicas: {{ include "ui-base.replicas" . }} + selector: + matchLabels: + app: {{ include "ui-base.name" . }} + template: + metadata: + labels: + app: {{ include "ui-base.name" . }} + spec: + containers: + - name: {{ include "ui-base.name" . }} + image: {{ include "ui-base.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - containerPort: {{ .Values.ui.port }} \ No newline at end of file diff --git a/helm/templates/ingress.yaml b/helm/templates/ingress.yaml new file mode 100644 index 0000000..d1ca20f --- /dev/null +++ b/helm/templates/ingress.yaml @@ -0,0 +1,24 @@ +{{ if .Values.host }} +apiVersion: {{ include "capabilities.ingress.apiVersion" . }} +kind: Ingress +metadata: + name: {{ include "ui-base.name" . }} + labels: + {{ include "ui-base.labels" . | nindent 4 }} + annotations: + nginx.ingress.kubernetes.io/proxy-buffer-size: "8k" +spec: + ingressClassName: nginx + rules: + - host: {{ .Values.host | quote }} + http: + paths: + - path: / + pathType: Prefix + backend: +{{ include "ingress.backend" (dict + "serviceName" (printf "%s-oauth2-proxy" .Release.Name ) + "servicePort" (index .Values "oauth2-proxy" "service" "portNumber") + "context" $ +) | nindent 14 }} +{{ end }} diff --git a/helm/templates/oauth2-alpha-secret.yaml b/helm/templates/oauth2-alpha-secret.yaml new file mode 100644 index 0000000..787c9fb --- /dev/null +++ b/helm/templates/oauth2-alpha-secret.yaml @@ -0,0 +1,44 @@ +apiVersion: v1 +kind: Secret +metadata: + name: ui-oauth2-alpha-secret + labels: + {{- include "ui-base.labels" . | nindent 4 }} +type: Opaque +stringData: + oauth2_proxy.yml: | + server: + BindAddress: "0.0.0.0:{{ index .Values "oauth2-proxy" "service" "portNumber" }}" + + metricsServer: + BindAddress: "0.0.0.0:44180" + + injectRequestHeaders: + - name: Authorization + values: + - claim: access_token + prefix: "Bearer " + - name: X-Auth-Request-User + values: + - claim: user + - name: X-Auth-Request-Email + values: + - claim: email + + providers: + - provider: oidc + id: {{ .Values.identityProvider }} + clientID: ${OAUTH2_PROXY_CLIENT_ID} + clientSecret: ${OAUTH2_PROXY_CLIENT_SECRET} + scope: openid email profile offline_access + oidcConfig: + issuerURL: {{ include "ui-base.identityIssuerURL" . | quote }} + insecureAllowUnverifiedEmail: true + insecureSkipNonce: true + emailClaim: sub + audienceClaims: ["aud"] + + upstreamConfig: + proxyRawPath: false + upstreams: +{{ include "ui-base.renderUpstreams" . | indent 8 }} diff --git a/helm/templates/service.yaml b/helm/templates/service.yaml new file mode 100644 index 0000000..8e62008 --- /dev/null +++ b/helm/templates/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "ui-base.name" . }} + namespace: {{ .Release.Namespace }} +spec: + type: ClusterIP + selector: + app: {{ include "ui-base.name" . }} + ports: + - port: 80 + targetPort: {{ .Values.ui.port }} diff --git a/helm/values.schema.json b/helm/values.schema.json new file mode 100644 index 0000000..fb4b3c5 --- /dev/null +++ b/helm/values.schema.json @@ -0,0 +1,114 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "UI Base Chart Values", + "type": "object", + "required": ["name", "image", "host", "identityProvider"], + "properties": { + "name": { + "type": "string", + "description": "The name of the application deployment." + }, + "host": { + "type": "string", + "description": "The FQDN for the application ingress (e.g., visr.diamond.ac.uk)." + }, + "replicaCount": { + "type": "integer", + "minimum": 1, + "default": 1 + }, + "image": { + "type": "object", + "required": ["repository", "tag"], + "properties": { + "repository": { "type": "string" }, + "tag": { "type": "string" }, + "pullPolicy": { + "type": "string", + "enum": ["Always", "Never", "IfNotPresent"], + "default": "IfNotPresent" + } + } + }, + "ui": { + "type": "object", + "properties": { + "port": { + "type": "integer", + "minimum": 1, + "maximum": 65535, + "default": 8080 + } + } + }, + "identityProvider": { + "type": "string", + "description": "The key of the identity provider to use from the identityProviders list.", + "enum": ["prod", "test", "dev", "legacy"] + }, + "identityProviders": { + "type": "object", + "additionalProperties": { + "type": "string", + "format": "uri" + } + }, + "upstreams": { + "type": "array", + "items": { + "type": "object", + "required": ["id", "path", "target"], + "properties": { + "id": { "type": "string" }, + "path": { "type": "string" }, + "rewriteTarget": { "type": "string" }, + "sse": { "type": "boolean" }, + "passHostHeader": { "type": "boolean" }, + "target": { + "type": "object", + "oneOf": [ + { + "required": ["external"], + "properties": { + "external": { + "type": "object", + "required": ["uri"], + "properties": { + "uri": { "type": "string", "format": "uri" } + } + } + } + }, + { + "required": ["service"], + "properties": { + "service": { + "type": "object", + "required": ["name", "port"], + "properties": { + "name": { "type": "string" }, + "port": { "type": "integer", "minimum": 1 } + } + } + } + } + ] + } + } + } + }, + "oauth2-proxy": { + "type": "object", + "properties": { + "enabled": { "type": "boolean" }, + "config": { + "type": "object", + "properties": { + "existingSecret": { "type": "string" }, + "existingConfig": { "type": "string" } + } + } + } + } + } +} \ No newline at end of file diff --git a/helm/values.yaml b/helm/values.yaml new file mode 100644 index 0000000..1d151eb --- /dev/null +++ b/helm/values.yaml @@ -0,0 +1,47 @@ +name: ui-base + +image: + repository: "" + tag: "" + pullPolicy: IfNotPresent + +host: "" + +replicaCount: 1 + +ui: + port: 8080 + +upstreams: [] + +identityProviders: + prod: https://identity.diamond.ac.uk/realms/dls + test: https://identity-test.diamond.ac.uk/realms/dls + dev: https://identity-dev.diamond.ac.uk/realms/dls + legacy: https://authn.diamond.ac.uk/realms/master + +identityProvider: prod + +oauth2-proxy: + enabled: true + + extraArgs: + skip-provider-button: "true" + standard-logging: "true" + request-logging: "true" + auth-logging: "true" + show-debug-on-error: "true" + + ingress: + enabled: false + + config: + existingSecret: ui-keycloak-secret + existingConfig: oauth2-proxy-config + + alphaConfig: + enabled: true + existingSecret: ui-oauth2-alpha-secret + + service: + portNumber: 4180 \ No newline at end of file diff --git a/packages/vitest-conf/tsconfig.json b/packages/vitest-conf/tsconfig.json index 90ac412..72d7c4c 100644 --- a/packages/vitest-conf/tsconfig.json +++ b/packages/vitest-conf/tsconfig.json @@ -2,12 +2,11 @@ "extends": "../../tsconfig.json", "compilerOptions": { "allowImportingTsExtensions": false, - "allowSyntheticDefaultImports": true, "declaration": true, "esModuleInterop": true, "noEmit": false, - "rootDir": "src", - "outDir": "dist" + "outDir": "dist", + "rootDir": "src" }, "exclude": ["dist", "node_modules"], "include": ["src"] diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2c54bbb..a827fd6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -46,6 +46,106 @@ importers: specifier: ^4.0.13 version: 4.0.13(@types/node@24.5.2)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(msw@2.11.3(@types/node@24.5.2)(typescript@5.9.2)) + apps/visr: + dependencies: + '@diamondlightsource/davidia': + specifier: ^1.0.3 + version: 1.0.4(@h5web/lib@13.0.0(@react-three/fiber@8.18.0(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.167.1))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.167.1)(typescript@5.9.2))(@react-three/drei@9.122.0(@react-three/fiber@8.18.0(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.167.1))(@types/react@18.3.24)(@types/three@0.181.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.167.1)(use-sync-external-store@1.6.0(react@18.3.1)))(@react-three/fiber@8.18.0(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.167.1))(@types/react@18.3.24)(ndarray@1.0.19)(react-dom@18.3.1(react@18.3.1))(react-toastify@9.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(three@0.167.1) + '@diamondlightsource/sci-react-ui': + specifier: ^0.2.0 + version: 0.2.0(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@mui/icons-material@6.5.0(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + '@emotion/react': + specifier: ^11.14.0 + version: 11.14.0(@types/react@18.3.24)(react@18.3.1) + '@emotion/styled': + specifier: ^11.14.1 + version: 11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1) + '@jsonforms/core': + specifier: 3.6.0 + version: 3.6.0 + '@jsonforms/material-renderers': + specifier: 3.6.0 + version: 3.6.0(356738369fe2ead2b97aa81a07be8ba2) + '@jsonforms/react': + specifier: 3.6.0 + version: 3.6.0(@jsonforms/core@3.6.0)(react@18.3.1) + '@mui/icons-material': + specifier: ^6.5.0 + version: 6.5.0(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.24)(react@18.3.1) + '@mui/lab': + specifier: 6.0.0-beta.22 + version: 6.0.0-beta.22(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/material': + specifier: <7.0.0 + version: 6.5.0(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/x-date-pickers': + specifier: ^7.17.0 + version: 7.29.4(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@6.5.0(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(dayjs@1.10.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + ndarray: + specifier: ^1.0.19 + version: 1.0.19 + react-error-boundary: + specifier: ^6.0.0 + version: 6.0.0(react@18.3.1) + react-router-dom: + specifier: ^7.7.1 + version: 7.9.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + devDependencies: + '@atlas/vitest-conf': + specifier: workspace:* + version: link:../../packages/vitest-conf + '@eslint/js': + specifier: ^9.33.0 + version: 9.36.0 + '@types/ndarray': + specifier: ^1.0.14 + version: 1.0.14 + '@types/react-relay': + specifier: ^18.2.1 + version: 18.2.1 + '@types/relay-runtime': + specifier: ^19.0.2 + version: 19.0.3 + '@vitejs/plugin-react-swc': + specifier: ^3.11.0 + version: 3.11.0(vite@7.1.7(@types/node@24.5.2)) + ajv: + specifier: ^8.17.1 + version: 8.17.1 + babel-plugin-relay: + specifier: ^20.1.1 + version: 20.1.1 + eslint-plugin-react-hooks: + specifier: ^5.2.0 + version: 5.2.0(eslint@9.36.0) + eslint-plugin-react-refresh: + specifier: ^0.4.20 + version: 0.4.24(eslint@9.36.0) + globals: + specifier: ^16.3.0 + version: 16.5.0 + msw: + specifier: ^2.10.4 + version: 2.11.3(@types/node@24.5.2)(typescript@5.9.2) + react-relay: + specifier: ^20.1.1 + version: 20.1.1(react@18.3.1) + relay-compiler: + specifier: ^20.1.1 + version: 20.1.1 + relay-runtime: + specifier: ^20.1.1 + version: 20.1.1 + vite: + specifier: ^7.1.2 + version: 7.1.7(@types/node@24.5.2) + vite-plugin-relay: + specifier: ^2.1.0 + version: 2.1.0(babel-plugin-relay@20.1.1)(vite@7.1.7(@types/node@24.5.2)) + vitest: + specifier: '*' + version: 4.0.13(@types/node@24.5.2)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(msw@2.11.3(@types/node@24.5.2)(typescript@5.9.2)) + packages/vitest-conf: dependencies: vitest: @@ -80,14 +180,77 @@ packages: resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} + '@babel/compat-data@7.28.5': + resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.28.5': + resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.28.5': + resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + 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.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.3': + resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.27.1': resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} 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.28.4': + resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.5': + resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/runtime@7.28.4': resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} engines: {node: '>=6.9.0'} + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.28.5': + resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.28.5': + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} + engines: {node: '>=6.9.0'} + '@bundled-es-modules/cookie@2.0.1': resolution: {integrity: sha512-8o+5fRPLNbjbdGRRmJj3h6Hh1AQJf2dk3qQ/5ZFb+PXkRNiSoMGGUKlsgLfrxneb72axVJyIYji64E2+nNfYyw==} @@ -122,6 +285,95 @@ packages: resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} engines: {node: '>=18'} + '@date-io/core@3.2.0': + resolution: {integrity: sha512-hqwXvY8/YBsT9RwQITG868ZNb1MVFFkF7W1Ecv4P472j/ZWa7EFcgSmxy8PUElNVZfvhdvfv+a8j6NWJqOX5mA==} + + '@date-io/dayjs@3.2.0': + resolution: {integrity: sha512-+3LV+3N+cpQbEtmrFo8odg07k02AFY7diHgbi2EKYYANOOCPkDYUjDr2ENiHuYNidTs3tZwzDKckZoVNN4NXxg==} + peerDependencies: + dayjs: ^1.8.17 + peerDependenciesMeta: + dayjs: + optional: true + + '@diamondlightsource/davidia@1.0.4': + resolution: {integrity: sha512-GHlTV698AQPsjwxZ0g9ZcmO6dNgkVx8aePmUlwNmLe8uFditLFfwVwlfcBdNjS8XkfSw9Y2tN4ES9hzdNXkXPA==} + peerDependencies: + '@h5web/lib': ^13.0.0 + '@react-three/drei': ^9.111.2 + '@react-three/fiber': ^8.17.5 + ndarray: ^1.0.19 + react: ^18.3.1 + react-dom: ^18.3.1 + react-toastify: ^9.1.3 + three: ^0.167.1 + + '@diamondlightsource/sci-react-ui@0.2.0': + resolution: {integrity: sha512-6rcAHrgNzaycjTtIOUb8+COGnuZS1YhkF0m47UsBbTuH+rNatoPX+HPQNkz9p0gunYJnVF7RZOhNSdrfFRgquA==} + peerDependencies: + '@emotion/react': ^11.13.3 + '@emotion/styled': ^11.13.0 + '@mui/icons-material': ^6.1.7 + '@mui/material': ^6.1.7 + react: ^18.3.1 + + '@dimforge/rapier3d-compat@0.12.0': + resolution: {integrity: sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==} + + '@emotion/babel-plugin@11.13.5': + resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} + + '@emotion/cache@11.14.0': + resolution: {integrity: sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==} + + '@emotion/hash@0.9.2': + resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} + + '@emotion/is-prop-valid@1.4.0': + resolution: {integrity: sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==} + + '@emotion/memoize@0.9.0': + resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} + + '@emotion/react@11.14.0': + resolution: {integrity: sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==} + peerDependencies: + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@emotion/serialize@1.3.3': + resolution: {integrity: sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==} + + '@emotion/sheet@1.4.0': + resolution: {integrity: sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==} + + '@emotion/styled@11.14.1': + resolution: {integrity: sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==} + peerDependencies: + '@emotion/react': ^11.0.0-rc.0 + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@emotion/unitless@0.10.0': + resolution: {integrity: sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==} + + '@emotion/use-insertion-effect-with-fallbacks@1.2.0': + resolution: {integrity: sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==} + peerDependencies: + react: '>=16.8.0' + + '@emotion/utils@1.4.2': + resolution: {integrity: sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==} + + '@emotion/weak-memoize@0.4.0': + resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==} + '@esbuild/aix-ppc64@0.25.10': resolution: {integrity: sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==} engines: {node: '>=18'} @@ -316,6 +568,39 @@ packages: resolution: {integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@floating-ui/core@1.7.3': + resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} + + '@floating-ui/dom@1.7.4': + resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==} + + '@floating-ui/react-dom@2.1.6': + resolution: {integrity: sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/react@0.26.20': + resolution: {integrity: sha512-RixKJJG92fcIsVoqrFr4Onpzh7hlOx4U7NV4aLhMLmtvjZ5oTB/WzXaANYUZATKqXvvW7t9sCxtzejip26N5Ag==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/utils@0.2.10': + resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + + '@h5web/lib@13.0.0': + resolution: {integrity: sha512-Y4yOH5QgJ9RmM4xyS74z2R0C0h3rJ2xx/dQrlabYJzbPDI/x4EdZC9FmDKG7RO1Rpol6gcqz13u0NHN+JMlEdg==} + peerDependencies: + '@react-three/fiber': '>=8' + react: '>=18' + react-dom: '>=18' + three: '>=0.138' + typescript: '>=4.5' + peerDependenciesMeta: + typescript: + optional: true + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -367,13 +652,240 @@ packages: '@types/node': optional: true + '@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==} + + '@jsonforms/core@3.6.0': + resolution: {integrity: sha512-Qz7qJPf/yP4ybqknZ500zggIDZRJfcufu+3efp/xNWf05mpXvxN9TdfmA++BdXi5Nr4UAgjos2kFmQpZpQaCDw==} + + '@jsonforms/material-renderers@3.6.0': + resolution: {integrity: sha512-23ktHVnDDykOXQP2go312/7yNKiR1f/o0GJ2xNg+LVH6PtCWtzdPxaY6WFKWLt84s1DgEHyCw466XEVrPec5dA==} + peerDependencies: + '@emotion/react': ^11.4.1 + '@emotion/styled': ^11.3.0 + '@jsonforms/core': 3.6.0 + '@jsonforms/react': 3.6.0 + '@mui/icons-material': ^5.11.16 || ^6.0.0 + '@mui/material': ^5.13.0 || ^6.0.0 + '@mui/x-date-pickers': ^6.0.0 || ^7.0.0 + react: ^16.12.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@jsonforms/react@3.6.0': + resolution: {integrity: sha512-dor7FYltCkNkAM+SVZGtabjpUhGlj0/coAqx7GIZ8h+leET+d1sLEAc8kfxxh6gZBq9C4KAErb0Pj3uHedOs9Q==} + peerDependencies: + '@jsonforms/core': 3.6.0 + react: ^16.12.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@mediapipe/tasks-vision@0.10.17': + resolution: {integrity: sha512-CZWV/q6TTe8ta61cZXjfnnHsfWIdFhms03M9T7Cnd5y2mdpylJM0rF1qRq+wsQVRMLz1OYPVEBU9ph2Bx8cxrg==} + + '@monogrid/gainmap-js@3.4.0': + resolution: {integrity: sha512-2Z0FATFHaoYJ8b+Y4y4Hgfn3FRFwuU5zRrk+9dFWp4uGAdHGqVEdP7HP+gLA3X469KXHmfupJaUbKo1b/aDKIg==} + peerDependencies: + three: '>= 0.159.0' + '@mswjs/interceptors@0.39.6': resolution: {integrity: sha512-bndDP83naYYkfayr/qhBHMhk0YGwS1iv6vaEGcr0SQbO0IZtbOPqjKjds/WcG+bJA+1T5vCx6kprKOzn5Bg+Vw==} engines: {node: '>=18'} + '@mui/base@5.0.0-beta.68': + resolution: {integrity: sha512-F1JMNeLS9Qhjj3wN86JUQYBtJoXyQvknxlzwNl6eS0ZABo1MiohMONj3/WQzYPSXIKC2bS/ZbyBzdHhi2GnEpA==} + engines: {node: '>=14.0.0'} + deprecated: This package has been replaced by @base-ui-components/react + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/core-downloads-tracker@6.5.0': + resolution: {integrity: sha512-LGb8t8i6M2ZtS3Drn3GbTI1DVhDY6FJ9crEey2lZ0aN2EMZo8IZBZj9wRf4vqbZHaWjsYgtbOnJw5V8UWbmK2Q==} + + '@mui/icons-material@6.5.0': + resolution: {integrity: sha512-VPuPqXqbBPlcVSA0BmnoE4knW4/xG6Thazo8vCLWkOKusko6DtwFV6B665MMWJ9j0KFohTIf3yx2zYtYacvG1g==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@mui/material': ^6.5.0 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/lab@6.0.0-beta.22': + resolution: {integrity: sha512-9nwUfBj+UzoQJOCbqV+JcCSJ74T+gGWrM1FMlXzkahtYUcMN+5Zmh2ArlttW3zv2dZyCzp7K5askcnKF0WzFQg==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@mui/material': ^6.3.1 + '@mui/material-pigment-css': ^6.3.1 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@mui/material-pigment-css': + optional: true + '@types/react': + optional: true + + '@mui/material@6.5.0': + resolution: {integrity: sha512-yjvtXoFcrPLGtgKRxFaH6OQPtcLPhkloC0BML6rBG5UeldR0nPULR/2E2BfXdo5JNV7j7lOzrrLX2Qf/iSidow==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@mui/material-pigment-css': ^6.5.0 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@mui/material-pigment-css': + optional: true + '@types/react': + optional: true + + '@mui/private-theming@6.4.9': + resolution: {integrity: sha512-LktcVmI5X17/Q5SkwjCcdOLBzt1hXuc14jYa7NPShog0GBDCDvKtcnP0V7a2s6EiVRlv7BzbWEJzH6+l/zaCxw==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/styled-engine@6.5.0': + resolution: {integrity: sha512-8woC2zAqF4qUDSPIBZ8v3sakj+WgweolpyM/FXf8jAx6FMls+IE4Y8VDZc+zS805J7PRz31vz73n2SovKGaYgw==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.4.1 + '@emotion/styled': ^11.3.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + + '@mui/system@6.5.0': + resolution: {integrity: sha512-XcbBYxDS+h/lgsoGe78ExXFZXtuIlSBpn/KsZq8PtZcIkUNJInkuDqcLd2rVBQrDC1u+rvVovdaWPf2FHKJf3w==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@types/react': + optional: true + + '@mui/types@7.2.24': + resolution: {integrity: sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/types@7.4.8': + resolution: {integrity: sha512-ZNXLBjkPV6ftLCmmRCafak3XmSn8YV0tKE/ZOhzKys7TZXUiE0mZxlH8zKDo6j6TTUaDnuij68gIG+0Ucm7Xhw==} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/utils@6.4.9': + resolution: {integrity: sha512-Y12Q9hbK9g+ZY0T3Rxrx9m2m10gaphDuUMgWxyV5kNJevVxXYCLclYUCC9vXaIk1/NdNDTcW2Yfr2OGvNFNmHg==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/utils@7.3.5': + resolution: {integrity: sha512-jisvFsEC3sgjUjcPnR4mYfhzjCDIudttSGSbe1o/IXFNu0kZuR+7vqQI0jg8qtcVZBHWrwTfvAZj9MNMumcq1g==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/x-date-pickers@7.29.4': + resolution: {integrity: sha512-wJ3tsqk/y6dp+mXGtT9czciAMEO5Zr3IIAHg9x6IL0Eqanqy0N3chbmQQZv3iq0m2qUpQDLvZ4utZBUTJdjNzw==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.9.0 + '@emotion/styled': ^11.8.1 + '@mui/material': ^5.15.14 || ^6.0.0 || ^7.0.0 + '@mui/system': ^5.15.14 || ^6.0.0 || ^7.0.0 + date-fns: ^2.25.0 || ^3.2.0 || ^4.0.0 + date-fns-jalali: ^2.13.0-0 || ^3.2.0-0 || ^4.0.0-0 + dayjs: ^1.10.7 + luxon: ^3.0.2 + moment: ^2.29.4 + moment-hijri: ^2.1.2 || ^3.0.0 + moment-jalaali: ^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + date-fns: + optional: true + date-fns-jalali: + optional: true + dayjs: + optional: true + luxon: + optional: true + moment: + optional: true + moment-hijri: + optional: true + moment-jalaali: + optional: true + + '@mui/x-internals@7.29.0': + resolution: {integrity: sha512-+Gk6VTZIFD70XreWvdXBwKd8GZ2FlSCuecQFzm6znwqXg1ZsndavrhG9tkxpxo2fM1Zf7Tk8+HcOO0hCbhTQFA==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -395,6 +907,91 @@ packages: '@open-draft/until@2.1.0': resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} + '@popperjs/core@2.11.8': + resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} + + '@react-hookz/deep-equal@1.0.4': + resolution: {integrity: sha512-N56fTrAPUDz/R423pag+n6TXWbvlBZDtTehaGFjK0InmN+V2OFWLE/WmORhmn6Ce7dlwH5+tQN1LJFw3ngTJVg==} + deprecated: PACKAGE IS DEPRECATED AND WILL BE DETED SOON, USE @ver0/deep-equal INSTEAD + + '@react-hookz/web@24.0.4': + resolution: {integrity: sha512-DcIM6JiZklDyHF6CRD1FTXzuggAkQ+3Ncq2Wln7Kdih8GV6ZIeN9JfS6ZaQxpQUxan8/4n0J2V/R7nMeiSrb2Q==} + engines: {node: '>=18.0.0'} + peerDependencies: + js-cookie: ^3.0.5 + react: ^16.8 || ^17 || ^18 + react-dom: ^16.8 || ^17 || ^18 + peerDependenciesMeta: + js-cookie: + optional: true + + '@react-spring/animated@9.7.5': + resolution: {integrity: sha512-Tqrwz7pIlsSDITzxoLS3n/v/YCUHQdOIKtOJf4yL6kYVSDTSmVK1LI1Q3M/uu2Sx4X3pIWF3xLUhlsA6SPNTNg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + + '@react-spring/core@9.7.5': + resolution: {integrity: sha512-rmEqcxRcu7dWh7MnCcMXLvrf6/SDlSokLaLTxiPlAYi11nN3B5oiCUAblO72o+9z/87j2uzxa2Inm8UbLjXA+w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + + '@react-spring/rafz@9.7.5': + resolution: {integrity: sha512-5ZenDQMC48wjUzPAm1EtwQ5Ot3bLIAwwqP2w2owG5KoNdNHpEJV263nGhCeKKmuA3vG2zLLOdu3or6kuDjA6Aw==} + + '@react-spring/shared@9.7.5': + resolution: {integrity: sha512-wdtoJrhUeeyD/PP/zo+np2s1Z820Ohr/BbuVYv+3dVLW7WctoiN7std8rISoYoHpUXtbkpesSKuPIw/6U1w1Pw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + + '@react-spring/three@9.7.5': + resolution: {integrity: sha512-RxIsCoQfUqOS3POmhVHa1wdWS0wyHAUway73uRLp3GAL5U2iYVNdnzQsep6M2NZ994BlW8TcKuMtQHUqOsy6WA==} + peerDependencies: + '@react-three/fiber': '>=6.0' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + three: '>=0.126' + + '@react-spring/types@9.7.5': + resolution: {integrity: sha512-HVj7LrZ4ReHWBimBvu2SKND3cDVUPWKLqRTmWe/fNY6o1owGOX0cAHbdPDTMelgBlVbrTKrre6lFkhqGZErK/g==} + + '@react-three/drei@9.122.0': + resolution: {integrity: sha512-SEO/F/rBCTjlLez7WAlpys+iGe9hty4rNgjZvgkQeXFSiwqD4Hbk/wNHMAbdd8vprO2Aj81mihv4dF5bC7D0CA==} + peerDependencies: + '@react-three/fiber': ^8 + react: ^18 + react-dom: ^18 + three: '>=0.137' + peerDependenciesMeta: + react-dom: + optional: true + + '@react-three/fiber@8.18.0': + resolution: {integrity: sha512-FYZZqD0UUHUswKz3LQl2Z7H24AhD14XGTsIRw3SJaXUxyfVMi+1yiZGmqTcPt/CkPpdU7rrxqcyQ1zJE5DjvIQ==} + peerDependencies: + expo: '>=43.0' + expo-asset: '>=8.4' + expo-file-system: '>=11.0' + expo-gl: '>=11.0' + react: '>=18 <19' + react-dom: '>=18 <19' + react-native: '>=0.64' + three: '>=0.133' + peerDependenciesMeta: + expo: + optional: true + expo-asset: + optional: true + expo-file-system: + optional: true + expo-gl: + optional: true + react-dom: + optional: true + react-native: + optional: true + + '@rolldown/pluginutils@1.0.0-beta.27': + resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + '@rollup/rollup-android-arm-eabi@4.52.2': resolution: {integrity: sha512-o3pcKzJgSGt4d74lSZ+OCnHwkKBeAbFDmbEm5gg70eA8VkyCuC/zV9TwBnmw6VjDlRdF4Pshfb+WE9E6XY1PoQ==} cpu: [arm] @@ -508,34 +1105,112 @@ packages: '@standard-schema/spec@1.0.0': resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} - '@testing-library/dom@10.4.1': - resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} - engines: {node: '>=18'} + '@swc/core-darwin-arm64@1.15.3': + resolution: {integrity: sha512-AXfeQn0CvcQ4cndlIshETx6jrAM45oeUrK8YeEY6oUZU/qzz0Id0CyvlEywxkWVC81Ajpd8TQQ1fW5yx6zQWkQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [darwin] - '@testing-library/jest-dom@6.8.0': - resolution: {integrity: sha512-WgXcWzVM6idy5JaftTVC8Vs83NKRmGJz4Hqs4oyOuO2J4r/y79vvKZsb+CaGyCSEbUPI6OsewfPd0G1A0/TUZQ==} - engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + '@swc/core-darwin-x64@1.15.3': + resolution: {integrity: sha512-p68OeCz1ui+MZYG4wmfJGvcsAcFYb6Sl25H9TxWl+GkBgmNimIiRdnypK9nBGlqMZAcxngNPtnG3kEMNnvoJ2A==} + engines: {node: '>=10'} + cpu: [x64] + os: [darwin] - '@testing-library/react@16.3.0': - resolution: {integrity: sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw==} - engines: {node: '>=18'} - peerDependencies: - '@testing-library/dom': ^10.0.0 - '@types/react': ^18.0.0 || ^19.0.0 - '@types/react-dom': ^18.0.0 || ^19.0.0 - react: ^18.0.0 || ^19.0.0 - react-dom: ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true + '@swc/core-linux-arm-gnueabihf@1.15.3': + resolution: {integrity: sha512-Nuj5iF4JteFgwrai97mUX+xUOl+rQRHqTvnvHMATL/l9xE6/TJfPBpd3hk/PVpClMXG3Uvk1MxUFOEzM1JrMYg==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] - '@testing-library/user-event@14.6.1': - resolution: {integrity: sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==} - engines: {node: '>=12', npm: '>=6'} - peerDependencies: - '@testing-library/dom': '>=7.21.4' + '@swc/core-linux-arm64-gnu@1.15.3': + resolution: {integrity: sha512-2Nc/s8jE6mW2EjXWxO/lyQuLKShcmTrym2LRf5Ayp3ICEMX6HwFqB1EzDhwoMa2DcUgmnZIalesq2lG3krrUNw==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + + '@swc/core-linux-arm64-musl@1.15.3': + resolution: {integrity: sha512-j4SJniZ/qaZ5g8op+p1G9K1z22s/EYGg1UXIb3+Cg4nsxEpF5uSIGEE4mHUfA70L0BR9wKT2QF/zv3vkhfpX4g==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + + '@swc/core-linux-x64-gnu@1.15.3': + resolution: {integrity: sha512-aKttAZnz8YB1VJwPQZtyU8Uk0BfMP63iDMkvjhJzRZVgySmqt/apWSdnoIcZlUoGheBrcqbMC17GGUmur7OT5A==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + + '@swc/core-linux-x64-musl@1.15.3': + resolution: {integrity: sha512-oe8FctPu1gnUsdtGJRO2rvOUIkkIIaHqsO9xxN0bTR7dFTlPTGi2Fhk1tnvXeyAvCPxLIcwD8phzKg6wLv9yug==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + + '@swc/core-win32-arm64-msvc@1.15.3': + resolution: {integrity: sha512-L9AjzP2ZQ/Xh58e0lTRMLvEDrcJpR7GwZqAtIeNLcTK7JVE+QineSyHp0kLkO1rttCHyCy0U74kDTj0dRz6raA==} + engines: {node: '>=10'} + cpu: [arm64] + os: [win32] + + '@swc/core-win32-ia32-msvc@1.15.3': + resolution: {integrity: sha512-B8UtogMzErUPDWUoKONSVBdsgKYd58rRyv2sHJWKOIMCHfZ22FVXICR4O/VwIYtlnZ7ahERcjayBHDlBZpR0aw==} + engines: {node: '>=10'} + cpu: [ia32] + os: [win32] + + '@swc/core-win32-x64-msvc@1.15.3': + resolution: {integrity: sha512-SpZKMR9QBTecHeqpzJdYEfgw30Oo8b/Xl6rjSzBt1g0ZsXyy60KLXrp6IagQyfTYqNYE/caDvwtF2FPn7pomog==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + + '@swc/core@1.15.3': + resolution: {integrity: sha512-Qd8eBPkUFL4eAONgGjycZXj1jFCBW8Fd+xF0PzdTlBCWQIV1xnUT7B93wUANtW3KGjl3TRcOyxwSx/u/jyKw/Q==} + engines: {node: '>=10'} + peerDependencies: + '@swc/helpers': '>=0.5.17' + peerDependenciesMeta: + '@swc/helpers': + optional: true + + '@swc/counter@0.1.3': + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + + '@swc/types@0.1.25': + resolution: {integrity: sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==} + + '@testing-library/dom@10.4.1': + resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} + engines: {node: '>=18'} + + '@testing-library/jest-dom@6.8.0': + resolution: {integrity: sha512-WgXcWzVM6idy5JaftTVC8Vs83NKRmGJz4Hqs4oyOuO2J4r/y79vvKZsb+CaGyCSEbUPI6OsewfPd0G1A0/TUZQ==} + engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + + '@testing-library/react@16.3.0': + resolution: {integrity: sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw==} + engines: {node: '>=18'} + peerDependencies: + '@testing-library/dom': ^10.0.0 + '@types/react': ^18.0.0 || ^19.0.0 + '@types/react-dom': ^18.0.0 || ^19.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@testing-library/user-event@14.6.1': + resolution: {integrity: sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==} + engines: {node: '>=12', npm: '>=6'} + peerDependencies: + '@testing-library/dom': '>=7.21.4' + + '@tweenjs/tween.js@23.1.3': + resolution: {integrity: sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==} '@types/aria-query@5.0.4': resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} @@ -546,18 +1221,69 @@ packages: '@types/cookie@0.6.0': resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + '@types/d3-array@3.0.3': + resolution: {integrity: sha512-Reoy+pKnvsksN0lQUlcH6dOGjRZ/3WRwXR//m+/8lt1BXeI4xyaUZoqULNjyXXRuh0Mj4LNpkCvhUpQlY3X5xQ==} + + '@types/d3-color@3.1.0': + resolution: {integrity: sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA==} + + '@types/d3-delaunay@6.0.1': + resolution: {integrity: sha512-tLxQ2sfT0p6sxdG75c6f/ekqxjyYR0+LwPrsO1mbC9YDBzPJhs2HbJJRrn8Ez1DBoHRo2yx7YEATI+8V1nGMnQ==} + + '@types/d3-format@3.0.1': + resolution: {integrity: sha512-5KY70ifCCzorkLuIkDe0Z9YTf9RR2CjBX1iaJG+rgM/cPP+sO+q9YdQ9WdhQcgPj1EQiJ2/0+yUkkziTG6Lubg==} + + '@types/d3-geo@3.1.0': + resolution: {integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==} + + '@types/d3-interpolate@3.0.1': + resolution: {integrity: sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==} + + '@types/d3-path@1.0.11': + resolution: {integrity: sha512-4pQMp8ldf7UaB/gR8Fvvy69psNHkTpD/pVw3vmEi8iZAB9EPMBruB1JvHO4BIq9QkUUd2lV1F5YXpMNj7JPBpw==} + + '@types/d3-scale@4.0.2': + resolution: {integrity: sha512-Yk4htunhPAwN0XGlIwArRomOjdoBFXC3+kCxK2Ubg7I9shQlVSJy/pG/Ht5ASN+gdMIalpk8TJ5xV74jFsetLA==} + + '@types/d3-shape@1.3.12': + resolution: {integrity: sha512-8oMzcd4+poSLGgV0R1Q1rOlx/xdmozS4Xab7np0eamFFUYq71AU9pOCJEFnkXW2aI/oXdVYJzw6pssbSut7Z9Q==} + + '@types/d3-time-format@2.1.0': + resolution: {integrity: sha512-/myT3I7EwlukNOX2xVdMzb8FRgNzRMpsZddwst9Ld/VFe6LyJyRp0s32l/V9XoUzk+Gqu56F/oGk6507+8BxrA==} + + '@types/d3-time@3.0.0': + resolution: {integrity: sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==} + '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + '@types/draco3d@1.4.10': + resolution: {integrity: sha512-AX22jp8Y7wwaBgAixaSvkoG4M/+PlAcm3Qs4OW8yT9DM4xUpWKeFhLueTAyZF39pviAdcDdeJoACapiAceqNcw==} + '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/geojson@7946.0.16': + resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/lodash@4.17.21': + resolution: {integrity: sha512-FOvQ0YPD5NOfPgMzJihoT+Za5pdkDJWcbpuj1DjaKZIr/gxodQjY/uWEFlTNqW2ugXHUiL8lRQgw63dzKHZdeQ==} + + '@types/ndarray@1.0.14': + resolution: {integrity: sha512-oANmFZMnFQvb219SSBIhI1Ih/r4CvHDOzkWyJS/XRqkMrGH5/kaPSA1hQhdIBzouaE+5KpE/f5ylI9cujmckQg==} + '@types/node@24.5.2': resolution: {integrity: sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ==} + '@types/offscreencanvas@2019.7.3': + resolution: {integrity: sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==} + + '@types/parse-json@4.0.2': + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + '@types/prop-types@15.7.15': resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} @@ -566,12 +1292,40 @@ packages: peerDependencies: '@types/react': ^18.0.0 + '@types/react-reconciler@0.26.7': + resolution: {integrity: sha512-mBDYl8x+oyPX/VBb3E638N0B7xG+SPk/EAMcVPeexqus/5aTpTphQi0curhhshOqRrc9t6OPoJfEUkbymse/lQ==} + + '@types/react-reconciler@0.28.9': + resolution: {integrity: sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg==} + peerDependencies: + '@types/react': '*' + + '@types/react-relay@18.2.1': + resolution: {integrity: sha512-KgmFapsxAylhxcFfaAv5GZZJhTHnDvV8IDZVsUm5afpJUvgZC1Y68ssfOGsFfiFY/2EhxHM/YPfpdKbfmF3Ecg==} + + '@types/react-transition-group@4.4.12': + resolution: {integrity: sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==} + peerDependencies: + '@types/react': '*' + '@types/react@18.3.24': resolution: {integrity: sha512-0dLEBsA1kI3OezMBF8nSsb7Nk19ZnsyE1LLhB8r27KbgU5H4pvuqZLdtE+aUkJVoXgTVuA+iLIwmZ0TuK4tx6A==} + '@types/relay-runtime@19.0.3': + resolution: {integrity: sha512-pvpWWQq5e9KeESF8klQaP2igLLhr2bRd3XxVCxNpGElsPQiP6Mejr59RT9/OGY3O3i8jAGGQsshVe0QCQDbxNg==} + + '@types/stats.js@0.17.4': + resolution: {integrity: sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==} + '@types/statuses@2.0.6': resolution: {integrity: sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==} + '@types/three@0.181.0': + resolution: {integrity: sha512-MLF1ks8yRM2k71D7RprFpDb9DOX0p22DbdPqT/uAkc6AtQXjxWCVDjCy23G9t1o8HcQPk7woD2NIyiaWcWPYmA==} + + '@types/webxr@0.5.24': + resolution: {integrity: sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==} + '@typescript-eslint/eslint-plugin@8.44.1': resolution: {integrity: sha512-molgphGqOBT7t4YKCSkbasmu1tb1MgrZ2szGzHbclF7PNmOkSTQVHy+2jXOSnxvR3+Xe1yySHFZoqMpz3TfQsw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -631,6 +1385,87 @@ packages: resolution: {integrity: sha512-576+u0QD+Jp3tZzvfRfxon0EA2lzcDt3lhUbsC6Lgzy9x2VR4E+JUiNyGHi5T8vk0TV+fpJ5GLG1JsJuWCaKhw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@use-gesture/core@10.3.1': + resolution: {integrity: sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==} + + '@use-gesture/react@10.3.1': + resolution: {integrity: sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g==} + peerDependencies: + react: '>= 16.8.0' + + '@visx/axis@3.10.1': + resolution: {integrity: sha512-HBEDLcpZoJ16hFbkYu3S6mN5mbwlFmUWY5yN967X06RdIL4LmAG3gnZ7u4F9buA3LQo+trJXW78moN005odD4Q==} + peerDependencies: + react: ^16.3.0-0 || ^17.0.0-0 || ^18.0.0-0 + + '@visx/bounds@3.3.0': + resolution: {integrity: sha512-gESmN+4N2NkeUzqQEDZaS63umkGfMp9XjQcKBqtOR64mjjQtamh3lNVRWvKjJ2Zb421RbYHWq22Wv9nay6ZUOg==} + peerDependencies: + react: ^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0 + react-dom: ^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0 + + '@visx/curve@3.3.0': + resolution: {integrity: sha512-G1l1rzGWwIs8ka3mBhO/gj8uYK6XdU/3bwRSoiZ+MockMahQFPog0bUkuVgPwwzPSJfsA/E5u53Y/DNesnHQxg==} + + '@visx/drag@3.12.0': + resolution: {integrity: sha512-LXOoPVw//YPjpYhDJYBsCYDuv1QimsXjDV98duH0aCy4V94ediXMQpe2wHq4pnlDobLEB71FjOZMFrbFmqtERg==} + peerDependencies: + react: ^16.8.0-0 || ^17.0.0-0 || ^18.0.0-0 + + '@visx/drag@3.3.0': + resolution: {integrity: sha512-fLNsorq6GyANCqAE/dToG0q7YoGVxihGC9FZQUp0MCV1wMJIJ45ximhrl5NDng2ytbpWnBmXu8M8hdsdFuvIXw==} + peerDependencies: + react: ^16.8.0-0 || ^17.0.0-0 || ^18.0.0-0 + + '@visx/event@3.12.0': + resolution: {integrity: sha512-9Lvw6qJ0Fi+y1vsC1WspfdIKCxHTb7oy59Uql1uBdPGT8zChP0vuxW0jQNQRDbKgoefj4pCXAFi8+MF1mEtVTw==} + + '@visx/event@3.3.0': + resolution: {integrity: sha512-fKalbNgNz2ooVOTXhvcOx5IlEQDgVfX66rI7bgZhBxI2/scy+5rWcXJXpwkheRF68SMx9R93SjKW6tmiD0h+jA==} + + '@visx/grid@3.5.0': + resolution: {integrity: sha512-i1pdobTE223ItMiER3q4ojIaZWja3vg46TkS6FotnBZ4c0VRDHSrALQPdi0na+YEgppASWCQ2WrI/vD6mIkhSg==} + peerDependencies: + react: ^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0 + + '@visx/group@3.3.0': + resolution: {integrity: sha512-yKepDKwJqlzvnvPS0yDuW13XNrYJE4xzT6xM7J++441nu6IybWWwextyap8ey+kU651cYDb+q1Oi6aHvQwyEyw==} + peerDependencies: + react: ^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0 + + '@visx/point@3.12.0': + resolution: {integrity: sha512-I6UrHoJAEVbx3RORQNupgTiX5EzjuZpiwLPxn8L2mI5nfERotPKi1Yus12Cq2WtXqEBR/WgqTnoImlqOXBykcA==} + + '@visx/point@3.3.0': + resolution: {integrity: sha512-03eBBIJarkmX79WbeEGTUZwmS5/MUuabbiM9KfkGS9pETBTWkp1DZtEHZdp5z34x5TDQVLSi0rk1Plg3/8RtDg==} + + '@visx/scale@3.5.0': + resolution: {integrity: sha512-xo3zrXV2IZxrMq9Y9RUVJUpd93h3NO/r/y3GVi5F9AsbOzOhsLIbsPkunhO9mpUSR8LZ9TiumLEBrY+3frRBSg==} + + '@visx/shape@3.5.0': + resolution: {integrity: sha512-DP3t9jBQ7dSE3e6ptA1xO4QAIGxO55GrY/6P+S6YREuQGjZgq20TLYLAsiaoPEzFSS4tp0m12ZTPivWhU2VBTw==} + peerDependencies: + react: ^16.3.0-0 || ^17.0.0-0 || ^18.0.0-0 + + '@visx/text@3.3.0': + resolution: {integrity: sha512-fOimcsf0GtQE9whM5MdA/xIkHMaV29z7qNqNXysUDE8znSMKsN+ott7kSg2ljAEE89CQo3WKHkPNettoVsa84w==} + peerDependencies: + react: ^16.3.0-0 || ^17.0.0-0 || ^18.0.0-0 + + '@visx/tooltip@3.3.0': + resolution: {integrity: sha512-0ovbxnvAphEU/RVJprWHdOJT7p3YfBDpwXclXRuhIY2EkH59g8sDHatDcYwiNPeqk61jBh1KACRZxqToMuutlg==} + peerDependencies: + react: ^16.8.0-0 || ^17.0.0-0 || ^18.0.0-0 + react-dom: ^16.8.0-0 || ^17.0.0-0 || ^18.0.0-0 + + '@visx/vendor@3.5.0': + resolution: {integrity: sha512-yt3SEZRVmt36+APsCISSO9eSOtzQkBjt+QRxNRzcTWuzwMAaF3PHCCSe31++kkpgY9yFoF+Gfes1TBe5NlETiQ==} + + '@vitejs/plugin-react-swc@3.11.0': + resolution: {integrity: sha512-YTJCGFdNMHCMfjODYtxRNVAYmTWQ1Lb8PulP/2/f/oEEtglw8oKxKIZmmRkyXrVrHfsKOaVkAc3NT9/dMutO5w==} + peerDependencies: + vite: ^4 || ^5 || ^6 || ^7 + '@vitest/expect@3.2.4': resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} @@ -689,23 +1524,53 @@ packages: '@vitest/utils@4.0.13': resolution: {integrity: sha512-ydozWyQ4LZuu8rLp47xFUWis5VOKMdHjXCWhs1LuJsTNKww+pTHQNK4e0assIB9K80TxFyskENL6vCu3j34EYA==} + '@webgpu/types@0.1.66': + resolution: {integrity: sha512-YA2hLrwLpDsRueNDXIMqN9NTzD6bCDkuXbOSe0heS+f8YE8usA6Gbv1prj81pzVHrbaAma7zObnIC+I6/sXJgA==} + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + acorn@7.4.1: + resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} + engines: {node: '>=0.4.0'} + hasBin: true + acorn@8.15.0: resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} engines: {node: '>=0.4.0'} hasBin: true + afterframe@1.0.2: + resolution: {integrity: sha512-0JeMZI7dIfVs5guqLgidQNV7c6jBC2HO0QNSekAUB82Hr7PdU9QXNAF3kpFkvATvHYDDTGto7FPsRu1ey+aKJQ==} + agent-base@7.1.4: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} + ajv-formats@2.1.1: + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + + align-text@0.1.4: + resolution: {integrity: sha512-GrTZLRpmp6wIC2ztrWW9MjjTgSKccffgFagbNDOX95/dcjEcYZibYTeaOntySQLcdw1ztBoFkviiUvTMbb9MYg==} + engines: {node: '>=0.10.0'} + + amdefine@1.0.1: + resolution: {integrity: sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==} + engines: {node: '>=0.4.2'} + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -718,6 +1583,9 @@ packages: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -728,13 +1596,42 @@ packages: resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} engines: {node: '>= 0.4'} + asap@2.0.6: + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + babel-plugin-macros@2.8.0: + resolution: {integrity: sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==} + + babel-plugin-macros@3.1.0: + resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} + engines: {node: '>=10', npm: '>=6'} + + babel-plugin-relay@20.1.1: + resolution: {integrity: sha512-BWlqLPiHbxZTxlyng2rVgsZCwztHNje7H8FR4c+UKy3ErQJBG6BKLr9vUdeR7mAZCH2v0sOAxNhG6zR1FrWjAg==} + + balanced-match@0.4.2: + resolution: {integrity: sha512-STw03mQKnGUYtoNjmowo4F2cRmIIxYEGiMsjjwla/u5P1lxadj/05WkNaFjNiKTgJkj8KiXbgAiRTmcQRwQNtg==} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + baseline-browser-mapping@2.8.31: + resolution: {integrity: sha512-a28v2eWrrRWPpJSzxc+mKwm0ZtVx/G8SepdQZDArnXYU/XS+IF6mp8aB/4E+hH1tyGCoDo3KlUCdlSxGDsRkAw==} + hasBin: true + + bidi-js@1.0.3: + resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} + + bit-twiddle@1.0.2: + resolution: {integrity: sha512-B9UhK0DKFZhoTFcfvAzhqsjStvGJp9vYWf3+6SNTtdSQnvIgfkHbgHrg/e4+TH71N2GDu8tpmCVoyfrL1d7ntA==} + brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} @@ -745,6 +1642,17 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + browserslist@4.28.0: + resolution: {integrity: sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + bufferutil@4.0.9: resolution: {integrity: sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==} engines: {node: '>=6.14.2'} @@ -753,10 +1661,38 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} + caller-callsite@2.0.0: + resolution: {integrity: sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==} + engines: {node: '>=4'} + + caller-path@2.0.0: + resolution: {integrity: sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==} + engines: {node: '>=4'} + + callsites@2.0.0: + resolution: {integrity: sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==} + engines: {node: '>=4'} + callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} + camelcase@1.2.1: + resolution: {integrity: sha512-wzLkDa4K/mzI1OSITC+DUyjgIl/ETNHE9QvYgy6J6Jvqyyz4C0Xfd+lQhb19sX2jMpZV4IssUn0VDVmglV+s4g==} + engines: {node: '>=0.10.0'} + + camera-controls@2.10.1: + resolution: {integrity: sha512-KnaKdcvkBJ1Irbrzl8XD6WtZltkRjp869Jx8c0ujs9K+9WD+1D7ryBsCiVqJYUqt6i/HR5FxT7RLASieUD+Q5w==} + peerDependencies: + three: '>=0.126.1' + + caniuse-lite@1.0.30001757: + resolution: {integrity: sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ==} + + center-align@0.1.3: + resolution: {integrity: sha512-Baz3aNe2gd2LP2qk5U+sDk/m4oSuwSDcBfayTCTBoWpfIGO5XFxPmjILQII4NGiZjD6DoDI6kf7gKaxkf7s3VQ==} + engines: {node: '>=0.10.0'} + chai@5.3.3: resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} engines: {node: '>=18'} @@ -773,14 +1709,28 @@ packages: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} + classnames@2.5.1: + resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} + cli-width@4.1.0: resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} engines: {node: '>= 12'} + cliui@2.1.0: + resolution: {integrity: sha512-GIOYRizG+TGoc7Wgc1LiOTLare95R3mzKgoln+Q/lE4ceiYH19gUpl0l0Ffq4lJDEf3FxujMe6IBfOCs7pfqNA==} + cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} + clsx@1.2.1: + resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} + engines: {node: '>=6'} + + 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'} @@ -791,10 +1741,47 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + concat-stream@1.6.2: + resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} + engines: {'0': node >= 0.8} + + convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie@0.7.2: resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} engines: {node: '>= 0.6'} + cookie@1.0.2: + resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} + engines: {node: '>=18'} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + cosmiconfig@5.2.1: + resolution: {integrity: sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==} + engines: {node: '>=4'} + + cosmiconfig@6.0.0: + resolution: {integrity: sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==} + engines: {node: '>=8'} + + cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + + cross-env@7.0.3: + resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} + engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} + hasBin: true + + cross-fetch@3.2.0: + resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -809,10 +1796,84 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + cwise-compiler@1.1.3: + resolution: {integrity: sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==} + + cwise-parser@1.0.3: + resolution: {integrity: sha512-nAe238ctwjt9l5exq9CQkHS1Tj6YRGAQxqfL4VaN1B2oqG1Ss0VVqIrBG/vyOgN301PI22wL6ZIhe/zA+BO56Q==} + + cwise@1.0.10: + resolution: {integrity: sha512-4OQ6FXVTRO2bk/OkIEt0rNqDk63aOv3Siny6ZD2/WN9CH7k8X6XyQdcip4zKg1WG+L8GP5t2zicXbDb+H7Y77Q==} + + d3-array@3.2.1: + resolution: {integrity: sha512-gUY/qeHq/yNqqoCKNq4vtpFLdoCdvyNpWoC/KNjhGbhDuQpAM9sIQQKkXSNpXa9h5KySs/gzm7R88WkUutgwWQ==} + engines: {node: '>=12'} + + d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-delaunay@6.0.2: + resolution: {integrity: sha512-IMLNldruDQScrcfT+MWnazhHbDJhcRJyOEBAJfwQnHle1RPh6WDuLvxNArUju2VSMSUuKlY5BGHRJ2cYyoFLQQ==} + engines: {node: '>=12'} + + d3-format@3.1.0: + resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==} + engines: {node: '>=12'} + + d3-geo@3.1.0: + resolution: {integrity: sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==} + engines: {node: '>=12'} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-path@1.0.9: + resolution: {integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==} + + d3-scale-chromatic@3.1.0: + resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==} + engines: {node: '>=12'} + + d3-scale@4.0.2: + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + engines: {node: '>=12'} + + d3-shape@1.3.7: + resolution: {integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==} + + d3-time-format@4.1.0: + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + engines: {node: '>=12'} + + d3-time@3.1.0: + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + engines: {node: '>=12'} + + d@1.0.2: + resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==} + engines: {node: '>=0.12'} + data-urls@5.0.0: resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} engines: {node: '>=18'} + dayjs@1.10.7: + resolution: {integrity: sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig==} + + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -822,6 +1883,10 @@ packages: supports-color: optional: true + decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + decimal.js@10.6.0: resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} @@ -832,10 +1897,16 @@ packages: deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + delaunator@5.0.1: + resolution: {integrity: sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==} + dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} + detect-gpu@5.0.70: + resolution: {integrity: sha512-bqerEP1Ese6nt3rFkwPnGbsUF9a4q+gMmpTVVOEzoCyeCc+y7/RvJnQZJx1JwhgQI5Ntg0Kgat8Uu7XpBqnz1w==} + detect-indent@7.0.2: resolution: {integrity: sha512-y+8xyqdGLL+6sh0tVeHcfP/QDd8gUgbasolJJpY7NgeQGSZ739bDtSiaiDgtoicy+mtYB81dKLxO9xRhCyIB3A==} engines: {node: '>=12.20'} @@ -850,6 +1921,21 @@ packages: dom-accessibility-api@0.6.3: resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + dom-helpers@5.2.1: + resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} + + draco3d@1.5.7: + resolution: {integrity: sha512-m6WCKt/erDXcw+70IJXnG7M3awwQPAsZvJGX5zY7beBqpELw6RDGkYVU0W43AFxye4pDZ5i2Lbyc/NNGqwjUVQ==} + + dup@1.0.0: + resolution: {integrity: sha512-Bz5jxMMC0wgp23Zm15ip1x8IhYRqJvF3nFC0UInJUDkN1z4uNPk9jTnfCUJXbOGiQ1JbXLQsiV41Fb+HXcj5BA==} + + duplexer2@0.0.2: + resolution: {integrity: sha512-+AWBwjGadtksxjOQSFDhPNQbed7icNXApT4+2BNpsXzcCBiInq2H9XW0O8sfHFaPmnQRs7cg/P0fAr2IWQSW0g==} + + electron-to-chromium@1.5.260: + resolution: {integrity: sha512-ov8rBoOBhVawpzdre+Cmz4FB+y66Eqrk6Gwqd8NGxuhv99GQ8XqMAr351KEkOt7gukXWDg6gJWEMKgL2RLMPtA==} + emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -857,9 +1943,23 @@ packages: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + es-module-lexer@1.7.0: resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + es5-ext@0.10.64: + resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==} + engines: {node: '>=0.10'} + + es6-iterator@2.0.3: + resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==} + + es6-symbol@3.1.4: + resolution: {integrity: sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==} + engines: {node: '>=0.12'} + esbuild@0.25.10: resolution: {integrity: sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==} engines: {node: '>=18'} @@ -873,6 +1973,27 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} + escodegen@0.0.28: + resolution: {integrity: sha512-6ioQhg16lFs5c7XJlJFXIDxBjO4yRvXC9yK6dLNNGuhI3a/fJukHanPF6qtpjGDgAFzI8Wuq3PSIarWmaOq/5A==} + engines: {node: '>=0.4.0'} + hasBin: true + + escodegen@1.3.3: + resolution: {integrity: sha512-z9FWgKc48wjMlpzF5ymKS1AF8OIgnKLp9VyN7KbdtyrP/9lndwUFqCtMm+TAJmJf7KJFFYc4cFJfVTTGkKEwsA==} + engines: {node: '>=0.10.0'} + hasBin: true + + 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.24: + resolution: {integrity: sha512-nLHIW7TEq3aLrEYWpVaJ1dRgFR+wLDPN8e8FpYAql/bMV2oBEfC37K0gLEGgv9fy66juNShSMV8OkTqzltcG/w==} + 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} @@ -895,10 +2016,34 @@ packages: jiti: optional: true + esniff@2.0.1: + resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==} + engines: {node: '>=0.10'} + espree@10.4.0: resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + esprima@1.0.4: + resolution: {integrity: sha512-rp5dMKN8zEs9dfi9g0X1ClLmV//WRyk/R15mppFNICIFRG5P92VP7Z04p8pk++gABo9W2tY+kHyu6P1mEHgmTA==} + engines: {node: '>=0.4.0'} + hasBin: true + + esprima@1.1.1: + resolution: {integrity: sha512-qxxB994/7NtERxgXdFgLHIs9M6bhLXc6qtUmWZ3L8+gTQ9qaoyki2887P2IqAYsoENyr8SUbTutStDniOHSDHg==} + engines: {node: '>=0.4.0'} + hasBin: true + + esprima@1.2.5: + resolution: {integrity: sha512-S9VbPDU0adFErpDai3qDkjq8+G05ONtKzcyNrPKg/ZKa+tf879nX2KexNU95b31UoTJjRLInNBHHHjFPoCd7lQ==} + engines: {node: '>=0.4.0'} + hasBin: true + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + esquery@1.6.0: resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} engines: {node: '>=0.10'} @@ -907,6 +2052,14 @@ packages: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} + estraverse@1.3.2: + resolution: {integrity: sha512-OkbCPVUu8D9tbsLcUR+CKFRBbhZlogmkbWaP3BPERlkqzWL5Q6IdTz6eUk+b5cid2MTaCqJb2nNRGoJ8TpfPrg==} + engines: {node: '>=0.4.0'} + + estraverse@1.5.1: + resolution: {integrity: sha512-FpCjJDfmo3vsc/1zKSeqR5k42tcIhxFIlvq+h9j0fO2q/h2uLKyweq7rYJ+0CoVvrGQOxIS5wyBrW/+vF58BUQ==} + engines: {node: '>=0.4.0'} + estraverse@5.3.0: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} @@ -914,14 +2067,28 @@ packages: estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + esutils@1.0.0: + resolution: {integrity: sha512-x/iYH53X3quDwfHRz4y8rn4XcEwwCJeWsul9pF1zldMbGtgOtMNBEOuYWwB1EQlK2LRa1fev3YAgym/RElp5Cg==} + engines: {node: '>=0.10.0'} + esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} + event-emitter@0.3.5: + resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==} + expect-type@1.2.2: resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} engines: {node: '>=12.0.0'} + ext@1.7.0: + resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==} + + falafel@2.2.5: + resolution: {integrity: sha512-HuC1qF9iTnHDnML9YZAdCDQwT0yKl/U55K4XSUXqGAA2GLoafFgWRqdAbhWJxXaYD4pyoVxAJ8wH670jMpI9DQ==} + engines: {node: '>=0.4.0'} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -935,9 +2102,18 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-uri@3.1.0: + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + fbjs-css-vars@1.0.2: + resolution: {integrity: sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==} + + fbjs@3.0.5: + resolution: {integrity: sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==} + fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -947,6 +2123,12 @@ packages: picomatch: optional: true + fflate@0.6.10: + resolution: {integrity: sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg==} + + fflate@0.8.2: + resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} @@ -955,6 +2137,9 @@ packages: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} + find-root@1.1.0: + resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} + find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -971,10 +2156,20 @@ packages: 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-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} + get-node-dimensions@1.2.1: + resolution: {integrity: sha512-2MSPMu7S1iOTL+BOa6K1S62hB2zUAYNF/lV0gSVlOaacd087lc6nR1H1r0e3B1CerTo+RceOmi1iJW+vp21xcQ==} + git-hooks-list@4.1.1: resolution: {integrity: sha512-cmP497iLq54AZnv4YRAEMnEyQ1eIn4tGKbmswqwmFV4GBnAqE8NLtWxxdXa++AalfgL5EBH4IxTPyquEuGY/jA==} @@ -990,9 +2185,20 @@ packages: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} + globals@16.5.0: + resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==} + engines: {node: '>=18'} + + glsl-noise@0.0.0: + resolution: {integrity: sha512-b/ZCF6amfAUb7dJM/MxRs7AetQEahYzJ8PtgfrmEdtw6uyGOr+ZSGtgjFm6mfsBkxJ4d2W7kg+Nlqzqvn3Bc0w==} + graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + graphql@15.3.0: + resolution: {integrity: sha512-GTCJtzJmkFLWRfFJuoo9RWWa/FfamUHgiFosxi/X1Ani4AVWbeyBenZTNX6dM+7WSbbFfTo/25eh0LLkwHMw2w==} + engines: {node: '>= 10.x'} + graphql@16.11.0: resolution: {integrity: sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==} engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} @@ -1001,9 +2207,23 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} + has@1.0.4: + resolution: {integrity: sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==} + engines: {node: '>= 0.4.0'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + headers-polyfill@4.0.3: resolution: {integrity: sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==} + hls.js@1.6.15: + resolution: {integrity: sha512-E3a5VwgXimGHwpRGV+WxRTKeSp2DW5DI5MWv34ulL3t5UNmyJWCQ1KmLEHbYzcfThfXG8amBL+fCYPneGHC4VA==} + + hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + html-encoding-sniffer@4.0.0: resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} engines: {node: '>=18'} @@ -1020,6 +2240,9 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -1028,6 +2251,13 @@ packages: resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} + immediate@3.0.6: + resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + + import-fresh@2.0.0: + resolution: {integrity: sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==} + engines: {node: '>=4'} + import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} @@ -1040,6 +2270,33 @@ packages: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + + invariant@2.2.4: + resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} + + iota-array@1.0.0: + resolution: {integrity: sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-buffer@1.1.6: + resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-directory@0.3.1: + resolution: {integrity: sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==} + engines: {node: '>=0.10.0'} + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -1066,15 +2323,43 @@ packages: is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + is-promise@2.2.2: + resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==} + + is-typedarray@1.0.0: + resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} + + isarray@0.0.1: + resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isndarray@1.0.0: + resolution: {integrity: sha512-QvUHIbvXZE/N8Mmi0mndfk9KrqsHGE5U20dekg6SaQNY94yteoM+f4M4Dc7AnMjCwwEeHUqKggH8mvtJdSWvsg==} + engines: {node: '>= 0.8.0'} + + its-fine@1.2.5: + resolution: {integrity: sha512-fXtDA0X0t0eBYAGLVM5YsgJGsJ5jEmqZEPrGbzdf5awjv0xE7nqv3TVnvtUF060Tkes15DbDAKW/I48vsb6SyA==} + peerDependencies: + react: '>=18.0' + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} js-tokens@9.0.1: resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + js-yaml@3.14.2: + resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==} + hasBin: true + js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true @@ -1088,22 +2373,55 @@ packages: canvas: optional: 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-parse-better-errors@1.0.2: + resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + 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 + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + kind-of@3.2.2: + resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==} + engines: {node: '>=0.10.0'} + + lazy-cache@1.0.4: + resolution: {integrity: sha512-RE2g0b5VGZsOCFOCgP7omTRYFqydmZkBwl5oNnQ1lDYC57uyO9KqNnNVxT7COSHTxrRCWVcAVOcbjk+tvh/rgQ==} + engines: {node: '>=0.10.0'} + levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lie@3.3.0: + resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -1111,6 +2429,13 @@ packages: lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + longest@1.0.1: + resolution: {integrity: sha512-k+yt5n3l48JU4k8ftnKG6V7u32wyH2NfKzeMto9F/QRE0amxy/LayxwlvjjkZEIzqR+19IrtFO8p5kB9QaYUFg==} + engines: {node: '>=0.10.0'} + loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -1121,20 +2446,49 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lz-string@1.5.0: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true + maath@0.10.8: + resolution: {integrity: sha512-tRvbDF0Pgqz+9XUa4jjfgAQ8/aPKmQdWXilFu2tMy4GWj4NOsx99HlULO4IeREfbO3a0sA145DZYyvXPkybm0g==} + peerDependencies: + '@types/three': '>=0.134.0' + three: '>=0.134.0' + magic-string@0.30.19: resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + math-expression-evaluator@1.4.0: + resolution: {integrity: sha512-4vRUvPyxdO8cWULGTh9dZWL2tZK6LDBvj+OGHBER7poH9Qdt7kXEoj20wiz4lQUbUXQZFjPbe5mVDo9nutizCw==} + + memoize-one@5.2.1: + resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} + + memoize-one@6.0.0: + resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} + meshline@3.3.1: + resolution: {integrity: sha512-/TQj+JdZkeSUOl5Mk2J7eLcYTLiQm2IDzmlSvYm7ov15anEcDJ92GHqqazxTSreeNgfnYu24kiEvvv0WlbCdFQ==} + peerDependencies: + three: '>=0.137' + + meshoptimizer@0.22.0: + resolution: {integrity: sha512-IebiK79sqIy+E4EgOr+CAw+Ke8hAspXKzBd0JdgEmPHiAwmvEj2S4h1rfvo+o/BnfEYd/jAOg5IeeIjzlzSnDg==} + + messagepack@1.1.12: + resolution: {integrity: sha512-pNB6K4q4VMLRXdvlGZkTtQhmKFntvLisnOQnL0VhKpZooL8B8Wsv5TXuidIJil0bCH6V172p3+Onfyow0usPYQ==} + micromatch@4.0.8: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} @@ -1150,6 +2504,12 @@ packages: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} + minimist@0.0.8: + resolution: {integrity: sha512-miQKw5Hv4NS1Psg2517mV4e4dYNaO3++hjAvLOAzKqZ61rH8NS1SK+vbfBWZ5PY/Me/bEWhUwqMghEW5Fb9T7Q==} + + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -1175,13 +2535,59 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + ndarray-concat-rows@1.0.1: + resolution: {integrity: sha512-DXlnuu6ODvutYKg6wYz0PXXCXDzpR5keqZ+ryb4yfF3lYjULuKjWD6YW1bZlXs9FZplGAG2dibv7K5glFo0Kfw==} + + ndarray-fill@1.0.2: + resolution: {integrity: sha512-Ozx0GZTM0TXwhfBfgTiyuYVZAmSX9Q1CUF1zWrYKXRSiWr0dDjMkkHcSy0tCZ4so4cw/Hl6ddn5pK43xPwroLQ==} + + ndarray-linspace@2.0.3: + resolution: {integrity: sha512-7pJ4vJ5CkUg2QEzsAWrah3zKWOHXxbBq4jttwGcQu6q+4T3UzEZXf/MaHTRRL8mvTVlRpQIJRoBr6knR6InWzg==} + + ndarray-ops@1.2.2: + resolution: {integrity: sha512-BppWAFRjMYF7N/r6Ie51q6D4fs0iiGmeXIACKY66fLpnwIui3Wc3CXiD/30mgLbDjPpSLrsqcp3Z62+IcHZsDw==} + + ndarray-scratch@1.2.0: + resolution: {integrity: sha512-a4pASwB1jQyJcKLYrwrladVfDZDUGc78qLJZbHyb1Q4rhte0URhzc6ALQpBcauwgov0sXLwZz3vYH5jKAhSMIg==} + + ndarray@1.0.19: + resolution: {integrity: sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ==} + + next-tick@1.1.0: + resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + node-gyp-build@4.8.4: resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} hasBin: true + node-releases@2.0.27: + resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + + nullthrows@1.1.1: + resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==} + nwsapi@2.2.22: resolution: {integrity: sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ==} + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@0.4.0: + resolution: {integrity: sha512-8WvkvUZiKAjjsy/63rJjA7jw9uyF0CLVLjBKEfnPHE3Jxvs1LgwqL2OmJN+LliIX1vrzKW+AAu02Cc+xv27ncQ==} + + object-keys@0.4.0: + resolution: {integrity: sha512-ncrLw+X55z7bkl5PnUvHwFK9FcGuFYo9gtjws2XtSzL+aZ8tm830P60WJ0dSmFVaSalWieW5MD7kEdnXda9yJw==} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -1201,6 +2607,14 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} + parse-json@4.0.0: + resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} + engines: {node: '>=4'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} @@ -1212,9 +2626,16 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-to-regexp@6.3.0: resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} @@ -1237,6 +2658,9 @@ packages: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} + potpack@1.0.2: + resolution: {integrity: sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==} + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -1250,6 +2674,18 @@ packages: resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + promise-worker-transferable@1.0.4: + resolution: {integrity: sha512-bN+0ehEnrXfxV2ZQvU2PetO0n4gqBD4ulq3MI1WOPLgr7/Mg9yRQkX5+0v1vagr74ZTsl7XtzlaYDo2EuCeYJw==} + + promise@7.3.1: + resolution: {integrity: sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -1257,30 +2693,197 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + quote-stream@0.0.0: + resolution: {integrity: sha512-m4VtvjAMx00wgAS6eOy50ZDat1EBQeFKBIrtF/oxUt0MenEI33y7runJcRiOihc+JBBIt2aFFJhILIh4e9shJA==} + + react-colorful@5.6.1: + resolution: {integrity: sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + react-composer@5.0.3: + resolution: {integrity: sha512-1uWd07EME6XZvMfapwZmc7NgCZqDemcvicRi3wMJzXsQLvZ3L7fTHVyPy1bZdnWXM4iPjYuNE+uJ41MLKeTtnA==} + peerDependencies: + react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom@18.3.1: resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} peerDependencies: react: ^18.3.1 + react-draggable@4.5.0: + resolution: {integrity: sha512-VC+HBLEZ0XJxnOxVAZsdRi8rD04Iz3SiiKOoYzamjylUcju/hP9np/aZdLHf/7WOD268WMoNJMvYfB5yAK45cw==} + peerDependencies: + react: '>= 16.3.0' + react-dom: '>= 16.3.0' + + react-error-boundary@6.0.0: + resolution: {integrity: sha512-gdlJjD7NWr0IfkPlaREN2d9uUZUlksrfOx7SX62VRerwXbMY6ftGCIZua1VG1aXFNOimhISsTq+Owp725b9SiA==} + peerDependencies: + react: '>=16.13.1' + + react-icons@5.2.1: + resolution: {integrity: sha512-zdbW5GstTzXaVKvGSyTaBalt7HSfuK5ovrzlpyiWHAFXndXTdd/1hdDHI4xBM1Mn7YriT6aqESucFl9kEXzrdw==} + peerDependencies: + react: '*' + + react-icons@5.5.0: + resolution: {integrity: sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==} + peerDependencies: + react: '*' + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + react-is@19.2.0: + resolution: {integrity: sha512-x3Ax3kNSMIIkyVYhWPyO09bu0uttcAIoecO/um/rKGQ4EltYWVYtyiGkS/3xMynrbVQdS69Jhlv8FXUEZehlzA==} + + react-keyed-flatten-children@3.0.0: + resolution: {integrity: sha512-tSH6gvOyQjt3qtjG+kU9sTypclL1672yjpVufcE3aHNM0FhvjBUQZqsb/awIux4zEuVC3k/DP4p0GdTT/QUt/Q==} + peerDependencies: + react: '>=15.0.0' + + react-measure@2.5.2: + resolution: {integrity: sha512-M+rpbTLWJ3FD6FXvYV6YEGvQ5tMayQ3fGrZhRPHrE9bVlBYfDCLuDcgNttYfk8IqfOI03jz6cbpqMRTUclQnaA==} + peerDependencies: + react: '>0.13.0' + react-dom: '>0.13.0' + + react-reconciler@0.27.0: + resolution: {integrity: sha512-HmMDKciQjYmBRGuuhIaKA1ba/7a+UsM5FzOZsMO2JYHt9Jh8reCb7j1eDC95NOyUlKM9KRyvdx0flBuDvYSBoA==} + engines: {node: '>=0.10.0'} + peerDependencies: + react: ^18.0.0 + + react-relay@20.1.1: + resolution: {integrity: sha512-pwl7wHHXCOx32dOg4TVNkhVojgGVvOBMo9pcMGeM1BIFYvmwGauKTtBB+qr5buXHGooCL8TXjMVg+ZLizIsbeQ==} + peerDependencies: + react: ^16.9.0 || ^17 || ^18 || ^19 + + react-router-dom@7.9.6: + resolution: {integrity: sha512-2MkC2XSXq6HjGcihnx1s0DBWQETI4mlis4Ux7YTLvP67xnGxCvq+BcCQSO81qQHVUTM1V53tl4iVVaY5sReCOA==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + + 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-select@5.10.2: + resolution: {integrity: sha512-Z33nHdEFWq9tfnfVXaiM12rbJmk+QjFEztWLtmXqQhz6Al4UZZ9xc0wiatmGtUOCCnHN0WizL3tCMYRENX4rVQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + react-slider@2.0.4: + resolution: {integrity: sha512-sWwQD01n6v+MbeLCYthJGZPc0kzOyhQHyd0bSo0edg+IAxTVQmj3Oy4SBK65eX6gNwS9meUn6Z5sIBUVmwAd9g==} + peerDependencies: + react: ^16 || ^17 || ^18 + + react-toastify@9.1.3: + resolution: {integrity: sha512-fPfb8ghtn/XMxw3LkxQBk3IyagNpF/LIKjOBflbexr2AWxAH1MJgvnESwEwBn9liLFXgTKWgBSdZpw9m4OTHTg==} + peerDependencies: + react: '>=16' + react-dom: '>=16' + + react-transition-group@4.4.5: + resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} + peerDependencies: + react: '>=16.6.0' + react-dom: '>=16.6.0' + + react-use-measure@2.1.7: + resolution: {integrity: sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg==} + peerDependencies: + react: '>=16.13' + react-dom: '>=16.13' + peerDependenciesMeta: + react-dom: + optional: true + + react-use-websocket@4.13.0: + resolution: {integrity: sha512-anMuVoV//g2N76Wxqvqjjo1X48r9Np3y1/gMl7arX84tAPXdy5R7sB5lO5hvCzQRYjqXwV8XMAiEBOUbyrZFrw==} + + react-window@1.8.10: + resolution: {integrity: sha512-Y0Cx+dnU6NLa5/EvoHukUD0BklJ8qITCtVEPY1C/nL8wwoZ0b5aEw8Ff1dOVHw7fCzMt55XfJDd8S8W8LCaUCg==} + engines: {node: '>8.0.0'} + peerDependencies: + react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + react@18.3.1: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} + readable-stream@1.0.34: + resolution: {integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==} + + readable-stream@1.1.14: + resolution: {integrity: sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + redent@3.0.0: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} + reduce-css-calc@1.3.0: + resolution: {integrity: sha512-0dVfwYVOlf/LBA2ec4OwQ6p3X9mYxn/wOl2xTcLwjnPYrkgEfPx3VI4eGCH3rQLlPISG5v9I9bkZosKsNRTRKA==} + + reduce-function-call@1.0.3: + resolution: {integrity: sha512-Hl/tuV2VDgWgCSEeWMLwxLZqX7OK59eU1guxXsRKTAyeYimivsKdtcV4fu3r710tpG5GmDKDhQ0HSZLExnNmyQ==} + + relay-compiler@20.1.1: + resolution: {integrity: sha512-J/FFFLS/3vnbDkyQMw8l3Ev7dNHXMgC1RAs0fITz4Q63TFPOw142knKxY1Mm5ZZBABkAs9g2JghXaqM+phwTxw==} + hasBin: true + + relay-runtime@20.1.1: + resolution: {integrity: sha512-N98ZkkyuIHdXmHaPuljihM1QbFYXATF0gxA0CESFphszsQzXs9A/zZloVfzdOI/xg3B3CfM/5nozvIoeTDIcfw==} + + repeat-string@1.6.1: + resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} + engines: {node: '>=0.10'} + require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + resize-observer-polyfill@1.5.1: + resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} + + resolve-from@3.0.0: + resolution: {integrity: sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==} + engines: {node: '>=4'} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} + resolve@1.22.11: + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + engines: {node: '>= 0.4'} + hasBin: true + rettime@0.7.0: resolution: {integrity: sha512-LPRKoHnLKd/r3dVxcwO7vhCW+orkOGj9ViueosEBK6ie89CijnfRlhaDhHq/3Hxu4CkWQtxwlBG0mzTQY6uQjw==} @@ -1288,6 +2891,13 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + right-align@0.1.3: + resolution: {integrity: sha512-yqINtL/G7vs2v+dFIZmFUDbnVyFUJFKd6gK22Kgo6R4jfJGFtisKyncWDDULgjfqf4ASQuIQyjJ7XZ+3aWpsAg==} + engines: {node: '>=0.10.0'} + + robust-predicates@3.0.2: + resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} + rollup@4.52.2: resolution: {integrity: sha512-I25/2QgoROE1vYV+NQ1En9T9UFB9Cmfm2CJ83zZOlaDpvz29wGQSZXWKw7MiNXau7wYgB/T9fVIdIuEQ+KbiiA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -1299,6 +2909,9 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -1306,14 +2919,30 @@ packages: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} + scheduler@0.21.0: + resolution: {integrity: sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==} + scheduler@0.23.2: resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + semver@7.7.2: resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} engines: {node: '>=10'} hasBin: true + set-cookie-parser@2.7.2: + resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + + shallow-copy@0.0.1: + resolution: {integrity: sha512-b6i4ZpVuUxB9h5gfCxPiusKYkqTMOjEbBs4wMaFbkfia4yFv92UKZ6Df8WXcKbn08JNL/abvg3FnMAOfakDvUw==} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -1341,9 +2970,39 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + source-map@0.1.43: + resolution: {integrity: sha512-VtCvB9SIQhk3aF6h+N85EaqIaBFIAfZ9Cu+NJHHVvc8BbEcnvDcFw6sqQ2dQrT6SlOrZq3tIvyD9+EGq/lJryQ==} + engines: {node: '>=0.8.0'} + + source-map@0.5.7: + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + engines: {node: '>=0.10.0'} + + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + static-eval@0.2.4: + resolution: {integrity: sha512-6dWWPfa/0+1zULdQi7ssT5EQZHsGK8LygBzhE/HdafNCo4e/Ibt7vLPfxBw9VcdVV+t0ARtN4ZAJKtApVc0A5Q==} + + static-module@1.5.0: + resolution: {integrity: sha512-XTj7pQOHT33l77lK/Pu8UXqzI44C6LYAqwAc9hLTTESHRqJAFudBpReuopFPpoRr5CtOoSmGfFQC6FPlbDnyCw==} + + stats-gl@2.4.2: + resolution: {integrity: sha512-g5O9B0hm9CvnM36+v7SFl39T7hmAlv541tU81ME8YeSb3i1CIP5/QdDeSB3A0la0bKNHpxpwxOVRo2wFTYEosQ==} + peerDependencies: + '@types/three': '*' + three: '*' + + stats.js@0.17.0: + resolution: {integrity: sha512-hNKz8phvYLPEcRkeG1rsGmV5ChMjKDAWU7/OJJdDErPBNChQXxCo3WZurGpnWc6gZhAzEPFad1aVgyOANH1sMw==} + statuses@2.0.2: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} @@ -1361,6 +3020,12 @@ packages: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} + string_decoder@0.10.31: + resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -1376,13 +3041,45 @@ packages: strip-literal@3.0.0: resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==} + stylis@4.2.0: + resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + suspend-react@0.1.3: + resolution: {integrity: sha512-aqldKgX9aZqpoDp3e8/BZ8Dm7x1pJl+qI3ZKxDN0i/IQTWUwBx/ManmlVJ3wowqbno6c2bmiIfs+Um6LbsjJyQ==} + peerDependencies: + react: '>=17.0' + symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + tabbable@6.3.0: + resolution: {integrity: sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ==} + + three-mesh-bvh@0.7.8: + resolution: {integrity: sha512-BGEZTOIC14U0XIRw3tO4jY7IjP7n7v24nv9JXS1CyeVRWOCkcOMhRnmENUjuV39gktAw4Ofhr0OvIAiTspQrrw==} + deprecated: Deprecated due to three.js version incompatibility. Please use v0.8.0, instead. + peerDependencies: + three: '>= 0.151.0' + + three-stdlib@2.36.1: + resolution: {integrity: sha512-XyGQrFmNQ5O/IoKm556ftwKsBg11TIb301MB5dWNicziQBEs2g3gtOYIf7pFiLa0zI2gUwhtCjv9fmjnxKZ1Cg==} + peerDependencies: + three: '>=0.128.0' + + three@0.167.1: + resolution: {integrity: sha512-gYTLJA/UQip6J/tJvl91YYqlZF47+D/kxiWrbTon35ZHlXEN0VOo+Qke2walF1/x92v55H6enomymg4Dak52kw==} + + through2@0.4.2: + resolution: {integrity: sha512-45Llu+EwHKtAZYTPPVn3XZHBgakWMN3rokhEv5hu596XP+cNgplMg+Gj+1nmAvj+L0K7+N49zBKx5rah5u0QIQ==} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -1435,16 +3132,35 @@ packages: resolution: {integrity: sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==} engines: {node: '>=16'} + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + tr46@5.1.1: resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} engines: {node: '>=18'} + troika-three-text@0.52.4: + resolution: {integrity: sha512-V50EwcYGruV5rUZ9F4aNsrytGdKcXKALjEtQXIOBfhVoZU9VAqZNIoGQ3TMiooVqFAbR1w15T+f+8gkzoFzawg==} + peerDependencies: + three: '>=0.125.0' + + troika-three-utils@0.52.4: + resolution: {integrity: sha512-NORAStSVa/BDiG52Mfudk4j1FG4jC4ILutB3foPnfGbOeIs9+G5vZLa0pnmnaftZUGm4UwSoqEpWdqvC7zms3A==} + peerDependencies: + three: '>=0.125.0' + + troika-worker-utils@0.52.0: + resolution: {integrity: sha512-W1CpvTHykaPH5brv5VHLfQo9D1OYuo0cSBEUQFFT/nBUzM8iD6Lq2/tgG/f1OelbAS1WtaTPQzE5uM49egnngw==} + ts-api-utils@2.1.0: resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} engines: {node: '>=18.12'} peerDependencies: typescript: '>=4.8.4' + tunnel-rat@0.1.2: + resolution: {integrity: sha512-lR5VHmkPhzdhrM092lI2nACsLO4QubF0/yoOhzX7c+wIpbN1GjHNzCc91QlpxBi+cnx8vVJ+Ur6vL5cEoQPFpQ==} + turbo-darwin-64@2.5.6: resolution: {integrity: sha512-3C1xEdo4aFwMJAPvtlPqz1Sw/+cddWIOmsalHFMrsqqydcptwBfu26WW2cDm3u93bUzMbBJ8k3zNKFqxJ9ei2A==} cpu: [x64] @@ -1487,6 +3203,18 @@ packages: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} + type@2.7.3: + resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==} + + typedarray-pool@1.2.0: + resolution: {integrity: sha512-YTSQbzX43yvtpfRtIDAYygoYtgT+Rpjuxy9iOpczrjpXLgGoyG7aS5USJXV2d3nn8uHTeb9rXDvzS27zUg5KYQ==} + + typedarray-to-buffer@3.1.5: + resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} + + typedarray@0.0.6: + resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} + typescript-eslint@8.44.1: resolution: {integrity: sha512-0ws8uWGrUVTjEeN2OM4K1pLKHK/4NiNP/vz6ns+LjT/6sqpaYzIVFajZb1fj/IDwpsrrHb3Jy0Qm5u9CPcKaeg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1499,24 +3227,92 @@ packages: engines: {node: '>=14.17'} hasBin: true + ua-parser-js@1.0.41: + resolution: {integrity: sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug==} + hasBin: true + + uglify-js@2.8.29: + resolution: {integrity: sha512-qLq/4y2pjcU3vhlhseXGGJ7VbFO4pBANu0kwl8VCa9KEI0V8VfZIx2Fy3w01iSTA/pGwKZSmu/+I4etLNDdt5w==} + engines: {node: '>=0.8.0'} + hasBin: true + + uglify-to-browserify@1.0.2: + resolution: {integrity: sha512-vb2s1lYx2xBtUgy+ta+b2J/GLVUR+wmpINwHePmPRhOsIVCG2wDzKJ0n14GslH1BifsqVzSOwQhRaCAsZ/nI4Q==} + undici-types@7.12.0: resolution: {integrity: sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==} + uniq@1.0.1: + resolution: {integrity: sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==} + until-async@3.0.2: resolution: {integrity: sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw==} + update-browserslist-db@1.1.4: + resolution: {integrity: sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + use-isomorphic-layout-effect@1.2.1: + resolution: {integrity: sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + use-sync-external-store@1.2.0: + resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + utf-8-validate@5.0.10: resolution: {integrity: sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==} engines: {node: '>=6.14.2'} + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + util-extend@1.0.3: + resolution: {integrity: sha512-mLs5zAK+ctllYBj+iAQvlDCwoxU/WDOUaJkcFudeiAX6OajC6BKXJUa9a+tbtkC11dz2Ufb7h0lyvIOVn4LADA==} + + utility-types@3.11.0: + resolution: {integrity: sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==} + engines: {node: '>= 4'} + + validate.io-boolean@1.0.4: + resolution: {integrity: sha512-kQrj4QmbgBhOFg3T7B5WLxuQ5KFRy0D+hI53EojFUTVHQ+RXfy2VUjbpLa5hq+3c4OQb3qQGdF0VrlJHdKREgA==} + + validate.io-integer@1.0.5: + resolution: {integrity: sha512-22izsYSLojN/P6bppBqhgUDjCkr5RY2jd+N2a3DCAUey8ydvrZ/OkGvFPR7qfOpwR2LC5p4Ngzxz36g5Vgr/hQ==} + + validate.io-nonnegative-integer@1.0.0: + resolution: {integrity: sha512-uOMekPwcl84yg8NR7zgIZCZ9pHCtd9CK1Ri51N+ZJLTe1HyLbmdFdy7ZmfkiHkMvB1pOxeQmd1/LBjKhUD1L3A==} + + validate.io-number@1.0.3: + resolution: {integrity: sha512-kRAyotcbNaSYoDnXvb4MHg/0a1egJdLwS6oJ38TJY7aw9n93Fl/3blIXdyYvPOp55CNxywooG/3BcrwNrBpcSg==} + vite-node@3.2.4: resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true + vite-plugin-relay@2.1.0: + resolution: {integrity: sha512-k7tQFeJQlJy0S6OrQxuk5WrauHouGfRgFFCos0PatYiUY2gnwPZPi30KPuUtvdrzTPPTgZVHj51xJAp/3oMRiQ==} + peerDependencies: + babel-plugin-relay: '>=14.1.0' + vite: '>=2.0.0' + vite@7.1.7: resolution: {integrity: sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1626,10 +3422,23 @@ packages: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} - webidl-conversions@7.0.0: - resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + webgl-constants@1.1.1: + resolution: {integrity: sha512-LkBXKjU5r9vAW7Gcu3T5u+5cvSvh5WwINdr0C+9jpzVB41cjQAP5ePArDtk/WHYdVj0GefCgM73BA7FlIiNtdg==} + + webgl-sdf-generator@1.1.1: + resolution: {integrity: sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA==} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} engines: {node: '>=12'} + websocket@1.0.35: + resolution: {integrity: sha512-/REy6amwPZl44DDzvRCkaI1q1bIiQB0mEFQLUrhz3z2EK91cp3n72rAjUlrTP0zV22HJIUOVHQGPxhFRjxjt+Q==} + engines: {node: '>=4.0.0'} + whatwg-encoding@3.1.1: resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} engines: {node: '>=18'} @@ -1642,6 +3451,9 @@ packages: resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==} engines: {node: '>=18'} + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -1652,10 +3464,18 @@ packages: engines: {node: '>=8'} hasBin: true + window-size@0.1.0: + resolution: {integrity: sha512-1pTPQDKTdd61ozlKGNCjhNRd+KPmgLSGa3mZTHoOliaGcESD8G1PXhh7c1fgiPjVbNVfgy2Faw4BI8/m0cC8Mg==} + engines: {node: '>= 0.8.0'} + word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + wordwrap@0.0.2: + resolution: {integrity: sha512-xSBsCeh+g+dinoBv3GAOWM4LcVVO68wLXRanibtBSdUvkGWQRGeE9P7IwU9EmDDi4jA6L44lz15CGMwdw9N5+Q==} + engines: {node: '>=0.4.0'} + wrap-ansi@6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} @@ -1683,10 +3503,26 @@ packages: xmlchars@2.2.0: resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + xtend@2.1.2: + resolution: {integrity: sha512-vMNKzr2rHP9Dp/e1NQFnLQlwlhp9L/LfvnsVdHxN1f+uggyVI3i08uD14GPvCToPkdsRfyPqIyYGmIk58V98ZQ==} + engines: {node: '>=0.4'} + y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} + yaeti@0.0.6: + resolution: {integrity: sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==} + engines: {node: '>=0.10.32'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} @@ -1695,6 +3531,9 @@ packages: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} + yargs@3.10.0: + resolution: {integrity: sha512-QFzUah88GAGy9lyDKGBqZdkYApt63rCXYBGYnEP4xDJPXNqXXnBDACnbrXnViV6jRSqAePwrATi2i8mfYm4L1A==} + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -1703,6 +3542,63 @@ packages: resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==} engines: {node: '>=18'} + zustand@3.7.2: + resolution: {integrity: sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==} + engines: {node: '>=12.7.0'} + peerDependencies: + react: '>=16.8' + peerDependenciesMeta: + react: + optional: true + + zustand@4.5.4: + resolution: {integrity: sha512-/BPMyLKJPtFEvVL0E9E9BTUM63MNyhPGlvxk1XjrfWTUlV+BR8jufjsovHzrtR6YNcBEcL7cMHovL1n9xHawEg==} + engines: {node: '>=12.7.0'} + peerDependencies: + '@types/react': '>=16.8' + immer: '>=9.0.6' + react: '>=16.8' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + + zustand@4.5.7: + resolution: {integrity: sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==} + engines: {node: '>=12.7.0'} + peerDependencies: + '@types/react': '>=16.8' + immer: '>=9.0.6' + react: '>=16.8' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + + 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: '@adobe/css-tools@4.4.4': {} @@ -1721,19 +3617,111 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 + '@babel/compat-data@7.28.5': {} + + '@babel/core@7.28.5': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.28.5': + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.27.2': + dependencies: + '@babel/compat-data': 7.28.5 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.0 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.27.1': {} + '@babel/helper-validator-identifier@7.27.1': {} + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.28.4': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 + + '@babel/parser@7.28.5': + dependencies: + '@babel/types': 7.28.5 + '@babel/runtime@7.28.4': {} + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + + '@babel/traverse@7.28.5': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.28.5': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@bundled-es-modules/cookie@2.0.1': dependencies: cookie: 0.7.2 - optional: true '@bundled-es-modules/statuses@1.0.1': dependencies: statuses: 2.0.2 - optional: true '@csstools/color-helpers@5.1.0': {} @@ -1755,6 +3743,140 @@ snapshots: '@csstools/css-tokenizer@3.0.4': {} + '@date-io/core@3.2.0': {} + + '@date-io/dayjs@3.2.0(dayjs@1.10.7)': + dependencies: + '@date-io/core': 3.2.0 + optionalDependencies: + dayjs: 1.10.7 + + '@diamondlightsource/davidia@1.0.4(@h5web/lib@13.0.0(@react-three/fiber@8.18.0(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.167.1))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.167.1)(typescript@5.9.2))(@react-three/drei@9.122.0(@react-three/fiber@8.18.0(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.167.1))(@types/react@18.3.24)(@types/three@0.181.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.167.1)(use-sync-external-store@1.6.0(react@18.3.1)))(@react-three/fiber@8.18.0(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.167.1))(@types/react@18.3.24)(ndarray@1.0.19)(react-dom@18.3.1(react@18.3.1))(react-toastify@9.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(three@0.167.1)': + dependencies: + '@h5web/lib': 13.0.0(@react-three/fiber@8.18.0(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.167.1))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.167.1)(typescript@5.9.2) + '@react-hookz/web': 24.0.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@react-three/drei': 9.122.0(@react-three/fiber@8.18.0(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.167.1))(@types/react@18.3.24)(@types/three@0.181.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.167.1)(use-sync-external-store@1.6.0(react@18.3.1)) + '@react-three/fiber': 8.18.0(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.167.1) + '@visx/drag': 3.12.0(react@18.3.1) + afterframe: 1.0.2 + buffer: 6.0.3 + cwise: 1.0.10 + d3-array: 3.2.4 + d3-format: 3.1.0 + d3-scale: 4.0.2 + messagepack: 1.1.12 + ndarray: 1.0.19 + ndarray-concat-rows: 1.0.1 + ndarray-linspace: 2.0.3 + react: 18.3.1 + react-colorful: 5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-dom: 18.3.1(react@18.3.1) + react-draggable: 4.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-icons: 5.5.0(react@18.3.1) + react-select: 5.10.2(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-toastify: 9.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-use-websocket: 4.13.0 + three: 0.167.1 + websocket: 1.0.35 + transitivePeerDependencies: + - '@types/react' + - js-cookie + - supports-color + + '@diamondlightsource/sci-react-ui@0.2.0(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@mui/icons-material@6.5.0(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': + dependencies: + '@emotion/react': 11.14.0(@types/react@18.3.24)(react@18.3.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1) + '@mui/icons-material': 6.5.0(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.24)(react@18.3.1) + '@mui/material': 6.5.0(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-icons: 5.5.0(react@18.3.1) + + '@dimforge/rapier3d-compat@0.12.0': {} + + '@emotion/babel-plugin@11.13.5': + dependencies: + '@babel/helper-module-imports': 7.27.1 + '@babel/runtime': 7.28.4 + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/serialize': 1.3.3 + babel-plugin-macros: 3.1.0 + convert-source-map: 1.9.0 + escape-string-regexp: 4.0.0 + find-root: 1.1.0 + source-map: 0.5.7 + stylis: 4.2.0 + transitivePeerDependencies: + - supports-color + + '@emotion/cache@11.14.0': + dependencies: + '@emotion/memoize': 0.9.0 + '@emotion/sheet': 1.4.0 + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + stylis: 4.2.0 + + '@emotion/hash@0.9.2': {} + + '@emotion/is-prop-valid@1.4.0': + dependencies: + '@emotion/memoize': 0.9.0 + + '@emotion/memoize@0.9.0': {} + + '@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@emotion/babel-plugin': 11.13.5 + '@emotion/cache': 11.14.0 + '@emotion/serialize': 1.3.3 + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@18.3.1) + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + hoist-non-react-statics: 3.3.2 + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.24 + transitivePeerDependencies: + - supports-color + + '@emotion/serialize@1.3.3': + dependencies: + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/unitless': 0.10.0 + '@emotion/utils': 1.4.2 + csstype: 3.1.3 + + '@emotion/sheet@1.4.0': {} + + '@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@emotion/babel-plugin': 11.13.5 + '@emotion/is-prop-valid': 1.4.0 + '@emotion/react': 11.14.0(@types/react@18.3.24)(react@18.3.1) + '@emotion/serialize': 1.3.3 + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@18.3.1) + '@emotion/utils': 1.4.2 + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.24 + transitivePeerDependencies: + - supports-color + + '@emotion/unitless@0.10.0': {} + + '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@emotion/utils@1.4.2': {} + + '@emotion/weak-memoize@0.4.0': {} + '@esbuild/aix-ppc64@0.25.10': optional: true @@ -1877,6 +3999,66 @@ snapshots: '@eslint/core': 0.15.2 levn: 0.4.1 + '@floating-ui/core@1.7.3': + dependencies: + '@floating-ui/utils': 0.2.10 + + '@floating-ui/dom@1.7.4': + dependencies: + '@floating-ui/core': 1.7.3 + '@floating-ui/utils': 0.2.10 + + '@floating-ui/react-dom@2.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@floating-ui/dom': 1.7.4 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@floating-ui/react@0.26.20(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@floating-ui/react-dom': 2.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@floating-ui/utils': 0.2.10 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + tabbable: 6.3.0 + + '@floating-ui/utils@0.2.10': {} + + '@h5web/lib@13.0.0(@react-three/fiber@8.18.0(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.167.1))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.167.1)(typescript@5.9.2)': + dependencies: + '@floating-ui/react': 0.26.20(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@react-hookz/web': 24.0.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@react-three/fiber': 8.18.0(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.167.1) + '@visx/axis': 3.10.1(react@18.3.1) + '@visx/drag': 3.3.0(react@18.3.1) + '@visx/grid': 3.5.0(react@18.3.1) + '@visx/scale': 3.5.0 + '@visx/shape': 3.5.0(react@18.3.1) + '@visx/tooltip': 3.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + d3-array: 3.2.4 + d3-color: 3.1.0 + d3-format: 3.1.0 + d3-interpolate: 3.0.1 + d3-scale: 4.0.2 + d3-scale-chromatic: 3.1.0 + ndarray: 1.0.19 + ndarray-ops: 1.2.2 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-icons: 5.2.1(react@18.3.1) + react-keyed-flatten-children: 3.0.0(react@18.3.1) + react-measure: 2.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-slider: 2.0.4(react@18.3.1) + react-window: 1.8.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + three: 0.167.1 + zustand: 4.5.4(@types/react@18.3.24)(react@18.3.1) + optionalDependencies: + typescript: 5.9.2 + transitivePeerDependencies: + - '@types/react' + - immer + - js-cookie + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.7': @@ -1888,8 +4070,7 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} - '@inquirer/ansi@1.0.0': - optional: true + '@inquirer/ansi@1.0.0': {} '@inquirer/confirm@5.1.18(@types/node@24.5.2)': dependencies: @@ -1897,7 +4078,6 @@ snapshots: '@inquirer/type': 3.0.8(@types/node@24.5.2) optionalDependencies: '@types/node': 24.5.2 - optional: true '@inquirer/core@10.2.2(@types/node@24.5.2)': dependencies: @@ -1911,18 +4091,66 @@ snapshots: yoctocolors-cjs: 2.1.3 optionalDependencies: '@types/node': 24.5.2 - optional: true - '@inquirer/figures@1.0.13': - optional: true + '@inquirer/figures@1.0.13': {} '@inquirer/type@3.0.8(@types/node@24.5.2)': optionalDependencies: '@types/node': 24.5.2 - optional: true + + '@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 + + '@jsonforms/core@3.6.0': + dependencies: + '@types/json-schema': 7.0.15 + ajv: 8.17.1 + ajv-formats: 2.1.1(ajv@8.17.1) + lodash: 4.17.21 + + '@jsonforms/material-renderers@3.6.0(356738369fe2ead2b97aa81a07be8ba2)': + dependencies: + '@date-io/dayjs': 3.2.0(dayjs@1.10.7) + '@emotion/react': 11.14.0(@types/react@18.3.24)(react@18.3.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1) + '@jsonforms/core': 3.6.0 + '@jsonforms/react': 3.6.0(@jsonforms/core@3.6.0)(react@18.3.1) + '@mui/icons-material': 6.5.0(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.24)(react@18.3.1) + '@mui/material': 6.5.0(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/x-date-pickers': 7.29.4(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@6.5.0(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(dayjs@1.10.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + dayjs: 1.10.7 + lodash: 4.17.21 + react: 18.3.1 + + '@jsonforms/react@3.6.0(@jsonforms/core@3.6.0)(react@18.3.1)': + dependencies: + '@jsonforms/core': 3.6.0 + lodash: 4.17.21 + react: 18.3.1 + + '@mediapipe/tasks-vision@0.10.17': {} + + '@monogrid/gainmap-js@3.4.0(three@0.167.1)': + dependencies: + promise-worker-transferable: 1.0.4 + three: 0.167.1 + '@mswjs/interceptors@0.39.6': dependencies: '@open-draft/deferred-promise': 2.2.0 @@ -1931,7 +4159,168 @@ snapshots: is-node-process: 1.2.0 outvariant: 1.4.3 strict-event-emitter: 0.5.1 - optional: true + + '@mui/base@5.0.0-beta.68(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@floating-ui/react-dom': 2.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/types': 7.4.8(@types/react@18.3.24) + '@mui/utils': 6.4.9(@types/react@18.3.24)(react@18.3.1) + '@popperjs/core': 2.11.8 + clsx: 2.1.1 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.24 + + '@mui/core-downloads-tracker@6.5.0': {} + + '@mui/icons-material@6.5.0(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.24)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@mui/material': 6.5.0(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.24 + + '@mui/lab@6.0.0-beta.22(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@mui/base': 5.0.0-beta.68(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/material': 6.5.0(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/system': 6.5.0(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1) + '@mui/types': 7.4.8(@types/react@18.3.24) + '@mui/utils': 6.4.9(@types/react@18.3.24)(react@18.3.1) + clsx: 2.1.1 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@18.3.24)(react@18.3.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1) + '@types/react': 18.3.24 + + '@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@mui/core-downloads-tracker': 6.5.0 + '@mui/system': 6.5.0(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1) + '@mui/types': 7.2.24(@types/react@18.3.24) + '@mui/utils': 6.4.9(@types/react@18.3.24)(react@18.3.1) + '@popperjs/core': 2.11.8 + '@types/react-transition-group': 4.4.12(@types/react@18.3.24) + clsx: 2.1.1 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-is: 19.2.0 + react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@18.3.24)(react@18.3.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1) + '@types/react': 18.3.24 + + '@mui/private-theming@6.4.9(@types/react@18.3.24)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@mui/utils': 6.4.9(@types/react@18.3.24)(react@18.3.1) + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.24 + + '@mui/styled-engine@6.5.0(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@emotion/cache': 11.14.0 + '@emotion/serialize': 1.3.3 + '@emotion/sheet': 1.4.0 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@18.3.24)(react@18.3.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1) + + '@mui/system@6.5.0(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@mui/private-theming': 6.4.9(@types/react@18.3.24)(react@18.3.1) + '@mui/styled-engine': 6.5.0(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(react@18.3.1) + '@mui/types': 7.2.24(@types/react@18.3.24) + '@mui/utils': 6.4.9(@types/react@18.3.24)(react@18.3.1) + clsx: 2.1.1 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@18.3.24)(react@18.3.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1) + '@types/react': 18.3.24 + + '@mui/types@7.2.24(@types/react@18.3.24)': + optionalDependencies: + '@types/react': 18.3.24 + + '@mui/types@7.4.8(@types/react@18.3.24)': + dependencies: + '@babel/runtime': 7.28.4 + optionalDependencies: + '@types/react': 18.3.24 + + '@mui/utils@6.4.9(@types/react@18.3.24)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@mui/types': 7.2.24(@types/react@18.3.24) + '@types/prop-types': 15.7.15 + clsx: 2.1.1 + prop-types: 15.8.1 + react: 18.3.1 + react-is: 19.2.0 + optionalDependencies: + '@types/react': 18.3.24 + + '@mui/utils@7.3.5(@types/react@18.3.24)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@mui/types': 7.4.8(@types/react@18.3.24) + '@types/prop-types': 15.7.15 + clsx: 2.1.1 + prop-types: 15.8.1 + react: 18.3.1 + react-is: 19.2.0 + optionalDependencies: + '@types/react': 18.3.24 + + '@mui/x-date-pickers@7.29.4(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@6.5.0(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(dayjs@1.10.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@mui/material': 6.5.0(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/system': 6.5.0(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1) + '@mui/utils': 7.3.5(@types/react@18.3.24)(react@18.3.1) + '@mui/x-internals': 7.29.0(@types/react@18.3.24)(react@18.3.1) + '@types/react-transition-group': 4.4.12(@types/react@18.3.24) + clsx: 2.1.1 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@18.3.24)(react@18.3.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.24)(react@18.3.1))(@types/react@18.3.24)(react@18.3.1) + dayjs: 1.10.7 + transitivePeerDependencies: + - '@types/react' + + '@mui/x-internals@7.29.0(@types/react@18.3.24)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@mui/utils': 7.3.5(@types/react@18.3.24)(react@18.3.1) + react: 18.3.1 + transitivePeerDependencies: + - '@types/react' '@nodelib/fs.scandir@2.1.5': dependencies: @@ -1945,17 +4334,114 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 - '@open-draft/deferred-promise@2.2.0': - optional: true + '@open-draft/deferred-promise@2.2.0': {} '@open-draft/logger@0.3.0': dependencies: is-node-process: 1.2.0 outvariant: 1.4.3 - optional: true - '@open-draft/until@2.1.0': - optional: true + '@open-draft/until@2.1.0': {} + + '@popperjs/core@2.11.8': {} + + '@react-hookz/deep-equal@1.0.4': {} + + '@react-hookz/web@24.0.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@react-hookz/deep-equal': 1.0.4 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@react-spring/animated@9.7.5(react@18.3.1)': + dependencies: + '@react-spring/shared': 9.7.5(react@18.3.1) + '@react-spring/types': 9.7.5 + react: 18.3.1 + + '@react-spring/core@9.7.5(react@18.3.1)': + dependencies: + '@react-spring/animated': 9.7.5(react@18.3.1) + '@react-spring/shared': 9.7.5(react@18.3.1) + '@react-spring/types': 9.7.5 + react: 18.3.1 + + '@react-spring/rafz@9.7.5': {} + + '@react-spring/shared@9.7.5(react@18.3.1)': + dependencies: + '@react-spring/rafz': 9.7.5 + '@react-spring/types': 9.7.5 + react: 18.3.1 + + '@react-spring/three@9.7.5(@react-three/fiber@8.18.0(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.167.1))(react@18.3.1)(three@0.167.1)': + dependencies: + '@react-spring/animated': 9.7.5(react@18.3.1) + '@react-spring/core': 9.7.5(react@18.3.1) + '@react-spring/shared': 9.7.5(react@18.3.1) + '@react-spring/types': 9.7.5 + '@react-three/fiber': 8.18.0(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.167.1) + react: 18.3.1 + three: 0.167.1 + + '@react-spring/types@9.7.5': {} + + '@react-three/drei@9.122.0(@react-three/fiber@8.18.0(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.167.1))(@types/react@18.3.24)(@types/three@0.181.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.167.1)(use-sync-external-store@1.6.0(react@18.3.1))': + dependencies: + '@babel/runtime': 7.28.4 + '@mediapipe/tasks-vision': 0.10.17 + '@monogrid/gainmap-js': 3.4.0(three@0.167.1) + '@react-spring/three': 9.7.5(@react-three/fiber@8.18.0(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.167.1))(react@18.3.1)(three@0.167.1) + '@react-three/fiber': 8.18.0(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.167.1) + '@use-gesture/react': 10.3.1(react@18.3.1) + camera-controls: 2.10.1(three@0.167.1) + cross-env: 7.0.3 + detect-gpu: 5.0.70 + glsl-noise: 0.0.0 + hls.js: 1.6.15 + maath: 0.10.8(@types/three@0.181.0)(three@0.167.1) + meshline: 3.3.1(three@0.167.1) + react: 18.3.1 + react-composer: 5.0.3(react@18.3.1) + stats-gl: 2.4.2(@types/three@0.181.0)(three@0.167.1) + stats.js: 0.17.0 + suspend-react: 0.1.3(react@18.3.1) + three: 0.167.1 + three-mesh-bvh: 0.7.8(three@0.167.1) + three-stdlib: 2.36.1(three@0.167.1) + troika-three-text: 0.52.4(three@0.167.1) + tunnel-rat: 0.1.2(@types/react@18.3.24)(react@18.3.1) + utility-types: 3.11.0 + zustand: 5.0.8(@types/react@18.3.24)(react@18.3.1)(use-sync-external-store@1.6.0(react@18.3.1)) + optionalDependencies: + react-dom: 18.3.1(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + - '@types/three' + - immer + - use-sync-external-store + + '@react-three/fiber@8.18.0(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.167.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@types/react-reconciler': 0.26.7 + '@types/webxr': 0.5.24 + base64-js: 1.5.1 + buffer: 6.0.3 + its-fine: 1.2.5(@types/react@18.3.24)(react@18.3.1) + react: 18.3.1 + react-reconciler: 0.27.0(react@18.3.1) + react-use-measure: 2.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + scheduler: 0.21.0 + suspend-react: 0.1.3(react@18.3.1) + three: 0.167.1 + zustand: 3.7.2(react@18.3.1) + optionalDependencies: + react-dom: 18.3.1(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + + '@rolldown/pluginutils@1.0.0-beta.27': {} '@rollup/rollup-android-arm-eabi@4.52.2': optional: true @@ -1984,46 +4470,98 @@ snapshots: '@rollup/rollup-linux-arm64-gnu@4.52.2': optional: true - '@rollup/rollup-linux-arm64-musl@4.52.2': + '@rollup/rollup-linux-arm64-musl@4.52.2': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.52.2': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.52.2': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.52.2': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.52.2': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.52.2': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.52.2': + optional: true + + '@rollup/rollup-linux-x64-musl@4.52.2': + optional: true + + '@rollup/rollup-openharmony-arm64@4.52.2': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.52.2': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.52.2': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.52.2': optional: true - '@rollup/rollup-linux-loong64-gnu@4.52.2': + '@rollup/rollup-win32-x64-msvc@4.52.2': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.52.2': - optional: true + '@standard-schema/spec@1.0.0': {} - '@rollup/rollup-linux-riscv64-gnu@4.52.2': + '@swc/core-darwin-arm64@1.15.3': optional: true - '@rollup/rollup-linux-riscv64-musl@4.52.2': + '@swc/core-darwin-x64@1.15.3': optional: true - '@rollup/rollup-linux-s390x-gnu@4.52.2': + '@swc/core-linux-arm-gnueabihf@1.15.3': optional: true - '@rollup/rollup-linux-x64-gnu@4.52.2': + '@swc/core-linux-arm64-gnu@1.15.3': optional: true - '@rollup/rollup-linux-x64-musl@4.52.2': + '@swc/core-linux-arm64-musl@1.15.3': optional: true - '@rollup/rollup-openharmony-arm64@4.52.2': + '@swc/core-linux-x64-gnu@1.15.3': optional: true - '@rollup/rollup-win32-arm64-msvc@4.52.2': + '@swc/core-linux-x64-musl@1.15.3': optional: true - '@rollup/rollup-win32-ia32-msvc@4.52.2': + '@swc/core-win32-arm64-msvc@1.15.3': optional: true - '@rollup/rollup-win32-x64-gnu@4.52.2': + '@swc/core-win32-ia32-msvc@1.15.3': optional: true - '@rollup/rollup-win32-x64-msvc@4.52.2': + '@swc/core-win32-x64-msvc@1.15.3': optional: true - '@standard-schema/spec@1.0.0': {} + '@swc/core@1.15.3': + dependencies: + '@swc/counter': 0.1.3 + '@swc/types': 0.1.25 + optionalDependencies: + '@swc/core-darwin-arm64': 1.15.3 + '@swc/core-darwin-x64': 1.15.3 + '@swc/core-linux-arm-gnueabihf': 1.15.3 + '@swc/core-linux-arm64-gnu': 1.15.3 + '@swc/core-linux-arm64-musl': 1.15.3 + '@swc/core-linux-x64-gnu': 1.15.3 + '@swc/core-linux-x64-musl': 1.15.3 + '@swc/core-win32-arm64-msvc': 1.15.3 + '@swc/core-win32-ia32-msvc': 1.15.3 + '@swc/core-win32-x64-msvc': 1.15.3 + + '@swc/counter@0.1.3': {} + + '@swc/types@0.1.25': + dependencies: + '@swc/counter': 0.1.3 '@testing-library/dom@10.4.1': dependencies: @@ -2059,39 +4597,114 @@ snapshots: dependencies: '@testing-library/dom': 10.4.1 + '@tweenjs/tween.js@23.1.3': {} + '@types/aria-query@5.0.4': {} '@types/chai@5.2.2': dependencies: '@types/deep-eql': 4.0.2 - '@types/cookie@0.6.0': - optional: true + '@types/cookie@0.6.0': {} + + '@types/d3-array@3.0.3': {} + + '@types/d3-color@3.1.0': {} + + '@types/d3-delaunay@6.0.1': {} + + '@types/d3-format@3.0.1': {} + + '@types/d3-geo@3.1.0': + dependencies: + '@types/geojson': 7946.0.16 + + '@types/d3-interpolate@3.0.1': + dependencies: + '@types/d3-color': 3.1.0 + + '@types/d3-path@1.0.11': {} + + '@types/d3-scale@4.0.2': + dependencies: + '@types/d3-time': 3.0.0 + + '@types/d3-shape@1.3.12': + dependencies: + '@types/d3-path': 1.0.11 + + '@types/d3-time-format@2.1.0': {} + + '@types/d3-time@3.0.0': {} '@types/deep-eql@4.0.2': {} + '@types/draco3d@1.4.10': {} + '@types/estree@1.0.8': {} + '@types/geojson@7946.0.16': {} + '@types/json-schema@7.0.15': {} + '@types/lodash@4.17.21': {} + + '@types/ndarray@1.0.14': {} + '@types/node@24.5.2': dependencies: undici-types: 7.12.0 optional: true + '@types/offscreencanvas@2019.7.3': {} + + '@types/parse-json@4.0.2': {} + '@types/prop-types@15.7.15': {} '@types/react-dom@18.3.7(@types/react@18.3.24)': dependencies: '@types/react': 18.3.24 + '@types/react-reconciler@0.26.7': + dependencies: + '@types/react': 18.3.24 + + '@types/react-reconciler@0.28.9(@types/react@18.3.24)': + dependencies: + '@types/react': 18.3.24 + + '@types/react-relay@18.2.1': + dependencies: + '@types/react': 18.3.24 + '@types/relay-runtime': 19.0.3 + + '@types/react-transition-group@4.4.12(@types/react@18.3.24)': + dependencies: + '@types/react': 18.3.24 + '@types/react@18.3.24': dependencies: '@types/prop-types': 15.7.15 csstype: 3.1.3 - '@types/statuses@2.0.6': - optional: true + '@types/relay-runtime@19.0.3': {} + + '@types/stats.js@0.17.4': {} + + '@types/statuses@2.0.6': {} + + '@types/three@0.181.0': + dependencies: + '@dimforge/rapier3d-compat': 0.12.0 + '@tweenjs/tween.js': 23.1.3 + '@types/stats.js': 0.17.4 + '@types/webxr': 0.5.24 + '@webgpu/types': 0.1.66 + fflate: 0.8.2 + meshoptimizer: 0.22.0 + + '@types/webxr@0.5.24': {} '@typescript-eslint/eslint-plugin@8.44.1(@typescript-eslint/parser@8.44.1(eslint@9.36.0)(typescript@5.9.2))(eslint@9.36.0)(typescript@5.9.2)': dependencies: @@ -2186,6 +4799,157 @@ snapshots: '@typescript-eslint/types': 8.44.1 eslint-visitor-keys: 4.2.1 + '@use-gesture/core@10.3.1': {} + + '@use-gesture/react@10.3.1(react@18.3.1)': + dependencies: + '@use-gesture/core': 10.3.1 + react: 18.3.1 + + '@visx/axis@3.10.1(react@18.3.1)': + dependencies: + '@types/react': 18.3.24 + '@visx/group': 3.3.0(react@18.3.1) + '@visx/point': 3.3.0 + '@visx/scale': 3.5.0 + '@visx/shape': 3.5.0(react@18.3.1) + '@visx/text': 3.3.0(react@18.3.1) + classnames: 2.5.1 + prop-types: 15.8.1 + react: 18.3.1 + + '@visx/bounds@3.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@visx/curve@3.3.0': + dependencies: + '@types/d3-shape': 1.3.12 + d3-shape: 1.3.7 + + '@visx/drag@3.12.0(react@18.3.1)': + dependencies: + '@types/react': 18.3.24 + '@visx/event': 3.12.0 + '@visx/point': 3.12.0 + prop-types: 15.8.1 + react: 18.3.1 + + '@visx/drag@3.3.0(react@18.3.1)': + dependencies: + '@types/react': 18.3.24 + '@visx/event': 3.3.0 + '@visx/point': 3.3.0 + prop-types: 15.8.1 + react: 18.3.1 + + '@visx/event@3.12.0': + dependencies: + '@types/react': 18.3.24 + '@visx/point': 3.12.0 + + '@visx/event@3.3.0': + dependencies: + '@types/react': 18.3.24 + '@visx/point': 3.3.0 + + '@visx/grid@3.5.0(react@18.3.1)': + dependencies: + '@types/react': 18.3.24 + '@visx/curve': 3.3.0 + '@visx/group': 3.3.0(react@18.3.1) + '@visx/point': 3.3.0 + '@visx/scale': 3.5.0 + '@visx/shape': 3.5.0(react@18.3.1) + classnames: 2.5.1 + prop-types: 15.8.1 + react: 18.3.1 + + '@visx/group@3.3.0(react@18.3.1)': + dependencies: + '@types/react': 18.3.24 + classnames: 2.5.1 + prop-types: 15.8.1 + react: 18.3.1 + + '@visx/point@3.12.0': {} + + '@visx/point@3.3.0': {} + + '@visx/scale@3.5.0': + dependencies: + '@visx/vendor': 3.5.0 + + '@visx/shape@3.5.0(react@18.3.1)': + dependencies: + '@types/d3-path': 1.0.11 + '@types/d3-shape': 1.3.12 + '@types/lodash': 4.17.21 + '@types/react': 18.3.24 + '@visx/curve': 3.3.0 + '@visx/group': 3.3.0(react@18.3.1) + '@visx/scale': 3.5.0 + classnames: 2.5.1 + d3-path: 1.0.9 + d3-shape: 1.3.7 + lodash: 4.17.21 + prop-types: 15.8.1 + react: 18.3.1 + + '@visx/text@3.3.0(react@18.3.1)': + dependencies: + '@types/lodash': 4.17.21 + '@types/react': 18.3.24 + classnames: 2.5.1 + lodash: 4.17.21 + prop-types: 15.8.1 + react: 18.3.1 + reduce-css-calc: 1.3.0 + + '@visx/tooltip@3.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@types/react': 18.3.24 + '@visx/bounds': 3.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-use-measure: 2.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + + '@visx/vendor@3.5.0': + dependencies: + '@types/d3-array': 3.0.3 + '@types/d3-color': 3.1.0 + '@types/d3-delaunay': 6.0.1 + '@types/d3-format': 3.0.1 + '@types/d3-geo': 3.1.0 + '@types/d3-interpolate': 3.0.1 + '@types/d3-scale': 4.0.2 + '@types/d3-time': 3.0.0 + '@types/d3-time-format': 2.1.0 + d3-array: 3.2.1 + d3-color: 3.1.0 + d3-delaunay: 6.0.2 + d3-format: 3.1.0 + d3-geo: 3.1.0 + d3-interpolate: 3.0.1 + d3-scale: 4.0.2 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + internmap: 2.0.3 + + '@vitejs/plugin-react-swc@3.11.0(vite@7.1.7(@types/node@24.5.2))': + dependencies: + '@rolldown/pluginutils': 1.0.0-beta.27 + '@swc/core': 1.15.3 + vite: 7.1.7(@types/node@24.5.2) + transitivePeerDependencies: + - '@swc/helpers' + '@vitest/expect@3.2.4': dependencies: '@types/chai': 5.2.2 @@ -2207,7 +4971,16 @@ snapshots: dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 - magic-string: 0.30.19 + magic-string: 0.30.21 + optionalDependencies: + msw: 2.11.3(@types/node@24.5.2)(typescript@5.9.2) + vite: 7.1.7(@types/node@24.5.2) + + '@vitest/mocker@4.0.13(msw@2.11.3(@types/node@24.5.2)(typescript@5.9.2))(vite@7.1.7(@types/node@24.5.2))': + dependencies: + '@vitest/spy': 4.0.13 + estree-walker: 3.0.3 + magic-string: 0.30.21 optionalDependencies: msw: 2.11.3(@types/node@24.5.2)(typescript@5.9.2) vite: 7.1.7(@types/node@24.5.2) @@ -2243,7 +5016,13 @@ snapshots: '@vitest/snapshot@3.2.4': dependencies: '@vitest/pretty-format': 3.2.4 - magic-string: 0.30.19 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/snapshot@4.0.13': + dependencies: + '@vitest/pretty-format': 4.0.13 + magic-string: 0.30.21 pathe: 2.0.3 '@vitest/snapshot@4.0.13': @@ -2269,14 +5048,24 @@ snapshots: '@vitest/pretty-format': 4.0.13 tinyrainbow: 3.0.3 + '@webgpu/types@0.1.66': {} + acorn-jsx@5.3.2(acorn@8.15.0): dependencies: acorn: 8.15.0 + acorn@7.4.1: {} + acorn@8.15.0: {} + afterframe@1.0.2: {} + agent-base@7.1.4: {} + ajv-formats@2.1.1(ajv@8.17.1): + optionalDependencies: + ajv: 8.17.1 + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -2284,6 +5073,22 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + align-text@0.1.4: + dependencies: + kind-of: 3.2.2 + longest: 1.0.1 + repeat-string: 1.6.1 + + amdefine@1.0.1: + optional: true + ansi-regex@5.0.1: {} ansi-styles@4.3.0: @@ -2292,6 +5097,10 @@ snapshots: ansi-styles@5.2.0: {} + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + argparse@2.0.1: {} aria-query@5.3.0: @@ -2300,10 +5109,42 @@ snapshots: aria-query@5.3.2: {} + asap@2.0.6: {} + assertion-error@2.0.1: {} + babel-plugin-macros@2.8.0: + dependencies: + '@babel/runtime': 7.28.4 + cosmiconfig: 6.0.0 + resolve: 1.22.11 + + babel-plugin-macros@3.1.0: + dependencies: + '@babel/runtime': 7.28.4 + cosmiconfig: 7.1.0 + resolve: 1.22.11 + + babel-plugin-relay@20.1.1: + dependencies: + babel-plugin-macros: 2.8.0 + cosmiconfig: 5.2.1 + graphql: 15.3.0 + + balanced-match@0.4.2: {} + balanced-match@1.0.2: {} + base64-js@1.5.1: {} + + baseline-browser-mapping@2.8.31: {} + + bidi-js@1.0.3: + dependencies: + require-from-string: 2.0.2 + + bit-twiddle@1.0.2: {} + brace-expansion@1.1.12: dependencies: balanced-match: 1.0.2 @@ -2317,15 +5158,52 @@ snapshots: dependencies: fill-range: 7.1.1 + browserslist@4.28.0: + dependencies: + baseline-browser-mapping: 2.8.31 + caniuse-lite: 1.0.30001757 + electron-to-chromium: 1.5.260 + node-releases: 2.0.27 + update-browserslist-db: 1.1.4(browserslist@4.28.0) + + buffer-from@1.1.2: {} + + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + bufferutil@4.0.9: dependencies: node-gyp-build: 4.8.4 - optional: true cac@6.7.14: {} + caller-callsite@2.0.0: + dependencies: + callsites: 2.0.0 + + caller-path@2.0.0: + dependencies: + caller-callsite: 2.0.0 + + callsites@2.0.0: {} + callsites@3.1.0: {} + camelcase@1.2.1: {} + + camera-controls@2.10.1(three@0.167.1): + dependencies: + three: 0.167.1 + + caniuse-lite@1.0.30001757: {} + + center-align@0.1.3: + dependencies: + align-text: 0.1.4 + lazy-cache: 1.0.4 + chai@5.3.3: dependencies: assertion-error: 2.0.1 @@ -2343,15 +5221,25 @@ snapshots: check-error@2.1.1: {} - cli-width@4.1.0: - optional: true + classnames@2.5.1: {} + + cli-width@4.1.0: {} + + cliui@2.1.0: + dependencies: + center-align: 0.1.3 + right-align: 0.1.3 + wordwrap: 0.0.2 cliui@8.0.1: dependencies: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - optional: true + + clsx@1.2.1: {} + + clsx@2.1.1: {} color-convert@2.0.1: dependencies: @@ -2361,41 +5249,176 @@ snapshots: concat-map@0.0.1: {} - cookie@0.7.2: - optional: true + concat-stream@1.6.2: + dependencies: + buffer-from: 1.1.2 + inherits: 2.0.4 + readable-stream: 2.3.8 + typedarray: 0.0.6 + + convert-source-map@1.9.0: {} + + convert-source-map@2.0.0: {} + + cookie@0.7.2: {} + + cookie@1.0.2: {} + + core-util-is@1.0.3: {} + + cosmiconfig@5.2.1: + dependencies: + import-fresh: 2.0.0 + is-directory: 0.3.1 + js-yaml: 3.14.2 + parse-json: 4.0.0 + + cosmiconfig@6.0.0: + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.1 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + + cosmiconfig@7.1.0: + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.1 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + + cross-env@7.0.3: + dependencies: + cross-spawn: 7.0.6 + + cross-fetch@3.2.0: + dependencies: + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css.escape@1.5.1: {} + + cssstyle@4.6.0: + dependencies: + '@asamuzakjp/css-color': 3.2.0 + rrweb-cssom: 0.8.0 + + csstype@3.1.3: {} + + cwise-compiler@1.1.3: + dependencies: + uniq: 1.0.1 + + cwise-parser@1.0.3: + dependencies: + esprima: 1.2.5 + uniq: 1.0.1 + + cwise@1.0.10: + dependencies: + cwise-compiler: 1.1.3 + cwise-parser: 1.0.3 + static-module: 1.5.0 + uglify-js: 2.8.29 + + d3-array@3.2.1: + dependencies: + internmap: 2.0.3 + + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + + d3-color@3.1.0: {} + + d3-delaunay@6.0.2: + dependencies: + delaunator: 5.0.1 + + d3-format@3.1.0: {} + + d3-geo@3.1.0: + dependencies: + d3-array: 3.2.4 + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-path@1.0.9: {} + + d3-scale-chromatic@3.1.0: + dependencies: + d3-color: 3.1.0 + d3-interpolate: 3.0.1 + + d3-scale@4.0.2: + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.0 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 - cross-spawn@7.0.6: + d3-shape@1.3.7: dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 + d3-path: 1.0.9 - css.escape@1.5.1: {} + d3-time-format@4.1.0: + dependencies: + d3-time: 3.1.0 - cssstyle@4.6.0: + d3-time@3.1.0: dependencies: - '@asamuzakjp/css-color': 3.2.0 - rrweb-cssom: 0.8.0 + d3-array: 3.2.4 - csstype@3.1.3: {} + d@1.0.2: + dependencies: + es5-ext: 0.10.64 + type: 2.7.3 data-urls@5.0.0: dependencies: whatwg-mimetype: 4.0.0 whatwg-url: 14.2.0 + dayjs@1.10.7: {} + + debug@2.6.9: + dependencies: + ms: 2.0.0 + debug@4.4.3: dependencies: ms: 2.1.3 + decamelize@1.2.0: {} + decimal.js@10.6.0: {} deep-eql@5.0.2: {} deep-is@0.1.4: {} + delaunator@5.0.1: + dependencies: + robust-predicates: 3.0.2 + dequal@2.0.3: {} + detect-gpu@5.0.70: + dependencies: + webgl-constants: 1.1.1 + detect-indent@7.0.2: {} detect-newline@4.0.1: {} @@ -2404,13 +5427,49 @@ snapshots: dom-accessibility-api@0.6.3: {} - emoji-regex@8.0.0: - optional: true + dom-helpers@5.2.1: + dependencies: + '@babel/runtime': 7.28.4 + csstype: 3.1.3 + + draco3d@1.5.7: {} + + dup@1.0.0: {} + + duplexer2@0.0.2: + dependencies: + readable-stream: 1.1.14 + + electron-to-chromium@1.5.260: {} + + emoji-regex@8.0.0: {} entities@6.0.1: {} + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + es-module-lexer@1.7.0: {} + es5-ext@0.10.64: + dependencies: + es6-iterator: 2.0.3 + es6-symbol: 3.1.4 + esniff: 2.0.1 + next-tick: 1.1.0 + + es6-iterator@2.0.3: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + es6-symbol: 3.1.4 + + es6-symbol@3.1.4: + dependencies: + d: 1.0.2 + ext: 1.7.0 + esbuild@0.25.10: optionalDependencies: '@esbuild/aix-ppc64': 0.25.10 @@ -2440,11 +5499,33 @@ snapshots: '@esbuild/win32-ia32': 0.25.10 '@esbuild/win32-x64': 0.25.10 - escalade@3.2.0: - optional: true + escalade@3.2.0: {} escape-string-regexp@4.0.0: {} + escodegen@0.0.28: + dependencies: + esprima: 1.0.4 + estraverse: 1.3.2 + optionalDependencies: + source-map: 0.7.6 + + escodegen@1.3.3: + dependencies: + esprima: 1.1.1 + estraverse: 1.5.1 + esutils: 1.0.0 + optionalDependencies: + source-map: 0.1.43 + + eslint-plugin-react-hooks@5.2.0(eslint@9.36.0): + dependencies: + eslint: 9.36.0 + + eslint-plugin-react-refresh@0.4.24(eslint@9.36.0): + dependencies: + eslint: 9.36.0 + eslint-scope@8.4.0: dependencies: esrecurse: 4.3.0 @@ -2494,12 +5575,27 @@ snapshots: transitivePeerDependencies: - supports-color + esniff@2.0.1: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + event-emitter: 0.3.5 + type: 2.7.3 + espree@10.4.0: dependencies: acorn: 8.15.0 acorn-jsx: 5.3.2(acorn@8.15.0) eslint-visitor-keys: 4.2.1 + esprima@1.0.4: {} + + esprima@1.1.1: {} + + esprima@1.2.5: {} + + esprima@4.0.1: {} + esquery@1.6.0: dependencies: estraverse: 5.3.0 @@ -2508,16 +5604,36 @@ snapshots: dependencies: estraverse: 5.3.0 + estraverse@1.3.2: {} + + estraverse@1.5.1: {} + estraverse@5.3.0: {} estree-walker@3.0.3: dependencies: '@types/estree': 1.0.8 + esutils@1.0.0: {} + esutils@2.0.3: {} + event-emitter@0.3.5: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + expect-type@1.2.2: {} + ext@1.7.0: + dependencies: + type: 2.7.3 + + falafel@2.2.5: + dependencies: + acorn: 7.4.1 + isarray: 2.0.5 + fast-deep-equal@3.1.3: {} fast-glob@3.3.3: @@ -2532,14 +5648,34 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-uri@3.1.0: {} + fastq@1.19.1: dependencies: reusify: 1.1.0 + fbjs-css-vars@1.0.2: {} + + fbjs@3.0.5: + dependencies: + cross-fetch: 3.2.0 + fbjs-css-vars: 1.0.2 + loose-envify: 1.4.0 + object-assign: 4.1.1 + promise: 7.3.1 + setimmediate: 1.0.5 + ua-parser-js: 1.0.41 + transitivePeerDependencies: + - encoding + fdir@6.5.0(picomatch@4.0.3): optionalDependencies: picomatch: 4.0.3 + fflate@0.6.10: {} + + fflate@0.8.2: {} + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -2548,6 +5684,8 @@ snapshots: dependencies: to-regex-range: 5.0.1 + find-root@1.1.0: {} + find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -2563,8 +5701,13 @@ snapshots: fsevents@2.3.3: optional: true - get-caller-file@2.0.5: - optional: true + function-bind@1.1.2: {} + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-node-dimensions@1.2.1: {} git-hooks-list@4.1.1: {} @@ -2578,15 +5721,31 @@ snapshots: globals@14.0.0: {} + globals@16.5.0: {} + + glsl-noise@0.0.0: {} + graphemer@1.4.0: {} - graphql@16.11.0: - optional: true + graphql@15.3.0: {} + + graphql@16.11.0: {} has-flag@4.0.0: {} - headers-polyfill@4.0.3: - optional: true + has@1.0.4: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + headers-polyfill@4.0.3: {} + + hls.js@1.6.15: {} + + hoist-non-react-statics@3.3.2: + dependencies: + react-is: 16.13.1 html-encoding-sniffer@4.0.0: dependencies: @@ -2610,10 +5769,19 @@ snapshots: dependencies: safer-buffer: 2.1.2 + ieee754@1.2.1: {} + ignore@5.3.2: {} ignore@7.0.5: {} + immediate@3.0.6: {} + + import-fresh@2.0.0: + dependencies: + caller-path: 2.0.0 + resolve-from: 3.0.0 + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 @@ -2623,17 +5791,35 @@ snapshots: indent-string@4.0.0: {} + inherits@2.0.4: {} + + internmap@2.0.3: {} + + invariant@2.2.4: + dependencies: + loose-envify: 1.4.0 + + iota-array@1.0.0: {} + + is-arrayish@0.2.1: {} + + is-buffer@1.1.6: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-directory@0.3.1: {} + is-extglob@2.1.1: {} - is-fullwidth-code-point@3.0.0: - optional: true + is-fullwidth-code-point@3.0.0: {} is-glob@4.0.3: dependencies: is-extglob: 2.1.1 - is-node-process@1.2.0: - optional: true + is-node-process@1.2.0: {} is-number@7.0.0: {} @@ -2641,12 +5827,36 @@ snapshots: is-potential-custom-element-name@1.0.1: {} + is-promise@2.2.2: {} + + is-typedarray@1.0.0: {} + + isarray@0.0.1: {} + + isarray@1.0.0: {} + + isarray@2.0.5: {} + isexe@2.0.0: {} + isndarray@1.0.0: {} + + its-fine@1.2.5(@types/react@18.3.24)(react@18.3.1): + dependencies: + '@types/react-reconciler': 0.28.9(@types/react@18.3.24) + react: 18.3.1 + transitivePeerDependencies: + - '@types/react' + js-tokens@4.0.0: {} js-tokens@9.0.1: {} + js-yaml@3.14.2: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + js-yaml@4.1.0: dependencies: argparse: 2.0.1 @@ -2678,27 +5888,53 @@ snapshots: - supports-color - utf-8-validate + jsesc@3.1.0: {} + json-buffer@3.0.1: {} + json-parse-better-errors@1.0.2: {} + + json-parse-even-better-errors@2.3.1: {} + json-schema-traverse@0.4.1: {} + json-schema-traverse@1.0.0: {} + json-stable-stringify-without-jsonify@1.0.1: {} + json5@2.2.3: {} + keyv@4.5.4: dependencies: json-buffer: 3.0.1 + kind-of@3.2.2: + dependencies: + is-buffer: 1.1.6 + + lazy-cache@1.0.4: {} + levn@0.4.1: dependencies: prelude-ls: 1.2.1 type-check: 0.4.0 + lie@3.3.0: + dependencies: + immediate: 3.0.6 + + lines-and-columns@1.2.4: {} + locate-path@6.0.0: dependencies: p-locate: 5.0.0 lodash.merge@4.6.2: {} + lodash@4.17.21: {} + + longest@1.0.1: {} + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 @@ -2707,8 +5943,17 @@ snapshots: lru-cache@10.4.3: {} + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + lz-string@1.5.0: {} + maath@0.10.8(@types/three@0.181.0)(three@0.167.1): + dependencies: + '@types/three': 0.181.0 + three: 0.167.1 + magic-string@0.30.19: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -2717,8 +5962,22 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + math-expression-evaluator@1.4.0: {} + + memoize-one@5.2.1: {} + + memoize-one@6.0.0: {} + merge2@1.4.1: {} + meshline@3.3.1(three@0.167.1): + dependencies: + three: 0.167.1 + + meshoptimizer@0.22.0: {} + + messagepack@1.1.12: {} + micromatch@4.0.8: dependencies: braces: 3.0.3 @@ -2734,6 +5993,10 @@ snapshots: dependencies: brace-expansion: 2.0.2 + minimist@0.0.8: {} + + ms@2.0.0: {} + ms@2.1.3: {} msw@2.11.3(@types/node@24.5.2)(typescript@5.9.2): @@ -2761,20 +6024,65 @@ snapshots: typescript: 5.9.2 transitivePeerDependencies: - '@types/node' - optional: true - mute-stream@2.0.0: - optional: true + mute-stream@2.0.0: {} nanoid@3.3.11: {} natural-compare@1.4.0: {} - node-gyp-build@4.8.4: - optional: true + ndarray-concat-rows@1.0.1: + dependencies: + ndarray-ops: 1.2.2 + ndarray-scratch: 1.2.0 + util-extend: 1.0.3 + + ndarray-fill@1.0.2: + dependencies: + cwise: 1.0.10 + + ndarray-linspace@2.0.3: + dependencies: + isndarray: 1.0.0 + ndarray-fill: 1.0.2 + validate.io-boolean: 1.0.4 + validate.io-nonnegative-integer: 1.0.0 + + ndarray-ops@1.2.2: + dependencies: + cwise-compiler: 1.1.3 + + ndarray-scratch@1.2.0: + dependencies: + ndarray: 1.0.19 + ndarray-ops: 1.2.2 + typedarray-pool: 1.2.0 + + ndarray@1.0.19: + dependencies: + iota-array: 1.0.0 + is-buffer: 1.1.6 + + next-tick@1.1.0: {} + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + node-gyp-build@4.8.4: {} + + node-releases@2.0.27: {} + + nullthrows@1.1.1: {} nwsapi@2.2.22: {} + object-assign@4.1.1: {} + + object-inspect@0.4.0: {} + + object-keys@0.4.0: {} + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -2784,8 +6092,7 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 - outvariant@1.4.3: - optional: true + outvariant@1.4.3: {} p-limit@3.1.0: dependencies: @@ -2795,78 +6102,312 @@ snapshots: dependencies: p-limit: 3.1.0 - parent-module@1.0.1: + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-json@4.0.0: + dependencies: + error-ex: 1.3.4 + json-parse-better-errors: 1.0.2 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.27.1 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + parse5@7.3.0: + dependencies: + entities: 6.0.1 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-to-regexp@6.3.0: {} + + path-type@4.0.0: {} + + pathe@2.0.3: {} + + pathval@2.0.1: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.3: {} + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + potpack@1.0.2: {} + + prelude-ls@1.2.1: {} + + prettier@3.6.2: {} + + pretty-format@27.5.1: + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + + process-nextick-args@2.0.1: {} + + promise-worker-transferable@1.0.4: + dependencies: + is-promise: 2.2.2 + lie: 3.3.0 + + promise@7.3.1: + dependencies: + asap: 2.0.6 + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + punycode@2.3.1: {} + + queue-microtask@1.2.3: {} + + quote-stream@0.0.0: + dependencies: + minimist: 0.0.8 + through2: 0.4.2 + + react-colorful@5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + react-composer@5.0.3(react@18.3.1): + dependencies: + prop-types: 15.8.1 + react: 18.3.1 + + react-dom@18.3.1(react@18.3.1): + dependencies: + loose-envify: 1.4.0 + react: 18.3.1 + scheduler: 0.23.2 + + react-draggable@4.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + clsx: 2.1.1 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + react-error-boundary@6.0.0(react@18.3.1): dependencies: - callsites: 3.1.0 + '@babel/runtime': 7.28.4 + react: 18.3.1 - parse5@7.3.0: + react-icons@5.2.1(react@18.3.1): dependencies: - entities: 6.0.1 + react: 18.3.1 - path-exists@4.0.0: {} + react-icons@5.5.0(react@18.3.1): + dependencies: + react: 18.3.1 - path-key@3.1.1: {} + react-is@16.13.1: {} - path-to-regexp@6.3.0: - optional: true + react-is@17.0.2: {} - pathe@2.0.3: {} + react-is@18.3.1: {} - pathval@2.0.1: {} + react-is@19.2.0: {} - picocolors@1.1.1: {} + react-keyed-flatten-children@3.0.0(react@18.3.1): + dependencies: + react: 18.3.1 + react-is: 18.3.1 - picomatch@2.3.1: {} + react-measure@2.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.4 + get-node-dimensions: 1.2.1 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + resize-observer-polyfill: 1.5.1 - picomatch@4.0.3: {} + react-reconciler@0.27.0(react@18.3.1): + dependencies: + loose-envify: 1.4.0 + react: 18.3.1 + scheduler: 0.21.0 - postcss@8.5.6: + react-relay@20.1.1(react@18.3.1): dependencies: - nanoid: 3.3.11 - picocolors: 1.1.1 - source-map-js: 1.2.1 + '@babel/runtime': 7.28.4 + fbjs: 3.0.5 + invariant: 2.2.4 + nullthrows: 1.1.1 + react: 18.3.1 + relay-runtime: 20.1.1 + transitivePeerDependencies: + - encoding - prelude-ls@1.2.1: {} + react-router-dom@7.9.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-router: 7.9.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - prettier@3.6.2: {} + react-router@7.9.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + cookie: 1.0.2 + react: 18.3.1 + set-cookie-parser: 2.7.2 + optionalDependencies: + react-dom: 18.3.1(react@18.3.1) - pretty-format@27.5.1: + react-select@5.10.2(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - ansi-regex: 5.0.1 - ansi-styles: 5.2.0 - react-is: 17.0.2 + '@babel/runtime': 7.28.4 + '@emotion/cache': 11.14.0 + '@emotion/react': 11.14.0(@types/react@18.3.24)(react@18.3.1) + '@floating-ui/dom': 1.7.4 + '@types/react-transition-group': 4.4.12(@types/react@18.3.24) + memoize-one: 6.0.0 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + use-isomorphic-layout-effect: 1.2.1(@types/react@18.3.24)(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + - supports-color - punycode@2.3.1: {} + react-slider@2.0.4(react@18.3.1): + dependencies: + prop-types: 15.8.1 + react: 18.3.1 - queue-microtask@1.2.3: {} + react-toastify@9.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + clsx: 1.2.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) - react-dom@18.3.1(react@18.3.1): + react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: + '@babel/runtime': 7.28.4 + dom-helpers: 5.2.1 loose-envify: 1.4.0 + prop-types: 15.8.1 react: 18.3.1 - scheduler: 0.23.2 + react-dom: 18.3.1(react@18.3.1) - react-is@17.0.2: {} + react-use-measure@2.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + optionalDependencies: + react-dom: 18.3.1(react@18.3.1) + + react-use-websocket@4.13.0: {} + + react-window@1.8.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.4 + memoize-one: 5.2.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) react@18.3.1: dependencies: loose-envify: 1.4.0 + readable-stream@1.0.34: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 0.0.1 + string_decoder: 0.10.31 + + readable-stream@1.1.14: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 0.0.1 + string_decoder: 0.10.31 + + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + redent@3.0.0: dependencies: indent-string: 4.0.0 strip-indent: 3.0.0 - require-directory@2.1.1: - optional: true + reduce-css-calc@1.3.0: + dependencies: + balanced-match: 0.4.2 + math-expression-evaluator: 1.4.0 + reduce-function-call: 1.0.3 + + reduce-function-call@1.0.3: + dependencies: + balanced-match: 1.0.2 + + relay-compiler@20.1.1: {} + + relay-runtime@20.1.1: + dependencies: + '@babel/runtime': 7.28.4 + fbjs: 3.0.5 + invariant: 2.2.4 + transitivePeerDependencies: + - encoding + + repeat-string@1.6.1: {} + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + resize-observer-polyfill@1.5.1: {} + + resolve-from@3.0.0: {} resolve-from@4.0.0: {} - rettime@0.7.0: - optional: true + resolve@1.22.11: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + rettime@0.7.0: {} reusify@1.1.0: {} + right-align@0.1.3: + dependencies: + align-text: 0.1.4 + + robust-predicates@3.0.2: {} + rollup@4.52.2: dependencies: '@types/estree': 1.0.8 @@ -2901,18 +6442,32 @@ snapshots: dependencies: queue-microtask: 1.2.3 + safe-buffer@5.1.2: {} + safer-buffer@2.1.2: {} saxes@6.0.0: dependencies: xmlchars: 2.2.0 + scheduler@0.21.0: + dependencies: + loose-envify: 1.4.0 + scheduler@0.23.2: dependencies: loose-envify: 1.4.0 + semver@6.3.1: {} + semver@7.7.2: {} + set-cookie-parser@2.7.2: {} + + setimmediate@1.0.5: {} + + shallow-copy@0.0.1: {} + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -2921,8 +6476,7 @@ snapshots: siginfo@2.0.0: {} - signal-exit@4.1.0: - optional: true + signal-exit@4.1.0: {} sort-object-keys@1.1.3: {} @@ -2938,29 +6492,68 @@ snapshots: source-map-js@1.2.1: {} - stackback@0.0.2: {} + source-map@0.1.43: + dependencies: + amdefine: 1.0.1 + optional: true - statuses@2.0.2: + source-map@0.5.7: {} + + source-map@0.7.6: optional: true + sprintf-js@1.0.3: {} + + stackback@0.0.2: {} + + static-eval@0.2.4: + dependencies: + escodegen: 0.0.28 + + static-module@1.5.0: + dependencies: + concat-stream: 1.6.2 + duplexer2: 0.0.2 + escodegen: 1.3.3 + falafel: 2.2.5 + has: 1.0.4 + object-inspect: 0.4.0 + quote-stream: 0.0.0 + readable-stream: 1.0.34 + shallow-copy: 0.0.1 + static-eval: 0.2.4 + through2: 0.4.2 + + stats-gl@2.4.2(@types/three@0.181.0)(three@0.167.1): + dependencies: + '@types/three': 0.181.0 + three: 0.167.1 + + stats.js@0.17.0: {} + + statuses@2.0.2: {} + std-env@3.10.0: {} std-env@3.9.0: {} - strict-event-emitter@0.5.1: - optional: true + strict-event-emitter@0.5.1: {} string-width@4.2.3: dependencies: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - optional: true + + string_decoder@0.10.31: {} + + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 - optional: true strip-indent@3.0.0: dependencies: @@ -2972,12 +6565,43 @@ snapshots: dependencies: js-tokens: 9.0.1 + stylis@4.2.0: {} + supports-color@7.2.0: dependencies: has-flag: 4.0.0 + supports-preserve-symlinks-flag@1.0.0: {} + + suspend-react@0.1.3(react@18.3.1): + dependencies: + react: 18.3.1 + symbol-tree@3.2.4: {} + tabbable@6.3.0: {} + + three-mesh-bvh@0.7.8(three@0.167.1): + dependencies: + three: 0.167.1 + + three-stdlib@2.36.1(three@0.167.1): + dependencies: + '@types/draco3d': 1.4.10 + '@types/offscreencanvas': 2019.7.3 + '@types/webxr': 0.5.24 + draco3d: 1.5.7 + fflate: 0.6.10 + potpack: 1.0.2 + three: 0.167.1 + + three@0.167.1: {} + + through2@0.4.2: + dependencies: + readable-stream: 1.0.34 + xtend: 2.1.2 + tinybench@2.9.0: {} tinyexec@0.3.2: {} @@ -2997,8 +6621,7 @@ snapshots: tldts-core@6.1.86: {} - tldts-core@7.0.16: - optional: true + tldts-core@7.0.16: {} tldts@6.1.86: dependencies: @@ -3007,7 +6630,6 @@ snapshots: tldts@7.0.16: dependencies: tldts-core: 7.0.16 - optional: true to-regex-range@5.0.1: dependencies: @@ -3020,16 +6642,39 @@ snapshots: tough-cookie@6.0.0: dependencies: tldts: 7.0.16 - optional: true + + tr46@0.0.3: {} tr46@5.1.1: dependencies: punycode: 2.3.1 + troika-three-text@0.52.4(three@0.167.1): + dependencies: + bidi-js: 1.0.3 + three: 0.167.1 + troika-three-utils: 0.52.4(three@0.167.1) + troika-worker-utils: 0.52.0 + webgl-sdf-generator: 1.1.1 + + troika-three-utils@0.52.4(three@0.167.1): + dependencies: + three: 0.167.1 + + troika-worker-utils@0.52.0: {} + ts-api-utils@2.1.0(typescript@5.9.2): dependencies: typescript: 5.9.2 + tunnel-rat@0.1.2(@types/react@18.3.24)(react@18.3.1): + dependencies: + zustand: 4.5.7(@types/react@18.3.24)(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + - immer + - react + turbo-darwin-64@2.5.6: optional: true @@ -3061,8 +6706,20 @@ snapshots: dependencies: prelude-ls: 1.2.1 - type-fest@4.41.0: - optional: true + type-fest@4.41.0: {} + + type@2.7.3: {} + + typedarray-pool@1.2.0: + dependencies: + bit-twiddle: 1.0.2 + dup: 1.0.0 + + typedarray-to-buffer@3.1.5: + dependencies: + is-typedarray: 1.0.0 + + typedarray@0.0.6: {} typescript-eslint@8.44.1(eslint@9.36.0)(typescript@5.9.2): dependencies: @@ -3077,20 +6734,70 @@ snapshots: typescript@5.9.2: {} - undici-types@7.12.0: + ua-parser-js@1.0.41: {} + + uglify-js@2.8.29: + dependencies: + source-map: 0.5.7 + yargs: 3.10.0 + optionalDependencies: + uglify-to-browserify: 1.0.2 + + uglify-to-browserify@1.0.2: optional: true - until-async@3.0.2: + undici-types@7.12.0: optional: true + uniq@1.0.1: {} + + until-async@3.0.2: {} + + update-browserslist-db@1.1.4(browserslist@4.28.0): + dependencies: + browserslist: 4.28.0 + escalade: 3.2.0 + picocolors: 1.1.1 + uri-js@4.4.1: dependencies: punycode: 2.3.1 + use-isomorphic-layout-effect@1.2.1(@types/react@18.3.24)(react@18.3.1): + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.24 + + use-sync-external-store@1.2.0(react@18.3.1): + dependencies: + react: 18.3.1 + + use-sync-external-store@1.6.0(react@18.3.1): + dependencies: + react: 18.3.1 + utf-8-validate@5.0.10: dependencies: node-gyp-build: 4.8.4 - optional: true + + util-deprecate@1.0.2: {} + + util-extend@1.0.3: {} + + utility-types@3.11.0: {} + + validate.io-boolean@1.0.4: {} + + validate.io-integer@1.0.5: + dependencies: + validate.io-number: 1.0.3 + + validate.io-nonnegative-integer@1.0.0: + dependencies: + validate.io-integer: 1.0.5 + + validate.io-number@1.0.3: {} vite-node@3.2.4(@types/node@24.5.2): dependencies: @@ -3113,6 +6820,14 @@ snapshots: - tsx - yaml + vite-plugin-relay@2.1.0(babel-plugin-relay@20.1.1)(vite@7.1.7(@types/node@24.5.2)): + dependencies: + '@babel/core': 7.28.5 + babel-plugin-relay: 20.1.1 + vite: 7.1.7(@types/node@24.5.2) + transitivePeerDependencies: + - supports-color + vite@7.1.7(@types/node@24.5.2): dependencies: esbuild: 0.25.10 @@ -3210,8 +6925,25 @@ snapshots: dependencies: xml-name-validator: 5.0.0 + webgl-constants@1.1.1: {} + + webgl-sdf-generator@1.1.1: {} + + webidl-conversions@3.0.1: {} + webidl-conversions@7.0.0: {} + websocket@1.0.35: + dependencies: + bufferutil: 4.0.9 + debug: 2.6.9 + es5-ext: 0.10.64 + typedarray-to-buffer: 3.1.5 + utf-8-validate: 5.0.10 + yaeti: 0.0.6 + transitivePeerDependencies: + - supports-color + whatwg-encoding@3.1.1: dependencies: iconv-lite: 0.6.3 @@ -3223,6 +6955,11 @@ snapshots: tr46: 5.1.1 webidl-conversions: 7.0.0 + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + which@2.0.2: dependencies: isexe: 2.0.0 @@ -3232,21 +6969,23 @@ snapshots: siginfo: 2.0.0 stackback: 0.0.2 + window-size@0.1.0: {} + word-wrap@1.2.5: {} + wordwrap@0.0.2: {} + wrap-ansi@6.2.0: dependencies: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - optional: true wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - optional: true ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10): optionalDependencies: @@ -3257,11 +6996,19 @@ snapshots: xmlchars@2.2.0: {} - y18n@5.0.8: - optional: true + xtend@2.1.2: + dependencies: + object-keys: 0.4.0 - yargs-parser@21.1.1: - optional: true + y18n@5.0.8: {} + + yaeti@0.0.6: {} + + yallist@3.1.1: {} + + yaml@1.10.2: {} + + yargs-parser@21.1.1: {} yargs@17.7.2: dependencies: @@ -3272,9 +7019,38 @@ snapshots: string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 21.1.1 - optional: true + + yargs@3.10.0: + dependencies: + camelcase: 1.2.1 + cliui: 2.1.0 + decamelize: 1.2.0 + window-size: 0.1.0 yocto-queue@0.1.0: {} - yoctocolors-cjs@2.1.3: - optional: true + yoctocolors-cjs@2.1.3: {} + + zustand@3.7.2(react@18.3.1): + optionalDependencies: + react: 18.3.1 + + zustand@4.5.4(@types/react@18.3.24)(react@18.3.1): + dependencies: + use-sync-external-store: 1.2.0(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.24 + react: 18.3.1 + + zustand@4.5.7(@types/react@18.3.24)(react@18.3.1): + dependencies: + use-sync-external-store: 1.6.0(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.24 + react: 18.3.1 + + zustand@5.0.8(@types/react@18.3.24)(react@18.3.1)(use-sync-external-store@1.6.0(react@18.3.1)): + optionalDependencies: + '@types/react': 18.3.24 + react: 18.3.1 + use-sync-external-store: 1.6.0(react@18.3.1)