diff --git a/.github/workflows/ESLintFormat.yml b/.github/workflows/ESLintFormat.yml
new file mode 100644
index 0000000000..5cae0c1e66
--- /dev/null
+++ b/.github/workflows/ESLintFormat.yml
@@ -0,0 +1,44 @@
+name: Format Validation (ESLint)
+
+on:
+ workflow_dispatch: {}
+ push:
+ branches: [ master, dev ]
+ pull_request:
+ branches: [ master, dev ]
+
+jobs:
+ runFormatValidationScript:
+ name: Run ESLint Format Validation
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout Code
+ uses: actions/checkout@v2
+ - name: JavaScript Setup
+ uses: actions/setup-node@v2
+ with:
+ node-version: 20
+ - name: Install Dependencies
+ run: |
+ cd fission
+ npm install
+ - name: Linter
+ id: linter-validation
+ run: |
+ cd fission
+ npm run lint
+ continue-on-error: true
+ - name: Prettier
+ id: prettier-validation
+ run: |
+ cd fission
+ npm run prettier
+ continue-on-error: true
+ - name: Check Success
+ run: |
+ if [ ${{ steps.linter-validation.outcome }} == "success" ]; then
+ echo "Format Validation Passed"
+ else
+ echo "Format Validation Failed"
+ exit 1
+ fi
diff --git a/engine/Assets/Scripts/CEF.meta b/engine/Assets/Scripts/CEF.meta
new file mode 100644
index 0000000000..f5057abaae
--- /dev/null
+++ b/engine/Assets/Scripts/CEF.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: dacc8de941e53714eae8f63ad55c31a4
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/fission/.eslintrc.cjs b/fission/.eslintrc.cjs
index d30ae5ead3..646f760030 100644
--- a/fission/.eslintrc.cjs
+++ b/fission/.eslintrc.cjs
@@ -6,7 +6,7 @@ module.exports = {
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
],
- ignorePatterns: ['dist', '.eslintrc.cjs'],
+ ignorePatterns: ['dist', '.eslintrc.cjs', 'src/proto/mirabuf.*', 'src/samples/*'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {
diff --git a/fission/bun.lockb b/fission/bun.lockb
new file mode 100755
index 0000000000..8ca9f4a4ab
Binary files /dev/null and b/fission/bun.lockb differ
diff --git a/fission/index.html b/fission/index.html
index e4b78eae12..906d342d14 100644
--- a/fission/index.html
+++ b/fission/index.html
@@ -2,9 +2,10 @@
-
+
+
- Vite + React + TS
+ Fission | Synthesis
diff --git a/fission/package-lock.json b/fission/package-lock.json
index 2e0a8c7c23..b96a67ab3c 100644
--- a/fission/package-lock.json
+++ b/fission/package-lock.json
@@ -8,33 +8,47 @@
"name": "vite-proj",
"version": "0.0.0",
"dependencies": {
- "@barclah/jolt-physics": "^0.16.2",
+ "@barclah/jolt-physics": "^0.19.1",
"@react-three/drei": "^9.96.5",
"@react-three/fiber": "^8.15.15",
- "@types/node": "^20.10.6",
- "@types/react": "^18.2.47",
- "@types/react-dom": "^18.2.18",
- "@types/three": "^0.160.0",
+ "colord": "^2.9.3",
+ "framer-motion": "^10.13.1",
"react": "^18.2.0",
+ "react-colorful": "^5.6.1",
"react-dom": "^18.2.0",
+ "react-icons": "^4.10.1",
"three": "^0.159.0"
},
"devDependencies": {
"@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0",
"@mui/material": "^5.15.6",
- "@types/react": "^18.2.56",
- "@types/react-dom": "^18.2.19",
+ "@types/node": "^20.4.4",
+ "@types/pako": "^2.0.3",
+ "@types/react": "^18.2.47",
+ "@types/react-dom": "^18.2.18",
+ "@types/three": "^0.160.0",
"@typescript-eslint/eslint-plugin": "^7.0.2",
"@typescript-eslint/parser": "^7.0.2",
+ "@vitejs/plugin-react": "^4.0.3",
"@vitejs/plugin-react-swc": "^3.5.0",
+ "autoprefixer": "^10.4.14",
+ "cssnano": "^6.0.1",
"eslint": "^8.56.0",
+ "eslint-config-prettier": "^8.8.0",
+ "eslint-import-resolver-alias": "^1.1.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.5",
+ "pako": "^2.1.0",
+ "postcss": "^8.4.26",
+ "prettier": "^3.0.0",
"protobufjs": "^7.2.6",
"protobufjs-cli": "^1.1.2",
+ "tailwindcss": "^3.3.3",
+ "tsconfig-paths": "^4.2.0",
"typescript": "^5.2.2",
"vite": "^5.1.4",
+ "vite-plugin-singlefile": "^0.13.5",
"vitest": "^1.3.1"
}
},
@@ -47,6 +61,31 @@
"node": ">=0.10.0"
}
},
+ "node_modules/@alloc/quick-lru": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
+ "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@ampproject/remapping": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
+ "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
"node_modules/@babel/code-frame": {
"version": "7.23.5",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz",
@@ -131,6 +170,149 @@
"node": ">=4"
}
},
+ "node_modules/@babel/compat-data": {
+ "version": "7.23.5",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz",
+ "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.24.0",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.0.tgz",
+ "integrity": "sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==",
+ "dev": true,
+ "dependencies": {
+ "@ampproject/remapping": "^2.2.0",
+ "@babel/code-frame": "^7.23.5",
+ "@babel/generator": "^7.23.6",
+ "@babel/helper-compilation-targets": "^7.23.6",
+ "@babel/helper-module-transforms": "^7.23.3",
+ "@babel/helpers": "^7.24.0",
+ "@babel/parser": "^7.24.0",
+ "@babel/template": "^7.24.0",
+ "@babel/traverse": "^7.24.0",
+ "@babel/types": "^7.24.0",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/core/node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true
+ },
+ "node_modules/@babel/core/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.23.6",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz",
+ "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.23.6",
+ "@jridgewell/gen-mapping": "^0.3.2",
+ "@jridgewell/trace-mapping": "^0.3.17",
+ "jsesc": "^2.5.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.23.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz",
+ "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/compat-data": "^7.23.5",
+ "@babel/helper-validator-option": "^7.23.5",
+ "browserslist": "^4.22.2",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets/node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true
+ },
+ "node_modules/@babel/helper-environment-visitor": {
+ "version": "7.22.20",
+ "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
+ "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-function-name": {
+ "version": "7.23.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
+ "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/template": "^7.22.15",
+ "@babel/types": "^7.23.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-hoist-variables": {
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz",
+ "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.22.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
"node_modules/@babel/helper-module-imports": {
"version": "7.22.15",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz",
@@ -143,6 +325,58 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.23.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz",
+ "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-environment-visitor": "^7.22.20",
+ "@babel/helper-module-imports": "^7.22.15",
+ "@babel/helper-simple-access": "^7.22.5",
+ "@babel/helper-split-export-declaration": "^7.22.6",
+ "@babel/helper-validator-identifier": "^7.22.20"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.24.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz",
+ "integrity": "sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-simple-access": {
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz",
+ "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.22.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-split-export-declaration": {
+ "version": "7.22.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz",
+ "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.22.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
"node_modules/@babel/helper-string-parser": {
"version": "7.23.4",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz",
@@ -161,6 +395,29 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.23.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz",
+ "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.24.0",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.0.tgz",
+ "integrity": "sha512-ulDZdc0Aj5uLc5nETsa7EPx2L7rM0YJM8r7ck7U73AXi7qOV44IHHRAYZHY6iU1rr3C5N4NtTmMRUJP6kwCWeA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/template": "^7.24.0",
+ "@babel/traverse": "^7.24.0",
+ "@babel/types": "^7.24.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
"node_modules/@babel/highlight": {
"version": "7.23.4",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz",
@@ -258,6 +515,36 @@
"node": ">=6.0.0"
}
},
+ "node_modules/@babel/plugin-transform-react-jsx-self": {
+ "version": "7.23.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.23.3.tgz",
+ "integrity": "sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.22.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-source": {
+ "version": "7.23.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.23.3.tgz",
+ "integrity": "sha512-91RS0MDnAWDNvGC6Wio5XYkyWI39FMFO+JK9+4AlgaTH+yWwVTsw7/sn6LK0lH7c5F+TFkpv/3LfCJ1Ydwof/g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.22.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
"node_modules/@babel/runtime": {
"version": "7.24.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.0.tgz",
@@ -269,6 +556,50 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@babel/template": {
+ "version": "7.24.0",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz",
+ "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.23.5",
+ "@babel/parser": "^7.24.0",
+ "@babel/types": "^7.24.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.24.0",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.0.tgz",
+ "integrity": "sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.23.5",
+ "@babel/generator": "^7.23.6",
+ "@babel/helper-environment-visitor": "^7.22.20",
+ "@babel/helper-function-name": "^7.23.0",
+ "@babel/helper-hoist-variables": "^7.22.5",
+ "@babel/helper-split-export-declaration": "^7.22.6",
+ "@babel/parser": "^7.24.0",
+ "@babel/types": "^7.24.0",
+ "debug": "^4.3.1",
+ "globals": "^11.1.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse/node_modules/globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/@babel/types": {
"version": "7.24.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz",
@@ -284,9 +615,9 @@
}
},
"node_modules/@barclah/jolt-physics": {
- "version": "0.16.2",
- "resolved": "https://registry.npmjs.org/@barclah/jolt-physics/-/jolt-physics-0.16.2.tgz",
- "integrity": "sha512-HgSKDE2LadVNT1ZKZK6KbNmy5CFWLNvhnZHbYif8tpBfoN+JLpnUyZRaiR/0x1O0eV+upxWww+vnaos2gDB+cg=="
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@barclah/jolt-physics/-/jolt-physics-0.19.1.tgz",
+ "integrity": "sha512-bUqUovgZrGb3JEuylX7BXbfBdXu5RXhEWoS/+mALzvh8aO4VhMBBaQuBt/HF05IZLQLkZ584EBkwPBHmNfyOOQ=="
},
"node_modules/@emotion/babel-plugin": {
"version": "11.11.0",
@@ -973,6 +1304,50 @@
"integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==",
"dev": true
},
+ "node_modules/@isaacs/cliui": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+ "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+ "dev": true,
+ "dependencies": {
+ "string-width": "^5.1.2",
+ "string-width-cjs": "npm:string-width@^4.2.0",
+ "strip-ansi": "^7.0.1",
+ "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+ "wrap-ansi": "^8.1.0",
+ "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/ansi-regex": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+ "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
"node_modules/@jest/schemas": {
"version": "29.6.3",
"resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz",
@@ -985,12 +1360,54 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
+ "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/set-array": "^1.2.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/set-array": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
+ "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.4.15",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
"dev": true
},
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.25",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+ "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
"node_modules/@jsdoc/salty": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.7.tgz",
@@ -1271,6 +1688,16 @@
"node": ">= 8"
}
},
+ "node_modules/@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+ "dev": true,
+ "optional": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
"node_modules/@popperjs/core": {
"version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
@@ -1893,6 +2320,56 @@
"integrity": "sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==",
"dev": true
},
+ "node_modules/@trysound/sax": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
+ "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/@types/babel__core": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
+ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7",
+ "@types/babel__generator": "*",
+ "@types/babel__template": "*",
+ "@types/babel__traverse": "*"
+ }
+ },
+ "node_modules/@types/babel__generator": {
+ "version": "7.6.8",
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz",
+ "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__template": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
+ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
+ "dev": true,
+ "dependencies": {
+ "@babel/parser": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__traverse": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz",
+ "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.20.7"
+ }
+ },
"node_modules/@types/draco3d": {
"version": "1.4.9",
"resolved": "https://registry.npmjs.org/@types/draco3d/-/draco3d-1.4.9.tgz",
@@ -1910,6 +2387,13 @@
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
"dev": true
},
+ "node_modules/@types/json5": {
+ "version": "0.0.29",
+ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
+ "dev": true,
+ "peer": true
+ },
"node_modules/@types/linkify-it": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.5.tgz",
@@ -1936,6 +2420,7 @@
"version": "20.11.24",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz",
"integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==",
+ "dev": true,
"dependencies": {
"undici-types": "~5.26.4"
}
@@ -1945,6 +2430,12 @@
"resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz",
"integrity": "sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A=="
},
+ "node_modules/@types/pako": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.3.tgz",
+ "integrity": "sha512-bq0hMV9opAcrmE0Byyo0fY3Ew4tgOevJmQ9grUhpXQhYfyLJ1Kqg3P33JT5fdbT2AjeAjR51zqqVjAL/HMkx7Q==",
+ "dev": true
+ },
"node_modules/@types/parse-json": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
@@ -2236,8 +2727,27 @@
"react": ">= 16.8.0"
}
},
- "node_modules/@vitejs/plugin-react-swc": {
- "version": "3.6.0",
+ "node_modules/@vitejs/plugin-react": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.2.1.tgz",
+ "integrity": "sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/core": "^7.23.5",
+ "@babel/plugin-transform-react-jsx-self": "^7.23.3",
+ "@babel/plugin-transform-react-jsx-source": "^7.23.3",
+ "@types/babel__core": "^7.20.5",
+ "react-refresh": "^0.14.0"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "vite": "^4.2.0 || ^5.0.0"
+ }
+ },
+ "node_modules/@vitejs/plugin-react-swc": {
+ "version": "3.6.0",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.6.0.tgz",
"integrity": "sha512-XFRbsGgpGxGzEV5i5+vRiro1bwcIaZDIdBRP16qwm+jP68ue/S8FJTBEgOeojtVDYrbSua3XFp71kC8VJE6v+g==",
"dev": true,
@@ -2414,12 +2924,74 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
+ "node_modules/any-promise": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+ "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
+ "dev": true
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/arg": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
+ "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
+ "dev": true
+ },
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true
},
+ "node_modules/array-buffer-byte-length": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz",
+ "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.5",
+ "is-array-buffer": "^3.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array-includes": {
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz",
+ "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
+ "get-intrinsic": "^1.2.1",
+ "is-string": "^1.0.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/array-union": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
@@ -2429,6 +3001,107 @@
"node": ">=8"
}
},
+ "node_modules/array.prototype.filter": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/array.prototype.filter/-/array.prototype.filter-1.0.3.tgz",
+ "integrity": "sha512-VizNcj/RGJiUyQBgzwxzE5oHdeuXY5hSbbmKMlphj1cy1Vl7Pn2asCGbSrru6hSQjmCzqTBPVWAF/whmEOVHbw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
+ "es-array-method-boxes-properly": "^1.0.0",
+ "is-string": "^1.0.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.findlastindex": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.4.tgz",
+ "integrity": "sha512-hzvSHUshSpCflDR1QMUBLHGHP1VIEBegT4pix9H/Z92Xw3ySoy6c2qh7lJWTJnRJ8JCZ9bJNCgTyYaJGcJu6xQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.5",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.22.3",
+ "es-errors": "^1.3.0",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flat": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz",
+ "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
+ "es-shim-unscopables": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flatmap": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz",
+ "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
+ "es-shim-unscopables": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/arraybuffer.prototype.slice": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz",
+ "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.1",
+ "call-bind": "^1.0.5",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.22.3",
+ "es-errors": "^1.2.1",
+ "get-intrinsic": "^1.2.3",
+ "is-array-buffer": "^3.0.4",
+ "is-shared-array-buffer": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/assertion-error": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
@@ -2438,6 +3111,59 @@
"node": "*"
}
},
+ "node_modules/autoprefixer": {
+ "version": "10.4.18",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.18.tgz",
+ "integrity": "sha512-1DKbDfsr6KUElM6wg+0zRNkB/Q7WcKYAaK+pzXn+Xqmszm/5Xa9coeNdtP88Vi+dPzZnMjhge8GIV49ZQkDa+g==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/autoprefixer"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "browserslist": "^4.23.0",
+ "caniuse-lite": "^1.0.30001591",
+ "fraction.js": "^4.3.7",
+ "normalize-range": "^0.1.2",
+ "picocolors": "^1.0.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "bin": {
+ "autoprefixer": "bin/autoprefixer"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/available-typed-arrays": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
+ "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "possible-typed-array-names": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/babel-plugin-macros": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
@@ -2486,12 +3212,30 @@
"require-from-string": "^2.0.2"
}
},
+ "node_modules/binary-extensions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/bluebird": {
"version": "3.7.2",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
"dev": true
},
+ "node_modules/boolbase": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
+ "dev": true
+ },
"node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
@@ -2513,6 +3257,38 @@
"node": ">=8"
}
},
+ "node_modules/browserslist": {
+ "version": "4.23.0",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz",
+ "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "caniuse-lite": "^1.0.30001587",
+ "electron-to-chromium": "^1.4.668",
+ "node-releases": "^2.0.14",
+ "update-browserslist-db": "^1.0.13"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
"node_modules/buffer": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
@@ -2545,6 +3321,26 @@
"node": ">=8"
}
},
+ "node_modules/call-bind": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
+ "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -2554,6 +3350,15 @@
"node": ">=6"
}
},
+ "node_modules/camelcase-css": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
+ "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/camera-controls": {
"version": "2.8.3",
"resolved": "https://registry.npmjs.org/camera-controls/-/camera-controls-2.8.3.tgz",
@@ -2562,6 +3367,38 @@
"three": ">=0.126.1"
}
},
+ "node_modules/caniuse-api": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz",
+ "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==",
+ "dev": true,
+ "dependencies": {
+ "browserslist": "^4.0.0",
+ "caniuse-lite": "^1.0.0",
+ "lodash.memoize": "^4.1.2",
+ "lodash.uniq": "^4.5.0"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001597",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001597.tgz",
+ "integrity": "sha512-7LjJvmQU6Sj7bL0j5b5WY/3n7utXUJvAe1lxhsHDbLmwX9mdL86Yjtr+5SRCyf8qME4M7pU2hswj0FpyBVCv9w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ]
+ },
"node_modules/catharsis": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz",
@@ -2620,6 +3457,42 @@
"node": "*"
}
},
+ "node_modules/chokidar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+ "dev": true,
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/chokidar/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/clsx": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz",
@@ -2647,6 +3520,20 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
+ "node_modules/colord": {
+ "version": "2.9.3",
+ "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
+ "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw=="
+ },
+ "node_modules/commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 10"
+ }
+ },
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -2705,23 +3592,197 @@
"node": ">= 8"
}
},
- "node_modules/csstype": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
- "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
+ "node_modules/css-declaration-sorter": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.1.1.tgz",
+ "integrity": "sha512-dZ3bVTEEc1vxr3Bek9vGwfB5Z6ESPULhcRvO472mfjVnj8jRcTnKO8/JTczlvxM10Myb+wBM++1MtdO76eWcaQ==",
+ "dev": true,
+ "engines": {
+ "node": "^14 || ^16 || >=18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.0.9"
+ }
},
- "node_modules/debounce": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
- "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="
+ "node_modules/css-select": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
+ "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
+ "dev": true,
+ "dependencies": {
+ "boolbase": "^1.0.0",
+ "css-what": "^6.1.0",
+ "domhandler": "^5.0.2",
+ "domutils": "^3.0.1",
+ "nth-check": "^2.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
},
- "node_modules/debug": {
- "version": "4.3.4",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
- "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "node_modules/css-tree": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
+ "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
"dev": true,
"dependencies": {
- "ms": "2.1.2"
+ "mdn-data": "2.0.30",
+ "source-map-js": "^1.0.1"
+ },
+ "engines": {
+ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
+ }
+ },
+ "node_modules/css-what": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
+ "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/cssesc": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "dev": true,
+ "bin": {
+ "cssesc": "bin/cssesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/cssnano": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.1.0.tgz",
+ "integrity": "sha512-e2v4w/t3OFM6HTuSweI4RSdABaqgVgHlJp5FZrQsopHnKKHLFIvK2D3C4kHWeFIycN/1L1J5VIrg5KlDzn3r/g==",
+ "dev": true,
+ "dependencies": {
+ "cssnano-preset-default": "^6.1.0",
+ "lilconfig": "^3.1.1"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/cssnano"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/cssnano-preset-default": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.1.0.tgz",
+ "integrity": "sha512-4DUXZoDj+PI3fRl3MqMjl9DwLGjcsFP4qt+92nLUcN1RGfw2TY+GwNoG2B38Usu1BrcTs8j9pxNfSusmvtSjfg==",
+ "dev": true,
+ "dependencies": {
+ "browserslist": "^4.23.0",
+ "css-declaration-sorter": "^7.1.1",
+ "cssnano-utils": "^4.0.2",
+ "postcss-calc": "^9.0.1",
+ "postcss-colormin": "^6.1.0",
+ "postcss-convert-values": "^6.1.0",
+ "postcss-discard-comments": "^6.0.2",
+ "postcss-discard-duplicates": "^6.0.3",
+ "postcss-discard-empty": "^6.0.3",
+ "postcss-discard-overridden": "^6.0.2",
+ "postcss-merge-longhand": "^6.0.4",
+ "postcss-merge-rules": "^6.1.0",
+ "postcss-minify-font-values": "^6.0.3",
+ "postcss-minify-gradients": "^6.0.3",
+ "postcss-minify-params": "^6.1.0",
+ "postcss-minify-selectors": "^6.0.3",
+ "postcss-normalize-charset": "^6.0.2",
+ "postcss-normalize-display-values": "^6.0.2",
+ "postcss-normalize-positions": "^6.0.2",
+ "postcss-normalize-repeat-style": "^6.0.2",
+ "postcss-normalize-string": "^6.0.2",
+ "postcss-normalize-timing-functions": "^6.0.2",
+ "postcss-normalize-unicode": "^6.1.0",
+ "postcss-normalize-url": "^6.0.2",
+ "postcss-normalize-whitespace": "^6.0.2",
+ "postcss-ordered-values": "^6.0.2",
+ "postcss-reduce-initial": "^6.1.0",
+ "postcss-reduce-transforms": "^6.0.2",
+ "postcss-svgo": "^6.0.3",
+ "postcss-unique-selectors": "^6.0.3"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/cssnano-utils": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.2.tgz",
+ "integrity": "sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ==",
+ "dev": true,
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/csso": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz",
+ "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==",
+ "dev": true,
+ "dependencies": {
+ "css-tree": "~2.2.0"
+ },
+ "engines": {
+ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
+ "npm": ">=7.0.0"
+ }
+ },
+ "node_modules/csso/node_modules/css-tree": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz",
+ "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==",
+ "dev": true,
+ "dependencies": {
+ "mdn-data": "2.0.28",
+ "source-map-js": "^1.0.1"
+ },
+ "engines": {
+ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
+ "npm": ">=7.0.0"
+ }
+ },
+ "node_modules/csso/node_modules/mdn-data": {
+ "version": "2.0.28",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz",
+ "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==",
+ "dev": true
+ },
+ "node_modules/csstype": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
+ },
+ "node_modules/debounce": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
+ "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="
+ },
+ "node_modules/debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.1.2"
},
"engines": {
"node": ">=6.0"
@@ -2750,6 +3811,42 @@
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
"dev": true
},
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/define-properties": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
+ "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "define-data-property": "^1.0.1",
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/detect-gpu": {
"version": "5.0.38",
"resolved": "https://registry.npmjs.org/detect-gpu/-/detect-gpu-5.0.38.tgz",
@@ -2758,6 +3855,12 @@
"webgl-constants": "^1.1.1"
}
},
+ "node_modules/didyoumean": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
+ "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
+ "dev": true
+ },
"node_modules/diff-sequences": {
"version": "29.6.3",
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz",
@@ -2779,6 +3882,12 @@
"node": ">=8"
}
},
+ "node_modules/dlv": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
+ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
+ "dev": true
+ },
"node_modules/doctrine": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
@@ -2801,11 +3910,96 @@
"csstype": "^3.0.2"
}
},
+ "node_modules/dom-serializer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
+ "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
+ "dev": true,
+ "dependencies": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.2",
+ "entities": "^4.2.0"
+ },
+ "funding": {
+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+ }
+ },
+ "node_modules/dom-serializer/node_modules/entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/domelementtype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ]
+ },
+ "node_modules/domhandler": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
+ "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
+ "dev": true,
+ "dependencies": {
+ "domelementtype": "^2.3.0"
+ },
+ "engines": {
+ "node": ">= 4"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domhandler?sponsor=1"
+ }
+ },
+ "node_modules/domutils": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
+ "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
+ "dev": true,
+ "dependencies": {
+ "dom-serializer": "^2.0.0",
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domutils?sponsor=1"
+ }
+ },
"node_modules/draco3d": {
"version": "1.5.7",
"resolved": "https://registry.npmjs.org/draco3d/-/draco3d-1.5.7.tgz",
"integrity": "sha512-m6WCKt/erDXcw+70IJXnG7M3awwQPAsZvJGX5zY7beBqpELw6RDGkYVU0W43AFxye4pDZ5i2Lbyc/NNGqwjUVQ=="
},
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+ "dev": true
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.4.708",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.708.tgz",
+ "integrity": "sha512-iWgEEvREL4GTXXHKohhh33+6Y8XkPI5eHihDmm8zUk5Zo7HICEW+wI/j5kJ2tbuNUCXJ/sNXa03ajW635DiJXA==",
+ "dev": true
+ },
+ "node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "dev": true
+ },
"node_modules/entities": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz",
@@ -2824,6 +4018,135 @@
"is-arrayish": "^0.2.1"
}
},
+ "node_modules/es-abstract": {
+ "version": "1.22.5",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.5.tgz",
+ "integrity": "sha512-oW69R+4q2wG+Hc3KZePPZxOiisRIqfKBVo/HLx94QcJeWGU/8sZhCvc829rd1kS366vlJbzBfXf9yWwf0+Ko7w==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.1",
+ "arraybuffer.prototype.slice": "^1.0.3",
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.7",
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "es-set-tostringtag": "^2.0.3",
+ "es-to-primitive": "^1.2.1",
+ "function.prototype.name": "^1.1.6",
+ "get-intrinsic": "^1.2.4",
+ "get-symbol-description": "^1.0.2",
+ "globalthis": "^1.0.3",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2",
+ "has-proto": "^1.0.3",
+ "has-symbols": "^1.0.3",
+ "hasown": "^2.0.1",
+ "internal-slot": "^1.0.7",
+ "is-array-buffer": "^3.0.4",
+ "is-callable": "^1.2.7",
+ "is-negative-zero": "^2.0.3",
+ "is-regex": "^1.1.4",
+ "is-shared-array-buffer": "^1.0.3",
+ "is-string": "^1.0.7",
+ "is-typed-array": "^1.1.13",
+ "is-weakref": "^1.0.2",
+ "object-inspect": "^1.13.1",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.5",
+ "regexp.prototype.flags": "^1.5.2",
+ "safe-array-concat": "^1.1.0",
+ "safe-regex-test": "^1.0.3",
+ "string.prototype.trim": "^1.2.8",
+ "string.prototype.trimend": "^1.0.7",
+ "string.prototype.trimstart": "^1.0.7",
+ "typed-array-buffer": "^1.0.2",
+ "typed-array-byte-length": "^1.0.1",
+ "typed-array-byte-offset": "^1.0.2",
+ "typed-array-length": "^1.0.5",
+ "unbox-primitive": "^1.0.2",
+ "which-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/es-array-method-boxes-properly": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz",
+ "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
+ "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "get-intrinsic": "^1.2.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz",
+ "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "get-intrinsic": "^1.2.4",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-shim-unscopables": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz",
+ "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "hasown": "^2.0.0"
+ }
+ },
+ "node_modules/es-to-primitive": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+ "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "is-callable": "^1.1.4",
+ "is-date-object": "^1.0.1",
+ "is-symbol": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/esbuild": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz",
@@ -2862,6 +4185,15 @@
"@esbuild/win32-x64": "0.19.12"
}
},
+ "node_modules/escalade": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
+ "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@@ -3021,56 +4353,245 @@
"url": "https://opencollective.com/eslint"
}
},
- "node_modules/eslint-plugin-react-hooks": {
- "version": "4.6.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz",
- "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==",
+ "node_modules/eslint-config-prettier": {
+ "version": "8.10.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz",
+ "integrity": "sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==",
"dev": true,
- "engines": {
- "node": ">=10"
+ "bin": {
+ "eslint-config-prettier": "bin/cli.js"
},
"peerDependencies": {
- "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0"
+ "eslint": ">=7.0.0"
}
},
- "node_modules/eslint-plugin-react-refresh": {
- "version": "0.4.5",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.5.tgz",
- "integrity": "sha512-D53FYKJa+fDmZMtriODxvhwrO+IOqrxoEo21gMA0sjHdU6dPVH4OhyFip9ypl8HOF5RV5KdTo+rBQLvnY2cO8w==",
+ "node_modules/eslint-import-resolver-alias": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-alias/-/eslint-import-resolver-alias-1.1.2.tgz",
+ "integrity": "sha512-WdviM1Eu834zsfjHtcGHtGfcu+F30Od3V7I9Fi57uhBEwPkjDcii7/yW8jAT+gOhn4P/vOxxNAXbFAKsrrc15w==",
"dev": true,
+ "engines": {
+ "node": ">= 4"
+ },
"peerDependencies": {
- "eslint": ">=7"
+ "eslint-plugin-import": ">=1.4.0"
}
},
- "node_modules/eslint-scope": {
- "version": "7.2.2",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
- "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+ "node_modules/eslint-import-resolver-node": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz",
+ "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==",
"dev": true,
+ "peer": true,
"dependencies": {
- "esrecurse": "^4.3.0",
- "estraverse": "^5.2.0"
+ "debug": "^3.2.7",
+ "is-core-module": "^2.13.0",
+ "resolve": "^1.22.4"
+ }
+ },
+ "node_modules/eslint-import-resolver-node/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-module-utils": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz",
+ "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "debug": "^3.2.7"
},
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": ">=4"
},
- "funding": {
- "url": "https://opencollective.com/eslint"
+ "peerDependenciesMeta": {
+ "eslint": {
+ "optional": true
+ }
}
},
- "node_modules/eslint-visitor-keys": {
- "version": "3.4.3",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
- "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "node_modules/eslint-module-utils/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-plugin-import": {
+ "version": "2.29.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz",
+ "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==",
"dev": true,
+ "peer": true,
+ "dependencies": {
+ "array-includes": "^3.1.7",
+ "array.prototype.findlastindex": "^1.2.3",
+ "array.prototype.flat": "^1.3.2",
+ "array.prototype.flatmap": "^1.3.2",
+ "debug": "^3.2.7",
+ "doctrine": "^2.1.0",
+ "eslint-import-resolver-node": "^0.3.9",
+ "eslint-module-utils": "^2.8.0",
+ "hasown": "^2.0.0",
+ "is-core-module": "^2.13.1",
+ "is-glob": "^4.0.3",
+ "minimatch": "^3.1.2",
+ "object.fromentries": "^2.0.7",
+ "object.groupby": "^1.0.1",
+ "object.values": "^1.1.7",
+ "semver": "^6.3.1",
+ "tsconfig-paths": "^3.15.0"
+ },
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": ">=4"
},
- "funding": {
- "url": "https://opencollective.com/eslint"
+ "peerDependencies": {
+ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8"
}
},
- "node_modules/eslint/node_modules/brace-expansion": {
+ "node_modules/eslint-plugin-import/node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/json5": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
+ "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "minimist": "^1.2.0"
+ },
+ "bin": {
+ "json5": "lib/cli.js"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "peer": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/tsconfig-paths": {
+ "version": "3.15.0",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
+ "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "@types/json5": "^0.0.29",
+ "json5": "^1.0.2",
+ "minimist": "^1.2.6",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-react-hooks": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz",
+ "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0"
+ }
+ },
+ "node_modules/eslint-plugin-react-refresh": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.5.tgz",
+ "integrity": "sha512-D53FYKJa+fDmZMtriODxvhwrO+IOqrxoEo21gMA0sjHdU6dPVH4OhyFip9ypl8HOF5RV5KdTo+rBQLvnY2cO8w==",
+ "dev": true,
+ "peerDependencies": {
+ "eslint": ">=7"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "7.2.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
+ "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+ "dev": true,
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
@@ -3322,6 +4843,83 @@
"integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
"dev": true
},
+ "node_modules/for-each": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
+ "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "is-callable": "^1.1.3"
+ }
+ },
+ "node_modules/foreground-child": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
+ "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==",
+ "dev": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.0",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/fraction.js": {
+ "version": "4.3.7",
+ "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
+ "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "type": "patreon",
+ "url": "https://github.com/sponsors/rawify"
+ }
+ },
+ "node_modules/framer-motion": {
+ "version": "10.18.0",
+ "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.18.0.tgz",
+ "integrity": "sha512-oGlDh1Q1XqYPksuTD/usb0I70hq95OUzmL9+6Zd+Hs4XV0oaISBa/UUMSjYiq6m8EUF32132mOJ8xVZS+I0S6w==",
+ "dependencies": {
+ "tslib": "^2.4.0"
+ },
+ "optionalDependencies": {
+ "@emotion/is-prop-valid": "^0.8.2"
+ },
+ "peerDependencies": {
+ "react": "^18.0.0",
+ "react-dom": "^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "react": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/framer-motion/node_modules/@emotion/is-prop-valid": {
+ "version": "0.8.8",
+ "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz",
+ "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==",
+ "optional": true,
+ "dependencies": {
+ "@emotion/memoize": "0.7.4"
+ }
+ },
+ "node_modules/framer-motion/node_modules/@emotion/memoize": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz",
+ "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==",
+ "optional": true
+ },
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -3351,6 +4949,44 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/function.prototype.name": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz",
+ "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1",
+ "functions-have-names": "^1.2.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/functions-have-names": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
+ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
+ "dev": true,
+ "peer": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
"node_modules/get-func-name": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz",
@@ -3360,6 +4996,26 @@
"node": "*"
}
},
+ "node_modules/get-intrinsic": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
+ "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "has-proto": "^1.0.1",
+ "has-symbols": "^1.0.3",
+ "hasown": "^2.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/get-stream": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
@@ -3372,6 +5028,24 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/get-symbol-description": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz",
+ "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.5",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -3441,6 +5115,22 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/globalthis": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz",
+ "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "define-properties": "^1.1.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/globby": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
@@ -3466,6 +5156,19 @@
"resolved": "https://registry.npmjs.org/glsl-noise/-/glsl-noise-0.0.0.tgz",
"integrity": "sha512-b/ZCF6amfAUb7dJM/MxRs7AetQEahYzJ8PtgfrmEdtw6uyGOr+ZSGtgjFm6mfsBkxJ4d2W7kg+Nlqzqvn3Bc0w=="
},
+ "node_modules/gopd": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
+ "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "get-intrinsic": "^1.1.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
@@ -3478,6 +5181,16 @@
"integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
"dev": true
},
+ "node_modules/has-bigints": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
+ "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==",
+ "dev": true,
+ "peer": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@@ -3487,6 +5200,61 @@
"node": ">=8"
}
},
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-proto": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
+ "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/hasown": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz",
@@ -3592,15 +5360,102 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
},
- "node_modules/is-arrayish": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
- "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
- "dev": true
- },
- "node_modules/is-core-module": {
- "version": "2.13.1",
- "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
+ "node_modules/internal-slot": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz",
+ "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "hasown": "^2.0.0",
+ "side-channel": "^1.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/is-array-buffer": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz",
+ "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "get-intrinsic": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+ "dev": true
+ },
+ "node_modules/is-bigint": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
+ "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "has-bigints": "^1.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-boolean-object": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
+ "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.13.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
"integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
"dev": true,
"dependencies": {
@@ -3610,6 +5465,22 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-date-object": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
+ "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -3619,6 +5490,15 @@
"node": ">=0.10.0"
}
},
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
@@ -3631,6 +5511,19 @@
"node": ">=0.10.0"
}
},
+ "node_modules/is-negative-zero": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
+ "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
@@ -3640,6 +5533,22 @@
"node": ">=0.12.0"
}
},
+ "node_modules/is-number-object": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz",
+ "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-path-inside": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
@@ -3649,6 +5558,39 @@
"node": ">=8"
}
},
+ "node_modules/is-regex": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
+ "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-shared-array-buffer": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz",
+ "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-stream": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
@@ -3661,6 +5603,74 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/is-string": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
+ "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-symbol": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz",
+ "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "has-symbols": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-typed-array": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz",
+ "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "which-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakref": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
+ "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "dev": true,
+ "peer": true
+ },
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -3685,6 +5695,33 @@
"@types/react": "*"
}
},
+ "node_modules/jackspeak": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz",
+ "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==",
+ "dev": true,
+ "dependencies": {
+ "@isaacs/cliui": "^8.0.2"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ },
+ "optionalDependencies": {
+ "@pkgjs/parseargs": "^0.11.0"
+ }
+ },
+ "node_modules/jiti": {
+ "version": "1.21.0",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz",
+ "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==",
+ "dev": true,
+ "bin": {
+ "jiti": "bin/jiti.js"
+ }
+ },
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -3749,6 +5786,18 @@
"node": ">=8"
}
},
+ "node_modules/jsesc": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+ "dev": true,
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/json-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
@@ -3773,6 +5822,18 @@
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
"dev": true
},
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/jsonc-parser": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz",
@@ -3810,6 +5871,18 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/lilconfig": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz",
+ "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antonk52"
+ }
+ },
"node_modules/lines-and-columns": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
@@ -3862,12 +5935,24 @@
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
},
+ "node_modules/lodash.memoize": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
+ "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
+ "dev": true
+ },
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"dev": true
},
+ "node_modules/lodash.uniq": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
+ "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==",
+ "dev": true
+ },
"node_modules/long": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz",
@@ -3965,6 +6050,12 @@
"node": ">= 12"
}
},
+ "node_modules/mdn-data": {
+ "version": "2.0.30",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
+ "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
+ "dev": true
+ },
"node_modules/mdurl": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
@@ -4048,6 +6139,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/minipass": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz",
+ "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
"node_modules/mkdirp": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
@@ -4078,6 +6178,17 @@
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
+ "node_modules/mz": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
+ "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
+ "dev": true,
+ "dependencies": {
+ "any-promise": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "thenify-all": "^1.0.0"
+ }
+ },
"node_modules/nanoid": {
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
@@ -4102,6 +6213,30 @@
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
"dev": true
},
+ "node_modules/node-releases": {
+ "version": "2.0.14",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
+ "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==",
+ "dev": true
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/normalize-range": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
+ "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/npm-run-path": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
@@ -4129,6 +6264,18 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/nth-check": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+ "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+ "dev": true,
+ "dependencies": {
+ "boolbase": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/nth-check?sponsor=1"
+ }
+ },
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -4137,24 +6284,122 @@
"node": ">=0.10.0"
}
},
- "node_modules/once": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "node_modules/object-hash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
+ "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
"dev": true,
- "dependencies": {
- "wrappy": "1"
+ "engines": {
+ "node": ">= 6"
}
},
- "node_modules/onetime": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
- "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
+ "node_modules/object-inspect": {
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
+ "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
"dev": true,
- "dependencies": {
- "mimic-fn": "^4.0.0"
- },
- "engines": {
+ "peer": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.assign": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz",
+ "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.5",
+ "define-properties": "^1.2.1",
+ "has-symbols": "^1.0.3",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.fromentries": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz",
+ "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.groupby": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.2.tgz",
+ "integrity": "sha512-bzBq58S+x+uo0VjurFT0UktpKHOZmv4/xePiOA1nbB9pMqpGK7rUPNgf+1YC+7mE+0HzhTMqNUuCqvKhj6FnBw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "array.prototype.filter": "^1.0.3",
+ "call-bind": "^1.0.5",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.22.3",
+ "es-errors": "^1.0.0"
+ }
+ },
+ "node_modules/object.values": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz",
+ "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/onetime": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
+ "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
+ "dev": true,
+ "dependencies": {
+ "mimic-fn": "^4.0.0"
+ },
+ "engines": {
"node": ">=12"
},
"funding": {
@@ -4208,6 +6453,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/pako": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz",
+ "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==",
+ "dev": true
+ },
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -4261,96 +6512,677 @@
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
"engines": {
- "node": ">=8"
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true
+ },
+ "node_modules/path-scurry": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz",
+ "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==",
+ "dev": true,
+ "dependencies": {
+ "lru-cache": "^9.1.1 || ^10.0.0",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/path-scurry/node_modules/lru-cache": {
+ "version": "10.2.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz",
+ "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==",
+ "dev": true,
+ "engines": {
+ "node": "14 || >=16.14"
+ }
+ },
+ "node_modules/path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pathe": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz",
+ "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
+ "dev": true
+ },
+ "node_modules/pathval": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
+ "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
+ "dev": true
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/pirates": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz",
+ "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/pkg-types": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz",
+ "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==",
+ "dev": true,
+ "dependencies": {
+ "jsonc-parser": "^3.2.0",
+ "mlly": "^1.2.0",
+ "pathe": "^1.1.0"
+ }
+ },
+ "node_modules/possible-typed-array-names": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz",
+ "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.4.35",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz",
+ "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "nanoid": "^3.3.7",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/postcss-calc": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz",
+ "integrity": "sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==",
+ "dev": true,
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.11",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.2"
+ }
+ },
+ "node_modules/postcss-colormin": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.1.0.tgz",
+ "integrity": "sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==",
+ "dev": true,
+ "dependencies": {
+ "browserslist": "^4.23.0",
+ "caniuse-api": "^3.0.0",
+ "colord": "^2.9.3",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-convert-values": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.1.0.tgz",
+ "integrity": "sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==",
+ "dev": true,
+ "dependencies": {
+ "browserslist": "^4.23.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-discard-comments": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.2.tgz",
+ "integrity": "sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==",
+ "dev": true,
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-discard-duplicates": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.3.tgz",
+ "integrity": "sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw==",
+ "dev": true,
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-discard-empty": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.3.tgz",
+ "integrity": "sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ==",
+ "dev": true,
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-discard-overridden": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.2.tgz",
+ "integrity": "sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ==",
+ "dev": true,
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-import": {
+ "version": "15.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
+ "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.0.0",
+ "read-cache": "^1.0.0",
+ "resolve": "^1.1.7"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.0.0"
+ }
+ },
+ "node_modules/postcss-js": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz",
+ "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
+ "dev": true,
+ "dependencies": {
+ "camelcase-css": "^2.0.1"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >= 16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.21"
+ }
+ },
+ "node_modules/postcss-load-config": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz",
+ "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "lilconfig": "^3.0.0",
+ "yaml": "^2.3.4"
+ },
+ "engines": {
+ "node": ">= 14"
+ },
+ "peerDependencies": {
+ "postcss": ">=8.0.9",
+ "ts-node": ">=9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "postcss": {
+ "optional": true
+ },
+ "ts-node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/postcss-load-config/node_modules/yaml": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.1.tgz",
+ "integrity": "sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==",
+ "dev": true,
+ "bin": {
+ "yaml": "bin.mjs"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/postcss-merge-longhand": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.4.tgz",
+ "integrity": "sha512-vAfWGcxUUGlFiPM3nDMZA+/Yo9sbpc3JNkcYZez8FfJDv41Dh7tAgA3QGVTocaHCZZL6aXPXPOaBMJsjujodsA==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0",
+ "stylehacks": "^6.1.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-merge-rules": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.1.0.tgz",
+ "integrity": "sha512-lER+W3Gr6XOvxOYk1Vi/6UsAgKMg6MDBthmvbNqi2XxAk/r9XfhdYZSigfWjuWWn3zYw2wLelvtM8XuAEFqRkA==",
+ "dev": true,
+ "dependencies": {
+ "browserslist": "^4.23.0",
+ "caniuse-api": "^3.0.0",
+ "cssnano-utils": "^4.0.2",
+ "postcss-selector-parser": "^6.0.15"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-minify-font-values": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.0.3.tgz",
+ "integrity": "sha512-SmAeTA1We5rMnN3F8X9YBNo9bj9xB4KyDHnaNJnBfQIPi+60fNiR9OTRnIaMqkYzAQX0vObIw4Pn0vuKEOettg==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-minify-gradients": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.3.tgz",
+ "integrity": "sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q==",
+ "dev": true,
+ "dependencies": {
+ "colord": "^2.9.3",
+ "cssnano-utils": "^4.0.2",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-minify-params": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.1.0.tgz",
+ "integrity": "sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA==",
+ "dev": true,
+ "dependencies": {
+ "browserslist": "^4.23.0",
+ "cssnano-utils": "^4.0.2",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-minify-selectors": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.3.tgz",
+ "integrity": "sha512-IcV7ZQJcaXyhx4UBpWZMsinGs2NmiUC60rJSkyvjPCPqhNjVGsrJUM+QhAtCaikZ0w0/AbZuH4wVvF/YMuMhvA==",
+ "dev": true,
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.15"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-nested": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz",
+ "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==",
+ "dev": true,
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.11"
+ },
+ "engines": {
+ "node": ">=12.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.14"
+ }
+ },
+ "node_modules/postcss-normalize-charset": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.2.tgz",
+ "integrity": "sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ==",
+ "dev": true,
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-normalize-display-values": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.2.tgz",
+ "integrity": "sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-normalize-positions": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.2.tgz",
+ "integrity": "sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-normalize-repeat-style": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.2.tgz",
+ "integrity": "sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-normalize-string": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.2.tgz",
+ "integrity": "sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-normalize-timing-functions": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.2.tgz",
+ "integrity": "sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-normalize-unicode": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.1.0.tgz",
+ "integrity": "sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg==",
+ "dev": true,
+ "dependencies": {
+ "browserslist": "^4.23.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-normalize-url": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.2.tgz",
+ "integrity": "sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-normalize-whitespace": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.2.tgz",
+ "integrity": "sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q==",
+ "dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
+ "node_modules/postcss-ordered-values": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.2.tgz",
+ "integrity": "sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==",
+ "dev": true,
+ "dependencies": {
+ "cssnano-utils": "^4.0.2",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
}
},
- "node_modules/path-parse": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
- "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
- "dev": true
- },
- "node_modules/path-type": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
- "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "node_modules/postcss-reduce-initial": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.1.0.tgz",
+ "integrity": "sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw==",
"dev": true,
+ "dependencies": {
+ "browserslist": "^4.23.0",
+ "caniuse-api": "^3.0.0"
+ },
"engines": {
- "node": ">=8"
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
}
},
- "node_modules/pathe": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz",
- "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
- "dev": true
- },
- "node_modules/pathval": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
- "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==",
+ "node_modules/postcss-reduce-transforms": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.2.tgz",
+ "integrity": "sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA==",
"dev": true,
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
"engines": {
- "node": "*"
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
}
},
- "node_modules/picocolors": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
- "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
- "dev": true
- },
- "node_modules/picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "node_modules/postcss-selector-parser": {
+ "version": "6.0.16",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz",
+ "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==",
"dev": true,
- "engines": {
- "node": ">=8.6"
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
},
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
+ "engines": {
+ "node": ">=4"
}
},
- "node_modules/pkg-types": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz",
- "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==",
+ "node_modules/postcss-svgo": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.3.tgz",
+ "integrity": "sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g==",
"dev": true,
"dependencies": {
- "jsonc-parser": "^3.2.0",
- "mlly": "^1.2.0",
- "pathe": "^1.1.0"
+ "postcss-value-parser": "^4.2.0",
+ "svgo": "^3.2.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >= 18"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
}
},
- "node_modules/postcss": {
- "version": "8.4.35",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz",
- "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==",
+ "node_modules/postcss-unique-selectors": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.3.tgz",
+ "integrity": "sha512-NFXbYr8qdmCr/AFceaEfdcsKGCvWTeGO6QVC9h2GvtWgj0/0dklKQcaMMVzs6tr8bY+ase8hOtHW8OBTTRvS8A==",
"dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/postcss"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
"dependencies": {
- "nanoid": "^3.3.7",
- "picocolors": "^1.0.0",
- "source-map-js": "^1.0.2"
+ "postcss-selector-parser": "^6.0.15"
},
"engines": {
- "node": "^10 || ^12 || >=14"
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
}
},
+ "node_modules/postcss-value-parser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+ "dev": true
+ },
"node_modules/potpack": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz",
@@ -4365,6 +7197,21 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/prettier": {
+ "version": "3.2.5",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz",
+ "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==",
+ "dev": true,
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
"node_modules/pretty-format": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
@@ -4529,6 +7376,15 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-colorful": {
+ "version": "5.6.1",
+ "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz",
+ "integrity": "sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==",
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
"node_modules/react-composer": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/react-composer/-/react-composer-5.0.3.tgz",
@@ -4552,6 +7408,14 @@
"react": "^18.2.0"
}
},
+ "node_modules/react-icons": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.12.0.tgz",
+ "integrity": "sha512-IBaDuHiShdZqmfc/TwHu6+d6k2ltNCf3AszxNmjJc1KUfXdEeRJOKyNvLmAHaarhzGmTSVygNdyu8/opXv2gaw==",
+ "peerDependencies": {
+ "react": "*"
+ }
+ },
"node_modules/react-is": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
@@ -4581,6 +7445,15 @@
"loose-envify": "^1.1.0"
}
},
+ "node_modules/react-refresh": {
+ "version": "0.14.0",
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz",
+ "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/react-transition-group": {
"version": "4.4.5",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
@@ -4609,11 +7482,51 @@
"react-dom": ">=16.13"
}
},
+ "node_modules/read-cache": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
+ "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
+ "dev": true,
+ "dependencies": {
+ "pify": "^2.3.0"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
"node_modules/regenerator-runtime": {
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
},
+ "node_modules/regexp.prototype.flags": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz",
+ "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.6",
+ "define-properties": "^1.2.1",
+ "es-errors": "^1.3.0",
+ "set-function-name": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/require-from-string": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
@@ -4737,6 +7650,43 @@
"queue-microtask": "^1.2.2"
}
},
+ "node_modules/safe-array-concat": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz",
+ "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "get-intrinsic": "^1.2.4",
+ "has-symbols": "^1.0.3",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">=0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/safe-regex-test": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz",
+ "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.6",
+ "es-errors": "^1.3.0",
+ "is-regex": "^1.1.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/scheduler": {
"version": "0.23.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
@@ -4760,6 +7710,40 @@
"node": ">=10"
}
},
+ "node_modules/set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/set-function-name": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
+ "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "functions-have-names": "^1.2.3",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -4779,6 +7763,25 @@
"node": ">=8"
}
},
+ "node_modules/side-channel": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
+ "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.4",
+ "object-inspect": "^1.13.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/siginfo": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
@@ -4846,7 +7849,133 @@
"integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==",
"dev": true
},
- "node_modules/strip-ansi": {
+ "node_modules/string-width": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+ "dev": true,
+ "dependencies": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/string-width-cjs": {
+ "name": "string-width",
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "node_modules/string-width/node_modules/ansi-regex": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+ "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/string-width/node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/string.prototype.trim": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz",
+ "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimend": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz",
+ "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimstart": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz",
+ "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.2.0",
+ "es-abstract": "^1.22.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi-cjs": {
+ "name": "strip-ansi",
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
@@ -4858,6 +7987,15 @@
"node": ">=8"
}
},
+ "node_modules/strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/strip-final-newline": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
@@ -4900,12 +8038,81 @@
"integrity": "sha512-UfJMcSJc+SEXEl9lH/VLHSZbThQyLpw1vLO1Lb+j4RWDvG3N2f7yj3PVQA3cmkTBNldJ9eFnM+xEXxHIXrYiJw==",
"dev": true
},
+ "node_modules/stylehacks": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-6.1.0.tgz",
+ "integrity": "sha512-ETErsPFgwlfYZ/CSjMO2Ddf+TsnkCVPBPaoB99Ro8WMAxf7cglzmFsRBhRmKObFjibtcvlNxFFPHuyr3sNlNUQ==",
+ "dev": true,
+ "dependencies": {
+ "browserslist": "^4.23.0",
+ "postcss-selector-parser": "^6.0.15"
+ },
+ "engines": {
+ "node": "^14 || ^16 || >=18.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.31"
+ }
+ },
"node_modules/stylis": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
"integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==",
"dev": true
},
+ "node_modules/sucrase": {
+ "version": "3.35.0",
+ "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
+ "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.2",
+ "commander": "^4.0.0",
+ "glob": "^10.3.10",
+ "lines-and-columns": "^1.1.6",
+ "mz": "^2.7.0",
+ "pirates": "^4.0.1",
+ "ts-interface-checker": "^0.1.9"
+ },
+ "bin": {
+ "sucrase": "bin/sucrase",
+ "sucrase-node": "bin/sucrase-node"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/sucrase/node_modules/commander": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
+ "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/sucrase/node_modules/glob": {
+ "version": "10.3.10",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
+ "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
+ "dev": true,
+ "dependencies": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^2.3.5",
+ "minimatch": "^9.0.1",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
+ "path-scurry": "^1.10.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -4938,12 +8145,104 @@
"react": ">=17.0"
}
},
+ "node_modules/svgo": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.2.0.tgz",
+ "integrity": "sha512-4PP6CMW/V7l/GmKRKzsLR8xxjdHTV4IMvhTnpuHwwBazSIlw5W/5SmPjN8Dwyt7lKbSJrRDgp4t9ph0HgChFBQ==",
+ "dev": true,
+ "dependencies": {
+ "@trysound/sax": "0.2.0",
+ "commander": "^7.2.0",
+ "css-select": "^5.1.0",
+ "css-tree": "^2.3.1",
+ "css-what": "^6.1.0",
+ "csso": "^5.0.5",
+ "picocolors": "^1.0.0"
+ },
+ "bin": {
+ "svgo": "bin/svgo"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/svgo"
+ }
+ },
+ "node_modules/tailwindcss": {
+ "version": "3.4.1",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz",
+ "integrity": "sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==",
+ "dev": true,
+ "dependencies": {
+ "@alloc/quick-lru": "^5.2.0",
+ "arg": "^5.0.2",
+ "chokidar": "^3.5.3",
+ "didyoumean": "^1.2.2",
+ "dlv": "^1.1.3",
+ "fast-glob": "^3.3.0",
+ "glob-parent": "^6.0.2",
+ "is-glob": "^4.0.3",
+ "jiti": "^1.19.1",
+ "lilconfig": "^2.1.0",
+ "micromatch": "^4.0.5",
+ "normalize-path": "^3.0.0",
+ "object-hash": "^3.0.0",
+ "picocolors": "^1.0.0",
+ "postcss": "^8.4.23",
+ "postcss-import": "^15.1.0",
+ "postcss-js": "^4.0.1",
+ "postcss-load-config": "^4.0.1",
+ "postcss-nested": "^6.0.1",
+ "postcss-selector-parser": "^6.0.11",
+ "resolve": "^1.22.2",
+ "sucrase": "^3.32.0"
+ },
+ "bin": {
+ "tailwind": "lib/cli.js",
+ "tailwindcss": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/tailwindcss/node_modules/lilconfig": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
+ "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/text-table": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
"dev": true
},
+ "node_modules/thenify": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
+ "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
+ "dev": true,
+ "dependencies": {
+ "any-promise": "^1.0.0"
+ }
+ },
+ "node_modules/thenify-all": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
+ "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
+ "dev": true,
+ "dependencies": {
+ "thenify": ">= 3.1.0 < 4"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
"node_modules/three": {
"version": "0.159.0",
"resolved": "https://registry.npmjs.org/three/-/three-0.159.0.tgz",
@@ -5066,6 +8365,31 @@
"typescript": ">=4.2.0"
}
},
+ "node_modules/ts-interface-checker": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
+ "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
+ "dev": true
+ },
+ "node_modules/tsconfig-paths": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz",
+ "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==",
+ "dev": true,
+ "dependencies": {
+ "json5": "^2.2.2",
+ "minimist": "^1.2.6",
+ "strip-bom": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
+ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
+ },
"node_modules/tunnel-rat": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/tunnel-rat/-/tunnel-rat-0.1.2.tgz",
@@ -5134,6 +8458,83 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/typed-array-buffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz",
+ "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "es-errors": "^1.3.0",
+ "is-typed-array": "^1.1.13"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/typed-array-byte-length": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz",
+ "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "has-proto": "^1.0.3",
+ "is-typed-array": "^1.1.13"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-byte-offset": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz",
+ "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "has-proto": "^1.0.3",
+ "is-typed-array": "^1.1.13"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-length": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.5.tgz",
+ "integrity": "sha512-yMi0PlwuznKHxKmcpoOdeLwxBoVPkqZxd7q2FgMkmD3bNwvF5VW0+UlUQ1k1vmktTu4Yu13Q0RIxEP8+B+wloA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "has-proto": "^1.0.3",
+ "is-typed-array": "^1.1.13",
+ "possible-typed-array-names": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/typescript": {
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
@@ -5171,6 +8572,22 @@
"node": ">=0.8.0"
}
},
+ "node_modules/unbox-primitive": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
+ "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "has-bigints": "^1.0.2",
+ "has-symbols": "^1.0.3",
+ "which-boxed-primitive": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/underscore": {
"version": "1.13.6",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz",
@@ -5180,7 +8597,38 @@
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
- "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
+ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
+ "dev": true
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.0.13",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
+ "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "escalade": "^3.1.1",
+ "picocolors": "^1.0.0"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
},
"node_modules/uri-js": {
"version": "4.4.1",
@@ -5199,6 +8647,12 @@
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "dev": true
+ },
"node_modules/utility-types": {
"version": "3.11.0",
"resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz",
@@ -5296,6 +8750,22 @@
"url": "https://opencollective.com/vitest"
}
},
+ "node_modules/vite-plugin-singlefile": {
+ "version": "0.13.5",
+ "resolved": "https://registry.npmjs.org/vite-plugin-singlefile/-/vite-plugin-singlefile-0.13.5.tgz",
+ "integrity": "sha512-y/aRGh8qHmw2f1IhaI/C6PJAaov47ESYDvUv1am1YHMhpY+19B5k5Odp8P+tgs+zhfvak6QB1ykrALQErEAo7g==",
+ "dev": true,
+ "dependencies": {
+ "micromatch": "^4.0.5"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "rollup": ">=2.79.0",
+ "vite": ">=3.2.0"
+ }
+ },
"node_modules/vitest": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-1.3.1.tgz",
@@ -5385,6 +8855,43 @@
"node": ">= 8"
}
},
+ "node_modules/which-boxed-primitive": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
+ "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "is-bigint": "^1.0.1",
+ "is-boolean-object": "^1.1.0",
+ "is-number-object": "^1.0.4",
+ "is-string": "^1.0.5",
+ "is-symbol": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-typed-array": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz",
+ "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/why-is-node-running": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz",
@@ -5410,6 +8917,100 @@
"node": ">=0.10.0"
}
},
+ "node_modules/wrap-ansi": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs": {
+ "name": "wrap-ansi",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/ansi-regex": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+ "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
diff --git a/fission/package.json b/fission/package.json
index d7dc8f4d9d..66e49aa676 100644
--- a/fission/package.json
+++ b/fission/package.json
@@ -6,38 +6,56 @@
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
- "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
+ "lint": "eslint . --ext ts,tsx --report-unused-disable-directives",
"preview": "vite preview",
- "test": "vitest"
+ "test": "vitest",
+ "lint:fix": "npm run lint --fix",
+ "prettier": "npx prettier src --check",
+ "prettier:fix": "npm run prettier --write",
+ "format": "npm run prettier:fix && npm run lint:fix"
},
"dependencies": {
- "@barclah/jolt-physics": "^0.16.2",
+ "@barclah/jolt-physics": "^0.19.1",
"@react-three/drei": "^9.96.5",
"@react-three/fiber": "^8.15.15",
- "@types/node": "^20.10.6",
- "@types/react": "^18.2.47",
- "@types/react-dom": "^18.2.18",
- "@types/three": "^0.160.0",
+ "colord": "^2.9.3",
+ "framer-motion": "^10.13.1",
"react": "^18.2.0",
+ "react-colorful": "^5.6.1",
"react-dom": "^18.2.0",
+ "react-icons": "^4.10.1",
"three": "^0.159.0"
},
"devDependencies": {
"@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0",
"@mui/material": "^5.15.6",
- "@types/react": "^18.2.56",
- "@types/react-dom": "^18.2.19",
+ "@types/node": "^20.4.4",
+ "@types/pako": "^2.0.3",
+ "@types/react": "^18.2.47",
+ "@types/react-dom": "^18.2.18",
+ "@types/three": "^0.160.0",
"@typescript-eslint/eslint-plugin": "^7.0.2",
"@typescript-eslint/parser": "^7.0.2",
+ "@vitejs/plugin-react": "^4.0.3",
"@vitejs/plugin-react-swc": "^3.5.0",
+ "autoprefixer": "^10.4.14",
+ "cssnano": "^6.0.1",
"eslint": "^8.56.0",
+ "eslint-config-prettier": "^8.8.0",
+ "eslint-import-resolver-alias": "^1.1.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.5",
+ "pako": "^2.1.0",
+ "postcss": "^8.4.26",
+ "prettier": "^3.0.0",
"protobufjs": "^7.2.6",
"protobufjs-cli": "^1.1.2",
+ "tailwindcss": "^3.3.3",
+ "tsconfig-paths": "^4.2.0",
"typescript": "^5.2.2",
"vite": "^5.1.4",
+ "vite-plugin-singlefile": "^0.13.5",
"vitest": "^1.3.1"
}
}
diff --git a/fission/postcss.config.js b/fission/postcss.config.js
new file mode 100644
index 0000000000..c59a38c7a1
--- /dev/null
+++ b/fission/postcss.config.js
@@ -0,0 +1,7 @@
+export default {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ ...(process.env.NODE_ENV === 'production' ? { cssnano: {} } : {})
+ },
+}
diff --git a/fission/prettier.config.js b/fission/prettier.config.js
new file mode 100644
index 0000000000..3300a59e04
--- /dev/null
+++ b/fission/prettier.config.js
@@ -0,0 +1,14 @@
+/** @type {import("prettier").Options} */
+const config = {
+ trailingComma: "es5",
+ tabWidth: 4,
+ semi: false,
+ singleQuote: false,
+ quoteProps: "consistent",
+ jsxSingleQuote: false,
+ bracketSpacing: true,
+ bracketSameLine: false,
+ arrowParens: "avoid",
+}
+
+export default config;
diff --git a/fission/public/synthesis-logo.svg b/fission/public/synthesis-logo.svg
new file mode 100644
index 0000000000..6be8ba7d11
--- /dev/null
+++ b/fission/public/synthesis-logo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/fission/public/test_mira/Dozer_v2.mira b/fission/public/test_mira/Dozer_v2.mira
new file mode 100644
index 0000000000..294f081ff6
Binary files /dev/null and b/fission/public/test_mira/Dozer_v2.mira differ
diff --git a/fission/public/test_mira/FRC_Field_2018_v14.mira b/fission/public/test_mira/FRC_Field_2018_v14.mira
new file mode 100644
index 0000000000..49a68db3b5
Binary files /dev/null and b/fission/public/test_mira/FRC_Field_2018_v14.mira differ
diff --git a/fission/public/test_mira/PhysicsSpikeTest_v1.mira b/fission/public/test_mira/PhysicsSpikeTest_v1.mira
new file mode 100644
index 0000000000..1bb72d6f3f
Binary files /dev/null and b/fission/public/test_mira/PhysicsSpikeTest_v1.mira differ
diff --git a/fission/public/test_mira/Team_2471_(2018)_v7.mira b/fission/public/test_mira/Team_2471_(2018)_v7.mira
new file mode 100644
index 0000000000..d206af1321
Binary files /dev/null and b/fission/public/test_mira/Team_2471_(2018)_v7.mira differ
diff --git a/fission/public/TestCube_v1.mira b/fission/public/test_mira/TestCube_v1.mira
similarity index 100%
rename from fission/public/TestCube_v1.mira
rename to fission/public/test_mira/TestCube_v1.mira
diff --git a/fission/public/test_mira/TestCube_v3.mira b/fission/public/test_mira/TestCube_v3.mira
new file mode 100644
index 0000000000..13156ad112
Binary files /dev/null and b/fission/public/test_mira/TestCube_v3.mira differ
diff --git a/fission/public/vite.svg b/fission/public/vite.svg
deleted file mode 100644
index e7b8dfb1b2..0000000000
--- a/fission/public/vite.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/fission/src/App.tsx b/fission/src/App.tsx
index 6065990df7..8df75375fe 100644
--- a/fission/src/App.tsx
+++ b/fission/src/App.tsx
@@ -1,27 +1,9 @@
import './App.css';
-// import MyThree from './graphics/ThreeExample.tsx';
-import MyThree from './graphics/JoltExample.tsx'
-import { useEffect, useState } from 'react';
-
-// import { mirabuf as Mirabuf } from './proto/mirabuf.js';
-// import DetailsPanel from './components/Details.tsx';
+import MyThree from './samples/JoltExample.tsx';
function App() {
- const [joltLoaded, setJoltLoaded] = useState(false);
-
- useEffect(() => { (async () => {
- setJoltLoaded(true);
- })()}, []);
- if (joltLoaded) {
- return (
- < MyThree />
- );
- } else {
- return (
- {joltLoaded ? "Jolt has loaded!" : "Jolt is loading..."}
- )
- }
+ return ;
}
export default App;
diff --git a/fission/src/ModalContext.tsx b/fission/src/ModalContext.tsx
new file mode 100644
index 0000000000..2b0db95579
--- /dev/null
+++ b/fission/src/ModalContext.tsx
@@ -0,0 +1,124 @@
+import React, {
+ createContext,
+ useState,
+ useEffect,
+ useCallback,
+ useContext,
+ ReactNode,
+ ReactElement,
+} from "react"
+
+type ModalControlContextType = {
+ openModal: (
+ modalId: string,
+ onOpen?: () => void,
+ onClose?: () => void
+ ) => void
+ closeModal: () => void
+ children?: ReactNode
+}
+
+const ModalControlContext = createContext(null)
+
+export const useModalControlContext = () => {
+ const context = useContext(ModalControlContext)
+ if (!context)
+ throw new Error(
+ "useModalControlContext must be used within a ModalControlProvider"
+ )
+ return context
+}
+
+export const ModalControlProvider: React.FC = ({
+ children,
+ ...methods
+}) => {
+ return (
+
+ {children}
+
+ )
+}
+
+type ModalInstance = {
+ id: string
+ component: ReactElement
+ onOpen?: () => void
+ onClose?: () => void
+}
+
+export const useModalManager = (modals: ReactElement[]) => {
+ const [modalDictionary, setModalDictionary] = useState<{
+ [key: string]: ModalInstance
+ }>({})
+ const [activeModalId, setActiveModalId] = useState(null)
+
+ const openModal = useCallback(
+ (modalId: string, onOpen?: () => void, onClose?: () => void) => {
+ if (modalDictionary[modalId]) {
+ if (onOpen) {
+ modalDictionary[modalId].onOpen = onOpen
+ onOpen()
+ }
+ if (onClose) modalDictionary[modalId].onClose = onClose
+ }
+ setActiveModalId(modalId)
+ },
+ [modalDictionary]
+ )
+
+ const closeModal = useCallback(() => {
+ if (activeModalId) {
+ const inst = modalDictionary[activeModalId]
+ if (inst && inst.onClose) {
+ inst.onClose()
+ }
+ }
+ setActiveModalId(null)
+ }, [activeModalId, modalDictionary])
+
+ const registerModal = useCallback(
+ (modalId: string, modal: ModalInstance) => {
+ modalDictionary[modalId] = modal
+ },
+ [modalDictionary]
+ )
+
+ const unregisterModal = useCallback((modalId: string) => {
+ setModalDictionary(prevDictionary => {
+ const newDictionary = { ...prevDictionary }
+ delete newDictionary[modalId]
+ return newDictionary
+ })
+ }, [])
+
+ const getActiveModalElement = useCallback(() => {
+ if (activeModalId !== null) {
+ const modal = modalDictionary[activeModalId]
+ return modal ? modal.component : null
+ }
+ return null
+ }, [activeModalId, modalDictionary])
+
+ useEffect(() => {
+ modals.forEach(modalComponent => {
+ const id = modalComponent.props.modalId
+ registerModal(id, {
+ id: id,
+ component: modalComponent,
+ onOpen: () => {},
+ onClose: () => {},
+ })
+ })
+ }, [modals, closeModal, openModal, registerModal])
+
+ return {
+ modalDictionary,
+ activeModalId,
+ openModal,
+ closeModal,
+ registerModal,
+ unregisterModal,
+ getActiveModalElement,
+ }
+}
diff --git a/fission/src/PanelContext.tsx b/fission/src/PanelContext.tsx
new file mode 100644
index 0000000000..9e3a40eaad
--- /dev/null
+++ b/fission/src/PanelContext.tsx
@@ -0,0 +1,153 @@
+import React, {
+ createContext,
+ useState,
+ useEffect,
+ useCallback,
+ useContext,
+ ReactNode,
+ ReactElement,
+} from "react"
+
+type PanelControlContextType = {
+ openPanel: (panelId: string) => void
+ closePanel: (panelId: string) => void
+ children?: ReactNode
+}
+
+const PanelControlContext = createContext(null)
+
+export const usePanelControlContext = () => {
+ const context = useContext(PanelControlContext)
+ if (!context)
+ throw new Error(
+ "usePanelControlContext must be used within a PanelControlProvider"
+ )
+ return context
+}
+
+export const PanelControlProvider: React.FC = ({
+ children,
+ ...methods
+}) => {
+ return (
+
+ {children}
+
+ )
+}
+
+type PanelInstance = {
+ id: string
+ component: ReactElement
+ onOpen?: () => void
+ onClose?: () => void
+}
+
+export const usePanelManager = (panels: ReactElement[]) => {
+ const [panelDictionary, setPanelDictionary] = useState<{
+ [key: string]: PanelInstance
+ }>({})
+ const [activePanelIds, setActivePanelIds] = useState([])
+
+ const openPanel = useCallback(
+ (panelId: string, onOpen?: () => void, onClose?: () => void) => {
+ setActivePanelIds(prevPanelIds => {
+ if (prevPanelIds.includes(panelId)) return prevPanelIds
+ if (panelDictionary[panelId]) {
+ if (onOpen) {
+ panelDictionary[panelId].onOpen = onOpen
+ onOpen()
+ }
+ if (onClose) panelDictionary[panelId].onClose = onClose
+ }
+ return [...activePanelIds, panelId]
+ })
+ },
+ [activePanelIds, panelDictionary]
+ )
+
+ const closePanel = useCallback(
+ (panelId: string) => {
+ let inst = panelDictionary[panelId]
+ if (inst) {
+ if (inst.onClose) inst.onClose()
+ setActivePanelIds(activePanelIds.filter(i => i != panelId))
+ } else {
+ setActivePanelIds(
+ activePanelIds.filter(i => {
+ inst = panelDictionary[i]
+ if (inst.component.props.name == panelId) {
+ if (inst.onClose) inst.onClose()
+ return false
+ } else {
+ return true
+ }
+ })
+ )
+ }
+ },
+ [activePanelIds, panelDictionary]
+ )
+
+ const closeAllPanels = useCallback(() => {
+ if (activePanelIds.length > 0) {
+ activePanelIds.forEach(id => {
+ const inst = panelDictionary[id]
+ if (inst && inst.onClose) {
+ inst.onClose()
+ }
+ })
+ }
+ setActivePanelIds([])
+ }, [activePanelIds, panelDictionary])
+
+ const registerPanel = useCallback(
+ (panelId: string, panel: PanelInstance) => {
+ panelDictionary[panelId] = panel
+ },
+ [panelDictionary]
+ )
+
+ const unregisterPanel = useCallback((panelId: string) => {
+ setPanelDictionary(prevDictionary => {
+ const newDictionary = { ...prevDictionary }
+ delete newDictionary[panelId]
+ return newDictionary
+ })
+ }, [])
+
+ const getActivePanelElements = useCallback(() => {
+ if (activePanelIds !== null && activePanelIds.length > 0) {
+ return activePanelIds
+ .map((id: string) => {
+ const panel: PanelInstance = panelDictionary[id]
+ return panel ? panel.component : null
+ })
+ .filter(p => p != null)
+ }
+ return []
+ }, [activePanelIds, panelDictionary])
+
+ useEffect(() => {
+ panels.forEach(panelData => {
+ const id = panelData.props.panelId
+ registerPanel(id, {
+ id: id,
+ component: panelData,
+ onOpen: () => {},
+ onClose: () => {},
+ })
+ })
+ }, [panels, closePanel, openPanel, registerPanel])
+
+ return {
+ panelDictionary,
+ activePanelIds,
+ openPanel,
+ closePanel,
+ closeAllPanels,
+ registerPanel,
+ unregisterPanel,
+ getActivePanelElements,
+ }
+}
diff --git a/fission/src/Synthesis.tsx b/fission/src/Synthesis.tsx
new file mode 100644
index 0000000000..157f4a9871
--- /dev/null
+++ b/fission/src/Synthesis.tsx
@@ -0,0 +1,275 @@
+import Scene from './components/Scene.tsx';
+import GetSceneRenderer from './systems/scene/SceneRenderer.ts';
+import MirabufSceneObject from './mirabuf/MirabufSceneObject.ts';
+import { LoadMirabufRemote } from './mirabuf/MirabufLoader.ts';
+import { mirabuf } from './proto/mirabuf';
+import MirabufParser, { ParseErrorSeverity } from './mirabuf/MirabufParser.ts';
+import MirabufInstance from './mirabuf/MirabufInstance.ts';
+import { AnimatePresence, motion } from "framer-motion"
+import { ReactElement, useEffect } from "react"
+import { ModalControlProvider, useModalManager } from "./ModalContext"
+import { PanelControlProvider, usePanelManager } from "./PanelContext"
+import { useTheme } from "./ThemeContext"
+import { ToastContainer, ToastProvider } from "./ToastContext"
+import {
+ TOOLTIP_DURATION,
+ TooltipControl,
+ TooltipControlProvider,
+ TooltipType,
+ useTooltipManager,
+} from "./TooltipContext"
+import MainHUD from "./components/MainHUD"
+import DownloadAssetsModal from "./modals/DownloadAssetsModal"
+import ExitSynthesisModal from "./modals/ExitSynthesisModal"
+import MatchResultsModal from "./modals/MatchResultsModal"
+import SpawningModal from "./modals/SpawningModal"
+import UpdateAvailableModal from "./modals/UpdateAvailableModal"
+import ViewModal from "./modals/ViewModal"
+import ConnectToMultiplayerModal from "./modals/aether/ConnectToMultiplayerModal"
+import ServerHostingModal from "./modals/aether/ServerHostingModal"
+import ChangeInputsModal from "./modals/configuring/ChangeInputsModal"
+import ChooseMultiplayerModeModal from "./modals/configuring/ChooseMultiplayerModeModal"
+import ChooseSingleplayerModeModal from "./modals/configuring/ChooseSingleplayerModeModal"
+import ConfigMotorModal from "./modals/configuring/ConfigMotorModal"
+import DrivetrainModal from "./modals/configuring/DrivetrainModal"
+import PracticeSettingsModal from "./modals/configuring/PracticeSettingsModal"
+import RoboRIOModal from "./modals/configuring/RoboRIOModal"
+import SettingsModal from "./modals/configuring/SettingsModal"
+import RCConfigEncoderModal from "./modals/configuring/rio-config/RCConfigEncoderModal"
+import RCConfigPwmGroupModal from "./modals/configuring/rio-config/RCConfigPwmGroupModal"
+import RCCreateDeviceModal from "./modals/configuring/rio-config/RCCreateDeviceModal"
+import DeleteAllThemesModal from "./modals/configuring/theme-editor/DeleteAllThemesModal"
+import DeleteThemeModal from "./modals/configuring/theme-editor/DeleteThemeModal"
+import NewThemeModal from "./modals/configuring/theme-editor/NewThemeModal"
+import ThemeEditorModal from "./modals/configuring/theme-editor/ThemeEditorModal"
+import AddFieldModal from "./modals/spawning/AddFieldModal"
+import AddRobotModal from "./modals/spawning/AddRobotModal"
+import MatchModeModal from "./modals/spawning/MatchModeModal"
+import RobotSwitchPanel from "./panels/RobotSwitchPanel"
+import SpawnLocationsPanel from "./panels/SpawnLocationPanel"
+import ConfigureGamepiecePickupPanel from "./panels/configuring/ConfigureGamepiecePickupPanel"
+import ConfigureShotTrajectoryPanel from "./panels/configuring/ConfigureShotTrajectoryPanel"
+import ScoringZonesPanel from "./panels/configuring/scoring/ScoringZonesPanel"
+import ZoneConfigPanel from "./panels/configuring/scoring/ZoneConfigPanel"
+import ScoreboardPanel from "./panels/information/ScoreboardPanel"
+import DriverStationPanel from "./panels/simulation/DriverStationPanel"
+import ManageAssembliesModal from './modals/spawning/ManageAssembliesModal.tsx';
+
+const DEFAULT_MIRA_PATH = 'test_mira/Team_2471_(2018)_v7.mira';
+
+function Synthesis() {
+ const { openModal, closeModal, getActiveModalElement } =
+ useModalManager(initialModals)
+ const { openPanel, closePanel, closeAllPanels, getActivePanelElements } =
+ usePanelManager(initialPanels)
+ const { showTooltip } = useTooltipManager()
+
+ const { currentTheme, applyTheme } = useTheme()
+
+ useEffect(() => {
+ applyTheme(currentTheme)
+ }, [currentTheme, applyTheme])
+
+
+ const panelElements = getActivePanelElements()
+
+ const motionPanelElements = panelElements.map((el, i) => (
+
+ {el}
+
+ ))
+
+ const modalElement = getActiveModalElement()
+ const motionModalElement =
+ modalElement == null ? null : (
+
+ {getActiveModalElement()}
+
+ )
+
+
+
+ useEffect(() => {
+
+ let mira_path = DEFAULT_MIRA_PATH;
+
+ const urlParams = new URLSearchParams(document.location.search);
+
+ if (urlParams.has("mira")) {
+ mira_path = `test_mira/${urlParams.get("mira")!}`;
+ console.debug(`Selected Mirabuf File: ${mira_path}`);
+ }
+ console.log(urlParams)
+
+ const setup = async () => {
+ const miraAssembly = await LoadMirabufRemote(mira_path)
+ .catch(
+ _ => LoadMirabufRemote(DEFAULT_MIRA_PATH)
+ ).catch(console.error);
+
+ if (!miraAssembly || !(miraAssembly instanceof mirabuf.Assembly)) {
+ return;
+ }
+
+ const parser = new MirabufParser(miraAssembly);
+ if (parser.maxErrorSeverity >= ParseErrorSeverity.Unimportable) {
+ console.error(`Assembly Parser produced significant errors for '${miraAssembly.info!.name!}'`);
+ return;
+ }
+
+ const mirabufSceneObject = new MirabufSceneObject(new MirabufInstance(parser));
+ GetSceneRenderer().RegisterSceneObject(mirabufSceneObject);
+ };
+ setup();
+
+ let mainLoopHandle = 0;
+ const mainLoop = () => {
+ mainLoopHandle = requestAnimationFrame(mainLoop);
+
+ GetSceneRenderer().Update();
+ };
+ mainLoop();
+ // Cleanup
+ return () => {
+ // TODO: Teardown literally everything
+ cancelAnimationFrame(mainLoopHandle);
+ GetSceneRenderer().RemoveAllSceneObjects();
+ };
+ }, []);
+
+ return (
+
+ {
+ showTooltip(type, controls, duration)
+ }}
+ >
+ {
+ closeAllPanels()
+ openModal(modalId)
+ }}
+ closeModal={closeModal}
+ >
+ {
+ closePanel(id)
+ }}
+ >
+
+
+
+ {motionPanelElements.length > 0 &&
+ motionPanelElements}
+ {motionModalElement && motionModalElement}
+
+
+
+
+
+
+ )
+}
+
+const initialModals = [
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+]
+
+const initialPanels: ReactElement[] = [
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+]
+
+export default Synthesis;
diff --git a/fission/src/ThemeContext.tsx b/fission/src/ThemeContext.tsx
new file mode 100644
index 0000000000..49e2cea3c1
--- /dev/null
+++ b/fission/src/ThemeContext.tsx
@@ -0,0 +1,203 @@
+import React, { ReactNode, createContext, useContext, useState } from "react"
+import { RgbaColor } from "react-colorful"
+import { addGlobalFunc } from "./util/dom"
+
+export const defaultThemeName = "Default"
+export type ColorName =
+ | "InteractiveElementSolid"
+ | "InteractiveElementLeft"
+ | "InteractiveElementRight"
+ | "Background"
+ | "BackgroundSecondary"
+ | "InteractiveBackground"
+ | "BackgroundHUD"
+ | "InteractiveHover"
+ | "InteractiveSelect"
+ | "MainText"
+ | "Scrollbar"
+ | "AcceptButton"
+ | "CancelButton"
+ | "InteractiveElementText"
+ | "Icon"
+ | "MainHUDIcon"
+ | "MainHUDCloseIcon"
+ | "HighlightHover"
+ | "HighlightSelect"
+ | "SkyboxTop"
+ | "SkyboxBottom"
+ | "FloorGrid"
+ | "AcceptCancelButtonText"
+ | "MatchRedAlliance"
+ | "MatchBlueAlliance"
+ | "ToastInfo"
+ | "ToastWarning"
+ | "ToastError"
+
+export const colorNameToTailwind = (colorName: ColorName) => {
+ return "bg" +
+ colorName
+ .replace(/([A-Z]+)/g, "-$1")
+ .replace(/(?<=[A-Z])([A-Z])(?![A-Z]|$)/g, "-$1")
+ .toLowerCase()
+}
+export const colorNameToProp = (colorName: ColorName) => {
+ return (
+ "-" +
+ colorName
+ .replace(/([A-Z]+)/g, "-$1")
+ .replace(/(?<=[A-Z])([A-Z])(?![A-Z]|$)/g, "-$1")
+ .toLowerCase()
+ )
+}
+export type Theme = { [name in ColorName]: { color: RgbaColor, above: (ColorName | string)[] } }
+export type Themes = { [name: string]: Theme }
+
+type ThemeContextType = {
+ themes: Themes
+ initialThemeName: string
+ defaultTheme: Theme
+ currentTheme: string
+ setTheme: (themeName: string) => void
+ updateColor: (
+ themeName: string,
+ colorName: ColorName,
+ rgbaColor: RgbaColor
+ ) => void
+ createTheme: (themeName: string) => void
+ deleteTheme: (themeName: string) => void
+ deleteAllThemes: () => void
+ applyTheme: (themeName: string) => void
+}
+
+type ThemeProviderProps = {
+ themes: Themes
+ defaultTheme: Theme
+ initialThemeName: string
+ children: ReactNode
+}
+
+const ThemeContext = createContext(undefined)
+
+export const ThemeProvider: React.FC = ({
+ initialThemeName,
+ themes,
+ defaultTheme,
+ children,
+}) => {
+ const [currentTheme, setCurrentTheme] = useState(initialThemeName);
+
+ addGlobalFunc('getTheme', () => themes[currentTheme])
+
+ // potentially dumb algorithm
+ const findUnusedColor = () => {
+ if (process.env.NODE_ENV !== "production") return;
+ const MAX_VALUE = 255;
+ const sortFunc = (a: number, b: number) => a - b;
+ const themeValue: RgbaColor[] = Object.values(themes[currentTheme]).map(v => v.color);
+ const reds = themeValue.map((c) => c.r).sort(sortFunc);
+ const greens = themeValue.map((c) => c.g).sort(sortFunc);
+ const blues = themeValue.map((c) => c.b).sort(sortFunc);
+
+ const values = [];
+
+ for (const colorArr of [reds, greens, blues]) {
+ const color = Array.from(new Set(colorArr));
+ let lower = -1;
+ let largestGap = -1;
+
+ for (let i = 0; i < color.length - 1; i++) {
+ const col1 = color[i];
+ const col2 = color[i + 1];
+
+ if (col2 - col1 > largestGap) {
+ largestGap = col2 - col1;
+ lower = col1;
+ }
+ }
+
+ const col1 = color[color.length - 1];
+ const col2 = color[0] + MAX_VALUE;
+
+ if (col1 - col2 > largestGap) {
+ largestGap = col2 - col1;
+ lower = col1;
+ }
+
+ values.push((lower + largestGap / 2) % MAX_VALUE);
+ }
+
+ document.body.style.background = `rgba(${values[0]}, ${values[1]}, ${values[2]}, ${MAX_VALUE})`;
+ }
+
+ const updateColor = (
+ themeName: string,
+ colorName: ColorName,
+ rgbaColor: RgbaColor
+ ) => {
+ if (themes[themeName]) {
+ themes[themeName][colorName].color = rgbaColor
+ }
+ }
+
+ const createTheme = (themeName: string) => {
+ // deep copy
+ themes[themeName] = JSON.parse(JSON.stringify(defaultTheme))
+ }
+
+ const deleteTheme = (themeName: string) => {
+ if (themeName == defaultThemeName) return
+ if (themeName == currentTheme) {
+ setCurrentTheme(defaultThemeName)
+ }
+ delete themes[themeName]
+ }
+
+ const deleteAllThemes = () => {
+ for (const themeName of Object.keys(themes)) {
+ deleteTheme(themeName)
+ }
+ setCurrentTheme(defaultThemeName)
+ }
+
+ const applyTheme = (themeName: string) => {
+ const themeObject: Theme = themes[themeName]
+ if (!themeObject) return
+
+ const root = document.documentElement
+ Object.entries(themeObject).map(([n, c]) => {
+ const propName = colorNameToProp(n as ColorName)
+ root.style.setProperty(
+ propName,
+ `rgba(${c.color.r}, ${c.color.g}, ${c.color.b}, ${c.color.a})`
+ )
+ })
+ findUnusedColor();
+ }
+
+ return (
+
+ {children}
+
+ )
+}
+
+export const useTheme = () => {
+ const context = useContext(ThemeContext)
+ if (!context) {
+ throw new Error("useTheme must be used within a ThemeProvider!")
+ }
+ return context
+}
diff --git a/fission/src/ToastContext.tsx b/fission/src/ToastContext.tsx
new file mode 100644
index 0000000000..b8ca6f8039
--- /dev/null
+++ b/fission/src/ToastContext.tsx
@@ -0,0 +1,110 @@
+import React, {
+ createContext,
+ useState,
+ useContext,
+ useCallback,
+ ReactNode,
+} from "react"
+import Toast from "./components/Toast"
+import { AnimatePresence, motion } from "framer-motion"
+
+export type ToastType = "info" | "warning" | "error"
+
+export type ToastData = {
+ id: string
+ type: ToastType
+ title: string
+ description: string
+}
+
+type ToastContextType = {
+ toasts: ToastData[]
+ addToast: (type: ToastType, title: string, description: string) => void
+ removeToast: (toastId: string) => void
+}
+
+const ToastContext = createContext(null)
+
+export const useToastContext = () => {
+ const context = useContext(ToastContext)
+ if (!context)
+ throw new Error("useToastContext must be used within a ToastProvider")
+ return context
+}
+
+export const ToastProvider: React.FC<{ children: ReactNode }> = ({
+ children,
+}) => {
+ const [toasts, setToasts] = useState([])
+
+ const addToast = useCallback(
+ (type: ToastType, title: string, description: string) => {
+ // divide by 10 so that it's harder to have duplicates? could make smaller or remove
+ const id = "toast-" + Math.floor(Date.now() / 10).toString()
+ const newToast: ToastData = {
+ id,
+ type,
+ title,
+ description,
+ }
+ setToasts(prevToasts => {
+ if (prevToasts.some(t => t.id == id)) return prevToasts
+ return [...prevToasts, newToast]
+ })
+ },
+ []
+ )
+
+ const removeToast = useCallback((toastId: string) => {
+ setToasts(prevToasts => prevToasts.filter(t => t.id !== toastId))
+ }, [])
+
+ return (
+
+ {children}
+
+ )
+}
+
+export const ToastContainer: React.FC = () => {
+ const { toasts } = useToastContext()
+
+ return (
+
+
+ {toasts.length > 0 &&
+ toasts.map(t => (
+
+
+
+ ))}
+
+
+ )
+}
diff --git a/fission/src/TooltipContext.tsx b/fission/src/TooltipContext.tsx
new file mode 100644
index 0000000000..a3a78cebb0
--- /dev/null
+++ b/fission/src/TooltipContext.tsx
@@ -0,0 +1,111 @@
+import Tooltip from "@/components/Tooltip"
+import { AnimatePresence, motion } from "framer-motion"
+import React, {
+ ReactNode,
+ createContext,
+ useCallback,
+ useContext,
+ useState,
+} from "react"
+
+export type TooltipControl = { control: string; description: string }
+export type TooltipType = "controls"
+
+export const TOOLTIP_DURATION: number = 7_000
+
+type TooltipControlContextType = {
+ showTooltip: (
+ type: TooltipType,
+ controls?: TooltipControl[],
+ duration?: number
+ ) => void
+ children?: ReactNode
+}
+
+const TooltipControlContext = createContext(
+ null
+)
+
+export const useTooltipControlContext = () => {
+ const context = useContext(TooltipControlContext)
+ if (!context)
+ throw new Error(
+ "useTooltipControlContext must be used within a TooltipControlProvider"
+ )
+ return context
+}
+
+let tooltip: ReactNode
+
+export const TooltipControlProvider: React.FC = ({
+ children,
+ ...methods
+}) => {
+ return (
+
+
+ {tooltip && (
+
+ {tooltip}
+
+ )}
+ {children}
+
+
+ )
+}
+
+export const useTooltipManager = () => {
+ const [, setDuration] = useState(0)
+ const [timeout, setTimeoutState] = useState(null)
+
+ const showTooltip = useCallback(
+ (
+ type: TooltipType,
+ controls?: TooltipControl[],
+ duration: number = TOOLTIP_DURATION
+ ) => {
+ tooltip =
+ setDuration(duration)
+
+ if (timeout !== null) {
+ clearTimeout(timeout)
+ }
+
+ const newTimeout = setTimeout(() => {
+ tooltip = undefined
+ setDuration(0)
+ }, duration)
+ setTimeoutState(newTimeout)
+ },
+ [timeout]
+ )
+
+ return {
+ showTooltip,
+ }
+}
diff --git a/fission/src/assets/autodesk_logo.png b/fission/src/assets/autodesk_logo.png
new file mode 100644
index 0000000000..1b231757b9
Binary files /dev/null and b/fission/src/assets/autodesk_logo.png differ
diff --git a/fission/src/assets/react.svg b/fission/src/assets/react.svg
deleted file mode 100644
index 6c87de9bb3..0000000000
--- a/fission/src/assets/react.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/fission/src/components/Button.tsx b/fission/src/components/Button.tsx
new file mode 100644
index 0000000000..5e3e16971b
--- /dev/null
+++ b/fission/src/components/Button.tsx
@@ -0,0 +1,60 @@
+import React from "react"
+
+export enum ButtonSize {
+ Small,
+ Medium,
+ Large,
+ XL,
+}
+
+type ButtonProps = {
+ value: string
+ colorClass?: string
+ size?: ButtonSize
+ onClick?: () => void
+ className?: string
+}
+
+const Button: React.FC = ({
+ value,
+ colorClass,
+ size,
+ onClick,
+ className,
+}) => {
+ let sizeClassNames
+
+ if (!size) size = ButtonSize.Medium as ButtonSize
+
+ switch (size) {
+ case ButtonSize.Small:
+ sizeClassNames = "px-2 py-1"
+ break
+ case ButtonSize.Medium:
+ sizeClassNames = "px-4 py-1"
+ break
+ case ButtonSize.Large:
+ sizeClassNames = "px-8 py-2"
+ break
+ case ButtonSize.XL:
+ sizeClassNames = "px-10 py-2"
+ break
+ }
+
+ return (
+
+ )
+}
+
+export default Button
diff --git a/fission/src/components/Checkbox.tsx b/fission/src/components/Checkbox.tsx
new file mode 100644
index 0000000000..1d6ad3c6c4
--- /dev/null
+++ b/fission/src/components/Checkbox.tsx
@@ -0,0 +1,44 @@
+import React, { useState } from "react"
+import Stack, { StackDirection } from "./Stack"
+import Label, { LabelSize } from "./Label"
+
+type CheckboxProps = {
+ label: string
+ className?: string
+ defaultState: boolean
+ stateOverride?: boolean
+ onClick?: (checked: boolean) => void
+}
+
+const Checkbox: React.FC = ({
+ label,
+ className,
+ defaultState,
+ stateOverride,
+ onClick,
+}) => {
+ const [state, setState] = useState(defaultState)
+ return (
+
+
+ {
+ const checked = (e.target as HTMLInputElement).checked
+ setState(checked)
+ if (onClick) onClick(checked)
+ }}
+ className="bg-interactive-background translate-y-1/4 duration-200 cursor-pointer appearance-none w-5 h-5 rounded-full checked:bg-gradient-to-br checked:from-interactive-element-left checked:to-interactive-element-right"
+ checked={stateOverride != null ? stateOverride : undefined}
+ />
+
+ )
+}
+
+export default Checkbox
diff --git a/fission/src/components/Container.tsx b/fission/src/components/Container.tsx
new file mode 100644
index 0000000000..650418926c
--- /dev/null
+++ b/fission/src/components/Container.tsx
@@ -0,0 +1,12 @@
+import React, { ReactNode } from "react"
+
+type ContainerProps = {
+ children?: ReactNode
+ className?: string
+}
+
+const Container: React.FC = ({ className, children }) => {
+ return {children}
+}
+
+export default Container
diff --git a/fission/src/components/Details.css b/fission/src/components/Details.css
deleted file mode 100644
index 2e969b5153..0000000000
--- a/fission/src/components/Details.css
+++ /dev/null
@@ -1,38 +0,0 @@
-div#info {
- position: fixed;
- top: 10pt;
- right: 10pt;
- padding: 5pt;
-
- border: none;
- border-radius: 5pt;
-
- display: flex;
- flex-direction: column;
- justify-content: right;
- gap: 2pt;
-
- font-family: 'Ubuntu', sans-serif;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
-
-p {
- font-size: large;
- margin: 0pt;
- padding: 0pt;
- color: white;
-
- text-align: right;
-}
-
-button {
-
- font-family: 'Ubuntu', sans-serif;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-
- border: none;
- border-radius: 5pt;
- padding: 5pt;
-}
\ No newline at end of file
diff --git a/fission/src/components/Details.tsx b/fission/src/components/Details.tsx
deleted file mode 100644
index a6d74070db..0000000000
--- a/fission/src/components/Details.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import React from "react";
-import * as THREE_ADDONS from 'three-addons';
-import './Details.css'
-
-export default function DetailsPanel({ballCount, fps, toggleSpawning}) {
- return (
-
-
Ball Count: {ballCount}
-
{fps.toFixed(1)} FPS
-
-
- )
-}
\ No newline at end of file
diff --git a/fission/src/components/Dropdown.tsx b/fission/src/components/Dropdown.tsx
new file mode 100644
index 0000000000..23d8cebcfc
--- /dev/null
+++ b/fission/src/components/Dropdown.tsx
@@ -0,0 +1,73 @@
+import React, { ReactNode, useState } from "react"
+import { FaChevronDown, FaChevronUp } from "react-icons/fa"
+import Label, { LabelSize } from "./Label"
+
+type DropdownProps = {
+ children?: ReactNode
+ label?: string
+ className?: string
+ options: string[]
+ onSelect: (opt: string) => void
+}
+
+const Dropdown: React.FC = ({
+ label,
+ className,
+ options,
+ onSelect,
+}) => {
+ const [expanded, setExpanded] = useState(false)
+ const [optionList, setOptionList] = useState(options)
+
+ type DropdownOptionProps = {
+ value: string
+ children?: ReactNode
+ className?: string
+ }
+
+ const DropdownOption: React.FC = ({
+ children,
+ value,
+ className,
+ }) => (
+ {
+ const newOptions = options.filter(item => item !== value)
+ newOptions.unshift(value)
+ setOptionList(newOptions)
+ if (onSelect) onSelect(value)
+ }}
+ className={`block relative duration-100 hover:backdrop-brightness-90 w-full h-full px-2 py-2 ${className}`}
+ >
+ {children}
+
+ )
+
+ return (
+ <>
+ {label && }
+ setExpanded(!expanded)}
+ className={`relative flex flex-col gap-2 select-none cursor-pointer bg-gradient-to-r from-interactive-element-left to-interactive-element-right w-full rounded-md ${className}`}
+ >
+
+ {optionList[0]}
+ {optionList.length > 1 && (
+
+
+
+
+ )}
+
+ {expanded &&
+ optionList.slice(1).map(o => (
+
+ {o}
+
+ ))}
+
+ >
+ )
+}
+
+export default Dropdown
diff --git a/fission/src/components/Input.tsx b/fission/src/components/Input.tsx
new file mode 100644
index 0000000000..5bc7978345
--- /dev/null
+++ b/fission/src/components/Input.tsx
@@ -0,0 +1,54 @@
+import React from "react"
+import Label, { LabelSize } from "./Label"
+
+type InputProps = {
+ placeholder: string
+ value?: string
+ defaultValue?: string
+ numeric?: boolean
+ validate?: (s: string) => boolean
+ label?: string
+ onInput?: (value: string) => void
+ className?: string
+}
+
+const Input: React.FC = ({
+ placeholder,
+ value,
+ defaultValue,
+ numeric,
+ validate,
+ label,
+ onInput,
+ className,
+}) => {
+ return (
+ <>
+ {label && }
+ {
+ if (
+ e.key != null &&
+ numeric &&
+ !"1234567890,.".includes(e.key)
+ ) {
+ e.preventDefault()
+ }
+
+ if (validate && !validate(e.key)) e.preventDefault()
+ }}
+ onChange={e => {
+ if (onInput) onInput(e.target.value)
+ }}
+ className={`bg-interactive-background px-2 py-1 bg-[length:200%_100%] w-min rounded-md font-semibold cursor-pointer placeholder:italic ${
+ className || ""
+ }`}
+ />
+ >
+ )
+}
+
+export default Input
diff --git a/fission/src/components/Label.tsx b/fission/src/components/Label.tsx
new file mode 100644
index 0000000000..e8269a98f9
--- /dev/null
+++ b/fission/src/components/Label.tsx
@@ -0,0 +1,41 @@
+import React, { ReactNode } from "react"
+
+export enum LabelSize {
+ Small,
+ Medium,
+ Large,
+ XL,
+}
+
+const labelSizeToClassName = (size?: LabelSize) => {
+ switch (size) {
+ case LabelSize.Small:
+ return "text-sm"
+ case LabelSize.Medium:
+ return "text-xl"
+ case LabelSize.Large:
+ return "text-2xl"
+ case LabelSize.XL:
+ return "text-4xl"
+ default:
+ return "text-base"
+ }
+}
+
+type LabelProps = {
+ size?: LabelSize
+ children?: ReactNode
+ className?: string
+}
+
+const Label: React.FC = ({ children, size, className }) => (
+
+ {children}
+
+)
+
+export default Label
diff --git a/fission/src/components/LabeledButton.tsx b/fission/src/components/LabeledButton.tsx
new file mode 100644
index 0000000000..3840309dc8
--- /dev/null
+++ b/fission/src/components/LabeledButton.tsx
@@ -0,0 +1,71 @@
+import React from "react"
+import Button from "./Button"
+import Label, { LabelSize } from "./Label"
+import Stack, { StackDirection } from "./Stack"
+
+export enum LabelPlacement {
+ Left,
+ Right,
+ Top,
+ Bottom,
+}
+
+type LabeledButtonProps = {
+ label: string
+ value: string
+ placement: LabelPlacement
+ labelSize?: LabelSize
+ onClick?: () => void
+ labelClassName?: string
+ buttonClassName?: string
+}
+
+const LabeledButton: React.FC = ({
+ label,
+ value,
+ placement,
+ labelSize,
+ onClick,
+ labelClassName,
+ buttonClassName,
+}) => {
+ const buttonComponent = (
+
+ )
+ const labelComponent = (
+
+ )
+
+ const labelBefore =
+ placement == LabelPlacement.Left || placement == LabelPlacement.Top
+ const isHorizontal =
+ placement == LabelPlacement.Left || placement == LabelPlacement.Right
+
+ return (
+
+ {labelBefore && [labelComponent, buttonComponent]}
+ {!labelBefore && [buttonComponent, labelComponent]}
+
+ )
+}
+
+export default LabeledButton
diff --git a/fission/src/components/MainHUD.tsx b/fission/src/components/MainHUD.tsx
new file mode 100644
index 0000000000..c31f06b3e5
--- /dev/null
+++ b/fission/src/components/MainHUD.tsx
@@ -0,0 +1,180 @@
+import React, { useState } from "react"
+import { BsCodeSquare } from "react-icons/bs"
+import { FaCar, FaGear, FaHouse, FaMagnifyingGlass, FaPlus } from "react-icons/fa6"
+import { BiMenuAltLeft } from "react-icons/bi"
+import { GrFormClose } from "react-icons/gr"
+import { GiSteeringWheel } from "react-icons/gi"
+import { HiDownload } from "react-icons/hi"
+import { IoGameControllerOutline, IoPeople } from "react-icons/io5"
+import { useModalControlContext } from "../ModalContext"
+import { usePanelControlContext } from "../PanelContext"
+import { motion } from "framer-motion"
+import logo from "../assets/autodesk_logo.png"
+import { ToastType, useToastContext } from "../ToastContext"
+import { Random } from "@/util/Random"
+
+type ButtonProps = {
+ value: string
+ icon: React.ReactNode
+ onClick?: () => void
+ larger?: boolean
+}
+
+const MainHUDButton: React.FC = ({
+ value,
+ icon,
+ onClick,
+ larger,
+}) => {
+ if (larger == null) larger = false
+ return (
+
+ {larger && icon}
+ {!larger && (
+
+ {icon}
+
+ )}
+
+
+ )
+}
+
+const variants = {
+ open: { opacity: 1, y: "-50%", x: 0 },
+ closed: { opacity: 0, y: "-50%", x: "-100%" },
+}
+
+const MainHUD: React.FC = () => {
+
+ console.debug('Creating MainHUD');
+
+ const { openModal } = useModalControlContext()
+ const { openPanel } = usePanelControlContext()
+ const { addToast } = useToastContext()
+ const [isOpen, setIsOpen] = useState(false)
+
+ return (
+ <>
+ {!isOpen && (
+
+ )}
+
+
+
+
+
+ }
+ larger={true}
+ onClick={() => openModal("spawning")}
+ />
+
+ }
+ onClick={() => openModal("manage-assembles")}
+ />
+ }
+ onClick={() => openModal("settings")}
+ />
+ }
+ onClick={() => openModal("view")}
+ />
+ }
+ onClick={() => openModal("change-inputs")}
+ />
+ }
+ onClick={() => openPanel("multibot")}
+ />
+
+
+ }
+ onClick={() => openModal("download-assets")}
+ />
+ }
+ onClick={() => openModal("roborio")}
+ />
+ }
+ onClick={() => openPanel("driver-station")}
+ />
+ }
+ onClick={() => openModal("drivetrain")}
+ />
+ }
+ onClick={() => {
+ const type: ToastType = [
+ "info",
+ "warning",
+ "error",
+ ][Math.floor(Random() * 3)] as ToastType
+ addToast(
+ type,
+ type,
+ "This is a test toast to test the toast system"
+ )
+ }}
+ />
+
+ }
+ larger={true}
+ onClick={() => openModal("spawning")}
+ />
+
+ >
+ )
+}
+
+export default MainHUD
diff --git a/fission/src/components/Modal.tsx b/fission/src/components/Modal.tsx
new file mode 100644
index 0000000000..ba30a157a3
--- /dev/null
+++ b/fission/src/components/Modal.tsx
@@ -0,0 +1,129 @@
+import React, { ReactNode } from "react"
+import { useModalControlContext } from "../ModalContext"
+
+export type ModalPropsImpl = {
+ modalId: string
+}
+
+type ModalProps = {
+ name: string
+ modalId: string
+ icon?: ReactNode | string
+ onCancel?: () => void
+ onMiddle?: () => void
+ onAccept?: () => void
+ cancelName?: string
+ middleName?: string
+ acceptName?: string
+ cancelEnabled?: boolean
+ middleEnabled?: boolean
+ acceptEnabled?: boolean
+ cancelBlocked?: boolean
+ middleBlocked?: boolean
+ acceptBlocked?: boolean
+ children?: ReactNode
+ className?: string
+ contentClassName?: string
+}
+
+const Modal: React.FC = ({
+ children,
+ name,
+ icon,
+ onCancel,
+ onMiddle,
+ onAccept,
+ cancelName,
+ middleName,
+ acceptName,
+ cancelEnabled = true,
+ middleEnabled = false,
+ acceptEnabled = true,
+ cancelBlocked = false,
+ middleBlocked = false,
+ acceptBlocked = false,
+ className,
+ contentClassName,
+}) => {
+ const { closeModal } = useModalControlContext()
+
+ const iconEl: ReactNode =
+ typeof icon === "string" ? (
+
+ ) : (
+ icon
+ )
+
+ return (
+
+ {name && (
+
+ )}
+
+ {children}
+
+
+
+ )
+}
+
+export default Modal
diff --git a/fission/src/components/Panel.tsx b/fission/src/components/Panel.tsx
new file mode 100644
index 0000000000..7605e21942
--- /dev/null
+++ b/fission/src/components/Panel.tsx
@@ -0,0 +1,134 @@
+import React, { ReactNode } from "react"
+import { usePanelControlContext } from "../PanelContext"
+
+export type PanelPropsImpl = {
+ panelId: string
+}
+
+type PanelProps = {
+ panelId: string
+ name?: string
+ icon?: ReactNode | string
+ onCancel?: () => void
+ onMiddle?: () => void
+ onAccept?: () => void
+ cancelName?: string
+ middleName?: string
+ acceptName?: string
+ cancelEnabled?: boolean
+ middleEnabled?: boolean
+ acceptEnabled?: boolean
+ cancelBlocked?: boolean
+ middleBlocked?: boolean
+ acceptBlocked?: boolean
+ children?: ReactNode
+ className?: string
+ contentClassName?: string
+}
+
+const Panel: React.FC = ({
+ children,
+ name,
+ icon,
+ panelId,
+ onCancel,
+ onMiddle,
+ onAccept,
+ cancelName,
+ middleName,
+ acceptName,
+ cancelEnabled = true,
+ middleEnabled = false,
+ acceptEnabled = true,
+ cancelBlocked = false,
+ middleBlocked = false,
+ acceptBlocked = false,
+ className,
+ contentClassName,
+}) => {
+ const { closePanel } = usePanelControlContext()
+ const iconEl: ReactNode =
+ typeof icon === "string" ? (
+
+ ) : (
+ icon
+ )
+ return (
+
+ {name && (
+
+ )}
+
+ {children}
+
+ {(cancelEnabled || middleEnabled || acceptEnabled) && (
+
+ )}
+
+ )
+}
+
+export default Panel
diff --git a/fission/src/components/Radio.tsx b/fission/src/components/Radio.tsx
new file mode 100644
index 0000000000..fd23c43c24
--- /dev/null
+++ b/fission/src/components/Radio.tsx
@@ -0,0 +1,39 @@
+import React, { useState } from "react"
+import Stack, { StackDirection } from "./Stack"
+import Label, { LabelSize } from "./Label"
+
+type RadioProps = {
+ label: string
+ className?: string
+ defaultState: boolean
+ onClick?: () => void
+}
+
+const Radio: React.FC = ({
+ label,
+ className,
+ defaultState,
+ onClick,
+}) => {
+ const [, setState] = useState(defaultState)
+ return (
+
+
+ {
+ setState((e.target as HTMLInputElement).checked)
+ if (onClick) onClick()
+ }}
+ className="bg-interactive-background translate-y-1/4 duration-200 cursor-pointer appearance-none w-5 h-5 rounded-full checked:bg-gradient-to-br checked:from-interactive-element-left checked:to-interactive-element-right"
+ />
+
+ )
+}
+
+export default Radio
diff --git a/fission/src/components/Scene.css b/fission/src/components/Scene.css
new file mode 100644
index 0000000000..b08821d03b
--- /dev/null
+++ b/fission/src/components/Scene.css
@@ -0,0 +1,10 @@
+canvas {
+ position: fixed;
+ top: 0pt;
+ bottom: 0pt;
+ left: 0pt;
+ right: 0pt;
+
+ width: 100%;
+ height: 100%;
+}
\ No newline at end of file
diff --git a/fission/src/components/Scene.tsx b/fission/src/components/Scene.tsx
new file mode 100644
index 0000000000..6d83f803e1
--- /dev/null
+++ b/fission/src/components/Scene.tsx
@@ -0,0 +1,65 @@
+import './Scene.css';
+import { useEffect, useRef } from "react";
+import GetSceneRenderer from "../systems/scene/SceneRenderer";
+import Stats from 'stats.js';
+import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
+import SceneObject from "../systems/scene/SceneObject";
+
+let stats: Stats | null;
+
+let controls: OrbitControls;
+
+class SceneProps {
+ public useStats = false;
+}
+
+function Scene({ useStats }: SceneProps) {
+ const refContainer = useRef(null);
+
+ useEffect(() => {
+ if (refContainer.current) {
+ console.debug('Adding ThreeJs to DOM');
+
+ const sr = GetSceneRenderer();
+ sr.renderer.domElement.style.width = '100%';
+ sr.renderer.domElement.style.height = '100%';
+
+ refContainer.current.innerHTML = "";
+ refContainer.current.appendChild(sr.renderer.domElement)
+ window.addEventListener('resize', () => {
+ sr.UpdateCanvasSize();
+ });
+
+ if (useStats && !stats) {
+ console.log('Adding stat');
+ stats = new Stats();
+ stats.dom.style.position = 'absolute';
+ stats.dom.style.top = '0px';
+ refContainer.current.appendChild(stats.dom);
+ }
+
+ controls = new OrbitControls(sr.mainCamera, sr.renderer.domElement);
+ controls.update();
+
+ // Bit hacky but works
+ class ComponentSceneObject extends SceneObject {
+ public Setup(): void { }
+ public Update(): void {
+ stats?.update();
+ controls?.update();
+ }
+ public Dispose(): void { }
+ }
+ const cso = new ComponentSceneObject();
+ sr.RegisterSceneObject(cso);
+ }
+ }, [useStats]);
+
+ return (
+
+ );
+}
+
+export default Scene;
\ No newline at end of file
diff --git a/fission/src/components/ScrollView.tsx b/fission/src/components/ScrollView.tsx
new file mode 100644
index 0000000000..aef29f4df6
--- /dev/null
+++ b/fission/src/components/ScrollView.tsx
@@ -0,0 +1,19 @@
+import React, { ReactNode } from "react"
+
+type ScrollViewProps = {
+ children?: ReactNode
+ className?: string
+ maxHeight?: string
+}
+
+const ScrollView: React.FC = ({ className, maxHeight, children }) => {
+ return (
+
+ {children}
+
+ )
+}
+
+export default ScrollView
diff --git a/fission/src/components/SelectButton.tsx b/fission/src/components/SelectButton.tsx
new file mode 100644
index 0000000000..1bce63c3d5
--- /dev/null
+++ b/fission/src/components/SelectButton.tsx
@@ -0,0 +1,80 @@
+import React, { useCallback, useEffect, useRef, useState } from "react"
+import Button, { ButtonSize } from "./Button"
+import Label, { LabelSize } from "./Label"
+import Stack, { StackDirection } from "./Stack"
+import { Random } from '@/util/Random';
+
+type SelectButtonProps = {
+ colorClass?: string
+ size?: ButtonSize
+ placeholder?: string
+ onSelect?: (value: string) => void
+ className?: string
+}
+
+const SelectButton: React.FC = ({
+ colorClass,
+ size,
+ placeholder,
+ onSelect,
+ className,
+}) => {
+ const [value, setValue] = useState()
+ const [selecting, setSelecting] = useState(false)
+ const timeoutRef = useRef()
+
+ const onReceiveSelection = useCallback(
+ (value: string) => {
+ // TODO remove this when communication works
+ clearTimeout(timeoutRef.current)
+ setValue(value)
+ setSelecting(false)
+ if (onSelect) onSelect(value)
+ },
+ [setValue, setSelecting, onSelect]
+ )
+
+ useEffect(() => {
+ // simulate receiving a selection from Synthesis
+ if (selecting) {
+ timeoutRef.current = setTimeout(
+ () => {
+ if (selecting) {
+ const v = `node_${Math.floor(
+ Random() * 10
+ ).toFixed(0)}`
+ onReceiveSelection(v)
+ }
+ },
+ Math.floor(Random() * 2_750) + 250
+ )
+ }
+ }, [selecting, onReceiveSelection])
+
+ // should send selecting state when clicked and then receive string value to set selecting to false
+
+ return (
+
+
+
+ )
+}
+
+export default SelectButton
diff --git a/fission/src/components/Slider.tsx b/fission/src/components/Slider.tsx
new file mode 100644
index 0000000000..c207fb623c
--- /dev/null
+++ b/fission/src/components/Slider.tsx
@@ -0,0 +1,110 @@
+import React, { SyntheticEvent, useRef, useState } from "react"
+
+type CustomFormatOptions = {
+ prefix?: string
+ suffix?: string
+}
+
+type SliderProps = {
+ label?: string
+ min: number
+ max: number
+ defaultValue: number
+ onChange?: (v: number) => void
+ step?: number
+ locale?: string
+ format?: Intl.NumberFormatOptions & CustomFormatOptions
+}
+
+const Slider: React.FC = ({
+ label,
+ min,
+ max,
+ defaultValue,
+ onChange,
+ step,
+ locale,
+ format,
+}) => {
+ const containerRef = useRef(null)
+ const [value, setValue] = useState(defaultValue)
+ const [mouseDown, setMouseDown] = useState(false)
+
+ // non-inclusive top
+ // max += 1
+
+ if (!locale) {
+ locale = "en-us"
+ }
+
+ if (!format) {
+ format = {
+ maximumFractionDigits: 0,
+ prefix: "",
+ suffix: "",
+ }
+ }
+ if (!format.prefix) {
+ format.prefix = ""
+ }
+ if (!format.suffix) {
+ format.suffix = ""
+ }
+
+ const getPercent = () => ((value - min) / (max - min)) * 100
+
+ const onMouseMove = (e: SyntheticEvent) => {
+ if (mouseDown) {
+ const layerX = (e.nativeEvent as MouseEvent).offsetX
+ const w = containerRef.current!.offsetWidth
+ let percent = layerX / w
+ if (step) {
+ const diff = percent % step
+ if (diff < step / 2) percent -= diff
+ else percent += step - diff
+ }
+ const v = percent * (max - min) + min
+ if (onChange) onChange(v)
+ setValue(v)
+ }
+ }
+
+ // TODO thumb is hidden
+ return (
+
+
+
{label}
+
+ {format.prefix +
+ value.toLocaleString(locale, format) +
+ format.suffix}
+
+
+
onMouseMove(ev)}
+ onMouseDown={() => setMouseDown(true)}
+ onMouseUp={() => setMouseDown(false)}
+ className="relative w-full h-4 max-w-full cursor-pointer"
+ >
+
+
+
+
+
+ )
+}
+
+export default Slider
diff --git a/fission/src/components/Stack.tsx b/fission/src/components/Stack.tsx
new file mode 100644
index 0000000000..79a2f11285
--- /dev/null
+++ b/fission/src/components/Stack.tsx
@@ -0,0 +1,85 @@
+import React, { ReactNode } from "react"
+
+export enum StackDirection {
+ Horizontal,
+ Vertical,
+}
+
+type StackProps = {
+ children?: ReactNode
+ className?: string
+ direction: StackDirection
+ spacing?: number
+ justify?:
+ | "normal"
+ | "start"
+ | "end"
+ | "center"
+ | "between"
+ | "around"
+ | "evenly"
+ | "stretch"
+ align?:
+ | "normal"
+ | "center"
+ | "start"
+ | "end"
+ | "between"
+ | "around"
+ | "evenly"
+ | "baseline"
+ | "stretch"
+}
+
+const Stack: React.FC = ({
+ className,
+ children,
+ direction,
+ spacing,
+ justify,
+ align,
+}) => {
+ const directionClassName =
+ direction == StackDirection.Horizontal ? "flex-row" : "flex-col"
+ if (!justify) justify = "between"
+ if (!align) align = "center"
+ if (spacing == null) spacing = 10
+
+ return (
+
+ {" "}
+ {children}
+
+ )
+}
+
+export default Stack
diff --git a/fission/src/components/Toast.tsx b/fission/src/components/Toast.tsx
new file mode 100644
index 0000000000..71308ed561
--- /dev/null
+++ b/fission/src/components/Toast.tsx
@@ -0,0 +1,79 @@
+import React, { ReactElement, useEffect } from "react"
+import { ToastData, useToastContext } from "../ToastContext"
+import { GrFormClose } from "react-icons/gr"
+import { BsFillWrenchAdjustableCircleFill } from "react-icons/bs"
+import { AiFillWarning } from "react-icons/ai"
+import { BiSolidErrorCircle } from "react-icons/bi"
+
+const TOAST_TIMEOUT: number = 5_000
+
+const Toast: React.FC = ({ id, type, title, description }) => {
+ const { removeToast } = useToastContext()
+
+ useEffect(() => {
+ const timer = setTimeout(() => {
+ removeToast(id)
+ }, TOAST_TIMEOUT)
+
+ return () => clearTimeout(timer)
+ }, [id, removeToast])
+
+ const handleClose = () => removeToast(id)
+
+ let icon: ReactElement
+ let className: string
+
+ switch (type) {
+ case "info":
+ icon = (
+
+ )
+ className = "bg-toast-info"
+ break
+ case "warning":
+ icon = (
+
+ )
+ className = "bg-toast-warning"
+ break
+ case "error":
+ icon = (
+
+ )
+ className = "bg-toast-error"
+ break
+ }
+
+ return (
+
+
+
{icon}
+
+
{title}
+
{description}
+
+
+ )
+}
+
+export default Toast
diff --git a/fission/src/components/Tooltip.tsx b/fission/src/components/Tooltip.tsx
new file mode 100644
index 0000000000..fa3146a585
--- /dev/null
+++ b/fission/src/components/Tooltip.tsx
@@ -0,0 +1,45 @@
+import { TooltipControl, TooltipType } from "@/TooltipContext"
+import { FaInfoCircle } from "react-icons/fa"
+import Label, { LabelSize } from "./Label"
+import Stack, { StackDirection } from "./Stack"
+
+type TooltipProps = {
+ type: TooltipType
+ controls?: TooltipControl[]
+}
+
+const Tooltip: React.FC = ({ type, controls }) => {
+ if (type === "controls") {
+ return (
+
+
+ {controls?.map(c => (
+
+
+
+
+ ))}
+
+ )
+ } else {
+ return <>>
+ }
+}
+
+export default Tooltip
diff --git a/fission/src/graphics/JoltExample.tsx b/fission/src/graphics/JoltExample.tsx
deleted file mode 100644
index b3da35a477..0000000000
--- a/fission/src/graphics/JoltExample.tsx
+++ /dev/null
@@ -1,372 +0,0 @@
-/**
- * This example will be used to showcase how Jolt physics works.
- */
-
-/* eslint-disable @typescript-eslint/no-explicit-any */
-
-import * as THREE from 'three';
-import Stats from 'stats.js';
-import JOLT from '../util/loading/JoltSyncLoader.ts';
-
-import { useEffect, useRef } from 'react';
-import React from 'react';
-import { Random } from '../util/Random.ts';
-
-const clock = new THREE.Clock();
-let time = 0;
-
-let stats: any;
-
-let renderer: any;
-let camera: any;
-let scene: any;
-
-let joltInterface: any;
-let physicsSystem: any;
-let bodyInterface: any;
-
-const dynamicObjects: any[] = [];
-
-const LAYER_NOT_MOVING = 0;
-const LAYER_MOVING = 1;
-const COUNT_OBJECT_LAYERS = 2;
-
-const wrapVec3 = (v) => new THREE.Vector3(v.GetX(), v.GetY(), v.GetZ());
-const wrapQuat = (q) => new THREE.Quaternion(q.GetX(), q.GetY(), q.GetZ(), q.GetW());
-
-
-// vvv Below are the functions required to initalize everything and draw a basic floor with collisions. vvv
-
-function setupCollisionFiltering(settings) {
- let objectFilter = new JOLT.ObjectLayerPairFilterTable(COUNT_OBJECT_LAYERS);
- objectFilter.EnableCollision(LAYER_NOT_MOVING, LAYER_MOVING);
- objectFilter.EnableCollision(LAYER_MOVING, LAYER_MOVING);
-
- const BP_LAYER_NOT_MOVING = new JOLT.BroadPhaseLayer(LAYER_NOT_MOVING);
- const BP_LAYER_MOVING = new JOLT.BroadPhaseLayer(LAYER_MOVING);
- const COUNT_BROAD_PHASE_LAYERS = 2;
-
- let bpInterface = new JOLT.BroadPhaseLayerInterfaceTable(COUNT_OBJECT_LAYERS, COUNT_BROAD_PHASE_LAYERS);
- bpInterface.MapObjectToBroadPhaseLayer(LAYER_NOT_MOVING, BP_LAYER_NOT_MOVING);
- bpInterface.MapObjectToBroadPhaseLayer(LAYER_MOVING, BP_LAYER_MOVING);
-
- settings.mObjectLayerPairFilter = objectFilter;
- settings.mBroadPhaseLayerInterface = bpInterface;
- settings.mObjectVsBroadPhaseLayerFilter = new JOLT.ObjectVsBroadPhaseLayerFilterTable(settings.mBroadPhaseLayerInterface, COUNT_BROAD_PHASE_LAYERS, settings.mObjectLayerPairFilter, COUNT_OBJECT_LAYERS);
-}
-
-function initPhysics() {
- let settings = new JOLT.JoltSettings();
- setupCollisionFiltering(settings);
- joltInterface = new JOLT.JoltInterface(settings);
- JOLT.destroy(settings);
-
- physicsSystem = joltInterface.GetPhysicsSystem();
- bodyInterface = physicsSystem.GetBodyInterface();
-}
-
-function initGraphics() {
- renderer = new THREE.WebGLRenderer();
- renderer.setClearColor(0xbfd1e5);
- renderer.setPixelRatio(window.devicePixelRatio);
- renderer.setSize(window.innerWidth, window.innerHeight);
-
- camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.2, 2000);
- camera.position.set(-5, 4, 5);
- camera.lookAt(new THREE.Vector3(0, 0.5, 0));
-
- scene = new THREE.Scene();
-
- let directionalLight = new THREE.DirectionalLight(0xffffff, 2);
- directionalLight.position.set(10, 10, 5);
- scene.add(directionalLight);
-
- let ambientLight = new THREE.AmbientLight(0xffffff, 0.1);
- scene.add(ambientLight);
-
- // TODO: Add controls.
-
- // TODO: Add resize event
-}
-
-function createMeshForShape(shape) {
- let scale = new JOLT.Vec3(1, 1, 1);
- let triangleContext = new JOLT.ShapeGetTriangles(shape, JOLT.AABox.prototype.sBiggest(), shape.GetCenterOfMass(), JOLT.Quat.prototype.sIdentity(), scale);
- JOLT.destroy(scale);
-
- let vertices = new Float32Array(JOLT.HEAP32.buffer, triangleContext.GetVerticesData(), triangleContext.GetVerticesSize() / Float32Array.BYTES_PER_ELEMENT);
- let buffer = new THREE.BufferAttribute(vertices, 3).clone();
- JOLT.destroy(triangleContext);
-
- let geometry = new THREE.BufferGeometry();
- geometry.setAttribute('position', buffer);
- geometry.computeVertexNormals();
-
- return geometry;
-}
-
-function getThreeObjForBody(body, color) {
- let material = new THREE.MeshPhongMaterial({ color: color });
- let threeObj;
- let shape = body.GetShape();
-
- switch (shape.GetSubType()) {
- case JOLT.EShapeSubType_Box:
- let boxShape = JOLT.castObject(shape, JOLT.BoxShape);
- let extent = wrapVec3(boxShape.GetHalfExtent()).multiplyScalar(2);
- threeObj = new THREE.Mesh(new THREE.BoxGeometry(extent.x, extent.y, extent.z, 1, 1, 1), material);
- break;
- case JOLT.EShapeSubType_Capsule:
- // TODO
- case JOLT.EShapeSubType_Cylinder:
- // TODO
- case JOLT.EShapeSubType_Sphere:
- // TODO
- default:
- threeObj = new THREE.Mesh(createMeshForShape(shape), material);
- break;
- }
-
- threeObj.position.copy(wrapVec3(body.GetPosition()));
- threeObj.quaternion.copy(wrapQuat(body.GetRotation()));
-
- return threeObj;
-}
-
-function addToThreeScene(body, color) {
- let threeObj = getThreeObjForBody(body, color);
- threeObj.userData.body = body;
- scene.add(threeObj);
- dynamicObjects.push(threeObj);
-}
-
-function addToScene(body, color) {
- bodyInterface.AddBody(body.GetID(), JOLT.EActivation_Activate);
- addToThreeScene(body, color);
-}
-
-function removeFromScene(threeObject) {
- let id = threeObject.userData.body.GetID();
- bodyInterface.RemoveBody(id);
- bodyInterface.DestroyBody(id);
- delete threeObject.userData.body;
-
- scene.remove(threeObject);
- let idx = dynamicObjects.indexOf(threeObject);
- dynamicObjects.splice(idx, 1);
-}
-
-function createFloor(size = 50) {
- let shape = new JOLT.BoxShape(new JOLT.Vec3(size, 0.5, size), 0.05, undefined);
- let position = new JOLT.Vec3(0, -0.5, 0);
- let rotation = new JOLT.Quat(0, 0, 0, 1);
- let creationSettings = new JOLT.BodyCreationSettings(shape, position, rotation, JOLT.EMotionType_Static, LAYER_NOT_MOVING)
- let body = bodyInterface.CreateBody(creationSettings);
- JOLT.destroy(position);
- JOLT.destroy(rotation);
- JOLT.destroy(creationSettings);
- addToScene(body, 0xc7c7c7);
-
- return body;
-}
-
-function updatePhysics(deltaTime) {
- // If below 55hz run 2 steps. Otherwise things run very slow.
- let numSteps = deltaTime > 1.0 / 55.0 ? 2 : 1;
- joltInterface.Step(deltaTime, numSteps);
-}
-
-function render() {
- stats.update();
- requestAnimationFrame(render);
-
- // Prevents a problem when rendering at 30hz. Referred to as the spiral of death.
- let deltaTime = clock.getDelta();
- deltaTime = Math.min(deltaTime, 1.0 / 30.0);
-
- // Update transforms.
- for (let i = 0, j = dynamicObjects.length; i < j; i++) {
- let threeObj = dynamicObjects[i];
- let body = threeObj.userData.body;
- threeObj.position.copy(wrapVec3(body.GetPosition()));
- threeObj.quaternion.copy(wrapQuat(body.GetRotation()));
-
- if (body.GetBodyType() === JOLT.EBodyType_SoftBody) {
- // TODO: Special soft body handle.
- }
- }
-
- onTestUpdate(time, deltaTime);
-
- time += deltaTime;
- updatePhysics(1.0 / 60.0);
- // controls.update(deltaTime); // TODO: Add controls?
- renderer.render(scene, camera);
-}
-
-// vvv The following are test functions used to do various basic things. vvv
-
-const timePerObject = 0.05;
-let timeNextSpawn = time + timePerObject;
-
-// Swap the onTestUpdate function to run the performance test with the random cubes.
-// const onTestUpdate = (time, deltaTime) => spawnRandomCubes(time, deltaTime);
-const onTestUpdate = (time, deltaTime) => {};
-
-function spikeTestScene() {
- let boxShape = new JOLT.BoxShape(new JOLT.Vec3(0.5, 0.5, 0.5), 0.1, undefined);
- let boxCreationSettings = new JOLT.BodyCreationSettings(boxShape, new JOLT.Vec3(0, 0.5, 0), JOLT.Quat.prototype.sIdentity(), JOLT.EMotionType_Static, LAYER_NOT_MOVING);
- boxCreationSettings.mCollisionGroup.SetSubGroupID(0);
- let squareBodyBase = bodyInterface.CreateBody(boxCreationSettings);
- addToScene(squareBodyBase, 0x00ff00);
-
- let shape = new JOLT.BoxShape(new JOLT.Vec3(0.25, 1, 0.25), 0.1, undefined);
- shape.GetMassProperties().mMass = 1;
- let creationSettings = new JOLT.BodyCreationSettings(shape, new JOLT.Vec3(-0.25, 2, 0.75), JOLT.Quat.prototype.sIdentity(), JOLT.EMotionType_Dynamic, LAYER_MOVING);
-
- // RECTANGLE BODY 1 (Red)
- creationSettings.mCollisionGroup.SetSubGroupID(1);
- let rectangleBody1 = bodyInterface.CreateBody(creationSettings);
- addToScene(rectangleBody1, 0xff0000);
-
- // RECTANGLE BODY 2 (Blue)
- let shape2 = new JOLT.BoxShape(new JOLT.Vec3(0.25, 1, 0.25), 0.1, undefined);
- shape2.GetMassProperties().mMass = 1;
- let creationSettings2 = new JOLT.BodyCreationSettings(shape2, new JOLT.Vec3(-0.75, 4, 0.75), JOLT.Quat.prototype.sIdentity(), JOLT.EMotionType_Dynamic, LAYER_MOVING);
- let rectangleBody2 = bodyInterface.CreateBody(creationSettings2);
- addToScene(rectangleBody2, 0x3394e8);
-
- // RECTANGLE BODY 3 (Yellow)
- let shape3 = new JOLT.BoxShape(new JOLT.Vec3(0.25, 1, 0.25), 0.1, undefined);
- shape3.GetMassProperties().mMass = 10000;
- let creationSettings3 = new JOLT.BodyCreationSettings(shape3, new JOLT.Vec3(0.25, 4, 0.75), JOLT.Quat.prototype.sIdentity(), JOLT.EMotionType_Dynamic, LAYER_MOVING);
- let rectangleBody3 = bodyInterface.CreateBody(creationSettings3);
- addToScene(rectangleBody3, 0xffff00);
-
- // Left here for future reference.
- // GROUP FITLER
- // let a = squareBodyBase.GetCollisionGroup();
- // a.SetGroupID(0);
- // a.SetSubGroupID(0);
- // let b = rectangleBody1.GetCollisionGroup();
- // b.SetGroupID(0);
- // b.SetSubGroupID(0);
- // let c = rectangleBody2.GetCollisionGroup();
- // c.SetGroupID(0);
- // c.SetSubGroupID(0);
- // let filterTable = new Jolt.GroupFilterTable(3);
- // filterTable.DisableCollision(0, 0);
- // a.SetGroupFilter(filterTable);
- // b.SetGroupFilter(filterTable);
- // c.SetGroupFilter(filterTable);
-
- // HINGE CONSTRAINT
- let hingeConstraintSettings = new JOLT.HingeConstraintSettings();
- let anchorPoint = new JOLT.Vec3(creationSettings.mPosition.GetX(), creationSettings.mPosition.GetY() - 1.0, creationSettings.mPosition.GetZ() -0.25);
- hingeConstraintSettings.mPoint1 = hingeConstraintSettings.mPoint2 = anchorPoint;
- let axis = new JOLT.Vec3(1, 0, 0)
- let normAxis = new JOLT.Vec3(0, -1, 0);
- hingeConstraintSettings.mHingeAxis1 = hingeConstraintSettings.mHingeAxis2 = axis;
- hingeConstraintSettings.mNormalAxis1 = hingeConstraintSettings.mNormalAxis2 = normAxis;
- physicsSystem.AddConstraint(hingeConstraintSettings.Create(squareBodyBase, rectangleBody1));
-
- // HINGE CONSTRAINT 2
- let hingeConstraintSettings2 = new JOLT.HingeConstraintSettings();
- let anchorPoint2 = new JOLT.Vec3(creationSettings.mPosition.GetX() - 0.25, creationSettings.mPosition.GetY() + 1.0, creationSettings.mPosition.GetZ());
- hingeConstraintSettings2.mPoint1 = hingeConstraintSettings2.mPoint2 = anchorPoint2;
- let axis2 = new JOLT.Vec3(0, 0, 1)
- let normAxis2 = new JOLT.Vec3(-1, 0, 0);
- hingeConstraintSettings2.mHingeAxis1 = hingeConstraintSettings2.mHingeAxis2 = axis2;
- hingeConstraintSettings2.mNormalAxis1 = hingeConstraintSettings2.mNormalAxis2 = normAxis2;
- physicsSystem.AddConstraint(hingeConstraintSettings2.Create(rectangleBody1, rectangleBody2));
-
- // HINGE CONSTRAINT 3
- let hingeConstraintSettings3 = new JOLT.HingeConstraintSettings();
- let anchorPoint3 = new JOLT.Vec3(creationSettings.mPosition.GetX() + 0.25, creationSettings.mPosition.GetY() + 1.0, creationSettings.mPosition.GetZ());
- hingeConstraintSettings3.mPoint1 = hingeConstraintSettings3.mPoint2 = anchorPoint3;
- let axis3 = new JOLT.Vec3(0, 0, 1)
- let normAxis3 = new JOLT.Vec3(1, 0, 0);
- hingeConstraintSettings3.mHingeAxis1 = hingeConstraintSettings3.mHingeAxis2 = axis3;
- hingeConstraintSettings3.mNormalAxis1 = hingeConstraintSettings3.mNormalAxis2 = normAxis3;
- physicsSystem.AddConstraint(hingeConstraintSettings3.Create(rectangleBody1, rectangleBody3));
-}
-
-function spawnRandomCubes(time, deltaTime) {
- if (time > timeNextSpawn) {
- makeRandomBox();
- timeNextSpawn = time + timePerObject;
- }
-
- if (dynamicObjects.length > 500) {
- removeFromScene(dynamicObjects[2]); // 0 &&|| 1 is the floor, don't want to remove that.
- }
-}
-
-function getRandomQuat() {
- let vec = new JOLT.Vec3(0.001 + Random(), Random(), Random());
- let quat = JOLT.Quat.prototype.sRotation(vec.Normalized(), 2 * Math.PI * Random());
- JOLT.destroy(vec);
- return quat;
-}
-
-function makeRandomBox() {
- let pos = new JOLT.Vec3((Random() - 0.5) * 25, 15, (Random() - 0.5) * 25);
- let rot = getRandomQuat();
-
- let x = Random();
- let y = Random();
- let z = Random();
- let size = new JOLT.Vec3(x, y, z);
- let shape = new JOLT.BoxShape(size, 0.05, undefined);
- let creationSettings = new JOLT.BodyCreationSettings(shape, pos, rot, JOLT.EMotionType_Dynamic, LAYER_MOVING);
- creationSettings.mRestitution = 0.5;
- let body = bodyInterface.CreateBody(creationSettings);
-
- JOLT.destroy(pos);
- JOLT.destroy(rot);
- JOLT.destroy(size);
-
- // I feel as though this object should be freed at this point but doing so will cause a crash at runtime.
- // This is the only object where this happens. I'm not sure why. Seems problematic.
- // Jolt.destroy(shape);
-
- JOLT.destroy(creationSettings);
-
- addToScene(body, 0xff0000);
-}
-
-function MyThree() {
- console.log("Running...");
-
- const refContainer = useRef(null);
-
- useEffect(() => {
- initGraphics();
-
- if (refContainer.current) {
- refContainer.current.innerHTML = "";
- refContainer.current.appendChild(renderer.domElement)
-
- stats = new Stats();
- stats.domElement.style.position = 'absolute';
- stats.domElement.style.top = '0px';
- refContainer.current.appendChild(stats.domElement);
- }
-
- initPhysics();
- render();
-
- createFloor();
-
- // Spawn the y-cube of blocks as specified in the spike document.
- spikeTestScene();
- }, []);
-
- return (
-
- );
-}
-
-export default MyThree;
diff --git a/fission/src/index.css b/fission/src/index.css
index 6119ad9a8f..e7d4bb2feb 100644
--- a/fission/src/index.css
+++ b/fission/src/index.css
@@ -1,3 +1,7 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
diff --git a/fission/src/jolt/Wrapper.ts b/fission/src/jolt/Wrapper.ts
deleted file mode 100644
index 2c5df00f93..0000000000
--- a/fission/src/jolt/Wrapper.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export class Jolt_Shape {
-
-}
-
-export class Jolt_ConvexShape extends Jolt_Shape {
-
-}
\ No newline at end of file
diff --git a/fission/src/main.tsx b/fission/src/main.tsx
index 3d7150da80..9c32b6357b 100644
--- a/fission/src/main.tsx
+++ b/fission/src/main.tsx
@@ -1,10 +1,50 @@
-import React from 'react'
-import ReactDOM from 'react-dom/client'
-import App from './App.tsx'
-import './index.css'
+import React from "react"
+import ReactDOM from "react-dom/client"
+import { Theme, ThemeProvider } from "./ThemeContext"
+import Synthesis from "./Synthesis"
+import "./index.css"
-ReactDOM.createRoot(document.getElementById('root')!).render(
-
-
- ,
+const initialThemeName = "Default"
+const defaultColors: Theme = {
+ InteractiveElementSolid: { color: { r: 250, g: 162, b: 27, a: 1 }, above: [] },
+ InteractiveElementLeft: { color: { r: 224, g: 130, b: 65, a: 1 }, above: ['Background', 'BackgroundSecondary'] },
+ InteractiveElementRight: { color: { r: 218, g: 102, b: 89, a: 1 }, above: ['Background', 'BackgroundSecondary'] },
+ Background: { color: { r: 0, g: 0, b: 0, a: 1 }, above: [] },
+ BackgroundSecondary: { color: { r: 30, g: 30, b: 30, a: 1 }, above: [] },
+ InteractiveBackground: { color: { r: 52, g: 58, b: 64, a: 1 }, above: [] },
+ MainText: { color: { r: 255, g: 255, b: 255, a: 1 }, above: ['Background', 'BackgroundSecondary', 'BackgroundHUD', 'InteractiveBackground', 'InteractiveElementLeft', 'InteractiveElementRight'] },
+ Scrollbar: { color: { r: 170, g: 170, b: 170, a: 1 }, above: [] },
+ AcceptButton: { color: { r: 71, g: 138, b: 226, a: 1 }, above: [] },
+ CancelButton: { color: { r: 231, g: 85, b: 81, a: 1 }, above: [] },
+ InteractiveElementText: { color: { r: 255, g: 255, b: 255, a: 1 }, above: [] },
+ AcceptCancelButtonText: { color: { r: 0, g: 0, b: 0, a: 1 }, above: ['AcceptButton', 'CancelButton'] },
+ BackgroundHUD: { color: { r: 23, g: 23, b: 23, a: 1 }, above: [] },
+ InteractiveHover: { color: { r: 150, g: 150, b: 150, a: 1 }, above: [] },
+ InteractiveSelect: { color: { r: 100, g: 100, b: 100, a: 1 }, above: [] },
+ Icon: { color: { r: 255, g: 255, b: 255, a: 1 }, above: ['Background', 'BackgroundSecondary', 'InteractiveBackground'] },
+ MainHUDIcon: { color: { r: 255, g: 255, b: 255, a: 1 }, above: ['BackgroundHUD'] },
+ MainHUDCloseIcon: { color: { r: 0, g: 0, b: 0, a: 1 }, above: ['InteractiveElementRight', '#ffffff'] },
+ HighlightHover: { color: { r: 89, g: 255, b: 133, a: 1 }, above: [] },
+ HighlightSelect: { color: { r: 255, g: 89, b: 133, a: 1 }, above: [] },
+ SkyboxTop: { color: { r: 255, g: 255, b: 255, a: 1 }, above: [] },
+ SkyboxBottom: { color: { r: 255, g: 255, b: 255, a: 1 }, above: [] },
+ FloorGrid: { color: { r: 93, g: 93, b: 93, a: 1 }, above: [] },
+ MatchRedAlliance: { color: { r: 255, g: 0, b: 0, a: 1 }, above: [] },
+ MatchBlueAlliance: { color: { r: 0, g: 0, b: 255, a: 1 }, above: [] },
+ ToastInfo: { color: { r: 126, g: 34, b: 206, a: 1 }, above: [] },
+ ToastWarning: { color: { r: 234, g: 179, b: 8, a: 1 }, above: [] },
+ ToastError: { color: { r: 239, g: 68, b: 68, a: 1 }, above: [] },
+}
+const themes = {
+ Default: defaultColors,
+}
+
+ReactDOM.createRoot(document.getElementById("root")!).render(
+
+
+
)
diff --git a/fission/src/mirabuf/MirabufInstance.ts b/fission/src/mirabuf/MirabufInstance.ts
new file mode 100644
index 0000000000..a964336925
--- /dev/null
+++ b/fission/src/mirabuf/MirabufInstance.ts
@@ -0,0 +1,190 @@
+import * as THREE from 'three';
+import { mirabuf } from "../proto/mirabuf"
+import MirabufParser, { ParseErrorSeverity } from './MirabufParser.ts';
+import { MirabufTransform_ThreeMatrix4 } from '../util/TypeConversions.ts';
+
+const NORMAL_MATERIALS = false;
+
+export const matToString = (mat: THREE.Matrix4) => {
+ const arr = mat.toArray();
+ return `[\n${arr[0].toFixed(4)}, ${arr[4].toFixed(4)}, ${arr[8].toFixed(4)}, ${arr[12].toFixed(4)},\n`
+ + `${arr[1].toFixed(4)}, ${arr[5].toFixed(4)}, ${arr[9].toFixed(4)}, ${arr[13].toFixed(4)},\n`
+ + `${arr[2].toFixed(4)}, ${arr[6].toFixed(4)}, ${arr[10].toFixed(4)}, ${arr[14].toFixed(4)},\n`
+ + `${arr[3].toFixed(4)}, ${arr[7].toFixed(4)}, ${arr[11].toFixed(4)}, ${arr[15].toFixed(4)},\n]`
+}
+
+export const miraMatToString = (mat: mirabuf.ITransform) => {
+ const arr = mat.spatialMatrix!;
+ return `[\n${arr[0].toFixed(4)}, ${arr[1].toFixed(4)}, ${arr[2].toFixed(4)}, ${arr[3].toFixed(4)},\n`
+ + `${arr[4].toFixed(4)}, ${arr[5].toFixed(4)}, ${arr[6].toFixed(4)}, ${arr[7].toFixed(4)},\n`
+ + `${arr[8].toFixed(4)}, ${arr[9].toFixed(4)}, ${arr[10].toFixed(4)}, ${arr[11].toFixed(4)},\n`
+ + `${arr[12].toFixed(4)}, ${arr[13].toFixed(4)}, ${arr[14].toFixed(4)}, ${arr[15].toFixed(4)},\n]`
+}
+
+let nextFillerMaterial = 0;
+const fillerMaterials = [
+ new THREE.MeshToonMaterial({
+ color: 0xe32b50
+ }),
+ new THREE.MeshToonMaterial({
+ color: 0x4ccf57
+ }),
+ new THREE.MeshToonMaterial({
+ color: 0xcf4cca
+ })
+]
+
+const transformVerts = (mesh: mirabuf.IMesh) => {
+ const newVerts = new Float32Array(mesh.verts!.length);
+ for (let i = 0; i < mesh.verts!.length; i += 3) {
+ newVerts[i] = mesh.verts!.at(i)! / 100.0;
+ newVerts[i + 1] = mesh.verts!.at(i + 1)! / 100.0;
+ newVerts[i + 2] = mesh.verts!.at(i + 2)! / 100.0;
+ }
+ return newVerts;
+}
+
+const transformNorms = (mesh: mirabuf.IMesh) => {
+ const newNorms = new Float32Array(mesh.normals!.length);
+ for (let i = 0; i < mesh.normals!.length; i += 3) {
+ const normLength = Math.sqrt(mesh.normals!.at(i)! * mesh.normals!.at(i)! +
+ mesh.normals!.at(i + 1)! * mesh.normals!.at(i + 1)! +
+ mesh.normals!.at(i + 2)! * mesh.normals!.at(i + 2)!
+ );
+
+ newNorms[i] = mesh.normals!.at(i)! / normLength;
+ newNorms[i + 1] = mesh.normals!.at(i + 1)! / normLength;
+ newNorms[i + 2] = mesh.normals!.at(i + 2)! / normLength;
+ }
+ return newNorms;
+}
+
+const transformGeometry = (geometry: THREE.BufferGeometry, mesh: mirabuf.IMesh) => {
+ const newVerts = transformVerts(mesh);
+ const newNorms = transformNorms(mesh);
+
+ geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(newVerts), 3));
+ geometry.setAttribute('normal', new THREE.BufferAttribute(new Float32Array(newNorms), 3));
+ geometry.setAttribute('uv', new THREE.BufferAttribute(new Float32Array(mesh.uv!), 2));
+ geometry.setIndex(mesh.indices!);
+}
+
+class MirabufInstance {
+ private _mirabufParser: MirabufParser;
+ private _materials: Map;
+ private _meshes: Map>;
+
+ public get parser() { return this._mirabufParser; }
+ public get materials() { return this._materials; }
+ public get meshes() { return this._meshes; }
+
+ public constructor(parser: MirabufParser) {
+ if (parser.errors.some(x => x[0] >= ParseErrorSeverity.Unimportable)) {
+ throw new Error('Parser has significant errors...');
+ }
+
+ this._mirabufParser = parser;
+ this._materials = new Map();
+ this._meshes = new Map();
+
+ this.LoadMaterials();
+ this.CreateMeshes();
+ }
+
+ /**
+ * Parses all mirabuf appearances into ThreeJs materials.
+ */
+ private LoadMaterials() {
+ Object.entries(this._mirabufParser.assembly.data!.materials!.appearances!).forEach(([appearanceId, appearance]) => {
+ let hex = 0xe32b50;
+ if (appearance.albedo) {
+ const {A, B, G, R} = appearance.albedo;
+ if (A && B && G && R)
+ hex = A << 24 | R << 16 | G << 8 | B;
+ }
+
+ this._materials.set(
+ appearanceId,
+ new THREE.MeshPhongMaterial({
+ color: hex,
+ shininess: 0.0,
+ })
+ );
+ });
+ }
+
+ /**
+ * Creates ThreeJS meshes from the parsed mirabuf file.
+ */
+ private CreateMeshes() {
+ const assembly = this._mirabufParser.assembly;
+ const instances = assembly.data!.parts!.partInstances!;
+
+ let totalMeshCount = 0;
+
+ for (const instance of Object.values(instances)/* .filter(x => x.info!.name!.startsWith('EyeBall')) */) {
+ const definition = assembly.data!.parts!.partDefinitions![instance.partDefinitionReference!]!;
+ const bodies = definition.bodies;
+ const meshes = new Array();
+ if (bodies) {
+ for (const body of bodies) {
+ if (!body) continue;
+ const mesh = body.triangleMesh;
+ const geometry = new THREE.BufferGeometry();
+ if (mesh && mesh.mesh && mesh.mesh.verts && mesh.mesh.normals && mesh.mesh.uv && mesh.mesh.indices) {
+ transformGeometry(geometry, mesh.mesh!);
+
+ const appearanceOverride = body.appearanceOverride;
+ let material: THREE.Material =
+ appearanceOverride && this._materials.has(appearanceOverride)
+ ? this._materials.get(appearanceOverride)!
+ : fillerMaterials[nextFillerMaterial++ % fillerMaterials.length];
+
+ if (NORMAL_MATERIALS) {
+ material = new THREE.MeshNormalMaterial();
+ }
+
+ const threeMesh = new THREE.Mesh( geometry, material );
+ threeMesh.receiveShadow = true;
+ threeMesh.castShadow = true;
+
+ meshes.push(threeMesh);
+
+ const mat = this._mirabufParser.globalTransforms.get(instance.info!.GUID!)!;
+ threeMesh.position.setFromMatrixPosition(mat);
+ threeMesh.rotation.setFromRotationMatrix(mat);
+ }
+ }
+ }
+ totalMeshCount += meshes.length;
+ this._meshes.set(instance.info!.GUID!, meshes);
+ }
+
+ console.debug(`Created '${ totalMeshCount }' meshes for mira file '${this._mirabufParser.assembly.info!.name!}'`);
+ }
+
+ /**
+ * Adds all the meshes to the ThreeJs scene.
+ *
+ * @param scene
+ */
+ public AddToScene(scene: THREE.Scene) {
+ this._meshes.forEach(x => x.forEach(y => scene.add(y)));
+ }
+
+ /**
+ * Disposes of all ThreeJs scenes and materials.
+ */
+ public Dispose(scene: THREE.Scene) {
+ this._meshes.forEach(x => x.forEach(y => {
+ y.geometry.dispose();
+ scene.remove(y);
+ }));
+ this._meshes.clear();
+
+ this._materials.forEach(x => x.dispose());
+ this._materials.clear();
+ }
+}
+
+export default MirabufInstance;
diff --git a/fission/src/mirabuf/MirabufLoader.ts b/fission/src/mirabuf/MirabufLoader.ts
new file mode 100644
index 0000000000..5024d0e0c2
--- /dev/null
+++ b/fission/src/mirabuf/MirabufLoader.ts
@@ -0,0 +1,21 @@
+import { mirabuf } from "../proto/mirabuf";
+import Pako from "pako";
+import * as fs from 'fs';
+
+export function UnzipMira(buff: Uint8Array): Uint8Array {
+ if (buff[0] == 31 && buff[1] == 139) {
+ return Pako.ungzip(buff);
+ } else {
+ return buff;
+ }
+}
+
+export async function LoadMirabufRemote(fetchLocation: string, useCache: boolean = true): Promise {
+ const miraBuff = await fetch(fetchLocation, useCache ? undefined : {cache: "no-store"}).then(x => x.blob()).then(x => x.arrayBuffer());
+ const byteBuffer = UnzipMira(new Uint8Array(miraBuff));
+ return mirabuf.Assembly.decode(byteBuffer);
+}
+
+export function LoadMirabufLocal(fileLocation: string): mirabuf.Assembly {
+ return mirabuf.Assembly.decode(UnzipMira(new Uint8Array(fs.readFileSync(fileLocation))));
+}
\ No newline at end of file
diff --git a/fission/src/mirabuf/MirabufParser.ts b/fission/src/mirabuf/MirabufParser.ts
new file mode 100644
index 0000000000..f9a8edc08a
--- /dev/null
+++ b/fission/src/mirabuf/MirabufParser.ts
@@ -0,0 +1,341 @@
+import * as THREE from "three";
+import { mirabuf } from "../proto/mirabuf";
+import { MirabufTransform_ThreeMatrix4 } from "../util/TypeConversions";
+
+export enum ParseErrorSeverity {
+ Unimportable = 10,
+ LikelyIssues = 6,
+ ProbablyOkay = 5,
+ JustAWarning = 2
+}
+
+const GROUNDED_JOINT_ID = 'grounded';
+
+export type ParseError = [severity: ParseErrorSeverity, message: string];
+
+/**
+ * TODO:
+ * 1. Account for special versions
+ * 2. Gamepieces added to their own RigidNodes
+ */
+class MirabufParser {
+
+ private _nodeNameCounter: number = 0;
+
+ private _assembly: mirabuf.Assembly;
+ private _errors: Array;
+
+ protected _partTreeValues: Map = new Map();
+ private _designHierarchyRoot: mirabuf.INode = new mirabuf.Node();
+
+ protected _partToNodeMap: Map = new Map();
+ protected _rigidNodes: Array = [];
+ private _globalTransforms: Map;
+
+ public get errors() { return new Array(...this._errors); }
+
+ public get maxErrorSeverity() { return Math.max(...this._errors.map(x => x[0])); }
+
+ public get assembly() { return this._assembly; }
+
+ public get partTreeValues() { return this._partTreeValues; }
+
+ public get designHierarchyRoot() { return this._designHierarchyRoot; }
+
+ public get partToNodeMap() { return this._partToNodeMap; }
+
+ public get globalTransforms() { return this._globalTransforms; }
+
+ public get rigidNodes(): Array {
+ return this._rigidNodes.map(x => new RigidNodeReadOnly(x));
+ }
+
+ public constructor(assembly: mirabuf.Assembly) {
+ this._assembly = assembly;
+ this._errors = new Array();
+ this._globalTransforms = new Map();
+
+ this.GenerateTreeValues();
+ this.LoadGlobalTransforms();
+
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
+ const that = this;
+
+ function traverseTree(nodes: mirabuf.INode[], op: ((node: mirabuf.INode) => void)) {
+ nodes.forEach(x => {
+ if (x.children) {
+ traverseTree(x.children, op);
+ }
+ op(x);
+ });
+ }
+
+ // 1: Initial Rigidgroups from ancestorial breaks in joints
+ (Object.keys(assembly.data!.joints!.jointInstances!) as (string)[]).forEach(key => {
+ if (key != GROUNDED_JOINT_ID) {
+ const jInst = assembly.data!.joints!.jointInstances![key];
+ const [ancestorA, ancestorB] = this.FindAncestorialBreak(jInst.parentPart!, jInst.childPart!);
+ const parentRN = this.NewRigidNode();
+ this.MovePartToRigidNode(ancestorA, parentRN);
+ this.MovePartToRigidNode(ancestorB, this.NewRigidNode());
+ if (jInst.parts && jInst.parts.nodes)
+ traverseTree(jInst.parts.nodes, x => this.MovePartToRigidNode(x.value!, parentRN));
+ }
+ });
+
+ // Fields Only: Assign Game Piece rigid nodes
+ if (!assembly.dynamic) {
+ // Collect all definitions labelled as gamepieces (dynamic = true)
+ const gamepieceDefinitions: Set = new Set();
+ (Object.values(assembly.data!.parts!.partDefinitions!) as mirabuf.IPartDefinition[]).forEach(def => {
+ if (def.dynamic)
+ gamepieceDefinitions.add(def.info!.GUID!);
+ });
+
+ // Create gamepiece rigid nodes from partinstances with corresponding definitons
+ (Object.values(assembly.data!.parts!.partInstances!) as mirabuf.IPartInstance[]).forEach(inst => {
+ if (gamepieceDefinitions.has(inst.partDefinitionReference!)) {
+ const instNode = this.BinarySearchDesignTree(inst.info!.GUID!);
+ if (instNode) {
+ const gpRn = this.NewRigidNode('gp');
+ this.MovePartToRigidNode(instNode!.value!, gpRn);
+ instNode.children && traverseTree(instNode.children, x => this.MovePartToRigidNode(x.value!, gpRn));
+ } else {
+ this._errors.push([ParseErrorSeverity.LikelyIssues, 'Failed to find Game piece in Design Tree']);
+ }
+ }
+ });
+ }
+
+ // 2: Grounded joint
+ const gInst = assembly.data!.joints!.jointInstances![GROUNDED_JOINT_ID];
+ const gNode = this.NewRigidNode();
+
+ traverseTree(gInst.parts!.nodes!, x => (!this._partToNodeMap.has) && this.MovePartToRigidNode(x.value!, gNode));
+
+ // 3: Traverse and round up
+ const traverseNodeRoundup = (node: mirabuf.INode, parentNode: RigidNode) => {
+ const currentNode = that._partToNodeMap.get(node.value!);
+ if (!currentNode) {
+ that.MovePartToRigidNode(node.value!, parentNode);
+ }
+
+ if (node.children) {
+ node.children!.forEach(x => traverseNodeRoundup(x, currentNode ? currentNode : parentNode));
+ }
+ }
+ this._designHierarchyRoot.children?.forEach(x => traverseNodeRoundup(x, gNode));
+
+ // 4: Bandage via rigidgroups
+ assembly.data!.joints!.rigidGroups!.forEach(rg => {
+ let rn: RigidNode | null = null;
+ rg.occurrences!.forEach(y => {
+ const currentRn = this._partToNodeMap.get(y)!;
+ if (!rn) {
+ rn = currentRn;
+ } else if (currentRn.name != rn.name) {
+ rn = this.MergeRigidNodes(currentRn, rn);
+ }
+ });
+ });
+
+ // 5. Remove Empty RNs
+ this._rigidNodes = this._rigidNodes.filter(x => x.parts.size > 0);
+ }
+
+ private NewRigidNode(suffix?: string): RigidNode {
+ const node = new RigidNode((this._nodeNameCounter++).toString() + (suffix ? `_${suffix}` : ''));
+ this._rigidNodes.push(node);
+ return node;
+ }
+
+ private MergeRigidNodes(rnA: RigidNode, rnB: RigidNode) {
+ const newRn = this.NewRigidNode('merged');
+ const allParts = new Set([...rnA.parts, ...rnB.parts]);
+ allParts.forEach(x => this.MovePartToRigidNode(x, newRn));
+ return newRn;
+ }
+
+ private MovePartToRigidNode(part: string, node: RigidNode) {
+ if (part.length < 1)
+ return;
+
+ const original = this._partToNodeMap.get(part);
+ if (original) {
+ if (original === node)
+ return;
+
+ original.parts.delete(part);
+ this._partToNodeMap.delete(part);
+ }
+
+ node.parts.add(part);
+ this._partToNodeMap.set(part, node);
+ }
+
+ /**
+ * Loads this._globalTransforms with the world space transformations of each part instance.
+ */
+ private LoadGlobalTransforms() {
+ const root = this._designHierarchyRoot;
+ const partInstances = new Map(Object.entries(this._assembly.data!.parts!.partInstances!));
+ const partDefinitions = this._assembly.data!.parts!.partDefinitions!;
+
+ this._globalTransforms.clear();
+
+ const getTransforms = (node: mirabuf.INode, parent: THREE.Matrix4) => {
+ for (const child of node.children!) {
+ if (!partInstances.has(child.value!)) {
+ continue;
+ }
+ const partInstance = partInstances.get(child.value!)!;
+
+ if (this._globalTransforms.has(child.value!)) continue;
+ const mat = MirabufTransform_ThreeMatrix4(partInstance.transform!)!;
+
+ // console.log(`[${partInstance.info!.name!}] -> ${matToString(mat)}`);
+
+ this._globalTransforms.set(child.value!, mat.premultiply(parent));
+ getTransforms(child, mat);
+ }
+ }
+
+ for (const child of root.children!) {
+ const partInstance = partInstances.get(child.value!)!;
+ let mat;
+ if (!partInstance.transform) {
+ const def = partDefinitions[partInstances.get(child.value!)!.partDefinitionReference!];
+ if (!def.baseTransform) {
+ mat = new THREE.Matrix4().identity();
+ } else {
+ mat = MirabufTransform_ThreeMatrix4(def.baseTransform);
+ }
+ } else {
+ mat = MirabufTransform_ThreeMatrix4(partInstance.transform);
+ }
+
+ // console.log(`[${partInstance.info!.name!}] -> ${matToString(mat!)}`);
+
+ this._globalTransforms.set(partInstance.info!.GUID!, mat!);
+ getTransforms(child, mat!);
+ }
+ }
+
+ private FindAncestorialBreak(partA: string, partB: string): [string, string] {
+ if (!this._partTreeValues.has(partA) || !this._partTreeValues.has(partB)) {
+ this._errors.push([ParseErrorSeverity.LikelyIssues, 'Part not found in tree.']);
+ return [partA, partB];
+ } else if (partA == partB) {
+ this._errors.push([ParseErrorSeverity.LikelyIssues, 'Part A and B are the same.']);
+ }
+
+ const ptv = this._partTreeValues;
+ let pathA = this._designHierarchyRoot;
+ let pathB = this._designHierarchyRoot;
+ const valueA = ptv.get(partA)!;
+ const valueB = ptv.get(partB)!;
+
+ while (pathA.value! == pathB.value! && pathA.value! != partA && pathB.value! != partB) {
+ const ancestorIndexA = this.BinarySearchIndex(valueA, pathA.children!);
+ const ancestorValueA = ptv.get(pathA.children![ancestorIndexA].value!)!;
+ pathA = pathA.children![ancestorIndexA + (ancestorValueA < valueA ? 1 : 0)];
+
+ const ancestorIndexB = this.BinarySearchIndex(valueB, pathB.children!);
+ const ancestorValueB = ptv.get(pathB.children![ancestorIndexB].value!)!;
+ pathB = pathB.children![ancestorIndexB + (ancestorValueB < valueB ? 1 : 0)];
+ }
+
+ if (pathA.value! == partA && pathA.value! == pathB.value!) {
+ const ancestorIndexB = this.BinarySearchIndex(valueB, pathB.children!);
+ const ancestorValueB = ptv.get(pathB.children![ancestorIndexB].value!)!;
+ pathB = pathB.children![ancestorIndexB + (ancestorValueB < valueB ? 1 : 0)];
+ } else if (pathB.value! == partB && pathA.value! == pathB.value!) {
+ const ancestorIndexA = this.BinarySearchIndex(valueA, pathA.children!);
+ const ancestorValueA = ptv.get(pathA.children![ancestorIndexA].value!)!;
+ pathA = pathA.children![ancestorIndexA + (ancestorValueA < valueA ? 1 : 0)];
+ }
+
+ return [pathA.value!, pathB.value!];
+ }
+
+ private BinarySearchIndex(target: number, children: mirabuf.INode[]): number {
+ let l = 0;
+ let h = children.length;
+
+ while (h - l > 1) {
+ const i = Math.floor((h + l) / 2.0);
+ const iVal = this._partTreeValues.get(children[i].value!)!;
+ if (iVal > target) {
+ h = i;
+ } else if (iVal < target) {
+ l = i + 1;
+ } else {
+ return i;
+ }
+ }
+
+ return Math.floor((h + l) / 2.0);
+ }
+
+ private BinarySearchDesignTree(target: string): mirabuf.INode | null {
+ let node = this._designHierarchyRoot;
+ const targetValue = this._partTreeValues.get(target)!;
+
+ while (node.value != target && node.children) {
+ const i = this.BinarySearchIndex(targetValue, node.children!);
+ const iValue = this._partTreeValues.get(node.children![i].value!)!;
+ node = node.children![i + (iValue < targetValue ? 1 : 0)];
+ }
+
+ return node.value! == target ? node : null;
+ }
+
+ private GenerateTreeValues() {
+ let nextValue = 0;
+ const partTreeValues = new Map();
+
+ const recursive = (partNode: mirabuf.INode) => {
+ partNode.children = partNode.children?.filter(x => x.value != null);
+ partNode.children?.forEach(x => recursive(x));
+ partTreeValues.set(partNode.value!, nextValue++);
+ }
+
+ this._designHierarchyRoot = new mirabuf.Node();
+ this._designHierarchyRoot.value = "Importer Generated Root";
+ this._designHierarchyRoot.children = [];
+ this._designHierarchyRoot.children.push(...this._assembly.designHierarchy!.nodes!);
+
+ recursive(this._designHierarchyRoot);
+ this._partTreeValues = partTreeValues;
+ }
+}
+
+/**
+ * Collection of mirabuf parts that are bound together
+ */
+class RigidNode {
+ public name: string;
+ public parts: Set = new Set();
+
+ public constructor(name: string) {
+ this.name = name;
+ }
+}
+
+export class RigidNodeReadOnly {
+ private _original: RigidNode;
+
+ public get name(): string {
+ return this._original.name;
+ }
+
+ public get parts(): ReadonlySet {
+ return this._original.parts;
+ }
+
+ public constructor(original: RigidNode) {
+ this._original = original;
+ }
+}
+
+export default MirabufParser;
diff --git a/fission/src/mirabuf/MirabufSceneObject.ts b/fission/src/mirabuf/MirabufSceneObject.ts
new file mode 100644
index 0000000000..de4ed2d771
--- /dev/null
+++ b/fission/src/mirabuf/MirabufSceneObject.ts
@@ -0,0 +1,46 @@
+import { mirabuf } from "@/proto/mirabuf";
+import SceneObject from "../systems/scene/SceneObject";
+import GetSceneRenderer from "../systems/scene/SceneRenderer";
+import MirabufInstance from "./MirabufInstance";
+import { LoadMirabufRemote } from "./MirabufLoader";
+import MirabufParser, { ParseErrorSeverity } from "./MirabufParser";
+
+class MirabufSceneObject extends SceneObject {
+
+ private _mirabufInstance: MirabufInstance;
+
+ public constructor(mirabufInstance: MirabufInstance) {
+ super();
+
+ this._mirabufInstance = mirabufInstance;
+ }
+
+ public Setup(): void {
+ this._mirabufInstance.AddToScene(GetSceneRenderer().scene);
+ }
+
+ public Update(): void { }
+
+ public Dispose(): void {
+ this._mirabufInstance.Dispose(GetSceneRenderer().scene);
+ }
+}
+
+export async function CreateMirabufFromUrl(path: string): Promise {
+ const miraAssembly = await LoadMirabufRemote(path)
+ .catch(console.error);
+
+ if (!miraAssembly || !(miraAssembly instanceof mirabuf.Assembly)) {
+ return;
+ }
+
+ const parser = new MirabufParser(miraAssembly);
+ if (parser.maxErrorSeverity >= ParseErrorSeverity.Unimportable) {
+ console.error(`Assembly Parser produced significant errors for '${miraAssembly.info!.name!}'`);
+ return;
+ }
+
+ return new MirabufSceneObject(new MirabufInstance(parser));
+}
+
+export default MirabufSceneObject;
\ No newline at end of file
diff --git a/fission/src/modals/DownloadAssetsModal.tsx b/fission/src/modals/DownloadAssetsModal.tsx
new file mode 100644
index 0000000000..5c656345c3
--- /dev/null
+++ b/fission/src/modals/DownloadAssetsModal.tsx
@@ -0,0 +1,61 @@
+import React from "react"
+import Modal, { ModalPropsImpl } from "../components/Modal"
+import Stack, { StackDirection } from "../components/Stack"
+import Label, { LabelSize } from "../components/Label"
+import LabeledButton, { LabelPlacement } from "../components/LabeledButton"
+import { HiDownload } from "react-icons/hi"
+
+const DownloadAssetsModal: React.FC = ({ modalId }) => (
+ } modalId={modalId}>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+)
+
+export default DownloadAssetsModal
diff --git a/fission/src/modals/ExitSynthesisModal.tsx b/fission/src/modals/ExitSynthesisModal.tsx
new file mode 100644
index 0000000000..3c6447cac6
--- /dev/null
+++ b/fission/src/modals/ExitSynthesisModal.tsx
@@ -0,0 +1,24 @@
+import React from "react"
+import Modal, { ModalPropsImpl } from "../components/Modal"
+import { GrFormClose } from "react-icons/gr"
+import Label from "../components/Label"
+
+const ExitSynthesisModal: React.FC = ({ modalId }) => {
+ const isOnMainMenu = false
+ return (
+ }
+ modalId={modalId}
+ acceptName="Exit"
+ >
+
+
+ )
+}
+
+export default ExitSynthesisModal
diff --git a/fission/src/modals/MatchResultsModal.tsx b/fission/src/modals/MatchResultsModal.tsx
new file mode 100644
index 0000000000..8d719b294e
--- /dev/null
+++ b/fission/src/modals/MatchResultsModal.tsx
@@ -0,0 +1,40 @@
+import React from "react"
+import Modal, { ModalPropsImpl } from "../components/Modal"
+import { GrFormClose } from "react-icons/gr"
+import Stack, { StackDirection } from "../components/Stack"
+import Label from "../components/Label"
+
+type Entry = {
+ name: string
+ value: number
+}
+
+const MatchResultsModal: React.FC = ({ modalId }) => {
+ const entries: Entry[] = [
+ { name: "Red Score", value: 10 },
+ { name: "Blue Score", value: 5 },
+ ]
+
+ return (
+ }
+ modalId={modalId}
+ cancelName="Exit"
+ middleName="Configure"
+ acceptName="Restart"
+ middleEnabled={true}
+ >
+
+ {entries.map(e => (
+
+
+
+
+ ))}
+
+
+ )
+}
+
+export default MatchResultsModal
diff --git a/fission/src/modals/SpawningModal.tsx b/fission/src/modals/SpawningModal.tsx
new file mode 100644
index 0000000000..dcce5899e3
--- /dev/null
+++ b/fission/src/modals/SpawningModal.tsx
@@ -0,0 +1,27 @@
+import React from "react"
+import Modal, { ModalPropsImpl } from "../components/Modal"
+import { FaPlus } from "react-icons/fa6"
+import Stack, { StackDirection } from "../components/Stack"
+import Button from "../components/Button"
+import { useModalControlContext } from "../ModalContext"
+
+const SpawningModal: React.FC = ({ modalId }) => {
+ const { openModal } = useModalControlContext()
+
+ return (
+ } modalId={modalId}>
+
+
+
+ )
+}
+
+export default SpawningModal
diff --git a/fission/src/modals/UpdateAvailableModal.tsx b/fission/src/modals/UpdateAvailableModal.tsx
new file mode 100644
index 0000000000..20ef23014d
--- /dev/null
+++ b/fission/src/modals/UpdateAvailableModal.tsx
@@ -0,0 +1,22 @@
+import React from "react"
+import Modal, { ModalPropsImpl } from "../components/Modal"
+import { GrFormClose } from "react-icons/gr"
+import Label from "../components/Label"
+
+const UpdateAvailableModal: React.FC = ({ modalId }) => {
+ return (
+ }
+ modalId={modalId}
+ acceptName="Update"
+ >
+
+
+ )
+}
+
+export default UpdateAvailableModal
diff --git a/fission/src/modals/ViewModal.tsx b/fission/src/modals/ViewModal.tsx
new file mode 100644
index 0000000000..4afec763ca
--- /dev/null
+++ b/fission/src/modals/ViewModal.tsx
@@ -0,0 +1,54 @@
+import React, { useState } from "react"
+import Modal, { ModalPropsImpl } from "../components/Modal"
+import Dropdown from "../components/Dropdown"
+import { FaMagnifyingGlass } from "react-icons/fa6"
+import { TooltipControl, useTooltipControlContext } from "@/TooltipContext"
+
+type ViewType = "Orbit" | "Freecam" | "Overview" | "Driver Station"
+
+const ViewModal: React.FC = ({ modalId }) => {
+ const { showTooltip } = useTooltipControlContext()
+
+ const [view, setView] = useState("Orbit")
+
+ const controls: { [key in ViewType]: TooltipControl[] } = {
+ "Orbit": [
+ { control: "LMB + Drag", description: "Orbit Camera" },
+ { control: "Scroll", description: "Zoom Camera" },
+ ],
+ "Freecam": [
+ { control: "RMB + Drag", description: "Rotate Camera" },
+ { control: "RMB + WASD", description: "Move Camera" },
+ { control: "Scroll", description: "Zoom Camera" },
+ ],
+ "Overview": [{ control: "None", description: "Cannot Move Camera" }],
+ "Driver Station": [
+ { control: "RMB + Drag", description: "Rotate Camera" },
+ { control: "RMB + WASD", description: "Move Camera" },
+ { control: "Scroll", description: "Zoom Camera" },
+ ],
+ }
+
+ return (
+ }
+ modalId={modalId}
+ onAccept={() => showTooltip("controls", controls[view])}
+ >
+ setView(v as ViewType)}
+ />
+
+ )
+}
+
+export default ViewModal
diff --git a/fission/src/modals/aether/ConnectToMultiplayerModal.tsx b/fission/src/modals/aether/ConnectToMultiplayerModal.tsx
new file mode 100644
index 0000000000..32cf07c4cb
--- /dev/null
+++ b/fission/src/modals/aether/ConnectToMultiplayerModal.tsx
@@ -0,0 +1,35 @@
+import React from "react"
+import Modal, { ModalPropsImpl } from "../../components/Modal"
+import Button from "../../components/Button"
+import { FaGear } from "react-icons/fa6"
+import Stack, { StackDirection } from "../../components/Stack"
+import Dropdown from "../../components/Dropdown"
+import Label from "../../components/Label"
+
+const ConnectToMultiplayerModal: React.FC = ({ modalId }) => (
+ }
+ acceptEnabled={false}
+ cancelName="Disconnect"
+ modalId={modalId}
+ >
+
+
+
+
+
+
+
+ {}}
+ />
+
+
+
+
+
+)
+
+export default ConnectToMultiplayerModal
diff --git a/fission/src/modals/aether/ServerHostingModal.tsx b/fission/src/modals/aether/ServerHostingModal.tsx
new file mode 100644
index 0000000000..eac9175a29
--- /dev/null
+++ b/fission/src/modals/aether/ServerHostingModal.tsx
@@ -0,0 +1,43 @@
+import React, { useEffect, useState } from "react"
+import Modal, { ModalPropsImpl } from "../../components/Modal"
+import { FaPlus } from "react-icons/fa6"
+import Label from "../../components/Label"
+import Stack, { StackDirection } from "../../components/Stack"
+
+type Client = {
+ name: string
+ ping: number
+}
+
+const clients_source: Client[] = [
+ { name: "Client 1", ping: 100 },
+ { name: "Client 2", ping: 330 },
+ { name: "Client 3", ping: 50 },
+ { name: "Client 4", ping: 1000 },
+]
+
+const ServerHostingModal: React.FC = ({ modalId }) => {
+ const [clients, setClients] = useState([])
+ // should replace with actual clients when communication works
+ useEffect(() => {
+ setTimeout(() => {
+ setClients(clients_source)
+ }, 2_000)
+ }, [clients])
+ return (
+ } modalId={modalId}>
+ {clients.length == 0 ? (
+
+ ) : (
+ clients.map(c => (
+
+
+
+
+ ))
+ )}
+
+ )
+}
+
+export default ServerHostingModal
diff --git a/fission/src/modals/configuring/ChangeInputsModal.tsx b/fission/src/modals/configuring/ChangeInputsModal.tsx
new file mode 100644
index 0000000000..e1344f492b
--- /dev/null
+++ b/fission/src/modals/configuring/ChangeInputsModal.tsx
@@ -0,0 +1,135 @@
+import React, { useEffect, useState } from "react"
+import Modal, { ModalPropsImpl } from "../../components/Modal"
+import { FaGamepad } from "react-icons/fa6"
+import Stack, { StackDirection } from "../../components/Stack"
+import Label, { LabelSize } from "../../components/Label"
+import LabeledButton, { LabelPlacement } from "../../components/LabeledButton"
+
+type ModifierState = {
+ alt?: boolean
+ ctrl?: boolean
+ shift?: boolean
+ meta?: boolean
+}
+
+type Control = {
+ name: string
+ key: string
+ modifiers?: ModifierState
+}
+
+const globalControls: { [key: string]: Control } = {
+ "Intake": { name: "Intake", key: "E" },
+ "Shoot Gamepiece": { name: "Shoot Gamepiece", key: "Q" },
+ "Enable God Mode": { name: "Enable God Mode", key: "G" },
+}
+
+const robotControls: { [key: string]: Control } = {
+ "Arcade Forward": { name: "Arcade Forward", key: "W" },
+ "Arcade Backward": { name: "Arcade Backward", key: "A" },
+ "Arcade Left": { name: "Arcade Left", key: "S" },
+ "Arcade Right": { name: "Arcade Right", key: "D" },
+}
+
+// capitalize first letter
+const transformKeyName = (control: Control) => {
+ let suffix = ""
+ if (control.modifiers) {
+ if (control.modifiers.meta) suffix += " + Meta"
+ if (control.modifiers.shift) suffix += " + Shift"
+ if (control.modifiers.ctrl) suffix += " + Ctrl"
+ if (control.modifiers.alt) suffix += " + Alt"
+ }
+ return control.key[0].toUpperCase() + control.key.substring(1) + suffix
+}
+
+const ChangeInputsModal: React.FC = ({ modalId }) => {
+ const [loadedRobot, setLoadedRobot] = useState("")
+ const [selectedInput, setSelectedInput] = useState("")
+ const [chosenKey, setChosenKey] = useState("")
+ const [modifierState, setModifierState] = useState({})
+
+ useEffect(() => {
+ setTimeout(() => setLoadedRobot("Dozer v9"), 2_000)
+ })
+
+ if (selectedInput && chosenKey) {
+ const selected = globalControls[selectedInput]
+ selected.key = chosenKey
+ selected.modifiers = modifierState
+ setChosenKey("")
+ setSelectedInput("")
+ setModifierState({})
+ }
+
+ return (
+ } modalId={modalId}>
+
+ {
+ setChosenKey(selectedInput ? e.key : "")
+ setModifierState({
+ ctrl: e.ctrlKey,
+ alt: e.altKey,
+ shift: e.shiftKey,
+ meta: e.metaKey,
+ })
+ }}
+ >
+ {loadedRobot ? (
+ <>
+
+ {Object.values(robotControls).map(c => (
+ {
+ setSelectedInput(c.name)
+ }}
+ />
+ ))}
+ >
+ ) : (
+
+ )}
+
+ {
+ setChosenKey(selectedInput ? e.key : "")
+ setModifierState({
+ ctrl: e.ctrlKey,
+ alt: e.altKey,
+ shift: e.shiftKey,
+ meta: e.metaKey,
+ })
+ }}
+ >
+
+ {Object.values(globalControls).map(c => (
+ {
+ setSelectedInput(c.name)
+ }}
+ />
+ ))}
+
+
+
+ )
+}
+
+export default ChangeInputsModal
diff --git a/fission/src/modals/configuring/ChooseMultiplayerModeModal.tsx b/fission/src/modals/configuring/ChooseMultiplayerModeModal.tsx
new file mode 100644
index 0000000000..f03e69c38a
--- /dev/null
+++ b/fission/src/modals/configuring/ChooseMultiplayerModeModal.tsx
@@ -0,0 +1,21 @@
+import React from "react"
+import Modal, { ModalPropsImpl } from "../../components/Modal"
+import { FaGear } from "react-icons/fa6"
+import Button from "../../components/Button"
+
+const ChooseMultiplayerModeModal: React.FC = ({ modalId }) => {
+ return (
+ }
+ modalId={modalId}
+ cancelEnabled={false}
+ acceptEnabled={false}
+ >
+
+
+
+ )
+}
+
+export default ChooseMultiplayerModeModal
diff --git a/fission/src/modals/configuring/ChooseSingleplayerModeModal.tsx b/fission/src/modals/configuring/ChooseSingleplayerModeModal.tsx
new file mode 100644
index 0000000000..f35d0430dd
--- /dev/null
+++ b/fission/src/modals/configuring/ChooseSingleplayerModeModal.tsx
@@ -0,0 +1,21 @@
+import React from "react"
+import Modal, { ModalPropsImpl } from "../../components/Modal"
+import { FaGear } from "react-icons/fa6"
+import Button from "../../components/Button"
+
+const ChooseSingleplayerModeModal: React.FC = ({ modalId }) => {
+ return (
+ }
+ modalId={modalId}
+ cancelEnabled={false}
+ acceptEnabled={false}
+ >
+
+
+
+ )
+}
+
+export default ChooseSingleplayerModeModal
diff --git a/fission/src/modals/configuring/ConfigMotorModal.tsx b/fission/src/modals/configuring/ConfigMotorModal.tsx
new file mode 100644
index 0000000000..4a7e658af5
--- /dev/null
+++ b/fission/src/modals/configuring/ConfigMotorModal.tsx
@@ -0,0 +1,79 @@
+import { FaGear } from "react-icons/fa6"
+import Modal, { ModalPropsImpl } from "../../components/Modal"
+import Slider from "../../components/Slider"
+import Label, { LabelSize } from "../../components/Label"
+import { useState } from "react"
+
+type Motor = {
+ name: string
+ defaultVelocity: number
+ minVelocity: number
+ maxVelocity: number
+ unit: "RPM" | "M/S"
+}
+
+// Synthesis should send all of this because we already have logic to generate these entries
+// rather than calculating them here
+const sampleMotors: Motor[] = [
+ {
+ name: "Drive",
+ defaultVelocity: 100,
+ minVelocity: 0,
+ maxVelocity: 150,
+ unit: "RPM",
+ },
+]
+
+const ConfigMotorModal: React.FC = ({ modalId }) => {
+ const [motors, setMotors] = useState(sampleMotors)
+ const [initialData] = useState([...motors])
+
+ return (
+ }
+ modalId={modalId}
+ middleName="Session Save"
+ middleEnabled={true}
+ acceptName="Save"
+ onCancel={() => {
+ setMotors(initialData)
+ // send cancel
+ }}
+ onMiddle={() => {
+ // send session save
+ }}
+ onAccept={() => {
+ // send save
+ }}
+ >
+
+
+
+
+ |
+
+
+ |
+
+ {motors.map((m: Motor) => (
+
+
+
+ |
+
+
+ |
+
+ ))}
+
+
+ )
+}
+
+export default ConfigMotorModal
diff --git a/fission/src/modals/configuring/DrivetrainModal.tsx b/fission/src/modals/configuring/DrivetrainModal.tsx
new file mode 100644
index 0000000000..64d0a3211f
--- /dev/null
+++ b/fission/src/modals/configuring/DrivetrainModal.tsx
@@ -0,0 +1,54 @@
+import React, { useState } from "react"
+import Modal, { ModalPropsImpl } from "../../components/Modal"
+import { FaCar } from "react-icons/fa6"
+import Dropdown from "../../components/Dropdown"
+import { TooltipControl, useTooltipControlContext } from "@/TooltipContext"
+
+type DrivetrainType = "None" | "Tank" | "Arcade" | "Swerve"
+
+const DrivetrainModal: React.FC = ({ modalId }) => {
+ const { showTooltip } = useTooltipControlContext()
+ const [drivetrain, setDrivetrain] = useState("None")
+
+ const controls: { [key in DrivetrainType]: TooltipControl[] } = {
+ None: [{ control: "None", description: "Cannot Move" }],
+ Tank: [
+ { control: "WS", description: "Drivetrain Left" },
+ { control: "IK", description: "Drivetrain Right" },
+ { control: "E", description: "Intake" },
+ { control: "Q", description: "Dispense" },
+ ],
+ Arcade: [
+ { control: "WASD", description: "Drive" },
+ { control: "E", description: "Intake" },
+ { control: "Q", description: "Dispense" },
+ ],
+ Swerve: [
+ { control: "WASD", description: "Drive" },
+ { control: "< >", description: "Turn" },
+ { control: "E", description: "Intake" },
+ { control: "Q", description: "Dispense" },
+ ],
+ }
+
+ return (
+ }
+ modalId={modalId}
+ onAccept={() => showTooltip("controls", controls[drivetrain])}
+ >
+
+ setDrivetrain(selected as DrivetrainType)
+ }
+ />
+
+ )
+}
+
+export default DrivetrainModal
diff --git a/fission/src/modals/configuring/PracticeSettingsModal.tsx b/fission/src/modals/configuring/PracticeSettingsModal.tsx
new file mode 100644
index 0000000000..55542cb6dd
--- /dev/null
+++ b/fission/src/modals/configuring/PracticeSettingsModal.tsx
@@ -0,0 +1,30 @@
+import React from "react"
+import Modal, { ModalPropsImpl } from "../../components/Modal"
+import { FaGear } from "react-icons/fa6"
+import Button from "../../components/Button"
+import Label, { LabelSize } from "../../components/Label"
+import Stack, { StackDirection } from "../../components/Stack"
+import Dropdown from "../../components/Dropdown"
+
+const PracticeSettingsModal: React.FC = ({ modalId }) => {
+ return (
+ } modalId={modalId}>
+
+
+ {}}
+ />
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default PracticeSettingsModal
diff --git a/fission/src/modals/configuring/RoboRIOModal.tsx b/fission/src/modals/configuring/RoboRIOModal.tsx
new file mode 100644
index 0000000000..847362c258
--- /dev/null
+++ b/fission/src/modals/configuring/RoboRIOModal.tsx
@@ -0,0 +1,25 @@
+import React from "react"
+import Modal, { ModalPropsImpl } from "../../components/Modal"
+import LabeledButton, { LabelPlacement } from "../../components/LabeledButton"
+import { useModalControlContext } from "../../ModalContext"
+import { BsCodeSquare } from "react-icons/bs"
+
+const RoboRIOModal: React.FC = ({ modalId }) => {
+ const { openModal } = useModalControlContext()
+ return (
+ }
+ modalId={modalId}
+ >
+ openModal("create-device")}
+ />
+
+ )
+}
+
+export default RoboRIOModal
diff --git a/fission/src/modals/configuring/SettingsModal.tsx b/fission/src/modals/configuring/SettingsModal.tsx
new file mode 100644
index 0000000000..3a342e4e47
--- /dev/null
+++ b/fission/src/modals/configuring/SettingsModal.tsx
@@ -0,0 +1,61 @@
+import React from "react"
+import { useModalControlContext } from "../../ModalContext"
+import Modal, { ModalPropsImpl } from "../../components/Modal"
+import { FaGear } from "react-icons/fa6"
+import Label, { LabelSize } from "../../components/Label"
+import Dropdown from "../../components/Dropdown"
+import Button from "../../components/Button"
+import Slider from "../../components/Slider"
+import Checkbox from "../../components/Checkbox"
+
+const SettingsModal: React.FC = ({ modalId }) => {
+ const { openModal } = useModalControlContext()
+
+ return (
+ } modalId={modalId}>
+
+ {}}
+ />
+ {}}
+ />
+ openModal("theme-editor")}
+ />
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default SettingsModal
diff --git a/fission/src/modals/configuring/rio-config/RCConfigEncoderModal.tsx b/fission/src/modals/configuring/rio-config/RCConfigEncoderModal.tsx
new file mode 100644
index 0000000000..5247c45627
--- /dev/null
+++ b/fission/src/modals/configuring/rio-config/RCConfigEncoderModal.tsx
@@ -0,0 +1,72 @@
+import React, { useState } from "react"
+import Modal, { ModalPropsImpl } from "../../../components/Modal"
+import { useModalControlContext } from "../../../ModalContext"
+import { FaPlus } from "react-icons/fa6"
+import Label, { LabelSize } from "../../../components/Label"
+import Input from "../../../components/Input"
+import Dropdown from "../../../components/Dropdown"
+
+const RCConfigEncoderModal: React.FC = ({ modalId }) => {
+ const { openModal } = useModalControlContext()
+ const [name, setName] = useState("")
+ const [selectedSignal, setSelectedSignal] = useState("")
+ const [selectedChannelA, setSelectedChannelA] = useState(0)
+ const [selectedChannelB, setSelectedChannelB] = useState(0)
+ const [conversionFactor, setConversionFactor] = useState(1)
+
+ const numPorts = 10
+ const signals = ["Rev0 (uuid)", "Rev1 (uuid)", "Rev2 (uuid)", "Rev3 (uuid)"]
+
+ if (!selectedSignal) setSelectedSignal(signals[0])
+
+ return (
+ }
+ modalId={modalId}
+ acceptName="Done"
+ onAccept={() => {
+ // mostly doing this so eslint doesn't complain about unused variables
+ console.log(
+ name,
+ selectedSignal,
+ selectedChannelA,
+ selectedChannelB,
+ conversionFactor
+ )
+ }}
+ onCancel={() => {
+ openModal("roborio")
+ }}
+ >
+
+
+ setSelectedSignal(s)}
+ />
+ n.toString())}
+ onSelect={s => setSelectedChannelA(parseInt(s))}
+ />
+ n.toString())}
+ onSelect={s => setSelectedChannelB(parseInt(s))}
+ />
+ {
+ setConversionFactor(n != "" ? parseFloat(n) : 0)
+ }}
+ />
+
+ )
+}
+
+export default RCConfigEncoderModal
diff --git a/fission/src/modals/configuring/rio-config/RCConfigPwmGroupModal.tsx b/fission/src/modals/configuring/rio-config/RCConfigPwmGroupModal.tsx
new file mode 100644
index 0000000000..7f7be3aeab
--- /dev/null
+++ b/fission/src/modals/configuring/rio-config/RCConfigPwmGroupModal.tsx
@@ -0,0 +1,100 @@
+import React, { useState } from "react"
+import Modal, { ModalPropsImpl } from "../../../components/Modal"
+import { useModalControlContext } from "../../../ModalContext"
+import { FaPlus } from "react-icons/fa6"
+import ScrollView from "../../../components/ScrollView"
+import Stack, { StackDirection } from "../../../components/Stack"
+import Checkbox from "../../../components/Checkbox"
+import Container from "../../../components/Container"
+import Label, { LabelSize } from "../../../components/Label"
+import Input from "../../../components/Input"
+
+const RCConfigPwmGroupModal: React.FC = ({ modalId }) => {
+ const { openModal } = useModalControlContext()
+ const [name, setName] = useState("")
+ const [checkedPorts, setCheckedPorts] = useState([])
+ const [checkedSignals, setCheckedSignals] = useState([])
+
+ const numPorts = 8
+ const signals = ["Rev0 (uuid)", "Rev1 (uuid)", "Rev2 (uuid)", "Rev3 (uuid)"]
+
+ return (
+ }
+ modalId={modalId}
+ acceptName="Done"
+ onAccept={() => {
+ // no eslint complain
+ console.log(name, checkedPorts, checkedSignals)
+ }}
+ onCancel={() => {
+ openModal("roborio")
+ }}
+ >
+
+
+
+
+
+
+ {[...Array(numPorts).keys()].map(p => (
+ {
+ if (checked && !checkedPorts.includes(p)) {
+ setCheckedPorts([...checkedPorts, p])
+ } else if (
+ !checked &&
+ checkedPorts.includes(p)
+ ) {
+ setCheckedPorts(
+ checkedPorts.filter(a => a != p)
+ )
+ }
+ }}
+ />
+ ))}
+
+
+
+
+
+ {signals.map(p => (
+ {
+ if (
+ checked &&
+ !checkedSignals.includes(p)
+ ) {
+ setCheckedSignals([
+ ...checkedSignals,
+ p,
+ ])
+ } else if (
+ !checked &&
+ checkedSignals.includes(p)
+ ) {
+ setCheckedSignals(
+ checkedSignals.filter(a => a != p)
+ )
+ }
+ }}
+ />
+ ))}
+
+
+
+
+ )
+}
+
+export default RCConfigPwmGroupModal
diff --git a/fission/src/modals/configuring/rio-config/RCCreateDeviceModal.tsx b/fission/src/modals/configuring/rio-config/RCCreateDeviceModal.tsx
new file mode 100644
index 0000000000..6e1df80689
--- /dev/null
+++ b/fission/src/modals/configuring/rio-config/RCCreateDeviceModal.tsx
@@ -0,0 +1,44 @@
+import React, { useState } from "react"
+import Modal, { ModalPropsImpl } from "../../../components/Modal"
+import { useModalControlContext } from "../../../ModalContext"
+import { FaPlus } from "react-icons/fa6"
+import Dropdown from "../../../components/Dropdown"
+
+const RCCreateDeviceModal: React.FC = ({ modalId }) => {
+ const { openModal } = useModalControlContext()
+ const [type, setType] = useState("")
+
+ return (
+ }
+ modalId={modalId}
+ acceptName="Next"
+ onAccept={() => {
+ switch (type) {
+ case "PWM":
+ openModal("config-pwm")
+ break
+ case "Encoder":
+ openModal("config-encoder")
+ break
+ default:
+ break
+ }
+ }}
+ onCancel={() => {
+ openModal("roborio")
+ }}
+ >
+ {
+ setType(selected)
+ }}
+ />
+
+ )
+}
+
+export default RCCreateDeviceModal
diff --git a/fission/src/modals/configuring/theme-editor/DeleteAllThemesModal.tsx b/fission/src/modals/configuring/theme-editor/DeleteAllThemesModal.tsx
new file mode 100644
index 0000000000..0eb6525adc
--- /dev/null
+++ b/fission/src/modals/configuring/theme-editor/DeleteAllThemesModal.tsx
@@ -0,0 +1,27 @@
+import React from "react"
+import Modal, { ModalPropsImpl } from "../../../components/Modal"
+import { GrFormClose } from "react-icons/gr"
+import { useModalControlContext } from "../../../ModalContext"
+import { useTheme } from "../../../ThemeContext"
+
+const DeleteAllThemesModal: React.FC = ({ modalId }) => {
+ const { openModal } = useModalControlContext()
+ const { deleteAllThemes } = useTheme()
+
+ return (
+ }
+ modalId={modalId}
+ onAccept={() => {
+ deleteAllThemes()
+ openModal("theme-editor")
+ }}
+ onCancel={() => {
+ openModal("theme-editor")
+ }}
+ >
+ )
+}
+
+export default DeleteAllThemesModal
diff --git a/fission/src/modals/configuring/theme-editor/DeleteThemeModal.tsx b/fission/src/modals/configuring/theme-editor/DeleteThemeModal.tsx
new file mode 100644
index 0000000000..90330909b4
--- /dev/null
+++ b/fission/src/modals/configuring/theme-editor/DeleteThemeModal.tsx
@@ -0,0 +1,28 @@
+import React from "react"
+import Modal, { ModalPropsImpl } from "../../../components/Modal"
+import { GrFormClose } from "react-icons/gr"
+import { useModalControlContext } from "../../../ModalContext"
+import { useTheme } from "../../../ThemeContext"
+
+const DeleteThemeModal: React.FC = ({ modalId }) => {
+ const { currentTheme, deleteTheme } = useTheme()
+
+ const { openModal } = useModalControlContext()
+
+ return (
+ }
+ modalId={modalId}
+ onAccept={() => {
+ deleteTheme(currentTheme)
+ openModal("theme-editor")
+ }}
+ onCancel={() => {
+ openModal("theme-editor")
+ }}
+ >
+ )
+}
+
+export default DeleteThemeModal
diff --git a/fission/src/modals/configuring/theme-editor/NewThemeModal.tsx b/fission/src/modals/configuring/theme-editor/NewThemeModal.tsx
new file mode 100644
index 0000000000..8e77df75da
--- /dev/null
+++ b/fission/src/modals/configuring/theme-editor/NewThemeModal.tsx
@@ -0,0 +1,33 @@
+import React, { useState } from "react"
+import Modal, { ModalPropsImpl } from "../../../components/Modal"
+import { useModalControlContext } from "../../../ModalContext"
+import { FaPlus } from "react-icons/fa6"
+import Input from "../../../components/Input"
+import { useTheme } from "../../../ThemeContext"
+
+const NewThemeModal: React.FC = ({ modalId }) => {
+ const { openModal } = useModalControlContext()
+ const { createTheme, setTheme } = useTheme()
+ const [themeName, setThemeName] = useState("")
+
+ return (
+ }
+ modalId={modalId}
+ acceptBlocked={!themeName}
+ onAccept={() => {
+ createTheme(themeName)
+ setTheme(themeName)
+ openModal("theme-editor")
+ }}
+ onCancel={() => {
+ openModal("theme-editor")
+ }}
+ >
+
+
+ )
+}
+
+export default NewThemeModal
diff --git a/fission/src/modals/configuring/theme-editor/ThemeEditorModal.tsx b/fission/src/modals/configuring/theme-editor/ThemeEditorModal.tsx
new file mode 100644
index 0000000000..3c892ca160
--- /dev/null
+++ b/fission/src/modals/configuring/theme-editor/ThemeEditorModal.tsx
@@ -0,0 +1,184 @@
+import { useModalControlContext } from "@/ModalContext"
+import {
+ ColorName,
+ Theme,
+ useTheme
+} from "@/ThemeContext"
+import Button from "@/components/Button"
+import Dropdown from "@/components/Dropdown"
+import Modal, { ModalPropsImpl } from "@/components/Modal"
+import Stack, { StackDirection } from "@/components/Stack"
+import { Random } from "@/util/Random"
+import { extend as cdExtend, random as cdRandom, colord } from "colord"
+import a11yPlugin from "colord/plugins/a11y"
+import React, { useState } from "react"
+import { HexColorInput, RgbaColor, RgbaColorPicker } from "react-colorful"
+import { AiFillWarning } from "react-icons/ai"
+import { FaChessBoard } from "react-icons/fa6"
+cdExtend([a11yPlugin])
+
+const ThemeEditorModal: React.FC = ({ modalId }) => {
+ const readabilityIndicator: boolean = false;
+ const { themes, initialThemeName, currentTheme, setTheme, updateColor, applyTheme } =
+ useTheme()
+
+ const { openModal } = useModalControlContext();
+
+ const [selectedColor, setSelectedColor] = useState(
+ "InteractiveElementSolid"
+ )
+ const [selectedTheme, setSelectedTheme] = useState(currentTheme)
+ const [, setCurrentColor] = useState({ r: 0, g: 0, b: 0, a: 0 })
+ // needs to be useState so it doesn't get reset on re-render
+ const [initialThemeValues] = useState(JSON.parse(JSON.stringify(themes[selectedTheme])));
+
+ return (
+ }
+ modalId={modalId}
+ middleEnabled={true}
+ middleName="Preview"
+ onAccept={() => {
+ applyTheme(selectedTheme)
+ setTheme(selectedTheme)
+ }}
+ onMiddle={() => {
+ applyTheme(selectedTheme)
+ }}
+ onCancel={() => {
+ themes[currentTheme] = initialThemeValues
+ applyTheme(currentTheme)
+ }}
+ >
+
+
+
+ t != currentTheme
+ ),
+ ]}
+ onSelect={setSelectedTheme}
+ className="h-min"
+ />
+
+ {
+ openModal("new-theme")
+ }}
+ />
+ {
+ openModal("delete-theme")
+ }}
+ />
+ {false && ( {
+ openModal("delete-all-themes")
+ }}
+ />)}
+
+
+
+ {
+ if (selectedTheme == initialThemeName) return
+ setCurrentColor(c)
+ updateColor(selectedTheme, selectedColor, c)
+ }}
+ />
+ {
+ if (selectedTheme == initialThemeName) return
+ const color = colord(c).toRgb();
+ setCurrentColor(color)
+ updateColor(selectedTheme, selectedColor, color)
+
+ }}
+ />
+ {
+ if (selectedTheme == initialThemeName) return;
+ const keys: ColorName[] = Object.keys(themes[selectedTheme]) as ColorName[];
+ keys.forEach(k => {
+ const randAlpha = () => Math.max(0.1, Random());
+ updateColor(selectedTheme, k, { ...cdRandom().toRgb(), a: randAlpha() } as RgbaColor);
+ })
+ applyTheme(selectedTheme)
+ setSelectedTheme(selectedTheme)
+ setCurrentColor(selectedTheme && themes[selectedTheme] && selectedColor ? themes[selectedTheme][selectedColor].color : { r: 0, g: 0, b: 0, a: 0 })
+ setSelectedColor(selectedColor)
+ }} />
+
+
+
+
+ {Object.entries(themes[selectedTheme]).map(([n, c]) => (
+
{
+ if (currentTheme == initialThemeName) return;
+ setSelectedColor(n as ColorName)
+ }}
+ >
+
+
{n}
+ {readabilityIndicator && (() => {
+ if (c.color.a < 0.1 || ((n.toLowerCase().includes("text") || n.toLowerCase().includes("icon")) && !n.toLowerCase().includes("closeicon"))) {
+ const aboveColors = c.above.map((above: (ColorName | string)) => typeof (above) == "string" ? above : themes[selectedTheme][above]);
+ const conflicting = aboveColors.map((above: string | RgbaColor) => [above, colord(c.color).isReadable(above)]).filter(c => !c[1]).map(([n,]) => n);
+
+ if (currentTheme != initialThemeName && (conflicting.length > 0 || c.color.a < 0.1))
+ return (
+
)
+ else return (
)
+ }
+ })()
+ }
+
+ ))}
+
+
+
+
+ )
+}
+
+export default ThemeEditorModal
diff --git a/fission/src/modals/spawning/AddFieldModal.tsx b/fission/src/modals/spawning/AddFieldModal.tsx
new file mode 100644
index 0000000000..f79c8dfd08
--- /dev/null
+++ b/fission/src/modals/spawning/AddFieldModal.tsx
@@ -0,0 +1,23 @@
+import React from "react"
+import Modal, { ModalPropsImpl } from "../../components/Modal"
+import { FaPlus } from "react-icons/fa6"
+import Dropdown from "../../components/Dropdown"
+
+const FieldsModal: React.FC = ({ modalId }) => {
+ return (
+ } modalId={modalId}>
+ {}}
+ />
+
+ )
+}
+
+export default FieldsModal
diff --git a/fission/src/modals/spawning/AddRobotModal.tsx b/fission/src/modals/spawning/AddRobotModal.tsx
new file mode 100644
index 0000000000..622ef65c61
--- /dev/null
+++ b/fission/src/modals/spawning/AddRobotModal.tsx
@@ -0,0 +1,47 @@
+import React from "react"
+import Modal, { ModalPropsImpl } from "../../components/Modal"
+import { FaPlus } from "react-icons/fa6"
+import Dropdown from "../../components/Dropdown"
+import { useTooltipControlContext } from "@/TooltipContext"
+import { CreateMirabufFromUrl } from "@/mirabuf/MirabufSceneObject"
+import GetSceneRenderer from "@/systems/scene/SceneRenderer"
+
+const RobotsModal: React.FC = ({ modalId }) => {
+ // update tooltip based on type of drivetrain, receive message from Synthesis
+ const { showTooltip } = useTooltipControlContext()
+
+ let selectedRobot: string | null = null;
+
+ return (
+ }
+ modalId={modalId}
+ onAccept={() => {
+ showTooltip("controls", [
+ { control: "WASD", description: "Drive" },
+ { control: "E", description: "Intake" },
+ { control: "Q", description: "Dispense" },
+ ]);
+
+ if (selectedRobot) {
+ CreateMirabufFromUrl(`test_mira/${selectedRobot}`).then(x => {
+ if (x) {
+ GetSceneRenderer().RegisterSceneObject(x);
+ }
+ });
+ }
+ }
+ }
+ >
+ {
+ selectedRobot = op;
+ }}
+ />
+
+ )
+}
+
+export default RobotsModal
diff --git a/fission/src/modals/spawning/ManageAssembliesModal.tsx b/fission/src/modals/spawning/ManageAssembliesModal.tsx
new file mode 100644
index 0000000000..82c4d658b5
--- /dev/null
+++ b/fission/src/modals/spawning/ManageAssembliesModal.tsx
@@ -0,0 +1,48 @@
+import React from "react"
+import Modal, { ModalPropsImpl } from "../../components/Modal"
+import { FaPlus } from "react-icons/fa6"
+import ScrollView from "@/components/ScrollView"
+
+const ManageAssembliesModal: React.FC = ({ modalId }) => {
+ // update tooltip based on type of drivetrain, receive message from Synthesis
+ // const { showTooltip } = useTooltipControlContext()
+
+ return (
+ }
+ modalId={modalId}
+ onAccept={() => {
+ // showTooltip("controls", [
+ // { control: "WASD", description: "Drive" },
+ // { control: "E", description: "Intake" },
+ // { control: "Q", description: "Dispense" },
+ // ]);
+ }
+ }
+ >
+
+ Hello
+ Hello
+ Hello
+ Hello
+ Hello
+ Hello
+ Hello
+ Hello
+ Hello
+ Hello
+ Hello
+ Hello
+ Hello
+ Hello
+ Hello
+
+ }>
+
+
+ )
+}
+
+export default ManageAssembliesModal
\ No newline at end of file
diff --git a/fission/src/modals/spawning/MatchModeModal.tsx b/fission/src/modals/spawning/MatchModeModal.tsx
new file mode 100644
index 0000000000..869a547d46
--- /dev/null
+++ b/fission/src/modals/spawning/MatchModeModal.tsx
@@ -0,0 +1,44 @@
+import { FaGear } from "react-icons/fa6"
+import Modal, { ModalPropsImpl } from "../../components/Modal"
+import Dropdown from "../../components/Dropdown"
+import Label, { LabelSize } from "../../components/Label"
+
+const MatchModeModal: React.FC = ({ modalId }) => {
+ const robotsPerAlliance = 3
+ const robots = ["Dozer_v9.mira", "Team_2471_2018_v7.mira", "None"]
+ const fields = [
+ "FRC Field 2018_v12.mira",
+ "FRC Field 2019_v10.mira",
+ "FRC Field 2020-21_v4.mira",
+ "FRC Field 2022_v4.mira",
+ "FRC_Field_2023_v7.mira",
+ ]
+
+ return (
+ }
+ modalId={modalId}
+ acceptName="Load"
+ cancelEnabled={false}
+ onAccept={() => {}}
+ >
+
+ {Array(robotsPerAlliance)
+ .fill(0)
+ .map(() => (
+ {}} />
+ ))}
+
+ {Array(robotsPerAlliance)
+ .fill(0)
+ .map(() => (
+ {}} />
+ ))}
+
+ {}} />
+
+ )
+}
+
+export default MatchModeModal
diff --git a/fission/src/modals/spawning/SpawningModal.tsx b/fission/src/modals/spawning/SpawningModal.tsx
new file mode 100644
index 0000000000..ede69dbd30
--- /dev/null
+++ b/fission/src/modals/spawning/SpawningModal.tsx
@@ -0,0 +1,24 @@
+import { FaPlus } from "react-icons/fa6"
+import Modal, { ModalPropsImpl } from "../../components/Modal"
+import Stack, { StackDirection } from "../../components/Stack"
+import Button from "../../components/Button"
+import { useModalControlContext } from "../../ModalContext"
+
+const SpawningModal: React.FC = ({ modalId }) => {
+ const { openModal } = useModalControlContext()
+ return (
+ }
+ modalId={modalId}
+ acceptEnabled={false}
+ >
+
+ openModal("add-robot")} />
+ openModal("add-field")} />
+
+
+ )
+}
+
+export default SpawningModal
diff --git a/fission/src/panels/RobotSwitchPanel.tsx b/fission/src/panels/RobotSwitchPanel.tsx
new file mode 100644
index 0000000000..3147e164c3
--- /dev/null
+++ b/fission/src/panels/RobotSwitchPanel.tsx
@@ -0,0 +1,46 @@
+import React, { useState } from "react"
+import Label, { LabelSize } from "../components/Label"
+import Panel, { PanelPropsImpl } from "../components/Panel"
+import { IoPeople } from "react-icons/io5"
+import Stack, { StackDirection } from "../components/Stack"
+import Button from "../components/Button"
+import { useModalControlContext } from "../ModalContext"
+import Checkbox from "../components/Checkbox"
+
+const RobotSwitchPanel: React.FC = ({ panelId }) => {
+ const [robots, setRobots] = useState([
+ "Dozer_v9_0",
+ "Team 2471 (2018) v7_0",
+ ])
+ const [selected, setSelected] = useState(0)
+ const { openModal } = useModalControlContext()
+ return (
+ } panelId={panelId}>
+
+
+
+ openModal("robots")} />
+
+ setRobots(robots.filter(r => r !== robots[selected]))
+ }
+ />
+
+
+ )
+}
+
+export default RobotSwitchPanel
diff --git a/fission/src/panels/SpawnLocationPanel.tsx b/fission/src/panels/SpawnLocationPanel.tsx
new file mode 100644
index 0000000000..f5b44124a4
--- /dev/null
+++ b/fission/src/panels/SpawnLocationPanel.tsx
@@ -0,0 +1,46 @@
+import { useTooltipControlContext } from "@/TooltipContext"
+import Button from "../components/Button"
+import Panel, { PanelPropsImpl } from "../components/Panel"
+
+const SpawnLocationsPanel: React.FC = ({ panelId }) => {
+ const robotsPerAlliance = 3
+ const alliances = 2
+
+ const { showTooltip } = useTooltipControlContext()
+ showTooltip("controls", [
+ { control: "Scroll", description: "Rotate Robot" },
+ { control: "Shift", description: "Hold to Snap" },
+ ])
+
+ return (
+
+
+
+ {Array(alliances)
+ .fill(0)
+ .map((n, i) => (
+
+ {Array(robotsPerAlliance)
+ .fill(0)
+ .map((o: number, j: number) => (
+
+
+ |
+ ))}
+
+ ))}
+
+
+
+ )
+}
+
+export default SpawnLocationsPanel
diff --git a/fission/src/panels/configuring/ConfigureGamepiecePickupPanel.tsx b/fission/src/panels/configuring/ConfigureGamepiecePickupPanel.tsx
new file mode 100644
index 0000000000..371d59a92f
--- /dev/null
+++ b/fission/src/panels/configuring/ConfigureGamepiecePickupPanel.tsx
@@ -0,0 +1,36 @@
+import { useState } from "react"
+import { FaGear } from "react-icons/fa6"
+import Panel, { PanelPropsImpl } from "../../components/Panel"
+import SelectButton from "../../components/SelectButton"
+import Slider from "../../components/Slider"
+
+const ConfigureGamepiecePickupPanel: React.FC = ({
+ panelId,
+}) => {
+ const defaultZoneSize = 0.5
+ const [, setNode] = useState("Click to select")
+ const [, setZoneSize] = useState(defaultZoneSize)
+
+ return (
+ }
+ panelId={panelId}
+ onAccept={() => {
+ // send zone config
+ }}
+ >
+
+
+
+ )
+}
+
+export default ConfigureGamepiecePickupPanel
diff --git a/fission/src/panels/configuring/ConfigureShotTrajectoryPanel.tsx b/fission/src/panels/configuring/ConfigureShotTrajectoryPanel.tsx
new file mode 100644
index 0000000000..8df2edf41a
--- /dev/null
+++ b/fission/src/panels/configuring/ConfigureShotTrajectoryPanel.tsx
@@ -0,0 +1,36 @@
+import { useState } from "react"
+import { FaGear } from "react-icons/fa6"
+import Panel, { PanelPropsImpl } from "../../components/Panel"
+import SelectButton from "../../components/SelectButton"
+import Slider from "../../components/Slider"
+
+const ConfigureShotTrajectoryPanel: React.FC = ({
+ panelId,
+}) => {
+ const defaultShootSpeed = 5
+ const [, setNode] = useState("Click to select")
+ const [, setShootSpeed] = useState(defaultShootSpeed)
+
+ return (
+ }
+ panelId={panelId}
+ onAccept={() => {
+ // send node and speed config
+ }}
+ >
+
+
+
+ )
+}
+
+export default ConfigureShotTrajectoryPanel
diff --git a/fission/src/panels/configuring/scoring/ScoringZonesPanel.tsx b/fission/src/panels/configuring/scoring/ScoringZonesPanel.tsx
new file mode 100644
index 0000000000..9483629a97
--- /dev/null
+++ b/fission/src/panels/configuring/scoring/ScoringZonesPanel.tsx
@@ -0,0 +1,254 @@
+import { useState } from "react"
+import { usePanelControlContext } from "@/PanelContext"
+import Button from "@/components/Button"
+import Label, { LabelSize } from "@/components/Label"
+import Panel, { PanelPropsImpl } from "@/components/Panel"
+import ScrollView from "@/components/ScrollView"
+import Stack, { StackDirection } from "@/components/Stack"
+import { ScoringZone } from "./ZoneConfigPanel"
+
+type ScoringZoneRowProps = {
+ zone: ScoringZone
+ openPanel: (id: string) => void
+ deleteZone: () => void
+}
+
+const ScoringZoneRow: React.FC = ({
+ zone,
+ openPanel,
+ deleteZone,
+}) => {
+ return (
+
+
+
+
+
+
+
+
+
+ openPanel("zone-config")} />
+
+
+
+ )
+}
+
+const ScoringZonesPanel: React.FC = ({ panelId }) => {
+ const { openPanel } = usePanelControlContext()
+ const [zones, setZones] = useState([
+ {
+ name: "Blue 1",
+ alliance: "blue",
+ parent: "node_0",
+ points: 1,
+ destroyGamepiece: false,
+ persistentPoints: false,
+ scale: [1, 1, 1],
+ },
+ {
+ name: "Blue 2",
+ alliance: "blue",
+ parent: "node_0",
+ points: 1,
+ destroyGamepiece: false,
+ persistentPoints: false,
+ scale: [1, 1, 1],
+ },
+ {
+ name: "Blue 3",
+ alliance: "blue",
+ parent: "node_0",
+ points: 1,
+ destroyGamepiece: false,
+ persistentPoints: false,
+ scale: [1, 1, 1],
+ },
+ {
+ name: "Blue 4",
+ alliance: "blue",
+ parent: "node_0",
+ points: 1,
+ destroyGamepiece: false,
+ persistentPoints: false,
+ scale: [1, 1, 1],
+ },
+ {
+ name: "Blue 5",
+ alliance: "blue",
+ parent: "node_0",
+ points: 1,
+ destroyGamepiece: false,
+ persistentPoints: false,
+ scale: [1, 1, 1],
+ },
+ {
+ name: "Blue 6",
+ alliance: "blue",
+ parent: "node_0",
+ points: 1,
+ destroyGamepiece: false,
+ persistentPoints: false,
+ scale: [1, 1, 1],
+ },
+ {
+ name: "Blue 7",
+ alliance: "blue",
+ parent: "node_0",
+ points: 1,
+ destroyGamepiece: false,
+ persistentPoints: false,
+ scale: [1, 1, 1],
+ },
+ {
+ name: "Blue 8",
+ alliance: "blue",
+ parent: "node_0",
+ points: 1,
+ destroyGamepiece: false,
+ persistentPoints: false,
+ scale: [1, 1, 1],
+ },
+ {
+ name: "Blue 9",
+ alliance: "blue",
+ parent: "node_0",
+ points: 1,
+ destroyGamepiece: false,
+ persistentPoints: false,
+ scale: [1, 1, 1],
+ },
+ {
+ name: "Blue 10",
+ alliance: "blue",
+ parent: "node_0",
+ points: 1,
+ destroyGamepiece: false,
+ persistentPoints: false,
+ scale: [1, 1, 1],
+ },
+ {
+ name: "Blue 11",
+ alliance: "blue",
+ parent: "node_0",
+ points: 1,
+ destroyGamepiece: false,
+ persistentPoints: false,
+ scale: [1, 1, 1],
+ },
+ {
+ name: "Blue 12",
+ alliance: "blue",
+ parent: "node_0",
+ points: 1,
+ destroyGamepiece: false,
+ persistentPoints: false,
+ scale: [1, 1, 1],
+ },
+ {
+ name: "Blue 13",
+ alliance: "blue",
+ parent: "node_0",
+ points: 1,
+ destroyGamepiece: false,
+ persistentPoints: false,
+ scale: [1, 1, 1],
+ },
+ {
+ name: "Blue 14",
+ alliance: "blue",
+ parent: "node_0",
+ points: 1,
+ destroyGamepiece: false,
+ persistentPoints: false,
+ scale: [1, 1, 1],
+ },
+ {
+ name: "Blue 15",
+ alliance: "blue",
+ parent: "node_0",
+ points: 1,
+ destroyGamepiece: false,
+ persistentPoints: false,
+ scale: [1, 1, 1],
+ },
+ {
+ name: "Blue 16",
+ alliance: "blue",
+ parent: "node_0",
+ points: 1,
+ destroyGamepiece: false,
+ persistentPoints: false,
+ scale: [1, 1, 1],
+ },
+ {
+ name: "Blue 17",
+ alliance: "blue",
+ parent: "node_0",
+ points: 1,
+ destroyGamepiece: false,
+ persistentPoints: false,
+ scale: [1, 1, 1],
+ },
+ {
+ name: "Red 1",
+ alliance: "red",
+ parent: "node_0",
+ points: 1,
+ destroyGamepiece: false,
+ persistentPoints: false,
+ scale: [1, 1, 1],
+ },
+ ])
+
+ return (
+
+
+ {zones.map((z: ScoringZone, i: number) => (
+ {
+ setZones(zones.filter((_, idx) => idx !== i))
+ }}
+ />
+ ))}
+
+ openPanel("zone-config")}
+ className="px-36 w-full"
+ />
+
+ )
+}
+
+export default ScoringZonesPanel
diff --git a/fission/src/panels/configuring/scoring/ZoneConfigPanel.tsx b/fission/src/panels/configuring/scoring/ZoneConfigPanel.tsx
new file mode 100644
index 0000000000..e92c73d689
--- /dev/null
+++ b/fission/src/panels/configuring/scoring/ZoneConfigPanel.tsx
@@ -0,0 +1,109 @@
+import { useState } from "react"
+import Input from "@/components/Input"
+import Panel, { PanelPropsImpl } from "@/components/Panel"
+import Button from "@/components/Button"
+import SelectButton from "@/components/SelectButton"
+import Checkbox from "@/components/Checkbox"
+import Slider from "@/components/Slider"
+
+export type ScoringZone = {
+ name: string
+ alliance: "red" | "blue"
+ parent: string
+ points: number
+ destroyGamepiece: boolean
+ persistentPoints: boolean
+ scale: [number, number, number]
+}
+
+const ZoneConfigPanel: React.FC = ({ panelId }) => {
+ // somehow get and store which zone is being edited
+ // maybe a global ConfigProvider in App.tsx?
+ // then set all default values to the state of the zone
+ const [, setName] = useState("")
+ const [alliance, setAlliance] = useState<"red" | "blue">("blue")
+ const [, setParent] = useState("")
+ const [, setPoints] = useState(1)
+ const [, setDestroy] = useState(false)
+ const [, setPersistent] = useState(false)
+ const [, setScale] = useState<[number, number, number]>([1, 1, 1])
+
+ return (
+
+
+ setAlliance(alliance == "blue" ? "red" : "blue")}
+ colorClass={`bg-match-${alliance}-alliance`}
+ />
+ setParent(p)}
+ />
+ setPoints(parseInt(v))}
+ numeric
+ />
+
+
+
+ setScale(s => {
+ s[0] = v
+ return s
+ })
+ }
+ />
+
+ setScale(s => {
+ s[1] = v
+ return s
+ })
+ }
+ />
+
+ setScale(s => {
+ s[2] = v
+ return s
+ })
+ }
+ />
+
+ )
+}
+
+export default ZoneConfigPanel
diff --git a/fission/src/panels/information/ScoreboardPanel.tsx b/fission/src/panels/information/ScoreboardPanel.tsx
new file mode 100644
index 0000000000..8dd07f1ec1
--- /dev/null
+++ b/fission/src/panels/information/ScoreboardPanel.tsx
@@ -0,0 +1,69 @@
+import { useCallback, useEffect, useState } from "react"
+import Label, { LabelSize } from "../../components/Label"
+import Panel, { PanelPropsImpl } from "../../components/Panel"
+import Stack, { StackDirection } from "../../components/Stack"
+
+const ScoreboardPanel: React.FC = ({ panelId }) => {
+ const [redScore] = useState(0)
+ const [blueScore] = useState(0)
+ const [initialTime, setInitialTime] = useState(-1)
+ const [startTime, setStartTime] = useState(Date.now())
+ const [time, setTime] = useState(-1)
+
+ // probably useless code because the time left should be sent by Synthesis and not calculated here
+ const startTimer = useCallback(
+ async (t: number) => {
+ setInitialTime(t)
+ setTime(t)
+ setStartTime(Date.now())
+ },
+ [setInitialTime, setTime, setStartTime]
+ )
+
+ useEffect(() => {
+ const interval: NodeJS.Timer = setInterval(() => {
+ const elapsed = Math.round((Date.now() - startTime) / 1_000)
+ if (initialTime > 0) {
+ if (elapsed <= initialTime) setTime(initialTime - elapsed)
+ else {
+ clearInterval(interval)
+ }
+ }
+ })
+ }, [initialTime, time, startTime])
+
+ useEffect(() => {
+ if (initialTime == -1) startTimer(15)
+ }, [initialTime, startTimer])
+
+ return (
+
+ {time >= 0 && (
+
+
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default ScoreboardPanel
diff --git a/fission/src/panels/simulation/DriverStationPanel.tsx b/fission/src/panels/simulation/DriverStationPanel.tsx
new file mode 100644
index 0000000000..ea80a56e32
--- /dev/null
+++ b/fission/src/panels/simulation/DriverStationPanel.tsx
@@ -0,0 +1,28 @@
+import React, { useState } from "react"
+import Panel, { PanelPropsImpl } from "../../components/Panel"
+import { GiSteeringWheel } from "react-icons/gi"
+import Stack, { StackDirection } from "../../components/Stack"
+import Button from "../../components/Button"
+import Dropdown from "../../components/Dropdown"
+
+const DriverStationPanel: React.FC = ({ panelId }) => {
+ const [enabled, setEnabled] = useState(false)
+
+ return (
+ }
+ panelId={panelId}
+ >
+
+ setEnabled(!enabled)}
+ />
+ {}} />
+
+
+ )
+}
+
+export default DriverStationPanel
diff --git a/fission/src/samples/JoltExample.tsx b/fission/src/samples/JoltExample.tsx
new file mode 100644
index 0000000000..91aea2f9dd
--- /dev/null
+++ b/fission/src/samples/JoltExample.tsx
@@ -0,0 +1,231 @@
+/**
+ * This example will be used to showcase how Jolt physics works.
+ */
+
+import * as THREE from 'three';
+import Stats from 'stats.js';
+import JOLT from '../util/loading/JoltSyncLoader.ts';
+import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
+
+import { useEffect, useRef } from 'react';
+import Jolt from '@barclah/jolt-physics';
+import { mirabuf } from "../proto/mirabuf"
+import { LoadMirabufRemote } from '../mirabuf/MirabufLoader.ts';
+import { JoltVec3_ThreeVector3, JoltQuat_ThreeQuaternion } from '../util/TypeConversions.ts';
+import { COUNT_OBJECT_LAYERS, LAYER_MOVING, LAYER_NOT_MOVING, addToScene, removeFromScene } from '../util/threejs/MeshCreation.ts';
+import MirabufInstance from '../mirabuf/MirabufInstance.ts';
+import MirabufParser, { ParseErrorSeverity } from '../mirabuf/MirabufParser.ts';
+
+const clock = new THREE.Clock();
+let time = 0;
+
+let stats: Stats;
+
+let renderer: THREE.WebGLRenderer;
+let camera: THREE.PerspectiveCamera;
+let scene: THREE.Scene;
+
+let joltInterface: Jolt.JoltInterface;
+let physicsSystem: Jolt.PhysicsSystem;
+let bodyInterface: Jolt.BodyInterface;
+
+const dynamicObjects: THREE.Mesh[] = [];
+
+const MIRA_FILE = "test_mira/Team_2471_(2018)_v7.mira"
+// const MIRA_FILE = "test_mira/Dozer_v2.mira"
+
+let controls: OrbitControls;
+
+
+// vvv Below are the functions required to initialize everything and draw a basic floor with collisions. vvv
+
+function setupCollisionFiltering(settings: Jolt.JoltSettings) {
+ const objectFilter = new JOLT.ObjectLayerPairFilterTable(COUNT_OBJECT_LAYERS);
+ objectFilter.EnableCollision(LAYER_NOT_MOVING, LAYER_MOVING);
+ objectFilter.EnableCollision(LAYER_MOVING, LAYER_MOVING);
+
+ const BP_LAYER_NOT_MOVING = new JOLT.BroadPhaseLayer(LAYER_NOT_MOVING);
+ const BP_LAYER_MOVING = new JOLT.BroadPhaseLayer(LAYER_MOVING);
+ const COUNT_BROAD_PHASE_LAYERS = 2;
+
+ const bpInterface = new JOLT.BroadPhaseLayerInterfaceTable(COUNT_OBJECT_LAYERS, COUNT_BROAD_PHASE_LAYERS);
+ bpInterface.MapObjectToBroadPhaseLayer(LAYER_NOT_MOVING, BP_LAYER_NOT_MOVING);
+ bpInterface.MapObjectToBroadPhaseLayer(LAYER_MOVING, BP_LAYER_MOVING);
+
+ settings.mObjectLayerPairFilter = objectFilter;
+ settings.mBroadPhaseLayerInterface = bpInterface;
+ settings.mObjectVsBroadPhaseLayerFilter = new JOLT.ObjectVsBroadPhaseLayerFilterTable(settings.mBroadPhaseLayerInterface, COUNT_BROAD_PHASE_LAYERS, settings.mObjectLayerPairFilter, COUNT_OBJECT_LAYERS);
+}
+
+function initPhysics() {
+ const settings = new JOLT.JoltSettings();
+ setupCollisionFiltering(settings);
+ joltInterface = new JOLT.JoltInterface(settings);
+ JOLT.destroy(settings);
+
+ physicsSystem = joltInterface.GetPhysicsSystem();
+ bodyInterface = physicsSystem.GetBodyInterface();
+}
+
+function initGraphics() {
+ camera = new THREE.PerspectiveCamera(
+ 75,
+ window.innerWidth / window.innerHeight,
+ 0.1,
+ 1000
+ );
+
+ camera.position.set(-5, 4, 5);
+
+ scene = new THREE.Scene();
+
+ renderer = new THREE.WebGLRenderer();
+ renderer.setClearColor(0x121212);
+ renderer.setPixelRatio(window.devicePixelRatio);
+ renderer.shadowMap.enabled = true;
+ renderer.shadowMap.type = THREE.PCFSoftShadowMap;
+ renderer.setSize(window.innerWidth, window.innerHeight);
+
+ controls = new OrbitControls(camera, renderer.domElement);
+ controls.update();
+
+ const directionalLight = new THREE.DirectionalLight(0xffffff, 3.0);
+ directionalLight.position.set(-1.0, 3.0, 2.0);
+ directionalLight.castShadow = true;
+ scene.add(directionalLight);
+
+ const shadowMapSize = Math.min(4096, renderer.capabilities.maxTextureSize);
+ const shadowCamSize = 15;
+ console.debug(`Shadow Map Size: ${shadowMapSize}`);
+
+ directionalLight.shadow.camera.top = shadowCamSize;
+ directionalLight.shadow.camera.bottom = -shadowCamSize;
+ directionalLight.shadow.camera.left = -shadowCamSize;
+ directionalLight.shadow.camera.right = shadowCamSize;
+ directionalLight.shadow.mapSize = new THREE.Vector2(shadowMapSize, shadowMapSize);
+ directionalLight.shadow.blurSamples = 16;
+ directionalLight.shadow.normalBias = 0.01;
+ directionalLight.shadow.bias = 0.00;
+
+ const ambientLight = new THREE.AmbientLight(0xffffff, 0.1);
+ scene.add(ambientLight);
+
+ // TODO: Add controls.
+
+ // TODO: Add resize event
+}
+
+function createFloor(size = 50) {
+ const shape = new JOLT.BoxShape(new JOLT.Vec3(size, 0.5, size), 0.05, undefined);
+ const position = new JOLT.Vec3(0, -0.5, 0);
+ const rotation = new JOLT.Quat(0, 0, 0, 1);
+ const creationSettings = new JOLT.BodyCreationSettings(shape, position, rotation, JOLT.EMotionType_Static, LAYER_NOT_MOVING)
+ const body = bodyInterface.CreateBody(creationSettings);
+ JOLT.destroy(position);
+ JOLT.destroy(rotation);
+ JOLT.destroy(creationSettings);
+ addToScene(scene, body, new THREE.Color(0xc7c7c7), bodyInterface, dynamicObjects);
+
+ return body;
+}
+
+function updatePhysics(deltaTime: number) {
+ // If below 55hz run 2 steps. Otherwise things run very slow.
+ const numSteps = deltaTime > 1.0 / 55.0 ? 2 : 1;
+ joltInterface.Step(deltaTime, numSteps);
+}
+
+function render() {
+ stats.update();
+ requestAnimationFrame(render);
+ controls.update();
+
+ // Prevents a problem when rendering at 30hz. Referred to as the spiral of death.
+ let deltaTime = clock.getDelta();
+ deltaTime = Math.min(deltaTime, 1.0 / 30.0);
+
+ // Update transforms.
+ for (let i = 0, j = dynamicObjects.length; i < j; i++) {
+ const threeObj = dynamicObjects[i];
+ const body = threeObj.userData.body;
+ threeObj.position.copy(JoltVec3_ThreeVector3(body.GetPosition()));
+ threeObj.quaternion.copy(JoltQuat_ThreeQuaternion(body.GetRotation()));
+
+ if (body.GetBodyType() === JOLT.EBodyType_SoftBody) {
+ // TODO: Special soft body handle.
+ }
+ }
+
+ time += deltaTime;
+ updatePhysics(1.0 / 60.0);
+ // controls.update(deltaTime); // TODO: Add controls?
+ renderer.render(scene, camera);
+}
+
+function MyThree() {
+ console.log("Running...");
+
+ const refContainer = useRef(null);
+ const urlParams = new URLSearchParams(document.location.search);
+ let mira_path = MIRA_FILE;
+
+ urlParams.forEach((v, k, p) => console.debug(`${k}: ${v}`));
+
+ if (urlParams.has("mira")) {
+ mira_path = `test_mira/${urlParams.get("mira")!}`;
+ console.debug(`Selected Mirabuf File: ${mira_path}`);
+ }
+ console.log(urlParams)
+
+ const addMiraToScene = (assembly: mirabuf.Assembly | undefined) => {
+ if (!assembly) {
+ console.error('Assembly is undefined');
+ return;
+ }
+
+ const parser = new MirabufParser(assembly);
+ if (parser.maxErrorSeverity >= ParseErrorSeverity.Unimportable) {
+ console.error(`Assembly Parser produced significant errors for '${assembly.info!.name!}'`);
+ return;
+ }
+
+ const instance = new MirabufInstance(parser);
+ instance.AddToScene(scene);
+ }
+
+ useEffect(() => {
+ LoadMirabufRemote(mira_path)
+ .then(
+ (assembly: mirabuf.Assembly | undefined) => addMiraToScene(assembly)
+ ).catch(
+ _ => LoadMirabufRemote(MIRA_FILE).then(
+ (assembly: mirabuf.Assembly | undefined) => addMiraToScene(assembly)
+ )
+ ).catch(console.error);
+
+ initGraphics();
+
+ if (refContainer.current) {
+ refContainer.current.innerHTML = "";
+ refContainer.current.appendChild(renderer.domElement)
+
+ stats = new Stats();
+ stats.dom.style.position = 'absolute';
+ stats.dom.style.top = '0px';
+ refContainer.current.appendChild(stats.dom);
+ }
+
+ initPhysics();
+ render();
+
+ // createFloor();
+ }, []);
+
+ return (
+
+ );
+}
+
+export default MyThree;
diff --git a/fission/src/graphics/ThreeExample.tsx b/fission/src/samples/ThreeExample.tsx
similarity index 100%
rename from fission/src/graphics/ThreeExample.tsx
rename to fission/src/samples/ThreeExample.tsx
diff --git a/fission/src/systems/Systems.ts b/fission/src/systems/Systems.ts
deleted file mode 100644
index 6832d624f7..0000000000
--- a/fission/src/systems/Systems.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export * from "./physics/PhysicsSystem";
-export * from "./rendering/RenderSystem";
\ No newline at end of file
diff --git a/fission/src/systems/physics/PhysicsSystem.ts b/fission/src/systems/physics/PhysicsSystem.ts
index 214f2d8f0d..fcfc8a8cf8 100644
--- a/fission/src/systems/physics/PhysicsSystem.ts
+++ b/fission/src/systems/physics/PhysicsSystem.ts
@@ -1,7 +1,9 @@
-import { ThreeVector3_JoltVec3, _JoltQuat } from "../../util/TypeConversions";
+import { MirabufVector3_JoltVec3, MirabufVector3_ThreeVector3, ThreeVector3_JoltVec3, _JoltQuat } from "../../util/TypeConversions";
import JOLT from "../../util/loading/JoltSyncLoader";
import Jolt from "@barclah/jolt-physics";
import * as THREE from 'three';
+import { mirabuf } from '../../proto/mirabuf';
+import MirabufParser from "../../mirabuf/MirabufParser";
const LAYER_NOT_MOVING = 0;
const LAYER_MOVING = 1;
@@ -118,6 +120,35 @@ export class PhysicsSystem {
return settings.Create();
}
+ public CreateBodiesFromParser(parser: MirabufParser): Map {
+ const rnToBodies = new Map();
+
+ parser.rigidNodes.forEach(rn => {
+ rn.parts.forEach(partId => {
+ const partInstance = parser.assembly.data!.parts!.partInstances![partId]!;
+ const partDefinition = parser.assembly.data!.parts!.partDefinitions![partInstance.partDefinitionReference!];
+
+ });
+ });
+
+ return rnToBodies;
+ }
+
+ private CreateColliderFromPart(partDefinition: mirabuf.PartDefinition): Jolt.ShapeResult {
+ const settings = new JOLT.ConvexHullShapeSettings();
+ const points = settings.mPoints;
+ partDefinition.bodies.forEach(body => {
+ if (body.triangleMesh && body.triangleMesh.mesh && body.triangleMesh.mesh.verts) {
+ const vertArr = body.triangleMesh.mesh.verts;
+ for (let i = 0; i < body.triangleMesh.mesh.verts.length; i += 3) {
+ points.push_back(new JOLT.Vec3(vertArr[i], vertArr[i + 1], vertArr[i + 2]));
+ }
+ }
+ });
+
+ return settings.Create();
+ }
+
public Step() {
this._joltInterface.Step(STANDARD_TIME_STEP, STANDARD_SUB_STEPS);
}
diff --git a/fission/src/systems/rendering/RenderSystem.ts b/fission/src/systems/rendering/RenderSystem.ts
deleted file mode 100644
index f9e40935fc..0000000000
--- a/fission/src/systems/rendering/RenderSystem.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export class RenderSystem {
-
-}
\ No newline at end of file
diff --git a/fission/src/systems/scene/SceneObject.ts b/fission/src/systems/scene/SceneObject.ts
new file mode 100644
index 0000000000..9c1f175835
--- /dev/null
+++ b/fission/src/systems/scene/SceneObject.ts
@@ -0,0 +1,30 @@
+abstract class SceneObject {
+ private _id: number | null = null;
+
+ public get id() {
+ return this._id!;
+ }
+
+ public set id(sceneId: number) {
+ if (this._id)
+ return;
+ this._id = sceneId;
+ }
+
+ /**
+ * Setup is executed after an ID is assigned and the SceneObject is registered with the SceneRenderer
+ */
+ public abstract Setup(): void;
+
+ /**
+ * Update is executed just before rendering of the scene to allow for uniform and rendering parameter updates.
+ */
+ public abstract Update(): void;
+
+ /**
+ * Dispose is executed just before the SceneObject is unregistered and removed from the SceneRenderer.
+ */
+ public abstract Dispose(): void;
+}
+
+export default SceneObject;
\ No newline at end of file
diff --git a/fission/src/systems/scene/SceneRenderer.ts b/fission/src/systems/scene/SceneRenderer.ts
new file mode 100644
index 0000000000..f68f800dca
--- /dev/null
+++ b/fission/src/systems/scene/SceneRenderer.ts
@@ -0,0 +1,115 @@
+import * as THREE from 'three';
+import SceneObject from './SceneObject';
+
+const CLEAR_COLOR = 0x121212;
+
+let nextSceneObjectId = 1;
+
+class SceneRenderer {
+
+ private _mainCamera: THREE.PerspectiveCamera;
+ private _scene: THREE.Scene;
+ private _renderer: THREE.WebGLRenderer;
+ private _clock: THREE.Clock;
+
+ private _sceneObjects: Map;
+
+ public get mainCamera() {
+ return this._mainCamera;
+ }
+
+ public get scene() {
+ return this._scene;
+ }
+
+ public get renderer(): THREE.WebGLRenderer {
+ return this._renderer;
+ }
+
+ public constructor() {
+ this._sceneObjects = new Map();
+ this._clock = new THREE.Clock();
+
+ this._mainCamera = new THREE.PerspectiveCamera(
+ 75,
+ window.innerWidth / window.innerHeight,
+ 0.1,
+ 1000
+ );
+ this._mainCamera.position.set(-2.5, 2, 2.5);
+
+
+ this._scene = new THREE.Scene();
+
+ this._renderer = new THREE.WebGLRenderer();
+ this._renderer.setClearColor(CLEAR_COLOR);
+ this._renderer.setPixelRatio(window.devicePixelRatio);
+ this._renderer.shadowMap.enabled = true;
+ this._renderer.shadowMap.type = THREE.PCFSoftShadowMap;
+ this._renderer.setSize(window.innerWidth, window.innerHeight);
+
+ const directionalLight = new THREE.DirectionalLight(0xffffff, 3.0);
+ directionalLight.position.set(-1.0, 3.0, 2.0);
+ directionalLight.castShadow = true;
+ this._scene.add(directionalLight);
+
+ const shadowMapSize = Math.min(4096, this._renderer.capabilities.maxTextureSize);
+ const shadowCamSize = 15;
+ console.debug(`Shadow Map Size: ${shadowMapSize}`);
+
+ directionalLight.shadow.camera.top = shadowCamSize;
+ directionalLight.shadow.camera.bottom = -shadowCamSize;
+ directionalLight.shadow.camera.left = -shadowCamSize;
+ directionalLight.shadow.camera.right = shadowCamSize;
+ directionalLight.shadow.mapSize = new THREE.Vector2(shadowMapSize, shadowMapSize);
+ directionalLight.shadow.blurSamples = 16;
+ directionalLight.shadow.normalBias = 0.01;
+ directionalLight.shadow.bias = 0.00;
+
+ const ambientLight = new THREE.AmbientLight(0xffffff, 0.1);
+ this._scene.add(ambientLight);
+ }
+
+ public UpdateCanvasSize() {
+ this._renderer.setSize(window.innerWidth, window.innerHeight);
+ // No idea why height would be zero, but just incase.
+ this._mainCamera.aspect = window.innerWidth / window.innerHeight;
+ this._mainCamera.updateProjectionMatrix();
+ }
+
+ public Update() {
+ // Prevents a problem when rendering at 30hz. Referred to as the spiral of death.
+ const deltaTime = this._clock.getDelta();
+
+ this._sceneObjects.forEach(obj => {
+ obj.Update();
+ });
+
+ // controls.update(deltaTime); // TODO: Add controls?
+ this._renderer.render(this._scene, this._mainCamera);
+ }
+
+ public RegisterSceneObject(obj: T): number {
+ const id = nextSceneObjectId++;
+ obj.id = id;
+ this._sceneObjects.set(id, obj);
+ obj.Setup();
+ return id;
+ }
+
+ public RemoveAllSceneObjects() {
+ this._sceneObjects.forEach(obj => obj.Dispose());
+ this._sceneObjects.clear();
+ }
+}
+
+let instance: SceneRenderer | null = null;
+
+function GetSceneRenderer() {
+ if (!instance) {
+ instance = new SceneRenderer();
+ }
+ return instance;
+}
+
+export default GetSceneRenderer;
\ No newline at end of file
diff --git a/fission/src/test/PhysicsSystem.test.ts b/fission/src/test/PhysicsSystem.test.ts
index 4c1d9e60b2..ee56e85741 100644
--- a/fission/src/test/PhysicsSystem.test.ts
+++ b/fission/src/test/PhysicsSystem.test.ts
@@ -1,6 +1,5 @@
-/* eslint-disable @typescript-eslint/no-explicit-any */
import { test, expect, describe, assert } from 'vitest';
-import { PhysicsSystem } from '../systems/Systems';
+import { PhysicsSystem } from '../systems/physics/PhysicsSystem';
describe('Physics System Tests', () => {
test('Convex Hull Shape (Cube)', () => {
diff --git a/fission/src/test/mirabuf/MirabufParser.test.ts b/fission/src/test/mirabuf/MirabufParser.test.ts
new file mode 100644
index 0000000000..c55061f556
--- /dev/null
+++ b/fission/src/test/mirabuf/MirabufParser.test.ts
@@ -0,0 +1,87 @@
+import { describe, test, expect } from "vitest";
+
+import { mirabuf } from "../../proto/mirabuf";
+import MirabufParser, { RigidNodeReadOnly } from "../../mirabuf/MirabufParser";
+import { LoadMirabufLocal } from "../../mirabuf/MirabufLoader";
+
+describe('Mirabuf Parser Tests', () => {
+ // test('Get Dozer JSON', () => {
+ // const dozer = LoadMirabufLocal('./public/test_mira/Dozer_v2.mira');
+ // Object.values(dozer.data!.parts!.partDefinitions!).forEach(x => x.bodies = new Array());
+ // const json = JSON.stringify(dozer);
+ // console.log(json);
+ // });
+
+ test('Test Load TestCube_v1.mira (Uncompressed)', () => {
+ const testCubeMira = LoadMirabufLocal('./public/test_mira/TestCube_v1.mira');
+
+ expect(testCubeMira).toBeDefined();
+ expect(testCubeMira.info!.name!).toBe('TestCube v1');
+ });
+
+ test('Test Load FRC_Field_2018_v14.mira (Compressed)', () => {
+ const field = LoadMirabufLocal('./public/test_mira/FRC_Field_2018_v14.mira');
+
+ expect(field).toBeDefined();
+ expect(field.info!.name!).toBe('FRC Field 2018 v14');
+ });
+
+ test('Generate Rigid Nodes (TestCube_v1.mira)', () => {
+ const testCubeMira = LoadMirabufLocal('./public/test_mira/TestCube_v1.mira');
+
+ const parser = new MirabufParser(testCubeMira);
+ const rn = parser.rigidNodes;
+
+ expect(filterNonPhysicsNodes(rn, testCubeMira).length).toBe(1);
+ // printRigidNodeParts(rn, testCubeMira);
+ });
+
+ test('Generate Rigid Nodes (PhysicsSpikeTest_v1.mira)', () => {
+ const spikeMira = LoadMirabufLocal('./public/test_mira/PhysicsSpikeTest_v1.mira');
+
+ const t = new MirabufParser(spikeMira);
+ const rn = t.rigidNodes;
+
+ expect(filterNonPhysicsNodes(rn, spikeMira).length).toBe(4);
+ // printRigidNodeParts(rn, spikeMira);
+ });
+
+ test('Generate Rigid Nodes (FRC_Field_2018_v14.mira)', () => {
+ const field = LoadMirabufLocal('./public/test_mira/FRC_Field_2018_v14.mira');
+
+ const t = new MirabufParser(field);
+
+ // printRigidNodeParts(t.rigidNodes, field);
+ expect(filterNonPhysicsNodes(t.rigidNodes, field).length).toBe(34);
+ });
+
+ test('Generate Rigid Nodes (Team_2471_(2018)_v7.mira)', () => {
+ const mm = LoadMirabufLocal('./public/test_mira/Team_2471_(2018)_v7.mira');
+
+ const t = new MirabufParser(mm);
+
+ // printRigidNodeParts(t.rigidNodes, mm);
+ expect(filterNonPhysicsNodes(t.rigidNodes, mm).length).toBe(10);
+ });
+});
+
+function filterNonPhysicsNodes(nodes: RigidNodeReadOnly[], mira: mirabuf.Assembly): RigidNodeReadOnly[] {
+ return nodes.filter(x => {
+ for (const part of x.parts) {
+ const inst = mira.data!.parts!.partInstances![part]!;
+ const def = mira.data!.parts!.partDefinitions![inst.partDefinitionReference!]!;
+ if (def.bodies && def.bodies.length > 0) {
+ return true;
+ }
+ }
+ return false;
+ });
+}
+
+function printRigidNodeParts(nodes: RigidNodeReadOnly[], mira: mirabuf.Assembly) {
+ nodes.forEach(x => {
+ console.log(`[ ${x.name} ]:`);
+ x.parts.forEach(y => console.log(`-> '${mira.data!.parts!.partInstances![y]!.info!.name!}'`));
+ console.log('');
+ });
+}
\ No newline at end of file
diff --git a/fission/src/test/util/Queue.test.ts b/fission/src/test/util/Queue.test.ts
index 4a55d56422..13b0a9f985 100644
--- a/fission/src/test/util/Queue.test.ts
+++ b/fission/src/test/util/Queue.test.ts
@@ -1,6 +1,7 @@
import { test, expect, describe } from 'vitest';
import Queue from '../../util/Queue';
+import { Random } from '../../util/Random';
describe('Queue Tests', () => {
test('Create Empty', () => {
@@ -59,4 +60,17 @@ describe('Queue Tests', () => {
expect(q.size).toBe(0);
expect(q.Dequeue()).toBeUndefined();
});
+
+ test('Queue Clone (100 Elements)', () => {
+ const q1 = new Queue();
+ for (let i = 0; i < 100; i++) {
+ q1.Enqueue(Math.floor(Random() * 400));
+ }
+
+ const q2 = q1.Clone();
+ expect(q2.size).toBe(q1.size);
+ for (let i = 0; i < q1.size; i++) {
+ expect(q2.Dequeue()).toBe(q1.Dequeue());
+ }
+ });
});
\ No newline at end of file
diff --git a/fission/src/test/util/TypeConversions.test.ts b/fission/src/test/util/TypeConversions.test.ts
index 9e800d33b2..a3049957b2 100644
--- a/fission/src/test/util/TypeConversions.test.ts
+++ b/fission/src/test/util/TypeConversions.test.ts
@@ -1,9 +1,48 @@
import { test, expect, describe } from 'vitest';
import * as THREE from 'three';
-import { ThreeEuler_JoltQuat, ThreeQuaternion_JoltQuat, ThreeVector3_JoltVec3, _JoltQuat } from '../../util/TypeConversions';
+import { JoltMat44_ThreeMatrix4, MirabufTransform_ThreeMatrix4, ThreeEuler_JoltQuat, ThreeMatrix4_JoltMat44, ThreeQuaternion_JoltQuat, ThreeVector3_JoltVec3, _JoltQuat } from '../../util/TypeConversions';
+import { mirabuf } from '../../proto/mirabuf';
+import JOLT from '../../util/loading/JoltSyncLoader';
+import Jolt from '@barclah/jolt-physics';
-describe('ThreeJS to Jolt Conversions', async () => {
- test('THREE.Vector3 -> JOLT.Vec3', () => {
+describe('Three to Jolt Conversions', async () => {
+
+ function compareMat(tM: THREE.Matrix4, jM: Jolt.Mat44) {
+ const threeArr = tM.toArray();
+
+ for (let c = 0; c < 4; c++) {
+ const column = jM.GetColumn4(c);
+ for (let r = 0; r < 4; r++) {
+ expect(threeArr[c * 4 + r]).toBeCloseTo(column.GetComponent(r), 4);
+ }
+ JOLT.destroy(column);
+ }
+
+ const threeTranslation = new THREE.Vector3();
+ const threeRotation = new THREE.Quaternion();
+ const threeScale = new THREE.Vector3();
+ tM.decompose(threeTranslation, threeRotation, threeScale);
+
+ const joltTranslation = jM.GetTranslation();
+ const joltRotation = jM.GetQuaternion();
+ const joltScale = new JOLT.Vec3(1, 1, 1);
+
+ expect(joltTranslation.GetX()).toBeCloseTo(threeTranslation.x, 4);
+ expect(joltTranslation.GetY()).toBeCloseTo(threeTranslation.y, 4);
+ expect(joltTranslation.GetZ()).toBeCloseTo(threeTranslation.z, 4);
+ // JOLT.destroy(joltTranslation); // Causes error for some reason?
+ expect(joltRotation.GetX()).toBeCloseTo(threeRotation.x, 4);
+ expect(joltRotation.GetY()).toBeCloseTo(threeRotation.y, 4);
+ expect(joltRotation.GetZ()).toBeCloseTo(threeRotation.z, 4);
+ expect(joltRotation.GetW()).toBeCloseTo(threeRotation.w, 4);
+ JOLT.destroy(joltRotation);
+ expect(joltScale.GetX()).toBeCloseTo(threeScale.x, 4);
+ expect(joltScale.GetY()).toBeCloseTo(threeScale.y, 4);
+ expect(joltScale.GetZ()).toBeCloseTo(threeScale.z, 4);
+ JOLT.destroy(joltScale);
+ }
+
+ test('THREE.Vector3 -> Jolt.Vec3', () => {
const a = new THREE.Vector3(2, 4, 1);
const joltVec = ThreeVector3_JoltVec3(a);
@@ -12,7 +51,8 @@ describe('ThreeJS to Jolt Conversions', async () => {
expect(joltVec.GetZ()).toBe(a.z);
expect(joltVec.Length() - a.length()).toBeLessThan(0.0001);
});
- test('THREE.Euler -> JOLT.Quat', () => {
+
+ test('THREE.Euler -> Jolt.Quat', () => {
const a = new THREE.Euler(30, 60, 15);
const joltQuat = ThreeEuler_JoltQuat(a);
const threeQuat = new THREE.Quaternion();
@@ -23,7 +63,8 @@ describe('ThreeJS to Jolt Conversions', async () => {
expect(joltQuat.GetZ() - threeQuat.z).toBeLessThan(0.0001);
expect(joltQuat.GetW() - threeQuat.w).toBeLessThan(0.0001);
});
- test('THREE.Quaternion -> JOLT.Quat', () => {
+
+ test('THREE.Quaternion -> Jolt.Quat', () => {
const a = new THREE.Quaternion(0.285, 0.450, 0.237, 0.812);
a.normalize();
const joltQuat = ThreeQuaternion_JoltQuat(a);
@@ -33,7 +74,8 @@ describe('ThreeJS to Jolt Conversions', async () => {
expect(joltQuat.GetZ() - a.z).toBeLessThan(0.0001);
expect(joltQuat.GetW() - a.w).toBeLessThan(0.0001);
});
- test('THREE.Quaterion -> JOLT.Quat (General Func)', () => {
+
+ test('THREE.Quaterion -> Jolt.Quat (General Func)', () => {
const a = new THREE.Quaternion(0.285, 0.450, 0.237, 0.812);
a.normalize();
const joltQuat = _JoltQuat(a);
@@ -43,7 +85,8 @@ describe('ThreeJS to Jolt Conversions', async () => {
expect(joltQuat.GetZ() - a.z).toBeLessThan(0.0001);
expect(joltQuat.GetW() - a.w).toBeLessThan(0.0001);
});
- test('THREE.Euler -> JOLT.Quat (General Func)', () => {
+
+ test('THREE.Euler -> Jolt.Quat (General Func)', () => {
const a = new THREE.Euler(30, 60, 15);
const joltQuat = _JoltQuat(a);
const threeQuat = new THREE.Quaternion();
@@ -54,7 +97,8 @@ describe('ThreeJS to Jolt Conversions', async () => {
expect(joltQuat.GetZ() - threeQuat.z).toBeLessThan(0.0001);
expect(joltQuat.GetW() - threeQuat.w).toBeLessThan(0.0001);
});
- test('undefined -> JOLT.Quat (General Func)', () => {
+
+ test('undefined -> Jolt.Quat (General Func)', () => {
const joltQuat = _JoltQuat(undefined);
expect(joltQuat.GetX()).toBe(0.0);
@@ -62,4 +106,255 @@ describe('ThreeJS to Jolt Conversions', async () => {
expect(joltQuat.GetZ()).toBe(0.0);
expect(joltQuat.GetW()).toBe(1.0);
});
+
+ test('THREE.Matrix4 [Identity] -> Jolt.Mat44', () => {
+ const threeMat = new THREE.Matrix4(
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1
+ );
+
+ const jMat = ThreeMatrix4_JoltMat44(threeMat);
+
+ compareMat(threeMat, jMat);
+ });
+
+ test('THREE.Matrix4 [+X Axis Rotation] -> Jolt.Mat44', () => {
+ const threeMat = new THREE.Matrix4(
+ 1, 0, 0, 0,
+ 0, 0, 1, 0,
+ 0,-1, 0, 0,
+ 0, 0, 0, 1
+ );
+
+ const jMat = ThreeMatrix4_JoltMat44(threeMat);
+
+ compareMat(threeMat, jMat);
+ });
+
+ test('THREE.Matrix4 [-X Axis Rotation] -> Jolt.Mat44', () => {
+ const threeMat = new THREE.Matrix4(
+ 1, 0, 0, 0,
+ 0, 0,-1, 0,
+ 0, 1, 0, 0,
+ 0, 0, 0, 1
+ );
+
+ const jMat = ThreeMatrix4_JoltMat44(threeMat);
+
+ compareMat(threeMat, jMat);
+ });
+
+ test('THREE.Matrix4 [XY Translation] -> Jolt.Mat44', () => {
+ const threeMat = new THREE.Matrix4(
+ 1, 0, 0, 3,
+ 0, 1, 0, 5,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1
+ );
+
+ const jMat = ThreeMatrix4_JoltMat44(threeMat);
+
+ compareMat(threeMat, jMat);
+ });
+
+});
+
+// function matToString(mat: THREE.Matrix4) {
+// const arr = mat.toArray();
+// return `[\n${arr[0].toFixed(4)}, ${arr[4].toFixed(4)}, ${arr[8].toFixed(4)}, ${arr[12].toFixed(4)},\n`
+// + `${arr[1].toFixed(4)}, ${arr[5].toFixed(4)}, ${arr[9].toFixed(4)}, ${arr[13].toFixed(4)},\n`
+// + `${arr[2].toFixed(4)}, ${arr[6].toFixed(4)}, ${arr[10].toFixed(4)}, ${arr[14].toFixed(4)},\n`
+// + `${arr[3].toFixed(4)}, ${arr[7].toFixed(4)}, ${arr[11].toFixed(4)}, ${arr[15].toFixed(4)},\n]`
+// }
+
+// function miraMatToString(mat: mirabuf.ITransform) {
+// const arr = mat.spatialMatrix!;
+// return `[\n${arr[0].toFixed(4)}, ${arr[1].toFixed(4)}, ${arr[2].toFixed(4)}, ${arr[3].toFixed(4)},\n`
+// + `${arr[4].toFixed(4)}, ${arr[5].toFixed(4)}, ${arr[6].toFixed(4)}, ${arr[7].toFixed(4)},\n`
+// + `${arr[8].toFixed(4)}, ${arr[9].toFixed(4)}, ${arr[10].toFixed(4)}, ${arr[11].toFixed(4)},\n`
+// + `${arr[12].toFixed(4)}, ${arr[13].toFixed(4)}, ${arr[14].toFixed(4)}, ${arr[15].toFixed(4)},\n]`
+// }
+
+describe('Mirabuf to Three Conversions', () => {
+ test('Mirabuf.Transform [Identity] -> THREE.Matrix4', () => {
+ const miraMat = new mirabuf.Transform();
+ miraMat.spatialMatrix = [
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1
+ ];
+
+ // console.debug(`Mira: ${miraMatToString(miraMat)}`);
+
+ const threeMat = MirabufTransform_ThreeMatrix4(miraMat);
+ // console.debug(`Three: ${matToString(threeMat)}`);
+
+ const miraArr = miraMat.spatialMatrix;
+ const threeArr = threeMat.transpose().toArray(); // To Array gives column major for some reason. See docs
+
+ for (let i = 0; i < 16; i++) {
+ expect(threeArr[i]).toBe(miraArr[i]);
+ }
+ });
+
+ test('Mirabuf.Transform [+X Axis Rotation] -> THREE.Matrix4', () => {
+ const miraMat = new mirabuf.Transform();
+ miraMat.spatialMatrix = [
+ 1, 0, 0, 0,
+ 0, 0, 1, 0,
+ 0,-1, 0, 0,
+ 0, 0, 0, 1
+ ];
+
+ // console.debug(`Mira: ${miraMatToString(miraMat)}`);
+
+ const threeMat = MirabufTransform_ThreeMatrix4(miraMat);
+ // console.debug(`Three: ${matToString(threeMat)}`);
+
+ const miraArr = miraMat.spatialMatrix;
+ const threeArr = threeMat.transpose().toArray(); // To Array gives column major for some reason. See docs
+
+ for (let i = 0; i < 16; i++) {
+ expect(threeArr[i]).toBeCloseTo(miraArr[i]);
+ }
+ });
+
+ test('Mirabuf.Transform [-X Axis Rotation] -> THREE.Matrix4', () => {
+ const miraMat = new mirabuf.Transform();
+ miraMat.spatialMatrix = [
+ 1, 0, 0, 0,
+ 0, 0,-1, 0,
+ 0, 1, 0, 0,
+ 0, 0, 0, 1
+ ];
+
+ // console.debug(`Mira: ${miraMatToString(miraMat)}`);
+
+ const threeMat = MirabufTransform_ThreeMatrix4(miraMat);
+ // console.debug(`Three: ${matToString(threeMat)}`);
+
+ const miraArr = miraMat.spatialMatrix;
+ const threeArr = threeMat.transpose().toArray(); // To Array gives column major for some reason. See docs
+
+ for (let i = 0; i < 16; i++) {
+ expect(threeArr[i]).toBeCloseTo(miraArr[i]);
+ }
+ });
+});
+
+describe('Jolt to Three Conversions', () => {
+
+ function compareMat(jM: Jolt.Mat44, tM: THREE.Matrix4) {
+ const threeArr = tM.toArray();
+
+ for (let c = 0; c < 4; c++) {
+ const column = jM.GetColumn4(c);
+ for (let r = 0; r < 4; r++) {
+ expect(threeArr[c * 4 + r]).toBeCloseTo(column.GetComponent(r), 4);
+ }
+ JOLT.destroy(column);
+ }
+
+ const threeTranslation = new THREE.Vector3();
+ const threeRotation = new THREE.Quaternion();
+ const threeScale = new THREE.Vector3();
+ tM.decompose(threeTranslation, threeRotation, threeScale);
+
+ const joltTranslation = jM.GetTranslation();
+ const joltRotation = jM.GetQuaternion();
+ const joltScale = new JOLT.Vec3(1, 1, 1);
+
+ expect(threeTranslation.x).toBeCloseTo(joltTranslation.GetX(), 4);
+ expect(threeTranslation.y).toBeCloseTo(joltTranslation.GetY(), 4);
+ expect(threeTranslation.z).toBeCloseTo(joltTranslation.GetZ(), 4);
+ // JOLT.destroy(joltTranslation); // Causes error for some reason?
+ expect(threeRotation.x).toBeCloseTo(joltRotation.GetX(), 4);
+ expect(threeRotation.y).toBeCloseTo(joltRotation.GetY(), 4);
+ expect(threeRotation.z).toBeCloseTo(joltRotation.GetZ(), 4);
+ expect(threeRotation.w).toBeCloseTo(joltRotation.GetW(), 4);
+ JOLT.destroy(joltRotation);
+ expect(threeScale.x).toBeCloseTo(joltScale.GetX(), 4);
+ expect(threeScale.y).toBeCloseTo(joltScale.GetY(), 4);
+ expect(threeScale.z).toBeCloseTo(joltScale.GetZ(), 4);
+ JOLT.destroy(joltScale);
+ }
+
+ test('Jolt.Mat44 [Identity] -> THREE.Matrix4', () => {
+ const tmp = new JOLT.Mat44();
+ const joltMat = tmp.sIdentity();
+ const threeMat = JoltMat44_ThreeMatrix4(joltMat);
+
+ compareMat(joltMat, threeMat);
+
+ JOLT.destroy(joltMat);
+ });
+
+ test('Jolt.Mat44 [+X Axis Rotation] -> THREE.Matrix4', () => {
+ const joltMat = new JOLT.Mat44();
+ const c0 = new JOLT.Vec4(1, 0, 0, 0);
+ const c1 = new JOLT.Vec4(0, 0,-1, 0);
+ const c2 = new JOLT.Vec4(0, 1, 0, 0);
+ const c3 = new JOLT.Vec4(0, 0, 0, 1);
+ joltMat.SetColumn4(0, c0);
+ joltMat.SetColumn4(1, c1);
+ joltMat.SetColumn4(2, c2);
+ joltMat.SetColumn4(3, c3);
+ JOLT.destroy(c0);
+ JOLT.destroy(c1);
+ JOLT.destroy(c2);
+ JOLT.destroy(c3);
+
+ const threeMat = JoltMat44_ThreeMatrix4(joltMat);
+
+ compareMat(joltMat, threeMat);
+
+ JOLT.destroy(joltMat);
+ });
+
+ test('Jolt.Mat44 [-X Axis Rotation] -> THREE.Matrix4', () => {
+ const joltMat = new JOLT.Mat44();
+ const c0 = new JOLT.Vec4(1, 0, 0, 0);
+ const c1 = new JOLT.Vec4(0, 0, 1, 0);
+ const c2 = new JOLT.Vec4(0,-1, 0, 0);
+ const c3 = new JOLT.Vec4(0, 0, 0, 1);
+ joltMat.SetColumn4(0, c0);
+ joltMat.SetColumn4(1, c1);
+ joltMat.SetColumn4(2, c2);
+ joltMat.SetColumn4(3, c3);
+ JOLT.destroy(c0);
+ JOLT.destroy(c1);
+ JOLT.destroy(c2);
+ JOLT.destroy(c3);
+
+ const threeMat = JoltMat44_ThreeMatrix4(joltMat);
+
+ compareMat(joltMat, threeMat);
+
+ JOLT.destroy(joltMat);
+ });
+
+ test('Jolt.Mat44 [X,Y Translation] -> THREE.Matrix4', () => {
+ const joltMat = new JOLT.Mat44();
+ const c0 = new JOLT.Vec4(1, 0, 0, 0);
+ const c1 = new JOLT.Vec4(0, 1, 0, 0);
+ const c2 = new JOLT.Vec4(0, 0, 1, 0);
+ const c3 = new JOLT.Vec4(5, 3, 0, 1);
+ joltMat.SetColumn4(0, c0);
+ joltMat.SetColumn4(1, c1);
+ joltMat.SetColumn4(2, c2);
+ joltMat.SetColumn4(3, c3);
+ JOLT.destroy(c0);
+ JOLT.destroy(c1);
+ JOLT.destroy(c2);
+ JOLT.destroy(c3);
+
+ const threeMat = JoltMat44_ThreeMatrix4(joltMat);
+
+ compareMat(joltMat, threeMat);
+
+ JOLT.destroy(joltMat);
+ });
});
\ No newline at end of file
diff --git a/fission/src/util/Queue.ts b/fission/src/util/Queue.ts
index 398f7b8eb5..631a632035 100644
--- a/fission/src/util/Queue.ts
+++ b/fission/src/util/Queue.ts
@@ -32,6 +32,16 @@ class Queue {
}
return retVal;
}
+
+ public Clone(): Queue {
+ const queue = new Queue();
+ let node = this._head;
+ while (node != null) {
+ queue.Enqueue(node.value);
+ node = node.next;
+ }
+ return queue;
+ }
}
class LinkedNode {
diff --git a/fission/src/util/TypeConversions.ts b/fission/src/util/TypeConversions.ts
index 9cf3daa6c3..5443f55762 100644
--- a/fission/src/util/TypeConversions.ts
+++ b/fission/src/util/TypeConversions.ts
@@ -1,5 +1,7 @@
import * as THREE from 'three';
import JOLT from './loading/JoltSyncLoader';
+import Jolt from '@barclah/jolt-physics';
+import { mirabuf } from "../proto/mirabuf";
export function _JoltQuat(a: THREE.Euler | THREE.Quaternion | undefined) {
if (a instanceof THREE.Euler) {
@@ -23,4 +25,54 @@ export function ThreeQuaternion_JoltQuat(quat: THREE.Quaternion) {
export function ThreeVector3_JoltVec3(vec: THREE.Vector3) {
return new JOLT.Vec3(vec.x, vec.y, vec.z);
+}
+
+export function ThreeMatrix4_JoltMat44(m: THREE.Matrix4) {
+ const jMat = new JOLT.Mat44();
+ const threeArr = m.toArray();
+ for (let c = 0; c < 4; c++) {
+ const column = new JOLT.Vec4(
+ threeArr[4 * c + 0],
+ threeArr[4 * c + 1],
+ threeArr[4 * c + 2],
+ threeArr[4 * c + 3]
+ );
+ jMat.SetColumn4(c, column);
+ JOLT.destroy(column);
+ }
+
+ return jMat;
+}
+
+export function JoltVec3_ThreeVector3(vec: Jolt.Vec3 | Jolt.RVec3) {
+ return new THREE.Vector3(vec.GetX(), vec.GetY(), vec.GetZ());
+}
+
+export function JoltQuat_ThreeQuaternion(quat: Jolt.Quat) {
+ return new THREE.Quaternion(quat.GetX(), quat.GetY(), quat.GetZ(), quat.GetW());
+}
+
+export function JoltMat44_ThreeMatrix4(m: Jolt.Mat44): THREE.Matrix4 {
+ return new THREE.Matrix4().compose(
+ JoltVec3_ThreeVector3(m.GetTranslation()),
+ JoltQuat_ThreeQuaternion(m.GetQuaternion()),
+ new THREE.Vector3(1, 1, 1)
+ );
+}
+
+export function MirabufTransform_ThreeMatrix4(m: mirabuf.ITransform): THREE.Matrix4 {
+ const arr = m.spatialMatrix!;
+ const pos = new THREE.Vector3(arr[3] * 0.01, arr[7] * 0.01, arr[11] * 0.01);
+ const mat = new THREE.Matrix4().fromArray(arr);
+ const onlyRotation = new THREE.Matrix4().extractRotation(mat).transpose();
+ const quat = new THREE.Quaternion().setFromRotationMatrix(onlyRotation);
+ return new THREE.Matrix4().compose(pos, quat, new THREE.Vector3(1, 1, 1));
+}
+
+export function MirabufVector3_ThreeVector3(v: mirabuf.Vector3): THREE.Vector3 {
+ return new THREE.Vector3(v.x / 100.0, v.y / 100.0, v.z / 100.0);
+}
+
+export function MirabufVector3_JoltVec3(v: mirabuf.Vector3): Jolt.Vec3 {
+ return new Jolt.Vec3(v.x / 100.0, v.y / 100.0, v.z / 100.0);
}
\ No newline at end of file
diff --git a/fission/src/util/dom.ts b/fission/src/util/dom.ts
new file mode 100644
index 0000000000..e401ea4fbc
--- /dev/null
+++ b/fission/src/util/dom.ts
@@ -0,0 +1,31 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+export const click = (btn: number, x: number, y: number) => {
+ const el = document.elementFromPoint(x, y);
+
+ const event = new MouseEvent("click", {
+ clientX: x,
+ clientY: y,
+ bubbles: true,
+ button: btn
+ })
+ el?.dispatchEvent(event)
+}
+
+export const mousePosition = (x: number, y: number) => {
+ const el = document.elementFromPoint(x, y);
+
+ const event = new MouseEvent('mouseover', {
+ view: window,
+ bubbles: true,
+ cancelable: true
+ })
+
+ el?.dispatchEvent(event);
+}
+
+export const addGlobalFunc = (name: string, func: (...args: any[]) => T) => {
+ (window as any)[name] = func;
+}
+
+addGlobalFunc('click', click);
+addGlobalFunc('mousePosition', mousePosition);
diff --git a/fission/src/util/threejs/MeshCreation.ts b/fission/src/util/threejs/MeshCreation.ts
new file mode 100644
index 0000000000..267a8ce542
--- /dev/null
+++ b/fission/src/util/threejs/MeshCreation.ts
@@ -0,0 +1,120 @@
+import JOLT from '../loading/JoltSyncLoader.ts';
+import Jolt from '@barclah/jolt-physics';
+import * as THREE from 'three';
+import { JoltVec3_ThreeVector3, JoltQuat_ThreeQuaternion } from '../TypeConversions.ts';
+import { Random } from '../Random.ts';
+
+export const LAYER_NOT_MOVING = 0;
+export const LAYER_MOVING = 1;
+export const COUNT_OBJECT_LAYERS = 2;
+
+export function createMeshForShape(shape: Jolt.Shape) {
+ const scale = new JOLT.Vec3(1, 1, 1);
+ const triangleContext = new JOLT.ShapeGetTriangles(shape, JOLT.AABox.prototype.sBiggest(), shape.GetCenterOfMass(), JOLT.Quat.prototype.sIdentity(), scale);
+ JOLT.destroy(scale);
+
+ const vertices = new Float32Array(JOLT.HEAP32.buffer, triangleContext.GetVerticesData(), triangleContext.GetVerticesSize() / Float32Array.BYTES_PER_ELEMENT);
+ const buffer = new THREE.BufferAttribute(vertices, 3).clone();
+ JOLT.destroy(triangleContext);
+
+ const geometry = new THREE.BufferGeometry();
+ geometry.setAttribute('position', buffer);
+ geometry.computeVertexNormals();
+
+ return geometry;
+}
+
+export function getThreeObjForBody(body: Jolt.Body, color: THREE.Color) {
+ const material = new THREE.MeshPhongMaterial({ color: color, shininess: 0.1 });
+ let threeObj;
+ const shape = body.GetShape();
+
+ switch (shape.GetSubType()) {
+ case JOLT.EShapeSubType_Box: {
+ const boxShape = JOLT.castObject(shape, JOLT.BoxShape);
+ const extent = JoltVec3_ThreeVector3(boxShape.GetHalfExtent()).multiplyScalar(2);
+ threeObj = new THREE.Mesh(new THREE.BoxGeometry(extent.x, extent.y, extent.z, 1, 1, 1), material);
+ threeObj.receiveShadow = true;
+ threeObj.castShadow = true;
+ break;
+ }
+ case JOLT.EShapeSubType_Capsule:
+ // TODO
+ break;
+ case JOLT.EShapeSubType_Cylinder:
+ // TODO
+ break;
+ case JOLT.EShapeSubType_Sphere:
+ // TODO
+ break;
+ default:
+ threeObj = new THREE.Mesh(createMeshForShape(shape), material);
+ threeObj.receiveShadow = true;
+ threeObj.castShadow = true;
+ break;
+ }
+
+ if (!threeObj) return undefined;
+
+ threeObj.position.copy(JoltVec3_ThreeVector3(body.GetPosition()));
+ threeObj.quaternion.copy(JoltQuat_ThreeQuaternion(body.GetRotation()));
+
+ return threeObj;
+}
+
+export function addToThreeScene(scene: THREE.Scene, body: Jolt.Body, color: THREE.Color, dynamicObjects: THREE.Mesh[]) {
+ const threeObj = getThreeObjForBody(body, color);
+ if (!threeObj) return;
+ threeObj.userData.body = body;
+ scene.add(threeObj);
+ dynamicObjects.push(threeObj);
+}
+
+export function addToScene(scene: THREE.Scene, body: Jolt.Body, color: THREE.Color, bodyInterface: Jolt.BodyInterface, dynamicObjects: THREE.Mesh[]) {
+ bodyInterface.AddBody(body.GetID(), JOLT.EActivation_Activate);
+ addToThreeScene(scene, body, color, dynamicObjects);
+}
+
+export function removeFromScene(scene: THREE.Scene, threeObject: THREE.Mesh, bodyInterface: Jolt.BodyInterface, dynamicObjects: THREE.Mesh[]) {
+ const id = threeObject.userData.body.GetID();
+ bodyInterface.RemoveBody(id);
+ bodyInterface.DestroyBody(id);
+ delete threeObject.userData.body;
+
+ scene.remove(threeObject);
+ const idx = dynamicObjects.indexOf(threeObject);
+ dynamicObjects.splice(idx, 1);
+}
+
+function getRandomQuat() {
+ const vec = new JOLT.Vec3(0.001 + Random(), Random(), Random());
+ const quat = JOLT.Quat.prototype.sRotation(vec.Normalized(), 2 * Math.PI * Random());
+ JOLT.destroy(vec);
+ return quat;
+}
+
+function makeRandomBox(scene: THREE.Scene, bodyInterface: Jolt.BodyInterface, dynamicObjects: THREE.Mesh[]) {
+ const pos = new JOLT.Vec3((Random() - 0.5) * 25, 15, (Random() - 0.5) * 25);
+ const rot = getRandomQuat();
+
+ const x = Random();
+ const y = Random();
+ const z = Random();
+ const size = new JOLT.Vec3(x, y, z);
+ const shape = new JOLT.BoxShape(size, 0.05, undefined);
+ const creationSettings = new JOLT.BodyCreationSettings(shape, pos, rot, JOLT.EMotionType_Dynamic, LAYER_MOVING);
+ creationSettings.mRestitution = 0.5;
+ const body = bodyInterface.CreateBody(creationSettings);
+
+ JOLT.destroy(pos);
+ JOLT.destroy(rot);
+ JOLT.destroy(size);
+
+ // I feel as though this object should be freed at this point but doing so will cause a crash at runtime.
+ // This is the only object where this happens. I'm not sure why. Seems problematic.
+ // Jolt.destroy(shape);
+
+ JOLT.destroy(creationSettings);
+
+ addToScene(scene, body, new THREE.Color(0xff0000), bodyInterface, dynamicObjects);
+}
diff --git a/fission/tailwind.config.js b/fission/tailwind.config.js
new file mode 100644
index 0000000000..a8d01e4a25
--- /dev/null
+++ b/fission/tailwind.config.js
@@ -0,0 +1,52 @@
+/** @type {import('tailwindcss').Config} */
+
+let colors = {
+ 'interactive-element-solid': 'var(--interactive-element-solid)',
+ 'interactive-element-left': 'var(--interactive-element-left)',
+ 'interactive-element-right': 'var(--interactive-element-right)',
+ 'background': 'var(--background)',
+ 'background-secondary': 'var(--background-secondary)',
+ 'interactive-background': 'var(--interactive-background)',
+ 'background-hud': 'var(--background-hud)',
+ 'interactive-hover': 'var(--interactive-hover)',
+ 'interactive-select': 'var(--interactive-select)',
+ 'main-text': 'var(--main-text)',
+ 'scrollbar': 'var(--scrollbar)',
+ 'accept-button': 'var(--accept-button)',
+ 'cancel-button': 'var(--cancel-button)',
+ 'interactive-element-text': 'var(--interactive-element-text)',
+ 'icon': 'var(--icon)',
+ 'main-hud-icon': 'var(--main-hud-icon)',
+ 'main-hud-close-icon': 'var(--main-hud-close-icon)',
+ 'highlight-hover': 'var(--highlight-hover)',
+ 'highlight-select': 'var(--highlight-select)',
+ 'skybox-top': 'var(--skybox-top)',
+ 'skybox-bottom': 'var(--skybox-bottom)',
+ 'floor-grid': 'var(--floor-grid)',
+ 'accept-cancel-button-text': 'var(--accept-cancel-button-text)',
+ 'match-red-alliance': 'var(--match-red-alliance)',
+ 'match-blue-alliance': 'var(--match-blue-alliance)',
+ 'toast-info': 'var(--toast-info)',
+ 'toast-warning': 'var(--toast-warning)',
+ 'toast-error': 'var(--toast-error)',
+}
+
+let safelist = Object.keys(colors).map(c => "bg-" + c);
+
+export default {
+ content: [
+ "./index.html",
+ "./src/**/*.{js,jsx,ts,tsx}",
+ ],
+ theme: {
+ extend: {
+ colors: colors,
+ maxHeight: {
+ '70vh': '70vh',
+ }
+ },
+ },
+ safelist: safelist,
+ plugins: [],
+}
+
diff --git a/fission/tsconfig.json b/fission/tsconfig.json
index a7fc6fbf23..cd343babff 100644
--- a/fission/tsconfig.json
+++ b/fission/tsconfig.json
@@ -5,6 +5,12 @@
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
+ "baseUrl": ".",
+ "paths": {
+ "@/*": [
+ "./src/*"
+ ]
+ },
/* Bundler mode */
"moduleResolution": "bundler",
diff --git a/fission/vite.config.ts b/fission/vite.config.ts
index 409fa5890c..0adffa11cd 100644
--- a/fission/vite.config.ts
+++ b/fission/vite.config.ts
@@ -1,9 +1,16 @@
import { defineConfig } from 'vite'
+import * as path from 'path'
import react from '@vitejs/plugin-react-swc'
+import { viteSingleFile } from 'vite-plugin-singlefile'
// https://vitejs.dev/config/
export default defineConfig({
- plugins: [react()],
+ plugins: [react(), /* viteSingleFile() */],
+resolve: {
+ alias: [
+ { find: '@', replacement: path.resolve(__dirname, 'src') }
+ ]
+},
server: {
// this ensures that the browser opens upon server start
open: true,