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 (
+
+
+
+
+
+
+
+
+ );
+}
+
+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 (
+ {
+ const taskRequest: TaskRequest = {
+ name: name,
+ params: params,
+ instrument_session: instrumentSession,
+ };
+ await createAndStartTask(taskRequest);
+ }}
+ >
+ Run
+
+ );
+};
+
+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
+
+
+ }
+ sx={{ width: 150, height: 50 }}
+ >
+ Spectroscopy
+
+ }
+ sx={{ width: 150, height: 50 }}
+ >
+ Plans
+
+ }
+ sx={{ width: 150, height: 50, gap: "0.5rem" }}
+ >
+ Workflows
+
+
+
+
+
+ >
+ );
+}
+
+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)