commit 9b6424dfa12e6e49ab62e94830d5677dec88945c Author: MunchDev-oss Date: Sun Jan 4 16:29:29 2026 -0500 Initial commit: OSSM Configurator with share and export functionality diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000..c0f1d02 --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,21 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:react/recommended', + 'plugin:react/jsx-runtime', + 'plugin:react-hooks/recommended', + ], + ignorePatterns: ['dist', '.eslintrc.cjs'], + parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, + settings: { react: { version: '18.2' } }, + plugins: ['react-refresh'], + rules: { + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + 'react/prop-types': 'off', + }, +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/.vite/deps_temp_93d425d7/package.json b/.vite/deps_temp_93d425d7/package.json new file mode 100644 index 0000000..3dbc1ca --- /dev/null +++ b/.vite/deps_temp_93d425d7/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/BOM.xlsx b/BOM.xlsx new file mode 100644 index 0000000..2545485 Binary files /dev/null and b/BOM.xlsx differ diff --git a/README.md b/README.md new file mode 100644 index 0000000..7fbaba8 --- /dev/null +++ b/README.md @@ -0,0 +1,130 @@ +# OSSM Configurator + +A web-based configuration tool for the Open Source Sex Machine (OSSM) project. This application provides an intuitive wizard interface that guides users through selecting and customizing components for their OSSM build, generating a complete Bill of Materials (BOM) and configuration summary. + +## Project Structure + +``` +OSSM-Configurator/ +├── website/ # Main web application +│ ├── src/ # React source code +│ ├── public/ # Static assets (images, etc.) +│ ├── dist/ # Build output (generated) +│ ├── node_modules/ # Dependencies (generated) +│ └── ... # Configuration files +├── BOM.xlsx # Bill of Materials spreadsheet +├── Screen Shots/ # Application screenshots +└── README.md # This file +``` + +## Website Overview + +The OSSM Configurator is a React-based single-page application built with Vite. It provides a step-by-step wizard interface that allows users to: + +1. **Select Motor** - Choose from available motor options (42AIM30, 57AIM30, iHSV57) +2. **Choose Power Supply** - Select appropriate power supply (24V PSU, 24V USB-C PD) +3. **Customize Colors** - Pick primary and accent colors for 3D printed parts +4. **Configure Options** - Select mounting options, stands, toy mounts, actuators, and other components +5. **Review Summary** - View complete BOM with pricing, filament estimates, and export options + +### Key Features + +- **Interactive Wizard Interface**: Step-by-step configuration process with progress tracking +- **Component Compatibility**: Ensures selected components are compatible with each other +- **Real-time Pricing**: Calculates total cost including hardware and printed parts +- **Filament Estimates**: Provides 3D printing filament requirements for each component +- **BOM Export**: Generate and download a complete Bill of Materials +- **Visual Component Selection**: Image-based component selection for better user experience + +### Technology Stack + +- **React 18** - UI framework +- **Vite** - Build tool and dev server +- **Tailwind CSS** - Styling +- **JSZip** - For generating downloadable BOM packages + +## Getting Started + +### Prerequisites + +- Node.js (v16 or higher recommended) +- npm or yarn + +### Installation + +1. Navigate to the website directory: + ```bash + cd website + ``` + +2. Install dependencies: + ```bash + npm install + ``` + +### Development + +Run the development server: + +```bash +npm run dev +``` + +The application will be available at `http://localhost:5173` (or the port shown in the terminal). + +### Building for Production + +Create an optimized production build: + +```bash +npm run build +``` + +The built files will be in the `website/dist/` directory. + +### Preview Production Build + +Preview the production build locally: + +```bash +npm run preview +``` + +## Configuration Data + +The application uses JSON data files located in `website/src/data/`: + +- `motors.json` - Available motor options +- `powerSupplies.json` - Power supply options +- `colors.json` - Available color options +- `options.json` - Main configuration options +- `components/` - Detailed component data: + - `actuator.json` - Actuator components + - `mounting.json` - Mounting options + - `remote.json` - Remote control components + - `stand.json` - Stand components + - `toyMounts.json` - Toy mount options + +## Project Purpose + +The OSSM Configurator serves as a comprehensive tool for users building their own Open Source Sex Machine. It simplifies the configuration process by: + +- **Guiding Selection**: Step-by-step wizard prevents missing critical components +- **Ensuring Compatibility**: Validates component combinations +- **Providing Transparency**: Shows costs, filament requirements, and time estimates +- **Generating Documentation**: Creates exportable BOM for ordering parts and printing + +This tool is essential for both beginners and experienced builders who want to ensure they have all necessary components and understand the full scope of their build before starting. + +## Contributing + +When adding new components or options: + +1. Update the appropriate JSON data files in `website/src/data/` +2. Add corresponding images to `website/public/images/` +3. Test the configuration flow to ensure compatibility +4. Update component pricing and filament estimates as needed + +## License + +This project is part of the Open Source Sex Machine (OSSM) project. Please refer to the OSSM project license for usage terms. diff --git a/Screen Shots/Color Selector.png b/Screen Shots/Color Selector.png new file mode 100644 index 0000000..09dc067 Binary files /dev/null and b/Screen Shots/Color Selector.png differ diff --git a/Screen Shots/Motor-Selected.png b/Screen Shots/Motor-Selected.png new file mode 100644 index 0000000..8e1a877 Binary files /dev/null and b/Screen Shots/Motor-Selected.png differ diff --git a/Screen Shots/Motor-unselected.png b/Screen Shots/Motor-unselected.png new file mode 100644 index 0000000..90e98d2 Binary files /dev/null and b/Screen Shots/Motor-unselected.png differ diff --git a/Screen Shots/Options List.png b/Screen Shots/Options List.png new file mode 100644 index 0000000..eae326c Binary files /dev/null and b/Screen Shots/Options List.png differ diff --git a/Screen Shots/PSU selector.png b/Screen Shots/PSU selector.png new file mode 100644 index 0000000..5aab54c Binary files /dev/null and b/Screen Shots/PSU selector.png differ diff --git a/Screen Shots/Summary.png b/Screen Shots/Summary.png new file mode 100644 index 0000000..4ed8e1d Binary files /dev/null and b/Screen Shots/Summary.png differ diff --git a/custom-covers/blank/OSSM - Actuator - Body - Cover.3mf b/custom-covers/blank/OSSM - Actuator - Body - Cover.3mf new file mode 100644 index 0000000..a65db11 Binary files /dev/null and b/custom-covers/blank/OSSM - Actuator - Body - Cover.3mf differ diff --git a/website/index.html b/website/index.html new file mode 100644 index 0000000..5ac46fd --- /dev/null +++ b/website/index.html @@ -0,0 +1,13 @@ + + + + + + + OSSM Configurator + + +
+ + + diff --git a/website/package-lock.json b/website/package-lock.json new file mode 100644 index 0000000..cbb25ae --- /dev/null +++ b/website/package-lock.json @@ -0,0 +1,5754 @@ +{ + "name": "ossm-configurator", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "ossm-configurator", + "version": "0.0.0", + "dependencies": { + "jszip": "^3.10.1", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "xlsx": "^0.18.5" + }, + "devDependencies": { + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", + "@vitejs/plugin-react": "^4.3.1", + "autoprefixer": "^10.4.20", + "eslint": "^8.57.0", + "eslint-plugin-react": "^7.34.1", + "eslint-plugin-react-hooks": "^4.6.2", + "eslint-plugin-react-refresh": "^0.4.7", + "postcss": "^8.4.41", + "tailwindcss": "^3.4.9", + "vite": "^5.4.2" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", + "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.5", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.54.0.tgz", + "integrity": "sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.54.0.tgz", + "integrity": "sha512-Skx39Uv+u7H224Af+bDgNinitlmHyQX1K/atIA32JP3JQw6hVODX5tkbi2zof/E69M1qH2UoN3Xdxgs90mmNYw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.54.0.tgz", + "integrity": "sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.54.0.tgz", + "integrity": "sha512-cOo7biqwkpawslEfox5Vs8/qj83M/aZCSSNIWpVzfU2CYHa2G3P1UN5WF01RdTHSgCkri7XOlTdtk17BezlV3A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.54.0.tgz", + "integrity": "sha512-miSvuFkmvFbgJ1BevMa4CPCFt5MPGw094knM64W9I0giUIMMmRYcGW/JWZDriaw/k1kOBtsWh1z6nIFV1vPNtA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.54.0.tgz", + "integrity": "sha512-KGXIs55+b/ZfZsq9aR026tmr/+7tq6VG6MsnrvF4H8VhwflTIuYh+LFUlIsRdQSgrgmtM3fVATzEAj4hBQlaqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.54.0.tgz", + "integrity": "sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.54.0.tgz", + "integrity": "sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.54.0.tgz", + "integrity": "sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.54.0.tgz", + "integrity": "sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.54.0.tgz", + "integrity": "sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.54.0.tgz", + "integrity": "sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.54.0.tgz", + "integrity": "sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.54.0.tgz", + "integrity": "sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.54.0.tgz", + "integrity": "sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.54.0.tgz", + "integrity": "sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.54.0.tgz", + "integrity": "sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.54.0.tgz", + "integrity": "sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.54.0.tgz", + "integrity": "sha512-c2V0W1bsKIKfbLMBu/WGBz6Yci8nJ/ZJdheE0EwB73N3MvHYKiKGs3mVilX4Gs70eGeDaMqEob25Tw2Gb9Nqyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.54.0.tgz", + "integrity": "sha512-woEHgqQqDCkAzrDhvDipnSirm5vxUXtSKDYTVpZG3nUdW/VVB5VdCYA2iReSj/u3yCZzXID4kuKG7OynPnB3WQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.54.0.tgz", + "integrity": "sha512-dzAc53LOuFvHwbCEOS0rPbXp6SIhAf2txMP5p6mGyOXXw5mWY8NGGbPMPrs4P1WItkfApDathBj/NzMLUZ9rtQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.54.0.tgz", + "integrity": "sha512-hYT5d3YNdSh3mbCU1gwQyPgQd3T2ne0A3KG8KSBdav5TiBg6eInVmV+TeR5uHufiIgSFg0XsOWGW5/RhNcSvPg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.27", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz", + "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/adler-32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz", + "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.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.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.23", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.23.tgz", + "integrity": "sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.1", + "caniuse-lite": "^1.0.30001760", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/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, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.11", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz", + "integrity": "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "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", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001762", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001762.tgz", + "integrity": "sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/cfb": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz", + "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "crc-32": "~1.2.0" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/codepage": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz", + "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/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, + "license": "MIT", + "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, + "license": "MIT", + "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/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.267", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", + "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", + "dev": true, + "license": "ISC" + }, + "node_modules/es-abstract": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", + "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "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, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.2.tgz", + "integrity": "sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.1", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.1.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.3.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.5", + "safe-array-concat": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.3", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.2.1", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.9", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.1", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.12", + "string.prototype.repeat": "^1.0.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", + "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", + "dev": true, + "license": "MIT", + "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.26", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.26.tgz", + "integrity": "sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.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==", + "dev": true, + "license": "BSD-2-Clause", + "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, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/frac": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz", + "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "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, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "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", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "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", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "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, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "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, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "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, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "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, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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, + "license": "MIT", + "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", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "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", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "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, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "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, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "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, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-import/node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.54.0.tgz", + "integrity": "sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.54.0", + "@rollup/rollup-android-arm64": "4.54.0", + "@rollup/rollup-darwin-arm64": "4.54.0", + "@rollup/rollup-darwin-x64": "4.54.0", + "@rollup/rollup-freebsd-arm64": "4.54.0", + "@rollup/rollup-freebsd-x64": "4.54.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.54.0", + "@rollup/rollup-linux-arm-musleabihf": "4.54.0", + "@rollup/rollup-linux-arm64-gnu": "4.54.0", + "@rollup/rollup-linux-arm64-musl": "4.54.0", + "@rollup/rollup-linux-loong64-gnu": "4.54.0", + "@rollup/rollup-linux-ppc64-gnu": "4.54.0", + "@rollup/rollup-linux-riscv64-gnu": "4.54.0", + "@rollup/rollup-linux-riscv64-musl": "4.54.0", + "@rollup/rollup-linux-s390x-gnu": "4.54.0", + "@rollup/rollup-linux-x64-gnu": "4.54.0", + "@rollup/rollup-linux-x64-musl": "4.54.0", + "@rollup/rollup-openharmony-arm64": "4.54.0", + "@rollup/rollup-win32-arm64-msvc": "4.54.0", + "@rollup/rollup-win32-ia32-msvc": "4.54.0", + "@rollup/rollup-win32-x64-gnu": "4.54.0", + "@rollup/rollup-win32-x64-msvc": "4.54.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/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, + "license": "MIT", + "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, + "license": "MIT", + "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/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ssf": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz", + "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", + "license": "Apache-2.0", + "dependencies": { + "frac": "~1.1.2" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "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, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.7", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "license": "MIT" + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wmf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz", + "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/word": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz", + "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/xlsx": { + "version": "0.18.5", + "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz", + "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "cfb": "~1.2.1", + "codepage": "~1.15.0", + "crc-32": "~1.2.1", + "ssf": "~0.11.2", + "wmf": "~1.0.1", + "word": "~0.3.0" + }, + "bin": { + "xlsx": "bin/xlsx.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/website/package.json b/website/package.json new file mode 100644 index 0000000..9529a5b --- /dev/null +++ b/website/package.json @@ -0,0 +1,31 @@ +{ + "name": "ossm-configurator", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview" + }, + "dependencies": { + "jszip": "^3.10.1", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "xlsx": "^0.18.5" + }, + "devDependencies": { + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", + "@vitejs/plugin-react": "^4.3.1", + "autoprefixer": "^10.4.20", + "eslint": "^8.57.0", + "eslint-plugin-react": "^7.34.1", + "eslint-plugin-react-hooks": "^4.6.2", + "eslint-plugin-react-refresh": "^0.4.7", + "postcss": "^8.4.41", + "tailwindcss": "^3.4.9", + "vite": "^5.4.2" + } +} diff --git a/website/postcss.config.js b/website/postcss.config.js new file mode 100644 index 0000000..2e7af2b --- /dev/null +++ b/website/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/website/public/images/README.md b/website/public/images/README.md new file mode 100644 index 0000000..adc07c7 --- /dev/null +++ b/website/public/images/README.md @@ -0,0 +1,10 @@ +# Images Directory + +Place product images in the appropriate subdirectories: + +- `motors/` - Motor images (e.g., 57AIM30.jpg, 42AIM30.jpg, iHSV57.jpg) +- `power-supplies/` - Power supply images (e.g., 12v-5a.jpg, 12v-8a.jpg) + +Image paths are referenced in `src/data/parts.json`. The image paths should match the filenames you place here. + +Supported image formats: JPG, PNG, WebP, etc. diff --git a/website/public/images/motors/42AIM30.png b/website/public/images/motors/42AIM30.png new file mode 100644 index 0000000..c2643d4 Binary files /dev/null and b/website/public/images/motors/42AIM30.png differ diff --git a/website/public/images/motors/57AIM30.png b/website/public/images/motors/57AIM30.png new file mode 100644 index 0000000..6de1eec Binary files /dev/null and b/website/public/images/motors/57AIM30.png differ diff --git a/website/public/images/motors/iHSV57.png b/website/public/images/motors/iHSV57.png new file mode 100644 index 0000000..75b469f Binary files /dev/null and b/website/public/images/motors/iHSV57.png differ diff --git a/website/public/images/options/PitClamp Mini Base.png b/website/public/images/options/PitClamp Mini Base.png new file mode 100644 index 0000000..5441728 Binary files /dev/null and b/website/public/images/options/PitClamp Mini Base.png differ diff --git a/website/public/images/options/middle-pivot.png b/website/public/images/options/middle-pivot.png new file mode 100644 index 0000000..b9f9276 Binary files /dev/null and b/website/public/images/options/middle-pivot.png differ diff --git a/website/public/images/options/pitclamp-reinforced-3030-hinges.jpg b/website/public/images/options/pitclamp-reinforced-3030-hinges.jpg new file mode 100644 index 0000000..6b6df8c Binary files /dev/null and b/website/public/images/options/pitclamp-reinforced-3030-hinges.jpg differ diff --git a/website/public/images/options/pivot-plate.webp b/website/public/images/options/pivot-plate.webp new file mode 100644 index 0000000..51d5abd Binary files /dev/null and b/website/public/images/options/pivot-plate.webp differ diff --git a/website/public/images/power-supplies/24v-PSU.png b/website/public/images/power-supplies/24v-PSU.png new file mode 100644 index 0000000..671be61 Binary files /dev/null and b/website/public/images/power-supplies/24v-PSU.png differ diff --git a/website/public/images/power-supplies/24v-usbc-pd.png b/website/public/images/power-supplies/24v-usbc-pd.png new file mode 100644 index 0000000..61a3546 Binary files /dev/null and b/website/public/images/power-supplies/24v-usbc-pd.png differ diff --git a/website/public/images/remote/radr-remote.png b/website/public/images/remote/radr-remote.png new file mode 100644 index 0000000..b3dd37a Binary files /dev/null and b/website/public/images/remote/radr-remote.png differ diff --git a/website/public/images/remote/standard-remote.png b/website/public/images/remote/standard-remote.png new file mode 100644 index 0000000..d2920fb Binary files /dev/null and b/website/public/images/remote/standard-remote.png differ diff --git a/website/src/App.jsx b/website/src/App.jsx new file mode 100644 index 0000000..6010342 --- /dev/null +++ b/website/src/App.jsx @@ -0,0 +1,161 @@ +import { useState, useEffect } from 'react'; +import MainPage from './components/MainPage'; +import Wizard from './components/Wizard'; +import partsData from './data/index.js'; +import { getSharedConfig } from './utils/shareService'; + +function App() { + const [buildType, setBuildType] = useState(null); + const [config, setConfig] = useState({ + motor: null, + powerSupply: null, + primaryColor: 'black', + accentColor: 'black', + mount: null, + cover: null, + standHinge: null, + standFeet: null, + standCrossbarSupports: [], + toyMountOptions: [], + toyMounts: [], + actuatorMount: null, + standParts: [], + pcbMount: null, + }); + + // Check for share link on load + useEffect(() => { + const urlParams = new URLSearchParams(window.location.search); + const shareId = urlParams.get('share'); + const isSession = urlParams.get('session') === 'true'; + + if (shareId) { + const sharedConfig = getSharedConfig(shareId, isSession); + if (sharedConfig) { + setConfig(sharedConfig); + setBuildType('self-source'); // Default build type for shared configs + // Clean up the URL + window.history.replaceState({}, document.title, window.location.pathname); + } else { + alert('This share link has expired or is invalid. Share links are valid for 7 days.'); + } + } + }, []); + + const handleSelectBuildType = (type) => { + if (type === 'rad-kit') { + // Pre-select RAD kit parts + const radKitConfig = getRADKitConfig(); + setConfig(radKitConfig); + setBuildType('rad-kit'); + } else { + setBuildType(type); + } + }; + + const getRADKitConfig = () => { + // Standard RAD kit configuration + // Assuming the kit includes: + // - 57AIM30 motor (recommended) + // - 24V 5A PSU + // - Standard mount (middle-pivot or pitclamp - using middle-pivot as default) + // - Standard colors (black/black) + // - Basic stand components + // - Default toy mount options (flange mount base) + + const motor = partsData.motors.find(m => m.id === '57AIM30') || partsData.motors[0]; + const powerSupply = partsData.powerSupplies.find(ps => ps.id === 'psu-24v-5a') || partsData.powerSupplies[0]; + + // Get mount from options data to ensure proper structure + const mountOptions = partsData.options?.actuator?.sections?.mounts?.options || []; + const mount = mountOptions.find(m => m.id === 'middle-pivot') || mountOptions[0] || null; + + // Get stand hinge (default: pivot-plate) + const hingeOptions = partsData.options?.stand?.sections?.hinges?.options || []; + const standHinge = hingeOptions.find(h => h.id === 'pivot-plate') || hingeOptions[0] || null; + + // Get stand feet (default: standard-feet) + const feetOptions = partsData.options?.stand?.sections?.feet?.options || []; + const standFeet = feetOptions.find(f => f.id === 'standard-feet') || feetOptions[0] || null; + + // Get stand crossbar supports (default: standard-90-degree-support) + const crossbarSupportOptions = partsData.options?.stand?.sections?.crossbarSupports?.options || []; + const standCrossbarSupports = []; + const standardSupport = crossbarSupportOptions.find(s => s.id === 'standard-90-degree-support') || crossbarSupportOptions[0]; + if (standardSupport) { + standCrossbarSupports.push(standardSupport); + } + + // Get default toy mount options (flange mount base - first option) + const toyMountOptions = []; + const flangeMountOptions = partsData.options?.toyMounts?.sections?.flangeMount?.options || []; + if (flangeMountOptions.length > 0) { + // Select the first option (flange-base-24mm-threaded) + toyMountOptions.push(flangeMountOptions[0]); + } + + // Get cover (default: standard-cover) + const coverOptions = partsData.options?.actuator?.sections?.cover?.options || []; + const cover = coverOptions.find(c => c.id === 'standard-cover') || coverOptions[0] || null; + + // Get PCB mount (default: 3030-mount) + const pcbMountOptions = partsData.options?.actuator?.sections?.pcbMount?.options || []; + const pcbMount = pcbMountOptions.find(p => p.id === '3030-mount') || pcbMountOptions[0] || null; + + return { + motor, + powerSupply, + primaryColor: 'black', + accentColor: 'black', + mount, + cover, + standHinge, + standFeet, + standCrossbarSupports, + toyMountOptions, + toyMounts: [], + actuatorMount: null, + standParts: [], + pcbMount, + }; + }; + + const updateConfig = (updates) => { + setConfig((prev) => ({ ...prev, ...updates })); + }; + + const handleBackToMain = () => { + setBuildType(null); + setConfig({ + motor: null, + powerSupply: null, + primaryColor: 'black', + accentColor: 'black', + mount: null, + cover: null, + standHinge: null, + standFeet: null, + standCrossbarSupports: [], + toyMountOptions: [], + toyMounts: [], + actuatorMount: null, + standParts: [], + pcbMount: null, + }); + }; + + if (!buildType) { + return ; + } + + return ( + + ); +} + +export default App; diff --git a/website/src/components/BOMSummary.jsx b/website/src/components/BOMSummary.jsx new file mode 100644 index 0000000..dcbe020 --- /dev/null +++ b/website/src/components/BOMSummary.jsx @@ -0,0 +1,1541 @@ +import { useState } from 'react'; +import partsData from '../data/index.js'; +import { formatPrice, getNumericPrice } from '../utils/priceFormat'; +import JSZip from 'jszip'; +import * as XLSX from 'xlsx'; +import { createShareLink } from '../utils/shareService'; +import { generateMarkdownOverview, generateExcelBOM, generateExcelPrintList } from '../utils/exportUtils'; + +export default function BOMSummary({ config }) { + const [isExportingZip, setIsExportingZip] = useState(false); + const [zipProgress, setZipProgress] = useState({ current: 0, total: 0, currentFile: '' }); + const [hardwareViewMode, setHardwareViewMode] = useState('unified'); // 'unified' or 'expanded' + const [activeTab, setActiveTab] = useState('overview'); // 'overview', 'printed', 'hardware' + const calculateTotal = () => { + let total = 0; + + if (config.motor) total += getNumericPrice(config.motor.price); + if (config.powerSupply) total += getNumericPrice(config.powerSupply.price); + + if (config.mount) { + const mountOption = partsData.options?.mounts?.find(m => m.id === config.mount.id); + if (mountOption?.hardwareCost) total += getNumericPrice(mountOption.hardwareCost); + } + + if (config.standHinge) { + // Check new structure (systems) first, then fall back to options + const hingeSystem = partsData.components?.hinges?.systems?.[config.standHinge.id]; + if (hingeSystem?.hardwareCost) { + total += getNumericPrice(hingeSystem.hardwareCost); + } else { + const hingeOption = partsData.options?.standHinges?.find(h => h.id === config.standHinge.id); + if (hingeOption?.hardwareCost) total += getNumericPrice(hingeOption.hardwareCost); + } + } + + if (config.standFeet) { + const feetOption = partsData.options?.standFeet?.find(f => f.id === config.standFeet.id); + if (feetOption?.hardwareCost) total += getNumericPrice(feetOption.hardwareCost); + } + + if (config.standCrossbarSupports) { + config.standCrossbarSupports.forEach((support) => { + const supportOption = partsData.options?.standCrossbarSupports?.find(s => s.id === support.id); + if (supportOption?.hardwareCost) total += getNumericPrice(supportOption.hardwareCost); + }); + } + + return total; + }; + + const getRequiredPrintedParts = () => { + const parts = []; + const isMiddlePivotSelected = config.mount?.id === 'middle-pivot'; + const coverId = config.cover?.id; + const isStandardCover = coverId === 'standard-cover'; + const isBlankCover = coverId === 'blank-cover'; + const isCustomCover = config.cover !== null && !isStandardCover && !isBlankCover; + + // Always include actuator parts, but exclude ossm-actuator-body-middle if middlePivot is selected + // and exclude ossm-actuator-body-cover if a non-standard cover is selected + if (partsData.components?.actuator?.printedParts) { + partsData.components.actuator.printedParts.forEach((part) => { + if (part.required) { + // Skip the actuator body middle part if middle pivot is selected + if (isMiddlePivotSelected && part.id === 'ossm-actuator-body-middle') { + return; + } + // Skip the default cover if a non-standard cover is selected (blank or custom) + if ((isBlankCover || isCustomCover) && part.id === 'ossm-actuator-body-cover') { + return; + } + parts.push({ ...part, category: 'Actuator Body' }); + } + }); + } + + // Include mount-specific parts based on selection + if (config.mount) { + const mountId = config.mount.id; + if (mountId === 'middle-pivot' && partsData.components?.middlePivot?.printedParts) { + partsData.components.middlePivot.printedParts.forEach((part) => { + if (part.required) { + parts.push({ ...part, category: 'Mount', replacesActuatorMiddle: true }); + } + }); + } else if (mountId === 'pitclamp' && partsData.components?.pitClamp?.printedParts) { + const selectedMotorId = config.motor?.id; + partsData.components.pitClamp.printedParts.forEach((part) => { + if (part.required) { + // Filter motor-specific parts - only include the one that matches the selected motor + const isMotorSpecificPart = part.id.includes('57AIM30') || part.id.includes('42AIM30') || part.id.includes('iHSV57'); + if (isMotorSpecificPart) { + // Only include if it matches the selected motor + const expectedPartId = `ossm-pitclamp-mini-${selectedMotorId}`; + if (part.id === expectedPartId) { + parts.push({ ...part, category: 'Mount' }); + } + } else { + // Include non-motor-specific parts (lower, upper, handle, dogbone-nuts) + parts.push({ ...part, category: 'Mount' }); + } + } + }); + } + + // Include mount option part + if (partsData.components?.mounts?.printedParts) { + const mountPart = partsData.components.mounts.printedParts.find(p => p.id === mountId); + if (mountPart) { + parts.push({ ...mountPart, category: 'Mount' }); + } + } + } + + // Include custom cover if selected (replaces default cover) + // Skip blank cover and standard cover (standard uses the component, blank uses nothing) + if (isCustomCover) { + const coverOption = config.cover; + // Convert cover option to part format for BOM + parts.push({ + id: coverOption.id, + name: coverOption.name, + description: coverOption.description || coverOption.name, + filamentEstimate: coverOption.filamentEstimate ? parseFloat(coverOption.filamentEstimate.replace('~', '').replace('g', '')) : 0, + filePath: `${coverOption.id}.3mf`, + category: 'Cover', + required: true, + colour: 'primary', + }); + } + + // Include toy mount parts if any toy mounts are selected + if (config.toyMountOptions && config.toyMountOptions.length > 0) { + if (partsData.components?.toyMounts?.printedParts) { + const selectedToyMountIds = new Set(config.toyMountOptions.map(opt => opt.id)); + partsData.components.toyMounts.printedParts.forEach((part) => { + if (selectedToyMountIds.has(part.id)) { + parts.push({ ...part, category: 'Toy Mounts' }); + } + }); + } + } + + // Include stand parts if stand options are selected + if (config.standHinge || config.standFeet || (config.standCrossbarSupports && config.standCrossbarSupports.length > 0)) { + if (partsData.components?.stand?.printedParts) { + partsData.components.stand.printedParts.forEach((part) => { + if (part.required) { + parts.push({ ...part, category: 'Stand' }); + } + }); + } + } + + // Include stand hinge system parts + if (config.standHinge && partsData.components?.hinges?.systems) { + const hingeSystem = partsData.components.hinges.systems[config.standHinge.id]; + if (hingeSystem?.printedParts) { + hingeSystem.printedParts.forEach((part) => { + if (part.required) { + parts.push({ ...part, category: 'Stand Hinges' }); + } + }); + } + } + + // Include stand feet option part + if (config.standFeet && partsData.components?.feet?.printedParts) { + const feetPart = partsData.components.feet.printedParts.find(p => p.id === config.standFeet.id); + if (feetPart) { + parts.push({ ...feetPart, category: 'Stand Feet' }); + } + } + + // Include stand crossbar support option parts + if (config.standCrossbarSupports && config.standCrossbarSupports.length > 0 && partsData.components?.crossbarSupports?.printedParts) { + const selectedSupportIds = new Set(config.standCrossbarSupports.map(opt => opt.id)); + partsData.components.crossbarSupports.printedParts.forEach((part) => { + if (selectedSupportIds.has(part.id)) { + // Skip hardware-only parts (e.g., aluminum standard-90-degree-support) + // These should only appear in the hardware list + if (part.isHardwareOnly) { + return; + } + parts.push({ ...part, category: 'Stand Crossbar Supports' }); + } + }); + } + + // Include remote system parts if a remote knob is selected + if (config.remoteKnob && partsData.components?.remotes?.systems) { + // Find which system contains this knob + let remoteSystem = null; + Object.values(partsData.components.remotes.systems).forEach((system) => { + if (system.knobs && system.knobs.find(k => k.id === config.remoteKnob.id)) { + remoteSystem = system; + } + }); + + if (remoteSystem) { + // Include body parts from the system + if (remoteSystem.bodyParts) { + remoteSystem.bodyParts.forEach((part) => { + if (part.required) { + parts.push({ ...part, category: 'Remote Body' }); + } + }); + } + + // Include the selected remote knob part + const knobPart = remoteSystem.knobs?.find(p => p.id === config.remoteKnob.id); + if (knobPart) { + parts.push({ ...knobPart, category: 'Remote Knobs' }); + } + } + } + + return parts; + }; + + // Helper function to categorize hardware by type + const getHardwareType = (hardware) => { + const id = hardware.id?.toLowerCase() || ''; + const name = hardware.name?.toLowerCase() || ''; + + // Fasteners + if (id.includes('fastener') || id.includes('screw') || id.includes('nut') || id.includes('washer') || + id.includes('bolt') || id.includes('handle') || name.includes('fastener') || name.includes('screw') || + name.includes('nut') || name.includes('washer') || name.includes('bolt') || name.includes('handle')) { + return 'Fasteners'; + } + + // Motion components + if (id.includes('bearing') || id.includes('pulley') || id.includes('belt') || id.includes('gear') || + id.includes('motor') || id.includes('rail') || id.includes('linear') || + name.includes('bearing') || name.includes('pulley') || name.includes('belt') || + name.includes('gear') || name.includes('motor') || name.includes('rail') || name.includes('linear')) { + return 'Motion Components'; + } + + // Aluminum extrusion / 3030 + if (id.includes('3030') || id.includes('extrusion') || id.includes('aluminum') || id.includes('support') || + name.includes('3030') || name.includes('extrusion') || name.includes('aluminum') || name.includes('90 degree support')) { + return 'Aluminum Extrusion'; + } + + // Electronics + if (id.includes('pcb') || id.includes('board') || id.includes('circuit') || id.includes('sensor') || + id.includes('switch') || id.includes('led') || name.includes('pcb') || name.includes('board') || + name.includes('circuit') || name.includes('sensor') || name.includes('switch') || name.includes('led')) { + return 'Electronics'; + } + + // Other / General Hardware + return 'Other Hardware'; + }; + + const getRequiredHardwareParts = () => { + const printedParts = getRequiredPrintedParts(); + const printedPartIds = new Set(printedParts.map(p => p.id)); + const hardwareParts = []; + const hardwareMap = new Map(); // To aggregate quantities for same hardware part + + // Collect all selected option IDs (including hardware-only options) + const selectedOptionIds = new Set(); + + // Add selected crossbar supports (may include hardware-only options) + if (config.standCrossbarSupports && config.standCrossbarSupports.length > 0) { + config.standCrossbarSupports.forEach(opt => selectedOptionIds.add(opt.id)); + } + + // Add other selected options that might be hardware-only + if (config.standHinge) selectedOptionIds.add(config.standHinge.id); + if (config.standFeet) selectedOptionIds.add(config.standFeet.id); + if (config.mount) selectedOptionIds.add(config.mount.id); + if (config.cover) selectedOptionIds.add(config.cover.id); + if (config.remoteKnob) selectedOptionIds.add(config.remoteKnob.id); + if (config.toyMountOptions && config.toyMountOptions.length > 0) { + config.toyMountOptions.forEach(opt => selectedOptionIds.add(opt.id)); + } + + // Handle hinges systems (new structure) + if (config.standHinge && partsData.components?.hinges?.systems) { + const hingeSystem = partsData.components.hinges.systems[config.standHinge.id]; + if (hingeSystem?.hardwareParts) { + hingeSystem.hardwareParts.forEach((hardware) => { + if (!hardware.required) return; + const key = hardware.id; + if (hardwareMap.has(key)) { + const existing = hardwareMap.get(key); + existing.quantity = (existing.quantity || 1) + (hardware.quantity || 1); + } else { + hardwareMap.set(key, { + ...hardware, + quantity: hardware.quantity || 1, + category: partsData.components.hinges.category || 'Hardware', + hardwareType: getHardwareType(hardware) + }); + } + }); + } + } + + // Handle remote systems (new structure) + if (config.remoteKnob && partsData.components?.remotes?.systems) { + // Find which system contains this knob + let remoteSystem = null; + Object.values(partsData.components.remotes.systems).forEach((system) => { + if (system.knobs && system.knobs.find(k => k.id === config.remoteKnob.id)) { + remoteSystem = system; + } + }); + + if (remoteSystem?.hardwareParts) { + remoteSystem.hardwareParts.forEach((hardware) => { + if (!hardware.required) return; + const key = hardware.id; + if (hardwareMap.has(key)) { + const existing = hardwareMap.get(key); + existing.quantity = (existing.quantity || 1) + (hardware.quantity || 1); + } else { + hardwareMap.set(key, { + ...hardware, + quantity: hardware.quantity || 1, + category: partsData.components.remotes.category || 'Hardware' + }); + } + }); + } + } + + // Build a map of component keys to their printed part IDs for quick lookup + const componentPrintedPartIds = new Map(); + Object.entries(partsData.components || {}).forEach(([componentKey, component]) => { + // Skip hinges and remotes as they're handled separately above + if (componentKey === 'hinges' || componentKey === 'remotes') return; + + if (component.printedParts) { + const partIds = component.printedParts.map(p => p.id); + componentPrintedPartIds.set(componentKey, new Set(partIds)); + } + }); + + // Iterate through all components to find hardware parts (excluding hinges and remotes) + Object.entries(partsData.components || {}).forEach(([componentKey, component]) => { + // Skip hinges and remotes as they're handled separately above + if (componentKey === 'hinges' || componentKey === 'remotes') return; + + if (!component.hardwareParts) return; + + // Check if this component has any selected printed parts OR selected options + const componentPartIds = componentPrintedPartIds.get(componentKey); + const hasSelectedParts = componentPartIds && Array.from(componentPartIds).some(id => printedPartIds.has(id) || selectedOptionIds.has(id)); + + component.hardwareParts.forEach((hardware) => { + if (!hardware.required) return; + + // If component has selected parts, check if hardware should be included + let shouldInclude = false; + + if (hasSelectedParts) { + const relatedParts = hardware.relatedParts || []; + // If no relatedParts specified, include if component has selected parts + // If relatedParts specified, include if any related part is selected (printed or option) + if (relatedParts.length === 0) { + shouldInclude = true; + } else { + shouldInclude = relatedParts.some(partId => printedPartIds.has(partId) || selectedOptionIds.has(partId)); + } + } + + if (shouldInclude) { + const key = hardware.id; + if (hardwareMap.has(key)) { + // Aggregate quantities if same hardware appears multiple times + const existing = hardwareMap.get(key); + existing.quantity = (existing.quantity || 1) + (hardware.quantity || 1); + } else { + hardwareMap.set(key, { + ...hardware, + quantity: hardware.quantity || 1, + category: component.category || 'Hardware', + hardwareType: getHardwareType(hardware) + }); + } + } + }); + }); + + // Convert map to array + hardwareMap.forEach((hardware) => { + hardwareParts.push(hardware); + }); + + return hardwareParts; + }; + + // Parse time estimate string (e.g., "2h14m", "1h3m", "40m25s") to minutes + const parseTimeToMinutes = (timeStr) => { + if (!timeStr || typeof timeStr !== 'string') return 0; + + let totalMinutes = 0; + const hourMatch = timeStr.match(/(\d+)h/); + const minuteMatch = timeStr.match(/(\d+)m/); + const secondMatch = timeStr.match(/(\d+)s/); + + if (hourMatch) totalMinutes += parseInt(hourMatch[1], 10) * 60; + if (minuteMatch) totalMinutes += parseInt(minuteMatch[1], 10); + if (secondMatch) totalMinutes += parseFloat(secondMatch[1]) / 60; + + return totalMinutes; + }; + + // Format minutes to readable string (e.g., "2h 14m") + const formatTimeFromMinutes = (minutes) => { + if (minutes === 0) return '0m'; + + const hours = Math.floor(minutes / 60); + const mins = Math.round(minutes % 60); + + if (hours > 0 && mins > 0) { + return `${hours}h ${mins}m`; + } else if (hours > 0) { + return `${hours}h`; + } else { + return `${mins}m`; + } + }; + + const getTotalFilamentEstimate = () => { + const parts = getRequiredPrintedParts(); + const totals = { + primary: 0, + secondary: 0, + total: 0 + }; + + parts.forEach((part) => { + let estimate = 0; + + // Handle both numeric and string values (e.g., "~147g" or 147.19) + if (typeof part.filamentEstimate === 'number') { + estimate = part.filamentEstimate; + } else if (typeof part.filamentEstimate === 'string') { + // Parse string format like "~147g" or "147g" + const cleaned = part.filamentEstimate.replace(/[~g]/g, '').trim(); + estimate = parseFloat(cleaned) || 0; + } + + // Multiply by quantity if specified (default to 1) + const quantity = part.quantity || 1; + estimate = estimate * quantity; + + const colour = part.colour || 'primary'; + + if (colour === 'primary') { + totals.primary += estimate; + } else if (colour === 'secondary') { + totals.secondary += estimate; + } + totals.total += estimate; + }); + + return totals; + }; + + const getTotalTimeEstimate = () => { + const parts = getRequiredPrintedParts(); + let totalMinutes = 0; + + parts.forEach((part) => { + if (part.timeEstimate) { + const timeMinutes = parseTimeToMinutes(part.timeEstimate); + // Multiply by quantity if specified (default to 1) + const quantity = part.quantity || 1; + totalMinutes += timeMinutes * quantity; + } + }); + + return formatTimeFromMinutes(totalMinutes); + }; + + const getColorName = (colorId, type = 'primary') => { + const colors = type === 'primary' ? partsData.colors.primary : partsData.colors.accent; + const color = colors.find((c) => c.id === colorId); + return color ? color.name : colorId; + }; + + const getColorHex = (colorId, type = 'primary') => { + const colors = type === 'primary' ? partsData.colors.primary : partsData.colors.accent; + const color = colors.find((c) => c.id === colorId); + return color ? color.hex : '#000000'; + }; + + const total = calculateTotal(); + const printedParts = getRequiredPrintedParts(); + const hardwareParts = getRequiredHardwareParts(); + const filamentTotals = getTotalFilamentEstimate(); + const totalTime = getTotalTimeEstimate(); + + // Group parts by category + const partsByCategory = printedParts.reduce((acc, part) => { + if (!acc[part.category]) { + acc[part.category] = []; + } + acc[part.category].push(part); + return acc; + }, {}); + + // Group hardware parts by category (unified view - aggregated quantities) + const hardwareByCategory = hardwareParts.reduce((acc, part) => { + if (!acc[part.category]) { + acc[part.category] = []; + } + acc[part.category].push(part); + return acc; + }, {}); + + // Group hardware parts by type for unified view (Fasteners, Motion Components, etc.) + const hardwareByType = hardwareParts.reduce((acc, part) => { + const type = part.hardwareType || 'Other Hardware'; + if (!acc[type]) { + acc[type] = []; + } + acc[type].push(part); + return acc; + }, {}); + + // Expanded view: Get hardware parts grouped by component (for expanded view) + const getExpandedHardwareParts = () => { + const printedParts = getRequiredPrintedParts(); + const printedPartIds = new Set(printedParts.map(p => p.id)); + const expandedHardware = []; + const selectedOptionIds = new Set(); + + if (config.standCrossbarSupports && config.standCrossbarSupports.length > 0) { + config.standCrossbarSupports.forEach(opt => selectedOptionIds.add(opt.id)); + } + if (config.standHinge) selectedOptionIds.add(config.standHinge.id); + if (config.standFeet) selectedOptionIds.add(config.standFeet.id); + if (config.mount) selectedOptionIds.add(config.mount.id); + if (config.cover) selectedOptionIds.add(config.cover.id); + if (config.remoteKnob) selectedOptionIds.add(config.remoteKnob.id); + if (config.toyMountOptions && config.toyMountOptions.length > 0) { + config.toyMountOptions.forEach(opt => selectedOptionIds.add(opt.id)); + } + + // Handle hinges systems + if (config.standHinge && partsData.components?.hinges?.systems) { + const hingeSystem = partsData.components.hinges.systems[config.standHinge.id]; + if (hingeSystem?.hardwareParts) { + const componentHardware = []; + hingeSystem.hardwareParts.forEach((hardware) => { + if (hardware.required) { + componentHardware.push({ + ...hardware, + quantity: hardware.quantity || 1, + hardwareType: getHardwareType(hardware) + }); + } + }); + if (componentHardware.length > 0) { + expandedHardware.push({ + component: partsData.components.hinges.category || 'Hinges', + parts: componentHardware, + }); + } + } + } + + // Handle remote systems + if (config.remoteKnob && partsData.components?.remotes?.systems) { + let remoteSystem = null; + Object.values(partsData.components.remotes.systems).forEach((system) => { + if (system.knobs && system.knobs.find(k => k.id === config.remoteKnob.id)) { + remoteSystem = system; + } + }); + if (remoteSystem?.hardwareParts) { + const componentHardware = []; + remoteSystem.hardwareParts.forEach((hardware) => { + if (hardware.required) { + componentHardware.push({ + ...hardware, + quantity: hardware.quantity || 1, + hardwareType: getHardwareType(hardware) + }); + } + }); + if (componentHardware.length > 0) { + expandedHardware.push({ + component: partsData.components.remotes.category || 'Remote', + parts: componentHardware, + }); + } + } + } + + // Handle other components + const componentPrintedPartIds = new Map(); + Object.entries(partsData.components || {}).forEach(([componentKey, component]) => { + if (componentKey === 'hinges' || componentKey === 'remotes') return; + if (component.printedParts) { + const partIds = component.printedParts.map(p => p.id); + componentPrintedPartIds.set(componentKey, new Set(partIds)); + } + }); + + Object.entries(partsData.components || {}).forEach(([componentKey, component]) => { + if (componentKey === 'hinges' || componentKey === 'remotes') return; + if (!component.hardwareParts) return; + + const componentPartIds = componentPrintedPartIds.get(componentKey); + const hasSelectedParts = componentPartIds && Array.from(componentPartIds).some(id => printedPartIds.has(id) || selectedOptionIds.has(id)); + + const componentHardware = []; + component.hardwareParts.forEach((hardware) => { + if (!hardware.required) return; + + let shouldInclude = false; + if (hasSelectedParts) { + const relatedParts = hardware.relatedParts || []; + if (relatedParts.length === 0) { + shouldInclude = true; + } else { + shouldInclude = relatedParts.some(partId => printedPartIds.has(partId) || selectedOptionIds.has(partId)); + } + } + + if (shouldInclude) { + componentHardware.push({ + ...hardware, + quantity: hardware.quantity || 1, + }); + } + }); + + if (componentHardware.length > 0) { + expandedHardware.push({ + component: component.category || componentKey, + parts: componentHardware, + }); + } + }); + + return expandedHardware; + }; + + const expandedHardwareByComponent = getExpandedHardwareParts(); + + // Define main sections and their subcategories + const mainSections = { + 'Actuator + Mount': ['Actuator Body', 'Mount', 'Cover'], + 'Stand': ['Stand', 'Stand Hinges', 'Stand Feet', 'Stand Crossbar Supports'], + 'Remote': ['Remote Body', 'Remote Knobs'], + }; + + // Helper to check if a section has any parts + const sectionHasParts = (subcategories) => { + return subcategories.some(cat => partsByCategory[cat] && partsByCategory[cat].length > 0); + }; + + // Define tabs + const tabs = [ + { id: 'overview', label: 'Overview' }, + { id: 'printed', label: 'Printed Parts' }, + { id: 'hardware', label: 'Hardware' }, + ]; + + return ( +
+

BOM Summary

+

+ Review your configuration and bill of materials. +

+ + {/* Tab Navigation */} +
+ +
+ + {/* Tab Content */} +
+ {/* Overview Tab */} + {activeTab === 'overview' && ( +
+ {/* Hardware (Motor & Power Supply) */} + {(config.motor || config.powerSupply) && ( +
+

Hardware

+
+ {config.motor && ( +
+ {config.motor.image && ( + {config.motor.name} { + e.target.style.display = 'none'; + }} + /> + )} + {config.motor.name} + {formatPrice(config.motor.price)} +
+ )} + {config.powerSupply && ( +
+ {config.powerSupply.image && ( + {config.powerSupply.name} { + e.target.style.display = 'none'; + }} + /> + )} + {config.powerSupply.name} + {formatPrice(config.powerSupply.price)} +
+ )} +
+
+ )} + + {/* Filament Usage */} + {(filamentTotals.total > 0 || totalTime !== '0m') && ( +
+

Filament Usage

+
+ {filamentTotals.total > 0 && ( +
+
+ Total Filament: + {Math.round(filamentTotals.total)}g +
+ {filamentTotals.primary > 0 && ( +
+
+
+ Primary ({getColorName(config.primaryColor, 'primary')}): +
+ {Math.round(filamentTotals.primary)}g +
+ )} + {filamentTotals.secondary > 0 && ( +
+
+
+ Secondary ({getColorName(config.accentColor, 'accent')}): +
+ {Math.round(filamentTotals.secondary)}g +
+ )} +
+ )} + {totalTime !== '0m' && ( +
+ Total Printing Time: + {totalTime} +
+ )} +
+
+ )} + + {/* Selected Options/Kit */} + {(config.mount || config.cover || config.standHinge || config.standFeet || + (config.standCrossbarSupports && config.standCrossbarSupports.length > 0) || + (config.remoteType || config.remote?.id)) && ( +
+

Selected Options

+
+ {config.mount && ( +
+ {config.mount.image && ( + {config.mount.name} { + e.target.style.display = 'none'; + }} + /> + )} + {config.mount.name} +
+ )} + {config.cover && ( +
+ {config.cover.image && ( + {config.cover.name} { + e.target.style.display = 'none'; + }} + /> + )} + {config.cover.name} +
+ )} + {config.standHinge && ( +
+ {config.standHinge.image && ( + {config.standHinge.name} { + e.target.style.display = 'none'; + }} + /> + )} + {config.standHinge.name} +
+ )} + {config.standFeet && ( +
+ {config.standFeet.image && ( + {config.standFeet.name} { + e.target.style.display = 'none'; + }} + /> + )} + {config.standFeet.name} +
+ )} + {config.standCrossbarSupports && config.standCrossbarSupports.length > 0 && ( + <> + {config.standCrossbarSupports.map((support) => ( +
+ {support.image && ( + {support.name} { + e.target.style.display = 'none'; + }} + /> + )} + {support.name} +
+ ))} + + )} + {(config.remoteType || config.remote?.id) && (() => { + const remoteId = config.remoteType || config.remote?.id; + const remoteSystem = partsData.components?.remotes?.systems?.[remoteId]; + return remoteSystem ? ( +
+ {remoteSystem.image && ( + {remoteSystem.name} { + e.target.style.display = 'none'; + }} + /> + )} + {remoteSystem.name} +
+ ) : null; + })()} +
+
+ )} + + {/* Toy Mounts */} + {config.toyMountOptions && config.toyMountOptions.length > 0 && ( +
+

Toy Mounts

+
+ {config.toyMountOptions.map((toyMount) => ( +
+ {toyMount.image && ( + {toyMount.name} { + e.target.style.display = 'none'; + }} + /> + )} + {toyMount.name} + {toyMount.description && ( + {toyMount.description} + )} +
+ ))} +
+
+ )} + + {/* Total */} +
+
+

Total Hardware Cost

+

${total.toFixed(2)}

+
+
+
+ )} + + {/* Printed Parts Tab */} + {activeTab === 'printed' && ( +
+ {printedParts.length > 0 ? ( + <> +
+

Required Printed Parts

+
+
+ {Object.entries(mainSections).map(([mainSectionName, subcategories]) => { + if (!sectionHasParts(subcategories)) { + return null; + } + + return ( +
+

{mainSectionName}

+
+ {subcategories.map((category) => { + const parts = partsByCategory[category]; + if (!parts || parts.length === 0) { + return null; + } + + return ( +
+
+
{category}
+ {parts.some(p => p.replacesActuatorMiddle) && ( + + Replaces standard ossm-actuator-body-middle + + )} +
+
+ + + + + + + + + + + + + {parts.map((part) => { + const partColour = part.colour || 'primary'; + const colorHex = getColorHex( + partColour === 'primary' ? config.primaryColor : config.accentColor, + partColour + ); + const colorName = getColorName( + partColour === 'primary' ? config.primaryColor : config.accentColor, + partColour + ); + + return ( + + + + + + + + + ); + })} + +
Part NameColorDescriptionFile PathQuantityFilament
+

{part.name}

+
+
+
+ {partColour} +
+
+

{part.description || '-'}

+
+ {part.isHardwareOnly ? ( + Hardware only + ) : part.filePath ? ( +

{part.filePath}

+ ) : ( + - + )} +
+

{part.quantity || 1}

+
+ {part.isHardwareOnly ? ( + - + ) : part.filamentEstimate !== undefined && part.filamentEstimate > 0 ? ( +

+ {typeof part.filamentEstimate === 'number' + ? (part.filamentEstimate * (part.quantity || 1)).toFixed(1) + : part.filamentEstimate}g + {(part.quantity || 1) > 1 && ( + + ({(typeof part.filamentEstimate === 'number' ? part.filamentEstimate : parseFloat(part.filamentEstimate.replace(/[~g]/g, '').trim()) || 0).toFixed(1)}g × {part.quantity}) + + )} +

+ ) : ( + - + )} +
+
+
+ ); + })} +
+
+ ); + })} + + {/* Other categories not in main sections (e.g., Toy Mounts) */} + {Object.entries(partsByCategory).map(([category, parts]) => { + const isInMainSection = Object.values(mainSections).flat().includes(category); + if (isInMainSection) { + return null; + } + + return ( +
+
+

{category}

+ {parts.some(p => p.replacesActuatorMiddle) && ( + + Replaces standard ossm-actuator-body-middle + + )} +
+
+ + + + + + + + + + + + + {parts.map((part) => { + const partColour = part.colour || 'primary'; + const colorHex = getColorHex( + partColour === 'primary' ? config.primaryColor : config.accentColor, + partColour + ); + const colorName = getColorName( + partColour === 'primary' ? config.primaryColor : config.accentColor, + partColour + ); + + return ( + + + + + + + + + ); + })} + +
Part NameColorDescriptionFile PathQuantityFilament
+

{part.name}

+
+
+
+ {partColour} +
+
+

{part.description || '-'}

+
+ {part.isHardwareOnly ? ( + Hardware only + ) : part.filePath ? ( +

{part.filePath}

+ ) : ( + - + )} +
+

{part.quantity || 1}

+
+ {part.filamentEstimate !== undefined && part.filamentEstimate > 0 ? ( +

+ {typeof part.filamentEstimate === 'number' + ? (part.filamentEstimate * (part.quantity || 1)).toFixed(1) + : part.filamentEstimate}g + {(part.quantity || 1) > 1 && ( + + ({(typeof part.filamentEstimate === 'number' ? part.filamentEstimate : parseFloat(part.filamentEstimate.replace(/[~g]/g, '').trim()) || 0).toFixed(1)}g × {part.quantity}) + + )} +

+ ) : ( + - + )} +
+
+
+ ); + })} + {(filamentTotals.total > 0 || totalTime !== '0m') && ( +
+ {filamentTotals.total > 0 && ( +
+
+ Total Filament Estimate: + {Math.round(filamentTotals.total)}g +
+ {filamentTotals.primary > 0 && ( +
+ Primary ({getColorName(config.primaryColor, 'primary')}): + {Math.round(filamentTotals.primary)}g +
+ )} + {filamentTotals.secondary > 0 && ( +
+ Secondary ({getColorName(config.accentColor, 'accent')}): + {Math.round(filamentTotals.secondary)}g +
+ )} +
+ )} + {totalTime !== '0m' && ( +
+ Total Printing Time: + {totalTime} +
+ )} +
+ )} +
+ + ) : ( +
+

No printed parts required for this configuration.

+
+ )} +
+ )} + + {/* Hardware Tab */} + {activeTab === 'hardware' && ( +
+ {hardwareParts.length > 0 ? ( + <> +
+
+

Required Hardware Parts

+
+ + +
+
+
+
+ {hardwareViewMode === 'unified' ? ( + // Unified view: Group by hardware type (Fasteners, Motion Components, etc.) + Object.entries(hardwareByType).sort(([a], [b]) => { + // Sort order: Fasteners, Motion Components, Aluminum Extrusion, Electronics, Other Hardware + const order = ['Fasteners', 'Motion Components', 'Aluminum Extrusion', 'Electronics', 'Other Hardware']; + const indexA = order.indexOf(a); + const indexB = order.indexOf(b); + if (indexA === -1 && indexB === -1) return a.localeCompare(b); + if (indexA === -1) return 1; + if (indexB === -1) return -1; + return indexA - indexB; + }).map(([type, parts]) => ( +
+

{type}

+
+ + + + + + + + + + + {parts.map((part) => ( + + + + + + + ))} + +
Part NameDescriptionQuantityPrice
+

{part.name}

+
+

{part.description || '-'}

+
+

{part.quantity || 1}

+
+ {part.price && part.price > 0 ? ( +

{formatPrice(part.price)}

+ ) : ( + - + )} +
+
+
+ )) + ) : ( + // Expanded view: Group by component BOMs (shows hardware breakdown by component) + expandedHardwareByComponent.map(({ component, parts }) => ( +
+

{component}

+
+ + + + + + + + + + + + {parts.map((part) => ( + + + + + + + + ))} + +
Part NameDescriptionTypeQuantityPrice
+

{part.name}

+
+

{part.description || '-'}

+
+ + {part.hardwareType || 'Other Hardware'} + + +

{part.quantity || 1}

+
+ {part.price && part.price > 0 ? ( +

{formatPrice(part.price)}

+ ) : ( + - + )} +
+
+
+ )) + )} +
+ + ) : ( +
+

No hardware parts required for this configuration.

+
+ )} +
+ )} + + {/* Export & Share Buttons - Show on all tabs */} +
+
+ {/* Share Button */} + + + {/* Export Button */} + +
+ + {/* Legacy export buttons (hidden but kept for backward compatibility) */} +
+ Export includes: Overview (Markdown), BOM (Excel), Print List (Excel), and Print Files (organized by component/color) +
+
+
+
+ ); +} diff --git a/website/src/components/MainPage.jsx b/website/src/components/MainPage.jsx new file mode 100644 index 0000000..52307b5 --- /dev/null +++ b/website/src/components/MainPage.jsx @@ -0,0 +1,125 @@ +import partsData from '../data/index.js'; + +export default function MainPage({ onSelectBuildType }) { + const handleSelect = (buildType) => { + onSelectBuildType(buildType); + }; + + return ( +
+
+ {/* Header */} +
+

+ OSSM Configurator +

+

+ Configure your Open Source Sex Machine +

+
+ + {/* Build Type Selection */} +
+

+ Select Your Build Type +

+ +
+ {/* New Build - RAD Kit */} + + + {/* New Build - Self Source */} + + + {/* Upgrade */} + +
+
+
+
+ ); +} diff --git a/website/src/components/Wizard.jsx b/website/src/components/Wizard.jsx new file mode 100644 index 0000000..811b515 --- /dev/null +++ b/website/src/components/Wizard.jsx @@ -0,0 +1,302 @@ +import { useState, useEffect } from 'react'; +import MotorStep from './steps/MotorStep'; +import PowerSupplyStep from './steps/PowerSupplyStep'; +import ColorsStep from './steps/ColorsStep'; +import OptionsStep from './steps/OptionsStep'; +import RemoteStep from './steps/RemoteStep'; +import ToyMountStep from './steps/ToyMountStep'; +import BOMSummary from './BOMSummary'; + +const steps = [ + { id: 'motor', name: 'Motor', component: MotorStep }, + { id: 'powersupply', name: 'Power Supply', component: PowerSupplyStep }, + { id: 'colors', name: 'Colors', component: ColorsStep }, + { id: 'options', name: 'Options', component: OptionsStep }, + { id: 'remote', name: 'Remote', component: RemoteStep }, + { id: 'toymounts', name: 'Toy Mounts', component: ToyMountStep }, + { id: 'summary', name: 'Summary', component: BOMSummary }, +]; + +export default function Wizard({ buildType = 'self-source', initialConfig, updateConfig: updateConfigProp, onBackToMain }) { + // For RAD Kit, start at Remote step (index 4) + const getInitialStep = () => { + if (buildType === 'rad-kit') { + // Remote step is at index 4 (Motor=0, PowerSupply=1, Colors=2, Options=3, Remote=4, ToyMounts=5, Summary=6) + return 4; + } + return 0; + }; + + const [currentStep, setCurrentStep] = useState(getInitialStep()); + const [config, setConfig] = useState(initialConfig || { + motor: '57AIM30', + powerSupply: '24V PSU', + primaryColor: 'black', + accentColor: 'black', + mount: 'Middle Pivot', + cover: 'Simple', + standHinge: 'Pivot Plate', + standFeet: '3030 Extrusion', + standCrossbarSupports: 'standard', + pcbMount: null, + }); + + useEffect(() => { + if (initialConfig) { + setConfig(initialConfig); + } + }, [initialConfig]); + + const updateConfig = (updates) => { + const newConfig = { ...config, ...updates }; + setConfig(newConfig); + if (updateConfigProp) { + updateConfigProp(newConfig); + } + }; + + // Filter steps for upgrade mode - skip motor and power supply, start with options + const getFilteredSteps = () => { + if (buildType === 'upgrade') { + // For upgrade mode, only show options and summary + return [ + { id: 'options', name: 'Upgrade Options', component: OptionsStep }, + { id: 'summary', name: 'Summary', component: BOMSummary }, + ]; + } + return steps; + }; + + const filteredSteps = getFilteredSteps(); + + const nextStep = () => { + // In upgrade mode, no validation needed + if (buildType === 'upgrade') { + if (currentStep < filteredSteps.length - 1) { + setCurrentStep(currentStep + 1); + } + return; + } + + // Validate required selections before moving to next step + if (currentStep === 0 && !config.motor) { + // Motor step - require motor selection + return; + } + if (currentStep === 1 && !config.powerSupply) { + // Power Supply step - require power supply selection + return; + } + + if (currentStep < filteredSteps.length - 1) { + setCurrentStep(currentStep + 1); + } + }; + + const prevStep = () => { + if (currentStep > 0) { + setCurrentStep(currentStep - 1); + } + }; + + const canNavigateToStep = (stepIndex) => { + // Can always go back or stay on current step + if (stepIndex <= currentStep) { + return true; + } + + // In upgrade mode, no validation needed + if (buildType === 'upgrade') { + return true; + } + + // In RAD Kit mode, all steps are pre-selected, so navigation is always allowed + if (buildType === 'rad-kit') { + return true; + } + + // Check if required steps are completed before jumping ahead + if (stepIndex > 0 && !config.motor) { + return false; // Can't skip motor step + } + if (stepIndex > 1 && !config.powerSupply) { + return false; // Can't skip power supply step + } + + return true; + }; + + const goToStep = (stepIndex) => { + if (stepIndex >= 0 && stepIndex < filteredSteps.length && canNavigateToStep(stepIndex)) { + setCurrentStep(stepIndex); + } + }; + + const canProceedToNextStep = () => { + // In upgrade mode, no validation needed + if (buildType === 'upgrade') { + return true; + } + + if (currentStep === 0 && !config.motor) { + return false; // Motor step - require motor selection + } + if (currentStep === 1 && !config.powerSupply) { + return false; // Power Supply step - require power supply selection + } + return true; + }; + + const CurrentStepComponent = filteredSteps[currentStep].component; + + // Adjust current step if we're in upgrade mode + useEffect(() => { + if (buildType === 'upgrade' && currentStep >= 2) { + // Skip to options step (index 0 in filtered steps) + setCurrentStep(0); + } + }, [buildType, currentStep]); + + return ( +
+
+ {/* Back Button */} + {onBackToMain && ( +
+ +
+ )} + + {/* Header */} +
+

+ OSSM Configurator +

+

+ {buildType === 'upgrade' + ? 'Select upgrade components and modifications' + : 'Configure your Open Source Sex Machine'} +

+ {buildType === 'upgrade' && ( +
+

+ Upgrade Mode: Only modification and upgrade components are shown +

+
+ )} +
+ + {/* Step Indicator */} +
+
+ {filteredSteps.map((step, index) => ( +
+ {/* Circle */} + + {/* Connecting line to the right */} + {index < filteredSteps.length - 1 && ( +
+ )} + {/* Text label */} + +
+ ))} +
+
+ + {/* Step Content */} +
+ +
+ + {/* Navigation Buttons */} + {currentStep < filteredSteps.length - 1 && ( +
+ +
+ +
+
+ )} +
+
+ ); +} diff --git a/website/src/components/steps/ColorsStep.jsx b/website/src/components/steps/ColorsStep.jsx new file mode 100644 index 0000000..b7b5f68 --- /dev/null +++ b/website/src/components/steps/ColorsStep.jsx @@ -0,0 +1,108 @@ +import partsData from '../../data/index.js'; + +export default function ColorsStep({ config, updateConfig }) { + const handlePrimaryColorSelect = (color) => { + updateConfig({ primaryColor: color.id }); + }; + + const handleAccentColorSelect = (color) => { + updateConfig({ accentColor: color.id }); + }; + + return ( +
+

Select Colors

+

+ Choose primary and accent colors for your OSSM build. +

+ +
+ {/* Primary Color */} +
+

Primary Color

+
+ {partsData.colors.primary.map((color) => ( + + ))} +
+
+ + {/* Accent Color */} +
+

Accent Color

+
+ {partsData.colors.accent.map((color) => ( + + ))} +
+
+
+
+ ); +} diff --git a/website/src/components/steps/MotorStep.jsx b/website/src/components/steps/MotorStep.jsx new file mode 100644 index 0000000..412040f --- /dev/null +++ b/website/src/components/steps/MotorStep.jsx @@ -0,0 +1,159 @@ +import partsData from '../../data/index.js'; +import { formatPrice } from '../../utils/priceFormat'; + +export default function MotorStep({ config, updateConfig }) { + const selectedMotorId = config.motor?.id; + + const handleSelect = (motor) => { + updateConfig({ motor }); + }; + + const recommendedMotors = partsData.motors.filter(m => m.recommended); + const otherMotors = partsData.motors.filter(m => !m.recommended); + const hasSingleRecommended = recommendedMotors.length === 1; + + const renderMotorCard = (motor, isRecommended = false, isSlightlyLarger = false) => ( + + ); + + return ( +
+

Select Motor

+

+ Choose the stepper motor for your OSSM build. +

+ + {/* Recommended Motor(s) */} + {recommendedMotors.length > 0 && ( +
+ {hasSingleRecommended ? ( +
+ {renderMotorCard(recommendedMotors[0], true, true)} +
+ ) : ( +
+

Recommended Options

+
+ {recommendedMotors.map((motor) => renderMotorCard(motor, true, false))} +
+
+ )} +
+ )} + + {/* Other Motors - Smaller Grid */} + {otherMotors.length > 0 && ( +
+

Other Options

+
+ {otherMotors.map((motor) => renderMotorCard(motor, false, false))} +
+
+ )} +
+ ); +} diff --git a/website/src/components/steps/OptionsStep.jsx b/website/src/components/steps/OptionsStep.jsx new file mode 100644 index 0000000..9fb046c --- /dev/null +++ b/website/src/components/steps/OptionsStep.jsx @@ -0,0 +1,446 @@ +import { useState, useEffect } from 'react'; +import partsData from '../../data/index.js'; +import { formatPrice } from '../../utils/priceFormat'; + +export default function OptionsStep({ config, updateConfig, buildType }) { + const [expandedMainSections, setExpandedMainSections] = useState({}); + const [expandedSubSections, setExpandedSubSections] = useState({}); + + const handleMountSelect = (option) => { + updateConfig({ mount: option }); + setExpandedSubSections((prev) => ({ ...prev, 'actuator.mounts': false })); + }; + + const handleCoverSelect = (option) => { + updateConfig({ cover: option }); + setExpandedSubSections((prev) => ({ ...prev, 'actuator.cover': false })); + }; + + const handlePcbMountSelect = (option) => { + updateConfig({ pcbMount: option }); + setExpandedSubSections((prev) => ({ ...prev, 'actuator.pcbMount': false })); + }; + + const handleStandHingeSelect = (option) => { + updateConfig({ standHinge: option }); + setExpandedSubSections((prev) => ({ ...prev, 'stand.hinges': false })); + }; + + const handleStandFeetSelect = (option) => { + updateConfig({ standFeet: option }); + setExpandedSubSections((prev) => ({ ...prev, 'stand.feet': false })); + }; + + const handleStandCrossbarSupportToggle = (option) => { + const currentSupports = config.standCrossbarSupports || []; + const isSelected = currentSupports.some((opt) => opt.id === option.id); + + if (isSelected) { + updateConfig({ + standCrossbarSupports: currentSupports.filter((opt) => opt.id !== option.id), + }); + } else { + updateConfig({ + standCrossbarSupports: [...currentSupports, option], + }); + } + }; + + const toggleMainSection = (mainSectionId) => { + setExpandedMainSections((prev) => ({ + ...prev, + [mainSectionId]: !prev[mainSectionId], + })); + }; + + const toggleSubSection = (subSectionKey) => { + setExpandedSubSections((prev) => ({ + ...prev, + [subSectionKey]: !prev[subSectionKey], + })); + }; + + const getSelectedOptionsForSubSection = (mainSectionId, subSectionId, subSection = null) => { + const key = `${mainSectionId}.${subSectionId}`; + + switch (key) { + case 'actuator.mounts': + return config.mount ? [config.mount] : []; + case 'actuator.cover': + return config.cover ? [config.cover] : []; + case 'actuator.pcbMount': + return config.pcbMount ? [config.pcbMount] : []; + case 'stand.hinges': + return config.standHinge ? [config.standHinge] : []; + case 'stand.feet': + return config.standFeet ? [config.standFeet] : []; + case 'stand.crossbarSupports': + return config.standCrossbarSupports || []; + default: + return []; + } + }; + + const isOptionSelected = (option, mainSectionId, subSectionId, subSection = null) => { + const selected = getSelectedOptionsForSubSection(mainSectionId, subSectionId, subSection); + return selected.some((opt) => opt.id === option.id); + }; + + const isMainSectionComplete = (mainSectionId, mainSection) => { + const subSections = Object.entries(mainSection.sections || {}); + + // Check if all sub-sections with options are complete + for (const [subSectionId, subSection] of subSections) { + // Skip if no options available + if (!subSection.options || subSection.options.length === 0) { + continue; + } + + const selectedOptions = getSelectedOptionsForSubSection(mainSectionId, subSectionId, subSection); + + // All sub-sections (both single-select and multi-select) require at least one selection + // Multi-select means you can select multiple items, but you still need at least one + if (selectedOptions.length === 0) { + return false; + } + } + + return true; + }; + + // Auto-collapse main sections when they become complete + useEffect(() => { + const mainSections = partsData.options ? Object.entries(partsData.options) : []; + + mainSections.forEach(([mainSectionId, mainSection]) => { + // Skip toyMounts and remoteControl sections (now in their own steps) + if (mainSectionId === 'toyMounts' || mainSectionId === 'remoteControl') { + return; + } + + if (isMainSectionComplete(mainSectionId, mainSection)) { + setExpandedMainSections((prev) => { + // Only auto-collapse if the section is currently expanded (undefined or true) + // Don't override if user has explicitly collapsed it (false) + const currentlyExpanded = prev[mainSectionId] !== false; + if (currentlyExpanded) { + return { ...prev, [mainSectionId]: false }; + } + return prev; + }); + } + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [config.mount, config.cover, config.pcbMount, config.standHinge, config.standFeet, config.standCrossbarSupports]); + + const handleOptionClick = (option, mainSectionId, subSectionId) => { + const key = `${mainSectionId}.${subSectionId}`; + + switch (key) { + case 'actuator.mounts': + handleMountSelect(option); + break; + case 'actuator.cover': + handleCoverSelect(option); + break; + case 'actuator.pcbMount': + handlePcbMountSelect(option); + break; + case 'stand.hinges': + handleStandHingeSelect(option); + break; + case 'stand.feet': + handleStandFeetSelect(option); + break; + case 'stand.crossbarSupports': + handleStandCrossbarSupportToggle(option); + break; + default: + break; + } + }; + + const renderOptionCard = (option, mainSectionId, subSectionId, subSection = null, isMultiSelect = false) => { + const isSelected = isOptionSelected(option, mainSectionId, subSectionId, subSection); + + return ( + + ); + }; + + const renderSubSection = (mainSectionId, subSectionId, subSection) => { + const subSectionKey = `${mainSectionId}.${subSectionId}`; + const selectedOptions = getSelectedOptionsForSubSection(mainSectionId, subSectionId, subSection); + const hasSelection = selectedOptions.length > 0; + const isExpanded = expandedSubSections[subSectionKey] !== false && (!hasSelection || expandedSubSections[subSectionKey] === true); + + return ( +
+ + {isExpanded && subSection.options && subSection.options.length > 0 && ( +
+
+ {subSection.options.map((option) => + renderOptionCard(option, mainSectionId, subSectionId, subSection, subSection.isMultiSelect) + )} +
+
+ )} +
+ ); + }; + + const renderMainSection = (mainSectionId, mainSection) => { + const isExpanded = expandedMainSections[mainSectionId] !== false; + const subSections = Object.entries(mainSection.sections || {}); + const isComplete = isMainSectionComplete(mainSectionId, mainSection); + + return ( +
+ + {isExpanded && ( +
+ {subSections.map(([subSectionId, subSection]) => + renderSubSection(mainSectionId, subSectionId, subSection) + )} +
+ )} +
+ ); + }; + + const mainSections = partsData.options ? Object.entries(partsData.options) : []; + + // Filter sections and options for upgrade mode + const getFilteredSections = () => { + if (buildType !== 'upgrade') { + return mainSections; + } + + // In upgrade mode, only show sections with mod components + return mainSections + .map(([mainSectionId, mainSection]) => { + // Filter sub-sections to only show those with mod options + const filteredSubSections = {}; + Object.entries(mainSection.sections || {}).forEach(([subSectionId, subSection]) => { + // Check if this sub-section has mod options + const hasModOptions = subSection.options?.some(opt => opt.type === 'mod') || + subSection.componentType === 'mod'; + + if (hasModOptions) { + // Filter options to only show mods + const modOptions = subSection.options?.filter(opt => opt.type === 'mod') || []; + if (modOptions.length > 0 || subSection.componentType === 'mod') { + filteredSubSections[subSectionId] = { + ...subSection, + options: modOptions.length > 0 ? modOptions : subSection.options, + }; + } + } + }); + + // Only include main section if it has any filtered sub-sections + if (Object.keys(filteredSubSections).length > 0) { + return [mainSectionId, { ...mainSection, sections: filteredSubSections }]; + } + return null; + }) + .filter(Boolean); + }; + + const filteredSections = getFilteredSections(); + + // Filter out toyMounts and remoteControl sections (now in their own steps) + const sectionsToRender = filteredSections.filter(([mainSectionId]) => mainSectionId !== 'toyMounts' && mainSectionId !== 'remoteControl'); + + return ( +
+

+ {buildType === 'upgrade' ? 'Select Upgrades & Modifications' : 'Select Options'} +

+

+ {buildType === 'upgrade' + ? 'Choose upgrade components and modifications for your existing build.' + : 'Choose your preferred mounting options and accessories.'} +

+ + {sectionsToRender.length === 0 && buildType === 'upgrade' && ( +
+

+ No upgrade components available. All components are base modules. +

+
+ )} + +
+ {sectionsToRender.map(([mainSectionId, mainSection]) => + renderMainSection(mainSectionId, mainSection) + )} +
+
+ ); +} diff --git a/website/src/components/steps/PowerSupplyStep.jsx b/website/src/components/steps/PowerSupplyStep.jsx new file mode 100644 index 0000000..432e413 --- /dev/null +++ b/website/src/components/steps/PowerSupplyStep.jsx @@ -0,0 +1,136 @@ +import partsData from '../../data/index.js'; +import { formatPrice } from '../../utils/priceFormat'; + +export default function PowerSupplyStep({ config, updateConfig }) { + const selectedPowerSupplyId = config.powerSupply?.id; + const selectedMotorId = config.motor?.id; + + const handleSelect = (powerSupply) => { + updateConfig({ powerSupply }); + }; + + // Filter compatible power supplies + const compatiblePowerSupplies = partsData.powerSupplies.filter((psu) => { + if (!selectedMotorId) return true; + return psu.compatibleMotors.includes(selectedMotorId); + }); + + return ( +
+

Select Power Supply

+

+ Choose a compatible power supply for your selected motor. +

+ + {selectedMotorId && ( +
+

+ Showing power supplies compatible with:{' '} + + {config.motor?.name || 'Selected Motor'} + +

+
+ )} + +
+ {compatiblePowerSupplies.map((powerSupply) => ( + + ))} +
+
+ ); +} diff --git a/website/src/components/steps/RemoteStep.jsx b/website/src/components/steps/RemoteStep.jsx new file mode 100644 index 0000000..f565961 --- /dev/null +++ b/website/src/components/steps/RemoteStep.jsx @@ -0,0 +1,322 @@ +import { useState, useEffect } from 'react'; +import partsData from '../../data/index.js'; +import { formatPrice } from '../../utils/priceFormat'; + +export default function RemoteStep({ config, updateConfig, buildType }) { + const [expandedKnobs, setExpandedKnobs] = useState(false); + + const availableRemotes = [ + { + id: 'ossm-remote-standard', + name: 'OSSM - Remote', + description: 'Standard OSSM remote (can be purchased from RAD or self-sourced with PCB Way)', + radrOnly: false, + }, + { + id: 'ossm-remote-radr', + name: 'OSSM - RADR', + description: 'RADR remote system (RAD only)', + radrOnly: true, + }, + ]; + + // Show all available remotes for both build types + const getAvailableRemotes = () => { + return availableRemotes; + }; + + const selectedRemoteId = config.remoteType || config.remote?.id; + const selectedRemotePCB = config.remotePCB || null; + + const handleRemoteSelect = (remoteId) => { + // Consolidate all updates into a single call to prevent state issues + const updates = { + remoteType: remoteId, + }; + + // Reset PCB selection when switching remotes + if (remoteId === 'ossm-remote-radr') { + // RADR only available from RAD + updates.remotePCB = 'rad'; + } else { + updates.remotePCB = null; + } + + // Clear knob selection when switching remotes + updates.remoteKnob = null; + + updateConfig(updates); + }; + + const handlePCBSelect = (source) => { + updateConfig({ remotePCB: source }); + }; + + const handleKnobSelect = (knob) => { + updateConfig({ remoteKnob: knob }); + }; + + const getSelectedRemoteSystem = () => { + if (!selectedRemoteId) return null; + return partsData.components?.remotes?.systems?.[selectedRemoteId] || null; + }; + + const getAvailableKnobs = () => { + const remoteSystem = getSelectedRemoteSystem(); + if (!remoteSystem || !remoteSystem.knobs) return []; + + return remoteSystem.knobs.map((knob) => ({ + id: knob.id, + name: knob.name, + description: knob.description, + filamentEstimate: knob.filamentEstimate !== undefined ? `~${knob.filamentEstimate}g` : "0g", + timeEstimate: knob.timeEstimate, + colour: knob.colour, + })); + }; + + const isKnobSelected = (knobId) => { + return config.remoteKnob?.id === knobId; + }; + + const renderRemoteCard = (remote) => { + const isSelected = selectedRemoteId === remote.id; + const remoteSystem = partsData.components?.remotes?.systems?.[remote.id]; + const imagePath = remoteSystem?.image ? `${remoteSystem.image}` : null; + + return ( + + ); + }; + + const renderPCBSelection = () => { + if (!selectedRemoteId || selectedRemoteId === 'ossm-remote-radr') { + // RADR only available from RAD, so no selection needed + return null; + } + + return ( +
+

PCB Purchase Source

+
+ + +
+
+ ); + }; + + const renderKnobCard = (knob) => { + const isSelected = isKnobSelected(knob.id); + + return ( + + ); + }; + + const availableRemotesFiltered = getAvailableRemotes(); + const availableKnobs = getAvailableKnobs(); + const hasRemoteSelected = !!selectedRemoteId; + const hasKnobSelected = !!config.remoteKnob; + + // Auto-expand knobs section when remote is selected + useEffect(() => { + if (hasRemoteSelected && availableKnobs.length > 0) { + setExpandedKnobs(true); + } + }, [hasRemoteSelected, availableKnobs.length]); + + return ( +
+

Select Remote Control

+

+ Choose your remote control system and knob option. +

+ + {/* Remote Selection */} +
+

Remote System

+
+ {availableRemotesFiltered.map((remote) => renderRemoteCard(remote))} +
+
+ + {/* PCB Purchase Source (only for OSSM - Remote) */} + {hasRemoteSelected && renderPCBSelection()} + + {/* Knobs Selection (only shown when remote is selected) */} + {hasRemoteSelected && availableKnobs.length > 0 && ( +
+ + {expandedKnobs && ( +
+
+ {availableKnobs.map((knob) => renderKnobCard(knob))} +
+
+ )} +
+ )} + + {!hasRemoteSelected && ( +
+

+ Note: Please select a remote control system to continue. +

+
+ )} +
+ ); +} diff --git a/website/src/components/steps/ToyMountStep.jsx b/website/src/components/steps/ToyMountStep.jsx new file mode 100644 index 0000000..6b4b94d --- /dev/null +++ b/website/src/components/steps/ToyMountStep.jsx @@ -0,0 +1,201 @@ +import { useState } from 'react'; +import partsData from '../../data/index.js'; +import { formatPrice } from '../../utils/priceFormat'; + +export default function ToyMountStep({ config, updateConfig }) { + const [expandedSubSections, setExpandedSubSections] = useState({}); + + const handleToyMountToggle = (option) => { + const currentToyMounts = config.toyMountOptions || []; + const isSelected = currentToyMounts.some((opt) => opt.id === option.id); + + if (isSelected) { + updateConfig({ + toyMountOptions: currentToyMounts.filter((opt) => opt.id !== option.id), + }); + } else { + updateConfig({ + toyMountOptions: [...currentToyMounts, option], + }); + } + }; + + const toggleSubSection = (subSectionKey) => { + setExpandedSubSections((prev) => ({ + ...prev, + [subSectionKey]: !prev[subSectionKey], + })); + }; + + const getSelectedOptionsForSubSection = (subSectionId, subSection) => { + const allToyMountOptions = config.toyMountOptions || []; + const subsectionOptionIds = new Set(subSection.options.map(opt => opt.id)); + return allToyMountOptions.filter(opt => subsectionOptionIds.has(opt.id)); + }; + + const isOptionSelected = (option, subSectionId, subSection) => { + const selected = getSelectedOptionsForSubSection(subSectionId, subSection); + return selected.some((opt) => opt.id === option.id); + }; + + const handleOptionClick = (option, subSectionId, subSection) => { + handleToyMountToggle(option); + }; + + const renderOptionCard = (option, subSectionId, subSection) => { + const isSelected = isOptionSelected(option, subSectionId, subSection); + + return ( + + ); + }; + + const renderSubSection = (subSectionId, subSection) => { + const subSectionKey = `toyMounts.${subSectionId}`; + const selectedOptions = getSelectedOptionsForSubSection(subSectionId, subSection); + const hasSelection = selectedOptions.length > 0; + const isExpanded = expandedSubSections[subSectionKey] !== false && (!hasSelection || expandedSubSections[subSectionKey] === true); + + return ( +
+ + {isExpanded && subSection.options && subSection.options.length > 0 && ( +
+ {subSection.options.map((option) => + renderOptionCard(option, subSectionId, subSection) + )} +
+ )} +
+ ); + }; + + const toyMountsSection = partsData.options?.toyMounts; + const subSections = toyMountsSection ? Object.entries(toyMountsSection.sections || {}) : []; + const hasSelection = (config.toyMountOptions || []).length > 0; + + return ( +
+

Select Toy Mounts

+

+ Choose your preferred toy mount options. You can select multiple options from different categories. +

+ + {subSections.length > 0 && ( +
+ {subSections.map(([subSectionId, subSection]) => + renderSubSection(subSectionId, subSection) + )} +
+ )} + + {!hasSelection && ( +
+

+ Note: At least one toy mount option is recommended for your build. +

+
+ )} +
+ ); +} diff --git a/website/src/data/colors.json b/website/src/data/colors.json new file mode 100644 index 0000000..fd5009e --- /dev/null +++ b/website/src/data/colors.json @@ -0,0 +1,76 @@ +{ + "primary": [ + { + "id": "black", + "name": "Black", + "hex": "#000000" + }, + { + "id": "white", + "name": "White", + "hex": "#FFFFFF" + }, + { + "id": "red", + "name": "Red", + "hex": "#EF4444" + }, + { + "id": "blue", + "name": "Blue", + "hex": "#3B82F6" + }, + { + "id": "purple", + "name": "Purple", + "hex": "#A855F7" + }, + { + "id": "pink", + "name": "Pink", + "hex": "#EC4899" + } + ], + "accent": [ + { + "id": "black", + "name": "Black", + "hex": "#000000" + }, + { + "id": "white", + "name": "White", + "hex": "#FFFFFF" + }, + { + "id": "red", + "name": "Red", + "hex": "#EF4444" + }, + { + "id": "blue", + "name": "Blue", + "hex": "#3B82F6" + }, + { + "id": "purple", + "name": "Purple", + "hex": "#A855F7" + }, + { + "id": "pink", + "name": "Pink", + "hex": "#EC4899" + }, + { + "id": "gold", + "name": "Gold", + "hex": "#F59E0B" + }, + { + "id": "silver", + "name": "Silver", + "hex": "#9CA3AF" + } + ] +} \ No newline at end of file diff --git a/website/src/data/compatibility.json b/website/src/data/compatibility.json new file mode 100644 index 0000000..c7e5c75 --- /dev/null +++ b/website/src/data/compatibility.json @@ -0,0 +1,29 @@ +{ + "rules": [ + { + "type": "mountToStandCrossbar", + "description": "Only 1 mount option can be selected", + "check": "mountOptions.length === 1" + }, + { + "type": "hingeToStandCrossbar", + "description": "Only 1 hinge option can be selected", + "check": "hingeOptions.length === 1" + }, + { + "type": "standCrossbarSupportToStandCrossbar", + "description": "Only 1 stand crossbar support option can be selected", + "check": "standCrossbarSupportOptions.length === 1" + } + ], + "requiredParts": { + "motor": true, + "powerSupply": true, + "actuatorMount": true, + "pcbMount": true + }, + "optionalParts": { + "toyMounts": false, + "standParts": false + } +} diff --git a/website/src/data/components/actuator.json b/website/src/data/components/actuator.json new file mode 100644 index 0000000..975db01 --- /dev/null +++ b/website/src/data/components/actuator.json @@ -0,0 +1,188 @@ +{ + "actuator": { + "category": "Actuator", + "type": "base", + "printedParts": [ + { + "id": "ossm-actuator-body-bottom", + "name": "Actuator Bottom", + "description": "Actuator bottom part", + "filamentEstimate": 56.7, + "timeEstimate": "2h14m", + "colour": "primary", + "required": true, + "filePath": "OSSM - Actuator Body Bottom.stl", + "url": "https://github.com/KinkyMakers/OSSM-hardware/blob/main/Printed%20Parts/Actuator/OSSM%20-%20Actuator%20-%20Body%20-%20Bottom.stl?raw=true" + }, + { + "id": "ossm-actuator-body-middle", + "name": "Actuator Middle", + "description": "Actuator middle part", + "filamentEstimate": 65.69, + "timeEstimate": "2h23m", + "colour": "primary", + "required": true, + "filePath": "OSSM - Actuator Body Middle.stl", + "url": "https://github.com/KinkyMakers/OSSM-hardware/blob/main/Printed%20Parts/Actuator/OSSM%20-%20Actuator%20-%20Body%20-%20Middle.stl?raw=true" + }, + { + "id": "ossm-actuator-body-cover", + "name": "Actuator Cover", + "description": "Actuator cover part", + "filamentEstimate": 27.61, + "timeEstimate": "1h3m", + "colour": "primary", + "required": true, + "filePath": "OSSM - Actuator Body Cover.stl", + "url": "https://github.com/KinkyMakers/OSSM-hardware/blob/main/Printed%20Parts/Actuator/OSSM%20-%20Actuator%20-%20Body%20-%20Cover.stl?raw=true" + }, + { + "id": "ossm-belt-tensioner", + "name": "Belt Tensioner", + "description": "Belt tensioner part", + "filamentEstimate": 10.51, + "timeEstimate": "40m25s", + "colour": "secondary", + "required": true, + "filePath": "OSSM - Belt Tensioner.stl", + "url": "https://github.com/KinkyMakers/OSSM-hardware/blob/main/Printed%20Parts/Actuator/OSSM%20-%20Belt%20Tensioner.stl?raw=true" + }, + { + "id": "ossm-24mm-clamping-thread-belt-clamp", + "name": "24mm Clamping Thread Belt Clamp", + "description": "24mm clamping thread part", + "filamentEstimate": 2.01, + "timeEstimate": "19m36s", + "colour": "secondary", + "required": true, + "filePath": "OSSM - 24mm Clamping Thread Belt Clamp.stl", + "url": "https://github.com/KinkyMakers/OSSM-hardware/blob/main/Printed%20Parts/Actuator/OSSM%20-%2024mm%20Clamping%20Thread%20-%20Belt%20Clamp.stl?raw=true" + }, + { + "id": "ossm-24mm-clamping-thread-end-effector", + "name": "24mm Clamping Thread End Effector", + "description": "24mm clamping thread end effector part", + "filamentEstimate": 18.52, + "timeEstimate": "1h20m", + "colour": "secondary", + "required": true, + "filePath": "OSSM - 24mm Clamping Thread End Effector.stl", + "url": "https://github.com/KinkyMakers/OSSM-hardware/blob/main/Printed%20Parts/Actuator/OSSM%20-%2024mm%20Clamping%20Thread%20-%20End%20Effector.stl?raw=true" + }, + { + "id": "ossm-24mm-nut-5-sided", + "name": "24mm Nut 5 Sided", + "description": "24mm nut 5 sided part", + "filamentEstimate": 5.12, + "timeEstimate": "21m10s", + "colour": "secondary", + "required": true, + "filePath": "OSSM - 24mm Nut 5 Sided.stl", + "url": "https://github.com/KinkyMakers/OSSM-hardware/blob/main/Printed%20Parts/Actuator/OSSM%20-%2024mm%20Nut%20-%205%20Sided.stl?raw=true" + } + ], + "hardwareParts": [ + { + "id": "hardware-fasteners-m3x8-shcs", + "required": true, + "quantity": 8, + "relatedParts": [ + "ossm-actuator-body-bottom" + ] + }, + { + "id": "hardware-fasteners-m3x16-shcs", + "required": true, + "quantity": 2, + "relatedParts": [ + "ossm-24mm-nut-6-sided" + ] + }, + { + "id": "hardware-fasteners-m3x20-shcs", + "required": true, + "quantity": 1, + "relatedParts": [ + "ossm-24mm-nut-5-sided" + ] + }, + { + "id": "hardware-fasteners-m3-hex-nut", + "required": true, + "quantity": 7, + "relatedParts": [ + "ossm-24mm-nut-hex" + ] + }, + { + "id": "hardware-fasteners-m5-hex-nut", + "required": true, + "quantity": 7, + "relatedParts": [ + "ossm-actuator-body-bottom" + ] + }, + { + "id": "hardware-fasteners-m5x20-shcs", + "required": true, + "quantity": 7, + "relatedParts": [ + "ossm-actuator-body-bottom", + "ossm-actuator-body-middle", + "ossm-actuator-body-middle-pivot" + ] + }, + { + "id": "hardware-fasteners-m5x35-shcs", + "required": true, + "quantity": 7, + "relatedParts": [ + "ossm-24mm-nut-shcs" + ] + }, + { + "id": "hardware-fasteners-m5x20mm-hex-coupling-nut", + "required": true, + "quantity": 7, + "relatedParts": [ + "ossm-24mm-nut-hex" + ] + }, + { + "id": "hardware-gt2-pulley", + "required": true, + "quantity": 1, + "relatedParts": [ + "ossm-actuator-body-bottom" + ] + }, + { + "id": "hardware-gt2-belt", + "required": true, + "quantity": 1, + "relatedParts": [ + "ossm-24mm-clamping-thread-belt-clamp", + "ossm-belt-tensioner" + ] + }, + { + "id": "hardware-mgn12h-linear-rail", + "required": true, + "quantity": 1, + "relatedParts": [ + "ossm-gt2-belt-clamp", + "ossm-24mm-nut-shcs", + "ossm-actuator-body-bottom" + ] + }, + { + "id": "hardware-bearing-MR115-2RS 5x11x4mm", + "required": true, + "quantity": 6, + "relatedParts": [ + "ossm-actuator-body-bottom" + ] + } + ] + } +} \ No newline at end of file diff --git a/website/src/data/components/mounting.json b/website/src/data/components/mounting.json new file mode 100644 index 0000000..6536f90 --- /dev/null +++ b/website/src/data/components/mounting.json @@ -0,0 +1,138 @@ +{ + "pitClamp": { + "category": "PitClamp", + "type": "base", + "printedParts": [ + { + "id": "ossm-pitclamp-mini-lower", + "name": "PitClamp Mini Lower", + "description": "PitClamp mounting system", + "filamentEstimate": 49.45, + "timeEstimate": "1h55m", + "colour": "primary", + "required": true, + "filePath": "OSSM - Base - PitClamp Mini - Lower V1.1.stl", + "url": "https://github.com/KinkyMakers/OSSM-hardware/blob/main/Printed%20Parts/Mounting/OSSM%20-%20Base%20-%20PitClamp%20Mini%20-%20Lower%20V1.1.stl?raw=true" + }, + { + "id": "ossm-pitclamp-mini-upper", + "name": "PitClamp Mini Upper", + "description": "PitClamp mounting system", + "filamentEstimate": 27.36, + "timeEstimate": "1h11m", + "colour": "primary", + "required": true, + "filePath": "OSSM - Base - PitClamp Mini - Upper V1.1.stl", + "url": "https://github.com/KinkyMakers/OSSM-hardware/blob/main/Printed%20Parts/Mounting/OSSM%20-%20Base%20-%20PitClamp%20Mini%20-%20Upper%20V1.1.stl?raw=true" + }, + { + "id": "ossm-pitclamp-mini-57AIM30", + "name": "PitClamp Mini 57AIM30", + "description": "PitClamp mounting system", + "filamentEstimate": 46.03, + "timeEstimate": "2h10m", + "colour": "primary", + "required": true, + "filePath": "OSSM - Base - PitClamp Mini - 57AIM30 V1.1.stl", + "url": "https://github.com/KinkyMakers/OSSM-hardware/blob/main/Printed%20Parts/Mounting/OSSM%20-%20Mounting%20Ring%20-%20PitClamp%20Mini%20-%2057AIM%20V1.1.stl?raw=true" + }, + { + "id": "ossm-pitclamp-mini-42AIM30", + "name": "PitClamp Mini 42AIM30", + "description": "PitClamp mounting system", + "filamentEstimate": 46.03, + "timeEstimate": "2h10m", + "colour": "primary", + "required": true, + "filePath": "OSSM - Base - PitClamp Mini - 42AIM30 V1.1.stl", + "url": "https://github.com/KinkyMakers/OSSM-hardware/blob/main/Printed%20Parts/Mounting/Non-standard/OSSM%20-%20Mounting%20Ring%20-%20PitClamp%20Mini%20-%2042AIM%20V1.1.stl?raw=true" + }, + { + "id": "ossm-pitclamp-mini-iHSV57", + "name": "PitClamp Mini iHSV57", + "description": "PitClamp mounting system", + "filamentEstimate": 46.03, + "timeEstimate": "2h10m", + "colour": "primary", + "required": true, + "filePath": "OSSM - Base - PitClamp Mini - iHSV57 V1.1.stl", + "url": "https://github.com/KinkyMakers/OSSM-hardware/blob/main/Printed%20Parts/Mounting/Non-standard/OSSM%20-%20Mounting%20Ring%20-%20PitClamp%20Mini%20-%20iHSV57.stl?raw=true" + }, + { + "id": "ossm-pitclamp-mini-handle", + "name": "PitClamp Mini Handle", + "description": "PitClamp mounting system", + "filamentEstimate": 9.23, + "timeEstimate": "2h10m", + "colour": "secondary", + "required": true, + "filePath": "OSSM - Handle - PitClamp Mini V1.1.stl", + "url": "https://github.com/KinkyMakers/OSSM-hardware/blob/main/Printed%20Parts/Mounting/OSSM%20-%20Base%20-%20PitClamp%20Mini%20-%20Handle.stl?raw=true" + }, + { + "id": "ossm-pitclamp-mini-dogbone-nuts", + "name": "PitClamp Mini Dogbone Nuts", + "description": "PitClamp mounting system", + "filamentEstimate": 4.44, + "timeEstimate": "20m49s", + "colour": "secondary", + "required": true, + "quantity": 2, + "filePath": "OSSM - Dogbone Nuts - PitClamp Mini V1.1.stl", + "url": "https://github.com/KinkyMakers/OSSM-hardware/blob/main/Printed%20Parts/Mounting/OSSM%20-%20Base%20-%20PitClamp%20Mini%20-%20Dogbone%20Nuts.stl?raw=true" + }, + { + "id": "ossm-pitclamp-mini-dogbone-bolts ", + "name": "PitClamp Mini Dogbone Bolts", + "description": "PitClamp mounting system", + "filamentEstimate": 4.44, + "timeEstimate": "20m49s", + "colour": "secondary", + "required": true, + "quantity": 2, + "filePath": "OSSM - Dogbone Bolts - PitClamp Mini V1.1.stl", + "url": "https://github.com/KinkyMakers/OSSM-hardware/blob/main/Printed%20Parts/Mounting/OSSM%20-%20Base%20-%20PitClamp%20Mini%20-%20Dogbone%20Bolts.stl?raw=true" + } + ], + "hardwareParts": [ + { + "id": "pitclamp-hardware", + "required": true + } + ] + }, + "middlePivot": { + "category": "Middle Pivot", + "type": "base", + "printedParts": [ + { + "id": "ossm-actuator-body-middle-pivot", + "name": "Actuator Body Middle Pivot", + "description": "Middle Pivot mounting system", + "filamentEstimate": 147.19, + "timeEstimate": "5h8m", + "colour": "primary", + "required": true, + "filePath": "OSSM - Actuator Body Middle Pivot.stl", + "url": "https://github.com/KinkyMakers/OSSM-hardware/blob/main/Printed%20Parts/Actuator/Non-standard/OSSM%20-%20Actuator%20-%20Body%20-%20Middle%20Pivot.stl?raw=true" + }, + { + "id": "ossm-handle-spacer", + "name": "Handle Spacer", + "description": "Handle spacer part", + "filamentEstimate": 0, + "colour": "secondary", + "required": true, + "quantity": 2, + "filePath": "OSSM - Handle Spacer.stl", + "url": "https://github.com/KinkyMakers/OSSM-hardware/blob/main/Printed%20Parts/Stand/OSSM%20-%20Stand%20-%203030%20Extrusion%20Base%20-%20Handle%20Spacer.stl?raw=true" + } + ], + "hardwareParts": [ + { + "id": "middle-pivot-hardware", + "required": true + } + ] + } +} \ No newline at end of file diff --git a/website/src/data/components/remote.json b/website/src/data/components/remote.json new file mode 100644 index 0000000..cb7702a --- /dev/null +++ b/website/src/data/components/remote.json @@ -0,0 +1,108 @@ +{ + "remotes": { + "category": "Remote", + "type": "mod", + "systems": { + "ossm-remote-standard": { + "name": "OSSM Remote Standard", + "description": "Standard OSSM remote system", + "image": "/images/remote/standard-remote.png", + "bodyParts": [ + { + "id": "ossm-remote-body", + "name": "Remote Body", + "description": "Remote system", + "filamentEstimate": 23.39, + "timeEstimate": "53m42s", + "colour": "primary", + "required": true, + "filePath": "ossm-remote-body.stl", + "url": "https://github.com/KinkyMakers/OSSM-hardware/blob/main/Printed%20Parts/Remote/OSSM%20-%20Remote%20-%20Body.stl?raw=true" + }, + { + "id": "ossm-remote-top-cover", + "name": "Remote Top Cover", + "description": "Remote system", + "filamentEstimate": 12.37, + "timeEstimate": "37m32s", + "colour": "secondary", + "required": true, + "filePath": "ossm-remote-top-cover.stl", + "url": "https://github.com/KinkyMakers/OSSM-hardware/blob/main/Printed%20Parts/Remote/OSSM%20-%20Remote%20-%20Top%20Cover.stl?raw=true" + } + ], + "knobs": [ + { + "id": "ossm-remote-knob", + "name": "Remote Knob", + "description": "Remote system", + "filamentEstimate": 20.79, + "timeEstimate": "1h14m", + "colour": "primary", + "required": true, + "filePath": "ossm-remote-knob.stl", + "url": "https://github.com/KinkyMakers/OSSM-hardware/blob/main/Printed%20Parts/Remote/OSSM%20-%20Remote%20-%20Knob%20-%20Rounded.stl?raw=true" + }, + { + "id": "ossm-remote-knob-simple", + "name": "Remote Knob Simple", + "description": "Remote system", + "filamentEstimate": 20.79, + "timeEstimate": "1h14m", + "colour": "primary", + "required": true, + "filePath": "ossm-remote-knob-simple.stl", + "url": "https://github.com/KinkyMakers/OSSM-hardware/tree/main/Printed%20Parts/Remote/Non-standard/OSSM%20-%20Remote%20-%20Knob%20-%20Simple.stl?raw=true" + }, + { + "id": "ossm-remote-knob-simple-with-position-indicator", + "name": "Remote Knob Simple With Position Indicator", + "description": "Remote system", + "filamentEstimate": 0, + "colour": "primary", + "required": false, + "filePath": "ossm-remote-knob-simple-with-position-indicator.stl", + "url": "https://github.com/KinkyMakers/OSSM-hardware/blob/main/Printed%20Parts/Remote/Non-standard/OSSM%20-%20Remote%20-%20Knob%20-%20Simple%20With%20Position%20Indicator.stl?raw=true" + }, + { + "id": "ossm-remote-knob-knurled", + "name": "Remote Knob Knurled", + "description": "Remote system", + "filamentEstimate": 0, + "colour": "primary", + "required": false, + "filePath": "ossm-remote-knob-knurled.stl", + "url": "https://github.com/KinkyMakers/OSSM-hardware/blob/main/Printed%20Parts/Remote/Non-standard/OSSM%20-%20Remote%20-%20Knob%20-%20Knurled.stl?raw=true" + }, + { + "id": "ossm-remote-knob-knurled-with-position-indicator", + "name": "Remote Knob Knurled With Position Indicator", + "description": "Remote system", + "filamentEstimate": 0, + "colour": "primary", + "required": false, + "filePath": "ossm-remote-knob-knurled-with-position-indicator.stl", + "url": "https://github.com/KinkyMakers/OSSM-hardware/blob/main/Printed%20Parts/Remote/Non-standard/OSSM%20-%20Remote%20-%20Knob%20-%20Knurled%20With%20Position%20Indicator.stl?raw=true" + } + ], + "hardwareParts": [ + { + "id": "remote-hardware", + "required": true + } + ] + }, + "ossm-remote-radr": { + "name": "OSSM - RADR", + "description": "RADR remote system (RAD only)", + "image": "/images/remote/radr-remote.png", + "hardwareParts": [ + { + "id": "remote-hardware", + "required": true + } + ] + } + } + } +} \ No newline at end of file diff --git a/website/src/data/components/stand.json b/website/src/data/components/stand.json new file mode 100644 index 0000000..81b669e --- /dev/null +++ b/website/src/data/components/stand.json @@ -0,0 +1,212 @@ +{ + "hinges": { + "category": "Hinges", + "type": "mod", + "systems": { + "pivot-plate": { + "name": "Pivot Plate", + "description": "Pivot plate for the stand", + "image": "/images/options/pivot-plate.webp", + "hardwareCost": 10, + "price": 0, + "printedParts": [ + { + "id": "pivot-plate", + "name": "Pivot Plate Left", + "description": "Pivot plate for the stand", + "filamentEstimate": 150, + "colour": "primary", + "required": true, + "filePath": "OSSM - Stand - Pivot Plate.stl", + "url": "https://github.com/KinkyMakers/OSSM-hardware/blob/main/Printed%20Parts/Stand/OSSM%20-%20Stand%20-%203030%20Extrusion%20Base%20-%20Pivot%20Plate%20Left.stl?raw=true" + }, + { + "id": "pivot-plate-right", + "name": "Pivot Plate Right", + "description": "Pivot plate for the stand", + "filamentEstimate": 150, + "colour": "primary", + "required": true, + "filePath": "OSSM - Stand - Pivot Plate.stl", + "url": "https://github.com/KinkyMakers/OSSM-hardware/blob/main/Printed%20Parts/Stand/OSSM%20-%20Stand%20-%203030%20Extrusion%20Base%20-%20Pivot%20Plate%20Right.stl?raw=true" + } + ], + "hardwareParts": [ + { + "id": "hardware-fasteners-m6x25-shcs", + "required": true, + "quantity": 6 + }, + { + "id": "hardware-fasteners-m6-t-nuts", + "required": true, + "quantity": 6 + }, + { + "id": "hardware-fasteners-m6-washer", + "required": true, + "quantity": 6 + }, + { + "id": "hardware-fasteners-m6x25-handle", + "required": true, + "quantity": 2 + } + ] + }, + "pitclamp-reinforced-3030": { + "name": "PitClamp Reinforced 3030 Hinges", + "description": "Reinforced 3030 hinges for PitClamp", + "image": "/images/options/pitclamp-reinforced-3030-hinges.jpg", + "hardwareCost": 15, + "price": 0, + "printedParts": [ + { + "id": "pitclamp-reinforced-3030", + "name": "PitClamp Reinforced 3030 Hinges", + "description": "Reinforced 3030 hinges for PitClamp", + "filamentEstimate": 200, + "colour": "primary", + "required": true + } + ], + "hardwareParts": [ + { + "id": "pitclamp-reinforced-3030-hardware", + "required": true + } + ] + } + } + }, + "feet": { + "category": "Feet", + "type": "mod", + "printedParts": [ + { + "id": "standard-feet", + "name": "Standard", + "description": "Standard feet", + "filamentEstimate": 50, + "image": "/images/options/standard-feet.jpg", + "hardwareCost": 0, + "price": 0, + "colour": "secondary", + "required": true + }, + { + "id": "suction-feet", + "name": "Suction", + "description": "Suction feet for better stability", + "filamentEstimate": 60, + "image": "/images/options/suction-feet.jpg", + "hardwareCost": 5, + "price": 0, + "colour": "secondary", + "required": true + } + ], + "hardwareParts": [ + { + "id": "hardware-fasteners-m6x12-shcs", + "required": true, + "quantity": 4, + "relatedParts": [ + "standard-feet" + ] + }, + { + "id": "hardware-fasteners-m6-t-nuts", + "required": true, + "quantity": 4, + "relatedParts": [ + "standard-feet" + ] + } + ] + }, + "caps": { + "category": "Caps", + "type": "base", + "printedParts": [ + { + "id": "ossm-3030-cap", + "name": "3030 Cap", + "description": "Cap mounting system", + "filamentEstimate": 10, + "timeEstimate": "2h10m", + "colour": "secondary", + "required": true, + "filePath": "OSSM - 3030 Cap.stl", + "quantity": 6, + "url": "https://github.com/KinkyMakers/OSSM-hardware/blob/main/Printed%20Parts/Stand/OSSM%20-%20Stand%20-%203030%20Extrusion%20Base%20-%20Extrusion%20Cap.stl?raw=true" + } + ] + }, + "crossbarSupports": { + "category": "Crossbar Supports", + "type": "mod", + "printedParts": [ + { + "id": "standard-90-degree-support", + "name": "Standard 90 Degree Support", + "description": "Standard 90 degree support for the stand (hardware only)", + "filamentEstimate": 0, + "image": "/images/options/standard-90-degree-support.jpg", + "hardwareCost": 10, + "price": "$10.00-$20.00", + "colour": "primary", + "required": true, + "isHardwareOnly": true + }, + { + "id": "3d-printed-90-degree-support", + "name": "3D Printed 90 Degree Support", + "description": "3D printed 90 degree support for the stand", + "filamentEstimate": 100, + "image": "/images/options/3d-printed-90-degree-support.jpg", + "hardwareCost": 2, + "price": "$2.00-$4.00", + "colour": "secondary", + "required": true + } + ], + "hardwareParts": [ + { + "id": "hardware-fasteners-m6x12-shcs", + "required": true, + "quantity": 8, + "relatedParts": [ + "3d-printed-90-degree-support", + "standard-90-degree-support" + ] + }, + { + "id": "hardware-fasteners-m6-t-nuts", + "required": true, + "quantity": 8, + "relatedParts": [ + "3d-printed-90-degree-support", + "standard-90-degree-support" + ] + }, + { + "id": "hardware-fasteners-m6-washer", + "required": true, + "quantity": 8, + "relatedParts": [ + "3d-printed-90-degree-support", + "standard-90-degree-support" + ] + }, + { + "id": "hardware-fasteners-3030-90-degree-support", + "required": true, + "quantity": 4, + "relatedParts": [ + "standard-90-degree-support" + ] + } + ] + } +} \ No newline at end of file diff --git a/website/src/data/components/toyMounts.json b/website/src/data/components/toyMounts.json new file mode 100644 index 0000000..3cbf1bd --- /dev/null +++ b/website/src/data/components/toyMounts.json @@ -0,0 +1,115 @@ +{ + "toyMounts": { + "category": "Toy Mounts", + "type": "mod", + "printedParts": [ + { + "id": "ossm-toy-mount-flange-base-24mm-threaded", + "name": "Toy Mount Flange Base 24mm Threaded", + "description": "Toy mount system", + "filamentEstimate": 46.26, + "timeEstimate": "1h48m", + "colour": "secondary", + "required": true, + "filePath": "ossm-toy-mount-flange-base-24mm-threaded.stl" + }, + { + "id": "ossm-toy-mount-flange-base-dildo-ring-2.5in ", + "name": "Toy Mount Flange Base Dildo Ring 2.5in", + "description": "Toy mount system", + "filamentEstimate": 15.24, + "timeEstimate": "55m", + "colour": "secondary", + "required": true, + "filePath": "ossm-toy-mount-flange-base-dildo-ring-2_5in.stl" + }, + { + "id": "ossm-toy-mount-flange-base-dildo-ring-2in", + "name": "Toy Mount Flange Base Dildo Ring 2in", + "description": "Toy mount system", + "filamentEstimate": 15.24, + "timeEstimate": "55m", + "colour": "secondary", + "required": true, + "filePath": "ossm-toy-mount-flange-base-dildo-ring-2in.stl" + }, + { + "id": "ossm-toy-mount-double-double-24mm-threaded", + "name": "Toy Mount Double Double 24mm Threaded", + "description": "Toy mount system", + "filamentEstimate": 15.24, + "timeEstimate": "55m", + "colour": "secondary", + "required": true, + "filePath": "ossm-toy-mount-double-double-24mm-threaded.stl" + }, + { + "id": "ossm-toy-mount-double-double-rail-mounted", + "name": "Toy Mount Double Double Rail Mounted", + "description": "Toy mount system", + "filamentEstimate": 15.24, + "timeEstimate": "55m", + "colour": "primary", + "required": true, + "filePath": "ossm-toy-mount-double-double-rail-mounted.stl" + }, + { + "id": "ossm-toy-mount-sucson-mount-base-plate-24mm-threaded", + "name": "Toy Mount Sucson Mount Base Plate 24mm Threaded", + "description": "Toy mount system", + "filamentEstimate": 15.24, + "timeEstimate": "55m", + "colour": "secondary", + "required": true, + "filePath": "ossm-toy-mount-sucson-mount-base-plate-24mm-threaded.stl" + }, + { + "id": "ossm-toy-mount-sucson-mount-ring-insert-55mm", + "name": "Toy Mount Sucson Mount Ring Insert 55mm", + "description": "Toy mount system", + "filamentEstimate": 15.24, + "timeEstimate": "55m", + "colour": "primary", + "required": true, + "filePath": "ossm-toy-mount-sucson-mount-ring-insert-55mm.stl" + }, + { + "id": "ossm-toy-mount-sucson-mount-threaded-ring", + "name": "Toy Mount Sucson Mount Threaded Ring", + "description": "Toy mount system", + "filamentEstimate": 15.24, + "timeEstimate": "55m", + "colour": "secondary", + "required": true, + "filePath": "ossm-toy-mount-sucson-mount-threaded-ring.stl" + }, + { + "id": "ossm-toy-mount-tie-down-and-suction-plate-110mm", + "name": "Toy Mount Tie Down and Suction Plate 110mm", + "description": "Toy mount system", + "filamentEstimate": 15.24, + "timeEstimate": "55m", + "colour": "secondary", + "required": true, + "filePath": "ossm-toy-mount-tie-down-and-suction-plate-110mm.stl" + }, + { + "id": "ossm-toy-mount-tie-down-and-suction-plate-135mm", + "name": "Toy Mount Tie Down and Suction Plate 135mm", + "description": "Toy mount system", + "filamentEstimate": 15.24, + "timeEstimate": "55m", + "colour": "secondary", + "required": true, + "filePath": "ossm-toy-mount-tie-down-and-suction-plate-135mm.stl" + } + ], + "hardwareParts": [ + { + "id": "toy-mount-hardware", + "required": true, + "relatedParts": [] + } + ] + } +} \ No newline at end of file diff --git a/website/src/data/hardware.json b/website/src/data/hardware.json new file mode 100644 index 0000000..634a025 --- /dev/null +++ b/website/src/data/hardware.json @@ -0,0 +1,148 @@ +{ + "fasteners": { + "M3x8 Socket Head cap Screw": { + "id": "hardware-fasteners-m3x8-shcs", + "name": "M3x8 SHCS", + "description": "Hardware fasteners m3x8 socket head cap screw", + "price": 0 + }, + "M3x16 Socket Head cap Screw": { + "id": "hardware-fasteners-m3x16-shcs", + "name": "M3x16 SHCS", + "description": "Hardware fasteners m3x16 socket head cap screw", + "price": 0 + }, + "M3x20 Socket Head cap Screw": { + "id": "hardware-fasteners-m3x20-shcs", + "name": "M3x20 SHCS", + "description": "m3x20 socket head cap screw", + "price": 0 + }, + "M3 Hex Nut": { + "id": "hardware-fasteners-m3-hex-nut", + "name": "M3 Hex Nut", + "description": "Hardware fasteners m3 hex nut", + "price": 0 + }, + "M5 Hex Nut": { + "id": "hardware-fasteners-m5-hex-nut", + "name": "M5 Hex Nut", + "description": "Hardware fasteners m5 hex nut", + "price": 0 + }, + "M5x20 Socket Head cap Screw": { + "id": "hardware-fasteners-m5x20-shcs", + "name": "M5x20 SHCS", + "description": "Hardware fasteners m5x20 socket head cap screw", + "price": 0 + }, + "M5x35 Socket Head cap Screw": { + "id": "hardware-fasteners-m5x35-shcs", + "name": "M5x35 SHCS", + "description": "Hardware fasteners m5x35 socket head cap screw", + "price": 0 + }, + "M5x20mm Hex Coupling Nut": { + "id": "hardware-fasteners-m5x20mm-hex-coupling-nut", + "name": "M5x20mm Hex Coupling Nut", + "description": "Hardware fasteners m5x20mm hex coupling nut", + "price": 0 + }, + "M6x12 Socket Head cap Screw": { + "id": "hardware-fasteners-m6x12-shcs", + "name": "M6x12 SHCS", + "description": "Hardware fasteners m6x12 socket head cap screw", + "price": 0 + }, + "M6x25 Socket Head cap Screw": { + "id": "hardware-fasteners-m6x25-shcs", + "name": "M6x25 SHCS", + "description": "Hardware fasteners m6x25 socket head cap screw", + "price": 0 + }, + "M6 T Nuts": { + "id": "hardware-fasteners-m6-t-nuts", + "name": "M6 T Nuts", + "description": "Hardware fasteners m6 t nuts", + "price": 0 + }, + "M6 Washer": { + "id": "hardware-fasteners-m6-washer", + "name": "M6 Washer", + "description": "Hardware fasteners m6 washer", + "price": 0 + }, + "M6x25 Handle": { + "id": "hardware-fasteners-m6x25-handle", + "name": "M6x25 Handle", + "description": "Hardware fasteners m6x25 handle", + "price": 0 + } + }, + "motionComponents": { + "GT2 Pulley": { + "id": "hardware-gt2-pulley", + "name": "GT2 Pulley", + "description": "8mm Bore, 20T, 10mm Wide", + "price": 0 + }, + "GT2 Belt": { + "id": "hardware-gt2-belt", + "name": "GT2 Belt", + "description": "10mm wide, 500mm long", + "price": 0 + }, + "MGN12H Linear Rail": { + "id": "hardware-mgn12h-linear-rail", + "name": "MGN12H Linear Rail", + "description": "MGN12H Linear Rail, 350mm long [Min 250mm, recommended 350mm, Max 550mm]", + "price": 0 + }, + "Bearing MR115-2RS": { + "id": "hardware-bearing-MR115-2RS 5x11x4mm", + "name": "Bearing MR115-2RS 5x11x4mm", + "description": "MR115-2RS 5x11x4mm", + "price": 0 + } + }, + "extrusions": { + "3030 90 Degree Support": { + "id": "hardware-fasteners-3030-90-degree-support", + "name": "3030 90 Degree Support", + "description": "Hardware fasteners 3030 90 degree support", + "price": 0 + } + }, + "other": { + "Remote Hardware": { + "id": "remote-hardware", + "name": "Remote Hardware", + "description": "Remote hardware", + "price": 0 + }, + "PitClamp Hardware": { + "id": "pitclamp-hardware", + "name": "PitClamp Hardware", + "description": "PitClamp hardware", + "price": 0 + }, + "PitClamp Reinforced 3030 Hardware": { + "id": "pitclamp-reinforced-3030-hardware", + "name": "PitClamp Reinforced 3030 Hardware", + "description": "Hardware for PitClamp Reinforced 3030 hinges", + "price": 0 + }, + "Middle Pivot Hardware": { + "id": "middle-pivot-hardware", + "name": "Middle Pivot Hardware", + "description": "Middle Pivot hardware", + "price": 0 + }, + "Toy Mount Hardware": { + "id": "toy-mount-hardware", + "name": "Toy Mount Hardware", + "description": "Toy mount hardware", + "price": 0 + } + } +} \ No newline at end of file diff --git a/website/src/data/index.js b/website/src/data/index.js new file mode 100644 index 0000000..0eceef3 --- /dev/null +++ b/website/src/data/index.js @@ -0,0 +1,250 @@ +import motors from './motors.json'; +import powerSupplies from './powerSupplies.json'; +import optionsData from './options.json'; +import colors from './colors.json'; +import hardwareData from './hardware.json'; +import actuatorComponents from './components/actuator.json'; +import standComponents from './components/stand.json'; +import mountingComponents from './components/mounting.json'; +import toyMountsComponents from './components/toyMounts.json'; +import remoteComponents from './components/remote.json'; + +// Create a hardware lookup map from hardware.json +const hardwareLookup = new Map(); +Object.values(hardwareData).forEach((category) => { + Object.values(category).forEach((hardware) => { + hardwareLookup.set(hardware.id, hardware); + }); +}); + +// Function to resolve hardware references (IDs) to full hardware definitions +const resolveHardwareReferences = (components) => { + const resolvedComponents = {}; + + Object.entries(components).forEach(([componentKey, component]) => { + resolvedComponents[componentKey] = { ...component }; + + // Resolve hardwareParts if they exist + if (component.hardwareParts) { + resolvedComponents[componentKey].hardwareParts = component.hardwareParts.map((hw) => { + // If it's already a full object, return as-is + if (hw.id && hw.name) { + return hw; + } + // If it's a reference (just an ID string or object with hardwareId), resolve it + const hardwareId = typeof hw === 'string' ? hw : hw.hardwareId || hw.id; + const hardwareDef = hardwareLookup.get(hardwareId); + if (!hardwareDef) { + console.warn(`Hardware not found: ${hardwareId}`); + return hw; + } + // Merge the base definition with any overrides (quantity, relatedParts, required, etc.) + return { + ...hardwareDef, + ...(typeof hw === 'object' ? hw : {}), + id: hardwareDef.id, + name: hardwareDef.name, + description: hardwareDef.description, + price: hardwareDef.price, + // required comes from the component reference, not from hardware.json + }; + }); + } + + // Also resolve hardwareParts in systems + if (component.systems) { + resolvedComponents[componentKey].systems = {}; + Object.entries(component.systems).forEach(([systemKey, system]) => { + resolvedComponents[componentKey].systems[systemKey] = { ...system }; + if (system.hardwareParts) { + resolvedComponents[componentKey].systems[systemKey].hardwareParts = system.hardwareParts.map((hw) => { + if (hw.id && hw.name) { + return hw; + } + const hardwareId = typeof hw === 'string' ? hw : hw.hardwareId || hw.id; + const hardwareDef = hardwareLookup.get(hardwareId); + if (!hardwareDef) { + console.warn(`Hardware not found: ${hardwareId}`); + return hw; + } + return { + ...hardwareDef, + ...(typeof hw === 'object' ? hw : {}), + id: hardwareDef.id, + name: hardwareDef.name, + description: hardwareDef.description, + price: hardwareDef.price, + // required comes from the component reference, not from hardware.json + }; + }); + } + }); + } + }); + + return resolvedComponents; +}; + +// Combine all component files into a single components object +const rawComponents = { + ...actuatorComponents, + ...standComponents, + ...mountingComponents, + ...toyMountsComponents, + ...remoteComponents, +}; + +// Resolve hardware references +const components = resolveHardwareReferences(rawComponents); + +// Convert component parts to options format +const convertComponentPartsToOptions = (componentIds, componentData) => { + if (!componentIds || !Array.isArray(componentIds) || componentIds.length === 0) { + return componentIds && Array.isArray(componentIds) ? [] : undefined; + } + + // Check if componentData has systems (new structure) or printedParts (old structure) + if (componentData?.systems) { + // Special handling for remotes: flatten knobs from all systems + if (componentData.category === 'Remote' && componentIds.length > 0) { + // This is for remote knobs - flatten all knobs from all systems + const allKnobs = []; + Object.values(componentData.systems).forEach((system) => { + if (system.knobs) { + system.knobs.forEach((knob) => { + if (componentIds.includes(knob.id)) { + allKnobs.push({ + id: knob.id, + name: knob.name, + description: knob.description, + image: knob.image || `/images/options/${knob.id}.jpg`, + filamentEstimate: knob.filamentEstimate !== undefined ? `~${knob.filamentEstimate}g` : "0g", + hardwareCost: system.hardwareCost !== undefined ? system.hardwareCost : 0, + price: system.price !== undefined ? system.price : 0, + timeEstimate: knob.timeEstimate, + colour: knob.colour, + type: componentData.type || 'base', + }); + } + }); + } + }); + return allKnobs; + } + + // New structure: systems with printedParts and hardwareParts (for hinges, etc.) + return componentIds + .map((systemId) => { + const system = componentData.systems[systemId]; + if (!system) { + console.warn(`Component system not found: ${systemId}`); + return null; + } + + // Calculate total filament estimate from printed parts + const totalFilament = (system.printedParts || system.bodyParts)?.reduce((sum, part) => { + return sum + (part.filamentEstimate || 0); + }, 0) || 0; + + return { + id: systemId, + name: system.name, + description: system.description, + image: system.image || `/images/options/${systemId}.jpg`, + filamentEstimate: totalFilament > 0 ? `~${totalFilament}g` : "0g", + hardwareCost: system.hardwareCost !== undefined ? system.hardwareCost : 0, + price: system.price !== undefined ? system.price : 0, + colour: (system.printedParts || system.bodyParts)?.[0]?.colour || 'primary', + type: componentData.type || 'base', + }; + }) + .filter((opt) => opt !== null); + } + + // Old structure: printedParts array + if (!componentData || !componentData.printedParts) { + console.warn(`Component data not found or missing printedParts/systems`); + return []; + } + + return componentIds + .map((componentId) => { + const part = componentData.printedParts.find((p) => p.id === componentId); + if (!part) { + console.warn(`Component part not found: ${componentId}`); + return null; + } + + return { + id: part.id, + name: part.name, + description: part.description, + image: part.image || `/images/options/${part.id}.jpg`, + filamentEstimate: part.filamentEstimate !== undefined ? `~${part.filamentEstimate}g` : "0g", + hardwareCost: part.hardwareCost !== undefined ? part.hardwareCost : 0, + price: part.price !== undefined ? part.price : 0, + timeEstimate: part.timeEstimate, + colour: part.colour, + type: componentData.type || 'base', + }; + }) + .filter((opt) => opt !== null); +}; + +// Merge component options into options +const processOptions = (options, componentsData) => { + const processedOptions = { ...options }; + + // Process each option category + Object.keys(processedOptions).forEach((optionKey) => { + const optionCategory = processedOptions[optionKey]; + if (!optionCategory || !optionCategory.sections) return; + + const sections = { ...optionCategory.sections }; + const categoryUseComponents = optionCategory.useComponents; + + // Convert component parts to options format for each section + Object.keys(sections).forEach((sectionKey) => { + const section = sections[sectionKey]; + + // Check if section has componentIds to process + if (section.componentIds !== undefined) { + // Determine which component category to use + const componentKey = section.useComponents || categoryUseComponents; + + if (componentKey) { + const componentData = componentsData[componentKey]; + const options = convertComponentPartsToOptions(section.componentIds, componentData); + // Store component type info for filtering + section.componentType = componentData?.type || 'base'; + section.options = options; + } else { + console.warn(`No useComponents specified for ${optionKey}.${sectionKey}`); + section.options = []; + } + + // Clean up temporary properties + delete section.componentIds; + delete section.useComponents; + } + }); + + processedOptions[optionKey].sections = sections; + if (categoryUseComponents) { + delete processedOptions[optionKey].useComponents; + } + }); + + return processedOptions; +}; + +const options = processOptions(optionsData, components); + +export default { + motors, + powerSupplies, + options, + colors, + components, + hardware: hardwareData, +}; diff --git a/website/src/data/motors.json b/website/src/data/motors.json new file mode 100644 index 0000000..b42b536 --- /dev/null +++ b/website/src/data/motors.json @@ -0,0 +1,38 @@ +[ + { + "id": "57AIM30", + "name": "57AIM30 \"Gold Motor\"", + "description": "Standard NEMA 17 stepper motor with 1.8° step angle", + "speed": "1500 RPM", + "wattage": "100W", + "gear_count": "RS485", + "price": "$125-$250", + "image": "/images/motors/57AIM30.png", + "required": true, + "recommended": true + }, + { + "id": "42AIM30", + "name": "42AIM30 \"Round Motor\"", + "description": "High precision NEMA 17 stepper motor with 0.9° step angle", + "speed": "1500 RPM", + "wattage": "100W", + "gear_count": "RS485", + "price": "$135-$270", + "image": "/images/motors/42AIM30.png", + "required": true, + "recommended": false + }, + { + "id": "iHSV57", + "name": "iHSV57 \"Legacy Motor\"", + "description": "High precision NEMA 17 stepper motor with 0.9° step angle", + "speed": "3000 RPM", + "wattage": "180W", + "gear_count": "RS485", + "price": "$150-$300", + "image": "/images/motors/iHSV57.png", + "required": true, + "recommended": false + } +] \ No newline at end of file diff --git a/website/src/data/options.json b/website/src/data/options.json new file mode 100644 index 0000000..edac07d --- /dev/null +++ b/website/src/data/options.json @@ -0,0 +1,123 @@ +{ + "actuator": { + "title": "Actuator", + "sections": { + "mounts": { + "title": "Mounts", + "options": [ + { + "id": "middle-pivot", + "name": "Middle Pivot", + "description": "Middle Pivot mounting system", + "image": "/images/options/middle-pivot.png", + "filamentEstimate": "~147g", + "type": "base" + }, + { + "id": "pitclamp", + "name": "PitClamp Mini", + "description": "PitClamp Mini mounting system", + "image": "/images/options/PitClamp Mini Base.png", + "filamentEstimate": "~137g", + "type": "base" + } + ], + "isMultiSelect": false + }, + "cover": { + "title": "Cover", + "options": [ + { + "id": "standard-cover", + "name": "Standard Cover", + "description": "Standard actuator cover", + "image": null, + "filamentEstimate": "~27.61g", + "type": "base", + "componentId": "ossm-actuator-body-cover" + }, + { + "id": "blank-cover", + "name": "Blank Cover", + "description": "Blank cover option", + "image": null, + "filamentEstimate": "0g", + "type": "base" + } + ], + "isMultiSelect": false + }, + "pcbMount": { + "title": "PCB Mount", + "options": [ + { + "id": "3030-mount", + "name": "3030 Mount", + "description": "PCB mount for 3030 extrusion", + "image": null, + "filamentEstimate": null, + "type": "base" + }, + { + "id": "aio-cover-mount", + "name": "AIO Cover Mount", + "description": "All-in-one cover mount on the actuator", + "image": null, + "filamentEstimate": null, + "type": "base" + } + ], + "isMultiSelect": false + } + } + }, + "stand": { + "title": "Stand", + "sections": { + "hinges": { + "title": "Hinges", + "useComponents": "hinges", + "componentIds": ["pivot-plate", "pitclamp-reinforced-3030"], + "isMultiSelect": false + }, + "feet": { + "title": "Feet", + "useComponents": "feet", + "componentIds": ["standard-feet", "suction-feet"], + "isMultiSelect": false + }, + "crossbarSupports": { + "title": "Crossbar Supports", + "useComponents": "crossbarSupports", + "componentIds": ["standard-90-degree-support", "3d-printed-90-degree-support"], + "isMultiSelect": false + } + } + }, + "toyMounts": { + "title": "Toy Mounts", + "useComponents": "toyMounts", + "sections": { + "vacULock": { + "title": "Vac-U-Lock", + "componentIds": ["ossm-toy-mount-double-double-24mm-threaded", "ossm-toy-mount-double-double-rail-mounted"], + "isMultiSelect": true + }, + "flangeMount": { + "title": "Flange Mount", + "componentIds": ["ossm-toy-mount-flange-base-24mm-threaded", "ossm-toy-mount-flange-base-dildo-ring-2.5in", "ossm-toy-mount-flange-base-dildo-ring-2in"], + "isMultiSelect": true + }, + "suCSOn": { + "title": "SuCSOn", + "componentIds": ["ossm-toy-mount-sucson-mount-base-plate-24mm-threaded", "ossm-toy-mount-sucson-mount-ring-insert-55mm", "ossm-toy-mount-sucson-mount-threaded-ring"], + "isMultiSelect": true + }, + "tieDown": { + "title": "TieDown", + "componentIds": ["ossm-toy-mount-tie-down-and-suction-plate-110mm", "ossm-toy-mount-tie-down-and-suction-plate-135mm"], + "isMultiSelect": true + } + } + } +} diff --git a/website/src/data/powerSupplies.json b/website/src/data/powerSupplies.json new file mode 100644 index 0000000..40d7a08 --- /dev/null +++ b/website/src/data/powerSupplies.json @@ -0,0 +1,60 @@ +[ + { + "id": "psu-24v-5a", + "name": "24V 5A Power Supply", + "description": "24V DC power supply, 5A output", + "voltage": "24V", + "current": "5A", + "price": 20, + "image": "/images/power-supplies/24v-PSU.png", + "compatibleMotors": [ + "57AIM30", + "42AIM30", + "iHSV57" + ], + "required": true, + "links": [ + { + "store": "Amazon", + "link": "https://www.amazon.ca/Adapter-Female-5-5x2-5mm-Printer-Generator/dp/B0CR7DBKX5/ref=sr_1_5?crid=8CCHI94WM1J2&dib=eyJ2IjoiMSJ9.THY1sfJvVZbDjX-py4dIhAQXj69L2lE1OXB-OZijGqhizoxtEtZo3mrvVSGttuDBQXEHAAMoWxabFOZCD_9Drj4m3NxldA6I3NP2YB3LS14b2_uszbzhrCF_Xyu588Mzhuc59YSTgo3hw_uCub4NUFQZP-hGloBM4rXUYSgKsWrT_RL3l4dzQM9aY0QPVuDUbJreMnLwMF_rOkiH9r2-7jKHwDcEoVH8eQ09rVpXVyUqpcStI62_O2Rq17mu_YexGSyz3_9mznJvQlMPgg_DVBFvg69rhvcjbguSMVP8TG8.iVFiqorJkZztDuddLlNrSh0CRknKRiOp2VbJRHl7RRs&dib_tag=se&keywords=USB%2BC%2BTo%2BDC%2B5.5x2.5mm%2BAdapter&qid=1767501555&sprefix=usb%2Bc%2Bto%2Bdc%2B5%2B5x2%2B5mm%2Badapter%2Caps%2C127&sr=8-5&th=1" + }, + { + "store": "AliExpress", + "link": "https://www.aliexpress.com/item/100500312131213.html" + }, + { + "store": "Research & Desire", + "link": "https://www.researchanddesire.com/products/ossm-24v-power-supply" + } + ] + }, + { + "id": "psu-24v-usbc-pd", + "name": "24v USB-C PD Adapter", + "description": "24V USB-C PD Adapter, Requires 100W+ Power Supply", + "voltage": "24V", + "current": "5A", + "price": 30, + "image": "/images/power-supplies/24v-usbc-pd.png", + "compatibleMotors": [ + "57AIM30", + "42AIM30", + "iHSV57" + ], + "required": true, + "links": [ + { + "store": "Amazon", + "link": "https://www.amazon.ca/Adapter-Female-5-5x2-5mm-Printer-Generator/dp/B0CR7DBKX5/ref=sr_1_5?crid=8CCHI94WM1J2&dib=eyJ2IjoiMSJ9.THY1sfJvVZbDjX-py4dIhAQXj69L2lE1OXB-OZijGqhizoxtEtZo3mrvVSGttuDBQXEHAAMoWxabFOZCD_9Drj4m3NxldA6I3NP2YB3LS14b2_uszbzhrCF_Xyu588Mzhuc59YSTgo3hw_uCub4NUFQZP-hGloBM4rXUYSgKsWrT_RL3l4dzQM9aY0QPVuDUbJreMnLwMF_rOkiH9r2-7jKHwDcEoVH8eQ09rVpXVyUqpcStI62_O2Rq17mu_YexGSyz3_9mznJvQlMPgg_DVBFvg69rhvcjbguSMVP8TG8.iVFiqorJkZztDuddLlNrSh0CRknKRiOp2VbJRHl7RRs&dib_tag=se&keywords=USB%2BC%2BTo%2BDC%2B5.5x2.5mm%2BAdapter&qid=1767501555&sprefix=usb%2Bc%2Bto%2Bdc%2B5%2B5x2%2B5mm%2Badapter%2Caps%2C127&sr=8-5&th=1" + }, + { + "store": "AliExpress", + "link": "https://www.aliexpress.com/item/100500312131213.html" + }, + { + "store": "Research & Desire", + "link": "https://www.researchanddesire.com/products/ossm-24v-usb-c-adapter" + } + ] + } +] \ No newline at end of file diff --git a/website/src/index.css b/website/src/index.css new file mode 100644 index 0000000..634141e --- /dev/null +++ b/website/src/index.css @@ -0,0 +1,12 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} diff --git a/website/src/main.jsx b/website/src/main.jsx new file mode 100644 index 0000000..b9a1a6d --- /dev/null +++ b/website/src/main.jsx @@ -0,0 +1,10 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import './index.css' +import App from './App.jsx' + +createRoot(document.getElementById('root')).render( + + + , +) diff --git a/website/src/utils/exportUtils.js b/website/src/utils/exportUtils.js new file mode 100644 index 0000000..83060ee --- /dev/null +++ b/website/src/utils/exportUtils.js @@ -0,0 +1,295 @@ +import * as XLSX from 'xlsx'; + +// Generate markdown overview +export const generateMarkdownOverview = (config, printedParts, hardwareParts, filamentTotals, totalTime, total) => { + const md = []; + + md.push('# OSSM Build Configuration'); + md.push(`\n**Generated:** ${new Date().toLocaleString()}\n`); + + // Motor + if (config.motor) { + md.push(`## Motor: ${config.motor.name}`); + md.push(`- **Price:** ${config.motor.price}`); + md.push(`- **Speed:** ${config.motor.speed}`); + md.push(`- **Wattage:** ${config.motor.wattage}`); + md.push(''); + } + + // Power Supply + if (config.powerSupply) { + md.push(`## Power Supply: ${config.powerSupply.name}`); + md.push(`- **Price:** ${config.powerSupply.price}`); + md.push(''); + } + + // Colors + md.push(`## Colors`); + md.push(`- **Primary:** ${config.primaryColor || 'Not selected'}`); + md.push(`- **Accent:** ${config.accentColor || 'Not selected'}`); + md.push(''); + + // Mount + if (config.mount) { + md.push(`## Mount: ${config.mount.name}`); + md.push(''); + } + + // Cover + if (config.cover) { + md.push(`## Cover: ${config.cover.name}`); + md.push(''); + } + + // PCB Mount + if (config.pcbMount) { + md.push(`## PCB Mount: ${config.pcbMount.name}`); + md.push(''); + } + + // Stand Options + if (config.standHinge || config.standFeet || config.standCrossbarSupports?.length > 0) { + md.push(`## Stand Options`); + if (config.standHinge) md.push(`- **Hinges:** ${config.standHinge.name}`); + if (config.standFeet) md.push(`- **Feet:** ${config.standFeet.name}`); + if (config.standCrossbarSupports?.length > 0) { + md.push(`- **Crossbar Supports:** ${config.standCrossbarSupports.map(s => s.name).join(', ')}`); + } + md.push(''); + } + + // Remote + if (config.remote || config.remoteKnob) { + md.push(`## Remote`); + if (config.remote) md.push(`- **Type:** ${config.remote.name}`); + if (config.remoteKnob) md.push(`- **Knob:** ${config.remoteKnob.name}`); + md.push(''); + } + + // Filament Summary + if (filamentTotals.total > 0) { + md.push(`## Filament Summary`); + md.push(`- **Total:** ~${filamentTotals.total.toFixed(2)}g`); + if (filamentTotals.primary > 0) md.push(` - Primary: ~${filamentTotals.primary.toFixed(2)}g`); + if (filamentTotals.secondary > 0) md.push(` - Accent: ~${filamentTotals.secondary.toFixed(2)}g`); + md.push(`- **Estimated Print Time:** ${totalTime}`); + md.push(''); + } + + // Print Parts Summary + if (printedParts.length > 0) { + md.push(`## Printed Parts Summary`); + md.push(`- **Total Parts:** ${printedParts.length}`); + + // Group by category + const partsByCategory = {}; + printedParts.forEach(part => { + const category = part.category || 'Other'; + if (!partsByCategory[category]) { + partsByCategory[category] = []; + } + partsByCategory[category].push(part); + }); + + Object.entries(partsByCategory).forEach(([category, parts]) => { + md.push(`### ${category} (${parts.length} parts)`); + parts.forEach(part => { + md.push(`- ${part.name}${part.filamentEstimate ? ` (~${part.filamentEstimate}g)` : ''}`); + }); + }); + md.push(''); + } + + // Hardware Summary + if (hardwareParts.length > 0) { + md.push(`## Hardware Summary`); + md.push(`- **Total Items:** ${hardwareParts.length}`); + md.push(''); + } + + // Cost Summary + if (total > 0) { + md.push(`## Estimated Cost`); + md.push(`- **Total:** $${total.toFixed(2)}`); + md.push(''); + } + + return md.join('\n'); +}; + +// Generate Excel BOM with purchase links +export const generateExcelBOM = (hardwareParts, printedParts, config) => { + const rows = []; + + // Header + rows.push(['Item', 'Name', 'Quantity', 'Price', 'Link', 'Category', 'Type']); + + // Add motor + if (config.motor) { + const motorLinks = config.motor.links || []; + const firstLink = motorLinks.length > 0 ? motorLinks[0].link : ''; + rows.push([ + 'Motor', + config.motor.name, + 1, + config.motor.price, + firstLink, + 'Motor', + 'Hardware' + ]); + } + + // Add power supply + if (config.powerSupply) { + const psuLinks = config.powerSupply.links || []; + const firstLink = psuLinks.length > 0 ? psuLinks[0].link : ''; + rows.push([ + 'Power Supply', + config.powerSupply.name, + 1, + config.powerSupply.price, + firstLink, + 'Power Supply', + 'Hardware' + ]); + } + + // Add hardware parts + hardwareParts.forEach(hw => { + const links = hw.links || []; + const firstLink = links.length > 0 ? links[0].link : (hw.url || ''); + rows.push([ + hw.id || '', + hw.name || '', + hw.quantity || 1, + hw.price ? `$${parseFloat(hw.price).toFixed(2)}` : '', + firstLink, + hw.category || 'Hardware', + 'Hardware' + ]); + }); + + // Add printed parts (for reference, not purchase) + printedParts.forEach(part => { + rows.push([ + part.id || '', + part.name || '', + 1, + 'N/A', + part.url || '', + part.category || 'Printed', + 'Printed Part' + ]); + }); + + // Create workbook and worksheet + const wb = XLSX.utils.book_new(); + const ws = XLSX.utils.aoa_to_sheet(rows); + + // Set column widths + ws['!cols'] = [ + { wch: 30 }, // Item + { wch: 40 }, // Name + { wch: 10 }, // Quantity + { wch: 12 }, // Price + { wch: 50 }, // Link + { wch: 20 }, // Category + { wch: 15 } // Type + ]; + + XLSX.utils.book_append_sheet(wb, ws, 'BOM'); + + return wb; +}; + +// Generate Excel Print List with completion tracker +export const generateExcelPrintList = (printedParts, filamentTotals) => { + const rows = []; + + // Header + rows.push(['Part Name', 'Category', 'Color', 'Quantity', 'Filament (g)', 'Print Time', 'Status', 'Completed']); + + // Group parts by category and color + const partsByCategoryColor = {}; + printedParts.forEach(part => { + const category = part.category || 'Other'; + const color = part.colour === 'primary' ? 'Primary' : part.colour === 'secondary' ? 'Accent' : 'Other'; + const key = `${category}_${color}`; + + if (!partsByCategoryColor[key]) { + partsByCategoryColor[key] = []; + } + partsByCategoryColor[key].push(part); + }); + + // Sort by category, then color + const sortedKeys = Object.keys(partsByCategoryColor).sort(); + + sortedKeys.forEach(key => { + const parts = partsByCategoryColor[key]; + const [category, color] = key.split('_'); + + parts.forEach(part => { + const filament = typeof part.filamentEstimate === 'number' + ? part.filamentEstimate + : parseFloat(part.filamentEstimate?.replace('~', '').replace('g', '')) || 0; + + rows.push([ + part.name || part.id || '', + category, + color, + 1, + filament > 0 ? filament.toFixed(2) : '', + part.timeEstimate || '', + '', // Status column (user fills in) + '' // Completed column (user checks) + ]); + }); + }); + + // Add summary row + rows.push([]); + rows.push(['TOTAL', '', '', printedParts.length, filamentTotals.total.toFixed(2), '', '', '']); + + // Create workbook and worksheet + const wb = XLSX.utils.book_new(); + const ws = XLSX.utils.aoa_to_sheet(rows); + + // Set column widths + ws['!cols'] = [ + { wch: 40 }, // Part Name + { wch: 20 }, // Category + { wch: 12 }, // Color + { wch: 10 }, // Quantity + { wch: 15 }, // Filament + { wch: 15 }, // Print Time + { wch: 15 }, // Status + { wch: 12 } // Completed + ]; + + XLSX.utils.book_append_sheet(wb, ws, 'Print List'); + + // Create a summary sheet with progress calculation + // Note: Excel formulas need to reference cells properly + const summaryRows = [ + ['Print Progress Summary'], + [], + ['Total Parts', printedParts.length], + ['Completed Parts', { f: `COUNTIF('Print List'.H:H,"✓")` }], + ['Progress %', { f: `IF(B3>0, (B4/B3)*100, 0)` }], + [], + ['Filament Summary'], + ['Total Filament (g)', filamentTotals.total.toFixed(2)], + ['Primary Color (g)', filamentTotals.primary.toFixed(2)], + ['Accent Color (g)', (filamentTotals.secondary || 0).toFixed(2)] + ]; + + const summaryWs = XLSX.utils.aoa_to_sheet(summaryRows); + summaryWs['!cols'] = [ + { wch: 25 }, + { wch: 15 } + ]; + XLSX.utils.book_append_sheet(wb, summaryWs, 'Summary'); + + return wb; +}; diff --git a/website/src/utils/priceFormat.js b/website/src/utils/priceFormat.js new file mode 100644 index 0000000..ea79923 --- /dev/null +++ b/website/src/utils/priceFormat.js @@ -0,0 +1,26 @@ +// Helper function to format price (handles both number and string prices) +export function formatPrice(price) { + if (typeof price === 'number') { + return `$${price.toFixed(2)}`; + } + if (typeof price === 'string') { + // If it's already formatted as a string (e.g., "$125-$250"), return as-is + return price.startsWith('$') ? price : `$${price}`; + } + return '$0.00'; +} + +// Helper function to get numeric price for calculations (returns 0 for string prices) +export function getNumericPrice(price) { + if (typeof price === 'number') { + return price; + } + if (typeof price === 'string') { + // Try to extract a number from string prices like "$125-$250" + const match = price.match(/\d+/); + if (match) { + return parseFloat(match[0]); + } + } + return 0; +} diff --git a/website/src/utils/shareService.js b/website/src/utils/shareService.js new file mode 100644 index 0000000..787c04b --- /dev/null +++ b/website/src/utils/shareService.js @@ -0,0 +1,95 @@ +// Share service for creating 7-day shareable links +// Uses localStorage to store shared configs with expiration + +const SHARE_PREFIX = 'ossm_share_'; +const SHARE_EXPIRY_DAYS = 7; + +export const createShareLink = (config) => { + // Generate a unique ID for this share + const shareId = `share_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`; + + // Calculate expiration date (7 days from now) + const expiresAt = Date.now() + (SHARE_EXPIRY_DAYS * 24 * 60 * 60 * 1000); + + // Store the config in localStorage with expiration + const shareData = { + config, + expiresAt, + createdAt: Date.now() + }; + + try { + localStorage.setItem(`${SHARE_PREFIX}${shareId}`, JSON.stringify(shareData)); + + // Clean up expired shares + cleanupExpiredShares(); + + // Return the shareable URL + const currentUrl = window.location.origin + window.location.pathname; + return `${currentUrl}?share=${shareId}`; + } catch (error) { + console.error('Error creating share link:', error); + // If localStorage is full, try using sessionStorage as fallback + try { + sessionStorage.setItem(`${SHARE_PREFIX}${shareId}`, JSON.stringify(shareData)); + const currentUrl = window.location.origin + window.location.pathname; + return `${currentUrl}?share=${shareId}&session=true`; + } catch (fallbackError) { + console.error('Error with fallback storage:', fallbackError); + throw new Error('Unable to create share link. Please try again.'); + } + } +}; + +export const getSharedConfig = (shareId, isSession = false) => { + const storage = isSession ? sessionStorage : localStorage; + const key = `${SHARE_PREFIX}${shareId}`; + const shareDataStr = storage.getItem(key); + + if (!shareDataStr) { + return null; + } + + try { + const shareData = JSON.parse(shareDataStr); + + // Check if expired + if (shareData.expiresAt && Date.now() > shareData.expiresAt) { + storage.removeItem(key); + return null; + } + + return shareData.config; + } catch (error) { + console.error('Error reading share data:', error); + return null; + } +}; + +export const cleanupExpiredShares = () => { + try { + const keys = Object.keys(localStorage); + const now = Date.now(); + + keys.forEach(key => { + if (key.startsWith(SHARE_PREFIX)) { + try { + const shareData = JSON.parse(localStorage.getItem(key)); + if (shareData.expiresAt && now > shareData.expiresAt) { + localStorage.removeItem(key); + } + } catch (e) { + // Invalid data, remove it + localStorage.removeItem(key); + } + } + }); + } catch (error) { + console.error('Error cleaning up expired shares:', error); + } +}; + +export const deleteShare = (shareId, isSession = false) => { + const storage = isSession ? sessionStorage : localStorage; + storage.removeItem(`${SHARE_PREFIX}${shareId}`); +}; diff --git a/website/tailwind.config.js b/website/tailwind.config.js new file mode 100644 index 0000000..dca8ba0 --- /dev/null +++ b/website/tailwind.config.js @@ -0,0 +1,11 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: [ + "./index.html", + "./src/**/*.{js,ts,jsx,tsx}", + ], + theme: { + extend: {}, + }, + plugins: [], +} diff --git a/website/vite.config.js b/website/vite.config.js new file mode 100644 index 0000000..5a33944 --- /dev/null +++ b/website/vite.config.js @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], +}) diff --git a/~$BOM.xlsx b/~$BOM.xlsx new file mode 100644 index 0000000..7bf2776 Binary files /dev/null and b/~$BOM.xlsx differ